* Add support for devices with multiple LUNs
* A device now has one or more LUNs which are used for LUN specific functions * Adapted these LUN specific functions to the new design and also change the (un-)publishing accordingly * Handle "no media" cases more correctly * Output some interesting device infos also with tracing disabled * Add some more helpful debug output here and there Devices that provide multiple logical units (like those n in 1 card readers) should now be usable with usb_disk too. Also we don't fail when a device is attached which currently does not contain a media. Instead handle media changes correctly so inserting a media works as expected. Note that hot-unplugging and media removal still does not quite work right at the devfs or disk device manager level and easily crashes the system. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@24799 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f27659f55e
commit
a380bc2513
@ -18,7 +18,7 @@
|
||||
|
||||
#define DRIVER_NAME "usb_disk"
|
||||
#define DEVICE_NAME_BASE "disk/usb/"
|
||||
#define DEVICE_NAME DEVICE_NAME_BASE"%ld/%ld/raw"
|
||||
#define DEVICE_NAME DEVICE_NAME_BASE"%ld/%d/raw"
|
||||
|
||||
|
||||
//#define TRACE_USB_DISK
|
||||
@ -35,6 +35,7 @@ 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 benaphore gDeviceListLock;
|
||||
static char **gDeviceNames = NULL;
|
||||
|
||||
@ -54,16 +55,35 @@ 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(disk_device *device, uint8 operation,
|
||||
status_t usb_disk_operation(device_lun *lun, uint8 operation,
|
||||
uint8 opLength, uint32 logicalBlockAddress,
|
||||
uint16 transferLength, void *data, uint32 *dataLength,
|
||||
bool directionIn);
|
||||
|
||||
status_t usb_disk_request_sense(disk_device *device);
|
||||
status_t usb_disk_test_unit_ready(disk_device *device);
|
||||
status_t usb_disk_inquiry(disk_device *device);
|
||||
status_t usb_disk_update_capacity(disk_device *device);
|
||||
status_t usb_disk_synchronize(disk_device *device, bool force);
|
||||
status_t usb_disk_request_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
|
||||
//
|
||||
|
||||
|
||||
void
|
||||
usb_disk_free_device_and_luns(disk_device *device)
|
||||
{
|
||||
benaphore_lock(&device->lock);
|
||||
benaphore_destroy(&device->lock);
|
||||
delete_sem(device->notify);
|
||||
for (uint8 i = 0; i < device->lun_count; i++)
|
||||
free(device->luns[i]);
|
||||
free(device->luns);
|
||||
free(device);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
@ -92,8 +112,8 @@ usb_disk_get_max_lun(disk_device *device)
|
||||
1, &result, &actualLength) != B_OK || actualLength != 1)
|
||||
return 0;
|
||||
|
||||
if (result > 15) {
|
||||
// invalid count, only up to 15 LUNs are possible
|
||||
if (result > MAX_LOGICAL_UNIT_NUMBER) {
|
||||
// invalid max lun
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -152,16 +172,17 @@ usb_disk_receive_csw(disk_device *device, command_status_wrapper *status)
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_operation(disk_device *device, uint8 operation, uint8 opLength,
|
||||
usb_disk_operation(device_lun *lun, uint8 operation, uint8 opLength,
|
||||
uint32 logicalBlockAddress, uint16 transferLength, void *data,
|
||||
uint32 *dataLength, bool directionIn)
|
||||
{
|
||||
disk_device *device = lun->device;
|
||||
command_block_wrapper command;
|
||||
command.signature = CBW_SIGNATURE;
|
||||
command.tag = device->current_tag++;
|
||||
command.data_transfer_length = (dataLength != NULL ? *dataLength : 0);
|
||||
command.flags = (directionIn ? CBW_DATA_INPUT : CBW_DATA_OUTPUT);
|
||||
command.lun = device->lun;
|
||||
command.lun = lun->logical_unit_number;
|
||||
command.command_block_length = opLength;
|
||||
memset(command.command_block, 0, sizeof(command.command_block));
|
||||
|
||||
@ -169,7 +190,7 @@ usb_disk_operation(disk_device *device, uint8 operation, uint8 opLength,
|
||||
case 6: {
|
||||
scsi_command_6 *commandBlock = (scsi_command_6 *)command.command_block;
|
||||
commandBlock->operation = operation;
|
||||
commandBlock->lun = device->lun << 5;
|
||||
commandBlock->lun = lun->logical_unit_number << 5;
|
||||
commandBlock->allocation_length = (uint8)transferLength;
|
||||
break;
|
||||
}
|
||||
@ -177,7 +198,7 @@ usb_disk_operation(disk_device *device, uint8 operation, uint8 opLength,
|
||||
case 10: {
|
||||
scsi_command_10 *commandBlock = (scsi_command_10 *)command.command_block;
|
||||
commandBlock->operation = operation;
|
||||
commandBlock->lun_flags = device->lun << 5;
|
||||
commandBlock->lun_flags = lun->logical_unit_number << 5;
|
||||
commandBlock->logical_block_address = htonl(logicalBlockAddress);
|
||||
commandBlock->transfer_length = htons(transferLength);
|
||||
break;
|
||||
@ -252,16 +273,18 @@ usb_disk_operation(disk_device *device, uint8 operation, uint8 opLength,
|
||||
result = B_OK;
|
||||
} else {
|
||||
// the operation is complete but has failed at the SCSI level
|
||||
TRACE_ALWAYS("operation failed at the SCSI level\n");
|
||||
usb_disk_request_sense(device);
|
||||
result = B_ERROR;
|
||||
TRACE_ALWAYS("operation 0x%02x failed at the SCSI level\n",
|
||||
operation);
|
||||
result = usb_disk_request_sense(lun);
|
||||
if (result == B_OK)
|
||||
result = B_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CSW_STATUS_PHASE_ERROR: {
|
||||
// a protocol or device error occured
|
||||
TRACE_ALWAYS("phase error in operation\n");
|
||||
TRACE_ALWAYS("phase error in operation 0x%02x\n", operation);
|
||||
usb_disk_reset_recovery(device);
|
||||
if (dataLength != NULL)
|
||||
*dataLength = 0;
|
||||
@ -279,18 +302,18 @@ usb_disk_operation(disk_device *device, uint8 operation, uint8 opLength,
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_request_sense(disk_device *device)
|
||||
usb_disk_request_sense(device_lun *lun)
|
||||
{
|
||||
uint32 dataLength = sizeof(scsi_request_sense_6_parameter);
|
||||
scsi_request_sense_6_parameter parameter;
|
||||
status_t result = usb_disk_operation(device, SCSI_REQUEST_SENSE_6, 6, 0,
|
||||
status_t result = usb_disk_operation(lun, SCSI_REQUEST_SENSE_6, 6, 0,
|
||||
dataLength, ¶meter, &dataLength, true);
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("getting request sense data failed\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (parameter.sense_key >= 2) {
|
||||
if (parameter.sense_key > SCSI_SENSE_KEY_NOT_READY) {
|
||||
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);
|
||||
@ -302,20 +325,26 @@ usb_disk_request_sense(disk_device *device)
|
||||
return B_OK;
|
||||
|
||||
case SCSI_SENSE_KEY_NOT_READY:
|
||||
TRACE_ALWAYS("request_sense: device not ready\n");
|
||||
return B_DEV_NOT_READY;
|
||||
|
||||
case SCSI_SENSE_KEY_MEDIUM_ERROR:
|
||||
case SCSI_SENSE_KEY_HARDWARE_ERROR:
|
||||
TRACE_ALWAYS("request_sense: no media or hardware error\n");
|
||||
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_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:
|
||||
TRACE_ALWAYS("request_sense: media changed\n");
|
||||
lun->media_changed = true;
|
||||
lun->media_present = true;
|
||||
return B_DEV_MEDIA_CHANGED;
|
||||
|
||||
case SCSI_SENSE_KEY_DATA_PROTECT:
|
||||
@ -332,71 +361,85 @@ usb_disk_request_sense(disk_device *device)
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_test_unit_ready(disk_device *device)
|
||||
usb_disk_test_unit_ready(device_lun *lun)
|
||||
{
|
||||
status_t result = usb_disk_operation(device, SCSI_TEST_UNIT_READY_6, 6, 0,
|
||||
0, NULL, NULL, true);
|
||||
if (result == B_OK)
|
||||
return B_OK;
|
||||
|
||||
return usb_disk_request_sense(device);
|
||||
return usb_disk_operation(lun, SCSI_TEST_UNIT_READY_6, 6, 0, 0, NULL, NULL,
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_inquiry(disk_device *device)
|
||||
usb_disk_inquiry(device_lun *lun)
|
||||
{
|
||||
uint32 dataLength = sizeof(scsi_inquiry_6_parameter);
|
||||
scsi_inquiry_6_parameter parameter;
|
||||
status_t result = usb_disk_operation(device, SCSI_INQUIRY_6, 6, 0,
|
||||
dataLength, ¶meter, &dataLength, true);
|
||||
status_t result = B_ERROR;
|
||||
for (uint32 tries = 0; tries < 3; tries++) {
|
||||
result = usb_disk_operation(lun, SCSI_INQUIRY_6, 6, 0, dataLength,
|
||||
¶meter, &dataLength, true);
|
||||
if (result == B_OK)
|
||||
break;
|
||||
}
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("getting inquiry data failed\n");
|
||||
device->removable = true;
|
||||
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("vendor_identification... \"%.8s\"\n", parameter.vendor_identification);
|
||||
TRACE("product_identification.. \"%.16s\"\n", parameter.product_identification);
|
||||
TRACE("product_revision_level.. \"%.4s\"\n", parameter.product_revision_level);
|
||||
device->device_type = parameter.peripherial_device_type; /* 1:1 mapping */
|
||||
device->removable = (parameter.removable_medium == 1);
|
||||
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_update_capacity(disk_device *device)
|
||||
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 = usb_disk_operation(device, SCSI_READ_CAPACITY_10, 10, 0,
|
||||
0, ¶meter, &dataLength, true);
|
||||
status_t result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
|
||||
¶meter, &dataLength, true);
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("failed to update capacity\n");
|
||||
device->block_size = 1;
|
||||
device->block_count = 0;
|
||||
lun->media_present = false;
|
||||
lun->media_changed = false;
|
||||
usb_disk_reset_capacity(lun);
|
||||
return result;
|
||||
}
|
||||
|
||||
device->block_size = ntohl(parameter.logical_block_length);
|
||||
device->block_count = ntohl(parameter.last_logical_block_address) + 1;
|
||||
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(disk_device *device, bool force)
|
||||
usb_disk_synchronize(device_lun *lun, bool force)
|
||||
{
|
||||
if (device->should_sync || force) {
|
||||
status_t result = usb_disk_operation(device, SCSI_SYNCHRONIZE_CACHE_10,
|
||||
if (lun->should_sync || force) {
|
||||
status_t result = usb_disk_operation(lun, SCSI_SYNCHRONIZE_CACHE_10,
|
||||
10, 0, 0, NULL, NULL, false);
|
||||
device->should_sync = false;
|
||||
lun->should_sync = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -415,7 +458,6 @@ usb_disk_callback(void *cookie, status_t status, void *data,
|
||||
{
|
||||
//TRACE("callback()\n");
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
|
||||
device->status = status;
|
||||
device->actual_length = actualLength;
|
||||
release_sem(device->notify);
|
||||
@ -432,8 +474,7 @@ usb_disk_device_added(usb_device newDevice, void **cookie)
|
||||
device->open_count = 0;
|
||||
device->interface = 0xff;
|
||||
device->current_tag = 0;
|
||||
device->should_sync = false;
|
||||
device->lun = 0;
|
||||
device->luns = NULL;
|
||||
|
||||
// scan through the interfaces to find our bulk-only data interface
|
||||
const usb_configuration_info *configuration = gUSBModule->get_configuration(newDevice);
|
||||
@ -483,6 +524,7 @@ usb_disk_device_added(usb_device newDevice, void **cookie)
|
||||
|
||||
if (device->interface == 0xff) {
|
||||
TRACE_ALWAYS("no valid bulk-only interface found\n");
|
||||
free(device);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@ -496,33 +538,58 @@ usb_disk_device_added(usb_device newDevice, void **cookie)
|
||||
if (device->notify < B_OK) {
|
||||
benaphore_destroy(&device->lock);
|
||||
free(device);
|
||||
return B_NO_MORE_SEMS;
|
||||
return device->notify;
|
||||
}
|
||||
|
||||
result = usb_disk_inquiry(device);
|
||||
for (uint32 tries = 0; tries < 3; tries++) {
|
||||
if (usb_disk_test_unit_ready(device) == B_OK)
|
||||
device->lun_count = usb_disk_get_max_lun(device) + 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;
|
||||
|
||||
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)
|
||||
break;
|
||||
snooze(10000);
|
||||
}
|
||||
|
||||
if (result != B_OK)
|
||||
break;
|
||||
snooze(10000);
|
||||
}
|
||||
|
||||
if (result != B_OK || usb_disk_update_capacity(device) != B_OK) {
|
||||
TRACE_ALWAYS("failed to read device data\n");
|
||||
delete_sem(device->notify);
|
||||
benaphore_destroy(&device->lock);
|
||||
free(device);
|
||||
return B_ERROR;
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("failed to initialize logical units\n");
|
||||
usb_disk_free_device_and_luns(device);
|
||||
return result;
|
||||
}
|
||||
|
||||
benaphore_lock(&gDeviceListLock);
|
||||
device->link = (void *)gDeviceList;
|
||||
gDeviceList = device;
|
||||
uint32 deviceNumber = gDeviceCount++;
|
||||
|
||||
// ToDo: find out about LUNs and publish those too
|
||||
//uint8 maxLun = usb_disk_get_max_lun(device);
|
||||
//TRACE("device has max lun %d\n", maxLun);
|
||||
sprintf(device->name, DEVICE_NAME, deviceNumber, 0L);
|
||||
gLunCount += device->lun_count;
|
||||
for (uint8 i = 0; i < device->lun_count; i++)
|
||||
sprintf(device->luns[i]->name, DEVICE_NAME, deviceNumber, i);
|
||||
benaphore_unlock(&gDeviceListLock);
|
||||
|
||||
TRACE("new device: 0x%08lx\n", (uint32)device);
|
||||
@ -551,18 +618,14 @@ usb_disk_device_removed(void *cookie)
|
||||
element = (disk_device *)element->link;
|
||||
}
|
||||
}
|
||||
gLunCount -= device->lun_count;
|
||||
gDeviceCount--;
|
||||
benaphore_unlock(&gDeviceListLock);
|
||||
|
||||
device->device = 0;
|
||||
device->removed = true;
|
||||
if (device->open_count == 0) {
|
||||
benaphore_lock(&device->lock);
|
||||
benaphore_destroy(&device->lock);
|
||||
delete_sem(device->notify);
|
||||
free(device);
|
||||
}
|
||||
|
||||
if (device->open_count == 0)
|
||||
usb_disk_free_device_and_luns(device);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -573,15 +636,15 @@ usb_disk_device_removed(void *cookie)
|
||||
|
||||
|
||||
static bool
|
||||
usb_disk_needs_partial_buffer(disk_device *device, off_t position,
|
||||
size_t length, uint32 &blockPosition, uint16 &blockCount)
|
||||
usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
|
||||
uint32 &blockPosition, uint16 &blockCount)
|
||||
{
|
||||
blockPosition = (uint32)(position / device->block_size);
|
||||
if ((off_t)blockPosition * device->block_size != position)
|
||||
blockPosition = (uint32)(position / lun->block_size);
|
||||
if ((off_t)blockPosition * lun->block_size != position)
|
||||
return true;
|
||||
|
||||
blockCount = (uint16)(length / device->block_size);
|
||||
if ((size_t)blockCount * device->block_size != length)
|
||||
blockCount = (uint16)(length / lun->block_size);
|
||||
if ((size_t)blockCount * lun->block_size != length)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -589,43 +652,43 @@ usb_disk_needs_partial_buffer(disk_device *device, off_t position,
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_block_read(disk_device *device, uint32 blockPosition,
|
||||
uint16 blockCount, void *buffer, size_t *length)
|
||||
usb_disk_block_read(device_lun *lun, uint32 blockPosition, uint16 blockCount,
|
||||
void *buffer, size_t *length)
|
||||
{
|
||||
status_t result = usb_disk_operation(device, SCSI_READ_10, 10,
|
||||
blockPosition, blockCount, buffer, length, true);
|
||||
status_t result = usb_disk_operation(lun, SCSI_READ_10, 10, blockPosition,
|
||||
blockCount, buffer, length, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_block_write(disk_device *device, uint32 blockPosition,
|
||||
uint16 blockCount, void *buffer, size_t *length)
|
||||
usb_disk_block_write(device_lun *lun, uint32 blockPosition, uint16 blockCount,
|
||||
void *buffer, size_t *length)
|
||||
{
|
||||
status_t result = usb_disk_operation(device, SCSI_WRITE_10, 10,
|
||||
blockPosition, blockCount, buffer, length, false);
|
||||
status_t result = usb_disk_operation(lun, SCSI_WRITE_10, 10, blockPosition,
|
||||
blockCount, buffer, length, false);
|
||||
if (result == B_OK)
|
||||
device->should_sync = true;
|
||||
lun->should_sync = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_prepare_partial_buffer(disk_device *device, off_t position,
|
||||
size_t length, void *&partialBuffer, void *&blockBuffer,
|
||||
uint32 &blockPosition, uint16 &blockCount)
|
||||
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 / device->block_size);
|
||||
blockCount = (uint16)((uint32)((position + length + device->block_size - 1)
|
||||
/ device->block_size) - blockPosition);
|
||||
size_t blockLength = blockCount * device->block_size;
|
||||
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(device, blockPosition, blockCount,
|
||||
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");
|
||||
@ -633,7 +696,7 @@ usb_disk_prepare_partial_buffer(disk_device *device, off_t position,
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t offset = position - (blockPosition * device->block_size);
|
||||
off_t offset = position - (blockPosition * lun->block_size);
|
||||
partialBuffer = (uint8 *)blockBuffer + offset;
|
||||
return B_OK;
|
||||
}
|
||||
@ -667,11 +730,18 @@ usb_disk_open(const char *name, uint32 flags, void **cookie)
|
||||
benaphore_lock(&gDeviceListLock);
|
||||
disk_device *device = gDeviceList;
|
||||
while (device) {
|
||||
if (!device->removed && strncmp(rawName, device->name, 32) == 0) {
|
||||
device->open_count++;
|
||||
*cookie = device;
|
||||
benaphore_unlock(&gDeviceListLock);
|
||||
return B_OK;
|
||||
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)
|
||||
return B_ERROR;
|
||||
|
||||
device->open_count++;
|
||||
*cookie = lun;
|
||||
benaphore_unlock(&gDeviceListLock);
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
device = (disk_device *)device->link;
|
||||
@ -686,8 +756,8 @@ static status_t
|
||||
usb_disk_close(void *cookie)
|
||||
{
|
||||
TRACE("close()\n");
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
usb_disk_synchronize(device, false);
|
||||
device_lun *lun = (device_lun *)cookie;
|
||||
usb_disk_synchronize(lun, false);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -698,13 +768,13 @@ usb_disk_free(void *cookie)
|
||||
TRACE("free()\n");
|
||||
benaphore_lock(&gDeviceListLock);
|
||||
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
device_lun *lun = (device_lun *)cookie;
|
||||
disk_device *device = lun->device;
|
||||
device->open_count--;
|
||||
if (device->removed && device->open_count == 0) {
|
||||
benaphore_lock(&device->lock);
|
||||
benaphore_destroy(&device->lock);
|
||||
delete_sem(device->notify);
|
||||
free(device);
|
||||
// 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);
|
||||
}
|
||||
|
||||
benaphore_unlock(&gDeviceListLock);
|
||||
@ -715,7 +785,8 @@ usb_disk_free(void *cookie)
|
||||
static status_t
|
||||
usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
||||
{
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
device_lun *lun = (device_lun *)cookie;
|
||||
disk_device *device = lun->device;
|
||||
if (device->removed)
|
||||
return B_DEV_NOT_READY;
|
||||
|
||||
@ -724,21 +795,27 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
||||
|
||||
switch (op) {
|
||||
case B_GET_MEDIA_STATUS: {
|
||||
*(status_t *)buffer = usb_disk_test_unit_ready(device);
|
||||
*(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 = device->block_size;
|
||||
geometry->cylinder_count = device->block_count;
|
||||
geometry->bytes_per_sector = lun->block_size;
|
||||
geometry->cylinder_count = lun->block_count;
|
||||
geometry->sectors_per_track = geometry->head_count = 1;
|
||||
geometry->device_type = device->device_type;
|
||||
geometry->removable = device->removable;
|
||||
geometry->read_only = (device->device_type == B_CD);
|
||||
geometry->write_once = (device->device_type == B_WORM);
|
||||
geometry->device_type = lun->device_type;
|
||||
geometry->removable = lun->removable;
|
||||
geometry->read_only = (lun->device_type == B_CD);
|
||||
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;
|
||||
@ -746,7 +823,8 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
||||
}
|
||||
|
||||
case B_FLUSH_DRIVE_CACHE:
|
||||
usb_disk_synchronize(device, true);
|
||||
TRACE("B_FLUSH_DRIVE_CACHE\n");
|
||||
usb_disk_synchronize(lun, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -763,7 +841,8 @@ static status_t
|
||||
usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
|
||||
{
|
||||
TRACE("read(%lld, %ld)\n", position, *length);
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
device_lun *lun = (device_lun *)cookie;
|
||||
disk_device *device = lun->device;
|
||||
if (device->removed) {
|
||||
*length = 0;
|
||||
return B_DEV_NOT_READY;
|
||||
@ -776,20 +855,20 @@ usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
|
||||
status_t result = B_ERROR;
|
||||
uint32 blockPosition = 0;
|
||||
uint16 blockCount = 0;
|
||||
bool needsPartial = usb_disk_needs_partial_buffer(device, position,
|
||||
*length, blockPosition, blockCount);
|
||||
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(device, position, *length,
|
||||
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(device, blockPosition, blockCount,
|
||||
buffer, length);
|
||||
result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
|
||||
length);
|
||||
}
|
||||
|
||||
benaphore_unlock(&device->lock);
|
||||
@ -809,7 +888,8 @@ usb_disk_write(void *cookie, off_t position, const void *buffer,
|
||||
size_t *length)
|
||||
{
|
||||
TRACE("write(%lld, %ld)\n", position, *length);
|
||||
disk_device *device = (disk_device *)cookie;
|
||||
device_lun *lun = (device_lun *)cookie;
|
||||
disk_device *device = lun->device;
|
||||
if (device->removed) {
|
||||
*length = 0;
|
||||
return B_DEV_NOT_READY;
|
||||
@ -822,22 +902,22 @@ usb_disk_write(void *cookie, off_t position, const void *buffer,
|
||||
status_t result = B_ERROR;
|
||||
uint32 blockPosition = 0;
|
||||
uint16 blockCount = 0;
|
||||
bool needsPartial = usb_disk_needs_partial_buffer(device, position,
|
||||
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(device, position, *length,
|
||||
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 * device->block_size;
|
||||
result = usb_disk_block_write(device, blockPosition, blockCount,
|
||||
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(device, blockPosition, blockCount,
|
||||
result = usb_disk_block_write(lun, blockPosition, blockCount,
|
||||
(void *)buffer, length);
|
||||
}
|
||||
|
||||
@ -881,6 +961,7 @@ init_driver()
|
||||
|
||||
gDeviceList = NULL;
|
||||
gDeviceCount = 0;
|
||||
gLunCount = 0;
|
||||
status_t result = benaphore_init(&gDeviceListLock, "usb_disk device list lock");
|
||||
if (result < B_OK) {
|
||||
TRACE("failed to create device list lock\n");
|
||||
@ -931,7 +1012,7 @@ publish_devices()
|
||||
gDeviceNames = NULL;
|
||||
}
|
||||
|
||||
gDeviceNames = (char **)malloc(sizeof(char *) * (gDeviceCount + 1));
|
||||
gDeviceNames = (char **)malloc(sizeof(char *) * (gLunCount + 1));
|
||||
if (gDeviceNames == NULL)
|
||||
return NULL;
|
||||
|
||||
@ -939,7 +1020,9 @@ publish_devices()
|
||||
benaphore_lock(&gDeviceListLock);
|
||||
disk_device *device = gDeviceList;
|
||||
while (device) {
|
||||
gDeviceNames[index++] = strdup(device->name);
|
||||
for (uint8 i = 0; i < device->lun_count; i++)
|
||||
gDeviceNames[index++] = strdup(device->luns[i]->name);
|
||||
|
||||
device = (disk_device *)device->link;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#define REQUEST_MASS_STORAGE_RESET 0xff
|
||||
#define REQUEST_GET_MAX_LUN 0xfe
|
||||
#define MAX_LOGICAL_UNIT_NUMBER 15
|
||||
|
||||
#define CBW_SIGNATURE 0x43425355
|
||||
#define CBW_DATA_OUTPUT 0x00
|
||||
@ -24,12 +25,14 @@
|
||||
#define CSW_STATUS_COMMAND_FAILED 0x01
|
||||
#define CSW_STATUS_PHASE_ERROR 0x02
|
||||
|
||||
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;
|
||||
bool removed;
|
||||
uint32 open_count;
|
||||
benaphore lock;
|
||||
char name[32];
|
||||
void * link;
|
||||
|
||||
// device state
|
||||
@ -37,22 +40,35 @@ typedef struct disk_device_s {
|
||||
usb_pipe bulk_out;
|
||||
uint8 interface;
|
||||
uint32 current_tag;
|
||||
bool should_sync;
|
||||
uint8 lun;
|
||||
|
||||
// device information through read capacity/inquiry
|
||||
uint32 block_count;
|
||||
uint32 block_size;
|
||||
uint8 device_type;
|
||||
bool removable;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
|
||||
typedef struct command_block_wrapper_s {
|
||||
uint32 signature;
|
||||
uint32 tag;
|
||||
|
Loading…
Reference in New Issue
Block a user