SylixOS字符设备驱动开发(三)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月17日22:19:39 1 1,912

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_ERRORORIG型驱动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");
}
gewenbin
  • 本文由 发表于 2020年12月17日22:19:39
  • 转载请务必保留本文链接:http://www.databusworld.cn/8927.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

评论:1   其中:访客  1   博主  0
    • 打卡 打卡 4

      打卡