1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4
5#include <barrelfish/barrelfish.h>
6#include <barrelfish_kpi/platform.h>
7#include <barrelfish/nameservice_client.h>
8#include <barrelfish/inthandler.h>
9#include <driverkit/driverkit.h>
10
11#include <usb/usb.h>
12
13#include <if/usb_driver_defs.h>
14#include <if/usb_manager_defs.h>
15#include <if/usb_manager_defs.h>
16#include <if/monitor_blocking_defs.h>
17
18#include <usb_controller.h>
19#include <usb_request.h>
20#include <usb_device.h>
21#include <usb_transfer.h>
22#include <usb_driver.h>
23
24#include "platform.h"
25
26/*
27 * ========================================================================
28 * Flounder callbacks and service connect handling
29 * ========================================================================
30 */
31
32/**
33 * struct representing the state of a new USB driver connection
34 */
35struct usb_manager_connect_state {
36    struct usb_manager_binding *b;      ///< the usb_manager_binding struct
37    struct usb_driver_binding *driver;  ///< the usb drivers service
38    void *desc;                         ///< configuration descriptor
39    uint32_t length;                    ///< length of the descirptor
40    usb_error_t error;                  ///< the outcome of the initial setup
41    iref_t driver_iref;                 ///< the drivers iref
42};
43
44/**
45 * \brief callback for USB driver binding
46 *
47 * \param st the supplied state
48 * \param err the outcome of the binding process
49 * \param b the driver binding
50 *
51 * This function is the last step in the setup procedure and frees up the state
52 */
53static void usb_driver_bind_cb(void *st, errval_t err,
54        struct usb_driver_binding *b)
55{
56    USB_DEBUG_IDC("usb_driver_bind_cb\n");
57
58    if (err_is_fail(err)) {
59        USB_DEBUG("driver binding failed..\n");
60    }
61
62    struct usb_manager_connect_state *cs = st;
63
64    cs->driver = b;
65    struct usb_device *dev = cs->b->st;
66    dev->usb_driver_binding = b;
67
68    free(cs->desc);
69    free(cs);
70}
71
72/*
73 * \brief callback for successful sent replies to the connect rpc
74 *
75 * \param a the state of the connection call
76 *
77 * This function binds to the USB driver iref
78 */
79static void usb_driver_connect_cb(void *a)
80{
81    USB_DEBUG_IDC("usb_driver_connect_cb->binding...\n");
82    struct usb_manager_connect_state *st = a;
83    errval_t err = usb_driver_bind(st->driver_iref, usb_driver_bind_cb, st,
84            get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
85    if (err_is_fail(err)) {
86        DEBUG_ERR(err, "usb driver bind failed");
87        usb_device_free(st->b->st, 0);
88        free(st->desc);
89        free(st);
90    }
91}
92
93/*
94 * \brief sends the response to the connect rpc
95 *
96 * \param a the connection state
97 */
98static void usb_driver_connect_response(void *a)
99{
100    errval_t err;
101    struct usb_manager_connect_state *st = a;
102
103    struct event_closure txcont = MKCONT(usb_driver_connect_cb, st);
104
105    err = usb_manager_connect_response__tx(st->b, txcont, st->error, st->desc,
106            st->length);
107
108    if (err_is_fail(err)) {
109        if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
110            // try to resend
111            txcont = MKCONT(usb_driver_connect_response, st);
112            err = st->b->register_send(st->b, get_default_waitset(), txcont);
113            if (err_is_fail(err)) {
114                DEBUG_ERR(err, "failed to register send");
115            }
116        } else {
117            // error
118            DEBUG_ERR(err, "error while seniding driver connect response");
119            /* free the device */
120            usb_device_free(st->b->st, 0);
121            free(st->desc);
122            free(st);
123        }
124    }
125}
126
127/**
128 * \brief this function handles connections of new USB device driver processes
129 *
130 * \param bind  the binding which we received the connect cal
131 * \param driver_iref the iref of the usb driver
132 * \param init_config the initial configuration to set the device
133 *
134 * This function associates the USB device with an flounder binding. Further
135 * the usb manager connects to the usb drivers service for notifications
136 */
137static void usb_rx_connect_call(struct usb_manager_binding *bind,
138        iref_t driver_iref, uint16_t init_config)
139{
140    struct usb_manager_connect_state *st;
141
142    st = malloc(sizeof(struct usb_manager_connect_state));
143
144    if (st == NULL) {
145        USER_PANIC("cannot reply, out of memory!");
146    }
147
148    st->b = bind;
149    st->driver_iref = driver_iref;
150
151    // associate the bindings with the usb device
152    usb_driver_connected(bind, st->driver, init_config);
153
154    if (bind->st == NULL) {
155        /* if the connection fails, there will not be an association */
156        debug_printf("ERROR: no state associated..\n");
157        st->error = USB_ERR_IOERROR;
158        usb_driver_connect_response(st);
159        return;
160    }
161
162    /*
163     * all went fine so the binding state pointer is now refering to the
164     * usb device and we can setup the reply
165     */
166
167    struct usb_device *dev = bind->st;
168
169    /* we reply with the initial configuration descriptor */
170    st->length = sizeof((dev->device_desc)) + dev->config_desc_size;
171    st->desc = malloc(st->length);
172
173    memcpy(st->desc, &(dev->device_desc), sizeof((dev->device_desc)));
174    memcpy(st->desc + sizeof((dev->device_desc)), dev->config_desc,
175            dev->config_desc_size);
176
177    st->error = USB_ERR_OK;
178
179    // send response
180    usb_driver_connect_response(st);
181}
182
183/// the receive function handles
184static struct usb_manager_rx_vtbl usb_manager_handle_fn = {
185    .request_read_call = usb_rx_request_read_call,
186    .request_write_call = usb_rx_request_write_call,
187    .request_call = usb_rx_request_call,
188    .connect_call = usb_rx_connect_call,
189    .transfer_setup_call = usb_rx_transfer_setup_call,
190    .transfer_unsetup_call = usb_rx_transfer_unsetup_call,
191    .transfer_start_call = usb_rx_transfer_start_call,
192    .transfer_stop_call = usb_rx_transfer_stop_call,
193    .transfer_status_call = usb_rx_transfer_status_call,
194    .transfer_state_call = usb_rx_transfer_state_call,
195    .transfer_clear_stall_call = usb_rx_transfer_clear_stall_call,
196};
197
198/**
199 * \brief this function sets the receive handlers for a newly connected
200 *        USB driver process
201 *
202 * \param st the state (currently NULL)
203 * \param b  the binding of the new connection
204 */
205static errval_t service_connected_cb(void *st, struct usb_manager_binding *b)
206{
207    USB_DEBUG_IDC("service_connected_cb(): Setting handler functions.\n");
208
209    b->rx_vtbl = usb_manager_handle_fn;
210
211    return (SYS_ERR_OK);
212}
213
214/// state variable for the usb manager service
215static volatile uint8_t usb_manager_service_exported = 0;
216
217/**
218 * \brief call back function for the export of the USB manager service
219 *
220 * \param st   the supplied state (currently NULL)
221 * \param err  the outcome of the service export
222 * \param iref the iref which the service is associated with
223 *
224 * NOTE: the usb manager blocks untill the service is exported and the
225 *       and registered with the name service.
226 */
227static void service_exported_cb(void *st, errval_t err, iref_t iref)
228{
229    if (err_is_fail(err)) {
230        USER_PANIC_ERR(err, "service export failed.");
231    }
232
233    err = nameservice_register(USB_MANAGER_SERVICE, iref);
234    if (err_is_fail(err)) {
235        USER_PANIC_ERR(err, "registration with name server failed");
236    }
237
238    usb_manager_service_exported = 1;
239
240}
241
242#if 0
243/**
244 * \brief this function maps the supplied device capability in our memory
245 *
246 * The capability is expected to be in the argcn slot of the rootcn. The
247 * spawning domain has to ensure that the needed capability is at the right
248 * location.
249 *
250 * XXX: Maybe it would be better to move the caps into the inheritcn slot
251 *      to support one device capability per host controller.
252 */
253static uintptr_t map_device_cap(void)
254{
255    errval_t err;
256
257    struct capref dev_cap = {
258        .cnode = cnode_root,
259        .slot = ROOTCN_SLOT_ARGCN
260    };
261
262    struct frame_identity frameid;
263
264    err = invoke_frame_identify(dev_cap, &frameid);
265    if (err_is_fail(err)) {
266        USER_PANIC_ERR(err, "could not identify the frame.\n");
267        return (0);
268    }
269
270    void *ret_addr = NULL;
271    size_t size = (1UL << frameid.bits); /* bytes */
272
273    err = vspace_map_one_frame_attr(&ret_addr, size, dev_cap,
274            VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
275
276    if (err_is_fail(err)) {
277        USER_PANIC_ERR(err, "failed to create a vspace mapping.\n");
278        return (0);
279    }
280
281    return ((uintptr_t) ret_addr);
282}
283#endif
284
285/*
286 * ========================================================================
287 * MAIN
288 * ========================================================================
289 */
290
291/*
292 * \brief   main function of the usb manager
293 *
294 * The USB manager must be called with very the necessary arguments and supplied
295 * with the needed capability to access the device registers.
296 *
297 * On x86:
298 * On ARM:
299 */
300int main(int argc, char *argv[])
301{
302    errval_t err;
303
304    debug_printf("USB Manager started.\n");
305
306    /* starting the usb manager service */
307    err = usb_manager_export(NULL, service_exported_cb, service_connected_cb,
308            get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
309
310    if (err_is_fail(err)) {
311        USER_PANIC_ERR(err, "failed to start the usb manager service.");
312        return (err);
313    }
314
315    /* wait till the service is exported */
316    while (!usb_manager_service_exported) {
317        event_dispatch(get_default_waitset());
318    }
319
320    /* map de device capability into our address space */
321    uintptr_t base; // = map_device_cap();
322    // TODO: Change when new API is ready!
323    err = map_device_register(0x4A064000, 0x1000, &base);
324    assert(err_is_ok(err));
325
326    if (base == 0) {
327        USER_PANIC("failed to map the device capability");
328    }
329
330    /* the default tuple size is 2, since on x86 the interrupts can be routed */
331    uint8_t arg_tuple_size = 2;
332
333    struct monitor_blocking_binding *cl = get_monitor_blocking_binding();
334    assert(cl != NULL);
335    uint32_t arch, platform;
336    err = cl->rpc_tx_vtbl.get_platform(cl, &arch, &platform);
337    assert(err_is_ok(err));
338
339    if (arch == PI_ARCH_ARMV7A && platform == PI_PLATFORM_OMAP44XX) {
340        /* checking the command line parameter count
341         * platform_checkup just derefs argv[2], so we need to check arg count
342         * here */
343        if (argc != 3) {
344            debug_printf("Usage: usb_manager [host-controller offset interrupt]\n");
345        }
346
347        /* ARM / PandaBoard related setup and checks */
348
349        if (platform_checkup(base, argc, argv) != USB_ERR_OK) {
350            USER_PANIC("Pandaboard checkup failed!\n");
351        }
352
353        /*
354         * the argument tuple size must be 3, i.e. the host usb manager expects
355         * [host-controller offset interrupt] as arguments, because the interrupts
356         * are fixed and cannot be allocated as we like.
357         */
358        arg_tuple_size = 3;
359
360        uint32_t irq = strtoul(argv[2], NULL, 10);
361
362        /*
363         * setting up interrupt handler for the EHCI interrupt
364         * XXX: this should be done for each host controller eventually...
365         */
366        err = inthandler_setup_arm(usb_hc_intr_handler, NULL, irq);
367        if (err_is_fail(err)) {
368            DEBUG_ERR(err, "failed to enable interrupt. Step 16.\n");
369        }
370    } else {
371        if (argc == 0 || argc % 2) {
372            debug_printf("Usage: usb_manager [host-controller offset]\n");
373        }
374        uint64_t intr_vector;
375        err = inthandler_setup(usb_hc_intr_handler, NULL,
376                &intr_vector);
377        /* TODO: register interrupt routing.. */
378    }
379
380    usb_error_t uerr = USB_ERR_INVAL;
381
382    /*
383     * start initializing the host controllers supplied by the arguments
384     * XXX: Currently just one
385     */
386    for (uint16_t i = 0; i < argc; i += arg_tuple_size) {
387
388        /* allocate the general host controller */
389        uerr = USB_ERR_INVAL;
390        usb_host_controller_t *hc = malloc(sizeof(*hc));
391        memset(hc, 0, sizeof(*hc));
392
393        uintptr_t controller_base = base + strtoul(argv[i + 1], NULL, 10);
394
395        /* -------------------------------------------------------------------
396         * EHCI Controller
397         * -------------------------------------------------------------------
398         */
399        if (strcmp(argv[i], "ehci") == 0) {
400            uerr = usb_hc_init(hc, USB_EHCI, controller_base);
401        }
402
403        /* -------------------------------------------------------------------
404         * OHCI Controller
405         * -------------------------------------------------------------------
406         */
407        if (strcmp(argv[i], "ohci") == 0) {
408            uerr = usb_hc_init(hc, USB_OHCI, controller_base);
409        }
410
411        /* -------------------------------------------------------------------
412         * UHCI Controller
413         * -------------------------------------------------------------------
414         */
415        if (strcmp(argv[i], "uhci") == 0) {
416            uerr = usb_hc_init(hc, USB_UHCI, controller_base);
417        }
418
419        /* -------------------------------------------------------------------
420         * XHCI Controller
421         * -------------------------------------------------------------------
422         */
423        if (strcmp(argv[i], "xhci") == 0) {
424            uerr = usb_hc_init(hc, USB_XHCI, controller_base);
425        }
426
427        if (uerr != USB_ERR_OK && hc != NULL) {
428            free(hc);
429            continue;
430        }
431    }
432
433    messages_handler_loop();
434}
435