第03章 字符驱动
时间:2010-12-21 来源:purple_river
源码:
insmod my_dev.ko 之后发生了什么故事?
(1) /proc/devices 中有下面这一行:
251 my_test_char_dev
这里显示的设备名称---my_test_char_dev 是下面这条语句决定的:
result = alloc_chrdev_region(&dev, scull_minor, 1, "my_test_char_dev");
参考 install.sh 是如何用 awk 命令取得动态分配的设备号的~~
(2) /proc/modules 和 lsmod 是相同的.
这里也有个名称,但是就是 ko 文件的名称,由 Makefile 中的配置决定.
这里我的设备名定义为: my_dev
(3) /sys/module/my_dev/
这目录下存放了加载该模块的信息,包括加载地址,版本等,有如下文件:
# ls /sys/module/my_dev -l
drwxr-xr-x 2 root root 0 2010-12-21 10:24 holders
drwxr-xr-x 2 root root 0 2010-12-21 10:24 notes
drwxr-xr-x 2 root root 0 2010-12-21 10:24 parameters
drwxr-xr-x 2 root root 0 2010-12-21 10:24 sections
-r--r--r-- 1 root root 4096 2010-12-21 10:24 initstate
-r--r--r-- 1 root root 4096 2010-12-21 10:24 refcnt
-r--r--r-- 1 root root 4096 2010-12-21 10:24 srcversion
(4) 字符设备驱动的几个要素: 参考 main.c 中修改的部分.
1) 定义自己的扩展数据结构:
struct t_my_dev {
struct cdev cdev; //这个必须的.
unsigned long size;
struct semaphore sem;
wait_queue_head_t inq;
};
struct t_my_dev my_dev;
一般会扩展 信号量,queue等:这两个东西需要初始化.
2) 初始化
//1)取得设备号:所谓设备号就是个整数,其中包含了
result = alloc_chrdev_region(&dev_no, 0, 1, "my_test_char_dev");
//2)信号量一般是必须的
//这是初始化信号量,不是创建(是静态声明的),因此没有对应的 free 函数~~
init_MUTEX(&my_dev.sem);
init_waitqueue_head(&my_dev.inq);
//3)初始化设备结构体:一般会自己定义结构体.
cdev_init(&my_dev.cdev, &my_dev_fops);
//4)增加设备到内核
result = cdev_add (&my_dev.cdev, dev_no, 1);
//5)如果失败,必须做好清理工作.
fail:
unregister_chrdev_region(dev_no, 1);
3) 结束化
//1)删除设备
cdev_del(&my_dev.cdev);
//2)删除设备号
unregister_chrdev_region(dev_no, 1);
4) 函数指针:就是结构体 my_dev_fops 中提供的.
//1) open的模板:
struct t_my_dev *dev;
dev = container_of(inode->i_cdev, struct t_my_dev, cdev);
filp->private_data = dev; //这是技巧所在,在其他函数中,取得回来使用.
//2) read/write中: PV操作 --- 同步
P: down_interruptible(&dev->sem)
V: up(&dev->sem);
//3) read中等待queue,write中唤醒queue:
Read: wait_event_interruptible( dev->inq, (dev->size != 0) )
Write: wake_up_interruptible(&dev->inq); //唤醒读等待.
5) 返回值解释
read 的返回值:
= count参数: 请求的字节数已经被传送. 这是最好的情况.
< count参数: 有部分数据被传送. 这可能由于几个原因, 依赖于设备.
常常, 应用程序重新试着读取. 例如, 如果你使用 fread 函数来读取, 库函数重新发出系统调用直到请求的数据传送完成.
=0 :到达了文件末尾(没有读取数据). <0: 一个负值表示有一个错误. 这个值指出了什么错误, 根据 <linux/errno.h>.
出错的典型返回值包括 -EINTR( 被打断的系统调用) 或者 -EFAULT( 坏地址 ).
write返回值: 类似 read
6) 老方法: int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
慢慢会淡出视野.
|
(1) /proc/devices 中有下面这一行:
251 my_test_char_dev
这里显示的设备名称---my_test_char_dev 是下面这条语句决定的:
result = alloc_chrdev_region(&dev, scull_minor, 1, "my_test_char_dev");
参考 install.sh 是如何用 awk 命令取得动态分配的设备号的~~
(2) /proc/modules 和 lsmod 是相同的.
这里也有个名称,但是就是 ko 文件的名称,由 Makefile 中的配置决定.
这里我的设备名定义为: my_dev
(3) /sys/module/my_dev/
这目录下存放了加载该模块的信息,包括加载地址,版本等,有如下文件:
# ls /sys/module/my_dev -l
drwxr-xr-x 2 root root 0 2010-12-21 10:24 holders
drwxr-xr-x 2 root root 0 2010-12-21 10:24 notes
drwxr-xr-x 2 root root 0 2010-12-21 10:24 parameters
drwxr-xr-x 2 root root 0 2010-12-21 10:24 sections
-r--r--r-- 1 root root 4096 2010-12-21 10:24 initstate
-r--r--r-- 1 root root 4096 2010-12-21 10:24 refcnt
-r--r--r-- 1 root root 4096 2010-12-21 10:24 srcversion
(4) 字符设备驱动的几个要素: 参考 main.c 中修改的部分.
1) 定义自己的扩展数据结构:
struct t_my_dev {
struct cdev cdev; //这个必须的.
unsigned long size;
struct semaphore sem;
wait_queue_head_t inq;
};
struct t_my_dev my_dev;
一般会扩展 信号量,queue等:这两个东西需要初始化.
2) 初始化
//1)取得设备号:所谓设备号就是个整数,其中包含了
result = alloc_chrdev_region(&dev_no, 0, 1, "my_test_char_dev");
//2)信号量一般是必须的
//这是初始化信号量,不是创建(是静态声明的),因此没有对应的 free 函数~~
init_MUTEX(&my_dev.sem);
init_waitqueue_head(&my_dev.inq);
//3)初始化设备结构体:一般会自己定义结构体.
cdev_init(&my_dev.cdev, &my_dev_fops);
//4)增加设备到内核
result = cdev_add (&my_dev.cdev, dev_no, 1);
//5)如果失败,必须做好清理工作.
fail:
unregister_chrdev_region(dev_no, 1);
3) 结束化
//1)删除设备
cdev_del(&my_dev.cdev);
//2)删除设备号
unregister_chrdev_region(dev_no, 1);
4) 函数指针:就是结构体 my_dev_fops 中提供的.
//1) open的模板:
struct t_my_dev *dev;
dev = container_of(inode->i_cdev, struct t_my_dev, cdev);
filp->private_data = dev; //这是技巧所在,在其他函数中,取得回来使用.
//2) read/write中: PV操作 --- 同步
P: down_interruptible(&dev->sem)
V: up(&dev->sem);
//3) read中等待queue,write中唤醒queue:
Read: wait_event_interruptible( dev->inq, (dev->size != 0) )
Write: wake_up_interruptible(&dev->inq); //唤醒读等待.
5) 返回值解释
read 的返回值:
= count参数: 请求的字节数已经被传送. 这是最好的情况.
< count参数: 有部分数据被传送. 这可能由于几个原因, 依赖于设备.
常常, 应用程序重新试着读取. 例如, 如果你使用 fread 函数来读取, 库函数重新发出系统调用直到请求的数据传送完成.
=0 :到达了文件末尾(没有读取数据). <0: 一个负值表示有一个错误. 这个值指出了什么错误, 根据 <linux/errno.h>.
出错的典型返回值包括 -EINTR( 被打断的系统调用) 或者 -EFAULT( 坏地址 ).
write返回值: 类似 read
6) 老方法: int register_chrdev(unsigned int major, const char *name, struct file_operations *fops);
慢慢会淡出视野.
相关阅读 更多 +