SylixOS I/O系统简介
SylixOS 兼容标准的POSIX输入输出系统,使用户非常容易上手。SylixOS 的I/O概念与UNIX兼容系统相同,认为一切都是文件。这些文件有不同的类型,它们包括:
目录文件:这个文件包含了其他文件的名字以及这些文件的指针。
块设备文件:这种文件提供的 I/O 接口标准符合 SylixOS 对块设备的定义。
字符设备文件:这是标准的不带缓冲的设备文件,系统中的设备不是块设备就是字符设备。
FIFO文件:管道通信文件。它对应了一个命名管道。
套接字(socket)文件:进程间或者主机间的网络通信。
在SylixOS中对文件的操作也同样遵循UNIX的方法,主要通过open、read、write、close等函数调用来操作文件。
SylixOS I/O驱动结构
SylixOS中I/O驱动结构分为ORIG型和NEW_1型,下面的两幅图分别对应了这两种类型的驱动结构。
从两幅图可以看出NEW_1型驱动结构在ORIG型驱动结构的基础上增加了文件节点层(这一层相当于UNIX系统的vnode),这一层的引入,使SylixOS对文件的操作支持了记录锁,文件权限的功能。下面我重点学习一下文件节点层之下的各层。
SylixOS设备头
typedef struct {
LW_LIST_LINE DEVHDR_lineManage; /* 设备头管理链表 */
UINT16 DEVHDR_usDrvNum; /* 设备驱动程序索引号 */
PCHAR DEVHDR_pcName; /* 设备名称 */
UCHAR DEVHDR_ucType; /* 设备 dirent d_type */
atomic_t DEVHDR_atomicOpenNum; /* 打开的次数 */
PVOID DEVHDR_pvReserve; /* 保留 */
} LW_DEV_HDR;
typedef LW_DEV_HDR *PLW_DEV_HDR;
上面结构就是SylixOS设备头结构,此结构中包含以下一些信息:
- DEVHDR_lineManage这是一个设备链表,设备安装时将自动链到系统设备链表上;
- DEVHDR_usDrvNum设备驱动程序索引号,SylixOS内核维护了一个驱动表如上面的两个图所示,每一个SylixOS设备都对应了一个驱动表项,当然不同的设备可以对应到同一个驱动表项,也就是说可以拥有一套驱动方法;
- DEVHDR_pcName设备名字,每一个SylixOS设备都应该有一个唯一的名字,如“/dev/hotplug”;
- DEVHDR_ucType设备类型,例如,字符设备类型、块设备类型等;
- DEVHDR_atomicOpenNum这个成员记录了设备的打开次数,内核提供了下面几个函数对它进行操作;
INT LW_DEV_INC_USE_COUNT(PLW_DEV_HDR pdevhdrHdr); /* 增加一次打开计数 */
INT LW_DEV_DEC_USE_COUNT(PLW_DEV_HDR pdevhdrHdr); /* 减少一次打开计数 */
INT LW_DEV_GET_USE_COUNT(PLW_DEV_HDR pdevhdrHdr); /* 获得打开计数 */
- DEVHDR_pvReserve暂时保留。
所有安装的设备将被链到下面这个链表头上,系统进行统一管理。
LW_LIST_LINE_HEADER _S_plineDevHdrHeader; /* 设备表表头 */
SylixOS驱动结构
typedef struct {
LONGFUNCPTR DEVENTRY_pfuncDevCreate; /* 建立函数 */
FUNCPTR DEVENTRY_pfuncDevDelete; /* 删除函数 */
LONGFUNCPTR DEVENTRY_pfuncDevOpen; /* 打开函数 */
FUNCPTR DEVENTRY_pfuncDevClose; /* 关闭函数 */
SSIZETFUNCPTR DEVENTRY_pfuncDevRead; /* 读设备函数 */
SSIZETFUNCPTR DEVENTRY_pfuncDevWrite; /* 写设备函数 */
SSIZETFUNCPTR DEVENTRY_pfuncDevReadEx; /* 读设备函数扩展函数 */
SSIZETFUNCPTR DEVENTRY_pfuncDevWriteEx; /* 读设备函数扩展函数 */
FUNCPTR DEVENTRY_pfuncDevIoctl; /* 设备控制函数 */
FUNCPTR DEVENTRY_pfuncDevSelect; /* select 功能 */
OFFTFUNCPTR DEVENTRY_pfuncDevLseek; /* lseek 功能 */
FUNCPTR DEVENTRY_pfuncDevFstat; /* fstat 功能 */
FUNCPTR DEVENTRY_pfuncDevLstat; /* lstat 功能 */
FUNCPTR DEVENTRY_pfuncDevSymlink; /* 建立链接文件 */
SSIZETFUNCPTR DEVENTRY_pfuncDevReadlink; /* 读取链接文件 */
FUNCPTR DEVENTRY_pfuncDevMmap; /* 文件映射 */
FUNCPTR DEVENTRY_pfuncDevUnmap; /* 映射结束 */
BOOL DEVENTRY_bInUse; /* 是否被使用 */
INT DEVENTRY_iType; /* 设备驱动类型 */
LW_DRV_LICENSE DEVENTRY_drvlicLicense; /* 驱动程序许可证 */
} LW_DEV_ENTRY;
typedef LW_DEV_ENTRY *PLW_DEV_ENTRY;
上面就是SylixOS目前支持的驱动方法,基本上涵盖了设备的各种操作,当然, Linux程序员可能更习惯于struct file_operations,SylixOS为了兼容这部分,也加入了struct file_operations。
SylixOS字符设备驱动
通过上面的一个简单介绍,接下来我们写一个简单的字符设备驱动来简单的练习一下。下面的程序展示了一个SylixOS字符设备的编写方法,程序实现了一个字符设备的读、写。
#define __SYLIXOS_KERNEL
#include <SylixOS.h>
typedef struct {
LW_DEV_HDR CDEV_devhdr;
CHAR CDEV_cBuf[64];
time_t CDEV_time;
} CDEV;
static INT devDrvNum = -1;
static LONG devOpen (CDEV *pcdev, PCHAR pcName, INT iFlag, INT iMode)
{
if (!pcdev) {
_ErrorHandle(EFAULT);
return (PX_ERROR);
}
LW_DEV_INC_USE_COUNT(&pcdev->CDEV_devhdr);
return ((LONG)pcdev);
}
static INT devClose (CDEV *pcdev)
{
if (!pcdev) {
_ErrorHandle(EFAULT);
return (PX_ERROR);
}
if (LW_DEV_GET_USE_COUNT(&pcdev->CDEV_devhdr)) {
LW_DEV_DEC_USE_COUNT(&pcdev->CDEV_devhdr);
}
return (ERROR_NONE);
}
static ssize_t devRead (CDEV *pcdev, PVOID pvBuf, size_t stLen)
{
ssize_t stTempLen;
stTempLen = min(stLen, 64);
lib_memcpy(pvBuf, (PVOID)&pcdev->CDEV_cBuf[0], stTempLen);
return (stTempLen);
}
static ssize_t devWrite (CDEV *pcdev, PVOID pvBuf, size_t stLen)
{
ssize_t stTempLen;
stTempLen = min(stLen, 64);
lib_memcpy((PVOID)&pcdev->CDEV_cBuf[0], pvBuf, stTempLen);
return (stTempLen);
}
static INT devIoctl (CDEV *pcdev, INT iCmd, LONG lArg)
{
INT iError = ERROR_NONE;
struct stat *pstatGet;
switch (iCmd) {
case FIOFSTATGET:
pstatGet = (struct stat *)lArg;
if (pstatGet) {
pstatGet->st_dev = (dev_t)pcdev;
pstatGet->st_ino = (ino_t)0; /* 相当于唯一节点 */
pstatGet->st_mode = 0666 | S_IFCHR;
pstatGet->st_nlink = 1;
pstatGet->st_uid = 0;
pstatGet->st_gid = 0;
pstatGet->st_rdev = 1;
pstatGet->st_size = 0;
pstatGet->st_blksize = 0;
pstatGet->st_blocks = 0;
pstatGet->st_atime = pcdev->CDEV_time;
pstatGet->st_mtime = pcdev->CDEV_time;
pstatGet->st_ctime = pcdev->CDEV_time;
} else {
errno = EINVAL;
iError = (PX_ERROR);
}
}
return (iError);
}
static struct file_operations fop = {
.owner = THIS_MODULE,
.fo_open = devOpen,
.fo_close = devClose,
.fo_read = devRead,
.fo_write = devWrite,
.fo_ioctl = devIoctl
};
INT devDrvInstall (VOID)
{
if (devDrvNum > 0) {
return (ERROR_NONE);
}
devDrvNum = API_IosDrvInstallEx(&fop);
DRIVER_LICENSE(devDrvNum, "Dual BSD/GPL->Ver 1.0");
DRIVER_AUTHOR(devDrvNum, "xx.xxx");
DRIVER_DESCRIPTION(devDrvNum, "char dev driver.");
return (devDrvNum > 0 ? ERROR_NONE : PX_ERROR);
}
INT devCreate (VOID)
{
CDEV *pcdev;
INT iRet;
pcdev = __SHEAP_ALLOC(sizeof(CDEV));
if (!pcdev) {
return (PX_ERROR);
}
lib_bzero(pcdev, sizeof(CDEV));
iRet = API_IosDevAddEx(&pcdev->CDEV_devhdr, "/dev/cdev", devDrvNum, DT_CHR);
if (iRet != ERROR_NONE) {
__SHEAP_FREE(pcdev);
return (PX_ERROR);
}
pcdev->CDEV_time = lib_time(LW_NULL);
return (ERROR_NONE);
}
VOID devDelete (VOID)
{
PCHAR pcTail;
PLW_DEV_HDR pdevhdr;
pdevhdr = API_IosDevFind("/dev/cdev", &pcTail);
API_IosDevDelete(pdevhdr);
API_IosDrvRemove(devDrvNum, LW_TRUE);
}
void module_init (void)
{
INT iRet;
iRet = devDrvInstall();
if (iRet == ERROR_NONE) {
iRet = devCreate();
if (iRet != ERROR_NONE) {
printk(KERN_ERR "Char device install failed.\n");
}
} else {
printk(KERN_ERR "Char driver install failed.\n");
}
}
void module_exit (void)
{
devDelete();
}