diff options
Diffstat (limited to 'scsi/rz_cpu.c')
-rw-r--r-- | scsi/rz_cpu.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/scsi/rz_cpu.c b/scsi/rz_cpu.c new file mode 100644 index 0000000..77c0683 --- /dev/null +++ b/scsi/rz_cpu.c @@ -0,0 +1,450 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * File: rz_cpu.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 7/91 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains operations specific to CPU-like devices. + * + * We handle here the case of simple devices which do not use any + * sophisticated host-to-host communication protocol, they look + * very much like degenerative cases of TAPE devices. + * + * For documentation and debugging, we also provide code to act like one. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + +#if (NSCSI > 0) + +void sccpu_act_as_target(); /* forwards */ +void sccpu_start(); + +/* + * This function decides which 'protocol' we well speak + * to a cpu target. For now the decision is left to a + * global var. XXXXXXX + */ +extern scsi_devsw_t scsi_host; +scsi_devsw_t *scsi_cpu_protocol = /* later &scsi_host*/ + &scsi_devsw[SCSI_CPU]; + +void sccpu_new_initiator(self, initiator) + target_info_t *self, *initiator; +{ + initiator->dev_ops = scsi_cpu_protocol; + if (initiator == self) { + self->flags = TGT_DID_SYNCH|TGT_FULLY_PROBED|TGT_ONLINE| + TGT_ALIVE|TGT_US; + self->dev_info.cpu.req_pending = FALSE; + } else { + initiator->flags = TGT_ONLINE|TGT_ALIVE; + initiator->dev_info.cpu.req_pending = TRUE; + } +} + +void sccpu_strategy(ior) + register io_req_t ior; +{ + void sccpu_start(); + + rz_simpleq_strategy(ior, sccpu_start); +} + +void sccpu_start(tgt, done) + target_info_t *tgt; + boolean_t done; +{ + io_req_t head, ior; + scsi_ret_t ret; + + /* this is to the doc & debug code mentioned in the beginning */ + if (!done && tgt->dev_info.cpu.req_pending) { + panic("sccpu_act_as_target called"); +#if 0 + sccpu_act_as_target( tgt); +#endif + return; + } + + ior = tgt->ior; + if (ior == 0) + return; + + if (done) { + + /* see if we must retry */ + if ((tgt->done == SCSI_RET_RETRY) && + ((ior->io_op & IO_INTERNAL) == 0)) { + delay(1000000);/*XXX*/ + goto start; + } else + /* got a bus reset ? shouldn't matter */ + if ((tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) && + ((ior->io_op & IO_INTERNAL) == 0)) { + goto start; + } else + + /* check completion status */ + + if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) { + scsi_sense_data_t *sns; + + ior->io_op = ior->io_temporary; + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + + sns = (scsi_sense_data_t *)tgt->cmd_ptr; + if (scsi_debug) + scsi_print_sense_data(sns); + + if (scsi_check_sense_data(tgt, sns)) { + if (sns->u.xtended.ili) { + if (ior->io_op & IO_READ) { + int residue; + + residue = sns->u.xtended.info0 << 24 | + sns->u.xtended.info1 << 16 | + sns->u.xtended.info2 << 8 | + sns->u.xtended.info3; + if (scsi_debug) + printf("Cpu Short Read (%d)\n", residue); + /* + * NOTE: residue == requested - actual + * We only care if > 0 + */ + if (residue < 0) residue = 0;/* sanity */ + ior->io_residual += residue; + ior->io_error = 0; + ior->io_op &= ~IO_ERROR; + /* goto ok */ + } + } + } + } + + else if (tgt->done != SCSI_RET_SUCCESS) { + + if (tgt->done == SCSI_RET_NEED_SENSE) { + + ior->io_temporary = ior->io_op; + ior->io_op = IO_INTERNAL; + if (scsi_debug) + printf("[NeedSns x%x x%x]", ior->io_residual, ior->io_count); + scsi_request_sense(tgt, ior, 0); + return; + + } else if (tgt->done == SCSI_RET_RETRY) { + /* only retry here READs and WRITEs */ + if ((ior->io_op & IO_INTERNAL) == 0) { + ior->io_residual = 0; + goto start; + } else{ + ior->io_error = D_WOULD_BLOCK; + ior->io_op |= IO_ERROR; + } + } else { + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } + } + + if (scsi_debug) + printf("[Resid x%x]", ior->io_residual); + + /* dequeue next one */ + head = ior; + + simple_lock(&tgt->target_lock); + ior = head->io_next; + tgt->ior = ior; + if (ior) + ior->io_prev = head->io_prev; + simple_unlock(&tgt->target_lock); + + iodone(head); + + if (ior == 0) + return; + } + ior->io_residual = 0; +start: + if (ior->io_op & IO_READ) { + ret = scsi_receive( tgt, ior ); + } else if ((ior->io_op & IO_INTERNAL) == 0) { + ret = scsi_send( tgt, ior ); + } +} + + +#if 0 +/* XX turned off this code because it's impossible + to reference 'end' and other such magic symbols + from boot modules. */ +/* + * This is a simple code to make us act as a dumb + * processor type. Use for debugging only. + */ +static struct io_req sccpu_ior; +vm_offset_t sccpu_buffer; /* set this with debugger */ + +void sccpu_act_as_target(self) + target_info_t *self; +{ + static char inq_data[] = "\3\0\1\0\040\0\0\0Mach3.0 Processor Link v0.1"; + static char sns_data[] = "\160\0\0\0\0\0\0\0\0"; + + self->dev_info.cpu.req_pending = FALSE; + sccpu_ior.io_next = 0; +#define MAXSIZE 1024*64 + sccpu_ior.io_count = (MAXSIZE < self->dev_info.cpu.req_len) ? + MAXSIZE : self->dev_info.cpu.req_len; + + switch (self->dev_info.cpu.req_cmd) { + case SCSI_CMD_INQUIRY: + sccpu_ior.io_data = inq_data; break; + case SCSI_CMD_REQUEST_SENSE: + sccpu_ior.io_data = sns_data; break; + default: + if (sccpu_buffer == 0) { + /* ( read my lips :-) */ + /* extern char end[]; */ + sccpu_buffer = trunc_page(kalloc(MAXSIZE)); + } + sccpu_ior.io_data = (char*)sccpu_buffer; break; + } + + if (self->dev_info.cpu.req_cmd == SCSI_CMD_SEND) { + self->cur_cmd = SCSI_CMD_READ; + sccpu_ior.io_op = IO_READ; + } else { + self->cur_cmd = SCSI_CMD_WRITE; + sccpu_ior.io_op = IO_WRITE; + } + self->ior = &sccpu_ior; +} +#endif + +/*#define PERF*/ +#ifdef PERF +int test_read_size = 512; +int test_read_nreads = 1000; +int test_read_bdev = 0; +int test_read_or_write = 1; + +#include <sys/time.h> +#include <machine/machspl.h> /* spl */ + +test_read(max) +{ + int i, ssk, usecs; + struct timeval start, stop; + spl_t s; + + if (max != 0) + test_read_nreads = max; + + s = spl0(); + start = time; + if (test_read_or_write) read_test(); else write_test(); + stop = time; + splx(s); + + usecs = stop.tv_usec - start.tv_usec; + if (usecs < 0) { + stop.tv_sec -= 1; + usecs += 1000000; + } + printf("Size %d count %d time %3d sec %d us\n", + test_read_size, test_read_nreads, + stop.tv_sec - start.tv_sec, usecs); +} + +read_test() +{ + struct io_req io, io1; + register int i; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_op = IO_READ; + io.io_count = test_read_size; + io.io_data = (char*)sccpu_buffer; + io1 = io; + + sccpu_strategy(&io); + for (i = 1; i < test_read_nreads; i += 2) { + io1.io_op = IO_READ; + sccpu_strategy(&io1); + iowait(&io); + io.io_op = IO_READ; + sccpu_strategy(&io); + iowait(&io1); + } + iowait(&io); +} + +write_test() +{ + struct io_req io, io1; + register int i; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_op = IO_WRITE; + io.io_count = test_read_size; + io.io_data = (char*)sccpu_buffer; + io1 = io; + + sccpu_strategy(&io); + for (i = 1; i < test_read_nreads; i += 2) { + io1.io_op = IO_WRITE; + sccpu_strategy(&io1); + iowait(&io); + io.io_op = IO_WRITE; + sccpu_strategy(&io); + iowait(&io1); + } + iowait(&io); +} + +tur_test() +{ + struct io_req io; + register int i; + char *a; + struct timeval start, stop; + spl_t s; + target_info_t *tgt; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_data = (char*)&io;/*unused but kernel space*/ + + rz_check(io.io_unit, &a, &tgt); + s = spl0(); + start = time; + for (i = 0; i < test_read_nreads; i++) { + io.io_op = IO_INTERNAL; + scsi_test_unit_ready(tgt,&io); + } + stop = time; + splx(s); + i = stop.tv_usec - start.tv_usec; + if (i < 0) { + stop.tv_sec -= 1; + i += 1000000; + } + printf("%d test-unit-ready took %3d sec %d us\n", + test_read_nreads, + stop.tv_sec - start.tv_sec, i); +} + +/*#define MEM_PERF*/ +#ifdef MEM_PERF +int mem_read_size = 1024; /* ints! */ +int mem_read_nreads = 1000; +volatile int *mem_read_address = (volatile int*)0xb0080000; +volatile int *mem_write_address = (volatile int*)0xb0081000; + +mem_test(max, which) +{ + int i, ssk, usecs; + struct timeval start, stop; + int (*fun)(), mwrite_test(), mread_test(), mcopy_test(); + spl_t s; + + if (max == 0) + max = mem_read_nreads; + + switch (which) { + case 1: fun = mwrite_test; break; + case 2: fun = mcopy_test; break; + default:fun = mread_test; break; + } + + s = spl0(); + start = time; + for (i = 0; i < max; i++) + (*fun)(mem_read_size); + stop = time; + splx(s); + + usecs = stop.tv_usec - start.tv_usec; + if (usecs < 0) { + stop.tv_sec -= 1; + usecs += 1000000; + } + printf("Size %d count %d time %3d sec %d us\n", + mem_read_size*4, max, + stop.tv_sec - start.tv_sec, usecs); +} + +mread_test(max) + register int max; +{ + register int i; + register volatile int *addr = mem_read_address; + + for (i = 0; i < max; i++) { + register int j = *addr++; + } +} +mwrite_test(max) + register int max; +{ + register int i; + register volatile int *addr = mem_read_address; + + for (i = 0; i < max; i++) { + *addr++ = i; + } +} + +mcopy_test(max) + register int max; +{ + register volatile int *from = mem_read_address; + register volatile int *to = mem_write_address; + register volatile int *endaddr; + + endaddr = to + max; + while (to < endaddr) + *to++ = *from++; + +} +#endif /*MEM_PERF*/ + +#endif /*PERF*/ + +#endif /* NSCSI > 0 */ |