本篇主要讲在Android系统中的linux下控制GPIO的方法,android 通过jni控制GPIO可基于本篇内容继续开展;

 

linux控制GPIO基本上有两种方法,一是通过pinctrl体系,直接使用/sys下的文件进行控制,另一种就是编写驱动,在驱动中进行控制,下面分别进行介绍:

一、是用sys文件系统控制

Sysfs路径


/sys/class/gpio有3个入口条目:


控制接口       用于用户空间获取GPIO控制 (export和unexport)


GPIO自己    (gpioxx)


GPIO控制器(gpiochipxx)




这是对于标准文件的补充,包括“device”符号




控制接口是只写的:


    /sys/class/gpio/ export


例如:“echo 19 > export”将创建一个GPIO #19的“gpio19”节点(假设内核代码未申请此GPIO号)。


注意,此处返回错误基本上是因为内核驱动中使用了gpio_request占用或者获取了该GPIO




“unexport”————与“export”效果相反




例如:"echo 19 > unexport"将移除一个由“export”文件导出的“gpio19”节点。




GPIO信号拥有如/sys/class/gpio/gpio42/(对应于GPIO#42)的路径,并且具有下列读写属性:


    /sys/class/gpio/gpioN/





“direction”————读为“in”或是“out”。这个值通常可写。写“out”默认初始化此值为低。为了确定无障碍操作,值“low”和“high”可以被写入以配置GPIO的输出初始化值。




注意这个属性“将不存在”如果内核不支持改变一个GPIO的方向,或者它不能被内核代码导出(不能显式的允许用户空间来重新配置GPIO的选项。)




“value”—————读作“0”(低)或“1”(高)。如果GPIO被配置为一个输出,这个值可写;任何非零值均被视为高。


如果管脚可以被配置为中断产生中断管脚,且如果它已经被配置为产生中断(参考“edge”描述),你可以poll(2)此文件并且当中断触发时poll(2)将返回。如果你使用了poll(2),设置POLLPRI和POLLERR事件。如果你使用select(2),在exceptfds中设置文件描述符。在poll(2)返回之后,有两个选择一是lseek(2)到sysfs文件的开始且读新的值,另一个是关闭文件且重打开它来读取新的值。(为何这样设置?)




“edge”————读作“none”、“rising”、“falling”或是“both”。写这些字符串以选择边沿信号,他将使得“value”文件上的poll(2)操作返回。



这个文件只在管脚可以配置为中断产生输入管脚时存在。




“active_low”————读为0(false)或1(true)。写任何非零值都会反转读或写的值。目前和后来的poll(2)支持经由edge属性配置为“rising”或“falling”上升沿或下降沿将遵循这个设置。




GPIO控制器具有如/sys/class/gpio/gpiochip42/(针对控制器,实现GPIO开始于#42)的路径,且具有下列制度属性:


    /sys/class/gpio/gpiochipN/





“base”————与N相等,是第一个被此芯片管理的GPIO



“label”————提供用于诊断(并不总是独一无二的)



“ngpio”————管理的GPIO数(N到N+ngpio-1)


 


从内核代码中导出


--------------------------




内核代码可以显式管理那些使用gpio_request()申请的GPIO的导出


/* export the GPIO to userspace */


int gpio_export(unsigned gpio, bool direction_may_change);




/* reverse gpio_export() */


void gpio_unexport();




/* create a sysfs link to an exported GPIO node */


int gpio_export_link(struct device *dev, const char *name,


unsigned gpio)




/* change the polarity of a GPIO node in sysfs */


int gpio_sysfs_set_active_low(unsigned gpio, int value);






一个内核驱动申请一个GPIO后,它可以使用gpio_export()使得sysfs接口有效。驱动可以控制信号方向是否可以改变。这使得驱动可以防止用户空间代码不小心冲击重要的系统状态。




明确的exporting有助于调试(使得一些实验更简单),或是提供一个总是可以使用的接口,适合于bsp文档。




GPIO被导出后,gpio_export_link()允许在sysfs的任何地方创建GPIO sysfs节点的符号链接。驱动可以用此在它们自己设备sysfs目录下提供指定名字的接口(链接到GPIO节点)




驱动可以使用gpio_sysfs_set_active_low()隐藏GPIO在用户空间和单板之间的线极性不同。这仅影响sysfs接口。极性变换可以在gpio_export()之前和之后完成,并且前面使能的poll(2) (支持上升沿或下降沿事件)将被重新配置为遵循此设置。



 

 

二、编写驱动

 

<span style="font-size:14px;">#include <dt-bindings/gpio/gpio.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <asm/uaccess.h>





static struct UserData{
                int gpio;
                int state;
};


static struct of_device_id luobogpio_of_match[] = {
        { .compatible = "luobogpio" },
        { }
};

MODULE_DEVICE_TABLE(of, luobogpio_of_match);


static int luobogpio_open(struct inode *inode, struct file *filp)
{
    printk("luobogpio_open\n");

        return 0;
}

static ssize_t luobogpio_read(struct file *filp, char __user *ptr, size_t size, loff_t *pos)
{
        if (ptr == NULL)
                printk("%s: user space address is NULL\n", __func__);
        return sizeof(int);
}

static long luobogpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
        long ret = 0;        
        struct UserData userdata;
        unsigned char label[10];


        printk("luobogpio_ioctl: cmd = %d arg = %ld\n",cmd, arg);

        switch (cmd){

        case 0:
                                printk("gpio_request\n");
                                
                                if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData))) 
                return -EFAULT;
                printk("copy_from_user  gpio=%d ,state=%d\n",userdata.gpio,userdata.state);
                


                sprintf(label,"gpio-%d",userdata.gpio);
                printk("----->%s\n",label);
                                ret = gpio_request(userdata.gpio, label);
                                if (ret) {
                                           printk("failed to request GPIO%d for you ret:%d\n",userdata.gpio,ret);
                                 }
                          break;
         case 1:
                  
                            printk("gpio_direction_output\n");
                          
                            if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData))) 
                return -EFAULT;
                printk("copy_from_user  gpio=%d ,state=%d\n",userdata.gpio,userdata.state);
                            ret=gpio_direction_output(userdata.gpio, userdata.state);
                                  if (ret) {
                                printk("failed to gpio_direction_output  for you ret:%d\n",userdata.gpio);
                                 }
          break;
                        
          case 5:
                                printk("gpio_free\n");
                                  if (copy_from_user((void*)&userdata,(void __user *)arg, sizeof(struct UserData))) 
                return -EFAULT;
                printk("copy_from_user  gpio=%d ,state=%d\n",userdata.gpio,userdata.state);
                                gpio_free(userdata.gpio);

          break;
                
                default:
                        printk("unknown ioctl cmd!\n");
                        ret = -EINVAL;
                        break;
        }
        return ret;
}

static int luobogpio_release(struct inode *inode, struct file *filp)
{
    printk("luobogpio_release\n");
    
        return 0;
}

static struct file_operations luobogpio_fops = {
        .owner   = THIS_MODULE,
        .open    = luobogpio_open,
        .read    = luobogpio_read,
        .unlocked_ioctl   = luobogpio_ioctl,
        .release = luobogpio_release,
};

static struct miscdevice luobogpio_dev = 
{
    .minor = MISC_DYNAMIC_MINOR,
    .name = "luobogpio",
    .fops = &luobogpio_fops,
};


static int luobogpio_probe(struct platform_device *pdev)
{
    int ret=-1;
        ret = misc_register(&luobogpio_dev);
        if (ret < 0){
                printk("ac_usb_switch register err!\n");
                return ret;
        }

        printk("func: %s\n", __func__); 
        return 0;
}

static int luobogpio_remove(struct platform_device *pdev)
{
        //printk("func: %s\n", __func__); 
        return 0;
}

#ifdef CONFIG_PM_SLEEP
static int luobogpio_suspend(struct device *dev)
{
        //printk("func: %s\n", __func__); 
        return 0;
}

static int luobogpio_resume(struct device *dev)
{
        //printk("func: %s\n", __func__); 
        return 0;
}
#endif

static const struct dev_pm_ops luobogpio_pm_ops = {
#ifdef CONFIG_PM_SLEEP
        .suspend = luobogpio_suspend,
        .resume = luobogpio_resume,
        .poweroff = luobogpio_suspend,
        .restore = luobogpio_resume,
#endif
};

static struct platform_driver luogpio_driver = {
        .driver                = {
                .name                = "luobogpio",
                .owner                = THIS_MODULE,
                .pm                = &luobogpio_pm_ops,
                .of_match_table        = of_match_ptr(luobogpio_of_match),
        },
        .probe                = luobogpio_probe,
        .remove                = luobogpio_remove,
};

module_platform_driver(luogpio_driver);

MODULE_DESCRIPTION("luobogpio Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:luobogpio");
</span>