字符设备驱动(1)驱动代码完整源码:charButtons.c
2025-01-09 来源:cnblogs
内核版本:Linux3.0.8
开发板:基于三星S5PV210处理器的Tiny210开发板
驱动名称:charButtons.c
驱动描述:按键触发中断,中断处理程序执行相应的简单LED点亮操作
方案1注册字符设备使用新的接口实现(需要好几个函数来实现。貌似更复杂)
方案2注册字符设备使用老的接口实现(貌似老接口更简单)
/*****************************************************************************
简 述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
******************************************************************************/
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME 'buttons' struct button_desc { int gpio; int number; char *name; }; struct led_desc { int gpio; int number; char *name; }; static struct button_desc buttons[] = { { S5PV210_GPH2(0), 0, 'KEY0' }, { S5PV210_GPH2(1), 1, 'KEY1' }, { S5PV210_GPH2(2), 2, 'KEY2' }, { S5PV210_GPH2(3), 3, 'KEY3' }, { S5PV210_GPH3(0), 4, 'KEY4' }, { S5PV210_GPH3(1), 5, 'KEY5' }, { S5PV210_GPH3(2), 6, 'KEY6' }, { S5PV210_GPH3(3), 7, 'KEY7' }, }; static struct led_desc leds[] = { {S5PV210_GPJ2(0),1,'LED1'}, {S5PV210_GPJ2(1),2,'LED2'}, {S5PV210_GPJ2(2),3,'LED3'}, {S5PV210_GPJ2(3),4,'LED4'}, }; #define OK (0) #define ERROR (-1) struct gpio_chip *chip; struct cdev *gDev; struct file_operations *gFile; dev_t devNum; unsigned int subDevNum = 1;//要申请的次设备号个数 int reg_major = 234; int reg_minor = 0; static irqreturn_t button_interrupt(int irq, void *dev_id) { struct button_desc *bdata = (struct button_desc *)dev_id; int down; unsigned tmp; tmp = gpio_get_value(bdata->gpio); /* active low */ down = !tmp; printk('KEY %d: %08xn', bdata->number, down); if(bdata->number < 4) { gpio_set_value(leds[bdata->number].gpio,0); printk('LED %d: On n',leds[bdata->number].number); } else { gpio_set_value(leds[(bdata->number) - 4].gpio,1); printk('LED %d: OFF n',leds[(bdata->number)-4].number); } return IRQ_HANDLED; } int butsOpen(struct inode *p, struct file *f) { int irq; int i; int err = 0; printk(KERN_EMERG'butsOpenrn'); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); // irq = IRQ_EINT(16)+i =160+i 'S5PV210_GPH2(i)' //irq = IRQ_EINT(24)+i =168+i 'S5PV210_GPH3(i)' err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_BOTH, buttons[i].name, (void *)&buttons[i]); if (err) break; } for(i = 0; i if(!leds[i].gpio) continue; gpio_direction_output(leds[i].gpio,1); } if (err) { i--; for (; i >= 0; i--) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return -EBUSY; } return 0; } int charDrvInit(void) { devNum = MKDEV(reg_major, reg_minor); printk(KERN_EMERG'devNum is %drn', devNum); if(OK == register_chrdev_region(devNum, subDevNum, DEVICE_NAME)) { printk(KERN_EMERG'register_chrdev_region okrn'); } else { printk(KERN_EMERG'register_chrdev_region errorrn'); return ERROR; } /*if(OK == alloc_chrdev_region(&devNum, subDevNum, subDevNum,'test')) { printk(KERN_EMERG'register_chrdev_region okrn'); } else { printk(KERN_EMERG'register_chrdev_region errorrn'); return ERROR; }*/ gDev = kzalloc(sizeof(struct cdev), GFP_KERNEL); gFile = kzalloc(sizeof(struct file_operations), GFP_KERNEL); gFile->open = butsOpen; //注册设备函数到file_operations结构体gFile //gDev->owner = THIS_MODULE; gFile->owner = THIS_MODULE; cdev_init(gDev, gFile); //在cdev结构体中添加指针指向file_operations结构体gFile cdev_add(gDev, devNum, 3); //建立设备号与cdev结构体联系 printk(KERN_EMERG'button driver initial done...rn'); return 0; } void __exit charDrvExit(void) { int i,irq; cdev_del(gDev); unregister_chrdev_region(devNum, subDevNum); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return; } module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始 module_exit(charDrvExit);//执行rmmod时,结束 MODULE_LICENSE('GPL'); /***************************************************************************** 简 述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点 ******************************************************************************/ #include #include #include #include #include #include #include #define DEVICE_NAME 'leds' struct button_desc { int gpio; int number; char *name; }; struct led_desc { int gpio; int number; char *name; }; static struct button_desc buttons[] = { { S5PV210_GPH2(0), 0, 'KEY0' }, { S5PV210_GPH2(1), 1, 'KEY1' }, { S5PV210_GPH2(2), 2, 'KEY2' }, { S5PV210_GPH2(3), 3, 'KEY3' }, { S5PV210_GPH3(0), 4, 'KEY4' }, { S5PV210_GPH3(1), 5, 'KEY5' }, { S5PV210_GPH3(2), 6, 'KEY6' }, { S5PV210_GPH3(3), 7, 'KEY7' }, }; static struct led_desc leds[] = { {S5PV210_GPJ2(0),1,'LED1'}, {S5PV210_GPJ2(1),2,'LED2'}, {S5PV210_GPJ2(2),3,'LED3'}, {S5PV210_GPJ2(3),4,'LED4'}, }; #define OK (0) #define ERROR (-1) dev_t devNum; unsigned int subDevNum = 1;//要申请的次设备号个数 int reg_major = 234; int reg_minor = 0; static irqreturn_t button_interrupt(int irq, void *dev_id) { struct button_desc *bdata = (struct button_desc *)dev_id; int down; unsigned tmp; tmp = gpio_get_value(bdata->gpio); /* active low */ down = !tmp; printk('KEY %d: %08xn', bdata->number, down); if(bdata->number < 4) { gpio_set_value(leds[bdata->number].gpio,0); printk('LED %d: On n',leds[bdata->number].number); } else { gpio_set_value(leds[(bdata->number) - 4].gpio,1); printk('LED %d: OFF n',leds[(bdata->number)-4].number); } return IRQ_HANDLED; } int butsOpen(struct inode *p, struct file *f) { int irq; int i; int err = 0; printk(KERN_EMERG'butsOpenrn'); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); // irq = IRQ_EINT(16)+i =160+i 'S5PV210_GPH2(i)' //irq = IRQ_EINT(24)+i =168+i 'S5PV210_GPH3(i)' err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, buttons[i].name, (void *)&buttons[i]); if (err) break; } for(i = 0; i if(!leds[i].gpio) continue; gpio_direction_output(leds[i].gpio,1); } if (err) { i--; for (; i >= 0; i--) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return -EBUSY; } return 0; } static const struct file_operations gFile = { .owner = THIS_MODULE, .open = butsOpen, }; int charDrvInit(void) { devNum = MKDEV(reg_major, reg_minor); printk(KERN_EMERG'devNum is %drn', devNum); if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile)) { printk(KERN_EMERG 'register_chrdev_region okrn'); } else { printk(KERN_EMERG'register_chrdev_region errorrn'); return ERROR; } printk(KERN_EMERG 'button driver initial done...rn'); return 0; } void __exit charDrvExit(void) { int i,irq; unregister_chrdev(reg_major, DEVICE_NAME); for (i = 0; i < ARRAY_SIZE(buttons); i++) { if (!buttons[i].gpio) continue; irq = gpio_to_irq(buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&buttons[i]); } return; } module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始 module_exit(charDrvExit);//执行rmmod时,结束 MODULE_LICENSE('GPL'); MODULE_AUTHOR('LiuB'); 函数修饰符 __init,本质上是一个宏定义,在内核源代码中定义:#define __init __section(.init.text) __cold notrace 作用就是,将被它修饰的函数放入.init.text段中去。所以所有的内核模块的__init修饰的函数被放在一起。内核启动时统一加载,加载完后统一释放以节省内存。 __exit,同上:#define __exit __section(.exit.text) __exitused __cold notrace printk()函数:内核封装出来的打印函数。格式: printk( 打印级别 “打印信息”) //printk的打印级别是用来控制printk打印的信息是否在终端显示 #define KERN_EMERG '<0>' /* system is unusable */ #define KERN_ALERT '<1>' /* action must be taken immediately */ #define KERN_CRIT '<2>' /* critical conditions */ #define KERN_ERR '<3>' /* error conditions */ #define KERN_WARNING '<4>' /* warning conditions */ #define KERN_NOTICE '<5>' /* normal but significant condition */ #define KERN_INFO '<6>' /* informational */ #define KERN_DEBUG '<7>' /* debug-level messages */ /* Use the default kernel loglevel */ #define KERN_DEFAULT ' 如果定义了模块,即这一驱动被动态编译为模块时 驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h) /* Each module must use one module_init(). */ #define module_init(initfn) static inline initcall_t __inittest(void) { return initfn; } int init_module(void) __attribute__((alias(#initfn))); typedef int (*initcall_t)(void); //static inline initcall_t __inittest(void) { return initfn; } //inittest这个函数返回值为一个initcall_t类型的initfn //而initcall_t是一个函数指针,返回值为整型,输入参数为空 /* This is only required if you want to be unloadable. */ #define module_exit(exitfn) static inline exitcall_t __exittest(void) { return exitfn; } void cleanup_module(void) __attribute__((alias(#exitfn))); typedef void (*exitcall_t)(void); 如果未定义模块,这一驱动被静态编译进内核 驱动代码中的module_init、module_exit的内核定义(/include/linux/init.h) /** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */ #define module_init(x) __initcall(x); #define __initcall(fn) device_initcall(fn) #define device_initcall(fn) __define_initcall('6',fn,6) /* initcalls are now grouped by functionality into separate * subsections. Ordering inside the subsections is determined * by link order. * For backwards compatibility, initcall() puts the call in * the device init subsection. * * The `id' arg to __define_initcall() is needed so that multiple initcalls * can point at the same handler without causing duplicate-symbol build errors. */ #define __define_initcall(level,fn,id) static initcall_t __initcall_##fn##id __used __attribute__((__section__('.initcall' level '.init'))) = fn 通过这些段代码,我们能够看出最终的结果是将我们使用module_init修饰的函数指针链接到一个叫.initcall的段里,也就是说最终所有使用module_init修饰的函数指针都被链接在这个段里,内核在启动的时候顺序调用所有链接在这个段里的函数,实现设备的初始化
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析
- 蓝牙信道探测技术原理与开发套件实践
- 意法半导体中国本地造STM32微控制器启动规模量产




