linux设备驱动的学习之一
由于项目上要用到,于是乎我要学习linux设备驱动的编写,开始的时候还比较清楚,能够对简单的GPIO控制操作实现出来,但是项目上要用到的是SPI和GPIO的输入中断来读取AD的电压值,然后就陷入到了一个庞大的设备代码阅读中去了,尤其是platform device的学习,到现在都还没有理清其中的关系,虽然搜索了很多网上的文章,但庆幸的是我有一种比着框框买鸭蛋的精神,我想要比着这些源码画一个出来。以前没有在LPC1768上使用过SPI,导致对SPI是一个完全陌生的状态,不清楚他的传输方式,这也是学习中的一个问题,也是一开始我的盲目无方向感的原因,因为这里的linux SPI设备驱动和SPI协议就是两个要学习的问题。
先来把“简单”的中断实现出来吧,其实中断处理并不简单,他是很多项目中必须要用到的东西,这里使用的S5PV210的GPH3(2)这一个GPIO来实现,查看芯片手册其对应的外部中断号为EINT26,所以在驱动中定义一个结构体来描述他如下:
struct s5pv210_gpio_key{ int pin;//引脚号 int eint;//外部中断号 int eintcfg;//外部中断使能 int inputcfg;//输入使能 }; struct s5pv210_gpio_key my_gpio_key={ .pin = S5PV210_GPH3(2), .eintcfg = 0X0f<<4, .inputcfg = 0<<4, .eint = IRQ_EINT(26), };
然后构建一个驱动的框架代码:
static int __init gpio_interrupt_init(void) { int err=0; gpio_int_num = MKDEV(MY_MAJOR,MY_MINOR); err = register_chrdev_region(gpio_int_num, 1, "GPH3_2_interrupt"); if(err < 0) { printk("register error, num: %d have been used!\n", gpio_int_num); return err; } cdev_init(&my_cdev , &gpio_interrupt_ops); my_cdev.owner = THIS_MODULE; err = cdev_add(&my_cdev, gpio_int_num, 1); if(err < 0) { printk("add my_cdev error!\n"); return err; } printk("init ok\n"); return 0; } static void __exit gpio_interrupt_exit(void) { cdev_del(&my_cdev); unregister_chrdev_region(gpio_int_num, 1); printk("exit ok\n"); } module_init(gpio_interrupt_init); module_exit(gpio_interrupt_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("galuo");
在加载模块的函数中,register_chrdev_region注册了一个设备号,在加载了该模块后,使用mknod命令来创建设备文件节点;然后使用cdev_init来初始化字符设备结构体变量cdev,将file_operations结构体gpio_interrupt_ops加载进来,就可以在用户空间使用的时候明确调用的关系;使用cdev_add向内核注册字符I/O设备。字符模块的退出函数使用cdev_del函数来删除内核中的字符设备;使用unregister_chrdev_region来释放设备号。
定义file_operations结构体实例gpio_interrupt_ops,在用户空间操作的时候实现调用对应的功能,其定义如下:
struct file_operations gpio_interrupt_ops={ .open=gpio_interrupt_open, .release=gpio_interrupt_close, };
.open实现中断的请求,并且使能中断
.release实现中断的释放
int gpio_interrupt_open(struct inode *inode,struct file *file) { int error; error = request_irq(my_gpio_key.eint, //中断号 gpio_keys_isr,//中断处理函数 IRQF_TRIGGER_FALLING ,//下降沿触发 "interrupt_test", NULL); if(error){ printk("request irq failed!\n"); return -1; } printk("hello irq\n"); return 0; } int gpio_interrupt_close(struct inode *inode,struct file *file) { free_irq(my_gpio_key.eint, NULL); printk("good bye irq\n"); return 0; }
在中断处理函数gpio_keys_isr中就可以实现中断的需求:
static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { printk("this is interrupt function\n"); return IRQ_HANDLED; }
这里中断处理函数的定义类型和返回值固定不变。