Linux 驱动开发 四十四:platform 设备驱动实验(二)

驱动测试通过操作 led 完成。

一、原理图

在这里插入图片描述
在这里插入图片描述

二、无设备树源码实现

无设备树时候通过 platform_device.name 和 platform_driver.driver.name 进行匹配

1、makefile

KERNELDIR := /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := led_device.o
obj-m += led_driver.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 灯可以正常点亮和关闭。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值