【Linux】驱动开发方法

使用Petalinux学习驱动开发时的一些经验。

部分图片和经验来源于网络,若有侵权麻烦联系我删除,主要是做笔记的时候忘记写来源了,做完笔记很久才写博客。

 专栏目录:记录自己的嵌入式学习之路-CSDN博客


目录

1    基础——字符设备驱动

1.1    分配设备号(驱动入口使用)

1.2    字符设备创建和注册

1.3    删除字符设备

1.4    注销设备号(驱动出口使用)

1.5    设备类的创建

1.6    设备类的删除

1.7    创建类下的设备

1.8    删除类下的设备

2    初级——GPIO子系统

2.1    GPIO子系统使用的前提——设备树    

2.2    GPIO常用的OF函数

2.3    GPIO子系统常用函数

2.4    常规的GPIO子驱动的编写步骤

2.5    类和设备操作示例

3    一切总线/框架的基础——平台总线框架

3.1    平台总线实例

3.2    平台总线的设备/驱动匹配

3.3    平台驱动结构体

3.4    平台驱动的编写步骤

3.5    平台设备结构体

3.6    平台设备的编写步骤

3.7    使用设备树代替平台设备

4    简单框架——LED驱动框架

4.1    LED设备驱动框架

4.2    LED框架提供的重要函数/宏定义函数

4.3    常规的LED框架驱动编写步骤

5    常用技巧

5.1    倒退式的错误处理方法

5.2    设置设备文件私有数据

5.3    鸡贼地判断指针是否需要自己分配空间

5.4    获取纳秒级时间

6    一些驱动编写常用的(宏)函数

6.1    container_of

6.2    platform_set_drvdata/platform_get_drvdata

6.3    platform_get_resource

6.4    of_address_to_resource

6.5    of_get_child_by_name

6.6    驱动程序打印数据(代替printk)

7    编译错误

7.1    将.c编译成模块时提示asm/types.h: 没有那个文件或目录

7.2    disagrees about version of symbol module_layout

7.3    *** 没有规则可制作目标

7.4    linux/ide.h: No such file or directory

7.5    implicit declaration of function ‘copy_to_user’

7.6    ‘struct file_operations’ has no member named ‘owner’

7.7    function declaration isn’t a prototype

7.8    modpost: __aeabi_dmul/__aeabi_i2d/__aeabi_d2iz undefined

8    驱动加载/卸载错误

8.1    depmod找不到目录

8.2    使用modprobe装载驱动时没反应

8.3     : loading out-of-tree module taints kernel

8.4    使用of_address_to_resource函数获取设备树资源的时候错误

9    其他错误

9.1    开机显示找不到eth0

9.2    printk输出浮点数没内容


1    基础——字符设备驱动

1.1    分配设备号(驱动入口使用)

        

1.2    字符设备创建和注册

        

1.3    删除字符设备

        

1.4    注销设备号(驱动出口使用)

        

1.5    设备类的创建

        为了实现设备节点文件的自动创建,需要在驱动加载时创建设备类。

        struct class *class_create (struct module *owner, const char *name)

        其中,参数 owner 一般为 THIS_MODULE,参数 name 是类名字。返回值是个指向结构体 class 的指针,也就是创建的类。

1.6    设备类的删除

        有设备类的创建就得有设备类的删除,在设备驱动卸载时进行。

        void class_destroy(struct class *cls);

        其中,参数 cls就是要删除的类。

1.7    创建类下的设备

        创建设备就是在/dev/下创建设备文件了

        struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)

        device_create 是个可变参数函数,参数 class 就是设备要创建哪个类下面;参数 parent 是父设备,一般为 NULL,也就是没有父设备;参数 devt 是设备号;参数 drvdata 是设备可能会使用的一些数据,一般为NULL;参数 fmt 是设备名字,如果设置 fmt=xxx 的话,就会生成/dev/xxx 这个设备文件。返回值就是创建好的设备。

1.8    删除类下的设备

        卸载驱动的时候需要删除掉创建的设备:

        void device_destroy(struct class *class, dev_t devt)


2    初级——GPIO子系统

        gpio子系统是linux内核当中用于管理GPIO资源的一套系统,它提供了很多GPIO相关的API接口。驱动程序中使用GPIO之前需要向gpio子系统申请,申请成功之后才可以使用。

        有了GPIO子系统,GPIO 的输入、输出方向,设置GPIO输出高或低电平、读取GPIO输入电平就无需傻傻地手动修改寄存器实现了。

2.1    GPIO子系统使用的前提——设备树    

        gpio子系统虽然方便了驱动开发者使用gpio,但是最终还是得去操作硬件寄存器;所以在使用gpio子系统前,需要在设备树中描述硬件相关资源。

        

        如上图所示,led-gpio属性用于描述该硬件由gpio0组的7号IO控制,且其有效电平是高电平。事实上这个led-gpio可以为xxx-gpio,可以理解为一个自定义属性,是用于驱动程序读取和gpio相关参数的。至于gpio0这个是怎么来的,就要看芯片厂商给出的设备树,对于我们来说,就是要看源码中的zynq-7000.dsti:

        

该节点描述了zynq的gpio设备。其中:

        compatible:表明了要匹配的驱动,在源码中全文搜索其内容就能找到相应的驱动程序,不过用Linux的nautilus没办法搜索到,用Windows可以,其驱动在<源码>/drivers/gpio中;

        #gpio-cells:表明了外部在引用该gpio外设的时候需要提供的额外参数数量,如上面之所以要<&gpi00 7 GPIO_ACTIVE_HIGH>,后面这两个参数就是#gpio-cells决定了的,至于参数具体含义是什么,可以参考<源码>/Documentation/devicetree/bindings/gpio下的gpio-zynq.txt;

        gpio-controller:表示gpio0节点是个GPIO控制器,表示这个节点对应的驱动程序是gpio驱动;
        reg:gpio的地址范围;

2.2    GPIO常用的OF函数

int of_gpio_named_count(*np, *propname):用于获取设备树某个属性里面定义了几个GPIO信息,要注意的是空的GPIO信息也会被统计到,其中np为设备节点,propname为要统计的GPIO属性;

intof_gpio_count(*np):此函数统计的是“gpios”这个属性的GPIO数量;

int of_get_named_gpio(*np, *propname, index):获取设备树中指定GPIO引脚的在内核中的编号;

2.3    GPIO子系统常用函数

int gpio_request(gpio, *label):向系统申请一个GPIO引脚,使用前必须申请;

void gpio_free(gpio):释放申请的GPIO引脚;

int gpio_direction_input(gpio):设置GPIO为输入

int gpio_direction_output(gpio, value):设置GPIO为输出并设置其默认输出值;

int gpio_get_value(gpio):获取GPIO的值;

void gpio_set_value(gpio, value):设定GPIO的值;

int gpio_is_valid(gpio):判断GPIO编号是否是有效编号;

2.4    常规的GPIO子驱动的编写步骤

(1)    of_get_named_gpio获取GPIO编号,并用gpio_is_valid验证编号是否有效。其中,需要的device_node参数由OF函数读取设备树或平台驱动提供(probe函数有);

(2)    使用gpio_request向系统申请使用指定的GPIO编号;

(3)    使用of_property_read_string读取设备树的default-state以确定GPIO的缺省状态;

(4)    使用gpio_direction_output/input设置GPIO为输出还是输入;

(5)    像字符设备驱动一样,进行设备号的分配、cdev的初始化、cdev的添加、创建类、创建设备等等;

(6)    在read/write函数中,利用gpio_get_value/gpio_set_value实现对GPIO的读写操作;
在驱动写卸载相关的函数(如驱动的exit或平台驱动的remove)中,使用gpio_free释放掉占用的GPIO编号;

2.5    类和设备操作示例

        


3    一切总线/框架的基础——平台总线框架

(1)    总线(Bus)负责维护注册(device)和驱动(driver);

(2)    注册进来一个device或者driver都会调用Bus->match函数将device与driver进行配对,并将它们加入链表,如果配对成功,调用Bus->probe或者driver->probe函数;

(3)    一个device只能配对一个driver;而一个driver可以对应多个device。

(4)    平台总线是一种虚拟的总线,其面向的是没有硬件总线,直接在CPU上取址的设备。

3.1    平台总线实例

        (源码/include/linux/device.h)

        系统中已经定义好的bus_type结构体变量platform_bus_type,其中定义了一些管理设备/驱动的函数。

3.2    平台总线的设备/驱动匹配

        除driver_override匹配外,其他匹配方法都会按优先级依次尝试,直到匹配到或者所有方法都用完后才结束。一般需要支持有无设备树两种情况,即(2)必须支持,(4)和(5)至少得支持一种。

(1)    driver_override(强制驱动匹配) :    优先级1

        平台设备结构体(platform_device)中的driver_override成员被赋值,则强制使用与该值同名(.name)的驱动,完全不考虑其他驱动。(哪怕没有匹配的呀不考虑别的驱动)

(2)    设备

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值