usbcas.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  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 -> CAS */
  39. static uint8_t u2c_buffer[512];
  40. static RingBuffer_t c2u_ringbuf; /* CAS -> 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. * Motor relay sense. The motor relay is connected to PD1/INT1#, but
  73. * the interrupt isn't being used for that purpose. Rather, it is used
  74. * to latch an upward flank as on ABC800 this will have the output data
  75. * fed back to it. Thus, we can check if *either* it is grounded *or*
  76. * there has been a flank.
  77. *
  78. * Set up timer 3 to wrap around at 100 Hz, use this to poll and
  79. * debounce/deglitch the relay, and to delay the sensing of the
  80. * motor relay switch with ~160 ms.
  81. */
  82. static void motor_sense_init(void)
  83. {
  84. /*
  85. * Set up INT1# to latch a rising edge, but mask the resulting
  86. * interrupt request.
  87. */
  88. EIMSK = 0; /* all external interrupts masked */
  89. EICRA = 3 << ISC10; /* rising edge */
  90. EIFR = -1; /* clear all external interrupt flags */
  91. /*
  92. * Set up timer 3 to count at fosc/8 in CTC mode,
  93. * with wraparound at 20000 = 100 Hz.
  94. */
  95. TCCR3B = 0; /* Disable timer */
  96. TCCR3A = 0; /* No capture, no waveforms */
  97. ICR3 = (F_CPU/(8*100)) - 1; /* Top of counter */
  98. TCNT3 = 0;
  99. TIMSK3 = TIFR3 = (1 << ICF3); /* Interrupt on ICR3 match */
  100. TCCR3B = (3 << WGM32)|(2 << CS30); /* fosc/8, CTC mode, TOP=ICR3 */
  101. }
  102. bool motor_relay;
  103. ISR(TIMER3_CAPT_vect)
  104. {
  105. static uint8_t relay_ctr;
  106. bool relay;
  107. if (!(PORTD & 2) || (EIFR & 2)) {
  108. /* Relay active */
  109. EIFR = 2; /* Clear edge detect */
  110. if (relay_ctr < 16) {
  111. relay_ctr++;
  112. } else if (!motor_relay) {
  113. motor_relay = true;
  114. fmtx_enable();
  115. }
  116. } else {
  117. /* Relay not active */
  118. if (relay_ctr) {
  119. relay_ctr--;
  120. } else {
  121. motor_relay = false;
  122. }
  123. }
  124. }
  125. /*
  126. * Probe for modem control status bits and send them if changed,
  127. * or if forced. DSR is always set, DCD indicates if the cassette
  128. * motor relay is active.
  129. */
  130. static void update_modem_status(USB_ClassInfo_CDC_Device_t * const cii,
  131. bool forced)
  132. {
  133. uint16_t lines = 0;
  134. if (motor_relay) /* Cassette relay active */
  135. lines |= CDC_CONTROL_LINE_IN_DCD | CDC_CONTROL_LINE_IN_DSR;
  136. if (forced || cii->State.ControlLineStates.DeviceToHost != lines) {
  137. cii->State.ControlLineStates.DeviceToHost = lines;
  138. CDC_Device_SendControlLineStateChange(cii);
  139. }
  140. }
  141. /*
  142. * This is called in the main loop, but also any time we are busy-waiting
  143. * on something.
  144. */
  145. void do_usb_task(void)
  146. {
  147. /* Update modem flags if needed */
  148. update_modem_status(&vcpif, false);
  149. CDC_Device_USBTask(&vcpif);
  150. USB_USBTask();
  151. }
  152. /*
  153. * Try to send the next byte of data to the host, abort without
  154. * dequeuing if there is an error. Returns true on error.
  155. */
  156. static bool usb_send_next_byte(void)
  157. {
  158. uint8_t byte = RingBuffer_Peek(&c2u_ringbuf);
  159. if (CDC_Device_SendByte(&vcpif, byte) != ENDPOINT_READYWAIT_NoError)
  160. return true;
  161. /*
  162. * Dequeue the already sent byte from the buffer now we have
  163. * confirmed that no transmission error occurred
  164. */
  165. RingBuffer_Remove(&c2u_ringbuf);
  166. return false;
  167. }
  168. static void usb_recv_data(void)
  169. {
  170. int byte;
  171. /*
  172. * Only try to read in bytes from the CDC interface if
  173. * the transmit buffer is not full
  174. */
  175. if (RingBuffer_IsFull(&u2c_ringbuf))
  176. return;
  177. byte = CDC_Device_ReceiveByte(&vcpif);
  178. if (byte < 0)
  179. return; /* No data */
  180. /*
  181. * Store received byte into the transmit buffer, then
  182. * enable the transmit ISR if disabled
  183. */
  184. RingBuffer_Insert(&u2c_ringbuf, byte);
  185. if (motor_relay)
  186. fmtx_enable();
  187. }
  188. static void usb_send_data(void)
  189. {
  190. uint16_t outbytes = RingBuffer_GetCount(&c2u_ringbuf);
  191. if (!outbytes)
  192. return;
  193. Endpoint_SelectEndpoint(vcpif.Config.DataINEndpoint.Address);
  194. /*
  195. * Check if a packet is already enqueued to
  196. * the host - if so, we shouldn't try to send
  197. * more data until it completes as there is a
  198. * chance nothing is listening and a lengthy
  199. * timeout could occur
  200. */
  201. if (!Endpoint_IsINReady())
  202. return;
  203. /*
  204. * Never send more than one bank size
  205. * less one byte to the host at a
  206. * time, so that we don't block while
  207. * a Zero Length Packet (ZLP) to
  208. * terminate the transfer is sent if
  209. * the host isn't listening */
  210. outbytes = MIN(outbytes, (CDC_TXRX_EPSIZE - 1));
  211. /*
  212. * Transfer output bytes to the USB
  213. * endpoint. Abort without dequeuing if
  214. * there is an error.
  215. */
  216. while (outbytes--)
  217. if (usb_send_next_byte())
  218. break;
  219. }
  220. /*
  221. * Main program entry point. This routine contains the overall program
  222. * flow, including initial setup of
  223. * all components and the main program
  224. * loop.
  225. */
  226. int main(void)
  227. {
  228. SetupHardware();
  229. RingBuffer_InitBuffer(&u2c_ringbuf, u2c_buffer, sizeof(u2c_buffer));
  230. RingBuffer_InitBuffer(&c2u_ringbuf, c2u_buffer, sizeof(c2u_buffer));
  231. GlobalInterruptEnable();
  232. for (;;)
  233. {
  234. usb_recv_data();
  235. usb_send_data();
  236. do_usb_task();
  237. }
  238. }
  239. static uint32_t current_baudrate;
  240. /** Configures the board hardware and chip peripherals for the demo's functionality. */
  241. void SetupHardware(void)
  242. {
  243. /* Disable watchdog if enabled by bootloader/fuses */
  244. MCUSR &= ~(1 << WDRF);
  245. wdt_disable();
  246. /* Disable clock division */
  247. clock_prescale_set(clock_div_1);
  248. /*
  249. * Configure ports.
  250. * NC: not connected, but have pads on board. Input/pullup.
  251. * NT: not terminated on board. Output/GND.
  252. */
  253. /*
  254. * PORT B:
  255. * PB0 - RXLED#
  256. * PB1 - NC
  257. * PB2 - NC
  258. * PB3 - NC
  259. * PB4 - NC
  260. * PB5 - NC
  261. * PB6 - NC
  262. * PB7 - NT
  263. */
  264. DDRB = 0x81;
  265. PORTB = 0x7f;
  266. /*
  267. * PORT C:
  268. * PC6 - NC
  269. * PC7 - NP
  270. */
  271. DDRC = 0x80;
  272. PORTC = 0x40;
  273. /*
  274. * PORT D:
  275. * PD0 - data in (alt)
  276. * PD1 - motor sense#
  277. * PD2 - NC
  278. * PD3 - data out
  279. * PD4 - data in (main)
  280. * PD5 - TXLED#
  281. * PD6 - NT
  282. * PD7 - NC
  283. */
  284. DDRD = 0x68;
  285. PORTD = 0x93;
  286. /*
  287. * PORT E:
  288. * PE6 - NC
  289. * PE2 - hardware bootloader, tied to GND on board
  290. */
  291. DDRE = 0x00;
  292. PORTE = 0x40;
  293. /*
  294. * PORT F:
  295. * PF0 - NT
  296. * PF1 - NT
  297. * PF4 - NC
  298. * PF5 - NC
  299. * PF6 - NC
  300. * PF7 - NC
  301. */
  302. DDRF = 0x03;
  303. PORTF = 0xf0;
  304. /* Hardware Initialization */
  305. USB_Init();
  306. /* Initialize motor sense setup */
  307. motor_sense_init();
  308. /* Initialize receiver and transmitter */
  309. fmrx_init();
  310. current_baudrate = fmtx_real_baudrate(0);
  311. fmrx_set_speed(current_baudrate);
  312. fmtx_init_speed(current_baudrate);
  313. }
  314. /** Event handler for the library USB Connection event. */
  315. void EVENT_USB_Device_Connect(void)
  316. {
  317. }
  318. /** Event handler for the library USB Disconnection event. */
  319. void EVENT_USB_Device_Disconnect(void)
  320. {
  321. }
  322. /** Event handler for the library USB Configuration Changed event. */
  323. void EVENT_USB_Device_ConfigurationChanged(void)
  324. {
  325. bool success = true;
  326. success &= CDC_Device_ConfigureEndpoints(&vcpif);
  327. if (success)
  328. update_modem_status(&vcpif, true);
  329. }
  330. /** Event handler for the library USB Control Request reception event. */
  331. void EVENT_USB_Device_ControlRequest(void)
  332. {
  333. CDC_Device_ProcessControlRequest(&vcpif);
  334. }
  335. /*
  336. * Called from the demodulation ISR to push a new byte into the buffer
  337. */
  338. void fmrx_recv_byte(uint8_t byte)
  339. {
  340. if (USB_DeviceState == DEVICE_STATE_Configured &&
  341. !RingBuffer_IsFull(&c2u_ringbuf))
  342. RingBuffer_Insert(&c2u_ringbuf, byte);
  343. }
  344. /*
  345. * Called from the transmit register empty ISR to fetch a new byte;
  346. * returns -1 if the ring buffer is empty.
  347. */
  348. int fmtx_next_byte(void)
  349. {
  350. if (USB_DeviceState != DEVICE_STATE_Configured ||
  351. RingBuffer_IsEmpty(&u2c_ringbuf))
  352. return -1;
  353. return RingBuffer_Remove(&u2c_ringbuf);
  354. }
  355. /*
  356. * Event handler for the CDC Class driver Line Encoding Changed event.
  357. */
  358. void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const cii)
  359. {
  360. uint32_t baudrate = cii->State.LineEncoding.BaudRateBPS;
  361. /* This is a hack to give a sensible default */
  362. if (baudrate == 9600)
  363. baudrate = CAS_BAUDRATE_ABC80;
  364. baudrate = fmtx_real_baudrate(baudrate);
  365. if (1 || baudrate != current_baudrate) {
  366. current_baudrate = baudrate;
  367. fmtx_drain();
  368. fmrx_set_speed(current_baudrate);
  369. fmtx_init_speed(current_baudrate);
  370. }
  371. }
  372. /*
  373. * Event handler for control line changes
  374. */
  375. void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t* const cii)
  376. {
  377. /* Currently not used */
  378. }