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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#include <stdio.h>
11#include <string.h>
12
13#include <barrelfish/barrelfish.h>
14#include <barrelfish/nameservice_client.h>
15
16#include <usb/usb.h>
17#include <usb/usb_driver.h>
18#include <usb/usb_device.h>
19
20#include "usb_manager_client.h"
21
22
23/// the usb manager RPC client structure
24struct usb_manager_binding *usb_manager;
25
26/*
27 * -------------------------------------------------------------------------
28 * USB driver service functions
29 * -------------------------------------------------------------------------
30 */
31
32
33/**
34 * \brief this function is called when the client driver receives a detach
35 *        notification
36 */
37void usb_driver_rx_detach_notify(struct usb_driver_binding *b)
38{
39    debug_printf("device detached... exiting driver.");
40    exit(0);
41}
42
43/**
44 * vtable with callbacks for received messages
45 */
46static struct usb_driver_rx_vtbl drv_rx_vtbl = {
47    .device_detach_notify = usb_driver_rx_detach_notify,
48    .transfer_done_notify = usb_driver_rx_done_notify,
49};
50
51static void usb_bind_cb(void *st, errval_t err, struct usb_manager_binding *b);
52
53/**
54 * \brief callback when the service export is successful
55 */
56static void usb_driver_export_cb(void *st, errval_t err, iref_t iref)
57{
58    struct usb_client_st *client_st = st;
59    USB_DEBUG_IDC("libusb: export cb completed\n");
60    if (err_is_fail(err)) {
61        USER_PANIC_ERR(err, "export failed");
62    }
63    /* no need to register with name server */
64    client_st->usb_driver_iref = iref;
65
66    iref_t usb_manager_iref;
67
68    err = nameservice_blocking_lookup(USB_MANAGER_SERVICE, &usb_manager_iref);
69    if (err_is_fail(err)) {
70        USER_PANIC_ERR(err, "USB manager service lookup failed");
71    }
72
73    // usb_bind_cb sets bound
74    err = usb_manager_bind(usb_manager_iref, usb_bind_cb,
75            st /* state for bind_cb */, get_default_waitset(),
76            IDC_BIND_FLAGS_DEFAULT);
77
78    if (err_is_fail(err)) {
79        USER_PANIC_ERR(err, "USB manager binding failed");
80    }
81}
82
83/**
84 * \brief this is the callback function when the USB Manager binds to the client
85 *        driver upon library initialization
86 */
87static errval_t usb_driver_connect_cb(void *st, struct usb_driver_binding *b)
88{
89    struct usb_client_st *client_st = st;
90    USB_DEBUG_IDC("libusb: usb_driver_connect_cb\b");
91
92    client_st->driver_binding = b;
93
94    b->rx_vtbl = drv_rx_vtbl;
95
96    client_st->manager_connected = 1;
97
98    return (SYS_ERR_OK);
99}
100
101
102/**
103 * \brief this is the callback function when the binding with the USB Manager
104 *        is completed
105 */
106static void usb_bind_cb(void *st, errval_t err, struct usb_manager_binding *b)
107{
108    struct usb_client_st *client_st = st;
109    USB_DEBUG_IDC("libusb: bind callback complete\n");
110
111    if (err_is_fail(err)) {
112        USER_PANIC_ERR(err, "USB manager binding failed");
113    }
114
115    usb_manager = b;
116    usb_manager_rpc_client_init(usb_manager);
117    debug_printf("vtbl.connect=%p\n", usb_manager->rpc_tx_vtbl.connect);
118
119    uint32_t ret_status;
120
121    size_t length;
122    uint8_t tmp[2048];
123
124    /* connect with the USB Manager */
125    err = usb_manager->rpc_tx_vtbl.connect(usb_manager, client_st->usb_driver_iref, client_st->init_config,
126            &ret_status, tmp, &length);
127
128    if (((usb_error_t) ret_status) != USB_ERR_OK) {
129        debug_printf("libusb: ERROR connecting to the USB manager\n");
130        client_st->callback(client_st->st, ret_status);
131        return;
132    }
133
134    /* check if we got enough data for an generic descriptor */
135    if (length < sizeof(struct usb_generic_descriptor)) {
136        debug_printf("libusb: ERROR received to less data for the generic "
137                "descriptor\n");
138        client_st->callback(client_st->st, USB_ERR_BAD_BUFSIZE);
139        return;
140    }
141
142    // TODO: This needs to be removed, but that requires changing the connect rpc
143    // to a message...
144    /* wait until the USB Manager connects with us. */
145    while(!(volatile uint8_t) client_st->manager_connected) {
146        err = event_dispatch(get_default_waitset());
147    }
148
149    /*
150     * initialize the devices with the descriptors
151     */
152    usb_device_init(tmp);
153
154    USB_DEBUG_IDC("libusb: driver connected (status=%i)\n", ret_status);
155
156    client_st->callback(client_st->st, err);
157}
158
159/**
160 * \brief   does the initialization of the USB library and the binding to the
161 *          USB manager service
162 *
163 * \param   init_config the configuration to update
164 */
165usb_error_t usb_lib_init(uint16_t init_config, lib_usb_callback cb, void* st)
166{
167    errval_t err;
168
169    debug_printf("libusb: initialization.\n");
170
171    struct usb_client_st *client_st = malloc(sizeof(*client_st));
172    if (!client_st) {
173        return USB_ERR_NOMEM;
174    }
175    client_st->callback = cb;
176    client_st->st = st;
177    client_st->init_config = init_config;
178
179    // driver_export_cb sets exported
180    err = usb_driver_export(client_st, usb_driver_export_cb, usb_driver_connect_cb,
181            get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
182    if (err_is_fail(err)) {
183        USER_PANIC_ERR(err, "could not export the driver interface");
184    }
185
186    return (USB_ERR_OK);
187}
188