SylixOS设备驱动之NEW_1型驱动
在第三节教程中说过SylixOS下的驱动分为ORIG 和 NEW_1 两种类型,通过前面十几节教程的学习,我们已经知道ORIG 型驱动如何开发了,但是NEW_1 型驱动有哪些不一样的地方呢?
关于ORIG和NEW_1型这两种IO系统结构框架上有什么具体的区别,请参见《SylixOS应用开发手册》5.1.3章节,本节只关注驱动层的差异。
区别1:非open函数首个入参类型改变
除了open函数的首个入参依然是LW_DEV_HDR 类型的指针外,其他的接口比如close、read等函数的首个入参变成了LW_FD_ENTRY 类型指针:
int demo_close(LW_FD_ENTRY *file);
SylixOS中的LW_FD_ENTRY 数据结构有点类似于linux下的struct file 数据结构,由于其他非open函数的首个入参并不是open函数的返回值了,所以如果想要使用demo_dev_t 数据结构可以通过以下方法获取:
demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr;
区别2:open和close函数处理流程改变
首先open函数的返回值并不是用户自定义的值了,而是一个称之为文件结点的数据结构地址:
/********************************************************************************************************* 文件节点 只有 NEW_1 或更高级的设备驱动类型会用到此结构 一个 dev_t 和 一个 ino_t 对应唯一一个实体文件, 操作系统统多次打开同一个实体文件时, 只有一个文件节点 多个 fd_entry 同时指向这个节点. 需要说明的是 FDNODE_oftSize 字段需要 NEW_1 驱动程序自己来维护. fd node 被锁定时, 将不允许写, 也不允许删除. 当关闭文件后此文件的 lock 将自动被释放. *********************************************************************************************************/ typedef struct { LW_LIST_LINE FDNODE_lineManage; /* 同一设备 fd_node 链表 */ LW_OBJECT_HANDLE FDNODE_ulSem; /* 内部操作锁 */ dev_t FDNODE_dev; /* 设备 */ ino64_t FDNODE_inode64; /* inode (64bit 为了兼容性) */ mode_t FDNODE_mode; /* 文件 mode */ uid_t FDNODE_uid; /* 文件所属用户信息 */ gid_t FDNODE_gid; off_t FDNODE_oftSize; /* 当前文件大小 */ struct __fd_lockf *FDNODE_pfdlockHead; /* 第一个锁 */ LW_LIST_LINE_HEADER FDNODE_plineBlockQ; /* 当前有阻塞的记录锁队列 */ BOOL FDNODE_bRemove; /* 是否在文件未关闭时有 unlink */ ULONG FDNODE_ulLock; /* 锁定, 不允许写, 不允许删除 */ ULONG FDNODE_ulRef; /* fd_entry 引用此 fd_node 数量*/ PVOID FDNODE_pvFile; /* 驱动使用此变量标示文件 */ PVOID FDNODE_pvFsExtern; /* 文件系统扩展使用 */ } LW_FD_NODE;
为了使用文件结点还需要在demo_dev_t 中添加一个fdnode_header 文件结点链表:
typedef struct _demo_dev { LW_DEV_HDR dev; int flag; LW_LIST_LINE_HEADER fdnode_header; LW_SEL_WAKEUPLIST wakeup_list; void *phy_addr; int map_flags; void *priv; } demo_dev_t;
在调用open函数时通过API_IosFdNodeAdd 来获得一个新的文件结点:
pfdnode = API_IosFdNodeAdd(&device->fdnode_header, (dev_t)device, 0, flag, mode, 0, 0, 0, NULL, &is_new); if (pfdnode == NULL) { return (PX_ERROR); } LW_DEV_INC_USE_COUNT(&device->dev); return ((long)pfdnode);
如果申请成功则open函数返回文件结点的地址。
在关闭文件时我们还需要通过API_IosFdNodeDec 减少文件结点的引用值:
static int demo_close(LW_FD_ENTRY *file) { demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr; PLW_FD_NODE pfdnode = (PLW_FD_NODE)file->FDENTRY_pfdnode; printk("%s.\r\n", __func__); if (file && pfdnode) { API_IosFdNodeDec(&dev->fdnode_header, pfdnode, NULL); LW_DEV_DEC_USE_COUNT(&dev->dev); return (ERROR_NONE); } return (PX_ERROR); }
区别3:驱动注册函数改变
NEW_1 型驱动使用iosDrvInstallEx2 接口来注册驱动:
drv_index = iosDrvInstallEx2(&demo_fops, LW_DRV_TYPE_NEW_1); if (drv_index < 0) { printk("driver install fail.\r\n"); return -1; }
LW_DRV_TYPE_NEW_1 表示将驱动注册为NEW_1 类型。
实际运行效果
在命令行加载驱动后,使用open 命令打开设备文件,接着使用file 命令查看内核打开的设备文件:
[root@sylixos:/root]# files kernel filedes show (process filedes in /proc/${pid}/filedes) >> fd abn name type drv 3 /dev/ttyS0 orig 18 GLB STD_IN GLB STD_OUT GLB STD_ERR 4 /dev/socket socket 32 5 /dev/socket socket 32 6 /dev/hotplug orig 12 7 /dev/input/touch0 new_1 29 8 /dev/demo0 new_1 37 [root@sylixos:/root]#
可以看出驱动的类型变成了new_1类型。
一般如果移植的驱动原来是VxWorks下的建议使用ORIG型驱动,如果是Linux下的建议使用NEW_1型。
附源码
driver_demo14.c源码:
#define __SYLIXOS_KERNEL #include <SylixOS.h> #include <module.h> #include <string.h> #include "driver.h" #include "vir_peripheral_device.h" typedef struct _demo_dev { LW_DEV_HDR dev; int flag; LW_LIST_LINE_HEADER fdnode_header; LW_SEL_WAKEUPLIST wakeup_list; void *phy_addr; int map_flags; 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; PLW_FD_NODE pfdnode; BOOL is_new; device->priv = NULL; device->flag = 0; printk("%s %s.\r\n", __func__, name); if (flag & O_NONBLOCK) { printk("open nonblock.\r\n"); device->flag |= O_NONBLOCK; } 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); pfdnode = API_IosFdNodeAdd(&device->fdnode_header, (dev_t)device, 0, flag, mode, 0, 0, 0, NULL, &is_new); if (pfdnode == NULL) { return (PX_ERROR); } LW_DEV_INC_USE_COUNT(&device->dev); return ((long)pfdnode); } static int demo_close(LW_FD_ENTRY *file) { demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr; PLW_FD_NODE pfdnode = (PLW_FD_NODE)file->FDENTRY_pfdnode; printk("%s.\r\n", __func__); if (file && pfdnode) { API_IosFdNodeDec(&dev->fdnode_header, pfdnode, NULL); LW_DEV_DEC_USE_COUNT(&dev->dev); return (ERROR_NONE); } return (PX_ERROR); } static int demo_ioctl(LW_FD_ENTRY *file, int cmd, long arg) { PLW_SEL_WAKEUPNODE pselnode; struct stat *pstat; struct data *data; demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr; 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 FIONBIO: if (*(int *)arg) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break; case FIOSETFL: if ((int)arg & O_NONBLOCK) { dev->flag |= O_NONBLOCK; } else { dev->flag &= ~O_NONBLOCK; } break; case FIOSELECT: pselnode = (PLW_SEL_WAKEUPNODE)arg; if (SEL_WAKE_UP_TYPE(pselnode) == SELREAD) { if (vir_device_readable()) { SEL_WAKE_UP(pselnode); } else { SEL_WAKE_NODE_ADD(&dev->wakeup_list, pselnode); vir_device_wakeup_register(&dev->wakeup_list); } } else { return (PX_ERROR); } break; case FIOUNSELECT: pselnode = (PLW_SEL_WAKEUPNODE)arg; if (SEL_WAKE_UP_TYPE(pselnode) == SELREAD) { SEL_WAKE_NODE_DELETE(&dev->wakeup_list, pselnode); } else { return (PX_ERROR); } break; case CMD_GET_VERSION: data = (struct data *)arg; data->version = 3; break; default: return (PX_ERROR); } return ERROR_NONE; } static ssize_t demo_read(LW_FD_ENTRY *file, char *buf, size_t size) { int ret = -1; BOOL non_block = FALSE; demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr; printk("%s.\r\n", __func__); if (dev->flag & O_NONBLOCK) { non_block = TRUE; } else { non_block = FALSE; } ret = vir_device_read(non_block); if (ret > 0) memcpy(buf, &ret, sizeof(int)); return ret; } static ssize_t demo_write(LW_FD_ENTRY *file, char *buf, size_t size) { char wdata[30] = {0}; int wdata_len = 30; printk("%s.\r\n", __func__); wdata_len = (size < wdata_len) ? size : wdata_len; memcpy(wdata, buf, size); printk("write data %s success.\r\n", wdata); return wdata_len; } static int demo_mmap(LW_FD_ENTRY *file, LW_DEV_MMAP_AREA *vm) { demo_dev_t *dev = (demo_dev_t *)file->FDENTRY_pdevhdrHdr; printk("%s.\r\n", __func__); if (API_VmmRemapArea(vm->DMAP_pvAddr, dev->phy_addr, vm->DMAP_stLen, dev->map_flags, LW_NULL, LW_NULL)) { 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, .fo_read = demo_read, .fo_write = demo_write, .fo_mmap = demo_mmap, }; int module_init (void) { int ret = 0; drv_index = iosDrvInstallEx2(&demo_fops, LW_DRV_TYPE_NEW_1); 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; } SEL_WAKE_UP_LIST_INIT(&demo_dev[0].wakeup_list); SEL_WAKE_UP_LIST_INIT(&demo_dev[1].wakeup_list); vir_device_init(); demo_dev[0].phy_addr = API_VmmPhyAlloc(4 * 1024); demo_dev[1].phy_addr = API_VmmPhyAlloc(4 * 1024); #if 1 demo_dev[0].map_flags = LW_VMM_FLAG_DMA; demo_dev[1].map_flags = LW_VMM_FLAG_DMA; #else demo_dev[0].map_flags = LW_VMM_FLAG_RDWR; demo_dev[1].map_flags = LW_VMM_FLAG_RDWR; #endif return 0; } void module_exit (void) { iosDevDelete(&demo_dev[0].dev); iosDevDelete(&demo_dev[1].dev); iosDrvRemove(drv_index, TRUE); if (demo_dev[0].phy_addr) API_VmmPhyFree(demo_dev[0].phy_addr); if (demo_dev[1].phy_addr) API_VmmPhyFree(demo_dev[1].phy_addr); }
2021年9月17日 16:45 1F
打卡。