📄 pehci.c
字号:
/*NEW, now need to get the memory for this transfer*/
length = qtd->length;
mem_addr = &qtd->mem_addr;
phci_hcd_mem_alloc(length,mem_addr,0);
if(length && (
(mem_addr->phy_addr == 0 )
|| (mem_addr->virt_addr == 0))){
err("Never Error: Cannot allocate memory for the current td,length %d\n",length);
return;
}
pehci_check("newqtd being scheduled, device: %d,map: %x\n", urb->dev->devnum,td_ptd_map->ptd_bitmap);
memset(qha, 0, sizeof(isp1761_qha));
/*convert qtd to qha*/
phci_hcd_qha_from_qtd(hcd,qtd,qtd->urb, (void *)qha,
td_ptd_map->ptd_ram_data_addr,
qh
/*td_ptd_map->datatoggle*/);
if(qh->type == TD_PTD_BUFF_TYPE_INTL){
phci_hcd_qhint_schedule(hcd, qh,qtd,(isp1761_qhint *)qha,qtd->urb);
}
length = PTD_XFERRED_LENGTH(qha->td_info1 >> 3);
if(length > HC_ATL_PL_SIZE){
err("Never Error: Bogus length,length %d(max %d)\n",qtd->length, HC_ATL_PL_SIZE);
}
/*write qha into the header of the host controller*/
isp1761_mem_write(hcd->dev, td_ptd_map->ptd_header_addr,0,(u32 *)(qha),PHCI_QHA_LENGTH,0);
/*if this is SETUP/OUT token , then need to write into the buffer*/
/*length should be valid*/
if(qtd->length && ( length <= HC_ATL_PL_SIZE))
switch(PTD_PID(qha->td_info2)){
case OUT_PID:
case SETUP_PID:
#ifdef LINUX_269
isp1761_mem_write(hcd->dev, (u32)mem_addr->phy_addr, 0,(le32_to_cpu(qtd->hw_buf[0])),length,0);
#else
isp1761_mem_write(hcd->dev, (u32)mem_addr->phy_addr, 0,(void*)(le32_to_cpu(qtd->hw_buf[0])),length,0);
#endif
break;
}
/*qtd is scheduled*/
qtd->state &= ~QTD_STATE_NEW;
qtd->state |= QTD_STATE_SCHEDULED;
pehci_entry("-- %s: Exit\n",__FUNCTION__);
return;
}
static void
pehci_hcd_urb_complete(phci_hcd *hcd,struct ehci_qh *qh, struct urb *urb,
td_ptd_map_t *td_ptd_map,struct pt_regs *regs)
{
static u32 remove = 0;
urb_priv_t *urb_priv = (urb_priv_t *)urb->hcpriv;
pehci_entry("++ %s: Entered\n",__FUNCTION__);
pehci_check("complete the td , length: %d\n", td_ptd_map->qtd->length);
urb_priv->timeout = 0;
if((td_ptd_map->state == TD_PTD_REMOVE) ||
(urb_priv->state == DELETE_URB) ||
!HCD_IS_RUNNING(hcd->state))
remove = 1;
qh->qh_state = QH_STATE_COMPLETING;
/*remove the done tds*/
spin_lock(&hcd_data_lock);
phci_hcd_urb_free_priv(hcd, urb_priv,qh);
spin_unlock(&hcd_data_lock);
urb_priv->timeout = 0;
kfree(urb_priv);
urb->hcpriv = 0;
/*if normal completion???*/
if(urb->status == -EINPROGRESS)
urb->status = 0;
spin_unlock(&hcd->lock);
#ifdef LINUX_269
usb_hcd_giveback_urb (&hcd->usb_hcd, urb, regs);
#else
usb_hcd_giveback_urb (&hcd->usb_hcd, urb);
#endif
spin_lock(&hcd->lock);
/*lets handle to the remove case*/
if(remove){
remove = 0;
if(list_empty(&qh->qtd_list)){
pehci_check("no transfers anymore, free the endpoint\n");
phci_hcd_release_td_ptd_index(qh);
}
}
pehci_entry("-- %s: Exit\n",__FUNCTION__);
}
/*update the error status of the td*/
static void
pehci_hcd_update_error_status(u32 ptdstatus,struct urb *urb)
{
/*if ptd status is halted*/
if(ptdstatus & PTD_STATUS_HALTED){
if(ptdstatus & PTD_XACT_ERROR){
/*transaction error results due to retry count goes to zero*/
if(PTD_RETRY(ptdstatus)){
/*halt the endpoint*/
pehci_check("transaction error , retries %d\n", PTD_RETRY(ptdstatus));
urb->status = -EPIPE;}
else {
pehci_check("transaction error , retries %d\n", PTD_RETRY(ptdstatus));
/*protocol error*/
urb->status = -EPROTO;}
}
else if(ptdstatus & PTD_BABBLE){
pehci_check("babble error, qha %x\n", ptdstatus);
/*babble error*/
urb->status = -EOVERFLOW;}
else if(PTD_RETRY(ptdstatus)){
pehci_check("endpoint halted with retrie remaining %d\n",PTD_RETRY(ptdstatus));
urb->status = -EPIPE;}
else { /*unknown error, i will report it as halted, as i will never see xact error bit set*/
pehci_check("protocol error, qha %x\n", ptdstatus);
urb->status = -EPIPE;}
/*if halted need to recover*/
if (urb->status == -EPIPE) {
}
}
}
#ifdef CONFIG_ISO_SUPPORT /* New code for ISO support */
/*******************************************************************
* phcd_iso_handler - ISOCHRONOUS Transfer handler
*
* phci_hcd *hcd,
* Host controller driver structure which contains almost all data
* needed by the host controller driver to process data and interact
* with the host controller.
*
* struct pt_regs *regs
*
* API Description
* This is the ISOCHRONOUS Transfer handler, mainly responsible for:
* - Checking the periodic list if there are any ITDs for scheduling or
* removal.
* - For ITD scheduling, converting an ITD into a PTD, which is the data
* structure that the host contrtoller can understand and process.
* - For ITD completion, checking the transfer status and performing the
* required actions depending on status.
* - Freeing up memory used by an ITDs once it is not needed anymore.
************************************************************************/
void phcd_iso_handler(phci_hcd *hcd, struct pt_regs *regs)
{
struct _isp1761_isoptd *iso_ptd;
struct isp1761_mem_addr *mem_addr;
struct ehci_itd *itd, *current_itd;
td_ptd_map_t *td_ptd_map;
td_ptd_map_buff_t *ptd_map_buff;
struct list_head *itd_sched, *itd_remove, *position, *lst_temp;
struct urb *urb;
unsigned long buff_stat, skip_map;
unsigned long last_map;
unsigned long frame_num, rmv_frm_num, sched_frm_num;
unsigned long uframe_cnt, usof_stat, length;
unsigned char schedule, remove, last_td;
/* Local variable initialization */
buff_stat = 0;
skip_map = 0;
frame_num = 0;
schedule = FALSE;
remove = FALSE;
#ifdef LINUX_269
iso_ptd = hcd->isotd;
#else
iso_ptd = &hcd->isotd;
#endif
last_map = 0;
/* Check if there are any ITDs scheduled for processing */
if( hcd->periodic_sched == 0 )
{
return;
}
ptd_map_buff = &(td_ptd_map_buff[TD_PTD_BUFF_TYPE_ISTL]);
/* Read buffer status register to check later if the ISO buffer is filled or not */
buff_stat = isp1761_reg_read32(hcd->dev, hcd->regs.buffer_status, buff_stat);
/* Read the contents of the ISO skipmap register */
skip_map = isp1761_reg_read32(hcd->dev, hcd->regs.isotdskipmap, skip_map);
/* Read the contents of the ISO lastmap register */
last_map = isp1761_reg_read32(hcd->dev, hcd->regs.isotdlastmap, last_map);
/* Get also the frame number, which we will use as index into the periodic frame list */
frame_num = isp1761_reg_read32(hcd->dev, hcd->regs.frameindex, frame_num);
/* Bits 0 to 2 are don't care, only bits 3 to 7 are used as index */
frame_num &= 0xff;
frame_num >>= 3;
/* Get a copy of the current frame number to adjust it depending on what we will be processing */
rmv_frm_num = frame_num;
sched_frm_num = frame_num;
/* For ITD removal, work on the ITD linked to the frame prior to the current frame */
if(rmv_frm_num == 0)
rmv_frm_num = 31;
else
rmv_frm_num = rmv_frm_num - 1;
rmv_frm_num %= PTD_PERIODIC_SIZE;
/* Process ITDs linked to this frame, checking for completed ITDs */
itd_remove = &hcd->periodic_list[rmv_frm_num].itd_head;
iso_dbg(ISO_DBG_DATA, "[phcd_iso_handler]: Removal Frame number: %d\n", (int) rmv_frm_num);
/* For ITD scheduling, work on the ITD linked to the frame after the current frame */
if(sched_frm_num == 31)
sched_frm_num = 0;
else
sched_frm_num = sched_frm_num + 1;
sched_frm_num %= PTD_PERIODIC_SIZE;
/* Process ITDs linked to this frame, checking if there are any that needs to be scheduled */
itd_sched = &hcd->periodic_list[sched_frm_num].itd_head;
iso_dbg(ISO_DBG_DATA, "[phcd_iso_handler]: Schedule Frame number: %d\n", (int) sched_frm_num);
/* Check if the ITD lists are empty */
if( !list_empty(itd_sched))
{
iso_dbg(ISO_DBG_INFO, "[phcd_iso_handler]: ISO schedule list not empty\n");
schedule = TRUE;
}
else
{
iso_dbg(ISO_DBG_INFO, "[phcd_iso_handler]: ISO schedule list empty\n");
}
if( !list_empty(itd_remove))
{
iso_dbg(ISO_DBG_INFO, "[phcd_iso_handler]: ISO remove list not empty\n");
remove = TRUE;
}
else
{
iso_dbg(ISO_DBG_INFO, "[phcd_iso_handler]: ISO remove list empty\n");
}
if( schedule == TRUE )
{
iso_dbg(ISO_DBG_INFO, "[phcd_iso_handler]: Something is scheduled\n");
/* There is an ITD to be scheduled, go over and check each ITD in this list */
list_for_each(position, itd_sched)
{
/* Get an ITD in the list for processing */
itd = list_entry(position, struct ehci_itd, itd_list);
iso_dbg(ISO_DBG_DATA, "[phcd_iso_handler]: ITD Index: %d\n", itd->itd_index);
/* Get the PTD allocated for this ITD. */
td_ptd_map = &ptd_map_buff->map_list[itd->itd_index];
memset(iso_ptd, 0, sizeof(struct _isp1761_isoptd));
/* Create a PTD from an ITD*/
phcd_iso_itd_to_ptd( hcd,
itd,
itd->urb,
(void *) iso_ptd
);
/* Indicate that this ITD's PTD have been filled up */
ptd_map_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
/*
* Place the newly initialized ISO PTD structure into the location allocated
* for this PTD in the ISO PTD memory region.
*/
isp1761_mem_write( hcd->dev,
td_ptd_map->ptd_header_addr,
0,
(__u32 *) iso_ptd,
PHCI_QHA_LENGTH,
0
);
/*
* Set this flag to avoid unlinking before schedule
* at particular frame number
*/
td_ptd_map->state = TD_PTD_IN_SCHEDULE;
/*
* If the length is not zero and the direction is OUT then copy the
* data to be transferred into the PAYLOAD memory area.
*/
if(itd->length)
{
switch(PTD_PID(iso_ptd->td_info2))
{
case OUT_PID:
/* Get the Payload memory allocated for this PTD */
mem_addr = &itd->mem_addr;
isp1761_mem_write( hcd->dev,
(unsigned long )mem_addr->phy_addr,
0,
(__u32 *) (le32_to_cpu(itd->hw_bufp[0])),
itd->length,
0
);
break;
} /* switch(PTD_PID(iso_ptd->td_info2)) */
} /* if(itd->length) */
/* If this is the last td, indicate to complete the URB */
if(itd->hw_next == EHCI_LIST_END)
td_ptd_map->lasttd = 1;
/*
* Clear the bit corresponding to this PTD in the skip map so that it will be
* processed on the next schedule traversal.
*/
skip_map &= ~td_ptd_map->ptd_bitmap;
iso_dbg(ISO_DBG_DATA, "[phcd_iso_handler]: Skip Map: 0x%08x\n", (unsigned int) skip_map);
isp1761_reg_write32(hcd->dev, hcd->regs.isotdskipmap, skip_map);
/*
* Update the last map register to indicate that the newly created PTD is the
* last PTD added only if it is larger than the previous bitmap.
*/
if(last_map < td_ptd_map->ptd_bitmap)
{
isp1761_reg_write32(hcd->dev, hcd->regs.isotdlastmap, td_ptd_map->ptd_bitmap);
iso_dbg(ISO_DBG_DATA, "[phcd_iso_handler]: Last Map: 0x%08x\n", td_ptd_map->ptd_bitmap);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -