xref: /haiku/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp (revision 85892ec52f476b254d75e2bb2e6560e72faa567c)
1 /*
2  * Copyright 2008-2012, 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 
10 #include "usb_disk.h"
11 
12 #include <ByteOrder.h>
13 #include <Drivers.h>
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <fs/devfs.h>
20 
21 #include "scsi_sense.h"
22 #include "usb_disk_scsi.h"
23 
24 
25 #define DRIVER_NAME			"usb_disk"
26 #define DEVICE_NAME_BASE	"disk/usb/"
27 #define DEVICE_NAME			DEVICE_NAME_BASE"%" B_PRIu32 "/%d/raw"
28 
29 
30 //#define TRACE_USB_DISK
31 #ifdef TRACE_USB_DISK
32 #define TRACE(x...)			dprintf(DRIVER_NAME ": " x)
33 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME ": " x)
34 #else
35 #define TRACE(x...)			/* nothing */
36 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME ": " x)
37 #endif
38 
39 
40 int32 api_version = B_CUR_DRIVER_API_VERSION;
41 static usb_module_info *gUSBModule = NULL;
42 static disk_device *gDeviceList = NULL;
43 static uint32 gDeviceCount = 0;
44 static uint32 gLunCount = 0;
45 static mutex gDeviceListLock;
46 static char **gDeviceNames = NULL;
47 
48 static const uint8 kDeviceIcon[] = {
49 	0x6e, 0x63, 0x69, 0x66, 0x0a, 0x04, 0x01, 0x73, 0x05, 0x01, 0x02, 0x01,
50 	0x06, 0x02, 0xb1, 0xf8, 0x5d, 0x3a, 0x2f, 0xbf, 0xbe, 0xdb, 0x67, 0xb6,
51 	0x98, 0x06, 0x4b, 0x22, 0x15, 0x47, 0x13, 0x02, 0x00, 0xed, 0xed, 0xed,
52 	0xff, 0xab, 0xbc, 0xc6, 0x02, 0x01, 0x06, 0x02, 0xb9, 0x82, 0x56, 0x32,
53 	0x7d, 0xfb, 0xb8, 0x06, 0x39, 0xbe, 0xd9, 0xb5, 0x4b, 0x7d, 0x31, 0x4a,
54 	0xa4, 0xe7, 0x00, 0xd1, 0xde, 0xe4, 0xff, 0x7a, 0x9c, 0xae, 0x02, 0x00,
55 	0x16, 0x02, 0x38, 0xe9, 0xaa, 0x3b, 0x7b, 0x1d, 0xbf, 0xb0, 0xa6, 0x3d,
56 	0x16, 0x76, 0x4b, 0x84, 0x81, 0x48, 0x37, 0x36, 0x00, 0x99, 0xff, 0x53,
57 	0x02, 0x00, 0x16, 0x02, 0xba, 0x38, 0x9a, 0xb8, 0xef, 0x79, 0x3e, 0x34,
58 	0x8b, 0xbf, 0x56, 0x52, 0x48, 0x2c, 0x61, 0x4c, 0x4e, 0xec, 0x00, 0x40,
59 	0xff, 0x01, 0x05, 0xff, 0x05, 0x46, 0x02, 0x01, 0x16, 0x02, 0x35, 0xc2,
60 	0x71, 0x3a, 0xf6, 0x84, 0xb9, 0xf3, 0x5b, 0x34, 0x81, 0xa0, 0x49, 0xc0,
61 	0x57, 0x49, 0x6e, 0x51, 0xff, 0xf3, 0x00, 0x52, 0x02, 0x01, 0x06, 0x02,
62 	0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x80, 0x00,
63 	0x49, 0xa0, 0x00, 0x49, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x06,
64 	0xe3, 0x06, 0x0c, 0x06, 0x09, 0xab, 0xaa, 0x03, 0x3e, 0x5e, 0x3c, 0x5f,
65 	0x3e, 0x5e, 0x60, 0x4d, 0x5a, 0x4a, 0x60, 0x47, 0x56, 0x42, 0x50, 0x45,
66 	0x4c, 0x43, 0x2a, 0x54, 0x36, 0x5d, 0x36, 0x5d, 0x3a, 0x60, 0x06, 0x08,
67 	0xfb, 0xea, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x37, 0x59,
68 	0x37, 0x59, 0x38, 0x5a, 0x3b, 0x59, 0x3a, 0x5a, 0x3b, 0x59, 0x58, 0x3c,
69 	0x52, 0x36, 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x06, 0x05,
70 	0xab, 0x03, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x52, 0x36,
71 	0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x0a, 0x05, 0xc2, 0x1c,
72 	0xb8, 0xf9, 0x4f, 0x25, 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x51, 0x39,
73 	0x0a, 0x04, 0xc5, 0x50, 0xbb, 0xc0, 0xc2, 0x1c, 0xb8, 0xf9, 0x4f, 0x25,
74 	0xc9, 0x4c, 0xb7, 0xc4, 0x0a, 0x04, 0x51, 0x39, 0xc5, 0x50, 0xbb, 0xc0,
75 	0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x0a, 0x04, 0x4f, 0x2f, 0x51, 0x31,
76 	0x53, 0x2f, 0x51, 0x2d, 0x06, 0x04, 0xee, 0x4f, 0x35, 0x53, 0x30, 0x51,
77 	0x32, 0x55, 0x2e, 0x58, 0x2c, 0x54, 0x31, 0x56, 0x2f, 0x52, 0x33, 0x06,
78 	0x04, 0xee, 0x31, 0x58, 0x40, 0x47, 0x39, 0x4e, 0x47, 0x40, 0x50, 0x38,
79 	0x41, 0x48, 0x48, 0x41, 0x3a, 0x4f, 0x08, 0x02, 0x3a, 0x40, 0x3e, 0x3c,
80 	0x02, 0x04, 0x3e, 0x3a, 0xbe, 0x48, 0x3a, 0xbf, 0x9f, 0x3a, 0x41, 0x3d,
81 	0x41, 0xbd, 0xe2, 0x41, 0xbf, 0x39, 0x3e, 0x40, 0xbf, 0x9f, 0x40, 0xbe,
82 	0x48, 0x40, 0x3b, 0x3d, 0x3b, 0xbf, 0x39, 0x3b, 0xbd, 0xe2, 0x06, 0x05,
83 	0xbe, 0x02, 0x32, 0x56, 0x36, 0x5a, 0x36, 0x5a, 0x37, 0x5b, 0x3a, 0x5a,
84 	0x39, 0x5b, 0x3a, 0x5a, 0x58, 0x3c, 0x52, 0x36, 0x11, 0x0a, 0x00, 0x01,
85 	0x00, 0x00, 0x0a, 0x01, 0x01, 0x03, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54,
86 	0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0,
87 	0xc5, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x02, 0x3f,
88 	0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6,
89 	0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x03, 0x01, 0x05, 0x02, 0x3f, 0xe9,
90 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d,
91 	0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x01, 0x12, 0x3f, 0xe9, 0x8e,
92 	0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64,
93 	0x46, 0x78, 0x3b, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x04, 0x01, 0x02,
94 	0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
95 	0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x05, 0x01, 0x0b, 0x02,
96 	0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e,
97 	0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f,
98 	0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6,
99 	0x5b, 0x2d, 0x45, 0x43, 0x93, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, 0xe9,
100 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc7, 0x7d,
101 	0x8b, 0x44, 0x36, 0x9a, 0x0a, 0x06, 0x02, 0x07, 0x08, 0x02, 0x3f, 0xe9,
102 	0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d,
103 	0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x09, 0x12, 0x3f, 0x6c, 0x5c,
104 	0xba, 0xea, 0x46, 0x3a, 0xea, 0x46, 0x3f, 0x6c, 0x5c, 0xc5, 0x19, 0x6c,
105 	0x46, 0x6b, 0x36, 0x01, 0x17, 0x8c, 0x22, 0x04, 0x0a, 0x07, 0x01, 0x09,
106 	0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
107 	0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x01, 0x17, 0x88, 0x22, 0x04,
108 	0x0a, 0x08, 0x01, 0x09, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b,
109 	0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x01, 0xed, 0x46, 0x11, 0xa8, 0x01,
110 	0x17, 0x85, 0x22, 0x04, 0x0a, 0x01, 0x01, 0x0a, 0x12, 0x3f, 0xe9, 0x8e,
111 	0xbb, 0x54, 0xe2, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, 0xc6, 0x59, 0xd0,
112 	0x46, 0xdb, 0x8c, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x06, 0x01, 0x0a,
113 	0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9,
114 	0x8e, 0xc6, 0x99, 0xc6, 0x45, 0x5e, 0x3a, 0x0a, 0x01, 0x01, 0x0a, 0x12,
115 	0x3f, 0x21, 0x43, 0xba, 0xaa, 0x52, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43,
116 	0xc7, 0xd2, 0xa7, 0x49, 0x5f, 0xed, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a,
117 	0x09, 0x01, 0x0a, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54,
118 	0xe2, 0x3f, 0xe9, 0x8e, 0xc8, 0xa5, 0xc8, 0x48, 0xeb, 0x05
119 };
120 
121 
122 //
123 //#pragma mark - Forward Declarations
124 //
125 
126 
127 static void	usb_disk_callback(void *cookie, status_t status, void *data,
128 				size_t actualLength);
129 
130 status_t	usb_disk_mass_storage_reset(disk_device *device);
131 uint8		usb_disk_get_max_lun(disk_device *device);
132 void		usb_disk_reset_recovery(disk_device *device);
133 status_t	usb_disk_transfer_data(disk_device *device, bool directionIn,
134 				void *data, size_t dataLength);
135 status_t	usb_disk_receive_csw(disk_device *device,
136 				usb_massbulk_command_status_wrapper *status);
137 status_t	usb_disk_operation(device_lun *lun, uint8 operation,
138 				uint8 opLength, uint32 logicalBlockAddress,
139 				uint16 transferLength, void *data, size_t *dataLength,
140 				bool directionIn, err_act *action = NULL);
141 
142 status_t	usb_disk_request_sense(device_lun *lun, err_act *action);
143 status_t	usb_disk_mode_sense(device_lun *lun);
144 status_t	usb_disk_test_unit_ready(device_lun *lun, err_act *action = NULL);
145 status_t	usb_disk_inquiry(device_lun *lun);
146 status_t	usb_disk_reset_capacity(device_lun *lun);
147 status_t	usb_disk_update_capacity(device_lun *lun);
148 status_t	usb_disk_synchronize(device_lun *lun, bool force);
149 
150 
151 //
152 //#pragma mark - Device Allocation Helper Functions
153 //
154 
155 
156 void
157 usb_disk_free_device_and_luns(disk_device *device)
158 {
159 	mutex_lock(&device->lock);
160 	mutex_destroy(&device->lock);
161 	delete_sem(device->notify);
162 	for (uint8 i = 0; i < device->lun_count; i++)
163 		free(device->luns[i]);
164 	free(device->luns);
165 	free(device);
166 }
167 
168 
169 //
170 //#pragma mark - Bulk-only Mass Storage Functions
171 //
172 
173 
174 status_t
175 usb_disk_mass_storage_reset(disk_device *device)
176 {
177 	return gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_OUT
178 		| USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_MASS_STORAGE_RESET, 0x0000,
179 		device->interface, 0, NULL, NULL);
180 }
181 
182 
183 uint8
184 usb_disk_get_max_lun(disk_device *device)
185 {
186 	uint8 result = 0;
187 	size_t actualLength = 0;
188 
189 	// devices that do not support multiple LUNs may stall this request
190 	if (gUSBModule->send_request(device->device, USB_REQTYPE_INTERFACE_IN
191 		| USB_REQTYPE_CLASS, USB_MASSBULK_REQUEST_GET_MAX_LUN, 0x0000,
192 		device->interface, 1, &result, &actualLength) != B_OK
193 			|| actualLength != 1) {
194 		return 0;
195 	}
196 
197 	if (result > MAX_LOGICAL_UNIT_NUMBER) {
198 		// invalid max lun
199 		return 0;
200 	}
201 
202 	return result;
203 }
204 
205 
206 void
207 usb_disk_reset_recovery(disk_device *device)
208 {
209 	usb_disk_mass_storage_reset(device);
210 	gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
211 	gUSBModule->clear_feature(device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
212 }
213 
214 
215 status_t
216 usb_disk_transfer_data(disk_device *device, bool directionIn, void *data,
217 	size_t dataLength)
218 {
219 	status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in
220 		: device->bulk_out, data, dataLength, usb_disk_callback, device);
221 	if (result != B_OK) {
222 		TRACE_ALWAYS("failed to queue data transfer: %s\n", strerror(result));
223 		return result;
224 	}
225 
226 	do {
227 		result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT,
228 			10 * 1000 * 1000);
229 		if (result == B_TIMED_OUT) {
230 			// Cancel the transfer and collect the sem that should now be
231 			// released through the callback on cancel. Handling of device
232 			// reset is done in usb_disk_operation() when it detects that
233 			// the transfer failed.
234 			gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in
235 				: device->bulk_out);
236 			acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0);
237 		}
238 	} while (result == B_INTERRUPTED);
239 
240 	if (result != B_OK) {
241 		TRACE_ALWAYS("acquire_sem failed while waiting for data transfer: %s\n",
242 			strerror(result));
243 		return result;
244 	}
245 
246 	return B_OK;
247 }
248 
249 
250 status_t
251 usb_disk_receive_csw(disk_device *device,
252 	usb_massbulk_command_status_wrapper *status)
253 {
254 	status_t result = usb_disk_transfer_data(device, true, status,
255 		sizeof(usb_massbulk_command_status_wrapper));
256 	if (result != B_OK)
257 		return result;
258 
259 	if (device->status != B_OK
260 		|| device->actual_length
261 			!= sizeof(usb_massbulk_command_status_wrapper)) {
262 		// receiving the command status wrapper failed
263 		return B_ERROR;
264 	}
265 
266 	return B_OK;
267 }
268 
269 
270 status_t
271 usb_disk_operation(device_lun *lun, uint8 operation, uint8 opLength,
272 	uint32 logicalBlockAddress, uint16 transferLength, void *data,
273 	size_t *dataLength, bool directionIn, err_act *_action)
274 {
275 	TRACE("operation: lun: %u; op: %u; oplen: %u; lba: %" B_PRIu32
276 		"; tlen: %u; data: %p; dlen: %p (%lu); in: %c\n",
277 		lun->logical_unit_number, operation, opLength, logicalBlockAddress,
278 		transferLength, data, dataLength, dataLength ? *dataLength : 0,
279 		directionIn ? 'y' : 'n');
280 
281 	disk_device *device = lun->device;
282 	usb_massbulk_command_block_wrapper command;
283 	command.signature = USB_MASSBULK_CBW_SIGNATURE;
284 	command.tag = device->current_tag++;
285 	command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
286 	command.flags = (directionIn ? USB_MASSBULK_CBW_DATA_INPUT
287 		: USB_MASSBULK_CBW_DATA_OUTPUT);
288 	command.lun = lun->logical_unit_number;
289 	command.command_block_length
290 		= device->is_atapi ? ATAPI_COMMAND_LENGTH : opLength;
291 	memset(command.command_block, 0, sizeof(command.command_block));
292 
293 	switch (opLength) {
294 		case 6:
295 		{
296 			scsi_command_6 *commandBlock
297 				= (scsi_command_6 *)command.command_block;
298 			commandBlock->operation = operation;
299 			commandBlock->lun = lun->logical_unit_number << 5;
300 			commandBlock->allocation_length = (uint8)transferLength;
301 			if (operation == SCSI_MODE_SENSE_6) {
302 				// we hijack the lba argument to transport the desired page
303 				commandBlock->reserved[1] = (uint8)logicalBlockAddress;
304 			}
305 			break;
306 		}
307 
308 		case 10:
309 		{
310 			scsi_command_10 *commandBlock
311 				= (scsi_command_10 *)command.command_block;
312 			commandBlock->operation = operation;
313 			commandBlock->lun_flags = lun->logical_unit_number << 5;
314 			commandBlock->logical_block_address = htonl(logicalBlockAddress);
315 			commandBlock->transfer_length = htons(transferLength);
316 			break;
317 		}
318 
319 		default:
320 			TRACE_ALWAYS("unsupported operation length %d\n", opLength);
321 			return B_BAD_VALUE;
322 	}
323 
324 	status_t result = usb_disk_transfer_data(device, false, &command,
325 		sizeof(usb_massbulk_command_block_wrapper));
326 	if (result != B_OK)
327 		return result;
328 
329 	if (device->status != B_OK ||
330 		device->actual_length != sizeof(usb_massbulk_command_block_wrapper)) {
331 		// sending the command block wrapper failed
332 		TRACE_ALWAYS("sending the command block wrapper failed: %s\n",
333 			strerror(device->status));
334 		usb_disk_reset_recovery(device);
335 		return B_ERROR;
336 	}
337 
338 	size_t transferedData = 0;
339 	if (data != NULL && dataLength != NULL && *dataLength > 0) {
340 		// we have data to transfer in a data stage
341 		result = usb_disk_transfer_data(device, directionIn, data,
342 			*dataLength);
343 		if (result != B_OK)
344 			return result;
345 
346 		transferedData = device->actual_length;
347 		if (device->status != B_OK || transferedData != *dataLength) {
348 			// sending or receiving of the data failed
349 			if (device->status == B_DEV_STALLED) {
350 				TRACE("stall while transfering data\n");
351 				gUSBModule->clear_feature(directionIn ? device->bulk_in
352 					: device->bulk_out, USB_FEATURE_ENDPOINT_HALT);
353 			} else {
354 				TRACE_ALWAYS("sending or receiving of the data failed: %s\n",
355 					strerror(device->status));
356 				usb_disk_reset_recovery(device);
357 				return B_ERROR;
358 			}
359 		}
360 	}
361 
362 	usb_massbulk_command_status_wrapper status;
363 	result =  usb_disk_receive_csw(device, &status);
364 	if (result != B_OK) {
365 		// in case of a stall or error clear the stall and try again
366 		gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT);
367 		result = usb_disk_receive_csw(device, &status);
368 	}
369 
370 	if (result != B_OK) {
371 		TRACE_ALWAYS("receiving the command status wrapper failed: %s\n",
372 			strerror(result));
373 		usb_disk_reset_recovery(device);
374 		return result;
375 	}
376 
377 	if (status.signature != USB_MASSBULK_CSW_SIGNATURE
378 		|| status.tag != command.tag) {
379 		// the command status wrapper is not valid
380 		TRACE_ALWAYS("command status wrapper is not valid: %#" B_PRIx32 "\n",
381 			status.signature);
382 		usb_disk_reset_recovery(device);
383 		return B_ERROR;
384 	}
385 
386 	switch (status.status) {
387 		case USB_MASSBULK_CSW_STATUS_COMMAND_PASSED:
388 		case USB_MASSBULK_CSW_STATUS_COMMAND_FAILED:
389 		{
390 			// The residue from "status.data_residue" is not maintained
391 			// correctly by some devices, so calculate it instead.
392 			uint32 residue = command.data_transfer_length - transferedData;
393 
394 			if (dataLength != NULL) {
395 				*dataLength -= residue;
396 				if (transferedData < *dataLength) {
397 					TRACE_ALWAYS("less data transfered than indicated: %"
398 						B_PRIuSIZE " vs. %" B_PRIuSIZE "\n", transferedData,
399 						*dataLength);
400 					*dataLength = transferedData;
401 				}
402 			}
403 
404 			if (status.status == USB_MASSBULK_CSW_STATUS_COMMAND_PASSED) {
405 				// the operation is complete and has succeeded
406 				return B_OK;
407 			} else {
408 				if (operation == SCSI_REQUEST_SENSE_6)
409 					return B_ERROR;
410 
411 				// the operation is complete but has failed at the SCSI level
412 				if (operation != SCSI_TEST_UNIT_READY_6) {
413 					TRACE_ALWAYS("operation %#" B_PRIx8
414 						" failed at the SCSI level\n", operation);
415 				}
416 
417 				result = usb_disk_request_sense(lun, _action);
418 				return result == B_OK ? B_ERROR : result;
419 			}
420 		}
421 
422 		case USB_MASSBULK_CSW_STATUS_PHASE_ERROR:
423 		{
424 			// a protocol or device error occured
425 			TRACE_ALWAYS("phase error in operation %#" B_PRIx8 "\n", operation);
426 			usb_disk_reset_recovery(device);
427 			return B_ERROR;
428 		}
429 
430 		default:
431 		{
432 			// command status wrapper is not meaningful
433 			TRACE_ALWAYS("command status wrapper has invalid status\n");
434 			usb_disk_reset_recovery(device);
435 			return B_ERROR;
436 		}
437 	}
438 }
439 
440 
441 //
442 //#pragma mark - Helper/Convenience Functions
443 //
444 
445 
446 status_t
447 usb_disk_request_sense(device_lun *lun, err_act *_action)
448 {
449 	size_t dataLength = sizeof(scsi_request_sense_6_parameter);
450 	scsi_request_sense_6_parameter parameter;
451 	status_t result = usb_disk_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
452 		dataLength, &parameter, &dataLength, true);
453 	if (result != B_OK) {
454 		TRACE_ALWAYS("getting request sense data failed: %s\n",
455 			strerror(result));
456 		return result;
457 	}
458 
459 	const char *label = NULL;
460 	err_act action = err_act_fail;
461 	status_t status = B_ERROR;
462 	scsi_get_sense_asc_info((parameter.additional_sense_code << 8)
463 		| parameter.additional_sense_code_qualifier, &label, &action,
464 		&status);
465 
466 	if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY
467 		&& parameter.sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) {
468 		TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: "
469 			"0x%02x; %s\n", parameter.sense_key, parameter.additional_sense_code,
470 			parameter.additional_sense_code_qualifier,
471 			label ? label : "(unknown)");
472 	}
473 
474 	if ((parameter.additional_sense_code == 0
475 			&& parameter.additional_sense_code_qualifier == 0)
476 		|| label == NULL) {
477 		scsi_get_sense_key_info(parameter.sense_key, &label, &action, &status);
478 	}
479 
480 	if (status == B_DEV_MEDIA_CHANGED) {
481 		lun->media_changed = true;
482 		lun->media_present = true;
483 	} else if (parameter.sense_key == SCSI_SENSE_KEY_UNIT_ATTENTION
484 		&& status != B_DEV_NO_MEDIA) {
485 		lun->media_present = true;
486 	} else if (status == B_DEV_NOT_READY) {
487 		lun->media_present = false;
488 		usb_disk_reset_capacity(lun);
489 	}
490 
491 	if (_action != NULL)
492 		*_action = action;
493 
494 	return status;
495 }
496 
497 
498 status_t
499 usb_disk_mode_sense(device_lun *lun)
500 {
501 	size_t dataLength = sizeof(scsi_mode_sense_6_parameter);
502 	scsi_mode_sense_6_parameter parameter;
503 	status_t result = usb_disk_operation(lun, SCSI_MODE_SENSE_6, 6,
504 		SCSI_MODE_PAGE_DEVICE_CONFIGURATION, dataLength, &parameter,
505 		&dataLength, true);
506 	if (result != B_OK) {
507 		TRACE_ALWAYS("getting mode sense data failed: %s\n", strerror(result));
508 		return result;
509 	}
510 
511 	lun->write_protected
512 		= (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT)
513 			!= 0;
514 	TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no");
515 	return B_OK;
516 }
517 
518 
519 status_t
520 usb_disk_test_unit_ready(device_lun *lun, err_act *_action)
521 {
522 	// if unsupported we assume the unit is fixed and therefore always ok
523 	if (!lun->device->tur_supported)
524 		return B_OK;
525 
526 	status_t result;
527 	if (lun->device->is_atapi) {
528 		result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 1,
529 			NULL, NULL, false, _action);
530 	} else {
531 		result = usb_disk_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0,
532 			NULL, NULL, true, _action);
533 	}
534 
535 	if (result == B_DEV_INVALID_IOCTL) {
536 		lun->device->tur_supported = false;
537 		return B_OK;
538 	}
539 
540 	return result;
541 }
542 
543 
544 status_t
545 usb_disk_inquiry(device_lun *lun)
546 {
547 	size_t dataLength = sizeof(scsi_inquiry_6_parameter);
548 	scsi_inquiry_6_parameter parameter;
549 	status_t result = B_ERROR;
550 	err_act action = err_act_ok;
551 	for (uint32 tries = 0; tries < 3; tries++) {
552 		result = usb_disk_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
553 			&parameter, &dataLength, true, &action);
554 		if (result == B_OK || (action != err_act_retry
555 				&& action != err_act_many_retries)) {
556 			break;
557 		}
558 	}
559 	if (result != B_OK) {
560 		TRACE_ALWAYS("getting inquiry data failed: %s\n", strerror(result));
561 		lun->device_type = B_DISK;
562 		lun->removable = true;
563 		return result;
564 	}
565 
566 	TRACE("peripherial_device_type  0x%02x\n",
567 		parameter.peripherial_device_type);
568 	TRACE("peripherial_qualifier    0x%02x\n",
569 		parameter.peripherial_qualifier);
570 	TRACE("removable_medium         %s\n",
571 		parameter.removable_medium ? "yes" : "no");
572 	TRACE("version                  0x%02x\n", parameter.version);
573 	TRACE("response_data_format     0x%02x\n", parameter.response_data_format);
574 	TRACE_ALWAYS("vendor_identification    \"%.8s\"\n",
575 		parameter.vendor_identification);
576 	TRACE_ALWAYS("product_identification   \"%.16s\"\n",
577 		parameter.product_identification);
578 	TRACE_ALWAYS("product_revision_level   \"%.4s\"\n",
579 		parameter.product_revision_level);
580 
581 	memcpy(lun->vendor_name, parameter.vendor_identification,
582 		MIN(sizeof(lun->vendor_name), sizeof(parameter.vendor_identification)));
583 	memcpy(lun->product_name, parameter.product_identification,
584 		MIN(sizeof(lun->product_name),
585 			sizeof(parameter.product_identification)));
586 	memcpy(lun->product_revision, parameter.product_revision_level,
587 		MIN(sizeof(lun->product_revision),
588 			sizeof(parameter.product_revision_level)));
589 
590 	lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
591 	lun->removable = (parameter.removable_medium == 1);
592 	return B_OK;
593 }
594 
595 
596 status_t
597 usb_disk_reset_capacity(device_lun *lun)
598 {
599 	lun->block_size = 512;
600 	lun->block_count = 0;
601 	return B_OK;
602 }
603 
604 
605 status_t
606 usb_disk_update_capacity(device_lun *lun)
607 {
608 	size_t dataLength = sizeof(scsi_read_capacity_10_parameter);
609 	scsi_read_capacity_10_parameter parameter;
610 	status_t result = B_ERROR;
611 	err_act action = err_act_ok;
612 
613 	// Retry reading the capacity up to three times. The first try might only
614 	// yield a unit attention telling us that the device or media status
615 	// changed, which is more or less expected if it is the first operation
616 	// on the device or the device only clears the unit atention for capacity
617 	// reads.
618 	for (int32 i = 0; i < 3; i++) {
619 		result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
620 			&parameter, &dataLength, true, &action);
621 		if (result == B_OK || (action != err_act_retry
622 				&& action != err_act_many_retries)) {
623 			break;
624 		}
625 	}
626 
627 	if (result != B_OK) {
628 		TRACE_ALWAYS("failed to update capacity: %s\n", strerror(result));
629 		lun->media_present = false;
630 		lun->media_changed = false;
631 		usb_disk_reset_capacity(lun);
632 		return result;
633 	}
634 
635 	lun->media_present = true;
636 	lun->media_changed = false;
637 	lun->block_size = ntohl(parameter.logical_block_length);
638 	lun->block_count = ntohl(parameter.last_logical_block_address) + 1;
639 	return B_OK;
640 }
641 
642 
643 status_t
644 usb_disk_synchronize(device_lun *lun, bool force)
645 {
646 	if (lun->device->sync_support == 0) {
647 		// this device reported an illegal request when syncing or repeatedly
648 		// returned an other error, it apparently does not support syncing...
649 		return B_UNSUPPORTED;
650 	}
651 
652 	if (!lun->should_sync && !force)
653 		return B_OK;
654 
655 	status_t result = usb_disk_operation(lun, SCSI_SYNCHRONIZE_CACHE_10, 10,
656 		0, 0, NULL, NULL, false);
657 
658 	if (result == B_OK) {
659 		lun->device->sync_support = SYNC_SUPPORT_RELOAD;
660 		lun->should_sync = false;
661 		return B_OK;
662 	}
663 
664 	if (result == B_DEV_INVALID_IOCTL)
665 		lun->device->sync_support = 0;
666 	else
667 		lun->device->sync_support--;
668 
669 	return result;
670 }
671 
672 
673 //
674 //#pragma mark - Device Attach/Detach Notifications and Callback
675 //
676 
677 
678 static void
679 usb_disk_callback(void *cookie, status_t status, void *data,
680 	size_t actualLength)
681 {
682 	//TRACE("callback()\n");
683 	disk_device *device = (disk_device *)cookie;
684 	device->status = status;
685 	device->actual_length = actualLength;
686 	release_sem(device->notify);
687 }
688 
689 
690 static status_t
691 usb_disk_device_added(usb_device newDevice, void **cookie)
692 {
693 	TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
694 	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
695 	device->device = newDevice;
696 	device->removed = false;
697 	device->open_count = 0;
698 	device->interface = 0xff;
699 	device->current_tag = 0;
700 	device->sync_support = SYNC_SUPPORT_RELOAD;
701 	device->tur_supported = true;
702 	device->is_atapi = false;
703 	device->luns = NULL;
704 
705 	// scan through the interfaces to find our bulk-only data interface
706 	const usb_configuration_info *configuration
707 		= gUSBModule->get_configuration(newDevice);
708 	if (configuration == NULL) {
709 		free(device);
710 		return B_ERROR;
711 	}
712 
713 	for (size_t i = 0; i < configuration->interface_count; i++) {
714 		usb_interface_info *interface = configuration->interface[i].active;
715 		if (interface == NULL)
716 			continue;
717 
718 		if (interface->descr->interface_class == USB_MASS_STORAGE_DEVICE_CLASS
719 			&& (interface->descr->interface_subclass == 0x06 /* SCSI */
720 				|| interface->descr->interface_subclass == 0x02 /* ATAPI */
721 				|| interface->descr->interface_subclass == 0x05 /* ATAPI */)
722 			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {
723 
724 			bool hasIn = false;
725 			bool hasOut = false;
726 			for (size_t j = 0; j < interface->endpoint_count; j++) {
727 				usb_endpoint_info *endpoint = &interface->endpoint[j];
728 				if (endpoint == NULL
729 					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
730 					continue;
731 
732 				if (!hasIn && (endpoint->descr->endpoint_address
733 					& USB_ENDPOINT_ADDR_DIR_IN) != 0) {
734 					device->bulk_in = endpoint->handle;
735 					hasIn = true;
736 				} else if (!hasOut && (endpoint->descr->endpoint_address
737 					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
738 					device->bulk_out = endpoint->handle;
739 					hasOut = true;
740 				}
741 
742 				if (hasIn && hasOut)
743 					break;
744 			}
745 
746 			if (!(hasIn && hasOut))
747 				continue;
748 
749 			device->interface = interface->descr->interface_number;
750 			device->is_atapi = interface->descr->interface_subclass != 0x06;
751 			break;
752 		}
753 	}
754 
755 	if (device->interface == 0xff) {
756 		TRACE_ALWAYS("no valid bulk-only interface found\n");
757 		free(device);
758 		return B_ERROR;
759 	}
760 
761 	mutex_init(&device->lock, "usb_disk device lock");
762 
763 	device->notify = create_sem(0, "usb_disk callback notify");
764 	if (device->notify < B_OK) {
765 		mutex_destroy(&device->lock);
766 		status_t result = device->notify;
767 		free(device);
768 		return result;
769 	}
770 
771 	device->lun_count = usb_disk_get_max_lun(device) + 1;
772 	device->luns = (device_lun **)malloc(device->lun_count
773 		* sizeof(device_lun *));
774 	for (uint8 i = 0; i < device->lun_count; i++)
775 		device->luns[i] = NULL;
776 
777 	status_t result = B_OK;
778 
779 	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
780 	for (uint8 i = 0; i < device->lun_count; i++) {
781 		// create the individual luns present on this device
782 		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
783 		if (lun == NULL) {
784 			result = B_NO_MEMORY;
785 			break;
786 		}
787 
788 		device->luns[i] = lun;
789 		lun->device = device;
790 		lun->logical_unit_number = i;
791 		lun->should_sync = false;
792 		lun->media_present = true;
793 		lun->media_changed = true;
794 
795 		memset(lun->vendor_name, 0, sizeof(lun->vendor_name));
796 		memset(lun->product_name, 0, sizeof(lun->product_name));
797 		memset(lun->product_revision, 0, sizeof(lun->product_revision));
798 
799 		usb_disk_reset_capacity(lun);
800 
801 		// initialize this lun
802 		result = usb_disk_inquiry(lun);
803 		err_act action = err_act_ok;
804 		for (uint32 tries = 0; tries < 8; tries++) {
805 			TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " begin\n",
806 				i, tries);
807 			status_t ready = usb_disk_test_unit_ready(lun, &action);
808 			if (ready == B_OK || ready == B_DEV_NO_MEDIA
809 				|| ready == B_DEV_MEDIA_CHANGED) {
810 				if (lun->device_type == B_CD)
811 					lun->write_protected = true;
812 				// TODO: check for write protection; disabled since some
813 				// devices lock up when getting the mode sense
814 				else if (/*usb_disk_mode_sense(lun) != B_OK*/true)
815 					lun->write_protected = false;
816 
817 				TRACE("usb lun %" B_PRIu8 " ready. write protected = %c%s\n", i,
818 					lun->write_protected ? 'y' : 'n',
819 					ready == B_DEV_NO_MEDIA ? " (no media inserted)" : "");
820 
821 				break;
822 			}
823 			TRACE("usb lun %" B_PRIu8 " inquiry attempt %" B_PRIu32 " failed\n",
824 				i, tries);
825 			if (action != err_act_retry && action != err_act_many_retries)
826 				break;
827 			bigtime_t snoozeTime = 1000000 * tries;
828 			TRACE("snoozing %" B_PRIu64 " microseconds for usb lun\n",
829 				snoozeTime);
830 			snooze(snoozeTime);
831 		}
832 
833 		if (result != B_OK)
834 			break;
835 	}
836 
837 	if (result != B_OK) {
838 		TRACE_ALWAYS("failed to initialize logical units: %s\n",
839 			strerror(result));
840 		usb_disk_free_device_and_luns(device);
841 		return result;
842 	}
843 
844 	mutex_lock(&gDeviceListLock);
845 	device->device_number = 0;
846 	disk_device *other = gDeviceList;
847 	while (other != NULL) {
848 		if (other->device_number >= device->device_number)
849 			device->device_number = other->device_number + 1;
850 
851 		other = (disk_device *)other->link;
852 	}
853 
854 	device->link = (void *)gDeviceList;
855 	gDeviceList = device;
856 	gLunCount += device->lun_count;
857 	for (uint8 i = 0; i < device->lun_count; i++)
858 		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
859 	mutex_unlock(&gDeviceListLock);
860 
861 	TRACE("new device: 0x%p\n", device);
862 	*cookie = (void *)device;
863 	return B_OK;
864 }
865 
866 
867 static status_t
868 usb_disk_device_removed(void *cookie)
869 {
870 	TRACE("device_removed(0x%p)\n", cookie);
871 	disk_device *device = (disk_device *)cookie;
872 
873 	mutex_lock(&gDeviceListLock);
874 	if (gDeviceList == device) {
875 		gDeviceList = (disk_device *)device->link;
876 	} else {
877 		disk_device *element = gDeviceList;
878 		while (element) {
879 			if (element->link == device) {
880 				element->link = device->link;
881 				break;
882 			}
883 
884 			element = (disk_device *)element->link;
885 		}
886 	}
887 	gLunCount -= device->lun_count;
888 	gDeviceCount--;
889 
890 	device->removed = true;
891 	gUSBModule->cancel_queued_transfers(device->bulk_in);
892 	gUSBModule->cancel_queued_transfers(device->bulk_out);
893 	if (device->open_count == 0)
894 		usb_disk_free_device_and_luns(device);
895 
896 	mutex_unlock(&gDeviceListLock);
897 	return B_OK;
898 }
899 
900 
901 //
902 //#pragma mark - Partial Buffer Functions
903 //
904 
905 
906 static bool
907 usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
908 	uint32 &blockPosition, uint16 &blockCount)
909 {
910 	blockPosition = (uint32)(position / lun->block_size);
911 	if ((off_t)blockPosition * lun->block_size != position)
912 		return true;
913 
914 	blockCount = (uint16)(length / lun->block_size);
915 	if ((size_t)blockCount * lun->block_size != length)
916 		return true;
917 
918 	return false;
919 }
920 
921 
922 static status_t
923 usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
924 	void *buffer, size_t *length)
925 {
926 	status_t result = usb_disk_operation(lun, SCSI_READ_10, 10, blockPosition,
927 		blockCount, buffer, length, true);
928 	return result;
929 }
930 
931 
932 static status_t
933 usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
934 	void *buffer, size_t *length)
935 {
936 	status_t result = usb_disk_operation(lun, SCSI_WRITE_10, 10, blockPosition,
937 		blockCount, buffer, length, false);
938 	if (result == B_OK)
939 		lun->should_sync = true;
940 	return result;
941 }
942 
943 
944 static status_t
945 usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
946 	void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
947 	uint16 &blockCount)
948 {
949 	blockPosition = (uint32)(position / lun->block_size);
950 	blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
951 		/ lun->block_size) - blockPosition);
952 	size_t blockLength = blockCount * lun->block_size;
953 	blockBuffer = malloc(blockLength);
954 	if (blockBuffer == NULL) {
955 		TRACE_ALWAYS("no memory to allocate partial buffer\n");
956 		return B_NO_MEMORY;
957 	}
958 
959 	status_t result = usb_disk_block_read(lun, blockPosition, blockCount,
960 		blockBuffer, &blockLength);
961 	if (result != B_OK) {
962 		TRACE_ALWAYS("block read failed when filling partial buffer: %s\n",
963 			strerror(result));
964 		free(blockBuffer);
965 		return result;
966 	}
967 
968 	off_t offset = position - (off_t)blockPosition * lun->block_size;
969 	partialBuffer = (uint8 *)blockBuffer + offset;
970 	return B_OK;
971 }
972 
973 
974 //
975 //#pragma mark - Driver Hooks
976 //
977 
978 
979 static status_t
980 usb_disk_open(const char *name, uint32 flags, void **cookie)
981 {
982 	TRACE("open(%s)\n", name);
983 	if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0)
984 		return B_NAME_NOT_FOUND;
985 
986 	int32 lastPart = 0;
987 	size_t nameLength = strlen(name);
988 	for (int32 i = nameLength - 1; i >= 0; i--) {
989 		if (name[i] == '/') {
990 			lastPart = i;
991 			break;
992 		}
993 	}
994 
995 	char rawName[nameLength + 4];
996 	strncpy(rawName, name, lastPart + 1);
997 	rawName[lastPart + 1] = 0;
998 	strcat(rawName, "raw");
999 	TRACE("opening raw device %s for %s\n", rawName, name);
1000 
1001 	mutex_lock(&gDeviceListLock);
1002 	disk_device *device = gDeviceList;
1003 	while (device) {
1004 		for (uint8 i = 0; i < device->lun_count; i++) {
1005 			device_lun *lun = device->luns[i];
1006 			if (strncmp(rawName, lun->name, 32) == 0) {
1007 				// found the matching device/lun
1008 				if (device->removed) {
1009 					mutex_unlock(&gDeviceListLock);
1010 					return B_ERROR;
1011 				}
1012 
1013 				device->open_count++;
1014 				*cookie = lun;
1015 				mutex_unlock(&gDeviceListLock);
1016 				return B_OK;
1017 			}
1018 		}
1019 
1020 		device = (disk_device *)device->link;
1021 	}
1022 
1023 	mutex_unlock(&gDeviceListLock);
1024 	return B_NAME_NOT_FOUND;
1025 }
1026 
1027 
1028 static status_t
1029 usb_disk_close(void *cookie)
1030 {
1031 	TRACE("close()\n");
1032 	device_lun *lun = (device_lun *)cookie;
1033 	disk_device *device = lun->device;
1034 
1035 	mutex_lock(&device->lock);
1036 	if (!device->removed)
1037 		usb_disk_synchronize(lun, false);
1038 	mutex_unlock(&device->lock);
1039 
1040 	return B_OK;
1041 }
1042 
1043 
1044 static status_t
1045 usb_disk_free(void *cookie)
1046 {
1047 	TRACE("free()\n");
1048 	mutex_lock(&gDeviceListLock);
1049 
1050 	device_lun *lun = (device_lun *)cookie;
1051 	disk_device *device = lun->device;
1052 	device->open_count--;
1053 	if (device->open_count == 0 && device->removed) {
1054 		// we can simply free the device here as it has been removed from
1055 		// the device list in the device removed notification hook
1056 		usb_disk_free_device_and_luns(device);
1057 	}
1058 
1059 	mutex_unlock(&gDeviceListLock);
1060 	return B_OK;
1061 }
1062 
1063 
1064 static inline void
1065 normalize_name(char *name, size_t nameLength)
1066 {
1067 	bool wasSpace = false;
1068 	size_t insertIndex = 0;
1069 	for (size_t i = 0; i < nameLength; i++) {
1070 		bool isSpace = name[i] == ' ';
1071 		if (isSpace && wasSpace)
1072 			continue;
1073 
1074 		name[insertIndex++] = name[i];
1075 		wasSpace = isSpace;
1076 	}
1077 
1078 	if (insertIndex > 0 && name[insertIndex - 1] == ' ')
1079 		insertIndex--;
1080 
1081 	name[insertIndex] = 0;
1082 }
1083 
1084 
1085 static status_t
1086 usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
1087 {
1088 	device_lun *lun = (device_lun *)cookie;
1089 	disk_device *device = lun->device;
1090 	mutex_lock(&device->lock);
1091 	if (device->removed) {
1092 		mutex_unlock(&device->lock);
1093 		return B_DEV_NOT_READY;
1094 	}
1095 
1096 	status_t result = B_DEV_INVALID_IOCTL;
1097 	switch (op) {
1098 		case B_GET_MEDIA_STATUS:
1099 		{
1100 			err_act action = err_act_ok;
1101 			for (uint32 tries = 0; tries < 3; tries++) {
1102 				status_t ready = usb_disk_test_unit_ready(lun, &action);
1103 				if (ready == B_OK || ready == B_DEV_NO_MEDIA
1104 					|| (action != err_act_retry
1105 						&& action != err_act_many_retries)) {
1106 					*(status_t *)buffer = ready;
1107 					break;
1108 				}
1109 				snooze(500000);
1110 			}
1111 			TRACE("B_GET_MEDIA_STATUS: 0x%08" B_PRIx32 "\n",
1112 				*(status_t *)buffer);
1113 			result = B_OK;
1114 			break;
1115 		}
1116 
1117 		case B_GET_GEOMETRY:
1118 		{
1119 			if (lun->media_changed) {
1120 				result = usb_disk_update_capacity(lun);
1121 				if (result != B_OK)
1122 					break;
1123 			}
1124 
1125 			device_geometry geometry;
1126 			devfs_compute_geometry_size(&geometry, lun->block_count,
1127 				lun->block_size);
1128 
1129 			geometry.device_type = lun->device_type;
1130 			geometry.removable = lun->removable;
1131 			geometry.read_only = lun->write_protected;
1132 			geometry.write_once = lun->device_type == B_WORM;
1133 			TRACE("B_GET_GEOMETRY: %" B_PRId32 " sectors at %" B_PRId32
1134 				" bytes per sector\n", geometry.cylinder_count,
1135 				geometry.bytes_per_sector);
1136 			result = user_memcpy(buffer, &geometry, sizeof(device_geometry));
1137 			break;
1138 		}
1139 
1140 		case B_FLUSH_DRIVE_CACHE:
1141 			TRACE("B_FLUSH_DRIVE_CACHE\n");
1142 			result = usb_disk_synchronize(lun, true);
1143 			break;
1144 
1145 		case B_EJECT_DEVICE:
1146 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 2,
1147 				NULL, NULL, false);
1148 			break;
1149 
1150 		case B_LOAD_MEDIA:
1151 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 3,
1152 				NULL, NULL, false);
1153 			break;
1154 
1155 #if HAIKU_TARGET_PLATFORM_HAIKU
1156 		case B_GET_ICON:
1157 			// We don't support this legacy ioctl anymore, but the two other
1158 			// icon ioctls below instead.
1159 			break;
1160 
1161 		case B_GET_ICON_NAME:
1162 			result = user_strlcpy((char *)buffer,
1163 				"devices/drive-removable-media-usb", B_FILE_NAME_LENGTH);
1164 			break;
1165 
1166 		case B_GET_VECTOR_ICON:
1167 		{
1168 			if (length != sizeof(device_icon)) {
1169 				result = B_BAD_VALUE;
1170 				break;
1171 			}
1172 
1173 			device_icon iconData;
1174 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
1175 				result = B_BAD_ADDRESS;
1176 				break;
1177 			}
1178 
1179 			if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) {
1180 				if (user_memcpy(iconData.icon_data, kDeviceIcon,
1181 						sizeof(kDeviceIcon)) != B_OK) {
1182 					result = B_BAD_ADDRESS;
1183 					break;
1184 				}
1185 			}
1186 
1187 			iconData.icon_size = sizeof(kDeviceIcon);
1188 			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
1189 			break;
1190 		}
1191 
1192 		case B_GET_DEVICE_NAME:
1193 		{
1194 			size_t nameLength = sizeof(lun->vendor_name)
1195 				+ sizeof(lun->product_name) + sizeof(lun->product_revision) + 3;
1196 
1197 			char name[nameLength];
1198 			snprintf(name, nameLength, "%.8s %.16s %.4s", lun->vendor_name,
1199 				lun->product_name, lun->product_revision);
1200 
1201 			normalize_name(name, nameLength);
1202 
1203 			result = user_strlcpy((char *)buffer, name, length);
1204 			if (result > 0)
1205 				result = B_OK;
1206 
1207 			TRACE_ALWAYS("got device name \"%s\": %s\n", name,
1208 				strerror(result));
1209 			break;
1210 		}
1211 #endif
1212 
1213 		default:
1214 			TRACE_ALWAYS("unhandled ioctl %" B_PRId32 "\n", op);
1215 			break;
1216 	}
1217 
1218 	mutex_unlock(&device->lock);
1219 	return result;
1220 }
1221 
1222 
1223 static status_t
1224 usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
1225 {
1226 	if (buffer == NULL || length == NULL)
1227 		return B_BAD_VALUE;
1228 
1229 	TRACE("read(%" B_PRIdOFF ", %ld)\n", position, *length);
1230 	device_lun *lun = (device_lun *)cookie;
1231 	disk_device *device = lun->device;
1232 	mutex_lock(&device->lock);
1233 	if (device->removed) {
1234 		*length = 0;
1235 		mutex_unlock(&device->lock);
1236 		return B_DEV_NOT_READY;
1237 	}
1238 
1239 	status_t result = B_ERROR;
1240 	uint32 blockPosition = 0;
1241 	uint16 blockCount = 0;
1242 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length,
1243 		blockPosition, blockCount);
1244 	if (needsPartial) {
1245 		void *partialBuffer = NULL;
1246 		void *blockBuffer = NULL;
1247 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1248 			partialBuffer, blockBuffer, blockPosition, blockCount);
1249 		if (result == B_OK) {
1250 			memcpy(buffer, partialBuffer, *length);
1251 			free(blockBuffer);
1252 		}
1253 	} else {
1254 		result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
1255 			length);
1256 	}
1257 
1258 	mutex_unlock(&device->lock);
1259 	if (result == B_OK) {
1260 		TRACE("read successful with %ld bytes\n", *length);
1261 		return B_OK;
1262 	}
1263 
1264 	*length = 0;
1265 	TRACE_ALWAYS("read failed: %s\n", strerror(result));
1266 	return result;
1267 }
1268 
1269 
1270 static status_t
1271 usb_disk_write(void *cookie, off_t position, const void *buffer,
1272 	size_t *length)
1273 {
1274 	if (buffer == NULL || length == NULL)
1275 		return B_BAD_VALUE;
1276 
1277 	TRACE("write(%" B_PRIdOFF", %ld)\n", position, *length);
1278 	device_lun *lun = (device_lun *)cookie;
1279 	disk_device *device = lun->device;
1280 	mutex_lock(&device->lock);
1281 	if (device->removed) {
1282 		*length = 0;
1283 		mutex_unlock(&device->lock);
1284 		return B_DEV_NOT_READY;
1285 	}
1286 
1287 	status_t result = B_ERROR;
1288 	uint32 blockPosition = 0;
1289 	uint16 blockCount = 0;
1290 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position,
1291 		*length, blockPosition, blockCount);
1292 	if (needsPartial) {
1293 		void *partialBuffer = NULL;
1294 		void *blockBuffer = NULL;
1295 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1296 			partialBuffer, blockBuffer, blockPosition, blockCount);
1297 		if (result == B_OK) {
1298 			memcpy(partialBuffer, buffer, *length);
1299 			size_t blockLength = blockCount * lun->block_size;
1300 			result = usb_disk_block_write(lun, blockPosition, blockCount,
1301 				blockBuffer, &blockLength);
1302 			free(blockBuffer);
1303 		}
1304 	} else {
1305 		result = usb_disk_block_write(lun, blockPosition, blockCount,
1306 			(void *)buffer, length);
1307 	}
1308 
1309 	mutex_unlock(&device->lock);
1310 	if (result == B_OK) {
1311 		TRACE("write successful with %ld bytes\n", *length);
1312 		return B_OK;
1313 	}
1314 
1315 	*length = 0;
1316 	TRACE_ALWAYS("write failed: %s\n", strerror(result));
1317 	return result;
1318 }
1319 
1320 
1321 //
1322 //#pragma mark - Driver Entry Points
1323 //
1324 
1325 
1326 status_t
1327 init_hardware()
1328 {
1329 	TRACE("init_hardware()\n");
1330 	return B_OK;
1331 }
1332 
1333 
1334 status_t
1335 init_driver()
1336 {
1337 	TRACE("init_driver()\n");
1338 	static usb_notify_hooks notifyHooks = {
1339 		&usb_disk_device_added,
1340 		&usb_disk_device_removed
1341 	};
1342 
1343 	static usb_support_descriptor supportedDevices[] = {
1344 		{ 0x08 /* mass storage */, 0x06 /* SCSI */, 0x50 /* bulk */, 0, 0 },
1345 		{ 0x08 /* mass storage */, 0x02 /* ATAPI */, 0x50 /* bulk */, 0, 0 },
1346 		{ 0x08 /* mass storage */, 0x05 /* ATAPI */, 0x50 /* bulk */, 0, 0 }
1347 	};
1348 
1349 	gDeviceList = NULL;
1350 	gDeviceCount = 0;
1351 	gLunCount = 0;
1352 	mutex_init(&gDeviceListLock, "usb_disk device list lock");
1353 
1354 	TRACE("trying module %s\n", B_USB_MODULE_NAME);
1355 	status_t result = get_module(B_USB_MODULE_NAME,
1356 		(module_info **)&gUSBModule);
1357 	if (result < B_OK) {
1358 		TRACE_ALWAYS("getting module failed: %s\n", strerror(result));
1359 		mutex_destroy(&gDeviceListLock);
1360 		return result;
1361 	}
1362 
1363 	gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 3, NULL);
1364 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
1365 	return B_OK;
1366 }
1367 
1368 
1369 void
1370 uninit_driver()
1371 {
1372 	TRACE("uninit_driver()\n");
1373 	gUSBModule->uninstall_notify(DRIVER_NAME);
1374 	mutex_lock(&gDeviceListLock);
1375 
1376 	if (gDeviceNames) {
1377 		for (int32 i = 0; gDeviceNames[i]; i++)
1378 			free(gDeviceNames[i]);
1379 		free(gDeviceNames);
1380 		gDeviceNames = NULL;
1381 	}
1382 
1383 	mutex_destroy(&gDeviceListLock);
1384 	put_module(B_USB_MODULE_NAME);
1385 }
1386 
1387 
1388 const char **
1389 publish_devices()
1390 {
1391 	TRACE("publish_devices()\n");
1392 	if (gDeviceNames) {
1393 		for (int32 i = 0; gDeviceNames[i]; i++)
1394 			free(gDeviceNames[i]);
1395 		free(gDeviceNames);
1396 		gDeviceNames = NULL;
1397 	}
1398 
1399 	gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1));
1400 	if (gDeviceNames == NULL)
1401 		return NULL;
1402 
1403 	int32 index = 0;
1404 	mutex_lock(&gDeviceListLock);
1405 	disk_device *device = gDeviceList;
1406 	while (device) {
1407 		for (uint8 i = 0; i < device->lun_count; i++)
1408 			gDeviceNames[index++] = strdup(device->luns[i]->name);
1409 
1410 		device = (disk_device *)device->link;
1411 	}
1412 
1413 	gDeviceNames[index++] = NULL;
1414 	mutex_unlock(&gDeviceListLock);
1415 	return (const char **)gDeviceNames;
1416 }
1417 
1418 
1419 device_hooks *
1420 find_device(const char *name)
1421 {
1422 	TRACE("find_device()\n");
1423 	static device_hooks hooks = {
1424 		&usb_disk_open,
1425 		&usb_disk_close,
1426 		&usb_disk_free,
1427 		&usb_disk_ioctl,
1428 		&usb_disk_read,
1429 		&usb_disk_write,
1430 		NULL,
1431 		NULL,
1432 		NULL,
1433 		NULL
1434 	};
1435 
1436 	return &hooks;
1437 }
1438