UHCI或第一个USB



美好的一天,亲爱的读者! 我被要求写有关UHCI的文章-好吧,我写。

例如,如果您没有足够的驱动程序编写技能和硬件的阅读文档,您可能会发现本文很有用。 一个简单的例子:您要为小型PC编写OS,以使某些Windows或其他Linux发行版不下载硬件,并且将其所有功能专用于您自己的目的。

什么是UHCI?


我想,为了避免再次谈论什么和为什么这个话题,请留下我以前有关EHCI的文章的链接。 在这里戳
UHCI-通用主机控制器接口,作为PCI设备运行,但是与EHCI不同,它使用端口而不是MMIO(内存映射的IO)。



以下使用的术语


  • USB驱动程序(USBD)-USB驱动程序本身
  • HC(主机控制器)-主机控制器,或者仅仅是我们的UHCI
  • 主机控制器驱动程序(HCD)-连接硬件和USBD的驱动程序
  • USB设备-USB设备本身

数据传输类型


等时-等时传输,具有给定的数据传输频率。 它可以用于例如USB麦克风等。

中断-从设备进行小的自发数据传输。 中断传输类型支持需要可预测的服务间隔但不一定提供可预测的数据流的设备。 通常用于诸如键盘和定点设备之类的设备,这些设备可能不会长时间提供数据,但是在有数据要发送时需要快速响应。

控制-有关设备状态,状态和配置的信息的传输类型。 控制传输类型用于提供从主机到USB设备的控制通道。 控制传输始终由设置阶段,零个或多个数据阶段以及状态阶段组成。 必须以FIFO模式处理向给定端点的控制传输。 如果将控制权传递给同一端点,则交织会导致不可预测的行为。

批量-数据数组的传输类型。 例如,在MassStorage设备中使用。



这就是1ms时间分布的样子-处理一帧。

时间分配


主机控制器通过每1 ms生成一个帧开始(SOF)数据包来支持实时数据传递。 当主机控制器中的SOF计数器到期时,将生成一个SOF数据包(图3)。 主机控制器将SOF计数器初始化为1 ms的帧时间。 通过对SOF更改寄存器进行编程,可以对此值(以及帧时间段)进行小的更改。 如果需要,此功能使您可以对帧时间段进行较小的更改,以在整个USB系统中保持实时同步。

主机控制器在每个SOF数据包中包含帧号。 该帧号实时唯一地确定帧周期。 当主机控制器开始下一个帧时间并生成具有相应帧号的另一个SOF数据包时,帧结束条件(EOF)发生在1 ms时间间隔的结尾。 在帧周期中,数据作为信息包传输。 帧时间段由主机控制器严格执行,并且当前帧中的数据包不能超出EOF(请参见USB规范的第11章)。 主机控制器支持帧之间的实时数据传输同步,链接帧号以执行帧列表中的特定条目。 主机控制器的帧计数器生成一个帧号(11位值),并将其包括在每个SOF数据包中。 计数器通过寄存器编程,每个帧周期递增。 主机控制器使用帧号的低10位作为具有1024帧的帧列表中的索引,该索引存储在系统内存中。 因此,由于帧计数器控制从帧列表中选择一个条目,因此主机控制器在给定帧周期内处理列表中的每个条目。 主机控制器将扩展到每个新帧的帧列表中的下一个条目。 这确保了在特定帧中执行同步传输。

图3:



UHCI结构


一切都与EHCI完全相同。 向HC请求的示例:



配置和访问UHCI


因此,正如我之前所说,UHCI通过端口工作,因此从PCI我们需要找出UHCI寄存器的基础。



在偏移量0x20处有4个字节-IO Base。 关于IO Base,我们可以使用以下寄存器:



UHCI寄存器


  • USBCMD是用于控制HC的寄存器。 位:
    • 位6是标志,表明设备已成功配置和初始化。
    • 位1-HC复位。 设置为重置HC。
    • 位0-运行/停止。 显示HC状态。 1-有效,0-否。
  • USBSTS-状态寄存器。 位:
    • 位5-HC停止。 发生错误,或者控制器已成功完成HC复位。
    • 位4-主机控制器进程错误。 当发生严重错误并且HC无法继续排队和TD时,该位置1。
    • 位3-主机系统错误。 PCI错误。
    • 位1-错误中断。 表示已发生错误,并且HC产生了中断。
    • 位0-中断。 表示HC产生了一个中断。
  • USBINTR-中断设置寄存器。 位:
    • 位2-IOC-完成中断-在事务结束时生成一个中断。
  • FRNUM-当前帧的编号(将其取值为0x3FF,以获取正确的值)。
  • FLBASEADD-帧列表基地址-帧列表的地址。
  • PORTSC-端口状态和控制-状态和端口控制寄存器。 位:
    • 位9-端口复位-1-要复位的端口。
    • 位8-指示将低速设备连接到端口
    • 位3-指示端口打开状态已更改
    • 位2-指示端口是否启用
    • 位1-指示设备的状态已连接到端口
    • 位0-指示设备已连接到端口。

结构体


框架列表指针




传输解码器




TD控制与状态
。 位:
  • 位28-27-错误计数器,类似于EHCI。
    • 位26-1 =低速设备,0 =全速设备。
    • 位25-1 =等时TD
    • Bit 24-国际奥委会
    • 位23-16-状态:
    • 位23-表示它是活动的TD
    • Bit 22-停转
    • 位21-数据缓冲区错误
    • 第20位-检测到声音不稳
    • Bit 19-NAK
  • 位10-0:主机控制器传输的字节数。

TD代币

  • 位31:21-Max Packet Len,类似于EHCI
  • 位19-数据切换,类似于EHCI
  • 位18:15-端点号
  • 位18:14-设备地址
  • 位7:0-PID。 输入= 0x69,输出= 0xE1,设置= 0x2D

队列头




代号


初始化和配置HC:

PciBar bar; PciGetBar(&bar, id, 4); if (~bar.flags & PCI_BAR_IO) { // Only Port I/O supported return; } unsigned int ioAddr = bar.u.port; UhciController *hc = VMAlloc(sizeof(UhciController)); hc->ioAddr = ioAddr; hc->frameList = VMAlloc(1024 * sizeof(u32) + 8292); hc->frameList = ((int)hc->frameList / 4096) * 4096 + 4096; hc->qhPool = (UhciQH *)VMAlloc(sizeof(UhciQH) * MAX_QH + 8292); hc->qhPool = ((int)hc->qhPool / 4096) * 4096 + 4096; hc->tdPool = (UhciTD *)VMAlloc(sizeof(UhciTD) * MAX_TD + 8292); hc->tdPool = ((int)hc->tdPool / 4096) * 4096 + 4096; memset(hc->qhPool, 0, sizeof(UhciQH) * MAX_QH); memset(hc->tdPool, 0, sizeof(UhciTD) * MAX_TD); memset(hc->frameList, 0, 4 * 1024); // Frame list setup UhciQH *qh = UhciAllocQH(hc); qh->head = TD_PTR_TERMINATE; qh->element = TD_PTR_TERMINATE; qh->transfer = 0; qh->qhLink.prev = &qh->qhLink; qh->qhLink.next = &qh->qhLink; hc->asyncQH = qh; for (uint i = 0; i < 1024; ++i) hc->frameList[i] = 2 | (u32)(uintptr_t)qh; IoWrite16(hc->ioAddr + REG_INTR, 0); IoWrite16(hc->ioAddr + REG_CMD, IoRead16(hc->ioAddr + REG_CMD)&(~1)); unsigned short cfg = PciRead16(id, 4); PciWrite16(id, 4, cfg & (~1)); PciWrite16(id, 0x20, (short)-1); unsigned short size = ~(PciRead16(id, 0x20)&(~3)) + 1; PciWrite16(id, 0x20, hc->ioAddr); PciWrite16(id, 4, cfg | 5); // Disable Legacy Support IoWrite16(hc->ioAddr + REG_LEGSUP, 0x8f00); // Disable interrupts IoWrite16(hc->ioAddr + REG_INTR, 0); // Assign frame list IoWrite16(hc->ioAddr + REG_FRNUM, 0); IoWrite32(hc->ioAddr + REG_FRBASEADD, (int)hc->frameList); IoWrite16(hc->ioAddr + REG_SOFMOD, 0x40); // Clear status IoWrite16(hc->ioAddr + REG_STS, 0xffff); // Enable controller IoWrite16(hc->ioAddr + REG_CMD, 0x1); // Probe devices UhciProbe(hc, size); 

端点和控制请求:

 // ------------------------------------------------------------------------------------------------ static void UhciDevControl(UsbDevice *dev, UsbTransfer *t) { UhciController *hc = (UhciController *)dev->hc; UsbDevReq *req = t->req; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint endp = 0; uint maxSize = dev->maxPacketSize; uint type = req->type; uint len = req->len; // Create queue of transfer descriptors UhciTD *td = UhciAllocTD(hc); if (!td) { return; } UhciTD *head = td; UhciTD *prev = 0; // Setup packet uint toggle = 0; uint packetType = TD_PACKET_SETUP; uint packetSize = sizeof(UsbDevReq); UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, req); prev = td; // Data in/out packets packetType = type & RT_DEV_TO_HOST ? TD_PACKET_IN : TD_PACKET_OUT; u8 *it = (u8 *)t->data; u8 *end = it + len; while (it < end) { td = UhciAllocTD(hc); if (!td) { return; } toggle ^= 1; packetSize = end - it; if (packetSize > maxSize) { packetSize = maxSize; } UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, it); it += packetSize; prev = td; } // Status packet td = UhciAllocTD(hc); if (!td) { return; } toggle = 1; packetType = type & RT_DEV_TO_HOST ? TD_PACKET_OUT : TD_PACKET_IN; UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, 0, 0); // Initialize queue head UhciQH *qh = UhciAllocQH(hc); UhciInitQH(qh, t, head); // Wait until queue has been processed UhciInsertQH(hc, qh); UhciWaitForQH(hc, qh); } // ------------------------------------------------------------------------------------------------ static void UhciDevIntr(UsbDevice *dev, UsbTransfer *t) { UhciController *hc = (UhciController *)dev->hc; // Determine transfer properties uint speed = dev->speed; uint addr = dev->addr; uint endp = t->endp->desc->addr & 0xf; // Create queue of transfer descriptors UhciTD *td = UhciAllocTD(hc); if (!td) { t->success = false; t->complete = true; return; } UhciTD *head = td; UhciTD *prev = 0; // Data in/out packets uint toggle = t->endp->toggle; uint packetType = TD_PACKET_IN; //Here for compiler, on some last expression hadn't worked if (t->endp->desc->addr & 0x80) packetType = TD_PACKET_IN; else packetType = TD_PACKET_OUT; uint packetSize = t->len; UhciInitTD(td, prev, speed, addr, endp, toggle, packetType, packetSize, t->data); // Initialize queue head UhciQH *qh = UhciAllocQH(hc); UhciInitQH(qh, t, head); // Schedule queue UhciInsertQH(hc, qh); if(t->w) UhciWaitForQH(hc, qh); } 

Source: https://habr.com/ru/post/zh-CN429422/


All Articles