1/*
2 * Copyright (c) 2007-2013 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdlib.h>
11#include <string.h>
12#include <barrelfish/barrelfish.h>
13
14#include <usb/usb.h>
15#include <usb/usb_descriptor.h>
16
17#include <usb_controller.h>
18
19#include <usb_xfer.h>
20#include <usb_pipe.h>
21
22
23/**
24 * \brief this function handles the start of a new transfer when it is on the
25 *        endpoint queue
26 */
27void usb_pipe_start(struct usb_xfer_queue *queue)
28{
29    USB_DEBUG_TR_ENTER;
30
31    /* get the xfer from the queue */
32    struct usb_xfer *xfer = queue->current;
33
34    assert(xfer != NULL);
35
36    struct usb_endpoint *ep = xfer->endpoint;
37
38    uint8_t type;
39
40    if (ep->is_stalled) {
41        USB_DEBUG_XFER("NOTICE: endpoint is already stalled...\n");
42
43        USB_DEBUG_TR_RETURN;
44        return;
45    }
46
47    if (xfer->flags.pipe_stalled) {
48        USB_DEBUG("NOTICE: Stalling pipe...\n");
49        /* TODO: Stall pipe */
50        assert(!"NYI: Stall pipe");
51    }
52
53    if (xfer->num_frames == 0) {
54        USB_DEBUG_XFER("NOTICE: No frames to process... finishing transfer");
55        xfer->actual_frames = 0;
56        usb_xfer_done(xfer, USB_ERR_OK);
57        USB_DEBUG_TR_RETURN;
58        return;
59    }
60
61    if (xfer->interval > 0) {
62        type = (ep->descriptor->bmAttributes.xfer_type);
63        switch (type) {
64            case USB_ENDPOINT_TYPE_BULK:
65            case USB_ENDPOINT_TYPE_CONTROL:
66               /* TODO: Delay the transfer start... */
67                assert(!"NYI: delayed start");
68                return;
69                break;
70            default:
71                /* noop */
72                break;
73        }
74    }
75
76    if (xfer->error == USB_ERR_OK) {
77        xfer->flags_internal.notify = 1;
78        /* call the start function */
79        ep->pipe_fn->start(xfer);
80    }
81
82    xfer->flags_internal.cancellable = 1;
83
84    if (xfer->error != USB_ERR_OK) {
85        /* there was an error while starting, cancel the transfer */
86        usb_xfer_done(xfer, 0);
87    }
88    USB_DEBUG_TR_RETURN;
89
90}
91
92/**
93 * \brief this function handles the event when a new transfer enters the system
94 *
95 */
96void usb_pipe_enter(struct usb_xfer *xfer)
97{
98    USB_DEBUG_TR_ENTER;
99
100    assert(xfer != NULL);
101
102    struct usb_endpoint *ep = xfer->endpoint;
103
104    /* call the enter function of the pipe */
105    (ep->pipe_fn->enter)(xfer);
106
107    xfer->flags_internal.cancellable = 1;
108
109    if (xfer->error != USB_ERR_OK) {
110        USB_DEBUG("ERROR: xfer returned with error...");
111        usb_xfer_done(xfer, 0);
112        USB_DEBUG_TR_RETURN;
113        return;
114    }
115
116    if (ep->transfers.current != xfer) {
117        /* there is already a transfer happening, so enqueue it on the endpoint */
118        usb_xfer_enqueue(&ep->transfers, xfer);
119
120        if (ep->transfers.current != NULL) {
121            USB_DEBUG_XFER("Some thing is already processing...\n");
122
123            USB_DEBUG_TR_RETURN;
124            return;
125        }
126    }
127
128    if (!ep->transfers.recurse_1) {
129        ep->transfers.recurse_1 = 1;
130        if (ep->transfers.current == NULL) {
131            xfer = ep->transfers.head.first;
132
133            if (xfer) {
134                if (xfer->wait_entry.next != NULL) {
135                    xfer->wait_entry.next->wait_entry.prev_next = xfer
136                            ->wait_entry.prev_next;
137                    *(xfer->wait_entry.prev_next) = xfer->wait_entry.next;
138                } else {
139                    (&ep->transfers.head)->last_next = &(&ep->transfers.head)->first;
140                    (&ep->transfers.head)->first = NULL;
141                            //xfer->wait_entry.prev_next;
142                    xfer->wait_entry.prev_next = &xfer->wait_entry.next;
143                }
144                xfer->wait_queue = NULL;
145                /* set the current xfer to be handled in the queue */
146                ep->transfers.current = xfer;
147                USB_DEBUG_XFER("ep->transfers.command\n");
148                /* execute the start command on the new xfer on the endpoint */
149                (ep->transfers.command)(&ep->transfers);
150            }
151            ep->transfers.recurse_1 = 0;
152        }
153    }
154
155    USB_DEBUG_TR_RETURN;
156}
157