diff options
| -rw-r--r-- | libmaple/include/libmaple/usb_midi_descr.h | 150 | ||||
| -rw-r--r-- | libmaple/include/libmaple/usb_midi_device.h | 104 | ||||
| -rw-r--r-- | libmaple/usb/stm32f1/usb_midi_descr.c | 308 | ||||
| -rw-r--r-- | libmaple/usb/stm32f1/usb_midi_device.c | 491 | ||||
| -rw-r--r-- | wirish/include/wirish/usb_midi.h | 66 | ||||
| -rw-r--r-- | wirish/usb_midi.cpp | 139 |
6 files changed, 1258 insertions, 0 deletions
diff --git a/libmaple/include/libmaple/usb_midi_descr.h b/libmaple/include/libmaple/usb_midi_descr.h new file mode 100644 index 0000000..023b034 --- /dev/null +++ b/libmaple/include/libmaple/usb_midi_descr.h @@ -0,0 +1,150 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs LLC. + * Copyright (c) 2013 Magnus Lundin. + * + * 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/include/libmaple/usb_midi_descr.h + * @brief USB MIDI Descriptor support + * + * IMPORTANT: this API is unstable, and may change without notice. + */ + +#ifndef _LIBMAPLE_USB_MIDI_DESCR_H_ +#define _LIBMAPLE_USB_MIDI_DESCR_H_ + +#include <libmaple/libmaple_types.h> +#include <libmaple/usb.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * USB MIDI Requests + */ + +/* + * Descriptors, etc. + */ +#define USB_DESCRIPTOR_TYPE_CS_INTERFACE 0x24 +#define USB_DESCRIPTOR_TYPE_CS_ENDPOINT 0x25 + + +#define USB_DEVICE_CLASS_UNDEFINED 0x00 +#define USB_DEVICE_CLASS_CDC 0x02 +#define USB_DEVICE_SUBCLASS_UNDEFINED 0x00 + +#define USB_INTERFACE_CLASS_AUDIO 0x01 +#define USB_INTERFACE_SUBCLASS_UNDEFINED 0x00 +#define USB_INTERFACE_AUDIOCONTROL 0x01 +#define USB_INTERFACE_AUDIOSTREAMING 0x02 +#define USB_INTERFACE_MIDISTREAMING 0x03 + +/* MIDI Streaming class specific interfaces */ +#define MIDI_IN_JACK 0x02 +#define MIDI_OUT_JACK 0x03 + +#define MIDI_JACK_EMBEDDED 0x01 +#define MIDI_JACK_EXTERNAL 0x02 + + +#define USB_MIDI_DECLARE_DEV_DESC(vid, pid) \ +{ \ +.bLength = sizeof(usb_descriptor_device), \ +.bDescriptorType = USB_DESCRIPTOR_TYPE_DEVICE, \ +.bcdUSB = 0x0110, \ +.bDeviceClass = USB_DEVICE_CLASS_UNDEFINED, \ +.bDeviceSubClass = USB_DEVICE_SUBCLASS_UNDEFINED, \ +.bDeviceProtocol = 0x00, \ +.bMaxPacketSize0 = 0x40, \ +.idVendor = vid, \ +.idProduct = pid, \ +.bcdDevice = 0x0200, \ +.iManufacturer = 0x01, \ +.iProduct = 0x02, \ +.iSerialNumber = 0x00, \ +.bNumConfigurations = 0x01, \ +} + +#define AC_CS_INTERFACE_DESCRIPTOR_SIZE(DataSize) (8 + DataSize) +#define AC_CS_INTERFACE_DESCRIPTOR(DataSize) \ + struct { \ + uint8 bLength; \ + uint8 bDescriptorType; \ + uint8 SubType; \ + uint16 bcdADC; \ + uint16 wTotalLength; \ + uint8 bInCollection; \ + uint8 baInterfaceNr[DataSize]; \ + } __packed + +typedef struct { + uint8 bLength; + uint8 bDescriptorType; + uint8 SubType; + uint16 bcdADC; + uint16 wTotalLength; + } __packed MS_CS_INTERFACE_DESCRIPTOR; + +typedef struct { + uint8 bLength; + uint8 bDescriptorType; + uint8 SubType; + uint8 bJackType; + uint8 bJackId; + uint8 iJack; + } __packed MIDI_IN_JACK_DESCRIPTOR; + +#define MIDI_OUT_JACK_DESCRIPTOR_SIZE(DataSize) (7 + 2*DataSize) +#define MIDI_OUT_JACK_DESCRIPTOR(DataSize) \ + struct { \ + uint8 bLength; \ + uint8 bDescriptorType; \ + uint8 SubType; \ + uint8 bJackType; \ + uint8 bJackId; \ + uint8 bNrInputPins; \ + uint8 baSourceId[DataSize]; \ + uint8 baSourcePin[DataSize]; \ + uint8 iJack; \ + } __packed + + +#define MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(DataSize) (4 + DataSize) +#define MS_CS_BULK_ENDPOINT_DESCRIPTOR(DataSize) \ + struct { \ + uint8 bLength; \ + uint8 bDescriptorType; \ + uint8 SubType; \ + uint8 bNumEmbMIDIJack; \ + uint8 baAssocJackID[DataSize]; \ + } __packed + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmaple/include/libmaple/usb_midi_device.h b/libmaple/include/libmaple/usb_midi_device.h new file mode 100644 index 0000000..3d48bd7 --- /dev/null +++ b/libmaple/include/libmaple/usb_midi_device.h @@ -0,0 +1,104 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs LLC. + * Copyright (c) 2013 Magnus Lundin. + * + * 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/include/libmaple/usb_midi_device.h + * @brief USB MIDI support, device configuration + * + * IMPORTANT: this API is unstable, and may change without notice. + */ + +#ifndef _LIBMAPLE_USB_MIDI_DEVICE_H_ +#define _LIBMAPLE_USB_MIDI_DEVICE_H_ + +#include <libmaple/libmaple_types.h> +#include <libmaple/gpio.h> +#include <libmaple/usb.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef union { + uint8 byte[4]; + uint32 data; +} USB_MIDI_Event_Packet; + +/* + * USB MIDI Requests + */ + +/* + * USB Device configuration + */ + +/* FIXME move to Wirish */ + +#define LEAFLABS_ID_VENDOR 0x1EAF +#define MAPLE_ID_PRODUCT 0x0014 + +#define MAX_POWER (100 >> 1) + +/* + * Endpoint configuration + */ + +#define USB_MIDI_CTRL_ENDP 0 +#define USB_MIDI_CTRL_RX_ADDR 0x40 +#define USB_MIDI_CTRL_TX_ADDR 0x80 +#define USB_MIDI_CTRL_EPSIZE 0x40 + +#define USB_MIDI_TX_ENDP 1 +#define USB_MIDI_TX_ADDR 0xC0 +#define USB_MIDI_TX_EPSIZE 0x40 + +#define USB_MIDI_RX_ENDP 2 +#define USB_MIDI_RX_ADDR 0x100 +#define USB_MIDI_RX_EPSIZE 0x40 + +/* + * MIDI interface + */ + +void usb_midi_enable(gpio_dev*, uint8); +void usb_midi_disable(gpio_dev*, uint8); + +void usb_midi_putc(char ch); +uint32 usb_midi_tx(const uint8* buf, uint32 len); +uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len); +uint32 usb_midi_rx(uint8* buf, uint32 len); +uint32 usb_midi_peek(uint8* buf, uint32 len); + +uint32 usb_midi_data_available(void); /* in RX buffer */ +uint16 usb_midi_get_pending(void); +uint8 usb_midi_is_transmitting(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libmaple/usb/stm32f1/usb_midi_descr.c b/libmaple/usb/stm32f1/usb_midi_descr.c new file mode 100644 index 0000000..d476fd2 --- /dev/null +++ b/libmaple/usb/stm32f1/usb_midi_descr.c @@ -0,0 +1,308 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs LLC. + * Copyright (c) 2013 Magnus Lundin. + * + * 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_midi_descr.c + * @brief USB MIDI Class descriptor. + * + * This file uses no device specific code + * + */ + +#include <libmaple/usb_midi_device.h> +#include <libmaple/usb_midi_descr.h> + +/* Private headers */ +#include "usb_lib_globals.h" + +/* usb_lib headers */ +#include "usb_type.h" +#include "usb_core.h" +#include "usb_def.h" + +/* + * Descriptors + */ + +static const usb_descriptor_device usbMIDIDescriptor_Device = + USB_MIDI_DECLARE_DEV_DESC(LEAFLABS_ID_VENDOR, MAPLE_ID_PRODUCT); + +typedef struct { + usb_descriptor_config_header Config_Header; + /* Control Interface */ + usb_descriptor_interface AC_Interface; + AC_CS_INTERFACE_DESCRIPTOR(1) AC_CS_Interface; + /* Control Interface */ + usb_descriptor_interface MS_Interface; + MS_CS_INTERFACE_DESCRIPTOR MS_CS_Interface; + MIDI_IN_JACK_DESCRIPTOR MIDI_IN_JACK_1; + MIDI_IN_JACK_DESCRIPTOR MIDI_IN_JACK_2; + MIDI_OUT_JACK_DESCRIPTOR(1) MIDI_OUT_JACK_3; + MIDI_OUT_JACK_DESCRIPTOR(1) MIDI_OUT_JACK_4; + usb_descriptor_endpoint DataOutEndpoint; + MS_CS_BULK_ENDPOINT_DESCRIPTOR(1) MS_CS_DataOutEndpoint; + usb_descriptor_endpoint DataInEndpoint; + MS_CS_BULK_ENDPOINT_DESCRIPTOR(1) MS_CS_DataInEndpoint; +} __packed usb_descriptor_config; + +static const usb_descriptor_config usbMIDIDescriptor_Config = { + .Config_Header = { + .bLength = sizeof(usb_descriptor_config_header), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CONFIGURATION, + .wTotalLength = sizeof(usb_descriptor_config), + .bNumInterfaces = 0x02, + .bConfigurationValue = 0x01, + .iConfiguration = 0x00, + .bmAttributes = (USB_CONFIG_ATTR_BUSPOWERED | + USB_CONFIG_ATTR_SELF_POWERED), + .bMaxPower = MAX_POWER, + }, + + .AC_Interface = { + .bLength = sizeof(usb_descriptor_interface), + .bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x00, + .bInterfaceClass = USB_INTERFACE_CLASS_AUDIO, + .bInterfaceSubClass = USB_INTERFACE_AUDIOCONTROL, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00, + }, + + .AC_CS_Interface = { + .bLength = AC_CS_INTERFACE_DESCRIPTOR_SIZE(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = 0x01, + .bcdADC = 0x0100, + .wTotalLength = AC_CS_INTERFACE_DESCRIPTOR_SIZE(1), + .bInCollection = 0x01, + .baInterfaceNr = {0x01}, + }, + + .MS_Interface = { + .bLength = sizeof(usb_descriptor_interface), + .bDescriptorType = USB_DESCRIPTOR_TYPE_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = USB_INTERFACE_CLASS_AUDIO, + .bInterfaceSubClass = USB_INTERFACE_MIDISTREAMING, + .bInterfaceProtocol = 0x00, + .iInterface = 0x04, + }, + + .MS_CS_Interface = { + .bLength = sizeof(MS_CS_INTERFACE_DESCRIPTOR), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = 0x01, + .bcdADC = 0x0100, + .wTotalLength = sizeof(MS_CS_INTERFACE_DESCRIPTOR) + +sizeof(MIDI_IN_JACK_DESCRIPTOR) + +sizeof(MIDI_IN_JACK_DESCRIPTOR) + +MIDI_OUT_JACK_DESCRIPTOR_SIZE(1) + +MIDI_OUT_JACK_DESCRIPTOR_SIZE(1) + +sizeof(usb_descriptor_endpoint) + +MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1) + +sizeof(usb_descriptor_endpoint) + +MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1) + /* 0x41-4 */, + }, + + .MIDI_IN_JACK_1 = { + .bLength = sizeof(MIDI_IN_JACK_DESCRIPTOR), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = MIDI_IN_JACK, + .bJackType = MIDI_JACK_EMBEDDED, + .bJackId = 0x01, + .iJack = 0x05, + }, + + .MIDI_IN_JACK_2 = { + .bLength = sizeof(MIDI_IN_JACK_DESCRIPTOR), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = MIDI_IN_JACK, + .bJackType = MIDI_JACK_EXTERNAL, + .bJackId = 0x02, + .iJack = 0x00, + }, + + .MIDI_OUT_JACK_3 = { + .bLength = MIDI_OUT_JACK_DESCRIPTOR_SIZE(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = MIDI_OUT_JACK, + .bJackType = MIDI_JACK_EMBEDDED, + .bJackId = 0x03, + .bNrInputPins = 0x01, + .baSourceId = {0x02}, + .baSourcePin = {0x01}, + .iJack = 0x00, + }, + + .MIDI_OUT_JACK_4 = { + .bLength = MIDI_OUT_JACK_DESCRIPTOR_SIZE(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_INTERFACE, + .SubType = MIDI_OUT_JACK, + .bJackType = MIDI_JACK_EXTERNAL, + .bJackId = 0x04, + .bJackId = 0x03, + .bNrInputPins = 0x01, + .baSourceId = {0x01}, + .baSourcePin = {0x01}, + .iJack = 0x00, + }, + + .DataOutEndpoint = { + .bLength = sizeof(usb_descriptor_endpoint), + .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_OUT | + USB_MIDI_RX_ENDP), + .bmAttributes = USB_EP_TYPE_BULK, + .wMaxPacketSize = USB_MIDI_RX_EPSIZE, + .bInterval = 0x00, + }, + + .MS_CS_DataOutEndpoint = { + .bLength = MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_ENDPOINT, + .SubType = 0x01, + .bNumEmbMIDIJack = 0x01, + .baAssocJackID = {0x01}, + }, + + .DataInEndpoint = { + .bLength = sizeof(usb_descriptor_endpoint), + .bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT, + .bEndpointAddress = (USB_DESCRIPTOR_ENDPOINT_IN | USB_MIDI_TX_ENDP), + .bmAttributes = USB_EP_TYPE_BULK, + .wMaxPacketSize = USB_MIDI_TX_EPSIZE, + .bInterval = 0x00, + }, + + .MS_CS_DataInEndpoint = { + .bLength = MS_CS_BULK_ENDPOINT_DESCRIPTOR_SIZE(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_CS_ENDPOINT, + .SubType = 0x01, + .bNumEmbMIDIJack = 0x01, + .baAssocJackID = {0x03}, + }, + +}; + +/* + String Descriptors: + + we may choose to specify any or none of the following string + identifiers: + + iManufacturer: LeafLabs + iProduct: Maple + iSerialNumber: NONE + iConfiguration: NONE + iInterface(CCI): NONE + iInterface(DCI): NONE + +*/ + +/* Unicode language identifier: 0x0409 is US English */ +/* FIXME move to Wirish */ +static const usb_descriptor_string usbMIDIDescriptor_LangID = { + .bLength = USB_DESCRIPTOR_STRING_LEN(1), + .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING, + .bString = {0x09, 0x04}, +}; + +/* FIXME move to Wirish */ +static const usb_descriptor_string usbMIDIDescriptor_iManufacturer = { + .bLength = USB_DESCRIPTOR_STRING_LEN(8), + .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING, + .bString = {'L', 0, 'e', 0, 'a', 0, 'f', 0, + 'L', 0, 'a', 0, 'b', 0, 's', 0}, +}; + +/* FIXME move to Wirish */ +static const usb_descriptor_string usbMIDIDescriptor_iProduct = { + .bLength = USB_DESCRIPTOR_STRING_LEN(10), + .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING, + .bString = {'M', 0, 'a', 0, 'p', 0, 'l', 0, 'e', 0, ' ', 0, 'M', 0, 'I', 0, 'D', 0, 'I', 0}, +}; + +/* FIXME move to Wirish */ +static const usb_descriptor_string usbMIDIDescriptor_iInterface = { + .bLength = USB_DESCRIPTOR_STRING_LEN(4), + .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING, + .bString = {'M', 0, 'I', 0, 'D', 0, 'I', 0}, +}; + +/* FIXME move to Wirish */ +static const usb_descriptor_string usbMIDIDescriptor_iJack1 = { + .bLength = USB_DESCRIPTOR_STRING_LEN(5), + .bDescriptorType = USB_DESCRIPTOR_TYPE_STRING, + .bString = {'J', 0, 'a', 0, 'c', 0, 'k', 0, '1', 0}, +}; + + +static ONE_DESCRIPTOR Device_Descriptor = { + (uint8*)&usbMIDIDescriptor_Device, + sizeof(usb_descriptor_device) +}; + +static ONE_DESCRIPTOR Config_Descriptor = { + (uint8*)&usbMIDIDescriptor_Config, + sizeof(usb_descriptor_config) +}; + +#define N_STRING_DESCRIPTORS 5 +static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = { + {(uint8*)&usbMIDIDescriptor_LangID, USB_DESCRIPTOR_STRING_LEN(1)}, + {(uint8*)&usbMIDIDescriptor_iManufacturer,USB_DESCRIPTOR_STRING_LEN(8)}, + {(uint8*)&usbMIDIDescriptor_iProduct, USB_DESCRIPTOR_STRING_LEN(10)}, + {(uint8*)&usbMIDIDescriptor_iInterface, USB_DESCRIPTOR_STRING_LEN(4)}, + {(uint8*)&usbMIDIDescriptor_iJack1, USB_DESCRIPTOR_STRING_LEN(5)} +}; + +/* + * Funtions to access the descriptor structures + */ + +uint8* usbGetMidiDeviceDescriptor(uint16 length) { + return Standard_GetDescriptorData(length, &Device_Descriptor); +} + +uint8* usbGetMidiConfigDescriptor(uint16 length) { + return Standard_GetDescriptorData(length, &Config_Descriptor); +} + +uint8* usbGetMidiStringDescriptor(uint16 length) { + uint8 wValue0 = pInformation->USBwValue0; + + if (wValue0 > N_STRING_DESCRIPTORS) { + return NULL; + } + return Standard_GetDescriptorData(length, &String_Descriptor[wValue0]); +} + diff --git a/libmaple/usb/stm32f1/usb_midi_device.c b/libmaple/usb/stm32f1/usb_midi_device.c new file mode 100644 index 0000000..02a0ba2 --- /dev/null +++ b/libmaple/usb/stm32f1/usb_midi_device.c @@ -0,0 +1,491 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2011 LeafLabs LLC. + * Copyright (c) 2013 Magnus Lundin. + * + * 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_midi_device.c + * @brief USB MIDI. + * + * 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. + */ + +#define USETXBUFFER + +#include <libmaple/usb_midi_device.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" + +/****************************************************************************** + ****************************************************************************** + *** + *** HACK ALERT! FIXME FIXME FIXME FIXME! + *** + *** A bunch of LeafLabs-specific configuration lives in here for + *** now. This mess REALLY needs to get teased apart, with + *** appropriate pieces moved into Wirish. + *** + ****************************************************************************** + *****************************************************************************/ + +#if !(defined(BOARD_maple) || defined(BOARD_maple_RET6) || \ + defined(BOARD_maple_mini) || defined(BOARD_maple_native)) +#warning USB MIDI relies on LeafLabs board-specific configuration.\ + You may have problems on non-LeafLabs boards. +#endif + +/* Descriptor handling functions from usb_midi_descr.c */ +extern uint8* usbGetMidiDeviceDescriptor(uint16 length); +extern uint8* usbGetMidiConfigDescriptor(uint16 length); +extern uint8* usbGetMidiStringDescriptor(uint16 length); + +/* interrupt callbacks */ +static void midiDataTxCb(void); +static void midiDataRxCb(void); + +static void usbInit(void); +static void usbReset(void); +static RESULT usbDataSetup(uint8 request); +static RESULT usbNoDataSetup(uint8 request); +static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting); +static void usbSetConfiguration(void); +static void usbSetDeviceAddress(void); + + +/* I/O state */ + +/* Received data */ +static volatile uint8 midiBufferRx[USB_MIDI_RX_EPSIZE]; +/* Read index into midiBufferRx */ +static volatile uint32 rx_offset = 0; +/* Transmit data */ +static volatile uint8 midiBufferTx[USB_MIDI_TX_EPSIZE]; +/* Write index into midiBufferTx */ +static volatile uint32 tx_offset = 0; +/* 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; +/* Number of unread bytes */ +static volatile uint32 n_unread_bytes = 0; + +/* + * Endpoint callbacks + */ + +static void (*ep_int_in[7])(void) = + {midiDataTxCb, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process}; + +static void (*ep_int_out[7])(void) = + {NOP_Process, + midiDataRxCb, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process}; + +/* + * Globals required by usb_lib/ + * + * Mark these weak so they can be overriden to implement other USB + * functionality. + */ + +#define NUM_ENDPTS 0x04 +__weak DEVICE Device_Table = { + .Total_Endpoint = NUM_ENDPTS, + .Total_Configuration = 1 +}; + +#define MAX_PACKET_SIZE 0x40 /* 64B, maximum for USB FS Devices */ +__weak DEVICE_PROP Device_Property = { + .Init = usbInit, + .Reset = usbReset, + .Process_Status_IN = NOP_Process, + .Process_Status_OUT = NOP_Process, + .Class_Data_Setup = usbDataSetup, + .Class_NoData_Setup = usbNoDataSetup, + .Class_Get_Interface_Setting = usbGetInterfaceSetting, + .GetDeviceDescriptor = usbGetMidiDeviceDescriptor, + .GetConfigDescriptor = usbGetMidiConfigDescriptor, + .GetStringDescriptor = usbGetMidiStringDescriptor, + .RxEP_buffer = NULL, + .MaxPacketSize = MAX_PACKET_SIZE +}; + +__weak USER_STANDARD_REQUESTS User_Standard_Requests = { + .User_GetConfiguration = NOP_Process, + .User_SetConfiguration = usbSetConfiguration, + .User_GetInterface = NOP_Process, + .User_SetInterface = NOP_Process, + .User_GetStatus = NOP_Process, + .User_ClearFeature = NOP_Process, + .User_SetEndPointFeature = NOP_Process, + .User_SetDeviceFeature = NOP_Process, + .User_SetDeviceAddress = usbSetDeviceAddress +}; + +/* + * MIDI interface + */ + +void usb_midi_enable(gpio_dev *disc_dev, uint8 disc_bit) { + /* Present ourselves to the host. Writing 0 to "disc" pin must + * pull USB_DP pin up while leaving USB_DM pulled down by the + * transceiver. See USB 2.0 spec, section 7.1.7.3. */ + gpio_set_mode(disc_dev, disc_bit, GPIO_OUTPUT_PP); + gpio_write_bit(disc_dev, disc_bit, 0); + + /* Initialize the USB peripheral. */ + usb_init_usblib(USBLIB, ep_int_in, ep_int_out); +} + +void usb_midi_disable(gpio_dev *disc_dev, uint8 disc_bit) { + /* Turn off the interrupt and signal disconnect (see e.g. USB 2.0 + * spec, section 7.1.7.3). */ + nvic_irq_disable(NVIC_USB_LP_CAN_RX0); + gpio_write_bit(disc_dev, disc_bit, 1); +} + +void usb_midi_putc(char ch) { + while (!usb_midi_tx_buffered((uint8*)&ch, 1)) + ; +/* + while (!usb_midi_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 usb_midi_tx(const uint8* buf, uint32 len) { + /* Last transmission hasn't finished, so abort. */ + if (usb_midi_is_transmitting()) { + /* Copy to TxBuffer */ + + return 0; /* return len */ + } + + /* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */ + if (len > USB_MIDI_TX_EPSIZE) { + len = USB_MIDI_TX_EPSIZE; + } + + /* Queue bytes for sending. */ + if (len) { + usb_copy_to_pma(buf, len, USB_MIDI_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_MIDI_TX_ENDP, len); + n_unsent_bytes = len; + transmitting = 1; + usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID); + + return len; +} + +/* + * Theese functions replaces EP double buffering: + * + * usb_midi_tx_buffered(const uint8* buf, uint32 len) + * usb_midi_tx_send_buffer() + * + * TODO: Use the modules EP double buffering instead + * + * */ +uint32 usb_midi_tx_buffered(const uint8* buf, uint32 len) { + if (usb_midi_is_transmitting()) { + uint8 count; + /* Copy to TxBuffer */ + /* Disable USB EP interrupts */ + USB_BASE->CNTR = USB_ISR_MSK&~USB_CNTR_CTRM; + if (len>(USB_MIDI_TX_EPSIZE-tx_offset)) len = USB_MIDI_TX_EPSIZE-tx_offset; + for (count=0;count<len;count++) + { + midiBufferTx[tx_offset+count]=buf[count]; + } + tx_offset += len; + /* Reenable USB EP interrupts */ + USB_BASE->CNTR = USB_ISR_MSK&~USB_CNTR_CTRM; + return len; /* return len */ + } + + /* We can only put USB_MIDI_TX_EPSIZE bytes in the buffer. */ + if (len > USB_MIDI_TX_EPSIZE) { + len = USB_MIDI_TX_EPSIZE; + } + + /* Queue bytes for sending. */ + if (len) { + usb_copy_to_pma(buf, len, USB_MIDI_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_MIDI_TX_ENDP, len); + n_unsent_bytes = len; + transmitting = 1; + usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID); + + return len; +} + +#ifdef USETXBUFFER +uint32 usb_midi_tx_send_buffer() { + if (usb_midi_is_transmitting()) { + return 0; + } + /* Queue bytes for sending. */ + if (tx_offset) { + usb_copy_to_pma((const uint8 *)midiBufferTx, tx_offset, USB_MIDI_TX_ADDR); + } + else return 0; + // 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_MIDI_TX_ENDP, tx_offset); + n_unsent_bytes = tx_offset; + tx_offset = 0; + transmitting = 1; + usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_VALID); + + return n_unsent_bytes; +} +#endif + +uint32 usb_midi_data_available(void) { + return n_unread_bytes; +} + +uint8 usb_midi_is_transmitting(void) { + return transmitting; +} + +uint16 usb_midi_get_pending(void) { + return n_unsent_bytes; +} + +/* Nonblocking byte receive. + * + * Copies up to len bytes from our private data buffer (*NOT* the PMA) + * into buf and deq's the FIFO. */ +uint32 usb_midi_rx(uint8* buf, uint32 len) { + /* Copy bytes to buffer. */ + uint32 n_copied = usb_midi_peek(buf, len); + + /* Mark bytes as read. */ + n_unread_bytes -= n_copied; + rx_offset += n_copied; + + /* If all bytes have been read, re-enable the RX endpoint, which + * was set to NAK when the current batch of bytes was received. */ + if (n_unread_bytes == 0) { + usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE); + usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID); + rx_offset = 0; + } + + return n_copied; +} + +/* Nonblocking byte lookahead. + * + * Looks at unread bytes without marking them as read. */ +uint32 usb_midi_peek(uint8* buf, uint32 len) { + int i; + + if (len > n_unread_bytes) { + len = n_unread_bytes; + } + + for (i = 0; i < len; i++) { + buf[i] = midiBufferRx[i + rx_offset]; + } + + return len; +} + +/* + * Callbacks + */ + +static void midiDataTxCb(void) { + n_unsent_bytes = 0; + transmitting = 0; +#ifdef USETXBUFFER + usb_midi_tx_send_buffer(); +#endif +} + +static void midiDataRxCb(void) { + usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_NAK); + n_unread_bytes = usb_get_ep_rx_count(USB_MIDI_RX_ENDP); + /* This copy won't overwrite unread bytes, since we've set the RX + * endpoint to NAK, and will only set it to VALID when all bytes + * have been read. */ + usb_copy_from_pma((uint8*)midiBufferRx, n_unread_bytes, + USB_MIDI_RX_ADDR); + + + if (n_unread_bytes == 0) { + usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE); + usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID); + rx_offset = 0; + } + +} + +/* NOTE: Nothing specific to this device class in this function, but depenedent on the device, move to usb_lib or stm32fxx*/ +static void usbInit(void) { + pInformation->Current_Configuration = 0; + + USB_BASE->CNTR = USB_CNTR_FRES; + + USBLIB->irq_mask = 0; + USB_BASE->CNTR = USBLIB->irq_mask; + USB_BASE->ISTR = 0; + USBLIB->irq_mask = USB_CNTR_RESETM | USB_CNTR_SUSPM | USB_CNTR_WKUPM; + USB_BASE->CNTR = USBLIB->irq_mask; + + USB_BASE->ISTR = 0; + USBLIB->irq_mask = USB_ISR_MSK; + USB_BASE->CNTR = USBLIB->irq_mask; + + nvic_irq_enable(NVIC_USB_LP_CAN_RX0); + USBLIB->state = USB_UNCONNECTED; +} + +#define BTABLE_ADDRESS 0x00 +static void usbReset(void) { + pInformation->Current_Configuration = 0; + + /* current feature is current bmAttributes */ + pInformation->Current_Feature = (USB_CONFIG_ATTR_BUSPOWERED | + USB_CONFIG_ATTR_SELF_POWERED); + + USB_BASE->BTABLE = BTABLE_ADDRESS; + + /* setup control endpoint 0 */ + usb_set_ep_type(USB_EP0, USB_EP_EP_TYPE_CONTROL); + usb_set_ep_tx_stat(USB_EP0, USB_EP_STAT_TX_STALL); + usb_set_ep_rx_addr(USB_EP0, USB_MIDI_CTRL_RX_ADDR); + usb_set_ep_tx_addr(USB_EP0, USB_MIDI_CTRL_TX_ADDR); + usb_clear_status_out(USB_EP0); + + usb_set_ep_rx_count(USB_EP0, pProperty->MaxPacketSize); + usb_set_ep_rx_stat(USB_EP0, USB_EP_STAT_RX_VALID); + + /* TODO figure out differences in style between RX/TX EP setup */ + + /* set up data endpoint OUT (RX) */ + usb_set_ep_type(USB_MIDI_RX_ENDP, USB_EP_EP_TYPE_BULK); + usb_set_ep_rx_addr(USB_MIDI_RX_ENDP, USB_MIDI_RX_ADDR); + usb_set_ep_rx_count(USB_MIDI_RX_ENDP, USB_MIDI_RX_EPSIZE); + usb_set_ep_rx_stat(USB_MIDI_RX_ENDP, USB_EP_STAT_RX_VALID); + + /* set up data endpoint IN (TX) */ + usb_set_ep_type(USB_MIDI_TX_ENDP, USB_EP_EP_TYPE_BULK); + usb_set_ep_tx_addr(USB_MIDI_TX_ENDP, USB_MIDI_TX_ADDR); + usb_set_ep_tx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_TX_NAK); + usb_set_ep_rx_stat(USB_MIDI_TX_ENDP, USB_EP_STAT_RX_DISABLED); + + USBLIB->state = USB_ATTACHED; + SetDeviceAddress(0); + + /* Reset the RX/TX state */ + n_unread_bytes = 0; + n_unsent_bytes = 0; + rx_offset = 0; + tx_offset = 0; +} + +static RESULT usbDataSetup(uint8 request) { + uint8* (*CopyRoutine)(uint16) = 0; + + if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { + + } + + if (CopyRoutine == NULL) { + return USB_UNSUPPORT; + } + + pInformation->Ctrl_Info.CopyData = CopyRoutine; + pInformation->Ctrl_Info.Usb_wOffset = 0; + (*CopyRoutine)(0); + return USB_SUCCESS; +} + +static RESULT usbNoDataSetup(uint8 request) { + RESULT ret = USB_UNSUPPORT; + + if (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { + } + return ret; +} + +static RESULT usbGetInterfaceSetting(uint8 interface, uint8 alt_setting) { + if (alt_setting > 0) { + return USB_UNSUPPORT; + } else if (interface > 1) { + return USB_UNSUPPORT; + } + + return USB_SUCCESS; +} + +static void usbSetConfiguration(void) { + if (pInformation->Current_Configuration != 0) { + USBLIB->state = USB_CONFIGURED; + } +} + +static void usbSetDeviceAddress(void) { + USBLIB->state = USB_ADDRESSED; +} diff --git a/wirish/include/wirish/usb_midi.h b/wirish/include/wirish/usb_midi.h new file mode 100644 index 0000000..3ee1989 --- /dev/null +++ b/wirish/include/wirish/usb_midi.h @@ -0,0 +1,66 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * Copyright (c) 2013 Magnus Lundin. + * + * 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. + *****************************************************************************/ + +/** + * @brief Wirish USB MIDI port (MidiUSB). + */ + +#ifndef _WIRISH_USB_MIDI_H_ +#define _WIRISH_USB_MIDI_H_ + +#include <wirish/Print.h> +#include <wirish/boards.h> + +/** + * @brief Virtual serial terminal. + */ +class USBMidi : public Print { +public: + USBMidi(void); + + void begin(void); + void end(void); + + uint32 available(void); + + uint32 read(void *buf, uint32 len); + uint8 read(void); + + void write(uint8); + void write(const char *str); + void write(const void*, uint32); + + uint8 isConnected(); + uint8 pending(); +}; + +#if BOARD_HAVE_MIDIUSB +extern USBMidi MidiUSB; +#endif + +#endif + diff --git a/wirish/usb_midi.cpp b/wirish/usb_midi.cpp new file mode 100644 index 0000000..5a48efb --- /dev/null +++ b/wirish/usb_midi.cpp @@ -0,0 +1,139 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Perry Hung. + * Copyright (c) 2013 Magnus Lundin. + * + * 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. + *****************************************************************************/ + +/** + * @brief USB MIDI device + */ + +#include <wirish/usb_midi.h> + +#include <string.h> +#include <stdint.h> + +#include <libmaple/nvic.h> +#include <libmaple/usb_midi_device.h> +#include <libmaple/usb.h> + +#include <wirish/wirish.h> + +/* + * USBMidi interface + */ + +#define USB_TIMEOUT 50 + +USBMidi::USBMidi(void) { +#if !BOARD_HAVE_MIDIUSB + ASSERT(0); +#endif +} + +void USBMidi::begin(void) { +#if BOARD_HAVE_MIDIUSB + usb_midi_enable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); +#endif +} + +void USBMidi::end(void) { +#if BOARD_HAVE_MIDIUSB + usb_midi_disable(BOARD_USB_DISC_DEV, BOARD_USB_DISC_BIT); +#endif +} + +void USBMidi::write(uint8 ch) { + this->write(&ch, 1); +} + +void USBMidi::write(const char *str) { + this->write(str, strlen(str)); +} + +void USBMidi::write(const void *buf, uint32 len) { + if (!this->isConnected() || !buf) { + return; + } + + uint32 txed = 0; + uint32 old_txed = 0; + uint32 start = millis(); + + uint32 sent = 0; + + while (txed < len && (millis() - start < USB_TIMEOUT)) { +// sent = usb_midi_tx_buffered((const uint8*)buf + txed, len - txed); + sent = usb_midi_tx((const uint8*)buf + txed, len - txed); + txed += sent; + if (old_txed != txed) { + start = millis(); + } + old_txed = txed; + } + + + if (sent == USB_MIDI_TX_EPSIZE) { + while (usb_midi_is_transmitting() != 0) { + } + /* flush out to avoid having the pc wait for more data */ + usb_midi_tx(NULL, 0); + } +} + +uint32 USBMidi::available(void) { + return usb_midi_data_available(); +} + +uint32 USBMidi::read(void *buf, uint32 len) { + if (!buf) { + return 0; + } + + uint32 rxed = 0; + while (rxed < len) { + rxed += usb_midi_rx((uint8*)buf + rxed, len - rxed); + } + + return rxed; +} + +/* Blocks forever until 1 byte is received */ +uint8 USBMidi::read(void) { + uint8 b; + this->read(&b, 1); + return b; +} + +uint8 USBMidi::pending(void) { + return usb_midi_get_pending(); +} + +uint8 USBMidi::isConnected(void) { + return usb_is_connected(USBLIB) && usb_is_configured(USBLIB); +} + +#if BOARD_HAVE_MIDIUSB +USBMidi MidiUSB; +#endif |
