尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

linux驱动学习(五)之字符设备

linux驱动学习(五)之字符设备 一、 linux设备驱动分类1、字符设备---char应用程序与驱动程序在进行数据传输时数据以字节为单位。特点[1] 按照顺序进行数据传输[2] 数据传输是实时的并没有缓存[3] 字符设备是没有文件系统的1)驱动程序的作用是应用程序访问底层硬件设备的一座桥梁2)应用程序如何去访问驱动程序系统调用应用程序系统IOopen、read、write、ioctl、close驱动程序: 文件操作集--- struct file_operations3)驱动程序如何去访问底层硬件 --- MMU驱动程序中的地址为虚拟地址 ---- MMU(io内存映射) ----底层硬件的物理地址虚拟地址-----是通过在驱动程序中来申请的虚拟地址4)什么设备叫作字符设备呢大多数的设备都是字符设备,比如led, lcd, beep, 按键, SPI等2、块设备---block应用程序与驱动程序在进行数据传输时数据以块为单位,1block 1024kb。特点[1]有缓存[2]有文件系统,比如:NTFS ,ext4大容量设备都是块设备U盘SD卡EMMC--电子硬盘[rootGEC6818 /mnt]#cat /proc/partitions major minor #blocks name 179 0 7634944 mmcblk0 ----电子硬盘 179 1 65536 mmcblk0p1 ----- 电子硬盘第一个分区 179 2 772096 mmcblk0p2 179 3 438272 mmcblk0p3 179 4 1 mmcblk0p4 179 5 8192 mmcblk0p5 179 6 22528 mmcblk0p6 179 7 6324224 mmcblk0p7 //u盘 [rootGEC6818 /dev]#ls -lh sda* brw-rw-rw- 1 root root 8, 0 Jan 1 00:30 sda --- 占1G brw-rw-rw- 1 root root 8, 1 Jan 1 00:30 sda1 ---- 31G应用程序如何访问块设备第一步挂载U盘到根文件系统上[rootGEC6818 /]#mount -t vfat /dev/sda1 /udata第二步像访问普通文件一样来访问块设备的内容标准的IO函数3、网络设备----socket网卡类的设备有线网卡、无线网卡 ...另外网络设备是没有设备文件的应用程序socket套接字根据ip地址 端口号二、字符设备驱动设计流程以led灯为例1、定义并初始一个字符设备(struct cdev)2、根据应用程序的API来定义并且初始化字符设备的文件操作集(struct file_operations).3、给字符设备申请一个设备号 -- 主设备号20 次设备号.4、初始化字符设备.5、把当前的字符设备加入到内核.6、创建class.7、创建设备(device)。其中device属于class应用程序可以通过设备文件来访问驱动程序.8、申请物理内存区申请SFR(Special Function Register特殊功能寄存器)地址区比如GPIOEOUTGPIOEOUTENB9、内存的动态映射得到物理地址的虚拟地址.10、在驱动程序中访问虚拟地址.具体实现如下1定义一个字符设备(struct 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; }; 关于struct cdev结构体的说明 1)struct kobject kobj; ----内核管理驱动时使用的一个object对象内核自己使用跟驱动程序设计者无关 2)struct module *owner; ---- 模块的拥有者当前驱动设计模块是属于谁的固定写法THIS_MODULE 3)const struct file_operations *ops; --- cdev的文件操作集一般由用户或者设计者来实现 static struct file_operations gec6818_led_ops; 4)struct list_head list; ---管理字符设备的链表由内核本身去负责 5)dev_t dev; ----设备号 6)unsigned int count; ----字符设备的次设备号的数目在linux内核中使用cdev来描述字符设备每个字符设备都有一个自己的cdev比如:static struct cdev gec6818_led;2) 定义一个字符设备的文件操作集(struct file_operations)#include linux/fs.h 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 *); 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 *, loff_t, loff_t, int datasync); int (*aio_fsync) (struct kiocb *, int datasync); int (*fasync) (int, struct file *, int); int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long); int (*check_flags)(int); int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease)(struct file *, long, struct file_lock **); long (*fallocate)(struct file *file, int mode, loff_t offset, loff_t len); }; 根据文件操作集中的函数指针定义相关类型的函数函数的格式是固定 int gec6818_led_release(struct inode *node, struct file *file) { printk(gec6818_led_release\n); return 0; } static struct file_operations gec6818_led_ops { .owner THIS_MODULE, .open gec6818_led_open, .write gec6818_led_write, .release gec6818_led_release,//当应用程序调用close时执行驱动程序中的接口函数 };3) 给设备申请一个设备号---dev_t1、什么是设备号每个设备文件(字符设备or块设备)都有一个设备号相当于设备文件的ID.设备号由主设备和次设备号组成.dev_t dev; --- 字符设备的设备号 /* typedef __kernel_dev_t dev_t; typedef __u32 __kernel_dev_t; ----无符号整型数字 */2、设备号的运算函数#define MKDEV(ma,mi) (((ma) MINORBITS) | (mi)) ---- 设备号 dev MKDEV(ma,mi); //根据主设备号与次设备号得到设备号3、得到主设备号与次设备号#define MINORBITS 20 #define MINORMASK ((1U MINORBITS) - 1) #define MAJOR(dev) ((unsigned int) ((dev) MINORBITS)) ---- 主设备号 #define MINOR(dev) ((unsigned int) ((dev) MINORMASK)) ---- 次设备号 unsigned int major MAJOR(dev); //根据设备号得到主设备号 unsigned int minor MINOR(dev); //根据设备号得到次设备号4、把得到的设备号注册到内核中一) 静态注册由用户先确定一个设备号通过注册函数去申请int register_chrdev_region(dev_t from, unsigned count, const char *name) 参数说明 dev_t from ---- 要申请的设备号 unsigned count --- 次设备号的数目 const char *name ---- 设备名字 ---字符串 返回值 成功0 失败: 0 比如register_chrdev_region(108,4,ttyGS);二) 动态注册由内核动态分配一个设备号给现有设备int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) 参数说明 dev_t *dev ---做为输出参数通过alloc_chrdev_region得到的设备号给dev unsigned baseminor --第一个次设备号 unsigned count --次设备号数目 const char *name ---- 设备名字 ---字符串 返回值 成功0 失败: 05、设备号的作用主设备号描述一个硬件是属于哪一类的设备比如uart,lcd,摄像头次设备号描述某种类型的设备中具体的哪一个设备比如uart0,uart1crw-rw---- 1 root root 247, 0 Jan 1 1970 ttyGS0 crw-rw---- 1 root root 247, 1 Jan 1 1970 ttyGS1 crw-rw---- 1 root root 247, 2 Jan 1 1970 ttyGS2 crw-rw---- 1 root root 247, 3 Jan 1 1970 ttyGS3 247 ----主设备号 -----表示串口 0 -----次设备号 ----- 串口1 ttyGS ----- 表示该硬件的设备文件如果该类型的设备有多个由从0开始由系统自动加16、释放设备号给系统void unregister_chrdev_region(dev_t from, unsigned count) 参数说明: dev_t from ---- 要申请的设备号 unsigned count --次设备号数目4) 初始化字符设备#include linux/cdev.h void cdev_init(struct cdev *cdev, const struct file_operations *fops) 参数说明 struct cdev *cdev ----- 字符设备的结构 const struct file_operations *fops ---- 字符设备对应的文件操作集5) 把设备加入到内核中#include linux/cdev.h //把字符设备增加到系统 int cdev_add(struct cdev *p, dev_t dev, unsigned count) 参数说明 struct cdev *cdev ----- 字符设备的结构 dev_t dev ----- 字符设备的设备号 unsigned count ---- 次设备号数目 失败返回一个负错误码 //从系统中把字符设备删除 void cdev_del(struct cdev *p)6) 创建class一个设备会有一个class[rootGEC6818 /sys/class]#ls ----查看设备的class android_usb graphics leds pvrusb2 spi_master arvo hwmon lirc pyra spidev axppower i2c-adapter mdio_bus rc switch backlight i2c-dev mem regulator timed_output //创建class #include linux/device.h struct class *class_create(struct module *owner, const char *name) 参数说明 struct module *owner ---- 模块的拥有者 const char *name ----- class的名字 ---- /sys/class---查看 返回值 成功struct class pointer ----class的地址 失败NULL //销毁class void class_destroy(struct class *cls)7) 创建device//创建一个device #include linux/device.h struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...) 参数说明 struct class *class ---- 说明device是属于哪 一个classclass_create返回值 struct device *parent ---- 该设备的父亲一般设置为NULL dev_t devt ----- 增加到系统中的字符设备的设备号 void *drvdata ---- 增加一个device时返回的数值如果不需要返回则使用NULL const char *fmt ---- 该设备的名字 返回值 成功struct device * 失败NULL //销毁device void device_destroy(struct class *class, dev_t devt)8) 申请物理内存区申请SFR地址区//申请物理内存区 struct resource *request_mem_region(resource_size_t start, resource_size_t n,const char *name) 参数说明 resource_size_t start ----- 虚拟内存的起始地址 resource_size_t n ---- 申请虚拟内存的大小字节 const char *name ---- 虚拟内存对应的名字 返回值 成功struct resource * 失败NULL //释放物理内存区 void release_mem_region(resource_size_t start, resource_size_t n)9) IO动态映射//IO动态映射 ---- 根据物理地址得到虚拟的起始地址 static inline void __iomem *ioremap(phys_addr_t offset, unsigned long size) 参数说明 phys_addr_t offset ---- 物理地址的起始地址 unsigned long size ----- IO映射的大小单位字节 //释放IO映射 static inline void iounmap(void __iomem *addr)10) 测试1 插入和删除驱动 [rootGEC6818 /6818_driver]#insmod led_drv.ko [ 173.611000] gec6818_led_init [rootGEC6818 /6818_driver]#rmmod led_drv.ko [ 220.248000] gec6818led_exit 2 查看/dev下设备文件、主设备号、次设备 [rootGEC6818 /dev]#ls -l gec6818_led_device crw-rw---- 1 root root 100, 0 Jan 1 02:46 gec6818_led_device //提供给应用程序来访问驱动程序 3 查看class [rootGEC6818 /sys/class]#ls -l gec6818_led_class/ total 0 lrwxrwxrwx 1 root root 0 Jan 1 02:51 gec6818_led_devicedemo下载led点击跳转beep点击跳转觉得有帮助的话打赏一下呗。。
返回列表