Created
December 31, 2015 13:42
-
-
Save sdalu/a6d06f8b7d9858638456 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
ChibiOS - Copyright (C) 2006..2015 Giovanni Di Sirio | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
*/ | |
/** | |
* @file AVR/usb_lld.c | |
* @brief AVR USB subsystem low level driver source. | |
* | |
* @addtogroup USB | |
* @{ | |
*/ | |
// http://www.avrfreaks.net/forum/usb-initialization-problem | |
// http://www.fourwalledcubicle.com/LUFA.php | |
#include <string.h> | |
#include <avr/pgmspace.h> | |
#include <avr/eeprom.h> | |
#include <avr/interrupt.h> | |
#include "hal.h" | |
#include <util/delay.h> | |
extern IOBus LED1Bus; | |
#if HAL_USE_USB || defined(__DOXYGEN__) | |
#if F_CPU == 16000000UL | |
# define PLL_SETTINGS (1 << PINDIV) /* Need 16 MHz xtal */ | |
#elif F_CPU == 8000000UL | |
# define PLL_SETTINGS 0 /* Need 8 MHz xtal */ | |
#else | |
# error "Clock rate of F_CPU not supported" | |
#endif | |
/*===========================================================================*/ | |
/* Driver local definitions. */ | |
/*===========================================================================*/ | |
/*===========================================================================*/ | |
/* Driver exported variables. */ | |
/*===========================================================================*/ | |
/** @brief USB1 driver identifier.*/ | |
#if AVR_USB_USE_USB1 || defined(__DOXYGEN__) | |
USBDriver USBD1; | |
#endif | |
/*===========================================================================*/ | |
/* Driver local variables and types. */ | |
/*===========================================================================*/ | |
/** | |
* @brief EP0 state. | |
* @note It is an union because IN and OUT endpoints are never used at the | |
* same time for EP0. | |
*/ | |
static union { | |
/** | |
* @brief IN EP0 state. | |
*/ | |
USBInEndpointState in; | |
/** | |
* @brief OUT EP0 state. | |
*/ | |
USBOutEndpointState out; | |
} ep0_state; | |
/** | |
* @brief Buffer for the EP0 setup packets. | |
*/ | |
static uint8_t ep0setup_buffer[8]; | |
/** | |
* @brief EP0 initialization structure. | |
*/ | |
static const USBEndpointConfig ep0config = { | |
USB_EP_MODE_TYPE_CTRL, | |
_usb_ep0setup, | |
_usb_ep0in, | |
_usb_ep0out, | |
0x40, | |
0x40, | |
&ep0_state.in, | |
&ep0_state.out, | |
1, | |
ep0setup_buffer | |
}; | |
/*===========================================================================*/ | |
/* Driver local functions. */ | |
/*===========================================================================*/ | |
/** | |
* @brief Writes packet(s) | |
* | |
* @param[in] usbp pointer to a @p USBDriver | |
* @param[in] ep endpoint | |
* | |
* @notapi | |
*/ | |
static size_t usb_packet_write(USBDriver *usbp, usbep_t ep) { | |
USBInEndpointState *isp = usbp->epc[ep]->in_state; | |
size_t txcnt = isp->txcnt; | |
/* Select endpoint */ | |
UENUM = ep; | |
/* Don't send more than in_maxsize */ | |
size_t n = isp->txsize - isp->txcnt; | |
if (n > usbp->epc[ep]->in_maxsize) | |
n = usbp->epc[ep]->in_maxsize; | |
/* Write data | |
* NOTE: RWAL not used for EP0 | |
*/ | |
if (isp->txsize > 0) { /* Avoid null pointer */ | |
if (isp->txqueued) { | |
output_queue_t *oqp = isp->mode.queue.txqueue; | |
for ( ; n-- > 0 ; isp->txcnt++ ) { | |
msg_t msg = oqGetI(oqp); | |
osalDbgAssert((msg != Q_EMPTY), | |
"unexpected empty queue"); | |
osalDbgAssert((ep == 0) || (UEINTX & (1<<RWAL)), | |
"internal buffer full"); | |
UEDATX = (uint8_t)msg; | |
} | |
} else { | |
const uint8_t *buf = &isp->mode.linear.txbuf[isp->txcnt]; | |
for ( ; n-- > 0 ; isp->txcnt++ ) { | |
osalDbgAssert((ep == 0) || (UEINTX & (1<<RWAL)), | |
"internal buffer full"); | |
UEDATX = *buf++; | |
} | |
} | |
} | |
/* Transmit data by | |
* - acknowledging the interrupt | |
* - releasing fifo | |
* NOTE: TXINI must be cleared before FIFOCON | |
* NOTE: FIFOCON is not used for EP0 | |
*/ | |
UEINTX &= ~(1 << TXINI) & ~(1 << FIFOCON); | |
/* Bytes written */ | |
return isp->txcnt - txcnt; | |
} | |
/** | |
* @brief Reads from a dedicated packet buffer. | |
* | |
* @param[in] usbp pointer to a @p USBDriver | |
* @param[in] ep endpoint | |
* | |
* @notapi | |
*/ | |
static size_t usb_packet_read(USBDriver *usbp, usbep_t ep) { | |
USBOutEndpointState *osp = usbp->epc[ep]->out_state; | |
size_t i, n; | |
/* Select endpoint */ | |
UENUM = ep; | |
/* Number of bytes availlable */ | |
n = (UEBCX & 0x7fff); | |
/* Read data */ | |
if (osp->rxsize > 0) { /* Avoid null pointer */ | |
if (osp->rxqueued) { | |
input_queue_t *iqp = osp->mode.queue.rxqueue; | |
for (i = 0 ; i < n ; i++) { | |
osalDbgAssert((ep == 0) || (UEINTX & (1<<RWAL)), | |
"internal buffer empty"); | |
msg_t msg = iqPutI(iqp, UEDATX); | |
osalDbgAssert((msg != Q_FULL), | |
"unexpected full queue"); | |
} | |
} else { | |
uint8_t *buf = &osp->mode.linear.rxbuf[osp->rxcnt]; | |
for (i = 0 ; i < n ; i++) { | |
osalDbgAssert((ep == 0) || (UEINTX & (1<<RWAL)), | |
"internal buffer empty"); | |
*buf++ = UEDATX; | |
} | |
} | |
} | |
/* Incread received counter */ | |
osp->rxcnt += n; | |
/* Decrease number of packet to receive */ | |
osp->rxpkts -= 1; | |
/* Acknowledge interrupt */ | |
UEINTX &= ~(1 << RXOUTI) & ~(1 << FIFOCON); | |
/* Bytes read */ | |
return n; | |
} | |
/** | |
* @brief Common ISR code, serves the EP-related interrupts. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
static void usb_serve_endpoints(USBDriver *usbp, usbep_t ep) { | |
const USBEndpointConfig *epcp = usbp->epc[ep]; | |
int yo = 0; | |
/* Select endpoint */ | |
UENUM = ep; | |
if (ep == 0) { | |
if (UEINTX & (1 << RXSTPI)) { /* received setup */ | |
/* Setup packets are handled using a specific callback.*/ | |
_usb_isr_invoke_setup_cb(usbp, ep); | |
yo++; | |
} | |
if (UEINTX & (1 << RXOUTI)) { /* received out data */ | |
USBOutEndpointState *osp = epcp->out_state; | |
size_t n = usb_packet_read(usbp, ep); | |
/* The transaction is completed if the specified number of packets | |
has been received or the current packet is a short packet.*/ | |
if ((n < epcp->out_maxsize) || (osp->rxpkts == 0)) { | |
_usb_isr_invoke_out_cb(usbp, ep); | |
} | |
yo++; | |
} | |
if (yo > 1) { | |
palWriteBus(&LED1Bus, 1); | |
} | |
if (UEINTX & (1 << TXINI)) { /* ready to accept in */ | |
USBInEndpointState *isp = epcp->in_state; | |
if (isp->txcnt < isp->txsize) { | |
usb_packet_write(usbp, ep); | |
} else { | |
_usb_isr_invoke_in_cb(usbp, ep); /* Invokes the callback */ | |
} | |
yo++; | |
} | |
return; | |
} | |
if (UEINTX & (1 << RXOUTI)) { /* received out data */ | |
USBOutEndpointState *osp = epcp->out_state; | |
size_t n; | |
/* Read packet */ | |
usb_packet_read(usbp, ep); | |
/* The transaction is completed if the specified number of packets | |
* has been received or the current packet is a short packet. | |
*/ | |
if ((n < epcp->out_maxsize) || (osp->rxpkts == 0)) { | |
UEINTX &= ~(1 << RXOUTE); /* Disable interrupt */ | |
_usb_isr_invoke_out_cb(usbp, ep); /* Invokes the callback */ | |
} | |
} | |
if (UEINTX & (1 << TXINI)) { /* ready to accept in */ | |
USBInEndpointState *isp = epcp->in_state; | |
/* Data transfer completed? */ | |
if (isp->txcnt < isp->txsize) { | |
/* Write packet | |
* This will acknowledge the interrupt (and clear fifo) | |
*/ | |
usb_packet_write(usbp, ep); | |
} else { | |
//UEINTX &= ~(1 << TXINI); | |
UEINTX &= ~(1 << TXINE); /* Disable interrupt */ | |
_usb_isr_invoke_in_cb(usbp, ep); /* Invokes the callback */ | |
} | |
} | |
if (UEINTX & (1 << RXSTPI)) { /* received setup */ | |
/* Setup packets are handled using a specific callback.*/ | |
_usb_isr_invoke_setup_cb(usbp, ep); | |
} | |
} | |
/*===========================================================================*/ | |
/* Driver interrupt handlers. */ | |
/*===========================================================================*/ | |
#if AVR_USB_USE_USB1 || defined(__DOXYGEN__) | |
/** | |
* @brief USB high priority interrupt handler. | |
* | |
* @isr | |
*/ | |
OSAL_IRQ_HANDLER(USB_COM_vect) { | |
USBDriver *usbp = &USBD1; | |
OSAL_IRQ_PROLOGUE(); | |
while (UEINT) { | |
for (usbep_t ep = 0 ; ep <= USB_MAX_ENDPOINTS ; ep++) | |
if (UEINT & (1 << ep)) | |
usb_serve_endpoints(usbp, ep); | |
} | |
OSAL_IRQ_EPILOGUE(); | |
} | |
/** | |
* @brief USB low priority interrupt handler. | |
* | |
* @isr | |
*/ | |
OSAL_IRQ_HANDLER(USB_GEN_vect) { | |
USBDriver *usbp = &USBD1; | |
OSAL_IRQ_PROLOGUE(); | |
/* VBUS connected (Buggy on REV A, B) */ | |
if ((USBINT & (1 << VBUSTI)) && (USBCON & (1 << VBUSTE))) { | |
USBINT &= ~(1 << VBUSTI); /* Acknowledge */ | |
if (USBSTA & (1 << VBUS)) { | |
// Plugged | |
} else { | |
// Unplugged | |
} | |
} | |
/* SOF handling.*/ | |
if ((UDINT & (1 << SOFI)) && (UDIEN & (1 << SOFE))) { | |
/* Acknowledge interrupt */ | |
UDINT &= ~(1 << SOFI); | |
/* Invoke callback */ | |
_usb_isr_invoke_sof_cb(usbp); | |
} | |
/* USB bus SUSPEND condition handling.*/ | |
if ((UDINT & (1 << SUSPI)) && (UDIEN & (1 << SUSPE))) { | |
/* Disable interrupts for SUSPEND and enable interrupts for WAKEUP */ | |
UDIEN &= ~(1 << SUSPE); | |
UDIEN |= (1 << WAKEUPE); | |
/* Acknowledge interrupt */ | |
UDINT &= ~(1 << SUSPI); | |
/* Freeze clock and disable PLL */ | |
USBCON |= (1 << FRZCLK); | |
PLLCSR = 0; | |
/* Suspend */ | |
_usb_suspend(usbp); | |
} | |
/* USB bus WAKEUP condition handling.*/ | |
if ((UDINT & (1 << WAKEUPI)) && (UDIEN & (1 << WAKEUPE))) { | |
/* Set PLL to CPU speed and wait for lock */ | |
PLLCSR = PLL_SETTINGS | (1 << PLLE); | |
while (!(PLLCSR & (1 << PLOCK))) { | |
/* wait for lock pll */ | |
} | |
_delay_us(1); | |
/* Unfreeze clock */ | |
USBCON &= ~(1 << FRZCLK); | |
/* Acknowledge interrupt */ | |
UDINT &= ~(1 << WAKEUPI); | |
/* Disable interrupts for WAKEUP and enable interrupts for SUSPEND */ | |
UDIEN &= ~(1 << WAKEUPE); | |
UDIEN |= (1 << SUSPE); | |
/* Wake up */ | |
_usb_wakeup(usbp); | |
} | |
/* USB bus reset condition handling.*/ | |
if ((UDINT & (1 << EORSTI)) && (UDIEN & (1 << EORSTE))) { | |
/* Acknowleged interrupt */ | |
UDINT &= ~(1 << EORSTI); | |
/* Reset */ | |
_usb_reset(usbp); | |
} | |
OSAL_IRQ_EPILOGUE(); | |
} | |
#endif | |
/*===========================================================================*/ | |
/* Driver exported functions. */ | |
/*===========================================================================*/ | |
/** | |
* @brief Low level USB driver initialization. | |
* | |
* @notapi | |
*/ | |
void usb_lld_init(void) { | |
/* Driver initialization.*/ | |
#if AVR_USB_USE_USB1 | |
usbObjectInit(&USBD1); | |
#endif | |
/* Workaround for AVR8 bootloaders that fail to turn off the OTG pad | |
* before running the loaded application. This causes VBUS detection | |
* to fail unless we first force it off to reset it. | |
*/ | |
USBCON &= ~(1 << OTGPADE); | |
/* Start with PPL off */ | |
PLLCSR &= ~(1 << PLLE); | |
/* PLL Frequency */ | |
PLLFRQ = (1 << PDIV2); | |
/* Power internal regulator */ | |
UHWCON |= (1 << UVREGE); | |
/* Clock freezed, VBUS off, USB off, interrupts off/cleared */ | |
USBCON = ~(1 << USBE) & (1 << FRZCLK) & ~(1 << OTGPADE) & ~(1 << VBUSTE); | |
USBINT = 0; | |
/* Disable/Clear all Device interrupts */ | |
UDIEN = 0; | |
UDINT = 0; | |
/* Low power mode, Detached */ | |
UDCON |= (1 << LSM); /* UDCON access is allowed while */ | |
UDCON |= (1 << DETACH); /* clock is freezed */ | |
/* Enable VBUS pad, and VBUSTI interrupt */ | |
USBCON |= (1 << OTGPADE) | (1 << VBUSTE); | |
} | |
/** | |
* @brief Configures and activates the USB peripheral. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* | |
* @notapi | |
*/ | |
void usb_lld_start(USBDriver *usbp) { | |
if (usbp->state == USB_STOP) { | |
/* USB macro enable */ | |
USBCON |= (1 << USBE); | |
/* Set PLL to CPU speed and wait for lock */ | |
PLLCSR = PLL_SETTINGS | (1 << PLLE); | |
while (!(PLLCSR & (1 << PLOCK))) { | |
/* wait for lock pll */ | |
} | |
_delay_us(1); | |
/* Start USB clock (Unfreeze clock) */ | |
USBCON &= ~(1 << FRZCLK); | |
/* Activate devices */ | |
#if AVR_USB_USE_USB1 | |
if (&USBD1 == usbp) { | |
/* enable attach resistor, set full speed mode */ | |
UDCON &= ~(1 << RSTCPU) & ~(1 << LSM) & ~(1 << RMWKUP) & ~(1 << DETACH); | |
/* clear already pending WAKEUP / SUSPEND requests */ | |
UDINT &= ~(1 << WAKEUPI) & ~(1 << SUSPI); | |
/* Enable interrupts for EOR (End of Reset), SOF (start of frame), | |
* and SUSPEND | |
*/ | |
UDIEN = (1 << EORSTE) | (1 << SOFE) | (1 << SUSPE); | |
} | |
#endif | |
/* Reset procedure enforced on driver start.*/ | |
_usb_reset(usbp); | |
} | |
/* Configuration.*/ | |
} | |
/** | |
* @brief Deactivates the USB peripheral. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* | |
* @notapi | |
*/ | |
void usb_lld_stop(USBDriver *usbp) { | |
/* If in ready state then disables the USB clock.*/ | |
if (usbp->state != USB_STOP) { | |
/* Freeze clock and disable VBUS Pad */ | |
USBCON = (USBCON & ~(1<<OTGPADE)) | (1<<FRZCLK); | |
/* Turn off PLL */ | |
PLLCSR &= ~(1 << PLLE); | |
/* USB macro enable bit */ | |
USBCON &= ~(1<<USBE); | |
#if AVR_USB_USE_USB1 | |
if (&USBD1 == usbp) { | |
/* Detach, Clear/Supend interrupts */ | |
UDCON |= (1 << DETACH); | |
UDINT = 0; | |
UDIEN = 0; | |
} | |
#endif | |
} | |
} | |
/** | |
* @brief USB low level reset routine. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* | |
* @notapi | |
*/ | |
void usb_lld_reset(USBDriver *usbp) { | |
/* EP0 initialization.*/ | |
usbp->epc[0] = &ep0config; | |
usb_lld_init_endpoint(usbp, 0); | |
/* Post reset initialization.*/ | |
UERST = 0x7F; /* reset all endpoints (including ep0) */ | |
_delay_us(1); | |
UERST = 0; | |
/* Acknowledge interrupt */ | |
UDINT &= ~(1 << WAKEUPI); | |
UDINT &= ~(1 << SUSPI); | |
/* Disable interrupts for WAKEUP and enable interrupts for SUSPEND */ | |
UDIEN &= ~(1 << WAKEUPE); | |
UDIEN |= (1 << SUSPE); | |
/* Enable setup interupt for EP0 */ | |
UENUM = 0; | |
UEIENX |= (1 << RXSTPE); | |
UEIENX |= (1 << RXOUTE); | |
UEIENX |= (1 << TXINE); | |
} | |
/** | |
* @brief Sets the USB address. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* | |
* @notapi | |
*/ | |
void usb_lld_set_address(USBDriver *usbp) { | |
/* ADDEN and UADD shall not be written at the same time */ | |
UDADDR = (usbp->address) | (1<<ADDEN); | |
} | |
/** | |
* @brief Enables an endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) { | |
/* Chip doesn't support bidirectional endpoint, | |
* except for EP0 where IN and OUT are newer simultaneous | |
*/ | |
const USBEndpointConfig *epcp = usbp->epc[ep]; | |
size_t size; | |
uint8_t mode; | |
uint8_t bank = 0; | |
/* Mode type */ | |
switch (epcp->ep_mode & USB_EP_MODE_TYPE) { | |
case USB_EP_MODE_TYPE_ISOC: mode = (1<<EPTYPE0); break; | |
case USB_EP_MODE_TYPE_BULK: mode = (1<<EPTYPE1); break; | |
case USB_EP_MODE_TYPE_INTR: mode = (1<<EPTYPE1) | (1<<EPTYPE0); break; | |
case USB_EP_MODE_TYPE_CTRL: mode = 0x00; break; | |
} | |
osalDbgAssert((epcp->in_cb != NULL) || (epcp->out_cb != NULL), | |
"endpoint need to have a direction (in/out)"); | |
osalDbgAssert((ep == 0) || (epcp->in_cb == NULL) || (epcp->out_cb == NULL), | |
"birectionnal endpoints not supported by chip"); | |
/* Direction and size (we can either be IN or OUT) | |
* Special case for EP0 which is IN and OUT but with the same buffer. | |
*/ | |
if (ep == 0) { /* Special case of EP0 */ | |
osalDbgAssert((epcp->in_cb != NULL) && (epcp->out_cb != NULL), | |
"EP0 need IN/OUT endpoint"); | |
osalDbgAssert(epcp->in_maxsize == epcp->out_maxsize, | |
"Only in_maxsize == out_maxsize supported for EP0"); | |
size = epcp->in_maxsize; | |
} else if (epcp->in_cb != NULL) { /* IN endpoint */ | |
mode |= (1 << EPDIR); | |
size = epcp->in_maxsize; | |
} else { /* epcp->out_cb != NULL */ /* OUT endpoint */ | |
mode &= ~(1 << EPDIR); | |
size = epcp->out_maxsize; | |
} | |
/* Size */ | |
/* DPRAM = 832 bytes | |
* – 1 endpoint 64 bytes max. (default control endpoint) | |
* – 1 endpoints of 256 bytes max., (one or two banks) | |
* – 5 endpoints of 64 bytes max., (one or two banks) | |
* WARN: correct selection of memory size is not asserted | |
*/ | |
osalDbgAssert(((epcp->out_maxsize <= 512) && (epcp->in_maxsize <= 512)), | |
"max supported bank size: 512"); | |
if (size <= 64) { | |
if (size <= 16) { | |
if (size <= 8) { bank |= (0X0 << EPSIZE0); } /* 8 */ | |
else { bank |= (0x1 << EPSIZE0); } /* 16 */ | |
} else { | |
if (size <= 32) { bank |= (0x2 << EPSIZE0); } /* 32 */ | |
else { bank |= (0x3 << EPSIZE0); } /* 64 */ | |
} | |
/* Requested memory is reasonnable, allow double bank */ | |
bank |= (1 << EPBK0); | |
} else { | |
if (size <= 256) { | |
if (size <= 128) { bank |= (0X4 << EPSIZE0); } /* 128 */ | |
else { bank |= (0x5 << EPSIZE0); } /* 256 */ | |
} else { | |
if (size <= 512) { bank |= (0x6 << EPSIZE0); } /* 512 */ | |
else { /* unsupported */ } | |
} | |
} | |
bank |= (1 << ALLOC); | |
if (ep == 0) { /* disable double bank for EP0 */ | |
bank &= ~(1 << EPBK0); | |
} | |
UENUM = ep; | |
UECONX = (1 << EPEN); | |
UECFG0X = mode; | |
UECFG1X = bank; | |
osalDbgAssert((UESTA0X & (1 << CFGOK)), | |
"bad usb endpoint configration"); | |
UERST = (1 << ep); /* not sure if necessary?! */ | |
UEINTX = 0; | |
} | |
/** | |
* @brief Disables all the active endpoints except the endpoint zero. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* | |
* @notapi | |
*/ | |
void usb_lld_disable_endpoints(USBDriver *usbp) { | |
unsigned i; | |
/* Disabling all endpoint, except EP0 */ | |
/* We wont have DPRAM allocation probleme as all endpoints | |
* are disabled (and all memory freed) except ep0 | |
*/ | |
for (i = 1; i <= USB_ENDPOINTS_NUMBER; i++) { | |
UENUM = i; | |
UECONX &= ~(1 << EPEN); /* disable */ | |
UECFG1X &= ~(1 << ALLOC); /* free memory */ | |
} | |
} | |
/** | |
* @brief Returns the status of an endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* @return The endpoint status. | |
* @retval EP_STATUS_DISABLED The endpoint is not active. | |
* @retval EP_STATUS_STALLED The endpoint is stalled. | |
* @retval EP_STATUS_ACTIVE The endpoint is active. | |
* | |
* @notapi | |
*/ | |
static usbepstatus_t _usb_lld_get_status(USBDriver *usbp, usbep_t ep) { | |
(void)usbp; | |
UENUM = ep; | |
if (UECONX & (1 << EPEN)) { | |
if (UEINTX & ((1 << STALLEDI) | (1 << STALLRQ))) { | |
return EP_STATUS_STALLED; | |
} else { | |
return EP_STATUS_ACTIVE; | |
} | |
} else { | |
return EP_STATUS_DISABLED; | |
} | |
} | |
/** | |
* @brief Returns the status of an OUT endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* @return The endpoint status. | |
* @retval EP_STATUS_DISABLED The endpoint is not active. | |
* @retval EP_STATUS_STALLED The endpoint is stalled. | |
* @retval EP_STATUS_ACTIVE The endpoint is active. | |
* | |
* @note This bring the whole endpoint in the stalled state | |
* as there is no distinction between IN and OUT. | |
* | |
* @notapi | |
*/ | |
usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_get_status"))); | |
/** | |
* @brief Returns the status of an IN endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* @return The endpoint status. | |
* @retval EP_STATUS_DISABLED The endpoint is not active. | |
* @retval EP_STATUS_STALLED The endpoint is stalled. | |
* @retval EP_STATUS_ACTIVE The endpoint is active. | |
* | |
* @note This bring the whole endpoint in the stalled state | |
* as there is no distinction between IN and OUT. | |
* | |
* @notapi | |
*/ | |
usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_get_status"))); | |
/** | |
* @brief Reads a setup packet from the dedicated packet buffer. | |
* @details This function must be invoked in the context of the @p setup_cb | |
* callback in order to read the received setup packet. | |
* @pre In order to use this function the endpoint must have been | |
* initialized as a control endpoint. | |
* @post The endpoint is ready to accept another packet. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* @param[out] buf buffer where to copy the packet data | |
* | |
* @notapi | |
*/ | |
void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) { | |
(void)usbp; | |
/* Endpoint */ | |
osalDbgAssert(ep == 0, "for control endpoint only"); | |
UENUM = ep; | |
/* Read data */ | |
for (int i = 0 ; i < 8 ; i++) { | |
*buf++ = UEDATX; | |
} | |
/* Acknowledge interupts (This will release the bank) | |
* NOTE: FIFCON and RWAL are not used on EP0 | |
* XXX : Why do we need to also clear RXOUTI ?! | |
*/ | |
UEINTX &= ~(1<<RXSTPI) & ~(1<<RXOUTI) & ~(1<<TXINI); | |
} | |
/** | |
* @brief Prepares for a receive operation. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_prepare_receive(USBDriver *usbp, usbep_t ep) { | |
USBOutEndpointState *osp = usbp->epc[ep]->out_state; | |
/* Transfer initialization.*/ | |
if (osp->rxsize == 0) /* Special case for zero sized packets.*/ | |
osp->rxpkts = 1; | |
else | |
osp->rxpkts = (uint16_t)((osp->rxsize + usbp->epc[ep]->out_maxsize - 1) / | |
usbp->epc[ep]->out_maxsize); | |
} | |
/** | |
* @brief Prepares for a transmit operation. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_prepare_transmit(USBDriver *usbp, usbep_t ep) { | |
/* NOTHING */ | |
} | |
/** | |
* @brief Starts a receive operation on an OUT endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_start_out(USBDriver *usbp, usbep_t ep) { | |
/* Enable RX OUT Interrupt for endpoint */ | |
UENUM = ep; | |
UEIENX |= (1 << RXOUTE); | |
if (UEIENX & (1 << RXOUTI)) { | |
usb_packet_read(usbp, ep); | |
} | |
/* Data will be really read when we will got | |
* the RX OUT interrupt on USB_COM_vect | |
*/ | |
} | |
/** | |
* @brief Starts a transmit operation on an IN endpoint. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_start_in(USBDriver *usbp, usbep_t ep) { | |
/* Enable TX IN Interrupt for endpoint */ | |
UENUM = ep; | |
UEIENX |= (1 << TXINE); | |
if (UEINTX & (1 << TXINI)) { | |
usb_packet_write(usbp, ep); | |
} | |
/* Data will be really transmitted when we will got | |
* the TX IN interrupt on USB_COM_vect | |
*/ | |
} | |
/** | |
* @brief Brings an endpoint in the stalled state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
static void _usb_lld_stall(USBDriver *usbp, usbep_t ep) { | |
UENUM = ep; | |
UECONX = (1<<STALLRQ) | (1<<EPEN); | |
} | |
/** | |
* @brief Brings an OUT endpoint in the stalled state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @note This bring the whole endpoint in the stalled state | |
* as there is no distinction between IN and OUT. | |
* | |
* @notapi | |
*/ | |
void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_stall"))); | |
/** | |
* @brief Brings an IN endpoint in the stalled state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @note This bring the whole endpoint in the stalled state | |
* as there is no distinction between IN and OUT. | |
* | |
* @notapi | |
*/ | |
void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_stall"))); | |
/** | |
* @brief Brings an endpoint in the active state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void _usb_lld_clear(USBDriver *usbp, usbep_t ep) { | |
(void)usbp; | |
UENUM = ep; | |
UECONX = (1<<STALLRQC) | (1<<EPEN); | |
} | |
/** | |
* @brief Brings an OUT endpoint in the active state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @note This bring the whole endpoint in the active state | |
* as there is no distinction between IN and OUT. | |
* | |
* @notapi | |
*/ | |
void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_clear"))); | |
/** | |
* @brief Brings an IN endpoint in the active state. | |
* | |
* @param[in] usbp pointer to the @p USBDriver object | |
* @param[in] ep endpoint number | |
* | |
* @notapi | |
*/ | |
void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) | |
__attribute ((alias ("_usb_lld_clear"))); | |
#endif /* HAL_USE_USB */ | |
/** @} */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment