]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/usb.c
greybus: usb: implement hub_control callback
[karo-tx-linux.git] / drivers / staging / greybus / usb.c
1 /*
2  * USB host driver for the Greybus "generic" USB module.
3  *
4  * Copyright 2014 Google Inc.
5  * Copyright 2014 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  *
9  */
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <linux/usb.h>
14 #include <linux/usb/hcd.h>
15
16 #include "greybus.h"
17
18 /* Version of the Greybus USB protocol we support */
19 #define GB_USB_VERSION_MAJOR            0x00
20 #define GB_USB_VERSION_MINOR            0x01
21
22 /* Greybus USB request types */
23 #define GB_USB_TYPE_INVALID             0x00
24 #define GB_USB_TYPE_PROTOCOL_VERSION    0x01
25 #define GB_USB_TYPE_HCD_START           0x02
26 #define GB_USB_TYPE_HCD_STOP            0x03
27 #define GB_USB_TYPE_HUB_CONTROL         0x04
28
29 struct gb_usb_hub_control_request {
30         __le16 typeReq;
31         __le16 wValue;
32         __le16 wIndex;
33         __le16 wLength;
34 };
35
36 struct gb_usb_hub_control_response {
37         u8 buf[0];
38 };
39
40 struct gb_usb_device {
41         struct gb_connection *connection;
42
43         u8 version_major;
44         u8 version_minor;
45 };
46
47 static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd)
48 {
49         return (struct gb_usb_device *)hcd->hcd_priv;
50 }
51
52 static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev)
53 {
54         return container_of((void *)dev, struct usb_hcd, hcd_priv);
55 }
56
57 /* Define get_version() routine */
58 define_get_version(gb_usb_device, USB);
59
60 static void hcd_stop(struct usb_hcd *hcd)
61 {
62         struct gb_usb_device *dev = to_gb_usb_device(hcd);
63         int ret;
64
65         ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP,
66                                 NULL, 0, NULL, 0);
67         if (ret)
68                 dev_err(&dev->connection->dev, "HCD stop failed '%d'\n", ret);
69 }
70
71 static int hcd_start(struct usb_hcd *hcd)
72 {
73         struct usb_bus *bus = hcd_to_bus(hcd);
74         struct gb_usb_device *dev = to_gb_usb_device(hcd);
75         int ret;
76
77         ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START,
78                                 NULL, 0, NULL, 0);
79         if (ret) {
80                 dev_err(&dev->connection->dev, "HCD start failed '%d'\n", ret);
81                 return ret;
82         }
83
84         hcd->state = HC_STATE_RUNNING;
85         if (bus->root_hub)
86                 usb_hcd_resume_root_hub(hcd);
87         return 0;
88 }
89
90 static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
91 {
92         return -ENXIO;
93 }
94
95 static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
96 {
97         return -ENXIO;
98 }
99
100 static int get_frame_number(struct usb_hcd *hcd)
101 {
102         return 0;
103 }
104
105 static int hub_status_data(struct usb_hcd *hcd, char *buf)
106 {
107         return 0;
108 }
109
110 static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
111                        char *buf, u16 wLength)
112 {
113         struct gb_usb_device *dev = to_gb_usb_device(hcd);
114         struct gb_operation *operation;
115         struct gb_usb_hub_control_request *request;
116         struct gb_usb_hub_control_response *response;
117         size_t response_size;
118         int ret;
119
120         /* FIXME: handle unspecified lengths */
121         response_size = sizeof(*response) + wLength;
122
123         operation = gb_operation_create(dev->connection,
124                                         GB_USB_TYPE_HUB_CONTROL,
125                                         sizeof(*request),
126                                         response_size,
127                                         GFP_KERNEL);
128         if (!operation)
129                 return -ENOMEM;
130
131         request = operation->request->payload;
132         request->typeReq = cpu_to_le16(typeReq);
133         request->wValue = cpu_to_le16(wValue);
134         request->wIndex = cpu_to_le16(wIndex);
135         request->wLength = cpu_to_le16(wLength);
136
137         ret = gb_operation_request_send_sync(operation);
138         if (ret)
139                 goto out;
140
141         if (wLength) {
142                 /* Greybus core has verified response size */
143                 response = operation->response->payload;
144                 memcpy(buf, response->buf, wLength);
145         }
146 out:
147         gb_operation_put(operation);
148
149         return ret;
150 }
151
152 static struct hc_driver usb_gb_hc_driver = {
153         .description = "greybus-hcd",
154         .product_desc = "Greybus USB Host Controller",
155         .hcd_priv_size = sizeof(struct gb_usb_device),
156
157         .flags = HCD_USB2,
158
159         .start = hcd_start,
160         .stop = hcd_stop,
161
162         .urb_enqueue = urb_enqueue,
163         .urb_dequeue = urb_dequeue,
164
165         .get_frame_number = get_frame_number,
166         .hub_status_data = hub_status_data,
167         .hub_control = hub_control,
168 };
169
170 static int gb_usb_connection_init(struct gb_connection *connection)
171 {
172         struct device *dev = &connection->dev;
173         struct gb_usb_device *gb_usb_dev;
174         struct usb_hcd *hcd;
175
176         int retval;
177
178         hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev));
179         if (!hcd)
180                 return -ENOMEM;
181
182         gb_usb_dev = to_gb_usb_device(hcd);
183         gb_usb_dev->connection = connection;
184         connection->private = gb_usb_dev;
185
186         /* Check for compatible protocol version */
187         retval = get_version(gb_usb_dev);
188         if (retval)
189                 goto err_put_hcd;
190
191         hcd->has_tt = 1;
192
193         retval = usb_add_hcd(hcd, 0, 0);
194         if (retval)
195                 goto err_put_hcd;
196
197         return 0;
198
199 err_put_hcd:
200         usb_put_hcd(hcd);
201
202         return retval;
203 }
204
205 static void gb_usb_connection_exit(struct gb_connection *connection)
206 {
207         struct gb_usb_device *gb_usb_dev = connection->private;
208         struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev);
209
210         usb_remove_hcd(hcd);
211         usb_put_hcd(hcd);
212 }
213
214 static struct gb_protocol usb_protocol = {
215         .name                   = "usb",
216         .id                     = GREYBUS_PROTOCOL_USB,
217         .major                  = 0,
218         .minor                  = 1,
219         .connection_init        = gb_usb_connection_init,
220         .connection_exit        = gb_usb_connection_exit,
221         .request_recv           = NULL, /* FIXME we have requests!!! */
222 };
223
224 gb_builtin_protocol_driver(usb_protocol);