SylixOS字符设备驱动开发(十四)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月17日23:26:08 1 1,268

SylixOS设备驱动之NEW_1型驱动

在第三节教程中说过SylixOS下的驱动分为ORIGNEW_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);
}
gewenbin
  • 本文由 发表于 2020年12月17日23:26:08
  • 转载请务必保留本文链接:http://www.databusworld.cn/8960.html
匿名

发表评论

匿名网友 填写信息

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

评论:1   其中:访客  1   博主  0
    • liqingquan liqingquan 3

      打卡。