usbcas.c 11 KB

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