Linux用户空间与内核交互——ioclt

来源:互联网 发布:python 元组长度 编辑:程序博客网 时间:2024/06/08 04:52

    设备驱动程序除了读写设备外,大部分驱动还需要一种管理和控制设备的功能,这些操作通常通过ioctl方法支持。

用户空间接口:

       #include <sys/ioctl.h>       int ioctl(int d, int request, ...);

内核空间接口(2.6.36之后内核修改了接口):

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36))       int (*ioctl)(stuct file *filp, unsigned int cmd, unsigned long arg);#else       int (*ioctl)(struct inode *inode_p, struct file *filp, unsigned int cmd, unsigned long args);#endif



内核空间函数实现

filp对应着文件描述符

cmd由用户空间不经修改的传入内核空间

arg无论用户空间使用的是指针还是整数值,它都以unsigned long的形式传递给驱动程序,如果应用调用程序没有传递第三个参数,驱动程序接收的arg就处于未定义状态


编写ioctl需要预先统一用户空间和内核空间的cmd编号,编号不是随便填写的,需要根据内核约定的方法为驱动程序选择cmd编号。

Documentation/ioctl-number.txt文件中罗列了内核使用的幻数,在选择自己的幻数的时候要避免和内核冲突。

include/asm/ioctl.h中定义了要使用的位字段:

#define _IOC(dir,type,nr,size) \    (((dir)  << _IOC_DIRSHIFT) | \     ((type) << _IOC_TYPESHIFT) | \     ((nr)   << _IOC_NRSHIFT) | \     ((size) << _IOC_SIZESHIFT))

type

    幻数,选择一个号码并在整个驱动中使用这个号码。8bit字宽。

number

    序数,8bit字宽。

direction

    如果相关的命令涉及到数据传输,则此字段定义了数据的传输方向。可以使用的值:

        _IOC_NONE  没有数据传输

        _IOC_READ  从设备读数据

        _IOC_WRITE  往设备写数据

        _IOC_READ | _IOC_WRITE  双向数据传输

size

    用户数据大小

在kernel/include/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)))#define _IOR_BAD(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))#define _IOW_BAD(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))#define _IOWR_BAD(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))/* 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)/* ...and for the drivers/sound files... */#define IOC_IN      (_IOC_WRITE << _IOC_DIRSHIFT)#define IOC_OUT     (_IOC_READ << _IOC_DIRSHIFT)#define IOC_INOUT   ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)#define IOCSIZE_SHIFT   (_IOC_SIZESHIFT)


啰嗦了一大堆,其实写IOCTL关键就是cmd要写对,注意传参数。

写一个简单的上拉下拉GPIO的例子:

kernel部分

#define G_SIZE 32static char G_INFO_BUFFER[G_SIZE];#define GPIO152_MAGIC   'Y'#define GPIO152_MAX_NR  4#define XXX_IO_OFF  _IO(GPIO152_MAGIC, 0)#define XXX_IO_ON   _IO(GPIO152_MAGIC, 1)#define XXX_IOR     _IOR(GPIO152_MAGIC, 2, char[G_SIZE])#define XXX_IOW     _IOW(GPIO152_MAGIC, 3, char[G_SIZE])static long xxx_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){char buffer[G_SIZE];    if (_IOC_TYPE(cmd) != GPIO152_MAGIC) {        return -ENOTTY;    }    if (_IOC_NR(cmd) > GPIO152_MAX_NR) {        return -ENOTTY;    }    switch(cmd) {    case XXX_IO_ON:        gpio_pull_down(GPIO152);        break;    case XXX_IO_OFF:        gpio_pull_up(GPIO152);        break;    case XXX_IOR:        sprintf(G_INFO_BUFFER, "xxx power %s\n", (mt_get_gpio_out(GPIO152)?"disable":"enable"));        if(copy_to_user((char *)arg, G_INFO_BUFFER, G_SIZE)) {             return -EFAULT;        }        break;case XXX_IOW:if(copy_from_user(buffer, (char *)arg, G_SIZE)){            return -EFAULT;        }break;    default:        return -ENOTTY;    }    return 0;}

用户空间部分,跟kernel一样需要相同的命令

#define G_SIZE 32static char G_INFO_BUFFER[G_SIZE];#define GPIO152_MAGIC   'Y'#define GPIO152_MAX_NR  4#define XXX_IO_OFF  _IO(GPIO152_MAGIC, 0)#define XXX_IO_ON   _IO(GPIO152_MAGIC, 1)#define XXX_IOR     _IOR(GPIO152_MAGIC, 2, char[G_SIZE])#define XXX_IOW     _IOW(GPIO152_MAGIC, 3, char[G_SIZE])int gpio_pull_downup(int en) {    int fd;     int ret;    memset(G_INFO_BUFFER, 0, G_SIZE);    fd = open(XXX_DEV, O_RDONLY);    if (fd < 0)        printf(" %s: open %s failed", __func__, XXX_DEV);    if (en)        ret = ioctl(fd, XXX_IO_ON, G_INFO_BUFFER);    else         ret = ioctl(fd, XXX_IO_OFF, G_INFO_BUFFER);     if (ret < 0)        printf("%s: ioctl gpio %s failed", __func__, en?"up":"down");    ret = ioctl(fd, XXX_IOR, G_INFO_BUFFER);    printf("%s", G_INFO_BUFFER);sprintf(G_INFO_BUFFER, "%s", "test for ioctl")ret = ret = ioctl(fd, XXX_IOW, G_INFO_BUFFER);    close(fd);    return ret;}

0 0
原创粉丝点击