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