platform平台总线

一、platform设备模型

从Linux 2.6起引入了一套新的驱动管理和注册机制,platform_device和platform_driver,Linux中大部分的设备驱动都可以使用这套机制。platform是一条虚拟总线。设备用platform_device表示,驱动用platform_driver进行注册,linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。当我们将设备和驱动注册到虚拟总线上(内核)时,会互相寻找一次对方。如果device和driver中的name这个字符串是想相同的话platform_bus就会调用driver中的.probe函数。设备和驱动的关系是多对一的关系,即多个相同设备可使用一个driver,靠device(设备)中的id号来区别。

通过platform机制开发底层驱动的大致流程为:

定义platform_deviece -->注册platform_device -->定义platform_driver --> 注册platform_driver。

二、注册platform_device

在内核源码中添加arch/arm/mach-s5pv210/include/mach/leds-gpio.h文件

/* arch/arm/mach-s5pv210/include/mach/leds-gpio.h
 *
 * Copyright (c) 2006 Simtec Electronics
 *	http://armlinux.simtec.co.uk/
 *	Ben Dooks <[email protected]>
 *
 * s5pv210 - LEDs GPIO connector
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#ifndef __ASM_ARCH_LEDSGPIO_H
#define __ASM_ARCH_LEDSGPIO_H "leds-gpio.h"

#define S5PV210_LEDF_ACTLOW	(1<<0)		/* LED is on when GPIO low */
#define S5PV210_LEDF_TRISTATE	(1<<1)		/* tristate to turn off */

struct s5pv210_led_platdata {
	unsigned int		 gpio;
	unsigned int		 flags;

	char			*name;
	char			*def_trigger;
};

#endif /* __ASM_ARCH_LEDSGPIO_H */

在内核arch/arm/mach-s5pv210/mach-x210.c文件中添加如下代码

/* LEDS */
static struct s5pv210_led_platdata x210_led1_pdata = {
	.name		= "led1",
	.gpio		= S5PV210_GPJ0(3),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct s5pv210_led_platdata x210_led2_pdata = {
	.name		= "led2",
	.gpio		= S5PV210_GPJ0(4),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct s5pv210_led_platdata x210_led3_pdata = {
	.name		= "led3",
	.gpio		= S5PV210_GPJ0(5),
	.flags		= S5PV210_LEDF_ACTLOW | S5PV210_LEDF_TRISTATE,
	.def_trigger	= "",
};

static struct platform_device x210_led1 = {
	.name		= "s5pv210_led",
	.id		= 0,
	.dev		= {
		.platform_data	= &x210_led1_pdata,
	},
};

static struct platform_device x210_led2 = {
	.name		= "s5pv210_led",
	.id		= 1,
	.dev		= {
		.platform_data	= &x210_led2_pdata,
	},
};

static struct platform_device x210_led3 = {
	.name		= "s5pv210_led",
	.id		= 2,
	.dev		= {
		.platform_data	= &x210_led3_pdata,
	},
};

static struct platform_device *smdkc110_devices[] __initdata = {
    
    ...

	&x210_led1,
	&x210_led2,
	&x210_led3,
};

重新编译内核,三个led设备就会被注册到platform中,在/sys/bus/platform/devices中可以进行查看

三、注册platform_driver

#include <linux/module.h>
#include <linux/init.h>
#include <linux/leds.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
#include <linux/slab.h>

struct s5pv210_gpio_led {
	struct led_classdev		 cdev;
	struct s5pv210_led_platdata	*pdata;
};

static inline struct s5pv210_gpio_led *pdev_to_gpio(struct platform_device *dev)
{
	return platform_get_drvdata(dev);
}

static inline struct s5pv210_gpio_led *to_gpio(struct led_classdev *led_cdev)
{
	return container_of(led_cdev, struct s5pv210_gpio_led, cdev);
}

static void s5pv210_led_set(struct led_classdev *led_cdev, enum led_brightness value)
{
	struct s5pv210_gpio_led *led = to_gpio(led_cdev);
	struct s5pv210_led_platdata *pd = led->pdata;

	printk(KERN_INFO "s5pv210_led_set\n");
	
	/* 设置gpio状态 */
	if (value == LED_OFF)
		gpio_set_value(pd->gpio, 1);
	else
		gpio_set_value(pd->gpio, 0);
}

static int s5pv210_led_probe(struct platform_device *dev)
{
	struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
	struct s5pv210_gpio_led *led;
	int ret = -1;
	
	printk(KERN_INFO "s5pv210_led_probe\n");
	
	led = kzalloc(sizeof(struct s5pv210_gpio_led), GFP_KERNEL);
	if (led == NULL) {
		dev_err(&dev->dev, "No memory for device\n");
		return -ENOMEM;
	}

	platform_set_drvdata(dev, led);
	
	/* 申请gpio资源 */
	if (gpio_request(pdata->gpio, pdata->name))
	{
		printk(KERN_ERR "gpio_request failed\n");
		return -EINVAL;
	}

	/* 设置gpio方向为输出 */
	gpio_direction_output(pdata->gpio, 1);
	
	led->cdev.name = pdata->name;
	led->cdev.brightness = 0;	
	led->cdev.brightness_set = s5pv210_led_set;

	led->pdata = pdata;

	/* 注册led设备 */
	ret = led_classdev_register(&dev->dev, &led->cdev);
	if (ret < 0) {
		printk(KERN_ERR "led_classdev_register failed\n");
		return ret;
	}
	
	return 0;	
}

static int s5pv210_led_remove(struct platform_device *dev)
{
	struct s5pv210_gpio_led *led = pdev_to_gpio(dev);
	struct s5pv210_led_platdata *pd = led->pdata;
	
	/* 注销led设备 */
	led_classdev_unregister(&led->cdev);
	
	/* 释放gpio资源 */
	gpio_free(pd->gpio);

	kfree(led);
	
	return 0;
}

static struct platform_driver s5pv210_led_driver = {
	.probe		= s5pv210_led_probe,
	.remove		= s5pv210_led_remove,
	.driver		= {
		.name		= "s5pv210_led",
		.owner		= THIS_MODULE,
	},
};

static int __init s5pv210_led_init(void)
{
	return platform_driver_register(&s5pv210_led_driver);
}

static void __exit s5pv210_led_exit(void)
{
	platform_driver_unregister(&s5pv210_led_driver);
}

module_init(s5pv210_led_init);
module_exit(s5pv210_led_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");							// 描述模块的许可证
MODULE_AUTHOR("lsm");							// 描述模块的作者
MODULE_DESCRIPTION("s5pv210 led driver");		// 描述模块的介绍信息
MODULE_ALIAS("s5pv210_led");					// 描述模块的别名信息

加载驱动之后,在/sys/bus/platform/drivers中可以进行查看

四、测试

加载好led驱动之后,进入/sys/class/leds

猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/86540390