diff --git a/src/add-ons/kernel/drivers/disk/usb/Jamfile b/src/add-ons/kernel/drivers/disk/usb/Jamfile index 5a85aea58b..15974936a3 100644 --- a/src/add-ons/kernel/drivers/disk/usb/Jamfile +++ b/src/add-ons/kernel/drivers/disk/usb/Jamfile @@ -1,3 +1,4 @@ SubDir HAIKU_TOP src add-ons kernel drivers disk usb ; SubInclude HAIKU_TOP src add-ons kernel drivers disk usb usb_disk ; +SubInclude HAIKU_TOP src add-ons kernel drivers disk usb usb_floppy ; diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_floppy/Jamfile b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/Jamfile new file mode 100644 index 0000000000..323b908449 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/Jamfile @@ -0,0 +1,8 @@ +SubDir HAIKU_TOP src add-ons kernel drivers disk usb usb_floppy ; + +SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel bus_managers usb ; +UsePrivateHeaders kernel ; + +KernelAddon usb_floppy : + usb_disk.cpp + ; diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.cpp b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.cpp new file mode 100644 index 0000000000..1007c17a58 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.cpp @@ -0,0 +1,1298 @@ +/* + * Copyright 2008-2009, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#include +#include +#include +#include +#include +#include +#include "usb_disk.h" +#include "usb_disk_scsi.h" + + +#define DRIVER_NAME "usb_floppy" +#define DEVICE_NAME_BASE "disk/ufi/" +#define DEVICE_NAME DEVICE_NAME_BASE"%ld/%d/raw" + + +#define TRACE_USB_DISK +#ifdef TRACE_USB_DISK +#define TRACE(x...) dprintf(DRIVER_NAME": "x) +#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME": "x) +#else +#define TRACE(x...) /* nothing */ +#define TRACE_ALWAYS(x...) dprintf(DRIVER_NAME": "x) +#endif + + +int32 api_version = B_CUR_DRIVER_API_VERSION; +static usb_module_info *gUSBModule = NULL; +static disk_device *gDeviceList = NULL; +static uint32 gDeviceCount = 0; +static uint32 gLunCount = 0; +static mutex gDeviceListLock; +static char **gDeviceNames = NULL; + +static uint8 kDeviceIcon[] = { + 0x6e, 0x63, 0x69, 0x66, 0x0a, 0x04, 0x01, 0x73, 0x05, 0x01, 0x02, 0x01, + 0x06, 0x02, 0xb1, 0xf8, 0x5d, 0x3a, 0x2f, 0xbf, 0xbe, 0xdb, 0x67, 0xb6, + 0x98, 0x06, 0x4b, 0x22, 0x15, 0x47, 0x13, 0x02, 0x00, 0xed, 0xed, 0xed, + 0xff, 0xab, 0xbc, 0xc6, 0x02, 0x01, 0x06, 0x02, 0xb9, 0x82, 0x56, 0x32, + 0x7d, 0xfb, 0xb8, 0x06, 0x39, 0xbe, 0xd9, 0xb5, 0x4b, 0x7d, 0x31, 0x4a, + 0xa4, 0xe7, 0x00, 0xd1, 0xde, 0xe4, 0xff, 0x7a, 0x9c, 0xae, 0x02, 0x00, + 0x16, 0x02, 0x38, 0xe9, 0xaa, 0x3b, 0x7b, 0x1d, 0xbf, 0xb0, 0xa6, 0x3d, + 0x16, 0x76, 0x4b, 0x84, 0x81, 0x48, 0x37, 0x36, 0x00, 0x99, 0xff, 0x53, + 0x02, 0x00, 0x16, 0x02, 0xba, 0x38, 0x9a, 0xb8, 0xef, 0x79, 0x3e, 0x34, + 0x8b, 0xbf, 0x56, 0x52, 0x48, 0x2c, 0x61, 0x4c, 0x4e, 0xec, 0x00, 0x40, + 0xff, 0x01, 0x05, 0xff, 0x05, 0x46, 0x02, 0x01, 0x16, 0x02, 0x35, 0xc2, + 0x71, 0x3a, 0xf6, 0x84, 0xb9, 0xf3, 0x5b, 0x34, 0x81, 0xa0, 0x49, 0xc0, + 0x57, 0x49, 0x6e, 0x51, 0xff, 0xf3, 0x00, 0x52, 0x02, 0x01, 0x06, 0x02, + 0x38, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x80, 0x00, + 0x49, 0xa0, 0x00, 0x49, 0x80, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x06, + 0xe3, 0x06, 0x0c, 0x06, 0x09, 0xab, 0xaa, 0x03, 0x3e, 0x5e, 0x3c, 0x5f, + 0x3e, 0x5e, 0x60, 0x4d, 0x5a, 0x4a, 0x60, 0x47, 0x56, 0x42, 0x50, 0x45, + 0x4c, 0x43, 0x2a, 0x54, 0x36, 0x5d, 0x36, 0x5d, 0x3a, 0x60, 0x06, 0x08, + 0xfb, 0xea, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x37, 0x59, + 0x37, 0x59, 0x38, 0x5a, 0x3b, 0x59, 0x3a, 0x5a, 0x3b, 0x59, 0x58, 0x3c, + 0x52, 0x36, 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x06, 0x05, + 0xab, 0x03, 0x27, 0x49, 0x26, 0x48, 0x27, 0x49, 0x32, 0x56, 0x52, 0x36, + 0x43, 0x29, 0x27, 0x45, 0x27, 0x45, 0x26, 0x46, 0x0a, 0x05, 0xc2, 0x1c, + 0xb8, 0xf9, 0x4f, 0x25, 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x51, 0x39, + 0x0a, 0x04, 0xc5, 0x50, 0xbb, 0xc0, 0xc2, 0x1c, 0xb8, 0xf9, 0x4f, 0x25, + 0xc9, 0x4c, 0xb7, 0xc4, 0x0a, 0x04, 0x51, 0x39, 0xc5, 0x50, 0xbb, 0xc0, + 0xc9, 0x4c, 0xb7, 0xc4, 0x5a, 0x30, 0x0a, 0x04, 0x4f, 0x2f, 0x51, 0x31, + 0x53, 0x2f, 0x51, 0x2d, 0x06, 0x04, 0xee, 0x4f, 0x35, 0x53, 0x30, 0x51, + 0x32, 0x55, 0x2e, 0x58, 0x2c, 0x54, 0x31, 0x56, 0x2f, 0x52, 0x33, 0x06, + 0x04, 0xee, 0x31, 0x58, 0x40, 0x47, 0x39, 0x4e, 0x47, 0x40, 0x50, 0x38, + 0x41, 0x48, 0x48, 0x41, 0x3a, 0x4f, 0x08, 0x02, 0x3a, 0x40, 0x3e, 0x3c, + 0x02, 0x04, 0x3e, 0x3a, 0xbe, 0x48, 0x3a, 0xbf, 0x9f, 0x3a, 0x41, 0x3d, + 0x41, 0xbd, 0xe2, 0x41, 0xbf, 0x39, 0x3e, 0x40, 0xbf, 0x9f, 0x40, 0xbe, + 0x48, 0x40, 0x3b, 0x3d, 0x3b, 0xbf, 0x39, 0x3b, 0xbd, 0xe2, 0x06, 0x05, + 0xbe, 0x02, 0x32, 0x56, 0x36, 0x5a, 0x36, 0x5a, 0x37, 0x5b, 0x3a, 0x5a, + 0x39, 0x5b, 0x3a, 0x5a, 0x58, 0x3c, 0x52, 0x36, 0x11, 0x0a, 0x00, 0x01, + 0x00, 0x00, 0x0a, 0x01, 0x01, 0x03, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, + 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, + 0xc5, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x02, 0x01, 0x04, 0x02, 0x3f, + 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, + 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x03, 0x01, 0x05, 0x02, 0x3f, 0xe9, + 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, + 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x01, 0x12, 0x3f, 0xe9, 0x8e, + 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0xb0, 0x64, + 0x46, 0x78, 0x3b, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x04, 0x01, 0x02, + 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, + 0x8e, 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x05, 0x01, 0x0b, 0x02, + 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, + 0xc6, 0xb0, 0x64, 0x46, 0x78, 0x3b, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, + 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, + 0x5b, 0x2d, 0x45, 0x43, 0x93, 0x0a, 0x01, 0x01, 0x06, 0x02, 0x3f, 0xe9, + 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc7, 0x7d, + 0x8b, 0x44, 0x36, 0x9a, 0x0a, 0x06, 0x02, 0x07, 0x08, 0x02, 0x3f, 0xe9, + 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x4d, + 0xd9, 0x45, 0xc0, 0xc5, 0x0a, 0x01, 0x01, 0x09, 0x12, 0x3f, 0x6c, 0x5c, + 0xba, 0xea, 0x46, 0x3a, 0xea, 0x46, 0x3f, 0x6c, 0x5c, 0xc5, 0x19, 0x6c, + 0x46, 0x6b, 0x36, 0x01, 0x17, 0x8c, 0x22, 0x04, 0x0a, 0x07, 0x01, 0x09, + 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, + 0x8e, 0xc6, 0x4d, 0xd9, 0x45, 0xc0, 0xc5, 0x01, 0x17, 0x88, 0x22, 0x04, + 0x0a, 0x08, 0x01, 0x09, 0x12, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, + 0x54, 0xe2, 0x3f, 0xe9, 0x8e, 0xc6, 0x01, 0xed, 0x46, 0x11, 0xa8, 0x01, + 0x17, 0x85, 0x22, 0x04, 0x0a, 0x01, 0x01, 0x0a, 0x12, 0x3f, 0xe9, 0x8e, + 0xbb, 0x54, 0xe2, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, 0xc6, 0x59, 0xd0, + 0x46, 0xdb, 0x8c, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, 0x06, 0x01, 0x0a, + 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, 0xe2, 0x3f, 0xe9, + 0x8e, 0xc6, 0x99, 0xc6, 0x45, 0x5e, 0x3a, 0x0a, 0x01, 0x01, 0x0a, 0x12, + 0x3f, 0x21, 0x43, 0xba, 0xaa, 0x52, 0x3a, 0xaa, 0x52, 0x3f, 0x21, 0x43, + 0xc7, 0xd2, 0xa7, 0x49, 0x5f, 0xed, 0x01, 0x17, 0x84, 0x00, 0x04, 0x0a, + 0x09, 0x01, 0x0a, 0x02, 0x3f, 0xe9, 0x8e, 0xbb, 0x54, 0xe2, 0x3b, 0x54, + 0xe2, 0x3f, 0xe9, 0x8e, 0xc8, 0xa5, 0xc8, 0x48, 0xeb, 0x05 +}; + + +// +//#pragma mark - Forward Declarations +// + + +static void usb_disk_callback(void *cookie, status_t status, void *data, + size_t actualLength); + +status_t usb_disk_mass_storage_reset(disk_device *device); +uint8 usb_disk_get_max_lun(disk_device *device); +void usb_disk_reset_recovery(disk_device *device); +status_t usb_disk_transfer_data(disk_device *device, bool directionIn, + void *data, size_t dataLength); +status_t usb_disk_receive_csw(disk_device *device, + command_status_wrapper *status); +status_t usb_disk_operation(device_lun *lun, uint8* operation, + uint32 logicalBlockAddress, + uint16 transferLength, void *data, uint32 *dataLength, + bool directionIn); + +status_t usb_disk_request_sense(device_lun *lun); +status_t usb_disk_mode_sense(device_lun *lun); +status_t usb_disk_test_unit_ready(device_lun *lun); +status_t usb_disk_inquiry(device_lun *lun); +status_t usb_disk_reset_capacity(device_lun *lun); +status_t usb_disk_update_capacity(device_lun *lun); +status_t usb_disk_synchronize(device_lun *lun, bool force); + + +// +//#pragma mark - Device Allocation Helper Functions +// + + +// FIXME: should be part of the device struct. +static unsigned char interruptBuffer[2]; +sem_id interruptLock; + + +void +usb_disk_free_device_and_luns(disk_device *device) +{ + mutex_lock(&device->lock); + mutex_destroy(&device->lock); + delete_sem(device->notify); + delete_sem(interruptLock); + for (uint8 i = 0; i < device->lun_count; i++) + free(device->luns[i]); + free(device->luns); + free(device); +} + + +// +//#pragma mark - Bulk-only Mass Storage Functions +// + + +void +usb_disk_reset_recovery(disk_device *device) +{ + gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT); + gUSBModule->clear_feature(device->bulk_out, USB_FEATURE_ENDPOINT_HALT); +} + + +status_t +usb_disk_transfer_data(disk_device *device, bool directionIn, void *data, + size_t dataLength) +{ + status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in + : device->bulk_out, data, dataLength, usb_disk_callback, device); + if (result != B_OK) { + TRACE_ALWAYS("failed to queue data transfer\n"); + return result; + } + + do { + result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, + 10 * 1000 * 1000); + if (result == B_TIMED_OUT) { + // Cancel the transfer and collect the sem that should now be + // released through the callback on cancel. Handling of device + // reset is done in usb_disk_operation() when it detects that + // the transfer failed. + gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in + : device->bulk_out); + acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0); + } + } while (result == B_INTERRUPTED); + + if (result != B_OK) { + TRACE_ALWAYS("acquire_sem failed while waiting for data transfer\n"); + return result; + } + + return B_OK; +} + + +void +usb_disk_interrupt(void* cookie, int32 status, void* data, uint32 length) +{ + char* stRep = (char*)data; + TRACE("interrupt of length %ld! status: %d misc: %d\n", length, + stRep[0], stRep[1]); + release_sem(interruptLock); + + gUSBModule->queue_interrupt(((disk_device*)cookie)->interrupt, + interruptBuffer, 2, usb_disk_interrupt, cookie); +} + + +status_t +usb_disk_receive_csw(disk_device *device, command_status_wrapper *status) +{ + TRACE("Waiting for result...\n"); + gUSBModule->queue_interrupt(device->interrupt, + interruptBuffer, 2, usb_disk_interrupt, device); + + acquire_sem(interruptLock); + + status->status = interruptBuffer[0]; + status->misc = interruptBuffer[1]; + + return B_OK; +} + + +status_t +usb_disk_operation(device_lun *lun, uint8* operation, + uint32 logicalBlockAddress, uint16 transferLength, void *data, + uint32 *dataLength, bool directionIn) +{ + TRACE("operation: lun: %u; op: %u; lba: %lu; tlen: %u; data: %p; dlen: %p (%lu); in: %c\n", + lun->logical_unit_number, operation[0], logicalBlockAddress, + transferLength, data, dataLength, dataLength ? *dataLength : 0, + directionIn ? 'y' : 'n'); + + disk_device* device = lun->device; + + // Step 1 : send the SCSI operation as a class specific request + size_t actualLength = 0; + status_t result = gUSBModule->send_request(device->device, + USB_REQTYPE_CLASS | USB_REQTYPE_INTERFACE_OUT, 0, 0, + device->interface, 12, operation, &actualLength); + + if (result != B_OK || actualLength != 12) { + TRACE("Command stage: wrote %ld bytes (%s) (interface = %d)\n", actualLength, strerror(result), + device->interface); + return B_ERROR; + } + + // Step 2 : data phase : send or receive data + size_t transferedData = 0; + if (data != NULL && dataLength != NULL && *dataLength > 0) { + // we have data to transfer in a data stage + result = usb_disk_transfer_data(device, directionIn, data, *dataLength); + if (result != B_OK) + return result; + + transferedData = device->actual_length; + if (device->status != B_OK || transferedData != *dataLength) { + // sending or receiving of the data failed + if (device->status == B_DEV_STALLED) { + TRACE("stall while transfering data\n"); + gUSBModule->clear_feature(directionIn ? device->bulk_in + : device->bulk_out, USB_FEATURE_ENDPOINT_HALT); + } else { + TRACE_ALWAYS("sending or receiving of the data failed\n"); + usb_disk_reset_recovery(device); + return B_ERROR; + } + } + } + + // step 3 : wait for the device to send the interrupt ACK + command_status_wrapper status; + result = usb_disk_receive_csw(device, &status); + if (result != B_OK) { + // in case of a stall or error clear the stall and try again + gUSBModule->clear_feature(device->bulk_in, USB_FEATURE_ENDPOINT_HALT); + result = usb_disk_receive_csw(device, &status); + } + + if (result != B_OK) { + TRACE_ALWAYS("receiving the command status interrupt failed\n"); + usb_disk_reset_recovery(device); + return result; + } + + switch (status.status) { + case 0: + return B_OK; + default: { + // command status wrapper is not meaningful + TRACE_ALWAYS("command status wrapper has invalid status\n"); + usb_disk_reset_recovery(device); + return B_ERROR; + } + } +} + + +// +//#pragma mark - Helper/Convenience Functions +// + + +status_t +usb_disk_request_sense(device_lun *lun) +{ + uint32 dataLength = sizeof(scsi_request_sense_6_parameter); + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_REQUEST_SENSE_6; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[2] = 0; // page code + commandBlock[4] = dataLength; + + scsi_request_sense_6_parameter parameter; + status_t result = usb_disk_operation(lun, commandBlock, 0, + dataLength, ¶meter, &dataLength, true); + if (result != B_OK) { + TRACE_ALWAYS("getting request sense data failed\n"); + return result; + } + + if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY + && parameter.sense_key != SCSI_SENSE_KEY_UNIT_ATTENTION) { + TRACE_ALWAYS("request_sense: key: 0x%02x; asc: 0x%02x; ascq: 0x%02x;\n", + parameter.sense_key, parameter.additional_sense_code, + parameter.additional_sense_code_qualifier); + } + + switch (parameter.sense_key) { + case SCSI_SENSE_KEY_NO_SENSE: + case SCSI_SENSE_KEY_RECOVERED_ERROR: + return B_OK; + + case SCSI_SENSE_KEY_HARDWARE_ERROR: + case SCSI_SENSE_KEY_MEDIUM_ERROR: + TRACE_ALWAYS("request_sense: media or hardware error\n"); + return B_DEV_UNREADABLE; + + case SCSI_SENSE_KEY_ILLEGAL_REQUEST: + TRACE_ALWAYS("request_sense: illegal request\n"); + return B_DEV_INVALID_IOCTL; + + case SCSI_SENSE_KEY_UNIT_ATTENTION: + if (parameter.additional_sense_code != SCSI_ASC_MEDIUM_NOT_PRESENT) { + TRACE_ALWAYS("request_sense: media changed\n"); + lun->media_changed = true; + lun->media_present = true; + return B_DEV_MEDIA_CHANGED; + } + // fall through + + case SCSI_SENSE_KEY_NOT_READY: + TRACE("request_sense: device not ready (asc 0x%02x ascq 0x%02x)\n", + parameter.additional_sense_code, + parameter.additional_sense_code_qualifier); + lun->media_present = false; + usb_disk_reset_capacity(lun); + return B_DEV_NO_MEDIA; + + case SCSI_SENSE_KEY_DATA_PROTECT: + TRACE_ALWAYS("request_sense: write protected\n"); + return B_READ_ONLY_DEVICE; + + case SCSI_SENSE_KEY_ABORTED_COMMAND: + TRACE_ALWAYS("request_sense: command aborted\n"); + return B_CANCELED; + } + + return B_ERROR; +} + + +status_t +usb_disk_mode_sense(device_lun *lun) +{ + uint32 dataLength = sizeof(scsi_mode_sense_6_parameter); + + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_MODE_SENSE_6; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[2] = 0; // Current values + commandBlock[7] = dataLength >> 8; + commandBlock[8] = dataLength; + + scsi_mode_sense_6_parameter parameter; + status_t result = usb_disk_operation(lun, commandBlock, + SCSI_MODE_PAGE_DEVICE_CONFIGURATION, dataLength, ¶meter, + &dataLength, true); + if (result != B_OK) { + TRACE_ALWAYS("getting mode sense data failed\n"); + return result; + } + + lun->write_protected + = (parameter.device_specific & SCSI_DEVICE_SPECIFIC_WRITE_PROTECT) != 0; + TRACE_ALWAYS("write protected: %s\n", lun->write_protected ? "yes" : "no"); + return B_OK; +} + + +status_t +usb_disk_test_unit_ready(device_lun *lun) +{ + // if unsupported we assume the unit is fixed and therefore always ok + if (!lun->device->tur_supported) + return B_OK; + + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_INQUIRY_6; + commandBlock[1] = lun->logical_unit_number << 5; + + status_t result; + result = usb_disk_operation(lun, commandBlock, 0, 0, + NULL, NULL, true); + + if (result == B_DEV_INVALID_IOCTL) { + lun->device->tur_supported = false; + return B_OK; + } + + return result; +} + + +status_t +usb_disk_inquiry(device_lun *lun) +{ + uint32 dataLength = sizeof(scsi_inquiry_6_parameter); + + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_INQUIRY_6; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[2] = 0; // page code + commandBlock[4] = dataLength; + + scsi_inquiry_6_parameter parameter; + status_t result = B_ERROR; + for (uint32 tries = 0; tries < 3; tries++) { + result = usb_disk_operation(lun, commandBlock, 0, dataLength, + ¶meter, &dataLength, true); + if (result == B_OK) + break; + } + if (result != B_OK) { + TRACE_ALWAYS("getting inquiry data failed\n"); + lun->device_type = B_DISK; + lun->removable = true; + return result; + } + + TRACE("peripherial_device_type 0x%02x\n", parameter.peripherial_device_type); + TRACE("peripherial_qualifier 0x%02x\n", parameter.peripherial_qualifier); + TRACE("removable_medium %s\n", parameter.removable_medium ? "yes" : "no"); + TRACE("version 0x%02x\n", parameter.version); + TRACE("response_data_format 0x%02x\n", parameter.response_data_format); + TRACE_ALWAYS("vendor_identification \"%.8s\"\n", parameter.vendor_identification); + TRACE_ALWAYS("product_identification \"%.16s\"\n", parameter.product_identification); + TRACE_ALWAYS("product_revision_level \"%.4s\"\n", parameter.product_revision_level); + lun->device_type = parameter.peripherial_device_type; /* 1:1 mapping */ + lun->removable = (parameter.removable_medium == 1); + return B_OK; +} + + +status_t +usb_disk_reset_capacity(device_lun *lun) +{ + lun->block_size = 512; + lun->block_count = 0; + return B_OK; +} + + +status_t +usb_disk_update_capacity(device_lun *lun) +{ + uint32 dataLength = sizeof(scsi_read_capacity_10_parameter); + scsi_read_capacity_10_parameter parameter; + status_t result = B_ERROR; + + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_READ_CAPACITY_10; + commandBlock[1] = lun->logical_unit_number << 5; + + // Retry reading the capacity up to three times. The first try might only + // yield a unit attention telling us that the device or media status + // changed, which is more or less expected if it is the first operation + // on the device or the device only clears the unit atention for capacity + // reads. + for (int32 i = 0; i < 3; i++) { + result = usb_disk_operation(lun, commandBlock, 0, 0, + ¶meter, &dataLength, true); + if (result == B_OK) + break; + } + + if (result != B_OK) { + TRACE_ALWAYS("failed to update capacity\n"); + lun->media_present = false; + lun->media_changed = false; + usb_disk_reset_capacity(lun); + return result; + } + + lun->media_present = true; + lun->media_changed = false; + lun->block_size = ntohl(parameter.logical_block_length); + lun->block_count = ntohl(parameter.last_logical_block_address) + 1; + return B_OK; +} + + +status_t +usb_disk_synchronize(device_lun *lun, bool force) +{ + return B_UNSUPPORTED; +} + + +// +//#pragma mark - Device Attach/Detach Notifications and Callback +// + + +static void +usb_disk_callback(void *cookie, status_t status, void *data, + size_t actualLength) +{ + //TRACE("callback()\n"); + disk_device *device = (disk_device *)cookie; + device->status = status; + device->actual_length = actualLength; + release_sem(device->notify); +} + + +static status_t +usb_disk_device_added(usb_device newDevice, void **cookie) +{ + TRACE("device_added(0x%08lx)\n", newDevice); + disk_device *device = (disk_device *)malloc(sizeof(disk_device)); + device->device = newDevice; + device->removed = false; + device->open_count = 0; + device->interface = 0xff; + device->current_tag = 0; + device->sync_support = SYNC_SUPPORT_RELOAD; + device->tur_supported = true; + device->is_atapi = false; + device->luns = NULL; + + // scan through the interfaces to find our data interface + const usb_configuration_info *configuration + = gUSBModule->get_configuration(newDevice); + if (configuration == NULL) { + free(device); + return B_ERROR; + } + + for (size_t i = 0; i < configuration->interface_count; i++) { + usb_interface_info *interface = configuration->interface[i].active; + if (interface == NULL) + continue; + + if (interface->descr->interface_class == 0x08 /* mass storage */ + || interface->descr->interface_subclass == 0x04 /* UFI (floppy) */ + && interface->descr->interface_protocol == 0x00) { + + bool hasIn = false; + bool hasOut = false; + bool hasInt = false; + for (size_t j = 0; j < interface->endpoint_count; j++) { + usb_endpoint_info *endpoint = &interface->endpoint[j]; + if (endpoint == NULL) + continue; + + if (!hasIn && (endpoint->descr->endpoint_address + & USB_ENDPOINT_ADDR_DIR_IN) + && endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) { + device->bulk_in = endpoint->handle; + hasIn = true; + } else if (!hasOut && (endpoint->descr->endpoint_address + & USB_ENDPOINT_ADDR_DIR_IN) == 0 + && endpoint->descr->attributes == USB_ENDPOINT_ATTR_BULK) { + device->bulk_out = endpoint->handle; + hasOut = true; + } else if (!hasInt && (endpoint->descr->endpoint_address + & USB_ENDPOINT_ADDR_DIR_IN) + && endpoint->descr->attributes + == USB_ENDPOINT_ATTR_INTERRUPT) { + // TODO:ensure there is only one interrupt endpoint and + // stop enumerating + device->interrupt = endpoint->handle; + hasInt = true; + } + + if (hasIn && hasOut && hasInt) + break; + } + + if (!(hasIn && hasOut && hasInt)) { + TRACE("in: %d, out: %d, int: %d\n", hasIn, hasOut, hasInt); + continue; + } + + device->interface = interface->descr->interface_number; + device->is_atapi = interface->descr->interface_subclass != 0x06; + break; + } + } + + if (device->interface == 0xff) { + TRACE_ALWAYS("no valid CBI interface found\n"); + free(device); + return B_ERROR; + } + + mutex_init(&device->lock, "usb_disk device lock"); + + device->notify = create_sem(0, "usb_disk callback notify"); + if (device->notify < B_OK) { + mutex_destroy(&device->lock); + free(device); + return device->notify; + } + + interruptLock = create_sem(0, "usb_disk interrupt lock"); + if (interruptLock < B_OK) { + mutex_destroy(&device->lock); + free(device); + return interruptLock; + } + + // TODO: handle more than 1 unit + device->lun_count = 1; + device->luns = (device_lun **)malloc(device->lun_count + * sizeof(device_lun *)); + for (uint8 i = 0; i < device->lun_count; i++) + device->luns[i] = NULL; + + status_t result = B_OK; + + TRACE_ALWAYS("device reports a lun count of %d\n", device->lun_count); + for (uint8 i = 0; i < device->lun_count; i++) { + // create the individual luns present on this device + device_lun *lun = (device_lun *)malloc(sizeof(device_lun)); + if (lun == NULL) { + result = B_NO_MEMORY; + break; + } + + device->luns[i] = lun; + lun->device = device; + lun->logical_unit_number = i; + lun->should_sync = false; + lun->media_present = true; + lun->media_changed = true; + usb_disk_reset_capacity(lun); + + // initialize this lun + result = usb_disk_inquiry(lun); + for (uint32 tries = 0; tries < 3; tries++) { + status_t ready = usb_disk_test_unit_ready(lun); + if (ready == B_OK || ready == B_DEV_NO_MEDIA) { + if (ready == B_OK) { + if (lun->device_type == B_CD) + lun->write_protected = true; + // TODO: check for write protection; disabled since + // some devices lock up when getting the mode sense + else if (/*usb_disk_mode_sense(lun) != B_OK*/true) + lun->write_protected = false; + } + + break; + } + + snooze(10000); + } + + if (result != B_OK) + break; + } + + if (result != B_OK) { + TRACE_ALWAYS("failed to initialize logical units\n"); + usb_disk_free_device_and_luns(device); + return result; + } + + mutex_lock(&gDeviceListLock); + device->device_number = 0; + disk_device *other = gDeviceList; + while (other != NULL) { + if (other->device_number >= device->device_number) + device->device_number = other->device_number + 1; + + other = (disk_device *)other->link; + } + + device->link = (void *)gDeviceList; + gDeviceList = device; + gLunCount += device->lun_count; + for (uint8 i = 0; i < device->lun_count; i++) + sprintf(device->luns[i]->name, DEVICE_NAME, device->device_number, i); + mutex_unlock(&gDeviceListLock); + + TRACE("new device: 0x%08lx\n", (uint32)device); + *cookie = (void *)device; + return B_OK; +} + + +static status_t +usb_disk_device_removed(void *cookie) +{ + TRACE("device_removed(0x%08lx)\n", (uint32)cookie); + disk_device *device = (disk_device *)cookie; + + mutex_lock(&gDeviceListLock); + if (gDeviceList == device) { + gDeviceList = (disk_device *)device->link; + } else { + disk_device *element = gDeviceList; + while (element) { + if (element->link == device) { + element->link = device->link; + break; + } + + element = (disk_device *)element->link; + } + } + gLunCount -= device->lun_count; + gDeviceCount--; + + device->removed = true; + gUSBModule->cancel_queued_transfers(device->bulk_in); + gUSBModule->cancel_queued_transfers(device->bulk_out); + if (device->open_count == 0) + usb_disk_free_device_and_luns(device); + + mutex_unlock(&gDeviceListLock); + return B_OK; +} + + +// +//#pragma mark - Partial Buffer Functions +// + + +static bool +usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length, + uint32 &blockPosition, uint16 &blockCount) +{ + blockPosition = (uint32)(position / lun->block_size); + if ((off_t)blockPosition * lun->block_size != position) + return true; + + blockCount = (uint16)(length / lun->block_size); + if ((size_t)blockCount * lun->block_size != length) + return true; + + return false; +} + + +static status_t +usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount, + void *buffer, size_t *length) +{ + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_READ_10; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[2] = blockPosition >> 24; + commandBlock[3] = blockPosition >> 16; + commandBlock[4] = blockPosition >> 8; + commandBlock[5] = blockPosition; + commandBlock[7] = *length >> 8; + commandBlock[8] = *length; + + status_t result = usb_disk_operation(lun, commandBlock, blockPosition, + blockCount, buffer, length, true); + return result; +} + + +static status_t +usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount, + void *buffer, size_t *length) +{ + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_WRITE_10; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[2] = blockPosition >> 24; + commandBlock[3] = blockPosition >> 16; + commandBlock[4] = blockPosition >> 8; + commandBlock[5] = blockPosition; + commandBlock[7] = *length >> 8; + commandBlock[8] = *length; + + status_t result = usb_disk_operation(lun, commandBlock, blockPosition, + blockCount, buffer, length, false); + if (result == B_OK) + lun->should_sync = true; + return result; +} + + +static status_t +usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length, + void *&partialBuffer, void *&blockBuffer, uint32 &blockPosition, + uint16 &blockCount) +{ + blockPosition = (uint32)(position / lun->block_size); + blockCount = (uint16)((uint32)((position + length + lun->block_size - 1) + / lun->block_size) - blockPosition); + size_t blockLength = blockCount * lun->block_size; + blockBuffer = malloc(blockLength); + if (blockBuffer == NULL) { + TRACE_ALWAYS("no memory to allocate partial buffer\n"); + return B_NO_MEMORY; + } + + status_t result = usb_disk_block_read(lun, blockPosition, blockCount, + blockBuffer, &blockLength); + if (result != B_OK) { + TRACE_ALWAYS("block read failed when filling partial buffer\n"); + free(blockBuffer); + return result; + } + + off_t offset = position - (blockPosition * lun->block_size); + partialBuffer = (uint8 *)blockBuffer + offset; + return B_OK; +} + + +// +//#pragma mark - Driver Hooks +// + + +static status_t +usb_disk_open(const char *name, uint32 flags, void **cookie) +{ + TRACE("open(%s)\n", name); + if (strncmp(name, DEVICE_NAME_BASE, strlen(DEVICE_NAME_BASE)) != 0) + return B_NAME_NOT_FOUND; + + int32 lastPart = 0; + size_t nameLength = strlen(name); + for (int32 i = nameLength - 1; i >= 0; i--) { + if (name[i] == '/') { + lastPart = i; + break; + } + } + + char rawName[nameLength + 4]; + strncpy(rawName, name, lastPart + 1); + rawName[lastPart + 1] = 0; + strcat(rawName, "raw"); + TRACE("opening raw device %s for %s\n", rawName, name); + + mutex_lock(&gDeviceListLock); + disk_device *device = gDeviceList; + while (device) { + for (uint8 i = 0; i < device->lun_count; i++) { + device_lun *lun = device->luns[i]; + if (strncmp(rawName, lun->name, 32) == 0) { + // found the matching device/lun + if (device->removed) { + mutex_unlock(&gDeviceListLock); + return B_ERROR; + } + + device->open_count++; + *cookie = lun; + mutex_unlock(&gDeviceListLock); + return B_OK; + } + } + + device = (disk_device *)device->link; + } + + mutex_unlock(&gDeviceListLock); + return B_NAME_NOT_FOUND; +} + + +static status_t +usb_disk_close(void *cookie) +{ + TRACE("close()\n"); + device_lun *lun = (device_lun *)cookie; + disk_device *device = lun->device; + + mutex_lock(&device->lock); + if (!device->removed) + usb_disk_synchronize(lun, false); + mutex_unlock(&device->lock); + + return B_OK; +} + + +static status_t +usb_disk_free(void *cookie) +{ + TRACE("free()\n"); + mutex_lock(&gDeviceListLock); + + device_lun *lun = (device_lun *)cookie; + disk_device *device = lun->device; + device->open_count--; + if (device->open_count == 0 && device->removed) { + // we can simply free the device here as it has been removed from + // the device list in the device removed notification hook + usb_disk_free_device_and_luns(device); + } + + mutex_unlock(&gDeviceListLock); + return B_OK; +} + + +static status_t +usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length) +{ + device_lun *lun = (device_lun *)cookie; + disk_device *device = lun->device; + mutex_lock(&device->lock); + if (device->removed) { + mutex_unlock(&device->lock); + return B_DEV_NOT_READY; + } + + status_t result = B_DEV_INVALID_IOCTL; + switch (op) { + case B_GET_MEDIA_STATUS: { + *(status_t *)buffer = usb_disk_test_unit_ready(lun); + TRACE("B_GET_MEDIA_STATUS: 0x%08lx\n", *(status_t *)buffer); + result = B_OK; + break; + } + + case B_GET_GEOMETRY: { + if (lun->media_changed) { + result = usb_disk_update_capacity(lun); + if (result != B_OK) + break; + } + + device_geometry *geometry = (device_geometry *)buffer; + geometry->bytes_per_sector = lun->block_size; + geometry->cylinder_count = lun->block_count; + geometry->sectors_per_track = geometry->head_count = 1; + geometry->device_type = lun->device_type; + geometry->removable = lun->removable; + geometry->read_only = lun->write_protected; + geometry->write_once = (lun->device_type == B_WORM); + TRACE("B_GET_GEOMETRY: %ld sectors at %ld bytes per sector\n", + geometry->cylinder_count, geometry->bytes_per_sector); + result = B_OK; + break; + } + + case B_FLUSH_DRIVE_CACHE: + TRACE("B_FLUSH_DRIVE_CACHE\n"); + result = usb_disk_synchronize(lun, true); + break; + + case B_EJECT_DEVICE: + { + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_START_STOP_UNIT_6; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[4] = 2; + + result = usb_disk_operation(lun, commandBlock, 0, 2, + NULL, NULL, false); + break; + } + + case B_LOAD_MEDIA: + { + uint8 commandBlock[12]; + memset(commandBlock, 0, sizeof(commandBlock)); + + commandBlock[0] = SCSI_START_STOP_UNIT_6; + commandBlock[1] = lun->logical_unit_number << 5; + commandBlock[4] = 3; + + result = usb_disk_operation(lun, commandBlock, 0, 3, + NULL, NULL, false); + break; + } + +#if HAIKU_TARGET_PLATFORM_HAIKU + case B_GET_ICON: + // We don't support this legacy ioctl anymore, but the two other + // icon ioctls below instead. + break; + + case B_GET_ICON_NAME: + result = user_strlcpy((char *)buffer, + "devices/drive-removable-media-usb", B_FILE_NAME_LENGTH); + break; + + case B_GET_VECTOR_ICON: + { + if (length != sizeof(device_icon)) { + result = B_BAD_VALUE; + break; + } + + device_icon iconData; + if (user_memcpy(&iconData, buffer, sizeof(device_icon)) != B_OK) { + result = B_BAD_ADDRESS; + break; + } + + if (iconData.icon_size >= (int32)sizeof(kDeviceIcon)) { + if (user_memcpy(iconData.icon_data, kDeviceIcon, + sizeof(kDeviceIcon)) != B_OK) { + result = B_BAD_ADDRESS; + break; + } + } + + iconData.icon_size = sizeof(kDeviceIcon); + result = user_memcpy(buffer, &iconData, sizeof(device_icon)); + break; + } +#endif + + default: + TRACE_ALWAYS("unhandled ioctl %ld\n", op); + break; + } + + mutex_unlock(&device->lock); + return result; +} + + +static status_t +usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length) +{ + if (buffer == NULL || length == NULL) + return B_BAD_VALUE; + + TRACE("read(%lld, %ld)\n", position, *length); + device_lun *lun = (device_lun *)cookie; + disk_device *device = lun->device; + mutex_lock(&device->lock); + if (device->removed) { + *length = 0; + mutex_unlock(&device->lock); + return B_DEV_NOT_READY; + } + + status_t result = B_ERROR; + uint32 blockPosition = 0; + uint16 blockCount = 0; + bool needsPartial = usb_disk_needs_partial_buffer(lun, position, *length, + blockPosition, blockCount); + if (needsPartial) { + void *partialBuffer = NULL; + void *blockBuffer = NULL; + result = usb_disk_prepare_partial_buffer(lun, position, *length, + partialBuffer, blockBuffer, blockPosition, blockCount); + if (result == B_OK) { + memcpy(buffer, partialBuffer, *length); + free(blockBuffer); + } + } else { + result = usb_disk_block_read(lun, blockPosition, blockCount, buffer, + length); + } + + mutex_unlock(&device->lock); + if (result == B_OK) { + TRACE("read successful with %ld bytes\n", *length); + return B_OK; + } + + *length = 0; + TRACE_ALWAYS("read fails with 0x%08lx\n", result); + return result; +} + + +static status_t +usb_disk_write(void *cookie, off_t position, const void *buffer, + size_t *length) +{ + if (buffer == NULL || length == NULL) + return B_BAD_VALUE; + + TRACE("write(%lld, %ld)\n", position, *length); + device_lun *lun = (device_lun *)cookie; + disk_device *device = lun->device; + mutex_lock(&device->lock); + if (device->removed) { + *length = 0; + mutex_unlock(&device->lock); + return B_DEV_NOT_READY; + } + + status_t result = B_ERROR; + uint32 blockPosition = 0; + uint16 blockCount = 0; + bool needsPartial = usb_disk_needs_partial_buffer(lun, position, + *length, blockPosition, blockCount); + if (needsPartial) { + void *partialBuffer = NULL; + void *blockBuffer = NULL; + result = usb_disk_prepare_partial_buffer(lun, position, *length, + partialBuffer, blockBuffer, blockPosition, blockCount); + if (result == B_OK) { + memcpy(partialBuffer, buffer, *length); + size_t blockLength = blockCount * lun->block_size; + result = usb_disk_block_write(lun, blockPosition, blockCount, + blockBuffer, &blockLength); + free(blockBuffer); + } + } else { + result = usb_disk_block_write(lun, blockPosition, blockCount, + (void *)buffer, length); + } + + mutex_unlock(&device->lock); + if (result == B_OK) { + TRACE("write successful with %ld bytes\n", *length); + return B_OK; + } + + *length = 0; + TRACE_ALWAYS("write fails with 0x%08lx\n", result); + return result; +} + + +// +//#pragma mark - Driver Entry Points +// + + +status_t +init_hardware() +{ + TRACE("init_hardware()\n"); + return B_OK; +} + + +status_t +init_driver() +{ + TRACE("init_driver()\n"); + static usb_notify_hooks notifyHooks = { + &usb_disk_device_added, + &usb_disk_device_removed + }; + + static usb_support_descriptor supportedDevices[] = { + { 0x08 /* mass storage */, 0x04 /* UFI */, 0x00, 0, 0 } + }; + + gDeviceList = NULL; + gDeviceCount = 0; + gLunCount = 0; + mutex_init(&gDeviceListLock, "usb_disk device list lock"); + + TRACE("trying module %s\n", B_USB_MODULE_NAME); + status_t result = get_module(B_USB_MODULE_NAME, + (module_info **)&gUSBModule); + if (result < B_OK) { + TRACE_ALWAYS("getting module failed 0x%08lx\n", result); + mutex_destroy(&gDeviceListLock); + return result; + } + + gUSBModule->register_driver(DRIVER_NAME, supportedDevices, 3, NULL); + gUSBModule->install_notify(DRIVER_NAME, ¬ifyHooks); + return B_OK; +} + + +void +uninit_driver() +{ + TRACE("uninit_driver()\n"); + gUSBModule->uninstall_notify(DRIVER_NAME); + mutex_lock(&gDeviceListLock); + + if (gDeviceNames) { + for (int32 i = 0; gDeviceNames[i]; i++) + free(gDeviceNames[i]); + free(gDeviceNames); + gDeviceNames = NULL; + } + + mutex_destroy(&gDeviceListLock); + put_module(B_USB_MODULE_NAME); +} + + +const char ** +publish_devices() +{ + TRACE("publish_devices()\n"); + if (gDeviceNames) { + for (int32 i = 0; gDeviceNames[i]; i++) + free(gDeviceNames[i]); + free(gDeviceNames); + gDeviceNames = NULL; + } + + gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1)); + if (gDeviceNames == NULL) + return NULL; + + int32 index = 0; + mutex_lock(&gDeviceListLock); + disk_device *device = gDeviceList; + while (device) { + for (uint8 i = 0; i < device->lun_count; i++) + gDeviceNames[index++] = strdup(device->luns[i]->name); + + device = (disk_device *)device->link; + } + + gDeviceNames[index++] = NULL; + mutex_unlock(&gDeviceListLock); + return (const char **)gDeviceNames; +} + + +device_hooks * +find_device(const char *name) +{ + TRACE("find_device()\n"); + static device_hooks hooks = { + &usb_disk_open, + &usb_disk_close, + &usb_disk_free, + &usb_disk_ioctl, + &usb_disk_read, + &usb_disk_write, + NULL, + NULL, + NULL, + NULL + }; + + return &hooks; +} diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.h b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.h new file mode 100644 index 0000000000..e10fe9b728 --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk.h @@ -0,0 +1,97 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _USB_DISK_H_ +#define _USB_DISK_H_ + +#include +#include + +#define REQUEST_MASS_STORAGE_RESET 0xff +#define REQUEST_GET_MAX_LUN 0xfe +#define MAX_LOGICAL_UNIT_NUMBER 15 +#define ATAPI_COMMAND_LENGTH 12 + +#define CBW_SIGNATURE 0x43425355 +#define CBW_DATA_OUTPUT 0x00 +#define CBW_DATA_INPUT 0x80 + +#define CSW_SIGNATURE 0x53425355 +#define CSW_STATUS_COMMAND_PASSED 0x00 +#define CSW_STATUS_COMMAND_FAILED 0x01 +#define CSW_STATUS_PHASE_ERROR 0x02 + +#define SYNC_SUPPORT_RELOAD 5 + +typedef struct device_lun_s device_lun; + +// holds common information about an attached device (pointed to by luns) +typedef struct disk_device_s { + usb_device device; + uint32 device_number; + bool removed; + uint32 open_count; + mutex lock; + void * link; + + // device state + usb_pipe bulk_in; + usb_pipe bulk_out; + usb_pipe interrupt; + uint8 interface; + uint32 current_tag; + uint8 sync_support; + bool tur_supported; + bool is_atapi; + + // used to store callback information + sem_id notify; + status_t status; + size_t actual_length; + + // logical units of this device + uint8 lun_count; + device_lun **luns; +} disk_device; + + +// represents a logical unit on the pointed to device - this gets published +struct device_lun_s { + disk_device *device; + char name[32]; + uint8 logical_unit_number; + bool should_sync; + + // device information through read capacity/inquiry + bool media_present; + bool media_changed; + uint32 block_count; + uint32 block_size; + uint8 device_type; + bool removable; + bool write_protected; +}; + + +typedef struct command_block_wrapper_s { + uint32 signature; + uint32 tag; + uint32 data_transfer_length; + uint8 flags; + uint8 lun; + uint8 command_block_length; + uint8 command_block[16]; +} _PACKED command_block_wrapper; + + +typedef struct command_status_wrapper_s { + uint8 status; + uint8 misc; +} _PACKED command_status_wrapper; + +#endif // _USB_DISK_H_ diff --git a/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk_scsi.h b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk_scsi.h new file mode 100644 index 0000000000..c4d09a3e8e --- /dev/null +++ b/src/add-ons/kernel/drivers/disk/usb/usb_floppy/usb_disk_scsi.h @@ -0,0 +1,91 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Michael Lotz + */ + +#ifndef _USB_DISK_SCSI_H_ +#define _USB_DISK_SCSI_H_ + +typedef enum { + SCSI_TEST_UNIT_READY_6 = 0x00, + SCSI_REQUEST_SENSE_6 = 0x03, + SCSI_INQUIRY_6 = 0x12, + SCSI_MODE_SENSE_6 = 0x1a, + SCSI_START_STOP_UNIT_6 = 0x1b, + SCSI_READ_CAPACITY_10 = 0x25, + SCSI_READ_10 = 0x28, + SCSI_WRITE_10 = 0x2a, + SCSI_SYNCHRONIZE_CACHE_10 = 0x35, +} scsi_operations; + +// parameters = returned data +typedef struct scsi_request_sense_6_parameter_s { + uint8 error_code : 7; + uint8 valid : 1; + uint8 segment_number; + uint8 sense_key : 4; + uint8 unused_1 : 4; + uint32 information; + uint8 additional_sense_length; + uint32 command_specific_information; + uint8 additional_sense_code; + uint8 additional_sense_code_qualifier; + uint8 field_replacable_unit_code; + uint8 sense_key_specific[3]; +} _PACKED scsi_request_sense_6_parameter; + +typedef struct scsi_inquiry_6_parameter_s { + uint8 peripherial_device_type : 5; + uint8 peripherial_qualifier : 3; + uint8 reserved_1 : 7; + uint8 removable_medium : 1; + uint8 version; + uint8 response_data_format : 4; + uint8 unused_1 : 4; + uint8 additional_length; + uint8 unused_2[3]; + char vendor_identification[8]; + char product_identification[16]; + char product_revision_level[4]; +} _PACKED scsi_inquiry_6_parameter; + +typedef struct scsi_mode_sense_6_parameter_s { + uint8 data_length; + uint8 medium_type; + uint8 device_specific; + uint8 block_descriptor_length; + uint8 densitiy; + uint8 num_blocks[3]; + uint8 reserved; + uint8 block_length[3]; +} _PACKED scsi_mode_sense_6_parameter; + +typedef struct scsi_read_capacity_10_parameter_s { + uint32 last_logical_block_address; + uint32 logical_block_length; +} _PACKED scsi_read_capacity_10_parameter; + +// request sense keys/codes +typedef enum { + SCSI_SENSE_KEY_NO_SENSE = 0x00, + SCSI_SENSE_KEY_RECOVERED_ERROR = 0x01, + SCSI_SENSE_KEY_NOT_READY = 0x02, + SCSI_SENSE_KEY_MEDIUM_ERROR = 0x03, + SCSI_SENSE_KEY_HARDWARE_ERROR = 0x04, + SCSI_SENSE_KEY_ILLEGAL_REQUEST = 0x05, + SCSI_SENSE_KEY_UNIT_ATTENTION = 0x06, + SCSI_SENSE_KEY_DATA_PROTECT = 0x07, + SCSI_SENSE_KEY_ABORTED_COMMAND = 0x0b, +} scsi_sense_key; + +// request sense additional sense codes +#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3a + +// mode sense page code/parameter +#define SCSI_MODE_PAGE_DEVICE_CONFIGURATION 0x10 +#define SCSI_DEVICE_SPECIFIC_WRITE_PROTECT 0x80 + +#endif // _USB_DISK_SCSI_H_