1 引言
- APP:open("/dev/xxx"),read,write —— 设备字符驱动文件包含属性、主设备号和次设备号
- C Library
- System call interface
- 内核:根据文件类型为字符设备,查找字符设备。根据主设备号查找file_operation;
- 根据调用类型,选择不同的操作函数;
2 简单驱动程序
- a. 定义file_operations结构体,然后填充该结构体,构造底层驱动函数;其中file_operations结构体定义在\include\linux\fs.h中。
- b. 使用register_chrdev(主设备号,名字,结构体file_operations)在内核中注册该字符设备驱动;
- c. 定义驱动入口函数fun1/fun2...,调用注册函数register_chrdev();
- d. 用宏module_init(fun1),在内核中注册该字符设备;
- e. 出口函数funx()调用unregister_chrdev();
- f. 用宏module_exit(funx),从内核卸载该字符设备。
注:
- APP调用接口时,调用字符设备时,主设备号查找外设类型。
- Makefile基于内核编写,make后生成xxx.ko文件。
- 使用insmod xxx.ko命令加载驱动;使用rmmod xxx卸载驱动;
- 使用mknod /dev/xxx c 主设备号 次设备号创建字符设备;也可以使用程序class_create()/class_device_create()创建设备节点。
- APP使用open("/dev/xxx",O_RWD)命令打开设备文件。
调试心得:
1. Makefile文件中M=`pwd`语句,需要使用键盘按键中的~键,而不是“键;
3 查询机制
- 软件框架
- 操作硬件寄存器:将虚拟地址用ioremap()映射为物理地址,用iounmap()解除映射。
- 使用copy_from_user从用户空间拷贝数据,使用copy_to_user拷贝数据到用户空间。
4 中断机制
- 硬件中断信号产生;
- CPU发生中断,跳到中断异常地址;
- 调用中断处理函数,保存被中断现场,处理中断信号,恢复中断现成,退出
- 中断。
注:
使用request_irq()函数注册中断;
使用exe 5</dev/xxxx命令打开中断;
使用exec 5<&-命令关闭中断;
使用free_irq()函数释放中断。
wait_event_interruptible(wq, condition)函数使程序休眠
wake_up_interruptible()唤醒程序
5 Polling机制
在file_operations中定义一个poll的成员;
在poll函数中调用poll_wait();
内核:
- sys_poll
- poll_initwait()
- init_poll_funcptr()
- __pollwait 将poll_wait的对象插入到poll队列中
- do_poll()
- do_pollfd()
- 判断是否满足条件,如果满足,直接break
- schedule_timeout() 定时休眠
Poll函数用法:
include <poll.h>
调用poll()函数
6 异步通知
使用signal()框架:
应用程序:注册信号处理函数;
调用signal(SIGIO, fun_pointer)
调用open()函数,打开设备;
调用fcntl(fd, F_SETOWN, pid)函数,告知驱动程序进程ID;
调用oflags = fcntl(fd, F_GETFL);
调用fcntl(fd, F_SETFL, oflags | FASYNC);改变fasync标记,最终调用驱动初始化或释放fasync:初始化/释放fasync_struct.
驱动程序:
file_operations函数注册fasync系统调用,在该系统调用中初始化发信号时结构体fp;
中断:
按键触发中断;
调用kill_fasync(fp, sig, band)函数发送信号到应用层;
7 同步、互斥和阻塞
原子操作框架:
atomic_t v = ATOMIC_INIT(0); 定义原子变量v并初始化为0
automic read(atomic_t *v); 返回原子变量的值
void atomic_inc(atomic_t *v); 原子变量增加1
void atomic_dec(atomic_t *v); 原子变量减少1
int atomic_dec_and_test(atomic_t *v); 自减操作后测试其是否为0,为0则返回true,否则返回false。
信号量:
定义信号量
struct semaphore sem;
初始化信号量
void sema_init(struct semaphore *sem, int val);
void init_MUTEX(struct semaphore *sem);
static DECLARE_MUTEX(button_lock); /*定义信号量并初始化*/
获得信号量
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);
释放信号量
void up(struct semaphore *sem);
阻塞
8 定时中断
1. 定义结构体timer_list
2. 在驱动入口函数中调用init_timer(),并且设置如下成员:
function:中断函数,
expires:设置中断定时时间(若不设置,默认为0,会立即进入一次中断处理函数),
data:中断处理函数输入参数。
3. 调用add_timer()函数将定时器注册到内核中;
4. 调用mod_timer(),设置定时器超时时间:系统当前时间jiffies+超时时间。