标签 ldd 下的文章

LDD读书笔记3

现在已经有很多驱动支技sysfs,实现与设备交互的功能,用户空间借用echo,cat命令即可操作相应设备。sysfs提供统一的文本格式,也就是属性文件。一个属性文件代表一个属性值,可读可写。属性值结构的表示如下:
struct attribute
{
char∗ name;
struct module ∗owner;
mode_t mode;
};

这里的name也是sysfs下的属性文件的名称。对属性的读写通过两个函数完成,sysfs中称为show和store。
struct device_attribute
{
struct attribute attr;
ssize_t (∗show)(struct device ∗dev, char ∗buf);
ssize_t (∗store)(struct device ∗dev, const char ∗buf, size_t count);
};

int device_create_file(struct device ∗device, struct device_attribute ∗entry);
void device_remove_file(struct device ∗dev, struct device_attribute ∗attr);

填充好attribute结构体后,实现相关的store和show函数,最后创建属性文件即可。这里有属性的静态创建方法:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)


下面是增加sysfs属性文件的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;
}


static void led_ops(unsigned int sw){
int tmp = 0;

if(sw){
// 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);
}
}

static ssize_t led_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return printk("write a number in to led\n");
}

static ssize_t led_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned int value;

sscanf(buf, "%d", &value);
//printk("the string is %s, %d\n", buf, value);
if(value)
led_ops(1);
else
led_ops(0);

return 1;
}

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

static DEVICE_ATTR(led_state, S_IRUGO | S_IWUSR, led_show, led_store);


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

ret = device_create_file(&pdev->dev, &dev_attr_led_state);

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_remove_file(&pdev->dev, &dev_attr_led_state);
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");


Resource Links:
sysfs https://www.kernel.org/pub/linux/kernel/people/mochel/doc/papers/ols-2005/mochel.pdf
http://www.makelinux.net/ldd3/chp-14-sect-2
Linux理解devfs、sysfs、udev http://www.mike.org.cn/articles/linux-understand-devfs-sysfs-udev/
https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt

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

最新文章

最近回复

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

分类

归档

其它