一.platform相关结构体与函数
1.匹配列表 - struct of_device_id
struct of_device_id
{char name[32];char type[32];/* compatible 很重要,需要与设备树节点的 compatible 属性一致,才能匹配 */char compatible[128]; const void *data;
};
2.device_driver
struct device_driver
{/* 设备名字,在不用设备树的情况下可以使用 name 匹配 */const char *name; struct bus_type *bus;struct module *owner;const char *mod_name; /* used for built-in modules */bool suppress_bind_attrs; /* disables bind/unbind via sysfs *//* 设备树下的匹配列表结构体 */const struct of_device_id *of_match_table;const struct acpi_device_id *acpi_match_table;int (*probe) (struct device *dev);int (*remove) (struct device *dev);void (*shutdown) (struct device *dev);int (*suspend) (struct device *dev, pm_message_t state);int (*resume) (struct device *dev);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;
};
3.平台驱动结构体 - struct platform_driver
struct platform_driver
{/* 匹配成功后此函数会执行,将注册字符设备等内容放入这个函数,例如注册字符设备驱动、添加cdev、创建类等 */int (*probe)(struct platform_device *);/* 当关闭 platform 设备驱动的时候此函数会执行,把以前在 exit 中要做的事情放到这个里面,如删除cdev、注销设备号等 */int (*remove)(struct platform_device *);void (*shutdown)(struct platform_device *);int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);/* 用于匹配设备和 platform 驱动 */struct device_driver driver;const struct platform_device_id *id_table;bool prevent_deferred_probe;
};
4.声明设备匹配列表 - MODULE_DEVICE_TABLE
函数原型:
/*
* @description: : 声明匹配列表
* @param - type : 设备类型,设备树下的设备就传入 of
* @param - xxx_of_match : 匹配列表结构体
*/
MODULE_DEVICE_TABLE(type,struct of_device_id xxx_of_match);
5.向Linux内核注册一个 platform 驱动 - platform_driver_register
函数原型:
/*** @description: 向Linux内核注册一个 platform 驱动* @param - driver : 要注册的 platform 驱动* @return : 成功时返回(0),失败则返回(负数)*/
int platform_driver_register(struct platform_driver *driver)
6.卸载 platform 驱动 - platform_driver_unregister
函数原型:
/*** @description: 卸载 platform 驱动* @param - drv : 要卸载的 platform 驱动* @return : 无*/
void platform_driver_unregister(struct platform_driver *drv)
二.platform下的LED实验
1.设备树
(1).流程图
注意:compatible 属性部分要与匹配列表中的 compatible 部分一致
(2).设备树代码
2.驱动部分
(1).流程图
(2).驱动代码
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/fs.h>#define LEDDEV_CNT 1 /* 设备号个数 */
#define LEDDEV_NAME "dtsplatled" /* 设备名字 */
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 *//* leddev 设备结构体 */
struct leddev_dev
{dev_t devid; /* 设备号 */struct cdev cdev; /* cdev */struct class *class; /* 类 */struct device *device; /* 设备 */int major; /* 主设备号 */int minor; /* 次设备号 */struct device_node *node; /* LED设备结点 */int led0; /* LED 灯的 GPIO 编号 */
};/* led 设备 */
struct leddev_dev leddev;/*
* @description : LED 打开/关闭
* @param - sta : LEDON(0) 打开 LED, LEDOFF(1) 关闭 LED
* @return : 无
*/
void led0_switch(u8 sta)
{if (sta == LEDON )gpio_set_value(leddev.led0, 0);else if (sta == LEDOFF)gpio_set_value(leddev.led0, 1);
}/*** @description: 打开设备* @param - inode : 传递给驱动的 inode* @param - filp : 要打开的文件,file结构体有个private_data的成员变量,一般在open的时候将private_data指向设备结构体* @return : 成功时返回(0),失败时返回(其他值)*/
static int led_open(struct inode *inode,struct file *filp)
{/* 设置私有数据 */filp->private_data = &leddev;return 0;
}/*** @description: 向设备写数据* @param - filp : 设备文件,表示打开的文件描述符* @param - buf : 要写入设备的数据* @param - cnt : 要写入的字节数* @param - offt : 相对于文件首地址的偏移量* @return : 成功时返回(成功写入的字节数),失败时返回(负值)*/
static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{int retvalue;unsigned char databuf[0];unsigned char ledstat;retvalue = copy_from_user(databuf,buf,cnt);if(0 > retvalue){printk("kernel write falied!\r\n");return -EFAULT;}ledstat = databuf[0];if(LEDON == ledstat){led0_switch(LEDON);}else if(LEDOFF == ledstat){led0_switch(LEDOFF);}return 0;
}/* 绑定设备操作函数 */
static struct file_operations led_fops =
{.owner = THIS_MODULE,.open = led_open,.write = led_write,
};/*** @description: platform 驱动的probe函数,当驱动与设备匹配以后此函数会执行* @param - dev : platform 设备* @return : 成功时返回(0),失败时返回(负值) */
static int led_probe(struct platform_device *dev)
{printk("led driver and device was matched!\r\n");/* 一.设置 LED 所使用的 GPIO *//* 1.获取设备结点 */leddev.node = of_find_node_by_path("/gpioled");/* 2.得到 GPIO 编号 */leddev.led0 = of_get_named_gpio(leddev.node,"led-gpio",0);/* 3.申请 gpio */gpio_request(leddev.led0,"led0");/* 4.设置gpio默认输出为高电平,关闭 LED 灯 */gpio_direction_output(leddev.led0,1);/* 二.注册字符设备驱动 *//* 1.创建设备号 */if(leddev.major){leddev.devid = MKDEV(leddev.major,0);register_chrdev_region(leddev.devid,LEDDEV_CNT,LEDDEV_NAME);}else{alloc_chrdev_region(&leddev.devid,0,LEDDEV_CNT,LEDDEV_NAME);leddev.major = MAJOR(leddev.devid);printk("leddev major : %d\r\n",leddev.major);}/* 2.初始化 cdev */cdev_init(&leddev.cdev,&led_fops);/* 3.添加一个 cdev */cdev_add(&leddev.cdev,leddev.devid,LEDDEV_CNT);/* 4.创建类 */leddev.class = class_create(THIS_MODULE,LEDDEV_NAME);if(IS_ERR(leddev.class)){return PTR_ERR(leddev.class);}/* 5.创建设备 */leddev.device = device_create(leddev.class,NULL,leddev.devid,NULL,LEDDEV_NAME);if(IS_ERR(leddev.device)){return PTR_ERR(leddev.device);}return 0;
}/*** @description: platform驱动的remove函数,移除platform驱动的时候会执行此函数* @param - dev : platform 设备* @return : 成功时返回(0),失败时返回(负值)*/
static int led_remove(struct platform_device *dev)
{gpio_set_value(leddev.led0,1); //卸载驱动的时候关闭 LED 灯/* 注销字符设备驱动 */cdev_del(&leddev.cdev);unregister_chrdev_region(leddev.devid,LEDDEV_CNT);device_destroy(leddev.class,leddev.devid);class_destroy(leddev.class);return 0;
}/* 匹配列表 */
static struct of_device_id led_of_match[] =
{{.compatible = "atkalpha-gpioled"},{}
};/* 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 leddriver_init(void)
{return platform_driver_register(&led_driver);
}/*** @description: 驱动出口函数* @param : 无*/
static void __exit leddriver_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(leddriver_init);
module_exit(leddriver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaneki");