source: mainline/uspace/lib/usbhost/src/ddf_helpers.c@ d1974966

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d1974966 was a3886be, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

libusbhost: Remove the old root hub initialization code.

  • Property mode set to 100644
File size: 12.3 KB
Line 
1/*
2 * Copyright (c) 2012 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbhost
30 * @{
31 */
32/** @file
33 *
34 */
35
36#include <usb_iface.h>
37#include <usb/classes/classes.h>
38#include <usb/debug.h>
39#include <usb/descriptor.h>
40#include <usb/request.h>
41#include <errno.h>
42#include <str_error.h>
43
44#include "ddf_helpers.h"
45
46#define CTRL_PIPE_MIN_PACKET_SIZE 8
47
48extern usbhc_iface_t hcd_iface;
49
50typedef struct hc_dev {
51 ddf_fun_t *hc_fun;
52 list_t devices;
53} hc_dev_t;
54
55static hc_dev_t *dev_to_hc_dev(ddf_dev_t *dev)
56{
57 return ddf_dev_data_get(dev);
58}
59
60hcd_t *dev_to_hcd(ddf_dev_t *dev)
61{
62 hc_dev_t *hc_dev = dev_to_hc_dev(dev);
63 if (!hc_dev || !hc_dev->hc_fun) {
64 usb_log_error("Invalid HCD device.\n");
65 return NULL;
66 }
67 return ddf_fun_data_get(hc_dev->hc_fun);
68}
69
70typedef struct usb_dev {
71 link_t link;
72 ddf_fun_t *fun;
73 usb_address_t address;
74 usb_speed_t speed;
75 devman_handle_t handle;
76} usb_dev_t;
77
78/** Get USB address assigned to root hub.
79 *
80 * @param[in] fun Root hub function.
81 * @param[out] address Store the address here.
82 * @return Error code.
83 */
84static int rh_get_my_address(ddf_fun_t *fun, usb_address_t *address)
85{
86 assert(fun);
87 if (address != NULL) {
88 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
89 *address = usb_dev->address;
90 }
91 return EOK;
92}
93
94/** Gets handle of the respective hc (this device, hc function).
95 *
96 * @param[in] root_hub_fun Root hub function seeking hc handle.
97 * @param[out] handle Place to write the handle.
98 * @return Error code.
99 */
100static int rh_get_hc_handle(ddf_fun_t *fun, devman_handle_t *handle)
101{
102 assert(fun);
103
104 if (handle != NULL) {
105 usb_dev_t *usb_dev = ddf_fun_data_get(fun);
106 *handle = usb_dev->handle;
107 }
108 return EOK;
109}
110
111/** Root hub USB interface */
112static usb_iface_t usb_iface = {
113 .get_hc_handle = rh_get_hc_handle,
114 .get_my_address = rh_get_my_address,
115};
116/** Standard USB RH options (RH interface) */
117static ddf_dev_ops_t usb_ops = {
118 .interfaces[USB_DEV_IFACE] = &usb_iface,
119};
120
121/** Standard USB HC options (HC interface) */
122static ddf_dev_ops_t hc_ops = {
123 .interfaces[USBHC_DEV_IFACE] = &hcd_iface,
124};
125
126#define GET_DEVICE_DESC(size) \
127{ \
128 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST \
129 | (USB_REQUEST_TYPE_STANDARD << 5) \
130 | USB_REQUEST_RECIPIENT_DEVICE, \
131 .request = USB_DEVREQ_GET_DESCRIPTOR, \
132 .value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8), \
133 .index = uint16_host2usb(0), \
134 .length = uint16_host2usb(size), \
135};
136
137#define SET_ADDRESS(address) \
138{ \
139 .request_type = SETUP_REQUEST_TYPE_HOST_TO_DEVICE \
140 | (USB_REQUEST_TYPE_STANDARD << 5) \
141 | USB_REQUEST_RECIPIENT_DEVICE, \
142 .request = USB_DEVREQ_SET_ADDRESS, \
143 .value = uint16_host2usb(address), \
144 .index = uint16_host2usb(0), \
145 .length = uint16_host2usb(0), \
146};
147
148static const usb_device_request_setup_packet_t set_address = {
149 .request_type = SETUP_REQUEST_TYPE_DEVICE_TO_HOST
150 | (USB_REQUEST_TYPE_STANDARD << 5)
151 | USB_REQUEST_RECIPIENT_DEVICE,
152 .request = USB_DEVREQ_GET_DESCRIPTOR,
153 .value = uint16_host2usb(USB_DESCTYPE_DEVICE << 8),
154 .index = uint16_host2usb(0),
155 .length = uint16_host2usb(CTRL_PIPE_MIN_PACKET_SIZE),
156};
157
158int hcd_ddf_add_usb_device(ddf_dev_t *parent,
159 usb_address_t address, usb_speed_t speed, const char *name,
160 const match_id_list_t *mids)
161{
162 assert(parent);
163 hc_dev_t *hc_dev = dev_to_hc_dev(parent);
164 devman_handle_t hc_handle = ddf_fun_get_handle(hc_dev->hc_fun);
165
166 char default_name[10] = { 0 }; /* usbxyz-ss */
167 if (!name) {
168 snprintf(default_name, sizeof(default_name) - 1,
169 "usb%u-%cs", address, usb_str_speed(speed)[0]);
170 name = default_name;
171 }
172
173 //TODO more checks
174 ddf_fun_t *fun = ddf_fun_create(parent, fun_inner, name);
175 if (!fun)
176 return ENOMEM;
177 usb_dev_t *info = ddf_fun_data_alloc(fun, sizeof(usb_dev_t));
178 if (!info) {
179 ddf_fun_destroy(fun);
180 return ENOMEM;
181 }
182 info->address = address;
183 info->speed = speed;
184 info->handle = hc_handle;
185 info->fun = fun;
186 link_initialize(&info->link);
187
188 ddf_fun_set_ops(fun, &usb_ops);
189 list_foreach(mids->ids, iter) {
190 match_id_t *mid = list_get_instance(iter, match_id_t, link);
191 ddf_fun_add_match_id(fun, mid->id, mid->score);
192 }
193
194 int ret = ddf_fun_bind(fun);
195 if (ret != EOK) {
196 ddf_fun_destroy(fun);
197 return ret;
198 }
199
200 ret = usb_device_manager_bind_address(&dev_to_hcd(parent)->dev_manager,
201 address, ddf_fun_get_handle(fun));
202 if (ret != EOK)
203 usb_log_warning("Failed to bind address: %s.\n",
204 str_error(ret));
205
206 list_append(&info->link, &hc_dev->devices);
207 return EOK;
208}
209
210
211#define ADD_MATCHID_OR_RETURN(list, sc, str, ...) \
212do { \
213 match_id_t *mid = malloc(sizeof(match_id_t)); \
214 if (!mid) { \
215 clean_match_ids(list); \
216 return ENOMEM; \
217 } \
218 char *id = NULL; \
219 int ret = asprintf(&id, str, ##__VA_ARGS__); \
220 if (ret < 0) { \
221 clean_match_ids(list); \
222 free(mid); \
223 return ENOMEM; \
224 } \
225 mid->score = sc; \
226 mid->id = id; \
227 add_match_id(list, mid); \
228} while (0)
229
230
231/* This is a copy of lib/usbdev/src/recognise.c */
232static int create_match_ids(match_id_list_t *l,
233 usb_standard_device_descriptor_t *d)
234{
235 assert(l);
236 assert(d);
237
238 if (d->vendor_id != 0) {
239 /* First, with release number. */
240 ADD_MATCHID_OR_RETURN(l, 100,
241 "usb&vendor=%#04x&product=%#04x&release=%x.%x",
242 d->vendor_id, d->product_id, (d->device_version >> 8),
243 (d->device_version & 0xff));
244
245 /* Next, without release number. */
246 ADD_MATCHID_OR_RETURN(l, 90, "usb&vendor=%#04x&product=%#04x",
247 d->vendor_id, d->product_id);
248 }
249
250 /* Class match id */
251 ADD_MATCHID_OR_RETURN(l, 50, "usb&class=%s",
252 usb_str_class(d->device_class));
253
254 /* As a last resort, try fallback driver. */
255 ADD_MATCHID_OR_RETURN(l, 10, "usb&fallback");
256
257 return EOK;
258
259}
260
261int hcd_ddf_new_device(ddf_dev_t *device)
262{
263 assert(device);
264
265 hcd_t *hcd = dev_to_hcd(device);
266 assert(hcd);
267
268 usb_speed_t speed = USB_SPEED_MAX;
269
270 /* This checks whether the default address is reserved and gets speed */
271 int ret = usb_device_manager_get_info_by_address(&hcd->dev_manager,
272 USB_ADDRESS_DEFAULT, NULL, &speed);
273 if (ret != EOK) {
274 return ret;
275 }
276
277 static const usb_target_t default_target = {{
278 .address = USB_ADDRESS_DEFAULT,
279 .endpoint = 0,
280 }};
281
282 const usb_address_t address = hcd_request_address(hcd, speed);
283 if (address < 0)
284 return address;
285
286 const usb_target_t target = {{
287 .address = address,
288 .endpoint = 0,
289 }};
290
291 /* Add default pipe on default address */
292 ret = hcd_add_ep(hcd,
293 default_target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
294 CTRL_PIPE_MIN_PACKET_SIZE, CTRL_PIPE_MIN_PACKET_SIZE);
295
296 if (ret != EOK) {
297 hcd_release_address(hcd, address);
298 return ret;
299 }
300
301 /* Get max packet size for default pipe */
302 usb_standard_device_descriptor_t desc = { 0 };
303 static const usb_device_request_setup_packet_t get_device_desc_8 =
304 GET_DEVICE_DESC(CTRL_PIPE_MIN_PACKET_SIZE);
305
306 // TODO CALLBACKS
307 ssize_t got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_IN,
308 &desc, CTRL_PIPE_MIN_PACKET_SIZE, *(uint64_t *)&get_device_desc_8,
309 "read first 8 bytes of dev descriptor");
310
311 if (got != CTRL_PIPE_MIN_PACKET_SIZE) {
312 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
313 hcd_release_address(hcd, address);
314 return got < 0 ? got : EOVERFLOW;
315 }
316
317 /* Register EP on the new address */
318 ret = hcd_add_ep(hcd, target, USB_DIRECTION_BOTH, USB_TRANSFER_CONTROL,
319 desc.max_packet_size, desc.max_packet_size);
320 if (ret != EOK) {
321 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
322 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
323 hcd_release_address(hcd, address);
324 return ret;
325 }
326
327 /* Set new address */
328 const usb_device_request_setup_packet_t set_address =
329 SET_ADDRESS(target.address);
330
331 // TODO CALLBACKS
332 got = hcd_send_batch_sync(hcd, default_target, USB_DIRECTION_OUT,
333 NULL, 0, *(uint64_t *)&set_address, "set address");
334
335 hcd_remove_ep(hcd, default_target, USB_DIRECTION_BOTH);
336
337 if (got != 0) {
338 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
339 hcd_release_address(hcd, address);
340 return got;
341 }
342
343 /* Get std device descriptor */
344 static const usb_device_request_setup_packet_t get_device_desc =
345 GET_DEVICE_DESC(sizeof(desc));
346
347 // TODO CALLBACKS
348 got = hcd_send_batch_sync(hcd, target, USB_DIRECTION_IN,
349 &desc, sizeof(desc), *(uint64_t *)&get_device_desc,
350 "read device descriptor");
351 if (ret != EOK) {
352 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
353 hcd_release_address(hcd, target.address);
354 return got < 0 ? got : EOVERFLOW;
355 }
356
357 /* Create match ids from the device descriptor */
358 match_id_list_t mids;
359 init_match_ids(&mids);
360
361 ret = create_match_ids(&mids, &desc);
362 if (ret != EOK) {
363 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
364 hcd_release_address(hcd, target.address);
365 return ret;
366 }
367
368
369
370 /* Register device */
371 ret = hcd_ddf_add_usb_device(device, address, speed, NULL, &mids);
372 clean_match_ids(&mids);
373 if (ret != EOK) {
374 hcd_remove_ep(hcd, target, USB_DIRECTION_BOTH);
375 hcd_release_address(hcd, target.address);
376 return ret;
377 }
378
379 return ret;
380}
381
382/** Announce root hub to the DDF
383 *
384 * @param[in] device Host controller ddf device
385 * @param[in] speed roothub communication speed
386 * @return Error code
387 */
388int hcd_ddf_setup_root_hub(ddf_dev_t *device, usb_speed_t speed)
389{
390 assert(device);
391 hcd_t *hcd = dev_to_hcd(device);
392 assert(hcd);
393
394 hcd_reserve_default_address(hcd, speed);
395 const int ret = hcd_ddf_new_device(device);
396 hcd_release_default_address(hcd);
397 return ret;
398}
399
400/** Initialize hc structures.
401 *
402 * @param[in] device DDF instance of the device to use.
403 *
404 * This function does all the ddf work for hc driver.
405 */
406int hcd_ddf_setup_device(ddf_dev_t *device, ddf_fun_t **hc_fun,
407 usb_speed_t max_speed, size_t bw, bw_count_func_t bw_count)
408{
409 if (device == NULL)
410 return EBADMEM;
411
412 hc_dev_t *instance = ddf_dev_data_alloc(device, sizeof(hc_dev_t));
413 if (instance == NULL) {
414 usb_log_error("Failed to allocate HCD ddf structure.\n");
415 return ENOMEM;
416 }
417 list_initialize(&instance->devices);
418
419#define CHECK_RET_DEST_FREE_RETURN(ret, message...) \
420if (ret != EOK) { \
421 if (instance->hc_fun) { \
422 ddf_fun_destroy(instance->hc_fun); \
423 } \
424 usb_log_error(message); \
425 return ret; \
426} else (void)0
427
428 instance->hc_fun = ddf_fun_create(device, fun_exposed, "hc");
429 int ret = instance->hc_fun ? EOK : ENOMEM;
430 CHECK_RET_DEST_FREE_RETURN(ret,
431 "Failed to create HCD HC function: %s.\n", str_error(ret));
432 ddf_fun_set_ops(instance->hc_fun, &hc_ops);
433 hcd_t *hcd = ddf_fun_data_alloc(instance->hc_fun, sizeof(hcd_t));
434 ret = hcd ? EOK : ENOMEM;
435 CHECK_RET_DEST_FREE_RETURN(ret,
436 "Failed to allocate HCD structure: %s.\n", str_error(ret));
437
438 hcd_init(hcd, max_speed, bw, bw_count);
439
440 ret = ddf_fun_bind(instance->hc_fun);
441 CHECK_RET_DEST_FREE_RETURN(ret,
442 "Failed to bind HCD device function: %s.\n", str_error(ret));
443
444#define CHECK_RET_UNBIND_FREE_RETURN(ret, message...) \
445if (ret != EOK) { \
446 ddf_fun_unbind(instance->hc_fun); \
447 CHECK_RET_DEST_FREE_RETURN(ret, message); \
448} else (void)0
449 ret = ddf_fun_add_to_category(instance->hc_fun, USB_HC_CATEGORY);
450 CHECK_RET_UNBIND_FREE_RETURN(ret,
451 "Failed to add hc to category: %s\n", str_error(ret));
452
453 /* HC should be ok at this point (except it can't do anything) */
454 if (hc_fun)
455 *hc_fun = instance->hc_fun;
456
457 return EOK;
458}
459
460/**
461 * @}
462 */
Note: See TracBrowser for help on using the repository browser.