Linux内核学习笔记之使用ioctl函数实现用户态命令

驱动程序:

 

 

/********************************
 * GPIO驱动程序控制GPIO接口高低电平
 * 基于gpio库,四个GPIO识别为一个设备
 * 使用miscdevice结构体动态分配设备号,自动创建/dev/文件
 * 使用ioctl函数实现用户态命令
 * 更多内容见于笔记05
 * 开发板:Tiny 4412
 * 主控芯片:Exynos 4412
 * author: zhangn
 * date: 2016-1-12
 ********************************/

#include 
#include 
#include 
#include 
//包含gpio库头文件
#include //所有处理器通用
#include //三棒子芯片公用
#include //只针对4412芯片

#define GPIO_NUM	4

//0.定义ioctl命令,本例我们定义两个带一个参数的命令
/****************************************************************
  关于_IOW,这是内核中定义的一个用于向内核写数据的一个宏
  其参数是_IOW(幻数, 命令序号, 命令所占内存大小)
  对于第三个字段,这里要使用参数的数据类型,内核里会使用sizeof获得
  这里具体说明详见ldd3的第六章关于ioctl的说明
  最后要注意的是这的宏定义要和用户态程序完全相同
 *****************************************************************/
#define GPIO_TYPE	'Z'//区分不同命令而取的8位幻数,为简便随便使用一个字母
#define GPIO_ON		_IOW(GPIO_TYPE, 1, int)//用户态定义命令参数
#define GPIO_OFF	_IOW(GPIO_TYPE, 2, int)

//四个设备,定义四个gpio号
static int gpios[GPIO_NUM];

//7.实现ioctl函数
//这里书上说函数实参应该在file前面还有个inode参数
//但加上该参数后用户态传进来的命令和参数错位,
//去掉该参数后程序正常,不晓得什么原因,是版本问题?
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为命令代码,代表开关.arg为命令参数,代表gpio序号
	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.准备file_operations
static struct file_operations fops = 
{
	//本例中使用了ioctl函数,不需要open,read等函数
	.owner = THIS_MODULE,
	.unlocked_ioctl = gpio_ioctl,
};

//2.准备miscdevice
//如果将4个GPIO看成一个设备,推荐使用miscdevice代替cdev
//如果一个驱动程序要驱动多个设备,那么就不应该使用miscdevice
//miscdevice会在内核中使用同一个主设备号,设备间使用次设备号进行区分
//内核中会为所有使用misc的设备维护一个链表,具体请自行查阅misc相关信息
//使用misc的好处是内核会自动创建/dev/文件,自动分配设备号
static struct miscdevice misc = 
{
	.minor = MISC_DYNAMIC_MINOR,//次设备号,使用MISC_DYNAMIC_MINOR系统会自动分配
	.name  = "gpios",//设备名称,自动创建的/dev/文件也使用这个名字
	.fops  = &fops,
};

static int __init my_init(void)
{
	int i, ret, gpio_num;
	//以下工作,每个设备完成一次
	for(i = 0; i < GPIO_NUM; ++i)
	{
		//3.在头文件中找到寄存器的宏定义
		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.申请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.对io进行配置
		s3c_gpio_cfgpin(gpio_num, S3C_GPIO_OUTPUT);
		//写入数据,这里是设置默认值
		gpio_set_value(gpio_num, 0);
	}

	//6.注册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");

 

 

 

 

 

用户态测试程序

 

 

/*******************************
 * GPIO的用户态测试
 * &> test /dev/gpios 0|1|2|3 on|off
 * author:zhangn
 * date:2016-1-12
 *******************************/
#include 
#include 
#include 
#include 
#include 

#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);

}

 

 

 

 

 

你可能感兴趣的:(Linux内核学习笔记)