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");
}