GPIO的驱动模型

    一、概述
    GPIO是嵌入式系统最简单、最常用的资源了,比如点亮LED,控制蜂鸣器,输出高低电平,检测按键,等等。GPIO分输入和输出,在davinci linux中,有关GPIO的最底层的寄存器驱动,\arch\arm\mach-davinci目录下的gpio.c,这个是寄存器级的驱动,搞过单片机MCU的朋友应该比较熟悉寄存器级的驱动。

    GPIO的驱动主要就是读取GPIO口的状态,或者设置GPIO口的状态。就是这么简单,但是为了能够写好的这个驱动,在LINUX上作了一些软件上的分层。为了让其它驱动可以方便的操作到GPIO,在LINUX里实现了对GPIO操作的统一接口,这个接口实则上就是GPIO驱动的框架,具体的实现文件为gpiolib.c在配置内核的时候,我们必须使用CONFIG_GENERIC_GPIO这个宏来支持GPIO驱动。

    GPIO是与硬件体系密切相关的,linux提供一个模型来让驱动统一处理GPIO,即各个板卡都有实现自己的gpio_chip控制模块:request, free,input,output, get,set,irq...然后把控制模块注册到内核中,这时会改变全局gpio数组:gpio_desc[]. 当用户请求gpio时,就会到这个数组中找到,并调用这个GPIO对应的gpio_chip的处理函数。gpio实现为一组可用的 gpio_chip, 由驱动传入对应 gpio的全局序号去 request, dataout,datain, free. 这时会调用gpio_chip中具体的实现。
    gpio是一组可控件的脚,由多个寄存器同时控制。通过设置对应的寄存器可以达到设置GPIO口对应状态与功能。数据状态,输入输出方向,清零,中断(那个边沿触发), 一般是一组(bank)一组的。
    寄存器读写函数: __raw_writel() __raw_writeb() __raw_readl() __raw_readb()

    二、linux 中GPIO模型的结构
    //表示一个gpio口,含对应的gpio_chip.
    //对于每一个gpio,都有一个gpio描述符,这个描述符包含了这个gpio所属的控制器即chip和一些标志,label等
    struct gpio_desc {
        struct gpio_chip    *chip;
        unsigned long    flags;
        /* flag symbols are bit numbers */
        #define FLAG_REQUESTED 0
        #define FLAG_IS_OUT 1
        #define FLAG_RESERVED 2
        #define FLAG_EXPORT 3 /* protected by sysfs_lock */
        #define FLAG_SYSFS 4 /* exported via /sys/class/gpio/control */
        #define FLAG_TRIG_FALL 5 /* trigger on falling edge */
        #define FLAG_TRIG_RISE 6 /* trigger on rising edge */
        #define FLAG_ACTIVE_LOW 7 /* sysfs value has active low */
        #define FLAG_OPEN_DRAIN 8 /* Gpio is open drain type */
        #define FLAG_OPEN_SOURCE 9 /* Gpio is open source type */
        
        #define ID_SHIFT 16 /* add new flags before this one */
        #define GPIO_FLAGS_MASK ((1 << ID_SHIFT) - 1)
        #define GPIO_TRIGGER_MASK (BIT(FLAG_TRIG_FALL) | BIT(FLAG_TRIG_RISE))
     
    #ifdef CONFIG_DEBUG_FS
        const char    *label;
    #endif
    };
    //采用了一个具有ARCH_NR_GPIOS大小的gpio描述符数组。这个描述符数组便代表了系统所有的gpio。
    static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];//ARCH_NR_GPIOS=144,即系统现在有144个GPIO口

    //static struct davinci_gpio_controller chips[DIV_ROUND_UP(DAVINCI_N_GPIO, 32)];//将144个GPIO分成每32个一组
    //一组GPIO控制器结构,例如GPIO0和GPIO1是一组(共32个GPIO口),共用一组寄存器,所以GPIO0和GPIO1荷载一起用chips[0]来控制
    ///共有144个GPIO,分为4组(GPIO0~GPIO8),每组有2个banks(即GPIO0和GPIO1为1组),每组最多可以有32个GPIO,每组的控制寄存器空间有10个。
    struct davinci_gpio_controller {
        struct gpio_chip    chip;//每组对应的gpio_chip
        int            irq_base;//每组对应的中断
        spinlock_t        lock;//自旋锁
        void __iomem        *regs;//每组的寄存器地址
        void __iomem        *set_data;//设置数据寄存器地址
        void __iomem        *clr_data;//清除数据寄存器地址
        void __iomem        *in_data;//输入数据寄存器地址
    };

    //每一个davinci_gpio_controller结构都对应于一个gpio_chip结构,gpio_chip既可看成是davinci_gpio_controller结构的补充
    //表示一个gpio controller.通过这个结构抽象化所有的GPIO源,而让板上其它的模块可以用相同的接口调用使用这些GPIO。
    struct gpio_chip {
        const char    *label;
        struct device    *dev;
        struct module    *owner;
        int    (*request)(struct gpio_chip *chip,unsigned offset);//请求gpio
        void    *free)(struct gpio_chip *chip,unsigned offset);//释放gpio
        int    (*get_direction)(struct gpio_chip *chip,unsigned offset);
        int    (*direction_input)(struct gpio_chip *chip,unsigned offset);//配置gpio为输入,返回当前gpio状态
        int    (*get)(struct gpio_chip *chip,unsigned offset);//获取gpio的状态
        int    (*direction_output)(struct gpio_chip *chip,unsigned offset, int value);//配置gpio为输出,并设置为value
        int    (*set_debounce)(struct gpio_chip *chip,unsigned offset, unsigned debounce);//设置消抖动时间,尤其是gpio按键时有用
        void    (*set)(struct gpio_chip *chip,unsigned offset, int value);//设置gpio为value值
        int    (*to_irq)(struct gpio_chip *chip,unsigned offset);//把gpio号转换为中断号
        void    (*dbg_show)(struct seq_file *s,struct gpio_chip *chip);
        int    base;// 这个gpio控制器的gpio开始编号
        u16    ngpio;//这个gpio控制器说控制的gpio数
        const char    *const *names;
        unsigned    can_sleep:1;
        unsigned    exported:1;
     
    #if defined(CONFIG_OF_GPIO)
        struct device_node *of_node;
        int of_gpio_n_cells;
        int (*of_xlate)(struct gpio_chip *gc,const struct of_phandle_args *gpiospec, u32 *flags);
    #endif
    #ifdef CONFIG_PINCTRL
        struct list_head pin_ranges;
    #endif
    };

    //GPIO寄存器结构
    struct davinci_gpio_regs {
        u32 dir; // gpio方向设置寄存器
        u32 out_data; // gpio设置为输出时,表示输出状态(0或1)
        u32 set_data; // gpio设置为输出时,用于输出高电平
        u32 clr_data; // gpio设置为输出时,用于输出低电平
        u32 in_data; // gpio设置为输入时,用于读取输入值
        u32 set_rising; // gpio中断上升沿触发设置
        u32 clr_rising; // gpio中断上升沿触发清除
        u32 set_falling; // gpio中断下降沿触发设置
        u32 clr_falling; // gpio中断下降沿触发清除
        u32 intstat; // gpio中断状态位,由硬件设置,可读取,写1时清除。
    };

    struct gpio {
        unsigned gpio;//gpio号
        unsigned long flags;//gpio标志
        const char *label;//gpio名
    };

    三、GPIO的初始化
    1.首先设置GPIO的管脚复用寄存器
    static __init void da850_evm_init(void)
    {
        //.......
        ret = davinci_cfg_reg_list(da850_gpio_test_pins);
        if (ret)
            pr_warning("da850_evm_init: gpio test ping mux setup failed: %d\n", ret);
        //.......
    }

    2.根据板级结构的资源初始化chips数组,此函数在系统初始化时自动调用
    static struct davinci_gpio_controller chips[DIV_ROUND_UP(DAVINCI_N_GPIO, 32)];//将144个GPIO分成每32个一组
    static int __init davinci_gpio_setup(void)
    {
        int i, base;
        unsigned ngpio;
        struct davinci_soc_info *soc_info = &davinci_soc_info;//板级资源结构
        struct davinci_gpio_regs *regs;

        if (soc_info->gpio_type != GPIO_TYPE_DAVINCI)//判断GPIO类型
            return 0;

        ngpio = soc_info->gpio_num;//GPIO数量144
        if (ngpio == 0){
            pr_err("GPIO setup: how many GPIOs?\n");
            return -EINVAL;
        }

        if (WARN_ON(DAVINCI_N_GPIO < ngpio))//DAVINCI_N_GPIO=144
            ngpio = DAVINCI_N_GPIO;

        gpio_base = ioremap(soc_info->gpio_base, SZ_4K);//将GPIO的寄存器物理基地址(#define DA8XX_GPIO_BASE        0x01e26000)映射到内存中
        if (WARN_ON(!gpio_base))
            return -ENOMEM;
        
        //共有144个GPIO,分为4组(GPIO0~GPIO8),每组有2个banks(即GPIO0和GPIO1为1组),每组最多可以有32个GPIO,每组的控制寄存器空间有10个。
        //chips[0]--chips[4],base值为0,32,64,96,128,ngpio分别为:32,32,32,32,16
        for (i= 0, base = 0; base < ngpio;i++, base += 32){
            chips[i].chip.label= "DaVinci";
            //设置操作函数
            chips[i].chip.direction_input= davinci_direction_in;
            chips[i].chip.get= davinci_gpio_get;
            chips[i].chip.direction_output= davinci_direction_out;
            chips[i].chip.set= davinci_gpio_set;

            chips[i].chip.base= base;//每一组开始的GPIO号
            //每组控制的GPIO个数,一般为32个
            chips[i].chip.ngpio= ngpio - base;
            if (chips[i].chip.ngpio > 32)
                chips[i].chip.ngpio= 32;

            spin_lock_init(&chips[i].lock);
            //找到这组GPIO的寄存器地址,初始化chips结构
            regs = gpio2regs(base);
            chips[i].regs= regs;//设置每组的寄存器
            chips[i].set_data= ?s->set_data;
            chips[i].clr_data= ?s->clr_data;
            chips[i].in_data= ?s->in_data;
            
            gpiochip_add(&chips[i].chip);//注册gpio_chip
        }
        //chips数组添加到板级资源中
        soc_info->gpio_ctlrs = chips;
        soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32);

        davinci_gpio_irq_setup();//设置GPIO中断
        return 0;
    }
    pure_initcall(davinci_gpio_setup);//linux初始化时会自动调用

    static struct davinci_gpio_regs __iomem __init *gpio2regs(unsigned gpio)
    {
        void __iomem *ptr;
        
        //根据GPIO的基地址累加,其中基地址(gpio_base+0)是REVID(Revision ID Register)寄存器
        //(gpio_base+8)是BINTEN(GPIO Interrupt Per-Bank Enable Register)寄存器
        //所以第一组寄存器从基地址+0x10开始
        if (gpio < 32 * 1)
            ptr = gpio_base + 0x10;
        else if (gpio < 32 * 2)
            ptr = gpio_base + 0x38;
        else if (gpio < 32 * 3)
            ptr = gpio_base + 0x60;
        else if (gpio < 32 * 4)
            ptr = gpio_base + 0x88;
        else if (gpio < 32 * 5)
            ptr = gpio_base + 0xb0;
        else
            ptr = NULL;
        return ptr;
    }

    int gpiochip_add(struct gpio_chip *chip)
    {
        unsigned long    flags;
        int        status = 0;
        unsigned    id;
        int        base = chip->base;
        
        //检测gpio的有效性,判断这组GPIO的起始号是否在有效范围内
        if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1))&& base >= 0){
            status = -EINVAL;
            goto fail;
        }

        spin_lock_irqsave(&gpio_lock, flags);
        
        //如果这组GPIO的起始号小于0,则动态的分配gpio的开始索引。
        if (base < 0){
            base = gpiochip_find_base(chip->ngpio);//这个函数在gpiolib.c中,在gpio_desc[]中分配chip->ngpio个空间(从最后往前分配),返回第一个index
            if (base < 0){
                status = base;
                goto unlock;
            }
            chip->base = base;
        }

        //确保这些分配的gpio号没有被其他chip占用
        for (id = base; id < base + chip->ngpio; id++){
            if (gpio_desc[id].chip != NULL){
                status = -EBUSY;
                break;
            }
        }
        //填充gpio_desc,将该组内的每个GPIO口的gpio_desc结构和该组的控制结构chip联系起来
        if (status == 0){
            for (id = base; id < base + chip->ngpio; id++){
                gpio_desc[id].chip = chip;
                gpio_desc[id].flags = !chip->direction_input? (1 << FLAG_IS_OUT): 0;//设置GPIO口标志
            }
        }
        of_gpiochip_add(chip);

    unlock:
        spin_unlock_irqrestore(&gpio_lock, flags);

        if (status)
            goto fail;

        status = gpiochip_export(chip);//与sysfs文件系统有关,这里不关心
        if (status)
            goto fail;

        return 0;
    fail:
        /* failures here can mean systems won't boot... */
        pr_err("gpiochip_add: gpios %d..%d (%s) failed to register\n",chip->base, chip->base + chip->ngpio - 1,chip->label ? : "generic");
        return status;
    }

    四.gpio的申请
    //所谓申请就是检测GPIO描述符desc->flags的FLAG_REQUESTED标志,已申请的话该标志是1,否则是0
    //往往多个gpio作为一个数组来进行申请
    int gpio_request_array(struct gpio *array, size_t num)
    {
        int i, err;

        for (i= 0; i < num; i++, array++){//遍历数组中的每一个GPIO,gpio是GPIO号,flags是输入输出标志等,label是其取一个名字
            err = gpio_request_one(array->gpio, array->flags, array->label);
            if (err)
                goto err_free;
        }
        return 0;

    err_free:
        while (i--)
            gpio_free((--array)->gpio);
        return err;
    }

    int gpio_request_one(unsigned gpio, unsigned long flags, constchar *label)
    {
        int err;
        
        //gpio则为你要申请的哪一个管脚,label则是为其取一个名字。
        err = gpio_request(gpio,label);
        if (err)
            return err;

        if (flags & GPIOF_DIR_IN)//GPIO标志是输入
            err = gpio_direction_input(gpio);//设置管脚为输入
        else//GPIO标志是输出
            err = gpio_direction_output(gpio,(flags & GPIOF_INIT_HIGH) ? 1: 0);//根据标志确定输出1还是0

        if (err)
            gpio_free(gpio);

        return err;
    }

    int gpio_request(unsigned gpio, constchar *label)
    {
        struct gpio_desc    *desc;
        struct gpio_chip    *chip;
        int            status = -EINVAL;
        unsigned long        flags;
        //屏蔽中断
        spin_lock_irqsave(&gpio_lock, flags);

        if (!gpio_is_valid(gpio))//判断是否有效,也就是参数的取值范围判断
            goto done;
            
        //根据GPIO号找到对应的GPIO描述符结构
        desc = &gpio_desc[gpio];
        chip = desc->chip;//找到该GPIO所在的组控制器
        if (chip == NULL)
            goto done;
            
        //计数加1
        if (!try_module_get(chip->owner))
            goto done;

        //这里测试并设置flags的第FLAG_REQUESTED位,如果没有被申请就返回该位的原值0
        if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)== 0){
            desc_set_label(desc,label ? : "?");//设置GPIO描述符结构desc的label字段
            status = 0;
        } else {
            status = -EBUSY;
            module_put(chip->owner);
            goto done;
        }

        if (chip->request){/* chip->request may sleep */
            spin_unlock_irqrestore(&gpio_lock, flags);
            status = chip->request(chip, gpio - chip->base);
            spin_lock_irqsave(&gpio_lock, flags);

            if (status < 0){
                desc_set_label(desc, NULL);
                module_put(chip->owner);
                clear_bit(FLAG_REQUESTED, &desc->flags);
            }
        }

    done:
        if (status)
            pr_debug("gpio_request: gpio-%d (%s) status %d\n",gpio,label ? : "?", status);
        spin_unlock_irqrestore(&gpio_lock, flags);
        return status;
    }

    五.GPIO的操作
    1.设置GPIO为输出或输入
    int gpio_direction_input(unsigned gpio)
    {
        unsigned long        flags;
        struct gpio_chip    *chip;
        struct gpio_desc    *desc = &gpio_desc[gpio];
        int            status = -EINVAL;

        spin_lock_irqsave(&gpio_lock, flags);
        
        //判断GPIO号是否有效
        if (!gpio_is_valid(gpio))
            goto fail;
        //找到GPIO对应的gpio_chip结构
        chip = desc->chip;
        if (!chip || !chip->get || !chip->direction_input)
            goto fail;
        
        //确保此GPIO是在此组内,chip->base是此组GPIO的起始号,chip->ngpio是此组GPIO的个数
        gpio -= chip->base;
        if (gpio >= chip->ngpio)
            goto fail;
            
        //确保GPIO已申请
        status = gpio_ensure_requested(desc, gpio);
        if (status < 0)
            goto fail;

        //到这里可以确保GPIO是有效的
        spin_unlock_irqrestore(&gpio_lock, flags);

        might_sleep_if(chip->can_sleep);
        //status=0
        if (status){
            status = chip->request(chip, gpio);
            if (status < 0){
                pr_debug("GPIO-%d: chip request fail, %d\n",chip->base + gpio, status);
                goto lose;
            }
        }
        //调用底层的已经设置过的操作,这里即davinci_direction_in
        status = chip->direction_input(chip, gpio);
        if (status == 0)//返回成功
            clear_bit(FLAG_IS_OUT, &desc->flags);//清除输出标志
    lose:
        return status;
    fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
        if (status)
            pr_debug("%s: gpio-%d status %d\n",__func__, gpio, status);
        return status;
    }

    int gpio_direction_output(unsigned gpio, int value)
    {
        //.........GPIO的检查,同上函数
        
        //调用底层的已经设置过的操作,这里即davinci_direction_out
        status = chip->direction_output(chip, gpio, value);
        if (status == 0)//返回成功
            set_bit(FLAG_IS_OUT, &desc->flags);//设置输出标志
    lose:
        return status;
    fail:
        spin_unlock_irqrestore(&gpio_lock, flags);
        if (status)
            pr_debug("%s: gpio-%d status %d\n",__func__, gpio, status);
        return status;
    }

    //根据前边对gpio_chip结构的初始化,会调用\arch\arm\mach-davinci\gpio.c里的函数
    static int davinci_direction_in(struct gpio_chip *chip, unsigned offset)
    {
        return __davinci_direction(chip, offset, false, 0);
    }

    static int davinci_direction_out(struct gpio_chip *chip, unsigned offset, int value)
    {
        return __davinci_direction(chip, offset, true, value);
    }

    static inline int __davinci_direction(struct gpio_chip *chip,unsigned offset, bool out, int value)
    {
        struct davinci_gpio_controller *d = chip2controller(chip);
        struct davinci_gpio_regs __iomem *g = d->regs;//找到此组GPIO的控制寄存器地址
        unsigned long flags;
        u32 temp;
        u32 mask = 1 << offset;

        spin_lock_irqsave(&d->lock, flags);
        temp = __raw_readl(&g->dir);//读出当前寄存器的输入输出方向
        
        if (out){//为1设置输出
            temp &= ~mask;
            __raw_writel(mask, value ? &g->set_data: &g->clr_data);//确定是用于输出高电平还是输出低电平
        }
        else {//为0设置为输入
            temp |= mask;
        }
        __raw_writel(temp, &g->dir);//写入方向寄存器
        spin_unlock_irqrestore(&d->lock, flags);

        return 0;
    }

    2.获取gpio的状态
    int __gpio_get_value(unsigned gpio)
    {
        struct gpio_chip    *chip;

        chip = gpio_to_chip(gpio);
        WARN_ON(chip->can_sleep);
        return chip->get ? chip->get(chip, gpio - chip->base): 0;//调用davinci_gpio_get
    }

    static int davinci_gpio_get(struct gpio_chip *chip, unsigned offset)
    {
        struct davinci_gpio_controller *d = chip2controller(chip);
        struct davinci_gpio_regs __iomem *g = d->regs;

        return (1 << offset) & __raw_readl(&g->in_data);
    }

    3.设置GPIO的值
    void __gpio_set_value(unsigned gpio, int value)
    {
        struct gpio_chip    *chip;

        chip = gpio_to_chip(gpio);
        WARN_ON(chip->can_sleep);
        chip->set(chip, gpio - chip->base, value);//调用davinci_gpio_set
    }

    static void davinci_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
    {
        struct davinci_gpio_controller *d = chip2controller(chip);
        struct davinci_gpio_regs __iomem *g = d->regs;

        __raw_writel((1 << offset), value ? &g->set_data: &g->clr_data);
    }

    六、GPIO驱动编写
    1.首先要申请GPIO口
    2.注册设备
    3.创建GPIO的sysfs相关文件
    #define GPIO_MAJOR 199         // major device NO.
    #define GPIO_MINOR 0         // minor device NO.
    #define DEVICE_NAME "omapl138_gpios" /*定义设备驱动的名字,或设备节点名称*/

    #define SET_OUTPUT_LOW 0
    #define SET_OUTPUT_HIGH 1
    #define GET_VALUE 2
    #define SET_INPUT 3

    static struct class *gpio_class;
    static struct gpio gpio_array[] =
    {
        /*{ GPIO_TO_PIN(0, 0), GPIOF_OUT_INIT_LOW,     "RTU_WDI_SIGNAL" },will request fail*/
        { GPIO_TO_PIN(0, 1), GPIOF_OUT_INIT_HIGH,"RTU_PLC_BAK_IO1"},
        { GPIO_TO_PIN(0, 2), GPIOF_OUT_INIT_LOW,     "RTU_CHG_EN" },
        { GPIO_TO_PIN(0, 3), GPIOF_IN,         "RTU_CHG_PG"},
        { GPIO_TO_PIN(0, 5), GPIOF_IN,         "RTU_USB_OC_OUT"},
        { GPIO_TO_PIN(0, 6), GPIOF_OUT_INIT_LOW,     "RTU_RUN_IND_LED" },
        { GPIO_TO_PIN(1, 10), GPIOF_IN,         "RTU_TSC_BUSY"},
        { GPIO_TO_PIN(1, 11), GPIOF_IN,        "RTU_PENIRQn"},
        { GPIO_TO_PIN(1, 12), GPIOF_OUT_INIT_LOW,"RTU_uP_Q26x_RESET" },
        { GPIO_TO_PIN(1, 13), GPIOF_OUT_INIT_HIGH,"RTU_uP_GPRS_PWR_EN" },
        { GPIO_TO_PIN(1, 14), GPIOF_OUT_INIT_HIGH,"RTU_uP_Q26x_ON/OFF" },
        { GPIO_TO_PIN(2, 1), GPIOF_OUT_INIT_LOW,"RTU_PLC_Reset" },
        { GPIO_TO_PIN(2, 2), GPIOF_OUT_INIT_LOW,"RTU_PLC_T_Reg" },
        { GPIO_TO_PIN(2, 4), GPIOF_OUT_INIT_LOW,"RTU_PLC_BAK_IO2" },
        { GPIO_TO_PIN(2, 5), GPIOF_OUT_INIT_LOW,"RTU_RS485_RE" },
        { GPIO_TO_PIN(2, 15), GPIOF_OUT_INIT_HIGH,"RTU_CHPWR_CS" },
        { GPIO_TO_PIN(3, 9), GPIOF_OUT_INIT_HIGH,"RTU_RS485_DE" },
        { GPIO_TO_PIN(6, 1), GPIOF_OUT_INIT_HIGH,"RTU_uP_VPIF_CLKO3" },
        { GPIO_TO_PIN(6, 9), GPIOF_IN,         "RTU_KEY_IN2"},
        { GPIO_TO_PIN(6, 11), GPIOF_IN,        "RTU_ALARM_IN5"},
        { GPIO_TO_PIN(6, 15), GPIOF_OUT_INIT_HIGH,"RTU_uP_RESETOUTn"},
    };

    static int gpio_open(struct inode *inode,struct file *file)
    {
        printk(KERN_WARNING"gpio open success!\n");
        return 0;
    }

    static int gpio_release(struct inode *inode, struct file *filp)
    {
      printk (KERN_ALERT "Device gpio released\n");
      return 0;
    }

    static int gpio_read(struct file*f,char *dst,size_tsize,loff_t*offset)
    {
        unsigned char num;
        __copy_to_user(&num,dst,1);
    #ifdef DEBUG
         printk("__copy_to_user:%d\n",num);
    #endif

        return 0;
    }

    static int gpio_write(struct file*f,constchar *src,size_tsize,loff_t *offset)
    {
         unsigned char num;
         __copy_from_user(&num,src,1);
    #ifdef DEBUG
         printk("__copy_from_user:%d\n",num);
    #endif
         return 0;

    }

    static long gpio_ioctl(struct file *file,unsigned int cmd,unsigned long gpio)
    {
        int i;
        unsigned long gpio_num = (gpio/100)*16+gpio%100;
        for (i= 0; i < ARRAY_SIZE(gpio_array);i++){
            if(gpio_array[i].gpio== gpio_num)
                goto valid_gpio;
        }
        return -1;
            
    valid_gpio:
        switch(cmd)//cmd表示应用程序传入的 GPIO 动作
        {
            case SET_OUTPUT_LOW://0
            {
                gpio_direction_output(gpio_num, 0);
                break;
            }
            case SET_OUTPUT_HIGH://1
            {
                gpio_direction_output(gpio_num, 1);
                break;
            }
            case GET_VALUE://2
            {
                return gpio_get_value(gpio_num);    
            }
            case SET_INPUT://3
            {
                gpio_direction_input(gpio_num);
                break;
            }
            default:
            {
                printk(KERN_EMERG "GPIO command mistake!!!\n");
                break;
            }
        }
        return 0;
    }
            
    static const struct file_operations gpio_fops=
    {
      .owner = THIS_MODULE,
      .open = gpio_open,
      .release = gpio_release,
      .read = gpio_read,
      .write = gpio_write,
      .unlocked_ioctl = gpio_ioctl,
    };

    static int __init gpio_init(void)/*内核初始化会调用该函数*/
    {
        int ret;
        
        ret = gpio_request_array(gpio_array, ARRAY_SIZE(gpio_array));
        if (ret < 0)
        {
            printk(KERN_EMERG "GPIO request failed\n");
            goto request_failed;
        }
        
        dev_t my_dev_no;
        struct cdev *gpio_cdev;
        gpio_cdev = cdev_alloc();
        if(gpio_cdev == NULL)
        {
            printk(KERN_EMERG "Cannot alloc cdev\n");
            goto request_failed;
        }
        cdev_init(gpio_cdev,&gpio_fops);
        gpio_cdev->owner=THIS_MODULE;
        int result=alloc_chrdev_region(&my_dev_no,0,1,DEVICE_NAME);
        if(result < 0)
        {
            printk(KERN_EMERG "alloc_chrdev_region failed\n");
            goto request_failed;
        }
        ret=cdev_add(gpio_cdev,my_dev_no,1);
        
        
      ret = register_chrdev(GPIO_MAJOR, DEVICE_NAME, &gpio_fops);//驱动字符设备
         if(ret < 0)
         {
        printk(KERN_EMERG "GPIO register failed\n");
        goto request_failed;
         }
        
        //在sysfs文件系统下创建一个类
      gpio_class = class_create(THIS_MODULE, DEVICE_NAME);
      //device_create-->device_create_vargs-->device_register创建相应的sysfs文件(如dev文件),用于udev根据sysfs文件系统下的dev文件创建设备节点
        device_create(gpio_class, NULL, MKDEV(GPIO_MAJOR, GPIO_MINOR), NULL, DEVICE_NAME);
        return ret;
        
    request_failed:
        gpio_free_array(gpio_array, ARRAY_SIZE(gpio_array));
        return ret;
    }

    static void __exit gpio_exit(void)
    {
        device_destroy(gpio_class, MKDEV(GPIO_MAJOR, GPIO_MINOR));
      class_unregister(gpio_class);
      class_destroy(gpio_class);
      unregister_chrdev(GPIO_MAJOR, DEVICE_NAME);
    }

    module_init(gpio_init);
    module_exit(gpio_exit);
    MODULE_LICENSE("GPL");
    MODULE_VERSION ("v2.0");
    MODULE_AUTHOR("wbl <>");
    MODULE_DESCRIPTION("OMAPL138 GPIO driver");


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 写 Linux GPIO 驱动程序需要具备一定的内核编程基础和熟悉 Linux 设备驱动框架。下面是一个简单的流程: 1. 创建设备文件:在 /dev 目录下创建设备文件,并为其分配一个设备号。 2. 实现设备驱动模块:写一个 Linux 模块,该模块实现了所有与设备相关的操作。 3. 初始化 GPIO:在驱动程序中读取 GPIO 设备的芯片信息,并初始化相应的 GPIO 端口。 4. 实现设备操作:为设备的不同操作(例如读写)实现相应的函数,并在驱动程序中调用。 5. 注册设备驱动:使用 Linux 内核提供的函数注册驱动程序,以使系统知道该驱动程序的存在。 6. 测试设备驱动:使用测试程序(例如用户空间的 C 程序)测试驱动程序的正确性。 这是写 Linux GPIO 驱动程序的大致流程,如果您是初学者,建议先学习 Linux 内核编程基础知识和设备驱动编程基础。 ### 回答2: 编写Linux GPIO驱动程序可以通过以下步骤完成: 1. 设计GPIO驱动程序的目的和功能。确定驱动程序需要实现的功能,例如读取输入、设置输出、中断处理等。 2. 在驱动程序中包含必要的头文件,如`<linux/gpio.h>`和`<linux/interrupt.h>`,这些头文件包含了GPIO驱动程序所需的API。 3. 定义GPIO设备的结构体。该结构体应包含GPIO设备的基本信息,如设备名称、GPIO引脚号、中断号等。此外,还可以包含用于存储设备状态的变量。 4. 实现GPIO设备的初始化函数。在初始化函数中,可以完成GPIO引脚的初始化,设置引脚为输入或输出模式,并根据需要使能中断功能。 5. 实现GPIO设备的读写函数。根据GPIO设备的类型(输入或输出),分别实现相应的读取和写入函数。 6. 实现GPIO中断处理函数。如果GPIO设备需要处理中断,可以在中断处理函数中完成相应的操作,如读取中断标志并清除中断,然后通过设备驱动发送信号等。 7. 将GPIO设备注册为字符设备。将驱动程序注册为字符设备,使用`struct file_operations`结构体注册设备的读写和中断处理函数。 8. 编译驱动程序。使用Linux内核提供的编译工具,如GCC,将驱动程序编译为内核模块。 9. 安装驱动程序。将编译生成的内核模块文件复制到适当的位置,并使用`insmod`命令加载驱动程序。 10. 测试驱动程序。使用GPIO设备的测试代码,通过读取和写入GPIO引脚,以及模拟中断触发,验证驱动程序的功能和正确性。 以上是编写Linux GPIO驱动程序的大体步骤。实际开发中可能还涉及到其他细节,如错误处理、用户态API等,具体需要根据实际需求进行调整和完善。 ### 回答3: 编写Linux GPIO驱动程序需要实现以下步骤: 1. 初始化GPIO引脚:在设备驱动程序中,首先需要初始化所需的GPIO引脚。这可以通过使用Linux GPIO子系统提供的API函数来实现。通常,需要指定引脚的编号、输入/输出方向、中断触发方式等参数。 2. 注册设备驱动程序:在驱动程序中,需要将初始化的GPIO引脚注册为一个称为“设备”的结构体。可以使用Linux的设备模型来实现注册。设备模型包含有关设备的信息,以及与Linux GPIO子系统交互所需的回调函数。 3. 编写读写函数:在设备驱动程序中,需要编写读和写函数,以便从GPIO引脚读取输入值或向其写入输出值。这些函数通常在设备注册时与GPIO引脚相关联。读函数从引脚读取输入值,而写函数将指定的值写入引脚。 4. 处理中断:如果需要在GPIO引脚状态发生变化时触发中断,可以在设备驱动程序中实现处理中断的功能。这通常涉及到注册中断处理函数,并在设备注册时关联相关的GPIO引脚。 5. 清理和释放:在设备驱动程序不再需要时,需要进行资源清理和释放。这可以通过编写一个“销毁设备”的函数来实现。在该函数中,可以关闭和释放GPIO引脚,并且从系统中注销设备。 编写Linux GPIO驱动程序需要了解Linux设备模型GPIO子系统和相关的API函数。此外,对于特定的硬件平台,可能还需要深入了解GPIO控制器和寄存器的工作原理。最好参考GPIO驱动程序示例代码来帮助开发。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值