usbcas.c 9.7 KB

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