236 lines
5.3 KiB
C
236 lines
5.3 KiB
C
/*
|
|
* \brief SCSI support emulation
|
|
* \author Christian Helmuth
|
|
* \author Sebastian Sumpf
|
|
* \date 2009-10-29
|
|
*
|
|
* XXX NOTES XXX
|
|
*
|
|
* struct scsi_host_template
|
|
*
|
|
* struct scsi_host
|
|
*
|
|
* host_lock used by scsi_unlock, scsi_lock
|
|
* max_id used by usb_stor_report_device_reset
|
|
*
|
|
* struct scsi_cmnd
|
|
*
|
|
* functions
|
|
*
|
|
* scsi_add_host
|
|
* scsi_host_alloc
|
|
* scsi_host_get
|
|
* scsi_host_put
|
|
* scsi_remove_host
|
|
* scsi_report_bus_reset
|
|
* scsi_report_device_reset
|
|
* scsi_scan_host
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2009-2013 Genode Labs GmbH
|
|
*
|
|
* This file is part of the Genode OS framework, which is distributed
|
|
* under the terms of the GNU General Public License version 2.
|
|
*/
|
|
|
|
#include <lx_emul.h>
|
|
|
|
#include <storage/scsi.h>
|
|
|
|
#define DEBUG_SCSI 0
|
|
|
|
/***************
|
|
** SCSI host **
|
|
***************/
|
|
|
|
struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *t, int priv_size)
|
|
{
|
|
lx_log(DEBUG_SCSI, "t=%p, priv_size=%d", t, priv_size);
|
|
|
|
static int free = 1;
|
|
|
|
/* XXX we not some extra space for hostdata[] */
|
|
static char buffer[4096] __attribute__((aligned(4096)));
|
|
static struct Scsi_Host *host = (struct Scsi_Host *)buffer;
|
|
|
|
/* FIXME we support only one host for now */
|
|
if (!free) return 0;
|
|
free = 0;
|
|
|
|
host->host_lock = &host->default_lock;
|
|
spin_lock_init(host->host_lock);
|
|
|
|
host->host_no = 13;
|
|
host->max_id = 8;
|
|
host->hostt = t;
|
|
|
|
return host;
|
|
}
|
|
|
|
|
|
static struct page *_page(struct scsi_cmnd *cmnd)
|
|
{
|
|
return (struct page *)cmnd->sdb.table.sgl->page_link;
|
|
}
|
|
|
|
|
|
void scsi_alloc_buffer(size_t size, struct scsi_cmnd *cmnd)
|
|
{
|
|
scsi_setup_buffer(cmnd, size, 0, 0);
|
|
struct scatterlist *sgl = cmnd->sdb.table.sgl;
|
|
struct page *page = _page(cmnd);
|
|
page->virt = kmalloc(size, GFP_NOIO);
|
|
page->phys = dma_map_single_attrs(0, page->virt, 0, 0, 0);
|
|
sgl->dma_address = page->phys;
|
|
}
|
|
|
|
|
|
void scsi_setup_buffer(struct scsi_cmnd *cmnd, size_t size, void *virt, dma_addr_t addr)
|
|
{
|
|
cmnd->sdb.table.nents = 1;
|
|
cmnd->sdb.length = size;
|
|
|
|
struct scatterlist *sgl = cmnd->sdb.table.sgl;
|
|
|
|
struct page *page = _page(cmnd);
|
|
page->virt = virt;
|
|
page->phys = addr;
|
|
|
|
sgl->page_link = (unsigned long)page;
|
|
sgl->offset = 0;
|
|
sgl->length = size;
|
|
sgl->dma_address = addr;
|
|
sgl->last = 1;
|
|
}
|
|
|
|
|
|
void scsi_free_buffer(struct scsi_cmnd *cmnd)
|
|
{
|
|
struct page *page = _page(cmnd);
|
|
if (page)
|
|
kfree(page->virt);
|
|
}
|
|
|
|
|
|
void *scsi_buffer_data(struct scsi_cmnd *cmnd)
|
|
{
|
|
return _page(cmnd)->virt;
|
|
}
|
|
|
|
|
|
struct scsi_cmnd *_scsi_alloc_command()
|
|
{
|
|
struct scsi_cmnd *cmnd = (struct scsi_cmnd *)kmalloc(sizeof(struct scsi_cmnd), GFP_KERNEL);
|
|
cmnd->sdb.table.sgl = (struct scatterlist *)kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
|
|
cmnd->cmnd = kzalloc(MAX_COMMAND_SIZE, 0);
|
|
cmnd->sdb.table.sgl->page_link = (unsigned long) kzalloc(sizeof(struct page), 0);
|
|
cmnd->sense_buffer = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL);
|
|
return cmnd;
|
|
}
|
|
|
|
|
|
void _scsi_free_command(struct scsi_cmnd *cmnd)
|
|
{
|
|
kfree((void *)cmnd->sdb.table.sgl->page_link);
|
|
kfree(cmnd->sdb.table.sgl);
|
|
kfree(cmnd->sense_buffer);
|
|
kfree(cmnd->cmnd);
|
|
kfree(cmnd);
|
|
}
|
|
|
|
|
|
static void inquiry_done(struct scsi_cmnd *cmnd)
|
|
{
|
|
char *data = (char *)scsi_buffer_data(cmnd);
|
|
lx_printf("Vendor id: %c%c%c%c%c%c%c%c Product id: %s\n",
|
|
data[8], data[9], data[10], data[11], data[12],
|
|
data[13], data[14], data[15], &data[16]);
|
|
complete(cmnd->back);
|
|
}
|
|
|
|
|
|
static void scsi_done(struct scsi_cmnd *cmd)
|
|
{
|
|
complete(cmd->back);
|
|
}
|
|
|
|
|
|
void scsi_scan_host(struct Scsi_Host *host)
|
|
{
|
|
struct scsi_cmnd *cmnd;
|
|
struct scsi_device *sdev;
|
|
struct scsi_target *target;
|
|
struct completion compl;
|
|
void *result;
|
|
|
|
init_completion(&compl);
|
|
|
|
sdev = (struct scsi_device *)kmalloc(sizeof(struct scsi_device), GFP_KERNEL);
|
|
target = (struct scsi_target *)kmalloc(sizeof(struct scsi_target), GFP_KERNEL);
|
|
|
|
cmnd = _scsi_alloc_command();
|
|
|
|
/* init device */
|
|
sdev->sdev_target = target;
|
|
sdev->host = host;
|
|
sdev->id = 0;
|
|
sdev->lun = 0;
|
|
host->hostt->slave_alloc(sdev);
|
|
host->hostt->slave_configure(sdev);
|
|
|
|
/* inquiry (36 bytes for usb) */
|
|
scsi_alloc_buffer(sdev->inquiry_len, cmnd);
|
|
cmnd->cmnd[0] = INQUIRY;
|
|
cmnd->cmnd[4] = sdev->inquiry_len;
|
|
cmnd->device = sdev;
|
|
cmnd->cmd_len = 6;
|
|
cmnd->sc_data_direction = DMA_FROM_DEVICE;
|
|
|
|
cmnd->back = &compl;
|
|
cmnd->scsi_done = inquiry_done;
|
|
|
|
host->hostt->queuecommand(host, cmnd);
|
|
wait_for_completion(&compl);
|
|
|
|
/* if PQ and PDT are zero we have a direct access block device conntected */
|
|
result = scsi_buffer_data(cmnd);
|
|
if (!((char*)result)[0])
|
|
scsi_add_device(sdev);
|
|
else {
|
|
kfree(sdev);
|
|
kfree(target);
|
|
}
|
|
|
|
scsi_free_buffer(cmnd);
|
|
_scsi_free_command(cmnd);
|
|
}
|
|
|
|
|
|
/**********************
|
|
** scsi/scsi_cmnd.h **
|
|
**********************/
|
|
|
|
unsigned scsi_bufflen(struct scsi_cmnd *cmnd) { return cmnd->sdb.length; }
|
|
struct scatterlist *scsi_sglist(struct scsi_cmnd *cmnd) { return cmnd->sdb.table.sgl; }
|
|
unsigned scsi_sg_count(struct scsi_cmnd *cmnd) { return cmnd->sdb.table.nents; }
|
|
|
|
|
|
/********************
|
|
** scsi/scsi_eh.h **
|
|
*******************/
|
|
|
|
void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd,
|
|
struct scsi_eh_save *ses, unsigned char *cmnd,
|
|
int cmnd_size, unsigned sense_bytes)
|
|
{
|
|
ses->cmd_len = scmd->cmd_len;
|
|
}
|
|
|
|
|
|
void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd,
|
|
struct scsi_eh_save *ses)
|
|
{
|
|
scmd->cmd_len = ses->cmd_len;
|
|
}
|