驱动测试通过操作 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 设备驱动实验(二)相关推荐

  1. 第四十六讲 设备驱动kobject

    第四十六讲 设备驱动 文章目录 第四十六讲 设备驱动 一.sysfs 1.发展 2.sysfs简介 3.kobject 4.kobj_type 二.设备驱动实验 1.代码 2.Makefile 3.实 ...

  2. 基于Cortex-A7架构的嵌入式linux ARM驱动开发<1>——字符设备驱动开发

    一.什么是字符设备 字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的.比如我们最常见的点灯.按键.IIC.SPI, L ...

  3. Linux 驱动开发 四十三:platform 设备驱动实验(一)

    一.platform 基本概念整理 Linux 系统要考虑到驱动的可重用性,因此提出了驱动的分离与分层这样的软件思路.因此提出驱动.总线和设备的驱动架构,总线负责管理驱动和设备.系统中有很多的物理总线 ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  5. Linux设备驱动开发---USB主机(控制器)与设备驱动(一)

    USB主机控制器与设备驱动---主机侧 一.Linux USB驱动层次 1.USB驱动(主机侧) 2.USB的逻辑组合(4个层次) 二.USB主机(控制器)驱动 1.主机控制器规格 2.主机控制器的相 ...

  6. Linux 驱动开发 三十四:Linux 内核定时器原理

    参考文档: <Cortex -A7 MPCore Technical Reference Manual> 中 Chapter 9:Generic Timer. <ARM ® Arch ...

  7. Linux 驱动开发 六十四:《pwm-backlight.txt》翻译

    文档路径:linux-imx-4.1.15\Documentation\devicetree\bindings\video\backlight\pwm-backlight.txt. PWM 背光绑定. ...

  8. linux驱动开发字符设备,linux驱动开发(三) 字符设备驱动框架

    还是老规矩先上代码 demo.c #include #include#include#include#include int demo_major = 250;int demo_minor = 0;i ...

  9. Linux驱动开发(十五)---如何使用内核现有驱动(显示屏)

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

  10. Linux驱动开发(十八)---网络(网卡)驱动学习

    前文回顾 <Linux驱动开发(一)-环境搭建与hello world> <Linux驱动开发(二)-驱动与设备的分离设计> <Linux驱动开发(三)-设备树> ...

最新文章

  1. 关于 typedef typedef struct typedef union理解 --写给不长脑子的我
  2. SEO交换链接时需遵循哪些原则?
  3. asp多表查询并显示_零公式实现多表数据查找!3步设置,简单高效!3分钟学会,真香...
  4. java如何实现乌龟爬行_乌龟是怎样爬行的
  5. IDEA中maven如何将jar包导入本地的maven库
  6. 项目不能上线,是开发的锅,还是产品的错?
  7. ws2812b程序51单片机_51单片机串口通信程序详解
  8. docker安装rabbitmq及简单管理
  9. 5在ios上无法选取文件_无法在 Ubuntu 20.04 上安装 Deb 文件?这是你需要做的! | Linux 中国...
  10. 利用Python进行数据分析——Ipython
  11. 多线程TCP客户端的设计
  12. WebCollector 简介与 快速入门
  13. 祝威廉 :Rust FFI 实践
  14. 【Java基础 项目实例--Bank项目5】Account 和 customer 对象等 继承、多态、方法的重写...
  15. 华为员工自曝百万级年终奖,论坛征女友!
  16. 编写一个VSCode插件
  17. 详解Unity中的Nav Mesh新特性|导航寻路系统 (二)
  18. 默小伟网站开发帮助文档UI模板
  19. 人工智能、机器学习、数据挖掘等基础概念-考研复试面试
  20. 判断JS数据类型的五种方法

热门文章

  1. Redis保存购物车信息
  2. 硬盘10K和15K有什么区别?
  3. access2007 mysql_access2007使用方法,access2007使用教程
  4. Pregel: A System for Large-Scale Graph Processing
  5. js match的()和g的幺蛾子
  6. 【题解】「JSOI2012」玄武密码(AC自动机)
  7. BackTrack5安装笔记
  8. 手机二维码业务行业报告
  9. linux firawll防火墙设置白名单/指定ip访问指定端口
  10. matplotlib绘制蜡烛图