51Testing软件测试论坛

标题: 字符设备驱动模块与测试代码编写。 [打印本页]

作者: MilgGtery    时间: 2018-4-24 17:30
标题: 字符设备驱动模块与测试代码编写。
设备驱动程序:以内核模块的形式存在也就是*.ko

设备驱动程序和系统调用关系.
系统调用:应有程序和操作系统(内核) 之间的接口(应用程序与内核的交互)
设备驱动程序:内核和设备硬件之间接口(内核与硬件的交互)

整个过程实现了应用程序间接访问了底层的硬件。

test.c中调用open-----》系统调用(sys_open())----->file_operation{.open = led_open}:调用驱动函数中的自
定义的open

---------------------------------------------------------------

linux设备驱动的三种类型:

1.字符设备ED,显卡,声卡,键盘,触摸屏。。。
2.块设备:硬盘,nandflash,SD,U盘....
3.网络设备:网卡(无线,有线)

---------------------------------------------------------------

二。字符设备特点
1.应用程序和设备进行数据交互时,是以字节单位进行的。
2。访问的数据是连续时,实时。
3.不带缓存,块设备驱动带有缓存.

三。字符设备的描述
1。cdev结构
  1. struct cdev {
  2. struct kobject kobj;
  3. struct module *owner;
  4. const struct file_operations *ops;
  5. struct list_head list; //内核链表
  6. dev_t dev;
  7. unsigned int count;//当前设备下有多少个次设备
  8. };
复制代码

cdev是描述一个字符设备,每写一字符设备,创建一个cdev结构,每个字符设备都有自已的cdev

--struct kobject kobj;
不关心的,是由内核管理设备使用,
--struct module *owner;
cdev属于那个体module,一般写THIS_MODULE;
--const struct file_operations *ops;
操作集(设备驱动),是应用程序访问设备的驱动接口

--struct list_head list;
内核链表:将当前cdev放到一链表,方便内核管理cdev

--dev_t dev;
typedef u_long dev_t;
typedef unsigned long u_long;
设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形
--unsigned int count;
在当设备下有多少个次设备...

  1. struct file_operations {
  2. struct module *owner;
  3. loff_t (*llseek) (struct file *, loff_t, int);
  4. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
  5. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
  6. ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  7. ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
  8. int (*readdir) (struct file *, void *, filldir_t);
  9. unsigned int (*poll) (struct file *, struct poll_table_struct *);
  10. int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
  11. long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
  12. long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
  13. int (*mmap) (struct file *, struct vm_area_struct *);
  14. int (*open) (struct inode *, struct file *);
  15. int (*flush) (struct file *, fl_owner_t id);
  16. int (*release) (struct inode *, struct file *);
  17. -----------------------------------------------
  18. };
复制代码

------------------------------------------------------------------

cdev中的设备号:

2.1定义
typedef u_long dev_t;
typedef unsigned long u_long;
设备号.每个字符设备都有自已的设备号,设备号在当内核中是唯一,在32 BITs,32无符号整形。

2.2设备号由主设备号和次设备号组成:
主设备号:某类型类的设备 [31:20]
次设备号:该类型的设备下,具体那个设备实例.[19:0]

2.3相关函数(宏):由设备号的得到主次设备号

#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)        ((unsigned int) ((dev) & MINORMASK))

  由主次设备号得到设备号:

#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
ma-->主设备号
mi-->次设备号

  向内核中注册设备号:

定义一个CDEV时候,首先在内核中申请一个设备号,获得设备号的方法有两种。

1、向内核中静态注册一个设备号:自已定制一个设备号,向内核注册(申请),如果内核中没有占用设备号,
代注册成功。

  1. * register_chrdev_region() - register a range of device numbers
  2. * @from: the first in the desired range of device numbers; must include
  3. * the major number.
  4. * @count: the number of consecutive device numbers required
  5. * @name: the name of the device or driver.
  6. *
  7. * Return value is zero on success, a negative error code on failure.
  8. */
  9. int register_chrdev_region(dev_t from, unsigned count, const char *name)
复制代码

参数说明:
from-->设备号的值
count-->次设备的数量
name->设备的名称.
返回值:
成功:0
失败:负数

例:
  1. [@GEC2103 /]# ls /dev/s3c241* -l
  2. crw-rw---- 1 0 0 204, 64 Jan 1 00:00 /dev/s3c2410_serial0
  3. crw-rw---- 1 0 0 204, 65 Jan 1 00:00 /dev/s3c2410_serial1
  4. crw-rw---- 1 0 0 204, 66 Jan 1 00:00 /dev/s3c2410_serial2
  5. crw-rw---- 1 0 0 204, 67 Jan 1 00:00 /dev/s3c2410_serial3

  6. register_chrdev_region(MKDEV(204,64),4,“s3c2410_serial”)
复制代码



#cat /proc/devices
204 s3c2410_serial

2、让内核动态分配一个空闲设备号

  1. /**
  2. * alloc_chrdev_region() - register a range of char device numbers
  3. * @dev: output parameter for first assigned number
  4. * @baseminor: first of the requested range of minor numbers
  5. * @count: the number of minor numbers required
  6. * @name: the name of the associated device or driver
  7. *
  8. * Allocates a range of char device numbers. The major number will be
  9. * chosen dynamically, and returned (along with the first minor number)
  10. * in @dev. Returns zero or a negative error code.
  11. */
  12. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)
复制代码

参数:
*dev:-->申请设备号
baseminor-->申请设备号第一个次设备号
count-->申请次设备的数量
name-->设备名称

返回值:
成功:0
失败:负数

注意:一定判断申请是成功


dev_t det;
alloc_chrdev_region(&det,0,2,"test");

3、注销设备号

3.3注销一个已经注册的设备号
当驱动程序移除时,一定先注销设备号
  1. /**
  2. * unregister_chrdev_region() - return a range of device numbers
  3. * @from: the first in the range of numbers to unregister
  4. * @count: the number of device numbers to unregister
  5. *
  6. * This function will unregister a range of @count device numbers,
  7. * starting with @from. The caller should normally be the one who
  8. * allocated those numbers in the first place...
  9. */
  10. void unregister_chrdev_region(dev_t from, unsigned count)
复制代码

参数:from-->设备号的值,第一个设备号
count-->次设备号的数量

--------------------------------------------------------------------------

3.文件操作集
struct file_operations

文件操作集是内核管理cdev那设备的驱动接口, 驱动程序提供应用程序一个接口

  1. int testopen(struct inode *inode, struct file *file)
  2. {



  3. }
  4. int testclose (struct inode *inode, struct file *file);
  5. {


  6. }
  7. ssize_t testread(struct file *, char __user *, size_t, loff_t *)
  8. {


  9. }

  10. struct file_operations fops= --->结构体初始化 .---???????????????
  11. {        .owner=THIS_MODULE,
  12. .open=testopen,
  13. .read=testread,
  14. .release=testclose,
  15. }
复制代码

----------------------------------------------------------------------

4.cdev初始化和注册
4.1.cdev的初始化函数
  1. /**
  2. * cdev_init() - initialize a cdev structure
  3. * @cdev: the structure to initialize
  4. * @fops: the file_operations for this device
  5. *
  6. * Initializes @cdev, remembering @fops, making it ready to add to the
  7. * system with cdev_add().
  8. */
  9. void cdev_init(struct cdev *cdev, const struct file_operations *fops)
复制代码

参数:       
cdev-->要初始化cdev
fops-->cdev的文件操作集




4.2.注册cdev到内核的函数
  1. /**
  2. * cdev_add() - add a char device to the system
  3. * @p: the cdev structure for the device
  4. * @dev: the first device number for which this device is responsible
  5. * @count: the number of consecutive minor numbers corresponding to this
  6. * device
  7. *
  8. * cdev_add() adds the device represented by @p to the system, making it
  9. * live immediately. A negative error code is returned on failure.
  10. */
  11. int cdev_add(struct cdev *p, dev_t dev, unsigned count)
复制代码

参数:p --->注册那个cdev
dev--->设备号
count-->次设备个数


返回值:
成功:0
失败:负数

例:
strcut cdev cd;
cdev_init(&cd,&fops)
cd.owner=THIS_MODULE;
cdev_add(&cd,dev,1);

--------------------------------------------------------------

4.3cdev的注销函数
  1. /**
  2. * cdev_del() - remove a cdev from the system
  3. * @p: the cdev structure to be removed
  4. *
  5. * cdev_del() removes @p from the system, possibly freeing the structure
  6. * itself.
  7. */
  8. void cdev_del(struct cdev *p)
复制代码

p-->要注销那个cdev .

注意:在linux驱动设计时候,我们使用的函数,一般都成对,申请<--->释放 注册--->注销

------------------------------------------------------------------------

5。字符设备的设计流程
5.1定义一个cdev
struct cdev chrdev3;
5.2申请一个设备号
例:
  1. unsigned int TestMajor=0;
  2. unsigned int TestMinor=0;
  3. dev_t dev_no;
  4. int ret;
  5. dev_no =MKDEV(TestMajor,TestMinor)


  6. if(dev_no>0)
  7. {
  8. ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

  9. }
  10. else        //动态申请设备号
  11. {

  12. alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

  13. }       

  14. if(ret<0)
  15. {
  16. return ret;
  17. }
复制代码


5.3定义文件操作集
  1. int testopen(struct inode *inode, struct file *file)
  2. {



  3. }
  4. int testclose (struct inode *inode, struct file *file);
  5. {


  6. }
  7. ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
  8. {


  9. }

  10. struct file_operations fops= --->结构体初始化 .---???????????????
  11. {        .owner=THIS_MODULE,
  12. .open=testopen,
  13. .write=testwrite,
  14. .release=testclose,
  15. }
复制代码

5.4cdev初始化
cdev_init(&chrdev3,&fops);

5.5cdev注册到内核
cdev_add(&chrdev3,dev_no,1);先初始化设备号,然后注册cdev

5.6设备号和cdev注销       
unregister_chrdev_region(dev_no, 1);
cdev_dev(&chrdev3);

-------------------------------------------------------------------------

.结合模块规则:写字符设备驱动程序



作者: MilgGtery    时间: 2018-4-24 17:31
test.c

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include<linux/cdev.h>

  4. struct cdev chrdev3;
  5. unsigned int TestMajor=0;
  6. unsigned int TestMinor=0;
  7. dev_t dev_no;



  8. int testopen(struct inode *inode, struct file *file)
  9. {
  10. //LED输出
  11. printk("led init \n");

  12. }
  13. int testclose (struct inode *inode, struct file *file);
  14. {
  15. printk("close");
  16. return 0;

  17. }
  18. ssize_t testwrtie(struct file *, char __user *usr, size_t len, loff_t *offf)
  19. {       
  20. char buf[12];
  21. copy_from_user(buf,usr,);
  22. buf[12];
  23. //if(buf[]=='1')
  24. //        led点明
  25. printk(,buf);

  26. }
  27. ssize_t testread(struct file *, char __user *usr, size_t len, loff_t *);
  28. {
  29. char buf='r';
  30. read_led;
  31. copy_to_user(usr,buf,);
  32. }

  33. struct file_operations fops= --->结构体初始化 .---???????????????
  34. {        .owner=THIS_MODULE,
  35. .open=testopen,
  36. .write=testwrite,
  37. .release=testclose,
  38. }


  39. static int __init test_init(void) //入口函数
  40. {
  41. printk("hello world!\n"); //相当于printf()
  42. int ret;
  43. dev_no =MKDEV(TestMajor,TestMinor)


  44. if(dev_no>0)
  45. {
  46. ret=register_chrdev_region(dev_no, 1,"chrdev_test");//静态注册设备号

  47. }
  48. else        //动态申请设备号
  49. {

  50. alloc_chrdev_region(&dev_no,TestMinor, 1,"chrdev_test");

  51. }       

  52. if(ret<0)
  53. {
  54. return ret;
  55. }


  56. cdev_init(&chrdev3,&fops);
  57. cdev.owner=THIS_MODULE;
  58. cdev_add(&chrdev3,dev_no,1);
  59. return 0;
  60. }

  61. static void __exit test_exit(void) //出口函数
  62. {
  63. unregister_chrdev_region(dev_no, 1);
  64. cdev_del(&chrdev3);
  65. }


  66. module_init(test_init); //驱动的入口 #insmod *.ko
  67. module_exit(test_exit); //驱动的出口 #rmmod *.ko

  68. //#modinfo *.ko 可以查看module的信息
  69. MODULE_AUTHOR("fbx@GEC");
  70. MODULE_DESCRIPTION("the first module of drivers");
  71. MODULE_LICENSE("GPL");
  72. MODULE_VERSION("V1.0");
复制代码

---------------------------------------------------------------------

8.linux用户空间与内核空间交互函数。
8.1将内核空间数据copy用户空间
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n)

8.2用户空间数据copy内核空间
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n) ---> write


练习:应用调用write写一个字符到内核空间,内核那驱动程序的.write---》 PRINTK();
应用调用read把一个数据从内核读到用户,用户空间打印出来



chrdev.c
  1. 1 #include<linux/module.h>
  2. 2 #include<linux/kernel.h>
  3. 3 #include<linux/cdev.h>
  4. 4 #include<linux/fs.h>
  5. 5 #include<linux/kdev_t.h>
  6. 6 #include<linux/types.h>
  7. 7 #include<linux/uaccess.h>
  8. 8 #include<linux/string.h>
  9. 9 struct cdev chrdev;
  10. 10 unsigned int TestMajor=0;
  11. 11 unsigned int TestMinor=0;
  12. 12 dev_t dev_no;
  13. 13 int ret;
  14. 14
  15. 15 int testopen(struct inode *inode,struct file *file)
  16. 16 {
  17. 17 printk("cdev init\n");
  18. 18 return 0;
  19. 19
  20. 20 }
  21. 21 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
  22. 22 {
  23. 23 char buf[12];
  24. 24
  25. 25 copy_from_user(buf,usr,strlen(usr));
  26. 26 printk("%s\n",buf);
  27. 27
  28. 28 }
  29. 29 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
  30. 30 {
  31. 31 char *buf = "hello,user!";
  32. 32 copy_to_user(usr,buf,20);
  33. 33
  34. 34
  35. 35 }
  36. 36 int testrelease(struct inode *inode, struct file *file)
  37. 37 {
  38. 38 printk("close\n");
  39. 39 return 0;
  40. 40
  41. 41 }
  42. 42
  43. 43 struct file_operations fops=
  44. 44 {
  45. 45 .owner=THIS_MODULE,
  46. 46 .open = testopen,
  47. 47 .write = testwrite,
  48. 48 .read = testread,
  49. 49 .release = testrelease,
  50. 50 };
  51. 51 static int __init test_init(void)
  52. 52 {
  53. 53 dev_no = MKDEV(TestMajor,TestMinor);
  54. 54 if(dev_no>0)
  55. 55 {
  56. 56 ret = register_chrdev_region(dev_no,1,"chrdev_test");
  57. 57 }
  58. 58 else
  59. 59 {
  60. 60 alloc_chrdev_region(&dev_no,0,1,"chrdev_test");
  61. 61 }
  62. 62 if(ret<0)
  63. 63 {
  64. 64 return ret;
  65. 65 }
  66. 66 cdev_init(&chrdev,&fops);
  67. 67 chrdev.owner=THIS_MODULE;
  68. 68 cdev_add(&chrdev,dev_no,1);
  69. 69 return 0;
  70. 70 }
  71. 71
  72. 72 static int __exit test_exit(void)
  73. 73 {
  74. 74 unregister_chrdev_region(dev_no,1);
  75. 75 cdev_del(&chrdev);
  76. 76
  77. 77 return 0;
  78. 78 }
  79. 79
  80. 80 module_init(test_init);
  81. 81 module_exit(test_exit);
  82. 82
  83. 83
  84. 84 MODULE_AUTHOR("FENG");
  85. 85 MODULE_DESCRIPTION("the first module of char drivers");
  86. 86 MODULE_LICENSE("GPL");
  87. 87 MODULE_VERSION("V1.0");
  88. 复制代码
  89. 7.字符设备的调试
  90. 7。1安装驱动
  91. [@GEC2103 /]# insmod chrdev.ko
  92. 7.2查看分配的设备号
  93. [@GEC2103 /]# cat /proc/devices

  94. 250 chrdev_test

  95. 7.3手动创建设备文件
  96. mknod /dev/chrdev c 250 0

  97. [@GEC2103 /]# ls /dev/chrdev -l
  98. crw-r--r-- 1 0 0 250, 0 Jan 1 00:45 /dev/chrdev
  99. 7.4应用程序来/dev/chrdev设备文件,等效操作设备文件对应那个设备

  100. int main()
  101. {
  102. fd=open("/dev/chrdev",O_RDWR);
  103. write(fd,usrbuf,sizeof(usrbuf))
  104. close(fd);

  105. }



  106. --------------------------------------------------------------------------------

  107. test.c

  108. 复制代码
  109. 1 #include<stdio.h>
  110. 2 #include <sys/stat.h>
  111. 3 #include <fcntl.h>
  112. 4 #include <errno.h>
  113. 5 #include <unistd.h>
  114. 6 #include <string.h>
  115. 7
  116. 8 int main()
  117. 9 {
  118. 10 char buf[20];
  119. 11 char buf1[20];
  120. 12 int fd = open("/dev/chrdev_test",O_RDWR);
  121. 13 if(fd<0)
  122. 14 perror("open() error!\n");
  123. 15 bzero(buf1,20);
  124. 16 strcpy(buf1,"hello kernel!");
  125. 17 write(fd,buf1,strlen(buf1)+1);
  126. 18 sleep(1);
  127. 19 read(fd,buf,20);
  128. 20 printf("%s\n",buf);
  129. 21 }
  130. 复制代码
  131. 9。字符设备设计老的方法
  132. 优点:比新简洁,比较好理解.


  133. 1.使用函数
  134. 1)注册字符设备
  135. static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);
  136. 参数:       
  137. major--->主设备号,如果major>0,静态注册
  138. =0 动态分配

  139. name-->设备名称 /proc/devices


  140. fops-->文件操作集


  141. 返回值:
  142. major >0 成功:0
  143. 失败:负数
  144. major =0 成功:分配后主设备号
  145. 失败:负数

  146. 2)注销字符设备
  147. static inline void unregister_chrdev(unsigned int major, const char *name)
  148. major->主设备号
  149. name-->设备名称       

  150. 2.使用旧方法设计字符设备驱动的流程
  151. 2.1unsigned int Testmajor =0;//251

  152. 2.2定义文件操作集
  153. int testopen(struct inode *inode, struct file *file)
  154. {



  155. }
  156. int testclose (struct inode *inode, struct file *file);
  157. {


  158. }
  159. ssize_t testwrtie(struct file *, char __user *, size_t, loff_t *)
  160. {


  161. }

  162. struct file_operations fops= --->结构体初始化 .---???????????????
  163. {        .owner=THIS_MODULE,
  164. .open=testopen,
  165. .write=testwrite,
  166. .release=testclose,
  167. }

  168. 2.3注册一个字符设备

  169. ret=register_chrdev(Testmajor,"chrdev_test",&fops);//注册字符设备到内核
  170. if(ret<0)
  171. {
  172. printk("register error")
  173. }

  174. if(Testmajor==0)
  175. {
  176. Testmajor =ret;

  177. }

  178. 复制代码
  179. 1 #include<linux/module.h>
  180. 2 #include<linux/kernel.h>
  181. 3 #include<linux/cdev.h>
  182. 4 #include<linux/fs.h>
  183. 5 #include<linux/kdev_t.h>
  184. 6 #include<linux/types.h>
  185. 7 #include<linux/uaccess.h>
  186. 8 #include<linux/string.h>
  187. 9
  188. 10 dev_t dev_no = 0;
  189. 11 int ret;
  190. 12
  191. 13 int testopen(struct inode *inode,struct file *file)
  192. 14 {
  193. 15 printk("cdev init\n");
  194. 16 return 0;
  195. 17
  196. 18 }
  197. 19 ssize_t testwrite(struct file *file, const char __user *usr, size_t len, loff_t *off)
  198. 20 {
  199. 21 char buf[12];
  200. 22
  201. 23 copy_from_user(buf,usr,len);
  202. 24 printk("%s\n",buf);
  203. 25
  204. 26 }
  205. 27 ssize_t testread(struct file *file, char __user *usr, size_t len, loff_t *off)
  206. 28 {
  207. 29 char buf[20] = "hello,user!";
  208. 30 copy_to_user(usr,buf,len);
  209. 31
  210. 32
  211. 33 }
  212. 34 int testrelease(struct inode *inode, struct file *file)
  213. 35 {
  214. 36 printk("close\n");
  215. 37 return 0;
  216. 38
  217. 39 }
  218. 40
  219. 41 struct file_operations fops=
  220. 42 {
  221. 43 .owner=THIS_MODULE,
  222. 44 .open = testopen,
  223. 45 .write = testwrite,
  224. 46 .read = testread,
  225. 47 .release = testrelease,
  226. 48 };
  227. 49 static int __init test_init(void)
  228. 50 {
  229. 51 ret = register_chrdev(dev_no,"chrdev_test",&fops);
  230. 52 if(ret < 0)
  231. 53 {
  232. 54 printk("register error!\n");
  233. 55 }
  234. 56 if(dev_no==0)
  235. 57 {
  236. 58 dev_no = ret;
  237. 59 }
  238. 60
  239. 61 return 0;
  240. 62 }
  241. 63
  242. 64 static int __exit test_exit(void)
  243. 65 {
  244. 66 unregister_chrdev(dev_no,"chrdev_test");
  245. 67
  246. 68 return 0;
  247. 69 }
  248. 70
  249. 71 module_init(test_init);
  250. 72 module_exit(test_exit);
  251. 73
  252. 74
  253. 75 MODULE_AUTHOR("FENG");
  254. 76 MODULE_DESCRIPTION("the first module of char drivers");
  255. 77 MODULE_LICENSE("GPL");
  256. 78 MODULE_VERSION("V1.0");
复制代码






欢迎光临 51Testing软件测试论坛 (http://bbs.51testing.com/) Powered by Discuz! X3.2