linux设备驱动 (0)

阅读: 评论:0

linux设备驱动 (0)

linux设备驱动 (0)

一、 linux中设备驱动的类型 字符设备 GPIO、EINT、ADC、WDT、LCD、CAMERA、 块设备 nand flash、SD卡、硬盘 网络设备 :有线网卡、无线网卡 ===================================================================================
二、字符设备驱动的描述 1、cdev cdev 是一个描述一个 字符设备的结构体 ,我们要设计一个字符设备,就必须定义一个 cdev的结构体,然后对cdev结构进行初始化,初始化结束后, cdev注册到内核 ,这样,内核中就添加了一个字符设备驱动。   #include <linux/cdev.h>   struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };   1)struct kobject kobj;     linux在做字符设备管理的时候,使用的驱动模型,利用kobject生成/sys下的驱动信息。   2)struct module *owner;      这个字符设备输入那一个 module, 一般设备为 THIS_MODULE     3)const struct file_operations *ops;      文件操作集 ,给应用程序提供 API接口,应用程序通过系统调用,来访问file_operations中的接口函数。   struct file_operations { struct module *owner; ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_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); int (*mmap) (struct file *, struct vm_area_struct *); int (*open) (struct inode *, struct file *); int (*release) (struct inode *, struct file *); };   以上的接口函数,是应用程序中,文件 IO的系统调用接口。 open、read、write、lseek、ioctl、mmap、close     提示 1:      文件 IO函数原型中的参数,与文件操作集中对应函数的参数是否一致???       是一个系统调用过程,中间是经过 vfs和linux内核,所以参数是不一致的,与函数调用不同。   提示 2:     ssize_t是什么类型? typedef __kernel_ssize_t ssize_t;   #ifndef __kernel_size_t #if __BITS_PER_LONG != 64 typedef unsigned int __kernel_size_t; typedef int __kernel_ssize_t; typedef int __kernel_ptrdiff_t; #else typedef unsigned long __kernel_size_t; typedef long __kernel_ssize_t; typedef long __kernel_ptrdiff_t; #endif #endif   4) struct list_head list;      内核双向链表,向内核中注册一个字符设备时,链表增加一项   5) dev_t dev; 设备号   6)unsigned int count; 次设备号的数量     ----------------------------------------------------------------------------------------------------------- 2、设备号   1) 设备号 的定义 dev_t dev;   linux内核中,每一个设备驱动都有一个设备号,设备号有 主设备号和次设备号 组成 主设备号:描述设备驱动的具体应用类型 次设备号:描述该具体应用类型下的一个应用实例     例如: 4个uart的驱动 [root@YueQian /]# ls /dev/s3c2410_serial* -l crw-rw----    1 root     root      204,  64 Jan  2 11:21 /dev/s3c2410_serial0 crw-rw----    1 root     root      204,  65 Jan  2 11:21 /dev/s3c2410_serial1 crw-rw----    1 root     root      204,  66 Jan  2 11:21 /dev/s3c2410_serial2 crw-rw----    1 root     root      204,  67 Jan  2 11:21 /dev/s3c2410_serial3   例如: nand flash的5个分区 [root@YueQian /]# ls /dev/mtdblock* -l brw-rw----    1 root     root       31,   0 Jan  2 11:21 /dev/mtdblock0 brw-rw----    1 root     root       31,   1 Jan  2 11:21 /dev/mtdblock1 brw-rw----    1 root     root       31,   2 Jan  2 11:21 /dev/mtdblock2 brw-rw----    1 root     root       31,   3 Jan  2 11:21 /dev/mtdblock3 brw-rw----    1 root     root       31,   4 Jan  2 11:21 /dev/mtdblock4   typedef unsigned int __u32; typedef __u32 __kernel_dev_t; typedef __kernel_dev_t dev_t;   设备号 是一个 无符号 32位的整型值 ,其中 12bits是主设备号,低20bits是次设备号     ----------------------------------------------------------------------------------------------------------- 2)主次设备号之间的函数   #define MINORBITS 20 #define MINORMASK ((1U << MINORBITS) - 1)  //0x000 fffff   #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))  //MINORBITS = 20 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))     ----------------------------------------------------------------------------------------------------------- 3)如何得到一个设备号 我们设置一个设备驱动,需要从内核中,申请一个设备号。 1) 静态注册设备号 :我们来指定设备号,不要使用系统已经使用的设备号 /**  * 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 register_chrdev_region(dev_t from, unsigned count, const char *name)   参数说明:     dev_t from:注册的一个设备号的开始值。如果是gec210 uart,from=(204<<20 + 64)     unsigned count: 设备实例的数量,即次设备 号的数目。如果是 gec210 uart,count=4     const char *name:字符设备的名称。如果是gec210 uart,name=s3c2410_serial 返回值:      注册成功,返回 0      注册失败,返回一个负数的错误码     2) 动态分配设备号 :让系统自动分配空闲的设备号 /**  * 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.  */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)   参数说明:     dev_t *dev:动态分配后的设备号。     unsigned baseminor:我们使用的第一个次设备号,次设备号的开始值,如果是gec210 uart,baseminor=64     unsigned count: 设备实例的数量,即次设备 号的数目。如果是 gec210 uart,count=4     const char *name:字符设备的名称。如果是gec210 uart,name=s3c2410_serial 返回值:      注册成功,返回 0      注册失败,返回一个负数的错误码   =================================================================================== 三、字符设备驱动的设计思路   1、 定义一个字符设备 struct cdev chrdev_test; ------------------------------------------------------------------------------------   2、 定义一个文件操作集,并对文件操作集进行初始化 struct file_operations  chdev_fops = { .open = test_open, .read = test_read, .write = test_write, .ioctl = test_ioctl, .release = test_release, }; ------------------------------------------------------------------------------------   3、 字符设备的初始化 /**  * cdev_init() - initialize a cdev structure  * @cdev: the structure to initialize  * @fops: the file_operations for this device  *  * Initializes @cdev, remembering @fops, making it ready to add to the  * system with cdev_add().  */ void cdev_init(struct cdev *cdev, const struct file_operations *fops) 参数:     struct cdev *cdev:定义的字符设备     const struct file_operations *fops:已经完成定义和初始化的一个文件操作集 ------------------------------------------------------------------------------------   4、申请设备号         dev_t ndev;//设备号         int TestMajor = 0;//主设备号 int TestMinor = 0;//次设备号 char drv_name[]="chrtest1"; int ret;   if(TestMajor) { ndev=MKDEV(TestMajor,TestMinor);//由主次设备号,得到设备号 ret=register_chrdev_region(ndev,1,drv_name);//注册字符设备编号 } else { ret=alloc_chrdev_region(&ndev,TestMinor,1,drv_name);//动态分配一个字符设备号,&ndev存放返回的设备号 TestMajor=MAJOR(ndev); } if(ret<0) { printk(KERN_WARNING"cannot get major %d n",TestMajor); goto fail_chrdev_region; //出错处理 goto }   ------------------------------------------------------------------------------------ 5、 将字符设备加入到内核   /**  * 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.  */ int cdev_add(struct cdev *p, dev_t dev, unsigned count) 参数: struct cdev *p :定义并初始化的字符设备cdev dev_t dev:已经申请到的设备号 unsigned count:次设备的数量 返回值:      注册成功,返回 0      注册失败,返回一个负数的错误码     ------------------------------------------------------------------------------------ 6、 注销一个设备号 /**  * unregister_chrdev_region() - return a range of device numbers  * @from: the first in the range of numbers to unregister  * @count: the number of device numbers to unregister  *  * This function will unregister a range of @count device numbers,  * starting with @from.  The caller should normally be the one who  * allocated those numbers in the  */ void unregister_chrdev_region(dev_t from, unsigned count) 参数说明:     dev_t from:注册的一个设备号的开始值。如果是gec210 uart,from=(204<<20 + 64)     unsigned count: 设备实例的数量,即次设备 号的数目。如果是 gec210 uart,count=4   ------------------------------------------------------------------------------------ 7、 注销一个字符设备 /**  * cdev_del() - remove a cdev from the system  * @p: the cdev structure to be removed  *  * cdev_del() removes @p from the system, possibly freeing the structure  * itself.  */ void cdev_del(struct cdev *p)       =================================================================================== 四、用户空间与内核空间交互数据的函数   1、 将用户空间的数据拷贝到内核空间 static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)     2、 将内核空间的数据拷贝到用户空间 static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)   返回值:如果数据拷贝成功,返回是 0;如果 数据拷贝失败,返回是尚未拷贝的字节数。     提示 1: __must_check 必须检查该函数的返回值,如果不检查,会有警告。   提示 2: inline --内联函数   =================================================================================== 五、驱动程序的设计   demo1   =================================================================================== 六、驱动的调试 写一个应用程序,利用应用程序调试驱动程序。 1、安装驱动 [root@YueQian /test]# insmod demo1.ko   [24273.778642] [24273.779733] this is the first demo of driver,  inserting successful !     2、查看内核模块 [root@YueQian /test]# lsmod demo1 2589 0 - Live 0xbf000000   3、查看字符设备的主设备号和设备名称 char drv_name[ ]="chrtest"; int TestMajor = 0;//主设备号 int TestMinor = 0;//次设备号   [root@YueQian /test]# cat /proc/devices   Character devices: 250 chrtest   --->主设备号和设备名称   4、 手动创建设备文件 (后面讲如何自动创建设备文件) [root@YueQian /test]# mknod /dev/chr_test1 c 250 0   5、查看设备文件 [root@YueQian /test]# ls /dev/chr_test1 -l crw-r--r--    1 root     root      250,   0 Jan  2 18:14 /dev/chr_test1   6、执行应用程序     7、 驱动的卸载 [root@YueQian /]# rmmod demo1 [25293.152108] the driver is exiting       =================================================================================== 七、字符设备驱动的老方法 老的方法更简洁,更直观;新的方法比较灵活。   1、注册字符设备   static inline int register_chrdev(unsigned int major, const char *name,   const struct file_operations *fops) 参数说明:     unsigned int major  主设备号,如果major=0,系统会自动分配一个主设备号,如果major!=0,静态注册主设备号。     const char *name  设备名称     const struct file_operations *fops 文件操作集。   返回值:     1)如果unsigned int major 是大于0的值,静态注册字符设备: 注册成功,返回 0      注册失败,返回一个负数的错误码 2)如果unsigned int major 是等于0的值,动态注册字符设备 分配成功,返回分配后的主设备号      注册失败,返回一个负数的错误码   将设备号申请、字符设备的初始化、字符设备的注册使用一个函数实现。   2、注销字符设备 static inline void unregister_chrdev(unsigned int major, const char *name)                        

本文发布于:2024-02-05 07:59:49,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170727857164751.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:设备   linux
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23