Merged usb midi code into current libusbmaster
+ merge fixes Signed-off-by: Amir Hammad <amir.hammad@hotmail.com>
This commit is contained in:
parent
4cbbb39624
commit
2963113e8e
6 changed files with 522 additions and 0 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
48
include/usbh_driver_ac_midi.h
Normal file
48
include/usbh_driver_ac_midi.h
Normal file
|
|
@ -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
|
||||
24
src/demo.c
24
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);
|
||||
|
||||
|
|
|
|||
391
src/usbh_driver_ac_midi.c
Normal file
391
src/usbh_driver_ac_midi.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
52
src/usbh_driver_ac_midi_private.h
Normal file
52
src/usbh_driver_ac_midi_private.h
Normal file
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue