Linux的总线设备驱动模型

在Linux中,一开始bus下的device有一个device链表,driver也有一个链表,当注册platform_device的时候,会把新的platform_device结构体放入device链表,然后从driver的链表中把已有的driver一个一个地取出来跟platform_device结构体中的name进行比较,如果匹配的话,就会调用platform_driver结构体中的probe函数,此时才会成功创建设备节点。反之先注册driver也一样,所以先注册platform_device还是先注册platform_driver都可以,最终如果匹配,都会去调用platform_driver中的probe函数。

在platform_device.h文件中定义了platform_device与platform_driver结构体:

struct platform_device {
	const char	* name;
	int		id;
	struct device	dev;
	u32		num_resources;
	struct resource	* resource;

	const struct platform_device_id	*id_entry;

	/* MFD cell pointer */
	struct mfd_cell *mfd_cell;

	/* arch specific additions */
	struct pdev_archdata	archdata;
};
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;
};

在platform_driver结构体中,成员platform_device_id结构体中有成员name:

struct platform_device_id {
	char name[PLATFORM_NAME_SIZE];
	kernel_ulong_t driver_data
			__attribute__((aligned(sizeof(kernel_ulong_t))));
};

成员struct device_driver中也有成员name:

struct device_driver {
	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;

	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;
};

platform_device与platform_driver通过platform.c文件中的platform_match函数进行匹配,优先比较id_table,如果没有id_table,则比较pdev->name与pdrv->name:

static int platform_match(struct device *dev, struct device_driver *drv)
{
	...
	/* 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);
}

在match函数中通过platform_match_id函数进行两者的比较:

static const struct platform_device_id *platform_match_id(const struct platform_device_id *id, struct platform_device *pdev)
{
	while (id->name[0]) {
		if (strcmp(pdev->name, id->name) == 0) {
			pdev->id_entry = id;
			return id;
		}
		id++;
	}
	return NULL;
}

传统的字符设备驱动程序写法思路是这样的:
分配、设置一个file_operations结构体,通过file_operations结构体中的成员函数完成对led的驱动,完善led_open函数,led_write函数和led_release函数。

static int led_open(struct inode *node, struct file *filp)
{
	......
}
static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
	......
}
static int led_release(struct inode *node, struct file *filp)
{
	......
}

static struct file_operations myled_oprs = {
	.owner   = THIS_MODULE,
	.open    = led_open,
	.write   = led_write,
	.release = led_release, 
};

通过init函数与exit函数完成字符设备的注册或卸载,类与设备节点的创建和注销:

static int myled_init(void)
{
	major = register_chrdev(0, "myled", &myled_oprs);

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led");
	
	return 0;
}

static void myled_exit(void)
{
	unregister_chrdev(major, "myled");
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);
}

以上是传统字符设备驱动程序的框架结构。
而在总线设备驱动模型中,一个字符设备驱动程序的框架是这样的,需要有device.c和driver.c两个文件,device文件中通过platform_device结构体中的成员指定硬件资源:

static struce resource led_resource[] = {
	[0] = {
		.start = s3c2440_GPF(5),
		.end   = s3c2440_GPF(5),
		.flags = IORESOURCE_MEM,
	},
};

static void led_release(struct device * dev)
{
}

/* 分配/设置一个platform_device */
static struct platform_device led_dev = {
	.name          = "myled",
	.id            = -1,
	.num_resources = ARREY_SIZE(led_resource),
	.resource      = led_resource,
};
 
/* 注册platform_device */
static int led_dev_init(void)
{
	platform_device_register(&led_dev);

	return 0;
}
/* 卸载platform_device */
static void led_dev_exit(void)
{
	platform_device_unregister(&led_dev);
}

module_init(led_dev_init);
module_exit(led_dev_exit);

MODULE_LICENSE("GPL");

在driver文件中是分配、设置一个platform_driver结构体,包含probe函数与remove函数:

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

传统字符设备驱动程序的init函数中对字符设备的注册、类的创建与设备节点的创建现在在probe函数中完成:

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

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

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

	led_class = class_create(THIS_MODULE, "myled");
	device_create(led_class, NULL, MKDEV(major, 0), NULL, "led");	/* /dev/led */

	return 0;
}

在probe函数中会调用file_operations结构体中的open函数、write函数等,但该open函数中不再进行字符设备的注册等操作,release函数中不再进行字符设备的卸载等操作:

static int led_open(struct inode *node, struct file *filp)
{
	/* 设置LED引脚为输出 */
	/* GPF5 - 0x56000050 */
	int bank = led_pin >> 16;	/* 先获得端口信息 */
	int base = gpio_base[bank];	/* 获得寄存器GPFCON的地址*/

	int pin = led_pin & 0xffff;	/* 再获得引脚信息 */
	gpio_con = ioremap(base, 8);
	gpio_dat = gpio_con + 1;

	*gpio_con &= ~(3 << (pin * 2));
	*gpio_con |= (1 << (pin * 2));

	return 0;
}

static ssize_t led_write(struct file *filp, const char __user *buf, size_t size, loff_t *off)
{
	/* 根据app传入的值设置led引脚 */
	unsigned char val;
	int pin = led_pin & 0xffff;

	copy_from_user(&val, buf, 1);

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

	return 1;	/* 已写入1个数据 */
}

static int led_release(struct inode *node, struct file *filp)
{
	iounmap(gpio_con);

	return 0;
}

static struct file_operations myled_oprs = {
	.owner   = THIS_MODULE,
	.open    = led_open,
	.write   = led_write,
	.release = led_release, 
};

对字符设备的卸载、类与设备节点的注销在remove函数中完成:

static int led_remove(struct platform_device * pdev)
{
	unregister_chrdev(major, "myled");
	device_destroy(led_class, MKDEV(major, 0));
	class_destroy(led_class);

	return 0;
}

现在的init函数中只是注册platform_driver,在exit函数中卸载platform_driver:

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

static void myled_exit(void)
{
	platform_driver_unregister(&led_drv);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值