Chinaunix首页 | 论坛 | 博客
  • 博客访问: 728169
  • 博文数量: 134
  • 博客积分: 3207
  • 博客等级: 中校
  • 技术积分: 1995
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-01 20:47
文章分类

全部博文(134)

文章存档

2022年(1)

2020年(7)

2018年(2)

2016年(5)

2015年(14)

2014年(21)

2013年(3)

2012年(1)

2011年(15)

2010年(30)

2009年(35)

分类: LINUX

2010-07-21 17:34:53

在调用 cdev_add()函数向系统注册字符设备之前,应首先调用 register_chrdev_region()或 alloc_chrdev_region() 函数向系统申请设备号,这两个函数在 中可以招到,其原型是:
引用

int register_chrdev_region(dev_t first, unsigned int count, const char *name);
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, const char *name);


register_chrdev_region() 函数用于已知起始设备的设备号的情况。
参数 first 表示起始设备号;
参数 count 表示从起始设备号开始连续的设备号数目,需要注意的是 count 不能过大,不然有可能溢出到下一个主设备号上;
参数 name 表示设备的名称,这个名称也会在 /proc/devices 文件以及sfsfs 中看到。

register_chrdev_region() 成功时返回 0 ,失败时返回负数。


alloc_chrdev_region() 用于设备号未知,向系统动态申请未被占用的设备号情况。

参数 dev ,在系统调用成功后,会把得到的设备号方到这个参数中;
参数 firstminor 是请求的第一个次设备号,一般为 0 ;
参数 count  表示一个范围值;
参数 name 表示设备名。

alloc_chrdev_region() 和 register_chrdev_region() 对比的优点在于它会自动避开设备号重复的冲突。
使用alloc_chrdev_region()会方便很多。

在注册好设备以后,我们都会手动用mknod命令建立设备文件。下面讲解自动建立设备文件的方法:
用udev在/dev/下动态生成设备文件,这样用户就不用手工调用mknod了。 
利用的kernel API: 
    class_create:    创建class 
    class_destroy:    销毁class 
    device_create:    创建device 
    device_destroy:   销毁device

class_creat:
-----------------------------------------------------------------
linux-2.6.28/include/linux/device.h
#define (, )               \
 271({                                              \
 272        static struct  ;     \
 273        (, , &);    \
 274})

linux-2.6.28/linux/drivers/base/class.c
struct  *(struct  *, const char *,
 224                             struct  *)

在/sys/class目录下创建类目录
owner 是模块结构体参数,一般是固定的:THIS_MODULE
name 在/sys/class 目录下的类目录的名字。
key 应该是一种锁机制,是函数内部使用的,不需要用户传递。(请高手解答)

class_destroy:
-----------------------------------------------------------------
linux-2.6.28/include/linux/device.h
 266extern void (struct  *);

在/sys/class目录下删除类目录

device_create:
----------------------------------------------------------------
linux-2.6.28/include/linux/device.h

 504extern struct device *device_create(struct  *, struct device *,
 505                                    dev_t devt, void *,
 506                                    const char *, ...)
 507                                    (((, 5, 6)));

cls:类的结构体。由class_creat创建
parent:父设备指针,一般是NULL。
dev:记录主设备号和次设备号。
drvdata : 一般是NULL
fmt: 所要建立的设备文件的名字,/dev目录下的设备文件。
后面是GCC内联函数。这个我也不懂。
这个函数可以自动建立设备文件
/dev目录下可以看到

device_destroy:
----------------------------------------------------------------
linux-2.6.28/include/linux/device.h
 508extern void device_destroy(struct  *, dev_t devt);
 509

销毁此设备。
在卸载模块时,必须要销毁class和device。不然就无法加载此设备。需要重启计算机才行。

下面一个例子使用以上函数:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/device.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lan");

static int char_read(struct file *filp, char __user *buffer, size_t, loff_t*);
static int char_open(struct inode*, struct file*);
static int char_write(struct file *filp, const char __user *buffer, size_t, loff_t*);
static int char_release(struct inode*, struct file*);

static int chropen;
struct cdev *chardev;
struct class *my_class;
static int len;
static dev_t dev;

static char *to;
static const struct file_operations char_ops={
    .read = char_read,
    .write = char_write,
    .open = char_open,
    .release = char_release,
};

static int __init char_init(void)
{
    printk(KERN_ALERT"Initing......\n");
    
    chardev = cdev_alloc();

    if(chardev == NULL){
        return -1;
    }
    if(alloc_chrdev_region(&dev, 0, 10, "chardev0")){
    printk(KERN_ALERT"Register char dev error\n");
    return -1;
    }
    chropen = 0;
    len = 0;
    cdev_init(chardev, &char_ops);
    if(cdev_add(chardev, dev, 1)){
        printk(KERN_ALERT"Add char dev error!\n");
    }
    
     my_class = class_create(THIS_MODULE, "my_class");

    if(IS_ERR(my_class)) {

        printk("Err: failed in creating class.\n");

        return -1;

    }
    /* register your own device in sysfs, and this will cause udevd to create corresponding device node */

    device_create(my_class, NULL, dev, NULL, "chardev0");
    return 0;
}
#define DP_MAJOR MAJOR(dev)
#define DP_MINOR MINOR(dev)    
static int char_open(struct inode *inode, struct file *file)
{
    if(chropen == 0)
        chropen++;
    else{
        printk(KERN_ALERT"Another process open the char device\n");
        return -1;
    }
    try_module_get(THIS_MODULE);
    return 0;
}

static int char_release(struct inode *inode,struct file *file)
{
    chropen--;
    module_put(THIS_MODULE);
    return 0;
}

static int char_read(struct file *filp,char __user *buffer,size_t length,loff_t *offset)
{
    unsigned long nn;
    nn = copy_to_user(buffer, to, length);
    printk("nn = %ld\n", nn);
    printk("buffer = %s\n", buffer);
    return length;
}

static int char_write(struct file *filp, const char __user *buffer, size_t length, loff_t *offset)
{
    unsigned long n;
    to = (char *)kmalloc((sizeof(char)) * (length+1), GFP_KERNEL);
    memset(to, '\0', length+1);
    n = copy_from_user(to, buffer, length);
    printk("n = %ld\n", n);
    printk("to = %s\n", to);
    return length;
}

static void __exit module_close(void)
{
        len=0;
        printk(KERN_ALERT"Unloading..........\n");

        unregister_chrdev_region(MKDEV(DP_MAJOR,DP_MINOR),10);
        cdev_del(chardev);
    device_destroy(my_class, dev);
    class_destroy(my_class);
}

module_init(char_init);
module_exit(module_close);


编译加载次模块后,我们看一下各个文件和目录:
$ cat /proc/devices | grep chardev0
250 chardev0

$ ls -l /sys/class/my_class/
总用量 0
lrwxrwxrwx 1 root root 0 2010-07-22 11:23 chardev0 -> ../../devices/virtual/my_class/chardev0

$ ls -l /dev/chardev0 
crw-rw---- 1 root root 250, 0 2010-07-22 11:20 /dev/chardev0

全部自动完成,这样就方便多了。
阅读(1514) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~