SylixOS 热插拔系统

1. 引入

所谓热插拔(hot-pluggin)即带电插拔,热插拔功能是允许用户在不关闭系统,不切断电源的情况下插入或拨出设备,例如:USB设备(U盘、USB鼠标等)SD卡设备。这个对用户看似简单的操作,在系统层面却有着复杂的处理。

2. SylixOS 热插拔系统简介

很早之前的计算机系统,程序员知道它们只需在启动时扫描所有的设备,并且他们从来不必关心设备消失直到整个机器被关闭。现在,随着USB设备、CardBus、PCI热插拔控制器的出现,SylixOS系统需要能够安全可靠的将硬件从系统中添加或者删除,这对驱动设计者无疑产生了额外的负担,因为现在他们必须处理一个突然冒出的或者突然消失的设备。

针对上面出现的情况好多操作系统增加了对热插拔的支持,如:Linux、Windows等,当然SylixOS也增加了自己的一套热插拔机制。

分两种情况来看待SylixOS热插拔,一种是内核看待热插拔是硬件,也即内核和设备驱动之间的交互;另一种是用户看待热插拔是“/dev/hotplug”设备文件,热插拔设备文件由内核创建。

3. SylixOS 热插拔系统原理分析


3.1 SylixOS 检测热插拔设备方法

  • 热插拔事件,例如: USB设备插入和拔出时可以通过API_HotplugEvent函数来异步的处理这两个事件.在事件处理函数中, 驱动程序可以使用oemDiskMount或其他挂载函数来挂载大容量设备.或者自行创建其他设备;
  • 循环检测,当有些热插拔设备不能产生事件时,(例如:没有插拔中断的设备)需要轮询检测某些事件标志,来获取设备的状态, 那么驱动程序并不需要自己创建线程或使用定时器来检查,只需要调用API_HotplugPollAdd函数将检测函数和参数插入hotplug循环检测队列即可,t_hotplug线程会定时调用检测函数.;
  • 热插拔消息,当设备热插拔操作结束时,可以通过API_HotplugEventMessage将热插拔的结果通知给应用程序,应用程序通过读取“/dev/hotplug”文件,即可获得驱动通知的热插拔消息.。

3.2 SylixOS热插拔系统分析

我们上面说过热插拔分两种情况来看,一是系统与驱动,二是系统与用户层。

3.2.1 系统与驱动层

系统与驱动层的交互,我们这里又分为热插拔事件和循环检查。热插拔事件是针对设备能够产生热插拔中断通知,这样SylixOS可将这个通知以异步的方式来处理;而循环检查是设备并不能产生一个热插拔中断通知,需要轮询检测某些事件标志,来获取设备的插入拔出状态。

1. 热插拔事件:

能够产生热插拔事件的设备,在驱动层只要调用函数API_HotplugEvent即可,SylixOS热插拔系统中对热插拔事件处理是通过我们之前博文中讲的JobQueue来实现的,这里我们不再做更多的解释。

2. 循环检测:

针对不能产生一个热插拔中断事件的设备,在驱动层可调用函数API_HotplugPollAdd来将驱动中的轮询检查函数来添加到系统中,这样系统会在一个确定的时间间隔轮询检查设备状态,这里我们介绍一下这种方法的原理。

typedef struct {
    LW_LIST_LINE    HPPN_lineManage;               /*  节点链表                    */
    VOIDFUNCPTR     HPPN_pfunc;                    /*  函数指针                    */
    PVOID           HPPN_pvArg;                    /*  函数参数                    */
    LW_RESOURCE_RAW HPPN_resraw;                   /*  资源管理节点                */
} LW_HOTPLUG_POLLNODE;
LW_LIST_LINE_HEADER   _G_plineHotplugPoll = LW_NULL;/*  热插拔循环检测链            */

上面的结构是轮询检测节点,函数指针和函数参数两个成员是驱动设计者所要关心的,也就是驱动的轮询检测函数,从下面的代码片段我们可以证实这一点,全局链表头_G_plineHotplugPoll是链接了所有的热插拔节点。

LW_API  
INT  API_HotplugPollAdd (VOIDFUNCPTR   pfunc, PVOID  pvArg)
{
    ...
    phppn->HPPN_pfunc = pfunc;
    phppn->HPPN_pvArg = pvArg;
    ...

    return  (ERROR_NONE);
}

在SylixOS系统层会通过“t_hotplug”线程循环检查热插拔节点链,从下面的代码片段我们可以证实这一点。

static VOID  _hotplugThread (VOID)
{
    ULONG  ulError;

    for (;;) {
        ...
            for (plineTemp  = _G_plineHotplugPoll;
                 plineTemp != LW_NULL;
                 plineTemp  = _list_line_get_next(plineTemp)) {

                phppn = _LIST_ENTRY(plineTemp, LW_HOTPLUG_POLLNODE, HPPN_lineManage);
                if (phppn->HPPN_pfunc) {
                    /*
                     *  调用先前安装的循环检测函数
                     */
                    phppn->HPPN_pfunc(phppn->HPPN_pvArg);
                }
            }
        ...
        }
    }
}
3.2.2 系统与用户层

系统与用户层的交互是系统将热插拔消息通知给用户层,这样用户层可以感知到设备的插入和拔出,通过调用函数API_HotplugEventMessage即可将热插拔消息通知出去,然后用户层通过读设备“/dev/hotplug”即可获得热插拔消息。

这里的实现原理分两部分,一是热插拔设备部分,二是消息传递部分:

1. 热插拔设备部分:

热插拔设备的创建涉及到了SylixOS设备驱动的知识,这里不再详述,在之后的文章中将做详细介绍。

2. 消息传递部分:

消息传递部分这里用到了之前博文中的块消息,从下面的代码片段可看出。

LW_API  
INT      API_HotplugEventMessage (INT       iMsg,
                                       BOOL      bInsert,
                                       CPCHAR    pcPath,
                                       UINT32    uiArg0,
                                       UINT32    uiArg1,
                                       UINT32    uiArg2,
                                       UINT32    uiArg3)
{
    ...
    _hotplugDevPutMsg(iMsg, ucBuffer, i);

    return  (ERROR_NONE);
}

VOID  _hotplugDevPutMsg (INT  iMsg, CPVOID pvMsg, size_t stSize)
{
    ...
    for (plineTemp  = _G_hotplugdev.HOTPDEV_plineFile;
         plineTemp != LW_NULL;
         plineTemp  = _list_line_get_next(plineTemp)) {

        photplugfil = _LIST_ENTRY(plineTemp, LW_HOTPLUG_FILE, HOTPFIL_lineManage);
        if ((photplugfil->HOTPFIL_iMsg == iMsg) ||
            (photplugfil->HOTPFIL_iMsg == LW_HOTPLUG_MSG_ALL)) {
            _bmsgPut(photplugfil->HOTPFIL_pbmsg, pvMsg, stSize);
            API_SemaphoreBPost(photplugfil->HOTPFIL_ulReadSync);
            bWakeup = LW_TRUE;
        }
    }
    ...
}

4. 应用实例

下面这段程序通过信号模仿硬件的中断以实现热插拔事件机制,程序中用信号SIGALRM模仿设备插入,用信号SIGUSR1模仿设备拔出,此例子仅供显示热插拔函数的使用。

#define __SYLIXOS_KERNEL
#include <SylixOS.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <pthread.h>
#include <unistd.h>

static char      *msg = "Dev";
static pthread_t  tid;
static int        stop = 0;

void *process_thread (void *arg)
{
    int  *fd = (int *)arg;
    char buf[64];

    while (1) {
        memset(buf, 0, 64);
        read(*fd, buf, 64);

        if (buf[4] == 1) {
            fprintf(stdout, "%s insert.\n", buf + 5);
        } else {
            fprintf(stdout, "%s pullout.\n", buf + 5);
        }

        if (stop) {
            pthread_exit(NULL);
        }
    }

    return  NULL;
}

void send_event (void  *arg)
{
    int signum = (int)arg;

    if (signum == SIGALRM) {
        API_HotplugEventMessage(LW_HOTPLUG_MSG_ALL, 1, msg, 0, 0, 0, 0);
    } else if (signum == SIGUSR1){
        API_HotplugEventMessage(LW_HOTPLUG_MSG_ALL, 0, msg, 0, 0, 0, 0);
    }
}

void pullout_handler (int  signum)
{
    API_HotplugEvent((VOIDFUNCPTR)send_event, (void *)signum, 0, 0, 0, 0, 0);
}

void insert_handler (int  signum)
{
    API_HotplugEvent((VOIDFUNCPTR)send_event, (void *)signum, 0, 0, 0, 0, 0);
}

int main (int argc, char *argv[])
{
    int   fd;
    int   ret, i;

    fd = open("/dev/hotplug", O_RDONLY);
    if (fd < 0) {
        fprintf(stderr, "Open hotplug dev failed.\n");
        return -1;
    }

    ret = pthread_create(&tid, NULL, process_thread, (void *)&fd);
    if (ret < 0) {
        fprintf(stderr, "Create thread failed.\n");
        return -1;
    }

    if (signal(SIGALRM, insert_handler) == SIG_ERR) {
        fprintf(stderr, "Install signal handler failed.\n");
        return -1;
    }

    if (signal(SIGUSR1, pullout_handler) == SIG_ERR) {
        fprintf(stderr, "Install signal handler failed.\n");
        return -1;
    }

    for (i = 0; i < 16; ++i) {
        alarm(2);
        pause();
        pthread_kill(getpid(), SIGUSR1);
    }

    stop = 1;
    pthread_join(tid, NULL);

    return  0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值