实现一个简单的linux字符设备驱动

实现一个简单的linux字符设备驱动
实现一个简单的linux字符设备驱动

实现一个简单的linux字符设备驱动

步骤1:编写驱动程序

view plaincopy to clipboardprint?

1. #include

2. #include

3. #include

4. #include

5. #include

6. #include

7. #include

8. #include

9. #define DEVICE_NAME "cdev_zhangwei"

10. int number_of_devices = 1;

11. struct cdev mydev;

12. dev_t dev = 0;

13. char data[128] = "/0"; // the data of my device

14. struct class *myclass;

15. static int mydev_open(struct inode *inode, struct file *file)

16. {

17. pr_info("mydev driver open!/n");

18. return 0;

19. }

20. static int mydev_release(struct inode *inode, struct file *file)

21. {

22. pr_info("mydev driver released!/n");

23. return 0;

24. }

25. ssize_t mydev_write(struct file *file, const char __user *buf, size_t count, loff_

t *f_pos)

26. {

27. ssize_t ret = 0;

28. pr_info("mydev_write!/n");

29. pr_info("writing %d bytes/n", count);

30. if (count > 127)

31. return -ENOMEM;

32. if (count < 0)

33. return -EINVAL;

34. if (copy_from_user(data, buf, count)) {

35. ret = -EFAULT;

36. }

37. else {

38. data[127] = ''/0'';

39. pr_info("kernel received: %s/n", data);

40. ret = count;

41. }

42. return ret;

43. }

44. static ssize_t mydev_read(struct file* filp, char* buf, size_t len,loff_t* off)

45. {

46. if( copy_to_user(buf,data,len) )

47. {

48. return -EFAULT;

49. }

50.

51. return len;

52. }

53. struct file_operations mydev_fops = {

54. .owner = THIS_MODULE,

55. .open = mydev_open,

56. .read = mydev_read,

57. .write = mydev_write,

58. .release = mydev_release

59.

60. };

61. static int __init mydev_init(void)

62. {

63. int result, error;

64. result = register_chrdev(0, DEVICE_NAME, &mydev_fops);

65. pr_info("udev_cdev: get major number: %d/n", result);

66. dev = MKDEV(result, 0);

67. myclass = class_create(THIS_MODULE, "mydev_class");

68. device_create(myclass, NULL, dev, NULL, DEVICE_NAME);

69. return 0;

70. }

71. static void __exit mydev_exit(void)

72. {

73. cdev_del(&mydev);

74. unregister_chrdev_region(dev, number_of_devices);

75. device_destroy(myclass, dev);

76. class_destroy(myclass);

77. pr_info("Goodbye cdev!/n");

78. }

79. module_init(mydev_init);

80. module_exit(mydev_exit);

81. MODULE_LICENSE("GPL");

82. MODULE_DESCRIPTION("Simple cdev udev driver test");

Makefile:

obj-m := mydrive.o

KDIR := /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

default:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules

clean:

$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) clean

rm -rf Module.markers modules.order Module.symvers

insmod mydrive.ko

lsmod查看

步骤3:创建设备节点:mknod /dev/mydriver c 主设备号次设备号

次设备号这里填0,主设备号可以利用cat /proc/devices 查看

ls –l /dev/mydriver

用df看看/dev/mydriver的使用情况了

步骤4:编写用户程序(测试咱们的驱动是否可行),如以下代码,这个嘛,简单的用gcc 命令编译就好了

1. #include

2. #include

3. #include

4. #include

5. #include

6. i nt main (void)

7. {

8. int fd,len;

9. pid_t pid;

10. char buff[] = "This is from userspace zhangwei fight it!";

11. char buff_read[100] ;

12. fd = open ("/dev/cdev_zhangwei", O_RDWR);

13. if (fd < 0) {

14. perror("open failed");

15. exit(0);

16. }

17. pid = fork();

18. if(pid>0)

19. {

20. len = write (fd, buff, sizeof(buff));

21. printf ("son Write returns %d/n",len );

22. }

23. else // parent

24. {

25. //waitpid(pid);

26. printf ("read returns %d/n", read(fd,buff_read,len) );

27. printf("buff_read = %s/n",buff_read);

28. }

29. close (fd);

30. return 0;

31. }

字符设备基础

Linux 字符设备基础 字符设备驱动程序在系统中的位置 操作系统内核需要访问两类主要设备,简单的字符设备,如打印机,键盘等;块设备,如软盘、硬盘等。与此对应,有两类设备驱动程序。分别称为字符设备驱动程序和块设备驱动程序。两者的主要差异是:与字符设备有关的系统调用几乎直接和驱动程序的内部功能结合在一起。而读写块设备则主要和快速缓冲存储区打交道。只有需要完成实际的输入/输出时,才用到块设备驱动程序。见下图: Linux 设备驱动程序的主要功能有: ● 对设备进行初始化; ● 使设备投入运行和退出服务; ● 从设备接收数据并将它们送到内核; ● 将数据从内核送到设备; ● 检测和处理设备出现的错误。 当引导系统时,内核调用每一个驱动程序的初始化函数。它的任务之一是将这一设备驱动程序使用的主设备号通知内核。同时,初始化函数还将驱动程序中的函数地址结构的指针送给内核。 内核中有两X 表。一X 表用于字符设备驱动程序,另一X 用于块设备驱动程序。这两X 表用来保存指向file_operations 结构的指针, 设备驱动程序内部的函数地址就保

存在这一结构中。内核用主设备号作为索引访问file_operations结构,因而能访问驱动程序内的子程序。 从开机到驱动程序的载入 系统启动过程中可能出现几种不同的方式检测设备硬件。首先机器硬件启动时BIOS会检测一部分必要的设备,如内存、显示器、键盘和硬盘等等。机器会把检测到的信息存放在特定的位置,如CMOS数据区。而另外某些设备会由设备驱动程序进行检测。 1 开机 2 引导部分(linux/config.h,arch/i386/boot/bootsect.S) 3 实模式下的系统初始化(arch/i386/boot/setup.S) 4 保护模式下的核心初始化 5 启动核心(init/main.c) init函数中函数调用关系如下: main.c init() filesystems.c sys_setup() genhd.c device_setup() mem.c chr_dev_init() 至此,驱动程序驻入内存。 设备驱动程序基本数据结构: struct device_struct 系统启动过程中要登记的块设备和字符设备管理表的定义在文件fs/devices.c中:struct device_struct { const char * name; struct file_operations * fops; }; static struct device_struct chrdevs[MAX_CHRDEV]; static struct device_struct blkdevs[MAX_BLKDEV]; 其实块设备表和字符设备表使用了相同的数据结构。在某些系统中,这些设备表也称作设备开关表,不同的是它们直接定义了一组函数指针进行对设备的管理。而这里系统用文件操作(file_operations)代替了那组开关。文件操作是文件系统与设备驱动程序之间的接口,系统特殊文件在建立的时候并没有把两者对应起来,只是把设备的缺省文件结构和i节点结构赋给设备文件,而真正的对应定义在系统启动之后,当设备被打开时时才进行的。 操作blkdev_open和chrdev_open定义在文件devices.c中,它们的基本功能是当设备文件初次打开时,根据该文件的i节点信息找到设备真正的文件操作接口,然后更新原来的设

字符设备驱动程序课程设计报告

中南大学 字符设备驱动程序 课程设计报告 姓名:王学彬 专业班级:信安1002班 学号:0909103108 课程:操作系统安全课程设计 指导老师:张士庚 一、课程设计目的 1.了解Linux字符设备驱动程序的结构; 2.掌握Linux字符设备驱动程序常用结构体和操作函数的使用方法; 3.初步掌握Linux字符设备驱动程序的编写方法及过程; 4.掌握Linux字符设备驱动程序的加载方法及测试方法。 二、课程设计内容 5.设计Windows XP或者Linux操作系统下的设备驱动程序; 6.掌握虚拟字符设备的设计方法和测试方法;

7.编写测试应用程序,测试对该设备的读写等操作。 三、需求分析 3.1驱动程序介绍 驱动程序负责将应用程序如读、写等操作正确无误的传递给相关的硬件,并使硬件能够做出正确反应的代码。驱动程序像一个黑盒子,它隐藏了硬件的工作细节,应用程序只需要通过一组标准化的接口实现对硬件的操作。 3.2 Linux设备驱动程序分类 Linux设备驱动程序在Linux的内核源代码中占有很大的比例,源代码的长度日益增加,主要是驱动程序的增加。虽然Linux内核的不断升级,但驱动程序的结构还是相对稳定。 Linux系统的设备分为字符设备(char device),块设备(block device)和网络设备(network device)三种。字符设备是指在存取时没有缓存的设备,而块设备的读写都有缓存来支持,并且块设备必须能够随机存取(random access)。典型的字符设备包括鼠标,键盘,串行口等。块设备主要包括硬盘软盘设备,CD-ROM等。 网络设备在Linux里做专门的处理。Linux的网络系统主要是基于BSD unix的socket 机制。在系统和驱动程序之间定义有专门的数据结构(sk_buff)进行数据传递。系统有支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。 3.3驱动程序的结构 驱动程序的结构如图3.1所示,应用程序经过系统调用,进入核心层,内核要控制硬件需要通过驱动程序实现,驱动程序相当于内核与硬件之间的“系统调用”。

一个简单的演示用的Linux字符设备驱动程序.

实现如下的功能: --字符设备驱动程序的结构及驱动程序需要实现的系统调用 --可以使用cat命令或者自编的readtest命令读出"设备"里的内容 --以8139网卡为例,演示了I/O端口和I/O内存的使用 本文中的大部分内容在Linux Device Driver这本书中都可以找到, 这本书是Linux驱动开发者的唯一圣经。 ================================================== ===== 先来看看整个驱动程序的入口,是char8139_init(这个函数 如果不指定MODULE_LICENSE("GPL", 在模块插入内核的 时候会出错,因为将非"GPL"的模块插入内核就沾污了内核的 "GPL"属性。 module_init(char8139_init; module_exit(char8139_exit; MODULE_LICENSE("GPL"; MODULE_AUTHOR("ypixunil"; MODULE_DESCRIPTION("Wierd char device driver for Realtek 8139 NIC"; 接着往下看char8139_init( static int __init char8139_init(void {

int result; PDBG("hello. init.\n"; /* register our char device */ result=register_chrdev(char8139_major, "char8139", &char8139_fops; if(result<0 { PDBG("Cannot allocate major device number!\n"; return result; } /* register_chrdev( will assign a major device number and return if it called * with "major" parameter set to 0 */ if(char8139_major == 0 char8139_major=result; /* allocate some kernel memory we need */ buffer=(unsigned char*(kmalloc(CHAR8139_BUFFER_SIZE, GFP_KERNEL; if(!buffer { PDBG("Cannot allocate memory!\n"; result= -ENOMEM;

字符设备驱动程序

Linux字符设备驱动(转载) 来源: ChinaUnix博客日期:2008.01.01 18:52(共有0条评论) 我要评论 Linux字符设备驱动(转载) 这篇文章描述了在Linux 2.4下,如何建立一个虚拟的设备,对初学者来说很有帮助。原文地址:https://www.360docs.net/doc/7115598295.html,/186/2623186.shtml Linux下的设备驱动程序被组织为一组完成不同任务的函数的集合,通过这些函数使得Windows的设备操作犹如文件一般。在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作,如open ()、close ()、read ()、write () 等。 Linux主要将设备分为二类:字符设备和块设备。字符设备是指设备发送和接收数据以字符的形式进行;而块设备则以整个数据缓冲区的形式进行。字符设备的驱动相对比较简单。 下面我们来假设一个非常简单的虚拟字符设备:这个设备中只有一个4个字节的全局变量int global_var,而这个设备的名字叫做"gobalvar"。对"gobalvar"设备的读写等操作即是对其中全局变量global_var的操作。 驱动程序是内核的一部分,因此我们需要给其添加模块初始化函数,该函数用来完成对所控设备的初始化工作,并调用register_chrdev() 函数注册字符设备: static int __init gobalvar_init(void) { if (register_chrdev(MAJOR_NUM, " gobalvar ", &gobalvar_fops)) { //…注册失败 } else

字符设备驱动开发实验

字符设备驱动实验 实验步骤: 1、将设备驱动程序使用马克file文件编译 生成模块firstdev.ko 2、将模块加载到系统中insmod firstdev.ko 3、手动创建设备节点 mknod /dev/first c 122 0 4、使用gcc语句编译firsttest.c生成可执行 文件 5、运行可执行文件firsttest,返回驱动程序 中的打印输出语句。 查看设备号:cat /proc/devices 卸载驱动:rmmod firstdev 删除设备节点:rm /dev/first 显示printk语句,(打开一个新的终端)while true do sudo dmesg -c sleep 1 done

源码分析 设备驱动程序firstdev.c #include #include #include #include #include #include //#include static int first_dev_open(struct inode *inode, struct file *file) { //int i; printk("this is a test!\n"); return 0; }

static struct file_operations first_dev_fops ={ .owner = THIS_MODULE, .open = first_dev_open, }; static int __init first_dev_init(void) { int ret; ret = register_chrdev(122,"/dev/first",&first_dev_fo ps); printk("Hello Modules\n"); if(ret<0) { printk("can't register major number\n"); return ret; }

一个简单字符设备驱动实例

如何编写Linux设备驱动程序 Linux是Unix操作系统的一种变种,在Linux下编写驱动程序的原理和思想完全类似于其他的Unix系统,但它dos或window环境下的驱动程序有很大的区别。在Linux环境下设计驱动程序,思想简洁,操作方便,功能也很强大,但是支持函数少,只能依赖kernel中的函数,有些常用的操作要自己来编写,而且调试也不方便。本文是在编写一块多媒体卡编制的驱动程序后的总结,获得了一些经验,愿与Linux fans共享,有不当之处,请予指正。 以下的一些文字主要来源于khg,johnsonm的Write linux device driver,Brennan's Guide to Inline Assembly,The Linux A-Z,还有清华BBS上的有关device driver的一些资料. 这些资料有的已经过时,有的还有一些错误,我依据自己的试验结果进行了修正. 一、Linux device driver 的概念 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1)对设备初始化和释放; 2)把数据从内核传送到硬件和从硬件读取数据; 3)读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4)检测和处理设备出现的错误。 在Linux操作系统下有两类主要的设备文件类型,一种是字符设备,另一种是块设备。字符设备和块设备的主要区别是:在对字符设备发出读/写请求时,实际的硬件I/O一般就紧接着发生了,块设备则不然,它利用一块系统内存作缓冲区,当用户进程对设备请求能满足用户的要求,就返回请求的数据,如果不能,就调用请求函数来进行实际的I/O操作。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待. 已经提到,用户进程是通过设备文件来与实际的硬件打交道。每个设备文件都都有其文件属性(c/b),表示是字符设备还是块设备。另外每个文件都有两个设备号,第一个是主设备号,标识驱动程序,第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分他们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序. 最后必须提到的是,在用户进程调用驱动程序时,系统进入核心态,这时不再是抢先式调度。也就是说,系统必须在你的驱动程序的子函数返回后才能进行其他的工作。如果你的驱动程序陷入死循环,不幸的是你只有重新启动机器了,然后就是漫长的fsck。 二、实例剖析 我们来写一个最简单的字符设备驱动程序。虽然它什么也不做,但是通过它可以了解Linux的设备驱动程序的工作原理.把下面的C代码输入机器,你就会获得一个真正的设备

linux字符设备驱动课程设计报告

一、课程设计目的 Linux 系统的开源性使其在嵌入式系统的开发中得到了越来越广泛的应用,但其本身并没有对种类繁多的硬件设备都提供现成的驱动程序,特别是由于工程应用中的灵活性,其驱动程序更是难以统一,这时就需开发一套适合于自己产品的设备驱动。对用户而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说是把设备映射为一个特殊的设备文件,用户程序可以像对其它文件一样对此设备文件进行操作。 通过这次课程设计可以了解linux的模块机制,懂得如何加载模块和卸载模块,进一步熟悉模块的相关操作。加深对驱动程序定义和设计的了解,了解linux驱动的编写过程,提高自己的动手能力。 二、课程设计内容与要求 字符设备驱动程序 1、设计目的:掌握设备驱动程序的编写、编译和装载、卸载方法,了解设备文件的创建,并知道如何编写测试程序测试自己的驱动程序是否能够正常工作 2、设计要求: 1) 编写一个简单的字符设备驱动程序,该字符设备包括打开、读、写、I\O控制与释放五个基本操作。 2) 编写一个测试程序,测试字符设备驱动程序的正确性。 3) 要求在实验报告中列出Linux内核的版本与内核模块加载过程。 三、系统分析与设计 1、系统分析 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。设备驱动程序是内核的一部分,它完成以下的功能: 1、对设备初始化和释放; 2、把数据从内核传送到硬件和从硬件读取数据; 3、读取应用程序传送给设备文件的数据和回送应用程序请求的数据; 4、检测和处理设备出现的错误。 字符设备提供给应用程序的是一个流控制接口,主要包括op e n、clo s e(或r ele as e)、r e ad、w r i t e、i o c t l、p o l l和m m a p等。在系统中添加一个字符设备驱动程序,实际上就是给上述操作添加对应的代码。对于字符设备和块设备,L i n u x内核对这些操作进行了统一的抽象,把它们定义在结构体fi le_operations中。 2、系统设计: 、模块设计:

Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻塞型IO和休眠]

Linux设备驱动程序学习(5)-高级字符驱动程序操作[(2)阻 塞型I/O和休眠] Linux设备驱动程序学习(5) -高级字符驱动程序操作[(2)阻塞型I/O和休眠]这一部分主要讨论:如果驱动程序无法立即满足请求,该如何响应?(65865346) 一、休眠 进程被置为休眠,意味着它被标识为处于一个特殊的状态并且从调度器的运行队列中移走。这个进程将不被在任何CPU 上调度,即将不会运行。直到发生某 些事情改变了那个状态。安全地进入休眠的两条规则: (1)永远不要在原子上下文中进入休眠,即当驱动在持有一个自旋锁、seqlock或者RCU 锁时不能睡眠;关闭中断也不能睡眠。持有一个信号量时休眠是 合法的,但你应当仔细查看代码:如果代码在持有一个信号量时睡眠,任何其他的等待这个信号量的线程也会休眠。因此发生在持有信号量时的休眠必须短暂, 而且决不能阻塞那个将最终唤醒你的进程。 (2)当进程被唤醒,它并不知道休眠了多长时间以及休眠时发生什么;也不知道是否另有进程也在休眠等待同一事件,且那个进程可能在它之前醒来并获取了 所等待的资源。所以不能对唤醒后的系统状态做任何的假设,并必须重新检查等待条件来确保正确的响应。 除非确信其他进程会在其他地方唤醒休眠的进程,否则也不能睡眠。使进程可被找到意味着:需要维护一个称为等待队列的数据结构。它是一个进程链表,其中饱含了等待某个特定事件的所有进程。在Linux 中,一个等待队列由一个wait_queue_head_t 结构体来管理,其定义在中。 wait_queue_head_t 类型的数据结构非常简单: 它包含一个自旋锁和一个链表。这个链表是一个等待队列入口,它被声明做wait_queue_t。wait_queue_head_t包含关于睡眠进程的信息和它想怎样被唤

字符设备驱动框架

Linux中设备分类: 按照对设备的访问方式可分为以下三类: 1.字符设备(char device) (1)例如:键盘、鼠标、串口、帧缓存等; (2)通过/dev/下的设备节点访问;以字节为单位访问; (3)一般只支持顺序访问;(特例:帧缓存framebuffer) (4)无缓冲。 2.块设备(block device) (1)例如:磁盘、光驱、flash等; (2)以固定大小为单位访问:磁盘以扇区(512B)为单位;flash以页为单位。 (3)支持随机访问; (4)有缓冲(减少磁盘IO,提高效率)。 3.网络设备(network device) (1)无设备文件(节点); (2)应用层通过socket接口访问网络设备(报文发送和接收的媒介)。 设备驱动在内核中的结构: 1.VFS虚拟文件系统作用:向应用层提供一致的文件访问接口,正是由于VFS 的存在,才可以将设备以文件的方式访问。 2.虚拟文件系统,存在于内存中,不在磁盘上,掉电丢失。例如:/proc、/sys、 /tmp。

设备号: 1.作用:唯一地标识一个设备; 2.类型:dev_t devno;即32位无符号整型; 3.组成: (1)主设备号:用于区分不同类型(按功能划分)的设备; (2)此设备号:用于区分相同类型的不同设备。 注意:相同类型的设备(主设备号相同)可以使用同一个驱动。 4.构建设备号: int major = 250; int minor = 0; (1)dev_t devno = (major << 20) | minor;不建议使用; (2)利用宏来构建:dev_t devno = MKDEV (major, minor); 注意:我们可以通过文件$(srctree)/documentation/device.txt来查看内核对设备号的分配情况。 (1)该文本中的有对应设备文件的设备号是已经被申请过的,我们不可以重 复使用(申请); (2)从中可以看出,我们在编写驱动程序时可以使用的主设备号范围为 240~254,为了方便记忆,通常使用250作为主设备号。 字符设备驱动框架: 驱动:作用,为应用层提供访问设备的接口(对设备发的各种操作)。 一、申请设备号 1.构建设备号:dev_t devno = MKDEV (major, minor); 2.申请设备号: (1)动态申请:alloc_chrdev_region; (2)静态申请: register_chrdev_region。

字符设备驱动步骤

编写字符设备驱动框架的步骤 Step 1: 申请设备号(主要是申请主设备号) 有两种方式: ⑴静态申请 通过下面这个函数实现: int register_chrdev_region(dev_t from, unsigned count, const char *name); /* register_chrdev_region() - register a range of device numbers * @from: the first in the desired range of device numbers; must include * the major number. * @count: the number of consecutive device numbers required * @name: the name of the device or driver. * * Return value is zero on success, a negative error code on failure.*/ 这种方式主要用于,驱动开发者事先知道该驱动主设备号的情况。 ⑵动态申请 通过下面这个函数实现: int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) /* alloc_chrdev_region() - register a range of char device numbers * @dev: output parameter for first assigned number * @baseminor: first of the requested range of minor numbers * @count: the number of minor numbers required * @name: the name of the associated device or driver * * Allocates a range of char device numbers. The major number will be * chosen dynamically, and returned (along with the first minor number) * in @dev. Returns zero or a negative error code.*/ 这种方式由系统动态分配一个设备号,返回的设备号保存在参数dev中。 Step 2 :注册字符设备 在linux 内核中用struct cdev表示一个字符设备。 字符设备的注册与注销分别通过下面的两个函数来实现: int cdev_add(struct cdev *p, dev_t dev, unsigned count); /** * cdev_add() - add a char device to the system * @p: the cdev structure for the device * @dev: the first device number for which this device is responsible * @count: the number of consecutive minor numbers corresponding to this * device * * cdev_add() adds the device represented by @p to the system, making it * live immediately. A negative error code is returned on failure.

字符设备驱动程序

字符设备驱动程序 字符设备驱动程序与块设备不同。所涉及的键盘驱动、控制台显示驱动和串口驱动以及与这些驱动有关的接口、算法程序都紧密相关。他们共同协作实现控制台终端和串口终端功能。 下图反映了控制台键盘中断处理过程。 以上为总的处理流程,下面对每一个驱动分开分析。首先是键盘驱动。键盘驱动用汇编写的,比较难理解,牵涉内容较多,有键盘控制器804X的编程,还有扫描码(共3套,这里用第二套)和控制命令及控制序列(p209~210有讲解)。由于键盘从XT发展到AT到现在PS/2,USB,无线键盘,发展较快,驱动各有不同,此版本驱动为兼容XT,将扫描码映射为XT再处理,因此仅供参考。CNIX操作系统的键盘驱动实现为C语言,可读性更好。 键盘驱动 键盘驱动就是上图键盘硬件中断的过程。keyboard.S中的_keyboard_interrupt 函数为中断主流程,文件中其他函数均被其调用。

以上打星处为键盘驱动的核心,即主要处理过程,针对不同扫描码分别处理,并最终将转换后所得ASCII 码或控制序列放入控制台tty 结构的读缓冲队列read_q 中。 键处理程序跳转表为key_table ,根据扫描码调用不同处理程序,对于“普通键”,即只有一个字符返回且没有含义变化的键,调用do_self 函数。其他均为“特殊键”:1. crtrl 键的按下和释放 2. alt 键的按下和释放 3. shift 键的按下和释放 4. caps lock 键的按下和释放(释放直接返回,不作任何处理) 5. scroll lock 键的按下 6. num lock 的按下 7. 数字键盘的处理(包括alt-ctrl+delete 的处理,因为老式键盘delete 键在数字小键盘上。还包括对光标移动键的分别处理) 8. 功能键 (F1~F12)的处理 9. 减号的处理(老键盘’/’与’-’以0xe0加以区分,可能其中一键要按shift ) do_self 是最常用的流程,即跳转表中使用频率最高的流程:

LINUX字符设备驱动编写基本流程

---简介 Linux下的MISC简单字符设备驱动虽然使用简单,但却不灵活。 只能建立主设备号为10的设备文件。字符设备比较容易理解,同时也能够满足大多数简 单的硬件设备,字符设备通过文件系统中的名字来读取。这些名字就是文件系统中的特 殊文件或者称为设备文件、文件系统的简单结点,一般位于/dev/目录下使用ls进行查 看会显示以C开头证明这是字符设备文件crw--w---- 1 root tty 4, 0 4月 14 11:05 tty0。 第一个数字是主设备号,第二个数字是次设备号。 ---分配和释放设备编号 1)在建立字符设备驱动时首先要获取设备号,为此目的的必要的函数是 register_chrdev_region,在linux/fs.h中声明:int register_chrdev_region(dev_t first, unsigned int count, char *name);first是你想 要分配的起始设备编号,first的次编号通常是0,count是你请求的连续设备编号的 总数。count如果太大会溢出到下一个主设备号中。name是设备的名字,他会出现在 /proc/devices 和sysfs中。操作成功返回0,如果失败会返回一个负的错误码。 2)如果明确知道设备号可用那么上一个方法可行,否则我们可以使用内核动态分配的设 备号int alloc_chrdev_region(dev_t *dev, unsigned int firstminor,unsigned int count, char *name);dev是个只输出的参数,firstminor请求的第一个要用的次编号, count和name的作用如上1)对于新驱动,最好的方法是进行动态分配 3)释放设备号,void unregister_chrdev_region(dev_t first unsigned int count); ---文件操作file_operations结构体,内部连接了多个设备具体操作函数。该变量内部 的函数指针指向驱动程序中的具体操作,没有对应动作的指针设置为NULL。 1)fops的第一个成员是struct module *owner 通常都是设置成THIS_MODULE。 linux/module.h中定义的宏。用来在他的操作还在被使用时阻止模块被卸载。 2)loff_t (*llseek) (struct file *, loff_t, int);该方法用以改变文件中的当前读/ 写位置 返回新位置。 3)ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);该函数用 以从设备文件 中读取数据,读取成功返回读取的字节数。

linux驱动程序的编写

linux驱动程序的编写 一、实验目的 1.掌握linux驱动程序的编写方法 2.掌握驱动程序动态模块的调试方法 3.掌握驱动程序填加到内核的方法 二、实验内容 1. 学习linux驱动程序的编写流程 2. 学习驱动程序动态模块的调试方法 3. 学习驱动程序填加到内核的流程 三、实验设备 PentiumII以上的PC机,LINUX操作系统,EL-ARM860实验箱 四、linux的驱动程序的编写 嵌入式应用对成本和实时性比较敏感,而对linux的应用主要体现在对硬件的驱动程序的编写和上层应用程序的开发上。 嵌入式linux驱动程序的基本结构和标准Linux的结构基本一致,也支持模块化模式,所以,大部分驱动程序编成模块化形式,而且,要求可以在不同的体系结构上安装。linux是可以支持模块化模式的,但由于嵌入式应用是针对具体的应用,所以,一般不采用该模式,而是把驱动程序直接编译进内核之中。但是这种模式是调试驱动模块的极佳方法。 系统调用是操作系统内核和应用程序之间的接口,设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分,它完成以下的功能:对设备初始化和释放;把数据从内核传送到硬件和从硬件读取数据;读取应用程序传送给设备文件的数据和回送应用程序请求的数据;检测和处理设备出现的错误。在linux操作系统下有字符设备和块设备,网络设备三类主要的设备文件类型。 字符设备和块设备的主要区别是:在对字符设备发出读写请求时,实际的硬件I/O一般就紧接着发生了;块设备利用一块系统内存作为缓冲区,当用户进程对设备请求满足用户要求时,就返回请求的数据。块设备是主要针对磁盘等慢速设备设计的,以免耗费过多的CPU时间来等待。 1 字符设备驱动结构 Linux字符设备驱动的关键数据结构是cdev和file_operations结构体。

linux设备驱动中常用函数

Linux2.6设备驱动常用的接口函数(一) ----字符设备 刚开始,学习linux驱动,觉得linux驱动很难,有字符设备,块设备,网络设备,针对每一种设备其接口函数,驱动的架构都不一样。这么多函数,要每一个的熟悉,那可多难啦!可后来发现linux驱动有很多规律可循,驱动的基本框架都差不多,再就是一些通用的模块。 基本的架构里包括:加载,卸载,常用的读写,打开,关闭,这是那种那基本的咯。利用这些基本的功能,当然无法实现一个系统。比方说:当多个执行单元对资源进行访问时,会引发竞态;当执行单元获取不到资源时,它是阻塞还是非阻塞?当突然间来了中断,该怎么办?还有内存管理,异步通知。而linux 针对这些问题提供了一系列的接口函数和模板框架。这样,在实际驱动设计中,根据具体的要求,选择不同的模块来实现其功能需求。 觉得能熟练理解,运用这些函数,是写号linux设备驱动的第一步。因为是设备驱动,是与最底层的设备打交道,就必须要熟悉底层设备的一些特性,例如字符设备,块设备等。系统提供的接口函数,功能模块就像是工具,能够根据不同的底层设备的的一些特性,选择不同的工具,方能在linux驱动中游刃有余。 最后就是调试,这可是最头疼的事。在调试过程中,总会遇到这样,那样的问题。怎样能更快,更好的发现并解决这些问题,就是一个人的道行咯!我个人觉得: 发现问题比解决问题更难! 时好时坏的东西,最纠结! 看得见的错误比看不见的错误好解决! 一:Fops结构体中函数: ①ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); 用来从设备中获取数据. 在这个位置的一个空指针导致 read 系统调用以-EINVAL("Invalid argument") 失败. 一个非负返回值代表了成功读取的字节数( 返回值是一个 "signed size" 类型, 常常是目标平台本地的整数类型). ②ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); 发送数据给设备. 如果 NULL, -EINVAL 返回给调用 write 系统调用的程序. 如果非负, 返回值代表成功写的字节数 ③loff_t (*llseek) (struct file *, loff_t, int); llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值. loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示. 如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file 结构中的位置计数器( 在"file 结构" 一节中描述). ④int (*open) (struct inode *, struct file *);

简单字符设备驱动程序的设计

实验五:简单字符设备驱动程序的设计 实验学时:4 实验类型:(设计) 一、实验目的 1. 理解设备驱动程序的处理过程; 2. 掌握Linux设备驱动程序开发的基本过程和设计方法; 3. 学会编写简单的字符设备驱动程序。 二、实验条件 Linux操作系统gcc 三、实验原理及相关知识 设备驱动程序是I/O进程与设备控制器之间的通信程序。 驱动程序的功能: ⑴接收由设备独立性软件发来的命令和参数,并将命令中的抽象要求转换为具体的要求。 ⑵检查用户I/O请求的合法性,了解I/O设备的状态,传递有关参数,设置设备的工作方式。 ⑶发出I/O命令。 ⑷及时响应由控制器或通道发来的中断请求,并根据其中断类型调用相应的中断处理程序进行处理。 ⑸对于设置有通道的计算机系统,驱动程序还应能够根据用户的I/O请求,自动地构建通道程序。 设备驱动程序的处理过程: ⑴将抽象要求转换为具体要求 ⑵检查I/O设备请求的合法性 ⑶读出和检查设备的状态 ⑷传送必要的参数 ⑸工作方式的设置 ⑹启动I/O设备 Linux系统中,设备驱动程序是操作系统内核的重要组成部分,它与硬件设备之间建立了标准的抽象接口。通过这个接口,用户可以像处理普通文件一样,对硬件设备进行打开(open)、关闭(close)、读写(read/write)等操作。

通常设备驱动程序接口是由结构file_operations结构体向系统说明的,它定义在include/linux/fs.h中。file_operations的数据结构如下: struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char_user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char _user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); int (*fsync) (struct file *, struct dentry *, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); ... }; ⑴open 入口点: open函数负责打开设备、准备I/O。任何时候对设备文件进行打开操作,都会调用设备的open入口点。所以,open函数必须对将要进行的I/O操作做好必要的准备工作,如清除缓冲区等。如果设备是独占的。则open函数必须将设备标记成忙状态。 ⑵close入口点 close函数负责关闭设备的操作,当最后一次使用设备完成后,调用close函数,关闭设备文件。独占设备必须标记为可再次使用。 close()函数作用是关闭打开的文件。 ⑶read入口点 read函数负责从设备上读数据和命令,有缓冲区的I/O设备操作一般是从缓冲区里读数据。

简单的虚拟字符设备驱动的实现

简单的虚拟字符设备驱动的实现 Linux業已成为嵌入式系统的主流,而各种Linux驱动程序的类型中,字符设备无疑是应用最广泛的。本文实现了一个简单的虚拟字符设备的驱动程序,用以演示Linux字符设备驱动的基本原理。在嵌入式Linux的教学中具有重要的作用。 标签:Linux 驱动程序字符设备虚拟嵌入式 Linux作为一种开放源代码的操作系统,在嵌入式系统领域业已成为主流,而为嵌入式Linux系统开发设备驱动程序,也成为一项重要的工作。Linux系统中的驱动程序主要分为三种:字符设备驱动程序、块设备驱动程序和网络驱动程序。其中字符设备是一类只能顺序读写,没有缓存的驱动程序,其实现方法相对简单,而应用则最为广泛。在嵌入式Linux的教学中,字符设备驱动程序也是一项重要内容。为了让学生能够理解字符设备驱动程序的原理,需要一个简单的字符设备驱动的例子,用以进行演示。 一、基本原理 把设备当作文件处理,是Linux系统的重要思想,即“一切皆文件”。在用户空间中,应用程序对字符设备的操作跟读写普通文件没有什么区别,也是通过open()、close()、read()、write()等函数实现的。操作系统将这些用户空间中的函数分别映射到内核空间中由驱动程序提供的对应接口。因此,内核空间中的驱动程序就需要通过对对应接口函数的实现来实现对用户空间中应用程序的支持。 file_opreations是字符设备驱动中最重要的结构,它包含了字符设备各种可能的接口函数。通常在嵌入式编程中,我们不需要全部实现,只需要实现我们实际用到的接口就可以了,这样可以有效降低程序的大小。该结构被定义在头文件“linux/fs.h”中,使用时只需声明该结构的一个变量并进行填充即可。 二、环境准备 为了进行嵌入式Linux的开发,必须首先安装Linux系统。这里采用最常用的Windows系统+VMWare虚拟机的形式,系统版本为RedHat Enterprise Linux 6.4,其自带的内核版本为2.6.32-358.el6.i686。该版本比之前沿用的RedHat9更新,同时也是一个被验证过的非常稳定的系统。 交叉编译器采用网上下载的Arm-Linux-gcc 4.5.1版本,同样兼顾到版本更新和稳定性之间的平衡关系。 各软件的安装过程本文不再赘述。

字符设备驱动程序的扩展操作

第5章字符设备驱动程序的扩展操作 在关于字符设备驱动程序的那一章中,我们构建了一个完整的设备驱动程序,从中用户可以读也可以写。但实际一个驱动程序通常会提供比同步read和write更多的功能。现在如果出了什么毛病,我已经配备了调试工具,我们可以大胆的实验并实现新操作。 通过补充设备读写操作的功能之一就是控制硬件,最常用的通过设备驱动程序完成控制动作的方法就是实现ioctl方法。另一种方法是检查写到设备中的数据流,使用特殊序列做为控制命令。尽管有时也使用后者,但应该尽量避免这样使用。不过稍后我们还是会在本章的“非ioctl设备控制”一节中介绍这项技术。 正如我在前一章中所猜想的,ioctl系统调用为驱动程序执行“命令”提供了一个设备相关的入口点。与read和其他方法不同,ioctl是设备相关的,它允许应用程序访问被驱动硬件的特殊功能――配置设备以及进入或退出操作模式。这些“控制操作”通常无法通过read/write文件操作完成。例如,你向串口写的所有数据都通过串口发送出去了,你无法通过写设备改变波特率。这就是ioctl所要做的:控制I/O通道。 实际设备(与scull不同)的另一个重要功能是,读或写的数据需要同其他硬件交互,需要某些同步机制。阻塞型I/O和异步触发的概念将满足这些需求,本章将通过一个改写的scull设备介绍这些内容。驱动程序利用不同进程间的交互产生异步事件。与最初的scull相同,你无需特殊硬件来测试驱动程序是否可以工作。直到第8章“硬件管理”我才会真正去与硬件打交道。 ioctl 在用户空间内调用ioctl函数的原型大致如下: int ioctl(int fd, int cmd, ...); 由于使用了一连串的“.”的缘故,该原型在Unix系统调用列表之中非常突出,这些点代表可变数目参数。但是在实际系统中,系统调用实际上不会有可变数目个参数。因为用户程序只能通过第2章“编写和运行模块”的“用户空间和内核空间”一节中介绍的硬件“门”才能访问内核,系统调用必须有精确定义的参数个数。因此,ioctl的第3个参数事实上只是一个可选参数,这里用点只是为了在编译时防止编译器进行类型检查。第3个参数的具体情况与要完成的控制命令(第2个参数)有关。某些命令不需要参数,某些需要一个整数做参数,而某些则需要一个指针做参数。使用指针通常是可以用来向ioctl传递任意数目数据;设备可以从用户空间接收任意大小的数据。 系统调用的参数根据方法的声明传递给驱动程序方法: int (*ioctl) (struct inode *inode, struct file *filp,unsigned int cmd, unsigned long arg) inode和filp指针是根据应用程序传递的文件描述符fd计算而得的,与read和write的用法一致。参数cmd不经修改地传递给驱动程序,可选的arg参数无论是指针还是整数值,它都以unsigned long的形式传递给驱动程序。如果调用程序没有传递第3个参数,驱动程序所接收的arg没有任何意义。

相关文档
最新文档