Arduino_STM32/STM32F1/libraries/USBComposite/usb_hid.c

579 lines
17 KiB
C

/******************************************************************************
* 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.
*****************************************************************************/
/**
* @file libmaple/usb/stm32f1/usb_hid.c
* @brief USB HID (human interface device) support
*
* 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_hid.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"
uint16 GetEPTxAddr(uint8 /*bEpNum*/);
/* usb_lib headers */
#include "usb_type.h"
#include "usb_core.h"
#include "usb_def.h"
static uint32 ProtocolValue = 0;
static void hidDataTxCb(void);
static void hidUSBReset(void);
static RESULT hidUSBDataSetup(uint8 request);
static RESULT hidUSBNoDataSetup(uint8 request);
//static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting);
static uint8* HID_GetReportDescriptor(uint16 Length);
static uint8* HID_GetProtocolValue(uint16 Length);
static volatile HIDBuffer_t hidBuffers[MAX_HID_BUFFERS] = {{ 0 }};
static volatile HIDBuffer_t* currentHIDBuffer = NULL;
//#define DUMMY_BUFFER_SIZE 0x40 // at least as big as a buffer size
#define HID_INTERFACE_OFFSET 0x00
#define NUM_HID_ENDPOINTS 1
#define HID_INTERFACE_NUMBER (HID_INTERFACE_OFFSET+usbHIDPart.startInterface)
/*
* Descriptors
*/
static ONE_DESCRIPTOR HID_Report_Descriptor = {
(uint8*)NULL,
0
};
#define HID_ENDPOINT_TX 0
typedef struct {
//HID
usb_descriptor_interface HID_Interface;
HIDDescriptor HID_Descriptor;
usb_descriptor_endpoint HIDDataInEndpoint;
} __packed hid_part_config;
static const hid_part_config hidPartConfigData = {
.HID_Interface = {
.bLength = sizeof(usb_descriptor_interface),
.bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE,
.bInterfaceNumber = HID_INTERFACE_OFFSET, // PATCH
.bAlternateSetting = 0x00,
.bNumEndpoints = NUM_HID_ENDPOINTS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID,
.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_HID,
.bInterfaceProtocol = 0x00, /* Common AT Commands */
.iInterface = 0x00,
},
.HID_Descriptor = {
.len = 9,//sizeof(HIDDescDescriptor),
.dtype = HID_DESCRIPTOR_TYPE,
.versionL = 0x10,
.versionH = 0x01,
.country = 0x00,
.numDesc = 0x01,
.desctype = REPORT_DESCRIPTOR,//0x22,
.descLenL = 0x00, //PATCH
.descLenH = 0x00, //PATCH
},
.HIDDataInEndpoint = {
.bLength = sizeof(usb_descriptor_endpoint),
.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT,
.bEndpointAddress = USB_DESCRIPTOR_ENDPOINT_IN | HID_ENDPOINT_TX, // PATCH
.bmAttributes = USB_ENDPOINT_TYPE_INTERRUPT,
.wMaxPacketSize = USB_HID_TX_EPSIZE,//0x40,//big enough for a keyboard 9 byte packet and for a mouse 5 byte packet
.bInterval = 0x0A,
}
};
static USBEndpointInfo hidEndpoints[1] = {
{
.callback = hidDataTxCb,
.bufferSize = USB_HID_TX_EPSIZE,
.type = USB_EP_EP_TYPE_INTERRUPT, // TODO: interrupt???
.tx = 1,
}
};
#define OUT_BYTE(s,v) out[(uint8*)&(s.v)-(uint8*)&s]
static void getHIDPartDescriptor(uint8* out) {
memcpy(out, &hidPartConfigData, sizeof(hid_part_config));
// patch to reflect where the part goes in the descriptor
OUT_BYTE(hidPartConfigData, HID_Interface.bInterfaceNumber) += usbHIDPart.startInterface;
OUT_BYTE(hidPartConfigData, HIDDataInEndpoint.bEndpointAddress) += usbHIDPart.startEndpoint;
OUT_BYTE(hidPartConfigData, HID_Descriptor.descLenL) = (uint8)HID_Report_Descriptor.Descriptor_Size;
OUT_BYTE(hidPartConfigData, HID_Descriptor.descLenH) = (uint8)(HID_Report_Descriptor.Descriptor_Size>>8);
}
USBCompositePart usbHIDPart = {
.numInterfaces = 1,
.numEndpoints = sizeof(hidEndpoints)/sizeof(*hidEndpoints),
.descriptorSize = sizeof(hid_part_config),
.getPartDescriptor = getHIDPartDescriptor,
.usbInit = NULL,
.usbReset = hidUSBReset,
.usbDataSetup = hidUSBDataSetup,
.usbNoDataSetup = hidUSBNoDataSetup,
.usbClearFeature = NULL,
.usbSetConfiguration = NULL,
.endpoints = hidEndpoints
};
#define HID_TX_BUFFER_SIZE 256 // must be power of 2
#define HID_TX_BUFFER_SIZE_MASK (HID_TX_BUFFER_SIZE-1)
// Tx data
static volatile uint8 hidBufferTx[HID_TX_BUFFER_SIZE];
// Write index to hidBufferTx
static volatile uint32 hid_tx_head = 0;
// Read index from hidBufferTx
static volatile uint32 hid_tx_tail = 0;
#define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2
#define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1)
void usb_hid_putc(char ch) {
while (!usb_hid_tx((uint8*)&ch, 1))
;
}
/*
static void hidStatusIn() {
if (pInformation->ControlState == WAIT_STATUS_IN) {
if (currentInFeature >= 0) {
if (featureBuffers[currentInFeature].bufferSize == featureBuffers[currentInFeature].currentDataSize)
featureBuffers[currentInFeature].state = HID_BUFFER_UNREAD;
currentInFeature = -1;
}
if (currentOutput >= 0) {
if (outputBuffers[currentOutput].bufferSize == outputBuffers[currentOutput].currentDataSize)
outputBuffers[currentOutput].state = HID_BUFFER_UNREAD;
currentOutput = -1;
}
}
}
*/
void usb_hid_set_report_descriptor(const uint8* report_descriptor, uint16 report_descriptor_length) {
HID_Report_Descriptor.Descriptor = (uint8*)report_descriptor;
HID_Report_Descriptor.Descriptor_Size = report_descriptor_length;
}
static volatile HIDBuffer_t* usb_hid_find_buffer(uint8 type, uint8 reportID) {
uint8 typeTest = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
for (int i=0; i<MAX_HID_BUFFERS; i++) {
if ( hidBuffers[i].buffer != NULL &&
( hidBuffers[i].mode & HID_BUFFER_MODE_OUTPUT ) == typeTest &&
hidBuffers[i].reportID == reportID) {
return hidBuffers+i;
}
}
return NULL;
}
void usb_hid_set_feature(uint8 reportID, uint8* data) {
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, reportID);
if (buffer != NULL) {
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_NAK);
unsigned delta = reportID != 0;
memcpy((uint8*)buffer->buffer+delta, data, buffer->bufferSize-delta);
if (reportID)
buffer->buffer[0] = reportID;
buffer->currentDataSize = buffer->bufferSize;
buffer->state = HID_BUFFER_READ;
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
return;
}
}
static uint8 have_unread_data_in_hid_buffer() {
for (int i=0;i<MAX_HID_BUFFERS; i++) {
if (hidBuffers[i].buffer != NULL && hidBuffers[i].state == HID_BUFFER_UNREAD)
return 1;
}
return 0;
}
uint16_t usb_hid_get_data(uint8 type, uint8 reportID, uint8* out, uint8 poll) {
volatile HIDBuffer_t* buffer;
unsigned ret = 0;
buffer = usb_hid_find_buffer(type, reportID);
if (buffer == NULL)
return 0;
nvic_irq_disable(NVIC_USB_LP_CAN_RX0);
if (buffer->reportID == reportID && buffer->state != HID_BUFFER_EMPTY && !(poll && buffer->state == HID_BUFFER_READ)) {
if (buffer->bufferSize != buffer->currentDataSize) {
buffer->state = HID_BUFFER_EMPTY;
ret = 0;
}
else {
unsigned delta = reportID != 0;
if (out != NULL)
memcpy(out, (uint8*)buffer->buffer+delta, buffer->bufferSize-delta);
if (poll) {
buffer->state = HID_BUFFER_READ;
}
ret = buffer->bufferSize-delta;
}
}
if (! have_unread_data_in_hid_buffer() ) {
usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID);
}
nvic_irq_enable(NVIC_USB_LP_CAN_RX0);
return ret;
}
void usb_hid_clear_buffers(uint8 type) {
uint8 typeTest = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
for (int i=0; i<MAX_HID_BUFFERS; i++) {
if (( hidBuffers[i].mode & HID_BUFFER_MODE_OUTPUT ) == typeTest) {
hidBuffers[i].buffer = NULL;
}
}
}
uint8 usb_hid_add_buffer(uint8 type, volatile HIDBuffer_t* buf) {
if (type == HID_BUFFER_MODE_OUTPUT)
buf->mode |= HID_BUFFER_MODE_OUTPUT;
else
buf->mode &= ~HID_BUFFER_MODE_OUTPUT;
memset((void*)buf->buffer, 0, buf->bufferSize);
buf->buffer[0] = buf->reportID;
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(type, buf->reportID);
if (buffer != NULL) {
*buffer = *buf;
return 1;
}
else {
for (int i=0; i<MAX_HID_BUFFERS; i++) {
if (hidBuffers[i].buffer == NULL) {
hidBuffers[i] = *buf;
return 1;
}
}
return 0;
}
}
void usb_hid_set_buffers(uint8 type, volatile HIDBuffer_t* bufs, int n) {
uint8 typeMask = type == HID_REPORT_TYPE_OUTPUT ? HID_BUFFER_MODE_OUTPUT : 0;
usb_hid_clear_buffers(type);
for (int i=0; i<n; i++) {
bufs[i].mode &= ~HID_REPORT_TYPE_OUTPUT;
bufs[i].mode |= typeMask;
usb_hid_add_buffer(type, bufs+i);
}
currentHIDBuffer = NULL;
}
/* This function is non-blocking.
*
* It copies data from a user buffer into the USB peripheral TX
* buffer, and returns the number of bytes copied. */
uint32 usb_hid_tx(const uint8* buf, uint32 len)
{
if (len==0) return 0; // no data to send
uint32 head = hid_tx_head; // load volatile variable
uint32 tx_unsent = (head - hid_tx_tail) & HID_TX_BUFFER_SIZE_MASK;
// We can only put bytes in the buffer if there is place
if (len > (HID_TX_BUFFER_SIZE-tx_unsent-1) ) {
len = (HID_TX_BUFFER_SIZE-tx_unsent-1);
}
if (len==0) return 0; // buffer full
uint16 i;
// copy data from user buffer to USB Tx buffer
for (i=0; i<len; i++) {
hidBufferTx[head] = buf[i];
head = (head+1) & HID_TX_BUFFER_SIZE_MASK;
}
hid_tx_head = head; // store volatile variable
while(usbGenericTransmitting >= 0);
if (usbGenericTransmitting<0) {
hidDataTxCb(); // initiate data transmission
}
return len;
}
uint16 usb_hid_get_pending(void) {
return (hid_tx_head - hid_tx_tail) & HID_TX_BUFFER_SIZE_MASK;
}
static void hidDataTxCb(void)
{
uint32 tail = hid_tx_tail; // load volatile variable
uint32 tx_unsent = (hid_tx_head - tail) & HID_TX_BUFFER_SIZE_MASK;
if (tx_unsent==0) {
if ( (--usbGenericTransmitting)==0) goto flush_hid; // no more data to send
return; // it was already flushed, keep Tx endpoint disabled
}
usbGenericTransmitting = 1;
// We can only send up to USBHID_CDCACM_TX_EPSIZE bytes in the endpoint.
if (tx_unsent > USB_HID_TX_EPSIZE) {
tx_unsent = USB_HID_TX_EPSIZE;
}
// copy the bytes from USB Tx buffer to PMA buffer
uint32 *dst = usb_pma_ptr(usbHIDPart.endpoints[HID_ENDPOINT_TX].pmaAddress);
uint16 tmp = 0;
uint16 val;
unsigned i;
for (i = 0; i < tx_unsent; i++) {
val = hidBufferTx[tail];
tail = (tail + 1) & HID_TX_BUFFER_SIZE_MASK;
if (i&1) {
*dst++ = tmp | (val<<8);
} else {
tmp = val;
}
}
if ( tx_unsent&1 ) {
*dst = tmp;
}
hid_tx_tail = tail; // store volatile variable
flush_hid:
// enable Tx endpoint
usb_set_ep_tx_count(usbHIDPart.endpoints[HID_ENDPOINT_TX].address, tx_unsent);
usb_set_ep_tx_stat(usbHIDPart.endpoints[HID_ENDPOINT_TX].address, USB_EP_STAT_TX_VALID);
}
static void hidUSBReset(void) {
/* Reset the RX/TX state */
hid_tx_head = 0;
hid_tx_tail = 0;
currentHIDBuffer = NULL;
}
static uint8* HID_Set(uint16 length) {
if (currentHIDBuffer == NULL)
return NULL;
if (length ==0) {
if ( (0 == (currentHIDBuffer->mode & HID_BUFFER_MODE_NO_WAIT)) &&
currentHIDBuffer->state == HID_BUFFER_UNREAD &&
pInformation->Ctrl_Info.Usb_wOffset < pInformation->USBwLengths.w) {
pInformation->Ctrl_Info.Usb_wLength = 0xFFFF;
return NULL;
}
uint16 len = pInformation->USBwLengths.w;
if (len > currentHIDBuffer->bufferSize)
len = currentHIDBuffer->bufferSize;
currentHIDBuffer->currentDataSize = len;
currentHIDBuffer->state = HID_BUFFER_EMPTY;
if (pInformation->Ctrl_Info.Usb_wOffset < len) {
pInformation->Ctrl_Info.Usb_wLength = len - pInformation->Ctrl_Info.Usb_wOffset;
}
else {
pInformation->Ctrl_Info.Usb_wLength = 0;
}
return NULL;
}
if (pInformation->USBwLengths.w <= pInformation->Ctrl_Info.Usb_wOffset + pInformation->Ctrl_Info.PacketSize) {
currentHIDBuffer->state = HID_BUFFER_UNREAD;
}
return (uint8*)currentHIDBuffer->buffer + pInformation->Ctrl_Info.Usb_wOffset;
}
static uint8* HID_GetFeature(uint16 length) {
if (currentHIDBuffer == NULL)
return NULL;
unsigned wOffset = pInformation->Ctrl_Info.Usb_wOffset;
if (length == 0)
{
pInformation->Ctrl_Info.Usb_wLength = currentHIDBuffer->bufferSize - wOffset;
return NULL;
}
return (uint8*)currentHIDBuffer->buffer + wOffset;
}
static RESULT hidUSBDataSetup(uint8 request) {
uint8* (*CopyRoutine)(uint16) = 0;
if (pInformation->USBwIndex0 != HID_INTERFACE_NUMBER)
return USB_UNSUPPORT;
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
switch (request) {
case SET_REPORT:
if (pInformation->USBwValue1 == HID_REPORT_TYPE_FEATURE) {
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, pInformation->USBwValue0);
if (buffer == NULL) {
return USB_UNSUPPORT;
}
if (0 == (buffer->mode & HID_BUFFER_MODE_NO_WAIT) && buffer->state == HID_BUFFER_UNREAD) {
return USB_NOT_READY;
}
else
{
currentHIDBuffer = buffer;
CopyRoutine = HID_Set;
}
}
else if (pInformation->USBwValue1 == HID_REPORT_TYPE_OUTPUT) {
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_OUTPUT, pInformation->USBwValue0);
if (buffer == NULL) {
return USB_UNSUPPORT;
}
if (0 == (buffer->mode & HID_BUFFER_MODE_NO_WAIT) && buffer->state == HID_BUFFER_UNREAD) {
return USB_NOT_READY;
}
else
{
currentHIDBuffer = buffer;
CopyRoutine = HID_Set;
}
}
break;
case GET_REPORT:
if (pInformation->USBwValue1 == HID_REPORT_TYPE_FEATURE) {
volatile HIDBuffer_t* buffer = usb_hid_find_buffer(HID_REPORT_TYPE_FEATURE, pInformation->USBwValue0);
if (buffer == NULL || buffer->state == HID_BUFFER_EMPTY) {
return USB_UNSUPPORT;
}
currentHIDBuffer = buffer;
CopyRoutine = HID_GetFeature;
break;
}
default:
break;
}
}
if(Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)){
switch (request){
case GET_DESCRIPTOR:
if (pInformation->USBwValue1 == REPORT_DESCRIPTOR){
CopyRoutine = HID_GetReportDescriptor;
}
break;
case GET_PROTOCOL: // TODO: check for interface number?
CopyRoutine = HID_GetProtocolValue;
break;
}
}
if (CopyRoutine == NULL){
return USB_UNSUPPORT;
}
pInformation->Ctrl_Info.CopyData = CopyRoutine;
pInformation->Ctrl_Info.Usb_wOffset = 0;
(*CopyRoutine)(0);
return USB_SUCCESS;
}
static RESULT hidUSBNoDataSetup(uint8 request) {
if (pInformation->USBwIndex0 != HID_INTERFACE_NUMBER)
return USB_UNSUPPORT;
RESULT ret = USB_UNSUPPORT;
if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) {
switch(request) {
case SET_PROTOCOL:
ProtocolValue = pInformation->USBwValue0;
ret = USB_SUCCESS;
break;
}
}
return ret;
}
/*
static RESULT HID_SetProtocol(void){
uint8 wValue0 = pInformation->USBwValue0;
ProtocolValue = wValue0;
return USB_SUCCESS;
}
*/
static uint8* HID_GetProtocolValue(uint16 Length){
if (Length == 0){
pInformation->Ctrl_Info.Usb_wLength = 1;
return NULL;
} else {
return (uint8 *)(&ProtocolValue);
}
}
static uint8* HID_GetReportDescriptor(uint16 Length){
return Standard_GetDescriptorData(Length, &HID_Report_Descriptor);
}