2.6内核字符设备驱动程序解析(续)

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

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 class_create(owner, name)               \
 271({                                              \
 272        static struct lock_class_key __key;     \
 273        __class_create(owner, name, &__key);    \
 274})

linux-2.6.28/linux/drivers/base/class.c
struct class *__class_create(struct module *owner, const char *name,
 224                             struct lock_class_key *key)

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


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

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

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

 504extern struct device *device_create(struct class *cls, struct device *parent,
 505                                    dev_t devt, void *drvdata,
 506                                    const char *fmt, ...)
 507                                    __attribute__((format(printf, 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 class *cls, 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

全部自动完成,这样就方便多了。
<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
阅读(669) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值