你好,我是拉依达。这是我的Linux驱动开发八股文详细解析系列。本系列最开始是我在csdn上更新的文章,目前已经是csdn搜索“linux驱动”综合推荐第一名,累计阅读次数4w次。全文总字数近8w字,是目前全网最全面,最清晰的入门linux驱动学习资料。现重新对内容进行整理,希望可以帮助到更多学习嵌入式的同学。【下面是拉依达推荐学习相关专栏:】 一、Linux驱动学习专栏:拉依达的Linux驱动八股文 - 牛客网 二、Linux应用学习专栏:拉依达的Linux应用八股文 - 牛客网 【我的嵌入式学习和校招经验】拉依达的嵌入式学习和秋招经验-CSDN博客 嵌入式学习规划/就业经验指导,可私信咨询———————————————————————————————————————————————————15.4 platform 总线框架例程一、应用层int main(int argc, char *argv[]){ int fd, retvalue; char *filename; unsigned char databuf[1]; unsigned char readbuf[1]; if(argc != 3) { printf("Error Usage!\r\n"); return -1; } filename = argv[1]; fd = open(filename, O_RDWR); if(fd < 0) { printf("file %s open failed!\r\n", argv[1]); return -1; } databuf[0] = atoi(argv[2]); /* 要执行的操作:打开或关闭 */ if(databuf[0] == 2) { retvalue = read(fd,readbuf,sizeof(readbuf)); if(retvalue < 0) { printf("read file %s failed!\r\n",filename); } else { printf("read date: %x\r\n",readbuf[0]); } }else { /* 向/dev/led 文件写入数据 */ retvalue = write(fd, databuf, sizeof(databuf)); if(retvalue < 0) { printf("LED Control Failed!\r\n"); close(fd); return -1; } } retvalue = close(fd); if(retvalue < 0) { printf("file %s close failed!\r\n", argv[1]); return -1; } return 0;} 二、驱动层(无设备树)device.c/* * 寄存器地址定义 */#define CCM_CCGR1_BASE (0X020C406C) #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)#define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)#define GPIO1_DR_BASE (0X0209C000)#define GPIO1_GDIR_BASE (0X0209C004)#define REGISTER_LENGTH 4/* * 设备资源信息,也就是LED0所使用的所有寄存器 */static struct resource led_resources[] = { [0] = { .start = CCM_CCGR1_BASE, .end = (CCM_CCGR1_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [1] = { .start = SW_MUX_GPIO1_IO03_BASE, .end = (SW_MUX_GPIO1_IO03_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [2] = { .start = SW_PAD_GPIO1_IO03_BASE, .end = (SW_PAD_GPIO1_IO03_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [3] = { .start = GPIO1_DR_BASE, .end = (GPIO1_DR_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, }, [4] = { .start = GPIO1_GDIR_BASE, .end = (GPIO1_GDIR_BASE + REGISTER_LENGTH - 1), .flags = IORESOURCE_MEM, },};/* * platform设备结构体 */static struct platform_device leddevice = { .name = "imx6ul-led", .id = -1, .dev = { .release = &led_release, }, .num_resources = ARRAY_SIZE(led_resources), .resource = led_resources,};static int __init leddevice_init(void){ return platform_device_register(&leddevice);}static void __exit leddevice_exit(void){ platform_device_unregister(&leddevice);}module_init(leddevice_init);module_exit(leddevice_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("songwei");driver.c#define LEDDEV_CNT 1 /* 设备号长度 */#define LEDDEV_NAME "platled" /* 设备名字 */#define LEDOFF 0#define LEDON 1/* 寄存器名 */static void __iomem *IMX6U_CCM_CCGR1;static void __iomem *SW_MUX_GPIO1_IO03;static void __iomem *SW_PAD_GPIO1_IO03;static void __iomem *GPIO1_DR;static void __iomem *GPIO1_GDIR;/* leddev设备结构体 */struct leddev_dev{ ...... };struct leddev_dev leddev; /* led设备 */static struct file_operations led_fops = { .owner = THIS_MODULE, .read = led_read, .open = led_open, .write = led_write,};/* * @description : flatform驱动的probe函数,当驱动与 * 设备匹配以后此函数就会执行 * @param - dev : platform设备 * @return : 0,成功;其他负值,失败 */static int led_probe(struct platform_device *dev){ int i = 0; int ressize[5]; u32 val = 0; struct resource *ledsource[5]; printk("led driver and device has matched!\r\n"); /* 1、获取资源 */ for (i = 0; i < 5; i++) { ledsource[i] = platform_get_resource(dev, IORESOURCE_MEM, i); /* 依次MEM类型资源 */ if (!ledsource[i]) { dev_err(&dev->dev, "No MEM resource for always on\n"); return -ENXIO; } ressize[i] = resource_size(ledsource[i]); } ......}static int led_remove(struct platform_device *dev){ iounmap(IMX6U_CCM_CCGR1); iounmap(SW_MUX_GPIO1_IO03); iounmap(SW_PAD_GPIO1_IO03); iounmap(GPIO1_DR); iounmap(GPIO1_GDIR); ......}/* platform驱动结构体 */static struct platform_driver led_driver = { .driver = { .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */ }, .probe = led_probe, .remove = led_remove,};static int __init leddriver_init(void){ return platform_driver_register(&led_driver);}static void __exit leddriver_exit(void){ platform_driver_unregister(&led_driver);}module_init(leddriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("songwei");二、驱动层(有设备树)driver.c#define LEDDEV_CNT 1 /* 设备号长度 */#define LEDDEV_NAME "dts_platled" /* 设备名字 */#define LEDOFF 0#define LEDON 1/* leddev设备结构体 */struct leddev_dev{ ...... struct device_node *node; /* LED设备节点 */ int led0; /* LED灯GPIO标号 */ };struct leddev_dev leddev; /* led设备 */static struct file_operations led_fops = { .owner = THIS_MODULE, .read = led_read, .open = led_open, .write = led_write,};/* * @description : flatform驱动的probe函数,当驱动与 * 设备匹配以后此函数就会执行 * @param - dev : platform设备 * @return : 0,成功;其他负值,失败 */static int led_probe(struct platform_device *dev){ ...... /* 6、初始化IO */ leddev.node = of_find_node_by_path("/gpioled"); if (leddev.node == NULL){ printk("gpioled node nost find!\r\n"); return -EINVAL; } leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0); if (leddev.led0 < 0) { printk("can't get led-gpio\r\n"); return -EINVAL; } gpio_request(leddev.led0, "led0"); gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平 */ return 0;}/* * @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行 * @param - dev : platform设备 * @return : 0,成功;其他负值,失败 */static int led_remove(struct platform_device *dev){ gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */ gpio_free(leddev.led0); /* 释放IO */ ......}/* 匹配列表 */static const struct of_device_id led_of_match[] = { { .compatible = "songwei-gpioled" }, { /* Sentinel */ }};/* platform驱动结构体 */static struct platform_driver led_driver = { .driver = { .name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */ .of_match_table = led_of_match, /* 设备树匹配表 */ }, .probe = led_probe, .remove = led_remove,};static int __init leddriver_init(void){ return platform_driver_register(&led_driver);}static void __exit leddriver_exit(void){ platform_driver_unregister(&led_driver);}module_init(leddriver_init);module_exit(leddriver_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("songwei");