系统调用ioctl函数的作用: 用户进程用于通过相应的设备驱动来获取或者设置硬件状态.
ioctl ---> kernel ---> cdev.fops->unlocked_ioctl(...)
在字符设备驱动里, 其中unlock_ioctl函数原形:
long (*unlocked_ioctl) (struct file *fl, unsigned int cmd, unsigned long arg);
cmd参数需要与应用程序调用ioctl时的参数约定,才可以表示一种功能.
cmd的值不能为2,内核里保留此值.
man 2 ioctl_list //可以查看系统里的ioctl关于cmd的参数值
在内核里,有提供帮助生成ioctl的cmd的宏(可用,可不用).
cmd是32位的数, 分成以下四个部分:
1). 最高两位表示方向: 读/写/读写(输出/输入/输出输入)
2). 第16位至第29位表示ioctl的第三个参数的大小(unlocked_ioctl的arg).
3). 第8位至第15位表示ioctl命令的类型.
4). 最低8位表示ioctl命令类型里的第几个命令
include/asm-generic/ioctl.h:
'k'
_IOC_DIRBITS << 30 | _IOC_SIZEBITS << 16 | _IOC_TYPEBITS << 8 | _IOC_NRBITS
#define _IOC_NRBITS 8 //顺序号 0 --- 7
#define _IOC_TYPEBITS 8 //类型 8 --- 15
#define _IOC_SIZEBITS 14 //ioctl第三个参数的大小 16 --- 29
#define _IOC_DIRBITS 2 //方向, 有没有参数, 读/写 30 --- 31
///方向位 ////
# define _IOC_NONE 0U
# define _IOC_WRITE 1U
# define _IOC_READ 2U
//////////用于生成一个ioctl的命令的宏定义///
#define _IOC(dir,type,nr,size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
如:#define LED_ON _IOC(_IOC_WRITE, 'L', 99, 0);
//方向, 类型, 第99个命令, ioctl的第三个参数大小为0(即没有第三个参数)
//定义一个没有指定方向,没有第三个参数,只指定ioctl命令的类型及命令类型里的序号
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOC_TYPECHECK(t) \
((sizeof(t) == sizeof(t[1]) && \
sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
sizeof(t) : __invalid_size_argument_for_IOC)
// 定义一个驱动里输出参数值(用户进程读), 指定ioctl命令的类型及命令类型里的序号及第三个参数的大小
#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)))
///////用于获取ioctl命令里方向,类型等信息
#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)
_IOC_DIR(nr) //获取nr命令里的方向值
_IOC_TYPE(nr) //获取nr命令里的类型
_IOC_NR(nr) //获取nr命令里的顺序号
_IOC_SIZE(nr) //获取nr命令里的第三个参数大小
//
如led控制的例子, test.c:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/ioctl.h> //生成ioctl命令的宏定义
#define MYMA 1234
#define MYMI 3344
#define COUNT 1
#define LED_IO GPIOA(15)
自定义的ioctl命令
#define LED_MAGIC 0xEF
#define LED_ON (_IO(LED_MAGIC, 0x1))
#define LED_OFF (_IO(LED_MAGIC, 0x2))
dev_t devid; //用于存放设备号
struct cdev mycdev;
//如有第三个参数,则arg的值为用户进程ioctl调用时传进来的地址
long myioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{
//如果ioctl命令的类型不是LED_MAGIC则退出
if (LED_MAGIC != _IOC_TYPE(cmd))
return -EINVAL;
//根据用户进程ioctl的第二个参数值来设置led灯亮或灭
if (LED_ON == cmd) // led on
gpio_set_value(LED_IO, 1);
else if (LED_OFF == cmd) // led off
gpio_set_value(LED_IO, 0);
return 0;
}
struct file_operations fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = myioctl,
};
static int __init test_init(void)
{
int ret;
devid = MKDEV(MYMA, MYMI); //生成一个设备号
ret = register_chrdev_region(devid, COUNT, "mydev");
if (ret < 0)
goto err0;
cdev_init(&mycdev, &fops);
mycdev.owner = THIS_MODULE;
ret = cdev_add(&mycdev, devid, COUNT);
if (ret < 0)
goto err1;
gpio_request(LED_IO, "mydev"); //请求gpio口
gpio_direction_output(LED_IO, 0); //配置gpio口为输出,先输出低电平
return 0;
err1:
unregister_chrdev_region(devid, COUNT);
err0:
return ret;
}
static void __exit test_exit(void)
{
unregister_chrdev_region(devid, COUNT);
cdev_del(&mycdev);
gpio_free(LED_IO); //释放gpio
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
//
app.c:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
自定义的ioctl命令
#define LED_MAGIC 0xEF
#define LED_ON (_IO(LED_MAGIC, 0x1))
#define LED_OFF (_IO(LED_MAGIC, 0x2))
int main(void)
{
int fd;
fd = open("/dev/mydev", O_RDWR);
if (fd < 0)
{
perror("open dev");
return 1;
}
while (1)
{
ioctl(fd, LED_ON); //led灯亮
sleep(1);
ioctl(fd, LED_OFF); // led灯灭
sleep(1);
}
return 0;
}
//
通过ioctl获取烟雾传感器的例子:
test.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <mach/gpio.h>
#include <linux/gpio.h>
#include <linux/ioctl.h> //生成ioctl命令的宏定义
#define MYMA 1234
#define MYMI 3344
#define COUNT 1
//烟雾传感器接在PA(7), 感应到烟雾时输出低电平,正常高电平
#define DETECT_IO GPIOA(7)
自定义的ioctl命令
#define DETECTOR_MAGIC 0xAF
#define DETECT_RET (_IOR(DETECTOR_MAGIC, 0x1, int))
dev_t devid; //用于存放设备号
struct cdev mycdev;
//如有第三个参数,则arg的值为用户进程ioctl调用时传进来的地址
long myioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{
if (DETECTOR_MAGIC != _IOC_TYPE(cmd))
return -EINVAL;
if (DETECT_RET == cmd)
*(int *)arg = gpio_get_value(DETECT_IO);
return 0; //返回值表示操作是成功与否
}
struct file_operations fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = myioctl,
};
static int __init test_init(void)
{
int ret;
devid = MKDEV(MYMA, MYMI); //生成一个设备号
ret = register_chrdev_region(devid, COUNT, "mydev");
if (ret < 0)
goto err0;
cdev_init(&mycdev, &fops);
mycdev.owner = THIS_MODULE;
ret = cdev_add(&mycdev, devid, COUNT);
if (ret < 0)
goto err1;
gpio_request(DETECT_IO, "mydev"); //请求gpio口
gpio_direction_input(DETECT_IO); //配置gpio口为输入
return 0;
err1:
unregister_chrdev_region(devid, COUNT);
err0:
return ret;
}
static void __exit test_exit(void)
{
unregister_chrdev_region(devid, COUNT);
cdev_del(&mycdev);
gpio_free(DETECT_IO); //释放gpio
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
///
app.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
自定义的ioctl命令
#define DETECTOR_MAGIC 0xAF
#define DETECT_RET (_IOR(DETECTOR_MAGIC, 0x1, int))
int main(void)
{
int fd, ret, val;
fd = open("/dev/mydev", O_RDWR);
if (fd < 0)
{
perror("open dev");
return 1;
}
while (1)
{
ret = ioctl(fd, DETECT_RET, &val);
if (ret < 0)
break;
if (!val) //有烟雾感应到了
{
printf("smoke detected ...\n");
break;
}
}
close(fd);
return 0;
}