Linux kernel study notes using ioctl function to implement user mode commands

driver:


/********************************
 * The GPIO driver controls the high and low levels of the GPIO interface
 * Based on gpio library, four GPIOs are recognized as one device
 * Use the miscdevice structure to dynamically allocate device numbers and automatically create /dev/ files
 * Use the ioctl function to implement user mode commands
 * See more in Note 05
 * Development board: Tiny 4412
 * Main control chip: Exynos 4412
 * author: zhangn
 * date: 2016-1-12
 ********************************/

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
//Include the gpio library header file
#include <linux/gpio.h>//Common to all processors
#include <plat/gpio-cfg.h>//Three stick chips common
#include <mach/gpio.h>//Only for 4412 chips

#define GPIO_NUM 4

//0. Define the ioctl command, in this example we define two commands with one parameter
/****************************************************************
  Regarding _IOW, this is a macro defined in the kernel for writing data to the kernel
  Its parameter is _IOW (magic number, command serial number, command memory size)
  For the third field, the data type of the parameter is used here, which will be obtained by sizeof in the kernel
  For details, please refer to the description of ioctl in Chapter 6 of ldd3
  The last thing to note is that this macro definition is exactly the same as the user mode program
 *****************************************************************/
#define GPIO_TYPE 'Z'//The 8-bit magic number used to distinguish different commands, just use a letter for convenience
#define GPIO_ON _IOW(GPIO_TYPE, 1, int)//User mode definition command parameters
#define GPIO_OFF	_IOW(GPIO_TYPE, 2, int)

//Four devices, define four gpio numbers
static int gpios[GPIO_NUM];

//7. Implement the ioctl function
//Here the book says that the function argument should have an inode parameter in front of the file
//But after adding this parameter, the command and parameter passed in from user mode are misplaced,
//After removing this parameter, the program is normal, I don't know why, is it a version problem?
static long gpio_ioctl(struct file *filp,
						unsigned int req, unsigned long arg)
{
	printk("req = %d, arg = %ld\n", req, arg);
/*	if(arg>3 || arg<0)
	{
		printk("Only support gpio 0~3\n");
		return -1;
	}*/
	//req is the command code, representing the switch. arg is the command parameter, representing the gpio serial number
	switch(req)
	{
		case GPIO_ON:
			gpio_set_value(gpios[arg], 0);
			printk("arg_on = %ld\n", arg);
			break;
		case GPIO_OFF:
			gpio_set_value(gpios[arg], 1);
			printk("arg_off = %ld\n", arg);
			break;
		default:
			printk("arg_err = %ld\n", arg);
			break;
	}
	return 0;
}

//1. Prepare file_operations
static struct file_operations fops =
{
	//In this example, the ioctl function is used, and functions such as open and read are not required
	.owner = THIS_MODULE,
	.unlocked_ioctl = gpio_ioctl,
};

//2. Prepare miscdevice
//If 4 GPIOs are regarded as a device, it is recommended to use miscdevice instead of cdev
//If a driver is going to drive multiple devices, then miscdevice should not be used
//miscdevice will use the same major device number in the kernel, and use the minor device number to distinguish between devices
//The kernel will maintain a linked list for all devices that use misc. For details, please refer to the related information of misc.
//The advantage of using misc is that the kernel will automatically create the /dev/ file and automatically assign the device number
static struct miscdevice misc =
{
	.minor = MISC_DYNAMIC_MINOR,//Minor device number, the system will automatically assign it using MISC_DYNAMIC_MINOR
	.name = "gpios",//device name, the automatically created /dev/ file also uses this name
	.fops  = &fops,
};

static int __init my_init(void)
{
	int i, ret, gpio_num;
	// The following works, done once per device
	for(i = 0; i < GPIO_NUM; ++i)
	{
		//3. Find the macro definition of the register in the header file
		gpios[i] = EXYNOS4_GPX3(i+2);
		gpio_num = gpios[i];
		printk("i = %d, gpios[%d] = %d, num = %d\n", i, i, gpios[i], gpio_num);
		//4. Apply for gpio
		ret = gpio_request(gpio_num, "myio");
		if (ret)
		{
			printk("Request failed...%d\n", ret);
			for(; i-1 > 0; --i)
				gpio_free(gpios[i-1]);
			return ret;
		}
		//5. Configure io
		s3c_gpio_cfgpin(gpio_num, S3C_GPIO_OUTPUT);
		//Write data, here is to set the default value
		gpio_set_value(gpio_num, 0);
	}

	//6. Register misc
	ret = misc_register(&misc);
	if (ret)
		for(i = 0; i < GPIO_NUM; ++i)
			gpio_free(gpios[i]);

	return ret;
}

static void __exit my_exit(void)
{
	int i;
	misc_deregister(&misc);
	for(i = 0; i < GPIO_NUM; ++i)
		gpio_free(gpios[i]);

}


module_init(my_init);
module_exit(my_exit);

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


User mode test program


/*******************************
 * User mode test of GPIO
 * &> test /dev/gpios 0|1|2|3 on|off
 * author:zhangn
 * date:2016-1-12
 *******************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#define GPIO_TYPE   'Z'
#define GPIO_ON     _IOW(GPIO_TYPE, 1, int)
#define GPIO_OFF    _IOW(GPIO_TYPE, 2, int)

int main(int argc, char **argv)
{
	int fd;
	int gpio_num;
	char * endp;
	
	fd = open(argv[1], O_RDWR);
	if(fd < 0)
	{
		printf("Cannot open %s\n", argv[1]);
		exit(1);
	}

	gpio_num = strtol(argv[2], &endp, 10);
	if(gpio_num > 3)
	{
		close(fd);
		printf("Only support gpio 0~3\n");
		exit(1);
	}

	printf("arg = %d\n", gpio_num);

	if(strncmp(argv[3], "on", 2) == 0)
	{
		ioctl(fd, GPIO_ON, gpio_num);
	}
	else if(strncmp(argv[3], "off", 3) == 0)
	{
		ioctl(fd, GPIO_OFF, gpio_num);
	}
	else
	{
		close(fd);
		exit(1);
	}

	close(fd);
	exit(0);

}


Guess you like

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