Root/target/linux/lantiq/files/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c

1/* ==========================================================================
2 * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd_queue.c $
3 * $Revision: 1.1.1.1 $
4 * $Date: 2009-04-17 06:15:34 $
5 * $Change: 537387 $
6 *
7 * Synopsys HS OTG Linux Software Driver and documentation (hereinafter,
8 * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless
9 * otherwise expressly agreed to in writing between Synopsys and you.
10 *
11 * The Software IS NOT an item of Licensed Software or Licensed Product under
12 * any End User Software License Agreement or Agreement for Licensed Product
13 * with Synopsys or any supplement thereto. You are permitted to use and
14 * redistribute this Software in source and binary forms, with or without
15 * modification, provided that redistributions of source code must retain this
16 * notice. You may not view, use, disclose, copy or distribute this file or
17 * any information contained herein except pursuant to this license grant from
18 * Synopsys. If you do not agree with this notice, including the disclaimer
19 * below, then you are not authorized to use the Software.
20 *
21 * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 * DAMAGE.
32 * ========================================================================== */
33#ifndef DWC_DEVICE_ONLY
34
35/**
36 * @file
37 *
38 * This file contains the functions to manage Queue Heads and Queue
39 * Transfer Descriptors.
40 */
41#include <linux/kernel.h>
42#include <linux/module.h>
43#include <linux/moduleparam.h>
44#include <linux/init.h>
45#include <linux/device.h>
46#include <linux/errno.h>
47#include <linux/list.h>
48#include <linux/interrupt.h>
49#include <linux/string.h>
50
51#include "dwc_otg_driver.h"
52#include "dwc_otg_hcd.h"
53#include "dwc_otg_regs.h"
54
55/**
56 * This function allocates and initializes a QH.
57 *
58 * @param _hcd The HCD state structure for the DWC OTG controller.
59 * @param[in] _urb Holds the information about the device/endpoint that we need
60 * to initialize the QH.
61 *
62 * @return Returns pointer to the newly allocated QH, or NULL on error. */
63dwc_otg_qh_t *dwc_otg_hcd_qh_create (dwc_otg_hcd_t *_hcd, struct urb *_urb)
64{
65    dwc_otg_qh_t *qh;
66
67    /* Allocate memory */
68    /** @todo add memflags argument */
69    qh = dwc_otg_hcd_qh_alloc ();
70    if (qh == NULL) {
71        return NULL;
72    }
73
74    dwc_otg_hcd_qh_init (_hcd, qh, _urb);
75    return qh;
76}
77
78/** Free each QTD in the QH's QTD-list then free the QH. QH should already be
79 * removed from a list. QTD list should already be empty if called from URB
80 * Dequeue.
81 *
82 * @param[in] _qh The QH to free.
83 */
84void dwc_otg_hcd_qh_free (dwc_otg_qh_t *_qh)
85{
86    dwc_otg_qtd_t *qtd;
87    struct list_head *pos;
88    unsigned long flags;
89
90    /* Free each QTD in the QTD list */
91    local_irq_save (flags);
92    for (pos = _qh->qtd_list.next;
93         pos != &_qh->qtd_list;
94         pos = _qh->qtd_list.next)
95    {
96        list_del (pos);
97        qtd = dwc_list_to_qtd (pos);
98        dwc_otg_hcd_qtd_free (qtd);
99    }
100    local_irq_restore (flags);
101
102    kfree (_qh);
103    return;
104}
105
106/** Initializes a QH structure.
107 *
108 * @param[in] _hcd The HCD state structure for the DWC OTG controller.
109 * @param[in] _qh The QH to init.
110 * @param[in] _urb Holds the information about the device/endpoint that we need
111 * to initialize the QH. */
112#define SCHEDULE_SLOP 10
113void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, struct urb *_urb)
114{
115    memset (_qh, 0, sizeof (dwc_otg_qh_t));
116
117    /* Initialize QH */
118    switch (usb_pipetype(_urb->pipe)) {
119    case PIPE_CONTROL:
120        _qh->ep_type = USB_ENDPOINT_XFER_CONTROL;
121        break;
122    case PIPE_BULK:
123        _qh->ep_type = USB_ENDPOINT_XFER_BULK;
124        break;
125    case PIPE_ISOCHRONOUS:
126        _qh->ep_type = USB_ENDPOINT_XFER_ISOC;
127        break;
128    case PIPE_INTERRUPT:
129        _qh->ep_type = USB_ENDPOINT_XFER_INT;
130        break;
131    }
132
133    _qh->ep_is_in = usb_pipein(_urb->pipe) ? 1 : 0;
134
135    _qh->data_toggle = DWC_OTG_HC_PID_DATA0;
136    _qh->maxp = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe)));
137    INIT_LIST_HEAD(&_qh->qtd_list);
138    INIT_LIST_HEAD(&_qh->qh_list_entry);
139    _qh->channel = NULL;
140
141    /* FS/LS Enpoint on HS Hub
142     * NOT virtual root hub */
143    _qh->do_split = 0;
144    _qh->speed = _urb->dev->speed;
145    if (((_urb->dev->speed == USB_SPEED_LOW) ||
146         (_urb->dev->speed == USB_SPEED_FULL)) &&
147        (_urb->dev->tt) && (_urb->dev->tt->hub) && (_urb->dev->tt->hub->devnum != 1)) {
148        DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n",
149               usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum,
150               _urb->dev->ttport);
151        _qh->do_split = 1;
152    }
153
154    if (_qh->ep_type == USB_ENDPOINT_XFER_INT ||
155        _qh->ep_type == USB_ENDPOINT_XFER_ISOC) {
156        /* Compute scheduling parameters once and save them. */
157        hprt0_data_t hprt;
158
159        /** @todo Account for split transfers in the bus time. */
160        int bytecount = dwc_hb_mult(_qh->maxp) * dwc_max_packet(_qh->maxp);
161        _qh->usecs = NS_TO_US(usb_calc_bus_time(_urb->dev->speed,
162                           usb_pipein(_urb->pipe),
163                    (_qh->ep_type == USB_ENDPOINT_XFER_ISOC),bytecount));
164
165        /* Start in a slightly future (micro)frame. */
166        _qh->sched_frame = dwc_frame_num_inc(_hcd->frame_number, SCHEDULE_SLOP);
167        _qh->interval = _urb->interval;
168#if 0
169        /* Increase interrupt polling rate for debugging. */
170        if (_qh->ep_type == USB_ENDPOINT_XFER_INT) {
171            _qh->interval = 8;
172        }
173#endif
174        hprt.d32 = dwc_read_reg32(_hcd->core_if->host_if->hprt0);
175        if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) &&
176            ((_urb->dev->speed == USB_SPEED_LOW) ||
177             (_urb->dev->speed == USB_SPEED_FULL)))
178        {
179            _qh->interval *= 8;
180            _qh->sched_frame |= 0x7;
181            _qh->start_split_frame = _qh->sched_frame;
182        }
183    }
184
185    DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n");
186    DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", _qh);
187    DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n",
188            _urb->dev->devnum);
189    DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n",
190            usb_pipeendpoint(_urb->pipe),
191            usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT");
192    DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n",
193            ({ char *speed; switch (_urb->dev->speed) {
194            case USB_SPEED_LOW: speed = "low"; break;
195            case USB_SPEED_FULL: speed = "full"; break;
196            case USB_SPEED_HIGH: speed = "high"; break;
197            default: speed = "?"; break;
198            }; speed;}));
199    DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n",
200            ({ char *type; switch (_qh->ep_type) {
201            case USB_ENDPOINT_XFER_ISOC: type = "isochronous"; break;
202            case USB_ENDPOINT_XFER_INT: type = "interrupt"; break;
203            case USB_ENDPOINT_XFER_CONTROL: type = "control"; break;
204            case USB_ENDPOINT_XFER_BULK: type = "bulk"; break;
205            default: type = "?"; break;
206            }; type;}));
207#ifdef DEBUG
208    if (_qh->ep_type == USB_ENDPOINT_XFER_INT) {
209        DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n",
210                _qh->usecs);
211        DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n",
212                _qh->interval);
213    }
214#endif
215    
216    return;
217}
218
219/**
220 * Microframe scheduler
221 * track the total use in hcd->frame_usecs
222 * keep each qh use in qh->frame_usecs
223 * when surrendering the qh then donate the time back
224 */
225const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 };
226
227/*
228 * called from dwc_otg_hcd.c:dwc_otg_hcd_init
229 */
230int init_hcd_usecs(dwc_otg_hcd_t *_hcd)
231{
232    int i;
233    for (i=0; i<8; i++) {
234        _hcd->frame_usecs[i] = max_uframe_usecs[i];
235    }
236    return 0;
237}
238
239static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
240{
241    int i;
242    unsigned short utime;
243    int t_left;
244    int ret;
245    int done;
246
247    ret = -1;
248    utime = _qh->usecs;
249    t_left = utime;
250    i = 0;
251    done = 0;
252    while (done == 0) {
253        /* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */
254        if (utime <= _hcd->frame_usecs[i]) {
255            _hcd->frame_usecs[i] -= utime;
256            _qh->frame_usecs[i] += utime;
257            t_left -= utime;
258            ret = i;
259            done = 1;
260            return ret;
261        } else {
262            i++;
263            if (i == 8) {
264                done = 1;
265                ret = -1;
266            }
267        }
268    }
269    return ret;
270}
271
272/*
273 * use this for FS apps that can span multiple uframes
274 */
275static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
276{
277    int i;
278    int j;
279    unsigned short utime;
280    int t_left;
281    int ret;
282    int done;
283    unsigned short xtime;
284
285    ret = -1;
286    utime = _qh->usecs;
287    t_left = utime;
288    i = 0;
289    done = 0;
290loop:
291    while (done == 0) {
292        if(_hcd->frame_usecs[i] <= 0) {
293            i++;
294            if (i == 8) {
295                done = 1;
296                ret = -1;
297            }
298            goto loop;
299        }
300
301        /*
302         * we need n consequtive slots
303         * so use j as a start slot j plus j+1 must be enough time (for now)
304         */
305        xtime= _hcd->frame_usecs[i];
306        for (j = i+1 ; j < 8 ; j++ ) {
307            /*
308             * if we add this frame remaining time to xtime we may
309             * be OK, if not we need to test j for a complete frame
310             */
311            if ((xtime+_hcd->frame_usecs[j]) < utime) {
312                if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) {
313                    j = 8;
314                    ret = -1;
315                    continue;
316                }
317            }
318            if (xtime >= utime) {
319                ret = i;
320                j = 8; /* stop loop with a good value ret */
321                continue;
322            }
323            /* add the frame time to x time */
324            xtime += _hcd->frame_usecs[j];
325            /* we must have a fully available next frame or break */
326            if ((xtime < utime)
327                && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) {
328                ret = -1;
329                j = 8; /* stop loop with a bad value ret */
330                continue;
331            }
332        }
333        if (ret >= 0) {
334            t_left = utime;
335            for (j = i; (t_left>0) && (j < 8); j++ ) {
336                t_left -= _hcd->frame_usecs[j];
337                if ( t_left <= 0 ) {
338                    _qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left;
339                    _hcd->frame_usecs[j]= -t_left;
340                    ret = i;
341                    done = 1;
342                } else {
343                    _qh->frame_usecs[j] += _hcd->frame_usecs[j];
344                    _hcd->frame_usecs[j] = 0;
345                }
346            }
347        } else {
348            i++;
349            if (i == 8) {
350                done = 1;
351                ret = -1;
352            }
353        }
354    }
355    return ret;
356}
357
358static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
359{
360    int ret;
361    ret = -1;
362
363    if (_qh->speed == USB_SPEED_HIGH) {
364        /* if this is a hs transaction we need a full frame */
365        ret = find_single_uframe(_hcd, _qh);
366    } else {
367        /* if this is a fs transaction we may need a sequence of frames */
368        ret = find_multi_uframe(_hcd, _qh);
369    }
370    return ret;
371}
372            
373/**
374 * Checks that the max transfer size allowed in a host channel is large enough
375 * to handle the maximum data transfer in a single (micro)frame for a periodic
376 * transfer.
377 *
378 * @param _hcd The HCD state structure for the DWC OTG controller.
379 * @param _qh QH for a periodic endpoint.
380 *
381 * @return 0 if successful, negative error code otherwise.
382 */
383static int check_max_xfer_size(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh)
384{
385    int status;
386    uint32_t max_xfer_size;
387    uint32_t max_channel_xfer_size;
388
389    status = 0;
390
391    max_xfer_size = dwc_max_packet(_qh->maxp) * dwc_hb_mult(_qh->maxp);
392    max_channel_xfer_size = _hcd->core_if->core_params->max_transfer_size;
393
394    if (max_xfer_size > max_channel_xfer_size) {
395        DWC_NOTICE("%s: Periodic xfer length %d > "
396                "max xfer length for channel %d\n",
397                __func__, max_xfer_size, max_channel_xfer_size);
398        status = -ENOSPC;
399    }
400
401    return status;
402}
403
404/**
405 * Schedules an interrupt or isochronous transfer in the periodic schedule.
406 *
407 * @param _hcd The HCD state structure for the DWC OTG controller.
408 * @param _qh QH for the periodic transfer. The QH should already contain the
409 * scheduling information.
410 *
411 * @return 0 if successful, negative error code otherwise.
412 */
413static int schedule_periodic(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh)
414{
415    int status = 0;
416
417    int frame;
418    status = find_uframe(_hcd, _qh);
419    frame = -1;
420    if (status == 0) {
421        frame = 7;
422    } else {
423        if (status > 0 )
424            frame = status-1;
425    }
426
427    /* Set the new frame up */
428    if (frame > -1) {
429        _qh->sched_frame &= ~0x7;
430        _qh->sched_frame |= (frame & 7);
431    }
432
433    if (status != -1 )
434        status = 0;
435    if (status) {
436        DWC_NOTICE("%s: Insufficient periodic bandwidth for "
437               "periodic transfer.\n", __func__);
438        return status;
439    }
440
441    status = check_max_xfer_size(_hcd, _qh);
442    if (status) {
443        DWC_NOTICE("%s: Channel max transfer size too small "
444                "for periodic transfer.\n", __func__);
445        return status;
446    }
447
448    /* Always start in the inactive schedule. */
449    list_add_tail(&_qh->qh_list_entry, &_hcd->periodic_sched_inactive);
450
451
452    /* Update claimed usecs per (micro)frame. */
453    _hcd->periodic_usecs += _qh->usecs;
454
455    /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */
456    hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated += _qh->usecs / _qh->interval;
457    if (_qh->ep_type == USB_ENDPOINT_XFER_INT) {
458        hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs++;
459        DWC_DEBUGPL(DBG_HCD, "Scheduled intr: qh %p, usecs %d, period %d\n",
460                _qh, _qh->usecs, _qh->interval);
461    } else {
462        hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs++;
463        DWC_DEBUGPL(DBG_HCD, "Scheduled isoc: qh %p, usecs %d, period %d\n",
464                _qh, _qh->usecs, _qh->interval);
465    }
466        
467    return status;
468}
469
470/**
471 * This function adds a QH to either the non periodic or periodic schedule if
472 * it is not already in the schedule. If the QH is already in the schedule, no
473 * action is taken.
474 *
475 * @return 0 if successful, negative error code otherwise.
476 */
477int dwc_otg_hcd_qh_add (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh)
478{
479    unsigned long flags;
480    int status = 0;
481
482    local_irq_save(flags);
483
484    if (!list_empty(&_qh->qh_list_entry)) {
485        /* QH already in a schedule. */
486        goto done;
487    }
488
489    /* Add the new QH to the appropriate schedule */
490    if (dwc_qh_is_non_per(_qh)) {
491        /* Always start in the inactive schedule. */
492        list_add_tail(&_qh->qh_list_entry, &_hcd->non_periodic_sched_inactive);
493    } else {
494        status = schedule_periodic(_hcd, _qh);
495    }
496
497 done:
498    local_irq_restore(flags);
499
500    return status;
501}
502
503/**
504 * This function adds a QH to the non periodic deferred schedule.
505 *
506 * @return 0 if successful, negative error code otherwise.
507 */
508int dwc_otg_hcd_qh_add_deferred(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh)
509{
510    unsigned long flags;
511    local_irq_save(flags);
512    if (!list_empty(&_qh->qh_list_entry)) {
513        /* QH already in a schedule. */
514        goto done;
515    }
516
517    /* Add the new QH to the non periodic deferred schedule */
518    if (dwc_qh_is_non_per(_qh)) {
519        list_add_tail(&_qh->qh_list_entry,
520                  &_hcd->non_periodic_sched_deferred);
521    }
522done:
523    local_irq_restore(flags);
524    return 0;
525}
526
527/**
528 * Removes an interrupt or isochronous transfer from the periodic schedule.
529 *
530 * @param _hcd The HCD state structure for the DWC OTG controller.
531 * @param _qh QH for the periodic transfer.
532 */
533static void deschedule_periodic(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh)
534{
535    int i;
536    list_del_init(&_qh->qh_list_entry);
537
538
539    /* Update claimed usecs per (micro)frame. */
540    _hcd->periodic_usecs -= _qh->usecs;
541
542    for (i = 0; i < 8; i++) {
543        _hcd->frame_usecs[i] += _qh->frame_usecs[i];
544        _qh->frame_usecs[i] = 0;
545    }
546    /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */
547    hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated -= _qh->usecs / _qh->interval;
548
549    if (_qh->ep_type == USB_ENDPOINT_XFER_INT) {
550        hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs--;
551        DWC_DEBUGPL(DBG_HCD, "Descheduled intr: qh %p, usecs %d, period %d\n",
552                _qh, _qh->usecs, _qh->interval);
553    } else {
554        hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs--;
555        DWC_DEBUGPL(DBG_HCD, "Descheduled isoc: qh %p, usecs %d, period %d\n",
556                _qh, _qh->usecs, _qh->interval);
557    }
558}
559
560/**
561 * Removes a QH from either the non-periodic or periodic schedule. Memory is
562 * not freed.
563 *
564 * @param[in] _hcd The HCD state structure.
565 * @param[in] _qh QH to remove from schedule. */
566void dwc_otg_hcd_qh_remove (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh)
567{
568    unsigned long flags;
569
570    local_irq_save(flags);
571
572    if (list_empty(&_qh->qh_list_entry)) {
573        /* QH is not in a schedule. */
574        goto done;
575    }
576
577    if (dwc_qh_is_non_per(_qh)) {
578        if (_hcd->non_periodic_qh_ptr == &_qh->qh_list_entry) {
579            _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next;
580        }
581        list_del_init(&_qh->qh_list_entry);
582    } else {
583        deschedule_periodic(_hcd, _qh);
584    }
585
586 done:
587    local_irq_restore(flags);
588}
589
590/**
591 * Defers a QH. For non-periodic QHs, removes the QH from the active
592 * non-periodic schedule. The QH is added to the deferred non-periodic
593 * schedule if any QTDs are still attached to the QH.
594 */
595int dwc_otg_hcd_qh_deferr(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh, int delay)
596{
597        int deact = 1;
598    unsigned long flags;
599    local_irq_save(flags);
600    if (dwc_qh_is_non_per(_qh)) {
601            _qh->sched_frame =
602          dwc_frame_num_inc(_hcd->frame_number,
603                    delay);
604        _qh->channel = NULL;
605        _qh->qtd_in_process = NULL;
606        deact = 0;
607        dwc_otg_hcd_qh_remove(_hcd, _qh);
608        if (!list_empty(&_qh->qtd_list)) {
609            /* Add back to deferred non-periodic schedule. */
610            dwc_otg_hcd_qh_add_deferred(_hcd, _qh);
611        }
612    }
613    local_irq_restore(flags);
614    return deact;
615}
616
617/**
618 * Deactivates a QH. For non-periodic QHs, removes the QH from the active
619 * non-periodic schedule. The QH is added to the inactive non-periodic
620 * schedule if any QTDs are still attached to the QH.
621 *
622 * For periodic QHs, the QH is removed from the periodic queued schedule. If
623 * there are any QTDs still attached to the QH, the QH is added to either the
624 * periodic inactive schedule or the periodic ready schedule and its next
625 * scheduled frame is calculated. The QH is placed in the ready schedule if
626 * the scheduled frame has been reached already. Otherwise it's placed in the
627 * inactive schedule. If there are no QTDs attached to the QH, the QH is
628 * completely removed from the periodic schedule.
629 */
630void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, int sched_next_periodic_split)
631{
632    unsigned long flags;
633    local_irq_save(flags);
634
635    if (dwc_qh_is_non_per(_qh)) {
636        dwc_otg_hcd_qh_remove(_hcd, _qh);
637        if (!list_empty(&_qh->qtd_list)) {
638            /* Add back to inactive non-periodic schedule. */
639            dwc_otg_hcd_qh_add(_hcd, _qh);
640        }
641    } else {
642        uint16_t frame_number = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(_hcd));
643
644        if (_qh->do_split) {
645            /* Schedule the next continuing periodic split transfer */
646            if (sched_next_periodic_split) {
647
648                _qh->sched_frame = frame_number;
649                if (dwc_frame_num_le(frame_number,
650                             dwc_frame_num_inc(_qh->start_split_frame, 1))) {
651                    /*
652                     * Allow one frame to elapse after start
653                     * split microframe before scheduling
654                     * complete split, but DONT if we are
655                     * doing the next start split in the
656                     * same frame for an ISOC out.
657                     */
658                    if ((_qh->ep_type != USB_ENDPOINT_XFER_ISOC) || (_qh->ep_is_in != 0)) {
659                        _qh->sched_frame = dwc_frame_num_inc(_qh->sched_frame, 1);
660                    }
661                }
662            } else {
663                _qh->sched_frame = dwc_frame_num_inc(_qh->start_split_frame,
664                                     _qh->interval);
665                if (dwc_frame_num_le(_qh->sched_frame, frame_number)) {
666                    _qh->sched_frame = frame_number;
667                }
668                _qh->sched_frame |= 0x7;
669                _qh->start_split_frame = _qh->sched_frame;
670            }
671        } else {
672            _qh->sched_frame = dwc_frame_num_inc(_qh->sched_frame, _qh->interval);
673            if (dwc_frame_num_le(_qh->sched_frame, frame_number)) {
674                _qh->sched_frame = frame_number;
675            }
676        }
677
678        if (list_empty(&_qh->qtd_list)) {
679            dwc_otg_hcd_qh_remove(_hcd, _qh);
680        } else {
681            /*
682             * Remove from periodic_sched_queued and move to
683             * appropriate queue.
684             */
685            if (dwc_frame_num_le(_qh->sched_frame, frame_number)) {
686                list_move(&_qh->qh_list_entry,
687                      &_hcd->periodic_sched_ready);
688            } else {
689                list_move(&_qh->qh_list_entry,
690                      &_hcd->periodic_sched_inactive);
691            }
692        }
693    }
694
695    local_irq_restore(flags);
696}
697
698/**
699 * This function allocates and initializes a QTD.
700 *
701 * @param[in] _urb The URB to create a QTD from. Each URB-QTD pair will end up
702 * pointing to each other so each pair should have a unique correlation.
703 *
704 * @return Returns pointer to the newly allocated QTD, or NULL on error. */
705dwc_otg_qtd_t *dwc_otg_hcd_qtd_create (struct urb *_urb)
706{
707    dwc_otg_qtd_t *qtd;
708
709    qtd = dwc_otg_hcd_qtd_alloc ();
710    if (qtd == NULL) {
711        return NULL;
712    }
713
714    dwc_otg_hcd_qtd_init (qtd, _urb);
715    return qtd;
716}
717
718/**
719 * Initializes a QTD structure.
720 *
721 * @param[in] _qtd The QTD to initialize.
722 * @param[in] _urb The URB to use for initialization. */
723void dwc_otg_hcd_qtd_init (dwc_otg_qtd_t *_qtd, struct urb *_urb)
724{
725    memset (_qtd, 0, sizeof (dwc_otg_qtd_t));
726    _qtd->urb = _urb;
727    if (usb_pipecontrol(_urb->pipe)) {
728        /*
729         * The only time the QTD data toggle is used is on the data
730         * phase of control transfers. This phase always starts with
731         * DATA1.
732         */
733        _qtd->data_toggle = DWC_OTG_HC_PID_DATA1;
734        _qtd->control_phase = DWC_OTG_CONTROL_SETUP;
735    }
736
737    /* start split */
738    _qtd->complete_split = 0;
739    _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL;
740    _qtd->isoc_split_offset = 0;
741
742    /* Store the qtd ptr in the urb to reference what QTD. */
743    _urb->hcpriv = _qtd;
744    return;
745}
746
747/**
748 * This function adds a QTD to the QTD-list of a QH. It will find the correct
749 * QH to place the QTD into. If it does not find a QH, then it will create a
750 * new QH. If the QH to which the QTD is added is not currently scheduled, it
751 * is placed into the proper schedule based on its EP type.
752 *
753 * @param[in] _qtd The QTD to add
754 * @param[in] _dwc_otg_hcd The DWC HCD structure
755 *
756 * @return 0 if successful, negative error code otherwise.
757 */
758int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * _qtd, dwc_otg_hcd_t * _dwc_otg_hcd)
759{
760    struct usb_host_endpoint *ep;
761    dwc_otg_qh_t *qh;
762    unsigned long flags;
763    int retval = 0;
764    struct urb *urb = _qtd->urb;
765
766    local_irq_save(flags);
767
768    /*
769     * Get the QH which holds the QTD-list to insert to. Create QH if it
770     * doesn't exist.
771     */
772    ep = dwc_urb_to_endpoint(urb);
773    qh = (dwc_otg_qh_t *)ep->hcpriv;
774    if (qh == NULL) {
775        qh = dwc_otg_hcd_qh_create (_dwc_otg_hcd, urb);
776        if (qh == NULL) {
777            retval = -1;
778            goto done;
779        }
780        ep->hcpriv = qh;
781    }
782
783    _qtd->qtd_qh_ptr = qh;
784    retval = dwc_otg_hcd_qh_add(_dwc_otg_hcd, qh);
785    if (retval == 0) {
786        list_add_tail(&_qtd->qtd_list_entry, &qh->qtd_list);
787    }
788
789 done:
790    local_irq_restore(flags);
791    return retval;
792}
793
794#endif /* DWC_DEVICE_ONLY */
795

Archive Download this file



interactive