xref: /haiku/src/add-ons/kernel/drivers/disk/usb/usb_disk/usb_disk.cpp (revision 675ffabd70492a962f8c0288a32208c22ce5de18)
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 				// the operation is complete but has failed at the SCSI level
390 				if (operation != SCSI_TEST_UNIT_READY_6) {
391 					TRACE_ALWAYS("operation 0x%02x failed at the SCSI level\n",
392 						operation);
393 				}
394 
395 				result = usb_disk_request_sense(lun);
396 				return result == B_OK ? B_ERROR : result;
397 			}
398 		}
399 
400 		case CSW_STATUS_PHASE_ERROR:
401 		{
402 			// a protocol or device error occured
403 			TRACE_ALWAYS("phase error in operation 0x%02x\n", operation);
404 			usb_disk_reset_recovery(device);
405 			return B_ERROR;
406 		}
407 
408 		default:
409 		{
410 			// command status wrapper is not meaningful
411 			TRACE_ALWAYS("command status wrapper has invalid status\n");
412 			usb_disk_reset_recovery(device);
413 			return B_ERROR;
414 		}
415 	}
416 }
417 
418 
419 //
420 //#pragma mark - Helper/Convenience Functions
421 //
422 
423 
424 status_t
425 usb_disk_request_sense(device_lun *lun)
426 {
427 	uint32 dataLength = sizeof(scsi_request_sense_6_parameter);
428 	scsi_request_sense_6_parameter parameter;
429 	status_t result = usb_disk_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
430 		dataLength, &parameter, &dataLength, true);
431 	if (result != B_OK) {
432 		TRACE_ALWAYS("getting request sense data failed\n");
433 		return result;
434 	}
435 
436 	if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY
437 		&& parameter.sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) {
438 		TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: "
439 			"0x%02x;\n", parameter.sense_key, parameter.additional_sense_code,
440 			parameter.additional_sense_code_qualifier);
441 	}
442 
443 	switch (parameter.sense_key) {
444 		case SCSI_SENSE_KEY_NO_SENSE:
445 		case SCSI_SENSE_KEY_RECOVERED_ERROR:
446 			return B_OK;
447 
448 		case SCSI_SENSE_KEY_HARDWARE_ERROR:
449 		case SCSI_SENSE_KEY_MEDIUM_ERROR:
450 			TRACE_ALWAYS("request_sense: media or hardware error\n");
451 			return B_DEV_UNREADABLE;
452 
453 		case SCSI_SENSE_KEY_ILLEGAL_REQUEST:
454 			TRACE_ALWAYS("request_sense: illegal request\n");
455 			return B_DEV_INVALID_IOCTL;
456 
457 		case SCSI_SENSE_KEY_UNIT_ATTENTION:
458 			if (parameter.additional_sense_code
459 					!= SCSI_ASC_MEDIUM_NOT_PRESENT) {
460 				TRACE_ALWAYS("request_sense: media changed\n");
461 				lun->media_changed = true;
462 				lun->media_present = true;
463 				return B_DEV_MEDIA_CHANGED;
464 			}
465 			// fall through
466 
467 		case SCSI_SENSE_KEY_NOT_READY:
468 			TRACE("request_sense: device not ready (asc 0x%02x ascq 0x%02x)\n",
469 				parameter.additional_sense_code,
470 				parameter.additional_sense_code_qualifier);
471 			lun->media_present = false;
472 			usb_disk_reset_capacity(lun);
473 			return B_DEV_NO_MEDIA;
474 
475 		case SCSI_SENSE_KEY_DATA_PROTECT:
476 			TRACE_ALWAYS("request_sense: write protected\n");
477 			return B_READ_ONLY_DEVICE;
478 
479 		case SCSI_SENSE_KEY_ABORTED_COMMAND:
480 			TRACE_ALWAYS("request_sense: command aborted\n");
481 			return B_CANCELED;
482 	}
483 
484 	return B_ERROR;
485 }
486 
487 
488 status_t
489 usb_disk_mode_sense(device_lun *lun)
490 {
491 	uint32 dataLength = sizeof(scsi_mode_sense_6_parameter);
492 	scsi_mode_sense_6_parameter parameter;
493 	status_t result = usb_disk_operation(lun, SCSI_MODE_SENSE_6, 6,
494 		SCSI_MODE_PAGE_DEVICE_CONFIGURATION, dataLength, &parameter,
495 		&dataLength, true);
496 	if (result != B_OK) {
497 		TRACE_ALWAYS("getting mode sense data failed\n");
498 		return result;
499 	}
500 
501 	lun->write_protected
502 		= (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT)
503 			!= 0;
504 	TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no");
505 	return B_OK;
506 }
507 
508 
509 status_t
510 usb_disk_test_unit_ready(device_lun *lun)
511 {
512 	// if unsupported we assume the unit is fixed and therefore always ok
513 	if (!lun->device->tur_supported)
514 		return B_OK;
515 
516 	status_t result;
517 	if (lun->device->is_atapi) {
518 		result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 1,
519 			NULL, NULL, false);
520 	} else {
521 		result = usb_disk_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0,
522 			NULL, NULL, true);
523 	}
524 
525 	if (result == B_DEV_INVALID_IOCTL) {
526 		lun->device->tur_supported = false;
527 		return B_OK;
528 	}
529 
530 	return result;
531 }
532 
533 
534 status_t
535 usb_disk_inquiry(device_lun *lun)
536 {
537 	uint32 dataLength = sizeof(scsi_inquiry_6_parameter);
538 	scsi_inquiry_6_parameter parameter;
539 	status_t result = B_ERROR;
540 	for (uint32 tries = 0; tries < 3; tries++) {
541 		result = usb_disk_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
542 			&parameter, &dataLength, true);
543 		if (result == B_OK)
544 			break;
545 	}
546 	if (result != B_OK) {
547 		TRACE_ALWAYS("getting inquiry data failed\n");
548 		lun->device_type = B_DISK;
549 		lun->removable = true;
550 		return result;
551 	}
552 
553 	TRACE("peripherial_device_type  0x%02x\n",
554 		parameter.peripherial_device_type);
555 	TRACE("peripherial_qualifier    0x%02x\n",
556 		parameter.peripherial_qualifier);
557 	TRACE("removable_medium         %s\n",
558 		parameter.removable_medium ? "yes" : "no");
559 	TRACE("version                  0x%02x\n", parameter.version);
560 	TRACE("response_data_format     0x%02x\n", parameter.response_data_format);
561 	TRACE_ALWAYS("vendor_identification    \"%.8s\"\n",
562 		parameter.vendor_identification);
563 	TRACE_ALWAYS("product_identification   \"%.16s\"\n",
564 		parameter.product_identification);
565 	TRACE_ALWAYS("product_revision_level   \"%.4s\"\n",
566 		parameter.product_revision_level);
567 	lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
568 	lun->removable = (parameter.removable_medium == 1);
569 	return B_OK;
570 }
571 
572 
573 status_t
574 usb_disk_reset_capacity(device_lun *lun)
575 {
576 	lun->block_size = 512;
577 	lun->block_count = 0;
578 	return B_OK;
579 }
580 
581 
582 status_t
583 usb_disk_update_capacity(device_lun *lun)
584 {
585 	uint32 dataLength = sizeof(scsi_read_capacity_10_parameter);
586 	scsi_read_capacity_10_parameter parameter;
587 	status_t result = B_ERROR;
588 
589 	// Retry reading the capacity up to three times. The first try might only
590 	// yield a unit attention telling us that the device or media status
591 	// changed, which is more or less expected if it is the first operation
592 	// on the device or the device only clears the unit atention for capacity
593 	// reads.
594 	for (int32 i = 0; i < 3; i++) {
595 		result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
596 			&parameter, &dataLength, true);
597 		if (result == B_OK)
598 			break;
599 	}
600 
601 	if (result != B_OK) {
602 		TRACE_ALWAYS("failed to update capacity\n");
603 		lun->media_present = false;
604 		lun->media_changed = false;
605 		usb_disk_reset_capacity(lun);
606 		return result;
607 	}
608 
609 	lun->media_present = true;
610 	lun->media_changed = false;
611 	lun->block_size = ntohl(parameter.logical_block_length);
612 	lun->block_count = ntohl(parameter.last_logical_block_address) + 1;
613 	return B_OK;
614 }
615 
616 
617 status_t
618 usb_disk_synchronize(device_lun *lun, bool force)
619 {
620 	if (lun->device->sync_support == 0) {
621 		// this device reported an illegal request when syncing or repeatedly
622 		// returned an other error, it apparently does not support syncing...
623 		return B_UNSUPPORTED;
624 	}
625 
626 	if (!lun->should_sync && !force)
627 		return B_OK;
628 
629 	status_t result = usb_disk_operation(lun, SCSI_SYNCHRONIZE_CACHE_10, 10,
630 		0, 0, NULL, NULL, false);
631 
632 	if (result == B_OK) {
633 		lun->device->sync_support = SYNC_SUPPORT_RELOAD;
634 		lun->should_sync = false;
635 		return B_OK;
636 	}
637 
638 	if (result == B_DEV_INVALID_IOCTL)
639 		lun->device->sync_support = 0;
640 	else
641 		lun->device->sync_support--;
642 
643 	return result;
644 }
645 
646 
647 //
648 //#pragma mark - Device Attach/Detach Notifications and Callback
649 //
650 
651 
652 static void
653 usb_disk_callback(void *cookie, status_t status, void *data,
654 	size_t actualLength)
655 {
656 	//TRACE("callback()\n");
657 	disk_device *device = (disk_device *)cookie;
658 	device->status = status;
659 	device->actual_length = actualLength;
660 	release_sem(device->notify);
661 }
662 
663 
664 static status_t
665 usb_disk_device_added(usb_device newDevice, void **cookie)
666 {
667 	TRACE("device_added(0x%08lx)\n", newDevice);
668 	disk_device *device = (disk_device *)malloc(sizeof(disk_device));
669 	device->device = newDevice;
670 	device->removed = false;
671 	device->open_count = 0;
672 	device->interface = 0xff;
673 	device->current_tag = 0;
674 	device->sync_support = SYNC_SUPPORT_RELOAD;
675 	device->tur_supported = true;
676 	device->is_atapi = false;
677 	device->luns = NULL;
678 
679 	// scan through the interfaces to find our bulk-only data interface
680 	const usb_configuration_info *configuration
681 		= gUSBModule->get_configuration(newDevice);
682 	if (configuration == NULL) {
683 		free(device);
684 		return B_ERROR;
685 	}
686 
687 	for (size_t i = 0; i < configuration->interface_count; i++) {
688 		usb_interface_info *interface = configuration->interface[i].active;
689 		if (interface == NULL)
690 			continue;
691 
692 		if (interface->descr->interface_class == 0x08 /* mass storage */
693 			&& (interface->descr->interface_subclass == 0x06 /* SCSI */
694 				|| interface->descr->interface_subclass == 0x02 /* ATAPI */
695 				|| interface->descr->interface_subclass == 0x05 /* ATAPI */)
696 			&& interface->descr->interface_protocol == 0x50 /* bulk-only */) {
697 
698 			bool hasIn = false;
699 			bool hasOut = false;
700 			for (size_t j = 0; j < interface->endpoint_count; j++) {
701 				usb_endpoint_info *endpoint = &interface->endpoint[j];
702 				if (endpoint == NULL
703 					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
704 					continue;
705 
706 				if (!hasIn && (endpoint->descr->endpoint_address
707 					& USB_ENDPOINT_ADDR_DIR_IN)) {
708 					device->bulk_in = endpoint->handle;
709 					hasIn = true;
710 				} else if (!hasOut && (endpoint->descr->endpoint_address
711 					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
712 					device->bulk_out = endpoint->handle;
713 					hasOut = true;
714 				}
715 
716 				if (hasIn && hasOut)
717 					break;
718 			}
719 
720 			if (!(hasIn && hasOut))
721 				continue;
722 
723 			device->interface = interface->descr->interface_number;
724 			device->is_atapi = interface->descr->interface_subclass != 0x06;
725 			break;
726 		}
727 	}
728 
729 	if (device->interface == 0xff) {
730 		TRACE_ALWAYS("no valid bulk-only interface found\n");
731 		free(device);
732 		return B_ERROR;
733 	}
734 
735 	mutex_init(&device->lock, "usb_disk device lock");
736 
737 	device->notify = create_sem(0, "usb_disk callback notify");
738 	if (device->notify < B_OK) {
739 		mutex_destroy(&device->lock);
740 		free(device);
741 		return device->notify;
742 	}
743 
744 	device->lun_count = usb_disk_get_max_lun(device) + 1;
745 	device->luns = (device_lun **)malloc(device->lun_count
746 		* sizeof(device_lun *));
747 	for (uint8 i = 0; i < device->lun_count; i++)
748 		device->luns[i] = NULL;
749 
750 	status_t result = B_OK;
751 
752 	TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count);
753 	for (uint8 i = 0; i < device->lun_count; i++) {
754 		// create the individual luns present on this device
755 		device_lun *lun = (device_lun *)malloc(sizeof(device_lun));
756 		if (lun == NULL) {
757 			result = B_NO_MEMORY;
758 			break;
759 		}
760 
761 		device->luns[i] = lun;
762 		lun->device = device;
763 		lun->logical_unit_number = i;
764 		lun->should_sync = false;
765 		lun->media_present = true;
766 		lun->media_changed = true;
767 		usb_disk_reset_capacity(lun);
768 
769 		// initialize this lun
770 		result = usb_disk_inquiry(lun);
771 		for (uint32 tries = 0; tries < 3; tries++) {
772 			status_t ready = usb_disk_test_unit_ready(lun);
773 			if (ready == B_OK || ready == B_DEV_NO_MEDIA) {
774 				if (ready == B_OK) {
775 					if (lun->device_type == B_CD)
776 						lun->write_protected = true;
777 					// TODO: check for write protection; disabled since
778 					// some devices lock up when getting the mode sense
779 					else if (/*usb_disk_mode_sense(lun) != B_OK*/true)
780 						lun->write_protected = false;
781 				}
782 
783 				break;
784 			}
785 
786 			snooze(10000);
787 		}
788 
789 		if (result != B_OK)
790 			break;
791 	}
792 
793 	if (result != B_OK) {
794 		TRACE_ALWAYS("failed to initialize logical units\n");
795 		usb_disk_free_device_and_luns(device);
796 		return result;
797 	}
798 
799 	mutex_lock(&gDeviceListLock);
800 	device->device_number = 0;
801 	disk_device *other = gDeviceList;
802 	while (other != NULL) {
803 		if (other->device_number >= device->device_number)
804 			device->device_number = other->device_number + 1;
805 
806 		other = (disk_device *)other->link;
807 	}
808 
809 	device->link = (void *)gDeviceList;
810 	gDeviceList = device;
811 	gLunCount += device->lun_count;
812 	for (uint8 i = 0; i < device->lun_count; i++)
813 		sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i);
814 	mutex_unlock(&gDeviceListLock);
815 
816 	TRACE("new device: 0x%08lx\n", (uint32)device);
817 	*cookie = (void *)device;
818 	return B_OK;
819 }
820 
821 
822 static status_t
823 usb_disk_device_removed(void *cookie)
824 {
825 	TRACE("device_removed(0x%08lx)\n", (uint32)cookie);
826 	disk_device *device = (disk_device *)cookie;
827 
828 	mutex_lock(&gDeviceListLock);
829 	if (gDeviceList == device) {
830 		gDeviceList = (disk_device *)device->link;
831 	} else {
832 		disk_device *element = gDeviceList;
833 		while (element) {
834 			if (element->link == device) {
835 				element->link = device->link;
836 				break;
837 			}
838 
839 			element = (disk_device *)element->link;
840 		}
841 	}
842 	gLunCount -= device->lun_count;
843 	gDeviceCount--;
844 
845 	device->removed = true;
846 	gUSBModule->cancel_queued_transfers(device->bulk_in);
847 	gUSBModule->cancel_queued_transfers(device->bulk_out);
848 	if (device->open_count == 0)
849 		usb_disk_free_device_and_luns(device);
850 
851 	mutex_unlock(&gDeviceListLock);
852 	return B_OK;
853 }
854 
855 
856 //
857 //#pragma mark - Partial Buffer Functions
858 //
859 
860 
861 static bool
862 usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
863 	uint32 &blockPosition, uint16 &blockCount)
864 {
865 	blockPosition = (uint32)(position / lun->block_size);
866 	if ((off_t)blockPosition * lun->block_size != position)
867 		return true;
868 
869 	blockCount = (uint16)(length / lun->block_size);
870 	if ((size_t)blockCount * lun->block_size != length)
871 		return true;
872 
873 	return false;
874 }
875 
876 
877 static status_t
878 usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
879 	void *buffer, size_t *length)
880 {
881 	status_t result = usb_disk_operation(lun, SCSI_READ_10, 10, blockPosition,
882 		blockCount, buffer, length, true);
883 	return result;
884 }
885 
886 
887 static status_t
888 usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
889 	void *buffer, size_t *length)
890 {
891 	status_t result = usb_disk_operation(lun, SCSI_WRITE_10, 10, blockPosition,
892 		blockCount, buffer, length, false);
893 	if (result == B_OK)
894 		lun->should_sync = true;
895 	return result;
896 }
897 
898 
899 static status_t
900 usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
901 	void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition,
902 	uint16 &blockCount)
903 {
904 	blockPosition = (uint32)(position / lun->block_size);
905 	blockCount = (uint16)((uint32)((position + length + lun->block_size - 1)
906 		/ lun->block_size) - blockPosition);
907 	size_t blockLength = blockCount * lun->block_size;
908 	blockBuffer = malloc(blockLength);
909 	if (blockBuffer == NULL) {
910 		TRACE_ALWAYS("no memory to allocate partial buffer\n");
911 		return B_NO_MEMORY;
912 	}
913 
914 	status_t result = usb_disk_block_read(lun, blockPosition, blockCount,
915 		blockBuffer, &blockLength);
916 	if (result != B_OK) {
917 		TRACE_ALWAYS("block read failed when filling partial buffer\n");
918 		free(blockBuffer);
919 		return result;
920 	}
921 
922 	off_t offset = position - (blockPosition * lun->block_size);
923 	partialBuffer = (uint8 *)blockBuffer + offset;
924 	return B_OK;
925 }
926 
927 
928 //
929 //#pragma mark - Driver Hooks
930 //
931 
932 
933 static status_t
934 usb_disk_open(const char *name, uint32 flags, void **cookie)
935 {
936 	TRACE("open(%s)\n", name);
937 	if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0)
938 		return B_NAME_NOT_FOUND;
939 
940 	int32 lastPart = 0;
941 	size_t nameLength = strlen(name);
942 	for (int32 i = nameLength - 1; i >= 0; i--) {
943 		if (name[i] == '/') {
944 			lastPart = i;
945 			break;
946 		}
947 	}
948 
949 	char rawName[nameLength + 4];
950 	strncpy(rawName, name, lastPart + 1);
951 	rawName[lastPart + 1] = 0;
952 	strcat(rawName, "raw");
953 	TRACE("opening raw device %s for %s\n", rawName, name);
954 
955 	mutex_lock(&gDeviceListLock);
956 	disk_device *device = gDeviceList;
957 	while (device) {
958 		for (uint8 i = 0; i < device->lun_count; i++) {
959 			device_lun *lun = device->luns[i];
960 			if (strncmp(rawName, lun->name, 32) == 0) {
961 				// found the matching device/lun
962 				if (device->removed) {
963 					mutex_unlock(&gDeviceListLock);
964 					return B_ERROR;
965 				}
966 
967 				device->open_count++;
968 				*cookie = lun;
969 				mutex_unlock(&gDeviceListLock);
970 				return B_OK;
971 			}
972 		}
973 
974 		device = (disk_device *)device->link;
975 	}
976 
977 	mutex_unlock(&gDeviceListLock);
978 	return B_NAME_NOT_FOUND;
979 }
980 
981 
982 static status_t
983 usb_disk_close(void *cookie)
984 {
985 	TRACE("close()\n");
986 	device_lun *lun = (device_lun *)cookie;
987 	disk_device *device = lun->device;
988 
989 	mutex_lock(&device->lock);
990 	if (!device->removed)
991 		usb_disk_synchronize(lun, false);
992 	mutex_unlock(&device->lock);
993 
994 	return B_OK;
995 }
996 
997 
998 static status_t
999 usb_disk_free(void *cookie)
1000 {
1001 	TRACE("free()\n");
1002 	mutex_lock(&gDeviceListLock);
1003 
1004 	device_lun *lun = (device_lun *)cookie;
1005 	disk_device *device = lun->device;
1006 	device->open_count--;
1007 	if (device->open_count == 0 && device->removed) {
1008 		// we can simply free the device here as it has been removed from
1009 		// the device list in the device removed notification hook
1010 		usb_disk_free_device_and_luns(device);
1011 	}
1012 
1013 	mutex_unlock(&gDeviceListLock);
1014 	return B_OK;
1015 }
1016 
1017 
1018 static status_t
1019 usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
1020 {
1021 	device_lun *lun = (device_lun *)cookie;
1022 	disk_device *device = lun->device;
1023 	mutex_lock(&device->lock);
1024 	if (device->removed) {
1025 		mutex_unlock(&device->lock);
1026 		return B_DEV_NOT_READY;
1027 	}
1028 
1029 	status_t result = B_DEV_INVALID_IOCTL;
1030 	switch (op) {
1031 		case B_GET_MEDIA_STATUS:
1032 		{
1033 			*(status_t *)buffer = usb_disk_test_unit_ready(lun);
1034 			TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer);
1035 			result = B_OK;
1036 			break;
1037 		}
1038 
1039 		case B_GET_GEOMETRY:
1040 		{
1041 			if (lun->media_changed) {
1042 				result = usb_disk_update_capacity(lun);
1043 				if (result != B_OK)
1044 					break;
1045 			}
1046 
1047 			device_geometry *geometry = (device_geometry *)buffer;
1048 			geometry->bytes_per_sector = lun->block_size;
1049 			geometry->cylinder_count = lun->block_count;
1050 			geometry->sectors_per_track = geometry->head_count = 1;
1051 			geometry->device_type = lun->device_type;
1052 			geometry->removable = lun->removable;
1053 			geometry->read_only = lun->write_protected;
1054 			geometry->write_once = (lun->device_type == B_WORM);
1055 			TRACE("B_GET_GEOMETRY: %ld sectors at %ld bytes per sector\n",
1056 				geometry->cylinder_count, geometry->bytes_per_sector);
1057 			result = B_OK;
1058 			break;
1059 		}
1060 
1061 		case B_FLUSH_DRIVE_CACHE:
1062 			TRACE("B_FLUSH_DRIVE_CACHE\n");
1063 			result = usb_disk_synchronize(lun, true);
1064 			break;
1065 
1066 		case B_EJECT_DEVICE:
1067 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 2,
1068 				NULL, NULL, false);
1069 			break;
1070 
1071 		case B_LOAD_MEDIA:
1072 			result = usb_disk_operation(lun, SCSI_START_STOP_UNIT_6, 6, 0, 3,
1073 				NULL, NULL, false);
1074 			break;
1075 
1076 #if HAIKU_TARGET_PLATFORM_HAIKU
1077 		case B_GET_ICON:
1078 			// We don't support this legacy ioctl anymore, but the two other
1079 			// icon ioctls below instead.
1080 			break;
1081 
1082 		case B_GET_ICON_NAME:
1083 			result = user_strlcpy((char *)buffer,
1084 				"devices/drive-removable-media-usb", B_FILE_NAME_LENGTH);
1085 			break;
1086 
1087 		case B_GET_VECTOR_ICON:
1088 		{
1089 			if (length != sizeof(device_icon)) {
1090 				result = B_BAD_VALUE;
1091 				break;
1092 			}
1093 
1094 			device_icon iconData;
1095 			if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) {
1096 				result = B_BAD_ADDRESS;
1097 				break;
1098 			}
1099 
1100 			if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) {
1101 				if (user_memcpy(iconData.icon_data, kDeviceIcon,
1102 						sizeof(kDeviceIcon)) != B_OK) {
1103 					result = B_BAD_ADDRESS;
1104 					break;
1105 				}
1106 			}
1107 
1108 			iconData.icon_size = sizeof(kDeviceIcon);
1109 			result = user_memcpy(buffer, &iconData, sizeof(device_icon));
1110 			break;
1111 		}
1112 #endif
1113 
1114 		default:
1115 			TRACE_ALWAYS("unhandled ioctl %ld\n", op);
1116 			break;
1117 	}
1118 
1119 	mutex_unlock(&device->lock);
1120 	return result;
1121 }
1122 
1123 
1124 static status_t
1125 usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
1126 {
1127 	if (buffer == NULL || length == NULL)
1128 		return B_BAD_VALUE;
1129 
1130 	TRACE("read(%lld, %ld)\n", position, *length);
1131 	device_lun *lun = (device_lun *)cookie;
1132 	disk_device *device = lun->device;
1133 	mutex_lock(&device->lock);
1134 	if (device->removed) {
1135 		*length = 0;
1136 		mutex_unlock(&device->lock);
1137 		return B_DEV_NOT_READY;
1138 	}
1139 
1140 	status_t result = B_ERROR;
1141 	uint32 blockPosition = 0;
1142 	uint16 blockCount = 0;
1143 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length,
1144 		blockPosition, blockCount);
1145 	if (needsPartial) {
1146 		void *partialBuffer = NULL;
1147 		void *blockBuffer = NULL;
1148 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1149 			partialBuffer, blockBuffer, blockPosition, blockCount);
1150 		if (result == B_OK) {
1151 			memcpy(buffer, partialBuffer, *length);
1152 			free(blockBuffer);
1153 		}
1154 	} else {
1155 		result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
1156 			length);
1157 	}
1158 
1159 	mutex_unlock(&device->lock);
1160 	if (result == B_OK) {
1161 		TRACE("read successful with %ld bytes\n", *length);
1162 		return B_OK;
1163 	}
1164 
1165 	*length = 0;
1166 	TRACE_ALWAYS("read fails with 0x%08lx\n", result);
1167 	return result;
1168 }
1169 
1170 
1171 static status_t
1172 usb_disk_write(void *cookie, off_t position, const void *buffer,
1173 	size_t *length)
1174 {
1175 	if (buffer == NULL || length == NULL)
1176 		return B_BAD_VALUE;
1177 
1178 	TRACE("write(%lld, %ld)\n", position, *length);
1179 	device_lun *lun = (device_lun *)cookie;
1180 	disk_device *device = lun->device;
1181 	mutex_lock(&device->lock);
1182 	if (device->removed) {
1183 		*length = 0;
1184 		mutex_unlock(&device->lock);
1185 		return B_DEV_NOT_READY;
1186 	}
1187 
1188 	status_t result = B_ERROR;
1189 	uint32 blockPosition = 0;
1190 	uint16 blockCount = 0;
1191 	bool needsPartial = usb_disk_needs_partial_buffer(lun, position,
1192 		*length, blockPosition, blockCount);
1193 	if (needsPartial) {
1194 		void *partialBuffer = NULL;
1195 		void *blockBuffer = NULL;
1196 		result = usb_disk_prepare_partial_buffer(lun, position, *length,
1197 			partialBuffer, blockBuffer, blockPosition, blockCount);
1198 		if (result == B_OK) {
1199 			memcpy(partialBuffer, buffer, *length);
1200 			size_t blockLength = blockCount * lun->block_size;
1201 			result = usb_disk_block_write(lun, blockPosition, blockCount,
1202 				blockBuffer, &blockLength);
1203 			free(blockBuffer);
1204 		}
1205 	} else {
1206 		result = usb_disk_block_write(lun, blockPosition, blockCount,
1207 			(void *)buffer, length);
1208 	}
1209 
1210 	mutex_unlock(&device->lock);
1211 	if (result == B_OK) {
1212 		TRACE("write successful with %ld bytes\n", *length);
1213 		return B_OK;
1214 	}
1215 
1216 	*length = 0;
1217 	TRACE_ALWAYS("write fails with 0x%08lx\n", result);
1218 	return result;
1219 }
1220 
1221 
1222 //
1223 //#pragma mark - Driver Entry Points
1224 //
1225 
1226 
1227 status_t
1228 init_hardware()
1229 {
1230 	TRACE("init_hardware()\n");
1231 	return B_OK;
1232 }
1233 
1234 
1235 status_t
1236 init_driver()
1237 {
1238 	TRACE("init_driver()\n");
1239 	static usb_notify_hooks notifyHooks = {
1240 		&usb_disk_device_added,
1241 		&usb_disk_device_removed
1242 	};
1243 
1244 	static usb_support_descriptor supportedDevices[] = {
1245 		{ 0x08 /* mass storage */, 0x06 /* SCSI */, 0x50 /* bulk */, 0, 0 },
1246 		{ 0x08 /* mass storage */, 0x02 /* ATAPI */, 0x50 /* bulk */, 0, 0 },
1247 		{ 0x08 /* mass storage */, 0x05 /* ATAPI */, 0x50 /* bulk */, 0, 0 }
1248 	};
1249 
1250 	gDeviceList = NULL;
1251 	gDeviceCount = 0;
1252 	gLunCount = 0;
1253 	mutex_init(&gDeviceListLock, "usb_disk device list lock");
1254 
1255 	TRACE("trying module %s\n", B_USB_MODULE_NAME);
1256 	status_t result = get_module(B_USB_MODULE_NAME,
1257 		(module_info **)&gUSBModule);
1258 	if (result < B_OK) {
1259 		TRACE_ALWAYS("getting module failed 0x%08lx\n", result);
1260 		mutex_destroy(&gDeviceListLock);
1261 		return result;
1262 	}
1263 
1264 	gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 3, NULL);
1265 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
1266 	return B_OK;
1267 }
1268 
1269 
1270 void
1271 uninit_driver()
1272 {
1273 	TRACE("uninit_driver()\n");
1274 	gUSBModule->uninstall_notify(DRIVER_NAME);
1275 	mutex_lock(&gDeviceListLock);
1276 
1277 	if (gDeviceNames) {
1278 		for (int32 i = 0; gDeviceNames[i]; i++)
1279 			free(gDeviceNames[i]);
1280 		free(gDeviceNames);
1281 		gDeviceNames = NULL;
1282 	}
1283 
1284 	mutex_destroy(&gDeviceListLock);
1285 	put_module(B_USB_MODULE_NAME);
1286 }
1287 
1288 
1289 const char **
1290 publish_devices()
1291 {
1292 	TRACE("publish_devices()\n");
1293 	if (gDeviceNames) {
1294 		for (int32 i = 0; gDeviceNames[i]; i++)
1295 			free(gDeviceNames[i]);
1296 		free(gDeviceNames);
1297 		gDeviceNames = NULL;
1298 	}
1299 
1300 	gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1));
1301 	if (gDeviceNames == NULL)
1302 		return NULL;
1303 
1304 	int32 index = 0;
1305 	mutex_lock(&gDeviceListLock);
1306 	disk_device *device = gDeviceList;
1307 	while (device) {
1308 		for (uint8 i = 0; i < device->lun_count; i++)
1309 			gDeviceNames[index++] = strdup(device->luns[i]->name);
1310 
1311 		device = (disk_device *)device->link;
1312 	}
1313 
1314 	gDeviceNames[index++] = NULL;
1315 	mutex_unlock(&gDeviceListLock);
1316 	return (const char **)gDeviceNames;
1317 }
1318 
1319 
1320 device_hooks *
1321 find_device(const char *name)
1322 {
1323 	TRACE("find_device()\n");
1324 	static device_hooks hooks = {
1325 		&usb_disk_open,
1326 		&usb_disk_close,
1327 		&usb_disk_free,
1328 		&usb_disk_ioctl,
1329 		&usb_disk_read,
1330 		&usb_disk_write,
1331 		NULL,
1332 		NULL,
1333 		NULL,
1334 		NULL
1335 	};
1336 
1337 	return &hooks;
1338 }
1339