summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--include/usbh_config.h6
-rw-r--r--include/usbh_driver_ac_midi.h48
-rw-r--r--src/demo.c24
-rw-r--r--src/usbh_driver_ac_midi.c391
-rw-r--r--src/usbh_driver_ac_midi_private.h52
6 files changed, 522 insertions, 0 deletions
diff --git a/README.md b/README.md
index ff631ad..f18ce09 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ Native device drivers (mostly for demonstration purposes):
- HUB
- Gamepad - XBox compatible Controller
- mouse (draft: only displays raw data)
+- USB MIDI devices (raw data + note on/off)
###Practical info
diff --git a/include/usbh_config.h b/include/usbh_config.h
index 1b60954..4fa38cb 100644
--- a/include/usbh_config.h
+++ b/include/usbh_config.h
@@ -43,6 +43,12 @@
#define USBH_HID_MOUSE_BUFFER (32)
+// MIDI
+// Maximal number of midi devices connected to whatever hub
+#define USBH_AC_MIDI_MAX_DEVICES (4)
+
+#define USBH_AC_MIDI_BUFFER (64)
+
// Gamepad XBOX
#define USBH_GP_XBOX_MAX_DEVICES (2)
diff --git a/include/usbh_driver_ac_midi.h b/include/usbh_driver_ac_midi.h
new file mode 100644
index 0000000..7b006a9
--- /dev/null
+++ b/include/usbh_driver_ac_midi.h
@@ -0,0 +1,48 @@
+/*
+ * 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_
+#define USBH_DRIVER_AC_MIDI_
+
+#include "usbh_hubbed.h"
+
+#include <stdint.h>
+
+BEGIN_DECLS
+
+struct _midi_config {
+ void (*read_callback)(int device_id, uint8_t *data);
+ void (*notify_connected)(int device_id);
+ void (*notify_disconnected)(int device_id);
+};
+typedef struct _midi_config midi_config_t;
+
+typedef void (*midi_write_callback_t)(uint8_t);
+
+void midi_driver_init(const midi_config_t *config);
+void usbh_midi_write(uint8_t device_id, const void *data, uint32_t length, midi_write_callback_t callback);
+
+extern const usbh_dev_driver_t usbh_midi_driver;
+
+END_DECLS
+
+#endif
diff --git a/src/demo.c b/src/demo.c
index ec37781..ae8da53 100644
--- a/src/demo.c
+++ b/src/demo.c
@@ -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