void udd_ep_abort(udd_ep_id_t ep)
{
	bool b_dir_in = ep & USB_EP_DIR_IN;
	irqflags_t flags;

	ep &= USB_EP_ADDR_MASK;
	if (USB_DEVICE_MAX_EP < ep)
		return;

	// Disable interrupts
	flags = cpu_irq_save();
	udd_disable_endpoint_interrupt(ep);
	cpu_irq_restore(flags);
	// Clear pending statuses
	if (b_dir_in) {
		// Kill banks
		if (Is_udd_transmit_ready(ep)) {
			udd_kill_data_in_fifo(ep,
					udd_get_endpoint_bank_max_nbr(ep)>1);
		}
		udd_ack_in_sent(ep);
		// Reset number of buffered banks
		udd_ep_job[ep - 1].bank = 0;
	} else {
		// Clear all pending banks statuses
		while(Is_udd_any_bank_received(ep)) {
			udd_ep_ack_out_received(ep);
		}
	}
	// Reset FIFO and data toggle
	udd_reset_endpoint(ep);
	// Abort job
	udd_ep_abort_job(ep);
}
Example #2
0
void udd_ep_abort(udd_ep_id_t ep)
{
	irqflags_t flags;
	udd_ep_job_t *ptr_job;

	ep &= USB_EP_ADDR_MASK;

	// Disable interrupt of endpoint
	flags = cpu_irq_save();
	udd_disable_endpoint_interrupt(ep);
	cpu_irq_restore(flags);

	// Stop transfer
	udd_enable_busy_bank0(ep);

	// Job complete then call callback
	ptr_job = &udd_ep_job[ep - 1];
	if (!ptr_job->busy) {
		return;
	}
	ptr_job->busy = false;
	if (NULL != ptr_job->call_trans) {
		if (Is_udd_endpoint_in(ep)) {
			ep |= USB_EP_DIR_IN;
		}
		// It can be a Transfer or stall callback
		ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->nb_trans, ep);
	}
}
Example #3
0
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++) {
		// Get job corresponding at endpoint
		ptr_job = &udd_ep_job[ep - 1];

		// Check DMA event
		if (Is_udd_endpoint_dma_interrupt_enabled(ep)
				&& Is_udd_endpoint_dma_interrupt(ep)) {
			uint32_t nb_remaining;
			if( udd_endpoint_dma_get_status(ep)
					& AVR32_USBB_UDDMA1_STATUS_CH_EN_MASK) {
				return true; // Ignore EOT_STA interrupt
			}
			udd_disable_endpoint_dma_interrupt(ep);
			// Save number of data no transfered
			nb_remaining = (udd_endpoint_dma_get_status(ep) &
					AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_MASK)
					>> AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_OFFSET;
			if (nb_remaining) {
				// Transfer no complete (short packet or ZLP) then:
				// Update number of data transfered
				ptr_job->nb_trans -= nb_remaining;
				// Set transfer complete to stop the transfer
				ptr_job->buf_size = ptr_job->nb_trans;
			}
			udd_ep_trans_done(ep);
			return true;
		}
		// Check empty bank interrupt event
		if (Is_udd_endpoint_interrupt_enabled(ep)) {
			if (Is_udd_in_send_interrupt_enabled(ep) && Is_udd_in_send(ep)) {
				udd_disable_in_send_interrupt(ep);
				// One bank is free then send a ZLP
				udd_ack_in_send(ep);
				udd_ack_fifocon(ep);
				udd_ep_finish_job(ptr_job, false, ep);
				return true;
			}
			if (Is_udd_bank_interrupt_enabled(ep) && (0 == udd_nb_busy_bank(ep))) {
				// End of background transfer on IN endpoint
				udd_disable_bank_interrupt(ep);
				udd_disable_endpoint_interrupt(ep);

				Assert(ptr_job->stall_requested);
				// A stall has been requested during background transfer
				ptr_job->stall_requested = false;
				udd_disable_endpoint_bank_autoswitch(ep);
				udd_enable_stall_handshake(ep);
				udd_reset_data_toggle(ep);
				return true;
			}
		}
	}
Example #4
0
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 DMA event
		if (Is_udd_endpoint_dma_interrupt_enabled(ep)
				&& Is_udd_endpoint_dma_interrupt(ep)) {
			uint32_t nb_remaining;
			udd_disable_endpoint_dma_interrupt(ep);
			// Save number of data no transfered
			nb_remaining = (udd_endpoint_dma_get_status(ep) &
					AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_MASK)
					>>
					AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_OFFSET;
			// Get job corresponding at endpoint
			ptr_job = &udd_ep_job[ep - 1];
			// Update number of data transfered
			ptr_job->buf_size -= nb_remaining;

			if (!Is_udd_endpoint_in(ep)) {
				// Disable autoswitch bank on OUT
				udd_disable_endpoint_bank_autoswitch(ep);
			} else {
				// Wait end of background transfer on IN endpoint before disabled autoswitch bank
				udd_enable_endpoint_interrupt(ep);
				udd_enable_bank_interrupt(ep);
			}
			// Call callback to signal end of transfer
			udd_ep_finish_job(&udd_ep_job[ep - 1], false);
			return true;
		}
		// Check empty bank interrupt event
		if (Is_udd_endpoint_interrupt_enabled(ep)
				&& (0 == udd_nb_busy_bank(ep))) {
			// End of background transfer on IN endpoint
			udd_disable_bank_interrupt(ep);
			udd_disable_endpoint_interrupt(ep);
			// If no new transfer running then disable autoswitch bank
			if (!udd_ep_job[ep - 1].busy) {
				udd_disable_endpoint_bank_autoswitch(ep);
			}
			// If a stall has been requested during backgound transfer then execute it
			if (udd_ep_job[ep - 1].stall_requested) {
				udd_ep_job[ep - 1].stall_requested = false;
				udd_enable_stall_handshake(ep);
				udd_reset_data_toggle(ep);
			}
			return true;
		}
	}
Example #5
0
void udd_ep_abort(udd_ep_id_t ep)
{
	uint8_t index = ep & USB_EP_ADDR_MASK;

	// Stop DMA transfer
	udd_disable_endpoint_dma_interrupt(index);
	udd_endpoint_dma_set_control(index, 0);
	// Kill banks
	udd_disable_endpoint_interrupt(index);
	while (udd_nb_busy_bank(index)) {
		udd_kill_last_in_bank(index);
		while(Is_udd_killing_last_in_bank(index));
	}
	udd_ep_abort_job(ep);
}
Example #6
0
bool udd_ep_clear_halt(udd_ep_id_t ep)
{
	bool b_stall_cleared = false;
	udd_ep_job_t *ptr_job;

	ep &= USB_EP_ADDR_MASK;
	if (USB_DEVICE_MAX_EP < ep)
		return false;
	ptr_job = &udd_ep_job[ep - 1];

	if (ptr_job->stall_requested) {
		// Endpoint stall has been requested but not done
		// Remove stall request
		ptr_job->stall_requested = false;
		udd_disable_bank_interrupt(ep);
		udd_disable_endpoint_interrupt(ep);
		b_stall_cleared = true;
	}
	if (Is_udd_endpoint_stall_requested(ep)) { 
		if (Is_udd_stall(ep)) {
			udd_ack_stall(ep);
			// A packet has been stalled
			// then reset datatoggle
			udd_reset_data_toggle(ep);
		}
		// Disable stall
		udd_disable_stall_handshake(ep);
		udd_enable_endpoint_bank_autoswitch(ep);
		b_stall_cleared = true;
	}
	if (b_stall_cleared) {
		// If a job is register on clear halt action
		// then execute callback
		if (ptr_job->busy == true) {
			ptr_job->busy = false;
			ptr_job->call_nohalt();
		}
	}
	return true;
}
static void udd_ep_out_received(udd_ep_id_t ep)
{
	udd_ep_job_t *ptr_job = &udd_ep_job[ep - 1];
	uint32_t nb_data = 0, i;
	uint32_t nb_remain = ptr_job->buf_size - ptr_job->buf_cnt;
	uint32_t pkt_size = ptr_job->size;
	uint8_t *ptr_dst = &ptr_job->buf[ptr_job->buf_cnt];
	bool b_full = false, b_short;

	// Read byte count
	nb_data = udd_byte_count(ep);
	b_short = (nb_data < pkt_size);

	// Copy data if there is
	if (nb_data > 0) {
		if (nb_data >= nb_remain) {
			nb_data = nb_remain;
			b_full = true;
		}
		// Modify job information
		ptr_job->buf_cnt += nb_data;

		// Copy FIFO (DPRAM) to buffer
		for (i = 0; i < nb_data; i++) {
			*ptr_dst++ = udd_endpoint_fifo_read(ep);
		}
	}
	// Clear FIFO Status
	udd_ep_ack_out_received(ep);
	// Finish job on error or short packet
	if ((b_full || b_short) &&
			!Is_udd_endpoint_stall_requested(ep)) {
		udd_disable_endpoint_interrupt(ep);
		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);
	}
}
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;
}
Example #9
0
static void udd_ep_trans_done(udd_ep_id_t ep)
{
	udd_ep_job_t *ptr_job;
	uint16_t ep_size, nb_trans;
	uint16_t next_trans;
	udd_ep_id_t ep_num;
	irqflags_t flags;

	ep_num = ep & USB_EP_ADDR_MASK;
	ep_size = udd_get_endpoint_size(ep_num);

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

	// Disable interrupt of endpoint
	flags = cpu_irq_save();
	udd_disable_endpoint_interrupt(ep_num);
	cpu_irq_restore(flags);

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

	if (USB_EP_DIR_IN == (ep & USB_EP_DIR_IN)) {
		// Transfer complete on IN
		nb_trans = udd_udesc_get_buf0_size(ep_num);

		// Lock emission of new IN packet
		udd_enable_busy_bank0(ep_num);

		// Ack interrupt
		udd_ack_in_send(ep_num);

		if (0 == nb_trans) {
			if (0 == udd_nb_busy_bank(ep_num)) {
				// All byte are transfered than take nb byte requested
				nb_trans = udd_udesc_get_buf0_ctn(ep_num);
			}
		}
		// Update number of data transfered
		ptr_job->nb_trans += nb_trans;

		// Need to send other data
		if ((ptr_job->nb_trans != ptr_job->buf_size)
				|| ptr_job->b_shortpacket) {
			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 -
						(UDD_ENDPOINT_MAX_TRANS % ep_size);
				udd_udesc_set_buf0_autozlp(ep_num, false);
			} else {
				// Need ZLP, if requested and last packet is not a short packet
				udd_udesc_set_buf0_autozlp(ep_num, ptr_job->b_shortpacket);
				ptr_job->b_shortpacket = false; // No need to request another ZLP
			}

			udd_udesc_set_buf0_ctn(ep_num, next_trans);
			udd_udesc_rst_buf0_size(ep_num);

			// Link the user buffer directly on USB hardware DMA
			udd_udesc_set_buf0_addr(ep_num,
					&ptr_job->buf[ptr_job->nb_trans]);

			// Start transfer
			udd_ack_fifocon(ep_num);
			udd_disable_busy_bank0(ep_num);

			// Enable interrupt
			flags = cpu_irq_save();
			udd_enable_in_send_interrupt(ep_num);
			udd_enable_endpoint_interrupt(ep_num);
			cpu_irq_restore(flags);
			return;
		}
	} else {
		// Transfer complete on OUT
		nb_trans = udd_udesc_get_buf0_ctn(ep_num);

		// Lock reception of new OUT packet
		udd_enable_busy_bank0(ep_num);

		// Ack interrupt
		udd_ack_out_received(ep_num);
		udd_ack_fifocon(ep_num);

		// Can be necessary to copy data receive from cache buffer to user buffer
		if (ptr_job->b_use_out_cache_buffer) {
			memcpy(&ptr_job->buf[ptr_job->nb_trans],
					udd_ep_out_cache_buffer[ep_num - 1],
					ptr_job->buf_size % ep_size);
		}

		// Update number of data transfered
		ptr_job->nb_trans += nb_trans;
		if (ptr_job->nb_trans > ptr_job->buf_size) {
			ptr_job->nb_trans = ptr_job->buf_size;
		}

		// If all previous data requested are received and user buffer not full
		// then need to receive other data
		if ((nb_trans == udd_udesc_get_buf0_size(ep_num))
			&& (ptr_job->nb_trans != ptr_job->buf_size)) {
			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
						- (UDD_ENDPOINT_MAX_TRANS % ep_size);
			} else {
				next_trans -= next_trans % ep_size;
			}

			udd_udesc_rst_buf0_ctn(ep_num);
			if (next_trans < ep_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_num,
						udd_ep_out_cache_buffer[ep_num-1]);
				udd_udesc_set_buf0_size(ep_num, ep_size);
			} else {
				// Link the user buffer directly on USB hardware DMA
				udd_udesc_set_buf0_addr(ep_num, &ptr_job->buf[ptr_job->nb_trans]);
				udd_udesc_set_buf0_size(ep_num, next_trans);
			}
			// Start transfer
			udd_disable_busy_bank0(ep_num);

			// Enable interrupt
			flags = cpu_irq_save();
			udd_enable_out_received_interrupt(ep_num);
			udd_enable_endpoint_interrupt(ep_num);
			cpu_irq_restore(flags);
			return;
		}
	}

	// Job complete then call callback
	ptr_job->busy = false;
	if (NULL != ptr_job->call_trans) {
		ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep);
	}
	return;
}
static void udd_ep_finish_job(udd_ep_id_t ep, bool b_abort)
{
	udd_ep_job_t *ptr_job;
	uint16_t ep_size;
	irqflags_t flags;

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

	// Test if a pending transfer is running. If not, disabled interrupt.
	if (!ptr_job->busy) {
		flags = cpu_irq_save();
		udd_disable_endpoint_interrupt(ep);
		cpu_irq_restore(flags);
		return;
	}

	if (Is_udd_endpoint_in(ep)) {
		// Update number of data transfered
		ptr_job->nb_trans = udd_udesc_get_buf0_size(ep);
		if (0 == ptr_job->nb_trans) {
			if (0 == udd_nb_busy_bank(ep)) {
				// All byte are transfered than take nb byte requested
				ptr_job->nb_trans = udd_udesc_get_buf0_ctn(ep);
			}
		}
	} else {
		// Transfer complete on OUT
		ep_size = udd_format_endpoint_size(ep);
		if (ptr_job->b_use_out_cache_buffer) {
			// Copy data receiv from cache buffer to user buffer
			memcpy(&ptr_job->buf[ptr_job->nb_trans],
					udd_ep_out_cache_buffer[ep - 1],
					ptr_job->buf_size % ep_size);
			ptr_job->nb_trans += udd_udesc_get_buf0_ctn(ep);
		} else {
			ptr_job->nb_trans = udd_udesc_get_buf0_ctn(ep);
			// If all previous data requested are received
			// and user buffer not full
			if ((ptr_job->nb_trans == udd_udesc_get_buf0_size(ep))
					&& (ptr_job->nb_trans !=
							ptr_job->buf_size)) {
				// Use the cache buffer to receiv last data
				// which can be more larger than user buffer remaining
				ptr_job->b_use_out_cache_buffer = true;
				udd_udesc_rst_buf0_ctn(ep);
				udd_udesc_set_buf0_addr(ep,
						udd_ep_out_cache_buffer[ep -
								1]);
				udd_udesc_set_buf0_size(ep, ep_size);
				// Free buffer to accept another data to reception
				udd_ack_out_received(ep);
				udd_ack_fifocon(ep);
				return;
			}
		}
		// Free buffer but not accept another data to reception
		udd_ack_out_received(ep);
		udd_enable_busy_bank0(ep);
		udd_ack_fifocon(ep);
	}

	// Call callback to signal end of transfer
	flags = cpu_irq_save();
	udd_disable_endpoint_interrupt(ep);
	cpu_irq_restore(flags);

	ptr_job->busy = false;
	if (NULL == ptr_job->call_trans)
		return;	// No callback linked to job
	ptr_job->call_trans((b_abort) ? UDD_EP_TRANSFER_ABORT :
			UDD_EP_TRANSFER_OK, ptr_job->nb_trans);
}