UHCI、または最初のUSB





読者の皆さん、こんにちは! UHCIについて書くように頼まれました-まあ、私は書きます。



この記事は、たとえば、ドライバーの十分なライティングスキルやハードウェアのドキュメントを読むことができない場合に役立つことがあります。 簡単な例:Windowsや他のLinuxディストリビューションがハードウェアをダウンロードしないように、ミニPC用にOSを作成し、そのパワーをすべて自分の目的にのみ使用したい場合。



UHCIとは何ですか?



何となぜのトピックに再びスプレーしないように、EHCIについての私の以前の記事へのリンクを残してください。 ここを突く

UHCI-Universal Host Controller InterfaceはPCIデバイスとして動作しますが、EHCIとは異なり、MMIO(Memory-Mapped-IO)の代わりにポートを使用します。







以下で使用される用語





データ転送の種類



等時性-一定のデータ転送頻度の等時性伝送。 たとえば、USBマイクなどに使用できます。



割り込み-デバイスからの小さな自発的なデータ転送。 割り込み送信タイプは、予測可能なサービス間隔を必要とするが、必ずしも予測可能なデータストリームを提供しないデバイスをサポートします。 キーボードやポインティングデバイスなど、長時間データを提供しない可能性があるが、送信するデータがある場合は迅速な応答が必要なデバイスに一般的に使用されます。



制御-デバイスのステータス、ステータス、および構成に関する情報の送信のタイプ。 制御転送タイプは、ホストからUSBデバイスへの制御チャネルを提供するために使用されます。 制御伝送は常に、セットアップフェーズと、ゼロまたはそれ以上のデータフェーズと、それに続くステータスフェーズで構成されます。 特定のエンドポイントへの制御転送は、FIFOモードで処理する必要があります。 制御が同じエンドポイントに渡される場合、インターリーブは予測できない動作を引き起こす可能性があります。



バルク-データ配列の転送のタイプ たとえば、MassStorageデバイスで使用されます。







これは、1msの時間分布-1フレームの処理方法です。



時間分布



ホストコントローラーは、1 msごとにフレーム開始(SOF)パケットを生成することにより、リアルタイムのデータ配信をサポートします。 ホストコントローラーのSOFカウンターが期限切れになると、SOFパケットが生成されます(図3)。 ホストコントローラーは、1 msのフレーム時間でSOFカウンターを初期化します。 SOF変更レジスタをプログラムすることにより、この値(およびフレーム期間)をわずかに変更できます。 この機能を使用すると、必要に応じてフレーム期間をわずかに変更して、USBシステム全体でリアルタイムの同期を維持できます。



ホストコントローラーは、各SOFパケットにフレーム番号を含めます。 このフレーム番号は、フレーム周期をリアルタイムで一意に決定します。 フレーム終了条件(EOF)は、ホストコントローラーが次のフレーム時間を開始する1 msの時間間隔の終わりに発生し、対応するフレーム番号を持つ別のSOFパケットを生成します。 フレーム期間中、データは情報パケットとして送信されます。 フレーム期間はホストコントローラーによって厳密に適用され、現在のフレームのデータパケットはEOFを超えることはできません(USB仕様の第11章を参照)。 ホストコントローラーは、フレーム間のデータ伝送のリアルタイムでの同期をサポートし、フレーム番号をリンクしてフレームリストの特定のエントリを実行します。 ホストコントローラーのフレームカウンターはフレーム番号(11ビット値)を生成し、各SOFパケットに含めます。 カウンタはレジスタを介してプログラムされ、各フレーム期間がインクリメントされます。 ホストコントローラーは、フレーム番号の下位10ビットを、システムメモリに格納されている1024フレームのフレームリストのインデックスとして使用します。 したがって、フレームカウンターはフレームのリストからのエントリの選択を制御するため、ホストコントローラーは指定されたフレーム期間でリスト内の各エントリを処理します。 ホストコントローラは、新しいフレームごとにフレームリストの次のエントリに展開します。 これにより、特定のフレームでアイソクロナス転送が実行されます。



図3:







UHCI構造



すべてがEHCIとまったく同じです。 HCへのクエリの例:







UHCIの構成とアクセス



したがって、前述したように、UHCIはポートを介して機能するため、PCIからUHCIレジスタのベースを見つける必要があります。







オフセット0x20に4バイト-IOベースがあります。 IO Baseに関しては、次のレジスタを使用できます。







UHCIレジスタ





構造



フレームリストポインタ







転送記述子







TDコントロールとステータス
。 ビット:



TDトークン




キューヘッド







コード



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






All Articles