Blackrose's Blog Blackrose's Blog

LDD读书笔记1

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

最近开始阅读驱动开发的基础书籍《Linux Device Drivers》,由于驱动开发相关书籍较少,也成为必读书籍之一。书里以Linux2.6内核为主讲解Linux下驱动的开发方式,虽然版本过老,但是里面对驱动开发的核心讲解还是很不错的。之前开始学嵌入式时,觉得驱动程序开发比较神秘,后来在几块单片机芯片上写祼机程序后,渐渐明白。驱动程序同祼机程序是一样的,都是对硬件的配置和使用,不同点就是Linux驱动程序要遵守Linux内核的相关约定来编写,同时对上层user space提供硬件的操作接口。

Linux驱动开发,实质上分为两个部分:

1.Physical device driver

2.Virtual device driver

Physical device driver就是对硬件的配置,使其正常工作,具体为就是对寄存器的操作。Virtual device driver则是对user space提供相关操作接口,众所周知,Linux一切皆文件的思想,设备也是通过文件访问,而对文件的open,read,write,close操作,是由VFS提供通一的接口支持,而具体的实现由每个硬件的驱动程序来实现。这一部分就是Virtual device driver。file_operations,proc fs,sysfs,同步,异步等等,都是为user space提供支持而提出的解决办法。

下面是以micro2440开发板的LED灯为例子,驱动程序提供控制LED的接口,即write()。同时,在/dev目录下提供相应的设备文件,以供操作。现在的Linux,都是通过Linux Device Model来获得当前系统的硬件状态,然后由udev动态创建相应设备文件(嵌入式中是mdev)。这里通过创建device,class来通知内核,有新设备加入内核,而不是书中的手动创建设备文件。

#include
#include
#include
#include
#include
#include
#include

#define GPB_CON 0x56000010
#define GPB_DAT 0x56000014
#define GPIO_OUTPUT 0x1

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;

ssize_t leds_write(struct file *filp, const char *buf, size_t len, loff_t *offset)
{
unsigned long tmp;
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(data_base);
tmp &= ~((1 << 5) | (1 << 6) | (1 << 7) | (1 << 8));
iowrite32(tmp, data_base);

}else{
// turn off the leds

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

}

return len;
}

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


static int __init leds_init(void)
{
int ret;
unsigned long tmp;

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(GPB_CON, 4);
data_base = ioremap(GPB_DAT, 4);

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;

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


static void __exit leds_exit(void)
{
iounmap(config_base);
iounmap(data_base);

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

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

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

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

文章二维码

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

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