• 字符设备驱动(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
    ", bdata->number, down);
            
        if(bdata->number < 4)
        {
            gpio_set_value(leds[bdata->number].gpio,0);
            printk("LED %d: On 
    ",leds[bdata->number].number);
        }
        else
        {
            gpio_set_value(leds[(bdata->number) - 4].gpio,1);
            printk("LED %d: OFF 
    ",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
    ");
        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
    ", devNum);
       
        if(OK == register_chrdev(reg_major, DEVICE_NAME, &gFile))
        {
            printk(KERN_EMERG "register_chrdev_region ok
    ");
        }
        else
        {
            printk(KERN_EMERG"register_chrdev_region error
    ");
            return ERROR;
        }
        
        printk(KERN_EMERG "button driver initial done...
    ");
        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 ------>针对第四种情况

  • 相关阅读:
    有关同时包含<winsock2.h>与<windows.h>头文件的问题
    如何使用微软提供的TCHAR.H头文件?
    下面的程序在VC6通过,在VS2008不能,错误信息都是“不能将参数……从const char[]转换为LPCWSTR”
    Android开发学习日志(四)
    爬虫开发(一)
    java集合源码详解
    Paxos算法
    linux 常用命令
    Bitmap的原理和应用
    Flink Checkpoint 问题排查实用指南
  • 原文地址:https://www.cnblogs.com/embeded-linux/p/10884928.html
Copyright © 2020-2023  润新知