- Create another branch for networking fixes
[reactos.git] / drivers / usb / nt4compat / usbdriver / compdrv.c
1 /**
2 * compdrv.c - USB driver stack project for Windows NT 4.0
3 *
4 * Copyright (c) 2002-2004 Zhiming mypublic99@yahoo.com
5 *
6 * This program/include file is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as published
8 * by the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program/include file is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13 * of 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 along with
17 * this program (in the main directory of the distribution, the file
18 * COPYING); if not, write to the Free Software Foundation,Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 //this driver is part of the dev manager responsible to manage if device
23 #include "usbdriver.h"
24
25 VOID compdev_set_cfg_completion(PURB purb, PVOID context);
26 VOID compdev_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
27 BOOLEAN compdev_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle);
28 BOOLEAN compdev_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
29 BOOLEAN compdev_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle);
30
31 BOOLEAN
32 compdev_driver_init(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
33 {
34 if (dev_mgr == NULL || pdriver == NULL)
35 return FALSE;
36
37 pdriver->driver_desc.flags = USB_DRIVER_FLAG_DEV_CAPABLE;
38 pdriver->driver_desc.vendor_id = 0xffff; // USB Vendor ID
39 pdriver->driver_desc.product_id = 0xffff; // USB Product ID.
40 pdriver->driver_desc.release_num = 0x100; // Release Number of Device
41
42 pdriver->driver_desc.config_val = 0; // Configuration Value
43 pdriver->driver_desc.if_num = 0; // Interface Number
44 pdriver->driver_desc.if_class = 0; // Interface Class
45 pdriver->driver_desc.if_sub_class = 0; // Interface SubClass
46 pdriver->driver_desc.if_protocol = 0; // Interface Protocol
47
48 pdriver->driver_desc.driver_name = "USB composit dev driver"; // Driver name for Name Registry
49 pdriver->driver_desc.dev_class = USB_CLASS_PER_INTERFACE;
50 pdriver->driver_desc.dev_sub_class = 0; // Device Subclass
51 pdriver->driver_desc.dev_protocol = 0; // Protocol Info.
52
53 //we have no extra data sturcture currently
54 pdriver->driver_ext = NULL;
55 pdriver->driver_ext_size = 0;
56
57 pdriver->disp_tbl.version = 1;
58 pdriver->disp_tbl.dev_connect = compdev_connect;
59 pdriver->disp_tbl.dev_disconnect = compdev_disconnect;
60 pdriver->disp_tbl.dev_stop = compdev_stop;
61 pdriver->disp_tbl.dev_reserved = NULL;
62
63 return TRUE;
64 }
65
66 BOOLEAN
67 compdev_driver_destroy(PUSB_DEV_MANAGER dev_mgr, PUSB_DRIVER pdriver)
68 {
69 UNREFERENCED_PARAMETER(dev_mgr);
70 UNREFERENCED_PARAMETER(pdriver);
71 return TRUE;
72 }
73
74 BOOLEAN
75 compdev_connect(PDEV_CONNECT_DATA param, DEV_HANDLE dev_handle)
76 {
77 PURB purb;
78 PUSB_CTRL_SETUP_PACKET psetup;
79 NTSTATUS status;
80 PUCHAR buf;
81 LONG credit, i, j;
82 PUSB_CONFIGURATION_DESC pconfig_desc;
83 PUSB_INTERFACE_DESC pif_desc;
84 PUSB_DEV_MANAGER dev_mgr;
85
86 if (param == NULL || dev_handle == 0)
87 return FALSE;
88
89 dev_mgr = param->dev_mgr;
90
91 // let's set the configuration
92 purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
93 if (purb == NULL)
94 return FALSE;
95
96 buf = usb_alloc_mem(NonPagedPool, 512);
97 if (buf == NULL)
98 {
99 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): can not alloc buf\n"));
100 usb_free_mem(purb);
101 return FALSE;
102 }
103
104 // before we set the configuration, let's search to find if there
105 // exist interfaces we supported
106 psetup = (PUSB_CTRL_SETUP_PACKET) (purb)->setup_packet;
107 urb_init((purb));
108 purb->endp_handle = dev_handle | 0xffff;
109 purb->data_buffer = buf;
110 purb->data_length = 512;
111 purb->completion = NULL; // this is an immediate request, no completion required
112 purb->context = NULL;
113 purb->reference = 0;
114 psetup->bmRequestType = 0x80;
115 psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
116 psetup->wValue = USB_DT_CONFIG << 8;
117 psetup->wIndex = 0;
118 psetup->wLength = 512;
119
120 status = usb_submit_urb(dev_mgr, purb);
121 if (status == STATUS_PENDING)
122 {
123 TRAP();
124 usb_free_mem(buf);
125 usb_free_mem(purb);
126 return FALSE;
127 }
128
129 // let's scan the interfacs for those we recognize
130 pconfig_desc = (PUSB_CONFIGURATION_DESC) buf;
131 if (pconfig_desc->wTotalLength > 512)
132 {
133 usb_free_mem(buf);
134 usb_free_mem(purb);
135 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): error, bad configuration desc\n"));
136 return FALSE;
137 }
138
139 pif_desc = (PUSB_INTERFACE_DESC) & pconfig_desc[1];
140 for(i = 0, credit = 0; i < (LONG) pconfig_desc->bNumInterfaces; i++)
141 {
142 for(j = 0; j < DEVMGR_MAX_DRIVERS; j++)
143 {
144 credit = dev_mgr_score_driver_for_if(dev_mgr, &dev_mgr->driver_list[j], pif_desc);
145 if (credit)
146 break;
147 }
148 if (credit)
149 break;
150
151 if (usb_skip_if_and_altif((PUCHAR *) & pif_desc))
152 break;
153 }
154
155 i = pconfig_desc->bConfigurationValue;
156 usb_free_mem(buf);
157 buf = NULL;
158 if (credit == 0)
159 {
160 usb_free_mem(purb);
161 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): oops..., no supported interface found\n"));
162 return FALSE;
163 }
164
165 //set the configuration
166 urb_init(purb);
167 purb->endp_handle = dev_handle | 0xffff;
168 purb->data_buffer = NULL;
169 purb->data_length = 0;
170 purb->completion = compdev_set_cfg_completion;
171 purb->context = dev_mgr;
172 purb->reference = (ULONG) param->pdriver;
173 psetup->bmRequestType = 0;
174 psetup->bRequest = USB_REQ_SET_CONFIGURATION;
175 psetup->wValue = (USHORT) i;
176 psetup->wIndex = 0;
177 psetup->wLength = 0;
178
179 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_connect(): start config the device, cfgval=%d\n", i));
180 status = usb_submit_urb(dev_mgr, purb);
181
182 if (status != STATUS_PENDING)
183 {
184 usb_free_mem(purb);
185
186 if (status == STATUS_SUCCESS)
187 return TRUE;
188
189 return FALSE;
190 }
191
192 return TRUE;
193 }
194
195 VOID
196 compdev_event_select_if_driver(PUSB_DEV pdev, ULONG event, ULONG context, ULONG param)
197 {
198 PUSB_DEV_MANAGER dev_mgr;
199 DEV_HANDLE dev_handle;
200
201 UNREFERENCED_PARAMETER(param);
202 UNREFERENCED_PARAMETER(context);
203 UNREFERENCED_PARAMETER(event);
204
205 if (pdev == NULL)
206 return;
207
208 //
209 // RtlZeroMemory( &cd, sizeof( cd ) );
210 //
211 dev_mgr = dev_mgr_from_dev(pdev);
212 dev_handle = usb_make_handle(pdev->dev_id, 0, 0);
213 compdev_select_driver(dev_mgr, dev_handle);
214 return;
215 }
216
217 BOOLEAN
218 compdev_post_event_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
219 {
220 PUSB_EVENT pevent;
221 BOOLEAN bret;
222 PUSB_DEV pdev;
223 USE_BASIC_NON_PENDING_IRQL;
224
225 if (dev_mgr == NULL || dev_handle == 0)
226 return FALSE;
227
228 if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
229 return FALSE;
230
231 KeAcquireSpinLockAtDpcLevel(&dev_mgr->event_list_lock);
232 lock_dev(pdev, TRUE);
233
234 if (dev_state(pdev) == USB_DEV_STATE_ZOMB)
235 {
236 bret = FALSE;
237 goto LBL_OUT;
238 }
239
240 pevent = alloc_event(&dev_mgr->event_pool, 1);
241 if (pevent == NULL)
242 {
243 bret = FALSE;
244 goto LBL_OUT;
245 }
246 pevent->flags = USB_EVENT_FLAG_ACTIVE;
247 pevent->event = USB_EVENT_DEFAULT;
248 pevent->pdev = pdev;
249 pevent->context = 0;
250 pevent->param = 0;
251 pevent->pnext = 0; //vertical queue for serialized operation
252 pevent->process_event = compdev_event_select_if_driver;
253 pevent->process_queue = event_list_default_process_queue;
254
255 InsertTailList(&dev_mgr->event_list, &pevent->event_link);
256 KeSetEvent(&dev_mgr->wake_up_event, 0, FALSE); // wake up the dev_mgr_thread
257 bret = TRUE;
258
259 LBL_OUT:
260
261 unlock_dev(pdev, TRUE);
262 KeReleaseSpinLockFromDpcLevel(&dev_mgr->event_list_lock);
263 usb_unlock_dev(pdev);
264 return bret;
265 }
266
267 VOID
268 compdev_set_cfg_completion(PURB purb, PVOID context)
269 {
270 DEV_HANDLE dev_handle;
271 PUSB_DEV_MANAGER dev_mgr;
272 PUSB_DRIVER pdriver;
273 NTSTATUS status;
274 PUSB_DEV pdev;
275 USE_BASIC_NON_PENDING_IRQL;
276
277 if (purb == NULL || context == NULL)
278 return;
279
280 dev_handle = purb->endp_handle & ~0xffff;
281 dev_mgr = (PUSB_DEV_MANAGER) context;
282 pdriver = (PUSB_DRIVER) purb->reference;
283
284 if (purb->status != STATUS_SUCCESS)
285 {
286 usb_free_mem(purb);
287 return;
288 }
289
290 usb_free_mem(purb);
291 purb = NULL;
292
293 // set the dev state
294 status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
295 if (status != STATUS_SUCCESS)
296 {
297 usb_unlock_dev(pdev);
298 return;
299 }
300 // safe to release the pdev ref since we are in urb completion
301 usb_unlock_dev(pdev);
302
303 lock_dev(pdev, TRUE);
304 if (dev_state(pdev) >= USB_DEV_STATE_BEFORE_ZOMB)
305 {
306 unlock_dev(pdev, TRUE);
307 return;
308 }
309
310 if (dev_mgr_set_driver(dev_mgr, dev_handle, pdriver, pdev) == FALSE)
311 return;
312
313 //transit the state to configured
314 pdev->flags &= ~USB_DEV_STATE_MASK;
315 pdev->flags |= USB_DEV_STATE_CONFIGURED;
316 unlock_dev(pdev, TRUE);
317
318 //
319 // we change to use our thread for driver choosing. it will reduce
320 // the race condition when different pnp event comes simultaneously
321 //
322 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_set_cfg_completion(): start select driver for the dev\n"));
323 compdev_post_event_select_driver(dev_mgr, dev_handle);
324
325 return;
326 }
327
328 VOID
329 compdev_select_driver(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
330 {
331 URB urb;
332 LONG i, j, k, credit;
333 ULONG dev_id;
334 PUCHAR buf;
335 NTSTATUS status;
336 PUSB_DRIVER pcand, ptemp_drv;
337
338 PUSB_CTRL_SETUP_PACKET psetup;
339 PUSB_INTERFACE_DESC pif_desc;
340 PUSB_CONFIGURATION_DESC pconfig_desc;
341 PUSB_DEV pdev;
342
343 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): entering...\n"));
344
345 dev_id = dev_handle >> 16;
346
347 buf = usb_alloc_mem(NonPagedPool, 512);
348
349 if (buf == NULL)
350 return;
351
352 // now let's get the descs, one configuration
353 urb_init(&urb);
354 psetup = (PUSB_CTRL_SETUP_PACKET) urb.setup_packet;
355 urb.endp_handle = dev_handle | 0xffff;
356 urb.data_buffer = buf;
357 urb.data_length = 512;
358 urb.completion = NULL; // this is an immediate request, no completion required
359 urb.context = NULL;
360 urb.reference = 0;
361 psetup->bmRequestType = 0x80;
362 psetup->bRequest = USB_REQ_GET_DESCRIPTOR;
363 psetup->wValue = USB_DT_CONFIG << 8;
364 psetup->wIndex = 0;
365 psetup->wLength = 512;
366
367 status = usb_submit_urb(dev_mgr, &urb);
368 if (status == STATUS_PENDING)
369 {
370 TRAP();
371 }
372
373 // let's scan the interfaces for those we recognize
374 pconfig_desc = (PUSB_CONFIGURATION_DESC) buf;
375 if (pconfig_desc->wTotalLength > 512)
376 {
377 usb_free_mem(buf);
378 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): error, bad configuration desc\n"));
379 return;
380 }
381 pif_desc = (PUSB_INTERFACE_DESC) & pconfig_desc[1];
382
383 if (usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev) != STATUS_SUCCESS)
384 {
385 usb_free_mem(buf);
386 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): error, dev does not exist\n"));
387 return;
388 }
389
390 usb_dbg_print(DBGLVL_MAXIMUM, ("compdev_select_driver(): got %d interfaces\n",
391 (LONG)pconfig_desc->bNumInterfaces));
392
393 for(i = 0; i < (LONG) pconfig_desc->bNumInterfaces; i++)
394 {
395 for(j = 0, credit = 0, pcand = NULL; j < DEVMGR_MAX_DRIVERS; j++)
396 {
397 ptemp_drv = &dev_mgr->driver_list[j];
398 k = dev_mgr_score_driver_for_if(dev_mgr, ptemp_drv, pif_desc);
399 if (k > credit)
400 credit = k, pcand = ptemp_drv;
401 }
402
403 if (credit)
404 {
405 // ok, we find one
406 DEV_CONNECT_DATA param;
407
408 if (pcand->disp_tbl.dev_connect)
409 {
410 param.dev_mgr = dev_mgr;
411 param.pdriver = pcand;
412 param.dev_handle = 0;
413 param.if_desc = pif_desc;
414 pcand->disp_tbl.dev_connect(&param, usb_make_handle(dev_id, i, 0));
415 }
416 }
417 if (usb_skip_if_and_altif((PUCHAR *) & pif_desc) == FALSE)
418 {
419 break;
420 }
421 }
422 usb_unlock_dev(pdev);
423
424 if (buf)
425 {
426 usb_free_mem(buf);
427 buf = NULL;
428 }
429 return;
430 }
431
432 BOOLEAN
433 compdev_stop(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
434 {
435 PUSB_DEV pdev;
436 LONG i;
437 ULONG dev_id;
438 PUSB_DRIVER pdrv;
439 NTSTATUS status;
440
441 if (dev_mgr == NULL || dev_handle == 0)
442 return FALSE;
443
444 pdev = NULL;
445 dev_id = dev_handle >> 16;
446 status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
447 if (pdev)
448 {
449 if (pdev->usb_config)
450 {
451 for(i = 0; i < pdev->usb_config->if_count; i++)
452 {
453 if ((pdrv = pdev->usb_config->interf[i].pif_drv))
454 {
455 pdrv->disp_tbl.dev_stop(dev_mgr, usb_make_handle(dev_id, i, 0));
456 }
457 }
458 }
459 }
460 if (status == STATUS_SUCCESS)
461 {
462 usb_unlock_dev(pdev);
463 }
464 return TRUE;
465 }
466
467 BOOLEAN
468 compdev_disconnect(PUSB_DEV_MANAGER dev_mgr, DEV_HANDLE dev_handle)
469 {
470 PUSB_DEV pdev;
471 LONG i;
472 ULONG dev_id;
473 PUSB_DRIVER pdrv;
474 NTSTATUS status;
475
476 if (dev_mgr == NULL || dev_handle == 0)
477 return FALSE;
478
479 pdev = NULL;
480 dev_id = dev_handle >> 16;
481 status = usb_query_and_lock_dev(dev_mgr, dev_handle, &pdev);
482 if (pdev)
483 {
484 if (pdev->usb_config)
485 {
486 for(i = 0; i < pdev->usb_config->if_count; i++)
487 {
488 if ((pdrv = pdev->usb_config->interf[i].pif_drv))
489 {
490 pdrv->disp_tbl.dev_disconnect(dev_mgr, usb_make_handle(dev_id, i, 0));
491 }
492 }
493 }
494 }
495 if (status == STATUS_SUCCESS)
496 {
497 usb_unlock_dev(pdev);
498 }
499 return TRUE;
500 }
501
502 // note:
503 // dev_mgr_set_driver seems to be dangeous since compdev, gendrv and hub and
504 // umss use it to set the driver while there may exist race condition when the
505 // dev_mgr_disconnect_dev is called. If the driver is set and the
506 // disconnect_dev cut in immediately, the stop or disconnect may not function
507 // well. Now hub and compdev's set dev_mgr_set_driver are ok.
508 //
509 // another danger comes from umss's dev irp processing. This may confuse the device
510 // when the disk is being read or written, and at the same time dmgrdisp's dispatch
511 // route irp request to the device a control request.