Added scsi CD and disk drivers, written by Thomas Kurschel.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7771 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers disk ;
|
||||||
|
|
||||||
|
SubInclude OBOS_TOP src add-ons kernel drivers disk scsi ;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers disk scsi ;
|
||||||
|
|
||||||
|
SubInclude OBOS_TOP src add-ons kernel drivers disk scsi scsi_cd ;
|
||||||
|
SubInclude OBOS_TOP src add-ons kernel drivers disk scsi scsi_dsk ;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers disk scsi scsi_cd ;
|
||||||
|
|
||||||
|
UsePrivateHeaders kernel ;
|
||||||
|
|
||||||
|
KernelAddon scsi_cd : kernel drivers bin :
|
||||||
|
device.c
|
||||||
|
handle.c
|
||||||
|
scsi_cd.c
|
||||||
|
;
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
**/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
Device management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_cd_int.h"
|
||||||
|
|
||||||
|
#include <pnp_devfs.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
status_t cd_init_device( pnp_node_handle node, void *user_cookie, void **cookie )
|
||||||
|
{
|
||||||
|
cd_device_info *device;
|
||||||
|
status_t res;
|
||||||
|
bool removable;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
device = (cd_device_info *)malloc( sizeof( *device ));
|
||||||
|
if( device == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset( device, 0, sizeof( *device ));
|
||||||
|
|
||||||
|
device->blkman_device = user_cookie;
|
||||||
|
device->node = node;
|
||||||
|
|
||||||
|
res = pnp->get_attr_uint8( node, "removable",
|
||||||
|
&removable, false );
|
||||||
|
if( res != B_OK )
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
device->removable = removable;
|
||||||
|
|
||||||
|
res = pnp->get_attr_uint8( node, SCSI_DEVICE_TYPE_ITEM,
|
||||||
|
&device->device_type, true );
|
||||||
|
if( res != B_OK )
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
// we don't verify type - in worst case, the device doesn't work
|
||||||
|
|
||||||
|
// register it everywhere
|
||||||
|
res = pnp->load_driver( pnp->get_parent( node ), NULL, (pnp_driver_info **)&device->scsi,
|
||||||
|
(void **)&device->scsi_device );
|
||||||
|
if( res != B_OK )
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
res = scsi_periph->register_device(
|
||||||
|
(periph_device_cookie)device, &callbacks, device->scsi_device,
|
||||||
|
device->scsi, device->node, removable, &device->scsi_periph_device );
|
||||||
|
if( res != B_OK )
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
// set capacity to zero, so it get checked on first opened handle
|
||||||
|
device->capacity = 0;
|
||||||
|
device->block_size = 0;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
*cookie = device;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err3:
|
||||||
|
pnp->unload_driver( pnp->get_parent( node ));
|
||||||
|
err2:
|
||||||
|
err1:
|
||||||
|
free( device );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t cd_uninit_device( cd_device_info *device )
|
||||||
|
{
|
||||||
|
scsi_periph->unregister_device( device->scsi_periph_device );
|
||||||
|
pnp->unload_driver( pnp->get_parent( device->node ));
|
||||||
|
free( device );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// called whenever a new device was added to system;
|
||||||
|
// if we really support it, we create a new node that gets
|
||||||
|
// server by blkdev
|
||||||
|
status_t cd_device_added( pnp_node_handle node )
|
||||||
|
{
|
||||||
|
char *str = NULL;
|
||||||
|
uint8 device_type;
|
||||||
|
scsi_res_inquiry *device_inquiry = NULL;
|
||||||
|
size_t inquiry_len;
|
||||||
|
char *name;
|
||||||
|
uint32 max_blocks;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
// make sure we can handle this parent device
|
||||||
|
if( pnp->get_attr_string( node, PNP_DRIVER_TYPE, &str, false ) != B_OK ||
|
||||||
|
strcmp( str, SCSI_DEVICE_TYPE_NAME ) != 0 )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// check whether it's really a CD-ROM or a WORM
|
||||||
|
if( pnp->get_attr_uint8( node, SCSI_DEVICE_TYPE_ITEM,
|
||||||
|
&device_type, true ) != B_OK ||
|
||||||
|
(device_type != scsi_dev_CDROM && device_type != scsi_dev_WORM) )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// get inquiry data
|
||||||
|
if( pnp->get_attr_raw( node, SCSI_DEVICE_INQUIRY_ITEM,
|
||||||
|
(void **)&device_inquiry, &inquiry_len, true ) != B_OK ||
|
||||||
|
inquiry_len < sizeof( device_inquiry ) )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// get block limit of underlying hardware to lower it (if necessary)
|
||||||
|
if( pnp->get_attr_uint32( node, BLKDEV_MAX_BLOCKS_ITEM,
|
||||||
|
&max_blocks, true ) != B_OK )
|
||||||
|
max_blocks = INT_MAX;
|
||||||
|
|
||||||
|
// using 10 byte commands, at most 0xffff blocks can be transmitted at once
|
||||||
|
// (sadly, we cannot update this value later on if only 6 byte commands
|
||||||
|
// are supported, but the blkdev can live with truncated transfers)
|
||||||
|
max_blocks = min( max_blocks, 0xffff );
|
||||||
|
|
||||||
|
name = scsi_periph->compose_device_name( node, "disk/scsi" );
|
||||||
|
if( name == NULL ) {
|
||||||
|
SHOW_ERROR0( 2, "Cannot compose device name" );
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "name=%s", name );
|
||||||
|
|
||||||
|
// ready to register
|
||||||
|
{
|
||||||
|
pnp_node_attr attrs[] =
|
||||||
|
{
|
||||||
|
{ PNP_DRIVER_DRIVER, B_STRING_TYPE, { string: SCSI_CD_MODULE_NAME }},
|
||||||
|
{ PNP_DRIVER_TYPE, B_STRING_TYPE, { string: BLKDEV_TYPE_NAME }},
|
||||||
|
// we always want blkdev on top of us
|
||||||
|
{ PNP_DRIVER_FIXED_CONSUMER, B_STRING_TYPE, { string: BLKMAN_MODULE_NAME }},
|
||||||
|
// tell blkdev whether the device is removable
|
||||||
|
{ "removable", B_UINT8_TYPE, { ui8: device_inquiry->RMB }},
|
||||||
|
// tell which name we want to have in devfs
|
||||||
|
{ PNP_DEVFS_FILENAME, B_STRING_TYPE, { string: name }},
|
||||||
|
// impose own max block restriction
|
||||||
|
{ BLKDEV_MAX_BLOCKS_ITEM, B_UINT32_TYPE, { ui32: max_blocks }},
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
res = pnp->register_device( node, attrs, NULL, &node );
|
||||||
|
|
||||||
|
free( name );
|
||||||
|
free( str );
|
||||||
|
free( device_inquiry );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
if( str != NULL )
|
||||||
|
free( str );
|
||||||
|
|
||||||
|
if( device_inquiry != NULL )
|
||||||
|
free( device_inquiry );
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
File handle management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_cd_int.h"
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
status_t cd_open( cd_device_info *device, cd_handle_info **handle_out )
|
||||||
|
{
|
||||||
|
cd_handle_info *handle;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
// SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
handle = (cd_handle_info *)malloc( sizeof( *handle ));
|
||||||
|
if( handle == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
handle->device = device;
|
||||||
|
|
||||||
|
res = scsi_periph->handle_open( device->scsi_periph_device,
|
||||||
|
(periph_handle_cookie)handle,
|
||||||
|
&handle->scsi_periph_handle );
|
||||||
|
if( res < 0 ) {
|
||||||
|
free( handle );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( device->block_size == 0 || device->capacity == 0 ) {
|
||||||
|
scsi_ccb *request;
|
||||||
|
|
||||||
|
request = device->scsi->alloc_ccb( device->scsi_device );
|
||||||
|
|
||||||
|
// don't care if no test was possible
|
||||||
|
if( request != NULL ) {
|
||||||
|
scsi_periph->check_capacity( device->scsi_periph_device, request );
|
||||||
|
|
||||||
|
device->scsi->free_ccb( request );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SHOW_FLOW0( 3, "opened" );
|
||||||
|
|
||||||
|
*handle_out = handle;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t cd_close( cd_handle_info *handle )
|
||||||
|
{
|
||||||
|
// SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
scsi_periph->handle_close( handle->scsi_periph_handle );
|
||||||
|
|
||||||
|
// SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t cd_free( cd_handle_info *handle )
|
||||||
|
{
|
||||||
|
// SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
scsi_periph->handle_free( handle->scsi_periph_handle );
|
||||||
|
free( handle );
|
||||||
|
|
||||||
|
// SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,715 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI CD-ROM driver
|
||||||
|
|
||||||
|
Main file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_cd_int.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <scsi.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
extern blkdev_interface cd_interface;
|
||||||
|
|
||||||
|
scsi_periph_interface *scsi_periph;
|
||||||
|
device_manager_info *pnp;
|
||||||
|
blkman_for_driver_interface *blkman;
|
||||||
|
|
||||||
|
|
||||||
|
#define SCSI_CD_STD_TIMEOUT 10
|
||||||
|
|
||||||
|
static status_t cd_read( cd_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred )
|
||||||
|
{
|
||||||
|
return scsi_periph->read( handle->scsi_periph_handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, 10 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t cd_write( cd_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred )
|
||||||
|
{
|
||||||
|
return scsi_periph->write( handle->scsi_periph_handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, 10 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t update_capacity( cd_device_info *device )
|
||||||
|
{
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
ccb = device->scsi->alloc_ccb( device->scsi_device );
|
||||||
|
if( ccb == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
res = scsi_periph->check_capacity( device->scsi_periph_device, ccb );
|
||||||
|
|
||||||
|
device->scsi->free_ccb( ccb );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t get_geometry( cd_handle_info *handle, void *buf, size_t len )
|
||||||
|
{
|
||||||
|
cd_device_info *device = handle->device;
|
||||||
|
device_geometry *geometry = (device_geometry *)buf;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
res = update_capacity( device );
|
||||||
|
|
||||||
|
// it seems that Be expects B_GET_GEOMETRY to always succeed unless
|
||||||
|
// the medium has been changed; e.g. if we report B_DEV_NO_MEDIA, the
|
||||||
|
// device is ignored by the CDPlayer and CDBurner
|
||||||
|
if( res == B_DEV_MEDIA_CHANGED )
|
||||||
|
return res;
|
||||||
|
|
||||||
|
geometry->bytes_per_sector = device->block_size;
|
||||||
|
geometry->sectors_per_track = 1;
|
||||||
|
geometry->cylinder_count = device->capacity;
|
||||||
|
geometry->head_count = 1;
|
||||||
|
geometry->device_type = device->device_type;
|
||||||
|
geometry->removable = device->removable;
|
||||||
|
|
||||||
|
// TBD: for all but CD-ROMs, read mode sense - medium type
|
||||||
|
// (bit 7 of block device specific parameter for Optical Memory Block Device)
|
||||||
|
// (same for Direct-Access Block Devices)
|
||||||
|
// (same for write-once block devices)
|
||||||
|
// (same for optical memory block devices)
|
||||||
|
geometry->read_only = true;
|
||||||
|
geometry->write_once = device->device_type == scsi_dev_WORM;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "%ld, %ld, %ld, %ld, %d, %d, %d, %d",
|
||||||
|
geometry->bytes_per_sector,
|
||||||
|
geometry->sectors_per_track,
|
||||||
|
geometry->cylinder_count,
|
||||||
|
geometry->head_count,
|
||||||
|
geometry->device_type,
|
||||||
|
geometry->removable,
|
||||||
|
geometry->read_only,
|
||||||
|
geometry->write_once );
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t get_toc( cd_device_info *device, scsi_toc *toc )
|
||||||
|
{
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
status_t res;
|
||||||
|
scsi_cmd_read_toc *cmd;
|
||||||
|
size_t data_len;
|
||||||
|
scsi_toc_general *short_response = (scsi_toc_general *)toc->toc_data;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 0, "" );
|
||||||
|
|
||||||
|
ccb = device->scsi->alloc_ccb( device->scsi_device );
|
||||||
|
|
||||||
|
if( ccb == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
// first read number of tracks only
|
||||||
|
ccb->flags = SCSI_DIR_IN;
|
||||||
|
|
||||||
|
cmd = (scsi_cmd_read_toc *)ccb->cdb;
|
||||||
|
|
||||||
|
memset( cmd, 0, sizeof( *cmd ));
|
||||||
|
cmd->opcode = SCSI_OP_READ_TOC;
|
||||||
|
cmd->TIME = 1;
|
||||||
|
cmd->format = SCSI_TOC_FORMAT_TOC;
|
||||||
|
cmd->track = 1;
|
||||||
|
cmd->high_allocation_length = 0;
|
||||||
|
cmd->low_allocation_length = sizeof( scsi_toc_general );
|
||||||
|
|
||||||
|
ccb->cdb_len = sizeof( *cmd );
|
||||||
|
|
||||||
|
ccb->sort = -1;
|
||||||
|
ccb->timeout = SCSI_CD_STD_TIMEOUT;
|
||||||
|
|
||||||
|
ccb->data = toc->toc_data;
|
||||||
|
ccb->sg_list = NULL;
|
||||||
|
ccb->data_len = sizeof( toc->toc_data );
|
||||||
|
|
||||||
|
res = scsi_periph->safe_exec( device->scsi_periph_device, ccb );
|
||||||
|
if( res != B_OK )
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "tracks: %d - %d", short_response->first, short_response->last );
|
||||||
|
|
||||||
|
// then read all track infos
|
||||||
|
// (little hint: number of tracks is last - first + 1;
|
||||||
|
// but scsi_toc_toc has already one track, so we get
|
||||||
|
// last - first extra tracks; finally, we want the lead-out as
|
||||||
|
// well, so we add an extra track)
|
||||||
|
data_len = (short_response->last - short_response->first + 1)
|
||||||
|
* sizeof( scsi_toc_track ) + sizeof( scsi_toc_toc );
|
||||||
|
data_len = min( data_len, sizeof( toc->toc_data ));
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "data_len: %d", (int)data_len );
|
||||||
|
|
||||||
|
cmd->high_allocation_length = data_len >> 8;
|
||||||
|
cmd->low_allocation_length = data_len & 0xff;
|
||||||
|
|
||||||
|
res = scsi_periph->safe_exec( device->scsi_periph_device, ccb );
|
||||||
|
|
||||||
|
err:
|
||||||
|
device->scsi->free_ccb( ccb );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t load_eject( cd_device_info *device, bool load )
|
||||||
|
{
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
err_res res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 0, "" );
|
||||||
|
|
||||||
|
ccb = device->scsi->alloc_ccb( device->scsi_device );
|
||||||
|
|
||||||
|
res = scsi_periph->send_start_stop( device->scsi_periph_device,
|
||||||
|
ccb, load, true );
|
||||||
|
|
||||||
|
device->scsi->free_ccb( ccb );
|
||||||
|
|
||||||
|
return res.error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t get_position( cd_device_info *device, scsi_position *position )
|
||||||
|
{
|
||||||
|
scsi_cmd_read_subchannel cmd;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
cmd.opcode = SCSI_OP_READ_SUB_CHANNEL;
|
||||||
|
cmd.TIME = 1;
|
||||||
|
cmd.SUBQ = 1;
|
||||||
|
cmd.parameter_list = scsi_sub_channel_parameter_list_cd_pos;
|
||||||
|
cmd.track = 0;
|
||||||
|
cmd.high_allocation_length = sizeof( *position ) >> 8;
|
||||||
|
cmd.low_allocation_length = sizeof( *position ) & 0xff;
|
||||||
|
|
||||||
|
return scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ),
|
||||||
|
position, sizeof( *position ), SCSI_DIR_IN );
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t get_set_volume( cd_device_info *device, scsi_volume *volume, bool set )
|
||||||
|
{
|
||||||
|
scsi_cmd_mode_sense_6 cmd;
|
||||||
|
scsi_mode_param_header_6 header;
|
||||||
|
size_t len;
|
||||||
|
void *buffer;
|
||||||
|
scsi_modepage_audio *page;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
// determine size of block descriptor
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
cmd.opcode = SCSI_OP_MODE_SENSE_6;
|
||||||
|
cmd.page_code = SCSI_MODEPAGE_AUDIO;
|
||||||
|
cmd.PC = SCSI_MODE_SENSE_PC_CURRENT;
|
||||||
|
cmd.allocation_length = sizeof( header );
|
||||||
|
|
||||||
|
memset( &header, -2, sizeof( header ));
|
||||||
|
|
||||||
|
res = scsi_periph->simple_exec( device->scsi_periph_device, &cmd, sizeof( cmd ),
|
||||||
|
&header, sizeof( header ), SCSI_DIR_IN );
|
||||||
|
if( res != B_OK )
|
||||||
|
return res;
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "block_desc_len=%d", header.block_desc_len );
|
||||||
|
return B_ERROR;
|
||||||
|
|
||||||
|
// retrieve param header, block descriptor and actual codepage
|
||||||
|
len = sizeof( header ) + header.block_desc_len +
|
||||||
|
sizeof( scsi_modepage_audio );
|
||||||
|
|
||||||
|
buffer = malloc( len );
|
||||||
|
if( buffer == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset( buffer, -1, sizeof( buffer ));
|
||||||
|
|
||||||
|
cmd.allocation_length = len;
|
||||||
|
|
||||||
|
res = scsi_periph->simple_exec( device->scsi_periph_device, &cmd, sizeof( cmd ),
|
||||||
|
buffer, len, SCSI_DIR_IN );
|
||||||
|
if( res != B_OK ) {
|
||||||
|
free( buffer );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "mode_data_len=%d, block_desc_len=%d",
|
||||||
|
((scsi_mode_param_header_6 *)buffer)->mode_data_len,
|
||||||
|
((scsi_mode_param_header_6 *)buffer)->block_desc_len );
|
||||||
|
|
||||||
|
// find control page and retrieve values
|
||||||
|
page = (scsi_modepage_audio *)((char *)buffer + sizeof( header ) + header.block_desc_len);
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "page=%p, codepage=%d", page, page->header.page_code );
|
||||||
|
|
||||||
|
if( !set ) {
|
||||||
|
volume->port0_channel = page->ports[0].channel;
|
||||||
|
volume->port0_volume = page->ports[0].volume;
|
||||||
|
volume->port1_channel = page->ports[1].channel;
|
||||||
|
volume->port1_volume = page->ports[1].volume;
|
||||||
|
volume->port2_channel = page->ports[2].channel;
|
||||||
|
volume->port2_volume = page->ports[2].volume;
|
||||||
|
volume->port3_channel = page->ports[3].channel;
|
||||||
|
volume->port3_volume = page->ports[3].volume;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "1: %d - %d", volume->port0_channel, volume->port0_volume );
|
||||||
|
SHOW_FLOW( 3, "2: %d - %d", volume->port1_channel, volume->port1_volume );
|
||||||
|
SHOW_FLOW( 3, "3: %d - %d", volume->port2_channel, volume->port2_volume );
|
||||||
|
SHOW_FLOW( 3, "4: %d - %d", volume->port3_channel, volume->port3_volume );
|
||||||
|
|
||||||
|
res = B_OK;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
scsi_cmd_mode_select_6 cmd;
|
||||||
|
|
||||||
|
if( volume->flags & 1 )
|
||||||
|
page->ports[0].channel = volume->port0_channel;
|
||||||
|
if( volume->flags & 2 )
|
||||||
|
page->ports[0].volume = volume->port0_volume;
|
||||||
|
if( volume->flags & 4 )
|
||||||
|
page->ports[1].channel = volume->port1_channel;
|
||||||
|
if( volume->flags & 8 )
|
||||||
|
page->ports[1].volume = volume->port1_volume;
|
||||||
|
if( volume->flags & 0x10 )
|
||||||
|
page->ports[2].channel = volume->port2_channel;
|
||||||
|
if( volume->flags & 0x20 )
|
||||||
|
page->ports[2].volume = volume->port2_volume;
|
||||||
|
if( volume->flags & 0x40 )
|
||||||
|
page->ports[3].channel = volume->port3_channel;
|
||||||
|
if( volume->flags & 0x80 )
|
||||||
|
page->ports[3].volume = volume->port3_volume;
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
cmd.opcode = SCSI_OP_MODE_SELECT_6;
|
||||||
|
cmd.PF = 1;
|
||||||
|
cmd.param_list_length = sizeof( header ) + header.block_desc_len
|
||||||
|
+ sizeof( *page );
|
||||||
|
|
||||||
|
res = scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ),
|
||||||
|
buffer, len, SCSI_DIR_OUT );
|
||||||
|
}
|
||||||
|
|
||||||
|
free( buffer );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// play audio cd; time is in MSF
|
||||||
|
static status_t play_msf( cd_device_info *device, scsi_play_position *buf )
|
||||||
|
{
|
||||||
|
scsi_cmd_play_msf cmd;
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "%d:%d:%d-%d:%d:%d",
|
||||||
|
buf->start_m, buf->start_s, buf->start_f,
|
||||||
|
buf->end_m, buf->end_s, buf->end_f );
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
|
||||||
|
cmd.opcode = SCSI_OP_PLAY_MSF;
|
||||||
|
cmd.start_minute = buf->start_m;
|
||||||
|
cmd.start_second = buf->start_s;
|
||||||
|
cmd.start_frame = buf->start_f;
|
||||||
|
cmd.end_minute = buf->end_m;
|
||||||
|
cmd.end_second = buf->end_s;
|
||||||
|
cmd.end_frame = buf->end_f;
|
||||||
|
|
||||||
|
return scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ),
|
||||||
|
NULL, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// play audio cd; time is in track/index
|
||||||
|
static status_t play_track_index( cd_device_info *device, scsi_play_track *buf )
|
||||||
|
{
|
||||||
|
scsi_toc generic_toc;
|
||||||
|
scsi_toc_toc *toc;
|
||||||
|
status_t res;
|
||||||
|
int start_track, end_track;
|
||||||
|
scsi_play_position position;
|
||||||
|
|
||||||
|
SHOW_FLOW( 0, "%d-%d", buf->start_track, buf->end_track );
|
||||||
|
|
||||||
|
// the corresponding command PLAY AUDIO TRACK/INDEX is deprecated,
|
||||||
|
// so we have to simulate it by converting track to time via TOC
|
||||||
|
res = get_toc( device, &generic_toc );
|
||||||
|
if( res != B_OK )
|
||||||
|
return res;
|
||||||
|
|
||||||
|
toc = (scsi_toc_toc *)&generic_toc.toc_data[0];
|
||||||
|
|
||||||
|
start_track = buf->start_track;
|
||||||
|
end_track = buf->end_track;
|
||||||
|
|
||||||
|
if( start_track > toc->last_track )
|
||||||
|
return B_BAD_INDEX;
|
||||||
|
|
||||||
|
if( end_track > toc->last_track )
|
||||||
|
end_track = toc->last_track + 1;
|
||||||
|
|
||||||
|
if( end_track < toc->last_track + 1)
|
||||||
|
++end_track;
|
||||||
|
|
||||||
|
start_track -= toc->first_track;
|
||||||
|
end_track -= toc->first_track;
|
||||||
|
|
||||||
|
if( start_track < 0 || end_track < 0 )
|
||||||
|
return B_BAD_INDEX;
|
||||||
|
|
||||||
|
position.start_m = toc->tracks[start_track].start.time.minute;
|
||||||
|
position.start_s = toc->tracks[start_track].start.time.second;
|
||||||
|
position.start_f = toc->tracks[start_track].start.time.frame;
|
||||||
|
|
||||||
|
position.end_m = toc->tracks[end_track].start.time.minute;
|
||||||
|
position.end_s = toc->tracks[end_track].start.time.second;
|
||||||
|
position.end_f = toc->tracks[end_track].start.time.frame;
|
||||||
|
|
||||||
|
return play_msf( device, &position );
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t stop_audio( cd_device_info *device )
|
||||||
|
{
|
||||||
|
scsi_cmd_stop_play cmd;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
cmd.opcode = SCSI_OP_STOP_PLAY;
|
||||||
|
|
||||||
|
return scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ), NULL, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t pause_resume( cd_device_info *device, bool resume )
|
||||||
|
{
|
||||||
|
scsi_cmd_pause_resume cmd;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
cmd.opcode = SCSI_OP_PAUSE_RESUME;
|
||||||
|
cmd.resume = resume;
|
||||||
|
|
||||||
|
return scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ), NULL, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static status_t scan( cd_device_info *device, scsi_scan *buf )
|
||||||
|
{
|
||||||
|
scsi_cmd_scan cmd;
|
||||||
|
scsi_position cur_pos;
|
||||||
|
scsi_cd_current_position *cd_pos;
|
||||||
|
status_t res;
|
||||||
|
/*uint8 *tmp;*/
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "direction=%d", buf->direction );
|
||||||
|
|
||||||
|
res = get_position( device, &cur_pos );
|
||||||
|
if( res != B_OK )
|
||||||
|
return res;
|
||||||
|
|
||||||
|
cd_pos = (scsi_cd_current_position *)((char *)&cur_pos
|
||||||
|
+ sizeof( scsi_subchannel_data_header ));
|
||||||
|
|
||||||
|
if( buf->direction == 0 ) {
|
||||||
|
scsi_play_position play_pos;
|
||||||
|
|
||||||
|
// to stop scan, we issue play command with "open end"
|
||||||
|
play_pos.start_m = cd_pos->absolute_address.time.minute;
|
||||||
|
play_pos.start_s = cd_pos->absolute_address.time.second;
|
||||||
|
play_pos.start_f = cd_pos->absolute_address.time.frame;
|
||||||
|
play_pos.end_m = 99;
|
||||||
|
play_pos.end_s = 59;
|
||||||
|
play_pos.end_f = 24;
|
||||||
|
|
||||||
|
return play_msf( device, &play_pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( &cmd, 0, sizeof( cmd ));
|
||||||
|
|
||||||
|
cmd.opcode = SCSI_OP_SCAN;
|
||||||
|
cmd.Direct = buf->direction < 0;
|
||||||
|
cmd.start.time = cd_pos->absolute_address.time;
|
||||||
|
cmd.type = scsi_scan_msf;
|
||||||
|
|
||||||
|
/*
|
||||||
|
tmp = (uint8 *)&cmd;
|
||||||
|
dprintf( "%d %d %d %d %d %d %d %d %d %d %d %d\n",
|
||||||
|
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5],
|
||||||
|
tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11] );
|
||||||
|
*/
|
||||||
|
|
||||||
|
return scsi_periph->simple_exec( device->scsi_periph_device,
|
||||||
|
&cmd, sizeof( cmd ), NULL, 0, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t read_cd( cd_device_info *device, scsi_read_cd *read_cd )
|
||||||
|
{
|
||||||
|
scsi_cmd_read_cd *cmd;
|
||||||
|
uint32 lba, length;
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
// we use safe_exec instead of simple_exec as we want to set
|
||||||
|
// the sorting order manually (only makes much sense if you grab
|
||||||
|
// multiple tracks at once, but we are prepared)
|
||||||
|
ccb = device->scsi->alloc_ccb( device->scsi_device );
|
||||||
|
|
||||||
|
if( ccb == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
cmd = (scsi_cmd_read_cd *)ccb->cdb;
|
||||||
|
memset( cmd, 0, sizeof( *cmd ));
|
||||||
|
cmd->opcode = SCSI_OP_READ_CD;
|
||||||
|
cmd->sector_type = 1;
|
||||||
|
|
||||||
|
// skip first two seconds, they are lead-in
|
||||||
|
lba = (read_cd->start_m * 60 + read_cd->start_s) * 75 + read_cd->start_f
|
||||||
|
- 2 * 75;
|
||||||
|
|
||||||
|
cmd->lba.top = (lba >> 24) & 0xff;
|
||||||
|
cmd->lba.high = (lba >> 16) & 0xff;
|
||||||
|
cmd->lba.mid = (lba >> 8) & 0xff;
|
||||||
|
cmd->lba.low = lba & 0xff;
|
||||||
|
|
||||||
|
length = (read_cd->length_m * 60 + read_cd->length_s) * 75 + read_cd->length_f;
|
||||||
|
|
||||||
|
cmd->high_length = (length >> 16) & 0xff;
|
||||||
|
cmd->mid_length = (length >> 8) & 0xff;
|
||||||
|
cmd->low_length = length & 0xff;
|
||||||
|
|
||||||
|
cmd->error_field = scsi_read_cd_error_none;
|
||||||
|
cmd->EDC_ECC = 0;
|
||||||
|
cmd->user_data = 1;
|
||||||
|
cmd->header_code = scsi_read_cd_header_none;
|
||||||
|
cmd->SYNC = 0;
|
||||||
|
cmd->sub_channel_selection = scsi_read_cd_sub_channel_none;
|
||||||
|
|
||||||
|
ccb->cdb_len = sizeof( *cmd );
|
||||||
|
|
||||||
|
ccb->flags = SCSI_DIR_IN | SCSI_DIS_DISCONNECT;
|
||||||
|
ccb->sort = lba;
|
||||||
|
// are 10 seconds enough for timeout?
|
||||||
|
ccb->timeout = 10;
|
||||||
|
|
||||||
|
ccb->data = read_cd->buffer;
|
||||||
|
ccb->sg_list = NULL;
|
||||||
|
ccb->data_len = read_cd->buffer_length;
|
||||||
|
|
||||||
|
res = scsi_periph->safe_exec( device->scsi_periph_device, ccb );
|
||||||
|
|
||||||
|
device->scsi->free_ccb( ccb );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t cd_ioctl( cd_handle_info *handle, int op, void *buf, size_t len )
|
||||||
|
{
|
||||||
|
cd_device_info *device = handle->device;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
switch( op ) {
|
||||||
|
case B_GET_DEVICE_SIZE:
|
||||||
|
res = update_capacity( device );
|
||||||
|
if( res == B_OK )
|
||||||
|
(size_t *)buf = device->capacity * device->block_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_GET_GEOMETRY:
|
||||||
|
res = get_geometry( handle, buf, len );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_GET_ICON:
|
||||||
|
res = scsi_periph->get_icon( icon_type_cd, (device_icon *)buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_GET_TOC:
|
||||||
|
res = get_toc( device, (scsi_toc *)buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_EJECT_DEVICE:
|
||||||
|
case B_SCSI_EJECT:
|
||||||
|
res = load_eject( device, false );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_LOAD_MEDIA:
|
||||||
|
res = load_eject( device, true );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_GET_POSITION:
|
||||||
|
res = get_position( device, buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_GET_VOLUME:
|
||||||
|
res = get_set_volume( device, buf, false );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_SET_VOLUME:
|
||||||
|
res = get_set_volume( device, buf, true );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_PLAY_TRACK:
|
||||||
|
res = play_track_index( device, buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_PLAY_POSITION:
|
||||||
|
res = play_msf( device, buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_STOP_AUDIO:
|
||||||
|
res = stop_audio( device );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_PAUSE_AUDIO:
|
||||||
|
res = pause_resume( device, false );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_RESUME_AUDIO:
|
||||||
|
res = pause_resume( device, true );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_SCAN:
|
||||||
|
res = scan( device, buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_SCSI_READ_CD:
|
||||||
|
res = read_cd( device, buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = scsi_periph->ioctl( handle->scsi_periph_handle, op, buf, len );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "%x: %s", op, strerror( res ));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int log2( uint32 x )
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
|
||||||
|
for( y = 31; y >= 0; --y )
|
||||||
|
if( x == ((uint32)1 << y) )
|
||||||
|
break;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void cd_set_capacity( cd_device_info *device, uint64 capacity,
|
||||||
|
uint32 block_size )
|
||||||
|
{
|
||||||
|
uint32 ld_block_size;
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "device=%p, capacity=%Ld, block_size=%ld",
|
||||||
|
device, capacity, block_size );
|
||||||
|
|
||||||
|
// get log2, if possible
|
||||||
|
ld_block_size = log2( block_size );
|
||||||
|
|
||||||
|
if( (1UL << ld_block_size) != block_size )
|
||||||
|
ld_block_size = 0;
|
||||||
|
|
||||||
|
device->capacity = capacity;
|
||||||
|
device->block_size = block_size;
|
||||||
|
|
||||||
|
blkman->set_media_params( device->blkman_device, block_size,
|
||||||
|
ld_block_size, capacity );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cd_media_changed( cd_device_info *device, scsi_ccb *request )
|
||||||
|
{
|
||||||
|
// do a capacity check
|
||||||
|
// TBD: is this a good idea (e.g. if this is an empty CD)?
|
||||||
|
scsi_periph->check_capacity( device->scsi_periph_device, request );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
scsi_periph_callbacks callbacks = {
|
||||||
|
(void (*)( periph_device_cookie,
|
||||||
|
uint64, uint32 )) cd_set_capacity,
|
||||||
|
(void (*)( periph_device_cookie, scsi_ccb *)) cd_media_changed
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
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[] = {
|
||||||
|
{ SCSI_PERIPH_MODULE_NAME, (module_info **)&scsi_periph },
|
||||||
|
{ BLKMAN_FOR_DRIVER_MODULE_NAME, (module_info **)&blkman },
|
||||||
|
{ DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
blkdev_interface scsi_cd_module = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SCSI_CD_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
std_ops
|
||||||
|
},
|
||||||
|
|
||||||
|
cd_init_device,
|
||||||
|
(status_t (*) (void *))cd_uninit_device,
|
||||||
|
cd_device_added,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_device_cookie, blkdev_handle_cookie * )) &cd_open,
|
||||||
|
(status_t (*)( blkdev_handle_cookie )) &cd_close,
|
||||||
|
(status_t (*)( blkdev_handle_cookie )) &cd_free,
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_handle_cookie, const phys_vecs *,
|
||||||
|
off_t, size_t, uint32, size_t * )) &cd_read,
|
||||||
|
(status_t (*)( blkdev_handle_cookie, const phys_vecs *,
|
||||||
|
off_t, size_t, uint32, size_t * )) &cd_write,
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_handle_cookie, int, void *, size_t )) &cd_ioctl,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !_BUILDING_kernel && !BOOT
|
||||||
|
_EXPORT
|
||||||
|
module_info *modules[] =
|
||||||
|
{
|
||||||
|
(module_info *)&scsi_cd_module,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2003, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI CD Driver
|
||||||
|
|
||||||
|
Peripheral driver to handle CD-ROM drives. To be more
|
||||||
|
precisely, it supports CD-ROM and WORM drives (well -
|
||||||
|
I've never _seen_ a WORM driver).
|
||||||
|
|
||||||
|
Much work is done by scsi_periph and blkdev.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SCSI_CD_H
|
||||||
|
#define _SCSI_CD_H
|
||||||
|
|
||||||
|
#include <device_manager.h>
|
||||||
|
|
||||||
|
#define SCSI_CD_MODULE_NAME "drivers/disk/scsi/scsi_cd/"SCSI_DEVICE_TYPE_NAME
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open IDE CD-ROM driver
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device_manager.h>
|
||||||
|
#include "scsi_cd.h"
|
||||||
|
#include <bus/scsi/scsi_periph.h>
|
||||||
|
#include <blkman.h>
|
||||||
|
|
||||||
|
#define debug_level_flow 0
|
||||||
|
#define debug_level_error 3
|
||||||
|
#define debug_level_info 3
|
||||||
|
|
||||||
|
#define DEBUG_MSG_PREFIX "SCSI_CD -- "
|
||||||
|
|
||||||
|
#include "wrapper.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct cd_device_info {
|
||||||
|
pnp_node_handle node;
|
||||||
|
scsi_periph_device scsi_periph_device;
|
||||||
|
scsi_device scsi_device;
|
||||||
|
scsi_device_interface *scsi;
|
||||||
|
blkman_device blkman_device;
|
||||||
|
|
||||||
|
uint64 capacity;
|
||||||
|
uint32 block_size;
|
||||||
|
|
||||||
|
bool removable;
|
||||||
|
uint8 device_type;
|
||||||
|
} cd_device_info;
|
||||||
|
|
||||||
|
typedef struct cd_handle_info {
|
||||||
|
scsi_periph_handle scsi_periph_handle;
|
||||||
|
cd_device_info *device;
|
||||||
|
} cd_handle_info;
|
||||||
|
|
||||||
|
extern scsi_periph_interface *scsi_periph;
|
||||||
|
extern device_manager_info *pnp;
|
||||||
|
extern scsi_periph_callbacks callbacks;
|
||||||
|
extern blkman_for_driver_interface *blkman;
|
||||||
|
|
||||||
|
|
||||||
|
// device_mgr.c
|
||||||
|
|
||||||
|
status_t cd_init_device( pnp_node_handle node, void *user_cookie, void **cookie );
|
||||||
|
status_t cd_uninit_device( cd_device_info *device );
|
||||||
|
status_t cd_device_added( pnp_node_handle node );
|
||||||
|
|
||||||
|
|
||||||
|
// handle_mgr.c
|
||||||
|
|
||||||
|
status_t cd_open( cd_device_info *device, cd_handle_info **handle_out );
|
||||||
|
status_t cd_close( cd_handle_info *handle );
|
||||||
|
status_t cd_free( cd_handle_info *handle );
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#ifndef _WRAPPER_H
|
||||||
|
#define _WRAPPER_H
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <lock.h>
|
||||||
|
|
||||||
|
|
||||||
|
// benaphores
|
||||||
|
|
||||||
|
#define INIT_BEN(x, prefix) benaphore_init(x, prefix)
|
||||||
|
#define DELETE_BEN(x) benaphore_destroy(x)
|
||||||
|
#define ACQUIRE_BEN(x) benaphore_lock(x)
|
||||||
|
#define RELEASE_BEN(x) benaphore_unlock(x)
|
||||||
|
|
||||||
|
// debug output
|
||||||
|
|
||||||
|
#ifdef DEBUG_WAIT_ON_MSG
|
||||||
|
# define DEBUG_WAIT snooze( DEBUG_WAIT_ON_MSG );
|
||||||
|
#else
|
||||||
|
# define DEBUG_WAIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_WAIT_ON_ERROR
|
||||||
|
# define DEBUG_WAIT_ERROR snooze( DEBUG_WAIT_ON_ERROR );
|
||||||
|
#else
|
||||||
|
# define DEBUG_WAIT_ERROR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_FLOW
|
||||||
|
# define DEBUG_MAX_LEVEL_FLOW 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_INFO
|
||||||
|
# define DEBUG_MAX_LEVEL_INFO 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_ERROR
|
||||||
|
# define DEBUG_MAX_LEVEL_ERROR 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MSG_PREFIX
|
||||||
|
# define DEBUG_MSG_PREFIX ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_flow
|
||||||
|
# define debug_level_flow 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_info
|
||||||
|
# define debug_level_info 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_error
|
||||||
|
# define debug_level_error 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FUNC_NAME DEBUG_MSG_PREFIX __FUNCTION__ ": "
|
||||||
|
|
||||||
|
#define SHOW_FLOW(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_FLOW0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_INFO(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_INFO0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_ERROR(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT_ERROR \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_ERROR0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT_ERROR \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#endif /* _BENAPHORE_H */
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
SubDir OBOS_TOP src add-ons kernel drivers disk scsi scsi_dsk ;
|
||||||
|
|
||||||
|
UsePrivateHeaders kernel ;
|
||||||
|
|
||||||
|
KernelAddon scsi_dsk : kernel drivers bin :
|
||||||
|
device.c
|
||||||
|
handle.c
|
||||||
|
scsi_dsk.c
|
||||||
|
;
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
Device management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_dsk_int.h"
|
||||||
|
|
||||||
|
#include <pnp_devfs.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
das_init_device(pnp_node_handle node, void *user_cookie, void **cookie)
|
||||||
|
{
|
||||||
|
das_device_info *device;
|
||||||
|
status_t res;
|
||||||
|
scsi_ccb *request;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
device = (das_device_info *)malloc(sizeof(*device));
|
||||||
|
if (device == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
memset(device, 0, sizeof(*device));
|
||||||
|
|
||||||
|
device->blkman_device = user_cookie;
|
||||||
|
device->node = node;
|
||||||
|
|
||||||
|
res = pnp->get_attr_uint8(node, "removable",
|
||||||
|
&device->removable, false);
|
||||||
|
if (res != B_OK)
|
||||||
|
goto err1;
|
||||||
|
|
||||||
|
// register it everywhere
|
||||||
|
res = pnp->load_driver(pnp->get_parent(node), NULL, (pnp_driver_info **)&device->scsi,
|
||||||
|
(void **)&device->scsi_device);
|
||||||
|
if (res != B_OK)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
res = scsi_periph->register_device((periph_device_cookie)device, &callbacks,
|
||||||
|
device->scsi_device, device->scsi, device->node, device->removable,
|
||||||
|
&device->scsi_periph_device);
|
||||||
|
if (res != B_OK)
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
// and get (initial) capacity
|
||||||
|
request = device->scsi->alloc_ccb(device->scsi_device);
|
||||||
|
if (request == NULL) {
|
||||||
|
res = B_NO_MEMORY;
|
||||||
|
goto err4;
|
||||||
|
}
|
||||||
|
|
||||||
|
scsi_periph->check_capacity(device->scsi_periph_device, request);
|
||||||
|
|
||||||
|
device->scsi->free_ccb(request);
|
||||||
|
|
||||||
|
SHOW_FLOW0(3, "done");
|
||||||
|
|
||||||
|
*cookie = device;
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
err4:
|
||||||
|
scsi_periph->unregister_device(device->scsi_periph_device);
|
||||||
|
err3:
|
||||||
|
pnp->unload_driver(pnp->get_parent(node));
|
||||||
|
err2:
|
||||||
|
err1:
|
||||||
|
free(device);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
das_uninit_device(das_device_info *device)
|
||||||
|
{
|
||||||
|
scsi_periph->unregister_device(device->scsi_periph_device);
|
||||||
|
pnp->unload_driver(pnp->get_parent(device->node));
|
||||||
|
free(device);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** called whenever a new device was added to system;
|
||||||
|
* if we really support it, we create a new node that gets
|
||||||
|
* server by blkdev
|
||||||
|
*/
|
||||||
|
|
||||||
|
status_t
|
||||||
|
das_device_added(pnp_node_handle node)
|
||||||
|
{
|
||||||
|
char *str = NULL;
|
||||||
|
scsi_res_inquiry *device_inquiry = NULL;
|
||||||
|
uint8 device_type;
|
||||||
|
size_t inquiry_len;
|
||||||
|
char *name;
|
||||||
|
uint32 max_blocks;
|
||||||
|
|
||||||
|
// make sure we can handle this parent device
|
||||||
|
if (pnp->get_attr_string(node, PNP_DRIVER_TYPE, &str, false) != B_OK
|
||||||
|
|| strcmp(str, SCSI_DEVICE_TYPE_NAME) != 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// check whether it's really a Direct Access Device
|
||||||
|
if (pnp->get_attr_uint8(node, SCSI_DEVICE_TYPE_ITEM, &device_type, true) != B_OK
|
||||||
|
|| device_type != scsi_dev_direct_access)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// get inquiry data
|
||||||
|
if (pnp->get_attr_raw(node, SCSI_DEVICE_INQUIRY_ITEM,
|
||||||
|
(void **)&device_inquiry, &inquiry_len, true) != B_OK
|
||||||
|
|| inquiry_len < sizeof(device_inquiry))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
// get block limit of underlying hardware to lower it (if necessary)
|
||||||
|
if (pnp->get_attr_uint32(node, BLKDEV_MAX_BLOCKS_ITEM, &max_blocks, true) != B_OK)
|
||||||
|
max_blocks = INT_MAX;
|
||||||
|
|
||||||
|
// using 10 byte commands, at most 0xffff blocks can be transmitted at once
|
||||||
|
// (sadly, we cannot update this value later on if only 6 byte commands
|
||||||
|
// are supported, but the blkdev can live with that)
|
||||||
|
max_blocks = min(max_blocks, 0xffff);
|
||||||
|
|
||||||
|
name = scsi_periph->compose_device_name(node, "disk/scsi");
|
||||||
|
if (name == NULL) {
|
||||||
|
SHOW_ERROR0(2, "Cannot compose device name");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW(3, "name=%s", name);
|
||||||
|
|
||||||
|
// ready to register
|
||||||
|
{
|
||||||
|
pnp_node_attr attrs[] =
|
||||||
|
{
|
||||||
|
{ PNP_DRIVER_DRIVER, B_STRING_TYPE, { string: SCSI_DSK_MODULE_NAME }},
|
||||||
|
{ PNP_DRIVER_TYPE, B_STRING_TYPE, { string: BLKDEV_TYPE_NAME }},
|
||||||
|
// we always want blkdev on top of us
|
||||||
|
{ PNP_DRIVER_FIXED_CONSUMER, B_STRING_TYPE, { string: BLKMAN_MODULE_NAME }},
|
||||||
|
// tell blkdev whether the device is removable
|
||||||
|
{ "removable", B_UINT8_TYPE, { ui8: device_inquiry->RMB }},
|
||||||
|
// tell which name we want to have in devfs
|
||||||
|
{ PNP_DEVFS_FILENAME, B_STRING_TYPE, { string: name }},
|
||||||
|
// impose own max block restriction
|
||||||
|
{ BLKDEV_MAX_BLOCKS_ITEM, B_UINT32_TYPE, { ui32: max_blocks }},
|
||||||
|
// in general, any disk can be a BIOS drive (even ZIP-disks)
|
||||||
|
{ BLKDEV_IS_BIOS_DRIVE, B_UINT8_TYPE, { ui8: 1 }},
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
status_t res = pnp->register_device(node, attrs, NULL, &node);
|
||||||
|
|
||||||
|
free(name);
|
||||||
|
free(str);
|
||||||
|
free(device_inquiry);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
free(str);
|
||||||
|
free(device_inquiry);
|
||||||
|
|
||||||
|
return B_ERROR;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
File handle management.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_dsk_int.h"
|
||||||
|
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
status_t das_open( das_device_info *device, das_handle_info **handle_out )
|
||||||
|
{
|
||||||
|
das_handle_info *handle;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
handle = (das_handle_info *)malloc( sizeof( *handle ));
|
||||||
|
if( handle == NULL )
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
handle->device = device;
|
||||||
|
|
||||||
|
res = scsi_periph->handle_open( device->scsi_periph_device,
|
||||||
|
(periph_handle_cookie)handle,
|
||||||
|
&handle->scsi_periph_handle );
|
||||||
|
if( res < 0 ) {
|
||||||
|
free( handle );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "opened" );
|
||||||
|
|
||||||
|
*handle_out = handle;
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t das_close( das_handle_info *handle )
|
||||||
|
{
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
scsi_periph->handle_close( handle->scsi_periph_handle );
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_t das_free( das_handle_info *handle )
|
||||||
|
{
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
scsi_periph->handle_free( handle->scsi_periph_handle );
|
||||||
|
free( handle );
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "done" );
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
Everything doing the real input/output stuff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_dsk_int.h"
|
||||||
|
|
||||||
|
#define DAS_STD_TIMEOUT 10
|
||||||
|
|
||||||
|
|
||||||
|
// we don't want to inline this function - it's just not worth it
|
||||||
|
static int das_read_write( das_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
|
||||||
|
bool write );
|
||||||
|
|
||||||
|
|
||||||
|
status_t das_read( das_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred )
|
||||||
|
{
|
||||||
|
return das_read_write( handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t das_write( das_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos, size_t num_blocks, uint32 block_size, size_t *bytes_transferred )
|
||||||
|
{
|
||||||
|
return das_read_write( handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// universal read/write function
|
||||||
|
static int das_read_write( das_handle_info *handle, const phys_vecs *vecs,
|
||||||
|
off_t pos64, size_t num_blocks, uint32 block_size, size_t *bytes_transferred,
|
||||||
|
bool write )
|
||||||
|
{
|
||||||
|
das_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 ) {
|
||||||
|
// 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)
|
||||||
|
num_blocks = min( num_blocks, 0x100000 - pos );
|
||||||
|
}
|
||||||
|
|
||||||
|
num_bytes = num_blocks * block_size;
|
||||||
|
|
||||||
|
request->data = NULL;
|
||||||
|
request->sg_list = vecs->vec;
|
||||||
|
request->data_len = num_bytes;
|
||||||
|
request->sglist_cnt = vecs->num;
|
||||||
|
request->sort = pos;
|
||||||
|
request->timeout = DAS_STD_TIMEOUT;
|
||||||
|
// see whether daemon instructed us to post an ordered command;
|
||||||
|
// reset flag after read
|
||||||
|
request->flags = atomic_and( &device->next_tag_action, 0 );
|
||||||
|
|
||||||
|
SHOW_FLOW( 3, "ordered: %s",
|
||||||
|
(request->flags & SCSI_ORDERED_QTAG) == 0 ? "yes" : "no" );
|
||||||
|
|
||||||
|
// use 6 byte 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_len = 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->RelAdr = 0;
|
||||||
|
cmd->FUA = 0;
|
||||||
|
cmd->DPO = 0;
|
||||||
|
|
||||||
|
cmd->top_LBA = (pos >> 24) & 0xff;
|
||||||
|
cmd->high_LBA = (pos >> 16) & 0xff;
|
||||||
|
cmd->mid_LBA = (pos >> 8) & 0xff;
|
||||||
|
cmd->low_LBA = pos & 0xff;
|
||||||
|
|
||||||
|
cmd->high_length = (num_blocks >> 8) & 0xff;
|
||||||
|
cmd->low_length = num_blocks & 0xff;
|
||||||
|
|
||||||
|
request->cdb_len = sizeof( *cmd );
|
||||||
|
}
|
||||||
|
|
||||||
|
// last chance to detect errors that occured during concurrent accesses
|
||||||
|
err = handle->pending_error;
|
||||||
|
|
||||||
|
if( err )
|
||||||
|
goto abort;
|
||||||
|
|
||||||
|
device->scsi->scsi_io( request );
|
||||||
|
|
||||||
|
acquire_sem( request->completion_sem );
|
||||||
|
|
||||||
|
// ask generic peripheral layer what to do now
|
||||||
|
res = scsi_periph->check_error( device->scsi_periph_device, request );
|
||||||
|
|
||||||
|
switch( res.action ) {
|
||||||
|
case err_act_ok:
|
||||||
|
*bytes_transferred = num_bytes - request->data_resid;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case err_act_start:
|
||||||
|
res = scsi_periph->send_start_stop(
|
||||||
|
device->scsi_periph_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;
|
||||||
|
else
|
||||||
|
return res.error_code;
|
||||||
|
|
||||||
|
abort:
|
||||||
|
device->scsi->free_ccb( request );
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// kernel daemon
|
||||||
|
// 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
|
||||||
|
// SCSI queuing system - the ordered command waits for all previous
|
||||||
|
// commands and thus no command can starve longer then a minute
|
||||||
|
void das_sync_queue_daemon( void *arg, int iteration )
|
||||||
|
{
|
||||||
|
das_device_info *device = (das_device_info *)arg;
|
||||||
|
|
||||||
|
atomic_or( &device->next_tag_action, SCSI_ORDERED_QTAG );
|
||||||
|
}
|
||||||
|
|
||||||
|
void das_handle_set_error( das_handle_info *handle, status_t error_code )
|
||||||
|
{
|
||||||
|
handle->pending_error = error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t das_handle_get_error( das_handle_info *handle )
|
||||||
|
{
|
||||||
|
return handle->pending_error;
|
||||||
|
}
|
||||||
@@ -0,0 +1,278 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
Main file.
|
||||||
|
|
||||||
|
You'll find das_... all over the place. This stands for
|
||||||
|
"Direct Access Storage" which is the official SCSI name for
|
||||||
|
normal (floppy/hard/ZIP)-disk drives.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scsi_dsk_int.h"
|
||||||
|
#include <scsi.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
extern blkdev_interface das_interface;
|
||||||
|
|
||||||
|
scsi_periph_interface *scsi_periph;
|
||||||
|
device_manager_info *pnp;
|
||||||
|
blkman_for_driver_interface *blkman;
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
das_read(das_handle_info *handle, const phys_vecs *vecs, off_t pos,
|
||||||
|
size_t num_blocks, uint32 block_size, size_t *bytes_transferred)
|
||||||
|
{
|
||||||
|
return scsi_periph->read(handle->scsi_periph_handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
das_write(das_handle_info *handle, const phys_vecs *vecs, off_t pos,
|
||||||
|
size_t num_blocks, uint32 block_size, size_t *bytes_transferred)
|
||||||
|
{
|
||||||
|
return scsi_periph->write(handle->scsi_periph_handle, vecs, pos,
|
||||||
|
num_blocks, block_size, bytes_transferred, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
update_capacity(das_device_info *device)
|
||||||
|
{
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
ccb = device->scsi->alloc_ccb(device->scsi_device);
|
||||||
|
if (ccb == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
res = scsi_periph->check_capacity(device->scsi_periph_device, ccb);
|
||||||
|
|
||||||
|
device->scsi->free_ccb(ccb);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
get_geometry(das_handle_info *handle, void *buf, size_t len)
|
||||||
|
{
|
||||||
|
das_device_info *device = handle->device;
|
||||||
|
device_geometry *geometry = (device_geometry *)buf;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 3, "" );
|
||||||
|
|
||||||
|
res = update_capacity(device);
|
||||||
|
|
||||||
|
if (res < B_OK)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
geometry->bytes_per_sector = device->block_size;
|
||||||
|
geometry->sectors_per_track = 1;
|
||||||
|
geometry->cylinder_count = device->capacity;
|
||||||
|
geometry->head_count = 1;
|
||||||
|
geometry->device_type = B_DISK;
|
||||||
|
geometry->removable = device->removable;
|
||||||
|
|
||||||
|
// TBD: for all but CD-ROMs, read mode sense - medium type
|
||||||
|
// (bit 7 of block device specific parameter for Optical Memory Block Device)
|
||||||
|
// (same for Direct-Access Block Devices)
|
||||||
|
// (same for write-once block devices)
|
||||||
|
// (same for optical memory block devices)
|
||||||
|
geometry->read_only = false;
|
||||||
|
geometry->write_once = false;
|
||||||
|
|
||||||
|
SHOW_FLOW(3, "%ld, %ld, %ld, %ld, %d, %d, %d, %d",
|
||||||
|
geometry->bytes_per_sector,
|
||||||
|
geometry->sectors_per_track,
|
||||||
|
geometry->cylinder_count,
|
||||||
|
geometry->head_count,
|
||||||
|
geometry->device_type,
|
||||||
|
geometry->removable,
|
||||||
|
geometry->read_only,
|
||||||
|
geometry->write_once);
|
||||||
|
|
||||||
|
SHOW_FLOW0(3, "done");
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
load_eject(das_device_info *device, bool load)
|
||||||
|
{
|
||||||
|
scsi_ccb *ccb;
|
||||||
|
err_res res;
|
||||||
|
|
||||||
|
SHOW_FLOW0( 0, "" );
|
||||||
|
|
||||||
|
ccb = device->scsi->alloc_ccb(device->scsi_device);
|
||||||
|
|
||||||
|
res = scsi_periph->send_start_stop(device->scsi_periph_device,
|
||||||
|
ccb, load, true);
|
||||||
|
|
||||||
|
device->scsi->free_ccb(ccb);
|
||||||
|
|
||||||
|
return res.error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
log2(uint32 x)
|
||||||
|
{
|
||||||
|
int y;
|
||||||
|
|
||||||
|
for (y = 31; y >= 0; --y)
|
||||||
|
if (x == ((uint32)1 << y))
|
||||||
|
break;
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
das_set_capacity(das_device_info *device, uint64 capacity,
|
||||||
|
uint32 block_size)
|
||||||
|
{
|
||||||
|
uint32 ld_block_size;
|
||||||
|
|
||||||
|
SHOW_FLOW(3, "device=%p, capacity=%Ld, block_size=%ld",
|
||||||
|
device, capacity, block_size);
|
||||||
|
|
||||||
|
// get log2, if possible
|
||||||
|
ld_block_size = log2(block_size);
|
||||||
|
|
||||||
|
if ((1UL << ld_block_size) != block_size)
|
||||||
|
ld_block_size = 0;
|
||||||
|
|
||||||
|
device->capacity = capacity;
|
||||||
|
device->block_size = block_size;
|
||||||
|
|
||||||
|
blkman->set_media_params(device->blkman_device, block_size,
|
||||||
|
ld_block_size, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
das_media_changed(das_device_info *device, scsi_ccb *request)
|
||||||
|
{
|
||||||
|
// do a capacity check
|
||||||
|
// TBD: is this a good idea (e.g. if this is an empty CD)?
|
||||||
|
scsi_periph->check_capacity(device->scsi_periph_device, request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
scsi_periph_callbacks callbacks = {
|
||||||
|
(void (*)( periph_device_cookie,
|
||||||
|
uint64, uint32 )) das_set_capacity,
|
||||||
|
(void (*)( periph_device_cookie, scsi_ccb *)) das_media_changed
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
das_ioctl(das_handle_info *handle, int op, void *buf, size_t len)
|
||||||
|
{
|
||||||
|
das_device_info *device = handle->device;
|
||||||
|
status_t res;
|
||||||
|
|
||||||
|
SHOW_FLOW( 4, "%d", op );
|
||||||
|
|
||||||
|
switch( op ) {
|
||||||
|
case B_GET_DEVICE_SIZE:
|
||||||
|
res = update_capacity( device );
|
||||||
|
if( res == B_OK )
|
||||||
|
(size_t *)buf = device->capacity * device->block_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_GET_GEOMETRY:
|
||||||
|
res = get_geometry( handle, buf, len );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_GET_ICON:
|
||||||
|
res = scsi_periph->get_icon( device->removable ? icon_type_floppy : icon_type_disk,
|
||||||
|
(device_icon *)buf );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_EJECT_DEVICE:
|
||||||
|
case B_SCSI_EJECT:
|
||||||
|
res = load_eject( device, false );
|
||||||
|
break;
|
||||||
|
|
||||||
|
case B_LOAD_MEDIA:
|
||||||
|
res = load_eject( device, true );
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
res = scsi_periph->ioctl( handle->scsi_periph_handle, op, buf, len );
|
||||||
|
}
|
||||||
|
|
||||||
|
SHOW_FLOW( 4, "%s", strerror( res ));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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[] = {
|
||||||
|
{ SCSI_PERIPH_MODULE_NAME, (module_info **)&scsi_periph },
|
||||||
|
{ BLKMAN_FOR_DRIVER_MODULE_NAME, (module_info **)&blkman },
|
||||||
|
{ DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
blkdev_interface scsi_dsk_module = {
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SCSI_DSK_MODULE_NAME,
|
||||||
|
0,
|
||||||
|
std_ops
|
||||||
|
},
|
||||||
|
|
||||||
|
das_init_device,
|
||||||
|
(status_t (*) (void *))das_uninit_device,
|
||||||
|
das_device_added,
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_device_cookie, blkdev_handle_cookie * )) &das_open,
|
||||||
|
(status_t (*)( blkdev_handle_cookie )) &das_close,
|
||||||
|
(status_t (*)( blkdev_handle_cookie )) &das_free,
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_handle_cookie, const phys_vecs *,
|
||||||
|
off_t, size_t, uint32, size_t * )) &das_read,
|
||||||
|
(status_t (*)( blkdev_handle_cookie, const phys_vecs *,
|
||||||
|
off_t, size_t, uint32, size_t * )) &das_write,
|
||||||
|
|
||||||
|
(status_t (*)( blkdev_handle_cookie, int, void *, size_t )) &das_ioctl,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !_BUILDING_kernel && !BOOT
|
||||||
|
_EXPORT
|
||||||
|
module_info *modules[] =
|
||||||
|
{
|
||||||
|
(module_info *)&scsi_dsk_module,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
Peripheral driver to handle any kind of SCSI disks,
|
||||||
|
i.e. hard disk and floopy disks (ZIP etc.)
|
||||||
|
|
||||||
|
Much work is done by scsi_periph and blkdev.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SCSI_DSK_H
|
||||||
|
#define _SCSI_DSK_H
|
||||||
|
|
||||||
|
#include <device_manager.h>
|
||||||
|
|
||||||
|
#define SCSI_DSK_MODULE_NAME "drivers/disk/scsi/scsi_dsk/"SCSI_DEVICE_TYPE_NAME
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
|
||||||
|
** Distributed under the terms of the OpenBeOS License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Part of Open SCSI Disk Driver
|
||||||
|
|
||||||
|
SCSI Direct Access Storage Device Driver (aka SCSI Disk Driver)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device_manager.h>
|
||||||
|
#include "scsi_dsk.h"
|
||||||
|
#include <bus/scsi/scsi_periph.h>
|
||||||
|
#include <blkman.h>
|
||||||
|
|
||||||
|
#define debug_level_flow 4
|
||||||
|
#define debug_level_error 4
|
||||||
|
#define debug_level_info 4
|
||||||
|
|
||||||
|
#define DEBUG_MSG_PREFIX "SCSI_DSK -- "
|
||||||
|
|
||||||
|
#include "wrapper.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct das_device_info {
|
||||||
|
pnp_node_handle node;
|
||||||
|
scsi_periph_device scsi_periph_device;
|
||||||
|
scsi_device scsi_device;
|
||||||
|
scsi_device_interface *scsi;
|
||||||
|
blkman_device blkman_device;
|
||||||
|
|
||||||
|
uint64 capacity;
|
||||||
|
uint32 block_size;
|
||||||
|
|
||||||
|
bool removable; // true, if device is removable
|
||||||
|
} das_device_info;
|
||||||
|
|
||||||
|
typedef struct das_handle_info {
|
||||||
|
scsi_periph_handle scsi_periph_handle;
|
||||||
|
das_device_info *device;
|
||||||
|
} das_handle_info;
|
||||||
|
|
||||||
|
extern scsi_periph_interface *scsi_periph;
|
||||||
|
extern device_manager_info *pnp;
|
||||||
|
extern scsi_periph_callbacks callbacks;
|
||||||
|
extern blkman_for_driver_interface *blkman;
|
||||||
|
|
||||||
|
|
||||||
|
// device_mgr.c
|
||||||
|
|
||||||
|
status_t das_init_device(pnp_node_handle node, void *user_cookie, void **cookie);
|
||||||
|
status_t das_uninit_device(das_device_info *device);
|
||||||
|
status_t das_device_added(pnp_node_handle node);
|
||||||
|
|
||||||
|
|
||||||
|
// handle_mgr.c
|
||||||
|
|
||||||
|
status_t das_open(das_device_info *device, das_handle_info **handle_out);
|
||||||
|
status_t das_close(das_handle_info *handle);
|
||||||
|
status_t das_free(das_handle_info *handle);
|
||||||
|
|
||||||
|
|
||||||
|
// scsi_dsk.c
|
||||||
|
|
||||||
|
void das_set_capacity(das_device_info *device, uint64 capacity,
|
||||||
|
uint32 block_size);
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
#ifndef _WRAPPER_H
|
||||||
|
#define _WRAPPER_H
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
#include <lock.h>
|
||||||
|
|
||||||
|
|
||||||
|
// benaphores
|
||||||
|
|
||||||
|
#define INIT_BEN(x, prefix) benaphore_init(x, prefix)
|
||||||
|
#define DELETE_BEN(x) benaphore_destroy(x)
|
||||||
|
#define ACQUIRE_BEN(x) benaphore_lock(x)
|
||||||
|
#define RELEASE_BEN(x) benaphore_unlock(x)
|
||||||
|
|
||||||
|
// debug output
|
||||||
|
|
||||||
|
#ifdef DEBUG_WAIT_ON_MSG
|
||||||
|
# define DEBUG_WAIT snooze( DEBUG_WAIT_ON_MSG );
|
||||||
|
#else
|
||||||
|
# define DEBUG_WAIT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG_WAIT_ON_ERROR
|
||||||
|
# define DEBUG_WAIT_ERROR snooze( DEBUG_WAIT_ON_ERROR );
|
||||||
|
#else
|
||||||
|
# define DEBUG_WAIT_ERROR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_FLOW
|
||||||
|
# define DEBUG_MAX_LEVEL_FLOW 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_INFO
|
||||||
|
# define DEBUG_MAX_LEVEL_INFO 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MAX_LEVEL_ERROR
|
||||||
|
# define DEBUG_MAX_LEVEL_ERROR 4
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEBUG_MSG_PREFIX
|
||||||
|
# define DEBUG_MSG_PREFIX ""
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_flow
|
||||||
|
# define debug_level_flow 3
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_info
|
||||||
|
# define debug_level_info 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef debug_level_error
|
||||||
|
# define debug_level_error 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FUNC_NAME DEBUG_MSG_PREFIX __FUNCTION__ ": "
|
||||||
|
|
||||||
|
#define SHOW_FLOW(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_FLOW0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_flow && seriousness <= DEBUG_MAX_LEVEL_FLOW ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_INFO(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_INFO0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_info && seriousness <= DEBUG_MAX_LEVEL_INFO ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_ERROR(seriousness, format, param...) \
|
||||||
|
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME, param ); DEBUG_WAIT_ERROR \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#define SHOW_ERROR0(seriousness, format) \
|
||||||
|
do { if( seriousness <= debug_level_error && seriousness <= DEBUG_MAX_LEVEL_ERROR ) { \
|
||||||
|
dprintf( "%s"##format"\n", FUNC_NAME); DEBUG_WAIT_ERROR \
|
||||||
|
}} while( 0 )
|
||||||
|
|
||||||
|
#endif /* _BENAPHORE_H */
|
||||||
Reference in New Issue
Block a user