22 自定义ioctl命令及用户进程操作GPIO

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ForFuture_/article/details/79406903

自定义ioctl命令


ioctl ---> kernel --->  cdev.fops->unlocked_ioctl(...)

系统调用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的参数值
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>

#define     MYMA    1234
#define     MYMI    5500
#define     COUNT   1

#define     LED_GPIO    GPIOA(15)

dev_t devid;

struct cdev mycdev;

long my_unlocked_ioctl(struct file *fl, unsigned int cmd, unsigned long arg)
{
    if(cmd)
        gpio_set_value(LED_GPIO, 1);
    else
        gpio_set_value(LED_GPIO, 0);

    return 0;
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .unlocked_ioctl = my_unlocked_ioctl,
};

static int __init test_init(void)
{
    int ret;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if(ret < 0)
    {
        printk("register dev num failed\n");
        return ret;
    }

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;

    ret = cdev_add(&mycdev, devid, COUNT);
    if(ret < 0)
    {
        unregister_chrdev_region(devid, COUNT);
        printk("add mycdev failed\n");
        return ret;
    }

    ret = gpio_request(LED_GPIO, "myled");
    if(ret < 0)
    {
        unregister_chrdev_region(devid, COUNT);
        cdev_del(&mycdev);
        printk("request myled failed\n");
        return ret;
    }

    gpio_direction_output(LED_GPIO, 0);

    printk("init success\n");

    return 0;
}

static void __exit test_exit(void)
{
    gpio_set_value(LED_GPIO, 1);
    gpio_free(LED_GPIO);
    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);

    printk("exited\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");

用户功能测试(app_test.c):

#include <stdio.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>

#define     LED_ON      1
#define     LED_OFF     0

int main(void)
{
    int fd;

    fd = open("/dev/mydev", O_RDWR);
    if(fd < 0)
        return fd;

    printf("this is the led ioctl to toggle\n");

    while(1)
    {
        ioctl(fd, LED_ON);
        sleep(1);
        ioctl(fd, LED_OFF);
        sleep(1);
    }

    return 0;
}

Makefile文件:

obj-m += test.o

KSRC := /目录路径/orangepi_sdk/source/linux-3.4.112/
export ARCH := arm
export CROSS_COMPILE := arm-linux-gnueabihf-

all : 
    make -C $(KSRC) modules M=`pwd`

.PHONY : clean
clean : 
    make -C $(KSRC) modules clean M=`pwd`

编译加载驱动模块后,需创建设备文件后,才能运行功能测试程序:

mknod  /dev/mydev c 1234  5500

通过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_test.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;
}

猜你喜欢

转载自blog.csdn.net/ForFuture_/article/details/79406903
22
今日推荐