网站建站专家建设网站首页应该采用
网站建站专家,建设网站首页应该采用,做淘宝要网站?,德州市建设工程质量监督站网站Linux驱动复习——驱动一、Linux下驱动的分类1. 字符设备驱动定义#xff1a;以字符为单位#xff0c;一个字节一个字节地读写操作设备特点#xff1a;以字符流的形式传输数据#xff0c;不带缓存常见设备#xff1a;鼠标、键盘、串口2. 块设备驱动定义#xff1a;以固定…Linux驱动复习——驱动一、Linux下驱动的分类1. 字符设备驱动定义以字符为单位一个字节一个字节地读写操作设备特点以字符流的形式传输数据不带缓存常见设备鼠标、键盘、串口2. 块设备驱动定义以固定块的大小为单位通常是512字节读写操作设备特点带缓存提高读写效率常见设备SD卡、磁盘、硬盘3. 网络设备驱动定义将网络数据包从物理层传输到网络层并将网络层的数据封装成物理层可以传输的形式特点需要集成多种网络协议及接口标准常见设备以太网卡二、字符设备驱动实现流程1. 驱动实现步骤构建cdev结构体用来描述字符型设备构建file_operations结构体用来描述驱动层操作函数集合通过module_init和module_exit指定驱动模块的入口函数和注销函数通过MODULE_LICENSE宏指定GNU组织规定的GPL协定实现驱动模块的入口函数实现驱动模块的注销函数完成file_operations结构体中各函数的具体实现2. 驱动模块入口函数实现static int __init driver_init(void) { // 1. 注册字符型设备两种方式 register_chrdev_region(dev, count, name); // 静态注册 // 或 alloc_chrdev_region(dev, baseminor, count, name); // 动态注册 // 2. 初始化cdev结构体 cdev_init(cdev, fops); // 3. 将cdev结构体添加到系统 cdev_add(cdev, dev, count); // 4. 创建类 class_create(THIS_MODULE, class_name); // 5. 在这个类下创建设备节点 device_create(class, NULL, dev, NULL, device_name); // 6. 完成对硬件的初始化 return 0; }3. 驱动模块注销函数实现static void __exit driver_exit(void) { // 1. 注销字符型设备 unregister_chrdev_region(dev, count); // 2. 从系统中删除cdev结构体 cdev_del(cdev); // 3. 删除类 class_destroy(class); // 4. 删除设备节点 device_destroy(class, dev); // 5. 完成对硬件的注销 }4. file_operations结构体实现static struct file_operations fops { .owner THIS_MODULE, .open open_function, // 打开设备 .read read_function, // 读取数据 .write write_function, // 写入数据 .release close_function, // 关闭设备 .unlocked_ioctl ioctl_function, // 控制命令 };三、驱动编译流程1. 编译步骤1. 在drivers/char下编写驱动程序如led.c 2. 修改Makefile文件 在drivers/char/Makefile中添加 obj-$(CONFIG_LED) led.o 3. 修改Kconfig文件增加驱动编译选项 在drivers/char/Kconfig中添加 config LED tristate LED Driver help This is a LED driver. 4. 回到内核源码顶层目录配置内核 make menuconfig 将对应的驱动选项选择为 # Y(*) - 静态编译进内核 # M - 编译成模块 # N - 不编译 # 5. 编译驱动 make # 编译整个内核包含静态编译的驱动 make modules # 只编译模块动态编译四、静态编译和动态编译的区别比较项静态编译动态编译编译方式直接编译进内核编译成.ko模块修改难度修改后需重新配置内核并编译修改后只需重新编译.ko模块开发调试比较复杂相对简单使用场景稳定、常用的驱动开发调试阶段的驱动五、设备号相关概念1. 主设备号和次设备号主设备号设备的类型次设备号该类设备下具体的某一个设备2. 设备号大小Linux下用32位的unsigned int类型表示设备号高12位代表主设备号低20位代表次设备号3. 设备号构建// 通过MKDEV宏传入主次设备号来构建一个新的设备号 dev_t dev MKDEV(major, minor);4. 杂项设备的主设备号杂项设备主设备号固定为105. 静态注册和动态注册的区别比较项静态注册动态注册函数register_chrdev_regionalloc_chrdev_region设备号来源自己定义主次设备号系统自动分配优点设备号固定不会冲突缺点可能和系统中设备号冲突设备号不固定六、字符设备驱动和杂项设备驱动的区别比较项字符设备驱动杂项设备驱动设备号需要自己定义或系统分配主设备号固定为10设备节点需要手动创建可以自动生成复杂度较高较低手动创建设备节点mknod /dev/xxx c 248 0 # /dev/xxx设备节点 # c设备类型字符型设备 # 248主设备号 # 0次设备号七、驱动模块加载1. 加载命令insmod xxx.ko # 加载驱动模块 modprobe xxx.ko # 加载驱动模块 rmmod xxx # 卸载驱动模块 lsmod # 查看已加载的模块2. insmod和modprobe区别命令特点insmod不会分析驱动模块之间的依赖关系modprobe会分析驱动模块之间的依赖关系自动加载依赖模块八、__init 和 __exit 作用1. __init 作用执行insmod加载驱动模块时会自动执行被__init修饰的函数执行完该函数后会自动回收内存空间2. __exit 作用执行rmmod卸载驱动模块时会执行被__exit修饰的函数执行完后会自动释放相关内存空间九、platform平台驱动1. platform思想目的实现设备和驱动分离方便更好地对驱动资源进行管理原理将device设备和driver驱动分离注册到platform总线bus上匹配方式通过name、设备树compatible、ID table进行匹配匹配成功后执行driver中的probe函数注销时执行remove函数2. platform匹配方式name匹配设备树compatible匹配ID table匹配3. platform驱动实现流程(1) 设备树描述device信息// 在设备树中增加设备节点 ptled { compatible pt-led; // 用于匹配 ptdht11-gpio gpio1 2 GPIO_ACTIVE_HIGH; status okay; };(2) 驱动实现// 1. 定义匹配表 static struct of_device_id led_table[] { {.compatible pt-led}, {} }; // 2. 定义platform_driver结构体 static struct platform_driver pdrv { .probe probe, // 匹配成功执行 .remove remove, // 注销时执行 .driver { .name led, .of_match_table led_table // 匹配表 } }; // 3. 注册platform驱动 static int __init led_driver_init(void) { return platform_driver_register(pdrv); } // 4. 注销platform驱动 static void __exit led_driver_exit(void) { platform_driver_unregister(pdrv); } // 5. probe函数实现 static int probe(struct platform_device * pdev) { // 获取设备树中的GPIO信息 led_gpio of_get_named_gpio(pdev-dev.of_node, ptdht11-gpio, 0); // 注册杂项设备/字符设备 misc_register(misc_dev); // 初始化硬件 gpio_request(led_gpio, led); gpio_direction_output(led_gpio, 1); return 0; }十、I2C子系统驱动框架1. I2C子系统层次结构用户层调用驱动层接口操作I2C设备 ↓ I2C设备驱动层自己实现的I2C设备和驱动 ↓ I2C核心层(core)提供I2C设备和驱动的注册、匹配、通信的方法 ↓ I2C适配器驱动层I2C控制器驱动一般由芯片厂家实现 ↓ I2C物理硬件层具体I2C硬件设备如LM75传感器2. 各层功能层次功能物理硬件层具体的I2C硬件设备适配器驱动层I2C控制器驱动由芯片厂家实现核心层管理设备和驱动的注册、匹配、通信方法设备驱动层自己实现的I2C设备驱动用户层调用驱动接口操作I2C设备3. 工作流程I2C传感器挂载在I2C总线上I2C适配器驱动层由芯片厂家实现I2C核心层管理设备和驱动的注册、匹配、通信设备驱动层将I2C总线上设备和驱动匹配匹配成功后执行probe函数注销时执行remove函数应用层调用驱动接口实现I2C数据读写十一、GPIO子系统相关操作函数函数功能gpio_request()申请一个GPIO引脚gpio_free()释放GPIO引脚gpio_direction_output()设置GPIO为输出模式gpio_direction_input()设置GPIO为输入模式gpio_set_value()设置GPIO引脚电平gpio_get_value()获取GPIO引脚电平使用示例// 1. 申请GPIO ret gpio_request(led_gpio, led); // 2. 设置方向 gpio_direction_output(led_gpio, 1); // 输出高电平 // 3. 控制电平 gpio_set_value(led_gpio, 0); // 输出低电平 // 4. 读取电平 int val gpio_get_value(key_gpio); // 5. 释放GPIO gpio_free(led_gpio);十二、中断实现1. 中断处理的两部分中断顶半部上半部中断服务函数特点处理速度要快不能加延时任务接收中断调度底半部处理中断底半部下半部处理费时的任务三种实现方式方式上下文能否延时说明软中断中断上下文不能加延时基于中断上下文tasklet中断上下文不能加延时基于软中断实现工作队列进程上下文可以加延时基于进程上下文2. 中断实现示例(1) tasklet方式// 定义tasklet结构体 static struct tasklet_struct tsk; // tasklet处理函数 static void key_tasklet_handler(unsigned long arg) { // 处理费时任务但不能延时 condition 1; wake_up_interruptible(wq); } // 中断顶半部 static irqreturn_t key_irq_handler(int irq, void * dev) { // 调度tasklet执行底半部 tasklet_schedule(tsk); return IRQ_HANDLED; } // 初始化tasklet tasklet_init(tsk, key_tasklet_handler, 100);(2) 工作队列方式// 定义工作结构体 static struct work_struct work; // 工作处理函数 static void key_work_func(struct work_struct *work) { // 处理费时任务可以延时 ssleep(1); // 可以休眠 condition 1; wake_up_interruptible(wq); } // 中断顶半部 static irqreturn_t key_irq_handler(int irq, void * dev) { // 调度工作队列执行底半部 schedule_work(work); return IRQ_HANDLED; } // 初始化工作队列 INIT_WORK(work, key_work_func);十三、同步机制互斥锁、自旋锁、读写锁1. 互斥锁特点加锁解锁过程中CPU可以切换调度任务适用场景长时间资源占用时使用2. 自旋锁特点加锁过程中CPU处于忙等待不会进行任务调度和切换适用场景短时间资源占用时使用3. 读写锁特点一把锁分为两部分读锁和写锁操作效果加读锁多个线程同时读不会阻塞读锁共享加写锁效果和互斥锁相同只允许一个线程写写锁独占应用场景多个线程操作同一个临界资源时读操作多于写操作时读写锁效率高于互斥锁十四、驱动整体框架总结1. 字符设备驱动完整示例#include linux/init.h #include linux/module.h #include linux/fs.h #include linux/cdev.h #include linux/device.h #define DEV_NAME demo static int major; static struct cdev cdev; static struct class *cls; // 文件操作函数 static int open(struct inode *inode, struct file *file) { printk(device open\n); return 0; } static ssize_t read(struct file *file, char __user *buf, size_t size, loff_t *off) { return 0; } static struct file_operations fops { .owner THIS_MODULE, .open open, .read read, }; // 模块入口 static int __init demo_init(void) { dev_t dev; // 1. 分配设备号 alloc_chrdev_region(dev, 0, 1, DEV_NAME); major MAJOR(dev); // 2. 初始化cdev cdev_init(cdev, fops); // 3. 添加cdev到系统 cdev_add(cdev, dev, 1); // 4. 创建类 cls class_create(THIS_MODULE, demo_class); // 5. 创建设备节点 device_create(cls, NULL, dev, NULL, DEV_NAME); printk(demo init success, major%d\n, major); return 0; } // 模块出口 static void __exit demo_exit(void) { dev_t dev MKDEV(major, 0); device_destroy(cls, dev); class_destroy(cls); cdev_del(cdev); unregister_chrdev_region(dev, 1); printk(demo exit\n); } module_init(demo_init); module_exit(demo_exit); MODULE_LICENSE(GPL);2. 杂项设备驱动简化版#include linux/miscdevice.h static struct file_operations fops { .owner THIS_MODULE, .open open, .read read, }; static struct miscdevice misc_dev { .minor MISC_DYNAMIC_MINOR, // 动态分配次设备号 .name demo, // 设备名 .fops fops // 文件操作 }; static int __init demo_init(void) { return misc_register(misc_dev); } static void __exit demo_exit(void) { misc_deregister(misc_dev); }