SylixOS字符设备操作之open和close
在第一节我们学习过在应用层如何操作普通的文件,就是通过open、close、read、write这些接口,普通文件是存放在具体的存储设备上的,所以从某种角度来说,我们是通过上面这四个接口操作了存储设备。
SylixOS采用Unix中万物皆文件的设计思想,将不同的设备都抽象成一个个文件,当然这个文件肯定不是普通的文件,这些文件叫做设备文件,在应用层同样通过open、close、read、write等接口来操作这些设备文件,这些接口在底层驱动中都有对应的接口。也就是说应用层调用open打开一个设备文件,最终会调用到这个设备底层驱动中的open函数,底层驱动这些所有操作函数构成了这个设备驱动所支持功能的集合,在SylixOS中,使用 struct file_operations 数据结构来表示这样的操作集:
typedef struct file_operations { struct module *owner; long (*fo_create)(); /* DEVENTRY_pfuncDevCreate */ int (*fo_release)(); /* DEVENTRY_pfuncDevDelete */ long (*fo_open)(); /* DEVENTRY_pfuncDevOpen */ int (*fo_close)(); /* DEVENTRY_pfuncDevClose */ ssize_t (*fo_read)(); /* DEVENTRY_pfuncDevRead */ ssize_t (*fo_read_ex)(); /* DEVENTRY_pfuncDevReadEx */ ssize_t (*fo_write)(); /* DEVENTRY_pfuncDevWrite */ ssize_t (*fo_write_ex)(); /* DEVENTRY_pfuncDevWriteEx */ int (*fo_ioctl)(); /* DEVENTRY_pfuncDevIoctl */ int (*fo_select)(); /* DEVENTRY_pfuncDevSelect */ int (*fo_lock)(); /* not support now */ off_t (*fo_lseek)(); /* DEVENTRY_pfuncDevLseek */ int (*fo_fstat)(); /* DEVENTRY_pfuncDevFstat */ int (*fo_lstat)(); /* DEVENTRY_pfuncDevLstat */ int (*fo_symlink)(); /* DEVENTRY_pfuncDevSymlink */ ssize_t (*fo_readlink)(); /* DEVENTRY_pfuncDevReadlink */ int (*fo_mmap)(); /* DEVENTRY_pfuncDevMmap */ int (*fo_unmap)(); /* DEVENTRY_pfuncDevUnmmap */ ULONG fo_pad[16]; /* reserve */ } FILE_OPERATIONS;
open和close函数是所有驱动都需要实现的,其他的操作函数根据驱动的具体功能来选择实现。
SylixOS驱动使用 LW_DEV_HDR 数据结构来表示一个设备实例:
typedef struct { LW_LIST_LINE DEVHDR_lineManage; /* 设备头管理链表 */ UINT16 DEVHDR_usDrvNum; /* 主设备号 */ UINT16 DEVHDR_usDevNum; /* 子设备号 */ PCHAR DEVHDR_pcName; /* 设备名称 */ UCHAR DEVHDR_ucType; /* 设备 dirent d_type */ atomic_t DEVHDR_atomicOpenNum; /* 打开的次数 */ PVOID DEVHDR_pvReserve; /* 保留 */ } LW_DEV_HDR;
一个设备实例就对应着一个具体的设备文件,SylixOS中的设备文件都创建在 /dev 目录下:
[root@sylixos:/root]# cd /dev/ [root@sylixos:/dev]# ll srwxrwxrwx root root Sat Jan 01 08:00:00 2000 0 B, log crw------- root root Sat Jan 01 08:00:00 2000 0 B, netbd crw------- root root Sat Jan 01 08:00:00 2000 0 B, netbr drwxr-xr-- root root Sat Jan 01 08:00:00 2000 net/ srw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, socket cr--r--r-- root root Sat Jan 01 08:00:00 2000 4096 B, netevent crw-rw-rw- root root Sat Jan 01 08:00:00 2000 750KB, fb0 crw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, ttyS0 cr--r--r-- root root Sat Jan 01 08:00:00 2000 0 B, urandom cr--r--r-- root root Sat Jan 01 08:00:00 2000 0 B, random drw-rw-rw- root root Sat Jan 01 08:00:00 2000 shm/ cr--r--r-- root root Sat Jan 01 08:00:00 2000 4096 B, hotplug crw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, epollfd drw-rw-rw- root root Sat Jan 01 08:00:00 2000 gpiofd/ cr--r--r-- root root Sat Jan 01 08:00:00 2000 0 B, signalfd cr--r--r-- root root Sat Jan 01 08:00:00 2000 0 B, hstimerfd cr--r--r-- root root Sat Jan 01 08:00:00 2000 0 B, timerfd drwxr-xr-- root root Sat Jan 01 08:00:00 2000 semfd/ drwxr-xr-- root root Sat Jan 01 08:00:00 2000 bmsg/ crw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, eventfd crw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, zero crw-rw-rw- root root Sat Jan 01 08:00:00 2000 0 B, null drwxr-xr-- root root Sat Jan 01 08:00:00 2000 blk/ drwxr-xr-- root root Sat Jan 01 08:00:00 2000 input/ drwxr-xr-- root root Sat Jan 01 08:00:00 2000 pipe/ drwxr-xr-- root root Sat Jan 01 08:00:00 2000 pty/ total items: 26 [root@sylixos:/dev]#
一般的做法是在驱动中定义一个全局的设备实例变量:
LW_DEV_HDR demo_dev;
这个设备实例会作为驱动中open函数的第一个参数:
static long demo_open(LW_DEV_HDR *dev, char *name, int flag, int mode) { 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); return (long)dev; }
注意:SylixOS中的IO系统有两种类型,分别是ORIG和NEW_1两种类型,对应的底层驱动也分为ORIG型驱动和NEW_1型驱动。ORIG型结构主要是为了兼容VxWorks系统的IO框架和驱动,这也是SylixOS早期使用的IO框架,而NEW_1型是为了兼容linux的驱动。本次教程先以ORIG型为例进行讲解,后面会介绍NEW_1型驱动和ORIG型驱动有哪些差异。
open函数的各参数意思:
- dev:设备实例数据结构地址。
- name:在ORIG型驱动中这个指针为设备名尾指针,无作用。
- flag:应用层打开文件的标志集合,比如只读方式打开的O_RDONLY或者只写方式打开的O_WRONLY等。
- mode:应用层打开文件的权限,这个权限意义和linux下文件的权限意义一样,如果打开文件时没有指定权限,则使用默认权限0644(八进制表示)。
- open函数执行失败返回PX_ERROR,ORIG型驱动open函数可以返回自定义数据结构的地址,这个地址会作为其他操作函数比如close、read等的第一个入参。
有了open函数相对应的有一个close函数:
static int demo_close(LW_DEV_HDR *dev) { printk("%s.\r\n", __func__); return ERROR_NONE; }
close函数成功返回ERROR_NONE,失败返回PX_ERROR。
有了open函数和close函数之后,就可以定义一个驱动操作集变量并初始化这个变量:
static struct file_operations demo_fops = { .owner = THIS_MODULE, .fo_open = demo_open, .fo_close = demo_close, };
有了设备实例变量和驱动操作集变量后,我们就可以进行驱动注册和设备文件创建,这个将在下一节讲解。
附源码
driver_demo3源码:
#define __SYLIXOS_KERNEL #include <SylixOS.h> #include <module.h> LW_DEV_HDR demo_dev; static long demo_open(LW_DEV_HDR *dev, char *name, int flag, int mode) { 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); return (long)dev; } static int demo_close(LW_DEV_HDR *dev) { printk("%s.\r\n", __func__); return ERROR_NONE; } static struct file_operations demo_fops = { .owner = THIS_MODULE, .fo_open = demo_open, .fo_close = demo_close, }; int module_init (void) { printk("hello_module init!\n"); return 0; } void module_exit (void) { printk("hello_module exit!\n"); }
2022年4月20日 12:21 1F
打卡