您的位置:首页 > 其它

使用platform虚拟总线驱动按键

2012-04-19 17:07 375 查看
Linux下按键驱动相对来说,编程简单。本例程使用了platform虚拟总线进行编程,主要是为了加深对总线,设备和驱动的了解,程序不是本人所写。只希望通过写下表笔记,梳理下思路。



在设备驱动程序中经常会见到和platform相关的字段,分布在驱动程序的多个角落,这也是2.6内核中比较重要的一种机制,把它的原理弄懂了,对以后分析驱动程序很有帮助,下面简单介绍一下:

在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动。相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。

一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等确不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线。






首先,需要注册设备:

static int __init platform_init(void)

{

s3c_buttons = platform_device_alloc("mini2440-buttons",-1);

platform_device_add_resources(s3c_buttons,&s3c_buttons_resource,7);

/*平台设备的注册*/

platform_device_add(s3c_buttons);

}

s3c_buttons = platform_device_alloc(const char name,int id);

为struct Platform_device设备分配空间.

struct platform_device *platform_device_alloc(const char *name, int id)

{

struct platform_object *pa;


pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);

if (pa) {

strcpy(pa->name, name);

pa->pdev.name = pa->name;

pa->pdev.id = id;

device_initialize(&pa->pdev.dev);

pa->pdev.dev.release = platform_device_release;

}


return pa ? &pa->pdev : NULL;

}




关于platform_device



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

关于platform_object:

struct platform_object {

struct platform_device pdev;

char name[1];

};


关于resource:

struct resource{

resource_size_t start; //资源的起始物理地址

resource_size_t end; //资源的终止物理地址

const char* name; //资源名称

unsigned long flags; //资源的类型,比如MEM,IO,IRQ

struct resource *parent,*sibling,*child; //资源链表指针

}

/*pdev:用于添加资源到由platform_device_alloc分配的platform device

int platform_device_add_resources(struct platform_device *pdev,

res:需要分配到device的资源集

num:资源数

*/

struct resource *res,unsigned int num)

{

struct resource *r;

r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);

if (r) {

memcpy(r, res, sizeof(struct resource) * num);

pdev->resource = r;

pdev->num_resources = num;

}

return r ? 0 : -ENOMEM;

}



/*

platform_device_register的第二部分

*/

int platform_device_add(struct platform_device *pdev)

{

int i, ret = 0;


if (!pdev)

return -EINVAL;


if (!pdev->dev.parent)

pdev->dev.parent = &platform_bus;


pdev->dev.bus = &platform_bus_type;

if (pdev->id != -1)

dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);

else

dev_set_name(&pdev->dev, pdev->name);


for (i = 0; i < pdev->num_resources; i++) {

struct resource *p, *r = &pdev->resource[i];


if (r->name == NULL)

r->name = dev_name(&pdev->dev);


p = r->parent;

if (!p) {

if (resource_type(r) == IORESOURCE_MEM)

p = &iomem_resource;

else if (resource_type(r) == IORESOURCE_IO)

p = &ioport_resource;

}


if (p && insert_resource(p, r)) {

printk(KERN_ERR

"%s: failed to claim resource %d\n",

dev_name(&pdev->dev), i);

ret = -EBUSY;

goto failed;

}

}


pr_debug("Registering platform device '%s'. Parent at %s\n",

dev_name(&pdev->dev), dev_name(pdev->dev.parent));


ret = device_add(&pdev->dev);

if (ret == 0)

return ret;


failed:

while (--i >= 0) {

struct resource *r = &pdev->resource[i];

unsigned long type = resource_type(r);


if (type == IORESOURCE_MEM || type == IORESOURCE_IO)

release_resource(r);

}


return ret;



调用此函数后,将在platform总线的devices下出现“mini2440-buttons”设备文件



关于资源:

/*平台资源的定义*/

static struct resource s3c_buttons_resource[] = {

[0]={

.start = S3C24XX_PA_GPIO,

.end = S3C24XX_PA_GPIO + S3C24XX_SZ_GPIO - 1,

.flags = IORESOURCE_MEM,

},



[1]={

.start = IRQ_EINT8,

.end = IRQ_EINT8,

.flags = IORESOURCE_IRQ,

},

[2]={

.start = IRQ_EINT11,

.end = IRQ_EINT11,

.flags = IORESOURCE_IRQ,

},

[3]={

.start = IRQ_EINT13,

.end = IRQ_EINT13,

.flags = IORESOURCE_IRQ,

},

[4]={

.start = IRQ_EINT14,

.end = IRQ_EINT14,

.flags = IORESOURCE_IRQ,

},

[5]={

.start = IRQ_EINT15,

.end = IRQ_EINT15,

.flags = IORESOURCE_IRQ,

},

[6]={

.start = IRQ_EINT19,

.end = IRQ_EINT19,

.flags = IORESOURCE_IRQ,

}

};

static struct platform_device *s3c_buttons;







介绍完设备,现在介绍驱动程序:

platform_driver_register(&mini2440buttons_driver)



int platform_driver_register(struct platform_driver *drv)

{

drv->driver.bus = &platform_bus_type; //将platform驱动注册到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;

if (drv->suspend)

drv->driver.suspend = platform_drv_suspend;

if (drv->resume)

drv->driver.resume = platform_drv_resume;

return driver_register(&drv->driver); 注册驱动

}




关于driver_register:

int driver_register(struct device_driver *drv)

{

int ret;

struct device_driver *other;


if ((drv->bus->probe && drv->probe) ||

(drv->bus->remove && drv->remove) ||

(drv->bus->shutdown && drv->shutdown))

printk(KERN_WARNING "Driver '%s' needs updating - please use "

"bus_type methods\n", drv->name);


other = driver_find(drv->name, drv->bus);

if (other) {

put_driver(other);

printk(KERN_ERR "Error: Driver '%s' is already registered, "

"aborting...\n", drv->name);

return -EEXIST;

}


ret = bus_add_driver(drv);

if (ret)

return ret;

ret = driver_add_groups(drv, drv->groups);

if (ret)

bus_remove_driver(drv);

return ret;

}




回头看mini2440buttons_driver:



static struct platform_driver mini2440buttons_driver = {

.probe = mini2440_buttons_probe,

.remove = mini2440_buttons_remove,

.driver = {

.owner = THIS_MODULE,

.name = "mini2440-buttons", //与设备名相同

},

};




当匹配了driver和device后,platform_driver通过probe检测platform总线上的设备:

static int mini2440_buttons_probe(struct platform_device *pdev)

{

struct resource *res;

struct device *dev;

int ret;

int size;

int i;



printk("probe:%s\n", __func__);

dev = &pdev->dev;

buttons_dev = &pdev->dev;



/*平台资源获取*/

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if (res == NULL) {

dev_err(dev, "no memory resource specified\n");

return -ENOENT;

}



size = (res->end - res->start) + 1;

buttons_mem = request_mem_region(res->start, size, pdev->name);

if (buttons_mem == NULL) {

dev_err(dev, "failed to get memory region\n");

ret = -ENOENT;

goto err_req;

}



buttons_base = ioremap(res->start, size);

if (buttons_base == NULL) {

dev_err(dev, "failed to ioremap() region\n");

ret = -EINVAL;

goto err_req;

}

printk(KERN_DEBUG"probe: mapped buttons_base=%p\n", buttons_base);


/*get irq number*/

for(i=0; i<6; i++){

buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);

if(buttons_irq == NULL){

dev_err(dev,"no irq resource specified\n");

ret = -ENOENT;

goto err_map;

}

button_irqs[i] = buttons_irq->start;

//printk("button_irqs[%d]=%d\n",i,button_irqs[i]);

}

ret = misc_register(&mini2440_miscdev);



return 0;


err_map:

iounmap(buttons_base);


err_req:

release_resource(buttons_mem);

kfree(buttons_mem);


return ret;

}




关于函数platform_get_resource(struct platform_device *dev,unsigned int type,

unsigned int num)

dev:资源所属的设备

type:获取的资源类型 IORESOURCE_IRQ.....

num:获取资源数目



关于request_mem_region()

request_mem_region() -- 将起始地址为[start, start+n-1]的资源插入根资源iomem_resource中。参数start是I/O内存资源的起始物理地址(是CPU的RAM物理地址空间中的物理地址),参数n指定I/O内存资源的大小。

#define request_mem_region(start, n, name) \

__request_region(&iomem_resource,
(start), (n), (name))

注: 调用request_mem_region()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。

关于ioremap:

驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中,原型如下:

void
* ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);



关于platform_get_resource(struct
pdev *dev,unsigned int type,unsigned int num)

dev:资源所属的设备

type:获取的资源类型

num:获取的资源数

struct
resource *platform_get_resource(struct platform_device *dev,

unsigned int type, unsigned int num)

{

int i;

for
(i = 0; i < dev->num_resources; i++) {

struct resource *r = &dev->resource[i];

if
(type == resource_type(r) && num-- == 0)

return r;

}

return NULL;

}



由函数可以看出,函数返回了第num个资源的首地址,但是感觉这个函数做了很多无用功。



关于misc_register()



注册杂项字符设备,该类设备使用同一个主设备号10

杂项字符设备使用的数据结构

struct miscdevice {

int minor;

const char *name;

struct file_operations *fops;

struct list_head list;

struct device *dev;

struct class_device *class;

char devfs_name[64];

};

杂项设备也是在嵌入式系统中用得比较多的一种设备驱动。在 Linux 内核的include\linux\miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主设备号10 ,一起归于misc device,其实misc_register就是用主设备号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备。

misc_device是特殊的字符设备。注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_create()或者device_create()。

再看mini2440_miscdev

static struct miscdevice mini2440_miscdev = {



.minor = MISC_DYNAMIC_MINOR,

.name ="buttons", //创建设备文件buttons

.fops = &mini2440buttons_fops,

};

static struct file_operations mini2440buttons_fops = {

.owner = THIS_MODULE,

.open = s3c24xx_buttons_open,

.release = s3c24xx_buttons_close,

.read = s3c24xx_buttons_read,

.poll = s3c24xx_buttons_poll,

};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: