📄 qtdptd.c
字号:
/*********************************************************************
* Philips ISP176x Host Controller Interface code file
*
* (c) 2002 Koninklijke Philips Electronics N.V. All rights reserved. <usb.linux@philips.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* File Name: qtdptd.c
*
* Refering linux kernel version 2.6.9
*
* History:
*
* Date Author Comments
* --------------------------------------------------------------------
* June 21, 2005 krishan Initial version
*
**********************************************************************
*/
/* Td managenment routines */
#define QUEUE_HEAD_NOT_EMPTY 0x001
/*free the location used by removed urb/endpoint*/
static void
phci_hcd_release_td_ptd_index(struct ehci_qh *qh)
{
td_ptd_map_buff_t *td_ptd_buff = &td_ptd_map_buff[qh->type];
td_ptd_map_t *td_ptd_map = &td_ptd_buff->map_list[qh->qtd_ptd_index];
pehci_entry("++ %s: Entered\n", __FUNCTION__);
/*hold the global lock here*/
td_ptd_map->state = TD_PTD_NEW;
qh->qh_state = QH_STATE_IDLE;
/*
set these values to NULL as schedule
is based on these values,
rather td_ptd_map state
*/
td_ptd_map->qh = NULL;
td_ptd_map->qtd = NULL;
td_ptd_buff->active_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
/* Only pending transfers on current QH must be cleared */
td_ptd_buff->pending_ptd_bitmap &= ~td_ptd_map->ptd_bitmap;
pehci_entry("-- %s: Exit\n",__FUNCTION__);
}
/*print ehciqtd*/
static void
print_ehci_qtd(struct ehci_qtd *qtd)
{
pehci_print("hwnext 0x%08x, altnext 0x%08x,token 0x%08x, length %d\n",
qtd->hw_next, qtd->hw_alt_next,
le32_to_cpu(qtd->hw_token), qtd->length);
pehci_print("buf[0] 0x%08x\n",qtd->hw_buf[0]);
}
/*delete all qtds linked with this urb*/
static void
phci_hcd_qtd_list_free (
phci_hcd *ehci,
struct urb *urb,
struct list_head *qtd_list)
{
struct list_head *entry, *temp;
pehci_entry("++ %s: Entered\n", __FUNCTION__);
list_for_each_safe (entry, temp, qtd_list) {
struct ehci_qtd *qtd;
qtd = list_entry (entry, struct ehci_qtd, qtd_list);
list_del (&qtd->qtd_list);
qha_free(qha_cache, qtd);
}
pehci_entry("-- %s: Exit \n", __FUNCTION__);
}
/*
* free all the qtds for this transfer, also
* free the Host memory to be reused
*/
static void
phci_hcd_urb_free_priv(
phci_hcd *hcd,
urb_priv_t *urb_priv_to_remove,
struct ehci_qh *qh)
{
int i = 0;
struct ehci_qtd *qtd;
for(i = 0;i<urb_priv_to_remove->length;i++){
if(urb_priv_to_remove->qtd[i]){
qtd = urb_priv_to_remove->qtd[i];
list_del(&qtd->qtd_list);
/* This is required when the device is abruptly disconnected and the
* PTDs are not completely processed
*/
if(qtd->length)
phci_hcd_mem_free(&qtd->mem_addr);
qha_free(qha_cache,qtd);
urb_priv_to_remove->qtd[i] = 0;
qtd=0;
}
}
return;
}
/*allocate the qtd*/
struct ehci_qtd *
phci_hcd_qtd_allocate(int mem_flags)
{
struct ehci_qtd *qtd = 0;
qtd = (struct ehci_qtd *)kmalloc(sizeof *qtd,mem_flags);
if(!qtd)
return 0;
memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = cpu_to_le32(qtd);
qtd->hw_next = EHCI_LIST_END;
qtd->hw_alt_next = EHCI_LIST_END;
qtd->state = QTD_STATE_NEW;
INIT_LIST_HEAD (&qtd->qtd_list);
return qtd;
}
/*
* calculates host memory for current length transfer td,
* maximum td length is 4K(custom made)
* */
static int
phci_hcd_qtd_fill(struct urb *urb,
struct ehci_qtd *qtd,
dma_addr_t buf,
size_t len,
int token,
int *status)
{
int count = 0;
qtd->hw_buf [0] = (u32)buf;
/*max lenggth is HC_ATL_PL_SIZE*/
if(len > HC_ATL_PL_SIZE)
count = HC_ATL_PL_SIZE;
else
count = len;
qtd->hw_token = cpu_to_le32 ((count << 16) | token);
qtd->length = count;
pehci_print ("%s:qtd %p, token %8x bytes %d dma %x\n",
__FUNCTION__,qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);
return count;
}
/*
* makes number of qtds required for
* interrupt/bulk/control transfer length
* and initilize qtds
* */
struct list_head*
phci_hcd_make_qtd(
phci_hcd *hcd,
struct list_head *head,
struct urb *urb,
int *status)
{
struct ehci_qtd *qtd, *qtd_prev;
dma_addr_t buf,map_buf;
int len, maxpacket;
int is_input;
u32 token;
int cnt = 0;
urb_priv_t *urb_priv = (urb_priv_t *)urb->hcpriv;
pehci_entry("++ %s, Entered\n",__FUNCTION__);
/*take the qtd from already allocated
structure from hcd_submit_urb
*/
qtd = urb_priv->qtd[cnt];
if (unlikely (!qtd)){
*status = -ENOMEM;
return 0;
}
qtd_prev = 0;
list_add_tail (&qtd->qtd_list, head);
qtd->urb = urb;
token = QTD_STS_ACTIVE;
token |= (EHCI_TUNE_CERR << 10);
len = urb->transfer_buffer_length;
is_input = usb_pipein (urb->pipe);
if (usb_pipecontrol (urb->pipe)) {
/* SETUP pid */
if(phci_hcd_qtd_fill (urb,qtd, cpu_to_le32(urb->setup_packet),
sizeof (struct usb_ctrlrequest),token | (2 /* "setup" */ << 8),
status) < 0)
goto cleanup;
cnt++; /* increment the index */
print_ehci_qtd(qtd);
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
qtd_prev = qtd;
qtd = urb_priv->qtd[cnt];
if (unlikely (!qtd)){
*status = -ENOMEM;
goto cleanup;
}
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
}
/*
* data transfer stage: buffer setup
*/
len = urb->transfer_buffer_length;
if (likely (len > 0))
/*update the buffer address*/
buf = cpu_to_le32(urb->transfer_buffer);
else
buf = map_buf = cpu_to_le32(0); /*set-up stage has no data.*/
/* So are we waiting for the ack only or there is a data stage with out.*/
if (!buf || usb_pipein (urb->pipe))
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
maxpacket = usb_maxpacket (urb->dev, urb->pipe,
usb_pipeout (urb->pipe)) & 0x07ff;
/*
* buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len)
* and may serve as a control status ack
*/
for (;;) {
int this_qtd_len;
this_qtd_len = phci_hcd_qtd_fill (urb,qtd, buf, len, token,status);
if(this_qtd_len < 0) goto cleanup;
print_ehci_qtd(qtd);
len -= this_qtd_len;
buf += this_qtd_len;
cnt++;
/* qh makes control packets use qtd toggle; maybe switch it */
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
token ^= QTD_TOGGLE;
if (likely (len <= 0))
break;
qtd_prev = qtd;
qtd = urb_priv->qtd[cnt];
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
}
/*
* control requests may need a terminating data "status" ack;
* bulk ones may need a terminating short packet (zero length).
*/
if (likely (buf != 0)) {
int one_more = 0;
if (usb_pipecontrol (urb->pipe)) {
one_more = 1;
token ^= 0x0100; /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
} else if (usb_pipebulk (urb->pipe) /* bulk data exactly terminated on zero lenth */
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
one_more = 1;
}
if (one_more) {
qtd_prev = qtd;
qtd = urb_priv->qtd[cnt];
if (unlikely (!qtd))
goto cleanup;
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
list_add_tail (&qtd->qtd_list, head);
phci_hcd_qtd_fill (urb,qtd, 0, 0, token,status);
print_ehci_qtd(qtd);
cnt++;
}
}
/*this is our last td for current transfer*/
qtd->state |= QTD_STATE_LAST;
/*number of tds*/
if(urb_priv->length != cnt){
err("Never Error: number of tds allocated %d exceeding %d\n",
urb_priv->length, cnt);
}
/* by default, enable interrupt on urb completion */
if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT)))
qtd->hw_token |= __constant_cpu_to_le32 (QTD_IOC);
pehci_entry("-- %s, Exit\n",__FUNCTION__);
return head;
cleanup:
phci_hcd_qtd_list_free (hcd, urb, head);
return 0;
}
/*allocates a queue head(endpoint*/
struct ehci_qh *
phci_hcd_qh_alloc (phci_hcd *hcd)
{
struct ehci_qh *qh = (struct ehci_qh *)kmalloc(sizeof(struct ehci_qh), GFP_ATOMIC);
if (!qh)
return qh;
memset (qh, 0, sizeof *qh);
atomic_set (&qh->refcount, 1);
init_waitqueue_head(&qh->waitforcomplete);
qh->qh_dma = (u32)qh;
INIT_LIST_HEAD (&qh->qtd_list);
INIT_LIST_HEAD(&qh->itd_list);
qh->next_uframe = -1;
return qh;
}
/* calculates header address for the tds*/
static int
phci_hcd_fill_ptd_addresses(
td_ptd_map_t *td_ptd_map,
int index,
int bufftype)
{
int i = 0;
unsigned long tdlocation = 0;
/*
* the below payloadlocation and
* payloadsize are redundant
* */
unsigned long payloadlocation = 0;
unsigned long payloadsize = 0;
pehci_entry("++ %s: enter\n",__FUNCTION__);
switch(bufftype){
/*atl header starts at 0xc00*/
case TD_PTD_BUFF_TYPE_ATL:
tdlocation = 0x0c00;
/*redundant*/
payloadsize = 0x1000;
payloadlocation = 0x1000;
break;
case TD_PTD_BUFF_TYPE_INTL:
/*interrupt header
* starts at 0x800
* */
tdlocation = 0x0800;
/*redundant*/
payloadlocation = 0x1000;
payloadsize = 0x1000;
break;
case TD_PTD_BUFF_TYPE_ISTL:
/*iso header starts
* at 0x400*/
tdlocation = 0x0400;
/*redunndant*/
payloadlocation = 0x1000;
payloadsize = 0x1000;
break;
}
i = index;
payloadlocation += (i) * payloadsize;/*each payload is of 4096 bytes*/
tdlocation += (i) * PHCI_QHA_LENGTH; /*each td is of 32 bytes*/
td_ptd_map->ptd_header_addr = tdlocation;
td_ptd_map->ptd_data_addr = payloadlocation;
td_ptd_map->ptd_ram_data_addr = ((payloadlocation - 0x0400) >> 3);
pehci_print("Index: %d, Header: 0x%08x, Payload: 0x%08x,Data start address: 0x%08x\n",index,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -