嵌入式linux驱动-bus-driver-device模型笔记

来源:互联网 发布:二战 日本 知乎 编辑:程序博客网 时间:2024/05/16 07:34

一、开发环境

1、内核:Linux 2.6.22.6;

2、JZ2440

3、ubuntu 9.10

二、概念

    一个现实的linux设备驱动通常需要挂接在一种总线上,对于本身依附于PCI,USB,IIC,SPI等的设备而言,这自然不是问题,但是在嵌入式系统里面,SOC系统中集成的独立的外设控制器,挂接在SOC内存空间的外设等确不依附于此类总线。基于这一背景,linux发明了一种虚拟的总线,称为platform总线,相应的设备称为platform_device,而驱动成为platform_driver.Platform总线是linux2.6内核加的一种虚拟总线。Platform驱动与传统的设备驱动模型相比,优势在于Platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,提高了程序的可移植性。Linux 2.6的设备驱动模型中,把I2C、RTC、LCD等都归纳为platform_device。总线将设备和驱动分别注册进各自的链表,在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。

三、过程

1、device层

(1)定义platform_device 结构体类型的变量,如建立led_device 。

static struct platform_device led_device = {
.name = "myled",
.id = -1,
.dev = {
.release = led_release, 
},
.resource = led_resources,
.num_resources= ARRAY_SIZE(led_resources),
};

其中有个重要的成员是resource,是设备的资源信息,如IO地址,中断号等。

(2)定义resource结构体类型的变量。硬件资源就是在这确定的。如定义led_resources

static struct resource led_resources[] = {
[0]={
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
[1]={
.start = 5,
.end = 5,
.flags = IORESOURCE_IRQ,
}
};

(3)在入口函数注册、注销平台设备。使用函数platform_device_register、platform_device_unregister。如:

static int led_device_init(void)
{
platform_device_register(&led_device);
return 0;
}

static void led_device_exit(void)
{
platform_device_unregister(&led_device);
}

2、driver层

(1)定义platform_driver 结构体类型的变量,如定义led_driver :

struct platform_driver led_driver = {
.probe = led_driver_probe,
.remove = __devexit_p(led_driver_remove),
.driver = {
.name = "myled",
}
};

注意.name,每当注册platform_driver,或上面platform_device 时,在bus层的platform_match函数首先判断是否有id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配,如果匹配则调用platform_driver的probe函数。

(2)在入口函数注册、注销平台设备驱动。使用函数platform_driver_register、platform_driver_unregister。如:

static int  led_driver_init(void)
{
platform_driver_register(&led_driver);
return 0;
}

static void  led_driver_exit(void)
{
platform_driver_unregister(&led_driver);
}

(3)编写probe函数。

     这里主要可以进行一些初始化操作。如例子中注册了字符设备驱动,设置了IO功能并进行了ioremap。需要注意的是,对于platform_device 里的资源resource 是使用platform_get_resource函数得到的,再对这些资源做具体的操作。

(4)编写remove函数。对probe函数中加载的资源进行释放。

(5)具体的驱动编写。如例子里实现了open、write。

3、bus层

这层不需要编写。韦老师架构图

在device层或driver层注册平台设备或平台设备驱动时,bus会调用device_add或driver_register分别加入各自的链表,名字为各自结构体变量的.name项。接着调用platform_match函数先判断是否有id_table,如果有则使用id_table来进行匹配,否则,判断platform_device和platform_driver成员里的name,如果二者的name字段相同则匹配成功,然后调用platform_driver的probe函数。

四、程序

device.c

#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/input.h>#include <linux/irq.h>#include <linux/gpio_keys.h>#include <asm/gpio.h>static void led_release(struct device * dev){}static struct resource led_resources[] = {[0]={.start= 0x56000050,.end= 0x56000050 + 8 - 1,.flags= IORESOURCE_MEM,},[1]={.start= 5,.end= 5,.flags= IORESOURCE_IRQ,}};static struct platform_device led_device = {.name= "myled",.id= -1,.dev= {//.platform_data= &nand_data,.release = led_release, },.resource= led_resources,.num_resources= ARRAY_SIZE(led_resources),};static int led_device_init(void){platform_device_register(&led_device);return 0;}static void led_device_exit(void){platform_device_unregister(&led_device);}module_init(led_device_init);module_exit(led_device_exit);MODULE_LICENSE("GPL");

driver.c

/* 分配/设置/注册一个platform_driver */#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/input.h>#include <linux/irq.h>#include <asm/uaccess.h>#include <asm/io.h>static struct class *leds_class;static struct class_device*leds_class_devs;static int pin;static volatile unsigned long *gpio_con;static volatile unsigned long *gpio_dat;static int ret;static int led_open(struct inode *inode, struct file *file){printk("led_open\n");/* 配置为输出 */*gpio_con &= ~(0x3<<(pin*2));*gpio_con |= (0x1<<(pin*2));return 0;}static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){int val;printk("led_write\n");copy_from_user(&val, buf, count); //copy_to_user();if (val == 1){// 点灯*gpio_dat &= ~(1<<pin);}else{// 灭灯*gpio_dat |= (1<<pin);}return 0;}static struct file_operations leds_fops = {    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */    .open   =   led_open,     .write=led_write,   };static int  led_driver_probe(struct platform_device *pdev){struct resource *res;printk("led_driver_probe\n");/* get a pointer to the register memory */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);gpio_con = ioremap(res->start, res->end - res->start + 1);gpio_dat = gpio_con + 1;res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);pin = res->start;ret = register_chrdev(0,"myled",&leds_fops);leds_class = class_create(THIS_MODULE, "myled");  class_device_create(leds_class, NULL, MKDEV(ret, 0), NULL, "myled");/* /dev/leds */return 0;}static int  led_driver_remove(struct platform_device *pdev){printk("led_driver_remove\n");class_device_destroy(leds_class, MKDEV(ret, 0));class_destroy(leds_class);unregister_chrdev(ret, "myled");iounmap(gpio_con);return 0;  }struct platform_driver led_driver = {.probe= led_driver_probe,.remove= __devexit_p(led_driver_remove),.driver= {.name= "myled",}};static int  led_driver_init(void){platform_driver_register(&led_driver);return 0;}static void  led_driver_exit(void){platform_driver_unregister(&led_driver);}module_init(led_driver_init);module_exit(led_driver_exit);MODULE_LICENSE("GPL");


测试程序test.c

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(int argc,char **argv){int fd;int val=1;fd=open("/dev/myled",O_RDWR);if(fd<0){printf("can't open!");}if(argc!=2){printf("Usage :\n");printf("%s <on|off>\n",argv[0]);return 0;}if(strcmp(argv[1],"on")==0){val =1;}else{val = 0;}write(fd,&val,4);return 0;}











0 0
原创粉丝点击