/* This work is licensed under the Creative Commons Attribution 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. */ // JTR V0.1a // JTR v0.1b // JTR v0.1c // tidy up added code line code handler to set the SPBRG based on line coding. // JTR V0.2a // 26th Jan 2012 #include "usb_stack_globals.h" // USB stack only defines Not function related. #include enum stopbits { one = 0, oneandahalf = 1, two = 2 }; enum parity { none = 0, odd = 1, even = 2, mark = 3, space = 4 }; const char parity_str[] = {'N', 'O', 'E', 'M', 'S'}; struct cdc_LineCodeing { unsigned long int dwDTERate; enum stopbits bCharFormat; enum parity bParityType; BYTE bDataBits; } linecodeing; #pragma udata usb_data BYTE cdc_acm_in_buffer[CDC_NOTICE_BUFFER_SIZE]; //JTR NEWLY defined NOTICE BUFFER SIZE and increased from 8 to 10 bytes in usb_config.h #pragma udata usb_data3 BYTE cdc_In_bufferA[CDC_BUFFER_SIZE]; BYTE cdc_In_bufferB[CDC_BUFFER_SIZE]; BYTE cdc_Out_bufferA[CDC_BUFFER_SIZE]; BYTE cdc_Out_bufferB[CDC_BUFFER_SIZE]; #pragma udata struct _cdc_ControlLineState cls; BYTE cdc_In_len; // total cdc In length volatile BYTE cdc_Out_len; // total cdc out length BYTE IsInBufferA; BYTE IsOutBufferA; BYTE *InPtr; BYTE *OutPtr; BYTE LineStateUpdated = 0; BYTE cdc_timeout_count = 0; BYTE ZLPpending = 0; BYTE lock = 0; BDentry *CDC_Outbdp, *CDC_Inbdp; BYTE CDCFunctionError; volatile BYTE cdc_trf_state; // JTR don't see that it is really volatile in current context may be in future. void initCDC(void) { // JTR The function usb_init() is now called from main.c prior to anything else belonging to the CDC CLASS // If we have multiple functions we want the USB initialization to be in only one consistant place. // The sort of things we would do in InitCDC would be to setup I/O pins and the HARDWARE UART so it // is not transmitting junk between a RESET and the device being enumerated. Hardware CTS/RTS // would also be setup here if being used. linecodeing.dwDTERate = 115200; linecodeing.bCharFormat = one; linecodeing.bParityType = none; linecodeing.bDataBits = 8; cls.DTR = 0; cls.RTS = 0; usb_register_class_setup_handler(cdc_setup); } void user_configured_init(void) { // JTR NEW FUNCTION // After the device is enumerated and configured then we set up non EP0 endpoints. // We only enable the endpoints we are using, not all of them. // Prior to this they are held in a disarmed state. // This function belongs to the current USB function and IS NOT generic. This is CLASS specific // and will vary from implementation to implementation. usb_unset_in_handler(1); usb_unset_in_handler(2); usb_unset_out_handler(2); USB_UEP1 = USB_EP_IN; USB_UEP2 = USB_EP_INOUT; /* Configure buffer descriptors */ #if USB_PP_BUF_MODE == 0 // JTR Setup CDC LINE_NOTICE EP (Interrupt IN) usb_bdt[USB_CALC_BD(1, USB_DIR_IN, USB_PP_EVEN)].BDCNT = 0; usb_bdt[USB_CALC_BD(1, USB_DIR_IN, USB_PP_EVEN)].BDADDR = cdc_acm_in_buffer; usb_bdt[USB_CALC_BD(1, USB_DIR_IN, USB_PP_EVEN)].BDSTAT = DTS + DTSEN; // Set DTS => First packet inverts, ie. is Data0 #else // TODO: Implement Ping-Pong buffering setup. #error "PP Mode not implemented yet" #endif usb_register_class_setup_handler(cdc_setup); cdc_trf_state = 0; CDC_Outbdp = &usb_bdt[USB_CALC_BD(2, USB_DIR_OUT, USB_PP_EVEN)]; CDC_Inbdp = &usb_bdt[USB_CALC_BD(2, USB_DIR_IN, USB_PP_EVEN)]; IsInBufferA = 0xFF; InPtr = cdc_In_bufferA; cdc_In_len = 0; CDC_Inbdp->BDADDR = &cdc_In_bufferA[0]; CDC_Inbdp->BDCNT = 0; CDC_Inbdp->BDSTAT = DTS + DTSEN; cdc_Out_len = 0; IsOutBufferA = 0xFF; OutPtr = cdc_Out_bufferA; CDC_Outbdp->BDCNT = CDC_BUFFER_SIZE; CDC_Outbdp->BDADDR = &cdc_Out_bufferA[0]; CDC_Outbdp->BDSTAT = UOWN + DTSEN; } void cdc_setup(void) { BYTE *packet; size_t reply_len; packet = EP0_Outbdp->BDADDR; switch (packet[USB_bmRequestType] & (USB_bmRequestType_TypeMask | USB_bmRequestType_RecipientMask)) { case (USB_bmRequestType_Class | USB_bmRequestType_Interface): switch (packet[USB_bRequest]) { //JTR This is just a dummy, nothing defined to do for CDC ACM case CDC_SEND_ENCAPSULATED_COMMAND: usb_ack_dat1(0); break; //JTR This is just a dummy, nothing defined to do for CDC ACM case CDC_GET_ENCAPSULATED_RESPONSE: //usb_ack_zero(rbdp); usb_ack_dat1(0); break; case CDC_SET_COMM_FEATURE: // Optional case CDC_GET_COMM_FEATURE: // Optional case CDC_CLEAR_COMM_FEATURE: // Optional usb_RequestError(); // Not advertised in ACM functional descriptor break; case CDC_SET_LINE_CODING: // Optional, strongly recomended usb_set_out_handler(0, cdc_set_line_coding_data); // Register out handler function break; case CDC_GET_LINE_CODING: // Optional, strongly recomended // JTR reply length (7) is always going to be less than minimum EP0 size (8) reply_len = *((unsigned int *) &packet[USB_wLength]); if (sizeof (struct cdc_LineCodeing) < reply_len) { reply_len = sizeof (struct cdc_LineCodeing); } memcpy(EP0_Inbdp->BDADDR, (const void *) &linecodeing, reply_len); usb_ack_dat1(reply_len); // JTR common addition for STD and CLASS ACK usb_set_in_handler(0, cdc_get_line_coding); break; case CDC_SET_CONTROL_LINE_STATE: // Optional cls = *((struct _cdc_ControlLineState *) &packet[USB_wValue]); usb_set_in_handler(0, cdc_set_control_line_state_status); // JTR why bother? usb_ack_dat1(0); // JTR common addition for STD and CLASS ACK LineStateUpdated = 1; break; case CDC_SEND_BREAK: // Optional default: usb_RequestError(); } break; default: usb_RequestError(); } } void cdc_get_line_coding(void) { usb_unset_in_handler(0); // Unregister IN handler; } void cdc_set_line_coding_data(void) { // JTR handling an OUT token In the CDC stack this is the only function that handles an OUT data stage. unsigned long dwBaud, dwBaudrem; memcpy(&linecodeing, (const void *) EP0_Outbdp->BDADDR, sizeof (struct cdc_LineCodeing)); dwBaud = BAUDCLOCK_FREQ / linecodeing.dwDTERate; dwBaudrem = BAUDCLOCK_FREQ % linecodeing.dwDTERate; if (linecodeing.dwDTERate > (dwBaudrem << 1)) dwBaud--; UART_BAUD_setup(dwBaud); usb_unset_out_handler(0); // Unregister OUT handler; JTR serious bug fix in macro! usb_set_in_handler(0, cdc_set_line_coding_status); // JTR why bother? usb_ack_dat1(0); // JTR common addition for STD and CLASS ACK // JTR This part of the USB-CDC stack is worth highlighting // This is the only place that we have an OUT DATA packet on // EP0. At this point it has been completed. This stack unlike // the microchip stack does not have a common IN or OUT data // packet complete tail and therefore it is the responsibility // of each section to ensure that EP0 is set-up correctly for // the next setup packet. // Force EP0 OUT to the DAT0 state // after we have all our data packets. EP0_Outbdp->BDCNT = USB_EP0_BUFFER_SIZE; EP0_Outbdp->BDSTAT = UOWN | DTSEN; } void cdc_set_line_coding_status(void) { usb_unset_in_handler(0); } void cdc_set_control_line_state_status(void) { usb_unset_in_handler(0); } /*****************************************************************************/ void WaitOutReady() // JTR2 added reduced overhead { while ((CDC_Outbdp->BDSTAT & UOWN)); } /******************************************************************************/ void WaitInReady() // JTR2 added reduced overhead { while ((CDC_Inbdp->BDSTAT & UOWN)); }//end WaitInReady /******************************************************************************/ BYTE getOutReady(void) { return !(CDC_Outbdp->BDSTAT & UOWN); // Do we have a packet from host? } /******************************************************************************/ BYTE getInReady(void) { return !(CDC_Inbdp->BDSTAT & UOWN); // Is the CDC In buffer ready? } /******************************************************************************/ BYTE getda_cdc(void) { CDCFunctionError = 0; WaitOutReady(); if ((IsOutBufferA & 1)) { OutPtr = &cdc_Out_bufferA[0]; CDC_Outbdp->BDADDR = &cdc_Out_bufferB[0]; } else { OutPtr = &cdc_Out_bufferB[0]; CDC_Outbdp->BDADDR = &cdc_Out_bufferA[0]; } IsOutBufferA ^= 0xFF; cdc_Out_len = CDC_Outbdp->BDCNT; CDC_Outbdp->BDCNT = CDC_BUFFER_SIZE; CDC_Outbdp->BDSTAT = ((CDC_Outbdp->BDSTAT ^ DTS) & DTS) | UOWN | DTSEN; #ifndef USB_INTERRUPTS usb_handler(); #endif return cdc_Out_len; }//end getCDC_Out_ArmNext BYTE putda_cdc(BYTE count) { // CDCFunctionError = 0; // WaitInReady(); while ((CDC_Inbdp->BDSTAT & UOWN)); if (IsInBufferA) { CDC_Inbdp->BDADDR = cdc_In_bufferA; InPtr = cdc_In_bufferB; } else { CDC_Inbdp->BDADDR = cdc_In_bufferB; InPtr = cdc_In_bufferA; } CDC_Inbdp->BDCNT = count; CDC_Inbdp->BDSTAT = ((CDC_Inbdp->BDSTAT ^ DTS) & DTS) | UOWN | DTSEN; IsInBufferA ^= 0xFF; #ifndef USB_INTERRUPTS usb_handler(); #endif return 0; //CDCFunctionError; } void SendZLP(void) { putda_cdc(0); } /******************************************************************************/ void CDC_Flush_In_Now(void) { if (cdc_In_len > 0) { while (!getInReady()); putda_cdc(cdc_In_len); if (cdc_In_len == CDC_BUFFER_SIZE) { ZLPpending = 1; } else { ZLPpending = 0; } cdc_In_len = 0; cdc_timeout_count = 0; } } /******************************************************************************/ void CDCFlushOnTimeout(void) { if (cdc_timeout_count >= CDC_FLUSH_MS) { // For timeout value see: cdc_config.h -> [hardware] -> CDC_FLUSH_MS if (cdc_In_len > 0) { if ((lock == 0) && getInReady()) { putda_cdc(cdc_In_len); if (cdc_In_len == CDC_BUFFER_SIZE) { ZLPpending = 1; } else { ZLPpending = 0; } cdc_In_len = 0; cdc_timeout_count = 0; } } else if (ZLPpending) { putda_cdc(0); ZLPpending = 0; cdc_timeout_count = 0; } } else { cdc_timeout_count++; } } /******************************************************************************/ void putc_cdc(BYTE c) { lock = 1; // Stops CDCFlushOnTimeout() from sending per chance it is on interrupts. *InPtr = c; InPtr++; cdc_In_len++; ZLPpending = 0; if (cdc_In_len == CDC_BUFFER_SIZE) { putda_cdc(cdc_In_len); // This will stall tranfers if both buffers are full then return when a buffer is available. cdc_In_len = 0; ZLPpending = 1; // timeout handled in the SOF handler below. } lock = 0; cdc_timeout_count = 0; //setup timer to throw data if the buffer doesn't fill } /******************************************************************************/ // Waits for a byte to be available and returns that byte as a // function return value. The byte is removed from the CDC OUT queue. // No return count is required as this function always returns one byte. BYTE getc_cdc(void) { // Must be used only in double buffer mode. BYTE c = 0; if (cdc_Out_len == 0) { do { cdc_Out_len = getda_cdc(); } while (cdc_Out_len == 0); // Skip any ZLP } c = *OutPtr; OutPtr++; cdc_Out_len--; return c; } /******************************************************************************/ // Checks to see if there is a byte available in the CDC buffer. // If so, it returns that byte at the dereferenced pointer *C // and the function returns a count of 1. The byte is effectively // removed from the queue. // IF no byte is available function returns immediately with a count of zero. BYTE poll_getc_cdc(BYTE * c) { // Must be used only in double buffer mode. if (cdc_Out_len) { // Do we have a byte waiting? *c = *OutPtr; // pass it on and adjust OutPtr and count OutPtr++; cdc_Out_len--; return 1; // Return byte count, always one. } if (getOutReady()) { // No byte in queue check for new arrivals. cdc_Out_len = getda_cdc(); if (cdc_Out_len) { *c = *OutPtr; OutPtr++; cdc_Out_len--; return 1; } } return 0; } /******************************************************************************/ // Checks (PEEKS) to see if there is a byte available in the CDC buffer. // If so, it returns that byte at the dereferenced pointer *C // and the function returns a count of 1. The byte however is NOT // removed from the queue and can still be read with the poll_getc_cdc() // and getc_cdc() functions that will remove it from the queue. // IF no byte is available function returns immediately with a count of zero. BYTE peek_getc_cdc(BYTE * c) { // Must be used only in double buffer mode. if (cdc_Out_len) { *c = *OutPtr; return 1; } if (getOutReady()) { cdc_Out_len = getda_cdc(); if (cdc_Out_len) { *c = *OutPtr; return 1; } } return 0; }