- 实现的框架:
- 新的驱动程序有3个ko(leddrv、chip_demo_gpio、board_A_led)
- makefile写法:
- 之前的驱动程序将三者编译成一个ko
- 加载模块
- chip_demo_gpio模块依赖于leddrv,所以需先加载leddrv模块
- 要修改引脚(增删改)只需修改board_A_led中的资源,然后重新编译、加载board_A_led模块即可;就不同编译加载所有的模块了
- 最终实现如下:
- board_A_led.c(板级相关的代码)
```c
static void led_dev_release(struct device dev)
{
/ release函数必须提供,否则在调用 platform_device_unregister 时会出现警告 /
}
/ 资源结构体数组 */
static struct resource resources[] = {
}; / platform_device结构体 / static struct platform_device board_A_led_dev = {{
.start = GROUP_PIN(3,1),
.flags = IORESOURCE_IRQ,
.name = "100ask_led_pin",
},
{
.start = GROUP_PIN(5,8), /* 表示GPIO组和引脚 */
.flags = IORESOURCE_IRQ, /* 没有GPIO类型,用IRQ类型当作GPIO */
.name = "100ask_led_pin", /* 名字 */
},
}; / 该模块的入口函数, 因为单独编译成ko文件,所以需要提供入口和出口函数 / static int __init led_dev_init(void) { int err;.name = "100ask_led",
.num_resources = ARRAY_SIZE(resources), /* 计算有多少个资源 */
.resource = resources, /* resource等于资源数组 */
.dev = {
.release = led_dev_release, /* 提供release函数,在unregister时被调用 */
},
err = platform_device_register(&board_A_led_dev); / 注册platform_device结构体 / return 0; }
static void __exit led_dev_exit(void) { platform_device_unregister(&board_A_led_dev); / 注销platform_device结构体 / }
module_init(led_dev_init); module_exit(led_dev_exit); MODULE_LICENSE(“GPL”);
- chip_demo_gpio.c中
```c
static int g_ledpins[100];
static int g_ledcnt = 0;
static int board_demo_led_init (int which) /* 初始化LED, which-哪个LED */
{
printk("init gpio: group %d, pin %d\n", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
switch(GROUP(g_ledpins[which]))
{
case 0:
{
printk("init pin of group 0 ...\n");
break;
}
case 1:
{
printk("init pin of group 1 ...\n");
break;
}
case 2:
{
printk("init pin of group 2 ...\n");
break;
}
case 3:
{
printk("init pin of group 3 ...\n");
break;
}
}
return 0;
}
static int board_demo_led_ctl (int which, char status) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
printk("set led %s: group %d, pin %d\n", status ? "on" : "off", GROUP(g_ledpins[which]), PIN(g_ledpins[which]));
switch(GROUP(g_ledpins[which]))
{
case 0:
{
printk("set pin of group 0 ...\n");
break;
}
case 1:
{
printk("set pin of group 1 ...\n");
break;
}
case 2:
{
printk("set pin of group 2 ...\n");
break;
}
case 3:
{
printk("set pin of group 3 ...\n");
break;
}
}
return 0;
}
/* 根据platform_device进行初始化 */
static int chip_demo_gpio_probe(struct platform_device *pdev)
{
struct resource *res;
int i = 0;
while (1)
{
res = platform_get_resource(pdev, IORESOURCE_IRQ, i++); /* 获取pdev设备中IORESOURCE_IRQ类型的第i个资源 */
if (!res)
break;
g_ledpins[g_ledcnt] = res->start; /* 将资源的起始地址赋给引脚数组 */
led_class_create_device(g_ledcnt); /* 根据次设备号调用leddrv提供的led_class_create_device */
g_ledcnt++;
}
return 0;
}
/* 移除platform_device */
static int chip_demo_gpio_remove(struct platform_device *pdev)
{
struct resource *res;
int i = 0;
while (1)
{
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res)
break;
led_class_destroy_device(i); /* 根据次设备号调用leddrv提供的led_class_destroy_device进行销毁 */
i++;
g_ledcnt--;
}
return 0;
}
/* 实现led_operations */
static struct led_operations board_demo_led_opr = {
.init = board_demo_led_init,
.ctl = board_demo_led_ctl,
};
/* 实现platform_driver */
static struct platform_driver chip_demo_gpio_driver = {
.probe = chip_demo_gpio_probe, /* 在注册时调用 */
.remove = chip_demo_gpio_remove, /* 在注销时调用 */
.driver = {
.name = "100ask_led",
},
};
/* 入口函数 */
static int __init chip_demo_gpio_drv_init(void)
{
int err;
err = platform_driver_register(&chip_demo_gpio_driver); /* 注册platform_driver */
register_led_operations(&board_demo_led_opr); /* 注册led_operations, register_led_operations由上层leddrv模块提供 */
return 0;
}
/* 出口函数 */
static void __exit chip_demo_gpio_drv_exit(void)
{
platform_driver_unregister(&chip_demo_gpio_driver); /* 注销platform_driver */
}
module_init(chip_demo_gpio_drv_init);
module_exit(chip_demo_gpio_drv_exit);
MODULE_LICENSE("GPL");
- leddrv.c ```c
/ 1. 确定主设备号 / static int major = 0; static struct class led_class; struct led_operations p_led_opr;
void led_class_create_device(int minor) { device_create(led_class, NULL, MKDEV(major, minor), NULL, “100ask_led%d”, minor); / /dev/100ask_led0,1,… / } void led_class_destroy_device(int minor) { device_destroy(led_class, MKDEV(major, minor)); } / chip_demo_gpio中调用时,将其实现的led_operations赋值给p_led_opr(使得p_led_opr指向实现的结构体) / void register_led_operations(struct led_operations *opr) { p_led_opr = opr; }
/ 进行函数的外部声明,使得这些函数可以在底层被chip_demo_gpio模块调用(这表示chip_demo_gpio模块依赖于该模块) / EXPORT_SYMBOL(led_class_create_device); EXPORT_SYMBOL(led_class_destroy_device); EXPORT_SYMBOL(register_led_operations); / 在驱动程序中,该模块作为ko加载,别的驱动程序想要引用这些函数的话,必须将它们导出来 /
/ 3. 实现对应的open/read/write等函数,填入file_operations结构体 / static ssizet leddrvread (struct file file, char __user buf, sizet size, lofft *offset) { printk(“%s %s line %d\n”, _FILE, __FUNCTION, __LINE); return 0; }
/ write(fd, &val, 1); / static ssize_t led_drv_write (struct file file, const char __user buf, size_t size, loff_t offset) { int err; char status; struct inode inode = file_inode(file); int minor = iminor(inode);
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);
/* 根据次设备号和status控制LED */
p_led_opr->ctl(minor, status);
return 1;
}
static int led_drv_open (struct inode node, struct file file) { int minor = iminor(node);
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/* 根据次设备号初始化LED */
p_led_opr->init(minor);
return 0;
}
static int leddrvclose (struct inode node, struct file file) { printk(“%s %s line %d\n”, FILE, FUNCTION, __LINE); return 0; }
/ 2. 定义自己的file_operations结构体 / static struct file_operations led_drv = { .owner = THIS_MODULE, .open = led_drv_open, .read = led_drv_read, .write = led_drv_write, .release = led_drv_close, };
/ 4. 把file_operations结构体告诉内核:注册驱动程序 / / 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 / static int __init led_init(void) { int err; major = register_chrdev(0, “100ask_led”, &led_drv); / 注册file_operations结构体 /
led_class = class_create(THIS_MODULE, "100ask_led_class");
err = PTR_ERR(led_class);
if (IS_ERR(led_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "led");
return -1;
}
return 0;
}
/ 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数 / static void exit led_exit(void) { printk(“%s %s line %d\n”, FILE, FUNCTION, LINE__); class_destroy(led_class); / 销毁class,至于device在platform_device移除时会调用platform_device的remove函数进行销毁 / unregister_chrdev(major, “100ask_led”); / 注销file_operations / }
/ 7. 其他完善:提供设备信息,自动创建设备节点 / module_init(led_init); module_exit(led_exit); MODULE_LICENSE(“GPL”); ```
- leddrv上层代码只是一个空架子(框架),都没有跟硬件挂钩
- a.c 编译为 a.ko,里面定义了 func_a;如果它想让 b.ko 使用该函数,那么 a.c 里需要导出此函数(如果 a.c, b.c 都编进内核,则无需导出): EXPORT_SYMBOL(led_device_create);
- 使用时要先加载 a.ko