platform驱动分离

---恢复内容开始---

--- title: platform驱动分离 tags: linux date: 2018/11/30 09:24:37 toc: true ---

platform驱动分离

框架结构

platformbus总线结构将一个硬件驱动分为device设备与driver驱动两个部分,使用platform设备将两个联系到一起.

简单来说,就是将以前所写的驱动,将数据与算法分离,左边dev表示可变的数据,右边的driver表示稳定的算法.比如I2C/USB等总线协议可以理解为稳定的算法,具体的不同的EEPROM/led等可以理解为数据,包含了特殊的自身信息,比如io信息等.

mark

与输入子系统联系

输入子系统是内核定义了一套统一的标准接口,而platform是将左侧的设备层分割为数据与算法

mark

mark

设备描述

这里的设备文件使用platform_device来描述,结构如下,总体来说可以称之为设备描述

  • platform_device_register 注册设备文件,匹配后会调用驱动算法的probe
  • platform_device_unregister卸载设备
  • 具体数据描述如下

驱动算法

这里将其称为驱动算法,其实也是一个正常的驱动程序,区别在于可以获取到上面注册的设备文件信息.当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_register用来注册驱动,当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.
  • platform_driver_unregister用来卸载驱动
  • platform框架中的管理结构如下:
    //driver
    struct platform_driver {
        //函数
        (*probe)		//匹配到设备文件的name与driver中的name一致将会在注册时执行
        (*remove)		//卸载驱动出口,卸载驱动设备的时候应该也会调用
        (*shutdown)
        (*suspend)
        (*suspend_late)
        (*resume_early)
        (*resume)
        //结构体
        struct device_driver driver
        {
            .name		//用作匹配
        }
    };
  • probe,当有设备文件和驱动算法匹配的时候自动执行,一般用来获取资源文件信息等,注册驱动,ioremap等,可以理解为执行驱动的第一个程序了

注意:

  1. 这里的platform_driver也是一个普通的驱动,可以用最原始的方式来注册驱动,创建class类,再在下面添加具体的设备文件来使用mdev机制自动创建所需要的文件.
  2. 也可以使用输入子系统的 框架,输入子系统所需要的驱动程序需要一套约定的接口,我们可以将输入子系统中的左侧的设备驱动再分割成设备描述与驱动算法.

mark

注册机制

驱动算法使用platform_driver_register来注册,如下:

int platform_driver_register(struct platform_driver *drv)
{
   drv->driver.bus = &platform_bus_type;   //(1)挂接到虚拟总线platform_bus_type上
   if (drv->probe)
         drv->driver.probe = platform_drv_probe;
   if (drv->remove)
        drv->driver.remove = platform_drv_remove;
   if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;
   if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;

   return driver_register(&drv->driver);      //(2) 注册到driver目录下
}
  1. 从代码上看,platform只是只是bus中的一种,挂载到platform_bus_type

    struct bus_type platform_bus_type = {              
        .nam			= "platform",           //设备名称
        .dev_attrs		= platform_dev_attrs,   //设备属性、含获取sys文件名,该总线会放在/sys/bus下
        .match			= platform_match,   	//匹配函数,成功调用driver的.probe函数
        .uevent			= platform_uevent,  	//消息传递,比如热插拔操作
        .suspend		= platform_suspend,     //电源管理的低功耗挂起
        .suspend_late	= platform_suspend_late,  
        .resume_early	= platform_resume_early,
        .resume			= platform_resume,   //恢复
    };
    
  2. 可以看到匹配函数match=platform_match,是匹配devdrivername

    static int platform_match(struct device * dev, struct device_driver * drv)
    {
        /*找到所有的device设备*/
    	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
    
  3. driver_register()函数就是用来创建/sys/bus/platform/driver

程序

设备文件

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>


/* 分配/设置/注册一个platform_device */

static struct resource led_resource[] = {
    [0] = {
        .start = 0x56000050,
        .end   = 0x56000050 + 8 - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 5,
        .end   = 5,
        .flags = IORESOURCE_IRQ,
    }

};

static void led_release(struct device * dev)
{
}


static struct platform_device led_dev = {
    .name         = "myled",
    .id       = -1,
    .num_resources    = ARRAY_SIZE(led_resource),
    .resource     = led_resource,
    .dev = { 
    	.release = led_release, 
	},
};

static int led_dev_init(void)
{
	platform_device_register(&led_dev);
	return 0;
}

static void led_dev_exit(void)
{
	platform_device_unregister(&led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");


驱动算法

/* 分配/设置/注册一个platform_driver */
#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>

static int major;


static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;

static int led_open(struct inode *inode, struct file *file)
{
	//printk("first_drv_open\n");
	/* 配置为输出 */
	*gpio_con &= ~(0x3<<(pin*2));
	*gpio_con |= (0x1<<(pin*2));
	return 0;	
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;

	//printk("first_drv_write\n");

	copy_from_user(&val, buf, count); //	copy_to_user();

	if (val == 1)
	{
		// 点灯
		*gpio_dat &= ~(1<<pin);
	}
	else
	{
		// 灭灯
		*gpio_dat |= (1<<pin);
	}
	
	return 0;
}


static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,     
	.write	=	led_write,	   
};

static int led_probe(struct platform_device *pdev)
{
	struct resource		*res;

	/* 根据platform_device的资源进行ioremap */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	gpio_con = ioremap(res->start, res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	pin = res->start;

	/* 注册字符设备驱动程序 */

	printk("led_probe, found led\n");

	major = register_chrdev(0, "myled", &led_fops);

	cls = class_create(THIS_MODULE, "myled");

	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
	
	return 0;
}

static int led_remove(struct platform_device *pdev)
{
	/* 卸载字符设备驱动程序 */
	/* iounmap */
	printk("led_remove, remove led\n");

	class_device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "myled");
	iounmap(gpio_con);
	
	return 0;
}


struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};


static int led_drv_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* led_test on
 * led_test off
 */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/led", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 4);
	return 0;
}

测试

  1. 注册两个驱动,会自动生成文件/sys/bus/platform/下的devicesdrives生成驱动
  2. 测试使用./test on 点灯

    ---恢复内容结束---

    ---
    title: platform驱动分离
    tags: linux
    date: 2018/11/30 09:24:37
    toc: true

platform驱动分离

框架结构

platformbus总线结构将一个硬件驱动分为device设备与driver驱动两个部分,使用platform设备将两个联系到一起.

简单来说,就是将以前所写的驱动,将数据与算法分离,左边dev表示可变的数据,右边的driver表示稳定的算法.比如I2C/USB等总线协议可以理解为稳定的算法,具体的不同的EEPROM/led等可以理解为数据,包含了特殊的自身信息,比如io信息等.

mark

与输入子系统联系

输入子系统是内核定义了一套统一的标准接口,而platform是将左侧的设备层分割为数据与算法

mark

mark

设备描述

这里的设备文件使用platform_device来描述,结构如下,总体来说可以称之为设备描述

  • platform_device_register 注册设备文件,匹配后会调用驱动算法的probe
  • platform_device_unregister卸载设备
  • 具体数据描述如下

驱动算法

这里将其称为驱动算法,其实也是一个正常的驱动程序,区别在于可以获取到上面注册的设备文件信息.当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_register用来注册驱动,当注册设备文件或者注册驱动文件的时候,都会调用驱动算法的probe.

  • platform_driver_unregister用来卸载驱动

  • platform框架中的管理结构如下:

    //driver
    struct platform_driver {
        //函数
        (*probe)		//匹配到设备文件的name与driver中的name一致将会在注册时执行
        (*remove)		//卸载驱动出口,卸载驱动设备的时候应该也会调用
        (*shutdown)
        (*suspend)
        (*suspend_late)
        (*resume_early)
        (*resume)
        //结构体
        struct device_driver driver
        {
            .name		//用作匹配
        }
    };
    
  • probe,当有设备文件和驱动算法匹配的时候自动执行,一般用来获取资源文件信息等,注册驱动,ioremap等,可以理解为执行驱动的第一个程序了

注意:

  1. 这里的platform_driver也是一个普通的驱动,可以用最原始的方式来注册驱动,创建class类,再在下面添加具体的设备文件来使用mdev机制自动创建所需要的文件.
  2. 也可以使用输入子系统的 框架,输入子系统所需要的驱动程序需要一套约定的接口,我们可以将输入子系统中的左侧的设备驱动再分割成设备描述与驱动算法.

mark

注册机制

驱动算法使用platform_driver_register来注册,如下:

int platform_driver_register(struct platform_driver *drv)
{
   drv->driver.bus = &platform_bus_type;   //(1)挂接到虚拟总线platform_bus_type上
   if (drv->probe)
         drv->driver.probe = platform_drv_probe;
   if (drv->remove)
        drv->driver.remove = platform_drv_remove;
   if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;
   if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;

   return driver_register(&drv->driver);      //(2) 注册到driver目录下
}
  1. 从代码上看,platform只是只是bus中的一种,挂载到platform_bus_type

    struct bus_type platform_bus_type = {              
        .nam			= "platform",           //设备名称
        .dev_attrs		= platform_dev_attrs,   //设备属性、含获取sys文件名,该总线会放在/sys/bus下
        .match			= platform_match,   	//匹配函数,成功调用driver的.probe函数
        .uevent			= platform_uevent,  	//消息传递,比如热插拔操作
        .suspend		= platform_suspend,     //电源管理的低功耗挂起
        .suspend_late	= platform_suspend_late,  
        .resume_early	= platform_resume_early,
        .resume			= platform_resume,   //恢复
    };
    
  2. 可以看到匹配函数match=platform_match,是匹配devdrivername

    static int platform_match(struct device * dev, struct device_driver * drv)
    {
        /*找到所有的device设备*/
    	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }
    
  3. driver_register()函数就是用来创建/sys/bus/platform/driver

程序

设备文件

#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>


/* 分配/设置/注册一个platform_device */

static struct resource led_resource[] = {
    [0] = {
        .start = 0x56000050,
        .end   = 0x56000050 + 8 - 1,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = 5,
        .end   = 5,
        .flags = IORESOURCE_IRQ,
    }

};

static void led_release(struct device * dev)
{
}


static struct platform_device led_dev = {
    .name         = "myled",
    .id       = -1,
    .num_resources    = ARRAY_SIZE(led_resource),
    .resource     = led_resource,
    .dev = { 
    	.release = led_release, 
	},
};

static int led_dev_init(void)
{
	platform_device_register(&led_dev);
	return 0;
}

static void led_dev_exit(void)
{
	platform_device_unregister(&led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");


驱动算法

/* 分配/设置/注册一个platform_driver */
#include <linux/module.h>
#include <linux/version.h>

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>

static int major;


static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;

static int led_open(struct inode *inode, struct file *file)
{
	//printk("first_drv_open\n");
	/* 配置为输出 */
	*gpio_con &= ~(0x3<<(pin*2));
	*gpio_con |= (0x1<<(pin*2));
	return 0;	
}

static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;

	//printk("first_drv_write\n");

	copy_from_user(&val, buf, count); //	copy_to_user();

	if (val == 1)
	{
		// 点灯
		*gpio_dat &= ~(1<<pin);
	}
	else
	{
		// 灭灯
		*gpio_dat |= (1<<pin);
	}
	
	return 0;
}


static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,     
	.write	=	led_write,	   
};

static int led_probe(struct platform_device *pdev)
{
	struct resource		*res;

	/* 根据platform_device的资源进行ioremap */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	gpio_con = ioremap(res->start, res->end - res->start + 1);
	gpio_dat = gpio_con + 1;

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	pin = res->start;

	/* 注册字符设备驱动程序 */

	printk("led_probe, found led\n");

	major = register_chrdev(0, "myled", &led_fops);

	cls = class_create(THIS_MODULE, "myled");

	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
	
	return 0;
}

static int led_remove(struct platform_device *pdev)
{
	/* 卸载字符设备驱动程序 */
	/* iounmap */
	printk("led_remove, remove led\n");

	class_device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "myled");
	iounmap(gpio_con);
	
	return 0;
}


struct platform_driver led_drv = {
	.probe		= led_probe,
	.remove		= led_remove,
	.driver		= {
		.name	= "myled",
	}
};


static int led_drv_init(void)
{
	platform_driver_register(&led_drv);
	return 0;
}

static void led_drv_exit(void)
{
	platform_driver_unregister(&led_drv);
}

module_init(led_drv_init);
module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

测试程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* led_test on
 * led_test off
 */
int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/led", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp(argv[1], "on") == 0)
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, 4);
	return 0;
}

测试

  1. 注册两个驱动,会自动生成文件/sys/bus/platform/下的devicesdrives生成驱动
  2. 测试使用./test on 点灯
posted @ 2018-12-03 09:56  zongzi10010  阅读(255)  评论(0编辑  收藏  举报