usb.c 8.7 KB

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