xref: /haiku/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp (revision cda5b8808fd0262f0fac472f6cfa809f846a83cf)
1 /*
2  * Copyright 2008, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Lotz <mmlr@mlotz.ch>
7  */
8 
9 #include <ByteOrder.h>
10 #include <KernelExport.h>
11 #include <Drivers.h>
12 #include <malloc.h>
13 #include <stdio.h>
14 #include <string.h>
15 #include "usb_disk.h"
16 #include "usb_disk_scsi.h"
17 
18 
19 #define DRIVER_NAME			"usb_disk"
20 #define DEVICE_NAME_BASE	"disk/usb/"
21 #define DEVICE_NAME			DEVICE_NAME_BASE"%ld/%d/raw"
22 
23 
24 //#define TRACE_USB_DISK
25 #ifdef TRACE_USB_DISK
26 #define TRACE(x...)			dprintf(DRIVER_NAME": "x)
27 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME": "x)
28 #else
29 #define TRACE(x...)			/* nothing */
30 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME": "x)
31 #endif
32 
33 
34 int32 api_version = B_CUR_DRIVER_API_VERSION;
35 static usb_module_info *gUSBModule = NULL;
36 static disk_device *gDeviceList = NULL;
37 static uint32 gDeviceCount = 0;
38 static uint32 gLunCount = 0;
39 static benaphore gDeviceListLock;
40 static char **gDeviceNames = NULL;
41 
42 
43 //
44 //#pragma mark - Forward Declarations
45 //
46 
47 
48 static void	usb_disk_callback(void *cookie, status_t status, void *data,
49 				size_t actualLength);
50 
51 status_t	usb_disk_mass_storage_reset(disk_device *device);
52 uint8		usb_disk_get_max_lun(disk_device *device);
53 void		usb_disk_reset_recovery(disk_device *device);
54 status_t	usb_disk_transfer_data(disk_device *device, bool directionIn,
55 				void *data, size_t dataLength);
56 status_t	usb_disk_receive_csw(disk_device *device,
57 				command_status_wrapper *status);
58 status_t	usb_disk_operation(device_lun *lun, uint8 operation,
59 				uint8 opLength, uint32 logicalBlockAddress,
60 				uint16 transferLength, void *data, uint32 *dataLength,
61 				bool directionIn);
62 
63 status_t	usb_disk_request_sense(device_lun *lun);
64 status_t	usb_disk_test_unit_ready(device_lun *lun);
65 status_t	usb_disk_inquiry(device_lun *lun);
66 status_t	usb_disk_reset_capacity(device_lun *lun);
67 status_t	usb_disk_update_capacity(device_lun *lun);
68 status_t	usb_disk_synchronize(device_lun *lun, bool force);
69 
70 
71 //
72 //#pragma mark - Device Allocation Helper Functions
73 //
74 
75 
76 void
77 usb_disk_free_device_and_luns(disk_device *device)
78 {
79 	benaphore_lock(&device->lock);
80 	benaphore_destroy(&device->lock);
81 	delete_sem(device->notify);
82 	for (uint8 i = 0; i < device->lun_count; i++)
83 		free(device->luns[i]);
84 	free(device->luns);
85 	free(device);
86 }
87 
88 
89 //
90 //#pragma mark - Bulk-only Mass Storage Functions
91 //
92 
93 
94 status_t
95 usb_disk_mass_storage_reset(disk_device *device)
96 {
97 	return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
98 		| USB_REQTYPE_CLASS, REQUEST_MASS_STORAGE_RESET, 0x0000,
99 		device->interface, 0, NULL, NULL);
100 }
101 
102 
103 uint8
104 usb_disk_get_max_lun(disk_device *device)
105 {
106 	uint8 result = 0;
107 	size_t actualLength = 0;
108 
109 	// devices that do not support multiple LUNs may stall this request
110 	if (gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_IN
111 		| USB_REQTYPE_CLASS, REQUEST_GET_MAX_LUN, 0x0000, device->interface,
112 		1, &result, &actualLength) != B_OK || actualLength != 1)
113 		return 0;
114 
115 	if (result > MAX_LOGICAL_UNIT_NUMBER) {
116 		// invalid max lun
117 		return 0;
118 	}
119 
120 	return result;
121 }
122 
123 
124 void
125 usb_disk_reset_recovery(disk_device *device)
126 {
127 	usb_disk_mass_storage_reset(device);
128 	gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
129 	gUSBModule->clear_feature(device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
130 }
131 
132 
133 status_t
134 usb_disk_transfer_data(disk_device *device, bool directionIn, void *data,
135 	size_t dataLength)
136 {
137 	status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in
138 		: device->bulk_out, data, dataLength, usb_disk_callback, device);
139 	if (result != B_OK) {
140 		TRACE_ALWAYS("failed to queue data transfer\n");
141 		return result;
142 	}
143 
144 	do {
145 		result = acquire_sem(device->notify);
146 	} while (result == B_INTERRUPTED);
147 	if (result != B_OK) {
148 		TRACE_ALWAYS("acquire_sem failed while waiting for data transfer\n");
149 		return result;
150 	}
151 
152 	return B_OK;
153 }
154 
155 
156 status_t
157 usb_disk_receive_csw(disk_device *device, command_status_wrapper *status)
158 {
159 	status_t result = usb_disk_transfer_data(device, true, status,
160 		sizeof(command_status_wrapper));
161 	if (result != B_OK)
162 		return result;
163 
164 	if (device->status != B_OK
165 		|| device->actual_length != sizeof(command_status_wrapper)) {
166 		// receiving the command status wrapper failed
167 		return B_ERROR;
168 	}
169 
170 	return B_OK;
171 }
172 
173 
174 status_t
175 usb_disk_operation(device_lun *lun, uint8 operation, uint8 opLength,
176 	uint32 logicalBlockAddress, uint16 transferLength, void *data,
177 	uint32 *dataLength, bool directionIn)
178 {
179 	disk_device *device = lun->device;
180 	command_block_wrapper command;
181 	command.signature = CBW_SIGNATURE;
182 	command.tag = device->current_tag++;
183 	command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
184 	command.flags = (directionIn ? CBW_DATA_INPUT : CBW_DATA_OUTPUT);
185 	command.lun = lun->logical_unit_number;
186 	command.command_block_length = opLength;
187 	memset(command.command_block, 0, sizeof(command.command_block));
188 
189 	switch (opLength) {
190 		case 6: {
191 			scsi_command_6 *commandBlock = (scsi_command_6 *)command.command_block;
192 			commandBlock->operation = operation;
193 			commandBlock->lun = lun->logical_unit_number << 5;
194 			commandBlock->allocation_length = (uint8)transferLength;
195 			break;
196 		}
197 
198 		case 10: {
199 			scsi_command_10 *commandBlock = (scsi_command_10 *)command.command_block;
200 			commandBlock->operation = operation;
201 			commandBlock->lun_flags = lun->logical_unit_number << 5;
202 			commandBlock->logical_block_address = htonl(logicalBlockAddress);
203 			commandBlock->transfer_length = htons(transferLength);
204 			break;
205 		}
206 
207 		default:
208 			TRACE_ALWAYS("unsupported operation length %d\n", opLength);
209 			return B_BAD_VALUE;
210 	}
211 
212 	status_t result = usb_disk_transfer_data(device, false, &command,
213 		sizeof(command_block_wrapper));
214 	if (result != B_OK)
215 		return result;
216 
217 	if (device->status != B_OK ||
218 		device->actual_length != sizeof(command_block_wrapper)) {
219 		// sending the command block wrapper failed
220 		TRACE_ALWAYS("sending the command block wrapper failed\n");
221 		usb_disk_reset_recovery(device);
222 		return B_ERROR;
223 	}
224 
225 	if (data != NULL && dataLength != NULL && *dataLength > 0) {
226 		// we have data to transfer in a data stage
227 		result = usb_disk_transfer_data(device, directionIn, data, *dataLength);
228 		if (result != B_OK)
229 			return result;
230 
231 		if (device->status != B_OK || device->actual_length != *dataLength) {
232 			// sending or receiving of the data failed
233 			if (device->status == B_DEV_STALLED) {
234 				TRACE("stall while transfering data\n");
235 				gUSBModule->clear_feature(directionIn ? device->bulk_in
236 					: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
237 			} else {
238 				TRACE_ALWAYS("sending or receiving of the data failed\n");
239 				return B_ERROR;
240 			}
241 		}
242 	}
243 
244 	command_status_wrapper status;
245 	result =  usb_disk_receive_csw(device, &status);
246 	if (result != B_OK && device->status == B_DEV_STALLED) {
247 		// in case of a stall clear the stall and try again
248 		gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
249 		result = usb_disk_receive_csw(device, &status);
250 	}
251 
252 	if (result != B_OK) {
253 		TRACE_ALWAYS("receiving the command status wrapper failed\n");
254 		usb_disk_reset_recovery(device);
255 		return result;
256 	}
257 
258 	if (status.signature != CSW_SIGNATURE || status.tag != command.tag) {
259 		// the command status wrapper is not valid
260 		TRACE_ALWAYS("command status wrapper is not valid\n");
261 		usb_disk_reset_recovery(device);
262 		return B_ERROR;
263 	}
264 
265 	switch (status.status) {
266 		case CSW_STATUS_COMMAND_PASSED:
267 		case CSW_STATUS_COMMAND_FAILED: {
268 			if (dataLength != NULL && status.data_residue <= *dataLength)
269 				*dataLength -= status.data_residue;
270 
271 			if (status.status == CSW_STATUS_COMMAND_PASSED) {
272 				// the operation is complete and has succeeded
273 				result = B_OK;
274 			} else {
275 				// the operation is complete but has failed at the SCSI level
276 				TRACE_ALWAYS("operation 0x%02x failed at the SCSI level\n",
277 					operation);
278 				result = usb_disk_request_sense(lun);
279 				if (result == B_OK)
280 					result = B_ERROR;
281 			}
282 			break;
283 		}
284 
285 		case CSW_STATUS_PHASE_ERROR: {
286 			// a protocol or device error occured
287 			TRACE_ALWAYS("phase error in operation 0x%02x\n", operation);
288 			usb_disk_reset_recovery(device);
289 			if (dataLength != NULL)
290 				*dataLength = 0;
291 			result = B_ERROR;
292 		}
293 	}
294 
295 	return result;
296 }
297 
298 
299 //
300 //#pragma mark - Helper/Convenience Functions
301 //
302 
303 
304 status_t
305 usb_disk_request_sense(device_lun *lun)
306 {
307 	uint32 dataLength = sizeof(scsi_request_sense_6_parameter);
308 	scsi_request_sense_6_parameter parameter;
309 	status_t result = usb_disk_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
310 		dataLength, &parameter, &dataLength, true);
311 	if (result != B_OK) {
312 		TRACE_ALWAYS("getting request sense data failed\n");
313 		return result;
314 	}
315 
316 	if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY) {
317 		TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: 0x%02x;\n",
318 			parameter.sense_key, parameter.additional_sense_code,
319 			parameter.additional_sense_code_qualifier);
320 	}
321 
322 	switch (parameter.sense_key) {
323 		case SCSI_SENSE_KEY_NO_SENSE:
324 		case SCSI_SENSE_KEY_RECOVERED_ERROR:
325 			return B_OK;
326 
327 		case SCSI_SENSE_KEY_NOT_READY:
328 			TRACE("request_sense: device not ready (asc 0x%02x ascq 0x%02x)\n",
329 				parameter.additional_sense_code,
330 				parameter.additional_sense_code_qualifier);
331 			lun->media_present = false;
332 			usb_disk_reset_capacity(lun);
333 			return B_DEV_NO_MEDIA;
334 
335 		case SCSI_SENSE_KEY_HARDWARE_ERROR:
336 		case SCSI_SENSE_KEY_MEDIUM_ERROR:
337 			TRACE_ALWAYS("request_sense: media or hardware error\n");
338 			return B_DEV_UNREADABLE;
339 
340 		case SCSI_SENSE_KEY_ILLEGAL_REQUEST:
341 			TRACE_ALWAYS("request_sense: illegal request\n");
342 			return B_DEV_INVALID_IOCTL;
343 
344 		case SCSI_SENSE_KEY_UNIT_ATTENTION:
345 			TRACE_ALWAYS("request_sense: media changed\n");
346 			lun->media_changed = true;
347 			lun->media_present = true;
348 			return B_DEV_MEDIA_CHANGED;
349 
350 		case SCSI_SENSE_KEY_DATA_PROTECT:
351 			TRACE_ALWAYS("request_sense: write protected\n");
352 			return B_READ_ONLY_DEVICE;
353 
354 		case SCSI_SENSE_KEY_ABORTED_COMMAND:
355 			TRACE_ALWAYS("request_sense: command aborted\n");
356 			return B_CANCELED;
357 	}
358 
359 	return B_ERROR;
360 }
361 
362 
363 status_t
364 usb_disk_test_unit_ready(device_lun *lun)
365 {
366 	return usb_disk_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0, NULL, NULL,
367 		true);
368 }
369 
370 
371 status_t
372 usb_disk_inquiry(device_lun *lun)
373 {
374 	uint32 dataLength = sizeof(scsi_inquiry_6_parameter);
375 	scsi_inquiry_6_parameter parameter;
376 	status_t result = B_ERROR;
377 	for (uint32 tries = 0; tries < 3; tries++) {
378 		result = usb_disk_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
379 			&parameter, &dataLength, true);
380 		if (result == B_OK)
381 			break;
382 	}
383 	if (result != B_OK) {
384 		TRACE_ALWAYS("getting inquiry data failed\n");
385 		lun->device_type = B_DISK;
386 		lun->removable = true;
387 		return result;
388 	}
389 
390 	TRACE("peripherial_device_type  0x%02x\n", parameter.peripherial_device_type);
391 	TRACE("peripherial_qualifier    0x%02x\n", parameter.peripherial_qualifier);
392 	TRACE("removable_medium         %s\n", parameter.removable_medium ? "yes" : "no");
393 	TRACE("version                  0x%02x\n", parameter.version);
394 	TRACE("response_data_format     0x%02x\n", parameter.response_data_format);
395 	TRACE_ALWAYS("vendor_identification    \"%.8s\"\n", parameter.vendor_identification);
396 	TRACE_ALWAYS("product_identification   \"%.16s\"\n", parameter.product_identification);
397 	TRACE_ALWAYS("product_revision_level   \"%.4s\"\n", parameter.product_revision_level);
398 	lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
399 	lun->removable = (parameter.removable_medium == 1);
400 	return B_OK;
401 }
402 
403 
404 status_t
405 usb_disk_reset_capacity(device_lun *lun)
406 {
407 	lun->block_size = 512;
408 	lun->block_count = 0;
409 	return B_OK;
410 }
411 
412 
413 status_t
414 usb_disk_update_capacity(device_lun *lun)
415 {
416 	uint32 dataLength = sizeof(scsi_read_capacity_10_parameter);
417 	scsi_read_capacity_10_parameter parameter;
418 	status_t result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
419 		&parameter, &dataLength, true);
420 	if (result != B_OK) {
421 		TRACE_ALWAYS("failed to update capacity\n");
422 		lun->media_present = false;
423 		lun->media_changed = false;
424 		usb_disk_reset_capacity(lun);
425 		return result;
426 	}
427 
428 	lun->media_present = true;
429 	lun->media_changed = false;
430 	lun->block_size = ntohl(parameter.logical_block_length);
431 	lun->block_count = ntohl(parameter.last_logical_block_address) + 1;
432 	return B_OK;
433 }
434 
435 
436 status_t
437 usb_disk_synchronize(device_lun *lun, bool force)
438 {
439 	if (lun->device->sync_support == 0) {
440 		// this device repeatedly reported an illegal request when syncing
441 		// it obviously does really not support this command...
442 		return B_UNSUPPORTED;
443 	}
444 
445 	if (lun->should_sync || force) {
446 		status_t result = usb_disk_operation(lun, SCSI_SYNCHRONIZE_CACHE_10,
447 			10, 0, 0, NULL, NULL, false);
448 		lun->should_sync = false;
449 
450 		if (result == B_OK)
451 			lun->device->sync_support = SYNC_SUPPORT_RELOAD;
452 		else if (result == B_DEV_INVALID_IOCTL)
453 			lun->device->sync_support--;
454 		return result;
455 	}
456 
457 	return B_OK;
458 }
459 
460 
461 //
462 //#pragma mark - Device Attach/Detach Notifications and Callback
463 //
464 
465 
466 static void
467 usb_disk_callback(void *cookie, status_t status, void *data,
468 	size_t actualLength)
469 {
470 	//TRACE("callback()\n");
471 	disk_device *device = (disk_device *)cookie;
472 	device->status = status;
473 	device->actual_length = actualLength;
474 	release_sem(device->notify);
475 }
476 
477 
478 static status_t
479 usb_disk_device_added(usb_device newDevice, void **cookie)
480 {
481 	TRACE("device_added(0x%08lx)\n", newDevice);
482 	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
483 	device->device = newDevice;
484 	device->removed = false;
485 	device->open_count = 0;
486 	device->interface = 0xff;
487 	device->current_tag = 0;
488 	device->sync_support = SYNC_SUPPORT_RELOAD;
489 	device->luns = NULL;
490 
491 	// scan through the interfaces to find our bulk-only data interface
492 	const usb_configuration_info *configuration = gUSBModule->get_configuration(newDevice);
493 	if (configuration == NULL) {
494 		free(device);
495 		return B_ERROR;
496 	}
497 
498 	for (size_t i = 0; i < configuration->interface_count; i++) {
499 		usb_interface_info *interface = configuration->interface[i].active;
500 		if (interface == NULL)
501 			continue;
502 
503 		if (interface->descr->interface_class == 0x08 /* mass storage */
504 			&& interface->descr->interface_subclass == 0x06 /* SCSI */
505 			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {
506 
507 			bool hasIn = false;
508 			bool hasOut = false;
509 			for (size_t j = 0; j < interface->endpoint_count; j++) {
510 				usb_endpoint_info *endpoint = &interface->endpoint[j];
511 				if (endpoint == NULL
512 					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
513 					continue;
514 
515 				if (!hasIn && (endpoint->descr->endpoint_address
516 					& USB_ENDPOINT_ADDR_DIR_IN)) {
517 					device->bulk_in = endpoint->handle;
518 					hasIn = true;
519 				} else if (!hasOut && (endpoint->descr->endpoint_address
520 					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
521 					device->bulk_out = endpoint->handle;
522 					hasOut = true;
523 				}
524 
525 				if (hasIn && hasOut)
526 					break;
527 			}
528 
529 			if (!(hasIn && hasOut))
530 				continue;
531 
532 			device->interface = interface->descr->interface_number;
533 			break;
534 		}
535 	}
536 
537 	if (device->interface == 0xff) {
538 		TRACE_ALWAYS("no valid bulk-only interface found\n");
539 		free(device);
540 		return B_ERROR;
541 	}
542 
543 	status_t result = benaphore_init(&device->lock, "usb_disk device lock");
544 	if (result < B_OK) {
545 		free(device);
546 		return result;
547 	}
548 
549 	device->notify = create_sem(0, "usb_disk callback notify");
550 	if (device->notify < B_OK) {
551 		benaphore_destroy(&device->lock);
552 		free(device);
553 		return device->notify;
554 	}
555 
556 	device->lun_count = usb_disk_get_max_lun(device) + 1;
557 	device->luns = (device_lun **)malloc(device->lun_count
558 		* sizeof(device_lun *));
559 	for (uint8 i = 0; i < device->lun_count; i++)
560 		device->luns[i] = NULL;
561 
562 	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
563 	for (uint8 i = 0; i < device->lun_count; i++) {
564 		// create the individual luns present on this device
565 		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
566 		if (lun == NULL) {
567 			result = B_NO_MEMORY;
568 			break;
569 		}
570 
571 		device->luns[i] = lun;
572 		lun->device = device;
573 		lun->logical_unit_number = i;
574 		lun->should_sync = false;
575 		lun->media_present = true;
576 		lun->media_changed = true;
577 		usb_disk_reset_capacity(lun);
578 
579 		// initialize this lun
580 		result = usb_disk_inquiry(lun);
581 		for (uint32 tries = 0; tries < 3; tries++) {
582 			status_t ready = usb_disk_test_unit_ready(lun);
583 			if (ready == B_OK || ready == B_DEV_NO_MEDIA)
584 				break;
585 			snooze(10000);
586 		}
587 
588 		if (result != B_OK)
589 			break;
590 	}
591 
592 	if (result != B_OK) {
593 		TRACE_ALWAYS("failed to initialize logical units\n");
594 		usb_disk_free_device_and_luns(device);
595 		return result;
596 	}
597 
598 	benaphore_lock(&gDeviceListLock);
599 	device->link = (void *)gDeviceList;
600 	gDeviceList = device;
601 	uint32 deviceNumber = gDeviceCount++;
602 	gLunCount += device->lun_count;
603 	for (uint8 i = 0; i < device->lun_count; i++)
604 		sprintf(device->luns[i]->name, DEVICE_NAME, deviceNumber, i);
605 	benaphore_unlock(&gDeviceListLock);
606 
607 	TRACE("new device: 0x%08lx\n", (uint32)device);
608 	*cookie = (void *)device;
609 	return B_OK;
610 }
611 
612 
613 static status_t
614 usb_disk_device_removed(void *cookie)
615 {
616 	TRACE("device_removed(0x%08lx)\n", (uint32)cookie);
617 	disk_device *device = (disk_device *)cookie;
618 
619 	benaphore_lock(&gDeviceListLock);
620 	if (gDeviceList == device) {
621 		gDeviceList = (disk_device *)device->link;
622 	} else {
623 		disk_device *element = gDeviceList;
624 		while (element) {
625 			if (element->link == device) {
626 				element->link = device->link;
627 				break;
628 			}
629 
630 			element = (disk_device *)element->link;
631 		}
632 	}
633 	gLunCount -= device->lun_count;
634 	gDeviceCount--;
635 
636 	benaphore_lock(&device->lock);
637 	device->removed = true;
638 	benaphore_unlock(&device->lock);
639 	if (device->open_count == 0)
640 		usb_disk_free_device_and_luns(device);
641 
642 	benaphore_unlock(&gDeviceListLock);
643 	return B_OK;
644 }
645 
646 
647 //
648 //#pragma mark - Partial Buffer Functions
649 //
650 
651 
652 static bool
653 usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
654 	uint32 &blockPosition, uint16 &blockCount)
655 {
656 	blockPosition = (uint32)(position / lun->block_size);
657 	if ((off_t)blockPosition * lun->block_size != position)
658 		return true;
659 
660 	blockCount = (uint16)(length / lun->block_size);
661 	if ((size_t)blockCount * lun->block_size != length)
662 		return true;
663 
664 	return false;
665 }
666 
667 
668 static status_t
669 usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
670 	void *buffer, size_t *length)
671 {
672 	status_t result = usb_disk_operation(lun, SCSI_READ_10, 10, blockPosition,
673 		blockCount, buffer, length, true);
674 	return result;
675 }
676 
677 
678 static status_t
679 usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
680 	void *buffer, size_t *length)
681 {
682 	status_t result = usb_disk_operation(lun, SCSI_WRITE_10, 10, blockPosition,
683 		blockCount, buffer, length, false);
684 	if (result == B_OK)
685 		lun->should_sync = true;
686 	return result;
687 }
688 
689 
690 static status_t
691 usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
692 	void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
693 	uint16 &blockCount)
694 {
695 	blockPosition = (uint32)(position / lun->block_size);
696 	blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
697 		/ lun->block_size) - blockPosition);
698 	size_t blockLength = blockCount * lun->block_size;
699 	blockBuffer = malloc(blockLength);
700 	if (blockBuffer == NULL) {
701 		TRACE_ALWAYS("no memory to allocate partial buffer\n");
702 		return B_NO_MEMORY;
703 	}
704 
705 	status_t result = usb_disk_block_read(lun, blockPosition, blockCount,
706 		blockBuffer, &blockLength);
707 	if (result != B_OK) {
708 		TRACE_ALWAYS("block read failed when filling partial buffer\n");
709 		free(blockBuffer);
710 		return result;
711 	}
712 
713 	off_t offset = position - (blockPosition * lun->block_size);
714 	partialBuffer = (uint8 *)blockBuffer + offset;
715 	return B_OK;
716 }
717 
718 
719 //
720 //#pragma mark - Driver Hooks
721 //
722 
723 
724 static status_t
725 usb_disk_open(const char *name, uint32 flags, void **cookie)
726 {
727 	TRACE("open(%s)\n", name);
728 	if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0)
729 		return B_NAME_NOT_FOUND;
730 
731 	int32 lastPart = 0;
732 	for (int32 i = strlen(name) - 1; i >= 0; i--) {
733 		if (name[i] == '/') {
734 			lastPart = i;
735 			break;
736 		}
737 	}
738 
739 	char rawName[32];
740 	strlcpy(rawName, name, lastPart + 2);
741 	strcat(rawName, "raw");
742 	TRACE("opening raw device %s for %s\n", rawName, name);
743 
744 	benaphore_lock(&gDeviceListLock);
745 	disk_device *device = gDeviceList;
746 	while (device) {
747 		for (uint8 i = 0; i < device->lun_count; i++) {
748 			device_lun *lun = device->luns[i];
749 			if (strncmp(rawName, lun->name, 32) == 0) {
750 				// found the matching device/lun
751 				if (device->removed)
752 					return B_ERROR;
753 
754 				device->open_count++;
755 				*cookie = lun;
756 				benaphore_unlock(&gDeviceListLock);
757 				return B_OK;
758 			}
759 		}
760 
761 		device = (disk_device *)device->link;
762 	}
763 
764 	benaphore_unlock(&gDeviceListLock);
765 	return B_NAME_NOT_FOUND;
766 }
767 
768 
769 static status_t
770 usb_disk_close(void *cookie)
771 {
772 	TRACE("close()\n");
773 	device_lun *lun = (device_lun *)cookie;
774 	usb_disk_synchronize(lun, false);
775 	return B_OK;
776 }
777 
778 
779 static status_t
780 usb_disk_free(void *cookie)
781 {
782 	TRACE("free()\n");
783 	benaphore_lock(&gDeviceListLock);
784 
785 	device_lun *lun = (device_lun *)cookie;
786 	disk_device *device = lun->device;
787 	device->open_count--;
788 	if (device->removed && device->open_count == 0) {
789 		// we can simply free the device here as it has been removed from the
790 		// device list in the device removed notification hook
791 		usb_disk_free_device_and_luns(device);
792 	}
793 
794 	benaphore_unlock(&gDeviceListLock);
795 	return B_OK;
796 }
797 
798 
799 static status_t
800 usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
801 {
802 	device_lun *lun = (device_lun *)cookie;
803 	disk_device *device = lun->device;
804 	benaphore_lock(&device->lock);
805 	if (device->removed) {
806 		benaphore_unlock(&device->lock);
807 		return B_DEV_NOT_READY;
808 	}
809 
810 	status_t result = B_DEV_INVALID_IOCTL;
811 	switch (op) {
812 		case B_GET_MEDIA_STATUS: {
813 			*(status_t *)buffer = usb_disk_test_unit_ready(lun);
814 			TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer);
815 			result = B_OK;
816 			break;
817 		}
818 
819 		case B_GET_GEOMETRY: {
820 			if (lun->media_changed) {
821 				result = usb_disk_update_capacity(lun);
822 				if (result != B_OK)
823 					break;
824 			}
825 
826 			device_geometry *geometry = (device_geometry *)buffer;
827 			geometry->bytes_per_sector = lun->block_size;
828 			geometry->cylinder_count = lun->block_count;
829 			geometry->sectors_per_track = geometry->head_count = 1;
830 			geometry->device_type = lun->device_type;
831 			geometry->removable = lun->removable;
832 			geometry->read_only = (lun->device_type == B_CD);
833 			geometry->write_once = (lun->device_type == B_WORM);
834 			TRACE("B_GET_GEOMETRY: %ld sectors at %ld bytes per sector\n",
835 				geometry->cylinder_count, geometry->bytes_per_sector);
836 			result = B_OK;
837 			break;
838 		}
839 
840 		case B_FLUSH_DRIVE_CACHE:
841 			TRACE("B_FLUSH_DRIVE_CACHE\n");
842 			usb_disk_synchronize(lun, true);
843 			break;
844 
845 		default:
846 			TRACE_ALWAYS("unhandled ioctl %ld\n", op);
847 			break;
848 	}
849 
850 	benaphore_unlock(&device->lock);
851 	return result;
852 }
853 
854 
855 static status_t
856 usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
857 {
858 	if (buffer == NULL || length == NULL)
859 		return B_BAD_VALUE;
860 
861 	TRACE("read(%lld, %ld)\n", position, *length);
862 	device_lun *lun = (device_lun *)cookie;
863 	disk_device *device = lun->device;
864 	benaphore_lock(&device->lock);
865 	if (device->removed) {
866 		*length = 0;
867 		benaphore_unlock(&device->lock);
868 		return B_DEV_NOT_READY;
869 	}
870 
871 	status_t result = B_ERROR;
872 	uint32 blockPosition = 0;
873 	uint16 blockCount = 0;
874 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length,
875 		blockPosition, blockCount);
876 	if (needsPartial) {
877 		void *partialBuffer = NULL;
878 		void *blockBuffer = NULL;
879 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
880 			partialBuffer, blockBuffer, blockPosition, blockCount);
881 		if (result == B_OK) {
882 			memcpy(buffer, partialBuffer, *length);
883 			free(blockBuffer);
884 		}
885 	} else {
886 		result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
887 			length);
888 	}
889 
890 	benaphore_unlock(&device->lock);
891 	if (result == B_OK) {
892 		TRACE("read successful with %ld bytes\n", *length);
893 		return B_OK;
894 	}
895 
896 	*length = 0;
897 	TRACE_ALWAYS("read fails with 0x%08lx\n", result);
898 	return result;
899 }
900 
901 
902 static status_t
903 usb_disk_write(void *cookie, off_t position, const void *buffer,
904 	size_t *length)
905 {
906 	if (buffer == NULL || length == NULL)
907 		return B_BAD_VALUE;
908 
909 	TRACE("write(%lld, %ld)\n", position, *length);
910 	device_lun *lun = (device_lun *)cookie;
911 	disk_device *device = lun->device;
912 	benaphore_lock(&device->lock);
913 	if (device->removed) {
914 		*length = 0;
915 		benaphore_unlock(&device->lock);
916 		return B_DEV_NOT_READY;
917 	}
918 
919 	status_t result = B_ERROR;
920 	uint32 blockPosition = 0;
921 	uint16 blockCount = 0;
922 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position,
923 		*length, blockPosition, blockCount);
924 	if (needsPartial) {
925 		void *partialBuffer = NULL;
926 		void *blockBuffer = NULL;
927 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
928 			partialBuffer, blockBuffer, blockPosition, blockCount);
929 		if (result == B_OK) {
930 			memcpy(partialBuffer, buffer, *length);
931 			size_t blockLength = blockCount * lun->block_size;
932 			result = usb_disk_block_write(lun, blockPosition, blockCount,
933 				blockBuffer, &blockLength);
934 			free(blockBuffer);
935 		}
936 	} else {
937 		result = usb_disk_block_write(lun, blockPosition, blockCount,
938 			(void *)buffer, length);
939 	}
940 
941 	benaphore_unlock(&device->lock);
942 	if (result == B_OK) {
943 		TRACE("write successful with %ld bytes\n", *length);
944 		return B_OK;
945 	}
946 
947 	*length = 0;
948 	TRACE_ALWAYS("write fails with 0x%08lx\n", result);
949 	return result;
950 }
951 
952 
953 //
954 //#pragma mark - Driver Entry Points
955 //
956 
957 
958 status_t
959 init_hardware()
960 {
961 	TRACE("init_hardware()\n");
962 	return B_OK;
963 }
964 
965 
966 status_t
967 init_driver()
968 {
969 	TRACE("init_driver()\n");
970 	static usb_notify_hooks notifyHooks = {
971 		&usb_disk_device_added,
972 		&usb_disk_device_removed
973 	};
974 
975 	static usb_support_descriptor supportedDevices = {
976 		0x08 /* mass storage */, 0x06 /* SCSI */, 0x50 /* bulk only */, 0, 0
977 	};
978 
979 	gDeviceList = NULL;
980 	gDeviceCount = 0;
981 	gLunCount = 0;
982 	status_t result = benaphore_init(&gDeviceListLock, "usb_disk device list lock");
983 	if (result < B_OK) {
984 		TRACE("failed to create device list lock\n");
985 		return result;
986 	}
987 
988 	TRACE("trying module %s\n", B_USB_MODULE_NAME);
989 	result = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule);
990 	if (result < B_OK) {
991 		TRACE_ALWAYS("getting module failed 0x%08lx\n", result);
992 		benaphore_destroy(&gDeviceListLock);
993 		return result;
994 	}
995 
996 	gUSBModule->register_driver(DRIVER_NAME, &supportedDevices, 1, NULL);
997 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
998 	return B_OK;
999 }
1000 
1001 
1002 void
1003 uninit_driver()
1004 {
1005 	TRACE("uninit_driver()\n");
1006 	gUSBModule->uninstall_notify(DRIVER_NAME);
1007 	benaphore_lock(&gDeviceListLock);
1008 
1009 	if (gDeviceNames) {
1010 		for (int32 i = 0; gDeviceNames[i]; i++)
1011 			free(gDeviceNames[i]);
1012 		free(gDeviceNames);
1013 		gDeviceNames = NULL;
1014 	}
1015 
1016 	benaphore_destroy(&gDeviceListLock);
1017 	put_module(B_USB_MODULE_NAME);
1018 }
1019 
1020 
1021 const char **
1022 publish_devices()
1023 {
1024 	TRACE("publish_devices()\n");
1025 	if (gDeviceNames) {
1026 		for (int32 i = 0; gDeviceNames[i]; i++)
1027 			free(gDeviceNames[i]);
1028 		free(gDeviceNames);
1029 		gDeviceNames = NULL;
1030 	}
1031 
1032 	gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1));
1033 	if (gDeviceNames == NULL)
1034 		return NULL;
1035 
1036 	int32 index = 0;
1037 	benaphore_lock(&gDeviceListLock);
1038 	disk_device *device = gDeviceList;
1039 	while (device) {
1040 		for (uint8 i = 0; i < device->lun_count; i++)
1041 			gDeviceNames[index++] = strdup(device->luns[i]->name);
1042 
1043 		device = (disk_device *)device->link;
1044 	}
1045 
1046 	gDeviceNames[index++] = NULL;
1047 	benaphore_unlock(&gDeviceListLock);
1048 	return (const char **)gDeviceNames;
1049 }
1050 
1051 
1052 device_hooks *
1053 find_device(const char *name)
1054 {
1055 	TRACE("find_device()\n");
1056 	static device_hooks hooks = {
1057 		&usb_disk_open,
1058 		&usb_disk_close,
1059 		&usb_disk_free,
1060 		&usb_disk_ioctl,
1061 		&usb_disk_read,
1062 		&usb_disk_write,
1063 		NULL,
1064 		NULL,
1065 		NULL,
1066 		NULL
1067 	};
1068 
1069 	return &hooks;
1070 }
1071