Linux 驱动开发 四十四:platform 设备驱动实验(二)
驱动测试通过操作 led 完成。
一、原理图
二、无设备树源码实现
无设备树时候通过 platform_device.name 和 platform_driver.driver.name 进行匹配。
1、makefile
KERNELDIR := /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := led_device.o
obj-m += led_driver.obuild: kernel_moduleskernel_modules:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
2、device
1、搭建框架
思路:
1、驱动加载函数 led_device_init 调用 platform_device_register 进行注册设备。
2、platform_device_register 函数将 platform_device 结构体变量注册到系统。
3、platform_device 结构体使用 resource 结构体变量指定资源信息。
源码:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"/* * 设备资源信息,也就是LED0所使用的所有寄存器*/
static struct resource led_resources[] = {};/* @description : 释放flatform设备模块的时候此函数会执行* @param - dev : 要释放的设备* @return : 无*/
static void led_release(struct device *dev)
{printk("led device released!\r\n");
}/** platform设备结构体 */
static struct platform_device led_device = {.name = "imx6ul-led",.id = -1,.dev = {.release = &led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};/** @description : 设备模块加载 * @param : 无* @return : 无*/
static int __init led_device_init(void)
{return platform_device_register(&led_device);
}/** @description : 设备模块注销* @param : 无* @return : 无*/
static void __exit led_device_exit(void)
{platform_device_unregister(&led_device);
}module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
编译:
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_driver.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/9_platform modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.oCC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.oBuilding modules, stage 2.MODPOST 2 modulesCC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.koCC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_device.mod.c led_device.o led_driver.ko led_driver.mod.o Makefile Module.symvers
led_device.ko led_device.mod.o led_driver.c led_driver.mod.c led_driver.o modules.order
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$
测试:
/ # ls
bin led_device.ko mnt sbin usr
dev lib proc sys
etc linuxrc root tmp
/ # lsmod
Module Size Used by Not tainted
/ # cd /sys/bus/platform/devices/
/sys/bus/platform/devices # ls
1804000.dma-apbh 2184000.usb
2000000.aips-bus 2184200.usb
2000000.aips-bus:tempmon 2184800.usbmisc
2000000.spba-bus 2188000.ethernet
2020000.serial 2190000.usdhc
202c000.sai 2194000.usdhc
2034000.asrc 21a0000.i2c
2040000.tsc 21a4000.i2c
2080000.pwm 21ac000.romcp
2084000.pwm 21b0000.mmdc
2088000.pwm 21b8000.weim
208c000.pwm 21bc000.ocotp-ctrl
2090000.can 21c4000.csi
2094000.can 21c8000.lcdif
2098000.gpt 21cc000.pxp
209c000.gpio 21e0000.qspi
20a0000.gpio 21e8000.serial
20a4000.gpio 2200000.aips-bus
20a8000.gpio 2280000.dcp
20ac000.gpio 2284000.rngb
20b0000.snvs 2290000.iomuxc-snvs
20b4000.ethernet 2294000.snvs-gpr
20bc000.wdog 900000.sram
20c4000.ccm 904000.sram
20c406c.lq-led 905000.sram
20c8000.anatop Vivante GCCore
20c8000.anatop:regulator-3p0@120 a01000.interrupt-controller
20c8000.anatop:regulator-vddcore@140 alarmtimer
20c8000.anatop:regulator-vddsoc@140 backlight
20c9000.usbphy ci_hdrc.0
20ca000.usbphy ci_hdrc.1
20cc000.snvs gpioled
20cc000.snvs:snvs-powerkey imx6q-cpufreq
20cc000.snvs:snvs-poweroff lq-key
20cc000.snvs:snvs-rtc-lp pxp_v4l2
20d8000.src reg-dummy
20dc000.gpc regulators
20e0000.iomuxc regulators:regulator-gpio
20e4000.iomuxc-gpr regulators:regulator@0
20e8000.gpt regulators:regulator@1
20ec000.sdma regulatory.0
20f0000.pwm snd-soc-dummy
20f4000.pwm soc
20f8000.pwm soc:busfreq
20fc000.pwm sound
2100000.aips-bus spi4
/sys/bus/platform/devices # cd
/ # ls
bin led_device.ko mnt sbin usr
dev lib proc sys
etc linuxrc root tmp
/ # insmod led_device.ko
/ # lsmod
Module Size Used by Tainted: G
led_device 1266 0
/ # cd /sys/bus/platform/devices/
/sys/bus/platform/devices # ls
1804000.dma-apbh 2184200.usb
2000000.aips-bus 2184800.usbmisc
2000000.aips-bus:tempmon 2188000.ethernet
2000000.spba-bus 2190000.usdhc
2020000.serial 2194000.usdhc
202c000.sai 21a0000.i2c
2034000.asrc 21a4000.i2c
2040000.tsc 21ac000.romcp
2080000.pwm 21b0000.mmdc
2084000.pwm 21b8000.weim
2088000.pwm 21bc000.ocotp-ctrl
208c000.pwm 21c4000.csi
2090000.can 21c8000.lcdif
2094000.can 21cc000.pxp
2098000.gpt 21e0000.qspi
209c000.gpio 21e8000.serial
20a0000.gpio 2200000.aips-bus
20a4000.gpio 2280000.dcp
20a8000.gpio 2284000.rngb
20ac000.gpio 2290000.iomuxc-snvs
20b0000.snvs 2294000.snvs-gpr
20b4000.ethernet 900000.sram
20bc000.wdog 904000.sram
20c4000.ccm 905000.sram
20c406c.lq-led Vivante GCCore
20c8000.anatop a01000.interrupt-controller
20c8000.anatop:regulator-3p0@120 alarmtimer
20c8000.anatop:regulator-vddcore@140 backlight
20c8000.anatop:regulator-vddsoc@140 ci_hdrc.0
20c9000.usbphy ci_hdrc.1
20ca000.usbphy gpioled
20cc000.snvs imx6q-cpufreq
20cc000.snvs:snvs-powerkey imx6ul-led
20cc000.snvs:snvs-poweroff lq-key
20cc000.snvs:snvs-rtc-lp pxp_v4l2
20d8000.src reg-dummy
20dc000.gpc regulators
20e0000.iomuxc regulators:regulator-gpio
20e4000.iomuxc-gpr regulators:regulator@0
20e8000.gpt regulators:regulator@1
20ec000.sdma regulatory.0
20f0000.pwm snd-soc-dummy
20f4000.pwm soc
20f8000.pwm soc:busfreq
20fc000.pwm sound
2100000.aips-bus spi4
2184000.usb
/sys/bus/platform/devices #
/sys/bus/platform/devices # cd
/ # lsmod
Module Size Used by Tainted: G
led_device 1266 0
/ # ls
bin led_device.ko mnt sbin usr
dev lib proc sys
etc linuxrc root tmp
/ # rmmod led_device.ko
led device released!
/ # lsmod
Module Size Used by Tainted: G
/ #
通过测试,imx6ul-led 设备信息出现在 /sys/bus/platform/devices 目录下,说明 led_device 加载成功。
2、添加资源信息
思路:
1、定义使用到的寄存器信息(主要是寄存器地址)
2、完善 resource 数组
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"/* * 寄存器地址定义*/
#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,},
};/* @description : 释放flatform设备模块的时候此函数会执行* @param - dev : 要释放的设备* @return : 无*/
static void led_release(struct device *dev)
{printk("led device released!\r\n");
}/** platform设备结构体 */
static struct platform_device led_device = {.name = "imx6ul-led",.id = -1,.dev = {.release = &led_release,},.num_resources = ARRAY_SIZE(led_resources),.resource = led_resources,
};/** @description : 设备模块加载 * @param : 无* @return : 无*/
static int __init led_device_init(void)
{return platform_device_register(&led_device);
}/** @description : 设备模块注销* @param : 无* @return : 无*/
static void __exit led_device_exit(void)
{platform_device_unregister(&led_device);
}module_init(led_device_init);
module_exit(led_device_exit);
MODULE_LICENSE("GPL");
3、driver
1、搭建框架
源码:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"/** @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{printk("led probe!\r\n");return 0;
}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{printk("led remove!\r\n");return 0;
}/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */},.probe = led_probe,.remove = led_remove,
};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
编译:
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_driver.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/9_platform modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.oCC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.oBuilding modules, stage 2.MODPOST 2 modulesCC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.koCC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_device.mod.c led_device.o led_driver.ko led_driver.mod.o Makefile Module.symvers
led_device.ko led_device.mod.o led_driver.c led_driver.mod.c led_driver.o modules.order
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$
测试:
/ # ls
bin led_device.ko linuxrc root tmp
dev led_driver.ko mnt sbin usr
etc lib proc sys
/ # lsmod
Module Size Used by Not tainted
/ # insmod led_device.ko
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # insmod led_driver.ko
led probe!
/ # lsmod
Module Size Used by Tainted: G
led_driver 1014 0
led_device 1410 0
/ #
/ # rmmod led_driver.ko
led remove!
/ # ls
bin led_device.ko linuxrc root tmp
dev led_driver.ko mnt sbin usr
etc lib proc sys
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # insmod led—
insmod: can't insert 'led—⚌': No such file or directory
/ #
/ # insmod led_driver.ko
led probe!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 1014 0
led_device 1410 0
/ #
/ # rmmod led_device.ko
led remove!
led device released!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 1014 0
/ #
/ # insmod led_device.ko
led probe!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
led_driver 1014 0
/ #
/ # rmmod led_driver.ko
led remove!
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # rmmod led_driver.ko
rmmod: can't unload module 'led_driver': No such file or directory
/ #
/ # rmmod led_device.ko
led device released!
/ #
/ # lsmod
Module Size Used by Tainted: G
/ #
2、完善驱动框架
思路:
1、在 probe 函数中对驱动进行初始化。
2、在 remove 函数中完成卸载。
源码:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/kdev_t.h"
#include "linux/printk.h"#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "imx6ul-led" /* 名子 */typedef struct{struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/int major; /* 主设备号 */int minor; /* 次设备号 */dev_t devid; /* 设备号 */struct class *class; /* 类 (声明在 "linux/device.h")*/struct device *device; /* 设备 (声明在 "linux/device.h") */
}newchrdev_t;newchrdev_t newchrdev;static const struct file_operations newchrdevops = {.owner = THIS_MODULE,
};/** @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{int ret;/* 1、字符设备号分配 */newchrdev.major = NEWCHRDEV_MAJOR;if(newchrdev.major){/* 指定设备号 */newchrdev.minor = NEWCHRDEV_MINOR;newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);printk("newchrdev.major > 0!\r\n");}else{/* 系统分配设备号 */ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);newchrdev.major = MAJOR(newchrdev.devid);newchrdev.minor = MINOR(newchrdev.devid);printk("newchrdev.major = 0!\r\n");}if(ret < 0){printk("newchrdev xxx_chrdev_region failed!\r\n");goto newchrdev_chrdev_region_failed;}printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);/* 2、注册字符设备 */newchrdev.dev.owner = THIS_MODULE;cdev_init(&newchrdev.dev,&newchrdevops);ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);if(ret < 0){printk("newchrdev cdev_add failed!\r\n");goto newchrdev_cdev_add_failed;}/* 3、创建类 */newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.class)) {printk("newchrdev class_create failed!\r\n");goto newchrdev_class_create_failed;}/* 4、创建设备 */newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.device)){printk("newchrdev device_create failed!\r\n");goto neschrdev_device_creat_failed;}return 0;//neschrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
// device_destroy(newchrdev.class,newchrdev.devid);
neschrdev_device_creat_failed: /* 删除类 */class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */printk("failed!\r\n");return ret;
}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{/* 4、删除设备 */device_destroy(newchrdev.class,newchrdev.devid);/* 3、删除类 */class_destroy(newchrdev.class);/* 2、注销字符设备 */cdev_del(&newchrdev.dev);/* 1、释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);printk("led remove!\r\n");return 0;
}/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */},.probe = led_probe,.remove = led_remove,
};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
编译:
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_driver.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/9_platform modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.oCC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.oBuilding modules, stage 2.MODPOST 2 modulesCC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.koCC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_device.c led_device.mod.c led_device.o led_driver.ko led_driver.mod.o Makefile Module.symvers
led_device.ko led_device.mod.o led_driver.c led_driver.mod.c led_driver.o modules.order
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$
测试:
/ # ls
bin led_device.ko linuxrc root tmp
dev led_driver.ko mnt sbin usr
etc lib proc sys
/ #
/ # lsmod
Module Size Used by Not tainted
/ #
/ # insmod led_device.ko
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
ls: /dev/imx6ul-led: No such file or directory
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 07:39 /dev/imx6ul-led
/ # random: nonblocking pool is initialized/ #
/ # rmmod led_driver.ko
led remove!
/ #
/ # ls /dev/imx6ul-led -l
ls: /dev/imx6ul-led: No such file or directory
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 2126 0
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 07:39 /dev/imx6ul-led
/ #
/ # rmmod led_device.ko
led remove!
led device released!
/ # lsmod
Module Size Used by Tainted: G
led_driver 2126 0
/ #
/ # ls /dev/imx6ul-led -l
ls: /dev/imx6ul-led: No such file or directory
/ #
/ # insmod led_device.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
led_driver 2126 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 07:40 /dev/imx6ul-led
/ #
/ #
3、添加 led 相关操作
源码:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/kdev_t.h"
#include "linux/printk.h"
#include "asm/io.h"
#include "linux/types.h"
#include "linux/compiler.h"
#include "linux/types.h"
#include "asm/uaccess.h"#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "imx6ul-led" /* 名子 */#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;typedef struct{struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/int major; /* 主设备号 */int minor; /* 次设备号 */dev_t devid; /* 设备号 */struct class *class; /* 类 (声明在 "linux/device.h")*/struct device *device; /* 设备 (声明在 "linux/device.h") */
}newchrdev_t;newchrdev_t newchrdev;int imx6ull_led_init(struct platform_device *dev)
{int i = 0;int ressize[5];u32 val = 0;struct resource *ledsource[5];/* 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 1;}ressize[i] = resource_size(ledsource[i]); } /* 2、初始化LED *//* 寄存器地址映射 */IMX6U_CCM_CCGR1 = ioremap(ledsource[0]->start, ressize[0]);SW_MUX_GPIO1_IO03 = ioremap(ledsource[1]->start, ressize[1]);SW_PAD_GPIO1_IO03 = ioremap(ledsource[2]->start, ressize[2]);GPIO1_DR = ioremap(ledsource[3]->start, ressize[3]);GPIO1_GDIR = ioremap(ledsource[4]->start, ressize[4]);val = readl(IMX6U_CCM_CCGR1);val &= ~(3 << 26); /* 清除以前的设置 */val |= (3 << 26); /* 设置新值 */writel(val, IMX6U_CCM_CCGR1);/* 设置GPIO1_IO03复用功能,将其复用为GPIO1_IO03 */writel(5, SW_MUX_GPIO1_IO03);writel(0x10B0, SW_PAD_GPIO1_IO03);/* 设置GPIO1_IO03为输出功能 */val = readl(GPIO1_GDIR);val &= ~(1 << 3); /* 清除以前的设置 */val |= (1 << 3); /* 设置为输出 */writel(val, GPIO1_GDIR);/* 默认关闭LED1 */val = readl(GPIO1_DR);val |= (1 << 3) ; writel(val, GPIO1_DR);printk("imx6ull led init!\r\n");return 0;
}int imx6ull_led_exit(void)
{iounmap(IMX6U_CCM_CCGR1);iounmap(SW_MUX_GPIO1_IO03);iounmap(SW_PAD_GPIO1_IO03);iounmap(GPIO1_DR);iounmap(GPIO1_GDIR);printk("imx6ull led exit!\r\n");return 0;
}/** @description : LED打开/关闭* @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return : 无*/
void led_switch(u8 sta)
{u32 val = 0;if(sta == LEDON) {val = readl(GPIO1_DR);val &= ~(1 << 3); writel(val, GPIO1_DR);}else if(sta == LEDOFF) {val = readl(GPIO1_DR);val|= (1 << 3); writel(val, GPIO1_DR);}
}// struct inode 声明在 linux/fs.h 中
// struct file 声明在 linux/fs.h 中
int led_open (struct inode *i, struct file *f)
{return 0;
}int led_release (struct inode *i, struct file *f)
{return 0;
}// ssize_t 定义在 linux/types.h 中
// __user 定义在 linux/compiler.h 中
// size_t 定义在 linux/types.h 中
// loff_t 定义在 linux/types.h 中
ssize_t led_read (struct file *f, char __user *b, size_t c, loff_t * l)
{printk("led read!\r\n");return 0;
}/** @description : 向设备写数据 * @param - f : 设备文件,表示打开的文件描述符* @param - b : 要写给设备写入的数据* @param - c : 要写入的数据长度* @param - l : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
ssize_t led_write (struct file *f, const char __user *b, size_t c, loff_t *l)
{int retvalue;unsigned char databuf[1];unsigned char ledstat;printk("led write!\r\n");retvalue = copy_from_user(databuf, b, c);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0]; /* 获取状态值 */if(ledstat == LEDON) { led_switch(LEDON); /* 打开LED灯 */} else if(ledstat == LEDOFF) {led_switch(LEDOFF); /* 关闭LED灯 */}return 0;
}static const struct file_operations newchrdevops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,.write = led_write,
};/** @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{int ret;/* 1、字符设备号分配 */newchrdev.major = NEWCHRDEV_MAJOR;if(newchrdev.major){/* 指定设备号 */newchrdev.minor = NEWCHRDEV_MINOR;newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);printk("newchrdev.major > 0!\r\n");}else{/* 系统分配设备号 */ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);newchrdev.major = MAJOR(newchrdev.devid);newchrdev.minor = MINOR(newchrdev.devid);printk("newchrdev.major = 0!\r\n");}if(ret < 0){printk("newchrdev xxx_chrdev_region failed!\r\n");goto newchrdev_chrdev_region_failed;}printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);/* 2、注册字符设备 */newchrdev.dev.owner = THIS_MODULE;cdev_init(&newchrdev.dev,&newchrdevops);ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);if(ret < 0){printk("newchrdev cdev_add failed!\r\n");goto newchrdev_cdev_add_failed;}/* 3、创建类 */newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.class)) {printk("newchrdev class_create failed!\r\n");goto newchrdev_class_create_failed;}/* 4、创建设备 */newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.device)){printk("newchrdev device_create failed!\r\n");goto newchrdev_device_creat_failed;}ret = imx6ull_led_init(dev);if(ret != 0){goto newchrdev_device_xxx_failed;}return 0;newchrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */device_destroy(newchrdev.class,newchrdev.devid);
newchrdev_device_creat_failed: /* 删除类 */class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */printk("failed!\r\n");return ret;
}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{imx6ull_led_exit();/* 4、删除设备 */device_destroy(newchrdev.class,newchrdev.devid);/* 3、删除类 */class_destroy(newchrdev.class);/* 2、注销字符设备 */cdev_del(&newchrdev.dev);/* 1、释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);printk("led remove!\r\n");return 0;
}/* platform驱动结构体 */
static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led", /* 驱动名字,用于和设备匹配 */},.probe = led_probe,.remove = led_remove,
};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
编译:
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/9_platform modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.oCC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.oBuilding modules, stage 2.MODPOST 2 modulesCC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.koCC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$ ls
led_app.c led_device.ko led_device.mod.o led_driver.c led_driver.mod.c led_driver.o modules.order
led_device.c led_device.mod.c led_device.o led_driver.ko led_driver.mod.o Makefile Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/9_platform$
测试:
/ # ls
bin led_app lib proc sys
dev led_device.ko linuxrc root tmp
etc led_driver.ko mnt sbin usr
/ # lsmod
Module Size Used by Not tainted
/ #
/ # insmod led_device.ko
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
/ # lsmod
Module Size Used by Tainted: G
led_driver 3854 0
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 00:24 /dev/imx6ul-led
/ #
/ # rmmod led_device.ko
imx6ull led exit!
led remove!
led device released!
/ # lsmod
Module Size Used by Tainted: G
led_driver 3854 0
/ # ls /dev/imx6ul-led -l
ls: /dev/imx6ul-led: No such file or directory
/ #
/ # insmod led_device.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
led_driver 3854 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 00:24 /dev/imx6ul-led
/ #
/ # rmmod led_driver.ko
imx6ull led exit!
led remove!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
ls: /dev/imx6ul-led: No such file or directory
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 3854 0
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 00:26 /dev/imx6ul-led
/ #
4、app程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stdio.h"int main(int argc, char *argv[])
{int fd = 0, retvalue = 0;char writebuf[1] = "";fd = open(argv[1],O_RDWR);if(fd < 0){printf("Can't open file %s\r\n", argv[1]);return -1;}writebuf[0] = atoi(argv[2]);// 打开 ledwrite(fd, writebuf, 1);retvalue = close(fd);if(retvalue < 0){printf("Can't close file %s\r\n", argv[1]);return -1;}return 0;
}
编译:
arm-linux-gnueabihf-gcc led_app.c -o led_app
5、测试
思路:
1、加载设备。
2、加载驱动。
3、执行app程序。
测试过程:
/ # ls
bin led_app lib proc sys
dev led_device.ko linuxrc root tmp
etc led_driver.ko mnt sbin usr
/ # lsmod
Module Size Used by Not tainted
/ #
/ # insmod led_device.ko
/ #
/ # lsmod
Module Size Used by Tainted: G
led_device 1410 0
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 3854 0
led_device 1410 0
/ #
/ # ls /dev/imx6ul-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 00:30 /dev/imx6ul-led
/ #
/ # ./led_app /dev/imx6ul-led 1
led write!
/ #
/ # ./led_app /dev/imx6ul-led 0
led write!
/ #
/ # random: nonblocking pool is initialized
/ # ./led_app /dev/imx6ul-led 1
led write!
/ #
/ # ./led_app /dev/imx6ul-led 0
led write!
/ #
/ # rmmod led_driver.ko
imx6ull led exit!
led remove!
/ #
/ # rmmod led_device.ko
led device released!
/ #
/ # lsmod
Module Size Used by Tainted: G
/ #
通过以上测试,开发板上 led 灯可以正常点亮和关闭。
三、有设备树源码实现
有设备树时,通过 platform_driver.driver.of_match_table.compatible 和 设备树中 compatible 进行匹配。
1、设备树
支持设备树驱动不需要 device 源码,设备信息由设备树提供。
gpioled {compatible = "gpioled";status = "okay";pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
};
2、driver
1、搭建架构
源码:
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/kdev_t.h"
#include "linux/printk.h"#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "imx6ull-led" /* 名子 */typedef struct{struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/int major; /* 主设备号 */int minor; /* 次设备号 */dev_t devid; /* 设备号 */struct class *class; /* 类 (声明在 "linux/device.h")*/struct device *device; /* 设备 (声明在 "linux/device.h") */
}newchrdev_t;newchrdev_t newchrdev;// struct inode 声明在 linux/fs.h 中
// struct file 声明在 linux/fs.h 中
int led_open (struct inode *i, struct file *f)
{return 0;
}int led_release (struct inode *i, struct file *f)
{return 0;
}// ssize_t 定义在 linux/types.h 中
// __user 定义在 linux/compiler.h 中
// size_t 定义在 linux/types.h 中
// loff_t 定义在 linux/types.h 中
ssize_t led_read (struct file *f, char __user *b, size_t c, loff_t * l)
{printk("led read!\r\n");return 0;
}/** @description : 向设备写数据 * @param - f : 设备文件,表示打开的文件描述符* @param - b : 要写给设备写入的数据* @param - c : 要写入的数据长度* @param - l : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
ssize_t led_write (struct file *f, const char __user *b, size_t c, loff_t *l)
{printk("led write!\r\n");return 0;
}static const struct file_operations newchrdevops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,.write = led_write,
};/** @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{int ret;/* 1、字符设备号分配 */newchrdev.major = NEWCHRDEV_MAJOR;if(newchrdev.major){/* 指定设备号 */newchrdev.minor = NEWCHRDEV_MINOR;newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);printk("newchrdev.major > 0!\r\n");}else{/* 系统分配设备号 */ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);newchrdev.major = MAJOR(newchrdev.devid);newchrdev.minor = MINOR(newchrdev.devid);printk("newchrdev.major = 0!\r\n");}if(ret < 0){printk("newchrdev xxx_chrdev_region failed!\r\n");goto newchrdev_chrdev_region_failed;}printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);/* 2、注册字符设备 */newchrdev.dev.owner = THIS_MODULE;cdev_init(&newchrdev.dev,&newchrdevops);ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);if(ret < 0){printk("newchrdev cdev_add failed!\r\n");goto newchrdev_cdev_add_failed;}/* 3、创建类 */newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.class)) {printk("newchrdev class_create failed!\r\n");goto newchrdev_class_create_failed;}/* 4、创建设备 */newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.device)){printk("newchrdev device_create failed!\r\n");goto newchrdev_device_creat_failed;}printk("led probe!\r\n");return 0;//newchrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
// device_destroy(newchrdev.class,newchrdev.devid);
newchrdev_device_creat_failed: /* 删除类 */class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */printk("failed!\r\n");return ret;
}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{/* 4、删除设备 */device_destroy(newchrdev.class,newchrdev.devid);/* 3、删除类 */class_destroy(newchrdev.class);/* 2、注销字符设备 */cdev_del(&newchrdev.dev);/* 1、释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);printk("led remove!\r\n");return 0;
}/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "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,
};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
编译:
onlylove@ubuntu:~/linux/driver/linux_driver/10_platform_dts$ ls
led_driver.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/10_platform_dts$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/10_platform_dts modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'CC [M] /home/onlylove/linux/driver/linux_driver/10_platform_dts/led_driver.oBuilding modules, stage 2.MODPOST 1 modulesCC /home/onlylove/linux/driver/linux_driver/10_platform_dts/led_driver.mod.oLD [M] /home/onlylove/linux/driver/linux_driver/10_platform_dts/led_driver.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/10_platform_dts$ ls
led_driver.c led_driver.ko led_driver.mod.c led_driver.mod.o led_driver.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/10_platform_dts$
测试:
/ # ls
bin led_driver.ko mnt sbin usr
dev lib proc sys
etc linuxrc root tmp
/ # lsmod
Module Size Used by Not tainted
/ # cd /sys/firmware/devicetree/base/
/sys/firmware/devicetree/base # ls
#address-cells lq-led
#size-cells memory
aliases model
backlight name
chosen pxp_v4l2
clocks regulators
compatible reserved-memory
cpus soc
gpioled sound
interrupt-controller@00a01000 spi4
lq-key
/sys/firmware/devicetree/base # cd gpioled/
/sys/firmware/devicetree/base/gpioled # ls
compatible name pinctrl-names
led-gpio pinctrl-0 status
/sys/firmware/devicetree/base/gpioled # cat compatible
gpioled
/sys/firmware/devicetree/base/gpioled #
通过以上日志可以确定,设备树 gpioled 节点成功加载。
/ # ls /sys/bus/platform/drivers/imx6ul-led -l
ls: /sys/bus/platform/drivers/imx6ul-led: No such file or directory
/ #
/ # ls /dev/imx6ull-led -l
ls: /dev/imx6ull-led: No such file or directory
/ #
/ # lsmod
Module Size Used by Not tainted
/ #
/ # ls
bin led_driver.ko mnt sbin usr
dev lib proc sys
etc linuxrc root tmp
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
led probe!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 2880 0
/ # ls /sys/bus/platform/drivers/imx6ul-led -l
total 0
--w------- 1 0 0 4096 Jan 1 06:21 bind
lrwxrwxrwx 1 0 0 0 Jan 1 06:21 gpioled -> ../../../../devices/platform/gpioled
lrwxrwxrwx 1 0 0 0 Jan 1 06:21 module -> ../../../../module/led_driver
--w------- 1 0 0 4096 Jan 1 06:20 uevent
--w------- 1 0 0 4096 Jan 1 06:21 unbind
/ # ls /dev/imx6ull-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 06:20 /dev/imx6ull-led
/ #
/ # rmmod led_driver.ko
led remove!
/ #
/ # lsmod
Module Size Used by Tainted: G
/ #
/ # ls /sys/bus/platform/drivers/imx6ul-led -l
ls: /sys/bus/platform/drivers/imx6ul-led: No such file or directory
/ # ls /dev/imx6ull-led -l
ls: /dev/imx6ull-led: No such file or directory
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
led probe!
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 2880 0
/ #
/ # ls /sys/bus/platform/drivers/imx6ul-led -l
total 0
--w------- 1 0 0 4096 Jan 1 06:21 bind
lrwxrwxrwx 1 0 0 0 Jan 1 06:21 gpioled -> ../../../../devices/platform/gpioled
lrwxrwxrwx 1 0 0 0 Jan 1 06:21 module -> ../../../../module/led_driver
--w------- 1 0 0 4096 Jan 1 06:21 uevent
--w------- 1 0 0 4096 Jan 1 06:21 unbind
/ #
/ # ls /dev/imx6ull-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 06:21 /dev/imx6ull-led
/ #
/ # rmmod led_driver.ko
led remove!
/ #
/ # ls /sys/bus/platform/drivers/imx6ul-led -l
ls: /sys/bus/platform/drivers/imx6ul-led: No such file or directory
/ #
/ # ls /dev/imx6ull-led -l
ls: /dev/imx6ull-led: No such file or directory
/ #
/ #
通过以上日志可以确定,驱动没有问题。
2、完善驱动
#include "linux/init.h"
#include "linux/module.h"
#include "linux/platform_device.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/fs.h"
#include "linux/kdev_t.h"
#include "linux/printk.h"
#include "linux/of.h"
#include "asm/uaccess.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "imx6ull-led" /* 名子 */#define LEDOFF 0
#define LEDON 1typedef struct{struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/int major; /* 主设备号 */int minor; /* 次设备号 */dev_t devid; /* 设备号 */struct class *class; /* 类 (声明在 "linux/device.h")*/struct device *device; /* 设备 (声明在 "linux/device.h") */struct device_node *node; /* LED设备节点 (声明在 "linux/of.h")*/int led0; /* LED灯GPIO标号 */
}newchrdev_t;newchrdev_t newchrdev;// EINVAL 定义在 "uapi/asm-generic/erron-base.h"
// of_find_node_by_path 声明在 "linux/of.h"
// of_get_named_gpio 声明在 "linux/of_gpio.h"
// gpio_request 声明在 "linux/gpio.h"
// gpio_direction_output 声明在 "linux/gpio.h"
int imx6ull_led_init(void)
{newchrdev.node = of_find_node_by_path("/gpioled");if (newchrdev.node == NULL){printk("gpioled node nost find!\r\n");return -EINVAL;} newchrdev.led0 = of_get_named_gpio(newchrdev.node, "led-gpio", 0);if (newchrdev.led0 < 0) {printk("can't get led-gpio\r\n");return -EINVAL;}gpio_request(newchrdev.led0, "led0");gpio_direction_output(newchrdev.led0, 1); /* led0 IO设置为输出,默认高电平 */printk("imx6ull led init!\r\n");return 0;
}int imx6ull_led_exit(void)
{gpio_set_value(newchrdev.led0, 1); /* 卸载驱动的时候关闭LED */printk("imx6ull led exit!\r\n");return 0;
}/** @description : LED打开/关闭* @param - sta : LEDON(0) 打开LED,LEDOFF(1) 关闭LED* @return : 无*/
void led0_switch(u8 sta)
{if (sta == LEDON )gpio_set_value(newchrdev.led0, 0);else if (sta == LEDOFF)gpio_set_value(newchrdev.led0, 1);
}// struct inode 声明在 linux/fs.h 中
// struct file 声明在 linux/fs.h 中
int led_open (struct inode *i, struct file *f)
{return 0;
}int led_release (struct inode *i, struct file *f)
{return 0;
}// ssize_t 定义在 linux/types.h 中
// __user 定义在 linux/compiler.h 中
// size_t 定义在 linux/types.h 中
// loff_t 定义在 linux/types.h 中
ssize_t led_read (struct file *f, char __user *b, size_t c, loff_t * l)
{printk("led read!\r\n");return 0;
}/** @description : 向设备写数据 * @param - f : 设备文件,表示打开的文件描述符* @param - b : 要写给设备写入的数据* @param - c : 要写入的数据长度* @param - l : 相对于文件首地址的偏移* @return : 写入的字节数,如果为负值,表示写入失败*/
ssize_t led_write (struct file *f, const char __user *b, size_t c, loff_t *l)
{int retvalue;unsigned char databuf[2];unsigned char ledstat;retvalue = copy_from_user(databuf, b, c);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}ledstat = databuf[0];if (ledstat == LEDON) {led0_switch(LEDON);} else if (ledstat == LEDOFF) {led0_switch(LEDOFF);}printk("led write!\r\n");return 0;
}static const struct file_operations newchrdevops = {.owner = THIS_MODULE,.open = led_open,.release = led_release,.read = led_read,.write = led_write,
};/** @description : flatform驱动的probe函数,当驱动与设备匹配以后此函数就会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_probe(struct platform_device *dev)
{int ret;/* 1、字符设备号分配 */newchrdev.major = NEWCHRDEV_MAJOR;if(newchrdev.major){/* 指定设备号 */newchrdev.minor = NEWCHRDEV_MINOR;newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);printk("newchrdev.major > 0!\r\n");}else{/* 系统分配设备号 */ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);newchrdev.major = MAJOR(newchrdev.devid);newchrdev.minor = MINOR(newchrdev.devid);printk("newchrdev.major = 0!\r\n");}if(ret < 0){printk("newchrdev xxx_chrdev_region failed!\r\n");goto newchrdev_chrdev_region_failed;}printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);/* 2、注册字符设备 */newchrdev.dev.owner = THIS_MODULE;cdev_init(&newchrdev.dev,&newchrdevops);ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);if(ret < 0){printk("newchrdev cdev_add failed!\r\n");goto newchrdev_cdev_add_failed;}/* 3、创建类 */newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.class)) {printk("newchrdev class_create failed!\r\n");goto newchrdev_class_create_failed;}/* 4、创建设备 */newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);if(IS_ERR(newchrdev.device)){printk("newchrdev device_create failed!\r\n");goto newchrdev_device_creat_failed;}ret = imx6ull_led_init();if(ret != 0){goto newchrdev_device_xxx_failed;}printk("led probe!\r\n");return 0;newchrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */device_destroy(newchrdev.class,newchrdev.devid);
newchrdev_device_creat_failed: /* 删除类 */class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */printk("failed!\r\n");return ret;
}/** @description : platform驱动的remove函数,移除platform驱动的时候此函数会执行* @param - dev : platform设备* @return : 0,成功;其他负值,失败*/
static int led_remove(struct platform_device *dev)
{imx6ull_led_exit();/* 4、删除设备 */device_destroy(newchrdev.class,newchrdev.devid);/* 3、删除类 */class_destroy(newchrdev.class);/* 2、注销字符设备 */cdev_del(&newchrdev.dev);/* 1、释放设备号 */unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);printk("led remove!\r\n");return 0;
}/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "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,
};/** @description : 驱动模块加载函数* @param : 无* @return : 无*/
static int __init led_driver_init(void)
{return platform_driver_register(&led_driver);
}/** @description : 驱动模块卸载函数* @param : 无* @return : 无*/
static void __exit led_driver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(led_driver_init);
module_exit(led_driver_exit);
MODULE_LICENSE("GPL");
3、app 测试
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stdio.h"int main(int argc, char *argv[])
{int fd = 0, retvalue = 0;char writebuf[1] = "";fd = open(argv[1],O_RDWR);if(fd < 0){printf("Can't open file %s\r\n", argv[1]);return -1;}writebuf[0] = atoi(argv[2]);// 打开 ledwrite(fd, writebuf, 1);retvalue = close(fd);if(retvalue < 0){printf("Can't close file %s\r\n", argv[1]);return -1;}return 0;
}
编译:
arm-linux-gnueabihf-gcc led_app.c -o led_app
4、测试
思路:
1、加载驱动。
2、执行app程序。
测试过程:
/ # ls
bin led_app linuxrc root tmp
dev led_driver.ko mnt sbin usr
etc lib proc sys
/ # ls /dev/imx6ull-led -l
ls: /dev/imx6ull-led: No such file or directory
/ #
/ # lsmod
Module Size Used by Not tainted
/ #
/ # insmod led_driver.konewchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
led probe!
/ #
/ #
/ # lsmod
Module Size Used by Tainted: G
led_driver 3845 0
/ #
/ # rmmod led_driver.ko
imx6ull led exit!
led remove!
/ #
/ # lsmod
Module Size Used by Tainted: G
/ #
/ # insmod led_driver.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
imx6ull led init!
led probe!
/ #
/ # ls /dev/imx6ull-led -l
crw-rw---- 1 0 0 248, 0 Jan 1 07:31 /dev/imx6ull-led
/ #
/ # ./led_app /dev/imx6ull-led 1
led write!
/ #
/ # ./led_app /dev/imx6ull-led 0
led write!
/ #
/ # ./led_app /dev/imx6ull-led 1
led write!
/ #
/ # ./led_app /dev/imx6ull-led 0
led write!
/ #
通过以上测试,开发板上 led 灯可以正常点亮和关闭。
Linux 驱动开发 四十四:platform 设备驱动实验(二)相关推荐
- 第四十六讲 设备驱动kobject
第四十六讲 设备驱动 文章目录 第四十六讲 设备驱动 一.sysfs 1.发展 2.sysfs简介 3.kobject 4.kobj_type 二.设备驱动实验 1.代码 2.Makefile 3.实 ...
- 基于Cortex-A7架构的嵌入式linux ARM驱动开发<1>——字符设备驱动开发
一.什么是字符设备 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI, L ...
- Linux 驱动开发 四十三:platform 设备驱动实验(一)
一.platform 基本概念整理 Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路.因此提出驱动.总线和设备的驱动架构,总线负责管理驱动和设备.系统中有很多的物理总线 ...
- 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型
文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...
- Linux设备驱动开发---USB主机(控制器)与设备驱动(一)
USB主机控制器与设备驱动---主机侧 一.Linux USB驱动层次 1.USB驱动(主机侧) 2.USB的逻辑组合(4个层次) 二.USB主机(控制器)驱动 1.主机控制器规格 2.主机控制器的相 ...
- Linux 驱动开发 三十四:Linux 内核定时器原理
参考文档: <Cortex -A7 MPCore Technical Reference Manual> 中 Chapter 9:Generic Timer. <ARM ® Arch ...
- Linux 驱动开发 六十四:《pwm-backlight.txt》翻译
文档路径:linux-imx-4.1.15\Documentation\devicetree\bindings\video\backlight\pwm-backlight.txt. PWM 背光绑定. ...
- linux驱动开发字符设备,linux驱动开发(三) 字符设备驱动框架
还是老规矩先上代码 demo.c #include #include#include#include#include int demo_major = 250;int demo_minor = 0;i ...
- Linux驱动开发(十五)---如何使用内核现有驱动(显示屏)
前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...
- Linux驱动开发(十八)---网络(网卡)驱动学习
前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...
最新文章
- 关于 typedef typedef struct typedef union理解 --写给不长脑子的我
- SEO交换链接时需遵循哪些原则?
- asp多表查询并显示_零公式实现多表数据查找!3步设置,简单高效!3分钟学会,真香...
- java如何实现乌龟爬行_乌龟是怎样爬行的
- IDEA中maven如何将jar包导入本地的maven库
- 项目不能上线,是开发的锅,还是产品的错?
- ws2812b程序51单片机_51单片机串口通信程序详解
- docker安装rabbitmq及简单管理
- 5在ios上无法选取文件_无法在 Ubuntu 20.04 上安装 Deb 文件?这是你需要做的! | Linux 中国...
- 利用Python进行数据分析——Ipython
- 多线程TCP客户端的设计
- WebCollector 简介与 快速入门
- 祝威廉 :Rust FFI 实践
- 【Java基础 项目实例--Bank项目5】Account 和 customer 对象等 继承、多态、方法的重写...
- 华为员工自曝百万级年终奖,论坛征女友!
- 编写一个VSCode插件
- 详解Unity中的Nav Mesh新特性|导航寻路系统 (二)
- 默小伟网站开发帮助文档UI模板
- 人工智能、机器学习、数据挖掘等基础概念-考研复试面试
- 判断JS数据类型的五种方法