GPIO control of Linux kernel study notes

/********************************
 * The GPIO driver controls the high and low levels of the GPIO interface
 * Four GPIOs are recognized as four devices
 * Create four files to control four GPIOs respectively
 * echo on|off > /dev/driverx
 * Use the meter to measure the pin voltage and observe the results
 * For details of this example, please refer to Chapter 3 of LDD3
 * Development board: Tiny 4412
 * Main control chip: Exynos 4412
 * author: zhangn
 * date: 2016-1-10
 ********************************/

#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>

//define the device number
#define GPIO_MAJOR  60  //主
#define GPIO_NUM    4   //次

//define the register address
#define GP_BASE 0X11000000 //The physical base address of the device, used by the ioremap function
#define GP_SIZE 0X1000 //The physical address is mapped to the space size of the kernel

//The register is based on the physical base address offset
#define GPX3CON     0x0C60
#define GPX3DAT     0x0C64
#define GPX3PUD		0x0c68

//1. Prepare a private structure for each char device
struct mygpio
{
	int gpio_num;//device number, there are four devices in this example
	int gpio_state;//Device status: on/1,off/0
	dev_t dev_id;//Device number, the kernel driver must have
	struct cdev gpio_cdev;//The kernel uses this structure internally to represent character devices, which must be allocated before the kernel calls device operations
};

static struct mygpio *gpios[GPIO_NUM];
static void __iomem *vir_base;//If you map multiple times, write it inside the structure

//9. Implement the function pointer in file_operations
static int gpio_open(struct inode *inode, struct file *filp)
{
	/* Each time the application layer opens a file, the kernel maintains a file structure
	  Also maintains an inode structure for each open file
	  A file has multiple files opened multiple times, but all point to the same inode
	  For the inode structure, we only care about two members here
	  dev_t i_rdev, contains the device number registered to the system during init
	  struct cdev *i_cdev points to a pointer to a cdev structure, a cdev corresponds to a device
	  Here is the pointer to the member gpio_cdev of the private structure mygpio
	  The application layer opens a file, and the kernel creates a file structure
	  In file, the function association of a set of operation files is described through the file_operations structure
	  At the same time, the kernel maintains an inode, the same file is opened and created for the first time, and the count is increased by opening it multiple times.
	  For details, please refer to the relevant information of vfs
	  When the kernel calls the gpio_open function, the device information is passed in through the inode parameter
	  At the same time, the information of the file each time the file is opened is passed in.
	  See Chapter 3 of ldd3 for details here
	   */
	struct mygpio *tmp = container_of(inode->i_cdev, struct mygpio, gpio_cdev);
	filp->private_data = tmp;//This member is mainly used to pass data through file in different functions
	return 0;
}

static int gpio_release(struct inode *inode, struct file *filp)
{
	//If there are resources in open that need to be released, release them here
	//The memory pointed to by tmp in open is allocated and released outside, and there is no need to release it here
	return 0;
}

static ssize_t gpio_write(struct file *filp, const char __user *buf,
							size_t count, loff_t *f_pos)
{
	struct mygpio *dev = filp->private_data;
	char tmp[10] = {0};
	int value;

	if(copy_from_user(tmp, buf, 3))
		return -EFAULT;
	if(strncmp(tmp, "on", 2) == 0)
	{
		value = readl (vir_base + GPX3DAT);
		value &= ~(1 << (dev->gpio_num+2));
		writel (value, vir_base + GPX3DAT);
		dev->gpio_state = 1;
	}
	else if(strncmp(tmp, "off", 3) == 0)
	{
		value = readl (vir_base + GPX3DAT);
		value |= 1 << (dev->gpio_num + 2);
		writel (value, vir_base + GPX3DAT);
		dev->gpio_state = 0;
	}
	else
		return -1;
	return count;
}

//2. Prepare the file_operations structure
static struct file_operations gpio_fops =
{
	.owner   = THIS_MODULE,
	.open    = gpio_open,
	.release = gpio_release,
	.write   = gpio_write,
};

static int __init my_init(void)
{
	int i, value;
	//3. Physical addresses are mapped to kernel virtual addresses
	vir_base = ioremap(GP_BASE, GP_SIZE);
	if (! vir_base)
	{
		printk("Cannot ioremap\n");
		return -EIO;
	}

	// The following work is done once per device
	for(i = 0; i < GPIO_NUM; ++i)
	{
		//4. Allocate memory for each device's private structure
		gpios[i] = kzalloc(sizeof(*gpios[i]), GFP_KERNEL);
		if(!gpios[i])
		{
			// release memory for the allocated structure
			for(; i >= 0; --i)
				kfree(gpios[i]);
			// release the address map
			iounmap(vir_base);
			return -ENOMEM;
		}

		//5. Initialize registers for each device
		value = readl (vir_base + GPX3CON);
		value |= 0x11111111;
		writel(value, vir_base + GPX3CON);

		value = readl (vir_base + GPX3DAT);
		value |= 1 << (i+2);//Use the 2345th pin of the register
		writel (value, vir_base + GPX3DAT);

		value = readl (vir_base + GPX3PUD);
		value = 0x3;
		writel (value, vir_base + GPX3PUD);

		gpios[i]->gpio_num = i;
		gpios[i]->gpio_state = 0;

		//6. Assign major and minor device numbers to the device
		gpios[i]->dev_id = MKDEV(GPIO_MAJOR, i);
		//7. Initialize the cdev structure and associate it with the file_operations structure
		cdev_init(&gpios[i]->gpio_cdev, &gpio_fops);
		gpios[i]->gpio_cdev.owner = THIS_MODULE;
		gpios[i]->gpio_cdev.ops = &gpio_fops;
		//8. Add the device number in mygpio to the cdev structure through cdev_add
		cdev_add(&gpios[i]->gpio_cdev, gpios[i]->dev_id, 1);
	}

	return 0;
}

static void __exit my_exit(void)
{
	int i;
	iounmap(vir_base);
	for(i = 0; i < GPIO_NUM; ++i)
	{
		if(gpios[i])
		{
			cdev_del(&gpios[i]->gpio_cdev);
			kfree(gpios[i]);
		}
	}
}


module_init(my_init);
module_exit(my_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ZhangN");

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325385398&siteId=291194637