|
|
@@ -1,6 +1,10 @@
|
|
|
/*
|
|
|
* Minimal polled USB CDC-ACM stack for ATmega8U2 @ 16 MHz crystal.
|
|
|
*
|
|
|
+ * Composite device:
|
|
|
+ * IAD -> iface 0 (CDC comm) + iface 1 (CDC data): virtual COM port (cdc_acm / usbser)
|
|
|
+ * iface 2: vendor class, no endpoints - i2c-tiny-usb protocol on EP0 (see tiny.c)
|
|
|
+ *
|
|
|
* Endpoints (176 B DPRAM total on 8U2):
|
|
|
* EP0 control 32 B
|
|
|
* EP1 bulk OUT 64 B (host -> device)
|
|
|
@@ -12,10 +16,11 @@
|
|
|
#include <util/delay.h>
|
|
|
#include "usb.h"
|
|
|
|
|
|
-#define USB_VID 0x1209 /* pid.codes test VID/PID - replace for production */
|
|
|
-#define USB_PID 0x0001
|
|
|
+/* i2c-tiny-usb (EZPrototypes) VID/PID, matched by the mainline i2c-tiny-usb driver.
|
|
|
+ * Not ours to ship - private/development use only. */
|
|
|
+#define USB_VID 0x1C40
|
|
|
+#define USB_PID 0x0534
|
|
|
|
|
|
-#define EP0_SIZE 32
|
|
|
#define EP_OUT 1
|
|
|
#define EP_IN 2
|
|
|
#define EP_NOTIF 3
|
|
|
@@ -27,7 +32,7 @@
|
|
|
/* ---------------------------------------------------------------- descriptors */
|
|
|
static const uint8_t dev_desc[] PROGMEM = {
|
|
|
18, 1, 0x00, 0x02, /* USB 2.0 */
|
|
|
- 0x02, 0x00, 0x00, /* CDC device class */
|
|
|
+ 0xEF, 0x02, 0x01, /* misc class, IAD composite */
|
|
|
EP0_SIZE,
|
|
|
USB_VID & 0xFF, USB_VID >> 8,
|
|
|
USB_PID & 0xFF, USB_PID >> 8,
|
|
|
@@ -37,7 +42,8 @@ static const uint8_t dev_desc[] PROGMEM = {
|
|
|
};
|
|
|
|
|
|
static const uint8_t cfg_desc[] PROGMEM = {
|
|
|
- 9, 2, 67, 0, 2, 1, 0, 0x80, 50, /* config, 2 ifaces, bus powered 100 mA */
|
|
|
+ 9, 2, 84, 0, 3, 1, 0, 0x80, 50, /* config, 3 ifaces, bus powered 100 mA */
|
|
|
+ 8, 0x0B, 0, 2, 0x02, 0x02, 0x00, 0, /* IAD: ifaces 0-1 are one CDC function */
|
|
|
/* Communication interface */
|
|
|
9, 4, 0, 0, 1, 0x02, 0x02, 0x00, 0, /* CDC, ACM, no protocol */
|
|
|
5, 0x24, 0x00, 0x10, 0x01, /* header, CDC 1.10 */
|
|
|
@@ -48,7 +54,9 @@ static const uint8_t cfg_desc[] PROGMEM = {
|
|
|
/* Data interface */
|
|
|
9, 4, 1, 0, 2, 0x0A, 0x00, 0x00, 0,
|
|
|
7, 5, EP_OUT, 2, USB_PACKET, 0, 0,
|
|
|
- 7, 5, 0x80 | EP_IN, 2, USB_PACKET, 0, 0
|
|
|
+ 7, 5, 0x80 | EP_IN, 2, USB_PACKET, 0, 0,
|
|
|
+ /* i2c-tiny-usb interface: vendor class, control endpoint only */
|
|
|
+ 9, 4, 2, 0, 0, 0xFF, 0x00, 0x00, 0
|
|
|
};
|
|
|
|
|
|
static const char str_mfr[] PROGMEM = "Open";
|
|
|
@@ -61,7 +69,7 @@ static uint8_t line_coding[7] = { 0x00, 0xC2, 0x01, 0x00, 0, 0, 8 }; /* 115200
|
|
|
bool usb_ready(void) { return configured; }
|
|
|
|
|
|
/* ---------------------------------------------------------------- EP0 helpers */
|
|
|
-static bool ep0_wait(uint8_t mask)
|
|
|
+bool ep0_wait(uint8_t mask)
|
|
|
{
|
|
|
uint16_t t = 40000; /* ~100 ms */
|
|
|
while (!(UEINTX & mask)) {
|
|
|
@@ -72,14 +80,14 @@ static bool ep0_wait(uint8_t mask)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
-static void ep0_status_in(void) /* zero-length IN status stage */
|
|
|
+void ep0_status_in(void) /* zero-length IN status stage */
|
|
|
{
|
|
|
if (ep0_wait(1 << TXINI)) UEINTX = ~(1 << TXINI);
|
|
|
}
|
|
|
|
|
|
-static void ep0_stall(void) { UECONX |= (1 << STALLRQ); }
|
|
|
+void ep0_stall(void) { UECONX |= (1 << STALLRQ); }
|
|
|
|
|
|
-static void ep0_send(const void *p, uint16_t len, uint16_t wlen, bool pgm)
|
|
|
+void ep0_send(const void *p, uint16_t len, uint16_t wlen, bool pgm)
|
|
|
{
|
|
|
const uint8_t *s = p;
|
|
|
bool zlp = len < wlen;
|
|
|
@@ -98,6 +106,29 @@ static void ep0_send(const void *p, uint16_t len, uint16_t wlen, bool pgm)
|
|
|
if (ep0_wait(1 << RXOUTI)) UEINTX = ~(1 << RXOUTI); /* status stage */
|
|
|
}
|
|
|
|
|
|
+uint8_t ep0_rx(uint8_t *buf) /* one OUT data-stage packet */
|
|
|
+{
|
|
|
+ if (!ep0_wait(1 << RXOUTI)) return 0xFF;
|
|
|
+ uint8_t n = UEBCLX;
|
|
|
+ for (uint8_t i = 0; i < n; i++) buf[i] = UEDATX;
|
|
|
+ UEINTX = ~(1 << RXOUTI);
|
|
|
+ return n;
|
|
|
+}
|
|
|
+
|
|
|
+bool ep0_tx(const uint8_t *buf, uint8_t n) /* one IN data-stage packet */
|
|
|
+{
|
|
|
+ if (!ep0_wait((1 << TXINI) | (1 << RXOUTI))) return false;
|
|
|
+ if (UEINTX & (1 << RXOUTI)) { UEINTX = ~(1 << RXOUTI); return false; } /* host aborted */
|
|
|
+ for (uint8_t i = 0; i < n; i++) UEDATX = buf[i];
|
|
|
+ UEINTX = ~(1 << TXINI);
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+void ep0_status_out(void) /* zero-length OUT status stage after IN data */
|
|
|
+{
|
|
|
+ if (ep0_wait(1 << RXOUTI)) UEINTX = ~(1 << RXOUTI);
|
|
|
+}
|
|
|
+
|
|
|
static uint8_t build_string(uint8_t *out, const char *pgm_s)
|
|
|
{
|
|
|
uint8_t n = 0, c;
|
|
|
@@ -114,6 +145,7 @@ static void eps_configure(void)
|
|
|
UENUM = EP_IN; UECONX = (1 << EPEN); UECFG0X = (2 << EPTYPE0) | (1 << EPDIR); UECFG1X = (3 << EPSIZE0) | (1 << ALLOC);
|
|
|
UENUM = EP_NOTIF; UECONX = (1 << EPEN); UECFG0X = (3 << EPTYPE0) | (1 << EPDIR); UECFG1X = (0 << EPSIZE0) | (1 << ALLOC);
|
|
|
UERST = 0x0E; UERST = 0;
|
|
|
+ UENUM = 0; /* back to EP0: the SET_CONFIGURATION status stage follows */
|
|
|
}
|
|
|
|
|
|
static void ep0_setup(void)
|
|
|
@@ -122,7 +154,6 @@ static void ep0_setup(void)
|
|
|
uint16_t wV = UEDATX; wV |= (uint16_t)UEDATX << 8;
|
|
|
uint16_t wI = UEDATX; wI |= (uint16_t)UEDATX << 8;
|
|
|
uint16_t wL = UEDATX; wL |= (uint16_t)UEDATX << 8;
|
|
|
- (void)wI;
|
|
|
UEINTX = ~(1 << RXSTPI);
|
|
|
|
|
|
uint8_t type = bmRT & 0x60;
|
|
|
@@ -171,6 +202,8 @@ static void ep0_setup(void)
|
|
|
case 0x22: case 0x23: ep0_status_in(); break; /* CONTROL_LINE_STATE, SEND_BREAK */
|
|
|
default: ep0_stall();
|
|
|
}
|
|
|
+ } else if (type == 0x40) { /* vendor */
|
|
|
+ usb_vendor_request(bmRT, bReq, wV, wI, wL);
|
|
|
} else ep0_stall();
|
|
|
}
|
|
|
|
|
|
@@ -199,6 +232,7 @@ void usb_task(void)
|
|
|
if (UDINT & (1 << EORSTI)) {
|
|
|
UDINT = ~(1 << EORSTI);
|
|
|
configured = 0;
|
|
|
+ usb_reset_hook();
|
|
|
UENUM = 0;
|
|
|
UECONX = (1 << EPEN);
|
|
|
UECFG0X = 0; /* control */
|