* 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:
Michael Lotz 2008-04-04 22:51:29 +00:00
parent f27659f55e
commit a380bc2513
2 changed files with 251 additions and 152 deletions

View File

@ -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, &parameter, &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, &parameter, &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,
&parameter, &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, &parameter, &dataLength, true);
status_t result = usb_disk_operation(lun, SCSI_READ_CAPACITY_10, 10, 0, 0,
&parameter, &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;
}

View File

@ -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;