⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 qtdptd.c

📁 usb isp1761驱动源代码 可编进内核。
💻 C
📖 第 1 页 / 共 3 页
字号:
/*********************************************************************
 * 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 + -