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