SylinOS字符驱动前四章总结(推荐看)

Abstract:
本章总结了前四章内容,简要的举例了驱动是如果被加载卸载的,并对代码进行了分析详细描述各个函数和变量之间的关系和作用。


打开文件通过调用open函数来实现,写文件通过write函数来实现,write的返回值表示写成功的字节数。读文件通过read函数来实现。read的返回值表示读成功的字节数,为0表示未读到数据或者读到文件末尾。通过一个demo来测试文件的基本操作。

比如open函数会返回一个int型的返回值,如果大于0则表示打开成功,其中还包括了读写的权限信息。所以我们会用定义一个int型的函数来接受open的返回值并打印它。

在应用层如何操作普通的文件,就是通过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;

可以看到定义了一个file_operation结构体,里面的元素包含了ffo_open等等函数和module这个结构体。

open和close函数是所有驱动都需要实现的,其他的操作函数根据驱动的具体功能来选择实现。

下面举一个具体对设备进行驱动的例子:

SylixOS驱动使用 LW_DEV_H设备的实例:DR 数据结构来表示一个设备:


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;

这里我们创建了一个名为 LW_DEV_H的结构体变量。其内容有设备头链表、主子设备号、设备名称类型、打开的次数等等的元素;

一般的做法是在驱动中定义一个全局的设备实例变量:

LW_DEV_HDR demo_dev;

也就是说demp_dev就表示一个设备。

这个demo_dev变量是如何使用的呢?

我们先讨论驱动是如何开始加载的:

SylixOS驱动模块通过IDE新建SylixOS Kernel Module工程,这种方式将驱动编译为xxx.ko模块的方式使用,ko就是kernel object的缩写,在系统启动后通过动态加载的形式加载驱动模块,使用方式类似linux下的模块。

由于驱动模块属于内核,在驱动中可能会调用到一些只能在内核中使用的接口,所以必须在源文件最开始定义 __SYLIXOS_KERNEL 这个宏,这样就可以使用一些只能由内核使用的接口:

驱动的入口函数是

module_init 

驱动的退出函数是

 module_exit 

也就是说驱动都是从module_init 开始加载的,我们分析一个驱动的程序就要从此处进行分析,我们来看看程序:

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, "/dev/demo", drv_index);
    if (ret != ERROR_NONE) {
        printk("device add fail.\r\n");
        iosDrvRemove(drv_index, TRUE);
        return -1;
    }
    return 0;
}

首先有个drv_index变量

这个变量的定义:int drv_index

使用: drv_index = iosDrvInstallEx(&demo_fops);

注意:需要传结构体的时候都定义为传地址,因为结构体占的字节多也会开辟新的内存空间,传地址的话更好点

我们将驱动注册到系统中,在SylixOS中这是通过调用iosDrvInstallEx 接口来实现的,这个接口其实就是API_IosDrvInstallEx 接口的宏定义,用宏重定义为iosDrvInstallEx 是为了和VxWorks的驱动相兼容,在SylixOS中,所有原生接口都是API_xxx 这种格式。(问题:为什么调用的时候会是xxx而不是API_xxx?)

API_IosDrvInstallEx接口的函数原型:

INT  API_IosDrvInstallEx (struct file_operations  *pfileop);

也就是说接口是一个file_oprations类型的 *pfileop的返回值。成功的话会返回一个驱动索引号,失败的话会返回PX_ERROR

一般来说,在驱动中会定义一个全局变量用来保存驱动索引号(也就是说用drv_index来保存的):

int drv_index;
int module_init (void)
{
    int ret = 0;
    drv_index = iosDrvInstallEx(&demo_fops);
    if (drv_index < 0) {
        printk("driver install fail.\r\n");
        return -1;
    }
    return 0;
}

这个驱动索引号会在驱动卸载的时候用到,卸载驱动所使用的接口是iosDrvRemoveiosDrvRemoveAPI_IosDrvRemove 的宏定义,函数原型:

ULONG  API_IosDrvRemove (INT  iDrvNum, BOOL  bForceClose);

iDrvNum就是驱动注册时的索引号,bForceClose表示是否强制删除驱动。成功返回ERROR_NONE,失败返回错误号。

另外&demo_fops这个函数,是定义的一个驱动操作集变量并初始化这个变量:

static struct file_operations demo_fops = {
    .owner    = THIS_MODULE,
    .fo_open  = demo_open,
    .fo_close = demo_close,
};

这里的demo_open,demo_close的函数如图:

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

在linux驱动中我们需要填写驱动所遵守的开源协议以及驱动的作者等信息,同样的,在SylixOS中也可以填入这些信息:

DRIVER_LICENSE(drv_index, "GPL->Ver 2.0");
DRIVER_AUTHOR(drv_index, "GeWenBin");
DRIVER_DESCRIPTION(drv_index, "demo driver.");

驱动模块默认是放在系统中的 /lib/modules 目录下,加载驱动使用 insmod 命令:

[root@sylixos:/root]# insmod /lib/modules/driver_demo2.ko
hello_module init!
module /lib/modules/driver_demo2.ko register ok, handle: 0x365c7e0
[root@sylixos:/root]#

可以通过 lsmod 命令查看当前系统中已经加载哪些驱动模块:

[root@sylixos:/root]# lsmod
            NAME           HANDLE   TYPE  GLB   BASE     SIZE   SYMCNT

VPROC: kernel             pid:   0 TOTAL MEM: 49152
+ xsiipc.ko               036688d0 KERNEL YES 10008000     49a4     14
+ xinput.ko               03668ea8 KERNEL YES 10006000     18b4      1
+ driver_demo2.ko         0365c7e0 KERNEL YES 10005000       68      1
total modules: 3
[root@sylixos:/root]#
使用 rmmod 命令卸载驱动模块:

[root@sylixos:/root]# rmmod /lib/modules/driver_demo2.ko
hello_module exit!
module /lib/modules/driver_demo2.ko unregister ok.
[root@sylixos:/root]#

回到我们刚才讨论的问题,demo_dev这个变量是如何调用的呢?

ret = iosDevAdd(&demo_dev, "/dev/demo", drv_index);
    if (ret != ERROR_NONE) {
        printk("device add fail.\r\n");
        iosDrvRemove(drv_index, TRUE);
        return -1;
    }

&demo_dev是通过iosDevAdd来进行的。我们就来看看这个iosDevAdd

驱动注册完成之后,我们就可以创建设备文件,这是通过iosDevAdd 接口实现的,iosDevAddAPI_IosDevAdd 的宏定义,函数原型:

ULONG  API_IosDevAdd (PLW_DEV_HDR pdevhdrHdr,CPCHAR pcName,INT iDrvNum);

pdevhdrHdr就是设备实例变量的地址,

pcName是设备文件的名称,比如“/dev/demo”,

iDrvNum就是注册驱动时返回的索引号。

成功返回ERROR_NONE,失败返回错误码。

我们在demo_dev给了驱动的信

删除设备文件使用iosDevDelete接口,iosDevDeleteAPI_IosDevDelete 的宏定义,函数原型:

VOID  API_IosDevDelete (PLW_DEV_HDR    pdevhdrHdr);

这个接口只有一个入参,就是设备实例变量的地址。

在驱动模块卸载时,一般是先删除设备文件再卸载设备驱动:

void module_exit (void)
{
    iosDevDelete(&demo_dev);
    iosDrvRemove(drv_index, TRUE);
}

附代码:

#define  __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <module.h>
int drv_index;
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)
{
    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, "/dev/demo", drv_index);
    if (ret != ERROR_NONE) {
        printk("device add fail.\r\n");
        iosDrvRemove(drv_index, TRUE);
        return -1;
    }
    return 0;
}
void module_exit (void)
{
    iosDevDelete(&demo_dev);
    iosDrvRemove(drv_index, TRUE);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值