Пример #1
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);
	}
}
Пример #2
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;
		}
	}
Пример #3
0
static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_num)
{
	if (ptr_job->busy == false) {
		return; // No on-going job
	}
	ptr_job->busy = false;
	if (NULL == ptr_job->call_trans) {
		return; // No callback linked to job
	}
	if (Is_udd_endpoint_in(ep_num)) {
		ep_num |= USB_EP_DIR_IN;
	}
	ptr_job->call_trans((b_abort) ? UDD_EP_TRANSFER_ABORT :
			UDD_EP_TRANSFER_OK, ptr_job->buf_size, ep_num);
}
Пример #4
0
static bool udd_ep_interrupt(void)
{
	udd_ep_id_t ep, ep_addr;

	// For each endpoint different of control endpoint (0)
	for (ep = 1; ep <= USB_DEVICE_MAX_EP; ep++) {
		if (!Is_udd_endpoint_interrupt_enabled(ep)
				|| !Is_udd_endpoint_interrupt(ep)) {
			continue;
		}
		ep_addr = Is_udd_endpoint_in(ep) ? (ep | USB_EP_DIR_IN) : ep;
		udd_ep_trans_done(ep_addr);
		return true;
	}
	return false;
}
Пример #5
0
bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes,
		uint16_t MaxEndpointSize)
{
	bool b_dir_in;
	uint16_t ep_allocated;
	uint8_t bank, i;

	b_dir_in = ep & USB_EP_DIR_IN;
	ep = ep & USB_EP_ADDR_MASK;

	if (ep > USB_DEVICE_MAX_EP) {
		return false;
	}
	if (Is_udd_endpoint_enabled(ep)) {
		return false;
	}

	// Bank choice
	switch(bmAttributes&USB_EP_TYPE_MASK) {
	case USB_EP_TYPE_ISOCHRONOUS:
		bank = UDD_ISOCHRONOUS_NB_BANK(ep);
		break;
	case USB_EP_TYPE_INTERRUPT:
		bank = UDD_INTERRUPT_NB_BANK(ep);
		break;
	case USB_EP_TYPE_BULK:
		bank = UDD_BULK_NB_BANK(ep);
		break;
	default:
		Assert(false);
		return false;
	}
	switch(bank) {
	case 1:
		bank = AVR32_USBB_UECFG0_EPBK_SINGLE;
		break;
	case 2:
		bank = AVR32_USBB_UECFG0_EPBK_DOUBLE;
		break;
	case 3:
		bank = AVR32_USBB_UECFG0_EPBK_TRIPLE;
		break;
	default:
		Assert(false);
		return false;
	}

	// Check if endpoint size is 8,16,32,64,128,256,512 or 1023
	Assert(MaxEndpointSize < 1024);
	Assert((MaxEndpointSize == 1023) || !(MaxEndpointSize & (MaxEndpointSize - 1)));
	Assert(MaxEndpointSize >= 8);

	// Set configuration of new endpoint
	udd_configure_endpoint(ep, bmAttributes, (b_dir_in ? 1 : 0),
			MaxEndpointSize, bank);
	ep_allocated = 1 << ep;

	// Unalloc endpoints superior
	for (i = USB_DEVICE_MAX_EP; i > ep; i--) {
		if (Is_udd_endpoint_enabled(i)) {
			ep_allocated |= 1 << i;
			udd_disable_endpoint(i);
			udd_unallocate_memory(i);
		}
	}

	// Realloc/Enable endpoints
	for (i = ep; i <= USB_DEVICE_MAX_EP; i++) {
		if (ep_allocated & (1 << i)) {
			udd_ep_job_t *ptr_job = &udd_ep_job[i - 1];
			bool b_restart = ptr_job->busy;
			ptr_job->busy = false;

			udd_allocate_memory(i);
			udd_enable_endpoint(i);
			if (!Is_udd_endpoint_configured(i)) {
				if (NULL == ptr_job->call_trans) {
					return false;
				}
				if (Is_udd_endpoint_in(i)) {
					i |= USB_EP_DIR_IN;
				}				
				ptr_job->call_trans(UDD_EP_TRANSFER_ABORT,
						ptr_job->buf_size, i);
				return false;
			}
			udd_enable_endpoint_bank_autoswitch(i);
			if (b_restart) {
				// Re-run the job
				udd_ep_run(i, ptr_job->b_shortpacket,
						ptr_job->buf,
						ptr_job->buf_size,
						ptr_job->call_trans);
			}
		}
	}
	return true;
}
Пример #6
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 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);
}
Пример #8
0
bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes,
		uint16_t MaxEndpointSize)
{
	bool b_dir_in;
	uint16_t ep_allocated;
	uint8_t nb_bank, bank, i;

	b_dir_in = ep & USB_EP_DIR_IN;
	ep = ep & USB_EP_ADDR_MASK;

	if (ep > USB_DEVICE_MAX_EP) {
		return false;
	}
	if (Is_udd_endpoint_enabled(ep)) {
		return false;
	}
	dbg_print("alloc(%x, %d) ", ep, MaxEndpointSize);

	// Bank choice
	switch (bmAttributes & USB_EP_TYPE_MASK) {
	case USB_EP_TYPE_ISOCHRONOUS:
		nb_bank = UDD_ISOCHRONOUS_NB_BANK(ep);
		break;
	case USB_EP_TYPE_INTERRUPT:
		nb_bank = UDD_INTERRUPT_NB_BANK(ep);
		break;
	case USB_EP_TYPE_BULK:
		nb_bank = UDD_BULK_NB_BANK(ep);
		break;
	default:
		Assert(false);
		return false;
	}
	switch (nb_bank) {
	case 1:
		bank = UOTGHS_DEVEPTCFG_EPBK_1_BANK >>
				UOTGHS_DEVEPTCFG_EPBK_Pos;
		break;
	case 2:
		bank = UOTGHS_DEVEPTCFG_EPBK_2_BANK >>
				UOTGHS_DEVEPTCFG_EPBK_Pos;
		break;
	case 3:
		bank = UOTGHS_DEVEPTCFG_EPBK_3_BANK >>
				UOTGHS_DEVEPTCFG_EPBK_Pos;
		break;
	default:
		Assert(false);
		return false;
	}

	// Check if endpoint size is 8,16,32,64,128,256,512 or 1023
	Assert(MaxEndpointSize < 1024);
	Assert((MaxEndpointSize == 1023)
		|| !(MaxEndpointSize & (MaxEndpointSize - 1)));
	Assert(MaxEndpointSize >= 8);

	// Set configuration of new endpoint
	udd_configure_endpoint(ep, bmAttributes, (b_dir_in ? 1 : 0),
			MaxEndpointSize, bank);
	ep_allocated = 1 << ep;

	// Unalloc endpoints superior
	for (i = USB_DEVICE_MAX_EP; i > ep; i--) {
		if (Is_udd_endpoint_enabled(i)) {
			ep_allocated |= 1 << i;
			udd_disable_endpoint(i);
			udd_unallocate_memory(i);
		}
	}

	// Realloc/Enable endpoints
	for (i = ep; i <= USB_DEVICE_MAX_EP; i++) {
		if (ep_allocated & (1 << i)) {
			udd_ep_job_t *ptr_job = &udd_ep_job[i - 1];
			bool b_restart = ptr_job->busy;
			// Restart running job because
			// memory window slides up and its data is lost
			ptr_job->busy = false;
			// Re-allocate memory
			udd_allocate_memory(i);
			udd_enable_endpoint(i);
			if (!Is_udd_endpoint_configured(i)) {
				dbg_print("ErrRealloc%d ", i);
				if (NULL == ptr_job->call_trans) {
					return false;
				}
				if (Is_udd_endpoint_in(i)) {
					i |= USB_EP_DIR_IN;
				}
				ptr_job->call_trans(UDD_EP_TRANSFER_ABORT,
						ptr_job->buf_cnt, i);
				return false;
			}
			udd_enable_endpoint_bank_autoswitch(i);
			if (b_restart) {
				// Re-run the job remaining part
				ptr_job->buf_cnt -= ptr_job->buf_load;
				b_restart = udd_ep_run(Is_udd_endpoint_in(i) ?
							(i | USB_EP_DIR_IN) : i,
						ptr_job->b_shortpacket,
						&ptr_job->buf[ptr_job->buf_cnt],
						ptr_job->buf_size
							- ptr_job->buf_cnt,
						ptr_job->call_trans);
				if (!b_restart) {
					dbg_print("ErrReRun%d ", i);
					return false;
				}
			}
		}
	}
	return true;
}