// // usbgamepad.c // // USPi - An USB driver for Raspberry Pi written in C // Copyright (C) 2014 R. Stange // Copyright (C) 2014 M. Maccaferri // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include // HID Report Items from HID 1.11 Section 6.2.2 #define HID_USAGE_PAGE 0x04 #define HID_USAGE 0x08 #define HID_COLLECTION 0xA0 #define HID_END_COLLECTION 0xC0 #define HID_REPORT_COUNT 0x94 #define HID_REPORT_SIZE 0x74 #define HID_USAGE_MIN 0x18 #define HID_USAGE_MAX 0x28 #define HID_LOGICAL_MIN 0x14 #define HID_LOGICAL_MAX 0x24 #define HID_PHYSICAL_MIN 0x34 #define HID_PHYSICAL_MAX 0x44 #define HID_INPUT 0x80 #define HID_REPORT_ID 0x84 #define HID_OUTPUT 0x90 // HID Report Usage Pages from HID Usage Tables 1.12 Section 3, Table 1 #define HID_USAGE_PAGE_GENERIC_DESKTOP 0x01 #define HID_USAGE_PAGE_KEY_CODES 0x07 #define HID_USAGE_PAGE_LEDS 0x08 #define HID_USAGE_PAGE_BUTTONS 0x09 // HID Report Usages from HID Usage Tables 1.12 Section 4, Table 6 #define HID_USAGE_POINTER 0x01 #define HID_USAGE_MOUSE 0x02 #define HID_USAGE_JOYSTICK 0x04 #define HID_USAGE_GAMEPAD 0x05 #define HID_USAGE_KEYBOARD 0x06 #define HID_USAGE_X 0x30 #define HID_USAGE_Y 0x31 #define HID_USAGE_Z 0x32 #define HID_USAGE_RX 0x33 #define HID_USAGE_RY 0x34 #define HID_USAGE_RZ 0x35 #define HID_USAGE_SLIDER 0x36 #define HID_USAGE_WHEEL 0x38 #define HID_USAGE_HATSWITCH 0x39 // HID Report Collection Types from HID 1.12 6.2.2.6 #define HID_COLLECTION_PHYSICAL 0 #define HID_COLLECTION_APPLICATION 1 // HID Input/Output/Feature Item Data (attributes) from HID 1.11 6.2.2.5 #define HID_ITEM_CONSTANT 0x1 #define HID_ITEM_VARIABLE 0x2 #define HID_ITEM_RELATIVE 0x4 static unsigned s_nDeviceNumber = 1; static const char FromUSBPad[] = "usbpad"; static boolean USBGamePadDeviceStartRequest (TUSBGamePadDevice *pThis); static void USBGamePadDeviceCompletionRoutine (TUSBRequest *pURB, void *pParam, void *pContext); static void USBGamePadDevicePS3Configure (TUSBGamePadDevice *pThis); void USBGamePadDevice (TUSBGamePadDevice *pThis, TUSBDevice *pDevice) { assert (pThis != 0); USBDeviceCopy (&pThis->m_USBDevice, pDevice); pThis->m_USBDevice.Configure = USBGamePadDeviceConfigure; pThis->m_pEndpointIn = 0; pThis->m_pEndpointOut = 0; pThis->m_pStatusHandler = 0; pThis->m_pURB = 0; pThis->m_pHIDReportDescriptor = 0; pThis->m_usReportDescriptorLength = 0; pThis->m_nReportSize = 0; pThis->m_State.naxes = 0; for (int i = 0; i < MAX_AXIS; i++) { pThis->m_State.axes[i].value = 0; pThis->m_State.axes[i].minimum = 0; pThis->m_State.axes[i].maximum = 0; } pThis->m_State.nhats = 0; for (int i = 0; i < MAX_HATS; i++) pThis->m_State.hats[i] = 0; pThis->m_State.nbuttons = 0; pThis->m_State.buttons = 0; pThis->m_pReportBuffer = malloc (64); assert (pThis->m_pReportBuffer != 0); } void _CUSBGamePadDevice (TUSBGamePadDevice *pThis) { assert (pThis != 0); if (pThis->m_pHIDReportDescriptor != 0) { free (pThis->m_pHIDReportDescriptor); pThis->m_pHIDReportDescriptor = 0; } if (pThis->m_pReportBuffer != 0) { free (pThis->m_pReportBuffer); pThis->m_pReportBuffer = 0; } if (pThis->m_pEndpointIn != 0) { _USBEndpoint (pThis->m_pEndpointIn); free (pThis->m_pEndpointIn); pThis->m_pEndpointIn = 0; } if (pThis->m_pEndpointOut != 0) { _USBEndpoint (pThis->m_pEndpointOut); free (pThis->m_pEndpointOut); pThis->m_pEndpointOut = 0; } _USBDevice (&pThis->m_USBDevice); } static u32 BitGetUnsigned(void *buffer, u32 offset, u32 length) { u8* bitBuffer; u8 mask; u32 result; bitBuffer = buffer; result = 0; for (u32 i = offset / 8, j = 0; i < (offset + length + 7) / 8; i++) { if (offset / 8 == (offset + length - 1) / 8) { mask = (1 << ((offset % 8) + length)) - (1 << (offset % 8)); result = (bitBuffer[i] & mask) >> (offset % 8); } else if (i == offset / 8) { mask = 0x100 - (1 << (offset % 8)); j += 8 - (offset % 8); result = ((bitBuffer[i] & mask) >> (offset % 8)) << (length - j); } else if (i == (offset + length - 1) / 8) { mask = (1 << ((offset % 8) + length)) - 1; result |= bitBuffer[i] & mask; } else { j += 8; result |= bitBuffer[i] << (length - j); } } return result; } static s32 BitGetSigned(void* buffer, u32 offset, u32 length) { u32 result = BitGetUnsigned(buffer, offset, length); if (result & (1 << (length - 1))) result |= 0xffffffff - ((1 << length) - 1); return result; } enum { None = 0, GamePad, GamePadButton, GamePadAxis, GamePadHat, }; #define UNDEFINED -123456789 static void USBGamePadDeviceDecodeReport(TUSBGamePadDevice *pThis) { s32 item, arg; u32 offset = 0, size = 0, count = 0; s32 lmax = UNDEFINED, lmin = UNDEFINED, pmin = UNDEFINED, pmax = UNDEFINED; s32 naxes = 0, nhats = 0; u32 id = 0, state = None; u8 *pReportBuffer = pThis->m_pReportBuffer; s8 *pHIDReportDescriptor = (s8 *)pThis->m_pHIDReportDescriptor; u16 wReportDescriptorLength = pThis->m_usReportDescriptorLength; USPiGamePadState *pState = &pThis->m_State; while (wReportDescriptorLength > 0) { item = *pHIDReportDescriptor++; wReportDescriptorLength--; switch(item & 0x03) { case 0: arg = 0; break; case 1: arg = *pHIDReportDescriptor++; wReportDescriptorLength--; break; case 2: arg = *pHIDReportDescriptor++ & 0xFF; arg = arg | (*pHIDReportDescriptor++ << 8); wReportDescriptorLength -= 2; break; default: arg = *pHIDReportDescriptor++; arg = arg | (*pHIDReportDescriptor++ << 8); arg = arg | (*pHIDReportDescriptor++ << 16); arg = arg | (*pHIDReportDescriptor++ << 24); wReportDescriptorLength -= 4; break; } if ((item & 0xFC) == HID_REPORT_ID) { if (id != 0) break; id = BitGetUnsigned(pReportBuffer, 0, 8); if (id != 0 && id != arg) return; id = arg; offset = 8; } switch(item & 0xFC) { case HID_USAGE_PAGE: switch(arg) { case HID_USAGE_PAGE_BUTTONS: if (state == GamePad) state = GamePadButton; break; } break; case HID_USAGE: switch(arg) { case HID_USAGE_JOYSTICK: case HID_USAGE_GAMEPAD: state = GamePad; break; case HID_USAGE_X: case HID_USAGE_Y: case HID_USAGE_Z: case HID_USAGE_RX: case HID_USAGE_RY: case HID_USAGE_RZ: case HID_USAGE_SLIDER: if (state == GamePad) state = GamePadAxis; break; case HID_USAGE_HATSWITCH: if (state == GamePad) state = GamePadHat; break; } break; case HID_LOGICAL_MIN: lmin = arg; break; case HID_PHYSICAL_MIN: pmin = arg; break; case HID_LOGICAL_MAX: lmax = arg; break; case HID_PHYSICAL_MAX: pmax = arg; break; case HID_REPORT_SIZE: // REPORT_SIZE size = arg; break; case HID_REPORT_COUNT: // REPORT_COUNT count = arg; break; case HID_INPUT: if ((arg & 0x03) == 0x02) { // INPUT(Data,Var) if (state == GamePadAxis) { for (int i = 0; i < count && i < MAX_AXIS; i++) { pState->axes[naxes].minimum = lmin != UNDEFINED ? lmin : pmin; pState->axes[naxes].maximum = lmax != UNDEFINED ? lmax : pmax; int value = (pState->axes[naxes].minimum < 0) ? BitGetSigned(pReportBuffer, offset + i * size, size) : BitGetUnsigned(pReportBuffer, offset + i * size, size); pState->axes[naxes++].value = value; } state = GamePad; } else if (state == GamePadHat) { for (int i = 0; i < count && i < MAX_HATS; i++) { int value = BitGetUnsigned(pReportBuffer, offset + i * size, size); pState->hats[nhats++] = value; } state = GamePad; } else if (state == GamePadButton) { pState->nbuttons = count; pState->buttons = BitGetUnsigned(pReportBuffer, offset, size * count); state = GamePad; } } offset += count * size; break; case HID_OUTPUT: break; } } pState->naxes = naxes; pState->nhats = nhats; pThis->m_nReportSize = (offset + 7) / 8; } boolean USBGamePadDeviceConfigure (TUSBDevice *pUSBDevice) { TUSBGamePadDevice *pThis = (TUSBGamePadDevice *) pUSBDevice; assert (pThis != 0); TUSBConfigurationDescriptor *pConfDesc = (TUSBConfigurationDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_CONFIGURATION); if ( pConfDesc == 0 || pConfDesc->bNumInterfaces < 1) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } TUSBInterfaceDescriptor *pInterfaceDesc = (TUSBInterfaceDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_INTERFACE); if ( pInterfaceDesc == 0 || pInterfaceDesc->bNumEndpoints < 1 || pInterfaceDesc->bInterfaceClass != 0x03 // HID Class || pInterfaceDesc->bInterfaceSubClass != 0x00 // Boot Interface Subclass || pInterfaceDesc->bInterfaceProtocol != 0x00) // GamePad { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } pThis->m_ucInterfaceNumber = pInterfaceDesc->bInterfaceNumber; pThis->m_ucAlternateSetting = pInterfaceDesc->bAlternateSetting; TUSBHIDDescriptor *pHIDDesc = (TUSBHIDDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_HID); if ( pHIDDesc == 0 || pHIDDesc->wReportDescriptorLength == 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } const TUSBEndpointDescriptor *pEndpointDesc; while ((pEndpointDesc = (TUSBEndpointDescriptor *) USBDeviceGetDescriptor (&pThis->m_USBDevice, DESCRIPTOR_ENDPOINT)) != 0) { if ((pEndpointDesc->bmAttributes & 0x3F) == 0x03) // Interrupt { if ((pEndpointDesc->bEndpointAddress & 0x80) == 0x80) // Input { if (pThis->m_pEndpointIn != 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } pThis->m_pEndpointIn = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint)); assert (pThis->m_pEndpointIn != 0); USBEndpoint2 (pThis->m_pEndpointIn, &pThis->m_USBDevice, pEndpointDesc); } else // Output { if (pThis->m_pEndpointOut != 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } pThis->m_pEndpointOut = (TUSBEndpoint *) malloc (sizeof (TUSBEndpoint)); assert (pThis->m_pEndpointOut != 0); USBEndpoint2 (pThis->m_pEndpointOut, &pThis->m_USBDevice, pEndpointDesc); } } } if (pThis->m_pEndpointIn == 0) { USBDeviceConfigurationError (&pThis->m_USBDevice, FromUSBPad); return FALSE; } pThis->m_usReportDescriptorLength = pHIDDesc->wReportDescriptorLength; pThis->m_pHIDReportDescriptor = (unsigned char *) malloc(pHIDDesc->wReportDescriptorLength); assert (pThis->m_pHIDReportDescriptor != 0); if (DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_IN | REQUEST_TO_INTERFACE, GET_DESCRIPTOR, (pHIDDesc->bReportDescriptorType << 8) | DESCRIPTOR_INDEX_DEFAULT, pThis->m_ucInterfaceNumber, pThis->m_pHIDReportDescriptor, pHIDDesc->wReportDescriptorLength) != pHIDDesc->wReportDescriptorLength) { LogWrite (FromUSBPad, LOG_ERROR, "Cannot get HID report descriptor"); return FALSE; } //DebugHexdump (pThis->m_pHIDReportDescriptor, pHIDDesc->wReportDescriptorLength, "hid"); pThis->m_pReportBuffer[0] = 0; USBGamePadDeviceDecodeReport (pThis); if (!USBDeviceConfigure (&pThis->m_USBDevice)) { LogWrite (FromUSBPad, LOG_ERROR, "Cannot set configuration"); return FALSE; } if (pThis->m_ucAlternateSetting != 0) { if (DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_OUT | REQUEST_TO_INTERFACE, SET_INTERFACE, pThis->m_ucAlternateSetting, pThis->m_ucInterfaceNumber, 0, 0) < 0) { LogWrite (FromUSBPad, LOG_ERROR, "Cannot set interface"); return FALSE; } } pThis->m_nDeviceIndex = s_nDeviceNumber++; if ( pUSBDevice->m_pDeviceDesc->idVendor == 0x054c && pUSBDevice->m_pDeviceDesc->idProduct == 0x0268) { USBGamePadDevicePS3Configure (pThis); } TString DeviceName; String (&DeviceName); StringFormat (&DeviceName, "upad%u", pThis->m_nDeviceIndex); DeviceNameServiceAddDevice (DeviceNameServiceGet (), StringGet (&DeviceName), pThis, FALSE); _String (&DeviceName); return USBGamePadDeviceStartRequest (pThis); } void USBGamePadDeviceRegisterStatusHandler (TUSBGamePadDevice *pThis, TGamePadStatusHandler *pStatusHandler) { assert (pThis != 0); assert (pStatusHandler != 0); pThis->m_pStatusHandler = pStatusHandler; } boolean USBGamePadDeviceStartRequest (TUSBGamePadDevice *pThis) { assert (pThis != 0); assert (pThis->m_pEndpointIn != 0); assert (pThis->m_pReportBuffer != 0); assert (pThis->m_pURB == 0); pThis->m_pURB = malloc (sizeof (TUSBRequest)); assert (pThis->m_pURB != 0); USBRequest (pThis->m_pURB, pThis->m_pEndpointIn, pThis->m_pReportBuffer, pThis->m_nReportSize, 0); USBRequestSetCompletionRoutine (pThis->m_pURB, USBGamePadDeviceCompletionRoutine, 0, pThis); return DWHCIDeviceSubmitAsyncRequest (USBDeviceGetHost (&pThis->m_USBDevice), pThis->m_pURB); } void USBGamePadDeviceCompletionRoutine (TUSBRequest *pURB, void *pParam, void *pContext) { TUSBGamePadDevice *pThis = (TUSBGamePadDevice *) pContext; assert (pThis != 0); assert (pURB != 0); assert (pThis->m_pURB == pURB); if ( USBRequestGetStatus (pURB) != 0 && USBRequestGetResultLength (pURB) > 0) { //DebugHexdump (pThis->m_pReportBuffer, 16, "report"); if (pThis->m_pHIDReportDescriptor != 0 && pThis->m_pStatusHandler != 0) { USBGamePadDeviceDecodeReport (pThis); (*pThis->m_pStatusHandler) (pThis->m_nDeviceIndex - 1, &pThis->m_State); } } _USBRequest (pThis->m_pURB); free (pThis->m_pURB); pThis->m_pURB = 0; USBGamePadDeviceStartRequest (pThis); } void USBGamePadDeviceGetReport (TUSBGamePadDevice *pThis) { if (DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_IN | REQUEST_CLASS | REQUEST_TO_INTERFACE, GET_REPORT, (REPORT_TYPE_INPUT << 8) | 0x00, pThis->m_ucInterfaceNumber, pThis->m_pReportBuffer, pThis->m_nReportSize) > 0) { USBGamePadDeviceDecodeReport (pThis); } } void USBGamePadDevicePS3Configure (TUSBGamePadDevice *pThis) { static u8 writeBuf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static u8 leds[] = { 0x00, // OFF 0x01, // LED1 0x02, // LED2 0x04, // LED3 0x08, // LED4 }; /* Special PS3 Controller enable commands */ pThis->m_pReportBuffer[0] = 0x42; pThis->m_pReportBuffer[1] = 0x0c; pThis->m_pReportBuffer[2] = 0x00; pThis->m_pReportBuffer[3] = 0x00; DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_OUT | REQUEST_CLASS | REQUEST_TO_INTERFACE, SET_REPORT, (REPORT_TYPE_FEATURE << 8) | 0xf4, pThis->m_ucInterfaceNumber, pThis->m_pReportBuffer, 4); /* Turn on LED */ writeBuf[9] |= (u8)(leds[pThis->m_nDeviceIndex] << 1); DWHCIDeviceControlMessage (USBDeviceGetHost (&pThis->m_USBDevice), USBDeviceGetEndpoint0 (&pThis->m_USBDevice), REQUEST_OUT | REQUEST_CLASS | REQUEST_TO_INTERFACE, SET_REPORT, (REPORT_TYPE_OUTPUT << 8) | 0x01, pThis->m_ucInterfaceNumber, writeBuf, 48); }