@@ -261,18 +261,28 @@ static ONE_DESCRIPTOR String_Descriptor[N_STRING_DESCRIPTORS] = {
261261
262262/* I/O state */
263263
264- #define CDC_SERIAL_BUFFER_SIZE 512
264+ #define CDC_SERIAL_RX_BUFFER_SIZE 256 // must be power of 2
265+ #define CDC_SERIAL_RX_BUFFER_SIZE_MASK (CDC_SERIAL_RX_BUFFER_SIZE-1)
265266
266267/* Received data */
267- static volatile uint8 vcomBufferRx [CDC_SERIAL_BUFFER_SIZE ];
268- /* Read index into vcomBufferRx */
269- static volatile uint32 rx_offset = 0 ;
270- /* Number of bytes left to transmit */
271- static volatile uint32 n_unsent_bytes = 0 ;
272- /* Are we currently sending an IN packet? */
273- static volatile uint8 transmitting = 0 ;
274- /* Number of unread bytes */
275- static volatile uint32 n_unread_bytes = 0 ;
268+ static volatile uint8 vcomBufferRx [CDC_SERIAL_RX_BUFFER_SIZE ];
269+ /* Write index to vcomBufferRx */
270+ static volatile uint32 rx_head ;
271+ /* Read index from vcomBufferRx */
272+ static volatile uint32 rx_tail ;
273+
274+ #define CDC_SERIAL_TX_BUFFER_SIZE 256 // must be power of 2
275+ #define CDC_SERIAL_TX_BUFFER_SIZE_MASK (CDC_SERIAL_TX_BUFFER_SIZE-1)
276+ // Tx data
277+ static volatile uint8 vcomBufferTx [CDC_SERIAL_TX_BUFFER_SIZE ];
278+ // Write index to vcomBufferTx
279+ static volatile uint32 tx_head ;
280+ // Read index from vcomBufferTx
281+ static volatile uint32 tx_tail ;
282+ // Are we currently sending an IN packet?
283+ static volatile int8 transmitting ;
284+
285+
276286
277287/* Other state (line coding, DTR/RTS) */
278288
@@ -393,100 +403,108 @@ void usb_cdcacm_putc(char ch) {
393403 ;
394404}
395405
396- /* This function is blocking.
406+ /* This function is non- blocking.
397407 *
398- * It copies data from a usercode buffer into the USB peripheral TX
408+ * It copies data from a user buffer into the USB peripheral TX
399409 * buffer, and returns the number of bytes copied. */
400- uint32 usb_cdcacm_tx (const uint8 * buf , uint32 len ) {
401- /* Last transmission hasn't finished, so abort. */
402- while ( usb_cdcacm_is_transmitting () > 0 ) ; // wait for end of transmission
410+ uint32 usb_cdcacm_tx (const uint8 * buf , uint32 len )
411+ {
412+ if ( len == 0 ) return 0 ; // no data to send
403413
404- /* We can only put USB_CDCACM_TX_EPSIZE bytes in the buffer. */
405- if (len > USB_CDCACM_TX_EPSIZE ) {
406- len = USB_CDCACM_TX_EPSIZE ;
407- }
414+ uint32 head = tx_head ; // load volatile variable
415+ uint32 tx_unsent = (head - tx_tail ) & CDC_SERIAL_TX_BUFFER_SIZE_MASK ;
408416
409- /* Queue bytes for sending. */
410- if (len ) {
411- usb_copy_to_pma ( buf , len , USB_CDCACM_TX_ADDR );
417+ // We can only put bytes in the buffer if there is place
418+ if (len > ( CDC_SERIAL_TX_BUFFER_SIZE - tx_unsent - 1 ) ) {
419+ len = ( CDC_SERIAL_TX_BUFFER_SIZE - tx_unsent - 1 );
412420 }
413- // We still need to wait for the interrupt, even if we're sending
414- // zero bytes. (Sending zero-size packets is useful for flushing
415- // host-side buffers.)
416- usb_set_ep_tx_count (USB_CDCACM_TX_ENDP , len );
417- n_unsent_bytes = len ;
418- transmitting = 1 ;
419- usb_set_ep_tx_stat (USB_CDCACM_TX_ENDP , USB_EP_STAT_TX_VALID );
421+ if (len == 0 ) return 0 ; // buffer full
422+
423+ uint16 i ;
424+ // copy data from user buffer to USB Tx buffer
425+ for (i = 0 ; i < len ; i ++ ) {
426+ vcomBufferTx [head ] = buf [i ];
427+ head = (head + 1 ) & CDC_SERIAL_TX_BUFFER_SIZE_MASK ;
428+ }
429+ tx_head = head ; // store volatile variable
430+
431+ if (transmitting < 0 ) {
432+ vcomDataTxCb (); // initiate data transmission
433+ }
420434
421435 return len ;
422436}
423437
424438
425439
426440uint32 usb_cdcacm_data_available (void ) {
427- return n_unread_bytes ;
441+ return ( rx_head - rx_tail ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
428442}
429443
430444uint8 usb_cdcacm_is_transmitting (void ) {
431- return transmitting ;
445+ return ( transmitting > 0 ? transmitting : 0 ) ;
432446}
433447
434448uint16 usb_cdcacm_get_pending (void ) {
435- return n_unsent_bytes ;
449+ return ( tx_head - tx_tail ) & CDC_SERIAL_TX_BUFFER_SIZE_MASK ;
436450}
437451
438- /* Nonblocking byte receive.
452+ /* Non-blocking byte receive.
439453 *
440454 * Copies up to len bytes from our private data buffer (*NOT* the PMA)
441455 * into buf and deq's the FIFO. */
442- uint32 usb_cdcacm_rx (uint8 * buf , uint32 len ) {
456+ uint32 usb_cdcacm_rx (uint8 * buf , uint32 len )
457+ {
443458 /* Copy bytes to buffer. */
444459 uint32 n_copied = usb_cdcacm_peek (buf , len );
445460
446461 /* Mark bytes as read. */
447- n_unread_bytes -= n_copied ;
448- rx_offset = (rx_offset + n_copied ) % CDC_SERIAL_BUFFER_SIZE ;
462+ uint16 tail = rx_tail ; // load volatile variable
463+ tail = (tail + n_copied ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
464+ rx_tail = tail ; // store volatile variable
449465
450- /* If all bytes have been read, re-enable the RX endpoint, which
451- * was set to NAK when the current batch of bytes was received. */
452- if (n_unread_bytes == 0 ) {
453- usb_set_ep_rx_count (USB_CDCACM_RX_ENDP , USB_CDCACM_RX_EPSIZE );
466+ uint32 rx_unread = (rx_head - tail ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
467+ // If buffer was emptied to a pre-set value, re-enable the RX endpoint
468+ if ( rx_unread <= 64 ) { // experimental value, gives the best performance
454469 usb_set_ep_rx_stat (USB_CDCACM_RX_ENDP , USB_EP_STAT_RX_VALID );
455- }
456-
470+ }
457471 return n_copied ;
458472}
459473
460- /* Nonblocking byte lookahead.
474+ /* Non-blocking byte lookahead.
461475 *
462476 * Looks at unread bytes without marking them as read. */
463- uint32 usb_cdcacm_peek (uint8 * buf , uint32 len ) {
477+ uint32 usb_cdcacm_peek (uint8 * buf , uint32 len )
478+ {
464479 int i ;
465- uint32 head = rx_offset ;
480+ uint32 tail = rx_tail ;
481+ uint32 rx_unread = (rx_head - tail ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
466482
467- if (len > n_unread_bytes ) {
468- len = n_unread_bytes ;
483+ if (len > rx_unread ) {
484+ len = rx_unread ;
469485 }
470486
471487 for (i = 0 ; i < len ; i ++ ) {
472- buf [i ] = vcomBufferRx [head ];
473- head = (head + 1 ) % CDC_SERIAL_BUFFER_SIZE ;
488+ buf [i ] = vcomBufferRx [tail ];
489+ tail = (tail + 1 ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
474490 }
475491
476492 return len ;
477493}
478494
479- uint32 usb_cdcacm_peek_ex (uint8 * buf , uint32 offset , uint32 len ) {
495+ uint32 usb_cdcacm_peek_ex (uint8 * buf , uint32 offset , uint32 len )
496+ {
480497 int i ;
481- uint32 head = (rx_offset + offset ) % CDC_SERIAL_BUFFER_SIZE ;
498+ uint32 tail = (rx_tail + offset ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
499+ uint32 rx_unread = (rx_head - tail ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
482500
483- if (len + offset > n_unread_bytes ) {
484- len = n_unread_bytes - offset ;
501+ if (len + offset > rx_unread ) {
502+ len = rx_unread - offset ;
485503 }
486504
487505 for (i = 0 ; i < len ; i ++ ) {
488- buf [i ] = vcomBufferRx [head ];
489- head = (head + 1 ) % CDC_SERIAL_BUFFER_SIZE ;
506+ buf [i ] = vcomBufferRx [tail ];
507+ tail = (tail + 1 ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
490508 }
491509
492510 return len ;
@@ -495,12 +513,12 @@ uint32 usb_cdcacm_peek_ex(uint8* buf, uint32 offset, uint32 len) {
495513/* Roger Clark. Added. for Arduino 1.0 API support of Serial.peek() */
496514int usb_cdcacm_peek_char ()
497515{
498- if (n_unread_bytes == 0 )
516+ if (usb_cdcacm_data_available () == 0 )
499517 {
500518 return -1 ;
501519 }
502520
503- return vcomBufferRx [rx_offset ];
521+ return vcomBufferRx [rx_tail ];
504522}
505523
506524uint8 usb_cdcacm_get_dtr () {
@@ -534,41 +552,75 @@ int usb_cdcacm_get_n_data_bits(void) {
534552 return line_coding .bDataBits ;
535553}
536554
537-
538555/*
539556 * Callbacks
540557 */
541-
542- static void vcomDataTxCb (void ) {
543- n_unsent_bytes = 0 ;
544- transmitting = 0 ;
558+ static void vcomDataTxCb (void )
559+ {
560+ uint32 tail = tx_tail ; // load volatile variable
561+ uint32 tx_unsent = (tx_head - tail ) & CDC_SERIAL_TX_BUFFER_SIZE_MASK ;
562+ if (tx_unsent == 0 ) {
563+ if ( (-- transmitting )== 0 ) goto flush ; // no more data to send
564+ return ; // it was already flushed, keep Tx endpoint disabled
565+ }
566+ transmitting = 1 ;
567+ // We can only send up to USB_CDCACM_TX_EPSIZE bytes in the endpoint.
568+ if (tx_unsent > USB_CDCACM_TX_EPSIZE ) {
569+ tx_unsent = USB_CDCACM_TX_EPSIZE ;
570+ }
571+ // copy the bytes from USB Tx buffer to PMA buffer
572+ uint32 * dst = usb_pma_ptr (USB_CDCACM_TX_ADDR );
573+ uint16 tmp = 0 ;
574+ uint16 val ;
575+ int i ;
576+ for (i = 0 ; i < tx_unsent ; i ++ ) {
577+ val = vcomBufferTx [tail ];
578+ tail = (tail + 1 ) & CDC_SERIAL_TX_BUFFER_SIZE_MASK ;
579+ if (i & 1 ) {
580+ * dst ++ = tmp | (val <<8 );
581+ } else {
582+ tmp = val ;
583+ }
584+ }
585+ if ( tx_unsent & 1 ) {
586+ * dst = tmp ;
587+ }
588+ tx_tail = tail ; // store volatile variable
589+ flush :
590+ // enable Tx endpoint
591+ usb_set_ep_tx_count (USB_CDCACM_TX_ENDP , tx_unsent );
592+ usb_set_ep_tx_stat (USB_CDCACM_TX_ENDP , USB_EP_STAT_TX_VALID );
545593}
546594
547- static void vcomDataRxCb (void ) {
548- uint32 ep_rx_size ;
549- uint32 tail = (rx_offset + n_unread_bytes ) % CDC_SERIAL_BUFFER_SIZE ;
550- uint8 ep_rx_data [USB_CDCACM_RX_EPSIZE ];
551- uint32 i ;
552-
553- usb_set_ep_rx_stat (USB_CDCACM_RX_ENDP , USB_EP_STAT_RX_NAK );
554- ep_rx_size = usb_get_ep_rx_count (USB_CDCACM_RX_ENDP );
555- /* This copy won't overwrite unread bytes, since we've set the RX
556- * endpoint to NAK, and will only set it to VALID when all bytes
557- * have been read. */
558- usb_copy_from_pma ((uint8 * )ep_rx_data , ep_rx_size ,
559- USB_CDCACM_RX_ADDR );
560595
596+ static void vcomDataRxCb (void )
597+ {
598+ uint32 head = rx_head ; // load volatile variable
599+
600+ uint32 ep_rx_size = usb_get_ep_rx_count (USB_CDCACM_RX_ENDP );
601+ // This copy won't overwrite unread bytes as long as there is
602+ // enough room in the USB Rx buffer for next packet
603+ uint32 * src = usb_pma_ptr (USB_CDCACM_RX_ADDR );
604+ uint16 tmp = 0 ;
605+ uint8 val ;
606+ uint32 i ;
561607 for (i = 0 ; i < ep_rx_size ; i ++ ) {
562- vcomBufferRx [tail ] = ep_rx_data [i ];
563- tail = (tail + 1 ) % CDC_SERIAL_BUFFER_SIZE ;
608+ if (i & 1 ) {
609+ val = tmp >>8 ;
610+ } else {
611+ tmp = * src ++ ;
612+ val = tmp & 0xFF ;
613+ }
614+ vcomBufferRx [head ] = val ;
615+ head = (head + 1 ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
564616 }
617+ rx_head = head ; // store volatile variable
565618
566- n_unread_bytes += ep_rx_size ;
567-
568- if ( n_unread_bytes == 0 ) {
569- usb_set_ep_rx_count (USB_CDCACM_RX_ENDP , USB_CDCACM_RX_EPSIZE );
570- usb_set_ep_rx_stat (USB_CDCACM_RX_ENDP , USB_EP_STAT_RX_VALID );
571- }
619+ uint32 rx_unread = (head - rx_tail ) & CDC_SERIAL_RX_BUFFER_SIZE_MASK ;
620+ // only enable further Rx if there is enough room to receive one more packet
621+ if ( rx_unread < (CDC_SERIAL_RX_BUFFER_SIZE - USB_CDCACM_RX_EPSIZE ) ) {
622+ usb_set_ep_rx_stat (USB_CDCACM_RX_ENDP , USB_EP_STAT_RX_VALID );
623+ }
572624
573625 if (rx_hook ) {
574626 rx_hook (USB_CDCACM_HOOK_RX , 0 );
@@ -646,10 +698,11 @@ static void usbReset(void) {
646698 SetDeviceAddress (0 );
647699
648700 /* Reset the RX/TX state */
649- n_unread_bytes = 0 ;
650- n_unsent_bytes = 0 ;
651- rx_offset = 0 ;
652- transmitting = 0 ;
701+ rx_head = 0 ;
702+ rx_tail = 0 ;
703+ tx_head = 0 ;
704+ tx_tail = 0 ;
705+ transmitting = -1 ;
653706}
654707
655708static RESULT usbDataSetup (uint8 request ) {
0 commit comments