]>
Commit | Line | Data |
---|---|---|
d64399b5 | 1 | /* usb.c - Generic USB interfaces. */ |
2 | /* | |
3 | * GRUB -- GRand Unified Bootloader | |
4 | * Copyright (C) 2008 Free Software Foundation, Inc. | |
5 | * | |
6 | * GRUB is free software: you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation, either version 3 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * GRUB is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include <grub/dl.h> | |
21 | #include <grub/mm.h> | |
22 | #include <grub/usb.h> | |
23 | #include <grub/misc.h> | |
440ab685 | 24 | #include <grub/list.h> |
52d8255d | 25 | #include <grub/term.h> |
d64399b5 | 26 | |
e745cf0c VS |
27 | GRUB_MOD_LICENSE ("GPLv3+"); |
28 | ||
d64399b5 | 29 | static grub_usb_controller_dev_t grub_usb_list; |
7d4e39d6 | 30 | static struct grub_usb_attach_desc *attach_hooks; |
d64399b5 | 31 | |
5fdbaed1 CW |
32 | /* Iterate over all controllers found by the driver. */ |
33 | static int | |
34 | grub_usb_controller_dev_register_iter (grub_usb_controller_t dev, void *data) | |
d64399b5 | 35 | { |
5fdbaed1 | 36 | grub_usb_controller_dev_t usb = data; |
d64399b5 | 37 | |
5fdbaed1 | 38 | dev->dev = usb; |
d64399b5 | 39 | |
5fdbaed1 CW |
40 | /* Enable the ports of the USB Root Hub. */ |
41 | grub_usb_root_hub (dev); | |
d64399b5 | 42 | |
5fdbaed1 CW |
43 | return 0; |
44 | } | |
d64399b5 | 45 | |
5fdbaed1 CW |
46 | void |
47 | grub_usb_controller_dev_register (grub_usb_controller_dev_t usb) | |
48 | { | |
d64399b5 | 49 | usb->next = grub_usb_list; |
50 | grub_usb_list = usb; | |
51 | ||
52 | if (usb->iterate) | |
5fdbaed1 | 53 | usb->iterate (grub_usb_controller_dev_register_iter, usb); |
d64399b5 | 54 | } |
55 | ||
56 | void | |
57 | grub_usb_controller_dev_unregister (grub_usb_controller_dev_t usb) | |
58 | { | |
59 | grub_usb_controller_dev_t *p, q; | |
60 | ||
61 | for (p = &grub_usb_list, q = *p; q; p = &(q->next), q = q->next) | |
62 | if (q == usb) | |
63 | { | |
64 | *p = q->next; | |
65 | break; | |
66 | } | |
67 | } | |
68 | ||
69 | #if 0 | |
5fdbaed1 CW |
70 | /* Context for grub_usb_controller_iterate. */ |
71 | struct grub_usb_controller_iterate_ctx | |
d64399b5 | 72 | { |
5fdbaed1 CW |
73 | grub_usb_controller_iterate_hook_t hook; |
74 | void *hook_data; | |
d64399b5 | 75 | grub_usb_controller_dev_t p; |
5fdbaed1 | 76 | }; |
d64399b5 | 77 | |
5fdbaed1 CW |
78 | /* Helper for grub_usb_controller_iterate. */ |
79 | static int | |
80 | grub_usb_controller_iterate_iter (grub_usb_controller_t dev, void *data) | |
81 | { | |
82 | struct grub_usb_controller_iterate_ctx *ctx = data; | |
d64399b5 | 83 | |
5fdbaed1 CW |
84 | dev->dev = ctx->p; |
85 | if (ctx->hook (dev, ctx->hook_data)) | |
86 | return 1; | |
87 | return 0; | |
88 | } | |
89 | ||
90 | int | |
91 | grub_usb_controller_iterate (grub_usb_controller_iterate_hook_t hook, | |
92 | void *hook_data) | |
93 | { | |
94 | struct grub_usb_controller_iterate_ctx ctx = { | |
95 | .hook = hook, | |
96 | .hook_data = hook_data | |
97 | }; | |
d64399b5 | 98 | |
99 | /* Iterate over all controller drivers. */ | |
5fdbaed1 | 100 | for (ctx.p = grub_usb_list; ctx.p; ctx.p = ctx.p->next) |
d64399b5 | 101 | { |
102 | /* Iterate over the busses of the controllers. XXX: Actually, a | |
103 | hub driver should do this. */ | |
5fdbaed1 | 104 | if (ctx.p->iterate (grub_usb_controller_iterate_iter, &ctx)) |
d64399b5 | 105 | return 1; |
106 | } | |
107 | ||
108 | return 0; | |
109 | } | |
110 | #endif | |
111 | ||
112 | \f | |
113 | grub_usb_err_t | |
114 | grub_usb_clear_halt (grub_usb_device_t dev, int endpoint) | |
115 | { | |
116 | dev->toggle[endpoint] = 0; | |
117 | return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT | |
118 | | GRUB_USB_REQTYPE_STANDARD | |
119 | | GRUB_USB_REQTYPE_TARGET_ENDP), | |
120 | GRUB_USB_REQ_CLEAR_FEATURE, | |
121 | GRUB_USB_FEATURE_ENDP_HALT, | |
122 | endpoint, 0, 0); | |
123 | } | |
124 | ||
125 | grub_usb_err_t | |
126 | grub_usb_set_configuration (grub_usb_device_t dev, int configuration) | |
127 | { | |
5d538b8b | 128 | grub_memset (dev->toggle, 0, sizeof (dev->toggle)); |
d64399b5 | 129 | |
130 | return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT | |
131 | | GRUB_USB_REQTYPE_STANDARD | |
132 | | GRUB_USB_REQTYPE_TARGET_DEV), | |
133 | GRUB_USB_REQ_SET_CONFIGURATION, configuration, | |
134 | 0, 0, NULL); | |
135 | } | |
136 | ||
137 | grub_usb_err_t | |
138 | grub_usb_get_descriptor (grub_usb_device_t dev, | |
139 | grub_uint8_t type, grub_uint8_t index, | |
140 | grub_size_t size, char *data) | |
141 | { | |
142 | return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN | |
143 | | GRUB_USB_REQTYPE_STANDARD | |
144 | | GRUB_USB_REQTYPE_TARGET_DEV), | |
145 | GRUB_USB_REQ_GET_DESCRIPTOR, | |
146 | (type << 8) | index, | |
147 | 0, size, data); | |
148 | } | |
149 | ||
d64399b5 | 150 | grub_usb_err_t |
151 | grub_usb_device_initialize (grub_usb_device_t dev) | |
152 | { | |
153 | struct grub_usb_desc_device *descdev; | |
154 | struct grub_usb_desc_config config; | |
155 | grub_usb_err_t err; | |
156 | int i; | |
157 | ||
778ff324 AN |
158 | /* First we have to read first 8 bytes only and determine |
159 | * max. size of packet */ | |
160 | dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */ | |
161 | err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, | |
12cd7239 | 162 | 0, 8, (char *) &dev->descdev); |
778ff324 AN |
163 | if (err) |
164 | return err; | |
165 | ||
166 | /* Now we have valid value in dev->descdev.maxsize0, | |
167 | * so we can read whole device descriptor */ | |
d64399b5 | 168 | err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE, |
169 | 0, sizeof (struct grub_usb_desc_device), | |
170 | (char *) &dev->descdev); | |
171 | if (err) | |
172 | return err; | |
173 | descdev = &dev->descdev; | |
174 | ||
175 | for (i = 0; i < 8; i++) | |
176 | dev->config[i].descconf = NULL; | |
177 | ||
ff44d107 VS |
178 | if (descdev->configcnt == 0) |
179 | { | |
180 | err = GRUB_USB_ERR_BADDEVICE; | |
181 | goto fail; | |
182 | } | |
183 | ||
d64399b5 | 184 | for (i = 0; i < descdev->configcnt; i++) |
185 | { | |
186 | int pos; | |
187 | int currif; | |
188 | char *data; | |
189 | ||
190 | /* First just read the first 4 bytes of the configuration | |
191 | descriptor, after that it is known how many bytes really have | |
192 | to be read. */ | |
193 | err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, 4, | |
194 | (char *) &config); | |
195 | ||
196 | data = grub_malloc (config.totallen); | |
197 | if (! data) | |
198 | { | |
199 | err = GRUB_USB_ERR_INTERNAL; | |
200 | goto fail; | |
201 | } | |
202 | ||
203 | dev->config[i].descconf = (struct grub_usb_desc_config *) data; | |
204 | err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_CONFIG, i, | |
205 | config.totallen, data); | |
206 | if (err) | |
207 | goto fail; | |
208 | ||
209 | /* Skip the configuration descriptor. */ | |
443a6c4b | 210 | pos = dev->config[i].descconf->length; |
b39f9d20 | 211 | |
d64399b5 | 212 | /* Read all interfaces. */ |
213 | for (currif = 0; currif < dev->config[i].descconf->numif; currif++) | |
214 | { | |
443a6c4b VS |
215 | while (pos < config.totallen |
216 | && ((struct grub_usb_desc *)&data[pos])->type | |
217 | != GRUB_USB_DESCRIPTOR_INTERFACE) | |
218 | pos += ((struct grub_usb_desc *)&data[pos])->length; | |
d64399b5 | 219 | dev->config[i].interf[currif].descif |
220 | = (struct grub_usb_desc_if *) &data[pos]; | |
443a6c4b VS |
221 | pos += dev->config[i].interf[currif].descif->length; |
222 | ||
223 | while (pos < config.totallen | |
224 | && ((struct grub_usb_desc *)&data[pos])->type | |
225 | != GRUB_USB_DESCRIPTOR_ENDPOINT) | |
226 | pos += ((struct grub_usb_desc *)&data[pos])->length; | |
d64399b5 | 227 | |
228 | /* Point to the first endpoint. */ | |
229 | dev->config[i].interf[currif].descendp | |
230 | = (struct grub_usb_desc_endp *) &data[pos]; | |
231 | pos += (sizeof (struct grub_usb_desc_endp) | |
232 | * dev->config[i].interf[currif].descif->endpointcnt); | |
233 | } | |
234 | } | |
235 | ||
1b43dba9 | 236 | return GRUB_USB_ERR_NONE; |
237 | ||
238 | fail: | |
239 | ||
240 | for (i = 0; i < 8; i++) | |
241 | grub_free (dev->config[i].descconf); | |
242 | ||
243 | return err; | |
244 | } | |
245 | ||
246 | void grub_usb_device_attach (grub_usb_device_t dev) | |
247 | { | |
248 | int i; | |
249 | ||
f5db3949 VS |
250 | /* XXX: Just check configuration 0 for now. */ |
251 | for (i = 0; i < dev->config[0].descconf->numif; i++) | |
252 | { | |
253 | struct grub_usb_desc_if *interf; | |
254 | struct grub_usb_attach_desc *desc; | |
255 | ||
256 | interf = dev->config[0].interf[i].descif; | |
257 | ||
258 | grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", | |
259 | i, interf->class, interf->subclass, interf->protocol); | |
260 | ||
261 | if (dev->config[0].interf[i].attached) | |
262 | continue; | |
263 | ||
264 | for (desc = attach_hooks; desc; desc = desc->next) | |
e744219b VS |
265 | if (interf->class == desc->class) |
266 | { | |
267 | grub_boot_time ("Probing USB device driver class %x", desc->class); | |
268 | if (desc->hook (dev, 0, i)) | |
269 | dev->config[0].interf[i].attached = 1; | |
270 | grub_boot_time ("Probed USB device driver class %x", desc->class); | |
271 | } | |
a17b90f0 VS |
272 | |
273 | if (dev->config[0].interf[i].attached) | |
274 | continue; | |
275 | ||
276 | switch (interf->class) | |
277 | { | |
278 | case GRUB_USB_CLASS_MASS_STORAGE: | |
279 | grub_dl_load ("usbms"); | |
cfe1288b | 280 | grub_print_error (); |
a17b90f0 VS |
281 | break; |
282 | case GRUB_USB_CLASS_HID: | |
283 | grub_dl_load ("usb_keyboard"); | |
cfe1288b | 284 | grub_print_error (); |
a17b90f0 | 285 | break; |
34364df6 | 286 | case 0xff: |
dd20a786 VS |
287 | /* FIXME: don't load useless modules. */ |
288 | grub_dl_load ("usbserial_ftdi"); | |
cfe1288b | 289 | grub_print_error (); |
9edd681b | 290 | grub_dl_load ("usbserial_pl2303"); |
cfe1288b | 291 | grub_print_error (); |
66a93674 VS |
292 | grub_dl_load ("usbserial_usbdebug"); |
293 | grub_print_error (); | |
34364df6 | 294 | break; |
a17b90f0 | 295 | } |
f5db3949 | 296 | } |
d64399b5 | 297 | } |
440ab685 | 298 | |
5fdbaed1 CW |
299 | /* Helper for grub_usb_register_attach_hook_class. */ |
300 | static int | |
301 | grub_usb_register_attach_hook_class_iter (grub_usb_device_t usbdev, void *data) | |
440ab685 | 302 | { |
5fdbaed1 CW |
303 | struct grub_usb_attach_desc *desc = data; |
304 | struct grub_usb_desc_device *descdev = &usbdev->descdev; | |
305 | int i; | |
440ab685 | 306 | |
5fdbaed1 CW |
307 | if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 |
308 | || descdev->configcnt == 0) | |
309 | return 0; | |
440ab685 | 310 | |
5fdbaed1 CW |
311 | /* XXX: Just check configuration 0 for now. */ |
312 | for (i = 0; i < usbdev->config[0].descconf->numif; i++) | |
313 | { | |
314 | struct grub_usb_desc_if *interf; | |
440ab685 | 315 | |
5fdbaed1 | 316 | interf = usbdev->config[0].interf[i].descif; |
440ab685 | 317 | |
5fdbaed1 CW |
318 | grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n", |
319 | i, interf->class, interf->subclass, interf->protocol); | |
440ab685 | 320 | |
5fdbaed1 CW |
321 | if (usbdev->config[0].interf[i].attached) |
322 | continue; | |
440ab685 | 323 | |
5fdbaed1 CW |
324 | if (interf->class != desc->class) |
325 | continue; | |
326 | if (desc->hook (usbdev, 0, i)) | |
327 | usbdev->config[0].interf[i].attached = 1; | |
440ab685 VS |
328 | } |
329 | ||
5fdbaed1 CW |
330 | return 0; |
331 | } | |
332 | ||
333 | void | |
334 | grub_usb_register_attach_hook_class (struct grub_usb_attach_desc *desc) | |
335 | { | |
440ab685 VS |
336 | desc->next = attach_hooks; |
337 | attach_hooks = desc; | |
338 | ||
5fdbaed1 | 339 | grub_usb_iterate (grub_usb_register_attach_hook_class_iter, desc); |
440ab685 VS |
340 | } |
341 | ||
342 | void | |
343 | grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc) | |
344 | { | |
87edb894 | 345 | grub_list_remove (GRUB_AS_LIST (desc)); |
440ab685 | 346 | } |
52d8255d VS |
347 | |
348 | ||
349 | GRUB_MOD_INIT(usb) | |
350 | { | |
351 | grub_term_poll_usb = grub_usb_poll_devices; | |
352 | } | |
353 | ||
354 | GRUB_MOD_FINI(usb) | |
355 | { | |
356 | grub_term_poll_usb = NULL; | |
357 | } |