在linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动。相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。
一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等确不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线。
{
platform_device_add(s3c_buttons);
}
{
struct platform_object *pa;
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;
}
}
struct platform_device { |
const char *name; |
u32 id; |
struct device dev; |
u32 num_resources; |
struct resource *resource; |
}; 关于platform_object: struct platform_object {
|
if (r) {
memcpy(r, res, sizeof(struct resource) * num);
pdev->resource = r;
pdev->num_resources = num;
}
return r ? 0 : -ENOMEM;
{
int i, ret = 0;
return -EINVAL;
pdev->dev.parent = &platform_bus;
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev, pdev->name);
struct resource *p, *r = &pdev->resource[i];
r->name = dev_name(&pdev->dev);
if (!p) {
if (resource_type(r) == IORESOURCE_MEM)
p = &iomem_resource;
else if (resource_type(r) == IORESOURCE_IO)
p = &ioport_resource;
}
printk(KERN_ERR
"%s: failed to claim resource %d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
dev_name(&pdev->dev), dev_name(pdev->dev.parent));
if (ret == 0)
return ret;
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
release_resource(r);
}
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,
}
};
{
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); 注册驱动
}
{
int ret;
struct device_driver *other;
(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);
if (other) {
put_driver(other);
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EEXIST;
}
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
.probe = mini2440_buttons_probe,
.remove = mini2440_buttons_remove,
.driver = {
.owner = THIS_MODULE,
.name = "mini2440-buttons", //与设备名相同
},
};
{
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);
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;
iounmap(buttons_base);
release_resource(buttons_mem);
kfree(buttons_mem);
}
#define request_mem_region(start, n, name) \
__request_region(&iomem_resource, (start), (n), (name))
注: 调用request_mem_region()不是必须的,但是建议使用。该函数的任务是检查申请的资源是否可用,如果可用则申请成功,并标志为已经使用,其他驱动想再申请该资源时就会失败。
unsigned int type, unsigned int num)
{
int i;
struct resource *r = &dev->resource[i];
return r;
}
return NULL;
}
注册杂项字符设备,该类设备使用同一个主设备号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,
};