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