Some records of linux operating gpio

  Some knowledge points of using GPIO in linux are recorded as follows:

1. Operate GPIO in the driver

  In the linux kernel, if the pinctrl subsystem reuses a PIN as GPIO, then you can use the API functions provided by the gpio subsystem to operate gpio, such as setting GPIO as input and output, reading GPIO values, etc. The main purpose of the gpio subsystem is to facilitate driver developers to use gpio. Driver developers add gpio-related information in the device tree, and then use the API functions provided by the gpio subsystem to operate GPIO in the driver.

1.1. Find the OF function of the node from the device tree

  Devices are "hanged" to the device tree in the form of nodes, so if you want to obtain other attribute information of this device, you must first obtain the node of this device. The Linux kernel uses the device_node structure to describe a node. This structure is defined in the file include/linux/of.h. There are 5 OF functions related to finding nodes.

1.1.1, of_find_node_by_name function

  The of_find_node_by_name function finds the specified node by the node name. The function prototype is as follows:

struct device_node *of_find_node_by_name(struct device_node *from,const char *name);

  The meanings of function parameters and return values ​​are as follows:
  from: the node to start searching, if it is NULL, it means searching the entire device tree from the root node.
  name: The name of the node to find.
  Return value: the found node, if it is NULL, it means the search failed.

1.1.2, of_find_node_by_type function

  The of_find_node_by_type function finds the specified node through the device_type attribute. The function prototype is as follows:

struct device_node *of_find_node_by_type(struct device_node *from, const char *type)

  The meanings of function parameters and return values ​​are as follows:
  from: the node to start searching, if it is NULL, it means searching the entire device tree from the root node.
  type: The type string corresponding to the node to be searched, that is, the device_type attribute value.
  Return value: the found node, if it is NULL, it means the search failed.

1.1.3, of_find_compatible_node function

  The of_find_compatible_node function finds the specified node according to the two attributes device_type and compatible.
  The function prototype is as follows:

struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

  The meanings of function parameters and return values ​​are as follows:
  from: the node to start searching, if it is NULL, it means searching the entire device tree from the root node.
  type: The type string corresponding to the node to be searched, that is, the value of the device_type attribute, which can be NULL, indicating that the device_type attribute is ignored.
  compatible: A list of compatible attributes corresponding to the node to be found.
  Return value: the found node, if it is NULL, it means the search failed

1.1.4、of_find_matching_node_and_match 函数

  The of_find_matching_node_and_match function finds the specified node through the of_device_id matching table. The function prototype is as follows:

struct device_node *of_find_matching_node_and_match(struct device_node *from,const struct of_device_id *matches,const struct of_device_id **match)

  The meanings of function parameters and return values ​​are as follows:
  from: the node to start searching, if it is NULL, it means searching the entire device tree from the root node.
  matches: of_device_id matching table, which is to find nodes in this matching table.
  match: The found matching of_device_id.
  Return value: the found node, if it is NULL, it means the search failed

1.1.5, of_find_node_by_path function

  The of_find_node_by_path function finds the specified node through the path. The function prototype is as follows:

inline struct device_node *of_find_node_by_path(const char *path)

  The meanings of the function parameters and return value are as follows:
  path: the node name with the full path, you can use the alias of the node, for example, "/backlight" is the full path of the backlight node.
  Return value: the found node, if it is NULL, it means the search failed

1.2. OF functions related to gpio

1.2.1, of_get_named_gpio function

  This function obtains the GPIO number, because the API functions about GPIO in the Linux kernel must use the GPIO number, this function will convert the attribute information similar to <&gpio5 7 GPIO_ACTIVE_LOW> in the device tree into the corresponding GPIO number, this function is used in the driver Very often! The function prototype is as follows:

int of_get_named_gpio(struct device_node *np,const char *propname, int index)

  The meanings of function parameters and return values ​​are as follows:
  np: device node. For example, the device node obtained from the of_find_node_by_path function.
  propname: Contains the property name to get GPIO information.
  index: GPIO index, because one attribute may contain multiple GPIOs, this parameter specifies which GPIO number to get, if there is only one GPIO information, this parameter is 0.
  Return value: positive value, the obtained GPIO number; negative value, failure.

1.3. API functions of gpio subsystem

  For driver developers, after setting up the device tree, they can use the API functions provided by the gpio subsystem to operate the specified GPIO. The gpio subsystem shields the driver developers from the specific process of reading and writing registers. The commonly used API functions provided by the gpio subsystem are as follows:

1.3.1, gpio_request function

  The gpio_request function is used to apply for a GPIO pin. Before using a GPIO, you must use gpio_request to apply. The function prototype is as follows:

int gpio_request(unsigned gpio, const char *label)

  The meanings of function parameters and return values ​​are as follows:
  gpio: the gpio label to apply for, use the of_get_named_gpio function to get the specified GPIO attribute information from the device tree, and this function will return the GPIO label.
  label: Set a name for gpio.
  Return value: 0, the application is successful; other values, the application fails.
  Generally speaking, a GPIO is only allocated to one device, so the driver of this device will request this GPIO. In this way, when other devices also want to request this GPIO, it will return failure. In fact, gpio_request just marks this GPIO and has no real effect. The operation of GPIO is done through functions such as gpio_set_value and gpio_direction_output. Even if there is no request, the level of GPIO can be set. For the device driver, it should be guaranteed that each GPIO related to the device should perform a gpio_request during initialization (usually a probe), and use gpio_free when removing. Of course, if the probe fails, the GPIO that has been requested should be freed in the probe. You don’t need to request and free every time you use it, you just need to directly gpio_set_value. The specific introduction of the gpio_request function can be viewed in the linux kernel file kernel/Documentation/gpio/gpio-legacy.txt.

1.3.2, gpio_free function

  If a GPIO is not used, then you can call the gpio_free function to release it. The function prototype is as follows:

void gpio_free(unsigned gpio)

  The meanings of function parameters and return values ​​are as follows:
  gpio: the gpio label to be released.
  Return value: None.

1.3.3, gpio_direction_input function

  This function is used to set a GPIO as an input, and the function prototype is as follows:

int gpio_direction_input(unsigned gpio)

  The meanings of function parameters and return values ​​are as follows:
  gpio: GPIO label to be set as input.
  Return value: 0, the setting is successful; negative value, the setting fails.

1.3.4, gpio_direction_output function

  This function is used to set a GPIO as an output and set the default output value. The function prototype is as follows:

int gpio_direction_output(unsigned gpio, int value)

  The meanings of function parameters and return value are as follows:
  gpio: GPIO label to be set as output.
  value: GPIO default output value.
  Return value: 0, the setting is successful; negative value, the setting fails.

1.3.5, gpio_get_value function

  This function is used to get the value (0 or 1) of a GPIO, this function is a macro, the definition is as follows:

#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)

  The meanings of function parameters and return values ​​are as follows
  gpio: the GPIO label to be obtained.
  Return value: non-negative value, obtained GPIO value; negative value, failed to obtain.

1.3.6, gpio_set_value function

  This function is used to set the value of a GPIO, this function is a macro, defined as follows

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)

  The meanings of function parameters and return values ​​are as follows:
  gpio: GPIO label to be set.
  value: the value to set.
  Return value: None

1.4. Example of using MISC to drive gpio

  The major device number of all MISC device drivers is 10, and different devices use different slave device numbers. MISC device will automatically create cdev, no need to manually create, so using MISC device driver can simplify the writing of character device driver.

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define MISCBEEP_NAME		"miscbeep"	/* 名字 在/dev目录下生成的文件,应用层根据此名字操作驱动程序	*/
#define BEEPOFF 			0			/* 关蜂鸣器 */
#define BEEPON 				1			/* 开蜂鸣器 */

/* miscbeep设备结构体 */
struct miscbeep_dev
{
    
    
	struct device_node	*nd; /* 设备节点 */
	int beep_gpio;			/* beep所使用的GPIO编号		*/
};

struct miscbeep_dev miscbeep;		/* beep设备 */

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int miscbeep_open(struct inode *inode, struct file *filp)
{
    
    
	filp->private_data = &miscbeep; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    
    
	int retvalue;
	unsigned char databuf[1];
	unsigned char beepstat;
	struct miscbeep_dev *dev = filp->private_data;

	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
    
    
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}

	beepstat = databuf[0];		/* 获取状态值 */
	if(beepstat == BEEPON) {
    
    	
		gpio_set_value(dev->beep_gpio, 0);	/* 打开蜂鸣器 */
	} else if(beepstat == BEEPOFF) {
    
    
		gpio_set_value(dev->beep_gpio, 1);	/* 关闭蜂鸣器 */
	}
	return 0;
}

/* 设备操作函数 */
static struct file_operations miscbeep_fops = {
    
    
	.owner = THIS_MODULE,
	.open = miscbeep_open,
	.write = miscbeep_write,
};

/* MISC设备结构体 */
static struct miscdevice beep_miscdev = {
    
    
	.minor = MISC_DYNAMIC_MINOR,//动态生成次设备号
	.name = MISCBEEP_NAME,
	.fops = &miscbeep_fops,
};

 /*
  * @description     : flatform驱动的probe函数,当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - dev     : platform设备
  * @return          : 0,成功;其他负值,失败
  */
static int miscbeep_probe(struct platform_device *dev)
{
    
    
	int ret = 0;

	printk("beep driver and device was matched!\r\n");
	/* 设置BEEP所使用的GPIO */
	/* 1、获取设备节点:beep */
	miscbeep.nd = of_find_node_by_path("/beep");
	if(miscbeep.nd == NULL) {
    
    
		printk("beep node not find!\r\n");
		return -EINVAL;
	} 

	/* 2、 获取设备树中的gpio属性,得到BEEP所使用的BEEP编号 */
	miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);
	if(miscbeep.beep_gpio < 0) {
    
    
		printk("can't get beep-gpio");
		return -EINVAL;
	}
	ret=gpio_request(miscbeep.beep_gpio, "beep");	/* 请求IO */
	if(ret < 0) 
	{
    
    
		printk("gpio_request error!\r\n");
	}
	/* 3、设置GPIO5_IO01为输出,并且输出高电平,默认关闭BEEP */
	ret = gpio_direction_output(miscbeep.beep_gpio, 1);
	if(ret < 0) 
	{
    
    
		printk("can't set gpio!\r\n");
	}
	
	/* 一般情况下会注册对应的字符设备,但是这里我们使用MISC设备
  	 * 所以我们不需要自己注册字符设备驱动,只需要注册misc设备驱动即可
	 */
	ret = misc_register(&beep_miscdev);
	if(ret < 0){
    
    
		printk("misc device register failed!\r\n");
		return -EFAULT;
	}

	return 0;
}

/*
 * @description     : platform驱动的remove函数,移除platform驱动的时候此函数会执行
 * @param - dev     : platform设备
 * @return          : 0,成功;其他负值,失败
 */
static int miscbeep_remove(struct platform_device *dev)
{
    
    
	/* 注销设备的时候关闭LED灯 */
	gpio_set_value(miscbeep.beep_gpio, 1);

	/* 注销misc设备 */
	misc_deregister(&beep_miscdev);
	return 0;
}

 /* 匹配列表 */
 static const struct of_device_id beep_of_match[] = {
    
    
     {
    
     .compatible = "atkalpha-beep" },
     {
    
     /* Sentinel */ }
 };
 
 /* platform驱动结构体 */
static struct platform_driver beep_driver = {
    
    
     .driver     = {
    
    
         .name   = "imx6ul-beep",         /* 驱动名字,用于和设备匹配 */
         .of_match_table = beep_of_match, /* 设备树匹配表          */
     },
     .probe      = miscbeep_probe,
     .remove     = miscbeep_remove,
};

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init miscbeep_init(void)
{
    
    
	return platform_driver_register(&beep_driver);
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit miscbeep_exit(void)
{
    
    
	platform_driver_unregister(&beep_driver);
}

module_init(miscbeep_init);
module_exit(miscbeep_exit);
MODULE_LICENSE("GPL");


Two, Linux does not directly control GPIO through the driver

  GPIO can be controlled through sysfs, enter the /sys/class/gpio directory, you can see that the directory contains two files export, unexport and 5 gpiochipX (X is equal to 0, 32, 64, 96, 128) named folder.
   gpiochipX: The GPIO controller included in the current SoC. We know that I.MX6UL/I.MX6ULL contains a total of 5 GPIO controllers, namely GPIO1, GPIO2, GPIO3, GPIO4, and GPIO5, which correspond to gpiochip0, gpiochip32, and gpiochip64 respectively. , gpiochip96, gpiochip128 these 5 folders, each gpiochipX folder is used to manage a group of GPIO.
  For a given GPIO pin, how to calculate its corresponding number in sysfs? In fact, it is very simple. For example, given a GPIO pin as GPIO4_IO16, what is its corresponding number? First of all, we need to make sure that GPIO4 corresponds to gpiochip96, and the minimum number of GPIO pins in this group is 96 (corresponding to GPIO4_IO0), so the number corresponding to GPIO4_IO16 is naturally 96 + 16 = 112; similarly, the number corresponding to GPIO3_IO20 is 64 + 20 = 84 .
  export: used to export the specified number of GPIO pins. Before using a GPIO pin, it needs to be exported, and it can be used only after the export is successful. Note that the export file is a write-only file and cannot be read. Write a specified number into the export file to export the corresponding GPIO pin.
  If there is no corresponding GPIO, you need to write the GPIO number to be operated into the export file , so that the operation interface of the GPIO is exposed from the kernel space to the user space.
  The following shell script can read the corresponding GPIO number

#! /bin/sh
for i in /sys/class/gpio/gpiochip*
do
echo `cat $i/label`: `cat $i/base`
done

  After exporting, you can operate in two ways

2.1. Direct reading and writing

2.1.1. In the shell, you can

  Export GPIO No. 110: /sys/class/gpio# echo 110 > export
  set the direction as output: /sys/class/gpio/gpio110# echo out > direction
  set the output high level: /sys/class/gpio/gpio110# echo 1 > value
  unexport GPIO 110: /sys/class/gpio# echo 110 > unexport

2.1.1. In the application program, you can

   Export GPIO No. 110: system("echo 110 > /sys/class/gpio/export");
  set the direction as output: system("echo out > /sys/class/gpio/gpio110/direction");
  set the output high ping: system("echo 1 > /sys/class/gpio/gpio110/value");
  unexport GPIO 110: system("echo 110 > /sys/class/gpio/unexport");

2.2. Reading and writing through files

static int GPIOConfig(const char *attr, const char *val)
{
    
    
    char file_path[100];
    int len;
    int fd;
    char *gpiopath= "/sys/class/gpio/gpio110";
    sprintf(file_path, "%s/%s", gpiopath, attr);
    if (0 > (fd = open(file_path, O_WRONLY))) 
    {
    
    
        perror("open error");
        return fd;
    }

    len = strlen(val);
    if (len != write(fd, val, len)) 
    {
    
    
        perror("write error");
        close(fd);
        return -1;
    }

    close(fd);  //关闭文件
    return 0;
}
int GPIOInit(void)
{
    
    
    int fd;
    char gpiopath[]= "/sys/class/gpio/gpio110";
    if (access(gpiopath, F_OK)) //如果目录不存在 则需要导出
    {
    
    

        if (0 > (fd = open("/sys/class/gpio/export", O_WRONLY))) //打开export
        {
    
    
            printf("open error: %s",strerror(errno));
            return -1;
        }
        if (3 != write(fd, "110", 3))//导出GPIO
        {
    
    
            printf("write error: %s",strerror(errno));
            close(fd);
            return -1;
        }
        close(fd);  //关闭文件
        sleep(1);//延时1S,防止还没有导出GPIO,就对GPIO进行操作
    } 
        
    if (GPIOConfig("direction", "out")) return -1;/* 配置为输出模式 */
    if (GPIOConfig("active_low", "0")) return -1; /* 配置极性为0 */
    if (GPIOConfig("value", "1")) return -1; /* 输出高电平 */
    return 0;
}

Guess you like

Origin blog.csdn.net/xxxx123041/article/details/131932175