- Implement ProtocolResetComplete
[reactos.git] / drivers / usb / nt4compat / usbdriver / bulkonly.c
1 /**
2 * bulkonly.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 #include "usbdriver.h"
23 #include <ntddscsi.h>
24
25 #define OLYMPUS_CSW( pdev_EXT, staTUS ) \
26 ( ( ( pdev_EXT )->flags & UMSS_DEV_FLAG_OLYMPUS_DEV ) ? ( ( staTUS ) == CSW_OLYMPUS_SIGNATURE ) : FALSE )
27
28 BOOLEAN umss_clear_pass_through_length(PIO_PACKET io_packet);
29
30 NTSTATUS umss_bulkonly_send_sense_req(PUMSS_DEVICE_EXTENSION pdev_ext);
31
32 VOID umss_bulkonly_send_cbw_completion(IN PURB purb, IN PVOID context);
33
34 VOID umss_bulkonly_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext);
35
36 VOID umss_sync_submit_urb_completion(PURB purb, PVOID context);
37
38 NTSTATUS umss_sync_submit_urb(PUMSS_DEVICE_EXTENSION pdev_ext, PURB purb);
39
40 VOID umss_bulkonly_get_status(PUMSS_DEVICE_EXTENSION pdev_ext);
41
42 VOID umss_bulkonly_transfer_data_complete(PURB purb, PVOID reference);
43
44 VOID umss_bulkonly_reset_pipe_and_get_status(IN PVOID reference);
45
46 VOID umss_bulkonly_reset_recovery(IN PVOID reference);
47
48 VOID umss_bulkonly_get_status_complete(IN PURB purb, IN PVOID context);
49
50 /*++
51 Routine Description:
52
53 Handler for all I/O requests using bulk-only protocol.
54
55 Arguments:
56
57 DeviceExtension - Device extension for our FDO.
58
59 Return Value:
60
61 NONE
62
63 --*/
64 NTSTATUS
65 umss_bulkonly_startio(IN PUMSS_DEVICE_EXTENSION pdev_ext, IN PIO_PACKET io_packet)
66 {
67 PCOMMAND_BLOCK_WRAPPER cbw;
68 NTSTATUS status;
69
70 if (pdev_ext == NULL || io_packet == NULL || io_packet->pirp == NULL)
71 return STATUS_INVALID_PARAMETER;
72
73 pdev_ext->retry = TRUE;
74 RtlCopyMemory(&pdev_ext->io_packet, io_packet, sizeof(pdev_ext->io_packet));
75
76 // Setup the command block wrapper for this request
77 cbw = &pdev_ext->cbw;
78 cbw->dCBWSignature = CBW_SIGNATURE;
79 cbw->dCBWTag = 0;
80 cbw->dCBWDataTransferLength = io_packet->data_length;
81 cbw->bmCBWFlags = (io_packet->flags & USB_DIR_IN) ? 0x80 : 0;
82 cbw->bCBWLun = io_packet->lun;
83 cbw->bCBWLength = io_packet->cdb_length;
84 RtlCopyMemory(cbw->CBWCB, io_packet->cdb, sizeof(cbw->CBWCB));
85
86 RtlZeroMemory(&pdev_ext->csw, sizeof(pdev_ext->csw));
87 // Send the command block wrapper to the device.
88 // Calls UMSS_BulkOnlySendCBWComplete when transfer completes.
89 status = umss_bulk_transfer(pdev_ext,
90 USB_DIR_OUT,
91 cbw, sizeof(COMMAND_BLOCK_WRAPPER), umss_bulkonly_send_cbw_completion);
92
93 return status;
94 }
95
96
97 NTSTATUS
98 umss_bulk_transfer(IN PUMSS_DEVICE_EXTENSION pdev_ext,
99 IN UCHAR trans_dir, IN PVOID buf, IN ULONG buf_length, IN PURBCOMPLETION completion)
100 {
101 PURB purb;
102 NTSTATUS status;
103 DEV_HANDLE endp_handle;
104
105 if (pdev_ext == NULL || buf == NULL || completion == NULL)
106 return STATUS_INVALID_PARAMETER;
107
108 if (buf_length > (ULONG) MAX_BULK_TRANSFER_LENGTH)
109 return STATUS_INVALID_PARAMETER;
110
111 purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
112
113 if (purb == NULL)
114 return STATUS_NO_MEMORY;
115
116 if (trans_dir == USB_DIR_OUT)
117 {
118 endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx);
119 }
120 else
121 {
122 endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx);
123 }
124
125 UsbBuildInterruptOrBulkTransferRequest(purb, endp_handle, buf, buf_length, completion, pdev_ext, 0);
126 dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
127 status = usb_submit_urb(pdev_ext->dev_mgr, purb);
128 if (status == STATUS_PENDING)
129 {
130 return status;
131 }
132
133 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
134 if (purb)
135 {
136 usb_free_mem(purb);
137 purb = NULL;
138 }
139 return status;
140 }
141
142 VOID
143 umss_bulkonly_send_cbw_completion(IN PURB purb, IN PVOID context)
144 {
145 NTSTATUS status;
146 PUMSS_DEVICE_EXTENSION pdev_ext;
147
148 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
149
150 status = purb->status;
151 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
152
153 if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
154 {
155 usb_free_mem(purb->data_buffer);
156 purb->data_buffer = NULL;
157 purb->data_length = 0;
158 }
159
160 if (status != STATUS_SUCCESS)
161 {
162 if (usb_halted(status))
163 {
164 //Schedule a work-item to do a reset recovery
165 if (!umss_schedule_workitem
166 ((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
167 {
168 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
169 }
170 }
171 else
172 {
173 // Device failed CBW without stalling, so complete with error
174 umss_complete_request(pdev_ext, status);
175 }
176 }
177 else
178 {
179 // CBW was accepted by device, so start data phase of I/O operation
180 umss_bulkonly_transfer_data(pdev_ext);
181 }
182
183 if (purb)
184 usb_free_mem(purb);
185
186 purb = NULL;
187 return;
188 }
189
190 //can only be called at passive level
191 NTSTATUS
192 umss_sync_submit_urb(PUMSS_DEVICE_EXTENSION pdev_ext, PURB purb)
193 {
194 NTSTATUS status;
195
196 if (pdev_ext == NULL || purb == NULL)
197 return STATUS_INVALID_PARAMETER;
198
199 purb->completion = umss_sync_submit_urb_completion;
200 purb->context = (PVOID) pdev_ext;
201
202 dev_mgr_register_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp, purb);
203 status = usb_submit_urb(pdev_ext->dev_mgr, purb);
204 if (status == STATUS_PENDING)
205 {
206 KeWaitForSingleObject(&pdev_ext->sync_event, Executive, KernelMode, TRUE, NULL);
207 status = purb->status;
208 }
209 else
210 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
211
212 return status;
213 }
214
215 VOID
216 umss_sync_submit_urb_completion(PURB purb, PVOID context)
217 {
218 PUMSS_DEVICE_EXTENSION pdev_ext;
219
220 if (purb == NULL || context == NULL)
221 return;
222
223 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
224 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
225 KeSetEvent(&pdev_ext->sync_event, 0, FALSE);
226 return;
227 }
228
229 /*++
230 Routine Description:
231
232 Worker function used to execute a reset recovery after a stall.
233
234 Arguments:
235
236 Reference - Our device extension.
237
238 Return Value:
239
240 NONE
241
242 --*/
243 VOID
244 umss_bulkonly_reset_recovery(IN PVOID reference)
245 {
246 PUMSS_DEVICE_EXTENSION pdev_ext;
247 URB urb;
248 NTSTATUS status;
249 DEV_HANDLE endp_handle;
250
251 pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
252 usb_dbg_print(DBGLVL_MAXIMUM, ("umss_bulkonly_reset_recovery(): entering...\n"));
253 // Steps for reset recovery:
254 // 1. Send device a mass storage reset command on the default endpoint.
255 // 2. Reset the bulk-in endpoint.
256 // 3. Reset the bulk-out endpoint.
257 // 4. Complete the original I/O request with error.
258
259
260 // Build the mass storage reset command
261 UsbBuildVendorRequest(&urb, pdev_ext->dev_handle | 0xffff, //default pipe
262 NULL, //no extra data
263 0, //no size
264 0x21, //class, interface
265 BULK_ONLY_MASS_STORAGE_RESET,
266 0,
267 pdev_ext->pif_desc->bInterfaceNumber,
268 NULL, //completion
269 NULL, //context
270 0); //reference
271
272 // Send mass storage reset command to device
273 status = umss_sync_submit_urb(pdev_ext, &urb);
274
275 if (status != STATUS_SUCCESS)
276 {
277 usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_recovery(): Reset Recovery failed!\n"));
278 }
279 else
280 {
281 //Reset Bulk-in endpoint
282 endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->in_endp_idx);
283 status = umss_reset_pipe(pdev_ext, endp_handle);
284
285 if (!NT_SUCCESS(status))
286 {
287 usb_dbg_print(DBGLVL_MINIMUM,
288 ("umss_bulkonly_reset_recovery(): Unable to clear Bulk-in endpoint\n"));
289 }
290
291 //Reset Bulk-out endpoint
292 endp_handle = usb_make_handle((pdev_ext->dev_handle >> 16), pdev_ext->if_idx, pdev_ext->out_endp_idx);
293 status = umss_reset_pipe(pdev_ext, endp_handle);
294
295 if (!NT_SUCCESS(status))
296 {
297 usb_dbg_print(DBGLVL_MINIMUM,
298 ("umss_bulkonly_reset_recovery(): Unable to clear Bulk-out endpoint\n"));
299 }
300 }
301 umss_complete_request(pdev_ext, status);
302 }
303
304 /*++
305 Routine Description:
306
307 Schedules a bulk data transfer to/from the device.
308
309 Arguments:
310
311 DeviceExtension - Our FDO's device extension.
312
313 Return Value:
314
315 NONE
316
317 --*/
318 VOID
319 umss_bulkonly_transfer_data(PUMSS_DEVICE_EXTENSION pdev_ext)
320 {
321 PVOID data_buf;
322 ULONG data_buf_length;
323 NTSTATUS status;
324 UCHAR trans_dir = USB_DIR_IN; // FIXME: Initialize this properly!
325
326 // Steps for data phase
327 // 1. Get data buffer fragment (either SGD list, flat buffer, or none).
328 // 2. Schedule data transfer if neccessary.
329 // 3. Repeat 1-2 until all data transferred, or endpoint stalls.
330 // 4. Move to status phase.
331
332 // Get next data buffer element, if any
333 data_buf = umss_get_buffer(pdev_ext, &data_buf_length);
334
335 if (NULL == data_buf)
336 {
337 //No data to transfer, so move to status phase
338 umss_bulkonly_get_status(pdev_ext);
339 }
340 else
341 {
342 // Schedule the data transfer.
343 // Calls umss_bulkonly_transfer_data_complete when transfer completes.
344
345 if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
346 trans_dir = (UCHAR) ((pdev_ext->cbw.bmCBWFlags & USB_DIR_IN) ? USB_DIR_IN : USB_DIR_OUT);
347 else if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
348 trans_dir = USB_DIR_IN;
349
350 if ((status = umss_bulk_transfer(pdev_ext,
351 trans_dir,
352 data_buf,
353 data_buf_length,
354 umss_bulkonly_transfer_data_complete)) != STATUS_PENDING)
355 {
356 umss_complete_request(pdev_ext, status);
357 }
358 }
359 return;
360 }
361
362 /*++
363 Routine Description:
364 Completion handler for bulk data transfer requests.
365 --*/
366 VOID
367 umss_bulkonly_transfer_data_complete(PURB purb, PVOID reference)
368 {
369 NTSTATUS status;
370 PUMSS_DEVICE_EXTENSION pdev_ext;
371 pdev_ext = (PUMSS_DEVICE_EXTENSION) reference;
372
373 status = purb->status;
374
375 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
376
377 if (status != STATUS_SUCCESS)
378 {
379 //
380 // clear the data length if this is a scsi pass through request
381 //
382 umss_clear_pass_through_length(&pdev_ext->io_packet);
383
384 // Device failed data phase
385 // Check if we need to clear stalled pipe
386 if (usb_halted(status))
387 {
388 PULONG buf;
389 buf = usb_alloc_mem(NonPagedPool, 32);
390 buf[0] = (ULONG) pdev_ext;
391 buf[1] = (ULONG) purb->endp_handle;
392
393 usb_dbg_print(DBGLVL_MINIMUM, ("umss_transfer_data_complete(): transfer data error!\n"));
394 if (!umss_schedule_workitem
395 ((PVOID) buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr,
396 pdev_ext->dev_handle))
397 {
398 usb_free_mem(buf), buf = NULL;
399 usb_dbg_print(DBGLVL_MINIMUM,
400 ("umss_transfer_data_complete(): Failed to allocate work-item to reset pipe!\n"));
401 TRAP();
402 umss_complete_request(pdev_ext, status);
403 }
404 }
405 else
406 {
407 //finish our request
408 umss_complete_request(pdev_ext, status);
409 }
410 }
411 else
412 {
413 // Start next part of data phase
414 //umss_bulkonly_transfer_data( pdev_ext );
415 umss_bulkonly_get_status(pdev_ext);
416 //umss_complete_request( pdev_ext, status );
417 }
418
419 usb_free_mem(purb);
420 purb = NULL;
421
422 return; // STATUS_MORE_PROCESSING_REQUIRED;
423 }
424
425
426 VOID
427 umss_bulkonly_reset_pipe_and_get_status(IN PVOID reference)
428 {
429 PUMSS_DEVICE_EXTENSION pdev_ext;
430 DEV_HANDLE endp_handle;
431 NTSTATUS status;
432
433 usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_pipe_and_get_status(): entering...\n"));
434
435 pdev_ext = (PUMSS_DEVICE_EXTENSION) (((PULONG) reference)[0]);
436 endp_handle = (DEV_HANDLE) ((PULONG) reference)[1];
437 usb_free_mem(reference);
438 reference = NULL;
439
440 // Reset the endpoint
441 if ((status = umss_reset_pipe(pdev_ext, endp_handle)) != STATUS_SUCCESS)
442 {
443 usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_reset_pipe_and_get_status(): reset pipe failed\n"));
444 umss_complete_request(pdev_ext, status);
445 return;
446 }
447 // Data phase is finished since the endpoint stalled, so go to status phase
448 usb_dbg_print(DBGLVL_MINIMUM,
449 ("umss_bulkonly_reset_pipe_and_get_status(): reset pipe succeeds, continue to get status\n"));
450 umss_bulkonly_get_status(pdev_ext);
451 }
452
453 VOID
454 umss_bulkonly_get_status(PUMSS_DEVICE_EXTENSION pdev_ext)
455 {
456 NTSTATUS status;
457 // Schedule bulk transfer to get command status wrapper from device
458 status = umss_bulk_transfer(pdev_ext,
459 USB_DIR_IN,
460 &(pdev_ext->csw),
461 sizeof(COMMAND_STATUS_WRAPPER), umss_bulkonly_get_status_complete);
462 if (status != STATUS_PENDING)
463 {
464 umss_complete_request(pdev_ext, status);
465 }
466 }
467
468 /*++
469 Routine Description:
470
471 Completion handler for bulk data transfer request.
472
473 Arguments:
474
475 DeviceObject - Previous device object.
476 Irp - Irp used for sending command.
477 Reference - Our FDO.
478
479 Return Value:
480
481 Driver-originated IRPs always return STATUS_MORE_PROCESSING_REQUIRED.
482
483 --*/
484 VOID
485 umss_bulkonly_get_status_complete(IN PURB purb, IN PVOID context)
486 {
487 NTSTATUS status;
488 PUMSS_DEVICE_EXTENSION pdev_ext;
489 PCOMMAND_STATUS_WRAPPER csw;
490
491 pdev_ext = (PUMSS_DEVICE_EXTENSION) context;
492 status = purb->status;
493
494 dev_mgr_remove_irp(pdev_ext->dev_mgr, pdev_ext->io_packet.pirp);
495
496 csw = &(pdev_ext->csw);
497 if (status == STATUS_SUCCESS &&
498 ((csw->dCSWSignature == CSW_SIGNATURE) || OLYMPUS_CSW(pdev_ext, csw->dCSWSignature)))
499 {
500 if (csw->bCSWStatus == CSW_STATUS_PASSED)
501 {
502 // Received valid CSW with good status
503
504 if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL &&
505 (pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE) && pdev_ext->io_packet.sense_data != NULL)
506 UMSS_FORGE_GOOD_SENSE(pdev_ext->io_packet.sense_data)
507 umss_complete_request(pdev_ext, STATUS_SUCCESS);
508 }
509 else if (csw->bCSWStatus == CSW_STATUS_FAILED)
510 {
511 // start a request sense if necessary
512 if ((pdev_ext->io_packet.flags & IOP_FLAG_REQ_SENSE) &&
513 (pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
514 {
515 if (umss_bulkonly_send_sense_req(pdev_ext) != STATUS_PENDING)
516 {
517 // don't know how to handle.
518 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
519 }
520 else
521 {
522 // fall through to free the urb
523 }
524 }
525 else
526 {
527 // error occurred, reset device
528 if (!umss_schedule_workitem
529 ((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
530 {
531 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
532 }
533 }
534 }
535 else
536 {
537 // error occurred, reset device
538 if (!umss_schedule_workitem
539 ((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
540 {
541 umss_complete_request(pdev_ext, STATUS_IO_DEVICE_ERROR);
542 }
543 }
544 }
545 else if ((status != STATUS_SUCCESS) && (usb_halted(status)) && (pdev_ext->retry))
546 {
547 // Device stalled CSW transfer, retry once before failing
548 PULONG buf;
549 pdev_ext->retry = FALSE;
550
551 buf = usb_alloc_mem(NonPagedPool, 32);
552 buf[0] = (ULONG) pdev_ext;
553 buf[1] = (ULONG) purb->endp_handle;
554
555 if (!umss_schedule_workitem
556 ((PVOID) buf, umss_bulkonly_reset_pipe_and_get_status, pdev_ext->dev_mgr, pdev_ext->dev_handle))
557 {
558 usb_free_mem(buf), buf = NULL;
559 usb_dbg_print(DBGLVL_MINIMUM,
560 ("umss_bulkonly_get_status_complete(): Failed to allocate work-item to reset pipe!\n"));
561 TRAP();
562 umss_complete_request(pdev_ext, status);
563 }
564 }
565 else if (status != STATUS_CANCELLED)
566 {
567 // An error has occured. Reset the device.
568 if (!umss_schedule_workitem
569 ((PVOID) pdev_ext, umss_bulkonly_reset_recovery, pdev_ext->dev_mgr, pdev_ext->dev_handle))
570 {
571 usb_dbg_print(DBGLVL_MINIMUM,
572 ("umss_bulkonly_get_status_complete(): Failed to schedule work-item to reset pipe!\n"));
573 TRAP();
574 umss_complete_request(pdev_ext, status);
575 }
576 }
577 else
578 {
579 // the request is canceled
580 usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_get_status_complete(): the request is canceled\n"));
581 umss_complete_request(pdev_ext, STATUS_CANCELLED);
582 }
583
584 usb_free_mem(purb);
585 purb = NULL;
586
587 return;
588 }
589
590 /*++
591 Routine Description:
592
593 Queries Bulk-Only device for maximum LUN number
594
595 Arguments:
596
597 DeviceExtension - Our device extension.
598
599 Return Value:
600
601 Maximum LUN number for device, or 0 if error occurred.
602
603 --*/
604 CHAR
605 umss_bulkonly_get_maxlun(IN PUMSS_DEVICE_EXTENSION pdev_ext)
606 {
607 PURB purb = NULL;
608 UCHAR max_lun;
609 NTSTATUS status;
610
611 purb = usb_alloc_mem(NonPagedPool, sizeof(URB));
612
613 if (!purb)
614 {
615 usb_dbg_print(DBGLVL_MINIMUM,
616 ("umss_bulkonly_get_maxlun(): Failed to allocate URB, setting max LUN to 0\n"));
617 max_lun = 0;
618 }
619 else
620 {
621 // Build the get max lun command
622 UsbBuildVendorRequest(purb, (pdev_ext->dev_handle | 0xffff), &max_lun, sizeof(max_lun), 0xb1, //class, interface, in
623 BULK_ONLY_GET_MAX_LUN, 0, pdev_ext->pif_desc->bInterfaceNumber, NULL, NULL, 0);
624
625 // Send get max lun command to device
626 status = umss_sync_submit_urb(pdev_ext, purb);
627
628 if (status != STATUS_PENDING)
629 {
630 usb_dbg_print(DBGLVL_MINIMUM,
631 ("umss_bulkonly_get_maxlun(): Get Max LUN command failed, setting max LUN to 0!\n"));
632 max_lun = 0;
633 }
634 }
635
636 if (purb)
637 usb_free_mem(purb);
638
639 usb_dbg_print(DBGLVL_MINIMUM, ("umss_bulkonly_get_maxlun(): Max LUN = %x\n", max_lun));
640
641 return max_lun;
642 }
643
644 PVOID
645 umss_get_buffer(PUMSS_DEVICE_EXTENSION pdev_ext, ULONG * buf_length)
646 {
647 PVOID buffer;
648
649 if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_NORMAL)
650 {
651 buffer = (PVOID) pdev_ext->io_packet.data_buffer;
652 *buf_length = pdev_ext->io_packet.data_length;
653 }
654 else if ((pdev_ext->io_packet.flags & IOP_FLAG_STAGE_MASK) == IOP_FLAG_STAGE_SENSE)
655 {
656 buffer = (PVOID) pdev_ext->io_packet.sense_data;
657 *buf_length = pdev_ext->io_packet.sense_data_length;
658 }
659 else
660 {
661 buffer = NULL;
662 *buf_length = 0;
663 }
664
665 return buffer;
666 }
667
668 BOOLEAN
669 umss_bulkonly_build_sense_cdb(PUMSS_DEVICE_EXTENSION pdev_ext, PCOMMAND_BLOCK_WRAPPER cbw)
670 {
671 UCHAR sub_class;
672 PUCHAR cdb;
673
674 if (pdev_ext == NULL || cbw == NULL)
675 return FALSE;
676
677 cdb = cbw->CBWCB;
678 RtlZeroMemory(cdb, MAX_CDB_LENGTH);
679 sub_class = pdev_ext->pif_desc->bInterfaceSubClass;
680
681 cdb[0] = SFF_REQUEST_SENSEE;
682 cdb[1] = pdev_ext->io_packet.lun << 5;
683 cdb[4] = 18;
684
685 switch (sub_class)
686 {
687 case UMSS_SUBCLASS_SFF8070I:
688 case UMSS_SUBCLASS_UFI:
689 {
690 cbw->bCBWLength = 12;
691 break;
692 }
693 case UMSS_SUBCLASS_RBC:
694 case UMSS_SUBCLASS_SCSI_TCS:
695 {
696 cbw->bCBWLength = 6;
697 break;
698 }
699 default:
700 return FALSE;
701 }
702 return TRUE;
703 }
704
705 NTSTATUS
706 umss_bulkonly_send_sense_req(PUMSS_DEVICE_EXTENSION pdev_ext)
707 {
708 PCOMMAND_BLOCK_WRAPPER cbw;
709 NTSTATUS status;
710
711 if (pdev_ext == NULL || pdev_ext->io_packet.sense_data == NULL
712 || pdev_ext->io_packet.sense_data_length < 18)
713 return STATUS_INVALID_PARAMETER;
714
715 pdev_ext->retry = TRUE;
716
717 cbw = usb_alloc_mem(NonPagedPool, sizeof(COMMAND_BLOCK_WRAPPER));
718 RtlZeroMemory(cbw, sizeof(COMMAND_BLOCK_WRAPPER));
719 pdev_ext->io_packet.flags &= ~IOP_FLAG_STAGE_MASK;
720 pdev_ext->io_packet.flags |= IOP_FLAG_STAGE_SENSE;
721
722 cbw->dCBWSignature = CBW_SIGNATURE;
723 cbw->dCBWTag = 0;
724 cbw->dCBWDataTransferLength = pdev_ext->io_packet.sense_data_length;
725 cbw->bmCBWFlags = USB_DIR_IN;
726 cbw->bCBWLun = 0;
727
728 if (umss_bulkonly_build_sense_cdb(pdev_ext, cbw) == FALSE)
729 {
730 usb_free_mem(cbw);
731 cbw = NULL;
732 return STATUS_UNSUCCESSFUL;
733 }
734
735 status = umss_bulk_transfer(pdev_ext,
736 USB_DIR_OUT,
737 cbw, sizeof(COMMAND_BLOCK_WRAPPER), umss_bulkonly_send_cbw_completion);
738
739 if (status != STATUS_PENDING)
740 {
741 usb_free_mem(cbw);
742 cbw = NULL;
743 }
744 return status;
745 }
746
747 BOOLEAN
748 umss_clear_pass_through_length(PIO_PACKET io_packet)
749 {
750 //
751 // clear the respective data length to meet request of scsi pass through requirement.
752 //
753
754 BOOLEAN sense_stage;
755 ULONG ctrl_code;
756 PIO_STACK_LOCATION cur_stack;
757 PSCSI_PASS_THROUGH pass_through;
758 PSCSI_PASS_THROUGH_DIRECT pass_through_direct;
759
760 if (io_packet == NULL)
761 return FALSE;
762
763 if ((io_packet->flags & IOP_FLAG_SCSI_CTRL_TRANSFER) == 0)
764 return FALSE;
765
766 sense_stage = FALSE;
767 if (io_packet->flags & IOP_FLAG_STAGE_SENSE)
768 sense_stage = TRUE;
769
770 cur_stack = IoGetCurrentIrpStackLocation(io_packet->pirp);
771 ctrl_code = cur_stack->Parameters.DeviceIoControl.IoControlCode;
772 if (ctrl_code == IOCTL_SCSI_PASS_THROUGH_DIRECT)
773 {
774 pass_through_direct = io_packet->pirp->AssociatedIrp.SystemBuffer;
775 if (sense_stage)
776 pass_through_direct->SenseInfoLength = 0;
777 else
778 pass_through_direct->DataTransferLength = 0;
779 }
780 else if (ctrl_code == IOCTL_SCSI_PASS_THROUGH)
781 {
782 pass_through = io_packet->pirp->AssociatedIrp.SystemBuffer;
783 if (sense_stage)
784 pass_through->SenseInfoLength = 0;
785 else
786 pass_through->DataTransferLength = 0;
787 }
788 else
789 return FALSE;
790
791 return TRUE;
792 }