text: /* Teensyduino Core Library
   * http://www.pjrc.com/teensy/
   * Copyright (c) 2017 PJRC.COM, LLC.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * 1. The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * 2. If the Software is incorporated into a build system that allows
   * selection among a list of target devices, then similar target
   * devices manufactured by PJRC.COM must be included in the list of
   * target devices and selectable in the same manner.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  // WMXZ 2022
  #include <stdint.h>
  #include <usb_desc.h>
  #ifdef AUDIO_INTERFACE
  
  extern volatile uint8_t usb_high_speed;
  static void rx_event(transfer_t *t);
  static void tx_event(transfer_t *t);
  
  /*static*/ transfer_t rx_transfer __attribute__ ((used, aligned(32)));
  /*static*/ transfer_t sync_transfer __attribute__ ((used, aligned(32)));
  /*static*/ transfer_t tx_transfer __attribute__ ((used, aligned(32)));
  DMAMEM static uint8_t rx_buffer[AUDIO_RX_SIZE] __attribute__ ((aligned(32)));
  DMAMEM static uint8_t tx_buffer[AUDIO_RX_SIZE] __attribute__ ((aligned(32)));
  DMAMEM uint32_t usb_audio_sync_feedback __attribute__ ((aligned(32)));
  
  uint8_t usb_audio_receive_setting=0;
  uint8_t usb_audio_transmit_setting=0;
  uint8_t usb_audio_sync_nbytes;
  uint8_t usb_audio_sync_rshift;
  
  uint32_t feedback_accumulator;
  
  volatile uint32_t usb_audio_underrun_count;
  volatile uint32_t usb_audio_overrun_count;
  
  
  static void rx_event(transfer_t *t)
  {
  	if (t) {
  		int len = AUDIO_RX_SIZE - ((rx_transfer.status >> 16) & 0x7FFF);
  		printf("rx %u\n", len);
  		usb_audio_receive_callback(len);
  	}
  	usb_prepare_transfer(&rx_transfer, rx_buffer, AUDIO_RX_SIZE, 0);
  	arm_dcache_delete(&rx_buffer, AUDIO_RX_SIZE);
  	usb_receive(AUDIO_RX_ENDPOINT, &rx_transfer);
  }
  
  static void sync_event(transfer_t *t)
  {
  	// USB 2.0 Specification, 5.12.4.2 Feedback, pages 73-75
  	//printf("sync %x\n", sync_transfer.status); // too slow, can't print this much
  	usb_audio_sync_feedback = feedback_accumulator >> usb_audio_sync_rshift;
  	usb_prepare_transfer(&sync_transfer, &usb_audio_sync_feedback, usb_audio_sync_nbytes, 0);
  	arm_dcache_flush(&usb_audio_sync_feedback, usb_audio_sync_nbytes);
  	usb_transmit(AUDIO_SYNC_ENDPOINT, &sync_transfer);
  }
  
  void usb_audio_configure(void)
  {
  	printf("usb_audio_configure\n");
  	usb_audio_underrun_count = 0;
  	usb_audio_overrun_count = 0;
  //	feedback_accumulator = 739875226; // 44.1 * 2^24
  	feedback_accumulator = 48<<24; 
  	if (usb_high_speed) {
  		usb_audio_sync_nbytes = 4;
  		usb_audio_sync_rshift = 8;
  	} else {
  		usb_audio_sync_nbytes = 3;
  		usb_audio_sync_rshift = 10;
  	}
  	memset(&rx_transfer, 0, sizeof(rx_transfer));
  	usb_config_rx_iso(AUDIO_RX_ENDPOINT, AUDIO_RX_SIZE, 1, rx_event);
  	rx_event(NULL);
  	memset(&sync_transfer, 0, sizeof(sync_transfer));
  	usb_config_tx_iso(AUDIO_SYNC_ENDPOINT, usb_audio_sync_nbytes, 1, sync_event);
  	sync_event(NULL);
  	memset(&tx_transfer, 0, sizeof(tx_transfer));
  	usb_config_tx_iso(AUDIO_TX_ENDPOINT, AUDIO_TX_SIZE, 1, tx_event);
  	tx_event(NULL);
  }
  
  
  // Called from the USB interrupt when an isochronous packet arrives
  // we must completely remove it from the receive buffer before returning
  //
  void usb_audio_receive_callback(unsigned int len)
  {
  	unsigned int count, avail;
  	const uint32_t *data;
  
  	len >>= 2; // 1 sample = 4 bytes: 2 left, 2 right
  	data = (const uint32_t *)rx_buffer;
  
  //	count = AudioInputUSB::incoming_count;
  //	left = AudioInputUSB::incoming_left;
  //	right = AudioInputUSB::incoming_right;
  	while (len > 0) {
  		avail = AUDIO_BLOCK_SAMPLES - count;
  		if (len < avail) {
  			memcpy(data, data + count, 4*len);
  			return;
  		} else if (avail > 0) {
  			memcpy(data, data + count, avail);
  			data += avail;
  			len -= avail;
  			send:
  		} else {
  			goto send; // recover from buffer overrun
  		}
  	}
  }
  
  /*DMAMEM*/ uint16_t usb_audio_transmit_buffer[AUDIO_TX_SIZE/2] __attribute__ ((used, aligned(32)));
  
  
  static void tx_event(transfer_t *t)
  {
  	int len = usb_audio_transmit_callback();
  	usb_audio_sync_feedback = feedback_accumulator >> usb_audio_sync_rshift;
  	usb_prepare_transfer(&tx_transfer, usb_audio_transmit_buffer, len, 0);
  	arm_dcache_flush_delete(usb_audio_transmit_buffer, len);
  	usb_transmit(AUDIO_TX_ENDPOINT, &tx_transfer);
  }
  
  // Called from the USB interrupt when ready to transmit another
  // isochronous packet.  If we place data into the transmit buffer,
  // the return is the number of bytes.  Otherwise, return 0 means
  // no data to transmit
  unsigned int usb_audio_transmit_callback(void)
  {
  	uint32_t avail, num, target, offset, len=0;
  
  	target = 48; // samples per millisecond
  	while (len < target) {
  		num = target - len;
  		avail = AUDIO_BLOCK_SAMPLES - offset;
  		if (num > avail) num = avail;
  
  		memcpy((uint32_t *)usb_audio_transmit_buffer + len, data + offset, 4*num);
  		len += num;
  		offset += num;
  	}
  	return target * 4;
  }
  
  
  struct setup_struct {
    union {
      struct {
  	uint8_t bmRequestType;
  	uint8_t bRequest;
  	union {
  		struct {
  			uint8_t bChannel;  // 0=main, 1=left, 2=right
  			uint8_t bCS;       // Control Selector
  		};
  		uint16_t wValue;
  	};
  	union {
  		struct {
  			uint8_t bIfEp;     // type of entity
  			uint8_t bEntityId; // UnitID, TerminalID, etc.
  		};
  		uint16_t wIndex;
  	};
  	uint16_t wLength;
      };
    };
  };
  
  int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen) // from Teensy to PC
  {
  	struct setup_struct setup = *((struct setup_struct *)stp);
  	if (setup.bmRequestType==0xA1) { // should check bRequest, bChannel, and UnitID
  			if (setup.bCS==0x01) { // mute
  //				data[0] = AudioInputUSB::features.mute;  // 1=mute, 0=unmute
  				*datalen = 1;
  				return 1;
  			}
  			else if (setup.bCS==0x02) { // volume
  				if (setup.bRequest==0x81) { // GET_CURR
  //					data[0] = AudioInputUSB::features.volume & 0xFF;
  //					data[1] = (AudioInputUSB::features.volume>>8) & 0xFF;
  				}
  				else if (setup.bRequest==0x82) { // GET_MIN
  					//serial_print("vol get_min\n");
  					data[0] = 0;     // min level is 0
  					data[1] = 0;
  				}
  				else if (setup.bRequest==0x83) { // GET_MAX
  					data[0] = FEATURE_MAX_VOLUME;  // max level, for range of 0 to MAX
  					data[1] = 0;
  				}
  				else if (setup.bRequest==0x84) { // GET_RES
  					data[0] = 1; // increment vol by by 1
  					data[1] = 0;
  				}
  				else { // pass over SET_MEM, etc.
  					return 0;
  				}
  				*datalen = 2;
  				return 1;
  			}
  	}
  	return 0;
  }
  
  int usb_audio_set_feature(void *stp, uint8_t *buf) // from PC to Teensy
  {
  	struct setup_struct setup = *((struct setup_struct *)stp);
  	if (setup.bmRequestType==0x21) { // should check bRequest, bChannel and UnitID
  			if (setup.bCS==0x01) { // mute
  				if (setup.bRequest==0x01) { // SET_CUR
  //					AudioInputUSB::features.mute = buf[0]; // 1=mute,0=unmute
  //					AudioInputUSB::features.change = 1;
  					return 1;
  				}
  			}
  			else if (setup.bCS==0x02) { // volume
  				if (setup.bRequest==0x01) { // SET_CUR
  //					AudioInputUSB::features.volume = buf[0];
  //					AudioInputUSB::features.change = 1;
  					return 1;
  				}
  			}
  	}
  	return 0;
  }
  
  #endif // AUDIO_INTERFACE
  