123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- /*
- * usbcas.c
- *
- * Emulator for the ABC80/800 cassette port
- * Based in part on USBtoSerial.c from the LUFA library
- *
- * Copyright (C) 2021 H. Peter Anvin
- */
- /*
- LUFA Library
- Copyright (C) Dean Camera, 2021.
- dean [at] fourwalledcubicle [dot] com
- www.lufa-lib.org
- */
- /*
- Copyright 2021 Dean Camera (dean [at] fourwalledcubicle [dot] com)
- Permission to use, copy, modify, distribute, and sell this
- software and its documentation for any purpose is hereby granted
- without fee, provided that the above copyright notice appear in
- all copies and that both that the copyright notice and this
- permission notice and warranty disclaimer appear in supporting
- documentation, and that the name of the author not be used in
- advertising or publicity pertaining to distribution of the
- software without specific, written prior permission.
- The author disclaims all warranties with regard to this
- software, including all implied warranties of merchantability
- and fitness. In no event shall the author be liable for any
- special, indirect or consequential damages or any damages
- whatsoever resulting from loss of use, data or profits, whether
- in an action of contract, negligence or other tortious action,
- arising out of or in connection with the use or performance of
- this software.
- */
- #include "usbcas.h"
- /*
- * Input and output ring buffers
- */
- static RingBuffer_t u2c_ringbuf; /* USB -> CDC */
- static uint8_t u2c_buffer[128];
- static RingBuffer_t c2u_ringbuf; /* CDC -> USB */
- static uint8_t c2u_buffer[128];
- /**
- * LUFA CDC Class driver interface configuration and state
- * information. This structure is passed to all CDC Class driver
- * functions, so that multiple instances of the same class within a
- * device can be differentiated from one another.
- *
- * "vcpif" stands for "Virtual COM Port Interface".
- */
- USB_ClassInfo_CDC_Device_t vcpif = {
- .Config = {
- .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI,
- .DataINEndpoint = {
- .Address = CDC_TX_EPADDR,
- .Size = CDC_TXRX_EPSIZE,
- .Banks = 1,
- },
- .DataOUTEndpoint = {
- .Address = CDC_RX_EPADDR,
- .Size = CDC_TXRX_EPSIZE,
- .Banks = 1,
- },
- .NotificationEndpoint =
- {
- .Address = CDC_NOTIFICATION_EPADDR,
- .Size = CDC_NOTIFICATION_EPSIZE,
- .Banks = 1,
- },
- },
- };
- /*
- * Probe for modem control status bits and send them if changed,
- * or if forced. DSR is always set, DCD indicates if the cassette
- * motor relay is active.
- */
- static void update_modem_status(USB_ClassInfo_CDC_Device_t * const cii,
- bool forced)
- {
- uint16_t lines = CDC_CONTROL_LINE_IN_DSR;
- if (!(PINB & (1 << 4))) /* Cassette relay active */
- lines |= CDC_CONTROL_LINE_IN_DCD;
- #if 0
- if (lines & CDC_CONTROL_LINE_IN_DCD)
- PORTB &= ~(1U << 0);
- else
- PORTB |= (1U << 0);
- #endif
- if (forced || cii->State.ControlLineStates.DeviceToHost != lines) {
- cii->State.ControlLineStates.DeviceToHost = lines;
- CDC_Device_SendControlLineStateChange(cii);
- }
- }
- /*
- * This is called in the main loop, but also any time we are busy-waiting
- * on something.
- */
- void do_usb_task(void)
- {
- /* Update modem flags if needed */
- update_modem_status(&vcpif, false);
- CDC_Device_USBTask(&vcpif);
- USB_USBTask();
- }
- /*
- * Try to send the next byte of data to the host, abort without
- * dequeuing if there is an error. Returns true on error.
- */
- static bool usb_send_next_byte(void)
- {
- uint8_t byte = RingBuffer_Peek(&c2u_ringbuf);
- if (CDC_Device_SendByte(&vcpif, byte) != ENDPOINT_READYWAIT_NoError)
- return true;
- /*
- * Dequeue the already sent byte from the buffer now we have
- * confirmed that no transmission error occurred
- */
- RingBuffer_Remove(&c2u_ringbuf);
- return false;
- }
- /*
- * Main program entry point. This routine contains the overall program
- * flow, including initial setup of
- * all components and the main program
- * loop.
- */
- int main(void)
- {
- SetupHardware();
- RingBuffer_InitBuffer(&u2c_ringbuf, u2c_buffer, sizeof(u2c_buffer));
- RingBuffer_InitBuffer(&c2u_ringbuf, c2u_buffer, sizeof(c2u_buffer));
- GlobalInterruptEnable();
- for (;;)
- {
- /*
- * Only try to read in bytes from the CDC interface if
- * the transmit buffer is not full
- */
- if (!(RingBuffer_IsFull(&u2c_ringbuf))) {
- int byte;
- byte = CDC_Device_ReceiveByte(&vcpif);
- /*
- * Store received byte into the transmit buffer, then
- * enable the transmit ISR if disabled
- */
- if (byte >= 0) {
- RingBuffer_Insert(&u2c_ringbuf, byte);
- fmtx_enable();
- }
- }
- uint16_t outbytes = RingBuffer_GetCount(&c2u_ringbuf);
- if (outbytes) {
- Endpoint_SelectEndpoint(vcpif.Config.DataINEndpoint.Address);
- /*
- * Check if a packet is already enqueued to
- * the host - if so, we shouldn't try to send
- * more data until it completes as there is a
- * chance nothing is listening and a lengthy
- * timeout could occur
- */
- if (Endpoint_IsINReady()) {
- /*
- * Never send more than one bank size
- * less one byte to the host at a
- * time, so that we don't block while
- * a Zero Length Packet (ZLP) to
- * terminate the transfer is sent if
- * the host isn't listening */
- outbytes = MIN(outbytes, (CDC_TXRX_EPSIZE - 1));
- /*
- * Transfer output bytes to the USB
- * endpoint. Abort without dequeuing if
- * there is an error.
- */
- while (outbytes--)
- if (usb_send_next_byte())
- break;
- }
- }
- do_usb_task();
- }
- }
- static uint32_t current_baudrate;
- /** Configures the board hardware and chip peripherals for the demo's functionality. */
- void SetupHardware(void)
- {
- #if (ARCH == ARCH_AVR8)
- /* Disable watchdog if enabled by bootloader/fuses */
- MCUSR &= ~(1 << WDRF);
- wdt_disable();
- /* Disable clock division */
- clock_prescale_set(clock_div_1);
- #endif
- /* Hardware Initialization */
- USB_Init();
- /* PORTB: PB0 - RXLED, PB4 - Relay (pullup), PB5 - relay out */
- PORTB |= (1 << 0) | (1 << 4);
- DDRB = (DDRB & ~((1 << 4))) | ((1 << 0) | (1 << 5));
- /* Initialize receiver and transmitter */
- fmrx_init();
- current_baudrate = fmtx_real_baudrate(0);
- fmrx_set_speed(current_baudrate);
- fmtx_init_speed(current_baudrate);
- }
- /** Event handler for the library USB Connection event. */
- void EVENT_USB_Device_Connect(void)
- {
- }
- /** Event handler for the library USB Disconnection event. */
- void EVENT_USB_Device_Disconnect(void)
- {
- }
- /** Event handler for the library USB Configuration Changed event. */
- void EVENT_USB_Device_ConfigurationChanged(void)
- {
- bool success = true;
- success &= CDC_Device_ConfigureEndpoints(&vcpif);
- if (success)
- update_modem_status(&vcpif, true);
- }
- /** Event handler for the library USB Control Request reception event. */
- void EVENT_USB_Device_ControlRequest(void)
- {
- CDC_Device_ProcessControlRequest(&vcpif);
- }
- /*
- * Called from the demodulation ISR to push a new byte into the buffer
- */
- void fmrx_recv_byte(uint8_t byte)
- {
- if (USB_DeviceState == DEVICE_STATE_Configured &&
- !RingBuffer_IsFull(&c2u_ringbuf))
- RingBuffer_Insert(&c2u_ringbuf, byte);
- }
- /*
- * Called from the transmit register empty ISR to fetch a new byte;
- * returns -1 if the ring buffer is empty.
- */
- int fmtx_next_byte(void)
- {
- if (USB_DeviceState != DEVICE_STATE_Configured ||
- RingBuffer_IsEmpty(&u2c_ringbuf))
- return -1;
- return RingBuffer_Remove(&u2c_ringbuf);
- }
- /*
- * Event handler for the CDC Class driver Line Encoding Changed event.
- */
- void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const cii)
- {
- uint32_t baudrate = cii->State.LineEncoding.BaudRateBPS;
- /* This is a hack to give a sensible default */
- if (baudrate == 9600)
- baudrate = CAS_BAUDRATE_ABC80;
- baudrate = fmtx_real_baudrate(baudrate);
- if (1 || baudrate != current_baudrate) {
- current_baudrate = baudrate;
- fmtx_drain();
- fmrx_set_speed(current_baudrate);
- fmtx_init_speed(current_baudrate);
- }
- }
- /*
- * Event handler for control line changes
- */
- void EVENT_CDC_Device_ControLineStateChanged(USB_ClassInfo_CDC_Device_t* const cii)
- {
- }
|