diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/demo.c | 24 | ||||
-rw-r--r-- | src/usbh_driver_ac_midi.c | 391 | ||||
-rw-r--r-- | src/usbh_driver_ac_midi_private.h | 52 |
3 files changed, 467 insertions, 0 deletions
@@ -26,6 +26,7 @@ #include "usbh_driver_hid_mouse.h" /// provides usb device driver Human Interface Device - type mouse
#include "usbh_driver_hub.h" /// provides usb full speed hub driver (Low speed devices on hub are not supported)
#include "usbh_driver_gp_xbox.h" /// provides usb device driver for Gamepad: Microsoft XBOX compatible Controller
+#include "usbh_driver_ac_midi.h" /// provides usb device driver for midi class devices
// STM32f407 compatible
#include <libopencm3/stm32/rcc.h>
@@ -117,6 +118,7 @@ static const usbh_dev_driver_t *device_drivers[] = { &usbh_hub_driver,
&usbh_hid_mouse_driver,
&usbh_gp_xbox_driver,
+ &usbh_midi_driver,
0
};
@@ -159,6 +161,27 @@ static const hid_mouse_config_t mouse_config = { .mouse_in_message_handler = &mouse_in_message_handler
};
+static void midi_in_message_handler(int device_id, uint8_t *data)
+{
+ (void)device_id;
+ switch (data[1]>>4) {
+ case 8:
+ LOG_PRINTF("\r\nNote Off");
+ break;
+
+ case 9:
+ LOG_PRINTF("\r\nNote On");
+ break;
+
+ default:
+ break;
+ }
+}
+
+const midi_config_t midi_config = {
+ .read_callback = &midi_in_message_handler
+};
+
int main(void)
{
clock_setup();
@@ -180,6 +203,7 @@ int main(void) hid_mouse_driver_init(&mouse_config);
hub_driver_init();
gp_xbox_driver_init(&gp_xbox_config);
+ midi_driver_init(&midi_config);
gpio_set(GPIOD, GPIO13);
diff --git a/src/usbh_driver_ac_midi.c b/src/usbh_driver_ac_midi.c new file mode 100644 index 0000000..65c7617 --- /dev/null +++ b/src/usbh_driver_ac_midi.c @@ -0,0 +1,391 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com> + * + * + * libusbhost is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "driver/usbh_device_driver.h" +#include "usbh_driver_ac_midi_private.h" +#include "usart_helpers.h" + +#include <libopencm3/usb/midi.h> +#include <libopencm3/usb/audio.h> +#include <libopencm3/usb/usbstd.h> + + +static void *midi_init(void *usbh_dev); +static bool midi_analyze_descriptor(void *drvdata, void *descriptor); +static void midi_poll(void *drvdata, uint32_t tflp); +static void midi_remove(void *drvdata); + +static midi_device_t midi_device[USBH_AC_MIDI_MAX_DEVICES]; +static const midi_config_t *midi_config = 0; +static bool initialized = false; + +static const usbh_dev_driver_info_t usbh_midi_driver_info = { + .deviceClass = -1, + .deviceSubClass = -1, + .deviceProtocol = -1, + .idVendor = -1, + .idProduct = -1, + .ifaceClass = 0x01, + .ifaceSubClass = 0x03, + .ifaceProtocol = -1, +}; + +const usbh_dev_driver_t usbh_midi_driver = { + .init = midi_init, + .analyze_descriptor = midi_analyze_descriptor, + .poll = midi_poll, + .remove = midi_remove, + .info = &usbh_midi_driver_info +}; + +void midi_driver_init(const midi_config_t *config) +{ + uint32_t i; + midi_config = config; + for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) { + midi_device[i].state = 0; + } + initialized = true; +} +/** + * + * + */ +static void *midi_init(void *usbh_dev) +{ + if (!midi_config || !initialized) { + LOG_PRINTF("\n%s/%d : driver not initialized\n", __FILE__, __LINE__); + return 0; + } + uint32_t i; + midi_device_t *drvdata = 0; + + // find free data space for midi device + for (i = 0; i < USBH_AC_MIDI_MAX_DEVICES; i++) { + if (midi_device[i].state == 0) { + drvdata = &midi_device[i]; + drvdata->device_id = i; + drvdata->endpoint_in_address = 0; + drvdata->endpoint_out_address = 0; + drvdata->endpoint_in_toggle = 0; + drvdata->endpoint_out_toggle = 0; + drvdata->usbh_device = usbh_dev; + drvdata->write_callback_user = 0; + drvdata->sending = false; + break; + } + } + + return drvdata; +} + +/** + * Returns true if all needed data are parsed + */ +static bool midi_analyze_descriptor(void *drvdata, void *descriptor) +{ + midi_device_t *midi = drvdata; + uint8_t desc_type = ((uint8_t *)descriptor)[1]; + switch (desc_type) { + case USB_DT_CONFIGURATION: + { + struct usb_config_descriptor *cfg = + (struct usb_config_descriptor*)descriptor; + midi->buffer[0] = cfg->bConfigurationValue; + } + break; + case USB_DT_DEVICE: + break; + case USB_DT_INTERFACE: + break; + case USB_DT_ENDPOINT: + { + struct usb_endpoint_descriptor *ep = + (struct usb_endpoint_descriptor*)descriptor; + if ((ep->bmAttributes&0x03) == USB_ENDPOINT_ATTR_BULK) { + uint8_t epaddr = ep->bEndpointAddress; + if (epaddr & (1<<7)) { + midi->endpoint_in_address = epaddr&0x7f; + if (ep->wMaxPacketSize < USBH_AC_MIDI_BUFFER) { + midi->endpoint_in_maxpacketsize = ep->wMaxPacketSize; + } else { + midi->endpoint_in_maxpacketsize = USBH_AC_MIDI_BUFFER; + } + } else { + midi->endpoint_out_address = epaddr; + midi->endpoint_out_maxpacketsize = ep->wMaxPacketSize; + } + + if (midi->endpoint_in_address && midi->endpoint_out_address) { + midi->state = 1; + return true; + } + } + } + break; + + case USB_AUDIO_DT_CS_ENDPOINT: + { + struct usb_midi_in_jack_descriptor *midi_in_jack_desc = + (struct usb_midi_in_jack_descriptor *) descriptor; + (void)midi_in_jack_desc; + } + break; + // TODO Class Specific descriptors + default: + break; + } + return false; +} + +static void midi_in_message(midi_device_t *midi, const uint8_t datalen) +{ + uint8_t i = 0; + if (midi_config->read_callback) { + for (i = 0; i < datalen; i += 4) { + +// uint8_t cable_number = (midi->buffer[i] & 0xf0) >> 4; + uint8_t code_id = midi->buffer[i]&0xf; + + uint8_t *ptrdata = &midi->buffer[i]; + if (code_id < 2) { + continue; + } + midi_config->read_callback(midi->device_id, ptrdata); + } + } +} + +static void event(usbh_device_t *dev, usbh_packet_callback_data_t status) +{ + midi_device_t *midi = (midi_device_t *)dev->drvdata; + switch (midi->state) { + case 26: + { + switch (status.status) { + case USBH_PACKET_CALLBACK_STATUS_OK: + midi_in_message(midi, midi->endpoint_in_maxpacketsize); + midi->state = 25; + break; + case USBH_PACKET_CALLBACK_STATUS_ERRSIZ: + midi_in_message(midi, status.transferred_length); + midi->state = 25; + break; + case USBH_PACKET_CALLBACK_STATUS_EFATAL: + case USBH_PACKET_CALLBACK_STATUS_EAGAIN: + LOG_PRINTF("FATAL ERROR, MIDI DRIVER DEAD \n"); + //~ dev->drv->remove(); + midi->state = 0; + break; + } + } + break; + + case 102: + { + midi->state = 101; + LOG_PRINTF("\n CAN'T TOUCH THIS... ignoring data\n"); + } + break; + case 2: + { + LOG_PRINTF("|empty packet read|"); + if (status.status == USBH_PACKET_CALLBACK_STATUS_OK) { + midi->state++; + device_xfer_control_read(0, 0, event, dev); + } + } + break; + case 3: // Configured + { + if (status.status == USBH_PACKET_CALLBACK_STATUS_OK) { + midi->state = 100; + + midi->endpoint_in_toggle = 0; + LOG_PRINTF("\nMIDI CONFIGURED\n"); + + // Notify user + if (midi_config->notify_connected) { + midi_config->notify_connected(midi->device_id); + } + } + } + break; + default: + break; + } +} + + +static void read_midi_in(void *drvdata, const uint8_t nextstate) +{ + midi_device_t *midi = drvdata; + usbh_packet_t packet; + + packet.address = midi->usbh_device->address; + packet.data = &midi->buffer[0]; + packet.datalen = midi->endpoint_in_maxpacketsize; + packet.endpoint_address = midi->endpoint_in_address; + packet.endpoint_size_max = midi->endpoint_in_maxpacketsize; + packet.endpoint_type = USBH_EPTYP_BULK; + packet.speed = midi->usbh_device->speed; + packet.callback = event; + packet.callback_arg = midi->usbh_device; + packet.toggle = &midi->endpoint_in_toggle; + + midi->state = nextstate; + usbh_read(midi->usbh_device,&packet); +} + +/** + * + * @param t_us global time us + */ +static void midi_poll(void *drvdata, uint32_t t_us) +{ + (void)drvdata; + + midi_device_t *midi = drvdata; + usbh_device_t *dev = midi->usbh_device; + switch (midi->state) { + + /// Upon configuration, some controllers send additional error data + /// case 100, 101, 102 cares for ignoring those data + case 100: + { + midi->time_us_config = t_us; + midi->state = 101; + } + break; + case 101: + { + read_midi_in(drvdata, 102); + } + break; + case 102: + { + // if elapsed MIDI initial delay microseconds + if (t_us - midi->time_us_config > MIDI_INITIAL_DELAY) { + midi->state = 26; + } + } + break; + case 25: + { + read_midi_in(drvdata, 26); + } + break; + + case 1: + { + //~ LOG_PRINTF("CFGVAL: %d\n", dev->config_val); + struct usb_setup_data setup_data; + + setup_data.bmRequestType = 0b00000000; + setup_data.bRequest = USB_REQ_SET_CONFIGURATION; + setup_data.wValue = midi->buffer[0]; + setup_data.wIndex = 0; + setup_data.wLength = 0; + + midi->state++; + + device_xfer_control_write(&setup_data, sizeof(setup_data), event, dev); + } + break; + } +} + +// don't call directly +static void write_callback(usbh_device_t *dev, usbh_packet_callback_data_t status) +{ + (void)status; + midi_device_t *midi = (midi_device_t *)dev->drvdata; + + if (midi->sending) { + midi->sending = false; + const midi_write_callback_t callback = midi->write_callback_user; + if (!callback) { + return; + } + + if (status.status & USBH_PACKET_CALLBACK_STATUS_OK) { + callback(midi->write_packet.datalen); + } else { + if (status.status & USBH_PACKET_CALLBACK_STATUS_ERRSIZ) { + const uint32_t length = status.transferred_length; + callback(length); + } else { + callback(0); + } + } + } +} + +void usbh_midi_write(uint8_t device_id, const void *data, uint32_t length, midi_write_callback_t callback) +{ + // bad device_id handling + if (device_id >= USBH_AC_MIDI_MAX_DEVICES) { + return; + } + + midi_device_t *midi = &midi_device[device_id]; + + // device with provided device_id is not alive + if (midi->state == 0) { + return; + } + + usbh_device_t *dev = midi->usbh_device; + if (midi->endpoint_out_address == 0) { + return; + } + + midi->sending = true; + midi->write_callback_user = callback; + + midi->write_packet.data = (void*)data; // it is safe cast since we are writing and function usbh_write cannot modify data + midi->write_packet.datalen = length; + midi->write_packet.address = dev->address; + midi->write_packet.endpoint_address = midi->endpoint_out_address; + midi->write_packet.endpoint_size_max = midi->endpoint_out_maxpacketsize; + midi->write_packet.endpoint_type = USBH_EPTYP_BULK; + midi->write_packet.speed = dev->speed; + midi->write_packet.callback = write_callback; + midi->write_packet.callback_arg = midi->usbh_device; + midi->write_packet.toggle = &midi->endpoint_out_toggle; + + + usbh_write(dev, &midi->write_packet); +} + +static void midi_remove(void *drvdata) +{ + midi_device_t *midi = drvdata; + + if (midi_config->notify_disconnected) { + midi_config->notify_disconnected(midi->device_id); + } + + midi->state = 0; + midi->endpoint_in_address = 0; + midi->endpoint_out_address = 0; +} diff --git a/src/usbh_driver_ac_midi_private.h b/src/usbh_driver_ac_midi_private.h new file mode 100644 index 0000000..b92ee97 --- /dev/null +++ b/src/usbh_driver_ac_midi_private.h @@ -0,0 +1,52 @@ +/* + * This file is part of the libusbhost library + * hosted at http://github.com/libusbhost/libusbhost + * + * Copyright (C) 2016 Amir Hammad <amir.hammad@hotmail.com> + * + * + * libusbhost is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef USBH_DRIVER_AC_MIDI_PRIVATE_ +#define USBH_DRIVER_AC_MIDI_PRIVATE_ + +#include "driver/usbh_device_driver.h" +#include "usbh_driver_ac_midi.h" + +#include <stdint.h> + + +#define MIDI_INITIAL_DELAY (100000) + +struct _midi_device { + usbh_device_t *usbh_device; + uint8_t buffer[USBH_AC_MIDI_BUFFER]; + uint16_t endpoint_in_maxpacketsize; + uint16_t endpoint_out_maxpacketsize; + uint8_t endpoint_in_address; + uint8_t endpoint_out_address; + uint8_t state; + uint8_t endpoint_in_toggle; + uint8_t endpoint_out_toggle; + uint8_t device_id; + bool sending; + midi_write_callback_t write_callback_user; + usbh_packet_t write_packet; + // Timestamp at sending config command + uint32_t time_us_config; +}; +typedef struct _midi_device midi_device_t; +#endif |