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设备注销           */
}

SylixOS中APIC HPET定时器字符驱动实现相关推荐

  1. SylixOS x86 HPET 定时器驱动

    HPET(High Precision Event Timer) 俗称高精度定时器,最低时钟频率为10MHZ,而且定义了比较严格的精确度(间隔 >= 1 毫秒的允许 +-0.05% 的误差,间隔 ...

  2. 关于linux字符驱动中read函数filp->f_pos 和 loff_t *ppos的关系

    在学习linux 字符驱动的时候会有这样的困惑 比如我们实现一个字符驱动的读函数,如下 static ssize_t globalmem_read(struct file *filp, char __ ...

  3. Linux字符驱动开发学习总结

    linux驱动编写(虚拟字符设备编写) 昨天我们说了一些简单模块编写方法,但是终归没有涉及到设备的编写内容,今天我们就可以了解一下相关方面的内容,并且用一个实例来说明在linux上面设备是如何编写的. ...

  4. kernel 自定义字符驱动 第一次实验

    第一次创建哈, Oh yeach 用了一个字符驱动的模板哈,第二次就可以正常了 第二次的结果就好的多, 但是也说明 系统调用 只要系统退出 都会 调用 字符设备的 release api FpgaRe ...

  5. linux2.6驱动学习笔记之字符驱动

    1.字符驱动组成 1.1字符驱动的模块加载与卸载 //设备结构体模板 struct xxx_dev_t { struct cdev cdev; ...... }xxx_dev; 在字符驱动模块加载函数 ...

  6. 详解Linux2.6内核中基于platform机制的驱动模型

    原文地址:详解Linux2.6内核中基于platform机制的驱动模型 作者:nacichan [摘要]本文以Linux 2.6.25 内核为例,分析了基于platform总线的驱动模型.首先介绍了P ...

  7. 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ

    基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ 0. 导语 学习进入到了下一个阶段,还是以AD9833为例,这次学习是向设备申请中断,实现触发,在未来很多场景,比如做用 ...

  8. SylixOS下基于NUC970的NAND驱动

    开发环境 开发环境 宿主机: Windows7 64bits 系统 开发板: 安米MDK972 软件环境: RealEvo-IDE3.0 NAND Flash: S34ML02G100TF100 S3 ...

  9. SylixOS中select原理及使用分析

    2019独角兽企业重金招聘Python工程师标准>>> 1. select接口简介 1.1 select接口使用用例 select是操作系统多路I/O复用技术实现的方式之一. 多路I ...

最新文章

  1. Activity与Fragment的生命周期详解
  2. python中文文本分析_python--文本分析
  3. 红橙Darren视频笔记 圆点loadingView 动画ANR
  4. curl post请求 header host_(科普文)curl quot;可quot;得一切
  5. Windows10桌面美化
  6. 利用格拉布斯准则,剔除异常数据
  7. 莫以物喜 -=莫以己悲!
  8. Android黄油刀插件使用记录
  9. 大数据开发中HBase高级特性和rowkey设计分析
  10. DRV8818步进电机一种应用场景及实现思路
  11. file 转换MultipartFile
  12. 基于OP放大器的有源模拟滤波器设计--基础知识
  13. 存储系统中的算法:LSM 树设计原理
  14. P6-Windows与网络基础-安装eNSP软件环境
  15. 关于MySQL的版本
  16. 小熊派4G开发板初体验SDK开发
  17. Anycodes,在线编程网站上线啦!支持六种编程语言,语法高亮,行数显示,和代码自动折叠。小伙伴们,请速度围观!
  18. 433M无限遥控发射与接收
  19. 可牛影像全新拍照功能使用教程
  20. canon 打印机 android,Canon PRINT Inkjet/SELPHY

热门文章

  1. python有趣小程序春节祝福-教你用python群发微信新年祝福
  2. 在360新员工入职培训上的讲话
  3. 国内外vps有什么区别?
  4. 2019TFE计算机科学排名,美国留学|2019TFE Times 硕士专业排名
  5. (文末送书)字符数组与字符串
  6. 打造老年人的健康监测产品很有市场(随记)
  7. EtherNet/IP协议开发2:理论学习
  8. cobar mysql部署方案_Cobar的安装和配置步骤
  9. 编译-POCO C++支持iOS平台的静态库
  10. 2019 siggraph_观看SIGGRAPH 2019的Unity图形会议