该文转载自:http://rangercyh.blog.51cto.com/1444712/521244

系统调用是操作系统内核和应用程序之间的接口,而设备驱动程序是操作系统内核和机器硬件之间的接口。

设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件, 应用程序可以像操作普通文件一样对硬件设备进行操作。

设备驱动程序是内核的一部分,它完成以下的功能:

(1) 对设备初始化和释放.

(2) 把数据从内核传送到硬件和从硬件读取数据.

(3) 读取应用程序传送给设备文件的数据和回送应用程序请求的数据.

(4) 检测和处理设备出现的错误.

Linux支持三中不同类型的设备:字符设备(character devices)、块设备(block devices)和网络设备(network interfaces)。

字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了。

块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待.

用户进程是通过设备文件来与实际的硬件打交道,每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。

另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,

比如有两个软盘,就可以用从设备号来区分他们.设备文件的的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序.。

设备驱动程序工作的基本原理:

用户进程利用系统调用对设备进行诸如read/write操作,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。

最后,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。

如果你的驱动程序陷入死循环,你只有重新启动机器了。

下面我们就来添加一个字符设备:

  • 编写设备驱动源代码

在设备驱动程序中有一个非常重要的结构file_operations,该结构的每个域都对应着一个系统调用。

用户进程利用系统调用对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。

  1. struct file_operations {
  2. int (*seek) (struct inode * ,struct file *, off_t ,int);
  3. int (*read) (struct inode * ,struct file *, char ,int);
  4. int (*write) (struct inode * ,struct file *, off_t ,int);
  5. int (*readdir) (struct inode * ,struct file *, struct dirent * ,int);
  6. int (*select) (struct inode * ,struct file *, int ,select_table *);
  7. int (*ioctl) (struct inode * ,struct file *, unsined int ,unsigned long);
  8. int (*mmap) (struct inode * ,struct file *, struct vm_area_struct *);
  9. int (*open) (struct inode * ,struct file *);
  10. int (*release) (struct inode * ,struct file *);
  11. int (*fsync) (struct inode * ,struct file *);
  12. int (*fasync) (struct inode * ,struct file *,int);
  13. int (*check_media_change) (struct inode * ,struct file *);
  14. int (*revalidate) (dev_t dev);
  15. }

编写设备驱动程序的主要工作是编写子函数,并填充file_operations的各个域。

例如:

  1. Struct file_operations my_fops={
  2. .read=my_read,
  3. .write=my_write,
  4. .open=my_open,
  5. .release=my_release
  6. }

然后再定义函数my_read,my_write,my_open,my_release相应的函数体。

例如:

  1. static ssize_t my_open(struct inode *inode,struct file *file){
  2. static int counter=0;
  3. if(Device_Open)
  4. return -EBUSY;
  5. Device_Open++;
  6. /*写入设备的信息*/
  7. sprintf(msg,"the device has been called %d times\n",counter++);
  8. msg_ptr=msg;
  9. return 0;
  10. }

同时对于可卸载的内核模块(LKM),至少还有两个基本的模块:

例如本例中的:

  1. static int __init my_init(void){
  2. int result;
  3. result=register_chrdev(0,"sky_driver",&my_fops);
  4. if(result<0){
  5. printk("error:can not register the device\n");
  6. return -1;
  7. }
  8. if(my_major==0){
  9. my_major=result;
  10. printk("&lt;1>hehe,the device has been registered!\n");
  11. printk("<1>the virtual device was assigned major number %d.\n",my_major);
  12. printk("<1>To talk to the driver,create a dev file with\n");
  13. printk("<1>'mknod/dev/my c %d 0'\n",my_major);
  14. printk("<1>Remove the dev and the file when done\n");
  15. }
  16. return 0;
  17. }
  18. static void __exit my_exit(void){
  19. unregister_chrdev(my_major,"sky_driver");
  20. printk("<1>unloading the device\n");
  21. }

my_init 用于注册设备,获得设备的主设备号

调用register_chrdev(0,“sky_driver(设备名)”,&my_fops);

my_exit 用于注销设备

调用unregister_chrdev(my_major, “sky_driver(设备名)”);

然后在程序尾再调用这两个函数

Module_init(my_init);

Module_exit(my_exit)

MODULE_LICENSE(“GPL”);

编写自己的驱动程序源文件mydriver.c:

  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/fs.h>
  4. #include <linux/init.h>
  5. #include <linux/uaccess.h>
  6. #if CONFIG_MODVERSIONS == 1
  7. #define MODVERSIONS
  8. #include <linux/version.h>
  9. #endif
  10. #define DEVICE_NUM 0 //随机产生一个设备号
  11. static int device_num = 0;  //用来保存创建成功后的设备号
  12. static char buffer[1024] = "mydriver";   //数据缓冲区
  13. static int open_nr = 0;     //打开设备的进程数,用于内核的互斥
  14. //函数声明
  15. static int mydriver_open(struct inode *inode, struct file *filp);
  16. static int mydriver_release(struct inode *inode, struct file* filp);
  17. static ssize_t mydriver_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos);
  18. static ssize_t mydriver_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos);
  19. //填充file_operations结构相关入口
  20. static struct file_operations mydriver_fops = {
  21. .read    = mydriver_read,
  22. .write   = mydriver_write,
  23. .open    = mydriver_open,
  24. .release = mydriver_release,
  25. };
  26. //打开函数
  27. static int mydriver_open(struct inode *inode, struct file *filp)
  28. {
  29. printk("\nMain device is %d, and the slave device is %d\n", MAJOR(inode->i_rdev), MINOR(inode->i_rdev));
  30. if (open_nr == 0) {
  31. open_nr++;
  32. try_module_get(THIS_MODULE);
  33. return 0;
  34. }
  35. else {
  36. printk(KERN_ALERT "Another process open the char device.\n");//进程挂起
  37. return -1;
  38. }
  39. }
  40. //读函数
  41. static ssize_t mydriver_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
  42. {
  43. //if (buf == NULL) return 0;
  44. if (copy_to_user(buf, buffer, sizeof(buffer))) //读缓冲
  45. {
  46. return -1;
  47. }
  48. return sizeof(buffer);
  49. }
  50. //写函数,将用户的输入字符串写入
  51. static ssize_t mydriver_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
  52. {
  53. //if (buf == NULL) return 0;
  54. if (copy_from_user(buffer, buf, sizeof(buffer))) //写缓冲
  55. {
  56. return -1;
  57. }
  58. return sizeof(buffer);
  59. }
  60. //释放设备函数
  61. static int mydriver_release(struct inode *inode, struct file* filp)
  62. {
  63. open_nr--; //进程数减1
  64. printk("The device is released!\n");
  65. module_put(THIS_MODULE);
  66. return 0;
  67. }
  68. //注册设备函数
  69. static int __init mydriver_init(void)
  70. {
  71. int result;
  72. printk(KERN_ALERT "Begin to init Char Device!"); //注册设备
  73. //向系统的字符登记表登记一个字符设备
  74. result = register_chrdev(DEVICE_NUM, "mydriver", &mydriver_fops);
  75. if (result < 0) {
  76. printk(KERN_WARNING "mydriver: register failure\n");
  77. return -1;
  78. }
  79. else {
  80. printk("mydriver: register success!\n");
  81. device_num = result;
  82. return 0;
  83. }
  84. }
  85. //注销设备函数
  86. static void __exit mydriver_exit(void)
  87. {
  88. printk(KERN_ALERT "Unloading...\n");
  89. unregister_chrdev(device_num, "mydriver"); //注销设备
  90. printk("unregister success!\n");
  91. }
  92. //模块宏定义
  93. module_init(mydriver_init);
  94. module_exit(mydriver_exit);
  95. MODULE_LICENSE("GPL");
  • 编译该设备驱动代码

然后将设备驱动源文件复制到/usr/src/linux/drivers/misc下

修改misc目录下的Makefile文件,只要在最后添加一句即可:obj-m +=mydriver.o。

/usr/src/linux/drivers/misc路径下执行命令:Make -C /usr/src/linux SUBDIRS=$PWD modules编译成功将得到mydriver.ko文件。

可以在misc目录下观察得到了mydriver.ko文件。

继续执行insmod ./mydriver.ko命令挂载内核中的模块。

然后通过lsmod命令可以看到增加的设备模块mydriver。

输入cat /var/log/messages可以看到设备注册成功。

此时进入/proc/devices文件会看到在字符设备中有250 mydriver。前面的是系统分配的主设备号,后面是设备注册名。

进入在/dev路径下,执行命令:

mknod /dev/mydriver c 250 0

第一个参数是新建设备文件的地址和名字。

第二个参数是指创建的是字符设备文件。

第三个参数是主设备号,第四个参数是从设备号,自己随便取。

执行成功会在/dev/char中看到一个新的设备文件mydriver

至此设备添加成功。

  • 编译测试程序。

编写测试代码如下:

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <fcntl.h>
  6. int main(void)
  7. {
  8. int fd;
  9. char buf[1024];
  10. char get[1024];
  11. memset(get, 0, sizeof(get));
  12. memset(buf, 0, sizeof(buf));
  13. printf("please enter a string you want input to mydriver:\n");
  14. gets(get);
  15. fd = open("/dev/mydriver", O_RDWR, S_IRUSR|S_IWUSR);//打开设备
  16. if (fd > 0) {
  17. read(fd, &buf, sizeof(buf));
  18. printf("The message in mydriver now is: %s\n", buf);
  19. //将输入写入设备
  20. write(fd, &get, sizeof(get));
  21. //读出设备的信息并打印
  22. read(fd, &buf, sizeof(buf));
  23. printf("The message changed to: %s\n", buf);
  24. sleep(1);
  25. }
  26. else {
  27. printf("OMG...");
  28. return -1;
  29. }
  30. close(fd);//释放设备
  31. return 0;
  32. }

gcc -o mydriver_test mydriver_test.c

./mydriver_test

输入任意字符串,驱动程序将字符串拷贝进新加入的设备,然后再读取出来,设备中保留字符串信息,再次输入将覆盖原来的信息

linux采用模块方法,添加一个新的设备的更多相关文章

  1. Linux 在添加一个新账号后却没有权限怎么办

    当添加一个新账号后,我们可能会发现新账号sudo 时会报告不在sudoers中,使用su -s时输入密码后也会认证失败 上网搜索大部分都要求修改/etc/sudoers中的内容,但修改这个文件必须需要 ...

  2. Flink资料(6) -- 如何添加一个新的Operator

    false false false false EN-US ZH-CN X-NONE /* Style Definitions */ table.MsoNormalTable {mso-style-n ...

  3. 012.Adding a New Field --【添加一个新字段】

    Adding a New Field 添加一个新字段 2016-10-14 3 分钟阅读时长 作者 By Rick Anderson In this section you'll use Entity ...

  4. Mysql学习(一)添加一个新的用户并用golang操作Mysql

    Mysql添加一个新的用户并赋予权限 添加一个自己的用户到mysql 首先我们需要先用root用户登录mysql,但是刚安装完没有密码,我们先跳过密码 ailumiyana@ailumiyana:~/ ...

  5. RK平台Android4.4 添加一个新的遥控器支持以及添加特殊按键【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/52904063 版权声明:本文为博主原创文章,未经博主允许不得转载. 瑞芯微平台 ...

  6. 【IntelliJ IDEA】添加一个新的tomcat,tomcat启动无法访问欢迎页面,空白页,404

    ===================================第一部分,添加一个tomcat================================================== ...

  7. JS对象 数组连接 concat() 方法用于连接两个或多个数组。此方法返回一个新数组,不改变原来的数组。 语法 arrayObject.concat(array1,array2,.arrayN)

    concat() 方法用于连接两个或多个数组.此方法返回一个新数组,不改变原来的数组. 语法 arrayObject.concat(array1,array2,...,arrayN) 参数说明: 注意 ...

  8. destoon 添加一个新的模块

    根目录rename,中config.inc.php文件/module/rename下两个文件,my.inc.php ,rename.class.php/module/rename/admin/三个文件 ...

  9. 使用layui 做后台管理界面,在Tab中的链接点击后添加一个新TAB的解决方法

    给链接或按钮  添加 onclick="self.parent.addTab('百度','http://www.baidu.com','icon-add')" 如: <a h ...

随机推荐

  1. POJ2739 - Sum of Consecutive Prime Numbers(素数问题)

    题目大意 给定N,要求你计算用连续的素数的和能够组成N的种数 题解 先筛选出素数,然后暴力判断即可... 代码: #include<iostream> #include<cstrin ...

  2. 高性能以太网芯片W5500 数据手册 V1.0(二)

    继续给大家介绍W5500 数据手册. 2.4       固定数据长度模式(FDM) 在外设主机不能控制 SCSn 时,可以使用固定数据长度模式. 此时,SCSn 必须连接到低电平(保持接地).与此同 ...

  3. cookie机制

    Cookie通过在客户端记录信息确定用户身份 一个用户的所有请求操作都应该属于同一个会话, HTTP协议是无状态的协议.一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接. ...

  4. android httpClient 支持HTTPS的2种处理方式

    摘自: http://www.kankanews.com/ICkengine/archives/9634.shtml 项目中Android https或http请求地址重定向为HTTPS的地址,相信很 ...

  5. Oracle Linux 6.3下安装Oracle 11g R2(11.2.0.3)

    本文主要描写叙述了在Oracle Linux 6.3下安装Oracle 11gR2(11.2.0.3).从Oracle 11g開始,Oracle官方站点不再提供其Patch的下载链接,须要使用Meat ...

  6. 关机相关(shutdown,reboot)

    慣用的關機指令: shutdown 由於Linux的關機是那麼重要的工作,因此除了你是在主機前面以tty7圖形介面來登入系統時, 不論用什麼身份都能夠關機之外,若你是使用遠端管理工具(如透過piett ...

  7. mysql-gdb--oracle

    https://blogs.oracle.com/ksplice/entry/8_gdb_tricks_you_should

  8. android开发工具类之获得WIFI IP地址或者手机网络IP

    有的时候我们需要获得WIFI的IP地址获得手机网络的IP地址,这是一个工具类,专门解决这个问题,这里需要两个权限: <uses-permission android:name="and ...

  9. ognl表达式和s标签

    1.ognl表达式: Ognl上下文对象:(他是一个可以存储数据的空间结构,而且在这个结构中包含之前 jsp中的作用域对象) (放在 value stack控件),当前访问的Action这个上下文对象 ...

  10. 转:oracle ebs po模块一揽子采购协议小结

    转自:http://yedward.net/?id=193 oracle ebs po模块一揽子采购协议小结 本文总结oracle ebs采购订单(po)模块一揽子采购协议的相关知识,总结如下: 1. ...