zoukankan      html  css  js  c++  java
  • am335x gpio 控制的另一种方法

    #include <linux/gpio.h>  
    #include <linux/module.h>  
    #include <linux/kernel.h>  
    #include <linux/moduleparam.h>  
    #include <linux/delay.h>  
    #include <linux/types.h>  
    #include <linux/cdev.h>  
    #include <linux/device.h>  
    #include <linux/fs.h>  
    #include <linux/init.h>  
    #include <linux/uaccess.h>
    #include <linux/io.h>
    #include <linux/slab.h>

    /*-----------------------------------------------------*/
    #define DEVICE_NAME        "kio"
    #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
    /*------------------------------------------------------*/

    struct gpio_qset;

    /*设备编号存储结构体*/
    struct dev_num
    {
            dev_t devNum;
            unsigned int major;
            unsigned int minor;
            unsigned int minor_first;
            unsigned int count;
    };
    struct dev_num gpio_dev_num;

    /*设备描述结构体*/
    struct gpio_dev
    {
            struct cdev cdev;
            struct gpio_qset* dptr;    //设备数据存储链表第一项
            unsigned long size;        //链表长度(随着申请的GPIO端口数增长)
    };
    struct gpio_dev *gpio_devp;

    /*设备数据存储结构体(采用链式存储,不理解的可以看《数据结构》)*/
    struct gpio_qset
    {
            unsigned long port;            //端口号 #define GPIO_TO_PIN(bank, gpio) (32 * (bank) + (gpio))
            unsigned int ddr;              //方向(输入(0)或输出(1))
            char value;                    //高(1)低(0)电平
            unsigned long num;             //当前编号(按照申请顺序编号)
            char label[10];                //申请的GPIO使用名称
            struct gpio_qset* next;        //指向链表下一项的指针
    };

    /**
     * 功能:初始化gpio_dev
     * *inode:
     * *filp:
     * 描述:用户空间调用open时运行
     *         int (*open) (struct inode *, struct file *);
     * 返回值:0
     */
    static int gpio_open(struct inode *inode, struct file *filp)
    {
            struct gpio_dev *dev;
            
            //由container_of获得结构体指针inode中结构体cdev的指针,
            //让结构体指针dev指向上述的指针所指的地址,
            //再让file->private指向这一地址,
            dev = container_of(inode->i_cdev, struct gpio_dev, cdev);
            filp->private_data = dev;
            dev->dptr = NULL;
            dev->size = 0;
            //printk(KERN_ERR "gpio_open success! ");
            return 0;
    }

    /**
     * 功能:释放申请的GPIO端口以及释放链表(gpio_qset)
     * *inode:
     * *filp:
     * 描述:用户空间调用close时运行
     *         int (*release) (struct inode *, struct file *);
     * 返回值:0
     */
    static int gpio_close(struct inode *inode, struct file *filp)
    {
            struct gpio_dev *dev = filp->private_data;
            struct gpio_qset *qset, *qsetTmp;
            qsetTmp = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);
            for(qset = dev->dptr; qset->next !=NULL; qset=qsetTmp)
            {
                    qsetTmp = qset->next;
                    gpio_free(qset->port);        //释放申请的端口
                    kfree(qset);                //释放gpio_qset内存
            }
            gpio_free(qsetTmp->port);
            kfree(qsetTmp);
             //printk(KERN_ERR "gpio release! ");
            return 0;
    }

    /**
     * 功能:申请新的gpio_qset的内存,确定相应的GPIO端口功能
     * *inode:
     * *filp:
     * cmd:实现的功能,
     *     0:读,
     *     1:写,
     *     2:释放GPIO端口
     * arg:执行操作的GPIO端口
     * 描述:用户空间调用ioctl时运行
     *         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
     * 返回值:成功返回操作的GPIO端口号
     *           错误返回相应的错误码
     */
    static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
            int ret;
            struct gpio_dev *dev = filp->private_data;
            struct gpio_qset *qset;

            //cmd == 2 设计成为释放arg端口的功能,之后申请内存的任务便不必执行了,所以直接返回
            if(cmd == 2)
            {
                    gpio_free(arg);
                    return arg;
            }

            dev->size++;

            if(dev->dptr == NULL)
            {
                    dev->dptr = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);
                    qset = dev->dptr;
            }
            else
            {
                    for(qset = dev->dptr; qset->next != NULL; qset = qset->next);                        //找到链表最后一项
                    qset->next = (struct gpio_qset *)kzalloc(sizeof(struct gpio_qset), GFP_KERNEL);
                    qset = qset->next;
            }
            /*链表数据*/
            qset->num = dev->size;        //确定自己的编号
            qset->ddr = cmd;              //确定方向
            qset->port = arg;             //确定端口号
            qset->next = NULL;            //最后一项地址清空

            //printk(KERN_ERR "qset->num=%d,qset->ddr=%d,qset->port=%d ", qset->num, qset->ddr, qset->port);
            sprintf(qset->label, "gpio%ld", qset->port);                //确定申请的GPIO使用名称(和端口直接相关)
            ret = gpio_request(qset->port, qset->label);                //申请端口
            
            /*由于gpio_requset会自己判断成功与否并且退出函数,故注释掉对ret的判断
            if(ret < 0)
                    printk(KERN_ERR "%s_requset failled!%d ", qset->label, ret);
            */

            /*判断GPIO工作方向(输出或输出)*/        
            switch(qset->ddr)
            {
                    case 0:        
                        ret = gpio_direction_input(qset->port);
                        if(ret < 0)
                            printk(KERN_ERR "gpio_direction_input failled! ");
                    break;
                    
                    case 1:        
                        ret = gpio_direction_output(qset->port, 1);
                        if(ret < 0)
                            printk(KERN_ERR "gpio_direction_output failled! ");
                    break;
                    
                    default:
                            return -EPERM;        /* Operation not permitted */
            }
            return qset->num;
    }

    /**
     * 功能:获取相应端口电平,写入相应的qset_dev->value,写到用户空间的readBuf
     * *inode:
     * *filp:
     * *readBuf:读数据缓存指针,用户空间的指针
     * port:被读取的GPIO端口号
     * *offp:
     * 描述:用户空间调用read时运行
     *         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
     * 返回值:成功返回qset->value
     *           错误返回相应的错误码
     */
    static ssize_t gpio_read (struct file *filp, char __user *readBuf, size_t port, loff_t *offp)
    {
            long ret;
            struct gpio_dev *dev = filp->private_data;
            struct gpio_qset *qset;
            
            if(dev->dptr == NULL)        
                    return -ENODEV;                /* No such device */
                            
            for(qset = dev->dptr; qset != NULL; qset = qset->next)
            {
                    if(qset->port == port)
                            break;
                    if(qset->next == NULL)        
                            return -ENODEV;        /* No such device */
            }

            if(qset->ddr != 0)                //判断是否ioctl设置为读操作
                    return -EPERM;                /* Operation not permitted */
            qset->value = gpio_get_value(qset->port);
            //printk(KERN_ERR "qset->port:%d, qset->value:%d ", qset->port, qset->value);
            switch(qset->value)
            {
                    case 0:        
                        ret = copy_to_user(readBuf, "0", 1);
                    break;
                    
                    case 1:        
                        ret = copy_to_user(readBuf, "1", 1);
                    break;
            }
            return qset->value;
    }
    /**
     * 功能:写入相应端口电平,写入相应的qset_dev->value,数据来自用户空间的writeBuf
     * *inode:
     * *filp:
     * *writeBuf:写数据缓存指针,用户空间的指针
     * port:被写入的GPIO端口号
     * *offp:
     * 描述:用户空间调用write时运行
     *         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
     * 返回值:成功返回qset->value
     *           错误返回相应的错误码
     */
    static ssize_t gpio_write (struct file *filp, const char __user *writeBuf, size_t port, loff_t *offp)
    {
            long ret;
            struct gpio_dev *dev = filp->private_data;
            struct gpio_qset *qset;

            if(dev->dptr == NULL)        
                    return -ENODEV;                /* No such device */
                            
            for(qset = dev->dptr; qset != NULL; qset = qset->next)
            {
            //        printk(KERN_ERR "qset->port=%d,port=%d ", qset->port, port);
                    if(qset->port == port)
                            break;
                    if(qset->next == NULL)        
                            return -ENODEV;        /* No such device */
            }

            if(qset->ddr != 1)                //判断是否ioctl设置为写操作
                    return -EPERM;                /* Operation not permitted */

            ret = copy_from_user(&qset->value, writeBuf, 1);
            //printk(KERN_ERR "write:%d ", qset->value);
            switch(qset->value)
            {
                    case '0': qset->value = 0;
                              gpio_set_value(qset->port, 0);
                              break;
                    default : qset->value = 1;
                              gpio_set_value(qset->port, 1);
                              break;
            }
            return qset->value;
    }

    /*文件操作结构体*/
    static const struct file_operations gpio_fops = {
            .owner = THIS_MODULE,
            .open = gpio_open,
            .unlocked_ioctl = gpio_ioctl,
            .write = gpio_write,
            .read = gpio_read,
            .release = gpio_close,
    };

    /*cdev注册函数*/
    static void gpio_setup_cdev(struct gpio_dev *dev, int index)
    {
            int ret,devno = gpio_dev_num.devNum + index;
            cdev_init(&dev->cdev, &gpio_fops);
            dev->cdev.owner = THIS_MODULE;
            dev->cdev.ops = &gpio_fops;

            ret = cdev_add(&dev->cdev, devno, 1);

            if(ret)
                    printk(KERN_ERR "error %d : adding gpioCtl%d",ret,index);
    }

    static struct class   *kio_class;

    /*设备初始化函数*/
    static int __init omap2gpio_init(void)
    {
            int ret;

            gpio_dev_num.count = 1;
            gpio_dev_num.minor_first = 0;
            
            ret = alloc_chrdev_region(&gpio_dev_num.devNum,
                    gpio_dev_num.minor_first,
                    gpio_dev_num.count,
                    DEVICE_NAME);
            
            if(ret < 0)        
                    return ret;
            
            gpio_dev_num.major = MAJOR(gpio_dev_num.devNum);
            gpio_dev_num.minor = MINOR(gpio_dev_num.devNum);

            gpio_devp = kzalloc(sizeof(struct gpio_dev),GFP_KERNEL);

            gpio_setup_cdev(gpio_devp, 0);

            printk(KERN_ERR "gpio alloc_chrdev_region success, major = %d ",
                    gpio_dev_num.major);
            
            kio_class = class_create(THIS_MODULE, DEVICE_NAME);
            
            device_create(kio_class, //pointer to the struct class that this device should be registered to
                      NULL,
                      gpio_dev_num.devNum,        //the dev_t for the char device to be added
                      NULL,
                      "%s",
                      DEVICE_NAME);
            return 0;
    }

    /*设备释放函数*/
    static void __exit omap2gpio_exit(void)
    {
            cdev_del(&gpio_devp->cdev);
            kfree(gpio_devp);
            unregister_chrdev_region(gpio_dev_num.devNum, 1);
            
            device_destroy(kio_class, gpio_dev_num.devNum);   //the dev_t for the char device to be added
            
            class_destroy(kio_class);
            
            printk(KERN_ERR "gpio unregister_chrdev_region success, major = %d ",
                    gpio_dev_num.major);
    }

    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("Mlo_Lv,Tute-421E-studio");
    MODULE_DESCRIPTION("This module is used to conrtol ti omap2 gpio");

    module_init(omap2gpio_init);
    module_exit(omap2gpio_exit);

  • 相关阅读:
    C#中页面之间跳转方法比较
    C#中web页面之间传递数组参数
    js跳转传递参数
    枚举
    js操作做GridView
    数据库登录问题汇总
    数据库登录遇到的问题
    关于配置服务器(IIS7)(二)
    关于配置服务器(IIS7)
    使用JavaScript设置、获取父子页面中的值
  • 原文地址:https://www.cnblogs.com/zym0805/p/3945286.html
Copyright © 2011-2022 走看看