1 Star 2 Fork 0

basic-learn/linux-drivers

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
2年前
2年前
2年前
2年前
2年前
2年前
2年前
Loading...
README

前言

本系列的项目代码主要参考文献^1,建议读者熟悉了^1之后再详细阅读^2^2详细介绍了Linux驱动模型。需要注意的是文献^1主要面向的是嵌入式设备驱动编写,与个人计算机驱动编写有一定不同。比如,设备树一般不在个人计算机中使用,而是依靠各种自动配置协议来识别硬件。

  • ch34x usb转串口驱动,不是自己写的,从网上下载的
  • helloworld 第一个驱动文件
  • micsdevice 杂项设备
  • characterdevice 字符设备,这里介绍了静态和动态申请设备号
  • registerchrdev 注册字符设备
  • parameter 介绍了如何在安装module时传入参数

驱动安装时传参

首先需要在驱动中定义接收数据的变量。

static int a = 0;
static int b[5];// 如果传入的参数超过五个会报错 could not insert module parameter/parameter.ko: Invalid parameters 
static int count; // 记录用户在安装驱动时传入给b的参数的个数
module_param(a, int, S_IRUSR);
module_param_array(b, int, &count, S_IRUSR);

传参方法是:

insmod modulename a=3 b=1,2,3,4,5
# b 可以传入少于5个的参数但是不能多于5个

杂项设备

使用misc_register函数注册杂项设备,注册成功后会自动在/dev/路径下创建与一个定义在struct miscdevice中的name字段同名的节点。

@startmindmap
+ 杂项设备
++ 1. 注册杂项设备
+++ misc_register(&misc)
++ 2. 构建杂项设备结构体
***:
<code>
static struct miscdevice misc = {
    .minor=12,
    .name="name",// /dev/下的名称
    .fops = &
}
</code>
;
++ 3. 构建file_operations
++ 4. 卸载杂项设备
+++ misc_deregister(&misc)
@endmindmap

struct miscdevice misc_dev = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = MODULENAME,
    .fops = &misc_fops,
};

字符设备

如果驱动没有自动创建设备节点

字符设备与杂项设备不同的是,在注册字符设备之后并不会自动在/dev/路径下创建节点。需要使用mknod命令创建节点。

格式:

mknod 名称 类型 主设备号 次设备号

例如:

mknod /dev/test c 499 0

驱动创建设备节点

{ // 创建设备文件
    // 在/sys/class/目录下创建一个类,类名为就是第二个参数
    cls = class_create(hellocdev.owner, MODULENAME);

    // 在/dev/目录下创建设备文件,文件名为最后一个参数
    device_create(cls,
                    NULL,
                    MKDEV(major_num, minor_num),
                    NULL,
                    MODULENAME);
}

{ // 删除设备文件
    device_destroy(cls, MKDEV(major_num, minor_num));
    class_destroy(cls);
}

总结

注意在4registerchrdev项目中,文献^1中的代码的释放顺序有问题。应当秉承先创建者后释放的原则。

@startmindmap

* 字符设备驱动框架
    ** 1.驱动初始化
        *** 分配设备号
            **** 静态分配
                ***** register_chrdev_region
            **** 动态分配
                ***** alloc_chrdev_region
            **** 操作设备号
                ***** dev_t本质上就是一个无符号32位整数
                ***** MAJOR宏从dev_t中获取主设备号,占12bit
                ***** MINOR宏从dev_t中获取次设备号,占20bit
                ***** MKDEV(major, minor)获取dev_t
        *** cdev_init
        *** cdev_add
    ** 2.构建file_operations
    ** 3.生成设备节点
        *** 自动生成设备节点
            **** a. class_create创建一个class
            **** b. device_create创建一个设备节点
        *** 手动生成设备节点
            **** mknod命令
    ** 4. 驱动卸载
        *** device_destroy 销毁设备节点
        *** class_destroy 销毁class
        *** cdev_del 卸载设备
        *** unregister_chrdev_region 释放设备号

@endmindmap

平台总线模型 platform bus model

建议详细阅读文献^2

什么是platform设备?

概括来说,Platform设备包括:基于端口的设备(已不推荐使用,保留下来只为兼容旧设备,legacy);连接物理总线的桥设备;集成在SOC平台上面的控制器;连接在其它bus上的设备(很少见)。等等。^2

这些设备有一个基本的特征:可以通过CPU bus直接寻址(例如在嵌入式系统常见的“寄存器”)。因此,由于这个共性,内核在设备模型的基础上(device和device_driver),对这些设备进行了更进一步的封装,抽象出paltform bus、platform device和platform driver,以便驱动开发人员可以方便的开发这类设备的驱动。^2

可以说,paltform设备对Linux驱动工程师是非常重要的,因为我们编写的大多数设备驱动,都是为了驱动plaftom设备。本文我们就来看看Platform设备在内核中的实现。^2

什么是platform bus

在计算机中有这样一类设备,它们通过各自的设备控制器,直接和CPU连接,CPU可以通过常规的寻址操作访问它们(或者说访问它们的控制器)。这种连接方式,并不属于传统意义上的总线连接。但设备模型应该具备普适性,因此Linux就虚构了一条Platform Bus,供这些设备挂靠。^2

platform 设备实现

在实现时就需要实现两个驱动,一个是device驱动,一个是driver驱动。前者实现与设备紧密相关的代码,后者实现若干种设备的相同代码。

platform_driver.driver.name用于和platform_device.name进行匹配,当platform_driverplatform_device匹配成功时,就会自动调用platform_driver.probe函数;

platform_driverplatform_device任意一个卸载时,就会调用platform_driver.remove函数;

当设备收到shutdown命令时,调用platform_driver.shutdown函数;

当设备休眠时,调用platform_driver.suspend函数;

当设备被唤醒时,调用platform_driver.resume函数。

platform_deviceplatform_driver进行匹配时,如果platform_driver.id_tableplatform_driver.driver.name都有值时,那么就会忽略platform_driver.driver.name,只拿platform_driver.id_table中的名字与platform_driver进行匹配,无论是否匹配。

struct platform_device {
    const char  *name;
    int     id;
    bool        id_auto;
    struct device   dev;
    u64     platform_dma_mask;
    struct device_dma_parameters dma_parms;
    u32     num_resources;
    struct resource *resource;
    const struct platform_device_id *id_entry;
    char *driver_override; /* Driver name to force a match */
    /* MFD cell pointer */
    struct mfd_cell *mfd_cell;
    /* arch specific additions */
    struct pdev_archdata    archdata;
};

struct platform_driver {
    int (*probe)(struct platform_device *);
    int (*remove)(struct platform_device *);
    void (*shutdown)(struct platform_device *);
    int (*suspend)(struct platform_device *, pm_message_t state);
    int (*resume)(struct platform_device *);
    struct device_driver driver;
    const struct platform_device_id *id_table;
    bool prevent_deferred_probe;
};

probe函数的实现

前面讲了,如果平台设备名称与平台驱动名称匹配那么就会调用platform_driver.probe函数,那么如何实现该函数呢?

  1. 需要从平台设备中获取硬件资源 a. 直接从int (*probe)(struct platform_device *pdev)传入的pdev获取,struct resource* res = pdev->res;(不安全,不推荐); b. 使用函数访问,struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int)
  2. 注册杂项/字符设备,完善file_operation结构体,并生成设备节点 a. 请求IO端口。 struct resource* request_mem_region(start,n,name),这里的目的是判断该IO端口的地址是否已经被请求使用了,如果被请求使用了或者部分地址重叠,那么就会请求失败,请求失败就不能进行IO映射。如果请求成功,那么就会将IO端口地址记录下来,如果后续有其它驱动尝试请求就会请求失败。参见[^3][^4] b. 端口映射 ioremap,参见[^3][^4]。 c. 注册杂项设备

参考信息

[^3]: Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析

[^4]: 内核request_mem_region 和 ioremap的理解

空文件

简介

这是一个记录了若干种Linux驱动样例的仓库 展开 收起
取消

发行版

暂无发行版

贡献者 (1)

全部

近期动态

12个月前推送了新的提交到 master 分支,88e76f6...d98ee6a
2年多前推送了新的提交到 master 分支,da0485e...88e76f6
2年多前推送了新的 master 分支
2年多前创建了仓库
不能加载更多了
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/basic-learn/linux-drivers.git
git@gitee.com:basic-learn/linux-drivers.git
basic-learn
linux-drivers
linux-drivers
master

搜索帮助