読者の皆さん、こんにちは! UHCIについて書くように頼まれました-まあ、私は書きます。
この記事は、たとえば、ドライバーの十分なライティングスキルやハードウェアのドキュメントを読むことができない場合に役立つことがあります。 簡単な例:Windowsや他のLinuxディストリビューションがハードウェアをダウンロードしないように、ミニPC用にOSを作成し、そのパワーをすべて自分の目的にのみ使用したい場合。
UHCIとは何ですか?
何となぜのトピックに再びスプレーしないように、EHCIについての私の以前の記事へのリンクを残してください。 ここを突く
UHCI-Universal Host Controller InterfaceはPCIデバイスとして動作しますが、EHCIとは異なり、MMIO(Memory-Mapped-IO)の代わりにポートを使用します。
以下で使用される用語
- USB Driver(USBD)-USBドライバー自体
- HC(ホストコントローラー)-ホストコントローラー、または単にUHCI
- ホストコントローラードライバー(HCD)-ハードウェアとUSBDを接続するドライバー
- USBデバイス-USBデバイス自体
データ転送の種類
等時性-一定のデータ転送頻度の等時性伝送。 たとえば、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レジスタ
- USBCMDは、HC'omを管理するためのレジスタです。 ビット:
- ビット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
- ビット24-IOC
- ビット23-16-ステータス:
- ビット23-アクティブTDであることを示します
- ビット22-ストール
- ビット21-データバッファエラー
- ビット20-検出されたバブル
- ビット19-NAK
- ビット10〜0:ホストコントローラーによって送信されたバイト数。
TDトークン
- ビット31:21-最大パケット長、EHCIと同様
- ビット19-EHCIと同様のデータトグル
- ビット18:15-エンドポイント番号
- ビット18:14-デバイスアドレス
- ビット7:0-PID。 In = 0x69、Out = 0xE1、Setup = 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); }