usb.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. * Minimal polled USB CDC-ACM stack for ATmega8U2 @ 16 MHz crystal.
  3. *
  4. * Composite device:
  5. * IAD -> iface 0 (CDC comm) + iface 1 (CDC data): virtual COM port (cdc_acm / usbser)
  6. * iface 2: vendor class, no endpoints - i2c-tiny-usb protocol on EP0 (see tiny.c)
  7. *
  8. * Endpoints (176 B DPRAM total on 8U2):
  9. * EP0 control 32 B
  10. * EP1 bulk OUT 64 B (host -> device)
  11. * EP2 bulk IN 64 B (device -> host)
  12. * EP3 interrupt IN 8 B (CDC notification, never used)
  13. */
  14. #include <avr/io.h>
  15. #include <avr/pgmspace.h>
  16. #include <util/delay.h>
  17. #include "usb.h"
  18. /* i2c-tiny-usb (EZPrototypes) VID/PID, matched by the mainline i2c-tiny-usb driver.
  19. * Not ours to ship - private/development use only. */
  20. #define USB_VID 0x1C40
  21. #define USB_PID 0x0534
  22. #define EP_OUT 1
  23. #define EP_IN 2
  24. #define EP_NOTIF 3
  25. #ifndef USE_VBUS_DETECT
  26. #define USE_VBUS_DETECT 0
  27. #endif
  28. /* ---------------------------------------------------------------- descriptors */
  29. static const uint8_t dev_desc[] PROGMEM = {
  30. 18, 1, 0x00, 0x02, /* USB 2.0 */
  31. 0xEF, 0x02, 0x01, /* misc class, IAD composite */
  32. EP0_SIZE,
  33. USB_VID & 0xFF, USB_VID >> 8,
  34. USB_PID & 0xFF, USB_PID >> 8,
  35. 0x00, 0x01, /* bcdDevice */
  36. 1, 2, 0, /* strings: mfr, product, no serial */
  37. 1 /* configurations */
  38. };
  39. static const uint8_t cfg_desc[] PROGMEM = {
  40. 9, 2, 84, 0, 3, 1, 0, 0x80, 50, /* config, 3 ifaces, bus powered 100 mA */
  41. 8, 0x0B, 0, 2, 0x02, 0x02, 0x00, 0, /* IAD: ifaces 0-1 are one CDC function */
  42. /* Communication interface */
  43. 9, 4, 0, 0, 1, 0x02, 0x02, 0x00, 0, /* CDC, ACM, no protocol */
  44. 5, 0x24, 0x00, 0x10, 0x01, /* header, CDC 1.10 */
  45. 5, 0x24, 0x01, 0x00, 1, /* call management, data iface 1 */
  46. 4, 0x24, 0x02, 0x02, /* ACM: line coding + break */
  47. 5, 0x24, 0x06, 0, 1, /* union */
  48. 7, 5, 0x80 | EP_NOTIF, 3, 8, 0, 0xFF, /* notification EP */
  49. /* Data interface */
  50. 9, 4, 1, 0, 2, 0x0A, 0x00, 0x00, 0,
  51. 7, 5, EP_OUT, 2, USB_PACKET, 0, 0,
  52. 7, 5, 0x80 | EP_IN, 2, USB_PACKET, 0, 0,
  53. /* i2c-tiny-usb interface: vendor class, control endpoint only */
  54. 9, 4, 2, 0, 0, 0xFF, 0x00, 0x00, 0
  55. };
  56. static const char str_mfr[] PROGMEM = "Open";
  57. static const char str_prod[] PROGMEM = "USB-I2C Bridge";
  58. /* ---------------------------------------------------------------- state */
  59. static uint8_t configured;
  60. static uint8_t line_coding[7] = { 0x00, 0xC2, 0x01, 0x00, 0, 0, 8 }; /* 115200 8N1 */
  61. bool usb_ready(void) { return configured; }
  62. /* ---------------------------------------------------------------- EP0 helpers */
  63. bool ep0_wait(uint8_t mask)
  64. {
  65. uint16_t t = 40000; /* ~100 ms */
  66. while (!(UEINTX & mask)) {
  67. if ((UEINTX & (1 << RXSTPI)) || (UDINT & (1 << EORSTI))) return false;
  68. _delay_us(2);
  69. if (!--t) return false;
  70. }
  71. return true;
  72. }
  73. void ep0_status_in(void) /* zero-length IN status stage */
  74. {
  75. if (ep0_wait(1 << TXINI)) UEINTX = ~(1 << TXINI);
  76. }
  77. void ep0_stall(void) { UECONX |= (1 << STALLRQ); }
  78. void ep0_send(const void *p, uint16_t len, uint16_t wlen, bool pgm)
  79. {
  80. const uint8_t *s = p;
  81. bool zlp = len < wlen;
  82. if (len > wlen) len = wlen;
  83. for (;;) {
  84. if (!ep0_wait((1 << TXINI) | (1 << RXOUTI))) return;
  85. if (UEINTX & (1 << RXOUTI)) { UEINTX = ~(1 << RXOUTI); return; } /* host aborted */
  86. uint8_t n = len > EP0_SIZE ? EP0_SIZE : (uint8_t)len;
  87. for (uint8_t i = 0; i < n; i++) UEDATX = pgm ? pgm_read_byte(s++) : *s++;
  88. UEINTX = ~(1 << TXINI);
  89. len -= n;
  90. if (n < EP0_SIZE) break; /* short packet ends transfer */
  91. if (len == 0 && !zlp) break;
  92. }
  93. if (ep0_wait(1 << RXOUTI)) UEINTX = ~(1 << RXOUTI); /* status stage */
  94. }
  95. uint8_t ep0_rx(uint8_t *buf) /* one OUT data-stage packet */
  96. {
  97. if (!ep0_wait(1 << RXOUTI)) return 0xFF;
  98. uint8_t n = UEBCLX;
  99. for (uint8_t i = 0; i < n; i++) buf[i] = UEDATX;
  100. UEINTX = ~(1 << RXOUTI);
  101. return n;
  102. }
  103. bool ep0_tx(const uint8_t *buf, uint8_t n) /* one IN data-stage packet */
  104. {
  105. if (!ep0_wait((1 << TXINI) | (1 << RXOUTI))) return false;
  106. if (UEINTX & (1 << RXOUTI)) { UEINTX = ~(1 << RXOUTI); return false; } /* host aborted */
  107. for (uint8_t i = 0; i < n; i++) UEDATX = buf[i];
  108. UEINTX = ~(1 << TXINI);
  109. return true;
  110. }
  111. void ep0_status_out(void) /* zero-length OUT status stage after IN data */
  112. {
  113. if (ep0_wait(1 << RXOUTI)) UEINTX = ~(1 << RXOUTI);
  114. }
  115. static uint8_t build_string(uint8_t *out, const char *pgm_s)
  116. {
  117. uint8_t n = 0, c;
  118. while (n < 24 && (c = pgm_read_byte(pgm_s++))) { out[2 + 2 * n] = c; out[3 + 2 * n] = 0; n++; }
  119. out[0] = 2 + 2 * n; out[1] = 3;
  120. return out[0];
  121. }
  122. static void eps_configure(void)
  123. {
  124. for (uint8_t e = EP_OUT; e <= EP_NOTIF; e++) { UENUM = e; UECONX = 0; UECFG1X = 0; }
  125. /* allocation must be ascending */
  126. UENUM = EP_OUT; UECONX = (1 << EPEN); UECFG0X = (2 << EPTYPE0); UECFG1X = (3 << EPSIZE0) | (1 << ALLOC);
  127. UENUM = EP_IN; UECONX = (1 << EPEN); UECFG0X = (2 << EPTYPE0) | (1 << EPDIR); UECFG1X = (3 << EPSIZE0) | (1 << ALLOC);
  128. UENUM = EP_NOTIF; UECONX = (1 << EPEN); UECFG0X = (3 << EPTYPE0) | (1 << EPDIR); UECFG1X = (0 << EPSIZE0) | (1 << ALLOC);
  129. UERST = 0x0E; UERST = 0;
  130. UENUM = 0; /* back to EP0: the SET_CONFIGURATION status stage follows */
  131. }
  132. static void ep0_setup(void)
  133. {
  134. uint8_t bmRT = UEDATX, bReq = UEDATX;
  135. uint16_t wV = UEDATX; wV |= (uint16_t)UEDATX << 8;
  136. uint16_t wI = UEDATX; wI |= (uint16_t)UEDATX << 8;
  137. uint16_t wL = UEDATX; wL |= (uint16_t)UEDATX << 8;
  138. UEINTX = ~(1 << RXSTPI);
  139. uint8_t type = bmRT & 0x60;
  140. if (type == 0x00) { /* standard */
  141. switch (bReq) {
  142. case 0: /* GET_STATUS */ { static const uint8_t z[2] = {0, 0}; ep0_send(z, 2, wL, false); break; }
  143. case 1: case 3: /* CLEAR/SET_FEATURE */ ep0_status_in(); break;
  144. case 5: /* SET_ADDRESS */
  145. UDADDR = wV & 0x7F;
  146. ep0_status_in();
  147. if (ep0_wait(1 << TXINI)) UDADDR |= (1 << ADDEN);
  148. break;
  149. case 6: /* GET_DESCRIPTOR */ {
  150. uint8_t t = wV >> 8, idx = wV & 0xFF;
  151. uint8_t buf[2 + 48];
  152. if (t == 1) ep0_send(dev_desc, sizeof dev_desc, wL, true);
  153. else if (t == 2 && idx == 0) ep0_send(cfg_desc, sizeof cfg_desc, wL, true);
  154. else if (t == 3 && idx == 0) { buf[0] = 4; buf[1] = 3; buf[2] = 0x09; buf[3] = 0x04; ep0_send(buf, 4, wL, false); }
  155. else if (t == 3 && idx == 1) ep0_send(buf, build_string(buf, str_mfr), wL, false);
  156. else if (t == 3 && idx == 2) ep0_send(buf, build_string(buf, str_prod), wL, false);
  157. else ep0_stall();
  158. break; }
  159. case 8: ep0_send(&configured, 1, wL, false); break; /* GET_CONFIGURATION */
  160. case 9: /* SET_CONFIGURATION */
  161. if ((wV & 0xFF) <= 1) {
  162. configured = wV & 0xFF;
  163. if (configured) eps_configure();
  164. ep0_status_in();
  165. } else ep0_stall();
  166. break;
  167. case 10: { static const uint8_t z = 0; ep0_send(&z, 1, wL, false); break; } /* GET_INTERFACE */
  168. case 11: ep0_status_in(); break; /* SET_INTERFACE */
  169. default: ep0_stall();
  170. }
  171. } else if (type == 0x20) { /* CDC class */
  172. switch (bReq) {
  173. case 0x20: /* SET_LINE_CODING */
  174. if (ep0_wait(1 << RXOUTI)) {
  175. for (uint8_t i = 0; i < 7; i++) line_coding[i] = UEDATX;
  176. UEINTX = ~(1 << RXOUTI);
  177. ep0_status_in();
  178. }
  179. break;
  180. case 0x21: ep0_send(line_coding, 7, wL, false); break; /* GET_LINE_CODING */
  181. case 0x22: case 0x23: ep0_status_in(); break; /* CONTROL_LINE_STATE, SEND_BREAK */
  182. default: ep0_stall();
  183. }
  184. } else if (type == 0x40) { /* vendor */
  185. usb_vendor_request(bmRT, bReq, wV, wI, wL);
  186. } else ep0_stall();
  187. }
  188. /* ---------------------------------------------------------------- public API */
  189. void usb_init(void)
  190. {
  191. REGCR &= ~(1 << REGDIS); /* enable 3.3 V USB pad regulator */
  192. USBCON = (1 << USBE) | (1 << FRZCLK);
  193. PLLCSR = (1 << PLLP0) | (1 << PLLE); /* PLLP=001: 16 MHz /2 = 8 MHz -> x6 = 48 MHz (as LUFA) */
  194. while (!(PLLCSR & (1 << PLOCK)));
  195. USBCON = (1 << USBE); /* unfreeze */
  196. UDCON = (1 << DETACH);
  197. #if !USE_VBUS_DETECT
  198. UDCON = 0; /* attach, full speed */
  199. #else
  200. DDRB &= ~(1 << PB6);
  201. #endif
  202. }
  203. void usb_task(void)
  204. {
  205. #if USE_VBUS_DETECT
  206. if (PINB & (1 << PB6)) UDCON &= ~(1 << DETACH);
  207. else { UDCON |= (1 << DETACH); configured = 0; return; }
  208. #endif
  209. if (UDINT & (1 << EORSTI)) {
  210. UDINT = ~(1 << EORSTI);
  211. configured = 0;
  212. usb_reset_hook();
  213. UENUM = 0;
  214. UECONX = (1 << EPEN);
  215. UECFG0X = 0; /* control */
  216. UECFG1X = (2 << EPSIZE0) | (1 << ALLOC); /* 32 B */
  217. }
  218. UENUM = 0;
  219. if (UEINTX & (1 << RXSTPI)) ep0_setup();
  220. }
  221. uint8_t usb_rx(uint8_t *buf)
  222. {
  223. if (!configured) return 0;
  224. UENUM = EP_OUT;
  225. if (!(UEINTX & (1 << RXOUTI))) return 0;
  226. uint8_t n = UEBCLX;
  227. for (uint8_t i = 0; i < n; i++) buf[i] = UEDATX;
  228. UEINTX = (uint8_t)~((1 << RXOUTI) | (1 << FIFOCON));
  229. return n;
  230. }
  231. void usb_tx(const uint8_t *buf, uint8_t n)
  232. {
  233. if (!configured || !n) return;
  234. UENUM = EP_IN;
  235. uint16_t t = 20000; /* ~50 ms: host not reading -> drop */
  236. while (!(UEINTX & (1 << TXINI))) {
  237. if (!--t) return;
  238. _delay_us(2);
  239. }
  240. for (uint8_t i = 0; i < n; i++) UEDATX = buf[i];
  241. UEINTX = (uint8_t)~((1 << TXINI) | (1 << FIFOCON));
  242. }