驱动

一.字符设备类型:
    dev_t:  定义于include/linux/types.h中,下面是摘自types.h:
typedef __u32 __kernel_dev_t;

typedef __kernel_fd_set     fd_set;
typedef __kernel_dev_t      dev_t;
typedef __kernel_ino_t      ino_t;
typedef __kernel_mode_t     mode_t;
typedef __kernel_nlink_t    nlink_t;
typedef __kernel_off_t      off_t;
typedef __kernel_pid_t      pid_t;
.....
可以看出其中dev_t和pid_t都是32为的无符号数.
dev_t dev=MKDEV(ma,mi);
MKDEV(ma,mi)和相关函数定义在include/linux/kdev_t.h中,如下摘取:
#define MINORBITS   20
#define MINORMASK   ((1U << MINORBITS) - 1)

#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))
.....
由此可以看出字符设备的主设备号是12为位的无符号整型数,次设备号是20位的无符号整型数.这里面的转换在源代码里看应该是十分清楚的了.

struct char_device_struct:内核中以这个数据结构来表示一个字符设备.
static struct char_device_struct {
    struct char_device_struct *next;
    unsigned int major;  //主设备号
    unsigned int baseminor;//词设备号的起始号.
    int minorct;
    char name[64];  //设备名
    struct file_operations *fops; //在词设备上的操作
    struct cdev *cdev;      //定义在include/linux/cdev.h
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
struct cdev:
struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

二.设备号的申请
    字符设备设备号的申请有两种方式:(这里是说主设备号)
1.指定申请:就是说你事先找到一个未使用的字符设备号,然后再向内核申请这个指定的设备号.
    这中方式的申请函数是:register_chrdev_region(),这个函数定义在
fs/char_dev.c中,下面是内核源码:
/**
 * register_chrdev_region() - register a range of device numbers
 * @from: 要申请的第一个设备的设备号,必须要有主设备号
 * @count: 要申请的有连续设备号的设备的个数
 * @name: 设备名称
 * 返回0表示申请成功,返回负数说明申请失败
 */
int register_chrdev_region(dev_t from, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    dev_t to = from + count;//要申请的最后一个设备的设备号.
    dev_t n, next;

    for (n = from; n < to; n = next) {
        next = MKDEV(MAJOR(n)+1, 0);
        if (next > to)            next = to;
   cd = __register_chrdev_region(MAJOR(n), MINOR(n),next - n, name);
//这个函数在alloc_chrdev_region中也调用了,具体解释见下面解释1.
        if (IS_ERR(cd))    goto fail;见解释2.
    }
    return 0;
fail:
    to = n;
    for (n = from; n < to; n = next) {
     next = MKDEV(MAJOR(n)+1, 0);
     kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
    }
    return PTR_ERR(cd);见解释3.
}
解释1:__register_chrdev_region//注册主设备号和次设备号
/*
major == 0此函数动态分配主设备号
major > 0 则是申请分配指定的主设备号
返回0表示申请成功,返回负数说明申请失败
 */
static struct char_device_struct *
__register_chrdev_region(unsigned int major, unsigned int baseminor,
               int minorct, const char *name)
{
    struct char_device_struct *cd, **cp;
    int ret = 0;    int i;

cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
//kzalloc分配内存并且全部初始化为0,见include/linux/lab.h
kzalloc其实是 kmalloc(size, flags | __GFP_ZERO);的封装
    if (cd == NULL)        return ERR_PTR(-ENOMEM);
//include/asm-generic/errno-base.h #define ENOMEM 12    mutex_lock(&chrdevs_lock);
    if (major == 0) { //下面是动态申请主设备号
        for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i—) {
// ARRAY_SIZE(arr)是://(sizeof(arr)/sizeof((arr)[0])+__must_be_array(arr)
// #define __must_be_array(a) //BUILD_BUG_ON_ZERO(__builtin_types_compatible_p(typeof(a), typeof(&a[0])))
//__builtin_types_compatible_p gcc内嵌函数用于判断一个变量的类型是
//否为某指定的类型,假如是就返回1,否则返回0。
//#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1)
//chrdevs是内核中所有已经注册了设备号的设备的一个数组
        if (chrdevs[i] == NULL)  break;         
}
        if (i == 0) {  ret = -EBUSY;   goto out;}
        major = i;        ret = major;//这里取到一个未使用的设备号
    }
    cd->major = major;       cd->baseminor = baseminor;
    cd->minorct = minorct;   strncpy(cd->name,name, 64);
//以上是申请到设备后对设备数据结构的填充
i = major_to_index(major); //将其转换为哈希表索引
//return major % CHRDEV_MAJOR_HASH_SIZE;
//#define CHRDEV_MAJOR_HASH_SIZE  255

    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
        if ((*cp)->major > major ||
            ((*cp)->major == major &&
             (((*cp)->baseminor >= baseminor) ||
              ((*cp)->baseminor + (*cp)->minorct > baseminor))))
            break;
//在字符设备数组中寻找现在注册的设备.
    /* Check for overlapping minor ranges.  */
    if (*cp && (*cp)->major == major) {
        int old_min = (*cp)->baseminor;
        int old_max = (*cp)->baseminor + (*cp)->minorct - 1;
        int new_min = baseminor;
        int new_max = baseminor + minorct - 1;

        /* New driver overlaps from the left.  */
        if (new_max >= old_min && new_max <= old_max) {
            ret = -EBUSY;
            goto out;
        }

        /* New driver overlaps from the right.  */
        if (new_min <= old_max && new_min >= old_min) {
            ret = -EBUSY;
            goto out;
        }
    }

    cd->next = *cp;
    *cp = cd;
    mutex_unlock(&chrdevs_lock);//在对这样的数组进行遍历的时候应该加锁的.
    return cd;
out:
    mutex_unlock(&chrdevs_lock);
    kfree(cd);
    return ERR_PTR(ret);
}
解释2:
static inline long IS_ERR(const void *ptr)
{
    return IS_ERR_VALUE((unsigned long)ptr);
}
S_ERR_VALUE:
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
MAX_ERRNO:这里可以看出它的值是0x80000fff
#define MAX_ERRNO   4095 //系统内的错误状态的最大值
解释3:
static inline long PTR_ERR(const void *ptr)
{
    return (long) ptr;
}

2.内核自动分配:也就是事先你并不知道哪一个设备号未使用,在申请时就须内核为你挑选一个未使用的设备号.
这中方式的申请函数是:alloc_chrdev_region(),这个函数定义在
fs/char_dev.c中,下面是内核源码:
/**
 * alloc_chrdev_region() - register a range of char device numbers
 * @dev: output parameter for first assigned number
 * @baseminor: 要申请的有连续设备号的第一个设备的次设备号
 * @count: 要申请的有连续设备号的设备的个数
 * @name: 设备或驱动的名称 *
 * 动态申请设备号,申请成功通常将第一个设备的设备号(包括主,次设备号)
 *传递给@dev,并且函数返回0,失败则函数返回负数
 */
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
{
    struct char_device_struct *cd;
    cd = __register_chrdev_region(0, baseminor, count, name);
    if (IS_ERR(cd))
        return PTR_ERR(cd);
    *dev = MKDEV(cd->major, cd->baseminor);
    return 0;
}
这里有了上面的分析,理解上面这一段程序应该不是什么大问题了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值