* First baby steps in letting our drivers use the new I/O request/scheduler

architecture: for now, we do this on the lowest layer only, therefore all
  requests are handled synchronously (ie. in the scheduler's thread).
* Instead of using the block_io module, scsi_disk (and scsi_cd) are now
  exporting a device on their own, and use an I/O scheduler with an appropriate
  DMA resource.
* There are still lots of TODOs, and it can easily panic - don't update if
  you intend to demo Haiku.
* scsi_periph now only has an io() function that get an io_operation, instead
  of the previous read/write functions, moved preferred CCB size from those
  functions into the device registration.
* Changed all scsi_periph files to C++.
* scsi_cd ported, too, but untested.
* Removed block_io from image - it will be removed completely soon.
* Temporarily commented an ASSERT() in the ATA bus manager (in case you use
  it); it's sometimes triggered by the code now, and I haven't yet looked into
  the issue -- doesn't seem to harm, at least.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26828 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2008-08-05 21:11:51 +00:00
parent 34faa5f214
commit 24593e2c79
19 changed files with 814 additions and 751 deletions
+2 -2
View File
@@ -160,7 +160,7 @@ AddFilesToHaikuImage beos system add-ons kernel debugger
AddFilesToHaikuImage beos system add-ons kernel file_systems AddFilesToHaikuImage beos system add-ons kernel file_systems
: $(BEOS_ADD_ONS_FILE_SYSTEMS) ; : $(BEOS_ADD_ONS_FILE_SYSTEMS) ;
AddFilesToHaikuImage beos system add-ons kernel generic AddFilesToHaikuImage beos system add-ons kernel generic
: block_io dpc ide_adapter locked_pool mpu401 scsi_periph ; : dpc ide_adapter locked_pool mpu401 scsi_periph ;
AddFilesToHaikuImage beos system add-ons kernel partitioning_systems AddFilesToHaikuImage beos system add-ons kernel partitioning_systems
: intel session ; : intel session ;
AddFilesToHaikuImage beos system add-ons kernel interrupt_controllers AddFilesToHaikuImage beos system add-ons kernel interrupt_controllers
@@ -372,7 +372,7 @@ AddFilesToHaikuImage beos system : zbeos ;
AddBootModuleSymlinksToHaikuImage AddBootModuleSymlinksToHaikuImage
pci $(X86_ONLY)isa config_manager ide scsi usb pci $(X86_ONLY)isa config_manager ide scsi usb
$(PPC_ONLY)openpic $(PPC_ONLY)openpic
block_io ide_adapter locked_pool scsi_periph ide_adapter locked_pool scsi_periph
ahci generic_ide_pci it8211 legacy_sata silicon_image_3112 $(X86_ONLY)ide_isa ahci generic_ide_pci it8211 legacy_sata silicon_image_3112 $(X86_ONLY)ide_isa
<usb>uhci <usb>ohci <usb>ehci <usb>uhci <usb>ohci <usb>ehci
scsi_cd scsi_disk usb_disk scsi_cd scsi_disk usb_disk
+1
View File
@@ -134,6 +134,7 @@ typedef struct scsi_ccb {
uint16 sg_count; // number of S/G entries uint16 sg_count; // number of S/G entries
uint32 data_length; // length of data uint32 data_length; // length of data
int32 data_resid; // data transfer residual length: 2's comp int32 data_resid; // data transfer residual length: 2's comp
void *io_operation;
uchar sense[SCSI_MAX_SENSE_SIZE]; // autosense data uchar sense[SCSI_MAX_SENSE_SIZE]; // autosense data
uchar sense_resid; // autosense resid length: 2's comp uchar sense_resid; // autosense resid length: 2's comp
+21 -35
View File
@@ -7,8 +7,7 @@
#ifndef _SCSI_PERIPH_H #ifndef _SCSI_PERIPH_H
#define _SCSI_PERIPH_H #define _SCSI_PERIPH_H
/* /*! Use this module to minimize work required to write a SCSI
Use this module to minimize work required to write a SCSI
peripheral driver. peripheral driver.
It takes care of: It takes care of:
@@ -64,21 +63,21 @@ typedef struct periph_device_info *periph_device_cookie;
// cookie issued by driver per file handle // cookie issued by driver per file handle
typedef struct periph_handle_info *periph_handle_cookie; typedef struct periph_handle_info *periph_handle_cookie;
typedef struct IOOperation io_operation;
// callbacks to be provided by peripheral driver // callbacks to be provided by peripheral driver
typedef struct scsi_periph_callbacks { typedef struct scsi_periph_callbacks {
// *** block devices *** // *** block devices ***
// informs of new size of medium // informs of new size of medium
// (set to NULL if not a block device) // (set to NULL if not a block device)
void (*set_capacity)( periph_device_cookie periph_device, uint64 capacity, void (*set_capacity)(periph_device_cookie cookie, uint64 capacity,
uint32 block_size ); uint32 blockSize);
// *** removable devices // *** removable devices
// called when media got changed (can be NULL if medium is not changable) // called when media got changed (can be NULL if medium is not changable)
// (you don't need to call periph->media_changed, but it's doesn't if you do) // (you don't need to call periph->media_changed, but it's doesn't if you do)
// ccb - request at your disposal // ccb - request at your disposal
void (*media_changed)( periph_device_cookie periph_device, void (*media_changed)(periph_device_cookie cookie, scsi_ccb *request);
scsi_ccb *request );
} scsi_periph_callbacks; } scsi_periph_callbacks;
@@ -87,48 +86,36 @@ typedef struct scsi_periph_interface {
module_info info; module_info info;
// *** init/cleanup *** // *** init/cleanup ***
status_t (*register_device)( // preferred_ccb_size - preferred command size; if zero, the shortest is used
periph_device_cookie periph_device, status_t (*register_device)(periph_device_cookie cookie,
scsi_periph_callbacks *callbacks, scsi_periph_callbacks *callbacks, scsi_device scsiDevice,
scsi_device scsi_device, scsi_device_interface *scsi, device_node *node, bool removable,
scsi_device_interface *scsi, int preferredCcbSize, scsi_periph_device *driver);
device_node *node,
bool removable,
scsi_periph_device *driver );
status_t (*unregister_device)(scsi_periph_device driver); status_t (*unregister_device)(scsi_periph_device driver);
// *** basic command execution *** // *** basic command execution ***
// exec command, retrying on problems // exec command, retrying on problems
status_t (*safe_exec)( status_t (*safe_exec)(scsi_periph_device periphCookie, scsi_ccb *request);
scsi_periph_device periph_device,
scsi_ccb *request );
// exec simple command // exec simple command
status_t (*simple_exec)( scsi_periph_device device, status_t (*simple_exec)(scsi_periph_device device, void *cdb,
void *cdb, uchar cdb_len, void *data, size_t data_len, uint8 cdbLength, void *data, size_t dataLength, int ccbFlags);
int ccb_flags );
// *** file handling *** // *** file handling ***
// to be called when a new file is opened // to be called when a new file is opened
status_t (*handle_open)(scsi_periph_device device, status_t (*handle_open)(scsi_periph_device device,
periph_handle_cookie periph_handle, scsi_periph_handle *res_handle ); periph_handle_cookie periph_handle, scsi_periph_handle *_handle);
// to be called when a file is closed // to be called when a file is closed
status_t (*handle_close)(scsi_periph_handle handle); status_t (*handle_close)(scsi_periph_handle handle);
// to be called when a file is freed // to be called when a file is freed
status_t (*handle_free)(scsi_periph_handle handle); status_t (*handle_free)(scsi_periph_handle handle);
// *** default implementation for block devices *** // *** default implementation for block devices ***
// block read status_t (*io)(scsi_periph_device device, io_operation *operation,
// preferred_ccb_size - preferred command size; if zero, the shortest is used size_t *_bytesTransferred);
status_t (*read)( scsi_periph_handle handle, const phys_vecs *vecs,
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size );
// block write
// (see block_read)
status_t (*write)( scsi_periph_handle handle, const phys_vecs *vecs,
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size );
// block ioctls // block ioctls
status_t (*ioctl)( scsi_periph_handle handle, int op, void *buf, size_t len ); status_t (*ioctl)(scsi_periph_handle handle, int op, void *buffer,
size_t length);
// check medium capacity (calls set_capacity callback on success) // check medium capacity (calls set_capacity callback on success)
// request - ccb for this device; is used to talk to device // request - ccb for this device; is used to talk to device
status_t (*check_capacity)(scsi_periph_device device, scsi_ccb *request); status_t (*check_capacity)(scsi_periph_device device, scsi_ccb *request);
@@ -139,10 +126,10 @@ typedef struct scsi_periph_interface {
// convert result of *request to err_res // convert result of *request to err_res
err_res (*check_error)(scsi_periph_device device, scsi_ccb *request); err_res (*check_error)(scsi_periph_device device, scsi_ccb *request);
// send start or stop command to device // send start or stop command to device
// with_LoEj = true - include loading/ejecting, // withLoadEject = true - include loading/ejecting,
// false - only do allow/deny // false - only do allow/deny
err_res (*send_start_stop)(scsi_periph_device device, scsi_ccb *request, err_res (*send_start_stop)(scsi_periph_device device, scsi_ccb *request,
bool start, bool with_LoEj ); bool start, bool withLoadEject);
// checks media status and waits for device to become ready // checks media status and waits for device to become ready
// returns: B_OK, B_DEV_MEDIA_CHANGE_REQUESTED, B_NO_MEMORY or // returns: B_OK, B_DEV_MEDIA_CHANGE_REQUESTED, B_NO_MEMORY or
// pending error reported by handle_get_error // pending error reported by handle_get_error
@@ -162,4 +149,3 @@ typedef struct scsi_periph_interface {
#define SCSI_PERIPH_MODULE_NAME "generic/scsi_periph/v1" #define SCSI_PERIPH_MODULE_NAME "generic/scsi_periph/v1"
#endif /* _SCSI_PERIPH_H */ #endif /* _SCSI_PERIPH_H */
@@ -77,7 +77,7 @@ ata_request_clear_sense(ata_request *request)
void void
ata_request_set_status(ata_request *request, uint8 status) ata_request_set_status(ata_request *request, uint8 status)
{ {
ASSERT(status != SCSI_REQ_CMP); // ASSERT(status != SCSI_REQ_CMP);
if (request && request->ccb) if (request && request->ccb)
request->ccb->subsys_status = status; request->ccb->subsys_status = status;
} }
@@ -106,7 +106,7 @@ ata_request_finish(ata_request *request, bool resubmit)
request, ccb->subsys_status, request->senseKey); request, ccb->subsys_status, request->senseKey);
// when the request completed and has set sense // when the request completed and has set sense
// data, report this to the scsci stack by setting // data, report this to the scsi stack by setting
// CHECK CONDITION status // CHECK CONDITION status
if (ccb->subsys_status == SCSI_REQ_CMP && request->senseKey != 0) { if (ccb->subsys_status == SCSI_REQ_CMP && request->senseKey != 0) {
@@ -40,6 +40,7 @@ scsi_alloc_ccb(scsi_device_info *device)
// reset some very important fields // reset some very important fields
// TODO: should we better omit that to find bugs easier? // TODO: should we better omit that to find bugs easier?
ccb->sg_list = NULL; ccb->sg_list = NULL;
ccb->io_operation = NULL;
ccb->sort = -1; ccb->sort = -1;
SHOW_FLOW(3, "path=%d", ccb->path_id); SHOW_FLOW(3, "path=%d", ccb->path_id);
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_disk ; SubDir HAIKU_TOP src add-ons kernel drivers disk scsi scsi_disk ;
UsePrivateHeaders drivers kernel ; UsePrivateHeaders drivers kernel ;
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
KernelAddon scsi_disk : KernelAddon scsi_disk :
scsi_disk.cpp scsi_disk.cpp
@@ -31,11 +31,10 @@
static scsi_periph_interface* sSCSIPeripheral; static scsi_periph_interface* sSCSIPeripheral;
static device_manager_info* sDeviceManager; static device_manager_info* sDeviceManager;
static block_io_for_driver_interface *sBlockIO;
static status_t static status_t
update_capacity(das_device_info *device) update_capacity(das_driver_info *device)
{ {
TRACE("update_capacity()\n"); TRACE("update_capacity()\n");
@@ -53,20 +52,20 @@ update_capacity(das_device_info *device)
static status_t static status_t
get_geometry(das_handle_info *handle, device_geometry *geometry) get_geometry(das_handle* handle, device_geometry* geometry)
{ {
das_device_info *device = handle->device; das_driver_info* info = handle->info;
status_t status = update_capacity(device); status_t status = update_capacity(info);
if (status < B_OK) if (status < B_OK)
return status; return status;
geometry->bytes_per_sector = device->block_size; geometry->bytes_per_sector = info->block_size;
geometry->sectors_per_track = 1; geometry->sectors_per_track = 1;
geometry->cylinder_count = device->capacity; geometry->cylinder_count = info->capacity;
geometry->head_count = 1; geometry->head_count = 1;
geometry->device_type = B_DISK; geometry->device_type = B_DISK;
geometry->removable = device->removable; geometry->removable = info->removable;
// TBD: for all but CD-ROMs, read mode sense - medium type // TBD: for all but CD-ROMs, read mode sense - medium type
// (bit 7 of block device specific parameter for Optical Memory Block Device) // (bit 7 of block device specific parameter for Optical Memory Block Device)
@@ -86,7 +85,7 @@ get_geometry(das_handle_info *handle, device_geometry *geometry)
static status_t static status_t
load_eject(das_device_info *device, bool load) load_eject(das_driver_info *device, bool load)
{ {
TRACE("load_eject()\n"); TRACE("load_eject()\n");
@@ -104,7 +103,7 @@ load_eject(das_device_info *device, bool load)
static status_t static status_t
synchronize_cache(das_device_info *device) synchronize_cache(das_driver_info *device)
{ {
TRACE("synchronize_cache()\n"); TRACE("synchronize_cache()\n");
@@ -135,36 +134,65 @@ log2(uint32 x)
} }
// #pragma mark - block_io API static status_t
do_io(void* cookie, IOOperation* operation)
static void
das_set_device(das_device_info *info, block_io_device device)
{ {
info->block_io_device = device; das_driver_info* info = (das_driver_info*)cookie;
// TODO: this can go away as soon as we pushed the IOOperation to the upper
// layers - we can then set scsi_periph::io() as callback for the scheduler
size_t bytesTransferred;
status_t status = sSCSIPeripheral->io(info->scsi_periph_device, operation,
&bytesTransferred);
info->io_scheduler->OperationCompleted(operation, status, bytesTransferred);
return status;
}
// #pragma mark - device module API
static status_t
das_init_device(void* _info, void** _cookie)
{
das_driver_info* info = (das_driver_info*)_info;
// and get (initial) capacity // and get (initial) capacity
scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device); scsi_ccb *request = info->scsi->alloc_ccb(info->scsi_device);
if (request == NULL) if (request == NULL)
return; return B_NO_MEMORY;
sSCSIPeripheral->check_capacity(info->scsi_periph_device, request); sSCSIPeripheral->check_capacity(info->scsi_periph_device, request);
info->scsi->free_ccb(request); info->scsi->free_ccb(request);
*_cookie = info;
return B_OK;
}
static void
das_uninit_device(void* _cookie)
{
das_driver_info* info = (das_driver_info*)_cookie;
delete info->io_scheduler;
delete info->dma_resource;
} }
static status_t static status_t
das_open(das_device_info *device, das_handle_info **_cookie) das_open(void* _info, const char* path, int openMode, void** _cookie)
{ {
TRACE("open()\n"); das_driver_info* info = (das_driver_info*)_info;
das_handle_info *handle = (das_handle_info *)malloc(sizeof(*handle)); das_handle* handle = (das_handle*)malloc(sizeof(das_handle));
if (handle == NULL) if (handle == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
handle->device = device; handle->info = info;
status_t status = sSCSIPeripheral->handle_open(device->scsi_periph_device, status_t status = sSCSIPeripheral->handle_open(info->scsi_periph_device,
(periph_handle_cookie)handle, &handle->scsi_periph_handle); (periph_handle_cookie)handle, &handle->scsi_periph_handle);
if (status < B_OK) { if (status < B_OK) {
free(handle); free(handle);
@@ -177,8 +205,9 @@ das_open(das_device_info *device, das_handle_info **_cookie)
static status_t static status_t
das_close(das_handle_info *handle) das_close(void* cookie)
{ {
das_handle* handle = (das_handle*)cookie;
TRACE("close()\n"); TRACE("close()\n");
sSCSIPeripheral->handle_close(handle->scsi_periph_handle); sSCSIPeripheral->handle_close(handle->scsi_periph_handle);
@@ -187,8 +216,9 @@ das_close(das_handle_info *handle)
static status_t static status_t
das_free(das_handle_info *handle) das_free(void* cookie)
{ {
das_handle* handle = (das_handle*)cookie;
TRACE("free()\n"); TRACE("free()\n");
sSCSIPeripheral->handle_free(handle->scsi_periph_handle); sSCSIPeripheral->handle_free(handle->scsi_periph_handle);
@@ -198,38 +228,80 @@ das_free(das_handle_info *handle)
static status_t static status_t
das_read(das_handle_info *handle, const phys_vecs *vecs, off_t pos, das_read(void* cookie, off_t pos, void* buffer, size_t* _length)
size_t num_blocks, uint32 block_size, size_t *bytes_transferred)
{ {
return sSCSIPeripheral->read(handle->scsi_periph_handle, vecs, pos, das_handle* handle = (das_handle*)cookie;
num_blocks, block_size, bytes_transferred, 10); size_t length = *_length;
IORequest request;
status_t status = request.Init(pos, buffer, length, false, 0);
if (status != B_OK)
return status;
status = handle->info->io_scheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
if (status == B_OK)
*_length = length;
else
dprintf("das_read(): request.Wait() returned: %s\n", strerror(status));
return status;
} }
static status_t static status_t
das_write(das_handle_info *handle, const phys_vecs *vecs, off_t pos, das_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
size_t num_blocks, uint32 block_size, size_t *bytes_transferred)
{ {
return sSCSIPeripheral->write(handle->scsi_periph_handle, vecs, pos, das_handle* handle = (das_handle*)cookie;
num_blocks, block_size, bytes_transferred, 10); size_t length = *_length;
IORequest request;
status_t status = request.Init(pos, (void*)buffer, length, true, 0);
if (status != B_OK)
return status;
status = handle->info->io_scheduler->ScheduleRequest(&request);
if (status != B_OK)
return status;
status = request.Wait(0, 0);
if (status == B_OK)
*_length = length;
else
dprintf("das_write(): request.Wait() returned: %s\n", strerror(status));
return status;
} }
static status_t static status_t
das_ioctl(das_handle_info *handle, int op, void *buffer, size_t length) das_io(void *cookie, io_request *request)
{ {
das_device_info *device = handle->device; das_handle* handle = (das_handle*)cookie;
return handle->info->io_scheduler->ScheduleRequest(request);
}
static status_t
das_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
{
das_handle* handle = (das_handle*)cookie;
das_driver_info* info = handle->info;
TRACE("ioctl(op = %d)\n", op); TRACE("ioctl(op = %d)\n", op);
switch (op) { switch (op) {
case B_GET_DEVICE_SIZE: case B_GET_DEVICE_SIZE:
{ {
status_t status = update_capacity(device); status_t status = update_capacity(info);
if (status != B_OK) if (status != B_OK)
return status; return status;
size_t size = device->capacity * device->block_size; size_t size = info->capacity * info->block_size;
return user_memcpy(buffer, &size, sizeof(size_t)); return user_memcpy(buffer, &size, sizeof(size_t));
} }
@@ -247,18 +319,18 @@ das_ioctl(das_handle_info *handle, int op, void *buffer, size_t length)
} }
case B_GET_ICON: case B_GET_ICON:
return sSCSIPeripheral->get_icon(device->removable return sSCSIPeripheral->get_icon(info->removable
? icon_type_floppy : icon_type_disk, (device_icon *)buffer); ? icon_type_floppy : icon_type_disk, (device_icon *)buffer);
case B_EJECT_DEVICE: case B_EJECT_DEVICE:
case B_SCSI_EJECT: case B_SCSI_EJECT:
return load_eject(device, false); return load_eject(info, false);
case B_LOAD_MEDIA: case B_LOAD_MEDIA:
return load_eject(device, true); return load_eject(info, true);
case B_FLUSH_DRIVE_CACHE: case B_FLUSH_DRIVE_CACHE:
return synchronize_cache(device); return synchronize_cache(info);
default: default:
return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op, return sSCSIPeripheral->ioctl(handle->scsi_periph_handle, op,
@@ -271,7 +343,7 @@ das_ioctl(das_handle_info *handle, int op, void *buffer, size_t length)
static void static void
das_set_capacity(das_device_info *device, uint64 capacity, uint32 blockSize) das_set_capacity(das_driver_info* info, uint64 capacity, uint32 blockSize)
{ {
TRACE("das_set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n", TRACE("das_set_capacity(device = %p, capacity = %Ld, blockSize = %ld)\n",
device, capacity, blockSize); device, capacity, blockSize);
@@ -282,19 +354,40 @@ das_set_capacity(das_device_info *device, uint64 capacity, uint32 blockSize)
if ((1UL << blockShift) != blockSize) if ((1UL << blockShift) != blockSize)
blockShift = 0; blockShift = 0;
device->capacity = capacity; info->capacity = capacity;
device->block_size = blockSize;
sBlockIO->set_media_params(device->block_io_device, blockSize, if (info->block_size != blockSize) {
blockShift, capacity); if (info->block_size != 0) {
dprintf("old %ld, new %ld\n", info->block_size, blockSize);
panic("updating DMAResource not yet implemented...");
}
// TODO: we need to replace the DMAResource in our IOScheduler
status_t status = info->dma_resource->Init(info->node, blockSize);
if (status != B_OK)
panic("initializing DMAResource failed: %s", strerror(status));
info->io_scheduler = new(std::nothrow) IOScheduler(info->dma_resource);
if (info->io_scheduler == NULL)
panic("allocating IOScheduler failed.");
// TODO: use whole device name here
status = info->io_scheduler->Init("scsi");
if (status != B_OK)
panic("initializing IOScheduler failed: %s", strerror(status));
info->io_scheduler->SetCallback(do_io, info);
}
info->block_size = blockSize;
} }
static void static void
das_media_changed(das_device_info *device, scsi_ccb *request) das_media_changed(das_driver_info *device, scsi_ccb *request)
{ {
// do a capacity check // do a capacity check
// TBD: is this a good idea (e.g. if this is an empty CD)? // TODO: is this a good idea (e.g. if this is an empty CD)?
sSCSIPeripheral->check_capacity(device->scsi_periph_device, request); sSCSIPeripheral->check_capacity(device->scsi_periph_device, request);
} }
@@ -363,53 +456,54 @@ das_register_device(device_node *node)
{"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}}, {"removable", B_UINT8_TYPE, {ui8: deviceInquiry->removable_medium}},
// impose own max block restriction // impose own max block restriction
{B_BLOCK_DEVICE_MAX_BLOCKS_ITEM, B_UINT32_TYPE, {ui32: maxBlocks}}, {B_BLOCK_DEVICE_MAX_BLOCKS_ITEM, B_UINT32_TYPE, {ui32: maxBlocks}},
// in general, any disk can be a BIOS drive (even ZIP-disks)
{B_BLOCK_DEVICE_IS_BIOS_DRIVE, B_UINT8_TYPE, {ui8: 1}},
{ NULL } { NULL }
}; };
return sDeviceManager->register_node(node, SCSI_DISK_MODULE_NAME, attrs, return sDeviceManager->register_node(node, SCSI_DISK_DRIVER_MODULE_NAME,
NULL, NULL); attrs, NULL, NULL);
} }
static status_t static status_t
das_init_driver(device_node *node, void **cookie) das_init_driver(device_node *node, void **cookie)
{ {
das_device_info *device;
status_t status;
uint8 removable;
TRACE("das_init_driver"); TRACE("das_init_driver");
status = sDeviceManager->get_attr_uint8(node, "removable", uint8 removable;
status_t status = sDeviceManager->get_attr_uint8(node, "removable",
&removable, false); &removable, false);
if (status != B_OK) if (status != B_OK)
return status; return status;
device = (das_device_info *)malloc(sizeof(*device)); das_driver_info* info = (das_driver_info*)malloc(sizeof(das_driver_info));
if (device == NULL) if (info == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
memset(device, 0, sizeof(*device)); memset(info, 0, sizeof(*info));
device->node = node; info->dma_resource = new(std::nothrow) DMAResource;
device->removable = removable; if (info->dma_resource == NULL) {
free(info);
return B_NO_MEMORY;
}
info->node = node;
info->removable = removable;
device_node* parent = sDeviceManager->get_parent_node(node); device_node* parent = sDeviceManager->get_parent_node(node);
sDeviceManager->get_driver(parent, (driver_module_info **)&device->scsi, sDeviceManager->get_driver(parent, (driver_module_info **)&info->scsi,
(void **)&device->scsi_device); (void **)&info->scsi_device);
sDeviceManager->put_node(parent); sDeviceManager->put_node(parent);
status = sSCSIPeripheral->register_device((periph_device_cookie)device, status = sSCSIPeripheral->register_device((periph_device_cookie)info,
&callbacks, device->scsi_device, device->scsi, device->node, &callbacks, info->scsi_device, info->scsi, info->node,
device->removable, &device->scsi_periph_device); info->removable, 10, &info->scsi_periph_device);
if (status != B_OK) { if (status != B_OK) {
free(device); free(info);
return status; return status;
} }
*cookie = device; *cookie = info;
return B_OK; return B_OK;
} }
@@ -417,26 +511,26 @@ das_init_driver(device_node *node, void **cookie)
static void static void
das_uninit_driver(void *_cookie) das_uninit_driver(void *_cookie)
{ {
das_device_info *device = (das_device_info *)_cookie; das_driver_info* info = (das_driver_info*)_cookie;
sSCSIPeripheral->unregister_device(device->scsi_periph_device); sSCSIPeripheral->unregister_device(info->scsi_periph_device);
free(device); free(info);
} }
static status_t static status_t
das_register_child_devices(void* _cookie) das_register_child_devices(void* _cookie)
{ {
das_device_info *device = (das_device_info *)_cookie; das_driver_info* info = (das_driver_info*)_cookie;
status_t status; status_t status;
char *name;
name = sSCSIPeripheral->compose_device_name(device->node, "disk/scsi"); char* name = sSCSIPeripheral->compose_device_name(info->node,
"disk/scsi");
if (name == NULL) if (name == NULL)
return B_ERROR; return B_ERROR;
status = sDeviceManager->publish_device(device->node, name, status = sDeviceManager->publish_device(info->node, name,
B_BLOCK_IO_DEVICE_MODULE_NAME); SCSI_DISK_DEVICE_MODULE_NAME);
free(name); free(name);
return status; return status;
@@ -445,15 +539,36 @@ das_register_child_devices(void *_cookie)
module_dependency module_dependencies[] = { module_dependency module_dependencies[] = {
{SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral}, {SCSI_PERIPH_MODULE_NAME, (module_info**)&sSCSIPeripheral},
{B_BLOCK_IO_FOR_DRIVER_MODULE_NAME, (module_info **)&sBlockIO},
{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager}, {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
{} {}
}; };
block_device_interface sSCSIDiskModule = { struct device_module_info sSCSIDiskDevice = {
{ {
SCSI_DISK_DEVICE_MODULE_NAME,
0,
NULL
},
das_init_device,
das_uninit_device,
NULL, //das_remove,
das_open,
das_close,
das_free,
das_read,
das_write,
das_io,
das_ioctl,
NULL, // select
NULL, // deselect
};
struct driver_module_info sSCSIDiskDriver = {
{ {
SCSI_DISK_MODULE_NAME, SCSI_DISK_DRIVER_MODULE_NAME,
0, 0,
NULL NULL
}, },
@@ -465,22 +580,10 @@ block_device_interface sSCSIDiskModule = {
das_register_child_devices, das_register_child_devices,
NULL, // rescan NULL, // rescan
NULL, // removed NULL, // removed
},
(void (*)(block_device_cookie *, block_io_device)) &das_set_device,
(status_t (*)(block_device_cookie *, block_device_handle_cookie **))&das_open,
(status_t (*)(block_device_handle_cookie *)) &das_close,
(status_t (*)(block_device_handle_cookie *)) &das_free,
(status_t (*)(block_device_handle_cookie *, const phys_vecs *,
off_t, size_t, uint32, size_t *)) &das_read,
(status_t (*)(block_device_handle_cookie *, const phys_vecs *,
off_t, size_t, uint32, size_t *)) &das_write,
(status_t (*)(block_device_handle_cookie *, int, void *, size_t))&das_ioctl,
}; };
module_info* modules[] = { module_info* modules[] = {
(module_info *)&sSCSIDiskModule, (module_info*)&sSCSIDiskDriver,
(module_info*)&sSCSIDiskDevice,
NULL NULL
}; };
@@ -12,27 +12,32 @@
#include <scsi.h> #include <scsi.h>
#include <scsi_periph.h> #include <scsi_periph.h>
#include "dma_resources.h"
#define SCSI_DISK_MODULE_NAME "drivers/disk/scsi/scsi_dsk/driver_v1" #include "io_requests.h"
#include "IOScheduler.h"
// must start as block_device_cookie #define SCSI_DISK_DRIVER_MODULE_NAME "drivers/disk/scsi/scsi_disk/driver_v1"
typedef struct das_device_info { #define SCSI_DISK_DEVICE_MODULE_NAME "drivers/disk/scsi/scsi_disk/device_v1"
struct das_driver_info {
device_node* node; device_node* node;
::scsi_periph_device scsi_periph_device; ::scsi_periph_device scsi_periph_device;
::scsi_device scsi_device; ::scsi_device scsi_device;
scsi_device_interface* scsi; scsi_device_interface* scsi;
::block_io_device block_io_device; IOScheduler* io_scheduler;
DMAResource* dma_resource;
uint64 capacity; uint64 capacity;
uint32 block_size; uint32 block_size;
bool removable; // true, if device is removable bool removable;
} das_device_info; };
typedef struct das_handle_info { struct das_handle {
::scsi_periph_handle scsi_periph_handle; ::scsi_periph_handle scsi_periph_handle;
das_device_info *device; das_driver_info* info;
} das_handle_info; };
#endif /* _SCSI_DISK_H */ #endif /* _SCSI_DISK_H */
+10 -9
View File
@@ -1,6 +1,7 @@
SubDir HAIKU_TOP src add-ons kernel generic scsi_periph ; SubDir HAIKU_TOP src add-ons kernel generic scsi_periph ;
UsePrivateHeaders drivers kernel ; UsePrivateHeaders drivers kernel ;
SubDirHdrs $(HAIKU_TOP) src system kernel device_manager ;
# disable debug output, if debugging is disabled # disable debug output, if debugging is disabled
if $(DEBUG) = 0 { if $(DEBUG) = 0 {
@@ -8,13 +9,13 @@ if $(DEBUG) = 0 {
} }
KernelAddon scsi_periph : KernelAddon scsi_periph :
block.c block.cpp
device_icons.c device_icons.cpp
device.c device.cpp
error_handling.c error_handling.cpp
handle.c handle.cpp
io.c io.cpp
removable.c removable.cpp
scsi_periph.c scsi_periph.cpp
sync.c sync.cpp
; ;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2007, Haiku, Inc. All RightsReserved. * Copyright 2004-2008, Haiku, Inc. All RightsReserved.
* Copyright 2002-2003, Thomas Kurschel. All rights reserved. * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
@@ -30,7 +30,7 @@ periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request)
request->flags = SCSI_DIR_IN; request->flags = SCSI_DIR_IN;
request->data = (char *)&capacityResult; request->data = (uint8*)&capacityResult;
request->data_length = sizeof(capacityResult); request->data_length = sizeof(capacityResult);
request->cdb_length = sizeof(scsi_cmd_read_capacity); request->cdb_length = sizeof(scsi_cmd_read_capacity);
request->timeout = device->std_timeout; request->timeout = device->std_timeout;
@@ -68,6 +68,8 @@ periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *request)
SHOW_FLOW(3, "capacity = %Ld, block_size = %ld", capacity, blockSize); SHOW_FLOW(3, "capacity = %Ld, block_size = %ld", capacity, blockSize);
device->block_size = blockSize;
device->callbacks->set_capacity(device->periph_device, device->callbacks->set_capacity(device->periph_device,
capacity, blockSize); capacity, blockSize);
@@ -1,13 +1,11 @@
/* /*
* Copyright 2004-2006, Haiku, Inc. All RightsReserved. * Copyright 2004-2008, Haiku, Inc. All RightsReserved.
* Copyright 2002/03, Thomas Kurschel. All rights reserved. * Copyright 2002/03, Thomas Kurschel. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
/* //! Basic handling of device.
Basic handling of device.
*/
#include "scsi_periph_int.h" #include "scsi_periph_int.h"
@@ -27,15 +25,15 @@ periph_compose_device_name(device_node *node, const char *prefix)
char name[128]; char name[128];
uint32 channel; uint32 channel;
if (pnp->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &pathID, true) != B_OK if (gDeviceManager->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &pathID, true) != B_OK
|| pnp->get_attr_uint8(node, SCSI_DEVICE_TARGET_ID_ITEM, &targetID, true) != B_OK || gDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TARGET_ID_ITEM, &targetID, true) != B_OK
|| pnp->get_attr_uint8(node, SCSI_DEVICE_TARGET_LUN_ITEM, &targetLUN, true) != B_OK) || gDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TARGET_LUN_ITEM, &targetLUN, true) != B_OK)
return NULL; return NULL;
// IDE devices have a different naming scheme // IDE devices have a different naming scheme
if (pnp->get_attr_uint32(node, "ide/channel_id", &channel, true) == B_OK if (gDeviceManager->get_attr_uint32(node, "ide/channel_id", &channel, true) == B_OK
&& pnp->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM, &type, true) == B_OK) { && gDeviceManager->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM, &type, true) == B_OK) {
// this is actually an IDE device, so we ignore the prefix // this is actually an IDE device, so we ignore the prefix
// a bus device for those // a bus device for those
@@ -56,7 +54,7 @@ periph_compose_device_name(device_node *node, const char *prefix)
status_t status_t
periph_register_device(periph_device_cookie periph_device, scsi_periph_callbacks *callbacks, periph_register_device(periph_device_cookie periph_device, scsi_periph_callbacks *callbacks,
scsi_device scsi_device, scsi_device_interface *scsi, device_node *node, scsi_device scsi_device, scsi_device_interface *scsi, device_node *node,
bool removable, scsi_periph_device *driver) bool removable, int preferredCcbSize, scsi_periph_device *driver)
{ {
scsi_periph_device_info *device; scsi_periph_device_info *device;
status_t res; status_t res;
@@ -85,6 +83,7 @@ periph_register_device(periph_device_cookie periph_device, scsi_periph_callbacks
// set some default options // set some default options
device->next_tag_action = 0; device->next_tag_action = 0;
device->preferred_ccb_size = preferredCcbSize;
device->rw10_enabled = true; device->rw10_enabled = true;
// launch sync daemon // launch sync daemon
@@ -374,7 +374,7 @@ periph_get_icon(icon_type type, device_icon *data)
{ {
SHOW_FLOW( 3, "%d", type ); SHOW_FLOW( 3, "%d", type );
if (type < 0 || type >= sizeof(icons) / sizeof(icons[0])) if (type < 0 || type >= int(sizeof(icons) / sizeof(icons[0])))
return B_BAD_INDEX; return B_BAD_INDEX;
switch (data->icon_size) { switch (data->icon_size) {
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2007, Haiku, Inc. All RightsReserved. * Copyright 2004-2008, Haiku, Inc. All Rights Reserved.
* Copyright 2002/03, Thomas Kurschel. All rights reserved. * Copyright 2002/03, Thomas Kurschel. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
@@ -15,184 +15,17 @@
#include <stdlib.h> #include <stdlib.h>
// we don't want to inline this function - it's just not worth it
static int periph_read_write(scsi_periph_handle_info *handle, const phys_vecs *vecs,
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size, bool write);
status_t
periph_read(scsi_periph_handle_info *handle, const phys_vecs *vecs, off_t pos,
size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size)
{
return periph_read_write(handle, vecs, pos, num_blocks, block_size,
bytes_transferred, preferred_ccb_size, false);
}
status_t
periph_write(scsi_periph_handle_info *handle, const phys_vecs *vecs, off_t pos,
size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size)
{
return periph_read_write(handle, vecs, pos, num_blocks, block_size,
bytes_transferred, preferred_ccb_size, true);
}
/*! Universal read/write function */
static int
periph_read_write(scsi_periph_handle_info *handle, const phys_vecs *vecs,
off_t pos64, size_t num_blocks, uint32 block_size,
size_t *bytes_transferred, int preferred_ccb_size, bool write)
{
scsi_periph_device_info *device = handle->device;
scsi_ccb *request;
err_res res;
int retries = 0;
int err;
uint32 pos = pos64;
// don't test rw10_enabled restrictions - this flag may get changed
request = device->scsi->alloc_ccb(device->scsi_device);
if (request == NULL)
return B_NO_MEMORY;
do {
size_t num_bytes;
bool is_rw10;
request->flags = write ? SCSI_DIR_OUT : SCSI_DIR_IN;
// make sure we avoid 10 byte commands if they aren't supported
if( !device->rw10_enabled || preferred_ccb_size == 6) {
// restricting transfer is OK - the block manager will
// take care of transferring the rest
if (num_blocks > 0x100)
num_blocks = 0x100;
// no way to break the 21 bit address limit
if (pos64 > 0x200000) {
err = B_BAD_VALUE;
goto abort;
}
// don't allow transfer cross the 24 bit address limit
// (I'm not sure whether this is allowed, but this way we
// are sure to not ask for trouble)
if (pos < 0x100000)
num_blocks = min(num_blocks, 0x100000 - pos);
}
num_bytes = num_blocks * block_size;
request->data = NULL;
request->sg_list = vecs->vec;
request->data_length = num_bytes;
request->sg_count = vecs->num;
request->sort = pos;
request->timeout = device->std_timeout;
// see whether daemon instructed us to post an ordered command;
// reset flag after read
SHOW_FLOW( 3, "flag=%x, next_tag=%x, ordered: %s",
(int)request->flags, (int)device->next_tag_action,
(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no" );
// use shortest commands whenever possible
if (pos + num_blocks < 0x200000 && num_blocks <= 0x100) {
scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
is_rw10 = false;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = write ? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
cmd->high_lba = (pos >> 16) & 0x1f;
cmd->mid_lba = (pos >> 8) & 0xff;
cmd->low_lba = pos & 0xff;
cmd->length = num_blocks;
request->cdb_length = sizeof(*cmd);
} else {
scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
is_rw10 = true;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = write ? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
cmd->relative_address = 0;
cmd->force_unit_access = 0;
cmd->disable_page_out = 0;
cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
cmd->length = B_HOST_TO_BENDIAN_INT16(num_blocks);
request->cdb_length = sizeof(*cmd);
}
// last chance to detect errors that occured during concurrent accesses
err = handle->pending_error;
if (err)
goto abort;
device->scsi->async_io(request);
acquire_sem(request->completion_sem);
// ask generic peripheral layer what to do now
res = periph_check_error(device, request);
switch (res.action) {
case err_act_ok:
*bytes_transferred = num_bytes - request->data_resid;
break;
case err_act_start:
res = periph_send_start_stop(device, request, 1, device->removable);
if (res.action == err_act_ok)
res.action = err_act_retry;
break;
case err_act_invalid_req:
// if this was a 10 byte command, the device probably doesn't
// support them, so disable them and retry
if (is_rw10) {
atomic_and(&device->rw10_enabled, 0);
res.action = err_act_retry;
} else
res.action = err_act_fail;
break;
}
} while((res.action == err_act_retry && retries++ < 3)
|| (res.action == err_act_many_retries && retries++ < 30));
device->scsi->free_ccb(request);
// peripheral layer only created "read" error, so we have to
// map them to "write" errors if this was a write request
if (res.error_code == B_DEV_READ_ERROR && write)
return B_DEV_WRITE_ERROR;
return res.error_code;
abort:
device->scsi->free_ccb(request);
return err;
}
static status_t static status_t
inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry) inquiry(scsi_periph_device_info *device, scsi_inquiry *inquiry)
{ {
const scsi_res_inquiry *device_inquiry = NULL; const scsi_res_inquiry *device_inquiry = NULL;
size_t inquiry_len; size_t inquiryLength;
if (pnp->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM, if (gDeviceManager->get_attr_raw(device->node, SCSI_DEVICE_INQUIRY_ITEM,
(const void **)&device_inquiry, &inquiry_len, true) != B_OK) (const void **)&device_inquiry, &inquiryLength, true) != B_OK)
return B_ERROR; return B_ERROR;
memcpy(inquiry, device_inquiry, min(inquiry_len, sizeof(scsi_inquiry))); memcpy(inquiry, device_inquiry, min_c(inquiryLength, sizeof(scsi_inquiry)));
return B_OK; return B_OK;
} }
@@ -234,7 +67,7 @@ raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
else else
request->flags |= SCSI_DIR_NONE; request->flags |= SCSI_DIR_NONE;
request->data = cmd->data; request->data = (uint8*)cmd->data;
request->sg_list = NULL; request->sg_list = NULL;
request->data_length = cmd->data_length; request->data_length = cmd->data_length;
request->sort = -1; request->sort = -1;
@@ -252,8 +85,8 @@ raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
cmd->scsi_status = request->device_status; cmd->scsi_status = request->device_status;
if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) { if ((request->subsys_status & SCSI_AUTOSNS_VALID) != 0 && cmd->sense_data) {
memcpy(cmd->sense_data, request->sense, memcpy(cmd->sense_data, request->sense, min_c(cmd->sense_data_length,
min(cmd->sense_data_length, (size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid)); (size_t)SCSI_MAX_SENSE_SIZE - request->sense_resid));
} }
if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) { if ((cmd->flags & B_RAW_DEVICE_REPORT_RESIDUAL) != 0) {
@@ -271,7 +104,8 @@ raw_command(scsi_periph_device_info *device, raw_device_command *cmd)
status_t status_t
periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer, size_t length) periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer,
size_t length)
{ {
switch (op) { switch (op) {
case B_GET_MEDIA_STATUS: { case B_GET_MEDIA_STATUS: {
@@ -293,7 +127,7 @@ periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer, size_t lengt
return prevent_allow(handle->device, *(bool *)buffer); return prevent_allow(handle->device, *(bool *)buffer);
case B_RAW_DEVICE_COMMAND: case B_RAW_DEVICE_COMMAND:
return raw_command(handle->device, buffer); return raw_command(handle->device, (raw_device_command*)buffer);
default: default:
if (handle->device->scsi->ioctl != NULL) { if (handle->device->scsi->ioctl != NULL) {
@@ -307,13 +141,12 @@ periph_ioctl(scsi_periph_handle_info *handle, int op, void *buffer, size_t lengt
} }
/** kernel daemon /*! kernel daemon
* once in a minute, it sets a flag so that the next command is executed once in a minute, it sets a flag so that the next command is executed
* ordered; this way, we avoid starvation of SCSI commands inside the ordered; this way, we avoid starvation of SCSI commands inside the
* SCSI queuing system - the ordered command waits for all previous SCSI queuing system - the ordered command waits for all previous
* commands and thus no command can starve longer then a minute commands and thus no command can starve longer then a minute
*/ */
void void
periph_sync_queue_daemon(void *arg, int iteration) periph_sync_queue_daemon(void *arg, int iteration)
{ {
@@ -322,3 +155,151 @@ periph_sync_queue_daemon(void *arg, int iteration)
SHOW_FLOW0(3, "Setting ordered flag for next R/W access"); SHOW_FLOW0(3, "Setting ordered flag for next R/W access");
atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG); atomic_or(&device->next_tag_action, SCSI_ORDERED_QTAG);
} }
/*! Universal read/write function */
status_t
periph_io(scsi_periph_device_info *device, io_operation *operation,
size_t* _bytesTransferred)
{
uint32 blockSize = device->block_size;
size_t numBlocks = operation->Length() / blockSize;
uint32 pos = operation->Offset() / blockSize;
scsi_ccb *request;
err_res res;
int retries = 0;
int err;
// don't test rw10_enabled restrictions - this flag may get changed
request = device->scsi->alloc_ccb(device->scsi_device);
if (request == NULL)
return B_NO_MEMORY;
do {
size_t numBytes;
bool is_rw10;
request->flags = operation->IsWrite() ? SCSI_DIR_OUT : SCSI_DIR_IN;
// make sure we avoid 10 byte commands if they aren't supported
if( !device->rw10_enabled || device->preferred_ccb_size == 6) {
// restricting transfer is OK - the block manager will
// take care of transferring the rest
if (numBlocks > 0x100)
numBlocks = 0x100;
// no way to break the 21 bit address limit
if (pos > 0x200000) {
err = B_BAD_VALUE;
goto abort;
}
// don't allow transfer cross the 24 bit address limit
// (I'm not sure whether this is allowed, but this way we
// are sure to not ask for trouble)
if (pos < 0x100000)
numBlocks = min_c(numBlocks, 0x100000 - pos);
}
numBytes = numBlocks * blockSize;
if (numBytes != operation->Length())
panic("I/O operation would need to be cut.");
request->data = NULL;
request->sg_list = (physical_entry*)operation->Vecs();
request->data_length = numBytes;
request->sg_count = operation->VecCount();
request->io_operation = operation;
request->sort = pos;
request->timeout = device->std_timeout;
// see whether daemon instructed us to post an ordered command;
// reset flag after read
SHOW_FLOW( 3, "flag=%x, next_tag=%x, ordered: %s",
(int)request->flags, (int)device->next_tag_action,
(request->flags & SCSI_ORDERED_QTAG) != 0 ? "yes" : "no" );
// use shortest commands whenever possible
if (pos + numBlocks < 0x200000 && numBlocks <= 0x100) {
scsi_cmd_rw_6 *cmd = (scsi_cmd_rw_6 *)request->cdb;
is_rw10 = false;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = operation->IsWrite()
? SCSI_OP_WRITE_6 : SCSI_OP_READ_6;
cmd->high_lba = (pos >> 16) & 0x1f;
cmd->mid_lba = (pos >> 8) & 0xff;
cmd->low_lba = pos & 0xff;
cmd->length = numBlocks;
request->cdb_length = sizeof(*cmd);
} else {
scsi_cmd_rw_10 *cmd = (scsi_cmd_rw_10 *)request->cdb;
is_rw10 = true;
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = operation->IsWrite()
? SCSI_OP_WRITE_10 : SCSI_OP_READ_10;
cmd->relative_address = 0;
cmd->force_unit_access = 0;
cmd->disable_page_out = 0;
cmd->lba = B_HOST_TO_BENDIAN_INT32(pos);
cmd->length = B_HOST_TO_BENDIAN_INT16(numBlocks);
request->cdb_length = sizeof(*cmd);
}
// last chance to detect errors that occured during concurrent accesses
err = 0; // TODO: handle->pending_error;
if (err)
goto abort;
device->scsi->async_io(request);
acquire_sem(request->completion_sem);
// ask generic peripheral layer what to do now
res = periph_check_error(device, request);
switch (res.action) {
case err_act_ok:
*_bytesTransferred = numBytes - request->data_resid;
break;
case err_act_start:
res = periph_send_start_stop(device, request, 1,
device->removable);
if (res.action == err_act_ok)
res.action = err_act_retry;
break;
case err_act_invalid_req:
// if this was a 10 byte command, the device probably doesn't
// support them, so disable them and retry
if (is_rw10) {
atomic_and(&device->rw10_enabled, 0);
res.action = err_act_retry;
} else
res.action = err_act_fail;
break;
}
} while((res.action == err_act_retry && retries++ < 3)
|| (res.action == err_act_many_retries && retries++ < 30));
device->scsi->free_ccb(request);
// peripheral layer only created "read" error, so we have to
// map them to "write" errors if this was a write request
if (res.error_code == B_DEV_READ_ERROR && write)
return B_DEV_WRITE_ERROR;
return res.error_code;
abort:
device->scsi->free_ccb(request);
return err;
}
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2007, Haiku, Inc. All RightsReserved. * Copyright 2004-2008, Haiku, Inc. All RightsReserved.
* Copyright 2002-2003, Thomas Kurschel. All rights reserved. * Copyright 2002-2003, Thomas Kurschel. All rights reserved.
* *
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
@@ -15,19 +15,16 @@
#include <string.h> #include <string.h>
device_manager_info *pnp; device_manager_info* gDeviceManager;
status_t status_t
periph_simple_exec(scsi_periph_device_info* device, void* cdb, uchar cdbLength, periph_simple_exec(scsi_periph_device_info* device, void* cdb, uchar cdbLength,
void* data, size_t dataLength, int ccb_flags) void* data, size_t dataLength, int ccb_flags)
{ {
scsi_ccb *ccb;
status_t res;
SHOW_FLOW0( 0, "" ); SHOW_FLOW0( 0, "" );
ccb = device->scsi->alloc_ccb(device->scsi_device); scsi_ccb* ccb = device->scsi->alloc_ccb(device->scsi_device);
if (ccb == NULL) if (ccb == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
@@ -39,15 +36,15 @@ periph_simple_exec(scsi_periph_device_info *device, void *cdb, uchar cdbLength,
ccb->sort = -1; ccb->sort = -1;
ccb->timeout = device->std_timeout; ccb->timeout = device->std_timeout;
ccb->data = data; ccb->data = (uint8*)data;
ccb->sg_list = NULL; ccb->sg_list = NULL;
ccb->data_length = dataLength; ccb->data_length = dataLength;
res = periph_safe_exec(device, ccb); status_t status = periph_safe_exec(device, ccb);
device->scsi->free_ccb(ccb); device->scsi->free_ccb(ccb);
return res; return status;
} }
@@ -111,31 +108,17 @@ periph_safe_exec(scsi_periph_device_info *device, scsi_ccb *request)
} }
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
module_dependency module_dependencies[] = { module_dependency module_dependencies[] = {
{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp }, {B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&gDeviceManager},
{} {}
}; };
scsi_periph_interface scsi_periph_module = { static scsi_periph_interface sSCSIPeripheralModule = {
{ {
SCSI_PERIPH_MODULE_NAME, SCSI_PERIPH_MODULE_NAME,
0, 0,
std_ops NULL
}, },
periph_register_device, periph_register_device,
@@ -148,8 +131,7 @@ scsi_periph_interface scsi_periph_module = {
periph_handle_close, periph_handle_close,
periph_handle_free, periph_handle_free,
periph_read, periph_io,
periph_write,
periph_ioctl, periph_ioctl,
periph_check_capacity, periph_check_capacity,
@@ -165,6 +147,6 @@ scsi_periph_interface scsi_periph_module = {
}; };
scsi_periph_interface *modules[] = { scsi_periph_interface *modules[] = {
&scsi_periph_module, &sSCSIPeripheralModule,
NULL NULL
}; };
@@ -1,4 +1,5 @@
/* /*
* Copyright 2004-2008, Haiku, Inc. All Rights Reserved.
* Copyright 2002, Thomas Kurschel. All rights reserved. * Copyright 2002, Thomas Kurschel. All rights reserved.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -6,19 +7,20 @@
#define __SCSI_PERIPH_INT_H__ #define __SCSI_PERIPH_INT_H__
#include <stddef.h>
#include <scsi_periph.h> #include <scsi_periph.h>
#include <block_io.h> #include <block_io.h>
#include <device_manager.h> #include <device_manager.h>
#include <stddef.h> #include "io_requests.h"
#include "wrapper.h" #include "wrapper.h"
typedef struct scsi_periph_device_info { typedef struct scsi_periph_device_info {
struct scsi_periph_handle_info *handles; struct scsi_periph_handle_info *handles;
scsi_device scsi_device; ::scsi_device scsi_device;
scsi_device_interface *scsi; scsi_device_interface *scsi;
periph_device_cookie periph_device; periph_device_cookie periph_device;
device_node *node; device_node *node;
@@ -27,10 +29,12 @@ typedef struct scsi_periph_device_info {
bool removable; // true, if device is removable bool removable; // true, if device is removable
uint32 block_size;
int32 preferred_ccb_size;
int32 rw10_enabled; // 10 byte r/w commands supported; access must be atomic int32 rw10_enabled; // 10 byte r/w commands supported; access must be atomic
int32 next_tag_action; // queuing flag for next r/w command; access must be atomic int32 next_tag_action; // queuing flag for next r/w command; access must be atomic
mutex mutex; struct mutex mutex;
int std_timeout; int std_timeout;
scsi_periph_callbacks *callbacks; scsi_periph_callbacks *callbacks;
@@ -46,7 +50,7 @@ typedef struct scsi_periph_handle_info {
periph_handle_cookie periph_handle; periph_handle_cookie periph_handle;
} scsi_periph_handle_info; } scsi_periph_handle_info;
extern device_manager_info *pnp; extern device_manager_info *gDeviceManager;
// removable.c // removable.c
@@ -79,20 +83,17 @@ status_t periph_check_capacity(scsi_periph_device_info *device, scsi_ccb *ccb);
// device.c // device.c
status_t periph_register_device(periph_device_cookie periph_device, status_t periph_register_device(periph_device_cookie periph_device,
scsi_periph_callbacks *callbacks, scsi_device scsi_device, scsi_device_interface *scsi, scsi_periph_callbacks *callbacks, scsi_device scsi_device,
device_node *node, bool removable, scsi_periph_device *driver); scsi_device_interface *scsi, device_node *node, bool removable,
int preferredCcbSize, scsi_periph_device *driver);
status_t periph_unregister_device(scsi_periph_device_info *driver); status_t periph_unregister_device(scsi_periph_device_info *driver);
char *periph_compose_device_name(device_node *device_node, const char *prefix); char *periph_compose_device_name(device_node *device_node, const char *prefix);
// io.c // io.c
status_t periph_read(scsi_periph_handle_info *handle, const phys_vecs *vecs, status_t periph_io(scsi_periph_device_info* device, io_operation* operation,
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred, size_t *_bytesTransferred);
int preferred_ccb_size);
status_t periph_write(scsi_periph_handle_info *handle, const phys_vecs *vecs,
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
int preferred_ccb_size);
status_t periph_ioctl(scsi_periph_handle_info *handle, int op, status_t periph_ioctl(scsi_periph_handle_info *handle, int op,
void *buf, size_t len); void *buf, size_t len);
void periph_sync_queue_daemon(void *arg, int iteration); void periph_sync_queue_daemon(void *arg, int iteration);