Linux驱动开发 (platform平台总线驱动LED)

1.platform平台总线简介

-> 相对于usb、pci、i2c等物理总线来说,platform总线是虚拟的、抽象出来的

-> CPU与外部通信的2种方式:地址总线式连接和专用接口式连接。平台总线对应地址总线式连接设备,也就是SoC内部集成的各种内部外设。

-> platform工作体系都定义在drivers/base/platform.c中。

-> 两个结构体:platform_device和platform_driver。

2.platform_device结构体

struct platform_device {
	const char	* name;			// 平台总线下设备的名字
	int		id;
	struct device	dev;		// 所有设备通用的属性部分
	u32		num_resources;		// 设备使用到的resource的个数
	struct resource	* resource;	// 设备使用到的资源数组的首地址

	const struct platform_device_id	*id_entry;	// 设备ID表

	/* arch specific additions */
	struct pdev_archdata	archdata;			// 自留地,用来提供扩展性的
};

3.platform_driver

struct platform_driver {
	int (*probe)(struct platform_device *);		// 驱动探测函数
	int (*remove)(struct platform_device *);	// 去掉一个设备
	void (*shutdown)(struct platform_device *);	// 关闭一个设备
	int (*suspend)(struct platform_device *, pm_message_t state);  //电源管理支持的挂起
	int (*resume)(struct platform_device *);                 //挂起对应的恢复
	struct device_driver driver;				// 所有设备共用的一些属性
	const struct platform_device_id *id_table;	// 设备ID表
};

4.platform平台总线工作原理

-> 系统启动时在bus系统中注册platform。

-> 内核移植的人负责提供platform_device。

-> 写驱动的人负责提供platform_driver。

-> platform的match函数发现driver和device匹配后,调用driver的probe函数来完成驱动的初始化和安装,然后设备就工作起来了。

5.平台总线的匹配方法match

static int platform_match(struct device *dev, struct device_driver *dr
{
 struct platform_device *pdev = to_platform_device(dev);
 struct platform_driver *pdrv = to_platform_driver(drv);
 
 /* Attempt an OF style match first */
 if (of_driver_match_device(dev, drv))
 return 1;
 
 /* Then try ACPI style match */
 if (acpi_driver_match_device(dev, drv))
 return 1;
 
 /* Then try to match against the id table */
 if (pdrv->id_table)
 return platform_match_id(pdrv->id_table, pdev) != NULL
 
 /* fall-back to driver name match */
 return (strcmp(pdev->name, drv->name) == 0);
}

每种总线(不光是platform,usb、i2c那些也是)都会带一个match方法,match方法用来对总线下的device和driver进行匹配

platform_match函数就是平台总线的匹配方法:

-> 如果有id_table就说明驱动可能支持多个设备,所以这时候要去对比id_table中所有的name,只要找到一个相同的就匹配上了不再找了。

-> 如果找完id_table都还没找到就说明没匹配上。

-> 如果没有id_table或者每匹配上,那就直接对比device和driver的name,如果匹配上就匹配上了,如果还没匹配上那就匹配失败。

6.platform设备和驱动的注册过程

两个接口函数:platform_device_register和platform_driver_register。

platform_driver_register:

int platform_driver_register(struct platform_driver *drv)
{
	drv->driver.bus = &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;

	return driver_register(&drv->driver);
}

platform_device_register:

int platform_device_register(struct platform_device *pdev)
{
	device_initialize(&pdev->dev);
	return platform_device_add(pdev);
}

7.platdata设备注册时提供的设备有关的一些数据

-> platdata其实就是设备注册时提供的设备有关的一些数据(譬如设备对应的gpio、使用到的中断号、设备名称····)

-> 这些数据在设备和驱动match之后,会由设备方转给驱动方。驱动拿到这些数据后,通过这些数据得知设备的具体信息,然后来操作设备。

在这里插入图片描述

8.probe函数的功能和意义 

probe在设备驱动被注册到内核中的时候,被总线型驱动调用。
总线驱动类似于用轮训方法bai探测总线上的所有设备,将设备的识别型信息和关键数据结构 (pci ids, usb ids, i2c ids and etc.)传递给probe函数,probe就会识别是否是自己负责驱动的设备,并负责完成该设备的初始化操作。
 

9.platform平台总线的LED驱动代码实践

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/fs.h>
#include <linux/leds.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <mach/gpio.h>
#include <linux/platform_device.h>
#include <mach/leds-gpio.h>
#include <linux/slab.h>


#define X210_LED_OFF	1			// X210中LED是正极接电源,负极节GPIO
#define X210_LED_ON		0			// 所以1是灭,0是亮

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 *p = to_gpio(led_cdev);
	
	printk(KERN_INFO "s5pv210_led_set\n");
	
	// 在这里根据用户设置的值来操作硬件
	// 用户设置的值就是value
	if (value == LED_OFF)
	{
		// 用户给了个0,希望LED灭
		gpio_set_value(p->pdata->gpio, X210_LED_OFF);
	}
	else
	{
		// 用户给的是非0,希望LED亮
		gpio_set_value(p->pdata->gpio, X210_LED_ON);
	}
}


static int s5pv210_led_probe(struct platform_device *dev)
{
	// 用户insmod安装驱动模块时会调用该函数
	// 该函数的主要任务就是去使用led驱动框架提供的设备注册函数来注册一个设备
	int ret = -1;
	struct s5pv210_led_platdata *pdata = dev->dev.platform_data;
	struct s5pv210_gpio_led *led;
	
	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");
	} 
	else 
	{
		// 设置为输出模式,并且默认输出1让LED灯灭
		gpio_direction_output(pdata->gpio, 1);
	}
	
	// led1
	led->cdev.name = pdata->name;
	led->cdev.brightness = 0;	
	led->cdev.brightness_set = s5pv210_led_set;
	led->pdata = pdata;
	
	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 *p = pdev_to_gpio(dev);

	led_classdev_unregister(&p->cdev);
	gpio_free(p->pdata->gpio);
	kfree(p);								// kfee放在最后一步
	
	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("aston <1264671872@qq.com>");		// 描述模块的作者
MODULE_DESCRIPTION("s5pv210 led driver");		// 描述模块的介绍信息
MODULE_ALIAS("s5pv210_led");					// 描述模块的别名信息

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式_笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值