Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2094565
  • 博文数量: 361
  • 博客积分: 10828
  • 博客等级: 上将
  • 技术积分: 4161
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-20 14:34
文章分类

全部博文(361)

文章存档

2011年(132)

2010年(229)

分类: LINUX

2011-04-26 15:57:59

(一)2.4与2.6注册字符设备的区别

2.4内核注册驱动要用:
int register_chrdev (unsigned int major, const char *name, struct file_operations *fops);
2.4内核注销驱动要用:
int unregister_chrdev( unsigned int major, const char *name );
2.4内核驱动注册完后,要用以下代码创建设备文件
   static devfs_handle_t devfs_handle;
   devfs_handle = devfs_register( NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
                           BUTTON_MAJOR,&sbc2410_buttons_fops, NULL);
2.4内核驱动要用以下代码移除设备文件:
devfs_unregister( devfs_handle);
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
2.6驱动i注册设备号要用:
(1)如果主设备号事先知道,要用:
int register_chrdev_region( dev_t first, unsigned int count, char *name );
(2)如果主设备号为0,则要用动态分配:
int alloc_chrdev_region( dev_t *dev, unsigned int firstminor,
            unsigned int count, char *name );
2.6释放设备号要用:
void unregister_chrdev_region( dev_t first, unsigned int count );
2.6内核字符设备驱动注册要用:
struct cdev *my_cdev = cdev_alloc();
my_cdev->ops = &chr_fops;
void cdev_init( struct cdev *cdev, struct file_operations *fops);
int cdev_add( struct cdev *dev, dev_t num, unsigned int count);
2.6内核字符设备驱动移除要用:
void cdev_del( struct cdev *dev );
2.6内核驱动注册完后,可以用以下代码创建设备节点
devfs_mk_cdev( MKDEV(LED_MAJOR, LED_MINOR),
      S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME);
2.6内核驱动要用以下代码移除设备节点:
devfs_remove(DEVICE_NAME);
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------
以上也可以用命令创建设备文件:
mknod /dev/设备文件名 字符设备(c是字符设备,b是块设备)   主设备号 次设备号
例如:mknod /dev/testChar c  100 0
删除设备入口:
    rm /dev/testChar


(二)使用devfs动态创建设备节点示例

设计驱动程序框架

字符设备指那些必须以串行顺序依次访问的设备,并且不需要缓冲,通常用于不需要大量数据请求传送的设备类型,所以对于该按键设备适合用字符设备类型来实现。对于设备驱动,通常根据设备类型的不同所以实现框架会有所不同,比如字符设备、块设备和网络设备分别都有自己的实现框架和相应的内核API函数。通常字符设备驱动实现的内容有:模块的加载(字符设备的注册,硬件的初始化,中断的注册等);字符设备的操作函数实现(open,close,read,write和ioctl等);中断程序的实现;模块的卸载(注销字符设备,注销其他申请的资源)。下面来分析一下该按键设备驱动的具体实现。

3.按键模块的加载

     1  static int __init buttons_init(void)

     2  {

     3          int ret,devno;

     4          dev_t dev;

     5          ret = alloc_chrdev_region(&dev,0,1,DEVICE_NAME); //在系统中申请一个字符设备区域,主设备号由系统动态分配。

     6          buttons_major_number = MAJOR(dev); //摘取出主设备号

     7          printk(KERN_INFO "Initial QT2410E Board Buttons driver!\n");

     8          if (ret<0) {

     9                  printk(KERN_WARNING "button:can't get major number %d\n",buttons_major_number);

    10                  return ret;

    11          }

    12          ret = request_irqs(); //注册中断,下文会具体分析该函数的实现

    13          if (ret) { //如果注册中断失败,则注销上面申请的字符设备区域。

    14                  unregister_chrdev_region(dev,1);

    15                  printk(KERN_WARNING "button:can't request irqs\n");

    16                  return ret;

    17          }

    18          devno = MKDEV(buttons_major_number,0);

    19          cdev_init(&buttons_dev,&buttons_fops); //初始化buttons_dev字符设备结构

    20          buttons_dev.owner = THIS_MODULE;

    21          

    22

    23          ret = cdev_add(&buttons_dev,devno,1);// 将字符设备加入到内核中

    24          if (ret) { //如果添加失败,则做上述注册的释放操作。

    25                  free_irqs();

    26                  unregister_chrdev_region(dev,1);

    27                  printk(KERN_NOTICE "Error %d adding buttons device\n",ret);

    28                  return ret;

    29          }

    30

    31  #ifdef CONFIG_DEVFS_FS //如果定义devfs,系统会自动创建/dev目录下的字符设备节点,比如这里会自动创建/dev/buttons字符设备节点

    32          devfs_mk_cdev(MKDEV(buttons_major_number,0), S_IFCHR | S_IRUSR | S_IWUSR,DEVICE_NAME);

    33          printk(KERN_INFO"/dev/%s has been added to your system.\n",DEVICE_NAME);

    34  #else //否则需要执行 mknod命令手动创建字符设备节点。

    35          printk(DEVICE_NAME "Initialized\n");

    36          printk(KERN_INFO "You must create the dev file manually.\n");

    37          printk(KERN_INFO "Todo: mknod c /dev/%s %d 0\n",DEVICE_NAME,buttons_major_number);

    38  #endif

    39          return 0;

    40  }

分析上述代码,buttons_init是该按键模块的驱动入口函数,也是该内核模块的加载函数,主要用于注册资源、申请资源和初始化设备等工作。第5行,alloc_chrdev_region()是内核提供的申请字符设备号函数,它会动态的为设备申请一个主设备号,并且根据输入参数申请多个次设备。第19行,cdev_init()是用于初始化一个cdev结构,这里初始化的是buttons_dev全局变量。第32行,devfs_mk_cdev()函数会在/dev目录下自动创建一个设备节点,早期的内核版本是需要手动通过mknod工具创建设备节点。


(三)用udev配合class创建设备节点示例

http://hi.baidu.com/zengzhaonong/blog/item/dd55b26efcad8ade81cb4a8a.html

使用udev在/dev/下动态生成设备文件

create_chrdev.c
---------------------------------------------
#include      //dev_t
#include       //struct cdev
#include         //alloc_chrdev_region()
#include     //class_create()

dev_t                  devid;
static struct cdev     *led_cdev;
static int             led_Major = 0;
static int             led_Minor = 0;
static struct class    *led_class;

static struct file_operations led_fops = {
    .owner        = THIS_MODULE,
};

static int __init hello_init(void)
{
    int err;

    //初始化cdev
    led_cdev        = cdev_alloc();
    cdev_init(led_cdev, &led_fops);
    led_cdev->owner = THIS_MODULE;

    //动态获取主设备号(dev_t devid中包含"主设备号"和"次设备号"信息)
    alloc_chrdev_region(&devid, 66, 1, "led");
    led_Major = MAJOR(devid);
    led_Minor = MINOR(devid);
    printk(KERN_INFO "I was assigned major number %d.\n", led_Major);
    printk(KERN_INFO "I was assigned minor number %d.\n", led_Minor);

    //注册字符设备 (1)
    err = cdev_add(led_cdev, devid, 1);
    if (err) {
        printk(KERN_NOTICE "Error %d adding device\n", err);
        return -1;
    }

    led_class = class_create(THIS_MODULE, "led_class1");
    if (IS_ERR(led_class)) {
        printk(KERN_INFO "create class error\n");
        return -1;
    }
    class_device_create(led_class, NULL, devid, NULL, "led" "%d", MINOR(devid));   


    return 0;
}

static void __exit hello_exit(void)
{
    unregister_chrdev_region(devid, 1);
    cdev_del(led_cdev);

    class_device_destroy(led_class, devid);
    class_destroy(led_class);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("zengxiaolong ");
MODULE_DESCRIPTION("A sample driver");
MODULE_SUPPORTED_DEVICE("testdevice");


(1)cdev_add()执行后将会在/proc/devices文件中看到"252 led",但是在/dev/目录下没有生成相应的设备文件。直到class_device_create()执行后才会在/dev/目录下生成设备文件led66。而class_create()执行后会在/sys/class/目录下生成led_class1目录


class_create,class_simple_create在/sysy/class创建类目录。

class_device_create,class_simple_

device_add两者功能类似,如果dev指向不为NULL,它将创建一个从类入口到/sys/devices下相应入口的符号链接。



Makefile
---------------------------------------------
all: default
obj-m += create_chrdev.o

default:
    make -C /home/zxl/soft/kernel/linux-2.6.22 M=`pwd` modules
clean:

    make -C /home/zxl/soft/kernel/linux-2.6.22 M=`pwd` clean

 

 

 

 

字符设备的生成 -- 这个版本简洁
-------------------------------------------------------
static struct class    *node_class;

static int __init init_mtdchar(void)
{
    if (register_chrdev(CHAR_MAJOR, "dev_node", &node_fops))
    {
        printk(KERN_NOTICE "Can't allocate major number %d.\n", CHAR_MAJOR);
        return -EAGAIN;
    }

    node_class = class_create(THIS_MODULE, "dev_node_class");

    if (IS_ERR(node_class)) {
        printk(KERN_ERR "Error creating node_class.\n");
        unregister_chrdev(CHAR_MAJOR, "dev_node");
        return PTR_ERR(node_class);
    }

    class_device_create(node_class, NULL, MKDEV(CHAR_MAJOR, 0), NULL,
            "node_name" "%d", CHAR_MINOR);

    return 0;
}

static void __exit cleanup_mtdchar(void)
{
    class_device_destroy(node_class, MKDEV(CHAR_MAJOR, 0));
    class_destroy(node_class);
    unregister_chrdev(CHAR_MAJOR, "dev_node");
}

 

 

 

linux-2.6.21.7 内核中的使用实例
-------------------------------------------------------
static void adbdev_init(void)
{
    if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {
        printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
        return;
    }

    adb_dev_class = class_create(THIS_MODULE, "adb");
    if (IS_ERR(adb_dev_class))
        return;
    class_device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb");
}

 

 

 

 

linux-2.6.28 内核中的使用实例
-------------------------------------------------------
static void __init adbdev_init(void)
{
    if (register_chrdev(ADB_MAJOR, "adb", &adb_fops)) {
        printk(KERN_ERR "adb: unable to get major %d\n", ADB_MAJOR);
        return;
    }

    adb_dev_class = class_create(THIS_MODULE, "adb");
    if (IS_ERR(adb_dev_class))
        return;
    device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL, "adb");

    platform_device_register(&adb_pfdev);
    platform_driver_probe(&adb_pfdrv, adb_dummy_probe);
}


阅读(1430) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~