当前位置: 首页 > news >正文

SylixOS中APIC HPET定时器字符驱动实现

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

1.简介

1.1 APIC介绍

“APIC”是Advanced Programmable Interrupt Controller的缩写,即高级可编程中断控制器。引入APIC机制是为了适应multiple processor(MP,多处理器)环境。

APIC分为两部分:Local APIC与I/O APIC。Local APIC位于处理器内部,而I/O APIC则呼吁芯片组的一部分。Local APIC与I/O APIC通过system bus进行通信。Local APIC 与I/O APIC的关系如图1.1所示。

图1.1 Local APIC与I/O APIC的关系

本文档使用的HPET的中断线是连接在I/OAPIC上的。在SylixOS中仅在主机是多核且在menu.lst中加入hpet=yes参数才会启用APIC的HPET功能。

1.2 HPET的工作原理

“HPET” 是High precision event timer的缩写,即高精度定时器。HPET有1个main counter(主计数器)寄存器和最多8个timer(定时器),记为timer0~timer7定时器。每个timer有自己的一对寄存器,分别是:configure(timer配置寄存器)和comparator value(timer比较值寄存器)。

HPET counter按照固定的频率进行计数,HPET会检查counter的值与timer的comparator值进行比较。当counter的值达到任何一个timer的comparator值时将产生中断(当配置可产生中断时)。那么,如果counter同时达到了多个timer所设定comparator值就会产生多个中断。HPET的8个timer可以配置为使用不同的IRQ线,这些同时产生的中断就可以同时进行处理。

2. HPET字符设备定时器的实现

正如在SylixOS中字符设备驱动的实现方式一样。HPET需要实现对应的字符设备的操作函数集并将其注册到系统中即可。和普通的字符设备不同,由于只需要实现HPET的定时功能,因此本文档中所描述的HPET字符设备定时器并没有实现读写的操作,只实现了ioctl的相关功能。

2.1 HPET的初始化

HPET的初始化如程序清单2.1所示。

程序清单2.1 HPET初始化

INT  bspHpetTimerInit (ACPI_TABLE_HPET  *pAcpiHpetPhy, ULONG  *pulVector)  
{  
    ACPI_TABLE_HPET  *pAcpiHpet;  
    UINT32            ulhpetTimerCfg;  
    /*  
     * 映射 ACPI HPET 表  
     */  
    pAcpiHpet = bspAcpiHpetMap((addr_t)pAcpiHpetPhy, LW_CFG_VMM_PAGE_SIZE);  
    if (!pAcpiHpet) {  
        return  (PX_ERROR);  
    }  
  
    /*  
     * 映射 HPET 寄存器  
     */  
    __GhpetBase = (addr_t)API_VmmIoRemapNocache((PVOID)(pAcpiHpet->Address.Address),  
                                                  LW_CFG_VMM_PAGE_SIZE);  
  
    /*  
     * 解除映射 ACPI HPET 表  
     */  
    API_VmmIoUnmap((PVOID)(((addr_t)pAcpiHpet) & LW_CFG_VMM_PAGE_MASK));  
  
    if (!__GhpetBase) {                         /*  映射 HPET 寄存器失败        */  
        return  (PX_ERROR);  
    }  
  
    __Gul32FsPerCnt = read32(HPET_ID_HI);  
  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
  
write32(    (((ulhpetTimerCfg &  
            (~(ROUTE_MSK << 9))) |  
                (LW_IRQ_20 << 9)) &               /*  设置中断为edge模式并关中断  */  
                (~(3 <<1 ))),                                                 
             HPET_TIMER_CONFIG_LO(2));  
  
    *pulVector = LW_IRQ_20;                     /*  IRQ20                       */  
  
    return  (ERROR_NONE);  
}  

其中bspAcpiHpetMap主要是实现ACPI HPET表的映射,实现如程序清单2.2所示。

程序清单2.2 映射ACPI HPET表

static VOID  *bspAcpiHpetMap (addr_t  ulAcpiPhyAddr, size_t  ulAcpiSize)  
{  
    addr_t  ulPhyBase = ROUND_DOWN(ulAcpiPhyAddr, LW_CFG_VMM_PAGE_SIZE);  
    addr_t  ulOffset  = ulAcpiPhyAddr - ulPhyBase;  
    addr_t  ulVirBase;  
  
    ulAcpiSize += ulOffset;  
    ulAcpiSize  = ROUND_UP(ulAcpiSize, LW_CFG_VMM_PAGE_SIZE);  
  
    ulVirBase = (addr_t)API_VmmIoRemapNocache((PVOID)ulPhyBase, ulAcpiSize);  
    if (ulVirBase) {  
        return  (VOID *)(ulVirBase + ulOffset);  
    } else {  
        return  (VOID *)(LW_NULL);  
    }  
}  

2.2 HPET设备的注册

HPET设备的注册如程序清单2.3所示。

程序清单2.3 HPET设备的注册

INT  __hpetRegister (VOID)  
{  
    INT iDrvNum = iosDrvInstallEx(&__GtimerOps);    /*  安装驱动程序          */  
  
return  (iosDevAdd( &__GtimerDevHdr,  
                        "/dev/timer",  
                    iDrvNum));                  /*  创建timer设备       */  
}  

其中__GtimerOps即为HPET字符设备对应的操作函数集。具体内容如程序清单 2.4所示。

程序清单2.4 HPET字符设备操作函数集

struct file_operations __GtimerOps = {  
    .fo_open  = __hpetOpen,  
    .fo_close = __hpetClose,  
    .fo_ioctl = __hpetIoctl  
};  

这里只实现了fo_open、fo_close和fo_ioctl这三个字符设备相关操作函数。

2.2.1 HPET设备的操作函数集

1. 打开操作

HPET设备的打开操作函数实现如程序清单 2.5所示。

程序清单2.5 fo_open实现

static LONG __hpetOpen(PLW_DEV_HDR pdevhdrHdr,  
                        PCHAR       pcName,  
                        INT         iFlag,  
                        INT         iMode)  
{  
    __GobSem = API_SemaphoreBCreate("wait_timer",  
                                    0,  
                                    LW_OPTION_OBJECT_GLOBAL,  
                                    LW_NULL);       /*  创建二进制型信号量          */  
  
    LW_DEV_INC_USE_COUNT(pdevhdrHdr);           /*  增加使用计数                */  
  
    return  ((LONG)pdevhdrHdr);  
}  

  2. 关闭操作

HPET设备的关闭操作函数实现如程序清单 2.6所示。

程序清单2.6 fo_close实现

static INT __hpetClose(PLW_DEV_HDR    pdevhdrHdr)  
{  
  
    if (pdevhdrHdr) {  
  
        LW_DEV_INC_USE_COUNT(pdevhdrHdr);       /*  减少使用计数                */  
  
        hpetTimerStop();  
  
        API_SemaphoreBDelete(&__GobSem);  
  
        return  (ERROR_NONE);  
    } else {  
        return  (PX_ERROR);  
    }  
}  

3. ioctl的实现

HPET设备的ioctl函数的实现如程序清单 2.7所示。

程序清单2.7 fo_ioctl实现

static INT __hpetIoctl (PLW_DEV_HDR    pdevhdrHdr, INT  iCmd, LONG  lArg)  
{  
    hard_timer      * ht;  
    ht = (hard_timer *)lArg;  
  
    switch (iCmd) {  
    case SET_DELAY:                             /*  设置延时时间(ns)            */  
        ht->iStatus = FALSE;  
        __GuiIncrementValue = ht->ptvPeriod->tv_nsec / NSEC_PER_COUNT;  
        hpetCounterSet(__GuiIncrementValue);  
        break;  
  
    case START_DELAY:                           /*  开始延时时间(ns)            */  
        if (FALSE == ht->iStatus) {  
            hpetTimerStart();  
            ht->iStatus = TRUE;  
        }  
  
        API_SemaphoreBPend(__GobSem, LW_OPTION_WAIT_INFINITE);  
        break;  
  
    default:  
        break;  
    }  
  
    return ERROR_NONE;  
}  

ioctl里主要调用了HPET定时器的开始与停止以及定时器计数值的设置相关函数。

定时器开始的函数实现如程序清单 2.8所示。

程序清单2.8 开始定时器工作

VOID hpetTimerStart(VOID)  
{  
    UINT32 ulhpetTimerCfg;  
  
    API_InterVectorEnable(__Gvector);           /*  使能中断                    */  
  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
    write32(ulhpetTimerCfg |  
            (1 <<2 ),  
            HPET_TIMER_CONFIG_LO(2));           /*  使能HPET timer2中断     */  
} 

定时器停止的函数实现如程序清单 2.9所示。

程序清单2.9 停止定时器工作

VOID hpetTimerStop(VOID)  
{  
    UINT32 ulhpetTimerCfg;  
    ulhpetTimerCfg = read32(HPET_TIMER_CONFIG_LO(2));  
    write32(ulhpetTimerCfg &  
            (~(1 <<2 )),  
            HPET_TIMER_CONFIG_LO(2));       /*  禁止HPET timer2中断         */  
  
    API_InterVectorDisable(__Gvector);      /*  禁止中断                       */  
}  

定时器计数值设置的函数实现如程序清单 2.10所示。

程序清单2.10 定时器数值设置

VOID hpetCounterSet(UINT32 uiCount)  
{  
    UINT32 ulcountLowVal;  
    UINT32 ulcountHighVal;  
    UINT32 ulcountSum;  
  
    ulcountLowVal = readl(HPET_COUNTER_LO);     /*  获取当前counter低32位值    */  
    ulcountHighVal = readl(HPET_COUNTER_HI);    /*  获取当前counter高32位值    */  
  
    ulcountSum = ulcountLowVal + uiCount;  
    if(ulcountSum < ulcountLowVal) {  
        ulcountHighVal += 1;                        /*  若低32位溢出,则高32位进1 */  
    }                                                                             
    /*  
     * 设置timer2比较值寄存器  
     */  
    writel(ulcountSum, HPET_TIMER_COMPARATOR_LO(2));  
    writel(ulcountHighVal, HPET_TIMER_COMPARATOR_HI(2));  
}

2.3 HPET设备的注销

HPET设备的注销如程序清单 2.11所示。

程序清单2.11 HPET设备的注销

INT  __hpetUnregister(VOID)  
{  
    PLW_DEV_HDR pDev;  
  
    pDev = &__GtimerDevHdr;  
  
    if (pDev) {                                                                    /*  
         * 卸载timer设备和驱动程序  
         */  
        iosDevDelete(pDev);                                                       
        return  (iosDrvRemove(pDev->DEVHDR_usDrvNum, 0));  
    } else {                                                                          
        return  (PX_ERROR);  
    }  
} 

2.4 HPET中断服务函数

HPET中断服务函数实现如程序清单 2.12所示。

程序清单2.12 HPET中断服务函数

static irqreturn_t  __tickHpetIsr (VOID)  
{  
    API_SemaphoreBPost(__GobSem);  
  
    hpetCounterSet(__GuiIncrementValue);        /*  设置counter计数值        */  
    hpetTimerStart();                           /*  开始定时器工作计时           */  
  
    return  (LW_IRQ_HANDLED);  
}  

2.5 HPET模块加载

HPET模块加载实现如程序清单 2.13所示。

程序清单2.13 HPET模块加载

int module_init (void)  
{  
    LW_CLASS_CPUSET         cpuset;  
  
    /*  
     * 初始化hpet timer  
     */  
    bspHpetTimerInit(_G_pAcpiHpet, &__Gvector); /*  定时器初始化              */  
  
    API_InterVectorConnect(__Gvector,           /*  连接中断                    */  
                          (PINT_SVR_ROUTINE)__tickHpetIsr,  
                           LW_NULL,  
                           "hpetIsr");  
  
    API_InterVectorEnable(__Gvector);           /*  使能中断                    */  
  
    /*  
     * 绑定定时器中断到cpu1  
     */  
    LW_CPU_ZERO(&cpuset);  
    LW_CPU_SET(1, &cpuset);  
    API_InterSetTarget(__Gvector, sizeof(LW_CLASS_CPUSET), &cpuset);  
  
    __hpetRegister();                           /*  timer设备注册           */  
    return  0;  
}  

2.6 HPET模块卸载

HPET模块卸载实现如程序清单 2.14所示。

程序清单2.14 HPET模块卸载

void module_exit (void)  
{  
    hpetTimerStop();                                /*  停止timer             */  
  
  
    API_InterVectorDisconnect(__Gvector,  
                             (PINT_SVR_ROUTINE)__tickHpetIsr,  
                             LW_NULL);  
  
    API_InterVectorDisable(__Gvector);  
  
    __hpetUnregister();                         /*  timer设备注销           */  
}  

 

转载于:https://my.oschina.net/u/3016467/blog/1579265

相关文章:

  • 如何在windows上调试安卓机谷歌浏览器上的页面
  • django-权限管理
  • [转]OOA/OOD/OOP区别
  • 绝对常用的Linux命令
  • 【HeadFirst 设计模式学习笔记】3 装饰模式
  • Windows Phone 7 学习笔记2:感应设备的方向
  • ssh 断开处理
  • 关于JavaScript 的事件[下]
  • table-layout
  • 双系统引导故障排除
  • python 守护进程(daemon)
  • Gradle与Makefile构建工具的对比
  • 如何读取Access里的OLE类型的图片
  • 让全世界的中小企业都可以用上的全方位上网行为管理系统。
  • 20171127-构建之法:现代软件工程-阅读笔记
  • 【划重点】MySQL技术内幕:InnoDB存储引擎
  • Angular6错误 Service: No provider for Renderer2
  • echarts的各种常用效果展示
  • Hibernate最全面试题
  • HTML-表单
  • Java 多线程编程之:notify 和 wait 用法
  • JavaScript服务器推送技术之 WebSocket
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • Spring核心 Bean的高级装配
  • Spring思维导图,让Spring不再难懂(mvc篇)
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 对象管理器(defineProperty)学习笔记
  • 前端性能优化--懒加载和预加载
  • 入门到放弃node系列之Hello Word篇
  • 写给高年级小学生看的《Bash 指南》
  • 怎样选择前端框架
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​用户画像从0到100的构建思路
  • #laravel 通过手动安装依赖PHPExcel#
  • #Ubuntu(修改root信息)
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (介绍与使用)物联网NodeMCUESP8266(ESP-12F)连接新版onenet mqtt协议实现上传数据(温湿度)和下发指令(控制LED灯)
  • (蓝桥杯每日一题)love
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (转)JAVA中的堆栈
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .NET : 在VS2008中计算代码度量值
  • .net CHARTING图表控件下载地址
  • .net FrameWork简介,数组,枚举
  • .net 提取注释生成API文档 帮助文档
  • [] 与 [[]], -gt 与 > 的比较
  • [17]JAVAEE-HTTP协议
  • [3D基础]理解计算机3D图形学中的坐标系变换
  • [Angular] 笔记 18:Angular Router