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