271 lines
5.4 KiB
C
271 lines
5.4 KiB
C
/*
|
|
* Creation Date: <2003/12/11 21:23:54 samuel>
|
|
* Time-stamp: <2004/01/07 19:38:45 samuel>
|
|
*
|
|
* <osi-scsi.c>
|
|
*
|
|
* SCSI device node
|
|
*
|
|
* Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "mol/mol.h"
|
|
#include "scsi_sh.h"
|
|
#include "osi_calls.h"
|
|
|
|
#define MAX_TARGETS 32
|
|
|
|
typedef struct {
|
|
int probed;
|
|
int valid; /* a useable device found */
|
|
|
|
int is_cd;
|
|
int blocksize;
|
|
} target_info_t;
|
|
|
|
static target_info_t scsi_devs[ MAX_TARGETS ];
|
|
|
|
typedef struct {
|
|
int target;
|
|
target_info_t *info;
|
|
} instance_data_t;
|
|
|
|
|
|
DECLARE_NODE( scsi, INSTALL_OPEN, sizeof(instance_data_t),
|
|
"/pci/pci-bridge/mol-scsi/sd", "/mol/mol-scsi/sd" );
|
|
|
|
|
|
static int
|
|
scsi_cmd_( instance_data_t *sd, const char *cmd, int cmdlen, char *dest,
|
|
int len, int prelen, int postlen )
|
|
{
|
|
char prebuf[4096], postbuf[4096];
|
|
scsi_req_t r[2]; /* the [2] is a hack to get space for the sg-list */
|
|
char sb[32];
|
|
|
|
/* memset( dest, 0, len ); */
|
|
|
|
if( (unsigned int)prelen > sizeof(prebuf) || (unsigned int)postlen > sizeof(postbuf) ) {
|
|
printk("bad pre/post len %d %d\n", prelen, postlen );
|
|
return 1;
|
|
}
|
|
|
|
memset( r, 0, sizeof(r[0]) );
|
|
r->lun = 0;
|
|
r->target = sd->target;
|
|
r->is_write = 0;
|
|
memcpy( r->cdb, cmd, cmdlen );
|
|
r->client_addr = (int)&r;
|
|
r->cdb_len = cmdlen;
|
|
r->sense[0].base = (int)&sb;
|
|
r->sense[0].size = sizeof(sb);
|
|
r->size = prelen + len + postlen;
|
|
r->n_sg = 3;
|
|
r->sglist.n_el = 3;
|
|
r->sglist.vec[0].base = (int)prebuf;
|
|
r->sglist.vec[0].size = prelen;
|
|
r->sglist.vec[1].base = (int)dest;
|
|
r->sglist.vec[1].size = len;
|
|
r->sglist.vec[2].base = (int)postbuf;
|
|
r->sglist.vec[2].size = postlen;
|
|
|
|
if( OSI_SCSISubmit((int)&r) ) {
|
|
printk("OSI_SCSISubmit: error!\n");
|
|
return 1;
|
|
}
|
|
while( !OSI_SCSIAck() )
|
|
OSI_USleep( 10 );
|
|
|
|
if( r->adapter_status )
|
|
return -1;
|
|
if( r->scsi_status )
|
|
return ((sb[2] & 0xf) << 16) | (sb[12] << 8) | sb[13];
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
scsi_cmd( instance_data_t *sd, const char *cmd, int cmdlen )
|
|
{
|
|
return scsi_cmd_( sd, cmd, cmdlen, NULL, 0, 0, 0 );
|
|
}
|
|
|
|
/* ( buf blk nblks -- actual ) */
|
|
static void
|
|
scsi_read_blocks( instance_data_t *sd )
|
|
{
|
|
int nblks = POP();
|
|
int blk = POP();
|
|
char *dest = (char*)POP();
|
|
unsigned char cmd[10];
|
|
int len = nblks * sd->info->blocksize;
|
|
|
|
memset( dest, 0, len );
|
|
|
|
/* printk("READ: blk: %d length %d\n", blk, len ); */
|
|
memset( cmd, 0, sizeof(cmd) );
|
|
cmd[0] = 0x28; /* READ_10 */
|
|
cmd[2] = blk >> 24;
|
|
cmd[3] = blk >> 16;
|
|
cmd[4] = blk >> 8;
|
|
cmd[5] = blk;
|
|
cmd[7] = nblks >> 8;
|
|
cmd[8] = nblks;
|
|
|
|
if( scsi_cmd_(sd, cmd, 10, dest, len, 0, 0) ) {
|
|
printk("read: scsi_cmd failed\n");
|
|
RET( -1 );
|
|
}
|
|
PUSH( nblks );
|
|
}
|
|
|
|
static int
|
|
inquiry( instance_data_t *sd )
|
|
{
|
|
char inquiry_cmd[6] = { 0x12, 0, 0, 0, 32, 0 };
|
|
char start_stop_unit_cmd[6] = { 0x1b, 0, 0, 0, 1, 0 };
|
|
char test_unit_ready_cmd[6] = { 0x00, 0, 0, 0, 0, 0 };
|
|
char prev_allow_medium_removal[6] = { 0x1e, 0, 0, 0, 1, 0 };
|
|
char set_cd_speed_cmd[12] = { 0xbb, 0, 0xff, 0xff, 0xff, 0xff,
|
|
0, 0, 0, 0, 0, 0 };
|
|
target_info_t *info = &scsi_devs[sd->target];
|
|
char ret[32];
|
|
int i, sense;
|
|
|
|
if( sd->target >= MAX_TARGETS )
|
|
return -1;
|
|
sd->info = info;
|
|
|
|
if( info->probed )
|
|
return info->valid ? 0:-1;
|
|
info->probed = 1;
|
|
|
|
if( (sense=scsi_cmd_(sd, inquiry_cmd, 6, ret, 2, 0, 0)) ) {
|
|
if( sense < 0 )
|
|
return -1;
|
|
printk("INQUIRY failed\n");
|
|
return -1;
|
|
}
|
|
|
|
/* medium present? */
|
|
if( (scsi_cmd(sd, test_unit_ready_cmd, 6) >> 8) == 0x23a ) {
|
|
printk("no media\n");
|
|
return -1;
|
|
}
|
|
|
|
info->is_cd = 0;
|
|
info->blocksize = 512;
|
|
|
|
if( ret[0] == 5 /* CD/DVD */ ) {
|
|
info->blocksize = 2048;
|
|
info->is_cd = 1;
|
|
|
|
scsi_cmd( sd, prev_allow_medium_removal, 6 );
|
|
scsi_cmd( sd, set_cd_speed_cmd, 12 );
|
|
scsi_cmd( sd, start_stop_unit_cmd, 6 );
|
|
|
|
} else if( ret[0] == 0 /* DISK */ ) {
|
|
scsi_cmd( sd, test_unit_ready_cmd, 6 );
|
|
scsi_cmd( sd, start_stop_unit_cmd, 6 );
|
|
} else {
|
|
/* don't boot from this device (could be a scanner :-)) */
|
|
return -1;
|
|
}
|
|
|
|
/* wait for spin-up (or whatever) to complete */
|
|
for( i=0; ; i++ ) {
|
|
if( i > 300 ) {
|
|
printk("SCSI timeout (sense %x)\n", sense );
|
|
return -1;
|
|
}
|
|
sense = scsi_cmd( sd, test_unit_ready_cmd, 6 );
|
|
if( (sense & 0xf0000) == 0x20000 ) {
|
|
OSI_USleep( 10000 );
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
info->valid = 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ( -- success? ) */
|
|
static void
|
|
scsi_open( instance_data_t *sd )
|
|
{
|
|
static int once = 0;
|
|
phandle_t ph;
|
|
|
|
fword("my-unit");
|
|
sd->target = POP();
|
|
|
|
if( !once ) {
|
|
once++;
|
|
OSI_SCSIControl( SCSI_CTRL_INIT, 0 );
|
|
}
|
|
|
|
/* obtiain device information */
|
|
if( inquiry(sd) )
|
|
RET(0);
|
|
|
|
selfword("open-deblocker");
|
|
|
|
/* interpose disk-label */
|
|
ph = find_dev("/packages/disk-label");
|
|
fword("my-args");
|
|
PUSH_ph( ph );
|
|
fword("interpose");
|
|
|
|
PUSH( -1 );
|
|
}
|
|
|
|
/* ( -- ) */
|
|
static void
|
|
scsi_close( instance_data_t *pb )
|
|
{
|
|
selfword("close-deblocker");
|
|
}
|
|
|
|
|
|
/* ( -- bs ) */
|
|
static void
|
|
scsi_block_size( instance_data_t *sd )
|
|
{
|
|
PUSH( sd->info->blocksize );
|
|
}
|
|
|
|
/* ( -- maxbytes ) */
|
|
static void
|
|
scsi_max_transfer( instance_data_t *sd )
|
|
{
|
|
PUSH( 1024*1024 );
|
|
}
|
|
|
|
static void
|
|
scsi_initialize( instance_data_t *sd )
|
|
{
|
|
fword("is-deblocker");
|
|
}
|
|
|
|
|
|
NODE_METHODS( scsi ) = {
|
|
{ NULL, scsi_initialize },
|
|
{ "open", scsi_open },
|
|
{ "close", scsi_close },
|
|
{ "read-blocks", scsi_read_blocks },
|
|
{ "block-size", scsi_block_size },
|
|
{ "max-transfer", scsi_max_transfer },
|
|
};
|
|
|
|
void
|
|
osiscsi_init( void )
|
|
{
|
|
REGISTER_NODE( scsi );
|
|
}
|