用户与驱动之间的ioctl协议构成
也就是request或cmd,本质上就是一个32位数字,理论上可以是任何一个数,但为了保证命令码的唯一性,linux定义了一套严格的规定,通过计算得到这个命令吗数字。linux将32位划分为四段。
含义如下。
1)dir,即direction,表示ioctl命令的访问模式,分为无数据(_IO)、读数据(_IOR)、写数据(_IOW)、读写数据(_IOWR)四种模式。
2)type,即device type,表示设备类型,也可翻译成“幻数”或“魔数”,可以是任意一个char型字符,如’a’、‘b’、‘c’等,其主要作用是使ioctl命令具有唯一的设备标识。不过在内核中’w’、‘y’、'z’三个字符已经被使用了。
3)nr,即number,命令编号/序数,取值范围0~255,在定义了多个ioctl命令的时候,通常从0开始顺次往下编号。
4)size,涉及到ioctl的参数arg,占据13bit或14bit,这个与体系有关,arm使用14bit。用来传递arg的数据类型的长度,比如如果arg是int型,我们就将这个参数填入int,系统会检查数据类型和长度的正确性。
在上面的四个参数都需要用户自己定义,linux系统提供了宏可以使程序员方便的定义ioctl命令码。
include/uapi/asm-generic/ioctl.h
--------------------------------------------
/* used to create numbers */
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
分别对应了四个dir:
_IO(type, nr):用来定义不带参数的ioctl命令。
_IOR(type,nr,size):用来定义用户程序向驱动程序写参数的ioctl命令。
_IOW(type,nr,size):用来定义用户程序从驱动程序读参数的ioctl命令。
_IOWR(type,nr,size):用来定义带读写参数的驱动命令。
当然了,系统也定义反向解析ioctl命令的宏。
include/uapi/asm-generic/ioctl.h
--------------------------------------------
/* used to decode ioctl numbers */
#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK
led_drv.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <asm/io.h>
#include <linux/pwm.h>
#include <linux/err.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/proc_fs.h>
#include <linux/ioctl.h>
#define LED_MAGIC 'l'
#define LEDREAD _IOR(LED_MAGIC, 0, int)
#define LEDWRITE _IOW(LED_MAGIC, 1, int)
static int major;
static struct class* dh_led_class;
static int led_drv_open(struct inode *node, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int led_drv_close(struct inode *node, struct file *file)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static long led_drv_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int led_read_val;
int ret;
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
if (_IOC_TYPE(cmd) != LED_MAGIC)
{
printk("[%s] command type [%c] error!\n", __FUNCTION__, _IOC_TYPE(cmd));
return -ENOTTY;
}
switch (cmd)
{
case LEDWRITE:
{
//memcpy((&led_read_val, (int*)(arg), sizeof(&arg)));
ret = copy_from_user(&led_read_val, (int*)arg, sizeof(int));
printk("write: led = %d\n", led_read_val);
break;
}
case LEDREAD:
{
ret = copy_to_user((int*)arg, &led_read_val, sizeof(int));
break;
}
default:
{
printk("error\n");
break;
}
}
return 0;
}
static struct file_operations led_drv =
{
.owner = THIS_MODULE,
.open = led_drv_open,
.release = led_drv_close,
.unlocked_ioctl = led_drv_ioctl,
};
static int __init led_init(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
major = register_chrdev(0, "dh_led", &led_drv);
dh_led_class = class_create(THIS_MODULE, "dh_led_class");
device_create(dh_led_class, NULL, MKDEV(major, 0), NULL, "dh_led_device_0");
return 0;
}
static void __exit led_exit(void)
{
printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
device_destroy(dh_led_class, MKDEV(major, 0));
class_destroy(dh_led_class);
unregister_chrdev(major, "dh_led");
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
led_test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <assert.h>
#include <sys/ioctl.h>
// #include "dh_led_drv.h"
#define LED_MAGIC 'l'
#define LEDREAD _IOR(LED_MAGIC, 0, int)
#define LEDWRITE _IOW(LED_MAGIC, 1, int)
int main(int argc, char const *argv[])
{
int fd;
char status;
int ret;
assert(argc == 3);
fd = open(argv[1], O_RDWR);
assert(fd != -1);
if (0 == strcmp(argv[2], "on"))
{
status = 1;
ret = ioctl(fd, LEDWRITE, &status);
}
else if (0 == strcmp(argv[2], "off"))
{
status = 0;
ret = ioctl(fd, LEDWRITE, &status);
}
else if (0 == strcmp(argv[2], "read"))
{
ret = ioctl(fd, LEDREAD, &status);
printf("read led status: %d\n", status);
}
else
{
printf("error\n");
}
close(fd);
return 0;
}
Makefile
KERNEL_HEAD = ../kernel-head
CUR_DIR := $(shell pwd)
all:
make ARCH=arm CROSS_COMPILE=arm-sigmastar-linux-uclibcgnueabihf- -C $(KERNEL_HEAD) M=$(CUR_DIR) modules
arm-sigmastar-linux-uclibcgnueabihf-gcc -o dh_led_test dh_led_test.c
clean:
make ARCH=arm CROSS_COMPILE=arm-sigmastar-linux-uclibcgnueabihf- -C $(KERNEL_HEAD) M=$(CUR_DIR) clean
rm -rf modules.order
rm -f dh_led_test
obj-m += dh_led_drv.o