usbcas.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * usbcas.c
  3. *
  4. * Emulator for the ABC80/800 cassette port
  5. * Based in part on USBtoSerial.c from the LUFA library
  6. *
  7. * Copyright (C) 2021 H. Peter Anvin
  8. */
  9. /*
  10. LUFA Library
  11. Copyright (C) Dean Camera, 2021.
  12. dean [at] fourwalledcubicle [dot] com
  13. www.lufa-lib.org
  14. */
  15. /*
  16. Copyright 2021 Dean Camera (dean [at] fourwalledcubicle [dot] com)
  17. Permission to use, copy, modify, distribute, and sell this
  18. software and its documentation for any purpose is hereby granted
  19. without fee, provided that the above copyright notice appear in
  20. all copies and that both that the copyright notice and this
  21. permission notice and warranty disclaimer appear in supporting
  22. documentation, and that the name of the author not be used in
  23. advertising or publicity pertaining to distribution of the
  24. software without specific, written prior permission.
  25. The author disclaims all warranties with regard to this
  26. software, including all implied warranties of merchantability
  27. and fitness. In no event shall the author be liable for any
  28. special, indirect or consequential damages or any damages
  29. whatsoever resulting from loss of use, data or profits, whether
  30. in an action of contract, negligence or other tortious action,
  31. arising out of or in connection with the use or performance of
  32. this software.
  33. */
  34. #include "usbcas.h"
  35. /*
  36. * Input and output ring buffers
  37. */
  38. static RingBuffer_t u2c_ringbuf; /* USB -> CDC */
  39. static uint8_t u2c_buffer[128];
  40. static RingBuffer_t c2u_ringbuf; /* CDC -> USB */
  41. static uint8_t c2u_buffer[128];
  42. /**
  43. * LUFA CDC Class driver interface configuration and state
  44. * information. This structure is passed to all CDC Class driver
  45. * functions, so that multiple instances of the same class within a
  46. * device can be differentiated from one another.
  47. *
  48. * "vcpif" stands for "Virtual COM Port Interface".
  49. */
  50. USB_ClassInfo_CDC_Device_t vcpif = {
  51. .Config = {
  52. .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI,
  53. .DataINEndpoint = {
  54. .Address = CDC_TX_EPADDR,
  55. .Size = CDC_TXRX_EPSIZE,
  56. .Banks = 1,
  57. },
  58. .DataOUTEndpoint = {
  59. .Address = CDC_RX_EPADDR,
  60. .Size = CDC_TXRX_EPSIZE,
  61. .Banks = 1,
  62. },
  63. .NotificationEndpoint =
  64. {
  65. .Address = CDC_NOTIFICATION_EPADDR,
  66. .Size = CDC_NOTIFICATION_EPSIZE,
  67. .Banks = 1,
  68. },
  69. },
  70. };
  71. /*
  72. * Probe for modem control status bits and send them if changed,
  73. * or if forced. DSR is always set, DCD indicates if the cassette
  74. * motor relay is active.
  75. */
  76. static void update_modem_status(USB_ClassInfo_CDC_Device_t * const cii,
  77. bool forced)
  78. {
  79. uint16_t lines = CDC_CONTROL_LINE_IN_DSR;
  80. if (!(PINB & (1 << 4))) /* Cassette relay active */
  81. lines |= CDC_CONTROL_LINE_IN_DCD;
  82. #if 0
  83. if (lines & CDC_CONTROL_LINE_IN_DCD)
  84. PORTB &= ~(1U << 0);
  85. else
  86. PORTB |= (1U << 0);
  87. #endif
  88. if (forced || cii->State.ControlLineStates.DeviceToHost != lines) {
  89. cii->State.ControlLineStates.DeviceToHost = lines;
  90. CDC_Device_SendControlLineStateChange(cii);
  91. }
  92. }
  93. /*
  94. * This is called in the main loop, but also any time we are busy-waiting
  95. * on something.
  96. */
  97. void do_usb_task(void)
  98. {
  99. /* Update modem flags if needed */
  100. update_modem_status(&vcpif, false);
  101. CDC_Device_USBTask(&vcpif);
  102. USB_USBTask();
  103. }
  104. /*
  105. * Try to send the next byte of data to the host, abort without
  106. * dequeuing if there is an error. Returns true on error.
  107. */
  108. static bool usb_send_next_byte(void)
  109. {
  110. uint8_t byte = RingBuffer_Peek(&c2u_ringbuf);
  111. if (CDC_Device_SendByte(&vcpif, byte) != ENDPOINT_READYWAIT_NoError)
  112. return true;
  113. /*
  114. * Dequeue the already sent byte from the buffer now we have
  115. * confirmed that no transmission error occurred
  116. */
  117. RingBuffer_Remove(&c2u_ringbuf);
  118. return false;
  119. }
  120. /*
  121. * Main program entry point. This routine contains the overall program
  122. * flow, including initial setup of
  123. * all components and the main program
  124. * loop.
  125. */
  126. int main(void)
  127. {
  128. SetupHardware();
  129. RingBuffer_InitBuffer(&u2c_ringbuf, u2c_buffer, sizeof(u2c_buffer));
  130. RingBuffer_InitBuffer(&c2u_ringbuf, c2u_buffer, sizeof(c2u_buffer));
  131. GlobalInterruptEnable();
  132. for (;;)
  133. {
  134. /*
  135. * Only try to read in bytes from the CDC interface if
  136. * the transmit buffer is not full
  137. */
  138. if (!(RingBuffer_IsFull(&u2c_ringbuf))) {
  139. int byte;
  140. byte = CDC_Device_ReceiveByte(&vcpif);
  141. /*
  142. * Store received byte into the transmit buffer, then
  143. * enable the transmit ISR if disabled
  144. */
  145. if (byte >= 0) {
  146. RingBuffer_Insert(&u2c_ringbuf, byte);
  147. fmtx_enable();
  148. }
  149. }
  150. uint16_t outbytes = RingBuffer_GetCount(&c2u_ringbuf);
  151. if (outbytes) {
  152. Endpoint_SelectEndpoint(vcpif.Config.DataINEndpoint.Address);
  153. /*
  154. * Check if a packet is already enqueued to
  155. * the host - if so, we shouldn't try to send
  156. * more data until it completes as there is a
  157. * chance nothing is listening and a lengthy
  158. * timeout could occur
  159. */
  160. if (Endpoint_IsINReady()) {
  161. /*
  162. * Never send more than one bank size
  163. * less one byte to the host at a
  164. * time, so that we don't block while
  165. * a Zero Length Packet (ZLP) to
  166. * terminate the transfer is sent if
  167. * the host isn't listening */
  168. outbytes = MIN(outbytes, (CDC_TXRX_EPSIZE - 1));
  169. /*
  170. * Transfer output bytes to the USB
  171. * endpoint. Abort without dequeuing if
  172. * there is an error.
  173. */
  174. while (outbytes--)
  175. if (usb_send_next_byte())
  176. break;
  177. }
  178. }
  179. do_usb_task();
  180. }
  181. }
  182. static uint32_t current_baudrate;
  183. /** Configures the board hardware and chip peripherals for the demo's functionality. */
  184. void SetupHardware(void)
  185. {
  186. #if (ARCH == ARCH_AVR8)
  187. /* Disable watchdog if enabled by bootloader/fuses */
  188. MCUSR &= ~(1 << WDRF);
  189. wdt_disable();
  190. /* Disable clock division */
  191. clock_prescale_set(clock_div_1);
  192. #endif
  193. /* Hardware Initialization */
  194. USB_Init();
  195. /* PORTB: PB0 - RXLED, PB4 - Relay (pullup), PB5 - relay out */
  196. PORTB |= (1 << 0) | (1 << 4);
  197. DDRB = (DDRB & ~((1 << 4))) | ((1 << 0) | (1 << 5));
  198. /* Initialize receiver and transmitter */
  199. fmrx_init();
  200. current_baudrate = fmtx_real_baudrate(0);
  201. fmrx_set_speed(current_baudrate);
  202. fmtx_init_speed(current_baudrate);
  203. }
  204. /** Event handler for the library USB Connection event. */
  205. void EVENT_USB_Device_Connect(void)
  206. {
  207. }
  208. /** Event handler for the library USB Disconnection event. */
  209. void EVENT_USB_Device_Disconnect(void)
  210. {
  211. }
  212. /** Event handler for the library USB Configuration Changed event. */
  213. void EVENT_USB_Device_ConfigurationChanged(void)
  214. {
  215. bool success = true;
  216. success &= CDC_Device_ConfigureEndpoints(&vcpif);
  217. if (success)
  218. update_modem_status(&vcpif, true);
  219. }
  220. /** Event handler for the library USB Control Request reception event. */
  221. void EVENT_USB_Device_ControlRequest(void)
  222. {
  223. CDC_Device_ProcessControlRequest(&vcpif);
  224. }
  225. /*
  226. * Called from the demodulation ISR to push a new byte into the buffer
  227. */
  228. void fmrx_recv_byte(uint8_t byte)
  229. {
  230. if (USB_DeviceState == DEVICE_STATE_Configured &&
  231. !RingBuffer_IsFull(&c2u_ringbuf))
  232. RingBuffer_Insert(&c2u_ringbuf, byte);
  233. }
  234. /*
  235. * Called from the transmit register empty ISR to fetch a new byte;
  236. * returns -1 if the ring buffer is empty.
  237. */
  238. int fmtx_next_byte(void)
  239. {
  240. if (USB_DeviceState != DEVICE_STATE_Configured ||
  241. RingBuffer_IsEmpty(&u2c_ringbuf))
  242. return -1;
  243. return RingBuffer_Remove(&u2c_ringbuf);
  244. }
  245. /*
  246. * Event handler for the CDC Class driver Line Encoding Changed event.
  247. */
  248. void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const cii)
  249. {
  250. uint32_t baudrate = cii->State.LineEncoding.BaudRateBPS;
  251. /* This is a hack to give a sensible default */
  252. if (baudrate == 9600)
  253. baudrate = CAS_BAUDRATE_ABC80;
  254. baudrate = fmtx_real_baudrate(baudrate);
  255. if (1 || baudrate != current_baudrate) {
  256. current_baudrate = baudrate;
  257. fmtx_drain();
  258. fmrx_set_speed(current_baudrate);
  259. fmtx_init_speed(current_baudrate);
  260. }
  261. }
  262. /*
  263. * Event handler for control line changes
  264. */
  265. void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t* const cii)
  266. {
  267. }