#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!\n");
        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!\n");
        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\n", 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 \n", 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!\n");
                break;
                
                case 1:        
                    ret = gpio_direction_output(qset->port, 1);
                    if(ret < 0)
                        printk(KERN_ERR "gpio_direction_output failled!\n");
                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\n", 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\n", 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\n", 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\n",
                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\n",
                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);

am335x gpio 控制的另一种方法的更多相关文章

  1. nodejs高并发大流量的设计实现,控制并发的三种方法

    nodejs高并发大流量的设计实现,控制并发的三种方法eventproxy.async.mapLimit.async.queue控制并发Node.js是建立在Google V8 JavaScript引 ...

  2. delphi控制 word的几种方法--转

    对几种方法的难易程度的判别 a.通过Delphi的控件TOleContainer 将Word嵌入 这是最简单的Ole嵌入,能够直接将Word文档调用,只需要使用ToleContainer.Run就可以 ...

  3. am335x gpio控制

    1.执行下面的命令,可以显示目前驱动已经申请到的IO状态 : $ mount -t debugfs debugfs /sys/kernel/debug  $ cat /sys/kernel/debug ...

  4. Linux 下操作GPIO(两种方法,驱动和mmap)(转载)

    目前我所知道的在Linux下操作GPIO有两种方法: 1.编写驱动,这当然要熟悉Linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据io ...

  5. Linux 下操作gpio(两种方法,驱动和mmap)

    目前我所知道的在linux下操作GPIO有两种方法: 1.  编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据 ...

  6. [转]iOS之浅谈纯代码控制UIViewController视图控制器跳转界面的几种方法

    参考:http://www.mamicode.com/info-detail-469709.html 一.最普通的视图控制器UIViewContoller 一个普通的视图控制器一般只有模态跳转的功能( ...

  7. css控制div显示/隐藏方法及2种方法比较原码 - czf164的专栏 - 博客频道 - CSDN.NET

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  8. iOS之浅谈纯代码控制UIViewController视图控制器跳转界面的几种方法

    .最普通的视图控制器UIViewContoller 一个普通的视图控制器一般只有模态跳转的功能(ipad我不了解除外,这里只说iPhone),这个方法是所有视图控制器对象都可以用的,而实现这种功能,有 ...

  9. 控制页面打印的2种方法(css3的media媒体查询和window.print())

    在实际开发中,有时可能会有打印的需求.下面我总结了2种打印的方法,希望对各位小伙伴有所帮助. ①:直接用window.print()方法就可以打印整个页面,下面是一个小demo <!DOCTYP ...

随机推荐

  1. 【转】Android:最全面的 Webview 详解

    原文: https://blog.csdn.net/carson_ho/article/details/52693322 前言 现在很多App里都内置了Web网页(Hyprid App),比如说很多电 ...

  2. [NPM] Avoid Duplicate Commands by Calling one NPM Script from Another

    We can get a lot of utility through CLI tools invoked via npm scripts. Many of these tools have APIs ...

  3. 广州高清卫星地图 用百度卫星地图server下载 含标签、道路数据叠加 可商用

    广州高清卫星地图的地图展示图片各自是15级别.17级别.19级别的地图.一般来说17级别的地图图片就行用于商用.地图包包括一整张高级别的图片,如要全图浏览请用专业图片处理软件PS等打开. 一般来说互联 ...

  4. 记一发Hive on tez的配置(Hive 3.1.1, Hadoop 3.0.3, Tez 0.9.1)

    直接下载Tez的binary包部署安装是有问题的,因为默认支持hadoop版本为2.7,2.7以上的就需要手动编译了. 下载Tez源码 CD到源码文件夹,mvn install -Dhadoop.ve ...

  5. 阿里云RDS实例内不同数据库之间的数据迁移

    适用场景 本文适用于使用DTS实现相同实例下库名不同的数据库之间的数据迁移.本文以使用DTS将同一RDS实例下的amptest库迁移到jiangliu_amptest库为例来说明如何使用DTS实现相同 ...

  6. checkbox显示选中内容个数

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  7. 腾讯QQ的发展与未来

    http://wenku.baidu.com/view/15166ddfc1c708a1284a447d.html 腾讯QQ的发展与未来

  8. Using AutoFac

    第一次整理了下关于autofac的一些具体的用法 1. 安装 Autofac: Install-Package Autofac -Version 4.8.1 2. 创建两个类库项目,IService ...

  9. C++求两个数的最大值

    //不使用if,:?等推断语句.求两个数字中最大的那个数字. #include<iostream> using namespace std; int main() { int a = -1 ...

  10. SDK Manager 闪退的解决方式

    打开电脑的执行  也就是win+R键    然后在命令行里面打上android即可了