mirror of
https://github.com/hathach/tinyusb.git
synced 2026-02-05 00:25:47 +00:00
@ -95,18 +95,23 @@ enum {
|
||||
TUH_CFGID_INVALID = 0,
|
||||
TUH_CFGID_RPI_PIO_USB_CONFIGURATION = 100, // cfg_param: pio_usb_configuration_t
|
||||
TUH_CFGID_MAX3421 = 200,
|
||||
TUH_CFGID_FSDEV = 300,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage
|
||||
uint8_t max_nak; // max NAK per endpoint per frame to save CPU/SPI bus usage (0=unlimited)
|
||||
uint8_t cpuctl; // R16: CPU Control Register
|
||||
uint8_t pinctl; // R17: Pin Control Register. FDUPSPI bit is ignored
|
||||
} tuh_configure_max3421_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t max_nak; // max NAK per endpoint per frame to save CPU usage (0=unlimited)
|
||||
} tuh_configure_fsdev_t;
|
||||
|
||||
typedef union {
|
||||
// For TUH_CFGID_RPI_PIO_USB_CONFIGURATION use pio_usb_configuration_t
|
||||
|
||||
tuh_configure_max3421_t max3421;
|
||||
tuh_configure_fsdev_t fsdev;
|
||||
} tuh_configure_param_t;
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
@ -60,48 +60,56 @@
|
||||
TU_VERIFY_STATIC(CFG_TUH_FSDEV_ENDPOINT_MAX <= 255, "currently only use 8-bit for index");
|
||||
|
||||
enum {
|
||||
HCD_XFER_ERROR_MAX = 3
|
||||
HCD_XFER_ERROR_MAX = 3,
|
||||
HCD_XFER_NAK_MAX = 15,
|
||||
HCD_XFER_NAK_DEFAULT = 3,
|
||||
};
|
||||
|
||||
// Host driver struct for each opened endpoint
|
||||
typedef struct {
|
||||
uint8_t *buffer;
|
||||
uint16_t buflen;
|
||||
uint16_t queued_len;
|
||||
uint16_t max_packet_size;
|
||||
uint8_t dev_addr;
|
||||
uint8_t ep_addr;
|
||||
uint8_t ep_type;
|
||||
uint8_t interval;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t low_speed : 1;
|
||||
uint8_t ls_pre : 1;
|
||||
uint8_t allocated : 1;
|
||||
uint8_t next_setup : 1;
|
||||
uint8_t pid : 1;
|
||||
};
|
||||
} hcd_endpoint_t;
|
||||
|
||||
// Channel direction state
|
||||
typedef struct {
|
||||
hcd_endpoint_t* edpt;
|
||||
struct TU_ATTR_PACKED {
|
||||
uint8_t allocated : 1;
|
||||
uint8_t retry : 3;
|
||||
uint8_t nak : 4; // Max NAK count in current frame
|
||||
};
|
||||
} hcd_channel_dir_t;
|
||||
|
||||
// Additional info for each channel when it is active
|
||||
typedef struct {
|
||||
hcd_endpoint_t* edpt[2]; // OUT/IN
|
||||
uint16_t queued_len[2];
|
||||
uint8_t dev_addr;
|
||||
uint8_t ep_num;
|
||||
uint8_t ep_type;
|
||||
uint8_t allocated[2];
|
||||
uint8_t retry[2];
|
||||
hcd_channel_dir_t out, in;
|
||||
} hcd_channel_t;
|
||||
|
||||
// Root hub port state
|
||||
static struct {
|
||||
bool connected;
|
||||
} _hcd_port;
|
||||
|
||||
typedef struct {
|
||||
hcd_channel_t channel[FSDEV_EP_COUNT];
|
||||
hcd_endpoint_t edpt[CFG_TUH_FSDEV_ENDPOINT_MAX];
|
||||
} hcd_data_t;
|
||||
bool connected;
|
||||
} _hcd_data;
|
||||
|
||||
hcd_data_t _hcd_data;
|
||||
static tuh_configure_fsdev_t _tuh_cfg = {
|
||||
.max_nak = HCD_XFER_NAK_DEFAULT,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Prototypes
|
||||
@ -114,7 +122,6 @@ static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type)
|
||||
static bool edpt_xfer_kickoff(uint8_t ep_id);
|
||||
static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir);
|
||||
static void edpoint_close(uint8_t ep_id);
|
||||
static void port_status_handler(uint8_t rhport, bool in_isr);
|
||||
static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
|
||||
static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
|
||||
static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir);
|
||||
@ -129,7 +136,11 @@ static inline void endpoint_dealloc(hcd_endpoint_t* edpt) {
|
||||
}
|
||||
|
||||
static inline void channel_dealloc(hcd_channel_t* ch, tusb_dir_t dir) {
|
||||
ch->allocated[dir] = 0;
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
ch->out.allocated = 0;
|
||||
} else {
|
||||
ch->in.allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Write channel state in specified direction
|
||||
@ -194,9 +205,11 @@ static inline uint16_t channel_get_rx_count(uint8_t ch_id) {
|
||||
// Optional HCD configuration, called by tuh_configure()
|
||||
bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
|
||||
(void) rhport;
|
||||
(void) cfg_id;
|
||||
(void) cfg_param;
|
||||
return false;
|
||||
TU_VERIFY(cfg_id == TUH_CFGID_FSDEV && cfg_param != NULL);
|
||||
|
||||
tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
|
||||
_tuh_cfg.max_nak = tu_min8(cfg->fsdev.max_nak, HCD_XFER_NAK_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Initialize controller to host mode
|
||||
@ -210,11 +223,11 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) {
|
||||
tu_memclr(&_hcd_data, sizeof(_hcd_data));
|
||||
|
||||
// Enable interrupts for host mode
|
||||
FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM |
|
||||
FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SOFM | USB_CNTR_SUSPM |
|
||||
USB_CNTR_WKUPM | USB_CNTR_ERRM | USB_CNTR_PMAOVRM;
|
||||
|
||||
// Initialize port state
|
||||
_hcd_port.connected = false;
|
||||
_hcd_data.connected = false;
|
||||
|
||||
fsdev_connect(rhport);
|
||||
|
||||
@ -229,35 +242,48 @@ bool hcd_deinit(uint8_t rhport) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void port_status_handler(uint8_t rhport, bool in_isr) {
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
static inline void sof_handler(void) {
|
||||
// Reset NAK counters for all active channels
|
||||
for (uint8_t ch_id = 0; ch_id < FSDEV_EP_COUNT; ch_id++) {
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
if (channel->out.allocated) {
|
||||
channel->out.nak = 0;
|
||||
}
|
||||
if (channel->in.allocated) {
|
||||
channel->in.nak = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void port_status_handler(uint8_t rhport, bool in_isr) {
|
||||
uint32_t const fnr_reg = FSDEV_REG->FNR;
|
||||
uint32_t const istr_reg = FSDEV_REG->ISTR;
|
||||
// SE0 detected USB Disconnected state
|
||||
if ((fnr_reg & (USB_FNR_RXDP | USB_FNR_RXDM)) == 0U) {
|
||||
_hcd_port.connected = false;
|
||||
_hcd_data.connected = false;
|
||||
hcd_event_device_remove(rhport, in_isr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_hcd_port.connected) {
|
||||
if (!_hcd_data.connected) {
|
||||
// J-state or K-state detected & LastState=Disconnected
|
||||
if (((fnr_reg & USB_FNR_RXDP) != 0U) || ((istr_reg & USB_ISTR_LS_DCONN) != 0U)) {
|
||||
_hcd_port.connected = true;
|
||||
_hcd_data.connected = true;
|
||||
hcd_event_device_attach(rhport, in_isr);
|
||||
}
|
||||
} else {
|
||||
// J-state or K-state detected & lastState=Connected: a Missed disconnection is detected
|
||||
if (((fnr_reg & USB_FNR_RXDP) != 0U) || ((istr_reg & USB_ISTR_LS_DCONN) != 0U)) {
|
||||
_hcd_port.connected = false;
|
||||
_hcd_data.connected = false;
|
||||
hcd_event_device_remove(rhport, in_isr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// Interrupt Helper Functions
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
// Handle ACK response
|
||||
static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
|
||||
@ -271,38 +297,40 @@ static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
// OUT/TX direction
|
||||
if (edpt->buflen != channel->queued_len[TUSB_DIR_OUT]) {
|
||||
if (edpt->buflen != edpt->queued_len) {
|
||||
// More data to send
|
||||
uint16_t const len = tu_min16(edpt->buflen - channel->queued_len[TUSB_DIR_OUT], edpt->max_packet_size);
|
||||
uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
|
||||
uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_TX);
|
||||
fsdev_write_packet_memory(pma_addr, &(edpt->buffer[channel->queued_len[TUSB_DIR_OUT]]), len);
|
||||
fsdev_write_packet_memory(pma_addr, &(edpt->buffer[edpt->queued_len]), len);
|
||||
btable_set_count(ch_id, BTABLE_BUF_TX, len);
|
||||
channel->queued_len[TUSB_DIR_OUT] += len;
|
||||
edpt->queued_len += len;
|
||||
channel_write_status(ch_id, ch_reg, TUSB_DIR_OUT, EP_STAT_VALID, false);
|
||||
channel->out.nak = 0;
|
||||
} else {
|
||||
// Transfer complete
|
||||
channel_dealloc(channel, TUSB_DIR_OUT);
|
||||
edpt->pid = (ch_reg & USB_CHEP_DTOG_TX) ? 1 : 0;
|
||||
hcd_event_xfer_complete(daddr, ep_num, channel->queued_len[TUSB_DIR_OUT], XFER_RESULT_SUCCESS, true);
|
||||
hcd_event_xfer_complete(daddr, ep_num, edpt->queued_len, XFER_RESULT_SUCCESS, true);
|
||||
}
|
||||
} else {
|
||||
// IN/RX direction
|
||||
uint16_t const rx_count = channel_get_rx_count(ch_id);
|
||||
uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_RX);
|
||||
|
||||
fsdev_read_packet_memory(edpt->buffer + channel->queued_len[TUSB_DIR_IN], pma_addr, rx_count);
|
||||
channel->queued_len[TUSB_DIR_IN] += rx_count;
|
||||
fsdev_read_packet_memory(edpt->buffer + edpt->queued_len, pma_addr, rx_count);
|
||||
edpt->queued_len += rx_count;
|
||||
|
||||
if ((rx_count < edpt->max_packet_size) || (channel->queued_len[TUSB_DIR_IN] >= edpt->buflen)) {
|
||||
if ((rx_count < edpt->max_packet_size) || (edpt->queued_len >= edpt->buflen)) {
|
||||
// Transfer complete (short packet or all bytes received)
|
||||
channel_dealloc(channel, TUSB_DIR_IN);
|
||||
edpt->pid = (ch_reg & USB_CHEP_DTOG_RX) ? 1 : 0;
|
||||
hcd_event_xfer_complete(daddr, ep_num | TUSB_DIR_IN_MASK, channel->queued_len[TUSB_DIR_IN], XFER_RESULT_SUCCESS, true);
|
||||
hcd_event_xfer_complete(daddr, ep_num | TUSB_DIR_IN_MASK, edpt->queued_len, XFER_RESULT_SUCCESS, true);
|
||||
} else {
|
||||
// More data expected
|
||||
uint16_t const cnt = tu_min16(edpt->buflen - channel->queued_len[TUSB_DIR_IN], edpt->max_packet_size);
|
||||
uint16_t const cnt = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
|
||||
btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, cnt);
|
||||
channel_write_status(ch_id, ch_reg, TUSB_DIR_IN, EP_STAT_VALID, false);
|
||||
channel->in.nak = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,10 +344,17 @@ static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
if (ep_id == TUSB_INDEX_INVALID_8) return;
|
||||
|
||||
hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
|
||||
// Retry non-periodic transfer immediately,
|
||||
// Retry non-periodic transfer immediately if NAK count not exceeded
|
||||
// Periodic transfer will be retried by next frame automatically
|
||||
if (edpt->ep_type == TUSB_XFER_CONTROL || edpt->ep_type == TUSB_XFER_BULK) {
|
||||
channel_write_status(ch_id, ch_reg, dir, EP_STAT_VALID, false);
|
||||
hcd_channel_dir_t* channel_dir =
|
||||
(dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in);
|
||||
if (channel_dir->nak < HCD_XFER_NAK_MAX) {
|
||||
channel_dir->nak++;
|
||||
}
|
||||
if (channel_dir->nak < _tuh_cfg.max_nak || _tuh_cfg.max_nak == 0) {
|
||||
channel_write_status(ch_id, ch_reg, dir, EP_STAT_VALID, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,13 +363,17 @@ static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
uint8_t const ep_num = ch_reg & USB_EPADDR_FIELD;
|
||||
uint8_t const daddr = (ch_reg & USB_CHEP_DEVADDR_Msk) >> USB_CHEP_DEVADDR_Pos;
|
||||
|
||||
uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
|
||||
if (ep_id == TUSB_INDEX_INVALID_8) return;
|
||||
|
||||
hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
channel_dealloc(channel, dir);
|
||||
|
||||
channel_write_status(ch_id, ch_reg, dir, EP_STAT_DISABLED, false);
|
||||
|
||||
hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0),
|
||||
channel->queued_len[dir], XFER_RESULT_STALLED, true);
|
||||
edpt->queued_len, XFER_RESULT_STALLED, true);
|
||||
}
|
||||
|
||||
// Handle error response
|
||||
@ -345,21 +384,24 @@ static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0));
|
||||
if (ep_id == TUSB_INDEX_INVALID_8) return;
|
||||
|
||||
hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id];
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
|
||||
ch_reg &= USB_EPREG_MASK | CH_STAT_MASK(dir);
|
||||
ch_reg &= ~(dir == TUSB_DIR_OUT ? USB_CH_ERRTX : USB_CH_ERRRX);
|
||||
|
||||
if (channel->retry[dir] < HCD_XFER_ERROR_MAX) {
|
||||
hcd_channel_dir_t* channel_dir =
|
||||
(dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in);
|
||||
if (channel_dir->retry < HCD_XFER_ERROR_MAX) {
|
||||
// Retry
|
||||
channel->retry[dir]++;
|
||||
channel_dir->retry++;
|
||||
ch_change_status(&ch_reg, dir, EP_STAT_VALID);
|
||||
} else {
|
||||
// Failed after retries
|
||||
channel_dealloc(channel, dir);
|
||||
ch_change_status(&ch_reg, dir, EP_STAT_DISABLED);
|
||||
hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0),
|
||||
channel->queued_len[dir], XFER_RESULT_FAILED, true);
|
||||
edpt->queued_len, XFER_RESULT_FAILED, true);
|
||||
}
|
||||
ch_write(ch_id, ch_reg, false);
|
||||
}
|
||||
@ -368,7 +410,7 @@ static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) {
|
||||
static inline void handle_ctr_tx(uint32_t ch_id) {
|
||||
uint32_t ch_reg = ch_read(ch_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
TU_VERIFY(channel->allocated[TUSB_DIR_OUT] == 1,);
|
||||
TU_VERIFY(channel->out.allocated == 1,);
|
||||
|
||||
if ((ch_reg & USB_CH_ERRTX) == 0U) {
|
||||
// No error
|
||||
@ -388,7 +430,7 @@ static inline void handle_ctr_tx(uint32_t ch_id) {
|
||||
static inline void handle_ctr_rx(uint32_t ch_id) {
|
||||
uint32_t ch_reg = ch_read(ch_id) | USB_EP_CTR_TX | USB_EP_CTR_RX;
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
TU_VERIFY(channel->allocated[TUSB_DIR_IN] == 1,);
|
||||
TU_VERIFY(channel->in.allocated == 1,);
|
||||
|
||||
if ((ch_reg & USB_CH_ERRRX) == 0U) {
|
||||
// No error
|
||||
@ -408,7 +450,13 @@ static inline void handle_ctr_rx(uint32_t ch_id) {
|
||||
void hcd_int_handler(uint8_t rhport, bool in_isr) {
|
||||
uint32_t int_status = FSDEV_REG->ISTR;
|
||||
|
||||
/* Port Change Detected (Connection/Disconnection) */
|
||||
// Start of Frame
|
||||
if (int_status & USB_ISTR_SOF) {
|
||||
FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
|
||||
sof_handler();
|
||||
}
|
||||
|
||||
// Port Change Detected (Connection/Disconnection)
|
||||
if (int_status & USB_ISTR_DCON) {
|
||||
FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_DCON;
|
||||
port_status_handler(rhport, in_isr);
|
||||
@ -464,7 +512,7 @@ uint32_t hcd_frame_number(uint8_t rhport) {
|
||||
// Get the current connect status of roothub port
|
||||
bool hcd_port_connect_status(uint8_t rhport) {
|
||||
(void) rhport;
|
||||
return _hcd_port.connected;
|
||||
return _hcd_data.connected;
|
||||
}
|
||||
|
||||
// Reset USB bus on the port
|
||||
@ -525,7 +573,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
|
||||
edpt->max_packet_size = packet_size;
|
||||
edpt->interval = ep_desc->bInterval;
|
||||
edpt->pid = 0;
|
||||
edpt->low_speed = (hcd_port_speed_get(rhport) == TUSB_SPEED_FULL && tuh_speed_get(dev_addr) == TUSB_SPEED_LOW) ? 1 : 0;
|
||||
edpt->ls_pre = (hcd_port_speed_get(rhport) == TUSB_SPEED_FULL && tuh_speed_get(dev_addr) == TUSB_SPEED_LOW) ? 1 : 0;
|
||||
|
||||
// EP0 is bi-directional, so we need to open both OUT and IN channels
|
||||
if (ep_addr == 0) {
|
||||
@ -574,6 +622,7 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *b
|
||||
|
||||
edpt->buffer = buffer;
|
||||
edpt->buflen = buflen;
|
||||
edpt->queued_len = 0;
|
||||
|
||||
return edpt_xfer_kickoff(ep_id);
|
||||
}
|
||||
@ -588,8 +637,9 @@ bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
|
||||
|
||||
for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
|
||||
hcd_channel_t* channel = &_hcd_data.channel[i];
|
||||
uint8_t const allocated = (dir == TUSB_DIR_OUT) ? channel->out.allocated : channel->in.allocated;
|
||||
|
||||
if (channel->allocated[dir] == 1 &&
|
||||
if (allocated == 1 &&
|
||||
channel->dev_addr == dev_addr &&
|
||||
channel->ep_num == tu_edpt_number(ep_addr)) {
|
||||
channel_dealloc(channel, dir);
|
||||
@ -664,11 +714,11 @@ static void edpoint_close(uint8_t ep_id) {
|
||||
for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
|
||||
hcd_channel_t* channel = &_hcd_data.channel[i];
|
||||
uint32_t ch_reg = ch_read(i) | USB_EP_CTR_TX | USB_EP_CTR_RX;
|
||||
if (channel->allocated[TUSB_DIR_OUT] == 1 && channel->edpt[TUSB_DIR_OUT] == edpt) {
|
||||
if (channel->out.allocated == 1 && channel->out.edpt == edpt) {
|
||||
channel_dealloc(channel, TUSB_DIR_OUT);
|
||||
channel_write_status(i, ch_reg, TUSB_DIR_OUT, EP_STAT_DISABLED, true);
|
||||
}
|
||||
if (channel->allocated[TUSB_DIR_IN] == 1 && channel->edpt[TUSB_DIR_IN] == edpt) {
|
||||
if (channel->in.allocated == 1 && channel->in.edpt == edpt) {
|
||||
channel_dealloc(channel, TUSB_DIR_IN);
|
||||
channel_write_status(i, ch_reg, TUSB_DIR_IN, EP_STAT_DISABLED, true);
|
||||
}
|
||||
@ -697,27 +747,37 @@ static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type)
|
||||
// Find channel allocate for same ep_num but other direction
|
||||
tusb_dir_t const other_dir = (dir == TUSB_DIR_IN) ? TUSB_DIR_OUT : TUSB_DIR_IN;
|
||||
for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
|
||||
if (_hcd_data.channel[i].allocated[dir] == 0 &&
|
||||
_hcd_data.channel[i].allocated[other_dir] == 1 &&
|
||||
uint8_t const allocated_dir = (dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated;
|
||||
uint8_t const allocated_other = (other_dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated;
|
||||
if (allocated_dir == 0 &&
|
||||
allocated_other == 1 &&
|
||||
_hcd_data.channel[i].dev_addr == dev_addr &&
|
||||
_hcd_data.channel[i].ep_num == ep_num &&
|
||||
_hcd_data.channel[i].ep_type == ep_type) {
|
||||
_hcd_data.channel[i].allocated[dir] = 1;
|
||||
_hcd_data.channel[i].queued_len[dir] = 0;
|
||||
_hcd_data.channel[i].retry[dir] = 0;
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
_hcd_data.channel[i].out.allocated = 1;
|
||||
_hcd_data.channel[i].out.retry = 0;
|
||||
} else {
|
||||
_hcd_data.channel[i].in.allocated = 1;
|
||||
_hcd_data.channel[i].in.retry = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Find free channel
|
||||
for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
|
||||
if (_hcd_data.channel[i].allocated[0] == 0 && _hcd_data.channel[i].allocated[1] == 0) {
|
||||
if (_hcd_data.channel[i].out.allocated == 0 && _hcd_data.channel[i].in.allocated == 0) {
|
||||
_hcd_data.channel[i].dev_addr = dev_addr;
|
||||
_hcd_data.channel[i].ep_num = ep_num;
|
||||
_hcd_data.channel[i].ep_type = ep_type;
|
||||
_hcd_data.channel[i].allocated[dir] = 1;
|
||||
_hcd_data.channel[i].queued_len[dir] = 0;
|
||||
_hcd_data.channel[i].retry[dir] = 0;
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
_hcd_data.channel[i].out.allocated = 1;
|
||||
_hcd_data.channel[i].out.retry = 0;
|
||||
} else {
|
||||
_hcd_data.channel[i].in.allocated = 1;
|
||||
_hcd_data.channel[i].in.retry = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -733,16 +793,19 @@ static bool edpt_xfer_kickoff(uint8_t ep_id) {
|
||||
TU_ASSERT(ch_id != TUSB_INDEX_INVALID_8); // all channel are in used
|
||||
|
||||
tusb_dir_t const dir = tu_edpt_dir(edpt->ep_addr);
|
||||
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
channel->edpt[dir] = edpt;
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
channel->out.edpt = edpt;
|
||||
} else {
|
||||
channel->in.edpt = edpt;
|
||||
}
|
||||
|
||||
return channel_xfer_start(ch_id, dir);
|
||||
}
|
||||
|
||||
static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir) {
|
||||
hcd_channel_t* channel = &_hcd_data.channel[ch_id];
|
||||
hcd_endpoint_t* edpt = channel->edpt[dir];
|
||||
hcd_endpoint_t* edpt = (dir == TUSB_DIR_OUT) ? channel->out.edpt : channel->in.edpt;
|
||||
|
||||
uint32_t ch_reg = ch_read(ch_id) & ~USB_EPREG_MASK;
|
||||
ch_reg |= tu_edpt_number(edpt->ep_addr) | edpt->dev_addr << USB_CHEP_DEVADDR_Pos |
|
||||
@ -771,28 +834,28 @@ static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir) {
|
||||
btable_set_addr(ch_id, dir == TUSB_DIR_OUT ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
|
||||
|
||||
if (dir == TUSB_DIR_OUT) {
|
||||
uint16_t const len = tu_min16(edpt->buflen - channel->queued_len[TUSB_DIR_OUT], edpt->max_packet_size);
|
||||
uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size);
|
||||
|
||||
fsdev_write_packet_memory(pma_addr, &(edpt->buffer[channel->queued_len[TUSB_DIR_OUT]]), len);
|
||||
fsdev_write_packet_memory(pma_addr, &(edpt->buffer[edpt->queued_len]), len);
|
||||
btable_set_count(ch_id, BTABLE_BUF_TX, len);
|
||||
|
||||
channel->queued_len[TUSB_DIR_OUT] += len;
|
||||
edpt->queued_len += len;
|
||||
} else {
|
||||
btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, edpt->max_packet_size);
|
||||
}
|
||||
|
||||
if (edpt->low_speed == 1) {
|
||||
if (edpt->ls_pre == 1) {
|
||||
ch_reg |= USB_CHEP_LSEP;
|
||||
} else {
|
||||
ch_reg &= ~USB_CHEP_LSEP;
|
||||
}
|
||||
|
||||
// Setup DATA/STATUS phase start with DATA1
|
||||
if (tu_edpt_number(edpt->ep_addr) == 0) {
|
||||
edpt->pid = 1;
|
||||
}
|
||||
|
||||
if (edpt->next_setup) {
|
||||
// Setup packet uses IN token
|
||||
edpt->next_setup = false;
|
||||
ch_reg |= USB_EP_SETUP;
|
||||
edpt->pid = 0;
|
||||
|
||||
Reference in New Issue
Block a user