A library to send and receive MIDI messages over USB using the default USB-MIDI drivers on Win/Mac

Fork of USBMIDI by Simon Ford

Revision:
1:ff74eabe02cd
Child:
2:10d694d6ccdc
diff -r 56b095524cf2 -r ff74eabe02cd usbcore.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usbcore.c	Sun Feb 20 13:10:05 2011 +0000
@@ -0,0 +1,278 @@
+/** @license The MIT License
+ * Copyright (c) 2011 mux, simon
+ *
+ * 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.
+ */
+
+#include "usbcore.h"
+
+#include "mbed.h"
+
+// Serial Interface Engine
+#define SIE_SET_ADDR    (0xD0)
+#define SIE_SET_STATUS  (0xFE)
+#define SIE_GET_STATUS  (0xFE)
+#define SIE_SET_MODE    (0xF3)
+#define SIE_CLR_BUFFER  (0xF2)
+#define SIE_VAL_BUFFER  (0xFA)
+#define SIE_SEL_EP      (0x00)
+#define SIE_SEL_CLR_EP  (0x28)
+#define SIE_SET_EP_STAT (0x40)
+#define SIE_READ_ERROR  (0xFB)
+#define SIE_CONFIG_DEVICE (0xD8)
+
+// EP status
+#define EP_FE           (1<<0) // Full/Empty
+#define EP_ST           (1<<1) // Stalled endpoint
+#define EP_STP          (1<<2) // Setup packet
+#define EP_PO           (1<<3) // packet overwritten
+#define EP_EPN          (1<<4) // EP NAKed
+#define B_1_FULL        (1<<5) // buffer 1 status
+#define B_2_FULL        (1<<6) // buffer 2 status
+
+// USB device interrupts
+#define DEV_FRAME       (1<<0)
+#define EP_FAST         (1<<1)
+#define EP_SLOW         (1<<2)
+#define DEV_STAT        (1<<3)
+#define RxENDPKT        (1<<6)
+#define TxENDPKT        (1<<7)
+#define EP_RLZED        (1<<8)
+#define CCEMPTY         (0x10)
+#define CDFULL          (0x20)
+
+// USB device status bits
+#define STAT_CON        (1<<0)
+#define STAT_CON_CH     (1<<1)
+#define STAT_SUS        (1<<2)
+#define STAT_SUS_CH     (1<<3)
+#define STAT_RST        (1<<4)
+
+// end points interrupts
+#define EP0RX_INT       (1<<0)
+#define EP0TX_INT       (1<<1)
+#define EP1RX_INT       (1<<2)
+#define EP1TX_INT       (1<<3)
+#define EP2RX_INT       (1<<4)
+#define EP2TX_INT       (1<<5)
+
+// USB control register
+#define RD_EN           (1<<0)
+#define WR_EN           (1<<1)
+#define PKT_RDY         (1<<11)
+#define LOG_ENDPOINT(ep) ((ep>>1)<<2)
+
+// configure state
+static int configured = 0;
+
+// USB interrupt handler
+void USB_IRQHandler(void);
+
+// Serial Interface Engine functions
+void sie_command(uint32_t code) {
+    LPC_USB->USBDevIntClr = CCEMPTY;                // clear CCEMPTY     
+    LPC_USB->USBCmdCode   = ((code<<16)|(0x05<<8)); // CMD_PHASE=Command 
+    while (!(LPC_USB->USBDevIntSt & CCEMPTY));      // wait for CCEMPTY 
+}
+
+void sie_write(uint32_t data) {
+    LPC_USB->USBDevIntClr = CCEMPTY;                // clear CCEMPTY     
+    LPC_USB->USBCmdCode   = ((data<<16)|(0x01<<8)); // CMD_PHASE=Write   
+    while (!(LPC_USB->USBDevIntSt & CCEMPTY));      // wait for CCEMPTY  
+}
+
+uint8_t sie_read(uint32_t code) {
+    LPC_USB->USBDevIntClr = CDFULL;                 // clear CCEMPTY     
+    LPC_USB->USBCmdCode   = ((code<<16)|(0x02<<8)); // CMD_PHASE=Read    
+    while (!(LPC_USB->USBDevIntSt & CDFULL));       // wait for CDFULL   
+    return (uint8_t) LPC_USB->USBCmdData;
+
+}
+
+// end point functions
+void ep_realize(uint8_t ep, uint32_t size) {
+    LPC_USB->USBDevIntClr = EP_RLZED;           // clear EP_RLZED
+    LPC_USB->USBReEp    |= (1<<ep);
+    LPC_USB->USBEpInd    = ep;                  // set USBEpIn
+    LPC_USB->USBMaxPSize = size;                // writing to EPn pointed to by USBEpInd
+    while (!(LPC_USB->USBDevIntSt & EP_RLZED)); // wait for EP_RLZED
+    LPC_USB->USBDevIntClr = EP_RLZED;           // clear EP_RLZED
+}
+
+void ep_stall(uint8_t ep) {
+    sie_command(SIE_SET_EP_STAT+ep);
+    sie_write(1);
+}
+
+void ep_unstall(uint8_t ep) {
+    sie_command(SIE_SET_EP_STAT+ep);
+    sie_write(0);
+}
+
+// initializes a pointer to the endpoint buffer
+uint8_t ep_select(uint8_t ep) {
+    sie_command(SIE_SEL_EP+ep);
+    return sie_read(SIE_SEL_EP+ep);
+}
+
+uint8_t ep_select_clear(uint8_t ep) {
+    LPC_USB->USBEpIntClr |= ep;                 // clear ep interrupt   
+    while (!(LPC_USB->USBDevIntSt & CDFULL));   // wait for cmd finish 
+    return LPC_USB->USBCmdData;
+}
+
+int ep_readable(uint8_t ep) {
+    uint8_t st = ep_select(ep);
+    return (st & EP_FE);
+}
+
+int ep_writable(uint8_t ep) {
+    uint8_t st = ep_select(ep);
+    return !(st & EP_FE);
+}
+
+int ep_read(uint8_t ep, uint8_t *tbuf) {
+    uint32_t *buf = (uint32_t*) tbuf;
+    LPC_USB->USBCtrl = LOG_ENDPOINT(ep)|RD_EN;  // RD_EN bit and LOG_ENDPOINT   
+    while (!(LPC_USB->USBRxPLen & PKT_RDY));    // wait for packet to be fetched
+    int len = LPC_USB->USBRxPLen & 0x3FF;       // read and mask packet length  
+    while (!(LPC_USB->USBDevIntSt & RxENDPKT)) {
+        *buf++ = LPC_USB->USBRxData;
+    }
+    LPC_USB->USBCtrl = 0;
+    LPC_USB->USBDevIntClr |= RxENDPKT;
+    sie_command(SIE_SEL_EP+ep);     // select endpoint   
+    sie_command(SIE_CLR_BUFFER);    // clear RX buffer   
+    return len;
+}
+
+void ep_write(uint8_t ep, uint8_t *tbuf, uint32_t len) {
+    uint32_t *buf = (uint32_t*) tbuf;
+    LPC_USB->USBCtrl   = LOG_ENDPOINT(ep)|WR_EN; // RD_EN bit and LOG_ENDPOINT  
+    LPC_USB->USBTxPLen |= (len & 0x3FF);         // write and mask packet length
+    while (!(LPC_USB->USBDevIntSt & TxENDPKT)) {
+        LPC_USB->USBTxData = *buf++;
+    }
+    LPC_USB->USBCtrl = 0;
+    LPC_USB->USBDevIntClr |= TxENDPKT;
+    sie_command(SIE_SEL_EP+ep);     // select endpoint   
+    sie_command(SIE_VAL_BUFFER);    // validate TX buffer
+}
+
+// USB device controller initialization
+void usb_init() {
+    // USB D+/D- pinsel functions
+    LPC_PINCON->PINSEL1 &=   0xC3FFFFFF;
+    LPC_PINCON->PINSEL1 |=   0x14000000;
+
+#if USB_UP_DEBUG
+    // USB_UP_LED pinsel function
+    LPC_PINCON->PINSEL3 &=   0xFFFFFFCF;
+    LPC_PINCON->PINSEL3 |=   0x00000010;
+#endif    
+
+    // USB connect pinsel function
+    LPC_PINCON->PINSEL4 &=   0xFFFCFFFF;
+    LPC_PINCON->PINSEL4 |=   0x00040000;
+    LPC_SC->PCONP       |=   (1UL<<31);         // enable the USB controller
+    LPC_USB->USBClkCtrl |=   ((1<<1)|(1<<4));   // enable the AHB and DEV clocks
+    while ((LPC_USB->USBClkSt & 0x12) != 0x12); // wait for the clocks to init
+
+    NVIC_SetVector(USB_IRQn, (uint32_t)&USB_IRQHandler);
+    NVIC_EnableIRQ(USB_IRQn);    // enable USB interrupts
+
+    usb_reset();
+    usb_set_address(0);          // default address
+}
+
+void usb_reset() {
+    ep_realize(EP0, MAX_EP0_PSIZE);
+    ep_realize(EP1, MAX_EP0_PSIZE);
+    LPC_USB->USBEpIntClr  = 0xFFFFFFFF;         // clear end points interrupts
+    LPC_USB->USBEpIntEn   = 0xFFFFFFFF;         // enable end points interrupts
+    LPC_USB->USBEpIntPri  = 0x0;                // route to EP_SLOW
+    LPC_USB->USBDevIntClr = 0xFFFFFFFF;         // clear USB device interrupts 
+    LPC_USB->USBDevIntEn  = (EP_SLOW|DEV_STAT); // enable USB device interrupts
+}
+
+void usb_configure(uint8_t conf) {
+    sie_command(SIE_CONFIG_DEVICE);
+    sie_write(conf);
+    configured = 1;
+}
+
+int usb_configured() {
+    return configured;
+}
+
+void usb_set_address(uint8_t addr) {
+    sie_command(SIE_SET_ADDR);
+    sie_write(addr|0x80);       // DEV_EN = 1
+}
+
+uint8_t usb_get_status() {
+    sie_command(SIE_GET_STATUS);
+    return sie_read(SIE_GET_STATUS);
+}
+
+void usb_connect() {
+    sie_command(SIE_GET_STATUS); // read current status
+    uint8_t st = sie_read(SIE_GET_STATUS);
+
+    sie_command(SIE_SET_STATUS); // set STAT_CON bit
+    sie_write(st|STAT_CON);
+}
+
+void USB_IRQHandler(void) {
+    if (LPC_USB->USBDevIntSt & DEV_STAT) { // DEV_STAT interrupt
+        LPC_USB->USBDevIntClr |= DEV_STAT;
+        if (usb_get_status() & STAT_RST) { // bus reset
+            usb_reset();
+        }
+        return;
+    }
+
+    if (LPC_USB->USBDevIntSt & EP_SLOW) {  // EP_SLOW interrupt
+        if (LPC_USB->USBEpIntSt & EP0RX_INT) {
+            if (ep_select_clear(EP0RX_INT) & EP_STP) { // setup transfer
+                ep0_setup();
+            } else {
+                ep0_out();
+            }
+        }
+
+        if (LPC_USB->USBEpIntSt & EP0TX_INT) {
+            ep_select_clear(EP0TX_INT);
+            ep0_in();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP2RX_INT) {
+            ep_select_clear(EP2TX_INT);
+            ep2_out();
+        }
+
+        if (LPC_USB->USBEpIntSt & EP2TX_INT) {
+            ep_select_clear(EP2TX_INT);
+            ep2_in();
+        }
+
+        // EP_SLOW should be cleared after clearing EPs interrupts
+        LPC_USB->USBDevIntClr |= EP_SLOW;
+    }
+}