SylixOS设备操作之ioctl
前面我们学习过驱动open和close操作,open一般用于申请和初始化一些资源,close用于回收资源。如果设备还有一些其他功能,比如可以设置某些参数,获取某些属性等,应用层我们可以使用ioctl接口来实现相应的这些操作。
要想在应用层使用ioctl接口,驱动层必须先实现ioctl操作:
static int demo_ioctl(demo_dev_t *dev, int cmd, long arg) { printk("%s %x.\r\n", __func__, cmd); return ERROR_NONE; }
cmd 表示是哪种命令,通过不同的命令来执行不同的功能,arg 表示一个命令对应的参数,可能表示一个具体的值或者是一个数据结构的地址,也可能不需要关心。命令可以分为两大类,一类是系统预留的,比如FIOFSTATGET、FIOTRUNC 等等;一类是用户自定义的,起始命令从FIOUSRFUNC 开始。函数成功返回ERROR_NONE,失败返回PX_ERROR。
每个驱动都必须实现的一个ioctl系统命令是FIOFSTATGET,这个命令用于获得设备文件的一些基本状态信息,比如文件权限、设备类型、大小等等:
static int demo_ioctl(demo_dev_t *dev, int cmd, long arg) { struct stat *pstat; printk("%s %x.\r\n", __func__, cmd); switch (cmd) { case FIOFSTATGET: pstat = (struct stat *)arg; if (!pstat) { return (PX_ERROR); } pstat->st_dev = LW_DEV_MAKE_STDEV(&dev->dev); pstat->st_ino = (ino_t)0; pstat->st_mode = (S_IRWXU | S_IRWXG | S_IRWXO | S_IFCHR); pstat->st_nlink = 1; pstat->st_uid = 0; pstat->st_gid = 0; pstat->st_rdev = 0; pstat->st_size = 0; pstat->st_blksize = 0; pstat->st_blocks = 0; pstat->st_atime = API_RootFsTime(LW_NULL); pstat->st_mtime = API_RootFsTime(LW_NULL); pstat->st_ctime = API_RootFsTime(LW_NULL); break; default: return (PX_ERROR); } return ERROR_NONE; }
由于SylixOS使用的是类似VxWorks5.5那种大平板地址设计,并不存在硬件上的内核权限和用户权限之分,所以驱动层ioctl的arg参数如果表示的是指针,那么驱动可以直接访问而不需要像linux那样从用户空间复制到内核空间,反之亦然。另外没有实现的命令一定要返回PX_ERROR。
加载驱动后通过ll 命令来查看设备文件的基本状态信息:
[root@sylixos:/root]# ll /dev/demo0 demo_open . open read only. file permission: 644. demo_ioctl 26. demo_close. crwxrwxrwx root root Sat Jan 01 08:00:00 2000 0 B, demo0 total items: 1 [root@sylixos:/root]#
通过设计自定义的命令和数据结构可以实现例如获取驱动版本的功能:
/* * driver.h * * Created on: Dec 7, 2020 * Author: Administrator */ #ifndef SRC_DRIVER_H_ #define SRC_DRIVER_H_ struct data { int version; }; #define CMD_GET_VERSION (FIOUSRFUNC + 0) #endif /* SRC_DRIVER_H_ */
在driver.h 中定义驱动所用到的命令和相应数据结构,并在驱动的ioctl中对CMD_GET_VERSION 做相应的处理:
static int demo_ioctl(demo_dev_t *dev, int cmd, long arg) { struct stat *pstat; struct data *data; printk("%s %x.\r\n", __func__, cmd); switch (cmd) { case FIOFSTATGET: pstat = (struct stat *)arg; if (!pstat) { return (PX_ERROR); } pstat->st_dev = LW_DEV_MAKE_STDEV(&dev->dev); pstat->st_ino = (ino_t)0; pstat->st_mode = (S_IRWXU | S_IRWXG | S_IRWXO | S_IFCHR); pstat->st_nlink = 1; pstat->st_uid = 0; pstat->st_gid = 0; pstat->st_rdev = 0; pstat->st_size = 0; pstat->st_blksize = 0; pstat->st_blocks = 0; pstat->st_atime = API_RootFsTime(LW_NULL); pstat->st_mtime = API_RootFsTime(LW_NULL); pstat->st_ctime = API_RootFsTime(LW_NULL); break; case CMD_GET_VERSION: data = (struct data *)arg; data->version = 3; break; default: return (PX_ERROR); } return ERROR_NONE; }
驱动开发者将driver.h 头文件提供给应用开发人员,应用开发者就可以通过应用层ioctl接口来获取驱动版本:
ret = ioctl(fd, CMD_GET_VERSION, &data); if (ret) { printf("CMD_GET_VERSION fail.\r\n"); close(fd); return -1; } printf("driver version %x\r\n", data.version);
执行app的结果:
[root@sylixos:/apps/app_demo8]# ./app_demo8 demo_open . open read/write. file permission: 644. demo_ioctl 2000. driver version 3 demo_close. [root@sylixos:/apps/app_demo8]#
可以看到应用层获取到了驱动版本号为3。
附源码
driver_demo8源码:
#define __SYLIXOS_KERNEL #include <SylixOS.h> #include <module.h> #include "driver.h" typedef struct _demo_dev { LW_DEV_HDR dev; void *priv; } demo_dev_t; int drv_index; demo_dev_t demo_dev[2]; static long demo_open(LW_DEV_HDR *dev, char *name, int flag, int mode) { demo_dev_t *device = (demo_dev_t *)dev; device->priv = NULL; printk("%s %s.\r\n", __func__, name); if ((flag & O_ACCMODE) == O_RDONLY) { printk("open read only.\r\n"); } else if ((flag & O_ACCMODE) == O_WRONLY) { printk("open write only.\r\n"); } else { printk("open read/write.\r\n"); } printk("file permission: %o.\r\n", mode); LW_DEV_INC_USE_COUNT(&device->dev); return (long)device; } static int demo_close(demo_dev_t *dev) { printk("%s.\r\n", __func__); LW_DEV_DEC_USE_COUNT(&dev->dev); return ERROR_NONE; } static int demo_ioctl(demo_dev_t *dev, int cmd, long arg) { struct stat *pstat; struct data *data; printk("%s %x.\r\n", __func__, cmd); switch (cmd) { case FIOFSTATGET: pstat = (struct stat *)arg; if (!pstat) { return (PX_ERROR); } pstat->st_dev = LW_DEV_MAKE_STDEV(&dev->dev); pstat->st_ino = (ino_t)0; pstat->st_mode = (S_IRWXU | S_IRWXG | S_IRWXO | S_IFCHR); pstat->st_nlink = 1; pstat->st_uid = 0; pstat->st_gid = 0; pstat->st_rdev = 0; pstat->st_size = 0; pstat->st_blksize = 0; pstat->st_blocks = 0; pstat->st_atime = API_RootFsTime(LW_NULL); pstat->st_mtime = API_RootFsTime(LW_NULL); pstat->st_ctime = API_RootFsTime(LW_NULL); break; case CMD_GET_VERSION: data = (struct data *)arg; data->version = 3; break; default: return (PX_ERROR); } return ERROR_NONE; } static struct file_operations demo_fops = { .owner = THIS_MODULE, .fo_open = demo_open, .fo_close = demo_close, .fo_ioctl = demo_ioctl, }; int module_init (void) { int ret = 0; drv_index = iosDrvInstallEx(&demo_fops); if (drv_index < 0) { printk("driver install fail.\r\n"); return -1; } DRIVER_LICENSE(drv_index, "GPL->Ver 2.0"); DRIVER_AUTHOR(drv_index, "GeWenBin"); DRIVER_DESCRIPTION(drv_index, "demo driver."); ret = iosDevAdd(&demo_dev[0].dev, "/dev/demo0", drv_index); if (ret != ERROR_NONE) { printk("device add fail.\r\n"); iosDrvRemove(drv_index, TRUE); return -1; } ret = iosDevAdd(&demo_dev[1].dev, "/dev/demo1", drv_index); if (ret != ERROR_NONE) { printk("device1 add fail.\r\n"); iosDevDelete(&demo_dev[0].dev); iosDrvRemove(drv_index, TRUE); return -1; } return 0; } void module_exit (void) { iosDevDelete(&demo_dev[0].dev); iosDevDelete(&demo_dev[1].dev); iosDrvRemove(drv_index, TRUE); }
app_demo8源码:
#include <stdio.h> #include "driver.h" int main (int argc, char **argv) { int fd; int ret; struct data data; fd = open("/dev/demo0", O_RDWR); if (fd < 0) { printf("open file fail.\r\n"); return -1; } ret = ioctl(fd, CMD_GET_VERSION, &data); if (ret) { printf("CMD_GET_VERSION fail.\r\n"); close(fd); return -1; } printf("driver version %x\r\n", data.version); close(fd); return (0); }
评论