文章目录
- 1.printk
- 2.module_param
- 3.MODULE_PARM_DESC
- 4.EXPORT_SYMBOL_GPL
- 5.register_chrdev
- 6.unregister_chrdev
- 7.copy_to_user
- 8.copy_from_user
- 9.ioremap
- 10.iounmap
- 11.writel
- 12.readl
- 13.class_create
- 14.device_create
- 15.class_destroy
- 16.device_destroy
- 17.ioctl
- 18.封装命令码的宏
- 19.字符设备驱动分布实现的API
- 20.中断屏蔽的API
- 21.自旋锁的API
- 22.信号量的API
- 23.互斥体的API
- 24.原子操作的API
- 25.阻塞IO的API
- 26.epoll
- 27.设备树相关的结构体
- 28.获取device_node的函数
- 29.设备树中获取属性
- 30.gpio子系统
- 31.gpiod子系统
- 32.内核定时器
- 34.中断处理
- 35.中断底半步之tasklet
- 36.中断底半部之工作队列
- 37.platform
- 38.i2c总线驱动
- 39.spi总线驱动
- 40.块设备驱动
- 41.块设备队列处理
1.printk
打印等级
#define KERN_EMERG "0" /* system is unusable */
#define KERN_ALERT "1" /* action must be taken immediately */
#define KERN_CRIT "2" /* critical conditions */
#define KERN_ERR "3" /* error conditions */
#define KERN_WARNING "4" /* warning conditions */
#define KERN_NOTICE "5" /* normal but significant condition */
#define KERN_INFO "6" /* informational */
#define KERN_DEBUG "7" /* debug-level messages */
其它用法和printf一样,可以利用打印等级区分错误信息
2.module_param
module_param(name, type, perm)
功能:接收命令行传递的参数
参数:
@name:变量名
@type:变量的类型
/* Standard types are:
* byte, hexint, short, ushort, int, uint, long, ulong
* charp: a character pointer
* bool: a bool, values 0/1, y/n, Y/N.
* invbool: the above, only sense-reversed (N = true). */
@perm:权限 权限的最大值0664,用户与组全部归属于root用户
在 /sys/module/驱动名字/parameters/目录下创建一个名字为 变量名 的文件,此文件的权限为 type
3.MODULE_PARM_DESC
MODULE_PARM_DESC(_parm, desc)
功能:对传递的参数进行描述,可以通过modinfo命令查看这个描述字段
参数:
@_parm:变量名
@desc:描述字段
4.EXPORT_SYMBOL_GPL
EXPORT_SYMBOL_GPL(x)
功能:导出符号表
参数:
@x:函数名/变量名
5.register_chrdev
int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)
#include <linux/fs.h>
功能:创建字符设备驱动
参数:
@major:主设备号 (一次创建256个设备号,主设备号+[0-255])
major > 0 :静态指定主设备号
major = 0 :动态申请主设备号
@name:驱动的名字
linux@ubuntu:/dev/input$ cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
| |
主设备号 驱动的名字
@fops:操作方法结构体
struct file_operations {
int (*open) (struct inode *, struct file *);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*release) (struct inode *, struct file *);
}
返回值:成功取决于第一个参数
major > 0 :成功返回0
major = 0 :成功返回的是主设备号
失败返回错误码
6.unregister_chrdev
#include <linux/fs.h>
void unregister_chrdev(unsigned int major, const char *name)
功能:销毁字符设备驱动
参数:
@major:主设备号
@name:驱动的名字
返回值:无
7.copy_to_user
#include <linux/uaccess.h>
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)
功能:从内核空间向用户空间拷贝数据(驱动的read函数中)
参数:
@to:用户空间的地址
@from:内核空间的地址
@n:拷贝的字节的个数
返回值:成功返回0,失败返回未拷贝的字节的个数
8.copy_from_user
#include <linux/uaccess.h>
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
功能:从用户空间向内核空间拷贝数据(驱动的write函数中)
参数:
@to:内核空间的地址
@from:用户空间的地址
@n:拷贝的字节的个数
返回值:成功返回0,失败返回未拷贝的字节的个数
9.ioremap
#include <linux/io.h>
void *ioremap(unsigned long phy_addr, unsigned long size)
功能:地址映射
参数:
@phy_addr:虚拟地址
@size:映射的大小,单位是字节
返回值:成功返回虚拟地址,失败返回NULL
10.iounmap
void iounmap(volatile void *addr)
功能:取消地址映射
参数:
@addr:虚拟地址
返回值:无
11.writel
writel(v,r)
功能:向寄存器中写(32bit) 数据
参数:
@v:写的值
@r:寄存器地址
12.readl
readl(r)
功能:读取寄存器中的数据
参数:
@r:寄存器地址
13.class_create
struct class * class_create(owner, name)
功能:向上层提交目录名
参数:
@owner:THIS_MODULE (编译器相关的宏)
@name:目录名 在/sys/class下
返回值:成功返回cls的结构体指针,失败返回错误码指针
eg:
struct class *cls = class_create(THIS_MODULE,"hello");
if(IS_ERR(cls)){ //进入if语句就是错误
printk("class create error\n");
return PTR_ERR(cls); //将错误码指针转换为错误码
}
14.device_create
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
功能:向上层提交设备信息
参数:
@class:cls的指针
@parent:一般NULL
@devt:设备号
@drvdata:写为NULL
@fmt,...:可变参数,节点名
返回值:成功返回dev的指针,失败返回错误码指针
15.class_destroy
void class_destroy(struct class *cls)
功能:销毁目录
参数:
@cls:目录的指针
返回值:无
16.device_destroy
void device_destroy(struct class *class, dev_t devt)
功能:销毁设备信息
参数:
@class:目录的指针
@devt:设备号
返回值:无
17.ioctl
#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...); --->应用层的ioctl函数
功能:设置的控制
参数:
@fd:文件描述符
@request:设备的请求码(命令码)
//方向 第三个参数的大小
@...:可变参数,可传递,也可以不传递
返回值:成功返回0,失败返回-1置位错误码
----------------------------------------------------------------
fops://应用层的ioctl的第二个和第三个参数是和驱动的后两个参数对应的。
#include <linux/ioctl.h>
long (*unlocked_ioctl) (struct file *file, unsigned int cmd, unsigned long args);
{
eg:
switch(cmd){
case 命令码:
//用户的操作
break;
}
return 0;
}
18.封装命令码的宏
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(sizeof(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(sizeof(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(sizeof(size)))
内部实现
type-->类型 nr--->功能 size--->类型
#define _IOC(dir,type,nr,size)
(((dir) << 30) |((size) << 16))|((type) << 8)|(nr) << 0)))
eg:
#define LED1_ON _IO('p',0)
#define LED1_OFF _IO('p',1)
#define LED_ON _IOW('p',0,int)
#define LED_OFF _IOW('p',1,int)
19.字符设备驱动分布实现的API
#include <linux/cdev.h>
#include <linux/slab.h> --->kfree头文件
1.分配对象
struct cdev {
struct module *owner; //THIS_MODULE
const struct file_operations *ops; //操作方法结构体
struct list_head list; //构成链表
dev_t dev; //设备号
unsigned int count; //设备的个数
} ;
struct cdev cdev;
struct cdev *cdev;
struct cdev *cdev_alloc(void)
//void kfree(const void *addr)
功能:申请内存 //申请内存的大小(sizeof(struct cdev))
参数:
@无
返回值:成功返回结构体指针,失败返回NULL
2.对象的初始化
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
功能:初始化cdev结构体的成员
参数:
@cdev:cdev的结构体指针
@fops:操作方法结构体指针
返回值:无
3.申请设备号
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//void unregister_chrdev_region(dev_t from, unsigned count)
功能:静态指定设备号
参数:
@from:设备号的起始值 //major一样 填minor的起始值即可
@count:设备的个数
@name:设备的名字 //可以通过cat/proc/devices查看
返回值:成功返回0,失败返回错误码
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
//void unregister_chrdev_region(dev_t from, unsigned count)
功能:动态申请设备号
参数:
@dev:申请到的设备号
@baseminor:起始的次设备号
@count:设备的个数
@name:设备的名字 //可以通过cat/proc/devices查看
返回值:成功返回0,失败返回错误码
4.注册
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
//void cdev_del(struct cdev *p)
功能:注册
参数:
@p:cdev结构体指针
@dev:设备号
@count:设备的个数
返回值:成功返回0,失败返回错误码
20.中断屏蔽的API
local_irq_disable(); //关闭中断
//临界资源
local_irq_enable(); //开启中断
21.自旋锁的API
1.定义自旋锁
spinlock_t lock;
2.初始化自旋锁
void spin_lock_init(spinlock_t *lock)
3.上锁
void spin_lock(spinlock_t *lock) //上锁的时候中断可以打断锁
void spin_lock_irq(spinlock_t *lock) //上锁的时候中断打断不了锁
4.解锁
void spin_unlock(spinlock_t *lock)
void spin_unlock_irq(spinlock_t *lock)
22.信号量的API
1.定义信号量
struct semaphore sem;
2.初始化信号量
void sema_init(struct semaphore *sem, int val)
//初始化信号量,当val初始值设置为1的时候才有互斥的效果。当val设置为0的时候是同步的效果。
3.上锁
void down(struct semaphore *sem); //上锁
int down_trylock(struct semaphore *sem) //尝试获取锁,成功返回0,失败返回1
4.解锁
void up(struct semaphore *sem); //解锁
23.互斥体的API
1.定义互斥体
struct mutex mutex;
2.初始化互斥体
mutex_init(&mutex);
3.上锁
void mutex_lock(struct mutex *lock);
int mutex_trylock(struct mutex *lock) //尝试获取锁,成功返回1,失败返回0
4.解锁
void mutex_unlock(struct mutex *lock)
24.原子操作的API
1.定义原子变量并初始化
atomic_t atm = ATOMIC_INIT(1);
atomic_t atm = ATOMIC_INIT(-1);
2.上锁
int atomic_dec_and_test(atomic_t *v)
//让原子变量的值减去1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
int atomic_inc_and_test(atomic_t *v);
//让原子变量的值加1,然后和0比较,如果结果为0代表获取锁成功了,这个函数返回真,否者返回假
3.解锁
atomic_inc(atomic_t *v) //加1 必须保证原子变量为初始化时的值
atomic_dec(atomic_t *v) //减1 必须保证原子变量为初始化时的值
25.阻塞IO的API
1.定义等待队列头
wait_queue_head_t wq;
2.初始化等待队列头
init_waitqueue_head(&wq);
3.调用对应的函数完成阻塞(定义等待队列项,将等待队列项放到队列的队尾,进程休眠)
wait_event(wq, condition)
//让进程进入到不可中断的等待态
wait_event_interruptible(wq, condition)
//让进程进入到可中断的等待态
//返回值:如果是数据准备好了它返回0,如果是信号唤醒的进程的休眠返回-ERESTARTSYS
注:condition代表的是数据是否准备好的标志,如果condition为真代表数据准备好了
进程不需要休眠,如果condition为假代表数据没有准备好进程需要进入休眠的状态。
4.唤醒休眠的进程
condition=1;
wake_up(&wq);
wake_up_interruptible(&wq);
注:上述的两个函数唤醒的是等待队列,队列中的进程是否真的被唤醒取决于
condition的真假,如果condition为真,进程就准备被唤醒了,如果condition
为假进程没有被唤醒,进程继续休眠。
26.epoll
#include <sys/epoll.h>
int epoll_create(int size);
功能:创建epoll
参数:
@size:参数已经被忽略了,只需要填写大于0的值即可
返回值:成功返回epfd,失败返回-1置位错误码
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能:关于epoll的控制操作
参数:
@epfd:epoll的文件描述符
@op:控制方式
EPOLL_CTL_ADD:添加
EPOLL_CTL_MOD:修改
EPOLL_CTL_DEL:删除
@fd:被操作的文件描述符
@event:(事件)结构体指针
typedef union epoll_data {
void *ptr;
int fd; <====一般填写这个成员
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; //EPOLLIN 读 EPOLLOUT 写
epoll_data_t data; //存放用户的数据
};
返回值:成功返回0,失败返回-1置位错误码
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
功能:阻塞等待文件描述符就绪
参数:
@epfd:epoll的文件描述符
@events:准备好的事件的结构体地址
@maxevents:返回的最大的文件描述符的个数
@timeout:超时
>0 :毫秒级别的超时时间
=0 :立即返回
=-1:不关心超时时间
返回值:
成功返回准备好的文件描述符的个数
返回0代表超时时间到了
失败返回-1置位错误码
27.设备树相关的结构体
struct device_node { //在设备树中每个节点被解析后都会产生一个device_node的成员
const char *name; //节点名 "mynode"
const char *full_name; //节点全名 "mynode@0x12345678"
struct property *properties;
//属性的指针,节点内核每个键值对都对应一个property结构体,所有的键值对构成单链表
struct device_node *parent; //节点的父节点
struct device_node *child; //节点的子节点
struct device_node *sibling; //节点的兄弟节点
};
struct property {
char *name; //键的名字
int length; //值的长度(字节)
void *value; //值
struct property *next; //指向下一个键值对
};
28.获取device_node的函数
struct device_node *of_find_node_by_path(const char *path)
功能:通过节点的路径获取节点
参数:
@path:节点的路径 "/mynode@0x12345678"
返回值:成功返回节点的指针,失败返回NULL
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)
功能:通过节点的名字获取节点
参数:
@from:NULL 从根节点开始解析
@name:节点名
返回值:成功返回节点的指针,失败返回NULL
29.设备树中获取属性
struct property *of_find_property(const struct device_node *np,const char *name,int*lenp)
功能:获取节点的属性
参数:
@np:节点的指针
@name:属性名
@lenp:值的长度
返回值:成功返回property结构体指针,失败返回NULL
int of_property_read_string(const struct device_node *np, const char *propname,
const char **out_string)
功能:解析字符串
参数:
@np:节点的指针
@propname:键
@out_string:获取到的值
返回值:成功返回0,失败返回错误码
int of_property_read_u8_array(const struct device_node *np,
const char *propname,u8 *out_values, size_t sz)
功能:解析u8数组
参数:
@np:节点的指针
@propname:键
@out_values:获取到的值
@sz:获取的个数
返回值:成功返回0,失败返回错误码
int of_property_read_u32_array(const struct device_node *np,
const char *propname,u32 *out_values, size_t sz)
功能:解析u32数组
参数:
@np:节点的指针
@propname:键
@out_values:获取到的值
@sz:获取的个数
返回值:成功返回0,失败返回错误码
30.gpio子系统
struct device_node *of_find_compatible_node(struct device_node *from,
const char *type, const char *compatible)
功能:通过compatible获取节点
参数:
@from:NULL
@type:NULL
@compatible:"厂商名,设备名"
返回值:成功返回节点的指针,失败返回NULL
int of_get_named_gpio(struct device_node *np,const char *propname, int index)
功能:获取gpio号
参数:
@np:节点的指针
@propname:键
@index:值的下标,从0开始
返回值:成功返回gpio号,失败返回错误码
int gpio_request(unsigned gpio, const char *label)
功能:申请要使用的gpio
参数:
@gpio:gpio号
@label:名字,不想使用可以写为NULL
返回值:成功返回0,失败返回错误码
int gpio_direction_output(unsigned gpio, int value)
功能:设置gpio为输出
参数:
@gpio:gpio号
@value: 1高电平 0低电平
返回值:成功返回0,失败返回错误码
int gpio_direction_input(unsigned gpio)
功能:设置gpio为输入
参数:
@gpio:gpio号
返回值:成功返回0,失败返回错误码
int gpio_get_value(unsigned gpio)
功能:读取管脚的电平
参数:
@gpio:gpio号
返回值:返回1高电平 0低电平
void gpio_set_value(unsigned gpio, int value)
功能:设置管脚的电平值
参数:
@gpio:gpio号
@value: 1高电平 0低电平
返回值:无
void gpio_free(unsigned gpio)
功能:释放gpio号
参数:
@gpio:gpio号
返回值:无
31.gpiod子系统
struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,const char *propname, int index,enum gpiod_flags dflags, const char *label)
功能:解析得到desc,申请要使用的desc,输出状态
参数:
@node:节点的指针
@propnanme:键名
@index:值的下标,从0开始
@dflags:输出状态 // 枚举常量 GPIOD_OUT_HIGH GPIOD_OUT_LOW GPIOD_IN等
@label:名字,一般填NULL
返回值:成功返回desc的结构体指针,失败返回错误码 对于返回值结构体指针
都可以使用ISS_ERR和PTR_ERR
int gpiod_direction_input(struct gpio_desc *desc)
功能:设置gpio为输入
参数:
@desc:结构体指针
返回值:成功返回0,失败返回错误码
int gpiod_direction_output(struct gpio_desc *desc, int value)
功能:设置gpio为位输出
参数:
@desc:结构体指针
@value:1高电平,0低电平
返回值:成功返回0,失败返回错误码
int gpiod_get_value(const struct gpio_desc *desc)
功能:读取管脚的电平值
参数:
@desc:结构体指针
返回值:成功返回0,失败返回错误码
void gpiod_set_value(struct gpio_desc *desc, int value)
功能:设置gpio管脚的电平值
参数:
@desc:结构体指针
@value:1高电平,0低电平
返回值:无
void gpiod_put(struct gpio_desc *desc)
功能:释放gpio号
参数:
@desc:结构体指针
返回值:无
32.内核定时器
#include <linux/timer.h>
1.分配对象
struct timer_list {
struct hlist_node entry; //构成内核链表使用的成员
unsigned long expires;//定时的时间 jiffies+100 jiffies+250
void (*function)(struct timer_list *); //定时器的处理函数,定时时间到了执行的函数
u32 flags; //一般填写为0
};
eg:
struct timer_list mytimer;
2.对象的初始化
void timer_handle(struct timer_list *timer)
{
//定时器的处理函数
}
mytimer.expires = jiffies+100;
timer_setup(&mytimer, 定时器处理函数, 0); //这个函数中并没有初始化expires变量,要手动初始化
3.启动定时器
void add_timer(struct timer_list *timer);
//同一个定时器只能add_timer一次,如果在次调用add_timer内核就崩溃了。
//在调用add_timer的定时器就启动了。当定时器时间到了之后执行定时器处理函数
//定时器处理函数执行结束他就结束了。
int mod_timer(struct timer_list *timer, unsigned long expires);
//再次启动定时器,expires是下一次定时器的时间
4.删除定时器
int del_timer(struct timer_list * timer);
//删除定时器
34.中断处理
unsigned int irq_of_parse_and_map(struct device_node *np, int index)
功能:解析并映射得到软中断号
参数
@np:节点的指针
@index:中断的索引号
返回值:成功返回软中断号,失败返回0
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
功能:注册中断
参数:
@irq:软中断号
@handler:指向中断处理函数的指针
irqreturn_t irq_handle(int irq, void *dev) //中断处理函数
{
//只能做简短的不耗时的操作
return IRQ_NONE; //中断没有被处理
//或者
return IRQ_HANDLED; //中断被处理完成了
}
@flags:中断触发方式
IRQF_TRIGGER_RISING //上升沿
IRQF_TRIGGER_FALLING //下降沿
IRQF_TRIGGER_HIGH //高电平
IRQF_TRIGGER_LOW //低电平
IRQF_SHARED //共享中断
@ name:中断的名字
cat/proc/interrupts可以查看
@ dev :向中断处理函数传递的参数
返回值:成功返回0,失败返回错误码
void *free_irq(unsigned int irq, void *dev);
功能:释放中断
参数:
@irq:软中断号
@dev:request_irq的第5个参数
返回值:返回的是设备名
35.中断底半步之tasklet
1.分配对象
struct tasklet_struct
{
struct tasklet_struct *next; //构成链表的成员
unsigned long state; //状态变量
atomic_t count; //触发底半部的次数
bool use_callback; //如果设置为true表示使用callback函数,否则使用func函数
union {
void (*func)(unsigned long data); //旧版本的底半部处理函数
void (*callback)(struct tasklet_struct *t); //新版本中加入的底半部处理函数
};
unsigned long data; //向底半部处理函数传递的参数
};
struct tasklet_struct tasklet;
2.对象的初始化
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
//旧版本的初始化函数
void tasklet_setup(struct tasklet_struct *t,
void (*callback)(struct tasklet_struct *))
//新版本的初始化函数
3.调用执行
void tasklet_schedule(struct tasklet_struct *t)
36.中断底半部之工作队列
1.分配对象
struct work_struct {
atomic_long_t data; //保存数据的变量
struct list_head entry; //构成队列
work_func_t func; //底半部处理函数
//typedef void (*work_func_t)(struct work_struct *work);
};
struct work_struct work;
2.初始化对象
INIT_WORK(&work, 底半部处理函数);
3.调用执行
bool schedule_work(struct work_struct *work)
37.platform
设备信息端:
1.分配并初始化对象
struct platform_device {
const char *name; //用于匹配的名字
int id; //总线号 PLATFORM_DEVID_AUTO
struct device dev; //父类
u32 num_resources; //设备信息的个数
struct resource *resource; //设备信息结构体
}
struct device{ //父类
void (*release)(struct device *dev); //用来释放资源的函数
}
struct resource { //设备信息结构体
resource_size_t start; //资源的起始值 0x50006000 0x56000000 71
resource_size_t end; //资源的结束值 0x50006000 + 3 0x56000000+49 71
unsigned long flags; //资源的类型 IORESOURCE_IO IORESOURCE_MEM IORESOURCE_IRQ
};
2.注册、注销
int platform_device_register(struct platform_device *pdev) //注册
void platform_device_unregister(struct platform_device *pdev) //注销
设备驱动端:
1.分配并初始化对象
struct platform_driver {
int (*probe)(struct platform_device *);
//匹配成功执行的函数
int (*remove)(struct platform_device *);
//分离的时候执行的函数
struct device_driver driver;
//父类
const struct platform_device_id *id_table;
//2.idtable匹配
};
struct device_driver {
const char *name; //1.用于匹配的名字
const struct of_device_id *of_match_table;
//3.设备树匹配方式
}
2.注册、注销
platform_driver_register(drv) //注册
void platform_driver_unregister(struct platform_driver *); //注销
module_platform_driver(pdrv); //一键注册注销的宏
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int index)
功能:获取resource资源的函数
参数:
@dev:platform_device结构体指针
@type:资源的类型 IORESOURCE_IO IORESOURCE_MEM IORESOURCE_IRQ
@index:同类型资源的下标
返回值:成功返回resouce的地址,失败返回NULL
int platform_get_irq(struct platform_device *dev, unsigned int index)
功能:获取中断类型的资源
参数:
@dev:platform_device结构体指针
@index:中断类型资源的下标
返回值:成功返回中断号,失败返回错误码
38.i2c总线驱动
1.分配并初始化对象
struct i2c_driver {
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id);
//匹配成功执行的函数
int (*remove)(struct i2c_client *client);
//分离的时候执行的函数
struct device_driver driver;
//父类
const struct i2c_device_id *id_table;
//idtable的匹配方式
}
struct device_driver {
const char *name; //name不用于匹配,但必须填写
const struct of_device_id *of_match_table; //设备树匹配方式
}
2.对象的注册、注销
i2c_add_driver(driver) //注册
void i2c_del_driver(struct i2c_driver *driver); //注销
module_i2c_driver(i2c对象); //一键注册注销的宏
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
功能:发送消息
参数:
@adap:总线驱动的指针 client->adapter
@msgs:消息结构体的首地址
@num:消息的个数
返回值:如果返回值等于num表示成功,否则就是失败
39.spi总线驱动
1.分配并初始化对象
struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
struct device_driver driver;
};
struct device_driver {
const char *name;
const struct of_device_id *of_match_table;
}
2.注册
#define spi_register_driver(driver) \
__spi_register_driver(THIS_MODULE, driver)
3.注销
void spi_unregister_driver(struct spi_driver *sdrv)
4.一键注册的宏
module_spi_driver(结构体变量名);
int spi_write(struct spi_device *spi, const void *buf, size_t len) //发数据
int spi_read(struct spi_device *spi, void *buf, size_t len) //接收数据
int spi_write_then_read(struct spi_device *spi, //同时收发
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx);
40.块设备驱动
1.gendisk的结构体对象
struct gendisk {
int major; //块设备的主设备号
int first_minor; //起始的次设备号
int minors; //设备的个数,分区的个数
char disk_name[DISK_NAME_LEN]; //磁盘的名字
struct disk_part_tbl *part_tbl;
//磁盘的分区表的首地址
struct hd_struct part0;
//part0分区的描述
const struct block_device_operations *fops;
//块设备的操作方法结构体
struct request_queue *queue;
//队列(重要)
void *private_data;
//私有数据
};
分区的结构体
struct hd_struct {
sector_t start_sect; //起始的扇区号
sector_t nr_sects; //扇区的个数
int partno; //分区号
};
//块设备的操作方法结构体
struct block_device_operations {
int (*open) (struct block_device *, fmode_t);
int (*release) (struct gendisk *, fmode_t);
int (*ioctl) (struct block_device *, fmode_t, unsigned, unsigned long);
int (*getgeo)(struct block_device *, struct hd_geometry *);
//设置磁盘的磁头,磁道,扇区的个数的。hd_geometry
}
2.结构体对象的初始化
struct gendisk *mydisk;
struct gendisk *alloc_disk(int minors)
//void put_disk(struct gendisk *disk)
//归还引用计数
功能:分配gendisk的内存,然后完成必要的初始化
参数:
@minors:分区的个数
返回值:成功返回分配到的内存的首地址,失败返回NULL
int register_blkdev(unsigned int major, const char *name)
//void unregister_blkdev(unsigned int major, const char *name)
功能:申请设备设备驱动的主设备号
参数:
@major : 0:自动申请
>0 :静态指定
@name :名字 cat /proc/devices
返回值:
major=0 ;成功返回主设备号,失败返回错误码
major>0 :成功返回0 ,失败返回错误码
void set_capacity(struct gendisk *disk, sector_t size)
功能:设置磁盘的容量
struct request_queue *blk_mq_init_sq_queue(struct blk_mq_tag_set *set,const struct blk_mq_ops
*ops,unsigned int queue_depth,unsigned int set_flags)
//void blk_cleanup_queue(struct request_queue *q)
功能:用于在给定队列深度的情况下使用mq ops设置队列的助手,以及通过mq ops标志传递的助手
参数:
@被初始化的tag对象,tag被上层使用,里面包含硬件队列的个数,队列的操作方法结构体,标志位等
@放入到tag中的操作方法结构体
@ tag中指定支持的队列深度
@将tag中队列的处理标志位,例如BLK_MQ_F_SHOULD_MERGE, BLK_MQ_F_BLOCKING等
返回值:成功返回队列指针,失败返回错误码指针
3.注册、注销
void add_disk(struct gendisk *disk)
//注册
void del_gendisk(struct gendisk *disk)
//注销
41.块设备队列处理
{
/*双向链表数据结构,将所有加入到队列的IO请求组建成一个双向链表*/
struct list_head queue_head;
struct list_head requeue_list; //request队列
spinlock_t requeue_lock; //队列自旋锁
unsigned long nr_requests; /* 最大的请求数量 */
unsigned long queue_flags;/*当前请求队列的状QUEUE_FLAG_STOPPED*/
…
};
struct request
{
struct list_head queuelist;/* 请求对象中的链表元素*/
struct request_queue *q; /* 指向存放当前请求的请求队列*/
unsigned int __data_len; /* 当前请求要求数据传输的总的数据量 */
sector_t __sector; /* 当前请求要求数据传输的块设备的起始扇区 */
struct bio *bio; /* bio对象所携带的信息转存至请求对象中*/
struct bio *biotail; /* bio链表*/
…
};
通常一个request请求可以包含多个bio,一个bio对应一个I/O请求
struct bio {
struct bio *bi_next; /* 指向当前bio的下一个对象*/
unsigned long bi_flags; /* 状态、命令等 */
unsigned long bi_rw; /* 表示READ/WRITE*/
struct block_device *bi_bdev; /* 与请求相关联的块设备对象指针*/
unsigned short bi_vcnt; /* bi_io_vec数组中元素个数 */
unsigned short bi_idx; /* 当前处理的bi_io_vec数组元素索引 */
unsigned int bi_size; /* 本次传输需要传输的数据总量,byte(扇区大小整数倍) */
struct bio_vec *bi_io_vec;/* 指向一个IO向量的数组,数组中的内各元素对应一个物理页的page对象 */
};
struct bio_vec {
struct page *bv_page; //指向用于数据传输的页面所对应的struct page对象
unsigned int bv_len; //表示当前要传输的数据大小
unsigned int bv_offset;//表示数据在页面内的偏移量
};
blk_mq_start_request(rq); //开始处理队列
blk_mq_end_request(rq, BLK_STS_OK); //结束队列处理
rq_for_each_segment(bvec, rq, iter) //从request->bio_vec
void* b_buf = page_address(bvec.bv_page) + bvec.bv_offset; //将页地址转换为线性地址(内地址)
rq_data_dir(rq)) //从request获取本次读写的方向 WRITE 1 READ 0
dev_addr+(rq->__sector *512) //磁盘设备的地址