usb_disk: Use DMAResource for bouncing and support physical I/O requests.
A "bypass" mechanism is left in for when DMAResource would just add overhead for no reason. All other I/O goes through it and is submitted to the USB stack as physical addresses. Tested in QEMU, can still boot from USB on all busses. Fixes #15569. Change-Id: I26bfd2208de4ebe1a17170a7034316076927663f Reviewed-on: https://review.haiku-os.org/c/haiku/+/6480 Reviewed-by: waddlesplash <waddlesplash@gmail.com>
This commit is contained in:
parent
55a468820c
commit
197e4b5a6a
@ -1,6 +1,7 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel drivers disk usb usb_disk ;
|
||||
|
||||
SubDirSysHdrs $(HAIKU_TOP) src add-ons kernel bus_managers usb ;
|
||||
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
|
||||
UsePrivateHeaders drivers ;
|
||||
UsePrivateKernelHeaders ;
|
||||
|
||||
|
@ -1,15 +1,17 @@
|
||||
/*
|
||||
* Copyright 2008-2012, Haiku Inc. All rights reserved.
|
||||
* Copyright 2008-2023, Haiku Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Michael Lotz <mmlr@mlotz.ch>
|
||||
* Augustin Cavalier <waddlesplash>
|
||||
*/
|
||||
|
||||
|
||||
#include "usb_disk.h"
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <StackOrHeapArray.h>
|
||||
#include <Drivers.h>
|
||||
#include <bus/USB.h>
|
||||
|
||||
@ -22,11 +24,15 @@
|
||||
#include <syscall_restart.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include "IORequest.h"
|
||||
|
||||
#include "scsi_sense.h"
|
||||
#include "usb_disk_scsi.h"
|
||||
#include "icons.h"
|
||||
|
||||
|
||||
#define MAX_IO_BLOCKS (128)
|
||||
|
||||
#define USB_DISK_DEVICE_MODULE_NAME "drivers/disk/usb_disk/device_v1"
|
||||
#define USB_DISK_DRIVER_MODULE_NAME "drivers/disk/usb_disk/driver_v1"
|
||||
#define USB_DISK_DEVICE_ID_GENERATOR "usb_disk/device_id"
|
||||
@ -84,13 +90,8 @@ static void usb_disk_callback(void *cookie, status_t status, void *data,
|
||||
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,
|
||||
usb_massbulk_command_status_wrapper *status);
|
||||
status_t usb_disk_operation(device_lun *lun, uint8* operation,
|
||||
size_t opLength, void *data, size_t *dataLength,
|
||||
bool directionIn, err_act *action = NULL);
|
||||
|
||||
status_t usb_disk_send_diagnostic(device_lun *lun);
|
||||
status_t usb_disk_request_sense(device_lun *lun, err_act *action);
|
||||
@ -102,9 +103,19 @@ 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
|
||||
//
|
||||
// #pragma mark - disk_device helper functions
|
||||
|
||||
|
||||
static DMAResource*
|
||||
get_dma_resource(disk_device *device, uint32 blockSize)
|
||||
{
|
||||
for (int32 i = 0; i < device->dma_resources.Count(); i++) {
|
||||
DMAResource* r = device->dma_resources[i];
|
||||
if (r->BlockSize() == blockSize)
|
||||
return r;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
@ -112,12 +123,14 @@ usb_disk_free_device_and_luns(disk_device *device)
|
||||
{
|
||||
mutex_lock(&device->lock);
|
||||
mutex_destroy(&device->lock);
|
||||
for (int32 i = 0; i < device->dma_resources.Count(); i++)
|
||||
delete device->dma_resources[i];
|
||||
delete_sem(device->notify);
|
||||
delete_sem(device->interruptLock);
|
||||
for (uint8 i = 0; i < device->lun_count; i++)
|
||||
free(device->luns[i]);
|
||||
free(device->luns);
|
||||
free(device);
|
||||
delete device;
|
||||
}
|
||||
|
||||
|
||||
@ -182,12 +195,29 @@ usb_disk_reset_recovery(disk_device *device, err_act *_action)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_transfer_data(disk_device *device, bool directionIn, void *data,
|
||||
size_t dataLength)
|
||||
struct transfer_data {
|
||||
union {
|
||||
physical_entry* phys_vecs;
|
||||
iovec* vecs;
|
||||
};
|
||||
uint32 vec_count = 0;
|
||||
bool physical = false;
|
||||
};
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_transfer_data(disk_device *device, bool directionIn, const transfer_data& data)
|
||||
{
|
||||
status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in
|
||||
: device->bulk_out, data, dataLength, usb_disk_callback, device);
|
||||
status_t result;
|
||||
if (data.physical) {
|
||||
result = gUSBModule->queue_bulk_v_physical(
|
||||
directionIn ? device->bulk_in : device->bulk_out,
|
||||
data.phys_vecs, data.vec_count, usb_disk_callback, device);
|
||||
} else {
|
||||
result = gUSBModule->queue_bulk_v(
|
||||
directionIn ? device->bulk_in : device->bulk_out,
|
||||
data.vecs, data.vec_count, usb_disk_callback, device);
|
||||
}
|
||||
if (result != B_OK) {
|
||||
TRACE_ALWAYS("failed to queue data transfer: %s\n", strerror(result));
|
||||
return result;
|
||||
@ -217,6 +247,22 @@ usb_disk_transfer_data(disk_device *device, bool directionIn, void *data,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_transfer_data(disk_device *device, bool directionIn,
|
||||
void* buffer, size_t dataLength)
|
||||
{
|
||||
iovec vec;
|
||||
vec.iov_base = buffer;
|
||||
vec.iov_len = dataLength;
|
||||
|
||||
struct transfer_data data;
|
||||
data.vecs = &vec;
|
||||
data.vec_count = 1;
|
||||
|
||||
return usb_disk_transfer_data(device, directionIn, data);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
usb_disk_interrupt(void* cookie, int32 status, void* data, size_t length)
|
||||
{
|
||||
@ -279,10 +325,11 @@ usb_disk_receive_csw_bulk(disk_device *device,
|
||||
|
||||
status_t
|
||||
usb_disk_operation_interrupt(device_lun *lun, uint8* operation,
|
||||
void *data, size_t *dataLength, bool directionIn, err_act *_action)
|
||||
const transfer_data& data, size_t *dataLength,
|
||||
bool directionIn, err_act *_action)
|
||||
{
|
||||
TRACE("operation: lun: %u; op: 0x%x; data: %p; dlen: %p (%lu); in: %c\n",
|
||||
lun->logical_unit_number, operation[0], data, dataLength,
|
||||
lun->logical_unit_number, operation[0], data.vecs, dataLength,
|
||||
dataLength ? *dataLength : 0, directionIn ? 'y' : 'n');
|
||||
|
||||
disk_device* device = lun->device;
|
||||
@ -306,9 +353,9 @@ usb_disk_operation_interrupt(device_lun *lun, uint8* operation,
|
||||
|
||||
// Step 2 : data phase : send or receive data
|
||||
size_t transferedData = 0;
|
||||
if (data != NULL && dataLength != NULL && *dataLength > 0) {
|
||||
if (data.vec_count != 0) {
|
||||
// we have data to transfer in a data stage
|
||||
result = usb_disk_transfer_data(device, directionIn, data, *dataLength);
|
||||
result = usb_disk_transfer_data(device, directionIn, data);
|
||||
if (result != B_OK) {
|
||||
TRACE("Error %s in data phase\n", strerror(result));
|
||||
return result;
|
||||
@ -354,13 +401,13 @@ usb_disk_operation_interrupt(device_lun *lun, uint8* operation,
|
||||
|
||||
|
||||
status_t
|
||||
usb_disk_operation_bulk(device_lun *lun, uint8* operation,
|
||||
size_t operationLength, void *data, size_t *dataLength, bool directionIn,
|
||||
err_act *_action)
|
||||
usb_disk_operation_bulk(device_lun *lun, uint8 *operation, size_t operationLength,
|
||||
const transfer_data& data, size_t *dataLength,
|
||||
bool directionIn, err_act *_action)
|
||||
{
|
||||
TRACE("operation: lun: %u; op: %u; data: %p; dlen: %p (%lu); in: %c\n",
|
||||
lun->logical_unit_number, operation[0],
|
||||
data, dataLength, dataLength ? *dataLength : 0,
|
||||
data.vecs, dataLength, dataLength ? *dataLength : 0,
|
||||
directionIn ? 'y' : 'n');
|
||||
|
||||
disk_device *device = lun->device;
|
||||
@ -391,10 +438,9 @@ usb_disk_operation_bulk(device_lun *lun, uint8* operation,
|
||||
}
|
||||
|
||||
size_t transferedData = 0;
|
||||
if (data != NULL && dataLength != NULL && *dataLength > 0) {
|
||||
if (data.vec_count != 0) {
|
||||
// we have data to transfer in a data stage
|
||||
result = usb_disk_transfer_data(device, directionIn, data,
|
||||
*dataLength);
|
||||
result = usb_disk_transfer_data(device, directionIn, data);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
@ -493,13 +539,14 @@ usb_disk_operation_bulk(device_lun *lun, uint8* operation,
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
static status_t
|
||||
usb_disk_operation(device_lun *lun, uint8* operation, size_t opLength,
|
||||
void *data, size_t *dataLength, bool directionIn, err_act *_action)
|
||||
const transfer_data& data, size_t *dataLength,
|
||||
bool directionIn, err_act *_action = NULL)
|
||||
{
|
||||
if (lun->device->is_ufi) {
|
||||
return usb_disk_operation_interrupt(lun, operation, data, dataLength,
|
||||
directionIn, _action);
|
||||
return usb_disk_operation_interrupt(lun, operation,
|
||||
data, dataLength, directionIn, _action);
|
||||
} else {
|
||||
return usb_disk_operation_bulk(lun, operation, opLength,
|
||||
data, dataLength, directionIn, _action);
|
||||
@ -507,6 +554,30 @@ usb_disk_operation(device_lun *lun, uint8* operation, size_t opLength,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_operation(device_lun *lun, uint8* operation, size_t opLength,
|
||||
void *buffer, size_t *dataLength,
|
||||
bool directionIn, err_act *_action = NULL)
|
||||
{
|
||||
iovec vec;
|
||||
vec.iov_base = buffer;
|
||||
|
||||
struct transfer_data data;
|
||||
data.vecs = &vec;
|
||||
|
||||
if (dataLength != NULL && *dataLength != 0) {
|
||||
vec.iov_len = *dataLength;
|
||||
data.vec_count = 1;
|
||||
} else {
|
||||
vec.iov_len = 0;
|
||||
data.vec_count = 0;
|
||||
}
|
||||
|
||||
return usb_disk_operation(lun, operation, opLength,
|
||||
data, dataLength, directionIn, _action);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//#pragma mark - Helper/Convenience Functions
|
||||
//
|
||||
@ -521,8 +592,7 @@ usb_disk_send_diagnostic(device_lun *lun)
|
||||
commandBlock[0] = SCSI_SEND_DIAGNOSTIC;
|
||||
commandBlock[1] = (lun->logical_unit_number << 5) | 4;
|
||||
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 6, NULL,
|
||||
NULL, false);
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 6, NULL, NULL, false);
|
||||
|
||||
int retry = 100;
|
||||
err_act action = err_act_ok;
|
||||
@ -846,8 +916,24 @@ usb_disk_update_capacity(device_lun *lun)
|
||||
B_BENDIAN_TO_HOST_INT32(parameter.last_logical_block_address) + 1;
|
||||
if (lun->block_count == 0) {
|
||||
// try SCSI_READ_CAPACITY_16
|
||||
return usb_disk_update_capacity_16(lun);
|
||||
result = usb_disk_update_capacity_16(lun);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
}
|
||||
|
||||
// ensure we have a DMAResource for this block_size
|
||||
if (get_dma_resource(lun->device, lun->block_size) == NULL) {
|
||||
dma_restrictions restrictions = {};
|
||||
restrictions.max_transfer_size = (lun->block_size * MAX_IO_BLOCKS);
|
||||
|
||||
DMAResource* dmaResource = new DMAResource;
|
||||
result = dmaResource->Init(restrictions, lun->block_size, 1, 1);
|
||||
if (result != B_OK)
|
||||
return result;
|
||||
|
||||
lun->device->dma_resources.Add(dmaResource);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@ -916,7 +1002,7 @@ static status_t
|
||||
usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
||||
{
|
||||
TRACE("device_added(0x%08" B_PRIx32 ")\n", newDevice);
|
||||
disk_device *device = (disk_device *)malloc(sizeof(disk_device));
|
||||
disk_device *device = new(std::nothrow) disk_device;
|
||||
device->node = node;
|
||||
device->device = newDevice;
|
||||
device->removed = false;
|
||||
@ -933,7 +1019,7 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
||||
const usb_configuration_info *configuration
|
||||
= gUSBModule->get_configuration(newDevice);
|
||||
if (configuration == NULL) {
|
||||
free(device);
|
||||
delete device;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@ -1000,7 +1086,7 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
||||
|
||||
if (device->interface == 0xff) {
|
||||
TRACE_ALWAYS("no valid bulk-only or CBI interface found\n");
|
||||
free(device);
|
||||
delete device;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@ -1010,7 +1096,7 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
||||
if (device->notify < B_OK) {
|
||||
mutex_destroy(&device->lock);
|
||||
status_t result = device->notify;
|
||||
free(device);
|
||||
delete device;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1020,7 +1106,7 @@ usb_disk_attach(device_node *node, usb_device newDevice, void **cookie)
|
||||
mutex_destroy(&device->lock);
|
||||
delete_sem(device->notify);
|
||||
status_t result = device->interruptLock;
|
||||
free(device);
|
||||
delete device;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -1129,30 +1215,24 @@ usb_disk_device_removed(void *cookie)
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//#pragma mark - Partial Buffer Functions
|
||||
//
|
||||
|
||||
|
||||
static bool
|
||||
usb_disk_needs_partial_buffer(device_lun *lun, off_t position, size_t length,
|
||||
uint64 &blockPosition, size_t &blockCount)
|
||||
usb_disk_needs_bounce(device_lun *lun, io_request *request)
|
||||
{
|
||||
blockPosition = (uint64)(position / lun->block_size);
|
||||
if ((off_t)blockPosition * lun->block_size != position)
|
||||
if (!request->Buffer()->IsVirtual())
|
||||
return true;
|
||||
|
||||
blockCount = length / lun->block_size;
|
||||
if (blockCount * lun->block_size != length)
|
||||
if ((request->Offset() % lun->block_size) != 0)
|
||||
return true;
|
||||
if ((request->Length() % lun->block_size) != 0)
|
||||
return true;
|
||||
if (request->Length() > (lun->block_size * MAX_IO_BLOCKS))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
void *buffer, size_t *length)
|
||||
struct transfer_data data, size_t *length)
|
||||
{
|
||||
uint8 commandBlock[16];
|
||||
memset(commandBlock, 0, sizeof(commandBlock));
|
||||
@ -1170,8 +1250,8 @@ usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
|
||||
status_t result = B_OK;
|
||||
for (int tries = 0; tries < 5; tries++) {
|
||||
result = usb_disk_operation(lun, commandBlock, 12, buffer, length,
|
||||
true);
|
||||
result = usb_disk_operation(lun, commandBlock, 12, data,
|
||||
length, true);
|
||||
if (result == B_OK)
|
||||
break;
|
||||
else
|
||||
@ -1188,7 +1268,7 @@ usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
commandBlock[7] = blockCount >> 8;
|
||||
commandBlock[8] = blockCount;
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 10,
|
||||
buffer, length, true);
|
||||
data, length, true);
|
||||
return result;
|
||||
} else {
|
||||
commandBlock[0] = SCSI_READ_16;
|
||||
@ -1206,7 +1286,7 @@ usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
commandBlock[12] = blockCount >> 8;
|
||||
commandBlock[13] = blockCount;
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 16,
|
||||
buffer, length, true);
|
||||
data, length, true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@ -1214,7 +1294,7 @@ usb_disk_block_read(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
|
||||
static status_t
|
||||
usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
void *buffer, size_t *length)
|
||||
struct transfer_data data, size_t *length)
|
||||
{
|
||||
uint8 commandBlock[16];
|
||||
memset(commandBlock, 0, sizeof(commandBlock));
|
||||
@ -1232,8 +1312,8 @@ usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
commandBlock[9] = blockCount;
|
||||
|
||||
status_t result;
|
||||
result = usb_disk_operation(lun, commandBlock, 12, buffer, length,
|
||||
false);
|
||||
result = usb_disk_operation(lun, commandBlock, 12,
|
||||
data, length, false);
|
||||
|
||||
int retry = 10;
|
||||
err_act action = err_act_ok;
|
||||
@ -1255,7 +1335,7 @@ usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
commandBlock[7] = blockCount >> 8;
|
||||
commandBlock[8] = blockCount;
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 10,
|
||||
buffer, length, false);
|
||||
data, length, false);
|
||||
if (result == B_OK)
|
||||
lun->should_sync = true;
|
||||
return result;
|
||||
@ -1275,7 +1355,7 @@ usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
commandBlock[12] = blockCount >> 8;
|
||||
commandBlock[13] = blockCount;
|
||||
status_t result = usb_disk_operation(lun, commandBlock, 16,
|
||||
buffer, length, false);
|
||||
data, length, false);
|
||||
if (result == B_OK)
|
||||
lun->should_sync = true;
|
||||
return result;
|
||||
@ -1283,36 +1363,6 @@ usb_disk_block_write(device_lun *lun, uint64 blockPosition, size_t blockCount,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_prepare_partial_buffer(device_lun *lun, off_t position, size_t length,
|
||||
void *&partialBuffer, void *&blockBuffer, uint64 &blockPosition,
|
||||
size_t &blockCount)
|
||||
{
|
||||
blockPosition = position / lun->block_size;
|
||||
blockCount = (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: %s\n",
|
||||
strerror(result));
|
||||
free(blockBuffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
off_t offset = position - (off_t)blockPosition * lun->block_size;
|
||||
partialBuffer = (uint8 *)blockBuffer + offset;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//#pragma mark - Driver Hooks
|
||||
//
|
||||
@ -1652,108 +1702,129 @@ usb_disk_ioctl(void *cookie, uint32 op, void *buffer, size_t length)
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_read(void *cookie, off_t position, void *buffer, size_t *length)
|
||||
usb_disk_bounced_io(device_lun *lun, io_request *request)
|
||||
{
|
||||
if (buffer == NULL || length == NULL)
|
||||
return B_BAD_VALUE;
|
||||
DMAResource* dmaResource = get_dma_resource(lun->device, lun->block_size);
|
||||
if (dmaResource == NULL)
|
||||
return B_NO_INIT;
|
||||
|
||||
TRACE("read(%" B_PRIdOFF ", %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;
|
||||
uint64 blockPosition = 0;
|
||||
size_t 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) {
|
||||
if (IS_USER_ADDRESS(buffer))
|
||||
result = user_memcpy(buffer, partialBuffer, *length);
|
||||
else
|
||||
memcpy(buffer, partialBuffer, *length);
|
||||
free(blockBuffer);
|
||||
if (!request->Buffer()->IsPhysical()) {
|
||||
status_t status = request->Buffer()->LockMemory(request->TeamID(), request->IsWrite());
|
||||
if (status != B_OK) {
|
||||
TRACE_ALWAYS("failed to lock memory: %s\n", strerror(status));
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
result = usb_disk_block_read(lun, blockPosition, blockCount, buffer,
|
||||
length);
|
||||
// SetStatusAndNotify() takes care of unlocking memory if necessary.
|
||||
}
|
||||
|
||||
mutex_unlock(&device->lock);
|
||||
if (result == B_OK) {
|
||||
TRACE("read successful with %ld bytes\n", *length);
|
||||
return B_OK;
|
||||
status_t status = B_OK;
|
||||
while (request->RemainingBytes() > 0) {
|
||||
IOOperation operation;
|
||||
status = dmaResource->TranslateNext(request, &operation, 0);
|
||||
if (status != B_OK)
|
||||
break;
|
||||
|
||||
do {
|
||||
TRACE("%p: IOO offset: %" B_PRIdOFF ", length: %" B_PRIuGENADDR
|
||||
", write: %s\n", request, operation.Offset(),
|
||||
operation.Length(), operation.IsWrite() ? "yes" : "no");
|
||||
|
||||
struct transfer_data data;
|
||||
data.physical = true;
|
||||
data.phys_vecs = (physical_entry*)operation.Vecs();
|
||||
data.vec_count = operation.VecCount();
|
||||
|
||||
size_t length = operation.Length();
|
||||
const uint64 blockPosition = operation.Offset() / lun->block_size;
|
||||
const size_t blockCount = length / lun->block_size;
|
||||
if (operation.IsWrite()) {
|
||||
status = usb_disk_block_write(lun,
|
||||
blockPosition, blockCount, data, &length);
|
||||
} else {
|
||||
status = usb_disk_block_read(lun,
|
||||
blockPosition, blockCount, data, &length);
|
||||
}
|
||||
|
||||
operation.SetStatus(status, length);
|
||||
} while (status == B_OK && !operation.Finish());
|
||||
|
||||
if (status == B_OK && operation.Status() != B_OK) {
|
||||
TRACE_ALWAYS("I/O succeeded but IOOperation failed!\n");
|
||||
status = operation.Status();
|
||||
}
|
||||
|
||||
request->OperationFinished(&operation);
|
||||
dmaResource->RecycleBuffer(operation.Buffer());
|
||||
|
||||
TRACE("%p: status %s, remaining bytes %" B_PRIuGENADDR "\n", request,
|
||||
strerror(status), request->RemainingBytes());
|
||||
if (status != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
*length = 0;
|
||||
TRACE_ALWAYS("read failed: %s\n", strerror(result));
|
||||
return result;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_write(void *cookie, off_t position, const void *buffer,
|
||||
size_t *length)
|
||||
usb_disk_direct_io(device_lun *lun, io_request *request)
|
||||
{
|
||||
if (buffer == NULL || length == NULL)
|
||||
return B_BAD_VALUE;
|
||||
generic_io_vec* genericVecs = request->Buffer()->Vecs();
|
||||
const uint32 count = request->Buffer()->VecCount();
|
||||
BStackOrHeapArray<iovec, 16> vecs(count);
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
vecs[i].iov_base = (void*)genericVecs[i].base;
|
||||
vecs[i].iov_len = genericVecs[i].length;
|
||||
}
|
||||
struct transfer_data data;
|
||||
data.vecs = vecs;
|
||||
data.vec_count = count;
|
||||
|
||||
size_t length = request->Length();
|
||||
const uint64 blockPosition = request->Offset() / lun->block_size;
|
||||
const size_t blockCount = length / lun->block_size;
|
||||
|
||||
status_t status;
|
||||
if (request->IsWrite()) {
|
||||
status = usb_disk_block_write(lun,
|
||||
blockPosition, blockCount, data, &length);
|
||||
} else {
|
||||
status = usb_disk_block_read(lun,
|
||||
blockPosition, blockCount, data, &length);
|
||||
}
|
||||
|
||||
request->SetTransferredBytes(length != request->Length(), length);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
usb_disk_io(void *cookie, io_request *request)
|
||||
{
|
||||
TRACE("io(%p)\n", request);
|
||||
|
||||
TRACE("write(%" B_PRIdOFF", %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;
|
||||
uint64 blockPosition = 0;
|
||||
size_t 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) {
|
||||
if (IS_USER_ADDRESS(buffer))
|
||||
result = user_memcpy(partialBuffer, buffer, *length);
|
||||
else
|
||||
memcpy(partialBuffer, buffer, *length);
|
||||
}
|
||||
if (result == B_OK) {
|
||||
size_t blockLength = blockCount * lun->block_size;
|
||||
result = usb_disk_block_write(lun, blockPosition, blockCount,
|
||||
blockBuffer, &blockLength);
|
||||
free(blockBuffer);
|
||||
}
|
||||
status_t status;
|
||||
if (!usb_disk_needs_bounce(lun, request)) {
|
||||
status = usb_disk_direct_io(lun, request);
|
||||
} else {
|
||||
result = usb_disk_block_write(lun, blockPosition, blockCount,
|
||||
(void *)buffer, length);
|
||||
status = usb_disk_bounced_io(lun, request);
|
||||
}
|
||||
|
||||
mutex_unlock(&device->lock);
|
||||
if (result == B_OK) {
|
||||
TRACE("write successful with %ld bytes\n", *length);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
*length = 0;
|
||||
TRACE_ALWAYS("write failed: %s\n", strerror(result));
|
||||
return result;
|
||||
if (request->Status() > 0)
|
||||
request->SetStatusAndNotify(status);
|
||||
else
|
||||
request->NotifyFinished();
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@ -1892,9 +1963,9 @@ struct device_module_info sUsbDiskDevice = {
|
||||
usb_disk_open,
|
||||
usb_disk_close,
|
||||
usb_disk_free,
|
||||
usb_disk_read,
|
||||
usb_disk_write,
|
||||
NULL, // io
|
||||
NULL, // read
|
||||
NULL, // write
|
||||
usb_disk_io,
|
||||
usb_disk_ioctl,
|
||||
|
||||
NULL, // select
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2008, Haiku Inc. All rights reserved.
|
||||
* Copyright 2008-2023, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
@ -9,6 +9,9 @@
|
||||
#define _USB_DISK_H_
|
||||
|
||||
|
||||
#include <util/Vector.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include <lock.h>
|
||||
#include <USB3.h>
|
||||
#include <device_manager.h>
|
||||
@ -22,6 +25,8 @@
|
||||
|
||||
#define SYNC_SUPPORT_RELOAD 5
|
||||
|
||||
struct IOScheduler;
|
||||
struct DMAResource;
|
||||
typedef struct device_lun_s device_lun;
|
||||
|
||||
// holds common information about an attached device (pointed to by luns)
|
||||
@ -34,6 +39,9 @@ typedef struct disk_device_s {
|
||||
uint32 open_count;
|
||||
mutex lock;
|
||||
|
||||
// IO operations
|
||||
Vector<DMAResource*> dma_resources;
|
||||
|
||||
// device state
|
||||
usb_pipe bulk_in;
|
||||
usb_pipe bulk_out;
|
||||
|
Loading…
x
Reference in New Issue
Block a user