Blackrose's Blog Blackrose's Blog

LDD读书笔记2

in technologyread (170) 文章转载请注明来源!

Linux Device Model是驱动开发中一个重要的概念,驱动的编写也是围绕这个思想展开。书中第十四章的前三节是说明创造Linux Device Model的原因,底层实现和用户空间的布署。所有的事物被创造,都是有一定原因,kernel中的各种方法和技术手段也是如此。早期的kernel设备文件的创建都是在内核空间完成,随着外围设备的增加,kernel已经无法有效管理这些设备和相应的驱动,同时USB接口的级联性,让kernel对设备的电源管理越来越繁杂。

那么,把设备文件的创建和删除操作放在用户空间就简单多了,也减小了kernel的负担。这就需要将kernel中的一些设备信息导出到用户空间,sys filesystem就是为解决这个问题而产生。如果只是导出kernel中的信息,proc filesystem己经做到了,proc fs的缺点是:
1.文件格式不统一
2.无法描述kernel中设备的组织结构
3.不能完全与用户空间交互

问题1和3比较容易解决,问题2对于proc fs是比较麻烦的,毕竟proc fs中大多数是对进程和系统基本信息的描述。

问题2中,如何组织并描述这些设备和驱动是个大问题。早期的8086芯片,每个管脚都有固定功能,可外接的设备也比较少。后来,需要更多外接设备时,芯片访问外围硬件比较耗时。bus的出现,解决了这些问题,PC中的南北桥芯片也是为解决同样的问题。目前,PC的主板上要扩充设备,大多是通过PCI总线完成,ARM芯片也采用同样的方法,如AHB,APB总线一样。设备应该通过总线来允许芯片访问,但是随着集成电路的发展,很多芯片可以内置一些常用的控制器,相应的设备也不连接在总线相连,kernel中就虚拟一个总线,让这些设备挂在这个总线上,这就是platform_bus,platform_device和platform_driver。platform这三个元素,是由bus,device,driver实现的。而问题2的解决方法是kobject,kset和subsystem,它们实现了描述bus,device,driver的问题。

platform驱动的编写:
1.描述platform_device的信息,并加入到内核
2.platform_driver中找到相对应的device,并通过操作resource,配置设备
3.完成virtual device driver的相关功能

下面把上次的代码改成platform_device的形式,同时在/sys/bus/platform/devices会有LED设备:
#include
#include
#include
#include
#include
#include
#include
#include

#define GPB_CON 0x56000010
#define GPIO_OUTPUT 0x1
#define GPB_DATA 0x4

#define LED_SZ 0x8

dev_t devt;
struct cdev leds_cdev =
{
.owner = THIS_MODULE,
};
struct class *leds_class;
struct device *leds_device;
void *config_base;
void *data_base;

int leds_buf = 0;

struct resource led_res[] =
{
[0] = {
.start = GPB_CON,
.end = GPB_CON + LED_SZ - 1,
.flags = IORESOURCE_MEM,
.name = "led_res",
},
};

void led_release(struct device *dev)
{
printk(KERN_ALERT "Device is released\n");
}

struct platform_device dev = {
.name = "micro2440-led",
.id = -1,
.num_resources = ARRAY_SIZE(led_res),
.resource = led_res,
.dev = {
.release = led_release,
}
};

ssize_t leds_write(struct file *filp, const char *buf, size_t len, loff_t *offset)
{
int tmp = 0;
if(len > sizeof(leds_buf))
return -1;
if(copy_from_user(&leds_buf, buf, len)){
printk(KERN_ALERT "write error\n");
return -2;
}

if(leds_buf > 0){
// turn on the leds
tmp = ioread32(config_base + GPB_DATA);
tmp &= ~((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8));
iowrite32(tmp, config_base + GPB_DATA);

}else{
// turn off the leds

tmp = ioread32(config_base + GPB_DATA);
tmp |= (0x1 << 5) | (0x1 << 6) | (0x1 << 7) | (0x1 << 8);
iowrite32(tmp, config_base + GPB_DATA);

}

return len;
}

struct file_operations leds_fops =
{
.owner = THIS_MODULE,
.write = leds_write,
};


int led_probe(struct platform_device *pdev)
{
int ret;
unsigned long tmp;
struct resource *res;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(!res)
return -EBUSY;


ret = alloc_chrdev_region(&devt, 0, 1, "leds");
if(ret){
printk(KERN_ALERT "alloc devt error\n");
goto ERR1;
}

cdev_init(&leds_cdev, &leds_fops);

ret = cdev_add(&leds_cdev, devt, 1);
if(ret){
printk(KERN_ALERT "add cdev error\n");
goto ERR2;
}

leds_class = class_create(THIS_MODULE, "leds");

leds_device = device_create(leds_class, NULL, devt, NULL, "leds");
if(leds_device == NULL){
printk(KERN_ALERT "create dev node error\n");
goto ERR3;
}

config_base = ioremap(res->start, res->end - res->start + 1);
if(config_base == NULL){
printk(KERN_ALERT "map io error\n");
goto ERR4;
}


tmp = ioread32(config_base);
tmp |= (GPIO_OUTPUT << 10) | (GPIO_OUTPUT << 12) | (GPIO_OUTPUT << 14) | (GPIO_OUTPUT << 16);
iowrite32(tmp, config_base);

printk(KERN_ALERT "leds init success\n");
return 0;

ERR4:
release_resource(led_res);
ERR3:
cdev_del(&leds_cdev);
ERR2:
unregister_chrdev_region(devt, 1);
ERR1:
return -1;
}

int led_remove(struct platform_device *pdev)
{
iounmap(config_base);

device_destroy(leds_class, devt);
class_destroy(leds_class);

cdev_del(&leds_cdev);
unregister_chrdev_region(devt, 1);

return 0;
}

struct platform_driver drv = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "micro2440-led"
}
};

static int __init leds_init(void)
{
platform_device_register(&dev);
platform_driver_register(&drv);

return 0;
}

static void __exit leds_exit(void)
{

platform_driver_unregister(&drv);
platform_device_unregister(&dev);

printk(KERN_ALERT "leds has been exited\n");
}

module_init(leds_init);
module_exit(leds_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Blackrose");


lwj103862095的专栏——Linux平台总线驱动设备模型 http://blog.csdn.net/lwj103862095/article/details/17957637

文章二维码

扫描二维码,在手机上阅读!

发表新评论
博客已萌萌哒运行
© 2018 由 Typecho 强力驱动.Theme by Yodu
前篇 后篇
雷姆
拉姆