1. 使用platform机制,编写LED驱动层
首先创建设备代码和驱动代码:led_dev.c 、led_drv.c
led_dev.c用来指定灯的引脚地址,当更换平台时,只需要修改这个就行
led_drv.c用来初始化灯以及如何控制灯的逻辑,当更换控制逻辑时,只需要修改这个就行
2.编写led.dev.c
2.1编写led_dev.c之前先来看看platform_device结构体和要使用的函数:
platform_device结构体如下:
struct platform_device {
const char * name; //设备名称,要与platform_driver的name一样,这样总线才能匹配成功
u32 id; //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1
struct device dev; //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)
u32 num_resources; //资源数量,
struct resource * resource; //资源结构体,保存设备的信息
};
其中resource资源结构体,如下:
struct resource {
resource_size_t start; //起始资源,如果是地址的话,必须是物理地址
resource_size_t end; //结束资源,如果是地址的话,必须是物理地址
const char *name; //资源名
unsigned long flags; //资源的标志
//比如IORESOURCE_MEM,表示地址资源, IORESOURCE_IRQ表示中断引脚... ...
struct resource *parent, *sibling, *child; //资源拓扑指针父、兄、子,可以构成链表
};
要用的函数如下,在dev设备的入口出口函数中用到
int platform_device_register(struct platform_device * pdev); //注册dev设备
int platform_device_unregister(struct platform_device * pdev); //注销dev设备
2.2代码如下
/*GPFCON 0xE0200280//本驱动使用,注意unsigned long 为4字节(4*8 bit),相当与ff
GPFDAT 0xE0200284/本驱动使用
GPFUP 0xE0200288//本驱动不使用,上拉电阻在驱动中不使用到,
*/
static struct resource led_resource[] = {
[0] = {
.start = 0xE0200280,
.end = 0xE0200280 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = 0,
.end = 0,
.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 __init led_dev_init(void)
{
platform_device_register(&led_dev);
return 0;
}
static void __exit led_dev_exit(void)
{
platform_device_unregister(&led_dev);
}
3.编写led.drv.c
3.1编写led_dev.c之前先来看看platform_device结构体和要使用的函数:
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 (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *); //唤醒
struct device_driver driver; //内嵌的driver,其中的name成员要等于设备的名称才能匹配
};
int platform_driver_register(struct platform_driver *drv); //注册驱动
platform_driver_unregister(struct platform_driver *drv); //卸载驱动
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type,unsigned int num);
//获取设备的某个资源,获取成功,则返回一个resource资源结构体
//参数:
// *dev :指向某个platform device设备
// type:获取的资源类型
// num: type资源下的第几个数组
3.2先写要注册的led驱动:platform_driver结构体
/*函数声明*/
static int led_remove(struct platform_device *led_dev);
static int led_probe(struct platform_device *led_dev);
struct platform_driver led_drv = {
.probe = led_probe, //当与设备匹配,则调用该函数
.remove = led_remove, //删除设备
.driver = {
.name = "myled", //与设备名称一样
}
};
3.3写file_operations 结构体、以及成员函数(.open、.write)、.probe函数、
当驱动和设备都insmod加载后,然后bus总线会匹配成功,就进入.probe函数,
在.probe函数中便使用platform_get_resource()函数获取LED的地址和引脚,然后初始化LED,并注册字符设备和设备节点"led"
tatic 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)
{
*gpio_con &= ~(0xf<<(pin*4));
*gpio_con |= (0x1<<(pin*4));
return 0;
}
static ssize_t led_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
int val = 0;
copy_from_user(&val,buf,count);
if(val == 1)
{
*gpio_dat &= ~(1<<pin);
}
else
{
*gpio_dat |= (1<<pin);
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
static int led_probe(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev,IORESOURCE_MEM,0);//获得第0+1个IORESOURCE_MEM资源
gpio_con = ioremap(res->start,res->end - res->start + 1);
gpio_dat = gpio_con + 1;
res = platform_get_resource(pdev,IORESOURCE_IRQ,0);//获得第0+1个IORESOURCE_IRQ资源
pin = res->start;
printk("led_probe, found led\n");
major = register_chrdev(0,"myled",&led_fops);
cls = class_create(THIS_MODULE,"myled");
device_create(cls,NULL,MKDEV(major,0),NULL,"led");// /dev/led
return 0;
}
struct platform_driver led_drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "myled",
},
};
3.4写.remove函数
如果驱动与设备已联系起来,当卸载驱动时,就会调用.remove函数卸载设备和.probe函数一样,注册了什么就卸载什么便可
static int led_remove(struct platform_device *pdev)
{
/* 卸载字符设备驱动程序 */
printk("enter remove\n");
class_device_destroy(cls, MKDEV(major, 0));
class_destroy(cls);
unregister_chrdev(major, "myled");
iounmap(gpio_con); //注销虚拟地址
return 0;
}
3.5最后写drv的入口出口函数
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");
4.测试