diff options
Diffstat (limited to 'qemu/roms/openbios/arch/ppc/mol/osi-scsi.c')
-rw-r--r-- | qemu/roms/openbios/arch/ppc/mol/osi-scsi.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/ppc/mol/osi-scsi.c b/qemu/roms/openbios/arch/ppc/mol/osi-scsi.c new file mode 100644 index 000000000..18f3dc577 --- /dev/null +++ b/qemu/roms/openbios/arch/ppc/mol/osi-scsi.c @@ -0,0 +1,271 @@ +/* + * 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 ); +} |