驱动测试通过操作 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.o
build: kernel_modules
kernel_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.o
CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.o
Building modules, stage 2.
MODPOST 2 modules
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.ko
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.o
LD [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.o
CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.o
Building modules, stage 2.
MODPOST 2 modules
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.ko
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.o
LD [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.o
CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.o
Building modules, stage 2.
MODPOST 2 modules
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.ko
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.o
LD [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.o
CC [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.o
Building modules, stage 2.
MODPOST 2 modules
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_device.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/9_platform/led_device.ko
CC /home/onlylove/linux/driver/linux_driver/9_platform/led_driver.mod.o
LD [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]);
// 打开 led
write(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.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/onlylove/linux/driver/linux_driver/10_platform_dts/led_driver.mod.o
LD [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 1
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") */
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]);
// 打开 led
write(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.ko
newchrdev.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 灯可以正常点亮和关闭。