首先看一张韦东山老师给出的系统图:
在驱动开发过程中,首先是编写驱动代码。对于字符设备驱动的框架,可以归纳如下:
1,写出具体驱动函数,如 led_open(),led_read(),led_write()等
如
static int first_drv_open(struct inode *inode, struct file *file)
{
//printk("first_drv_open\n");
/* 配置GPF4,5,6为输出 */
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
return 0;
}
2、定义一个file_operation()函数,它指向具体的驱动函数,为下一步注册到内核提供参数。
如:static struct file_operationsfirst_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = first_drv_open,
.write =first_drv_write,
};
3、编写入口函数,在入口函数中利用 register_chrdev()将驱动注册到内核。
static int first_drv_init(void)
{
major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核
firstdrv_class = class_create(THIS_MODULE, "firstdrv");
firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */
gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
gpfdat = gpfcon + 1;
return 0;
}
4、编写 module_init(first_drv_init); 在加载内核是调用该函数。相应的有出口函数和卸载模块的函数。
static void first_drv_exit(void)
{
unregister_chrdev(major, "first_drv"); // 卸载
class_device_unregister(firstdrv_class_dev);
class_destroy(firstdrv_class);
iounmap(gpfcon);
}
module_exit(first_drv_exit);
5、其他信息 主要是
MODULE_LICENSE("GPL");
驱动程序完成,现在分析应用程序是如何调用的
在 APP中 有open("/dev/XXX")其中dev为设备属性 字符设备为C,XXX为设备号,我们在编写驱动程序时,在编写register()函数时,有个参数为设备号major = register_chrdev(0, "first_drv", &first_drv_fops); ,该设备号就是为了APP认识它为做准备的。驱动在注册到内核之后,在内核中会生成一个数组,数组里面为各个设备号以及其对应的驱动操作的调用函数。通过这个 数组,就将APP层与驱动层联系起来了。