2014年4月

LDD读书笔记2

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

platform device

Platform devices are devices that typically appear as autonomous entities in the system. This includes legacy port-based devices and host bridges to peripheral buses, and most controllers integrated into system-on-chip platforms. What they usually have in common is direct addressing from a CPU bus. Rarely, a platform_device will be connected through a segment of some other kind of bus; but its registers will still be directly addressable.

sysfs sysfs.txt platform.txt 使用/sys 文件系统访问Linux 内核 Jarson's blog《linux设备模型》

LDD读书笔记1

最近开始阅读驱动开发的基础书籍《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");

恢复blog

Tags: typecho

AlienLayer沉睡一个月,我都快要忘记的时候,收到之前的ticket邮件,为了补偿损失,免费一个月。我都已经撤了,告诉我这个消息,玩我呢吧。登陆到SERVER上发现,数据都还在,看日志,确实是宕机一个月,中间没有再启动过。

迁移typecho时,出现些小问题,仅供参考:

1.打开所有页面显示为503 database的问题

2.登录后台时,用户名和密码跳转时,出现500 database问题

解决:

1.sqlite数据库的路径是否正确,查看config.ini.php文件

2.sqlite数据库的权限问题,PDO模块需要文件的父目录也要有写权限

另外,如果出现问题,添加如下到config.ini.php文件的最后一行即可,记得用完后注释: define('__TYPECHO_DEBUG__', true);

resource link:

SQLite: read-only database

解决typecho搬家后的500 Database Query Error错误

最新文章

最近回复

  • Blackrose: 感谢拍砖!嗯,那句话...
  • aa: “Raspberry...
  • Blackrose: 外部只是帮你把芯片启...
  • : 内部时钟比外部的频率...
  • Blackrose: 你这评论比正文更有内容么
  • 7hao: CR+LF ...
  • Blackrose: 可以是你更新软件源后...
  • sxk: 博主,我的系统是ub...
  • Blackrose: 你深得精髓么
  • qihao: 这篇文章的简单总结就...

分类

归档

其它