separate fake ehci's run async & period list

refractor list_find_previous_item & list_remove_qhd to act on ehci_link_t* instead of ehci_qhd_t*
fully support 1ms, 2ms, 4ms, 8ms for period list (each list has a dummy queue head)
- change period list structure
limit the maximum polling interval to 256 ms
add max_loop static MAX number of iteration for list_find_previous_item
add test for close 256ms polling interrupt
This commit is contained in:
hathach 2013-04-21 15:09:54 +07:00
parent 357888a5e5
commit a493fab753
7 changed files with 153 additions and 117 deletions

View File

@ -53,55 +53,55 @@ static const struct {
// MIC2555 1YML = 0101111, 0YML = 0101101 // MIC2555 1YML = 0101111, 0YML = 0101101
#define MIC255_ADDR BIN8(0101111) #define MIC255_ADDR BIN8(0101111)
static uint8_t mic255_regs_read(uint8_t regs_addr) //static uint8_t mic255_regs_read(uint8_t regs_addr)
{ //{
uint8_t value; // uint8_t value;
//
// ASSERT( SUCCESS == I2C_MasterTransferData(
// LPC_I2C0,
// & (I2C_M_SETUP_Type)
// {
// .sl_addr7bit = MIC255_ADDR,
// .retransmissions_max = 3,
//
// .tx_data = &regs_addr,
// .tx_length = 1,
//
// .rx_data = &value,
// .rx_length = 1
// },
// I2C_TRANSFER_POLLING), 0xFF);
//
// return value;
//}
ASSERT( SUCCESS == I2C_MasterTransferData( //static bool mic255_regs_write(uint8_t regs_addr, uint8_t data)
LPC_I2C0, //{
& (I2C_M_SETUP_Type) // uint8_t xfer_data[2] = { regs_addr, data} ;
{ //
.sl_addr7bit = MIC255_ADDR, // ASSERT( SUCCESS == I2C_MasterTransferData(
.retransmissions_max = 3, // LPC_I2C0,
// & (I2C_M_SETUP_Type)
.tx_data = &regs_addr, // {
.tx_length = 1, // .sl_addr7bit = MIC255_ADDR,
// .retransmissions_max = 3,
.rx_data = &value, //
.rx_length = 1 // .tx_data = xfer_data,
}, // .tx_length = 2,
I2C_TRANSFER_POLLING), 0xFF); // },
// I2C_TRANSFER_POLLING), false);
return value; //
} // return true;
//}
static bool mic255_regs_write(uint8_t regs_addr, uint8_t data)
{
uint8_t xfer_data[2] = { regs_addr, data} ;
ASSERT( SUCCESS == I2C_MasterTransferData(
LPC_I2C0,
& (I2C_M_SETUP_Type)
{
.sl_addr7bit = MIC255_ADDR,
.retransmissions_max = 3,
.tx_data = xfer_data,
.tx_length = 2,
},
I2C_TRANSFER_POLLING), false);
return true;
}
static uint16_t mic255_get_vendorid(void) //static uint16_t mic255_get_vendorid(void)
{ //{
uint8_t vendor_low = mic255_regs_read(0); // uint8_t vendor_low = mic255_regs_read(0);
uint8_t vendor_high = mic255_regs_read(1); // uint8_t vendor_high = mic255_regs_read(1);
//
return (vendor_high << 8) | vendor_low; // return (vendor_high << 8) | vendor_low;
} //}
void board_init(void) void board_init(void)
{ {

View File

@ -107,7 +107,7 @@ void test_hcd_init_async_list(void)
TEST_ASSERT_EQUAL_HEX(async_head, regs->async_list_base); TEST_ASSERT_EQUAL_HEX(async_head, regs->async_list_base);
TEST_ASSERT_EQUAL_HEX(async_head, align32(async_head) ); TEST_ASSERT_EQUAL_HEX(async_head, align32( (uint32_t) async_head) );
TEST_ASSERT_EQUAL(EHCI_QUEUE_ELEMENT_QHD, async_head->next.type); TEST_ASSERT_EQUAL(EHCI_QUEUE_ELEMENT_QHD, async_head->next.type);
TEST_ASSERT_FALSE(async_head->next.terminate); TEST_ASSERT_FALSE(async_head->next.terminate);
@ -136,20 +136,21 @@ void test_hcd_init_period_list(void)
TEST_ASSERT_EQUAL_HEX( (uint32_t) framelist, regs->periodic_list_base); TEST_ASSERT_EQUAL_HEX( (uint32_t) framelist, regs->periodic_list_base);
check_qhd_endpoint_link( framelist+1, period_head_arr+1); check_qhd_endpoint_link( framelist+0, period_head_arr+1);
check_qhd_endpoint_link( framelist+3, period_head_arr+1); check_qhd_endpoint_link( framelist+2, period_head_arr+1);
check_qhd_endpoint_link( framelist+5, period_head_arr+1); check_qhd_endpoint_link( framelist+4, period_head_arr+1);
check_qhd_endpoint_link( framelist+7, period_head_arr+1); check_qhd_endpoint_link( framelist+6, period_head_arr+1);
check_qhd_endpoint_link( framelist+2, period_head_arr+2); check_qhd_endpoint_link( framelist+1, period_head_arr+2);
check_qhd_endpoint_link( framelist+6, period_head_arr+2); check_qhd_endpoint_link( framelist+5, period_head_arr+2);
check_qhd_endpoint_link( framelist, period_head_arr); check_qhd_endpoint_link( framelist+3, period_head_arr+3);
check_qhd_endpoint_link( framelist+4, period_head_arr); check_qhd_endpoint_link( framelist+7, period_head_arr);
check_qhd_endpoint_link( (ehci_link_t*) (period_head_arr+1), period_head_arr); check_qhd_endpoint_link( (ehci_link_t*) (period_head_arr+1), period_head_arr);
check_qhd_endpoint_link( (ehci_link_t*) (period_head_arr+2), period_head_arr); check_qhd_endpoint_link( (ehci_link_t*) (period_head_arr+2), period_head_arr);
check_qhd_endpoint_link( (ehci_link_t*) (period_head_arr+3), period_head_arr);
for(uint32_t i=0; i<3; i++) for(uint32_t i=0; i<4; i++)
{ {
TEST_ASSERT(period_head_arr[i].interrupt_smask); TEST_ASSERT(period_head_arr[i].interrupt_smask);
TEST_ASSERT(period_head_arr[i].qtd_overlay.halted); TEST_ASSERT(period_head_arr[i].qtd_overlay.halted);

View File

@ -263,7 +263,7 @@ void test_open_interrupt_hs_interval_7(void)
void test_open_interrupt_hs_interval_8(void) void test_open_interrupt_hs_interval_8(void)
{ {
tusb_descriptor_endpoint_t int_edp_interval = desc_ept_interrupt_out; tusb_descriptor_endpoint_t int_edp_interval = desc_ept_interrupt_out;
int_edp_interval.bInterval = 8; int_edp_interval.bInterval = 16;
//------------- Code Under TEST -------------// //------------- Code Under TEST -------------//
pipe_hdl = hcd_pipe_open(dev_addr, &int_edp_interval, TUSB_CLASS_HID); pipe_hdl = hcd_pipe_open(dev_addr, &int_edp_interval, TUSB_CLASS_HID);
@ -327,6 +327,23 @@ void test_interrupt_close(void)
TEST_ASSERT_EQUAL(EHCI_QUEUE_ELEMENT_QHD, p_int_qhd->next.type); TEST_ASSERT_EQUAL(EHCI_QUEUE_ELEMENT_QHD, p_int_qhd->next.type);
} }
void test_interrupt_256ms_close(void)
{
tusb_descriptor_endpoint_t int_edp_interval = desc_ept_interrupt_out;
int_edp_interval.bInterval = 9;
pipe_hdl = hcd_pipe_open(dev_addr, &int_edp_interval, TUSB_CLASS_HID);
p_int_qhd = qhd_get_from_pipe_handle(pipe_hdl);
//------------- Code Under TEST -------------//
hcd_pipe_close(pipe_hdl);
TEST_ASSERT(p_int_qhd->is_removing);
TEST_ASSERT( align32(get_period_head(hostid, 8)->address) != (uint32_t) p_int_qhd );
TEST_ASSERT_EQUAL_HEX( (uint32_t) get_period_head(hostid, 8), align32(p_int_qhd->next.address ) );
TEST_ASSERT_EQUAL(EHCI_QUEUE_ELEMENT_QHD, p_int_qhd->next.type);
}
uint8_t count_set_bits(uint8_t x) uint8_t count_set_bits(uint8_t x)
{ {
uint8_t result = 0; uint8_t result = 0;

View File

@ -88,12 +88,8 @@ void ehci_controller_control_xfer_proceed(uint8_t dev_addr, uint8_t p_data[])
hcd_isr( usbh_devices[dev_addr].core_id ); hcd_isr( usbh_devices[dev_addr].core_id );
} }
bool complete_all_qtd_in_list(ehci_qhd_t *head) void complete_qtd_in_qhd(ehci_qhd_t *p_qhd)
{ {
ehci_qhd_t *p_qhd = head;
do
{
if ( !p_qhd->qtd_overlay.halted ) if ( !p_qhd->qtd_overlay.halted )
{ {
while(!p_qhd->qtd_overlay.next.terminate) while(!p_qhd->qtd_overlay.next.terminate)
@ -103,27 +99,47 @@ bool complete_all_qtd_in_list(ehci_qhd_t *head)
p_qhd->qtd_overlay = *p_qtd; p_qhd->qtd_overlay = *p_qtd;
} }
} }
if (!p_qhd->next.terminate) }
bool complete_all_qtd_in_async(ehci_qhd_t *head)
{
ehci_qhd_t *p_qhd = head;
do
{ {
complete_qtd_in_qhd(p_qhd);
p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address); p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
}
else
{
break;
}
}while(p_qhd != head); // stop if loop around }while(p_qhd != head); // stop if loop around
return true; return true;
} }
bool complete_all_qtd_in_period(ehci_link_t *head)
{
while(!head->terminate)
{
uint32_t queue_type = head->type;
head = (ehci_link_t*) align32(head->address);
if ( queue_type == EHCI_QUEUE_ELEMENT_QHD)
{
complete_qtd_in_qhd( (ehci_qhd_t*) head );
}
}
return true;
}
void ehci_controller_run(uint8_t hostid) void ehci_controller_run(uint8_t hostid)
{ {
//------------- Async List -------------// //------------- Async List -------------//
ehci_registers_t* const regs = get_operational_register(hostid); ehci_registers_t* const regs = get_operational_register(hostid);
complete_all_qtd_in_list((ehci_qhd_t*) regs->async_list_base); complete_all_qtd_in_async((ehci_qhd_t*) regs->async_list_base);
//------------- Period List -------------// //------------- Period List -------------//
complete_all_qtd_in_list( get_period_head(hostid, 1) ); for(uint8_t i=1; i <= EHCI_FRAMELIST_SIZE; i *= 2)
{
complete_all_qtd_in_period( get_period_head(hostid, i) );
}
regs->usb_sts = EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC; regs->usb_sts = EHCI_INT_MASK_NXP_ASYNC | EHCI_INT_MASK_NXP_PERIODIC;
hcd_isr(hostid); hcd_isr(hostid);

View File

@ -66,7 +66,7 @@ void ehci_controller_device_unplug(uint8_t hostid);
ehci_registers_t* get_operational_register(uint8_t hostid); ehci_registers_t* get_operational_register(uint8_t hostid);
ehci_link_t* get_period_frame_list(uint8_t hostid); ehci_link_t* get_period_frame_list(uint8_t hostid);
ehci_qhd_t* get_async_head(uint8_t hostid); ehci_qhd_t* get_async_head(uint8_t hostid);
ehci_qhd_t* get_period_head(uint8_t hostid, uint8_t interval_ms); ehci_link_t* get_period_head(uint8_t hostid, uint8_t interval_ms);
ehci_qhd_t* get_control_qhd(uint8_t dev_addr); ehci_qhd_t* get_control_qhd(uint8_t dev_addr);
ehci_qtd_t* get_control_qtds(uint8_t dev_addr); ehci_qtd_t* get_control_qtds(uint8_t dev_addr);
ehci_qhd_t* qhd_get_from_pipe_handle(pipe_handle_t pipe_hdl); ehci_qhd_t* qhd_get_from_pipe_handle(pipe_handle_t pipe_hdl);

View File

@ -98,8 +98,8 @@ static inline void qtd_remove_1st_from_qhd(ehci_qhd_t *p_qhd) ATTR_ALW
static void qtd_init(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes); static void qtd_init(ehci_qtd_t* p_qtd, uint32_t data_ptr, uint16_t total_bytes);
static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) ATTR_ALWAYS_INLINE; static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t new_type) ATTR_ALWAYS_INLINE;
static ehci_qhd_t* list_find_previous_qhd(ehci_qhd_t* p_head, ehci_qhd_t* p_qhd); static ehci_link_t* list_find_previous_item(ehci_link_t* p_head, ehci_link_t* p_current);
static tusb_error_t list_remove_qhd(ehci_qhd_t* p_head, ehci_qhd_t* p_qhd_remove); static tusb_error_t list_remove_qhd(ehci_link_t* p_head, ehci_link_t* p_remove);
static tusb_error_t hcd_controller_init(uint8_t hostid) ATTR_WARN_UNUSED_RESULT; static tusb_error_t hcd_controller_init(uint8_t hostid) ATTR_WARN_UNUSED_RESULT;
@ -181,7 +181,7 @@ static tusb_error_t hcd_controller_init(uint8_t hostid)
//------------- Periodic List -------------// //------------- Periodic List -------------//
// Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only // Build the polling interval tree with 1 ms, 2 ms, 4 ms and 8 ms (framesize) only
for(uint32_t i=0; i<3; i++) for(uint32_t i=0; i<4; i++)
{ {
ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ][i].interrupt_smask = 1; // queue head in period list must have smask non-zero ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ][i].interrupt_smask = 1; // queue head in period list must have smask non-zero
ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ][i].qtd_overlay.halted = 1; // dummy node, always inactive ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ][i].qtd_overlay.halted = 1; // dummy node, always inactive
@ -189,12 +189,10 @@ static tusb_error_t hcd_controller_init(uint8_t hostid)
ehci_link_t * const framelist = get_period_frame_list(hostid); ehci_link_t * const framelist = get_period_frame_list(hostid);
ehci_link_t * const period_1ms = get_period_head(hostid, 1); ehci_link_t * const period_1ms = get_period_head(hostid, 1);
ehci_link_t * const period_2ms = get_period_head(hostid, 2); // all links --> period_head_arr[0] (1ms)
ehci_link_t * const period_4ms = get_period_head(hostid, 4); // 0, 2, 4, 6 etc --> period_head_arr[1] (2ms)
// 1, 5 --> period_head_arr[2] (4ms)
// 1, 3, 5, 7 etc --> period_head_arr[2] (4ms) // 3 --> period_head_arr[3] (8ms)
// 2, 6 --> period_head_arr[2]
// 0, 4, + period_head_arr[1] + period_head_arr[2] --> period_head_arr[0]
// TODO EHCI_FRAMELIST_SIZE with other size than 8 // TODO EHCI_FRAMELIST_SIZE with other size than 8
for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i++) for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i++)
@ -203,16 +201,18 @@ static tusb_error_t hcd_controller_init(uint8_t hostid)
framelist[i].type = EHCI_QUEUE_ELEMENT_QHD; framelist[i].type = EHCI_QUEUE_ELEMENT_QHD;
} }
for(uint32_t i=1; i<EHCI_FRAMELIST_SIZE; i+=2) for(uint32_t i=0; i<EHCI_FRAMELIST_SIZE; i+=2)
{ {
list_insert(framelist + i, period_2ms, EHCI_QUEUE_ELEMENT_QHD); list_insert(framelist + i, get_period_head(hostid, 2), EHCI_QUEUE_ELEMENT_QHD);
} }
for(uint32_t i=2; i<EHCI_FRAMELIST_SIZE; i+=4) for(uint32_t i=1; i<EHCI_FRAMELIST_SIZE; i+=4)
{ {
list_insert(framelist + i, period_4ms, EHCI_QUEUE_ELEMENT_QHD); list_insert(framelist + i, get_period_head(hostid, 4), EHCI_QUEUE_ELEMENT_QHD);
} }
list_insert(framelist+3, get_period_head(hostid, 8), EHCI_QUEUE_ELEMENT_QHD);
period_1ms->terminate = 1; period_1ms->terminate = 1;
regs->periodic_list_base = (uint32_t) framelist; regs->periodic_list_base = (uint32_t) framelist;
@ -336,7 +336,8 @@ tusb_error_t hcd_pipe_control_close(uint8_t dev_addr)
if (dev_addr != 0) if (dev_addr != 0)
{ {
ASSERT_STATUS( list_remove_qhd(get_async_head( usbh_devices[dev_addr].core_id ), p_qhd) ); ASSERT_STATUS( list_remove_qhd( (ehci_link_t*) get_async_head( usbh_devices[dev_addr].core_id ),
(ehci_link_t*) p_qhd) );
} }
return TUSB_ERROR_NONE; return TUSB_ERROR_NONE;
@ -419,11 +420,13 @@ tusb_error_t hcd_pipe_close(pipe_handle_t pipe_hdl)
if ( pipe_hdl.xfer_type == TUSB_XFER_BULK ) if ( pipe_hdl.xfer_type == TUSB_XFER_BULK )
{ {
ASSERT_STATUS( list_remove_qhd( ASSERT_STATUS( list_remove_qhd(
get_async_head( usbh_devices[pipe_hdl.dev_addr].core_id ), p_qhd) ); (ehci_link_t*) get_async_head( usbh_devices[pipe_hdl.dev_addr].core_id ),
(ehci_link_t*) p_qhd) );
}else }else
{ {
ASSERT_STATUS( list_remove_qhd( ASSERT_STATUS( list_remove_qhd(
get_period_head( usbh_devices[pipe_hdl.dev_addr].core_id, 1 ), p_qhd) ); get_period_head( usbh_devices[pipe_hdl.dev_addr].core_id, p_qhd->interval_ms ),
(ehci_link_t*) p_qhd) );
} }
return TUSB_ERROR_NONE; return TUSB_ERROR_NONE;
@ -517,7 +520,7 @@ void async_list_process_isr(ehci_qhd_t * const async_head)
} }
p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address); p_qhd = (ehci_qhd_t*) align32(p_qhd->next.address);
max_loop++; max_loop++;
}while(p_qhd != async_head && max_loop <= EHCI_MAX_QHD); // async list traversal, stop if loop around }while(p_qhd != async_head && max_loop < EHCI_MAX_QHD); // async list traversal, stop if loop around
// TODO abstract max loop guard for async // TODO abstract max loop guard for async
} }
@ -697,15 +700,10 @@ STATIC_ INLINE_ ehci_qhd_t* get_async_head(uint8_t hostid)
STATIC_ INLINE_ ehci_link_t* get_period_head(uint8_t hostid, uint8_t interval_ms) STATIC_ INLINE_ ehci_link_t* get_period_head(uint8_t hostid, uint8_t interval_ms)
{ {
if (interval_ms < 8)
{
return (ehci_link_t*) (ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ] + return (ehci_link_t*) (ehci_data.period_head_arr[ hostid_to_data_idx(hostid) ] +
(interval_ms < 2 ? 0 : (interval_ms < 2 ? 0 :
interval_ms < 4 ? 1 : 2)); interval_ms < 4 ? 1 :
}else interval_ms < EHCI_FRAMELIST_SIZE ? 2 : 3));
{
return get_period_frame_list(hostid);
}
} }
STATIC_ INLINE_ ehci_qhd_t* get_control_qhd(uint8_t dev_addr) STATIC_ INLINE_ ehci_qhd_t* get_control_qhd(uint8_t dev_addr)
@ -811,7 +809,7 @@ static void qhd_init(ehci_qhd_t *p_qhd, uint8_t dev_addr, uint16_t max_packet_si
(interval == 2) ? BIN8(10101010) : BIN8(01000100); (interval == 2) ? BIN8(10101010) : BIN8(01000100);
}else }else
{ {
p_qhd->interval_ms = ( 1 << (interval-4) ); p_qhd->interval_ms = (uint8_t) min16_of( 1 << (interval-4), 255 );
p_qhd->interrupt_smask = BIT_(interval % 8); p_qhd->interrupt_smask = BIT_(interval % 8);
} }
}else }else
@ -872,27 +870,31 @@ static inline void list_insert(ehci_link_t *current, ehci_link_t *new, uint8_t n
current->address = ((uint32_t) new) | (new_type << 1); current->address = ((uint32_t) new) | (new_type << 1);
} }
static ehci_qhd_t* list_find_previous_qhd(ehci_qhd_t* p_head, ehci_qhd_t* p_qhd) static ehci_link_t* list_find_previous_item(ehci_link_t* p_head, ehci_link_t* p_current)
{ {
ehci_qhd_t *p_prev_qhd = p_head; ehci_link_t *p_prev = p_head;
while( (align32(p_prev_qhd->next.address) != (uint32_t) p_head) && (align32(p_prev_qhd->next.address) != (uint32_t) p_qhd) ) uint32_t max_loop = 0;
while( (align32(p_prev->address) != (uint32_t) p_head) &&
(align32(p_prev->address) != (uint32_t) p_current) &&
!p_prev->terminate &&
max_loop < EHCI_MAX_QHD)
{ {
p_prev_qhd = (ehci_qhd_t*) align32(p_prev_qhd->next.address); p_prev = (ehci_link_t*) align32(p_prev->address);
max_loop++;
} }
return align32(p_prev_qhd->next.address) != (uint32_t) p_head ? p_prev_qhd : NULL; return (align32(p_prev->address) != (uint32_t) p_head) ? p_prev : NULL;
} }
static tusb_error_t list_remove_qhd(ehci_qhd_t* p_head, ehci_qhd_t* p_qhd_remove) static tusb_error_t list_remove_qhd(ehci_link_t* p_head, ehci_link_t* p_remove)
{ {
ehci_qhd_t *p_prev_qhd = list_find_previous_qhd(p_head, p_qhd_remove); ehci_link_t *p_prev = list_find_previous_item(p_head, p_remove);
ASSERT_PTR(p_prev_qhd, TUSB_ERROR_INVALID_PARA); ASSERT_PTR(p_prev, TUSB_ERROR_INVALID_PARA);
p_prev_qhd->next.address = p_qhd_remove->next.address; p_prev->address = p_remove->address;
// EHCI 4.8.2 link the removing queue head to async/period head (which always reachable by Host Controller) // EHCI 4.8.2 link the removing queue head to async/period head (which always reachable by Host Controller)
p_qhd_remove->next.address = (uint32_t) p_head; p_remove->address = ((uint32_t) p_head) | (EHCI_QUEUE_ELEMENT_QHD << 1);
p_qhd_remove->next.type = EHCI_QUEUE_ELEMENT_QHD;
return TUSB_ERROR_NONE; return TUSB_ERROR_NONE;
} }

View File

@ -449,8 +449,8 @@ typedef struct {
#if EHCI_PERIODIC_LIST #if EHCI_PERIODIC_LIST
// for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist) // for NXP ECHI, only implement 1 ms & 2 ms & 4 ms, 8 ms (framelist)
// [0] : 1ms, [1] : 2ms, [2] : 4ms // [0] : 1ms, [1] : 2ms, [2] : 4ms, [3] : 8 ms
ehci_qhd_t period_head_arr[CONTROLLER_HOST_NUMBER][3]; ehci_qhd_t period_head_arr[CONTROLLER_HOST_NUMBER][4];
#endif #endif
//------------- Data for Address 0 (use async head as its queue head) -------------// //------------- Data for Address 0 (use async head as its queue head) -------------//