/** \brief Triggers the fifo flush for a given fifo. Raises the core fifo flush signal high, and then waits for the core to signal that it is ready again. */ Result HcdTransmitFifoFlush(enum CoreFifoFlush fifo) { u32 count = 0; if (fifo == FlushAll) LOG_DEBUG("HCD: TXFlush(All)\n"); else if (fifo == FlushNonPeriodic) LOG_DEBUG("HCD: TXFlush(NP)\n"); else LOG_DEBUGF("HCD: TXFlush(P%u)\n", fifo); ClearReg(&Core->Reset); Core->Reset.TransmitFifoFlushNumber = fifo; Core->Reset.TransmitFifoFlush = true; WriteThroughReg(&Core->Reset); count = 0; do { ReadBackReg(&Core->Reset); if (count++ >= 0x100000) { LOG("HCD: Device Hang!\n"); return ErrorDevice; } } while (Core->Reset.TransmitFifoFlush == true); return OK; }
void HcdTransmitChannel(u8 channel, void* buffer) { ReadBackReg(&Host->Channel[channel].SplitControl); Host->Channel[channel].SplitControl.CompleteSplit = false; WriteThroughReg(&Host->Channel[channel].SplitControl); if (((u32)buffer & 3) != 0) LOG_DEBUGF("HCD: Transfer buffer %#x is not DWORD aligned. Ignored, but dangerous.\n", buffer); Host->Channel[channel].DmaAddress = buffer; WriteThroughReg(&Host->Channel[channel].DmaAddress); ReadBackReg(&Host->Channel[channel].Characteristic); Host->Channel[channel].Characteristic.PacketsPerFrame = 1; Host->Channel[channel].Characteristic.Enable = true; Host->Channel[channel].Characteristic.Disable = false; WriteThroughReg(&Host->Channel[channel].Characteristic); }
Result HcdSumbitControlMessage(struct UsbDevice *device, struct UsbPipeAddress pipe, void* buffer, u32 bufferLength, struct UsbDeviceRequest *request) { Result result; struct UsbPipeAddress tempPipe; if (pipe.Device == RootHubDeviceNumber) { return HcdProcessRootHubMessage(device, pipe, buffer, bufferLength, request); } device->Error = Processing; device->LastTransfer = 0; // Setup tempPipe.Speed = pipe.Speed; tempPipe.Device = pipe.Device; tempPipe.EndPoint = pipe.EndPoint; tempPipe.MaxSize = pipe.MaxSize; tempPipe.Type = Control; tempPipe.Direction = Out; if ((result = HcdChannelSendWait(device, &tempPipe, 0, request, 8, request, Setup)) != OK) { LOGF("HCD: Could not send SETUP to %s.\n", UsbGetDescription(device)); return OK; } // Data if (buffer != NULL) { if (pipe.Direction == Out) { MemoryCopy(databuffer, buffer, bufferLength); } tempPipe.Speed = pipe.Speed; tempPipe.Device = pipe.Device; tempPipe.EndPoint = pipe.EndPoint; tempPipe.MaxSize = pipe.MaxSize; tempPipe.Type = Control; tempPipe.Direction = pipe.Direction; if ((result = HcdChannelSendWait(device, &tempPipe, 0, databuffer, bufferLength, request, Data1)) != OK) { LOGF("HCD: Could not send DATA to %s.\n", UsbGetDescription(device)); return OK; } ReadBackReg(&Host->Channel[0].TransferSize); if (pipe.Direction == In) { if (Host->Channel[0].TransferSize.TransferSize <= bufferLength) device->LastTransfer = bufferLength - Host->Channel[0].TransferSize.TransferSize; else{ LOG_DEBUGF("HCD: Weird transfer.. %d/%d bytes received.\n", Host->Channel[0].TransferSize.TransferSize, bufferLength); LOG_DEBUGF("HCD: Message %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x ...\n", ((u8*)databuffer)[0x0],((u8*)databuffer)[0x1],((u8*)databuffer)[0x2],((u8*)databuffer)[0x3], ((u8*)databuffer)[0x4],((u8*)databuffer)[0x5],((u8*)databuffer)[0x6],((u8*)databuffer)[0x7], ((u8*)databuffer)[0x8],((u8*)databuffer)[0x9],((u8*)databuffer)[0xa],((u8*)databuffer)[0xb], ((u8*)databuffer)[0xc],((u8*)databuffer)[0xd],((u8*)databuffer)[0xe],((u8*)databuffer)[0xf]); device->LastTransfer = bufferLength; } MemoryCopy(buffer, databuffer, device->LastTransfer); } else { device->LastTransfer = bufferLength; } } // Status tempPipe.Speed = pipe.Speed; tempPipe.Device = pipe.Device; tempPipe.EndPoint = pipe.EndPoint; tempPipe.MaxSize = pipe.MaxSize; tempPipe.Type = Control; tempPipe.Direction = ((bufferLength == 0) || pipe.Direction == Out) ? In : Out; if ((result = HcdChannelSendWait(device, &tempPipe, 0, databuffer, 0, request, Data1)) != OK) { LOGF("HCD: Could not send STATUS to %s.\n", UsbGetDescription(device)); return OK; } ReadBackReg(&Host->Channel[0].TransferSize); if (Host->Channel[0].TransferSize.TransferSize != 0) LOG_DEBUGF("HCD: Warning non zero status transfer! %d.\n", Host->Channel[0].TransferSize.TransferSize); device->Error = NoError; return OK; }
Result HcdChannelSendWaitOne(struct UsbDevice *device, struct UsbPipeAddress *pipe, u8 channel, void* buffer, u32 bufferLength, u32 bufferOffset, struct UsbDeviceRequest *request) { Result result; u32 timeout, tries, globalTries, actualTries; for (globalTries = 0, actualTries = 0; globalTries < 3 && actualTries < 10; globalTries++, actualTries++) { SetReg(&Host->Channel[channel].Interrupt); WriteThroughReg(&Host->Channel[channel].Interrupt); ReadBackReg(&Host->Channel[channel].TransferSize); ReadBackReg(&Host->Channel[channel].SplitControl); HcdTransmitChannel(channel, (u8*)buffer + bufferOffset); timeout = 0; do { if (timeout++ == RequestTimeout) { LOGF("HCD: Request to %s has timed out.\n", UsbGetDescription(device)); device->Error = ConnectionError; return ErrorTimeout; } ReadBackReg(&Host->Channel[channel].Interrupt); if (!Host->Channel[channel].Interrupt.Halt) MicroDelay(10); else break; } while (true); ReadBackReg(&Host->Channel[channel].TransferSize); if (Host->Channel[channel].SplitControl.SplitEnable) { if (Host->Channel[channel].Interrupt.Acknowledgement) { for (tries = 0; tries < 3; tries++) { SetReg(&Host->Channel[channel].Interrupt); WriteThroughReg(&Host->Channel[channel].Interrupt); ReadBackReg(&Host->Channel[channel].SplitControl); Host->Channel[channel].SplitControl.CompleteSplit = true; WriteThroughReg(&Host->Channel[channel].SplitControl); Host->Channel[channel].Characteristic.Enable = true; Host->Channel[channel].Characteristic.Disable = false; WriteThroughReg(&Host->Channel[channel].Characteristic); timeout = 0; do { if (timeout++ == RequestTimeout) { LOGF("HCD: Request split completion to %s has timed out.\n", UsbGetDescription(device)); device->Error = ConnectionError; return ErrorTimeout; } ReadBackReg(&Host->Channel[channel].Interrupt); if (!Host->Channel[channel].Interrupt.Halt) MicroDelay(100); else break; } while (true); if (!Host->Channel[channel].Interrupt.NotYet) break; } if (tries == 3) { MicroDelay(25000); continue; } else if (Host->Channel[channel].Interrupt.NegativeAcknowledgement) { globalTries--; MicroDelay(25000); continue; } else if (Host->Channel[channel].Interrupt.TransactionError) { MicroDelay(25000); continue; } if ((result = HcdChannelInterruptToError(device, Host->Channel[channel].Interrupt, false)) != OK) { LOG_DEBUGF("HCD: Control message to %#x: %02x%02x%02x%02x %02x%02x%02x%02x.\n", *(u32*)pipe, ((u8*)request)[0], ((u8*)request)[1], ((u8*)request)[2], ((u8*)request)[3], ((u8*)request)[4], ((u8*)request)[5], ((u8*)request)[6], ((u8*)request)[7]); LOGF("HCD: Request split completion to %s failed.\n", UsbGetDescription(device)); return result; } } else if (Host->Channel[channel].Interrupt.NegativeAcknowledgement) { globalTries--; MicroDelay(25000); continue; } else if (Host->Channel[channel].Interrupt.TransactionError) { MicroDelay(25000); continue; } } else { if ((result = HcdChannelInterruptToError(device, Host->Channel[channel].Interrupt, !Host->Channel[channel].SplitControl.SplitEnable)) != OK) { LOG_DEBUGF("HCD: Control message to %#x: %02x%02x%02x%02x %02x%02x%02x%02x.\n", *(u32*)pipe, ((u8*)request)[0], ((u8*)request)[1], ((u8*)request)[2], ((u8*)request)[3], ((u8*)request)[4], ((u8*)request)[5], ((u8*)request)[6], ((u8*)request)[7]); LOGF("HCD: Request to %s failed.\n", UsbGetDescription(device)); return ErrorRetry; } } break; } if (globalTries == 3 || actualTries == 10) { LOGF("HCD: Request to %s has failed 3 times.\n", UsbGetDescription(device)); if ((result = HcdChannelInterruptToError(device, Host->Channel[channel].Interrupt, !Host->Channel[channel].SplitControl.SplitEnable)) != OK) { LOG_DEBUGF("HCD: Control message to %#x: %02x%02x%02x%02x %02x%02x%02x%02x.\n", *(u32*)pipe, ((u8*)request)[0], ((u8*)request)[1], ((u8*)request)[2], ((u8*)request)[3], ((u8*)request)[4], ((u8*)request)[5], ((u8*)request)[6], ((u8*)request)[7]); LOGF("HCD: Request to %s failed.\n", UsbGetDescription(device)); return result; } device->Error = ConnectionError; return ErrorTimeout; } return OK; }
Result KeyboardAttach(struct UsbDevice *device, u32 interface __attribute__((unused))) { u32 keyboardNumber; struct HidDevice *hidData; struct KeyboardDevice *data; struct HidParserResult *parse; if ((KeyboardMaxKeyboards & 3) != 0) { LOG("KBD: Warning! KeyboardMaxKeyboards not a multiple of 4. The driver wasn't built for this!\n"); } if (keyboardCount == KeyboardMaxKeyboards) { LOGF("KBD: %s not connected. Too many keyboards connected (%d/%d). Change KeyboardMaxKeyboards in device.keyboard.c to allow more.\n", UsbGetDescription(device), keyboardCount, KeyboardMaxKeyboards); return ErrorIncompatible; } hidData = (struct HidDevice*)device->DriverData; if (hidData->Header.DeviceDriver != DeviceDriverHid) { LOGF("KBD: %s isn't a HID device. The keyboard driver is built upon the HID driver.\n", UsbGetDescription(device)); return ErrorIncompatible; } parse = hidData->ParserResult; if ((parse->Application.Page != GenericDesktopControl && parse->Application.Page != Undefined) || parse->Application.Desktop != DesktopKeyboard) { LOGF("KBD: %s doesn't seem to be a keyboard (%x != %x || %x != %x)...\n", UsbGetDescription(device), parse->Application.Page, GenericDesktopControl, parse->Application.Desktop, DesktopKeyboard); return ErrorIncompatible; } if (parse->ReportCount < 1) { LOGF("KBD: %s doesn't have enough outputs to be a keyboard.\n", UsbGetDescription(device)); return ErrorIncompatible; } hidData->HidDetached = KeyboardDetached; hidData->HidDeallocate = KeyboardDeallocate; if ((hidData->DriverData = MemoryAllocate(sizeof(struct KeyboardDevice))) == NULL) { LOGF("KBD: Not enough memory to allocate keyboard %s.\n", UsbGetDescription(device)); return ErrorMemory; } data = (struct KeyboardDevice*)hidData->DriverData; data->Header.DeviceDriver = DeviceDriverKeyboard; data->Header.DataSize = sizeof(struct KeyboardDevice); data->Index = keyboardNumber = 0xffffffff; for (u32 i = 0; i < KeyboardMaxKeyboards; i++) { if (keyboardAddresses[i] == 0) { data->Index = keyboardNumber = i; keyboardAddresses[i] = device->Number; keyboardCount++; break; } } if (keyboardNumber == 0xffffffff) { LOG("KBD: PANIC! Driver in inconsistent state! KeyboardCount is inaccurate.\n"); KeyboardDeallocate(device); return ErrorGeneral; } keyboards[keyboardNumber] = device; for (u32 i = 0; i < KeyboardMaxKeys; i++) data->Keys[i] = 0; *(u8*)&data->Modifiers = 0; *(u8*)&data->LedSupport = 0; for (u32 i = 0; i < 9; i++) data->KeyFields[i] = NULL; for (u32 i = 0; i < 8; i++) data->LedFields[i] = NULL; data->LedReport = NULL; data->KeyReport = NULL; for (u32 i = 0; i < parse->ReportCount; i++) { LOG_DEBUGF("KBD: type %x report %d. %d fields.\n", parse->Report[i]->Type, i, parse->Report[i]->FieldCount); if (parse->Report[i]->Type == Input && data->KeyReport == NULL) { LOG_DEBUGF("KBD: Output report %d. %d fields.\n", i, parse->Report[i]->FieldCount); data->KeyReport = parse->Report[i]; for (u32 j = 0; j < parse->Report[i]->FieldCount; j++) { if (parse->Report[i]->Fields[j].Usage.Page == KeyboardControl || parse->Report[i]->Fields[j].Usage.Page == Undefined) { if (parse->Report[i]->Fields[j].Attributes.Variable) { if (parse->Report[i]->Fields[j].Usage.Keyboard >= KeyboardLeftControl && parse->Report[i]->Fields[j].Usage.Keyboard <= KeyboardRightGui) LOG_DEBUGF("KBD: Modifier %d detected! Offset=%x, size=%x\n", parse->Report[i]->Fields[j].Usage.Keyboard, parse->Report[i]->Fields[j].Offset, parse->Report[i]->Fields[j].Size); data->KeyFields[(u16)parse->Report[i]->Fields[j].Usage.Keyboard - (u16)KeyboardLeftControl] = &parse->Report[i]->Fields[j]; } else { LOG_DEBUG("KBD: Key input detected!\n"); data->KeyFields[8] = &parse->Report[i]->Fields[j]; } } } } else if (parse->Report[i]->Type == Output && data->LedReport == NULL) { data->LedReport = parse->Report[i]; LOG_DEBUGF("KBD: Input report %d. %d fields.\n", i, parse->Report[i]->FieldCount); for (u32 j = 0; j < parse->Report[i]->FieldCount; j++) { if (parse->Report[i]->Fields[j].Usage.Page == Led) { switch (parse->Report[i]->Fields[j].Usage.Led) { case LedNumberLock: LOG_DEBUG("KBD: Number lock LED detected!\n"); data->LedFields[0] = &parse->Report[i]->Fields[j]; data->LedSupport.NumberLock = true; break; case LedCapsLock: LOG_DEBUG("KBD: Caps lock LED detected!\n"); data->LedFields[1] = &parse->Report[i]->Fields[j]; data->LedSupport.CapsLock = true; break; case LedScrollLock: LOG_DEBUG("KBD: Scroll lock LED detected!\n"); data->LedFields[2] = &parse->Report[i]->Fields[j]; data->LedSupport.ScrollLock = true; break; case LedCompose: LOG_DEBUG("KBD: Compose LED detected!\n"); data->LedFields[3] = &parse->Report[i]->Fields[j]; data->LedSupport.Compose = true; break; case LedKana: LOG_DEBUG("KBD: Kana LED detected!\n"); data->LedFields[4] = &parse->Report[i]->Fields[j]; data->LedSupport.Kana = true; break; case LedPower: LOG_DEBUG("KBD: Power LED detected!\n"); data->LedFields[5] = &parse->Report[i]->Fields[j]; data->LedSupport.Power = true; break; case LedShift: LOG_DEBUG("KBD: Shift LED detected!\n"); data->LedFields[6] = &parse->Report[i]->Fields[j]; data->LedSupport.Shift = true; break; case LedMute: LOG_DEBUG("KBD: Mute LED detected!\n"); data->LedFields[7] = &parse->Report[i]->Fields[j]; data->LedSupport.Mute = true; break; default: break; } } } } } LOG_DEBUGF("KBD: New keyboard assigned %d!\n", device->Number); return OK; }