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