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

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2020年12月17日23:01:45 3 1,378

SylixOS设备操作之阻塞read和write

在实际的外设驱动中,如果需要读取设备数据,设备都需要一定的准备时间,比如读硬盘扇区数据,硬盘需要一段时间准备好数据后,通过中断来通知cpu数据准备好。在数据没准备好之前,驱动主要有三种处理策略:

  • 轮询方法:通过一直查询相关寄存器直到确认数据准备好。这种方式编程简单,但是由于是轮询方式,cpu占用率高,降低程序并行执行效率。
  • 睡眠线程等待通知:将线程设置为阻塞状态,并在中断处理程序中唤醒阻塞的线程。这种方式编程略微复杂,因为要设置中断处理程序,但是好处是提高了cpu并行执行程序的能力。
  • 不处理直接返回:这种方式不做任何处理,直接从驱动返回,这时应用层的代码就需要严格判断read返回值来确定到底有没有读取到数据。

一般驱动read和write操作默认都是阻塞的,如何进行非阻塞read和write我们将在下一节教程中介绍。本小节以read为例讲解阻塞操作,write操作类似不作讲解。另外本节我们在驱动里编写了一个虚拟的外设,同时我们为封装了主要的两个接口来操作这个外设,一个是初始化接口:

int vir_device_init(void);

成功返回ERROR_NONE,失败返回PX_ERROR,另外还提供了读取外设数据的接口:

int vir_device_read (BOOL non_block);

non_block 参数表示是否需要非阻塞读取数据,此虚拟外设每隔3秒钟产生一个新的数据(其实就是将一个变量从1开始每隔3S加一),如果读取的时候数据没有准备好,会根据传入的参数来决定是阻塞等待数据还是直接返回。函数正常返回一个正数数值,失败返回PX_ERROR,如果非阻塞模式没有读取到数据则返回0。

将上一节教程中的read函数进行修改:

static ssize_t demo_read(demo_dev_t *dev, char *buf, size_t size)
{
    int data = -1;

    printk("%s.\r\n", __func__);

    data = vir_device_read(FALSE);

    memcpy(buf, &data, sizeof(int));

    return sizeof(int);
}

我们本节学习的是阻塞读取数据,所以vir_device_read 参数传入的是FALSE,应用层读取数据的流程:

while (1) {
    ret = read(fd, &data, 4);
    if (ret < 0) {
        printf("read data fail.\r\n");
        close(fd);
        return -1;
    } else if (ret == 0) {
        printf("read data end.\r\n");
    } else {
        printf("read %d data,data is: %d.\r\n", ret, data);
    }
}

正常执行的结果就是每隔3S终端上打印读取到的数据:

[root@sylixos:/apps/app_demo10]# ./app_demo10
demo_open .
open read/write.
file permission: 644.
demo_read.
read 4 data,data is: 2.
demo_read.
read 4 data,data is: 3.
demo_read.
read 4 data,data is: 4.
demo_read.
read 4 data,data is: 5.
demo_read.
read 4 data,data is: 6.
demo_read.

如上所示,每次从驱动中读取的数据都是自增的。

附源码

driver_demo10.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;
    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;
    device->priv = NULL;

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

    LW_DEV_INC_USE_COUNT(&device->dev);

    return (long)device;
}

static int demo_close(demo_dev_t *dev)
{
    printk("%s.\r\n", __func__);

    LW_DEV_DEC_USE_COUNT(&dev->dev);

    return ERROR_NONE;
}

static int demo_ioctl(demo_dev_t *dev, int cmd, long arg)
{
    struct stat *pstat;
    struct data *data;

    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 CMD_GET_VERSION:
        data = (struct data *)arg;
        data->version = 3;
        break;

    default:
        return  (PX_ERROR);
    }

    return ERROR_NONE;
}

static ssize_t demo_read(demo_dev_t *dev, char *buf, size_t size)
{
    int data = -1;

    printk("%s.\r\n", __func__);

    data = vir_device_read(FALSE);

    memcpy(buf, &data, sizeof(int));

    return sizeof(int);
}

static ssize_t demo_write(demo_dev_t *dev, 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 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,
};

int module_init (void)
{
    int ret = 0;

    drv_index = iosDrvInstallEx(&demo_fops);
    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;
    }

    vir_device_init();

    return 0;
}

void module_exit (void)
{
    iosDevDelete(&demo_dev[0].dev);
    iosDevDelete(&demo_dev[1].dev);
    iosDrvRemove(drv_index, TRUE);
}

vir_peripheral_device.c源码:

/*
 * vir_peripheral_device.c
 *
 *  Created on: Nov 28, 2020
 *      Author: Administrator
 */
#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include "vir_peripheral_device.h"

#define READ_PERIOD (3)
#define MUTEX_CREATE_DEF_FLAG        \
        LW_OPTION_WAIT_PRIORITY    | \
        LW_OPTION_INHERIT_PRIORITY | \
        LW_OPTION_DELETE_SAFE      | \
        LW_OPTION_OBJECT_GLOBAL

static LW_HANDLE tid;
static LW_HANDLE read_sync;
static BOOL readable = FALSE;
static int data = 1;

static void *vdev_thread (void *sarg)
{
    while (1) {
        sleep(READ_PERIOD);

        data++;
        readable = TRUE;
        API_SemaphoreBPost(read_sync);
    }

    return NULL;
}

int vir_device_init (void)
{
    LW_CLASS_THREADATTR threadattr;

    API_ThreadAttrBuild(&threadattr,
                        4 * LW_CFG_KB_SIZE,
                        LW_PRIO_NORMAL,
                        LW_OPTION_THREAD_STK_CHK,
                        LW_NULL);
    tid = API_ThreadCreate("t_vdev", vdev_thread, &threadattr, LW_NULL);
    if (tid == LW_OBJECT_HANDLE_INVALID) {
        return (PX_ERROR);
    }

    read_sync = API_SemaphoreBCreate("count_lock",
                                0,
                                LW_OPTION_OBJECT_LOCAL,
                                LW_NULL);
    if (read_sync == LW_OBJECT_HANDLE_INVALID) {
        API_ThreadDelete(&tid, NULL);
        printk("semaphore create failed.\n");
        return (PX_ERROR);
    }

    return ERROR_NONE;
}

int vir_device_deinit (void)
{
    API_ThreadDelete(&tid, NULL);

    return ERROR_NONE;
}

int vir_device_read (BOOL non_block)
{
    if (non_block) {
        if (readable) {
            goto return_data;
        } else {
            return 0;
        }
    }

    API_SemaphoreBPend(read_sync, LW_OPTION_WAIT_INFINITE);

return_data:
    readable = FALSE;
    return data;
}

vir_peripheral_device.h源码:

/*
 * vir_peripheral_device.h
 *
 *  Created on: Nov 28, 2020
 *      Author: Administrator
 */

#ifndef SRC_VIR_PERIPHERAL_DEVICE_H_
#define SRC_VIR_PERIPHERAL_DEVICE_H_

int vir_device_init(void);
int vir_device_deinit(void);
int vir_device_read (BOOL non_block);

#endif /* SRC_VIR_PERIPHERAL_DEVICE_H_ */

app_demo10.c源码:

#include <stdio.h>
#include <string.h>

int main (int argc, char **argv)
{
    int fd;
    int ret = 0;
    int data = -1;

    fd =  open("/dev/demo0", O_RDWR);
    if (fd < 0) {
        printf("open file fail.\r\n");
        return -1;
    }

    while (1) {
        ret = read(fd, &data, 4);
        if (ret < 0) {
            printf("read data fail.\r\n");
            close(fd);
            return -1;
        } else if (ret == 0) {
            printf("read data end.\r\n");
        } else {
            printf("read %d data,data is: %d.\r\n", ret, data);
        }
    }

    close(fd);

    return  (0);
}

 

gewenbin
  • 本文由 发表于 2020年12月17日23:01:45
  • 转载请务必保留本文链接:http://www.databusworld.cn/8951.html
匿名

发表评论

匿名网友 填写信息

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

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

      写的真好!

      • 小猎户 小猎户 4

        非常不错的教材

        • Willam Willam 4

          结构清晰,讲解详细,折服