#include "scsi_internal.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
static void
scsi_free_autosense_request(scsi_device_info *device)
{
SHOW_FLOW0( 3, "" );
if (device->auto_sense_request != NULL) {
scsi_free_ccb(device->auto_sense_request);
device->auto_sense_request = NULL;
}
if (device->auto_sense_area > 0) {
delete_area(device->auto_sense_area);
device->auto_sense_area = 0;
}
}
static void
scsi_free_device(scsi_device_info *device)
{
SHOW_FLOW0( 3, "" );
scsi_free_emulation_buffer(device);
scsi_free_autosense_request(device);
unregister_kernel_daemon(scsi_dma_buffer_daemon, device);
scsi_dma_buffer_free(&device->dma_buffer);
mutex_destroy(&device->dma_buffer_lock);
delete_sem(device->dma_buffer_owner);
free(device);
}
static void
beautify_string(char *dst, char *src, int dst_size)
{
int i;
memcpy(dst, src, dst_size - 1);
for (i = dst_size - 2; i >= 0; --i) {
if (dst[i] != ' ')
break;
}
dst[i + 1] = 0;
}
status_t
scsi_register_device(scsi_bus_info *bus, uchar target_id,
uchar target_lun, scsi_res_inquiry *inquiry_data)
{
bool is_atapi, manual_autosense;
uint32 orig_max_blocks, max_blocks;
SHOW_FLOW0( 3, "" );
bus->interface->get_restrictions(bus->sim_cookie,
target_id, &is_atapi, &manual_autosense, &max_blocks);
if (target_lun != 0)
dprintf("WARNING: SCSI target %d lun %d getting restrictions without lun\n",
target_id, target_lun);
orig_max_blocks = ~0;
pnp->get_attr_uint32(bus->node, B_DMA_MAX_TRANSFER_BLOCKS, &orig_max_blocks,
true);
max_blocks = std::min(max_blocks, orig_max_blocks);
{
char vendor_ident[sizeof( inquiry_data->vendor_ident ) + 1];
char product_ident[sizeof( inquiry_data->product_ident ) + 1];
char product_rev[sizeof( inquiry_data->product_rev ) + 1];
device_attr attrs[] = {
{ SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { .ui8 = target_id }},
{ SCSI_DEVICE_TARGET_LUN_ITEM, B_UINT8_TYPE, { .ui8 = target_lun }},
{ SCSI_DEVICE_INQUIRY_ITEM, B_RAW_TYPE,
{ .raw = { inquiry_data, sizeof( *inquiry_data ) }}},
{ SCSI_DEVICE_TYPE_ITEM, B_UINT8_TYPE, { .ui8 = inquiry_data->device_type }},
{ SCSI_DEVICE_VENDOR_ITEM, B_STRING_TYPE, { .string = vendor_ident }},
{ SCSI_DEVICE_PRODUCT_ITEM, B_STRING_TYPE, { .string = product_ident }},
{ SCSI_DEVICE_REVISION_ITEM, B_STRING_TYPE, { .string = product_rev }},
{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "scsi" }},
{ B_DMA_MAX_TRANSFER_BLOCKS, B_UINT32_TYPE, { .ui32 = max_blocks }},
{ SCSI_DEVICE_IS_ATAPI_ITEM, B_UINT8_TYPE, { .ui8 = is_atapi }},
{ SCSI_DEVICE_MANUAL_AUTOSENSE_ITEM, B_UINT8_TYPE, { .ui8 = manual_autosense }},
{ NULL }
};
beautify_string(vendor_ident, inquiry_data->vendor_ident, sizeof(vendor_ident));
beautify_string(product_ident, inquiry_data->product_ident, sizeof(product_ident));
beautify_string(product_rev, inquiry_data->product_rev, sizeof(product_rev));
return pnp->register_node(bus->node, SCSI_DEVICE_MODULE_NAME, attrs,
NULL, NULL);
}
return B_OK;
}
static scsi_device_info *
scsi_create_device(device_node *node, scsi_bus_info *bus,
int target_id, int target_lun)
{
scsi_device_info *device;
SHOW_FLOW0( 3, "" );
device = (scsi_device_info *)malloc(sizeof(*device));
if (device == NULL)
return NULL;
memset(device, 0, sizeof(*device));
device->lock_count = device->blocked[0] = device->blocked[1] = 0;
device->sim_overflow = 0;
device->queued_reqs = NULL;
device->bus = bus;
device->target_id = target_id;
device->target_lun = target_lun;
device->valid = true;
device->node = node;
scsi_dma_buffer_init(&device->dma_buffer);
mutex_init(&device->dma_buffer_lock, "dma_buffer");
device->dma_buffer_owner = create_sem(1, "dma_buffer");
if (device->dma_buffer_owner < 0)
goto err;
register_kernel_daemon(scsi_dma_buffer_daemon, device, 5 * 10);
return device;
err:
mutex_destroy(&device->dma_buffer_lock);
free(device);
return NULL;
}
static status_t
scsi_create_autosense_request(scsi_device_info *device)
{
scsi_ccb *request;
unsigned char *buffer;
scsi_cmd_request_sense *cmd;
size_t total_size;
SHOW_FLOW0( 3, "" );
device->auto_sense_request = request = scsi_alloc_ccb(device);
if (device->auto_sense_request == NULL)
return B_NO_MEMORY;
total_size = SCSI_MAX_SENSE_SIZE + sizeof(physical_entry);
total_size = (total_size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1);
device->auto_sense_area = create_area("auto_sense", (void**)&buffer,
B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE, B_32_BIT_FULL_LOCK,
B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (device->auto_sense_area < 0)
goto err;
request->data = buffer;
request->data_length = SCSI_MAX_SENSE_SIZE;
request->sg_list = (physical_entry *)(buffer + SCSI_MAX_SENSE_SIZE);
request->sg_count = 1;
get_memory_map(buffer, SCSI_MAX_SENSE_SIZE,
(physical_entry *)request->sg_list, 1);
request->flags = SCSI_DIR_IN | SCSI_DIS_AUTOSENSE |
SCSI_ORDERED_QTAG | SCSI_DMA_SAFE;
cmd = (scsi_cmd_request_sense *)request->cdb;
request->cdb_length = sizeof(*cmd);
memset(cmd, 0, sizeof(*cmd));
cmd->opcode = SCSI_OP_REQUEST_SENSE;
cmd->lun = device->target_lun;
cmd->allocation_length = SCSI_MAX_SENSE_SIZE;
return B_OK;
err:
scsi_free_ccb(request);
return B_NO_MEMORY;
}
#define SET_BIT(field, bit) field[(bit) >> 3] |= 1 << ((bit) & 7)
static status_t
scsi_init_device(device_node *node, void **cookie)
{
const scsi_res_inquiry *inquiry_data = NULL;
uint8 target_id, target_lun, path_id;
scsi_bus_info *bus;
scsi_device_info *device;
status_t res;
size_t inquiry_data_len;
uint8 is_atapi, manual_autosense;
SHOW_FLOW0(3, "");
if (pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_ID_ITEM, &target_id, false) != B_OK
|| pnp->get_attr_uint8( node, SCSI_DEVICE_TARGET_LUN_ITEM, &target_lun, false) != B_OK
|| pnp->get_attr_uint8( node, SCSI_DEVICE_IS_ATAPI_ITEM, &is_atapi, false) != B_OK
|| pnp->get_attr_uint8( node, SCSI_DEVICE_MANUAL_AUTOSENSE_ITEM, &manual_autosense, false) != B_OK
|| pnp->get_attr_raw( node, SCSI_DEVICE_INQUIRY_ITEM,
(const void **)&inquiry_data, &inquiry_data_len, false) != B_OK
|| inquiry_data_len != sizeof(*inquiry_data)) {
return B_ERROR;
}
{
device_node *parent = pnp->get_parent_node(node);
pnp->get_driver(parent, NULL, (void **)&bus);
pnp->put_node(parent);
}
device = scsi_create_device(node, bus, target_id, target_lun);
if (device == NULL)
return B_NO_MEMORY;
path_id = (uint8)-1;
pnp->get_attr_uint8(node, SCSI_BUS_PATH_ID_ITEM, &path_id, true);
device->inquiry_data = *inquiry_data;
device->is_atapi = is_atapi;
device->manual_autosense = manual_autosense;
device->total_slots = 4096;
if ((bus->inquiry_data.hba_inquiry & SCSI_PI_TAG_ABLE) == 0)
device->total_slots = 1;
if (device->manual_autosense)
device->total_slots = 1;
device->left_slots = device->total_slots;
if (device->manual_autosense) {
if (scsi_create_autosense_request(device) != B_OK) {
res = B_NO_MEMORY;
goto err;
}
}
if (scsi_init_emulation_buffer(device, SCSI_ATAPI_BUFFER_SIZE) != B_OK) {
res = B_NO_MEMORY;
goto err;
}
memset(device->emulation_map, 0, sizeof(device->emulation_map));
if (device->is_atapi) {
SET_BIT(device->emulation_map, SCSI_OP_READ_6);
SET_BIT(device->emulation_map, SCSI_OP_WRITE_6);
SET_BIT(device->emulation_map, SCSI_OP_MODE_SENSE_6);
SET_BIT(device->emulation_map, SCSI_OP_MODE_SELECT_6);
SET_BIT(device->emulation_map, SCSI_OP_INQUIRY);
}
*cookie = device;
return B_OK;
err:
scsi_free_device(device);
return res;
}
static void
scsi_uninit_device(scsi_device_info *device)
{
SHOW_FLOW0(3, "");
scsi_free_device(device);
}
static void
scsi_device_removed(scsi_device_info *device)
{
SHOW_FLOW0(3, "");
if (device == NULL)
return;
device->valid = false;
}
status_t
scsi_force_get_device(scsi_bus_info *bus, uchar target_id,
uchar target_lun, scsi_device_info **res_device)
{
device_attr attrs[] = {
{ SCSI_DEVICE_TARGET_ID_ITEM, B_UINT8_TYPE, { .ui8 = target_id }},
{ SCSI_DEVICE_TARGET_LUN_ITEM, B_UINT8_TYPE, { .ui8 = target_lun }},
{ NULL }
};
device_node *node;
status_t res;
driver_module_info *driver_interface;
scsi_device device;
SHOW_FLOW0(3, "");
acquire_sem(bus->scan_lun_lock);
node = NULL;
pnp->get_next_child_node(bus->node, attrs, &node);
SHOW_FLOW(3, "%p", node);
if (node != NULL) {
res = pnp->get_driver(node, &driver_interface, (void **)&device);
if (res != B_OK)
pnp->put_node(node);
} else {
device = scsi_create_device(NULL, bus, target_id, target_lun);
if (device == NULL)
res = B_NO_MEMORY;
else
res = B_OK;
}
*res_device = device;
if (res != B_OK)
release_sem(bus->scan_lun_lock);
return res;
}
void
scsi_put_forced_device(scsi_device_info *device)
{
scsi_bus_info *bus = device->bus;
SHOW_FLOW0(3, "");
if (device->node != NULL) {
pnp->put_node(device->node);
} else {
scsi_free_device(device);
}
release_sem(bus->scan_lun_lock);
}
static uchar
scsi_reset_device(scsi_device_info *device)
{
SHOW_FLOW0(3, "");
if (device->node == NULL)
return SCSI_DEV_NOT_THERE;
return device->bus->interface->reset_device(device->bus->sim_cookie,
device->target_id, device->target_lun);
}
static status_t
scsi_ioctl(scsi_device_info *device, uint32 op, void *buffer, size_t length)
{
if (device->bus->interface->ioctl != NULL) {
return device->bus->interface->ioctl(device->bus->sim_cookie,
device->target_id, op, buffer, length);
}
return B_DEV_INVALID_IOCTL;
}
static status_t
std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
{
module_info *dummy;
return get_module(SCSI_BUS_MODULE_NAME, &dummy);
}
case B_MODULE_UNINIT:
return put_module(SCSI_BUS_MODULE_NAME);
default:
return B_ERROR;
}
}
scsi_device_interface scsi_device_module = {
{
{
SCSI_DEVICE_MODULE_NAME,
0,
std_ops
},
NULL,
NULL,
scsi_init_device,
(void (*)(void *)) scsi_uninit_device,
NULL,
NULL,
(void (*)(void *)) scsi_device_removed
},
scsi_alloc_ccb,
scsi_free_ccb,
scsi_async_io,
scsi_sync_io,
scsi_abort,
scsi_reset_device,
scsi_term_io,
scsi_ioctl,
};