Arduino_STM32/STM32F1/libraries/USBComposite/usb_x360.c

486 lines
16 KiB
C
Raw Permalink Normal View History

2018-04-12 18:45:59 -07:00
/******************************************************************************
* The MIT License
*
* Copyright (c) 2011 LeafLabs LLC.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*****************************************************************************/
/**
* FIXME: this works on the STM32F1 USB peripherals, and probably no
* place else. Nonportable bits really need to be factored out, and
* the result made cleaner.
*/
#include "usb_generic.h"
#include "usb_x360.h"
#include <string.h>
#include <libmaple/usb.h>
#include <libmaple/nvic.h>
#include <libmaple/delay.h>
/* Private headers */
#include "usb_lib_globals.h"
#include "usb_reg_map.h"
/* usb_lib headers */
#include "usb_type.h"
#include "usb_core.h"
#include "usb_def.h"
typedef enum _HID_REQUESTS
{
GET_REPORT = 1,
GET_IDLE,
GET_PROTOCOL,
SET_REPORT = 9,
SET_IDLE,
SET_PROTOCOL
} HID_REQUESTS;
#define USB_ENDPOINT_IN(addr) ((addr) | 0x80)
#define HID_ENDPOINT_INT 1
#define USB_ENDPOINT_TYPE_INTERRUPT 0x03
#define HID_DESCRIPTOR_TYPE 0x21
#define REPORT_DESCRIPTOR 0x22
typedef struct
{
uint8_t len; // 9
uint8_t dtype; // 0x21
uint8_t versionL; // 0x101
uint8_t versionH; // 0x101
uint8_t country;
uint8_t numDesc;
uint8_t desctype; // 0x22 report
uint8_t descLenL;
uint8_t descLenH;
} HIDDescriptor;
#define X360_INTERFACE_OFFSET 0
#define X360_INTERFACE_NUMBER (X360_INTERFACE_OFFSET+usbX360Part.startInterface)
#define X360_ENDPOINT_TX 0
#define X360_ENDPOINT_RX 1
#define USB_X360_RX_ADDR x360Endpoints[X360_ENDPOINT_RX].pmaAddress
#define USB_X360_TX_ADDR x360Endpoints[X360_ENDPOINT_TX].pmaAddress
#define USB_X360_RX_ENDP x360Endpoints[X360_ENDPOINT_RX].address
#define USB_X360_TX_ENDP x360Endpoints[X360_ENDPOINT_TX].address
u16 GetEPTxAddr(u8 bEpNum);
static uint32 ProtocolValue;
static void x360DataTxCb(void);
static void x360DataRxCb(void);
static void (*x360_rumble_callback)(uint8 left, uint8 right);
static void (*x360_led_callback)(uint8 pattern);
static void x360Reset(void);
static RESULT x360DataSetup(uint8 request);
static RESULT x360NoDataSetup(uint8 request);
static uint8 *HID_GetProtocolValue(uint16 Length);
/*
* Descriptors
*/
#if 0
const uint8_t hid_report_descriptor[] = {
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x3a, // USAGE (Counted Buffer)
0xa1, 0x02, // COLLECTION (Logical)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x3f, // USAGE (Reserved)
0x09, 0x3b, // USAGE (Byte Count)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x95, 0x04, // REPORT_COUNT (4)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x0c, // USAGE_MINIMUM (Button 12)
0x29, 0x0f, // USAGE_MAXIMUM (Button 15)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x95, 0x04, // REPORT_COUNT (4)
0x05, 0x09, // USAGE_PAGE (Button)
0x09, 0x09, // USAGE (Button 9)
0x09, 0x0a, // USAGE (Button 10)
0x09, 0x07, // USAGE (Button 7)
0x09, 0x08, // USAGE (Button 8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x95, 0x03, // REPORT_COUNT (3)
0x05, 0x09, // USAGE_PAGE (Button)
0x09, 0x05, // USAGE (Button 5)
0x09, 0x06, // USAGE (Button 6)
0x09, 0x0b, // USAGE (Button 11)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x01, // INPUT (Cnst,Ary,Abs)
0x75, 0x01, // REPORT_SIZE (1)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x45, 0x01, // PHYSICAL_MAXIMUM (1)
0x95, 0x04, // REPORT_COUNT (4)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x04, // USAGE_MAXIMUM (Button 4)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0xff, 0x00, // PHYSICAL_MAXIMUM (255)
0x95, 0x02, // REPORT_COUNT (2)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x32, // USAGE (Z)
0x09, 0x35, // USAGE (Rz)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x75, 0x10, // REPORT_SIZE (16)
0x16, 0x00, 0x80, // LOGICAL_MINIMUM (-32768)
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x36, 0x00, 0x80, // PHYSICAL_MINIMUM (-32768)
0x46, 0xff, 0x7f, // PHYSICAL_MAXIMUM (32767)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x95, 0x02, // REPORT_COUNT (2)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x01, // USAGE (Pointer)
0xa1, 0x00, // COLLECTION (Physical)
0x95, 0x02, // REPORT_COUNT (2)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x33, // USAGE (Rx)
0x09, 0x34, // USAGE (Ry)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
#endif
typedef struct {
// usb_descriptor_config_header Config_Header;
usb_descriptor_interface HID_Interface;
uint8 unknown_descriptor1[17];
usb_descriptor_endpoint DataInEndpoint;
usb_descriptor_endpoint DataOutEndpoint;
} __packed usb_descriptor_config;
#define MAX_POWER (100 >> 1)
static const usb_descriptor_config X360Descriptor_Config =
{
#if 0
.Config_Header = {
.bLength = sizeof(usb_descriptor_config_header),
.bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION,
.wTotalLength = sizeof(usb_descriptor_config),//0,
.bNumInterfaces = 0x01,
.bConfigurationValue = 0x01,
.iConfiguration = 0x00,
.bmAttributes = (USB_CONFIG_ATTR_BUSPOWERED |
USB_CONFIG_ATTR_SELF_POWERED),
.bMaxPower = MAX_POWER,
},
#endif
.HID_Interface = {
.bLength = sizeof(usb_descriptor_interface),
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
.bInterfaceNumber = X360_INTERFACE_OFFSET, // PATCH
.bAlternateSetting = 0x00,
.bNumEndpoints = 0x02,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0x5D,
.bInterfaceProtocol = 0x01,
.iInterface = 0x00,
},
.unknown_descriptor1 = {
17,33,0,1,1,37,129,20,0,0,0,0,19,2,8,0,0,
},
.DataInEndpoint = {
.bLength = sizeof(usb_descriptor_endpoint),
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | X360_ENDPOINT_TX),//PATCH
.bmAttributes = USB_EP_TYPE_INTERRUPT,
.wMaxPacketSize = 0x20,
.bInterval = 4,
},
.DataOutEndpoint = {
.bLength = sizeof(usb_descriptor_endpoint),
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
.bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT | X360_ENDPOINT_RX),//PATCH
.bmAttributes = USB_EP_TYPE_INTERRUPT,
.wMaxPacketSize = 0x20,
.bInterval = 8,
},
};
static USBEndpointInfo x360Endpoints[2] = {
{
.callback = x360DataTxCb,
.bufferSize = 0x20,
.type = USB_EP_EP_TYPE_INTERRUPT,
.tx = 1
},
{
.callback = x360DataRxCb,
.bufferSize = 0x20,
.type = USB_EP_EP_TYPE_INTERRUPT,
.tx = 0,
}
};
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
static void getX360PartDescriptor(uint8* out) {
memcpy(out, &X360Descriptor_Config, sizeof(X360Descriptor_Config));
// patch to reflect where the part goes in the descriptor
OUT_BYTE(X360Descriptor_Config, HID_Interface.bInterfaceNumber) += usbX360Part.startInterface;
OUT_BYTE(X360Descriptor_Config, DataOutEndpoint.bEndpointAddress) += usbX360Part.startEndpoint;
OUT_BYTE(X360Descriptor_Config, DataInEndpoint.bEndpointAddress) += usbX360Part.startEndpoint;
}
USBCompositePart usbX360Part = {
.numInterfaces = 1,
.numEndpoints = sizeof(x360Endpoints)/sizeof(*x360Endpoints),
.descriptorSize = sizeof(X360Descriptor_Config),
.getPartDescriptor = getX360PartDescriptor,
.usbInit = NULL,
.usbReset = x360Reset,
.usbDataSetup = x360DataSetup,
.usbNoDataSetup = x360NoDataSetup,
.endpoints = x360Endpoints
};
/*
* Etc.
*/
/* I/O state */
/* Received data */
static volatile uint8 hidBufferRx[USB_X360_RX_EPSIZE];
/* Number of bytes left to transmit */
static volatile uint32 n_unsent_bytes = 0;
/* Are we currently sending an IN packet? */
static volatile uint8 transmitting = 0;
/*
* HID interface
*/
void x360_set_rumble_callback(void (*callback)(uint8 left, uint8 right)) {
x360_rumble_callback = callback;
}
void x360_set_led_callback(void (*callback)(uint8 pattern)) {
x360_led_callback = callback;
}
/*void x360_disable(void) {
x360_set_rumble_callback(NULL);
x360_set_led_callback(NULL);
usb_generic_disable();
}*/
void x360_putc(char ch) {
while (!x360_tx((uint8*)&ch, 1))
;
}
/* This function is non-blocking.
*
* It copies data from a usercode buffer into the USB peripheral TX
* buffer, and returns the number of bytes copied. */
uint32 x360_tx(const uint8* buf, uint32 len) {
/* Last transmission hasn't finished, so abort. */
if (x360_is_transmitting()) {
return 0;
}
/* We can only put USB_X360_TX_EPSIZE bytes in the buffer. */
if (len > USB_X360_TX_EPSIZE) {
len = USB_X360_TX_EPSIZE;
}
/* Queue bytes for sending. */
if (len) {
usb_copy_to_pma(buf, len, GetEPTxAddr(USB_X360_TX_ENDP));//USB_X360_TX_ADDR);
}
// We still need to wait for the interrupt, even if we're sending
// zero bytes. (Sending zero-size packets is useful for flushing
// host-side buffers.)
usb_set_ep_tx_count(USB_X360_TX_ENDP, len);
n_unsent_bytes = len;
transmitting = 1;
usb_set_ep_tx_stat(USB_X360_TX_ENDP, USB_EP_STAT_TX_VALID);
return len;
}
uint8 x360_is_transmitting(void) {
return transmitting;
}
uint16 x360_get_pending(void) {
return n_unsent_bytes;
}
static void x360DataRxCb(void)
{
uint32 ep_rx_size = usb_get_ep_rx_count(USB_X360_RX_ENDP);
// This copy won't overwrite unread bytes as long as there is
// enough room in the USB Rx buffer for next packet
uint32 *src = usb_pma_ptr(USB_X360_RX_ADDR);
uint16 tmp = 0;
uint8 val;
uint32 i;
for (i = 0; i < ep_rx_size; i++) {
if (i&1) {
val = tmp>>8;
} else {
tmp = *src++;
val = tmp&0xFF;
}
hidBufferRx[i] = val;
}
if (ep_rx_size == 3) {
if (x360_led_callback != NULL && hidBufferRx[0] == 1 && hidBufferRx[1] == 3)
x360_led_callback(hidBufferRx[2]);
}
else if (ep_rx_size == 8) {
if (x360_rumble_callback != NULL && hidBufferRx[0] == 0 && hidBufferRx[1] == 8)
x360_rumble_callback(hidBufferRx[3],hidBufferRx[4]);
}
usb_set_ep_rx_stat(USB_X360_RX_ENDP, USB_EP_STAT_RX_VALID);
}
/*
* Callbacks
*/
static void x360DataTxCb(void) {
n_unsent_bytes = 0;
transmitting = 0;
}
static RESULT x360DataSetup(uint8 request) {
uint8* (*CopyRoutine)(uint16) = 0;
#if 0
if (request == GET_DESCRIPTOR
&& pInformation->USBwIndex0 == X360_INTERFACE_NUMBER &&
&& (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT))
&& (pInformation->USBwIndex0 == 0)){
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR){
CopyRoutine = HID_GetReportDescriptor;
} else
if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE){
CopyRoutine = HID_GetHIDDescriptor;
}
} /* End of GET_DESCRIPTOR */
/*** GET_PROTOCOL ***/
else
#endif
if(pInformation->USBwIndex0 == X360_INTERFACE_NUMBER &&
(Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& request == GET_PROTOCOL){
CopyRoutine = HID_GetProtocolValue;
}
if (CopyRoutine == NULL){
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
static RESULT x360NoDataSetup(uint8 request) {
if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT))
&& (request == SET_PROTOCOL)){
uint8 wValue0 = pInformation->USBwValue0;
ProtocolValue = wValue0;
return USB_SUCCESS;
}else{
return USB_UNSUPPORT;
}
}
static uint8* HID_GetProtocolValue(uint16 Length){
if (Length == 0){
pInformation->Ctrl_Info.Usb_wLength = 1;
return NULL;
} else {
return (uint8 *)(&ProtocolValue);
}
}
static void x360Reset(void) {
/* Reset the RX/TX state */
n_unsent_bytes = 0;
transmitting = 0;
}