B.Linux

灵魂构造师

导航

字符设备驱动(1)代码分析---之register_chrdev

/*****************************************************************************
简    述:简单字符型驱动程序,手动静态分配设备号,手动创建设备节点
******************************************************************************/
#include <linux/module.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <linux/irq.h>
#include <linux/kdev_t.h>
#include <linux/interrupt.h>
#include <linux/init.h>

#define DEVICE_NAME        "leds"

struct button_desc {
    int gpio;
    int number;
    char *name;    
};

struct led_desc {
    int gpio;
    int number;
    char *name;    
};

static struct button_desc buttons[] = {
    { S5PV210_GPH2(0), 0, "KEY0" },
    { S5PV210_GPH2(1), 1, "KEY1" },
    { S5PV210_GPH2(2), 2, "KEY2" },
    { S5PV210_GPH2(3), 3, "KEY3" },
    { S5PV210_GPH3(0), 4, "KEY4" },
    { S5PV210_GPH3(1), 5, "KEY5" },
    { S5PV210_GPH3(2), 6, "KEY6" },
    { S5PV210_GPH3(3), 7, "KEY7" },
};

static struct led_desc leds[] = {
    {S5PV210_GPJ2(0),1,"LED1"},
    {S5PV210_GPJ2(1),2,"LED2"},    
    {S5PV210_GPJ2(2),3,"LED3"},
    {S5PV210_GPJ2(3),4,"LED4"},
};

#define OK            (0)
#define ERROR         (-1)
dev_t  devNum;
unsigned int subDevNum = 1;//要申请的次设备号个数
int reg_major  =  234;    
int reg_minor =   0;


static irqreturn_t button_interrupt(int irq, void *dev_id)
{
    struct button_desc *bdata = (struct button_desc *)dev_id;

    int down;
    unsigned tmp;
    tmp = gpio_get_value(bdata->gpio);

    /* active low */
    down = !tmp;
    printk("KEY %d: %08x\n", bdata->number, down);
        
    if(bdata->number < 4)
    {
        gpio_set_value(leds[bdata->number].gpio,0);
        printk("LED %d: On \n",leds[bdata->number].number);
    }
    else
    {
        gpio_set_value(leds[(bdata->number) - 4].gpio,1);
        printk("LED %d: OFF \n",leds[(bdata->number)-4].number);
    }
    
    return IRQ_HANDLED;
}

int butsOpen(struct inode *p, struct file *f)
{
    
        int irq;
    int i;
    int err = 0;
    printk(KERN_EMERG"butsOpen\r\n");
    for (i = 0; i < ARRAY_SIZE(buttons); i++) {
        if (!buttons[i].gpio)
            continue;

        irq = gpio_to_irq(buttons[i].gpio);
        // irq = IRQ_EINT(16)+i =160+i "S5PV210_GPH2(i)"
        //irq = IRQ_EINT(24)+i =168+i  "S5PV210_GPH3(i)"
        err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, 
                buttons[i].name, (void *)&buttons[i]);
        if (err)
            break;
    }
    
    for(i = 0; i<ARRAY_SIZE(leds);i++)
    {
        if(!leds[i].gpio)
            continue;
        gpio_direction_output(leds[i].gpio,1);
    }
    
    if (err) {
        i--;
        for (; i >= 0; i--) {
            if (!buttons[i].gpio)
                continue;

            irq = gpio_to_irq(buttons[i].gpio);
            disable_irq(irq);
            free_irq(irq, (void *)&buttons[i]);
        }

        return -EBUSY;
    }
    return 0;
}

static const struct file_operations gFile = 
{
    .owner = THIS_MODULE,
    .open  =  butsOpen,    
};


int charDrvInit(void)
{
    
    devNum = MKDEV(reg_major, reg_minor);
    printk(KERN_EMERG"devNum is %d\r\n", devNum);
   
    if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile))
    {
        printk(KERN_EMERG "register_chrdev_region ok\r\n");
    }
    else
    {
        printk(KERN_EMERG"register_chrdev_region error\r\n");
        return ERROR;
    }
    
    printk(KERN_EMERG "button driver initial done...\r\n");
    return 0;
}

void __exit charDrvExit(void)
{
    int i,irq;
    unregister_chrdev(reg_major, DEVICE_NAME);
    for (i = 0; i < ARRAY_SIZE(buttons); i++) {
            if (!buttons[i].gpio)
                continue;

            irq = gpio_to_irq(buttons[i].gpio);
            disable_irq(irq);
            free_irq(irq, (void *)&buttons[i]);
        }
    
    return;
}
module_init(charDrvInit);//执行insmod时会执行此行代码并调用charDrvInit,驱动开始
module_exit(charDrvExit);//执行rmmod时,结束
MODULE_LICENSE("GPL");
MODULE_AUTHOR("LiuB");
驱动源代码
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);
    static inline int register_chrdev(unsigned int major, const char *name,
                  const struct file_operations *fops)
    {
        return __register_chrdev(major, 0, 256, name, fops);
            
    }
    int __register_chrdev(unsigned int major, unsigned int baseminor,
              unsigned int count, const char *name,const struct file_operations *fops)
    {    
        struct char_device_struct *cd;
        struct cdev *cdev;
            cd = __register_chrdev_region(major, baseminor, count, name);
                {
                    struct char_device_struct *cd, **cp;
                    cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
                    cd->major = major;
                    cd->baseminor = baseminor;
                    cd->minorct = minorct;
                    strlcpy(cd->name, name, sizeof(cd->name));
                    i = major_to_index(major);
                    for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
                    。。。。。
                    //找到可以添加新的设备的位置*cp
            //此处cd为链表中唯一节点,cd->next = NULL,所以这句代码相当于在cd链表后添加一个节点*cp
                    cd->next = *cp;
                    //继续让cd指向链表尾
                    *cp = cd;
                    mutex_unlock(&chrdevs_lock);
                    return cd;
                }
            cdev = cdev_alloc();//给局部结构体变量cdev分配空间
            //将register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops)中的i2cdev_fops结构体的具体内容赋给新建的cdev结构体
            cdev->owner = fops->owner;
            cdev->ops = fops;
            kobject_set_name(&cdev->kobj, "%s", name);
            //添加设备
            err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
                {
                    //dev= MKDEV(cd->major, baseminor)
                    cdev->dev = dev;//添加设备号
                    cdev->count = count;//设备个数
                    //将指定的设备号加入到管理设备号及其对应设备的kobj_map结构体中的probe数组中
                    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);
                }
            cd->cdev = cdev;//将赋值完成的cdev传到结构体cd中的cdev结构体成员
            return major ? 0 : cd->major;
    }
for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)

退出for循环的四种情况:
1、已存在的主设备号大于即将新添加的主设备号
2、已存在的主设备号等于即将新添加的主设备号&&并且次设备最小号大于新注册的次设备最小号
3、已存在的主设备号等于即将新添加的主设备号&&次设备最小号等于新注册的次设备最小号
4、已存在的主设备号等于即将新添加的主设备号&&次设备最小号小于新注册的次设备最小号&&新注册的次设备最小号小于已存在的次设备号范围(最小号+个数)
注:
1、对于第一种情况跳过下面的if判断注册到链表中
2、对于第二、三、四种情况在跳出for循环后继续进行下面判断来分析是否冲突,如果不冲突则注册到链表中

/* 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) { //A
            ret = -EBUSY;
            goto out;
        }

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

 

//A   --------->这个if判断针对第二种情况

//B ------>针对第三种情况(肯定冲突)

//B ------>针对第四种情况

 

posted on 2019-06-26 21:55  B.Linux  阅读(315)  评论(0编辑  收藏  举报