Example #1
0
bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket,
		uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	udd_ep_id_t ep_num;
	udd_ep_job_t *ptr_job;
	irqflags_t flags;

	ep_num = ep & USB_EP_ADDR_MASK;
	if (USB_DEVICE_MAX_EP < ep_num) {
		return false;
	}
	if ((!Is_udd_endpoint_enabled(ep_num))
			|| Is_udd_endpoint_stall_requested(ep_num)) {
		return false; // Endpoint is halted
	}

	// Get job about endpoint
	ptr_job = &udd_ep_job[ep_num - 1];

	flags = cpu_irq_save();
	if (ptr_job->busy == true) {
		cpu_irq_restore(flags);
		return false; // Job already on going
	}
	ptr_job->busy = true;
	cpu_irq_restore(flags);

	// No job running. Let's setup a new one.
	//
	ptr_job->buf = buf;
	ptr_job->buf_size = buf_size;
	ptr_job->nb_trans = 0;
	ptr_job->call_trans = callback;
	ptr_job->b_shortpacket = b_shortpacket;
	ptr_job->b_use_out_cache_buffer = false;

	if ( (USB_EP_DIR_IN != (ep & USB_EP_DIR_IN))
			&& (AVR32_USBC_PTYPE_ISOCHRONOUS == udd_get_endpoint_type(ep_num))
			&& (0 != (buf_size % udd_get_endpoint_size(ep_num)))) {
		// The user must use a buffer size modulo endpoint size
		// for an isochronous IN endpoint
		ptr_job->busy = false;
		return false;
	}

	// Initialize value to simulate a empty transfer
	udd_udesc_rst_buf0_ctn(ep_num);
	udd_udesc_rst_buf0_size(ep_num);

	// Request next transfer
	udd_ep_trans_done(ep);
	return true;
}
Example #2
0
static void udd_ep_trans_done(udd_ep_id_t ep)
{
	uint32_t udd_dma_ctrl = 0;
	udd_ep_job_t *ptr_job;
	iram_size_t next_trans;
	irqflags_t flags;

	// Get job corresponding at endpoint
	ptr_job = &udd_ep_job[ep - 1];

	if (!ptr_job->busy) {
		return; // No job is running, then ignore it (system error)
	}

	if (ptr_job->nb_trans != ptr_job->buf_size) {
		// Need to send or receive other data
		next_trans = ptr_job->buf_size - ptr_job->nb_trans;

		if (UDD_ENDPOINT_MAX_TRANS < next_trans) {
			// The USB hardware support a maximum
			// transfer size of UDD_ENDPOINT_MAX_TRANS Bytes
			next_trans = UDD_ENDPOINT_MAX_TRANS;

			// Set 0 to transfer the maximum
			udd_dma_ctrl = (0 <<
					AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_OFFSET)
					& AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_MASK;
		} else {
			udd_dma_ctrl = (next_trans <<
					AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_OFFSET)
					& AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_MASK;
		}
		if (Is_udd_endpoint_in(ep)) {
			if (0 != next_trans % udd_get_endpoint_size(ep)) {
				// Enable short packet option
				// else the DMA transfer is accepted
				// and interrupt DMA valid but nothing is sent.
				udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_DMAEND_EN_MASK;
				// No need to request another ZLP
				ptr_job->b_shortpacket = false;
			}
		} else {
			if ((USB_EP_TYPE_ISOCHRONOUS != udd_get_endpoint_type(ep))
					|| (next_trans <= udd_get_endpoint_size(ep))) {

				// Enable short packet reception
				udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_EOT_IRQ_EN_MASK
						| AVR32_USBB_UDDMA1_CONTROL_BUFF_CLOSE_IN_EN_MASK;
			}
		}

		// Start USB DMA to fill or read fifo of the selected endpoint
		udd_endpoint_dma_set_addr(ep, (U32) &ptr_job->buf[ptr_job->nb_trans]);
		udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_EOBUFF_IRQ_EN_MASK |
				AVR32_USBB_UDDMA1_CONTROL_CH_EN_MASK;

		// Disable IRQs to have a short sequence
		// between read of EOT_STA and DMA enable
		flags = cpu_irq_save();
		if ( !(udd_endpoint_dma_get_status(ep)
				& AVR32_USBB_UDDMA1_STATUS_EOT_STA_MASK)) {
			udd_endpoint_dma_set_control(ep, udd_dma_ctrl);
			ptr_job->nb_trans += next_trans;
			udd_enable_endpoint_dma_interrupt(ep);
			cpu_irq_restore(flags);
			return;
		}
		cpu_irq_restore(flags);

		// Here a ZLP has been received
		// and the DMA transfer must be not started.
		// It is the end of transfer
		ptr_job->buf_size = ptr_job->nb_trans;
	}
	if (Is_udd_endpoint_in(ep)) {
		if (ptr_job->b_shortpacket) {
			// Need to send a ZLP (No possible with USB DMA)
			// enable interrupt to wait a free bank to sent ZLP
			udd_ack_in_send(ep);
			if (Is_udd_write_enabled(ep)) {
				// Force interrupt in case of ep already free
				udd_raise_in_send(ep);
			}
			udd_enable_in_send_interrupt(ep);
			udd_enable_endpoint_interrupt(ep);
			return;
		}
	}
	// Call callback to signal end of transfer
	udd_ep_finish_job(ptr_job, false, ep);
}
static bool udd_ep_interrupt(void)
{
	udd_ep_id_t ep;
	udd_ep_job_t *ptr_job;

	// For each endpoint different of control endpoint (0)
	for (ep = 1; ep <= USB_DEVICE_MAX_EP; ep++) {
		// Check RXRDY and TXEMPTY event for none DMA endpoints
		if (!Is_udd_endpoint_interrupt_enabled(ep)) {
			continue;
		}

		// Get job corresponding at endpoint
		ptr_job = &udd_ep_job[ep - 1];

		// RXOUT: Full packet received
		if (Is_udd_any_bank_received(ep)) {
			udd_ep_out_received(ep);
			return true;
		}
		// TXIN: packet sent
		if (Is_udd_in_sent(ep)) {

			ptr_job->bank--;
			// Stall when all banks free
			if (ptr_job->b_stall_requested) {
				if (ptr_job->bank) {
					// Send remaining
					udd_set_transmit_ready(ep);
					udd_ack_in_sent(ep);
				} else {
					// Ack last packet
					udd_ack_in_sent(ep);
					// Enable stall
					udd_enable_stall_handshake(ep);
					// Halt executed
					ptr_job->b_stall_requested = false;
				}
				return true;
			}
			// Finish Job when buffer end
			if (ptr_job->b_buf_end) {
				ptr_job->b_buf_end = false;
				ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count
				udd_ep_finish_job(ptr_job, UDD_EP_TRANSFER_OK, ep);
			}
			if (ptr_job->buf_cnt >= ptr_job->buf_size &&
					!ptr_job->b_shortpacket &&
					ptr_job->bank == 0) {
				// All transfer done, including ZLP
				irqflags_t flags = cpu_irq_save();
				udd_disable_endpoint_interrupt(ep);
				cpu_irq_restore(flags);
				// Ack last packet
				udd_ack_in_sent(ep);
				return true;
			} else if (udd_get_endpoint_bank_max_nbr(ep) > 1
					&& ptr_job->bank > 0) {
				// Already banks buffered, transmit while loading
				udd_set_transmit_ready(ep);
				udd_ack_in_sent(ep);
				udd_ep_in_sent(ep, false);
			} else if (udd_get_endpoint_bank_max_nbr(ep) > 1) {
				// Still bank free, load and transmit
				if (!udd_ep_in_sent(ep, true)) {
					ptr_job->b_buf_end = false;
					ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count
					udd_ep_finish_job(ptr_job, UDD_EP_TRANSFER_OK, ep);
				}
				udd_ack_in_sent(ep);
				udd_ep_in_sent(ep, false);
			} else {
				// Single bank transfer, ack when ready
				udd_ep_in_sent(ep, true);
				udd_ack_in_sent(ep);
			}
			return true;
		}
		// Stall sent/CRC error
		if (Is_udd_stall(ep)) {
			udd_ack_stall(ep);
			if (udd_get_endpoint_type(ep) == UDP_CSR_EPTYPE_ISO_OUT ||
				udd_get_endpoint_type(ep) == UDP_CSR_EPTYPE_ISO_IN) {
			}
			return true;
		}
	}
	return false;
}
bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket,
		uint8_t * buf, iram_size_t buf_size,
		udd_callback_trans_t callback)
{
	uint16_t ep_size, trans_size, short_packet;
	bool b_dir_in;
	udd_ep_job_t *ptr_job;
	irqflags_t flags;

	b_dir_in = (USB_EP_DIR_IN == (ep & USB_EP_DIR_IN));
	ep &= USB_EP_ADDR_MASK;
	if (USB_DEVICE_MAX_EP < ep)
		return false;

	// Get job about endpoint
	ptr_job = &udd_ep_job[ep - 1];

	if ((!Is_udd_endpoint_enabled(ep))
			|| Is_udd_endpoint_stall_requested(ep))
		return false;	// Endpoint is halted

   flags = cpu_irq_save();
	if (ptr_job->busy == true) {
		cpu_irq_restore(flags);
		return false;	// Job already on going
	}
	ptr_job->busy = true;
	cpu_irq_restore(flags);

	// No job running. Let's setup a new one.
	//
	// The USB hardware support a maximum transfer size of 0x7FFF Bytes
	ep_size = udd_get_endpoint_size(ep);
	if (0x7FFF < buf_size) {
		trans_size = 0x7FFF - (0x7FFF % ep_size);
		short_packet = 0;
	} else {
		trans_size = buf_size;
		short_packet = trans_size % ep_size;
	}

	if (b_dir_in) {
		// Need ZLP, if requested and last packet is not a short packet
		udd_udesc_set_buf0_autozlp(ep, b_shortpacket);
		udd_udesc_set_buf0_ctn(ep, trans_size);
		udd_udesc_rst_buf0_size(ep);
		// Link the user buffer directly on USB hardware DMA
		udd_udesc_set_buf0_addr(ep, buf);
	} else {
		udd_udesc_rst_buf0_ctn(ep);
		ptr_job->nb_trans = 0;
		if (trans_size < ep_size) {
			// The user buffer is smaller than endpoint size
			if (AVR32_USBC_PTYPE_ISOCHRONOUS ==
					udd_get_endpoint_type(ep)) {
				ptr_job->busy = false;
				return false;	// The user must use a buffer corresponding at isochrnous endpoint size
			}
			// Use the cache buffer for Bulk or Interrupt size endpoint
			ptr_job->b_use_out_cache_buffer = true;
			udd_udesc_set_buf0_addr(ep,
					udd_ep_out_cache_buffer[ep - 1]);
			udd_udesc_set_buf0_size(ep, ep_size);
		} else {
			// Link the user buffer directly on USB hardware DMA
			ptr_job->b_use_out_cache_buffer = false;
			udd_udesc_set_buf0_addr(ep, buf);
			udd_udesc_set_buf0_size(ep, trans_size - short_packet);
		}
	}

	// Update Job information
	ptr_job->buf = buf;
	ptr_job->buf_size = trans_size;
	ptr_job->call_trans = callback;
	ptr_job->busy = true;


	// Start transfer
	udd_disable_busy_bank0(ep);

	// Enable interrupt
	flags = cpu_irq_save();
	if (b_dir_in) {
		udd_ack_fifocon(ep);
		udd_ack_in_send(ep);
		udd_enable_in_send_interrupt(ep);
	} else {
		udd_enable_out_received_interrupt(ep);
	}
	udd_enable_endpoint_interrupt(ep);
	cpu_irq_restore(flags);

	return true;
}