首页 > 代码库 > linux设备驱动的学习之一

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;}

这里中断处理函数的定义类型和返回值固定不变。

linux设备驱动的学习之一