📄 qtdptd.c
字号:
td_ptd_map->ptd_header_addr, td_ptd_map->ptd_data_addr,
td_ptd_map->ptd_ram_data_addr);
pehci_entry("-- %s: Exit",__FUNCTION__);
return payloadlocation;
}
/*--------------------------------------------------------------*
* calculate the header location for the current
* endpoint, if found returns a valid index
* else invalid
-----------------------------------------------------------*/
static void
phci_hcd_get_qtd_ptd_index(struct ehci_qh *qh,
struct ehci_qtd *qtd,
struct ehci_itd *itd)
{
u8 buff_type = td_ptd_pipe_x_buff_type[qh->type];
u8 qtd_ptd_index;/*, index;*/
/*this is the location of the ptd's skip map/done map, also
calculating the td header, payload, data start address
location*/
u8 bitmap = 0x1;
u8 max_ptds;
td_ptd_map_buff_t *ptd_map_buff = &(td_ptd_map_buff[buff_type]);
pehci_entry("++ %s, Entered, buffer type %d\n",__FUNCTION__, buff_type);
/* ATL PTDs can wait */
max_ptds = (buff_type == TD_PTD_BUFF_TYPE_ATL)
? TD_PTD_MAX_BUFF_TDS : ptd_map_buff->max_ptds;
for(qtd_ptd_index = 0; qtd_ptd_index < max_ptds; qtd_ptd_index++) { /* Find the first free slot */
if(ptd_map_buff->map_list[qtd_ptd_index].state == TD_PTD_NEW) {
/* Found a free slot */
if( qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX ) {
qh->qtd_ptd_index = qtd_ptd_index;
}
ptd_map_buff->map_list[qtd_ptd_index].datatoggle = 0;
/*put the ptd_index into operational state*/
ptd_map_buff->map_list[qtd_ptd_index].state = TD_PTD_ACTIVE;
ptd_map_buff->map_list[qtd_ptd_index].qtd = qtd;
/* No td transfer is in progress */
ptd_map_buff->map_list[qtd_ptd_index].itd = itd;
/*initialize endpoint(queuehead)*/
ptd_map_buff->map_list[qtd_ptd_index].qh = qh;
ptd_map_buff->map_list[qtd_ptd_index].ptd_bitmap =
bitmap << qtd_ptd_index;
phci_hcd_fill_ptd_addresses(&ptd_map_buff->map_list[qtd_ptd_index],qh->qtd_ptd_index,buff_type);
ptd_map_buff->map_list[qtd_ptd_index].lasttd = 0;
ptd_map_buff->total_ptds ++; /* update # of total td's */
/*make the queuehead map, to process in the phci_schedule_ptds*/
ptd_map_buff->active_ptd_bitmap |= (bitmap << qtd_ptd_index);
break;
}
}
pehci_entry("-- %s, Exit\n", __FUNCTION__);
return;
} /* phci_get_td_ptd_index */
/*
* calculate the header location for the endpoint and
* all tds on this endpoint will use the same
* header location for all transfers on this endpoint.
* also puts the endpoint into the linked state
* */
static void
phci_hcd_qh_link_async (phci_hcd *hcd, struct ehci_qh *qh, int *status)
{
struct ehci_qtd *qtd = 0;
struct list_head *qtd_list = &qh->qtd_list;
td_ptd_map_buff_t *ptd_map_buff;
td_ptd_map_t *td_ptd_map;
/* take the first td, in case we are not able to schedule the new td
and this is going for remove
*/
qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
pehci_entry("++ %s: Entered\n", __FUNCTION__);
/* Assign a td-ptd index for this ed so that we can put ptd's in the HC buffers */
qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
phci_hcd_get_qtd_ptd_index(qh, qtd, NULL); /* Get a td-ptd index */
if(qh->qtd_ptd_index == TD_PTD_INV_PTD_INDEX){
err("can not find the location in our buffer\n");
*status = -ENOSPC;
return;
}
#ifdef MSEC_INT_BASED
/*first transfers in sof interrupt goes into pending*/
ptd_map_buff = &(td_ptd_map_buff[qh->type]);
td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
#endif
/* open the halt so that it acessed */
qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT);
qh->qh_state = QH_STATE_LINKED;
qh->qh_state |= QH_STATE_TAKE_NEXT;
pehci_entry("-- %s: Exit , qh %p\n", __FUNCTION__, qh);
}
/*-------------------------------------------------------------------------*/
/*
* mainly used for setting up current td on current
* endpoint(queuehead), endpoint may be new or
* halted one
* */
static inline void
phci_hcd_qh_update (phci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
{
/*make this current td*/
qh->hw_current = QTD_NEXT (qtd->qtd_dma);
qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
qh->hw_alt_next = EHCI_LIST_END;
/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
wmb ();
qh->hw_token &= __constant_cpu_to_le32 (QTD_TOGGLE | QTD_STS_PING);
}
/*
* used for ATL, INT transfers
* function creates new endpoint,
* calculates bandwidth for interrupt transfers,
* and initialize the qh based on endpoint type/speed
* */
struct ehci_qh *
phci_hcd_make_qh (
phci_hcd *hcd,
struct urb *urb,
struct list_head *qtd_list,
int *status)
{
struct ehci_qh *qh = 0;
u32 info1 = 0, info2 = 0;
int is_input, type;
int maxp = 0;
int mult = 0;
int bustime = 0;
struct ehci_qtd *qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
pehci_entry("++ %s: Entered\n",__FUNCTION__);
qh = phci_hcd_qh_alloc (hcd);
if (!qh){
*status = -ENOMEM;
return 0;
}
/*
* init endpoint/device data for this QH
*/
info1 |= usb_pipeendpoint(urb->pipe) << 8;
info1 |= usb_pipedevice(urb->pipe) << 0;
is_input = usb_pipein(urb->pipe);
type = usb_pipetype (urb->pipe);
maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input);
mult = 1 + ((maxp >> 11) & 0x3);
/*set this queueheads index to invalid*/
qh->qtd_ptd_index = TD_PTD_INV_PTD_INDEX;
switch(type){
case PIPE_CONTROL:
case PIPE_BULK:
qh->type = TD_PTD_BUFF_TYPE_ATL;
break;
case PIPE_INTERRUPT:
qh->type = TD_PTD_BUFF_TYPE_INTL;
break;
case PIPE_ISOCHRONOUS:
qh->type = TD_PTD_BUFF_TYPE_ISTL;
break;
}
if (type == PIPE_INTERRUPT) {
/*for this interrupt transfer check how much bustime in usecs required*/
bustime = usb_check_bandwidth(urb->dev, urb);
if(bustime < 0){
*status = -ENOSPC;
goto done;
}
usb_claim_bandwidth(urb->dev,urb,bustime,usb_pipeisoc(urb->pipe));
qh->usecs = bustime;
qh->start = NO_FRAME;
if (urb->dev->speed == USB_SPEED_HIGH) {
qh->c_usecs = 0;
qh->gap_uf = 0;
/*after how many uframes this interrupt is to be executed*/
qh->period = urb->interval >> 3;
if (qh->period < 1) {
printk("intr period %d uframes,\n",
urb->interval);
}
/*restore the original urb->interval in qh->period*/
qh->period = urb->interval;
} else {
/* gap is f(FS/LS transfer times) */
qh->gap_uf = 1 + 7;/*usb_calc_bus_time (urb->dev->speed,
is_input, 0, maxp) / (125 * 1000);*/
if (is_input) { /* SPLIT, gap, CSPLIT+DATA */
qh->c_usecs = qh->usecs + 1;/*HS_USECS (0);*/
qh->usecs = 10;/*HS_USECS (1);*/
} else { /* SPLIT+DATA, gap, CSPLIT */
qh->usecs += 10;/*HS_USECS (1);*/
qh->c_usecs = 1;/*HS_USECS (0);*/
}
/*take the period ss/cs scheduling will be
handled by submit urb
*/
qh->period = urb->interval;
}
}
/* using TT? */
switch (urb->dev->speed) {
case USB_SPEED_LOW:
info1 |= (1 << 12); /* EPS "low" */
/* FALL THROUGH */
case USB_SPEED_FULL:
/* EPS 0 means "full" */
if (type != PIPE_INTERRUPT)
info1 |= (EHCI_TUNE_RL_TT << 28);
if (type == PIPE_CONTROL) {
info1 |= (1 << 27); /* for TT */
info1 |= 1 << 14; /* toggle from qtd */
}
info1 |= maxp << 16;
info2 |= (EHCI_TUNE_MULT_TT << 30);
info2 |= urb->dev->ttport << 23;
info2 |= urb->dev->tt->hub->devnum << 16;
break;
case USB_SPEED_HIGH: /* no TT involved */
info1 |= (2 << 12); /* EPS "high" */
if (type == PIPE_CONTROL) {
info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else if (type == PIPE_BULK) {
info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 512 << 16; /* usb2 fixed maxpacket */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else { /* PIPE_INTERRUPT */
info1 |= (maxp & 0x7ff)/*max_packet (maxp)*/ << 16;
info2 |= mult/*hb_mult (maxp)*/ << 30;
}
break;
default:
pehci_print("bogus dev %p speed %d", urb->dev, urb->dev->speed);
done:
qha_free(qha_cache, qh);
return 0;
}/*end of switch*/
/* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */
/* init as halted, toggle clear, advance to dummy */
qh->qh_state = QH_STATE_IDLE;
qh->hw_info1 = cpu_to_le32 (info1);
qh->hw_info2 = cpu_to_le32 (info2);
/*link the tds here*/
list_splice(qtd_list, &qh->qtd_list);
phci_hcd_qh_update (hcd, qh, qtd);
qh->hw_token = cpu_to_le32 (QTD_STS_HALT);
if(!usb_pipecontrol(urb->pipe))
usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
pehci_entry("-- %s: Exit, qh %p\n",__FUNCTION__,qh);
return qh;
}
/*-----------------------------------------------------------*/
/*
* Hardware maintains data toggle (like OHCI) ... here we (re)initialize
* the hardware data toggle in the QH, and set the pseudo-toggle in udev
* so we can see if usb_clear_halt() was called. NOP for control, since
* we set up qh->hw_info1 to always use the QTD toggle bits.
*/
static inline void
phci_hcd_clear_toggle (struct usb_device *udev, int ep, int is_out, struct ehci_qh *qh)
{
pehci_print("clear toggle, dev %d ep 0x%x-%s\n",
udev->devnum, ep, is_out ? "out" : "in");
qh->hw_token &= ~__constant_cpu_to_le32 (QTD_TOGGLE);
usb_settoggle (udev, ep, is_out, 1);
}
/*-------------------------------------------------------------------------*/
/*
* For control/bulk/interrupt, return QH with these TDs appended.
* Allocates and initializes the QH if necessary.
* Returns null if it can't allocate a QH it needs to.
* If the QH has TDs (urbs) already, that's great.
*/
struct ehci_qh *
phci_hcd_qh_append_tds (
phci_hcd *hcd,
struct usb_host_endpoint *ep,
struct urb *urb,
struct list_head *qtd_list,
void **ptr,
int *status
)
{
int epnum;
struct ehci_qh *qh = 0;
struct ehci_qtd *qtd = list_entry(qtd_list->next, struct ehci_qtd, qtd_list);
td_ptd_map_buff_t *ptd_map_buff;
td_ptd_map_t *td_ptd_map;
pehci_entry("++ %s: Entered\n",__FUNCTION__);
epnum = ep->desc.bEndpointAddress;
qh = (struct ehci_qh *) *ptr;
if(likely(qh != 0)){
u32 hw_next = QTD_NEXT (qtd->qtd_dma);
pehci_print("%Queue head already %p\n",qh);
ptd_map_buff = &(td_ptd_map_buff[qh->type]);
td_ptd_map = &ptd_map_buff->map_list[qh->qtd_ptd_index];
/* maybe patch the qh used for set_address */
if (unlikely (epnum == 0
&& le32_to_cpu (qh->hw_info1 & 0x7f) == 0))
qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe));
/* is an URB is queued to this qh already? */
if (unlikely (!list_empty (&qh->qtd_list))) {
struct ehci_qtd *last_qtd;
/* update the last qtd's "next" pointer */
last_qtd = list_entry (qh->qtd_list.prev,
struct ehci_qtd, qtd_list);
/*queue head is not empty just add the
td at the end of it , and return from here
*/
last_qtd->hw_next = hw_next;
/*set the status as positive*/
*status = (u32)QUEUE_HEAD_NOT_EMPTY;
/* no URB queued */
} else {
if(td_ptd_map->state == TD_PTD_NEW){
qh->qh_state = QH_STATE_IDLE;
}
td_ptd_map->qtd = qtd;
/* usb_clear_halt() means qh data toggle gets reset */
if (usb_pipebulk (urb->pipe)
&& unlikely (!usb_gettoggle (urb->dev,
(epnum & 0x0f),
!(epnum & 0x10)))) {
phci_hcd_clear_toggle(urb->dev,
epnum & 0x0f, !(epnum & 0x10), qh);
/*reset our data toggle*/
td_ptd_map->datatoggle = 0;
qh->datatoggle = 0;
qh->ping = 0;
}
phci_hcd_qh_update (hcd,qh, qtd);
}
/*put everything in pedning, will be cleared during scheduling*/
ptd_map_buff->pending_ptd_bitmap |= td_ptd_map->ptd_bitmap;
list_splice (qtd_list, qh->qtd_list.prev);
}else{
qh = phci_hcd_make_qh(hcd, urb, qtd_list, status);
*ptr = qh;
}
pehci_entry("-- %s: Exit qh %p\n",__FUNCTION__,qh);
return qh;
}
/*link qtds to endpoint(qh)*/
struct ehci_qh *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -