24 ioctl的自定义命令

系统调用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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值