一,过程概述
驱动分离模型主要用途是讲驱动的硬件相关代码与硬件无关代码分离,提高驱动的移植性,可重复性。主要编写2个文件,一个driver文件和一个device文件
1. device文件的操作
a)创建一个 struct platform_device 结构体 static struct platform_device led_device
,设置 name, name必须与driver 文件下的struct platform_driver.driver.name 一致. 内核根据name来匹配driver是否支持某个device
b) 创建一个 struct resource 结构体, 里面主要包括硬件相关信息内存地址 中断号 等等,resource 结构体包含在 platform_device 结构体中,这样使两者关联起来
c) 在驱动 init 函数中注册 platform_device platform_device_register(&led_device); 在驱动exit函数中卸载 platform_device_unregister(&led_device);
2. deriver 文件的操作
a) 创建一个 struct platform_driver 结构体 static struct platform_driver led_drv 设置 probe 函数,当内核匹配上同名的 platform_device 结构体时调用。remove 函数 ,
当内核发 现与之匹配的 platform_device 驱动被卸载时调用。 driver.name 这个name 必须与platform_device 的name 保持一致。内核通过该项来进行匹配
b) 在驱动 init 函数中注册 struct platform_driver platform_driver_register(&led_drv);; 在驱动exit函数中卸载 platform_driver_unregister(&led_drv);
c) 在 probe 函数中进行真正的驱动相关操作。 获取platform_device 中的硬件相关信息 resource 。
二 示例代码
1. device文件代码
#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>
#include <linux/module.h>
#include <linux/version.h>
#include "s3c6410.h"
void led_device_release(struct device *dev)
{
printk(KERN_INFO"led dev, release led\n");
}
static struct resource led_res[] = {
[0] = {
.start = ELFIN_GPIO_BASE+GPKCON0_OFFSET,
.end = ELFIN_GPIO_BASE+GPKCON0_OFFSET + 16 -1,
.flags = IORESOURCE_MEM
},
[1] = {
.start = 4,
.end = 4,
.flags = IORESOURCE_IRQ
}
};
static struct platform_device led_device = {
.name = "led_drv",
.id = -1,
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
.dev = {
.release = led_device_release,
}
};
static int len_dev_init(void)
{
platform_device_register(&led_device);
return 0;
}
static void len_dev_exit(void)
{
platform_device_unregister(&led_device);
}
module_init(len_dev_init);
module_exit(len_dev_exit);
MODULE_LICENSE("GPL");
2. driver文件代码
#include <linux/module.h>
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/io.h>
#include "s3c6410.h"
static int major;
static struct class *led_class;
static struct device *led_class_dev;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;
static int led_drv_open(struct inode *inode, struct file *file)
{
*gpio_con &= ~(0xf<<(pin*4));
*gpio_con |= (0x1<<(pin*4));
return 0;
}
static ssize_t led_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
int val = 0;
if(copy_from_user(&val, buf, count))
return -EINVAL;
if(val)
{
*gpio_dat &= ~(1<<pin);
}
else
{
*gpio_dat |= (1<<pin);
}
return 0;
}
static struct file_operations led_drv_fops =
{
.owner = THIS_MODULE,
.open = led_drv_open,
.write = led_drv_write,
};
static int led_probe(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio_con = ioremap(res->start, res->end - res->start + 1);
gpio_dat = gpio_con+2;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
pin = res->start;
major= register_chrdev(0, "led_drv", &led_drv_fops);
led_class = class_create(THIS_MODULE, "led_class");
if(IS_ERR(led_class))
return PTR_ERR(led_class);
led_class_dev = device_create(led_class, NULL, MKDEV(major, 0), NULL, "led");
if(unlikely(IS_ERR(led_class_dev)))
return PTR_ERR(led_class_dev);;
printk(KERN_INFO"led drv, found led\n");
return 0;
}
static int led_remove(struct platform_device *pdev)
{
device_unregister(led_class_dev);
class_destroy(led_class);
unregister_chrdev(major, "led_drv");
iounmap(gpio_con);
printk(KERN_INFO"led drv, remove led\n");
return 0;
}
static struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "led_drv",
}
};
static int led_drv_init(void)
{
platform_driver_register(&led_drv);
return 0;
}
static void led_drc_exit(void)
{
platform_driver_unregister(&led_drv);
}
module_init(led_drv_init);
module_exit(led_drc_exit);
MODULE_LICENSE("GPL");