diff options
Diffstat (limited to 'scsi')
49 files changed, 30066 insertions, 0 deletions
diff --git a/scsi/adapters/README b/scsi/adapters/README new file mode 100644 index 0000000..1bc7e7c --- /dev/null +++ b/scsi/adapters/README @@ -0,0 +1,290 @@ + +This directory contains various examples of HBA support code, +among them: + + Chip/Board File Machine tested By + + NCR 53C94 scsi_53C94 DecStation 5000 af@cmu + DEC 7061 scsi_7061 DecStation 3100/2100 af@cmu + NCR 5380 scsi_5380 VaxStation 3100 af@cmu + Fujitsu 89352 scsi_89352 Omron Luna88k danner@cmu + Adaptec 1542B scsi_aha15 AT/PC af@cmu + +It should be trivial to modify them for some other machine that uses +the same SCSI chips, hopefully by properly conditionalizing and macroizing +the existing code. + +There are various rules and assumptions to keep in mind when designing/coding +the support code for a new HBA, here is a list. Pls extend this with +anything you find is not openly stated here and made your life miserable +during the port, and send it back to CMU. + + +AUTOCONF + +We assume the structures and procedures defined in chips/busses.*, +e.g. someone will call configure_bus_master to get to our foo_probe() +routine. This should make up its mind on how many targets it sees +[see later for dynamic reconfig], allocate a descriptor for each +one and leave the driver ready to accept commands (via foo_go()). + + On raw chips you should use a test_unit_ready command, + selecting without ATN, and timing out on non-existant + devices. Use LUN 0. + On boards, there probably is a command to let the board do + it (see Adaptec), if not do as above. + +The typical autoconf descriptor might look like + + caddr_t foo_std[NFOO] = { 0 }; + struct bus_device *foo_dinfo[NFOO*8]; + struct bus_ctlr *foo_minfo[NFOO]; + struct bus_driver foo_driver = + { foo_probe, scsi_slave, scsi_attach, foo_go, foo_std, "rz", + foo_dinfo, "foo", foo_minfo, BUS_INTR_B4_PROBE}; + +which indicates that foo_probe() and foo_go() are our interface functions, +and we use the generic scsi_slave() and scsi_attach() for the rest. +Their definition is + + foo_probe(reg, ui) + vm_offset_t reg; + struct bus_ctlr *ui; + +[the "reg" argument might actually be something else on architectures that + do not use memory mapped I/O] + + aha_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; + +The foo_go() routine is fairly common across chips, look at any example to +see how to structure it. Basically, the arguments tell us how much data +to expect in either direction, and whether (cmd_only) we think we should +be selecting with ATN (cmd_only==FALSE) or not. The only gotcha is cmd_count +actually includes the size of any parameters. + +The "go" field of the scsi_softc structure describing your HBA should be +set to your foo_go() routine, by the foo_probe() routine. + +DATA DEPENDENCIES + +The upper layer assumes that tgt->cmd_ptr is a pointer to good memory +[e.g. no funny padding] where it places the scsi command control blocks +AND small (less than 255 bytes) parameters. It also expects small results +in there (things like read_capacity, for instance). I think I cleaned +up all the places that used to assume tgt->cmd_ptr was aligned, but do not +be surprised if I missed one. + +It does NOT use the dma_ptr, or any of the transient_state fields. + +WATCHDOG + +There is an optional MI watchdog facility, which can be used quite simply by +filling in the "watchdog" field of the scsi_softc structure describing +your HBA. To disable it, leave the field zero (or, dynamically, zero the +timeout value). You can use a watchdog of your own if you like, or more +likely set this field to point to the MI scsi_watchdog(). +This requires that your foo_softc descriptor starts off with a watchdog_t +structure, with the "reset" field pointing to a function that will +reset the SCSI bus should the watchdog expire. + +When a new SCSI command is initiated you should + if (foo->wd.nactive++ == 0) + foo->wd.watchdog_state = SCSI_WD_ACTIVE; +to activate the watchdog, on each interrupt [or other signal that all +is proceeding well for the command and it is making progress] you should + if (foo->wd.nactive) + foo->wd.watchdog_state = SCSI_WD_ACTIVE; +bump down the watchdog 'trigger level', and when the command terminates + if (aha->wd.nactive-- == 1) + aha->wd.watchdog_state = SCSI_WD_INACTIVE; + +When you detect a SCSI bus reset (possibly we initiated it) you should + aha->wd.nactive = 0; +and after cleaning up properly invoke + scsi_bus_was_reset(sc) + scsi_softc_t sc; + +The functiona that is invoked on watchdog expiry is + foo_reset_scsibus(foo) + register foo_softc_t foo; + +Note that this can be used for dumb chips that do not support select timeouts +in hardware [see the 5380 or 7061 code], but its primary use is to detect +instances where a target is holding on the SCSI bus way too long. + +The one drawback of resetting the bus is that some devices (like tapes) +lose status in case of a reset, and the MI code does not (yet?) try to +keep enough information around to be able to recover. If you want to +add something very useful you might change the rz_tape.c code to do just +that, e.g. on SCSI_RET_ABORTs wait a while for the tape to do whatever, +then rewind, and seek forward where the tape should have been positioned at +the beginning of the command that failed, then reissue the command. +None of the examples so far tries to be 'smart' like making an attempt +to get the bus unstuck without resetting it, send us ideas if you have +some. + + +DYNAMIC RECONFIG + +Your code should be ready to add/remove targets on the fly. To do so, +notify the upper layer that a target went offline returning +SCSI_RET_DEVICE_DOWN when e.g. the select timed out, and clear out +the tgt->flags field. +To find new devices, define a function + + boolean_t + foo_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; + +and install it in the "probe" field of the scsi_softc_t structure describing +the HBA to the upper layer. This function should finalize all HBA-specific +info in the target_info structure, then do a scsi_inquiry and check the +return code. If this is not SCSI_RET_DEVICE_DOWN the target should be +marked TGT_ALIVE. + + +COMMAND TERMINATION + +Generally, command termination should be reported to the upper layers +by setting the tgt->done field to the proper value [it should remain +SCSI_RET_IN_PROGRESS while the command is executing] and invoking the +target's completion routine, like: + if (tgt->ior) { + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + } +Note that early on some commands will actually wait for completion +by spinning on the tgt->done value, because autoconf happens when +threads and the scheduler are not working. + +Return SCSI_RET_RETRY if the target was busy, the command will be retried +as appropriate. + +Check the completion routines [in rz_disk.c and rz_tape.c for instance] +if you are not sure what to return in a troubled case. + +HBA CHIPS GOTCHAS + +All of the examples so far use the idea of 'scripts': the interrupt routine +matches the chip state with what is expected and if this is ok (it is +in the common important case) it just calls a prepackaged function. +We have found this to be _extremely_ simpler than using a state machine +of various ridiculous and erroneous sorts, and much more handy for debugging +also. Not to mention the saving on code. +Nonetheless, there really are no restrictions on how to structure the HBA +code, if you prefer state machines go ahead and use them! + +Scheduling of the bus among competing targets is one of the major missing +pieces for simple HBAs. A winning strategy used so far is as follows. +Allocate a queue_head_t of waiting_targets in your foo_softc, and two +target_info_t pointers next_target and active_target. A three-valued +state field is also needed. If you enter the foo_go() routine +and find the state&BUSY simply enqueue_tail() your tgt on the waiting_targets +queue. Otherwise mark the bus BUSY, set next_target to tgt, and proceed +to a selection attempt. +Note that the attempt might fail and a reselection win over it, therefore +the attempt_selection() routine should just retrieve the next_target +and install it in active_target, start the selection and let the interrupt +routine take care of the rest [see scsi_5380 for a different setup]. +If a reselection wins we should note that we had a COLLISION in the state +field, install the reconecting target and proceed to completion. +When either a command is complete or a target disconnects you should invoke +a foo_release_bus() routine, which might look like: + +boolean_t +foo_release_bus(foo) + register foo_softc_t foo; +{ + boolean_t ret = TRUE; + + LOG(9,"release"); + if (foo->state & FOO_STATE_COLLISION) { + + LOG(0xB,"collided"); + foo->state &= ~FOO_STATE_COLLISION; + foo_attempt_selection(foo); + + } else if (queue_empty(&foo->waiting_targets)) { + + foo->state &= ~FOO_STATE_BUSY; + foo->active_target = 0; + foo->script = 0; + ret = FALSE; + + } else { + + LOG(0xC,"dequeue"); + foo->next_target = (target_info_t *) + dequeue_head(&foo->waiting_targets); + foo_attempt_selection(foo); + } + return ret; +} + +which indicates whether the bus has been handed off to a new target or not. +This provides the desired FIFO scheduling of the bus and gives maximum +parallelism when targets are allowed to (and infact do) disconnect. + +An area where there are problems most times is how to minimize the +interaction of selections and reselections in, e.g. foo_attempt_selection(). +This is very much chip specific, but sneaking on the SCSI bus should +be a viable alternative in most cases. Check in the specs what happens +if you send a command while there is already a reselection pending: +a well behaved chip would ignore the command and not screwup its status. +[Keep in mind that even if _now_ there is no reselection indication + on the next cycle there might be and you won't see it!] + +RANDOM GOTCHAS + +A number of workstations do not provide real DMA support [do not ask me why] +but rather a special 'buffer' more or less wide where you have to copy +data to and from. This has been handled, see esp the 52C94 code which has +even the extreme optimization of issuing the send command even before +the data has been copied into the buffer! We have handled even machines +where no DMA at all was provided. + +Speaking of DMA.. many of these chips 'prefetch' data, or have a FIFO +on board (they have to if they do synch xfers), and when the target +disconnects it is always a pain to find out how many bytes exactly did we +xfer. Be advised that this hurdle exists, and that the best way to +debug your code here is to use a tape. A safe way is to initially +disable disconnects [so that you can get the system up from disk] +and enable them only on the tape unit that you are using for testing. +Later on enable disks but make sure you have some way to recover from +a zapped disk ! + +MOVING TO USER SPACE + +All examples have hooks for user-space versions of the driver, the +ones for 54C94 and 7061 actually do work. Look in mapped_scsi.c +for how this is done, it is fairly simple as far as the kernel is +concerned. To keep the option of mapping to user space open you +should structure your interrupt routine such that it does all the +state gathering and clearing of the interrupt right away. This +scheme gives you some assurance that your code will keep on working +when the interrupt processing is actually delayed and you recover +the interrupt state from the saved structure in the mapped area. + + +IMPROVEMENTS + +There are a variety of things to be done still, for instance: + +- rewrite scsi_slave() and scsi_attach() to be fully SCSI-II compliant. + There are only comments right now as to how that should be done. + +- add enough machinery to the tape code to be able to recover from + bus resets. Do so in such a way that other devices might use the ideas. + +- add more devices, like printers scanners modems etc that are currently + missing + +- add a 'generic' set_status flavor which simply executes a scsi command + passed in from user. This seems the way many vendors and other people + have strutured their drivers, it would make it possible to have a common + user-level program to do special maintainance work like, for instance, + reformatting of disks. + diff --git a/scsi/adapters/scsi_33C93.h b/scsi/adapters/scsi_33C93.h new file mode 100644 index 0000000..454a8eb --- /dev/null +++ b/scsi/adapters/scsi_33C93.h @@ -0,0 +1,396 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_33C93.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/91 + * + * Defines for the WD/AMD 33C93 SBIC (SCSI Bus Interface Controller) + */ + +/* + * Register map, w mux addressing + */ + +typedef struct { + + volatile unsigned char sbic_myid; /* rw: My SCSI id */ +#define sbic_cdbsize sbic_myid /* w : size of CDB */ + + volatile unsigned char sbic_control; /* rw: Control register */ + + volatile unsigned char sbic_timeo; /* rw: Timeout period */ + + volatile unsigned char sbic_cdb1; /* rw: CDB, 1st byte */ +#define sbic_tsecs sbic_cdb1 /* rw: Xlate: nsectors */ + + volatile unsigned char sbic_cdb2; /* rw: CDB, 2nd byte */ +#define sbic_theads sbic_cdb2 /* rw: Xlate: nheads */ + + volatile unsigned char sbic_cdb3; /* rw: CDB, 3rd byte */ +#define sbic_tcyl_hi sbic_cdb3 /* rw: Xlate: ncyls, hi */ + + volatile unsigned char sbic_cdb4; /* rw: CDB, 4th byte */ +#define sbic_tcyl_lo sbic_cdb4 /* rw: Xlate: ncyls, lo */ + + volatile unsigned char sbic_cdb5; /* rw: CDB, 5th byte */ +#define sbic_addr_hi sbic_cdb5 /* rw: Xlate: address, hi */ + + volatile unsigned char sbic_cdb6; /* rw: CDB, 6th byte */ +#define sbic_addr_2 sbic_cdb6 /* rw: Xlate: address */ + + volatile unsigned char sbic_cdb7; /* rw: CDB, 7th byte */ +#define sbic_addr_3 sbic_cdb7 /* rw: Xlate: address */ + + volatile unsigned char sbic_cdb8; /* rw: CDB, 8th byte */ +#define sbic_addr_lo sbic_cdb8 /* rw: Xlate: address, lo */ + + volatile unsigned char sbic_cdb9; /* rw: CDB, 9th byte */ +#define sbic_secno sbic_cdb9 /* rw: Xlate: sector no */ + + volatile unsigned char sbic_cdb10; /* rw: CDB, 10th byte */ +#define sbic_headno sbic_cdb10 /* rw: Xlate: head no */ + + volatile unsigned char sbic_cdb11; /* rw: CDB, 11th byte */ +#define sbic_cylno_hi sbic_cdb11/* rw: Xlate: cyl no, hi */ + + volatile unsigned char sbic_cdb12; /* rw: CDB, 12th byte */ +#define sbic_cylno_lo sbic_cdb12/* rw: Xlate: cyl no, lo */ + + volatile unsigned char sbic_tlun; /* rw: Target LUN */ + + volatile unsigned char sbic_cmd_phase; /* rw: Command phase */ + + volatile unsigned char sbic_syn; /* rw: Synch xfer params */ + + volatile unsigned char sbic_count_hi; /* rw: Xfer count, hi */ + + volatile unsigned char sbic_count_med; /* rw: Xfer count, med */ + + volatile unsigned char sbic_count_lo; /* rw: Xfer count, lo */ + + volatile unsigned char sbic_selid; /* rw: Target ID (select) */ + + volatile unsigned char sbic_rselid; /* rw: Target ID (reselect) */ + + volatile unsigned char sbic_csr; /* r : Status register */ + + volatile unsigned char sbic_cmd; /* rw: Command register */ + + volatile unsigned char sbic_data; /* rw: FIFO top */ + + char u0; /* unused, padding */ + char u1; /* unused, padding */ + char u2; /* unused, padding */ + char u3; /* unused, padding */ + char u4; /* unused, padding */ + + volatile unsigned char sbic_asr; /* r : Aux Status Register */ + +} sbic_mux_regmap_t; + +/* + * Register map, non mux (indirect) addressing + */ +typedef struct { + volatile unsigned char sbic_asr; /* r : Aux Status Register */ +#define sbic_address sbic_asr /* w : desired register no */ + + volatile unsigned char sbic_value; /* rw: register value */ +} sbic_ind_regmap_t; + +#define sbic_read_reg(regs,regno,val) { \ + (regs)->sbic_address = (regno); \ + wbflush(); \ + (val) = (regs)->sbic_value; \ + } + +#define sbic_write_reg(regs,regno,val) { \ + (regs)->sbic_address = (regno); \ + wbflush(); \ + (regs)->sbic_value = (val); \ + } + +#define SBIC_myid 0 +#define SBIC_cdbsize 0 +#define SBIC_control 1 +#define SBIC_timeo 2 +#define SBIC_cdb1 3 +#define SBIC_tsecs 3 +#define SBIC_cdb2 4 +#define SBIC_theads 4 +#define SBIC_cdb3 5 +#define SBIC_tcyl_hi 5 +#define SBIC_cdb4 6 +#define SBIC_tcyl_lo 6 +#define SBIC_cdb5 7 +#define SBIC_addr_hi 7 +#define SBIC_cdb6 8 +#define SBIC_addr_2 8 +#define SBIC_cdb7 9 +#define SBIC_addr_3 9 +#define SBIC_cdb8 10 +#define SBIC_addr_lo 10 +#define SBIC_cdb9 11 +#define SBIC_secno 11 +#define SBIC_cdb10 12 +#define SBIC_headno 12 +#define SBIC_cdb11 13 +#define SBIC_cylno_hi 13 +#define SBIC_cdb12 14 +#define SBIC_cylno_lo 14 +#define SBIC_tlun 15 +#define SBIC_cmd_phase 16 +#define SBIC_syn 17 +#define SBIC_count_hi 18 +#define SBIC_count_med 19 +#define SBIC_count_lo 20 +#define SBIC_selid 21 +#define SBIC_rselid 22 +#define SBIC_csr 23 +#define SBIC_cmd 24 +#define SBIC_data 25 +/* sbic_asr is addressed directly */ + +/* + * Register defines + */ + +/* + * Auxiliary Status Register + */ + +#define SBIC_ASR_INT 0x80 /* Interrupt pending */ +#define SBIC_ASR_LCI 0x40 /* Last command ignored */ +#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */ +#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */ +#define SBIC_ASR_xxx 0x0c +#define SBIC_ASR_PE 0x02 /* Parity error (even) */ +#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */ + +/* + * My ID register, and/or CDB Size + */ + +#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */ + /* 11 Mhz is invalid */ +#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */ +#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */ +#define SBIC_ID_EHP 0x10 /* Enable host parity */ +#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */ +#define SBIC_ID_MASK 0x07 +#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */ + +/* + * Control register + */ + +#define SBIC_CTL_DMA 0x80 /* Single byte dma */ +#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/ +#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */ +#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */ +#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */ +#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */ +#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/ +#define SBIC_CTL_HA 0x02 /* Halt on ATN */ +#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */ + +/* + * Timeout period register + * [val in msecs, input clk in Mhz] + */ + +#define SBIC_TIMEOUT(val,clk) ((((val)*(clk))/80)+1) + +/* + * CDBn registers, note that + * cdb11 is used for status byte in target mode (send-status-and-cc) + * cdb12 sez if linked command complete, and w/flag if so + */ + +/* + * Target LUN register + * [holds target status when select-and-xfer] + */ + +#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */ +#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */ +#define SBIC_TLUN_xxx 0x38 +#define SBIC_TLUN_MASK 0x07 + +/* + * Command Phase register + */ + +#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */ +#define SBIC_CPH(p) ((p)&SBIC_CPH_MASK) + +/* + * FIFO register + */ + +#define SBIC_FIFO_DEEP 12 + +/* + * Synchronous xfer register + */ + +#define SBIC_SYN_OFF_MASK 0x0f +#define SBIC_SYN_MAX_OFFSET (SBIC_FIFO_DEEP-1) +#define SBIC_SYN_PER_MASK 0x70 +#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */ + +#define SBIC_SYN(o,p) (((o)&SBIC_SYN_OFF_MASK)|(((p)<<4)&SBIC_SYN_PER_MASK)) + +/* + * Transfer count register + * optimal access macros depend on addressing + */ + +/* + * Destination ID (selid) register + */ + +#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */ +#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */ +# define SBIC_SID_FROM_SCSI 0x40 +# define SBIC_SID_TO_SCSI 0x00 +#define SBIC_SID_xxx 0x38 +#define SBIC_SID_IDMASK 0x07 + +/* + * Source ID (rselid) register + */ + +#define SBIC_RID_ER 0x80 /* Enable reselection */ +#define SBIC_RID_ES 0x40 /* Enable selection */ +#define SBIC_RID_DSP 0x20 /* Disable select parity */ +#define SBIC_RID_SIV 0x08 /* Source ID valid */ +#define SBIC_RID_MASK 0x07 + +/* + * Status register + */ + +#define SBIC_CSR_CAUSE 0xf0 +# define SBIC_CSR_RESET 0x00 /* chip was reset */ +# define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */ +# define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/ +# define SBIC_CSR_CMD_ERR 0x40 /* end with error */ +# define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */ + +#define SBIC_CSR_QUALIFIER 0x0f + + /* Reset State Interrupts */ +# define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/ +# define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/ + + /* Successful Completion Interrupts */ +# define SBIC_CSR_TARGET 0x10 /* reselect complete */ +# define SBIC_CSR_INITIATOR 0x11 /* select complete */ +# define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */ +# define SBIC_CSR_W_ATN 0x14 /* ditto */ +# define SBIC_CSR_XLATED 0x15 /* translate address cmd */ +# define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/ +# define SBIC_CSR_XFERRED 0x18 /* phase in low bits */ + + /* Paused or Aborted Interrupts */ +# define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/ +# define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */ +# define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */ +# define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */ +# define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */ +# define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */ +# define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */ + + /* Terminated Interrupts */ +# define SBIC_CSR_CMD_INVALID 0x40 +# define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */ +# define SBIC_CSR_SEL_TIMEO 0x42 +# define SBIC_CSR_PE 0x43 /* parity error */ +# define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */ +# define SBIC_CSR_XLATE_TOOBIG 0x45 +# define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */ +# define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */ +# define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */ + + /* Service Required Interrupts */ +# define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */ +# define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */ +# define SBIC_CSR_SLT 0x82 /* selected, no ATN */ +# define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */ +# define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */ +# define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */ +# define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */ +# define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */ + +#define SBIC_PHASE(csr) SCSI_PHASE(csr) + +/* + * Command register (command codes) + */ + +#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */ +#define SBIC_CMD_MASK 0x7f + + /* Miscellaneous */ +#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */ +#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */ +#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */ +#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */ +#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */ +#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */ + + /* Initiator state */ +#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */ +#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */ +#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */ + + /* Target state */ +#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */ +#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */ +#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */ +#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */ +#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */ +#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */ +#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */ +#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */ +#define SBIC_CMD_SND 0x17 /* ( T ) lev II */ + + /* Disconnected state */ +#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */ +#define SBIC_CMD_SEL 0x07 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */ +#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */ +#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */ +#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */ +#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */ + + +/* approximate, but we won't do SBT on selects */ +#define sbic_isa_select(cmd) (((cmd)>0x5)&&((cmd)<0xa)) + diff --git a/scsi/adapters/scsi_33C93_hdw.c b/scsi/adapters/scsi_33C93_hdw.c new file mode 100644 index 0000000..169ccbf --- /dev/null +++ b/scsi/adapters/scsi_33C93_hdw.c @@ -0,0 +1,2078 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_33C93_hdw.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the WD/AMD 33C93 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +#if 0 +DISCLAIMER: THIS DOES NOT EVEN COMPILE YET, it went in by mistake. +Code that probably makes some sense is from here to "TILL HERE" + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <sbic.h> +#if NSBIC > 0 +#include <platforms.h> + +#ifdef IRIS +#define PAD(n) char n[3] /* or whatever */ +#define SBIC_MUX_ADDRESSING /* comment out if wrong */ +#define SBIC_CLOCK_FREQUENCY 20 /* FIXME FIXME FIXME */ +#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA /* FIXME FIXME FIXME */ + +#define SBIC_SET_RST_ADDR /*SCSI_INIT_ADDR*/ +#define SBIC_CLR_RST_ADDR /*SCSI_RDY_ADDR*/ +#define SBIC_MACHINE_RESET_SCSIBUS(regs,per) \ + { int temp; \ + temp = *(volatile unsigned int *)SBIC_SET_RST_ADDR; \ + delay(per); \ + temp = *(volatile unsigned int *)SBIC_CLR_RST_ADDR; \ + } + +#endif + +#include <machine/machspl.h> /* spl definitions */ +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> + +#include <scsi/adapters/scsi_33C93.h> +#include <scsi/scsi_defs.h> +#include <scsi/adapters/scsi_dma.h> + +/* + * Spell out all combinations of padded/nopadded and mux/nomux + */ +#ifdef PAD +typedef struct { + + volatile unsigned char sbic_myid; /* rw: My SCSI id */ +/*#define sbic_cdbsize sbic_myid /* w : size of CDB */ + PAD(pad0) + volatile unsigned char sbic_control; /* rw: Control register */ + PAD(pad1) + volatile unsigned char sbic_timeo; /* rw: Timeout period */ + PAD(pad2) + volatile unsigned char sbic_cdb1; /* rw: CDB, 1st byte */ + PAD(pad3) + volatile unsigned char sbic_cdb2; /* rw: CDB, 2nd byte */ + PAD(pad4) + volatile unsigned char sbic_cdb3; /* rw: CDB, 3rd byte */ + PAD(pad5) + volatile unsigned char sbic_cdb4; /* rw: CDB, 4th byte */ + PAD(pad6) + volatile unsigned char sbic_cdb5; /* rw: CDB, 5th byte */ + PAD(pad7) + volatile unsigned char sbic_cdb6; /* rw: CDB, 6th byte */ + PAD(pad8) + volatile unsigned char sbic_cdb7; /* rw: CDB, 7th byte */ + PAD(pad9) + volatile unsigned char sbic_cdb8; /* rw: CDB, 8th byte */ + PAD(pad10) + volatile unsigned char sbic_cdb9; /* rw: CDB, 9th byte */ + PAD(pad11) + volatile unsigned char sbic_cdb10; /* rw: CDB, 10th byte */ + PAD(pad12) + volatile unsigned char sbic_cdb11; /* rw: CDB, 11th byte */ + PAD(pad13) + volatile unsigned char sbic_cdb12; /* rw: CDB, 12th byte */ + PAD(pad14) + volatile unsigned char sbic_tlun; /* rw: Target LUN */ + PAD(pad15) + volatile unsigned char sbic_cmd_phase; /* rw: Command phase */ + PAD(pad16) + volatile unsigned char sbic_syn; /* rw: Synch xfer params */ + PAD(pad17) + volatile unsigned char sbic_count_hi; /* rw: Xfer count, hi */ + PAD(pad18) + volatile unsigned char sbic_count_med; /* rw: Xfer count, med */ + PAD(pad19) + volatile unsigned char sbic_count_lo; /* rw: Xfer count, lo */ + PAD(pad20) + volatile unsigned char sbic_selid; /* rw: Target ID (select) */ + PAD(pad21) + volatile unsigned char sbic_rselid; /* rw: Target ID (reselect) */ + PAD(pad22) + volatile unsigned char sbic_csr; /* r : Status register */ + PAD(pad23) + volatile unsigned char sbic_cmd; /* rw: Command register */ + PAD(pad24) + volatile unsigned char sbic_data; /* rw: FIFO top */ + PAD(pad25) + char u0; /* unused, padding */ + PAD(pad26) + char u1; /* unused, padding */ + PAD(pad27) + char u2; /* unused, padding */ + PAD(pad28) + char u3; /* unused, padding */ + PAD(pad29) + char u4; /* unused, padding */ + PAD(pad30) + volatile unsigned char sbic_asr; /* r : Aux Status Register */ + PAD(pad31) + +} sbic_padded_mux_regmap_t; + +typedef struct { + volatile unsigned char sbic_asr; /* r : Aux Status Register */ +/*#define sbic_address sbic_asr /* w : desired register no */ + PAD(pad0); + volatile unsigned char sbic_value; /* rw: register value */ + PAD(pad1); +} sbic_padded_ind_regmap_t; + +#else /* !PAD */ + +typedef sbic_mux_regmap_t sbic_padded_mux_regmap_t; +typedef sbic_ind_regmap_t sbic_padded_ind_regmap_t; + +#endif /* !PAD */ + +/* + * Could have used some non-ANSIsm in the following :-)) + */ +#ifdef SBIC_MUX_ADDRESSING + +typedef sbic_padded_mux_regmap_t sbic_padded_regmap_t; + +#define SET_SBIC_myid(regs,val) (regs)->sbic_myid = (val) +#define GET_SBIC_myid(regs,val) (val) = (regs)->sbic_myid +#define SET_SBIC_cdbsize(regs,val) (regs)->sbic_cdbsize = (val) +#define GET_SBIC_cdbsize(regs,val) (val) = (regs)->sbic_cdbsize +#define SET_SBIC_control(regs,val) (regs)->sbic_control = (val) +#define GET_SBIC_control(regs,val) (val) = (regs)->sbic_control +#define SET_SBIC_timeo(regs,val) (regs)->sbic_timeo = (val) +#define GET_SBIC_timeo(regs,val) (val) = (regs)->sbic_timeo +#define SET_SBIC_cdb1(regs,val) (regs)->sbic_cdb1 = (val) +#define GET_SBIC_cdb1(regs,val) (val) = (regs)->sbic_cdb1 +#define SET_SBIC_cdb2(regs,val) (regs)->sbic_cdb2 = (val) +#define GET_SBIC_cdb2(regs,val) (val) = (regs)->sbic_cdb2 +#define SET_SBIC_cdb3(regs,val) (regs)->sbic_cdb3 = (val) +#define GET_SBIC_cdb3(regs,val) (val) = (regs)->sbic_cdb3 +#define SET_SBIC_cdb4(regs,val) (regs)->sbic_cdb4 = (val) +#define GET_SBIC_cdb4(regs,val) (val) = (regs)->sbic_cdb4 +#define SET_SBIC_cdb5(regs,val) (regs)->sbic_cdb5 = (val) +#define GET_SBIC_cdb5(regs,val) (val) = (regs)->sbic_cdb5 +#define SET_SBIC_cdb6(regs,val) (regs)->sbic_cdb6 = (val) +#define GET_SBIC_cdb6(regs,val) (val) = (regs)->sbic_cdb6 +#define SET_SBIC_cdb7(regs,val) (regs)->sbic_cdb7 = (val) +#define GET_SBIC_cdb7(regs,val) (val) = (regs)->sbic_cdb7 +#define SET_SBIC_cdb8(regs,val) (regs)->sbic_cdb8 = (val) +#define GET_SBIC_cdb8(regs,val) (val) = (regs)->sbic_cdb8 +#define SET_SBIC_cdb9(regs,val) (regs)->sbic_cdb9 = (val) +#define GET_SBIC_cdb9(regs,val) (val) = (regs)->sbic_cdb9 +#define SET_SBIC_cdb10(regs,val) (regs)->sbic_cdb10 = (val) +#define GET_SBIC_cdb10(regs,val) (val) = (regs)->sbic_cdb10 +#define SET_SBIC_cdb11(regs,val) (regs)->sbic_cdb11 = (val) +#define GET_SBIC_cdb11(regs,val) (val) = (regs)->sbic_cdb11 +#define SET_SBIC_cdb12(regs,val) (regs)->sbic_cdb12 = (val) +#define GET_SBIC_cdb12(regs,val) (val) = (regs)->sbic_cdb12 +#define SET_SBIC_tlun(regs,val) (regs)->sbic_tlun = (val) +#define GET_SBIC_tlun(regs,val) (val) = (regs)->sbic_tlun +#define SET_SBIC_cmd_phase(regs,val) (regs)->sbic_cmd_phase = (val) +#define GET_SBIC_cmd_phase(regs,val) (val) = (regs)->sbic_cmd_phase +#define SET_SBIC_syn(regs,val) (regs)->sbic_syn = (val) +#define GET_SBIC_syn(regs,val) (val) = (regs)->sbic_syn +#define SET_SBIC_count_hi(regs,val) (regs)->sbic_count_hi = (val) +#define GET_SBIC_count_hi(regs,val) (val) = (regs)->sbic_count_hi +#define SET_SBIC_count_med(regs,val) (regs)->sbic_count_med = (val) +#define GET_SBIC_count_med(regs,val) (val) = (regs)->sbic_count_med +#define SET_SBIC_count_lo(regs,val) (regs)->sbic_count_lo = (val) +#define GET_SBIC_count_lo(regs,val) (val) = (regs)->sbic_count_lo +#define SET_SBIC_selid(regs,val) (regs)->sbic_selid = (val) +#define GET_SBIC_selid(regs,val) (val) = (regs)->sbic_selid +#define SET_SBIC_rselid(regs,val) (regs)->sbic_rselid = (val) +#define GET_SBIC_rselid(regs,val) (val) = (regs)->sbic_rselid +#define SET_SBIC_csr(regs,val) (regs)->sbic_csr = (val) +#define GET_SBIC_csr(regs,val) (val) = (regs)->sbic_csr +#define SET_SBIC_cmd(regs,val) (regs)->sbic_cmd = (val) +#define GET_SBIC_cmd(regs,val) (val) = (regs)->sbic_cmd +#define SET_SBIC_data(regs,val) (regs)->sbic_data = (val) +#define GET_SBIC_data(regs,val) (val) = (regs)->sbic_data + +#define SBIC_TC_SET(regs,val) { \ + (regs)->sbic_count_hi = ((val)>>16)); \ + (regs)->sbic_count_med = (val)>>8; \ + (regs)->sbic_count_lo = (val); \ + } +#define SBIC_TC_GET(regs,val) { \ + (val) = ((regs)->sbic_count_hi << 16) | \ + ((regs)->sbic_count_med << 8) | \ + ((regs)->sbic_count_lo); \ + } + +#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) { \ + register char *ptr = (char*)(cmd); \ + (regs)->cis_cdb1 = *ptr++; \ + (regs)->cis_cdb2 = *ptr++; \ + (regs)->cis_cdb3 = *ptr++; \ + (regs)->cis_cdb4 = *ptr++; \ + (regs)->cis_cdb5 = *ptr++; \ + (regs)->cis_cdb6 = *ptr++; \ + if (cmdsize > 6) { \ + (regs)->cis_cdb7 = *ptr++; \ + (regs)->cis_cdb8 = *ptr++; \ + (regs)->cis_cdb9 = *ptr++; \ + (regs)->cis_cdb10 = *ptr++; \ + } \ + if (cmdsize > 10) { \ + (regs)->cis_cdb11 = *ptr++; \ + (regs)->cis_cdb12 = *ptr; \ + } \ + } + +#else /*SBIC_MUX_ADDRESSING*/ + +typedef sbic_padded_ind_regmap_t sbic_padded_regmap_t; + +#define SET_SBIC_myid(regs,val) sbic_write_reg(regs,SBIC_myid,val) +#define GET_SBIC_myid(regs,val) sbic_read_reg(regs,SBIC_myid,val) +#define SET_SBIC_cdbsize(regs,val) sbic_write_reg(regs,SBIC_cdbsize,val) +#define GET_SBIC_cdbsize(regs,val) sbic_read_reg(regs,SBIC_cdbsize,val) +#define SET_SBIC_control(regs,val) sbic_write_reg(regs,SBIC_control,val) +#define GET_SBIC_control(regs,val) sbic_read_reg(regs,SBIC_control,val) +#define SET_SBIC_timeo(regs,val) sbic_write_reg(regs,SBIC_timeo,val) +#define GET_SBIC_timeo(regs,val) sbic_read_reg(regs,SBIC_timeo,val) +#define SET_SBIC_cdb1(regs,val) sbic_write_reg(regs,SBIC_cdb1,val) +#define GET_SBIC_cdb1(regs,val) sbic_read_reg(regs,SBIC_cdb1,val) +#define SET_SBIC_cdb2(regs,val) sbic_write_reg(regs,SBIC_cdb2,val) +#define GET_SBIC_cdb2(regs,val) sbic_read_reg(regs,SBIC_cdb2,val) +#define SET_SBIC_cdb3(regs,val) sbic_write_reg(regs,SBIC_cdb3,val) +#define GET_SBIC_cdb3(regs,val) sbic_read_reg(regs,SBIC_cdb3,val) +#define SET_SBIC_cdb4(regs,val) sbic_write_reg(regs,SBIC_cdb4,val) +#define GET_SBIC_cdb4(regs,val) sbic_read_reg(regs,SBIC_cdb4,val) +#define SET_SBIC_cdb5(regs,val) sbic_write_reg(regs,SBIC_cdb5,val) +#define GET_SBIC_cdb5(regs,val) sbic_read_reg(regs,SBIC_cdb5,val) +#define SET_SBIC_cdb6(regs,val) sbic_write_reg(regs,SBIC_cdb6,val) +#define GET_SBIC_cdb6(regs,val) sbic_read_reg(regs,SBIC_cdb6,val) +#define SET_SBIC_cdb7(regs,val) sbic_write_reg(regs,SBIC_cdb7,val) +#define GET_SBIC_cdb7(regs,val) sbic_read_reg(regs,SBIC_cdb7,val) +#define SET_SBIC_cdb8(regs,val) sbic_write_reg(regs,SBIC_cdb8,val) +#define GET_SBIC_cdb8(regs,val) sbic_read_reg(regs,SBIC_cdb8,val) +#define SET_SBIC_cdb9(regs,val) sbic_write_reg(regs,SBIC_cdb9,val) +#define GET_SBIC_cdb9(regs,val) sbic_read_reg(regs,SBIC_cdb9,val) +#define SET_SBIC_cdb10(regs,val) sbic_write_reg(regs,SBIC_cdb10,val) +#define GET_SBIC_cdb10(regs,val) sbic_read_reg(regs,SBIC_cdb10,val) +#define SET_SBIC_cdb11(regs,val) sbic_write_reg(regs,SBIC_cdb11,val) +#define GET_SBIC_cdb11(regs,val) sbic_read_reg(regs,SBIC_cdb11,val) +#define SET_SBIC_cdb12(regs,val) sbic_write_reg(regs,SBIC_cdb12,val) +#define GET_SBIC_cdb12(regs,val) sbic_read_reg(regs,SBIC_cdb12,val) +#define SET_SBIC_tlun(regs,val) sbic_write_reg(regs,SBIC_tlun,val) +#define GET_SBIC_tlun(regs,val) sbic_read_reg(regs,SBIC_tlun,val) +#define SET_SBIC_cmd_phase(regs,val) sbic_write_reg(regs,SBIC_cmd_phase,val) +#define GET_SBIC_cmd_phase(regs,val) sbic_read_reg(regs,SBIC_cmd_phase,val) +#define SET_SBIC_syn(regs,val) sbic_write_reg(regs,SBIC_syn,val) +#define GET_SBIC_syn(regs,val) sbic_read_reg(regs,SBIC_syn,val) +#define SET_SBIC_count_hi(regs,val) sbic_write_reg(regs,SBIC_count_hi,val) +#define GET_SBIC_count_hi(regs,val) sbic_read_reg(regs,SBIC_count_hi,val) +#define SET_SBIC_count_med(regs,val) sbic_write_reg(regs,SBIC_count_med,val) +#define GET_SBIC_count_med(regs,val) sbic_read_reg(regs,SBIC_count_med,val) +#define SET_SBIC_count_lo(regs,val) sbic_write_reg(regs,SBIC_count_lo,val) +#define GET_SBIC_count_lo(regs,val) sbic_read_reg(regs,SBIC_count_lo,val) +#define SET_SBIC_selid(regs,val) sbic_write_reg(regs,SBIC_selid,val) +#define GET_SBIC_selid(regs,val) sbic_read_reg(regs,SBIC_selid,val) +#define SET_SBIC_rselid(regs,val) sbic_write_reg(regs,SBIC_rselid,val) +#define GET_SBIC_rselid(regs,val) sbic_read_reg(regs,SBIC_rselid,val) +#define SET_SBIC_csr(regs,val) sbic_write_reg(regs,SBIC_csr,val) +#define GET_SBIC_csr(regs,val) sbic_read_reg(regs,SBIC_csr,val) +#define SET_SBIC_cmd(regs,val) sbic_write_reg(regs,SBIC_cmd,val) +#define GET_SBIC_cmd(regs,val) sbic_read_reg(regs,SBIC_cmd,val) +#define SET_SBIC_data(regs,val) sbic_write_reg(regs,SBIC_data,val) +#define GET_SBIC_data(regs,val) sbic_read_reg(regs,SBIC_data,val) + +#define SBIC_TC_SET(regs,val) { \ + sbic_write_reg(regs,SBIC_count_hi,((val)>>16)); \ + (regs)->sbic_value = (val)>>8; wbflush(); \ + (regs)->sbic_value = (val); \ + } +#define SBIC_TC_GET(regs,val) { \ + sbic_read_reg(regs,SBIC_count_hi,(val)); \ + (val) = ((val)<<8) | (regs)->sbic_value; \ + (val) = ((val)<<8) | (regs)->sbic_value; \ + } + +#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) { + register int n=cmdsize-1; \ + register char *ptr = (char*)(cmd); \ + sbic_write_reg(regs,SBIC_cdb1,*ptr++); \ + while (n-- > 0) (regs)->sbic_value = *ptr++; \ + } + +#endif /*SBIC_MUX_ADDRESSING*/ + +#define GET_SBIC_asr(regs,val) (val) = (regs)->sbic_asr + + +/* + * If all goes well (cross fingers) the typical read/write operation + * should complete in just one interrupt. Therefore our scripts + * have only two parts: a pre-condition and an action. The first + * triggers error handling if not satisfied and in our case it is a match + * of .... + * The action part is just a function pointer, invoked in a standard way. + * The script proceeds only if the action routine returns TRUE. + * See sbic_intr() for how and where this is all done. + */ + +typedef struct script { + struct { /* expected state at interrupt: */ + unsigned char csr; /* interrupt cause */ + unsigned char pha; /* command phase */ + } condition; +/* unsigned char unused[2]; /* unused padding */ + boolean_t (*action)(); /* extra operations */ +} *script_t; + +/* Matching on the condition value */ +#define ANY 0xff +#define SCRIPT_MATCH(csr,pha,cond) \ + (((cond).csr == (csr)) && \ + (((cond).pha == (pha)) || ((cond).pha==ANY))) + + +/* forward decls of script actions */ +boolean_t + sbic_end(), /* all come to an end */ + sbic_get_status(), /* get status from target */ + sbic_dma_in(), /* get data from target via dma */ + sbic_dma_in_r(), /* get data from target via dma (restartable)*/ + sbic_dma_out(), /* send data to target via dma */ + sbic_dma_out_r(), /* send data to target via dma (restartable) */ + sbic_dosynch(), /* negotiate synch xfer */ + sbic_msg_in(), /* receive the disconenct message */ + sbic_disconnected(), /* target has disconnected */ + sbic_reconnect(); /* target reconnected */ + +/* forward decls of error handlers */ +boolean_t + sbic_err_generic(), /* generic handler */ + sbic_err_disconn(), /* target disconnects amidst */ + gimmeabreak(); /* drop into the debugger */ + +int sbic_reset_scsibus(); +boolean_t sbic_probe_target(); +static sbic_wait(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) SCSI-33c93 interface + */ +struct sbic_softc { + watchdog_t wd; + sbic_padded_regmap_t *regs; /* 33c93 registers */ + + scsi_dma_ops_t *dma_ops; /* DMA operations and state */ + opaque_t dma_state; + + script_t script; /* what should happen next */ + boolean_t (*error_handler)();/* what if something is wrong */ + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SBIC_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SBIC_STATE_TARGET 0x04 /* currently selected as target */ +#define SBIC_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SBIC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ +#define SBIC_STATE_AM_MODE 0x20 /* 33c93A with advanced mode (AM) */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char unused; + + scsi_softc_t *sc; /* HBA-indep info */ + target_info_t *active_target; /* the current one */ + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + +} sbic_softc_data[NSBIC]; + +typedef struct sbic_softc *sbic_softc_t; + +sbic_softc_t sbic_softc[NSBIC]; + +/* + * Synch xfer parameters, and timing conversions + */ +int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */ +int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */ + +int sbic_to_scsi_period(regs,a) +{ + unsigned int fs; + + /* cycle = DIV / (2*CLK) */ + /* DIV = FS+2 */ + /* best we can do is 200ns at 20Mhz, 2 cycles */ + + GET_SBIC_myid(regs,fs); + fs = (fs >>6) + 2; /* DIV */ + fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */ + if (a < 2) a = 8; /* map to Cycles */ + return ((fs*a)>>2); /* in 4 ns units */ +} + +int scsi_period_to_sbic(regs,p) +{ + register unsigned int fs; + + /* Just the inverse of the above */ + + GET_SBIC_myid(regs,fs); + fs = (fs >>6) + 2; /* DIV */ + fs = (fs * 1000) / (SBIC_CLOCK_FREQUENCY<<1); /* Cycle, in ns */ + + ret = p << 2; /* in ns units */ + ret = ret / fs; /* in Cycles */ + if (ret < sbic_min_period) + return sbic_min_period; + /* verify rounding */ + if (sbic_to_scsi_period(regs,ret) < p) + ret++; + return (ret >= 8) ? 0 : ret; +} + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + +/* + * Definition of the controller for the auto-configuration program. + */ + +int sbic_probe(), scsi_slave(), scsi_attach(), sbic_go(), sbic_intr(); + +caddr_t sbic_std[NSBIC] = { 0 }; +struct bus_device *sbic_dinfo[NSBIC*8]; +struct bus_ctlr *sbic_minfo[NSBIC]; +struct bus_driver sbic_driver = + { sbic_probe, scsi_slave, scsi_attach, sbic_go, sbic_std, "rz", sbic_dinfo, + "sbic", sbic_minfo, BUS_INTR_B4_PROBE}; + + +sbic_set_dmaops(unit, dmaops) + unsigned int unit; + scsi_dma_ops_t *dmaops; +{ + if (unit < NSBIC) + sbic_std[unit] = (caddr_t)dmaops; +} + +/* + * Scripts + */ +struct script +sbic_script_any_cmd[] = { /* started with SEL & XFER */ + {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status}, +}, + +sbic_script_try_synch[] = { /* started with SEL */ + {{SBIC_CSR_INITIATOR, ANY}, sbic_dosynch}, + {{SBIC_CSR_S_XFERRED, 0x60}, sbic_get_status}, +}; + + +#define DEBUG +#ifdef DEBUG + +#define PRINT(x) if (scsi_debug) printf x + +sbic_state(regs, overrule) + sbic_padded_regmap_t *regs; +{ + register unsigned char asr,tmp; + + if (regs == 0) { + if (sbic_softc[0]) + regs = sbic_softc[0]->regs; + else + regs = (sbic_padded_regmap_t*)0xXXXXXXXX; + } + + GET_SBIC_asr(regs,asr); + + if ((asr & SBIC_ASR_BSY) && !overrule) + db_printf("-BUSY- "); + else { + unsigned char tlun,pha,selid,rselid; + unsigned int cnt; + GET_SBIC_tlun(regs,tlun); + GET_SBIC_cmd_phase(regs,pha); + GET_SBIC_selid(regs,selid); + GET_SBIC_rselid(regs,rselid); + SBIC_TC_GET(regs,cnt); + db_printf("tc %x tlun %x sel %x rsel %x pha %x ", + cnt, tlun, selid, rselid, pha); + } + + if (asr & SBIC_ASR_INT) + db_printf("-INT- "); + else { + GET_SBIC_csr(regs,tmp); + db_printf("csr %x ", tmp); + } + + if (asr & SBIC_ASR_CIP) + db_printf("-CIP-\n"); + else { + GET_SBIC_cmd(regs,tmp); + db_printf("cmd %x\n", tmp); + } + return 0; +} + +sbic_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = sbic_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", + tgt, + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym(spt,1); + db_printf(": %x %x ", spt->condition.csr, spt->condition.pha); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +sbic_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = sbic_softc[unit]->sc->target[i]; + if (tgt) + sbic_target_state(tgt); + } +} + +sbic_script_state(unit) +{ + script_t spt = sbic_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x %x ", spt->condition.csr, spt->condition.pha); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(sbic_softc[unit]->error_handler, 1); + return 0; +} + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int sbic_logpt; +char sbic_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x1e +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + sbic_log[sbic_logpt++] = (e); + if (sbic_logpt == LOGSIZE) sbic_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +sbic_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = sbic_logpt; i < LOGSIZE; i++) { + c = sbic_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-x%x", c & 0x7f); + } +} + +sbic_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /*TRACE*/ +#define LOG(e,f) +#define LOGSIZE +#endif /*TRACE*/ + +#else /*DEBUG*/ +#define PRINT(x) +#define LOG(e,f) +#define LOGSIZE + +#endif /*DEBUG*/ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send a test-unit-ready to each possible target on the bus + * except of course ourselves. + */ +sbic_probe(reg, ui) + unsigned reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + sbic_softc_t sbic = &sbic_softc_data[unit]; + int target_id; + scsi_softc_t *sc; + register sbic_padded_regmap_t *regs; + spl_t s; + boolean_t did_banner = FALSE; + + /* + * We are only called if the right board is there, + * but make sure anyways.. + */ + if (check_memory(reg, 0)) + return 0; + +#if MAPPABLE + /* Mappable version side */ + SBIC_probe(reg, ui); +#endif /*MAPPABLE*/ + + /* + * Initialize hw descriptor, cache some pointers + */ + sbic_softc[unit] = sbic; + sbic->regs = (sbic_padded_regmap_t *) (reg); + + if ((sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[unit]) == 0) + /* use same as unit 0 if undefined */ + sbic->dma_ops = (scsi_dma_ops_t *)sbic_std[0]; + + sbic->dma_state = (*sbic->dma_ops->init)(unit, reg); + + queue_init(&sbic->waiting_targets); + + sc = scsi_master_alloc(unit, sbic); + sbic->sc = sc; + + sc->go = sbic_go; + sc->watchdog = scsi_watchdog; + sc->probe = sbic_probe_target; + sbic->wd.reset = sbic_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + regs = sbic->regs; + + /* + * Reset chip, fully. Note that interrupts are already enabled. + */ + s = splbio(); + if (sbic_reset(regs, TRUE)) + sbic->state |= SBIC_STATE_AM_MODE; + + /* + * Our SCSI id on the bus. + * The user can probably set this via the prom. + * If not, it is easy to fix: make a default that + * can be changed as boot arg. Otherwise we keep + * what the prom used. + */ +#ifdef unneeded + SET_SBIC_myid(regs, (scsi_initiator_id[unit] & 0x7)); + sbic_reset(regs, TRUE); +#endif + GET_SBIC_myid(regs,sc->initiator_id); + sc->initiator_id &= 0x7; + printf("%s%d: SCSI id %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + for (target_id = 0; target_id < 8; target_id++) { + register unsigned char asr, csr, pha; + register scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + SBIC_TC_SET(regs,0); + SET_SBIC_selid(regs,target_id); + SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY)); + + /* + * See if the unit is ready. + * XXX SHOULD inquiry LUN 0 instead !!! + */ + { + scsi_command_test_unit_ready_y c; + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY; + SBIC_LOAD_COMMAND(regs,&c,sizeof(c)); + } + + /* select and send it */ + SET_SBIC_cmd(regs,SBIC_CMD_SEL_XFER); + + /* wait for the chip to complete, or timeout */ + asr = sbic_wait(regs, SBIC_ASR_INT); + GET_SBIC_csr(regs,csr); + + /* + * Check if the select timed out + */ + GET_SBIC_cmd_phase(regs,pha); + if ((SBIC_CPH(pha) == 0) && (csr & SBIC_CSR_CMD_ERR)) { + /* noone out there */ +#if notsure + SET_SBIC_cmd(regs,SBIC_CMD_DISC); + asr = sbic_wait(regs, SBIC_ASR_INT); + GET_SBIC_csr(regs,csr); +#endif + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + if (SBIC_CPH(pha) < 0x60) + /* XXX recover by hand XXX */ + panic(" target acts weirdo"); + + GET_SBIC_tlun(regs,status.bits); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + /* + * Found a target + */ + sbic->ntargets++; + { + register target_info_t *tgt; + tgt = scsi_slave_alloc(sc->masterno, target_id, sbic); + + (*sbic->dma_ops->new_target)(sbic->dma_state, tgt); + } + } + + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +sbic_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + sbic_softc_t sbic = sbic_softc[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + (*sbic->dma_ops->new_target)(sbic->dma_state, tgt); + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + +static sbic_wait(regs, until) + sbic_padded_regmap_t *regs; + char until; +{ + register unsigned char val; + int timeo = 1000000; + + GET_SBIC_asr(regs,val); + while ((val & until) == 0) { + if (!timeo--) { + printf("sbic_wait TIMEO with x%x\n", regs->sbic_csr); + break; + } + delay(1); + GET_SBIC_asr(regs,val); + } + return val; +} + +boolean_t +sbic_reset(regs, quick) + sbic_padded_regmap_t *regs; +{ + char my_id, csr; + + /* preserve our ID for now */ + GET_SBIC_myid(regs,my_id); + my_id &= SBIC_ID_MASK; + + if (SBIC_CLOCK_FREQUENCY < 11) + my_id |= SBIC_ID_FS_8_10; + else if (SBIC_CLOCK_FREQUENCY < 16) + my_id |= SBIC_ID_FS_12_15; + else if (SBIC_CLOCK_FREQUENCY < 21) + my_id |= SBIC_ID_FS_16_20; + + my_id |= SBIC_ID_EAF|SBIC_ID_EHP; + + SET_SBIC_myid(regs,myid); + wbflush(); + + /* + * Reset chip and wait till done + */ + SET_SBIC_cmd(regs,SBIC_CMD_RESET); + delay(25); + + (void) sbic_wait(regs, SBIC_ASR_INT); + GET_SBIC_csr(regs,csr); /* clears interrupt also */ + + /* + * Set up various chip parameters + */ + SET_SBIC_control(regs, SBIC_CTL_HHP|SBIC_CTL_EDI|SBIC_CTL_HSP| + SBIC_MACHINE_DMA_MODE); + /* will do IDI on the fly */ + SET_SBIC_rselid(regs, SBIC_RID_ER|SBIC_RID_ES|SBIC_RID_DSP); + SET_SBIC_syn(regs,SBIC_SYN(0,sbic_min_period)); /* asynch for now */ + + /* anything else was zeroed by reset */ + + if (quick) + return (csr & SBIC_CSR_RESET_AM); + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call sbic_bus_reset(). + */ + /* + * Now HOW do I do this ? I just want to drive the SCSI "RST" + * signal true for about 25 usecs; But the chip has no notion + * of such a signal at all. The spec suggest that the chip's + * reset pin be connected to the RST signal, which makes this + * operation a machdep one. + */ + SBIC_MACHINE_RESET_SCSIBUS(regs, 30); + + return (csr & SBIC_CSR_RESET_AM); +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +sbic_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + sbic_softc_t sbic; + register spl_t s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + sbic = (sbic_softc_t)tgt->hw_state; + + tgt->transient_state.cmd_count = cmd_count; /* keep it here */ + + (*sbic->dma_ops->map)(sbic->dma_state, tgt); + + disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); + disconn = disconn && (sbic->ntargets > 1); + disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? sbic_err_disconn : sbic_err_generic; + scp = sbic_script_any_cmd; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(2,"readop"); + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x1a,"writeop"); + break; + case SCSI_CMD_INQUIRY: + /* This is likely the first thing out: + do the synch neg if so */ + if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) { + scp = sbic_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + break; + } + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + tgt->transient_state.cmd_count = sizeof(scsi_command_group_0); + tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0); + /* fall through */ + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_TEST_UNIT_READY: + /* + * Do the synch negotiation here, unless prohibited + * or done already + */ + if ( ! (tgt->flags & TGT_DID_SYNCH)) { + scp = sbic_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + cmd_only = FALSE; + } + /* fall through */ + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + } + + tgt->transient_state.script = scp; + tgt->transient_state.handler = handler; + tgt->transient_state.identify = (cmd_only) ? 0xff : + (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT : + SCSI_IDENTIFY); + + if (in_count) + tgt->transient_state.in_count = + (in_count < tgt->block_size) ? tgt->block_size : in_count; + else + tgt->transient_state.in_count = 0; + + /* + * See if another target is currently selected on + * this SCSI bus, e.g. lock the sbic structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + * XXX here and everywhere, locks! + */ + /* + * Protection viz reconnections makes it tricky. + */ + s = splbio(); + + if (sbic->wd.nactive++ == 0) + sbic->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (sbic->state & SBIC_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + enqueue_tail(&sbic->waiting_targets, (queue_entry_t) tgt); + } else { + /* + * It is down to at most two contenders now, + * we will treat reconnections same as selections + * and let the scsi-bus arbitration process decide. + */ + sbic->state |= SBIC_STATE_BUSY; + sbic->next_target = tgt; + sbic_attempt_selection(sbic); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +sbic_attempt_selection(sbic) + sbic_softc_t sbic; +{ + target_info_t *tgt; + sbic_padded_regmap_t *regs; + register unsigned char val; + register int out_count; + + regs = sbic->regs; + tgt = sbic->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * We own the bus now.. unless we lose arbitration + */ + sbic->active_target = tgt; + + /* Try to avoid reselect collisions */ + GET_SBIC_asr(regs,val); + if (val & SBIC_ASR_INT) + return; + + /* + * Init bus state variables + */ + sbic->script = tgt->transient_state.script; + sbic->error_handler = tgt->transient_state.handler; + sbic->done = SCSI_RET_IN_PROGRESS; + + sbic->out_count = 0; + sbic->in_count = 0; + + /* Define how the identify msg should be built */ + GET_SBIC_rselid(regs, val); + val &= ~(SBIC_RID_MASK|SBIC_RID_ER); + /* the enable reselection bit is used to build the identify msg */ + if (tgt->transient_state.identify != 0xff) + val |= (tgt->transient_state.identify & SCSI_IFY_ENABLE_DISCONNECT) << 1; + SET_SBIC_rselid(regs, val); + SET_SBIC_tlun(regs, tgt->lun); + + /* + * Start the chip going + */ + out_count = (*sbic->dma_ops->start_cmd)(sbic->dma_state, tgt); + SBIC_TC_PUT(regs, out_count); + + val = tgt->target_id; + if (tgt->transient_state.in_count) + val |= SBIC_SID_FROM_SCSI; + SET_SBIC_selid(regs, val); + + SET_SBIC_timo(regs,SBIC_TIMEOUT(250,SBIC_CLOCK_FREQUENCY)); + + SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period)); + + /* ugly little help for compiler */ +#define command out_count + if (tgt->flags & TGT_DID_SYNCH) { + command = (tgt->transient_state.identify == 0xff) ? + SBIC_CMD_SEL_XFER : + SBIC_CMD_SEL_ATN_XFER; /*preferred*/ + } else if (tgt->flags & TGT_TRY_SYNCH) + command = SBIC_CMD_SEL_ATN; + else + command = SBIC_CMD_SEL_XFER; + + /* load 10 bytes anyways, the chip knows how much to use */ + SBIC_LOAD_COMMAND(regs, tgt->cmd_ptr, 10); + + /* Try to avoid reselect collisions */ + GET_SBIC_asr(regs,val); + if (val & SBIC_ASR_INT) + return; + + SET_SBIC_cmd_phase(regs, 0); /* not a resume */ + SET_SBIC_cmd(regs, command); +#undef command +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +sbic_intr(unit, spllevel) + spl_t spllevel; +{ + register sbic_softc_t sbic; + register script_t scp; + register int asr, csr, pha; + register sbic_padded_regmap_t *regs; +#if MAPPABLE + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SBIC_intr(unit,spllevel); +#endif /*MAPPABLE*/ + + sbic = sbic_softc[unit]; + regs = sbic->regs; + + LOG(5,"\n\tintr"); + + /* drop spurious interrupts */ + GET_SBIC_asr(regs, asr); + if ((asr & SBIC_ASR_INT) == 0) + return; + + /* collect ephemeral information */ + GET_SBIC_cmd_phase(regs, pha); + GET_SBIC_csr(regs, csr); + +TR(csr);TR(asr);TR(pha);TRCHECK + + /* XXX verify this is indeed the case for a SCSI RST asserted */ + if ((csr & SBIC_CSR_CAUSE) == SBIC_CSR_RESET) + return sbic_bus_reset(sbic); + + /* we got an interrupt allright */ + if (sbic->active_target) + sbic->wd.watchdog_state = SCSI_WD_ACTIVE; + + splx(spllevel); /* drop priority */ + + if ((sbic->state & SBIC_STATE_TARGET) || + (csr == SBIC_CSR_RSLT_AM) || (csr == SBIC_CSR_RSLT_NOAM) || + (csr == SBIC_CSR_SLT) || (csr == SBIC_CSR_SLT_ATN)) + return sbic_target_intr(sbic); + + /* + * In attempt_selection() we gave the select command even if + * the chip might have been reconnected already. + */ + if ((csr == SBIC_CSR_RSLT_NI) || (csr == SBIC_CSR_RSLT_IFY)) + return sbic_reconnect(sbic, csr, pha); + + /* + * Check for parity errors + */ + if (asr & SBIC_ASR_PE) { + char *msg; +printf("{PE %x,%x}", asr, pha); + + msg = "SCSI bus parity error"; + /* all we can do is to throw a reset on the bus */ + printf( "sbic%d: %s%s", sbic - sbic_softc_data, msg, + ", attempting recovery.\n"); + sbic_reset(regs, FALSE); + return; + } + + if ((scp = sbic->script) == 0) /* sanity */ + return; + + LOG(6,"match"); + if (SCRIPT_MATCH(csr,pha,scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(sbic, csr, pha)) { + sbic->script = scp + 1; + } + } else + return (*sbic->error_handler)(sbic, csr, pha); +} + +sbic_target_intr() +{ + panic("SBIC: TARGET MODE !!!\n"); +} + +/* + * Routines that the interrupt code might switch to + */ + +boolean_t +sbic_end(sbic, csr, pha) + register sbic_softc_t sbic; +{ + register target_info_t *tgt; + register io_req_t ior; + + LOG(8,"end"); + + tgt = sbic->active_target; + if ((tgt->done = sbic->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + sbic->script = 0; + + if (sbic->wd.nactive-- == 1) + sbic->wd.watchdog_state = SCSI_WD_INACTIVE; + + sbic_release_bus(sbic); + + if (ior = tgt->ior) { + (*sbic->dma_ops->end_cmd)(sbic->dma_state, tgt, ior); + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + } + + return FALSE; +} + +boolean_t +sbic_release_bus(sbic) + register sbic_softc_t sbic; +{ + boolean_t ret = TRUE; + + LOG(9,"release"); + if (sbic->state & SBIC_STATE_COLLISION) { + + LOG(0xB,"collided"); + sbic->state &= ~SBIC_STATE_COLLISION; + sbic_attempt_selection(sbic); + + } else if (queue_empty(&sbic->waiting_targets)) { + + sbic->state &= ~SBIC_STATE_BUSY; + sbic->active_target = 0; + sbic->script = 0; + ret = FALSE; + + } else { + + LOG(0xC,"dequeue"); + sbic->next_target = (target_info_t *) + dequeue_head(&sbic->waiting_targets); + sbic_attempt_selection(sbic); + } + return ret; +} + +boolean_t +sbic_get_status(sbic, csr, pha) + register sbic_softc_t sbic; +{ + register sbic_padded_regmap_t *regs = sbic->regs; + register scsi2_status_byte_t status; + int len; + io_req_t ior; + register target_info_t *tgt = sbic->active_target; + + LOG(0xD,"get_status"); +TRWRAP + + sbic->state &= ~SBIC_STATE_DMA_IN; + + /* + * Get the status byte + */ + GET_SBIC_tlun(regs, status.bits); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(sbic->active_target, SCSI_ERR_STATUS, status.bits, 0); + sbic->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + sbic->done = SCSI_RET_SUCCESS; + + /* Tell DMA engine we are done */ + (*sbic->dma_ops->end_xfer)(sbic->dma_state, tgt, tgt->transient_state.in_count); + + return sbic_end(sbic, csr, pha); + +} + +#if 0 + +boolean_t +sbic_dma_in(sbic, csr, ir) + register sbic_softc_t sbic; +{ + register target_info_t *tgt; + register sbic_padded_regmap_t *regs = sbic->regs; + register int count; + unsigned char ff = regs->sbic_flags; + + LOG(0xE,"dma_in"); + tgt = sbic->active_target; + + sbic->state |= SBIC_STATE_DMA_IN; + + count = (*sbic->dma_ops->start_datain)(sbic->dma_state, tgt); + SBIC_TC_PUT(regs, count); + + if ((sbic->in_count = count) == tgt->transient_state.in_count) + return TRUE; + regs->sbic_cmd = sbic->script->command; + sbic->script = sbic_script_restart_data_in; + return FALSE; +} + +sbic_dma_in_r(sbic, csr, ir) + register sbic_softc_t sbic; +{ + register target_info_t *tgt; + register sbic_padded_regmap_t *regs = sbic->regs; + register int count; + boolean_t advance_script = TRUE; + + + LOG(0xE,"dma_in"); + tgt = sbic->active_target; + + sbic->state |= SBIC_STATE_DMA_IN; + + if (sbic->in_count == 0) { + /* + * Got nothing yet, we just reconnected. + */ + register int avail; + + /* + * Rather than using the messy RFB bit in cnfg2 + * (which only works for synch xfer anyways) + * we just bump up the dma offset. We might + * endup with one more interrupt at the end, + * so what. + * This is done in sbic_err_disconn(), this + * way dma (of msg bytes too) is always aligned + */ + + count = (*sbic->dma_ops->restart_datain_1) + (sbic->dma_state, tgt); + + /* common case of 8k-or-less read ? */ + advance_script = (tgt->transient_state.in_count == count); + + } else { + + /* + * We received some data. + */ + register int offset, xferred; + + /* + * Problem: sometimes we get a 'spurious' interrupt + * right after a reconnect. It goes like disconnect, + * reconnect, dma_in_r, here but DMA is still rolling. + * Since there is no good reason we got here to begin with + * we just check for the case and dismiss it: we should + * get another interrupt when the TC goes to zero or the + * target disconnects. + */ + SBIC_TC_GET(regs,xferred); + if (xferred != 0) + return FALSE; + + xferred = sbic->in_count - xferred; + assert(xferred > 0); + + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + + count = (*sbic->dma_ops->restart_datain_2) + (sbic->dma_state, tgt, xferred); + + sbic->in_count = count; + SBIC_TC_PUT(regs, count); + regs->sbic_cmd = sbic->script->command; + + (*sbic->dma_ops->restart_datain_3) + (sbic->dma_state, tgt); + + /* last chunk ? */ + if (count == tgt->transient_state.in_count) + sbic->script++; + + return FALSE; + } + + sbic->in_count = count; + SBIC_TC_PUT(regs, count); + + if (!advance_script) { + regs->sbic_cmd = sbic->script->command; + } + return advance_script; +} + + +/* send data to target. Only called to start the xfer */ + +boolean_t +sbic_dma_out(sbic, csr, ir) + register sbic_softc_t sbic; +{ + register sbic_padded_regmap_t *regs = sbic->regs; + register int reload_count; + register target_info_t *tgt; + int command; + + LOG(0xF,"dma_out"); + + SBIC_TC_GET(regs, reload_count); + sbic->extra_count = regs->sbic_flags & SBIC_FLAGS_FIFO_CNT; + reload_count += sbic->extra_count; + SBIC_TC_PUT(regs, reload_count); + sbic->state &= ~SBIC_STATE_DMA_IN; + + tgt = sbic->active_target; + + command = sbic->script->command; + + if ((sbic->out_count = reload_count) >= + tgt->transient_state.out_count) + sbic->script++; + else + sbic->script = sbic_script_restart_data_out; + + if ((*sbic->dma_ops->start_dataout) + (sbic->dma_state, tgt, ®s->sbic_cmd, command)) { + regs->sbic_cmd = command; + } + + return FALSE; +} + +/* send data to target. Called in two different ways: + (a) to restart a big transfer and + (b) after reconnection + */ +boolean_t +sbic_dma_out_r(sbic, csr, ir) + register sbic_softc_t sbic; +{ + register sbic_padded_regmap_t *regs = sbic->regs; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count; + + + LOG(0xF,"dma_out"); + + tgt = sbic->active_target; + sbic->state &= ~SBIC_STATE_DMA_IN; + + if (sbic->out_count == 0) { + /* + * Nothing committed: we just got reconnected + */ + count = (*sbic->dma_ops->restart_dataout_1) + (sbic->dma_state, tgt); + + /* is this the last chunk ? */ + advance_script = (tgt->transient_state.out_count == count); + } else { + /* + * We sent some data. + */ + register int offset, xferred; + + SBIC_TC_GET(regs,count); + + /* see comment above */ + if (count) { + return FALSE; + } + + count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); + count -= sbic->extra_count; + xferred = sbic->out_count - count; + assert(xferred > 0); + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + count = (*sbic->dma_ops->restart_dataout_2) + (sbic->dma_state, tgt, xferred); + + /* last chunk ? */ + if (tgt->transient_state.out_count == count) + goto quickie; + + sbic->out_count = count; + + sbic->extra_count = (*sbic->dma_ops->restart_dataout_3) + (sbic->dma_state, tgt, ®s->sbic_fifo); + SBIC_TC_PUT(regs, count); + regs->sbic_cmd = sbic->script->command; + + (*sbic->dma_ops->restart_dataout_4)(sbic->dma_state, tgt); + + return FALSE; + } + +quickie: + sbic->extra_count = (*sbic->dma_ops->restart_dataout_3) + (sbic->dma_state, tgt, ®s->sbic_fifo); + + sbic->out_count = count; + + SBIC_TC_PUT(regs, count); + + if (!advance_script) { + regs->sbic_cmd = sbic->script->command; + } + return advance_script; +} +#endif /*0*/ + +boolean_t +sbic_dosynch(sbic, csr, pha) + register sbic_softc_t sbic; + register unsigned char csr, pha; +{ + register sbic_padded_regmap_t *regs = sbic->regs; + register unsigned char c; + int i, per, offs; + register target_info_t *tgt; + + /* + * Try synch negotiation + * Phase is MSG_OUT here. + */ + tgt = sbic->active_target; + +#if 0 + regs->sbic_cmd = SBIC_CMD_FLUSH; + delay(2); + + per = sbic_min_period; + if (BGET(scsi_no_synchronous_xfer,sbic->sc->masterno,tgt->target_id)) + offs = 0; + else + offs = sbic_max_offset; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + regs->sbic_fifo = SCSI_EXTENDED_MESSAGE; + regs->sbic_fifo = 3; + regs->sbic_fifo = SCSI_SYNC_XFER_REQUEST; + regs->sbic_fifo = sbic_to_scsi_period(regs,sbic_min_period); + regs->sbic_fifo = offs; + regs->sbic_cmd = SBIC_CMD_XFER_INFO; + csr = sbic_wait(regs, SBIC_CSR_INT); + ir = regs->sbic_intr; + + if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) + gimmeabreak(); + + regs->sbic_cmd = SBIC_CMD_XFER_INFO; + csr = sbic_wait(regs, SBIC_CSR_INT); + ir = regs->sbic_intr; + + while ((regs->sbic_flags & SBIC_FLAGS_FIFO_CNT) > 0) + c = regs->sbic_fifo; /* see what it says */ + + if (c == SCSI_MESSAGE_REJECT) { + printf(" did not like SYNCH xfer "); + + /* Tk50s get in trouble with ATN, sigh. */ + regs->sbic_cmd = SBIC_CMD_CLR_ATN; + + goto cmd; + } + + /* + * Receive the rest of the message + */ + regs->sbic_cmd = SBIC_CMD_MSG_ACPT; + sbic_wait(regs, SBIC_CSR_INT); + ir = regs->sbic_intr; + + if (c != SCSI_EXTENDED_MESSAGE) + gimmeabreak(); + + regs->sbic_cmd = SBIC_CMD_XFER_INFO; + sbic_wait(regs, SBIC_CSR_INT); + c = regs->sbic_intr; + if (regs->sbic_fifo != 3) + panic("sbic_dosynch"); + + for (i = 0; i < 3; i++) { + regs->sbic_cmd = SBIC_CMD_MSG_ACPT; + sbic_wait(regs, SBIC_CSR_INT); + c = regs->sbic_intr; + + regs->sbic_cmd = SBIC_CMD_XFER_INFO; + sbic_wait(regs, SBIC_CSR_INT); + c = regs->sbic_intr;/*ack*/ + c = regs->sbic_fifo; + + if (i == 1) tgt->sync_period = scsi_period_to_sbic(regs,c); + if (i == 2) tgt->sync_offset = c; + } + +cmd: + regs->sbic_cmd = SBIC_CMD_MSG_ACPT; + csr = sbic_wait(regs, SBIC_CSR_INT); + c = regs->sbic_intr; + + /* phase should normally be command here */ + if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) { + /* test unit ready or what ? */ + regs->sbic_fifo = 0; + regs->sbic_fifo = 0; + regs->sbic_fifo = 0; + regs->sbic_fifo = 0; + regs->sbic_fifo = 0; + regs->sbic_fifo = 0; + SBIC_TC_PUT(regs,0xff); + regs->sbic_cmd = SBIC_CMD_XFER_PAD; /*0x98*/ + csr = sbic_wait(regs, SBIC_CSR_INT); + ir = regs->sbic_intr;/*ack*/ + } + +status: + if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) + gimmeabreak(); + +#endif + return TRUE; +} + +/* + * The bus was reset + */ +sbic_bus_reset(sbic) + register sbic_softc_t sbic; +{ + register sbic_padded_regmap_t *regs = sbic->regs; + + LOG(0x1d,"bus_reset"); + + /* + * Clear bus descriptor + */ + sbic->script = 0; + sbic->error_handler = 0; + sbic->active_target = 0; + sbic->next_target = 0; + sbic->state &= SBIC_STATE_AM_MODE; /* save this one bit only */ + queue_init(&sbic->waiting_targets); + sbic->wd.nactive = 0; + (void) sbic_reset(regs, TRUE); + + printf("sbic: (%d) bus reset ", ++sbic->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (sbic->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(sbic->sc); +} + +/* + * Disconnect/reconnect mode ops + */ + +/* save all relevant data, free the BUS */ +boolean_t +sbic_disconnected(sbic, csr, pha) + register sbic_softc_t sbic; + register unsigned char csr, pha; + +{ + register target_info_t *tgt; + + LOG(0x11,"disconnected"); + + tgt = sbic->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = sbic->error_handler; + /* anything else was saved in sbic_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + sbic_release_bus(sbic); + + return FALSE; +} + +/* See who reconnected, restore BUS */ +boolean_t +sbic_reconnect(sbic, csr, ir) + register sbic_softc_t sbic; + register unsigned char csr, ir; + +{ + register target_info_t *tgt; + sbic_padded_regmap_t *regs; + int id, pha; + + LOG(0x12,"reconnect"); + /* + * See if this reconnection collided with a selection attempt + */ + if (sbic->state & SBIC_STATE_BUSY) + sbic->state |= SBIC_STATE_COLLISION; + + sbic->state |= SBIC_STATE_BUSY; + + /* find tgt */ + regs = sbic->regs; + GET_SBIC_rselid(regs,id); + + id &= 0x7; + + if ((sbic->state & SBIC_STATE_AM) == 0) { + /* Must pick the identify */ + pha = 0x44; + } else + pha = 0x45; + + tgt = sbic->sc->target[id]; + if (id > 7 || tgt == 0) panic("sbic_reconnect"); + + /* synch things*/ + SET_SBIC_syn(regs,SBIC_SYN(tgt->sync_offset,tgt->sync_period)); + + PRINT(("{R%d}", id)); + if (sbic->state & SBIC_STATE_COLLISION) + PRINT(("[B %d-%d]", sbic->active_target->target_id, id)); + + LOG(0x80+id,0); + + sbic->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + sbic->script = tgt->transient_state.script; + sbic->error_handler = tgt->transient_state.handler; + sbic->in_count = 0; + sbic->out_count = 0; + +set counter and setup dma, then + + /* Resume the command now */ + SET_SBIC_cmd_phase(regs, pha); + SET_SBIC_cmd(regs, SBIC_CMD_SEL_XFER); + + return FALSE; +} + +TILL HERE + +/* + * Error handlers + */ + +/* + * Fall-back error handler. + */ +sbic_err_generic(sbic, csr, ir) + register sbic_softc_t sbic; +{ + LOG(0x13,"err_generic"); + + /* handle non-existant or powered off devices here */ + if ((ir == SBIC_INT_DISC) && + (sbic_isa_select(sbic->cmd_was)) && + (SBIC_SS(sbic->ss_was) == 0)) { + /* Powered off ? */ + if (sbic->active_target->flags & TGT_FULLY_PROBED) + sbic->active_target->flags = 0; + sbic->done = SCSI_RET_DEVICE_DOWN; + sbic_end(sbic, csr, ir); + return; + } + + switch (SCSI_PHASE(csr)) { + case SCSI_PHASE_STATUS: + if (sbic->script[-1].condition == SCSI_PHASE_STATUS) { + /* some are just slow to get out.. */ + } else + sbic_err_to_status(sbic, csr, ir); + return; + break; + case SCSI_PHASE_DATAI: + if (sbic->script->condition == SCSI_PHASE_STATUS) { +/* printf("{P}");*/ + return; + } + break; + case SCSI_PHASE_DATAO: + if (sbic->script->condition == SCSI_PHASE_STATUS) { + /* + * See comment above. Actually seen on hitachis. + */ +/* printf("{P}");*/ + return; + } + } + gimmeabreak(); +} + +/* + * Handle disconnections as exceptions + */ +sbic_err_disconn(sbic, csr, ir) + register sbic_softc_t sbic; + register unsigned char csr, ir; +{ + register sbic_padded_regmap_t *regs; + register target_info_t *tgt; + int count; + boolean_t callback = FALSE; + + LOG(0x16,"err_disconn"); + + if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) + return sbic_err_generic(sbic, csr, ir); + + regs = sbic->regs; + tgt = sbic->active_target; + + switch (sbic->script->condition) { + case SCSI_PHASE_DATAO: + LOG(0x1b,"+DATAO"); + if (sbic->out_count) { + register int xferred, offset; + + SBIC_TC_GET(regs,xferred); /* temporary misnomer */ + xferred += regs->sbic_flags & SBIC_FLAGS_FIFO_CNT; + xferred -= sbic->extra_count; + xferred = sbic->out_count - xferred; /* ok now */ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + callback = (*sbic->dma_ops->disconn_1) + (sbic->dma_state, tgt, xferred); + + } else { + + callback = (*sbic->dma_ops->disconn_2) + (sbic->dma_state, tgt); + + } + sbic->extra_count = 0; + tgt->transient_state.script = sbic_script_restart_data_out; + break; + + + case SCSI_PHASE_DATAI: + LOG(0x17,"+DATAI"); + if (sbic->in_count) { + register int offset, xferred; + + SBIC_TC_GET(regs,count); + xferred = sbic->in_count - count; + assert(xferred > 0); + +if (regs->sbic_flags & 0xf) +printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + + callback = (*sbic->dma_ops->disconn_3) + (sbic->dma_state, tgt, xferred); + + tgt->transient_state.script = sbic_script_restart_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } + tgt->transient_state.script = sbic->script; + break; + + case SCSI_PHASE_STATUS: + /* will have to restart dma */ + SBIC_TC_GET(regs,count); + if (sbic->state & SBIC_STATE_DMA_IN) { + register int offset, xferred; + + LOG(0x1a,"+STATUS+R"); + + xferred = sbic->in_count - count; + assert(xferred > 0); + +if (regs->sbic_flags & 0xf) +printf("{Xf %x,%x,%x}", xferred, sbic->in_count, regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); + tgt->transient_state.in_count -= xferred; +/* assert(tgt->transient_state.in_count > 0);*/ + + callback = (*sbic->dma_ops->disconn_4) + (sbic->dma_state, tgt, xferred); + + tgt->transient_state.script = sbic_script_restart_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + /* add what's left in the fifo */ + count += (regs->sbic_flags & SBIC_FLAGS_FIFO_CNT); + /* take back the extra we might have added */ + count -= sbic->extra_count; + /* ..and drop that idea */ + sbic->extra_count = 0; + + LOG(0x19,"+STATUS+W"); + + + if ((count == 0) && (tgt->transient_state.out_count == sbic->out_count)) { + /* all done */ + tgt->transient_state.script = sbic->script; + tgt->transient_state.out_count = 0; + } else { + register int xferred, offset; + + /* how much we xferred */ + xferred = sbic->out_count - count; + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + callback = (*sbic->dma_ops->disconn_5) + (sbic->dma_state,tgt,xferred); + + tgt->transient_state.script = sbic_script_restart_data_out; + } + sbic->out_count = 0; + } + break; + default: + gimmeabreak(); + return; + } + sbic_msg_in(sbic,csr,ir); + sbic->script = sbic_script_disconnect; + regs->sbic_cmd = SBIC_CMD_XFER_INFO|SBIC_CMD_DMA; + if (callback) + (*sbic->dma_ops->disconn_callback)(sbic->dma_state, tgt); +} + +/* + * Watchdog + * + * We know that some (name withdrawn) disks get + * stuck in the middle of dma phases... + */ +sbic_reset_scsibus(sbic) + register sbic_softc_t sbic; +{ + register target_info_t *tgt = sbic->active_target; + register sbic_padded_regmap_t *regs = sbic->regs; + register int ir; + + if (scsi_debug && tgt) { + int dmalen; + SBIC_TC_GET(sbic->regs,dmalen); + printf("Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, tgt->transient_state.out_count, + sbic->in_count, sbic->out_count, + dmalen); + } + ir = regs->sbic_intr; + if ((ir & SBIC_INT_RESEL) && (SCSI_PHASE(regs->sbic_csr) == SCSI_PHASE_MSG_IN)) { + /* getting it out of the woods is a bit tricky */ + spl_t s = splbio(); + + (void) sbic_reconnect(sbic, regs->sbic_csr, ir); + sbic_wait(regs, SBIC_CSR_INT); + ir = regs->sbic_intr; + regs->sbic_cmd = SBIC_CMD_MSG_ACPT; + splx(s); + } else { + regs->sbic_cmd = SBIC_CMD_BUS_RESET; + delay(35); + } +} + +#endif NSBIC > 0 + +#endif 0 diff --git a/scsi/adapters/scsi_5380.h b/scsi/adapters/scsi_5380.h new file mode 100644 index 0000000..12be922 --- /dev/null +++ b/scsi/adapters/scsi_5380.h @@ -0,0 +1,126 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_5380.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 5/91 + * + * Defines for the NCR 5380 (SCSI chip), aka Am5380 + */ + +/* + * Register map + */ + +typedef struct { + volatile unsigned char sci_data; /* r: Current data */ +#define sci_odata sci_data /* w: Out data */ + volatile unsigned char sci_icmd; /* rw: Initiator command */ + volatile unsigned char sci_mode; /* rw: Mode */ + volatile unsigned char sci_tcmd; /* rw: Target command */ + volatile unsigned char sci_bus_csr; /* r: Bus Status */ +#define sci_sel_enb sci_bus_csr /* w: Select enable */ + volatile unsigned char sci_csr; /* r: Status */ +#define sci_dma_send sci_csr /* w: Start dma send data */ + volatile unsigned char sci_idata; /* r: Input data */ +#define sci_trecv sci_idata /* w: Start dma receive, target */ + volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */ +#define sci_irecv sci_iack /* w: Start dma receive, initiator */ +} sci_regmap_t; + + +/* + * Initiator command register + */ + +#define SCI_ICMD_DATA 0x01 /* rw: Assert data bus */ +#define SCI_ICMD_ATN 0x02 /* rw: Assert ATN signal */ +#define SCI_ICMD_SEL 0x04 /* rw: Assert SEL signal */ +#define SCI_ICMD_BSY 0x08 /* rw: Assert BSY signal */ +#define SCI_ICMD_ACK 0x10 /* rw: Assert ACK signal */ +#define SCI_ICMD_LST 0x20 /* r: Lost arbitration */ +#define SCI_ICMD_DIFF SCI_ICMD_LST /* w: Differential cable */ +#define SCI_ICMD_AIP 0x40 /* r: Arbitration in progress */ +#define SCI_ICMD_TEST SCI_ICMD_AIP /* w: Test mode */ +#define SCI_ICMD_RST 0x80 /* rw: Assert RST signal */ + + +/* + * Mode register + */ + +#define SCI_MODE_ARB 0x01 /* rw: Start arbitration */ +#define SCI_MODE_DMA 0x02 /* rw: Enable DMA xfers */ +#define SCI_MODE_MONBSY 0x04 /* rw: Monitor BSY signal */ +#define SCI_MODE_DMA_IE 0x08 /* rw: Enable DMA complete interrupt */ +#define SCI_MODE_PERR_IE 0x10 /* rw: Interrupt on parity errors */ +#define SCI_MODE_PAR_CHK 0x20 /* rw: Check parity */ +#define SCI_MODE_TARGET 0x40 /* rw: Target mode (Initiator if 0) */ +#define SCI_MODE_BLOCKDMA 0x80 /* rw: Block-mode DMA handshake (MBZ) */ + + +/* + * Target command register + */ + +#define SCI_TCMD_IO 0x01 /* rw: Assert I/O signal */ +#define SCI_TCMD_CD 0x02 /* rw: Assert C/D signal */ +#define SCI_TCMD_MSG 0x04 /* rw: Assert MSG signal */ +#define SCI_TCMD_PHASE_MASK 0x07 /* r: Mask for current bus phase */ +#define SCI_TCMD_REQ 0x08 /* rw: Assert REQ signal */ +#define SCI_TCMD_LAST_SENT 0x80 /* ro: Last byte was xferred + * (not on 5380/1) */ + +#define SCI_PHASE(x) SCSI_PHASE(x) + +/* + * Current (SCSI) Bus status + */ + +#define SCI_BUS_DBP 0x01 /* r: Data Bus parity */ +#define SCI_BUS_SEL 0x02 /* r: SEL signal */ +#define SCI_BUS_IO 0x04 /* r: I/O signal */ +#define SCI_BUS_CD 0x08 /* r: C/D signal */ +#define SCI_BUS_MSG 0x10 /* r: MSG signal */ +#define SCI_BUS_REQ 0x20 /* r: REQ signal */ +#define SCI_BUS_BSY 0x40 /* r: BSY signal */ +#define SCI_BUS_RST 0x80 /* r: RST signal */ + +#define SCI_CUR_PHASE(x) SCSI_PHASE((x)>>2) + +/* + * Bus and Status register + */ + +#define SCI_CSR_ACK 0x01 /* r: ACK signal */ +#define SCI_CSR_ATN 0x02 /* r: ATN signal */ +#define SCI_CSR_DISC 0x04 /* r: Disconnected (BSY==0) */ +#define SCI_CSR_PHASE_MATCH 0x08 /* r: Bus and SCI_TCMD match */ +#define SCI_CSR_INT 0x10 /* r: Interrupt request */ +#define SCI_CSR_PERR 0x20 /* r: Parity error */ +#define SCI_CSR_DREQ 0x40 /* r: DMA request */ +#define SCI_CSR_DONE 0x80 /* r: DMA count is zero */ + diff --git a/scsi/adapters/scsi_5380_hdw.c b/scsi/adapters/scsi_5380_hdw.c new file mode 100644 index 0000000..2fc7d89 --- /dev/null +++ b/scsi/adapters/scsi_5380_hdw.c @@ -0,0 +1,2423 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_5380_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 5380 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <sci.h> +#if NSCI > 0 +#include <platforms.h> + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <sys/syslog.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#ifdef VAXSTATION +#define PAD(n) char n[3] +#endif + +#include <scsi/adapters/scsi_5380.h> + +#ifdef PAD +typedef struct { + volatile unsigned char sci_data; /* r: Current data */ +/*#define sci_odata sci_data /* w: Out data */ + PAD(pad0); + + volatile unsigned char sci_icmd; /* rw: Initiator command */ + PAD(pad1); + + volatile unsigned char sci_mode; /* rw: Mode */ + PAD(pad2); + + volatile unsigned char sci_tcmd; /* rw: Target command */ + PAD(pad3); + + volatile unsigned char sci_bus_csr; /* r: Bus Status */ +/*#define sci_sel_enb sci_bus_csr /* w: Select enable */ + PAD(pad4); + + volatile unsigned char sci_csr; /* r: Status */ +/*#define sci_dma_send sci_csr /* w: Start dma send data */ + PAD(pad5); + + volatile unsigned char sci_idata; /* r: Input data */ +/*#define sci_trecv sci_idata /* w: Start dma receive, target */ + PAD(pad6); + + volatile unsigned char sci_iack; /* r: Interrupt Acknowledge */ +/*#define sci_irecv sci_iack /* w: Start dma receive, initiator */ + PAD(pad7); + +} sci_padded_regmap_t; +#else +typedef sci_regmap_t sci_padded_regmap_t; +#endif + +#ifdef VAXSTATION +#define check_memory(addr,dow) ((dow) ? wbadaddr(addr,4) : badaddr(addr,4)) + +/* vax3100 */ +#include <chips/vs42x_rb.h> +#define STC_5380_A VAX3100_STC_5380_A +#define STC_5380_B VAX3100_STC_5380_B +#define STC_DMAREG_OFF VAX3100_STC_DMAREG_OFF + +static int mem; /* mem++ seems to take approx 0.34 usecs */ +#define delay_1p2_us() {mem++;mem++;mem++;mem++;} +#define my_scsi_id(ctlr) (ka3100_scsi_id((ctlr))) +#endif /* VAXSTATION */ + + +#ifndef STC_5380_A /* cross compile check */ +typedef struct { + int sci_dma_dir, sci_dma_adr; +} *sci_dmaregs_t; +#define STC_DMAREG_OFF 0 +#define SCI_DMA_DIR_WRITE 0 +#define SCI_DMA_DIR_READ 1 +#define STC_5380_A 0 +#define STC_5380_B 0x100 +#define SCI_RAM_SIZE 0x10000 +#endif + +/* + * The 5380 can't tell you the scsi ID it uses, so + * unless there is another way use the defaults + */ +#ifndef my_scsi_id +#define my_scsi_id(ctlr) (scsi_initiator_id[(ctlr)]) +#endif + +/* + * Statically partition the DMA buffer between targets. + * This way we will eventually be able to attach/detach + * drives on-fly. And 18k/target is enough. + */ +#define PER_TGT_DMA_SIZE ((SCI_RAM_SIZE/7) & ~(sizeof(int)-1)) + +/* + * Round to 4k to make debug easier + */ +#define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12) +#define PER_TGT_BURST_SIZE (PER_TGT_BUFF_SIZE>>1) + +/* + * Macros to make certain things a little more readable + */ + +#define SCI_ACK(ptr,phase) (ptr)->sci_tcmd = (phase) +#define SCI_CLR_INTR(regs) {register int temp = regs->sci_iack;} + + +/* + * A script has a two parts: a pre-condition and an action. + * The first triggers error handling if not satisfied and in + * our case it is formed by the current bus phase and connected + * condition as per bus status bits. The action part is just a + * function pointer, invoked in a standard way. The script + * pointer is advanced only if the action routine returns TRUE. + * See sci_intr() for how and where this is all done. + */ + +typedef struct script { + int condition; /* expected state at interrupt */ + int (*action)(); /* action routine */ +} *script_t; + +#define SCRIPT_MATCH(cs,bs) (((bs)&SCI_BUS_BSY)|SCI_CUR_PHASE((bs))) + +#define SCI_PHASE_DISC 0x0 /* sort of .. */ + + +/* forward decls of script actions */ +boolean_t + sci_dosynch(), /* negotiate synch xfer */ + sci_dma_in(), /* get data from target via dma */ + sci_dma_out(), /* send data to target via dma */ + sci_get_status(), /* get status from target */ + sci_end_transaction(), /* all come to an end */ + sci_msg_in(), /* get disconnect message(s) */ + sci_disconnected(); /* current target disconnected */ +/* forward decls of error handlers */ +boolean_t + sci_err_generic(), /* generic error handler */ + sci_err_disconn(), /* when a target disconnects */ + gimmeabreak(); /* drop into the debugger */ + +int sci_reset_scsibus(); +boolean_t sci_probe_target(); + +scsi_ret_t sci_select_target(); + +#ifdef VAXSTATION +/* + * This should be somewhere else, and it was a + * mistake to share this buffer across SCSIs. + */ +struct dmabuffer { + volatile char *base; + char *sbrk; +} dmab[1]; + +volatile char * +sci_buffer_base(unit) +{ + return dmab[unit].base; +} + +sci_buffer_init(dmar, ram) + sci_dmaregs_t dmar; + volatile char *ram; +{ + dmar->sci_dma_rammode = SCI_RAM_EXPMODE; + dmab[0].base = dmab[0].sbrk = (char *) ram; + blkclr((char *) ram, SCI_RAM_SIZE); +} +char * +sci_buffer_sbrk(size) +{ + char *ret = dmab[0].sbrk; + + dmab[0].sbrk += size; + if ((dmab[0].sbrk - dmab[0].base) > SCI_RAM_SIZE) + panic("scialloc"); + return ret; +} + +#endif /* VAXSTATION */ + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) 5380 interface + */ +struct sci_softc { + watchdog_t wd; + sci_padded_regmap_t *regs; /* 5380 registers */ + sci_dmaregs_t dmar; /* DMA controller registers */ + volatile char *buff; /* DMA buffer memory (I/O space) */ + script_t script; + int (*error_handler)(); + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SCI_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SCI_STATE_TARGET 0x04 /* currently selected as target */ +#define SCI_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SCI_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char extra_byte; + + scsi_softc_t *sc; + target_info_t *active_target; + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + +} sci_softc_data[NSCI]; + +typedef struct sci_softc *sci_softc_t; + +sci_softc_t sci_softc[NSCI]; + +/* + * Definition of the controller for the auto-configuration program. + */ + +int sci_probe(), scsi_slave(), sci_go(), sci_intr(); +void scsi_attach(); + +vm_offset_t sci_std[NSCI] = { 0 }; +struct bus_device *sci_dinfo[NSCI*8]; +struct bus_ctlr *sci_minfo[NSCI]; +struct bus_driver sci_driver = + { sci_probe, scsi_slave, scsi_attach, sci_go, sci_std, "rz", sci_dinfo, + "sci", sci_minfo, BUS_INTR_B4_PROBE}; + +/* + * Scripts + */ +struct script +sci_script_data_in[] = { + { SCSI_PHASE_DATAI|SCI_BUS_BSY, sci_dma_in}, + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +sci_script_data_out[] = { + { SCSI_PHASE_DATAO|SCI_BUS_BSY, sci_dma_out}, + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +sci_script_cmd[] = { + { SCSI_PHASE_STATUS|SCI_BUS_BSY, sci_get_status}, + { SCSI_PHASE_MSG_IN|SCI_BUS_BSY, sci_end_transaction} +}, + +/* Synchronous transfer neg(oti)ation */ + +sci_script_try_synch[] = { + { SCSI_PHASE_MSG_OUT|SCI_BUS_BSY, sci_dosynch} +}, + +/* Disconnect sequence */ + +sci_script_disconnect[] = { + { SCI_PHASE_DISC, sci_disconnected} +}; + + + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + + +#define DEBUG +#ifdef DEBUG + +sci_state(base) + vm_offset_t base; +{ + sci_padded_regmap_t *regs; + sci_dmaregs_t dmar; + extern char *sci; + unsigned dmadr; + int cnt, i; + + if (base == 0) + base = (vm_offset_t)sci; + + for (i = 0; i < 2; i++) { + regs = (sci_padded_regmap_t*) (base + + (i ? STC_5380_B : STC_5380_A)); + dmar = (sci_dmaregs_t) ((char*)regs + STC_DMAREG_OFF); + SCI_DMADR_GET(dmar,dmadr); + SCI_TC_GET(dmar,cnt); + + db_printf("scsi%d: ph %x (sb %x), mode %x, tph %x, csr %x, cmd %x, ", + i, + (unsigned) SCI_CUR_PHASE(regs->sci_bus_csr), + (unsigned) regs->sci_bus_csr, + (unsigned) regs->sci_mode, + (unsigned) regs->sci_tcmd, + (unsigned) regs->sci_csr, + (unsigned) regs->sci_icmd); + db_printf("dma%c %x @ %x\n", + (dmar->sci_dma_dir) ? 'I' : 'O', cnt, dmadr); + } + return 0; +} +sci_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = sci_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n", + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +sci_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = sci_softc[unit]->sc->target[i]; + if (tgt) + sci_target_state(tgt); + } +} + +sci_script_state(unit) +{ + script_t spt = sci_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(sci_softc[unit]->error_handler, 1); + return 0; + +} + +#define PRINT(x) if (scsi_debug) printf x + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int sci_logpt; +char sci_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x24 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + sci_log[sci_logpt++] = (e); + if (sci_logpt == LOGSIZE) sci_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +sci_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = sci_logpt; i < LOGSIZE; i++) { + c = sci_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-%d", c & 0x7f); + } + db_printf("\n"); + return 0; +} + +sci_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /* TRACE */ +#define LOG(e,f) +#endif /* TRACE */ + +#else /* DEBUG */ +#define PRINT(x) +#define LOG(e,f) +#define TR(x) +#define TRCHECK +#define TRWRAP +#endif /* DEBUG */ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send an identify msg to each possible target on the bus + * except of course ourselves. + */ +sci_probe(reg, ui) + char *reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + sci_softc_t sci = &sci_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register sci_padded_regmap_t *regs; + spl_t s; + boolean_t did_banner = FALSE; + char *cmd_ptr; + static char *here = "sci_probe"; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + regs = (sci_padded_regmap_t *) (reg); + if (check_memory(regs, 0)) + return 0; + +#if notyet + /* Mappable version side */ + SCI_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor + */ + sci_softc[unit] = sci; + sci->regs = regs; + sci->dmar = (sci_dmaregs_t)(reg + STC_DMAREG_OFF); + sci->buff = sci_buffer_base(0); + + queue_init(&sci->waiting_targets); + + sc = scsi_master_alloc(unit, sci); + sci->sc = sc; + + sc->go = sci_go; + sc->probe = sci_probe_target; + sc->watchdog = scsi_watchdog; + sci->wd.reset = sci_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; /* unlimited */ +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + scsi_might_disconnect[unit] = 0; /* still true */ + + /* + * Reset chip + */ + s = splbio(); + sci_reset(sci, TRUE); + SCI_CLR_INTR(regs); + + /* + * Our SCSI id on the bus. + */ + + sc->initiator_id = my_scsi_id(unit); + printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + cmd_ptr = sci_buffer_sbrk(0); + for (target_id = 0; target_id < 8; target_id++) { + + register unsigned csr, dsr; + scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + if (sci_select_target( regs, sc->initiator_id, target_id, FALSE) == SCSI_RET_DEVICE_DOWN) { + SCI_CLR_INTR(regs); + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + /* should be command phase here: we selected wo ATN! */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD) + ; + + SCI_ACK(regs,SCSI_PHASE_CMD); + + /* build command in dma area */ + { + unsigned char *p = (unsigned char*) cmd_ptr; + + p[0] = SCSI_CMD_TEST_UNIT_READY; + p[1] = + p[2] = + p[3] = + p[4] = + p[5] = 0; + } + + sci_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr); + + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_STATUS) + ; + + SCI_ACK(regs,SCSI_PHASE_STATUS); + + sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + /* get cmd_complete message */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_IN) + ; + + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &i); + + /* check disconnected, clear all intr bits */ + while (regs->sci_bus_csr & SCI_BUS_BSY) + ; + SCI_ACK(regs,SCI_PHASE_DISC); + + SCI_CLR_INTR(regs); + + /* ... */ + + /* + * Found a target + */ + sci->ntargets++; + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(unit, target_id, sci); + + /* "virtual" address for our use */ + tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE); + /* "physical" address for dma engine */ + tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff); +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + } + } + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +sci_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + sci_softc_t sci = sci_softc[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + + /* "virtual" address for our use */ + tgt->cmd_ptr = sci_buffer_sbrk(PER_TGT_DMA_SIZE); + /* "physical" address for dma engine */ + tgt->dma_ptr = (char*)(tgt->cmd_ptr - sci->buff); +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + + +static sci_wait(preg, until) + volatile unsigned char *preg; +{ + int timeo = 1000000; + /* read it over to avoid bus glitches */ + while ( ((*preg & until) != until) || + ((*preg & until) != until) || + ((*preg & until) != until)) { + delay(1); + if (!timeo--) { + printf("sci_wait TIMEO with x%x\n", *preg); + break; + } + } + return *preg; +} + +scsi_ret_t +sci_select_target(regs, myid, id, with_atn) + register sci_padded_regmap_t *regs; + unsigned char myid, id; + boolean_t with_atn; +{ + register unsigned char bid, icmd; + scsi_ret_t ret = SCSI_RET_RETRY; + + if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL))) + return ret; + + /* for our purposes.. */ + myid = 1 << myid; + id = 1 << id; + + regs->sci_sel_enb = myid; /* if not there already */ + + regs->sci_odata = myid; + regs->sci_mode |= SCI_MODE_ARB; + /* AIP might not set if BSY went true after we checked */ + for (bid = 0; bid < 20; bid++) /* 20usec circa */ + if (regs->sci_icmd & SCI_ICMD_AIP) + break; + if ((regs->sci_icmd & SCI_ICMD_AIP) == 0) { + goto lost; + } + + delay(2); /* 2.2us arb delay */ + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + regs->sci_mode &= ~SCI_MODE_PAR_CHK; + bid = regs->sci_data; + + if ((bid & ~myid) > myid) { + goto lost; + } + if (regs->sci_icmd & SCI_ICMD_LST) { + goto lost; + } + + /* Won arbitration, enter selection phase now */ + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd |= (with_atn ? (SCI_ICMD_SEL|SCI_ICMD_ATN) : SCI_ICMD_SEL); + regs->sci_icmd = icmd; + + if (regs->sci_icmd & SCI_ICMD_LST) { + goto nosel; + } + + /* XXX a target that violates specs might still drive the bus XXX */ + /* XXX should put our id out, and after the delay check nothi XXX */ + /* XXX ng else is out there. XXX */ + + delay_1p2_us(); + + regs->sci_sel_enb = 0; + + regs->sci_odata = myid | id; + + icmd |= SCI_ICMD_BSY|SCI_ICMD_DATA; + regs->sci_icmd = icmd; + + regs->sci_mode &= ~SCI_MODE_ARB; /* 2 deskew delays, too */ + + icmd &= ~SCI_ICMD_BSY; + regs->sci_icmd = icmd; + + /* bus settle delay, 400ns */ + delay(0); /* too much ? */ + + regs->sci_mode |= SCI_MODE_PAR_CHK; + + { + register int timeo = 2500;/* 250 msecs in 100 usecs chunks */ + while ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) + if (--timeo > 0) + delay(100); + else { + goto nodev; + } + } + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL); + regs->sci_icmd = icmd; +/* regs->sci_sel_enb = myid;*/ /* looks like we should NOT have it */ + return SCSI_RET_SUCCESS; +nodev: + ret = SCSI_RET_DEVICE_DOWN; + regs->sci_sel_enb = myid; +nosel: + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_SEL|SCI_ICMD_ATN); + regs->sci_icmd = icmd; +lost: + bid = regs->sci_mode; + bid &= ~SCI_MODE_ARB; + bid |= SCI_MODE_PAR_CHK; + regs->sci_mode = bid; + + return ret; +} + +sci_data_out(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return count; + + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + icmd |= SCI_ICMD_DATA; + regs->sci_icmd = icmd; + regs->sci_odata = *data++; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~(SCI_ICMD_DATA|SCI_ICMD_ACK); + while ( (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ)) + ; + regs->sci_icmd = icmd; + if (--count > 0) + goto loop; + return 0; +} + +sci_data_in(regs, phase, count, data) + register sci_padded_regmap_t *regs; + unsigned char *data; +{ + register unsigned char icmd; + + /* ..checks.. */ + + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); +loop: + if (SCI_CUR_PHASE(regs->sci_bus_csr) != phase) + return count; + + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + *data++ = regs->sci_data; + icmd |= SCI_ICMD_ACK; + regs->sci_icmd = icmd; + + icmd &= ~SCI_ICMD_ACK; + while ( (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ) && + (regs->sci_bus_csr & SCI_BUS_REQ)) + ; + regs->sci_icmd = icmd; + if (--count > 0) + goto loop; + return 0; + +} + +sci_reset(sci, quickly) + sci_softc_t sci; + boolean_t quickly; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dma = sci->dmar; + int dummy; + + regs->sci_icmd = SCI_ICMD_TEST; /* don't drive outputs */ + regs->sci_icmd = SCI_ICMD_TEST|SCI_ICMD_RST; + delay(25); + regs->sci_icmd = 0; + + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_PERR_IE; + regs->sci_tcmd = SCI_PHASE_DISC; /* make sure we do not miss transition */ + regs->sci_sel_enb = 0; + + /* idle the dma controller */ + dma->sci_dma_adr = 0; + dma->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_TC_PUT(dma,0); + + /* clear interrupt (two might be queued?) */ + SCI_CLR_INTR(regs); + SCI_CLR_INTR(regs); + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call sci_bus_reset(). + */ + regs->sci_icmd = SCI_ICMD_RST; + +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +sci_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + sci_softc_t sci; + register spl_t s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + sci = (sci_softc_t)tgt->hw_state; + + /* + * We cannot do real DMA. + */ +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + if (tgt->ior) + fdma_map(&tgt->fdma, tgt->ior); +#endif /*MACH_KERNEL*/ + + if ((tgt->cur_cmd == SCSI_CMD_WRITE) || + (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ + io_req_t ior = tgt->ior; + register int len = ior->io_count; + + tgt->transient_state.out_count = len; + + if (len > PER_TGT_BUFF_SIZE) + len = PER_TGT_BUFF_SIZE; + bcopy( ior->io_data, + tgt->cmd_ptr + cmd_count, + len); + tgt->transient_state.copy_count = len; + + /* avoid leaks */ + if (len < tgt->block_size) { + bzero( tgt->cmd_ptr + cmd_count + len, + tgt->block_size - len); + tgt->transient_state.out_count = tgt->block_size; + } + } else { + tgt->transient_state.out_count = 0; + tgt->transient_state.copy_count = 0; + } + + tgt->transient_state.cmd_count = cmd_count; + + disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); + disconn = disconn && (sci->ntargets > 1); + disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? sci_err_disconn : sci_err_generic; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(0x13,"readop"); + scp = sci_script_data_in; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x14,"writeop"); + scp = sci_script_data_out; + break; + case SCSI_CMD_INQUIRY: + /* This is likely the first thing out: + do the synch neg if so */ + if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) { + scp = sci_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + break; + } + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ + case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + scp = sci_script_data_in; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ + tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd); + tgt->transient_state.out_count = + cmd_count - tgt->transient_state.cmd_count; + scp = sci_script_data_out; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_TEST_UNIT_READY: + /* + * Do the synch negotiation here, unless prohibited + * or done already + */ + if (tgt->flags & TGT_DID_SYNCH) { + scp = sci_script_cmd; + } else { + scp = sci_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + cmd_only = FALSE; + } + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + scp = sci_script_cmd; + } + + tgt->transient_state.script = scp; + tgt->transient_state.handler = handler; + tgt->transient_state.identify = (cmd_only) ? 0xff : + (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT : + SCSI_IDENTIFY); + + if (in_count) + tgt->transient_state.in_count = + (in_count < tgt->block_size) ? tgt->block_size : in_count; + else + tgt->transient_state.in_count = 0; + tgt->transient_state.dma_offset = 0; + + /* + * See if another target is currently selected on + * this SCSI bus, e.g. lock the sci structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + * XXX here and everywhere, locks! + */ + /* + * Protection viz reconnections makes it tricky. + */ +/* s = splbio();*/ + s = splhigh(); + + if (sci->wd.nactive++ == 0) + sci->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (sci->state & SCI_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + enqueue_tail(&sci->waiting_targets, (queue_entry_t) tgt); + } else { + /* + * It is down to at most two contenders now, + * we will treat reconnections same as selections + * and let the scsi-bus arbitration process decide. + */ + sci->state |= SCI_STATE_BUSY; + sci->next_target = tgt; + sci_attempt_selection(sci); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +sci_attempt_selection(sci) + sci_softc_t sci; +{ + target_info_t *tgt; + register int out_count; + sci_padded_regmap_t *regs; + sci_dmaregs_t dmar; + register int cmd; + boolean_t ok; + scsi_ret_t ret; + + regs = sci->regs; + dmar = sci->dmar; + tgt = sci->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + */ + sci->active_target = tgt; + + /* reselection pending ? */ + if ((regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL)) && + (regs->sci_bus_csr & (SCI_BUS_BSY|SCI_BUS_SEL))) + return; + + sci->script = tgt->transient_state.script; + sci->error_handler = tgt->transient_state.handler; + sci->done = SCSI_RET_IN_PROGRESS; + + sci->in_count = 0; + sci->out_count = 0; + sci->extra_byte = 0; + + /* + * This is a bit involved, but the bottom line is we want to + * know after we selected with or w/o ATN if the selection + * went well (ret) and if it is (ok) to send the command. + */ + ok = TRUE; + if (tgt->flags & TGT_DID_SYNCH) { + if (tgt->transient_state.identify == 0xff) { + /* Select w/o ATN */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, FALSE); + } else { + /* Select with ATN */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, TRUE); + if (ret == SCSI_RET_SUCCESS) { + register unsigned char icmd; + + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT) + ; + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd &= ~SCI_ICMD_ATN; + regs->sci_icmd = icmd; + SCI_ACK(regs,SCSI_PHASE_MSG_OUT); + ok = (sci_data_out(regs, SCSI_PHASE_MSG_OUT, + 1, &tgt->transient_state.identify) == 0); + } + } + } else if (tgt->flags & TGT_TRY_SYNCH) { + /* Select with ATN, do the synch xfer neg */ + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, TRUE); + if (ret == SCSI_RET_SUCCESS) { + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_MSG_OUT) + ; + ok = sci_dosynch( sci, regs->sci_csr, regs->sci_bus_csr); + } + } else { + ret = sci_select_target(regs, sci->sc->initiator_id, + tgt->target_id, FALSE); + } + + if (ret == SCSI_RET_DEVICE_DOWN) { + sci->done = ret; + sci_end(sci, regs->sci_csr, regs->sci_bus_csr); + return; + } + if ((ret != SCSI_RET_SUCCESS) || !ok) + return; + +/* time this out or do it via dma !! */ + while (SCI_CUR_PHASE(regs->sci_bus_csr) != SCSI_PHASE_CMD) + ; + + /* set dma pointer and counter to xfer command */ + out_count = tgt->transient_state.cmd_count; +#if 0 + SCI_ACK(regs,SCSI_PHASE_CMD); + sci_data_out(regs,SCSI_PHASE_CMD,out_count,tgt->cmd_ptr); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; +#else + SCI_DMADR_PUT(dmar,tgt->dma_ptr); + delay_1p2_us(); + SCI_TC_PUT(dmar,out_count); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_ACK(regs,SCSI_PHASE_CMD); + SCI_CLR_INTR(regs); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; + regs->sci_icmd = SCI_ICMD_DATA; + regs->sci_dma_send = 1; +#endif +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +sci_intr(unit) +{ + register sci_softc_t sci; + register script_t scp; + register unsigned csr, bs, cmd; + register sci_padded_regmap_t *regs; + boolean_t try_match; +#if notyet + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SCI_intr(unit); +#endif + + LOG(5,"\n\tintr"); + + sci = sci_softc[unit]; + regs = sci->regs; + + /* ack interrupt */ + csr = regs->sci_csr; + bs = regs->sci_bus_csr; + cmd = regs->sci_icmd; +TR(regs->sci_mode); + SCI_CLR_INTR(regs); + +TR(csr); +TR(bs); +TR(cmd); +TRCHECK; + + if (cmd & SCI_ICMD_RST){ + sci_bus_reset(sci); + return; + } + + /* we got an interrupt allright */ + if (sci->active_target) + sci->wd.watchdog_state = SCSI_WD_ACTIVE; + + /* drop spurious calls */ + if ((csr & SCI_CSR_INT) == 0) { + LOG(2,"SPURIOUS"); + return; + } + + /* Note: reselect has I/O asserted, select has not */ + if ((sci->state & SCI_STATE_TARGET) || + ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == SCI_BUS_SEL)) { + sci_target_intr(sci,csr,bs); + return; + } + + scp = sci->script; + + /* Race: disconnecting, we get the disconnected notification + (csr sez BSY dropped) at the same time a reselect is active */ + if ((csr & SCI_CSR_DISC) && + scp && (scp->condition == SCI_PHASE_DISC)) { + (void) (*scp->action)(sci, csr, bs); + /* takes care of calling reconnect if necessary */ + return; + } + + /* check who got the bus */ + if ((scp == 0) || (cmd & SCI_ICMD_LST) || + ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO))) { + sci_reconnect(sci, csr, bs); + return; + } + + if (SCRIPT_MATCH(csr,bs) != scp->condition) { + if (try_match = (*sci->error_handler)(sci, csr, bs)) { + csr = regs->sci_csr; + bs = regs->sci_bus_csr; + } + } else + try_match = TRUE; + + /* might have been side effected */ + scp = sci->script; + + if (try_match && (SCRIPT_MATCH(csr,bs) == scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(sci, csr, bs)) { + /* might have been side effected */ + scp = sci->script; + sci->script = scp + 1; + } + } +} + + +sci_target_intr(sci) + register sci_softc_t sci; +{ + panic("SCI: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ +boolean_t +sci_end_transaction( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + char cmc; + + LOG(0x1f,"end_t"); + + /* Stop dma, no interrupt on disconnect */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE); +/* dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + regs->sci_sel_enb = (1 << sci->sc->initiator_id); + + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc); + + if (cmc != SCSI_COMMAND_COMPLETE) + printf("{T%x}", cmc); + + /* check disconnected, clear all intr bits */ + while (regs->sci_bus_csr & SCI_BUS_BSY) + ; + SCI_CLR_INTR(regs); + SCI_ACK(regs,SCI_PHASE_DISC); + + if (!sci_end(sci, csr, bs)) { + SCI_CLR_INTR(regs); + (void) sci_reconnect(sci, csr, bs); + } + return FALSE; +} + +boolean_t +sci_end( sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register io_req_t ior; + register sci_padded_regmap_t *regs = sci->regs; + boolean_t reconn_pending; + + LOG(6,"end"); + + tgt = sci->active_target; + + if ((tgt->done = sci->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + sci->script = 0; + + if (sci->wd.nactive-- == 1) + sci->wd.watchdog_state = SCSI_WD_INACTIVE; + + /* check reconnection not pending */ + bs = regs->sci_bus_csr; + reconn_pending = ((bs & (SCI_BUS_BSY|SCI_BUS_SEL|SCI_BUS_IO)) == (SCI_BUS_SEL|SCI_BUS_IO)); + if (!reconn_pending) { + sci_release_bus(sci); + } else { + sci->active_target = 0; +/* sci->state &= ~SCI_STATE_BUSY; later */ + } + + if (ior = tgt->ior) { +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_unmap(&tgt->fdma, ior); +#endif /*MACH_KERNEL*/ + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + if (reconn_pending) + sci->state &= ~SCI_STATE_BUSY; + } + + return (!reconn_pending); +} + +boolean_t +sci_release_bus(sci) + register sci_softc_t sci; +{ + boolean_t ret = FALSE; + + LOG(9,"release"); + + sci->script = 0; + + if (sci->state & SCI_STATE_COLLISION) { + + LOG(0xB,"collided"); + sci->state &= ~SCI_STATE_COLLISION; + sci_attempt_selection(sci); + + } else if (queue_empty(&sci->waiting_targets)) { + + sci->state &= ~SCI_STATE_BUSY; + sci->active_target = 0; + ret = TRUE; + + } else { + + LOG(0xC,"dequeue"); + sci->next_target = (target_info_t *) + dequeue_head(&sci->waiting_targets); + sci_attempt_selection(sci); + } + return ret; +} + +boolean_t +sci_get_status( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + scsi2_status_byte_t status; + register target_info_t *tgt; + unsigned int len, mode; + + LOG(0xD,"get_status"); +TRWRAP; + + /* Stop dma */ + regs->sci_icmd = 0; + mode = regs->sci_mode; + regs->sci_mode = (mode & ~(SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + sci->state &= ~SCI_STATE_DMA_IN; + + tgt = sci->active_target; + + if (len = sci->in_count) { + register int count; + SCI_TC_GET(dmar,count); + if ((tgt->cur_cmd != SCSI_CMD_READ) && + (tgt->cur_cmd != SCSI_CMD_LONG_READ)){ + len -= count; + } else { + if (count) { +#if 0 + this is incorrect and besides.. + tgt->ior->io_residual = count; +#endif + len -= count; + } + sci_copyin( tgt, tgt->transient_state.dma_offset, + len, 0, 0); + } + } + + /* to get the phase mismatch intr */ + regs->sci_mode = mode; + + SCI_ACK(regs,SCSI_PHASE_STATUS); + + sci_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + + SCI_TC_PUT(dmar,0); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(sci->active_target, SCSI_ERR_STATUS, status.bits, 0); + sci->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + sci->done = SCSI_RET_SUCCESS; + + return TRUE; +} + +boolean_t +sci_dma_in( sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + char *dma_ptr; + register int count; + boolean_t advance_script = TRUE; + + LOG(0xE,"dma_in"); + + /* + * Problem: the 5380 pipelines xfers between the scsibus and + * itself and between itself and the DMA engine --> halting ? + * In the dmaout direction all is ok, except that (see NCR notes) + * the EOP interrupt is generated before the pipe is empty. + * In the dmain direction (here) the interrupt comes when + * one too many bytes have been xferred on chip! + * + * More specifically, IF we asked for count blindly and we had + * more than count bytes coming (double buffering) we would endup + * actually xferring count+1 from the scsibus, but only count + * to memory [hopefully the last byte sits in the sci_datai reg]. + * This could be helped, except most times count is an exact multiple + * of the sector size which is where disks disconnect.... + * + * INSTEAD, we recognize here that we expect more than count bytes + * coming and set the DMA count to count-1 but keep sci->in_count + * above to count. This will be wrong if the target disconnects + * amidst, but we can cure it. + * + * The places where this has an effect are marked by "EXTRA_BYTE" + */ + + tgt = sci->active_target; + sci->state |= SCI_STATE_DMA_IN; + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + regs->sci_icmd = 0; + + if (sci->in_count == 0) { + /* + * Got nothing yet: either just sent the command + * or just reconnected + */ + register int avail; + + count = tgt->transient_state.in_count; + count = u_min(count, (PER_TGT_BURST_SIZE)); + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + + /* common case of 8k-or-less read ? */ + advance_script = (tgt->transient_state.in_count == count); + + } else { + + /* + * We received some data. + */ + register int offset, xferred, eb; + unsigned char extrab = regs->sci_idata; /* EXTRA_BYTE */ + + SCI_TC_GET(dmar,xferred); + assert(xferred == 0); +if (scsi_debug) { +printf("{B %x %x %x (%x)}", + sci->in_count, xferred, sci->extra_byte, extrab); +} + /* ++EXTRA_BYTE */ + xferred = sci->in_count - xferred; + eb = sci->extra_byte; + /* --EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.in_count, (PER_TGT_BURST_SIZE)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int avail; + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + } + advance_script = (tgt->transient_state.in_count == count); + + /* get some more */ + dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset; + sci->in_count = count; + /* ++EXTRA_BYTE */ + if (!advance_script) { + sci->extra_byte = 1; /* that's the cure.. */ + count--; + } else + sci->extra_byte = 0; + /* --EXTRA_BYTE */ + SCI_TC_PUT(dmar,count); +/* regs->sci_icmd = 0;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_DATAI); + SCI_CLR_INTR(regs); + regs->sci_mode |= (advance_script ? SCI_MODE_DMA + : (SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, eb, extrab); + + /* last chunk ? */ + return advance_script; + } + + sci->in_count = count; + dma_ptr = tgt->dma_ptr + tgt->transient_state.dma_offset; + + /* ++EXTRA_BYTE */ + if (!advance_script) { + sci->extra_byte = 1; /* that's the cure.. */ + count--; + } else + sci->extra_byte = 0; + /* --EXTRA_BYTE */ + + SCI_TC_PUT(dmar,count); +/* regs->sci_icmd = 0;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_DATAI); + SCI_CLR_INTR(regs); + regs->sci_mode |= (advance_script ? SCI_MODE_DMA + : (SCI_MODE_DMA|SCI_MODE_DMA_IE)); + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + return advance_script; +} + +/* send data to target. Called in three different ways: + (a) to start transfer (b) to restart a bigger-than-8k + transfer (c) after reconnection + */ +int sci_delay = 1; + +boolean_t +sci_dma_out( sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + register char *dma_ptr; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count = sci->out_count; + spl_t s; + register int tmp; + + LOG(0xF,"dma_out"); + + tgt = sci->active_target; + sci->state &= ~SCI_STATE_DMA_IN; + + if (sci->out_count == 0) { + /* + * Nothing committed: either just sent the + * command or reconnected + */ + register int remains; + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */ + + regs->sci_icmd = SCI_ICMD_DATA; + + SCI_ACK(regs,SCSI_PHASE_DATAO); + + count = tgt->transient_state.out_count; + count = u_min(count, (PER_TGT_BURST_SIZE)); + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + + /* common case of 8k-or-less write ? */ + advance_script = (tgt->transient_state.out_count == count); + } else { + /* + * We sent some data. + * Also, take care of bogus interrupts + */ + register int offset, xferred; + +if (sci_delay & 1) delay(1000); + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/*hold it */ +/* regs->sci_icmd = SCI_ICMD_DATA; */ + + SCI_TC_GET(dmar,xferred); +if (xferred) printf("{A %x}", xferred); + xferred = sci->out_count - xferred; + assert(xferred > 0); + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.out_count, (PER_TGT_BURST_SIZE)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int remains; + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + } + /* last chunk ? */ + if (tgt->transient_state.out_count == count) + goto quickie; + + /* ship some more */ + dma_ptr = tgt->dma_ptr + + tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + sci->out_count = count; + /* + * Mistery: sometimes the first byte + * of an 8k chunk is missing from the tape, it must + * be that somehow touching the 5380 registers + * after the dma engine is ready screws up: false DRQ? + */ +s = splhigh(); + SCI_TC_PUT(dmar,count); +/* SCI_CLR_INTR(regs);*/ + regs->sci_mode = SCI_MODE_PAR_CHK | SCI_MODE_DMA | + SCI_MODE_MONBSY | SCI_MODE_DMA_IE; +/* regs->sci_icmd = SCI_ICMD_DATA;*/ + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + + regs->sci_dma_send = 1; +splx(s); + /* copy some more data */ + sci_copyout(tgt, offset, xferred); + return FALSE; + } + +quickie: + sci->out_count = count; + dma_ptr = tgt->dma_ptr + + tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + tmp = (advance_script ? + SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY: + SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY|SCI_MODE_DMA_IE); +s = splhigh(); + SCI_TC_PUT(dmar,count); +/* SCI_CLR_INTR(regs);*/ + regs->sci_mode = tmp; +/* regs->sci_icmd = SCI_ICMD_DATA;*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE; + regs->sci_dma_send = 1; +splx(s); + + return advance_script; +} + +/* disconnect-reconnect ops */ + +/* get the message in via dma */ +boolean_t +sci_msg_in(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + char *dma_ptr; + register sci_padded_regmap_t *regs = sci->regs; + register sci_dmaregs_t dmar = sci->dmar; + + LOG(0x15,"msg_in"); + + tgt = sci->active_target; + + dma_ptr = tgt->dma_ptr; + /* We would clobber the data for READs */ + if (sci->state & SCI_STATE_DMA_IN) { + register int offset; + offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + dma_ptr += offset; + } + + /* ought to stop dma to start another */ + regs->sci_mode &= ~ (SCI_MODE_DMA|SCI_MODE_DMA_IE); + regs->sci_icmd = 0; + + /* We only really expect two bytes */ + SCI_TC_PUT(dmar,sizeof(scsi_command_group_0)); +/* regs->sci_icmd = 0*/ + SCI_DMADR_PUT(dmar,dma_ptr); + delay_1p2_us(); + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + SCI_CLR_INTR(regs); + regs->sci_mode |= SCI_MODE_DMA; + dmar->sci_dma_dir = SCI_DMA_DIR_READ; + regs->sci_irecv = 1; + + return TRUE; +} + +/* check the message is indeed a DISCONNECT */ +boolean_t +sci_disconnect(sci, csr, bs) + register sci_softc_t sci; +{ + register int len; + boolean_t ok = FALSE; + register sci_dmaregs_t dmar = sci->dmar; + register char *msgs; + unsigned int offset; + + + SCI_TC_GET(dmar,len); + len = sizeof(scsi_command_group_0) - len; + PRINT(("{G%d}",len)); + + /* wherever it was, take it from there */ + SCI_DMADR_GET(dmar,offset); + msgs = (char*)sci->buff + offset - len; + + if ((len == 0) || (len > 2)) + ok = FALSE; + else { + /* A SDP message preceeds it in non-completed READs */ + ok = ((msgs[0] == SCSI_DISCONNECT) || /* completed op */ + ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* incomplete */ + (msgs[1] == SCSI_DISCONNECT))); + } + if (!ok) + printf("[tgt %d bad msg (%d): %x]", + sci->active_target->target_id, len, *msgs); + + return TRUE; +} + +/* save all relevant data, free the BUS */ +boolean_t +sci_disconnected(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + sci_padded_regmap_t *regs = sci->regs; + + regs->sci_mode &= ~(SCI_MODE_MONBSY|SCI_MODE_DMA); + SCI_CLR_INTR(regs);/*retriggered by MONBSY cuz intr routine did CLR */ + SCI_ACK(regs,SCI_PHASE_DISC); + + LOG(0x16,"disconnected"); + + sci_disconnect(sci,csr,bs); + + tgt = sci->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = sci->error_handler; + /* the rest has been saved in sci_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + sci_release_bus(sci); + + return FALSE; +} + +/* get reconnect message, restore BUS */ +boolean_t +sci_reconnect(sci, csr, bs) + register sci_softc_t sci; +{ + register target_info_t *tgt; + sci_padded_regmap_t *regs; + register int id; + int msg; + + LOG(0x17,"reconnect"); + + if (sci->wd.nactive == 0) { + LOG(2,"SPURIOUS"); + return FALSE; + } + + regs = sci->regs; + + regs->sci_mode &= ~SCI_MODE_PAR_CHK; + id = regs->sci_data;/*parity!*/ + regs->sci_mode |= SCI_MODE_PAR_CHK; + + /* xxx check our id is in there */ + + id &= ~(1 << sci->sc->initiator_id); + { + register int i; + for (i = 0; i < 8; i++) + if (id & (1 << i)) break; +if (i == 8) {printf("{P%x}", id);return;} + id = i; + } + regs->sci_icmd = SCI_ICMD_BSY; + while (regs->sci_bus_csr & SCI_BUS_SEL) + ; + regs->sci_icmd = 0; + delay_1p2_us(); + while ( ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) && + ((regs->sci_bus_csr & SCI_BUS_BSY) == 0) && + ((regs->sci_bus_csr & SCI_BUS_BSY) == 0)) + ; + + regs->sci_mode |= SCI_MODE_MONBSY; + + /* Now should wait for correct phase: REQ signals it */ + while ( ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0) && + ((regs->sci_bus_csr & SCI_BUS_REQ) == 0)) + ; + + /* + * See if this reconnection collided with a selection attempt + */ + if (sci->state & SCI_STATE_BUSY) + sci->state |= SCI_STATE_COLLISION; + + sci->state |= SCI_STATE_BUSY; + + /* Get identify msg */ + bs = regs->sci_bus_csr; +if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak(); + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + msg = 0; + sci_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg); + regs->sci_mode = SCI_MODE_PAR_CHK|SCI_MODE_DMA|SCI_MODE_MONBSY; + regs->sci_sel_enb = 0; + + if (msg != SCSI_IDENTIFY) + printf("{I%x %x}", id, msg); + + tgt = sci->sc->target[id]; + if (id > 7 || tgt == 0) panic("sci_reconnect"); + + PRINT(("{R%d}", id)); + if (sci->state & SCI_STATE_COLLISION) + PRINT(("[B %d-%d]", sci->active_target->target_id, id)); + + LOG(0x80+id,0); + + sci->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + sci->script = tgt->transient_state.script; + sci->error_handler = tgt->transient_state.handler; + sci->in_count = 0; + sci->out_count = 0; + + /* Should get a phase mismatch when tgt changes phase */ + + return TRUE; +} + + + +/* do the synch negotiation */ +boolean_t +sci_dosynch( sci, csr, bs) + register sci_softc_t sci; +{ + /* + * Phase is MSG_OUT here, cmd has not been xferred + */ + int len; + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + unsigned char off, icmd; + register unsigned char *p; + + regs->sci_mode |= SCI_MODE_MONBSY; + + LOG(0x11,"dosync"); + + /* ATN still asserted */ + SCI_ACK(regs,SCSI_PHASE_MSG_OUT); + + tgt = sci->active_target; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + p = (unsigned char *)tgt->cmd_ptr + tgt->transient_state.cmd_count + + tgt->transient_state.dma_offset; + p[0] = SCSI_IDENTIFY; + p[1] = SCSI_EXTENDED_MESSAGE; + p[2] = 3; + p[3] = SCSI_SYNC_XFER_REQUEST; + /* We cannot run synchronous */ +#define sci_to_scsi_period(x) 0xff +#define scsi_period_to_sci(x) (x) + off = 0; + p[4] = sci_to_scsi_period(sci_min_period); + p[5] = off; + + /* xfer all but last byte with ATN set */ + sci_data_out(regs, SCSI_PHASE_MSG_OUT, + sizeof(scsi_synch_xfer_req_t), p); + icmd = regs->sci_icmd & ~(SCI_ICMD_DIFF|SCI_ICMD_TEST); + icmd &= ~SCI_ICMD_ATN; + regs->sci_icmd = icmd; + sci_data_out(regs, SCSI_PHASE_MSG_OUT, 1, + &p[sizeof(scsi_synch_xfer_req_t)]); + + /* wait for phase change */ + while (regs->sci_csr & SCI_CSR_PHASE_MATCH) + ; + bs = regs->sci_bus_csr; + + /* The standard sez there nothing else the target can do but.. */ + if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) + panic("sci_dosync");/* XXX put offline */ + +msgin: + /* ack */ + SCI_ACK(regs,SCSI_PHASE_MSG_IN); + + /* get answer */ + len = sizeof(scsi_synch_xfer_req_t); + len = sci_data_in(regs, SCSI_PHASE_MSG_IN, len, p); + + /* do not cancel the phase mismatch interrupt ! */ + + /* look at the answer and see if we like it */ + if (len || (p[0] != SCSI_EXTENDED_MESSAGE)) { + /* did not like it at all */ + printf(" did not like SYNCH xfer "); + } else { + /* will NOT do synch */ + printf(" but we cannot do SYNCH xfer "); + tgt->sync_period = scsi_period_to_sci(p[3]); + tgt->sync_offset = p[4]; + /* sanity */ + if (tgt->sync_offset != 0) + printf(" ?OFFSET %x? ", tgt->sync_offset); + } + + /* wait for phase change */ + while (regs->sci_csr & SCI_CSR_PHASE_MATCH) + ; + bs = regs->sci_bus_csr; + + /* phase should be command now */ + /* continue with simple command script */ + sci->error_handler = sci_err_generic; + sci->script = sci_script_cmd; + + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_CMD ) + return TRUE; + +/* sci->script++;*/ + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS ) + return TRUE; /* intr is pending */ + + sci->script++; + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_MSG_IN ) + return TRUE; + + if ((bs & SCI_BUS_BSY) == 0) /* uhu? disconnected */ + return sci_end_transaction(sci, regs->sci_csr, regs->sci_bus_csr); + + panic("sci_dosynch"); + return FALSE; +} + +/* + * The bus was reset + */ +sci_bus_reset(sci) + register sci_softc_t sci; +{ + register target_info_t *tgt; + register sci_padded_regmap_t *regs = sci->regs; + int i; + + LOG(0x21,"bus_reset"); + + /* + * Clear bus descriptor + */ + sci->script = 0; + sci->error_handler = 0; + sci->active_target = 0; + sci->next_target = 0; + sci->state = 0; + queue_init(&sci->waiting_targets); + sci->wd.nactive = 0; + sci_reset(sci, TRUE); + + printf("sci%d: (%d) bus reset ", sci->sc->masterno, ++sci->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (sci->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(sci->sc); +} + +/* + * Error handlers + */ + +/* + * Generic, default handler + */ +boolean_t +sci_err_generic(sci, csr, bs) + register sci_softc_t sci; +{ + register int cond = sci->script->condition; + + LOG(0x10,"err_generic"); + + if (SCI_CUR_PHASE(bs) == SCSI_PHASE_STATUS) + return sci_err_to_status(sci, csr, bs); + gimmeabreak(); + return FALSE; +} + +/* + * Handle generic errors that are reported as + * an unexpected change to STATUS phase + */ +sci_err_to_status(sci, csr, bs) + register sci_softc_t sci; +{ + script_t scp = sci->script; + + LOG(0x20,"err_tostatus"); + while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS) + scp++; + sci->script = scp; +#if 0 + /* + * Normally, we would already be able to say the command + * is in error, e.g. the tape had a filemark or something. + * But in case we do disconnected mode WRITEs, it is quite + * common that the following happens: + * dma_out -> disconnect -> reconnect + * and our script might expect at this point that the dma + * had to be restarted (it didn't know it was completed + * because the tape record is shorter than we asked for). + * And in any event.. it is both correct and cleaner to + * declare error iff the STATUS byte says so. + */ + sci->done = SCSI_RET_NEED_SENSE; +#endif + return TRUE; +} + +/* + * Watch for a disconnection + */ +boolean_t +sci_err_disconn(sci, csr, bs) + register sci_softc_t sci; +{ + register sci_padded_regmap_t *regs; + register sci_dmaregs_t dmar = sci->dmar; + register target_info_t *tgt; + int count; + + LOG(0x18,"err_disconn"); + + if (SCI_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) + return sci_err_generic(sci, csr, bs); + + regs = sci->regs; + + tgt = sci->active_target; + + switch (SCSI_PHASE(sci->script->condition)) { + case SCSI_PHASE_DATAO: + LOG(0x1b,"+DATAO"); + +if (sci_delay & 1) delay(1000); + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */ + + if (sci->out_count) { + register int xferred, offset; + + SCI_TC_GET(dmar,xferred); +if (scsi_debug) +printf("{O %x %x}", xferred, sci->out_count); + /* 5380 prefetches */ + xferred = sci->out_count - xferred - 1; +/* assert(xferred > 0);*/ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + sci_copyout( tgt, offset, xferred); + + } + tgt->transient_state.script = sci_script_data_out; + break; + + case SCSI_PHASE_DATAI: + LOG(0x19,"+DATAI"); + + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + if (sci->in_count) { + register int offset, xferred; +/* unsigned char extrab = regs->sci_idata;*/ + + SCI_TC_GET(dmar,xferred); + /* ++EXTRA_BYTE */ +if (scsi_debug) +printf("{A %x %x %x}", xferred, sci->in_count, sci->extra_byte); + xferred = sci->in_count - xferred - sci->extra_byte; + /* ++EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, 0, 0/*extrab*/); + } + tgt->transient_state.script = sci_script_data_in; + break; + + case SCSI_PHASE_STATUS: + /* will have to restart dma */ + SCI_TC_GET(dmar,count); + if (sci->state & SCI_STATE_DMA_IN) { + register int offset, xferred; +/* unsigned char extrab = regs->sci_idata;*/ + + LOG(0x1a,"+STATUS+R"); + + + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_WRITE;/* make sure we steal not */ + + /* ++EXTRA_BYTE */ +if (scsi_debug) +printf("{A %x %x %x}", count, sci->in_count, sci->extra_byte); + xferred = sci->in_count - count - sci->extra_byte; + /* ++EXTRA_BYTE */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; +/* assert(tgt->transient_state.in_count > 0);*/ + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + sci_copyin( tgt, offset, xferred, 0, 0/*/extrab*/); + + tgt->transient_state.script = sci_script_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + LOG(0x1d,"+STATUS+W"); + +if (sci_delay & 1) delay(1000); + /* Stop dma */ + regs->sci_icmd = 0; + regs->sci_mode &= ~(SCI_MODE_DMA|SCI_MODE_DMA_IE); + dmar->sci_dma_dir = SCI_DMA_DIR_READ;/* make sure we steal not */ + +if (scsi_debug) +printf("{O %x %x}", count, sci->out_count); + if ((count == 0) && (tgt->transient_state.out_count == sci->out_count)) { + /* all done */ + tgt->transient_state.script = &sci_script_data_out[1]; + tgt->transient_state.out_count = 0; + } else { + register int xferred, offset; + + /* how much we xferred */ + xferred = sci->out_count - count - 1;/*prefetch*/ + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset >= PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + sci_copyout( tgt, offset, xferred); + + tgt->transient_state.script = sci_script_data_out; + } + sci->out_count = 0; + } + break; + default: + gimmeabreak(); + } + sci->extra_byte = 0; + +/* SCI_ACK(regs,SCSI_PHASE_MSG_IN); later */ + (void) sci_msg_in(sci,csr,bs); + + regs->sci_sel_enb = (1 << sci->sc->initiator_id); + + sci->script = sci_script_disconnect; + + return FALSE; +} + +/* + * Watchdog + * + */ +sci_reset_scsibus(sci) + register sci_softc_t sci; +{ + register target_info_t *tgt = sci->active_target; + if (tgt) { + int cnt; + SCI_TC_GET(sci->dmar,cnt); + log( LOG_KERN, + "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, tgt->transient_state.out_count, + sci->in_count, sci->out_count, cnt); + } + sci->regs->sci_icmd = SCI_ICMD_RST; + delay(25); +} + +/* + * Copy routines + */ +/*static*/ +sci_copyin(tgt, offset, len, isaobb, obb) + register target_info_t *tgt; + unsigned char obb; +{ + register char *from, *to; + register int count; + + count = tgt->transient_state.copy_count; + + from = tgt->cmd_ptr + offset; + to = tgt->ior->io_data + count; + tgt->transient_state.copy_count = count + len; + + bcopy( from, to, len); + /* check for last, poor little odd byte */ + if (isaobb) { + to += len; + to[-1] = obb; + } +} + +/*static*/ +sci_copyout( tgt, offset, len) + register target_info_t *tgt; +{ + register char *from, *to; + register int count, olen; + unsigned char c; + char *p; + + count = tgt->ior->io_count - tgt->transient_state.copy_count; + if (count > 0) { + + len = u_min(count, len); + offset += tgt->transient_state.cmd_count; + + count = tgt->transient_state.copy_count; + tgt->transient_state.copy_count = count + len; + + from = tgt->ior->io_data + count; + to = tgt->cmd_ptr + offset; + + bcopy(from, to, len); + + } +} + +#endif /*NSCI > 0*/ + diff --git a/scsi/adapters/scsi_53C700.h b/scsi/adapters/scsi_53C700.h new file mode 100644 index 0000000..224fc5b --- /dev/null +++ b/scsi/adapters/scsi_53C700.h @@ -0,0 +1,327 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_53C700.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/91 + * + * Defines for the NCR 53C700 (SCSI I/O Processor) + */ + +/* + * Register map + */ + +typedef struct { + volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */ + volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */ + volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */ + volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */ + volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */ + volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */ + volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */ + volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */ + volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */ + volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */ + volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */ + volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */ + volatile unsigned char siop_dstat; /* ro: DMA status */ + volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */ + volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */ + volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */ + volatile unsigned char siop_res1; + volatile unsigned char siop_res2; + volatile unsigned char siop_res3; + volatile unsigned char siop_res4; + volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */ + volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */ + volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */ + volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */ + volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */ + volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */ + volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */ + volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */ + volatile unsigned char siop_temp0; /* rw: Temporary Stack reg */ + volatile unsigned char siop_temp1; + volatile unsigned char siop_temp2; + volatile unsigned char siop_temp3; + volatile unsigned char siop_dfifo; /* rw: DMA FIFO */ + volatile unsigned char siop_istat; /* rw: Interrupt Status reg */ + volatile unsigned char siop_res5; + volatile unsigned char siop_res6; + volatile unsigned char siop_dbc0; /* rw: DMA Byte Counter reg */ + volatile unsigned char siop_dbc1; + volatile unsigned char siop_dbc2; + volatile unsigned char siop_dcmd; /* rw: DMA Command Register */ + volatile unsigned char siop_dnad0; /* rw: DMA Next Address */ + volatile unsigned char siop_dnad1; + volatile unsigned char siop_dnad2; + volatile unsigned char siop_dnad3; + volatile unsigned char siop_dsp0; /* rw: DMA SCRIPTS Pointer reg */ + volatile unsigned char siop_dsp1; + volatile unsigned char siop_dsp2; + volatile unsigned char siop_dsp3; + volatile unsigned char siop_dsps0; /* rw: DMA SCRIPTS Pointer Save reg */ + volatile unsigned char siop_dsps1; + volatile unsigned char siop_dsps2; + volatile unsigned char siop_dsps3; + volatile unsigned char siop_dmode; /* rw: DMA Mode reg */ + volatile unsigned char siop_res7; + volatile unsigned char siop_res8; + volatile unsigned char siop_res9; + volatile unsigned char siop_res10; + volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */ + volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */ + volatile unsigned char siop_dcntl; /* rw: DMA Control reg */ + volatile unsigned char siop_res11; + volatile unsigned char siop_res12; + volatile unsigned char siop_res13; + volatile unsigned char siop_res14; + +} siop_regmap_t; + +/* + * Register defines + */ + +/* Scsi control register 0 (scntl0) */ + +#define SIOP_SCNTL0_ARB 0xc0 /* Arbitration mode */ +# define SIOP_ARB_SIMPLE 0x00 +# define SIOP_ARB_FULL 0xc0 +#define SIOP_SCNTL0_START 0x20 /* Start Sequence */ +#define SIOP_SCNTL0_WATN 0x10 /* (Select) With ATN */ +#define SIOP_SCNTL0_EPC 0x08 /* Enable Parity Checking */ +#define SIOP_SCNTL0_EPG 0x04 /* Enable Parity Generation */ +#define SIOP_SCNTL0_AAP 0x02 /* Assert ATN on Parity Error */ +#define SIOP_SCNTL0_TRG 0x01 /* Target Mode */ + +/* Scsi control register 1 (scntl1) */ + +#define SIOP_SCNTL1_EXC 0x80 /* Extra Clock Cycle of data setup */ +#define SIOP_SCNTL1_ADB 0x40 /* Assert Data Bus */ +#define SIOP_SCNTL1_ESR 0x20 /* Enable Selection/Reselection */ +#define SIOP_SCNTL1_CON 0x10 /* Connected */ +#define SIOP_SCNTL1_RST 0x08 /* Assert RST */ +#define SIOP_SCNTL1_PAR 0x04 /* Force bad Parity */ +#define SIOP_SCNTL1_SND 0x02 /* Start Send operation */ +#define SIOP_SCNTL1_RCV 0x01 /* Start Receive operation */ + +/* Scsi interrupt enable register (sien) */ + +#define SIOP_SIEN_M_A 0x80 /* Phase Mismatch or ATN active */ +#define SIOP_SIEN_FC 0x40 /* Function Complete */ +#define SIOP_SIEN_STO 0x20 /* (Re)Selection timeout */ +#define SIOP_SIEN_SEL 0x10 /* (Re)Selected */ +#define SIOP_SIEN_SGE 0x08 /* SCSI Gross Error */ +#define SIOP_SIEN_UDC 0x04 /* Unexpected Disconnect */ +#define SIOP_SIEN_RST 0x02 /* RST asserted */ +#define SIOP_SIEN_PAR 0x01 /* Parity Error */ + +/* Scsi chip ID (scid) */ + +#define SIOP_SCID_VALUE(i) (1<<i) + +/* Scsi transfer register (sxfer) */ + +#define SIOP_SXFER_DHP 0x80 /* Disable Halt on Parity error/ ATN asserted */ +#define SIOP_SXFER_TP 0x70 /* Synch Transfer Period */ + /* see specs for formulas: + Period = TCP * (4 + XFERP ) + TCP = 1 + CLK + 1..2; + */ +#define SIOP_SXFER_MO 0x0f /* Synch Max Offset */ +# define SIOP_MAX_OFFSET 8 + +/* Scsi output data latch register (sodl) */ + +/* Scsi output control latch register (socl) */ + +#define SIOP_REQ 0x80 /* SCSI signal <x> asserted */ +#define SIOP_ACK 0x40 +#define SIOP_BSY 0x20 +#define SIOP_SEL 0x10 +#define SIOP_ATN 0x08 +#define SIOP_MSG 0x04 +#define SIOP_CD 0x02 +#define SIOP_IO 0x01 + +#define SIOP_PHASE(socl) SCSI_PHASE(socl) + +/* Scsi first byte received register (sfbr) */ + +/* Scsi input data latch register (sidl) */ + +/* Scsi bus data lines register (sbdl) */ + +/* Scsi bus control lines register (sbcl). Same as socl */ + +/* DMA status register (dstat) */ + +#define SIOP_DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define SIOP_DSTAT_RES 0x60 +#define SIOP_DSTAT_ABRT 0x10 /* Aborted */ +#define SIOP_DSTAT_SSI 0x08 /* SCRIPT Single Step */ +#define SIOP_DSTAT_SIR 0x04 /* SCRIPT Interrupt Instruction */ +#define SIOP_DSTAT_WTD 0x02 /* Watchdog Timeout Detected */ +#define SIOP_DSTAT_OPC 0x01 /* Invalid SCRIPTS Opcode */ + +/* Scsi status register 0 (sstat0) */ + +#define SIOP_SSTAT0_M_A 0x80 /* Phase Mismatch or ATN active */ +#define SIOP_SSTAT0_FC 0x40 /* Function Complete */ +#define SIOP_SSTAT0_STO 0x20 /* (Re)Selection timeout */ +#define SIOP_SSTAT0_SEL 0x10 /* (Re)Selected */ +#define SIOP_SSTAT0_SGE 0x08 /* SCSI Gross Error */ +#define SIOP_SSTAT0_UDC 0x04 /* Unexpected Disconnect */ +#define SIOP_SSTAT0_RST 0x02 /* RST asserted */ +#define SIOP_SSTAT0_PAR 0x01 /* Parity Error */ + +/* Scsi status register 1 (sstat1) */ + +#define SIOP_SSTAT1_ILF 0x80 /* Input latch (sidl) full */ +#define SIOP_SSTAT1_ORF 0x40 /* output reg (sodr) full */ +#define SIOP_SSTAT1_OLF 0x20 /* output latch (sodl) full */ +#define SIOP_SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define SIOP_SSTAT1_LOA 0x08 /* Lost arbitration */ +#define SIOP_SSTAT1_WOA 0x04 /* Won arbitration */ +#define SIOP_SSTAT1_RST 0x02 /* SCSI RST current value */ +#define SIOP_SSTAT1_SDP 0x01 /* SCSI SDP current value */ + +/* Scsi status register 2 (sstat2) */ + +#define SIOP_SSTAT2_FF 0xf0 /* SCSI FIFO flags (bytecount) */ +# define SIOP_SCSI_FIFO_DEEP 8 +#define SIOP_SSTAT2_SDP 0x08 /* Latched (on REQ) SCSI SDP */ +#define SIOP_SSTAT2_MSG 0x04 /* Latched SCSI phase */ +#define SIOP_SSTAT2_CD 0x02 +#define SIOP_SSTAT2_IO 0x01 + +/* Chip test register 0 (ctest0) */ + +#define SIOP_CTEST0_RES 0xfc +#define SIOP_CTEST0_RTRG 0x02 /* Real Target mode */ +#define SIOP_CTEST0_DDIR 0x01 /* Xfer direction (1-> from SCSI bus) */ + +/* Chip test register 1 (ctest1) */ + +#define SIOP_CTEST1_FMT 0xf0 /* Byte empty in DMA FIFO bottom (high->byte3) */ +#define SIOP_CTEST1_FFL 0x0f /* Byte full in DMA FIFO top, same */ + +/* Chip test register 2 (ctest2) */ + +#define SIOP_CTEST2_RES 0xc0 +#define SIOP_CTEST2_SOFF 0x20 /* Synch Offset compare (1-> zero Init, max Tgt */ +#define SIOP_CTEST2_SFP 0x10 /* SCSI FIFO Parity */ +#define SIOP_CTEST2_DFP 0x08 /* DMA FIFO Parity */ +#define SIOP_CTEST2_TEOP 0x04 /* True EOP (a-la 5380) */ +#define SIOP_CTEST2_DREQ 0x02 /* DREQ status */ +#define SIOP_CTEST2_DACK 0x01 /* DACK status */ + +/* Chip test register 3 (ctest3) read-only, top of SCSI FIFO */ + +/* Chip test register 4 (ctest4) */ + +#define SIOP_CTEST4_RES 0x80 +#define SIOP_CTEST4_ZMOD 0x40 /* High-impedance outputs */ +#define SIOP_CTEST4_SZM 0x20 /* ditto, SCSI "outputs" */ +#define SIOP_CTEST4_SLBE 0x10 /* SCSI loobpack enable */ +#define SIOP_CTEST4_SFWR 0x08 /* SCSI FIFO write enable (from sodl) */ +#define SIOP_CTEST4_FBL 0x07 /* DMA FIFO Byte Lane select (from ctest6) + 4->0, .. 7->3 */ + +/* Chip test register 5 (ctest5) */ + +#define SIOP_CTEST5_ADCK 0x80 /* Clock Address Incrementor */ +#define SIOP_CTEST5_BBCK 0x40 /* Clock Byte counter */ +#define SIOP_CTEST5_ROFF 0x20 /* Reset SCSI offset */ +#define SIOP_CTEST5_MASR 0x10 /* Master set/reset pulses (of bits 3-0) */ +#define SIOP_CTEST5_DDIR 0x08 /* (re)set internal DMA direction */ +#define SIOP_CTEST5_EOP 0x04 /* (re)set internal EOP */ +#define SIOP_CTEST5_DREQ 0x02 /* (re)set internal REQ */ +#define SIOP_CTEST5_DACK 0x01 /* (re)set internal ACK */ + +/* Chip test register 6 (ctest6) DMA FIFO access */ + +/* Chip test register 7 (ctest7) */ + +#define SIOP_CTEST7_RES 0xe0 +#define SIOP_CTEST7_STD 0x10 /* Disable selection timeout */ +#define SIOP_CTEST7_DFP 0x08 /* DMA FIFO parity bit */ +#define SIOP_CTEST7_EVP 0x04 /* Even parity (to host bus) */ +#define SIOP_CTEST7_DC 0x02 /* Drive DC pin low on SCRIPT fetches */ +#define SIOP_CTEST7_DIFF 0x01 /* Differential mode */ + +/* DMA FIFO register (dfifo) */ + +#define SIOP_DFIFO_FLF 0x80 /* Flush (spill) DMA FIFO */ +#define SIOP_DFIFO_CLF 0x40 /* Clear DMA and SCSI FIFOs */ +#define SIOP_DFIFO_BO 0x3f /* FIFO byte offset counter */ + +/* Interrupt status register (istat) */ + +#define SIOP_ISTAT_ABRT 0x80 /* Abort operation */ +#define SIOP_ISTAT_RES 0x70 +#define SIOP_ISTAT_CON 0x08 /* Connected */ +#define SIOP_ISTAT_PRE 0x04 /* Pointer register empty */ +#define SIOP_ISTAT_SIP 0x02 /* SCSI Interrupt pending */ +#define SIOP_ISTAT_DIP 0x01 /* DMA Interrupt pending */ + + +/* DMA Mode register (dmode) */ + +#define SIOP_DMODE_BL_MASK 0xc0 /* 0->1 1->2 2->4 3->8 */ +#define SIOP_DMODE_BW16 0x20 /* Bus Width is 16 bits */ +#define SIOP_DMODE_286 0x10 /* 286 mode */ +#define SIOP_DMODE_IO_M 0x08 /* xfer data to memory or I/O space */ +#define SIOP_DMODE_FAM 0x04 /* fixed address mode */ +#define SIOP_DMODE_PIPE 0x02 /* SCRIPTS in Pipeline mode */ +#define SIOP_DMODE_MAN 0x01 /* SCRIPTS in Manual start mode */ + +/* DMA interrupt enable register (dien) */ + +#define SIOP_DIEN_RES 0xe0 +#define SIOP_DIEN_ABRT 0x10 /* On Abort */ +#define SIOP_DIEN_SSI 0x08 /* On SCRIPTS sstep */ +#define SIOP_DIEN_SIR 0x04 /* On SCRIPTS intr instruction */ +#define SIOP_DIEN_WTD 0x02 /* On watchdog timeout */ +#define SIOP_DIEN_OPC 0x01 /* On SCRIPTS illegal opcode */ + +/* DMA control register (dcntl) */ + +#define SIOP_DCNTL_CF_MASK 0xc0 /* Clock frequency dividers: + 0 --> 37.51..50.00 Mhz, div=2 + 1 --> 25.01..37.50 Mhz, div=1.5 + 2 --> 16.67..25.00 Mhz, div=1 + 3 --> reserved + */ +#define SIOP_DCNTL_S16 0x20 /* SCRIPTS fetches 16bits at a time */ +#define SIOP_DCNTL_SSM 0x10 /* Single step mode */ +#define SIOP_DCNTL_LLM 0x08 /* Enable Low-level mode */ +#define SIOP_DCNTL_STD 0x04 /* Start SCRIPTS operation */ +#define SIOP_DCNTL_RES 0x02 +#define SIOP_DCNTL_RST 0x01 /* Software reset */ + diff --git a/scsi/adapters/scsi_53C700_hdw.c b/scsi/adapters/scsi_53C700_hdw.c new file mode 100644 index 0000000..61b5a3b --- /dev/null +++ b/scsi/adapters/scsi_53C700_hdw.c @@ -0,0 +1,696 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_53C700_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 8/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 53C700 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + + +#include <siop.h> +#if NSIOP > 0 +#include <platforms.h> + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <sys/syslog.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#include <scsi/adapters/scsi_53C700.h> + +#ifdef PAD +typedef struct { + volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */ + PAD(pad0); + volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */ + PAD(pad1); + volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */ + PAD(pad2); + volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */ + PAD(pad3); + volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */ + PAD(pad4); + volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */ + PAD(pad5); + volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */ + PAD(pad6); + volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */ + PAD(pad7); + volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */ + PAD(pad8); + volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */ + PAD(pad9); + volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */ + PAD(pad10); + volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */ + PAD(pad11); + volatile unsigned char siop_dstat; /* ro: DMA status */ + PAD(pad12); + volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */ + PAD(pad13); + volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */ + PAD(pad14); + volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */ + PAD(pad15); + volatile unsigned char siop_res1; + PAD(pad16); + volatile unsigned char siop_res2; + PAD(pad17); + volatile unsigned char siop_res3; + PAD(pad18); + volatile unsigned char siop_res4; + PAD(pad19); + volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */ + PAD(pad20); + volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */ + PAD(pad21); + volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */ + PAD(pad22); + volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */ + PAD(pad23); + volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */ + PAD(pad24); + volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */ + PAD(pad25); + volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */ + PAD(pad26); + volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */ + PAD(pad27); + volatile unsigned char siop_temp0; /* rw: Temporary Stack reg */ + PAD(pad28); + volatile unsigned char siop_temp1; + PAD(pad29); + volatile unsigned char siop_temp2; + PAD(pad30); + volatile unsigned char siop_temp3; + PAD(pad31); + volatile unsigned char siop_dfifo; /* rw: DMA FIFO */ + PAD(pad32); + volatile unsigned char siop_istat; /* rw: Interrupt Status reg */ + PAD(pad33); + volatile unsigned char siop_res5; + PAD(pad34); + volatile unsigned char siop_res6; + PAD(pad35); + volatile unsigned char siop_dbc0; /* rw: DMA Byte Counter reg */ + PAD(pad36); + volatile unsigned char siop_dbc1; + PAD(pad37); + volatile unsigned char siop_dbc2; + PAD(pad38); + volatile unsigned char siop_dcmd; /* rw: DMA Command Register */ + PAD(pad39); + volatile unsigned char siop_dnad0; /* rw: DMA Next Address */ + PAD(pad40); + volatile unsigned char siop_dnad1; + PAD(pad41); + volatile unsigned char siop_dnad2; + PAD(pad42); + volatile unsigned char siop_dnad3; + PAD(pad43); + volatile unsigned char siop_dsp0; /* rw: DMA SCRIPTS Pointer reg */ + PAD(pad44); + volatile unsigned char siop_dsp1; + PAD(pad45); + volatile unsigned char siop_dsp2; + PAD(pad46); + volatile unsigned char siop_dsp3; + PAD(pad47); + volatile unsigned char siop_dsps0; /* rw: DMA SCRIPTS Pointer Save reg */ + PAD(pad48); + volatile unsigned char siop_dsps1; + PAD(pad49); + volatile unsigned char siop_dsps2; + PAD(pad50); + volatile unsigned char siop_dsps3; + PAD(pad51); + volatile unsigned char siop_dmode; /* rw: DMA Mode reg */ + PAD(pad52); + volatile unsigned char siop_res7; + PAD(pad53); + volatile unsigned char siop_res8; + PAD(pad54); + volatile unsigned char siop_res9; + PAD(pad55); + volatile unsigned char siop_res10; + PAD(pad56); + volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */ + PAD(pad57); + volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */ + PAD(pad58); + volatile unsigned char siop_dcntl; /* rw: DMA Control reg */ + PAD(pad59); + volatile unsigned char siop_res11; + PAD(pad60); + volatile unsigned char siop_res12; + PAD(pad61); + volatile unsigned char siop_res13; + PAD(pad62); + volatile unsigned char siop_res14; + PAD(pad63); +} siop_padded_regmap_t; +#else +typedef siop_regmap_t siop_padded_regmap_t; +#endif + +/* + * Macros to make certain things a little more readable + */ + +/* forward decls */ + +int siop_reset_scsibus(); +boolean_t siop_probe_target(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) 53C700 interface + */ +struct siop_softc { + watchdog_t wd; + siop_padded_regmap_t *regs; /* 53C700 registers */ + scsi_dma_ops_t *dma_ops; /* DMA operations and state */ + opaque_t dma_state; + + script_t script; + int (*error_handler)(); + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SIOP_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SIOP_STATE_TARGET 0x04 /* currently selected as target */ +#define SIOP_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SIOP_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + + scsi_softc_t *sc; + target_info_t *active_target; + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + +} siop_softc_data[NSIOP]; + +typedef struct siop_softc *siop_softc_t; + +siop_softc_t siop_softc[NSIOP]; + +/* + * Definition of the controller for the auto-configuration program. + */ + +int siop_probe(), scsi_slave(), scsi_attach(), siop_go(), siop_intr(); + +caddr_t siop_std[NSIOP] = { 0 }; +struct bus_device *siop_dinfo[NSIOP*8]; +struct bus_ctlr *siop_minfo[NSIOP]; +struct bus_driver siop_driver = + { siop_probe, scsi_slave, scsi_attach, siop_go, siop_std, "rz", siop_dinfo, + "siop", siop_minfo, BUS_INTR_B4_PROBE}; + +/* + * Scripts + */ +struct script +siop_script_data_in[] = { +}, + +siop_script_data_out[] = { +}, + +siop_script_cmd[] = { +}, + +/* Synchronous transfer neg(oti)ation */ + +siop_script_try_synch[] = { +}, + +/* Disconnect sequence */ + +siop_script_disconnect[] = { +}; + + +#define DEBUG +#ifdef DEBUG + +siop_state(base) + vm_offset_t base; +{ + siop_padded_regmap_t *regs; +.... + return 0; +} +siop_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = siop_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", + tgt, + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +siop_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = siop_softc[unit]->sc->target[i]; + if (tgt) + siop_target_state(tgt); + } +} + +siop_script_state(unit) +{ + script_t spt = siop_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x ", spt->condition); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(siop_softc[unit]->error_handler, 1); + return 0; + +} + +#define PRINT(x) if (scsi_debug) printf x + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int siop_logpt; +char siop_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x24 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + siop_log[siop_logpt++] = (e); + if (siop_logpt == LOGSIZE) siop_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +siop_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = siop_logpt; i < LOGSIZE; i++) { + c = siop_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-%d", c & 0x7f); + } + db_printf("\n"); + return 0; +} + +siop_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /* TRACE */ +#define LOG(e,f) +#endif /* TRACE */ + +#else /* DEBUG */ +#define PRINT(x) +#define LOG(e,f) +#define TR(x) +#define TRCHECK +#define TRWRAP +#endif /* DEBUG */ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send an identify msg to each possible target on the bus + * except of course ourselves. + */ +siop_probe(reg, ui) + char *reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + siop_softc_t siop = &siop_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register siop_padded_regmap_t *regs; + int s; + boolean_t did_banner = FALSE; + char *cmd_ptr; + static char *here = "siop_probe"; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + regs = (siop_padded_regmap_t *) (reg); + if (check_memory(regs, 0)) + return 0; + +#if notyet + /* Mappable version side */ + SIOP_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor + */ + siop_softc[unit] = siop; + siop->regs = regs; + + if ((siop->dma_ops = (scsi_dma_ops_t *)siop_std[unit]) == 0) + /* use same as unit 0 if undefined */ + siop->dma_ops = (scsi_dma_ops_t *)siop_std[0]; + siop->dma_state = (*siop->dma_ops->init)(unit, reg); + + queue_init(&siop->waiting_targets); + + sc = scsi_master_alloc(unit, siop); + siop->sc = sc; + + sc->go = siop_go; + sc->probe = siop_probe_target; + sc->watchdog = scsi_watchdog; + siop->wd.reset = siop_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; /* unlimited */ +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + /* + * Reset chip + */ + s = splbio(); + siop_reset(siop, TRUE); + + /* + * Our SCSI id on the bus. + */ + + sc->initiator_id = my_scsi_id(unit); + printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + for (target_id = 0; target_id < 8; target_id++) { + + register unsigned csr, dsr; + scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + ..... + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + ..... + + + /* + * Found a target + */ + siop->ntargets++; + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(unit, target_id, siop); + + tgt->cmd_ptr = ... + tgt->dma_ptr = ... +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + } + } + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +siop_probe_target(sc, tgt, ior) + scsi_softc_t *sc; + target_info_t *tgt; + io_req_t ior; +{ + siop_softc_t siop = siop_softc[sc->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + + tgt->cmd_ptr = ... + tgt->dma_ptr = ... +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + + } + + if (scsi_inquiry(sc, tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + + +static siop_wait(preg, until) + volatile unsigned char *preg; +{ + int timeo = 1000000; + while ((*preg & until) != until) { + delay(1); + if (!timeo--) { + printf("siop_wait TIMEO with x%x\n", *preg); + break; + } + } + return *preg; +} + + +siop_reset(siop, quickly) + siop_softc_t siop; + boolean_t quickly; +{ + register siop_padded_regmap_t *regs = siop->regs; + + .... + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call siop_bus_reset(). + */ + .... + +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +siop_go(sc, tgt, cmd_count, in_count, cmd_only) + scsi_softc_t *sc; + target_info_t *tgt; + boolean_t cmd_only; +{ + siop_softc_t siop; + register int s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + siop = (siop_softc_t)tgt->hw_state; + + .... +} + +siop_attempt_selection(siop) + siop_softc_t siop; +{ + target_info_t *tgt; + register int out_count; + siop_padded_regmap_t *regs; + register int cmd; + boolean_t ok; + scsi_ret_t ret; + + regs = siop->regs; + tgt = siop->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + */ + siop->active_target = tgt; + + /* reselection pending ? */ + ...... +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +siop_intr(unit) +{ + register siop_softc_t siop; + register script_t scp; + register unsigned csr, bs, cmd; + register siop_padded_regmap_t *regs; + boolean_t try_match; +#if notyet + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SIOP_intr(unit); +#endif + + LOG(5,"\n\tintr"); + + siop = siop_softc[unit]; + regs = siop->regs; + + /* ack interrupt */ + .... +} + + +siop_target_intr(siop) + register siop_softc_t siop; +{ + panic("SIOP: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ + +#endif /*NSIOP > 0*/ + diff --git a/scsi/adapters/scsi_53C94.h b/scsi/adapters/scsi_53C94.h new file mode 100644 index 0000000..82891f3 --- /dev/null +++ b/scsi/adapters/scsi_53C94.h @@ -0,0 +1,253 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_53C94.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Defines for the NCR 53C94 ASC (SCSI interface) + * Some gotcha came from the "86C01/53C94 DMA lab work" written + * by Ken Stewart (NCR MED Logic Products Applications Engineer) + * courtesy of NCR. Thanks Ken ! + */ + +/* + * Register map + */ + +typedef struct { + volatile unsigned char asc_tc_lsb; /* rw: Transfer Counter LSB */ + volatile unsigned char asc_tc_msb; /* rw: Transfer Counter MSB */ + volatile unsigned char asc_fifo; /* rw: FIFO top */ + volatile unsigned char asc_cmd; /* rw: Command */ + volatile unsigned char asc_csr; /* r: Status */ +#define asc_dbus_id asc_csr /* w: Destination Bus ID */ + volatile unsigned char asc_intr; /* r: Interrupt */ +#define asc_sel_timo asc_intr /* w: (re)select timeout */ + volatile unsigned char asc_ss; /* r: Sequence Step */ +#define asc_syn_p asc_ss /* w: synchronous period */ + volatile unsigned char asc_flags; /* r: FIFO flags + seq step */ +#define asc_syn_o asc_flags /* w: synchronous offset */ + volatile unsigned char asc_cnfg1; /* rw: Configuration 1 */ + volatile unsigned char asc_ccf; /* w: Clock Conv. Factor */ + volatile unsigned char asc_test; /* w: Test Mode */ + volatile unsigned char asc_cnfg2; /* rw: Configuration 2 */ + volatile unsigned char asc_cnfg3; /* rw: Configuration 3 */ + volatile unsigned char asc_rfb; /* w: Reserve FIFO byte */ +} asc_regmap_t; + + +/* + * Transfer Count: access macros + * That a NOP is required after loading the dma counter + * I learned on the NCR test code. Sic. + */ + +#define ASC_TC_MAX 0x10000 + +#define ASC_TC_GET(ptr,val) \ + val = ((ptr)->asc_tc_lsb&0xff)|(((ptr)->asc_tc_msb&0xff)<<8) +#define ASC_TC_PUT(ptr,val) \ + (ptr)->asc_tc_lsb=(val); \ + (ptr)->asc_tc_msb=(val)>>8; mb(); \ + (ptr)->asc_cmd = ASC_CMD_NOP|ASC_CMD_DMA; + +/* + * FIFO register + */ + +#define ASC_FIFO_DEEP 16 + + +/* + * Command register (command codes) + */ + +#define ASC_CMD_DMA 0x80 + /* Miscellaneous */ +#define ASC_CMD_NOP 0x00 +#define ASC_CMD_FLUSH 0x01 +#define ASC_CMD_RESET 0x02 +#define ASC_CMD_BUS_RESET 0x03 + /* Initiator state */ +#define ASC_CMD_XFER_INFO 0x10 +#define ASC_CMD_I_COMPLETE 0x11 +#define ASC_CMD_MSG_ACPT 0x12 +#define ASC_CMD_XFER_PAD 0x18 +#define ASC_CMD_SET_ATN 0x1a +#define ASC_CMD_CLR_ATN 0x1b + /* Target state */ +#define ASC_CMD_SND_MSG 0x20 +#define ASC_CMD_SND_STATUS 0x21 +#define ASC_CMD_SND_DATA 0x22 +#define ASC_CMD_DISC_SEQ 0x23 +#define ASC_CMD_TERM 0x24 +#define ASC_CMD_T_COMPLETE 0x25 +#define ASC_CMD_DISC 0x27 +#define ASC_CMD_RCV_MSG 0x28 +#define ASC_CMD_RCV_CDB 0x29 +#define ASC_CMD_RCV_DATA 0x2a +#define ASC_CMD_RCV_CMD 0x2b +#define ASC_CMD_ABRT_DMA 0x04 + /* Disconnected state */ +#define ASC_CMD_RESELECT 0x40 +#define ASC_CMD_SEL 0x41 +#define ASC_CMD_SEL_ATN 0x42 +#define ASC_CMD_SEL_ATN_STOP 0x43 +#define ASC_CMD_ENABLE_SEL 0x44 +#define ASC_CMD_DISABLE_SEL 0x45 +#define ASC_CMD_SEL_ATN3 0x46 + +/* this is approximate (no ATN3) but good enough */ +#define asc_isa_select(cmd) (((cmd)&0x7c)==0x40) + +/* + * Status register, and phase encoding + */ + +#define ASC_CSR_INT 0x80 +#define ASC_CSR_GE 0x40 +#define ASC_CSR_PE 0x20 +#define ASC_CSR_TC 0x10 +#define ASC_CSR_VGC 0x08 +#define ASC_CSR_MSG 0x04 +#define ASC_CSR_CD 0x02 +#define ASC_CSR_IO 0x01 + +#define ASC_PHASE(csr) SCSI_PHASE(csr) + +/* + * Destination Bus ID + */ + +#define ASC_DEST_ID_MASK 0x07 + + +/* + * Interrupt register + */ + +#define ASC_INT_RESET 0x80 +#define ASC_INT_ILL 0x40 +#define ASC_INT_DISC 0x20 +#define ASC_INT_BS 0x10 +#define ASC_INT_FC 0x08 +#define ASC_INT_RESEL 0x04 +#define ASC_INT_SEL_ATN 0x02 +#define ASC_INT_SEL 0x01 + + +/* + * Timeout register: + * + * val = (timeout * CLK_freq) / (8192 * CCF); + */ + +#define asc_timeout_250(clk,ccf) ((31*clk)/ccf) + +/* + * Sequence Step register + */ + +#define ASC_SS_XXXX 0xf0 +#define ASC_SS_SOM 0x80 +#define ASC_SS_MASK 0x07 +#define ASC_SS(ss) ((ss)&ASC_SS_MASK) + +/* + * Synchronous Transfer Period + */ + +#define ASC_STP_MASK 0x1f +#define ASC_STP_MIN 0x05 /* 5 clk per byte */ +#define ASC_STP_MAX 0x04 /* after ovfl, 35 clk/byte */ + +/* + * FIFO flags + */ + +#define ASC_FLAGS_SEQ_STEP 0xe0 +#define ASC_FLAGS_FIFO_CNT 0x1f + +/* + * Synchronous offset + */ + +#define ASC_SYNO_MASK 0x0f /* 0 -> asyn */ + +/* + * Configuration 1 + */ + +#define ASC_CNFG1_SLOW 0x80 +#define ASC_CNFG1_SRD 0x40 +#define ASC_CNFG1_P_TEST 0x20 +#define ASC_CNFG1_P_CHECK 0x10 +#define ASC_CNFG1_TEST 0x08 +#define ASC_CNFG1_MY_BUS_ID 0x07 + +/* + * CCF register + */ + +#define ASC_CCF_10MHz 0x2 +#define ASC_CCF_15MHz 0x3 +#define ASC_CCF_20MHz 0x4 +#define ASC_CCF_25MHz 0x5 + +#define mhz_to_ccf(x) (((x-1)/5)+1) /* see specs for limits */ + +/* + * Test register + */ + +#define ASC_TEST_XXXX 0xf8 +#define ASC_TEST_HI_Z 0x04 +#define ASC_TEST_I 0x02 +#define ASC_TEST_T 0x01 + +/* + * Configuration 2 + */ + +#define ASC_CNFG2_RFB 0x80 +#define ASC_CNFG2_EPL 0x40 +#define ASC_CNFG2_EBC 0x20 +#define ASC_CNFG2_DREQ_HIZ 0x10 +#define ASC_CNFG2_SCSI2 0x08 +#define ASC_CNFG2_BPA 0x04 +#define ASC_CNFG2_RPE 0x02 +#define ASC_CNFG2_DPE 0x01 + +/* + * Configuration 3 + */ + +#define ASC_CNFG3_XXXX 0xf8 +#define ASC_CNFG3_SRB 0x04 +#define ASC_CNFG3_ALT_DMA 0x02 +#define ASC_CNFG3_T8 0x01 + diff --git a/scsi/adapters/scsi_53C94_hdw.c b/scsi/adapters/scsi_53C94_hdw.c new file mode 100644 index 0000000..dad9b22 --- /dev/null +++ b/scsi/adapters/scsi_53C94_hdw.c @@ -0,0 +1,2840 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,1991,1990,1989 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: scsi_53C94_hdw.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the NCR 53C94 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <asc.h> +#if NASC > 0 +#include <platforms.h> + +#ifdef DECSTATION +typedef unsigned char asc_register_t; +#define PAD(n) char n[3]; +#define mb() +#ifdef MACH_KERNEL +#define HAS_MAPPED_SCSI +#endif +#define ASC_PROBE_DYNAMICALLY FALSE /* established custom */ +#define DEBUG 1 +#define TRACE 1 +#endif + +#ifdef FLAMINGO +typedef unsigned int asc_register_t; +#define PAD(n) int n; /* sparse ! */ +#define mb() wbflush() /* memory barrier */ +#define ASC_PROBE_DYNAMICALLY TRUE +#define DEBUG 1 +#define TRACE 1 +#endif + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> + +#include <scsi/adapters/scsi_53C94.h> +#include <scsi/scsi_defs.h> +#include <scsi/adapters/scsi_dma.h> + +#define private static + +#ifdef PAD +typedef struct { + volatile asc_register_t asc_tc_lsb; /* rw: Transfer Counter LSB */ + PAD(pad0) + volatile asc_register_t asc_tc_msb; /* rw: Transfer Counter MSB */ + PAD(pad1) + volatile asc_register_t asc_fifo; /* rw: FIFO top */ + PAD(pad2) + volatile asc_register_t asc_cmd; /* rw: Command */ + PAD(pad3) + volatile asc_register_t asc_csr; /* r: Status */ +/*#define asc_dbus_id asc_csr /* w: Destination Bus ID */ + PAD(pad4) + volatile asc_register_t asc_intr; /* r: Interrupt */ +/*#define asc_sel_timo asc_intr /* w: (re)select timeout */ + PAD(pad5) + volatile asc_register_t asc_ss; /* r: Sequence Step */ +/*#define asc_syn_p asc_ss /* w: synchronous period */ + PAD(pad6) + volatile asc_register_t asc_flags; /* r: FIFO flags + seq step */ +/*#define asc_syn_o asc_flags /* w: synchronous offset */ + PAD(pad7) + volatile asc_register_t asc_cnfg1; /* rw: Configuration 1 */ + PAD(pad8) + volatile asc_register_t asc_ccf; /* w: Clock Conv. Factor */ + PAD(pad9) + volatile asc_register_t asc_test; /* w: Test Mode */ + PAD(pad10) + volatile asc_register_t asc_cnfg2; /* rw: Configuration 2 */ + PAD(pad11) + volatile asc_register_t asc_cnfg3; /* rw: Configuration 3 */ + PAD(pad12) + volatile asc_register_t asc_rfb; /* w: Reserve FIFO byte */ + PAD(pad13) +} asc_padded_regmap_t; + +#else /* !PAD */ + +typedef asc_regmap_t asc_padded_regmap_t; + +#endif /* !PAD */ + +#define get_reg(r,x) ((unsigned char)((r)->x)) + +#define fifo_count(r) ((r)->asc_flags & ASC_FLAGS_FIFO_CNT) +#define get_fifo(r) get_reg(r,asc_fifo) + +boolean_t asc_probe_dynamically = ASC_PROBE_DYNAMICALLY; + +/* + * We might need to use some fields usually + * handled by the DMA engine, if asked to. + * These are "dma_ptr" and "hba_dep". + */ +#define has_oddb hba_dep[0] +#define the_oddb hba_dep[1] + +/* + * A script has a three parts: a pre-condition, an action, and + * an optional command to the chip. The first triggers error + * handling if not satisfied and in our case it is a match + * of the expected and actual scsi-bus phases. + * The action part is just a function pointer, and the + * command is what the 53c90 should be told to do at the end + * of the action processing. This command is only issued and the + * script proceeds if the action routine returns TRUE. + * See asc_intr() for how and where this is all done. + */ + +typedef struct script { + unsigned char condition; /* expected state at interrupt */ + unsigned char command; /* command to the chip */ + unsigned short flags; /* unused padding */ + boolean_t (*action)(); /* extra operations */ +} *script_t; + +/* Matching on the condition value */ +#define ANY 0xff +#define SCRIPT_MATCH(csr,ir,value) ((SCSI_PHASE(csr)==(value)) || \ + (((value)==ANY) && \ + ((ir)&(ASC_INT_DISC|ASC_INT_FC)))) + +/* When no command is needed */ +#define SCRIPT_END -1 + +/* forward decls of script actions */ +boolean_t + asc_end(), /* all come to an end */ + asc_clean_fifo(), /* .. in preparation for status byte */ + asc_get_status(), /* get status from target */ + asc_put_status(), /* send status to initiator */ + asc_dma_in(), /* get data from target via dma */ + asc_dma_in_r(), /* get data from target via dma (restartable)*/ + asc_dma_out(), /* send data to target via dma */ + asc_dma_out_r(), /* send data to target via dma (restartable) */ + asc_dosynch(), /* negotiate synch xfer */ + asc_msg_in(), /* receive the disconenct message */ + asc_disconnected(), /* target has disconnected */ + asc_reconnect(); /* target reconnected */ + +/* forward decls of error handlers */ +boolean_t + asc_err_generic(), /* generic handler */ + asc_err_disconn(), /* target disconnects amidst */ + gimmeabreak(); /* drop into the debugger */ + +int asc_reset_scsibus(); +boolean_t asc_probe_target(); +private asc_wait(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) SCSI-53c90 interface + */ +struct asc_softc { + watchdog_t wd; + asc_padded_regmap_t *regs; /* 53c90 registers */ + + scsi_dma_ops_t *dma_ops; /* DMA operations and state */ + opaque_t dma_state; + + script_t script; /* what should happen next */ + boolean_t (*error_handler)();/* what if something is wrong */ + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define ASC_STATE_BUSY 0x01 /* selecting or currently connected */ +#define ASC_STATE_TARGET 0x04 /* currently selected as target */ +#define ASC_STATE_COLLISION 0x08 /* lost selection attempt */ +#define ASC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ +#define ASC_STATE_SPEC_DMA 0x20 /* special, 8 byte threshold dma */ +#define ASC_STATE_DO_RFB 0x40 /* DMA engine cannot handle odd bytes */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char extra_count; /* sleazy trick to spare an interrupt */ + int dmacnt_at_end; + + scsi_softc_t *sc; /* HBA-indep info */ + target_info_t *active_target; /* the current one */ + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + + unsigned char ss_was; /* districate powered on/off devices */ + unsigned char cmd_was; + + unsigned char timeout; /* cache a couple numbers */ + unsigned char ccf; + unsigned char clk; + +} asc_softc_data[NASC]; + +typedef struct asc_softc *asc_softc_t; + +asc_softc_t asc_softc[NASC]; + +/* + * Synch xfer parameters, and timing conversions + */ +int asc_min_period = 5; /* in CLKS/BYTE, e.g. 1 CLK = 40nsecs @25 Mhz */ +int asc_max_offset = 15; /* pure number */ + +int asc_to_scsi_period(a,clk) +{ + /* Note: the SCSI unit is 4ns, hence + A_P * 1,000,000,000 + ------------------- = S_P + C_Mhz * 4 + */ + return a * (250 / clk); + +} + +int scsi_period_to_asc(p,clk) +{ + register int ret; + + ret = (p * clk) / 250; + if (ret < asc_min_period) + return asc_min_period; + if ((asc_to_scsi_period(ret,clk)) < p) + return ret + 1; + return ret; +} + +#define readback(a) {register int foo; foo = a; mb();} + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + +/* + * Definition of the controller for the auto-configuration program. + */ + +int asc_probe(), scsi_slave(), asc_go(), asc_intr(); +void scsi_attach(); + +vm_offset_t asc_std[NASC] = { 0 }; +struct bus_device *asc_dinfo[NASC*8]; +struct bus_ctlr *asc_minfo[NASC]; +struct bus_driver asc_driver = + { asc_probe, scsi_slave, scsi_attach, asc_go, asc_std, "rz", asc_dinfo, + "asc", asc_minfo, BUS_INTR_B4_PROBE}; + + +int asc_clock_speed_in_mhz[NASC] = {25,25,25,25}; /* original 3max */ + +asc_set_dmaops(unit, dmaops) + unsigned int unit; + scsi_dma_ops_t *dmaops; +{ + if (unit < NASC) + asc_std[unit] = (vm_offset_t)dmaops; +} + +/* + * Scripts + */ +struct script +asc_script_data_in[] = { /* started with SEL & DMA */ + {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in}, + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_data_out[] = { /* started with SEL & DMA */ + {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out}, + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_try_synch[] = { + {SCSI_PHASE_MSG_OUT, ASC_CMD_I_COMPLETE,0, asc_dosynch}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_simple_cmd[] = { + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_disconnect[] = { + {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected}, +/**/ {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect} +}, + +asc_script_restart_data_in[] = { /* starts after disconnect */ + {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in_r}, + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_restart_data_out[] = { /* starts after disconnect */ + {SCSI_PHASE_DATAO, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_out_r}, + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +#if documentation +/* + * This is what might happen during a read + * that disconnects + */ +asc_script_data_in_wd[] = { /* started with SEL & DMA & allow disconnect */ + {SCSI_PHASE_MSG_IN, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_msg_in}, + {ANY, ASC_CMD_ENABLE_SEL, 0, asc_disconnected}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_reconnect}, + {SCSI_PHASE_DATAI, ASC_CMD_XFER_INFO|ASC_CMD_DMA, 0, asc_dma_in}, + {SCSI_PHASE_STATUS, ASC_CMD_I_COMPLETE, 0, asc_clean_fifo}, + {SCSI_PHASE_MSG_IN, ASC_CMD_MSG_ACPT, 0, asc_get_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, +#endif + +/* + * Target mode scripts + */ +asc_script_t_data_in[] = { + {SCSI_PHASE_CMD, ASC_CMD_RCV_DATA|ASC_CMD_DMA, 0, asc_dma_in_r}, + {SCSI_PHASE_DATAO, ASC_CMD_TERM, 0, asc_put_status}, + {ANY, SCRIPT_END, 0, asc_end} +}, + +asc_script_t_data_out[] = { + {SCSI_PHASE_CMD, ASC_CMD_SND_DATA|ASC_CMD_DMA, 0, asc_dma_out_r}, + {SCSI_PHASE_DATAI, ASC_CMD_TERM, 0, asc_put_status}, + {ANY, SCRIPT_END, 0, asc_end} +}; + + +#ifdef DEBUG + +#define PRINT(x) if (scsi_debug) printf x + +asc_state(regs) + asc_padded_regmap_t *regs; +{ + register unsigned char ff,csr,ir,d0,d1,cmd; + + if (regs == 0) { + if (asc_softc[0]) + regs = asc_softc[0]->regs; + else + regs = (asc_padded_regmap_t*)0xbf400000; + } + ff = get_reg(regs,asc_flags); + csr = get_reg(regs,asc_csr); +/* ir = get_reg(regs,asc_intr); nope, clears interrupt */ + d0 = get_reg(regs,asc_tc_lsb); + d1 = get_reg(regs,asc_tc_msb); + cmd = get_reg(regs,asc_cmd); + printf("dma %x ff %x csr %x cmd %x\n", + (d1 << 8) | d0, ff, csr, cmd); + return 0; +} + +asc_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = asc_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("@x%x: fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", + tgt, + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, + tgt->cmd_ptr, (long)tgt->target_id, + (long)tgt->sync_period, (long)tgt->sync_offset, + tgt->ior, (long)tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym(spt,1); + db_printf(": %x %x ", spt->condition, spt->command); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +asc_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = asc_softc[unit]->sc->target[i]; + if (tgt) + asc_target_state(tgt); + } +} + +asc_script_state(unit) +{ + script_t spt = asc_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %x %x ", spt->condition, spt->command); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(asc_softc[unit]->error_handler, 1); + return 0; +} + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + + +#ifdef TRACE + +#define LOGSIZE 256 +int asc_logpt; +char asc_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x42 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +/* private */ LOG(e,f) + char *f; +{ + asc_log[asc_logpt++] = (e); + if (asc_logpt == LOGSIZE) asc_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +asc_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = asc_logpt; i < LOGSIZE; i++) { + c = asc_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-x%x", c & 0x7f); + } +} + +asc_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /*TRACE*/ +#define LOG(e,f) +#define LOGSIZE +#endif /*TRACE*/ + +#else /*DEBUG*/ +#define PRINT(x) +#define LOG(e,f) +#define LOGSIZE +#define TR(x) +#define TRCHECK +#define TRWRAP + +#endif /*DEBUG*/ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send a test-unit-ready to each possible target on the bus + * except of course ourselves. + */ +asc_probe(reg, ui) + vm_offset_t reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + asc_softc_t asc = &asc_softc_data[unit]; + int target_id; + scsi_softc_t *sc; + register asc_padded_regmap_t *regs; + spl_t s; + boolean_t did_banner = FALSE; + + /* + * We are only called if the right board is there, + * but make sure anyways.. + */ + if (check_memory(reg, 0)) + return 0; + +#if defined(HAS_MAPPED_SCSI) + /* Mappable version side */ + ASC_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor, cache some pointers + */ + asc_softc[unit] = asc; + asc->regs = (asc_padded_regmap_t *) (reg); + + if ((asc->dma_ops = (scsi_dma_ops_t *)asc_std[unit]) == 0) + /* use same as unit 0 if undefined */ + asc->dma_ops = (scsi_dma_ops_t *)asc_std[0]; + { + int dma_bsize = 16; /* bits, preferred */ + boolean_t do_rfb = FALSE; + + asc->dma_state = (*asc->dma_ops->init)(unit, reg, &dma_bsize, &do_rfb); + if (dma_bsize > 16) + asc->state |= ASC_STATE_SPEC_DMA; + if (do_rfb) + asc->state |= ASC_STATE_DO_RFB; + } + + queue_init(&asc->waiting_targets); + + asc->clk = asc_clock_speed_in_mhz[unit]; + asc->ccf = mhz_to_ccf(asc->clk); /* see .h file */ + asc->timeout = asc_timeout_250(asc->clk,asc->ccf); + + sc = scsi_master_alloc(unit, asc); + asc->sc = sc; + + sc->go = asc_go; + sc->watchdog = scsi_watchdog; + sc->probe = asc_probe_target; + asc->wd.reset = asc_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + regs = asc->regs; + + /* + * Our SCSI id on the bus. + * The user can set this via the prom on 3maxen/pmaxen. + * If this changes it is easy to fix: make a default that + * can be changed as boot arg. + */ + { + register unsigned char my_id; + + my_id = scsi_initiator_id[unit] & 0x7; + if (my_id != 7) + regs->asc_cnfg1 = my_id; mb(); + } + + /* + * Reset chip, fully. Note that interrupts are already enabled. + */ + s = splbio(); + asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA); + + sc->initiator_id = regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID; + printf("%s%d: SCSI id %d", ui->name, unit, sc->initiator_id); + + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(sc->masterno, sc->initiator_id, asc); + (*asc->dma_ops->new_target)(asc->dma_state, tgt); + sccpu_new_initiator(tgt, tgt); + } + + if (asc_probe_dynamically) + printf("%s", ", will probe targets on demand"); + else { + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + for (target_id = 0; target_id < 8; target_id++) { + register unsigned char csr, ss, ir, ff; + register scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + regs->asc_cmd = ASC_CMD_FLUSH; /* empty fifo */ + mb(); + delay(2); + + regs->asc_dbus_id = target_id; mb(); + regs->asc_sel_timo = asc->timeout; mb(); + + /* + * See if the unit is ready. + * XXX SHOULD inquiry LUN 0 instead !!! + */ + regs->asc_fifo = SCSI_CMD_TEST_UNIT_READY; mb(); + regs->asc_fifo = 0; mb(); + regs->asc_fifo = 0; mb(); + regs->asc_fifo = 0; mb(); + regs->asc_fifo = 0; mb(); + regs->asc_fifo = 0; mb(); + + /* select and send it */ + regs->asc_cmd = ASC_CMD_SEL; mb(); + + /* wait for the chip to complete, or timeout */ + csr = asc_wait(regs, ASC_CSR_INT, 1); + ss = get_reg(regs,asc_ss); + ir = get_reg(regs,asc_intr); + + /* empty fifo, there is garbage in it if timeout */ + regs->asc_cmd = ASC_CMD_FLUSH; mb(); + delay(2); + + /* + * Check if the select timed out + */ + if ((ASC_SS(ss) == 0) && (ir == ASC_INT_DISC)) + /* noone out there */ + continue; + + if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) { + printf( " %s%d%s", "ignoring target at ", target_id, + " cuz it acts weirdo"); + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + regs->asc_cmd = ASC_CMD_I_COMPLETE; + wbflush(); + csr = asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); /* ack intr */ + mb(); + + status.bits = get_fifo(regs); /* empty fifo */ + mb(); + ff = get_fifo(regs); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); + csr = asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); /* ack intr */ + mb(); + + /* + * Found a target + */ + asc->ntargets++; + { + register target_info_t *tgt; + tgt = scsi_slave_alloc(sc->masterno, target_id, asc); + + (*asc->dma_ops->new_target)(asc->dma_state, tgt); + } + } + } /* asc_probe_dynamically */ + + regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb(); + + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +asc_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + asc_softc_t asc = asc_softc[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + (*asc->dma_ops->new_target)(asc->dma_state, tgt); + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + asc->ntargets++; + tgt->flags = TGT_ALIVE; + return TRUE; +} + +private asc_wait(regs, until, complain) + asc_padded_regmap_t *regs; +{ + int timeo = 1000000; + while ((regs->asc_csr & until) == 0) { + mb(); + delay(1); + if (!timeo--) { + if (complain) + printf("asc_wait TIMEO with x%x\n", get_reg(regs,asc_csr)); + break; + } + } + return get_reg(regs,asc_csr); +} + +asc_reset(asc, quick, special_dma) + asc_softc_t asc; +{ + char my_id; + int ccf; + asc_padded_regmap_t *regs; + + regs = asc->regs; + + /* preserve our ID for now */ + my_id = (regs->asc_cnfg1 & ASC_CNFG1_MY_BUS_ID); + + /* + * Reset chip and wait till done + */ + regs->asc_cmd = ASC_CMD_RESET; + wbflush(); delay(25); + + /* spec says this is needed after reset */ + regs->asc_cmd = ASC_CMD_NOP; + wbflush(); delay(25); + + /* + * Set up various chip parameters + */ + regs->asc_ccf = asc->ccf; + wbflush(); + delay(25); + regs->asc_sel_timo = asc->timeout; mb(); + /* restore our ID */ + regs->asc_cnfg1 = my_id | ASC_CNFG1_P_CHECK; mb(); + regs->asc_cnfg2 = ASC_CNFG2_SCSI2; + mb(); + regs->asc_cnfg3 = special_dma ? (ASC_CNFG3_T8|ASC_CNFG3_ALT_DMA) : 0; + mb(); + /* zero anything else */ + ASC_TC_PUT(regs, 0); mb(); + regs->asc_syn_p = asc_min_period; mb(); + regs->asc_syn_o = 0; mb(); /* asynch for now */ + + regs->asc_cmd = ASC_CMD_ENABLE_SEL; mb(); + + if (quick) return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call asc_bus_reset(). + */ + regs->asc_cmd = ASC_CMD_BUS_RESET; mb(); +} + + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +asc_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + asc_softc_t asc; + register spl_t s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + asc = (asc_softc_t)tgt->hw_state; + + tgt->transient_state.cmd_count = cmd_count; /* keep it here */ + tgt->transient_state.out_count = 0; /* default */ + + (*asc->dma_ops->map)(asc->dma_state, tgt); + + disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); + disconn = disconn && (asc->ntargets > 1); + disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? asc_err_disconn : asc_err_generic; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(2,"readop"); + scp = asc_script_data_in; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x18,"writeop"); + scp = asc_script_data_out; + break; + case SCSI_CMD_INQUIRY: + /* This is likely the first thing out: + do the synch neg if so */ + if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) { + scp = asc_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + disconn = FALSE; + break; + } + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ + case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + scp = asc_script_data_in; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ + tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd); + tgt->transient_state.out_count = + cmd_count - tgt->transient_state.cmd_count; + scp = asc_script_data_out; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_TEST_UNIT_READY: + /* + * Do the synch negotiation here, unless prohibited + * or done already + */ + if (tgt->flags & TGT_DID_SYNCH) { + scp = asc_script_simple_cmd; + } else { + scp = asc_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + cmd_only = FALSE; + disconn = FALSE; + } + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + scp = asc_script_simple_cmd; + } + + tgt->transient_state.script = scp; + tgt->transient_state.handler = handler; + tgt->transient_state.identify = (cmd_only) ? 0xff : + (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT : + SCSI_IDENTIFY); + + if (in_count) + tgt->transient_state.in_count = + (in_count < tgt->block_size) ? tgt->block_size : in_count; + else + tgt->transient_state.in_count = 0; + + /* + * See if another target is currently selected on + * this SCSI bus, e.g. lock the asc structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + * XXX here and everywhere, locks! + */ + /* + * Protection viz reconnections makes it tricky. + */ + s = splbio(); + + if (asc->wd.nactive++ == 0) + asc->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (asc->state & ASC_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + enqueue_tail(&asc->waiting_targets, (queue_entry_t) tgt); + } else { + /* + * It is down to at most two contenders now, + * we will treat reconnections same as selections + * and let the scsi-bus arbitration process decide. + */ + asc->state |= ASC_STATE_BUSY; + asc->next_target = tgt; + asc_attempt_selection(asc); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +asc_attempt_selection(asc) + asc_softc_t asc; +{ + target_info_t *tgt; + asc_padded_regmap_t *regs; + register int out_count; + + regs = asc->regs; + tgt = asc->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * We own the bus now.. unless we lose arbitration + */ + asc->active_target = tgt; + + /* Try to avoid reselect collisions */ + if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) == + (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) + return; + + /* + * Cleanup the FIFO + */ + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + /* + * This value is not from spec, I have seen it failing + * without this delay and with logging disabled. That had + * about 42 extra instructions @25Mhz. + */ + delay(2);/* XXX time & move later */ + + + /* + * Init bus state variables + */ + asc->script = tgt->transient_state.script; + asc->error_handler = tgt->transient_state.handler; + asc->done = SCSI_RET_IN_PROGRESS; + + asc->out_count = 0; + asc->in_count = 0; + asc->extra_count = 0; + + /* + * Start the chip going + */ + out_count = (*asc->dma_ops->start_cmd)(asc->dma_state, tgt); + if (tgt->transient_state.identify != 0xff) { + regs->asc_fifo = tgt->transient_state.identify | tgt->lun; + mb(); + } + ASC_TC_PUT(regs, out_count); + readback(regs->asc_cmd); + + regs->asc_dbus_id = tgt->target_id; + readback(regs->asc_dbus_id); + + regs->asc_sel_timo = asc->timeout; + readback(regs->asc_sel_timo); + + regs->asc_syn_p = tgt->sync_period; + readback(regs->asc_syn_p); + + regs->asc_syn_o = tgt->sync_offset; + readback(regs->asc_syn_o); + + /* ugly little help for compiler */ +#define command out_count + if (tgt->flags & TGT_DID_SYNCH) { + command = (tgt->transient_state.identify == 0xff) ? + ASC_CMD_SEL | ASC_CMD_DMA : + ASC_CMD_SEL_ATN | ASC_CMD_DMA; /*preferred*/ + } else if (tgt->flags & TGT_TRY_SYNCH) + command = ASC_CMD_SEL_ATN_STOP; + else + command = ASC_CMD_SEL | ASC_CMD_DMA; + + /* Try to avoid reselect collisions */ + if ((regs->asc_csr & (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) != + (ASC_CSR_INT|SCSI_PHASE_MSG_IN)) { + register int tmp; + + regs->asc_cmd = command; mb(); + /* + * Very nasty but infrequent problem here. We got/will get + * reconnected but the chip did not interrupt. The watchdog would + * fix it allright, but it stops the machine before it expires! + * Too bad we cannot just look at the interrupt register, sigh. + */ + tmp = get_reg(regs,asc_cmd); + if ((tmp != command) && (tmp == (ASC_CMD_NOP|ASC_CMD_DMA))) { + if ((regs->asc_csr & ASC_CSR_INT) == 0) { + delay(250); /* increase if in trouble */ + + if (get_reg(regs,asc_csr) == SCSI_PHASE_MSG_IN) { + /* ok, take the risk of reading the ir */ + tmp = get_reg(regs,asc_intr); mb(); + if (tmp & ASC_INT_RESEL) { + (void) asc_reconnect(asc, get_reg(regs,asc_csr), tmp); + asc_wait(regs, ASC_CSR_INT, 1); + tmp = get_reg(regs,asc_intr); mb(); + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + } else /* does not happen, but who knows.. */ + asc_reset(asc,FALSE,asc->state & ASC_STATE_SPEC_DMA); + } + } + } + } +#undef command +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +asc_intr(unit, spllevel) + spl_t spllevel; +{ + register asc_softc_t asc; + register script_t scp; + register int ir, csr; + register asc_padded_regmap_t *regs; +#if defined(HAS_MAPPED_SCSI) + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return ASC_intr(unit,spllevel); +#endif + + asc = asc_softc[unit]; + + LOG(5,"\n\tintr"); + /* collect ephemeral information */ + regs = asc->regs; + csr = get_reg(regs,asc_csr); mb(); + asc->ss_was = get_reg(regs,asc_ss); mb(); + asc->cmd_was = get_reg(regs,asc_cmd); mb(); + + /* drop spurious interrupts */ + if ((csr & ASC_CSR_INT) == 0) + return; + + ir = get_reg(regs,asc_intr); /* this re-latches CSR (and SSTEP) */ + mb(); + +TR(csr);TR(ir);TR(get_reg(regs,asc_cmd));TRCHECK + + /* this must be done within 250msec of disconnect */ + if (ir & ASC_INT_DISC) { + regs->asc_cmd = ASC_CMD_ENABLE_SEL; + readback(regs->asc_cmd); + } + + if (ir & ASC_INT_RESET) + return asc_bus_reset(asc); + + /* we got an interrupt allright */ + if (asc->active_target) + asc->wd.watchdog_state = SCSI_WD_ACTIVE; + +#if mips + splx(spllevel); /* drop priority */ +#endif + + if ((asc->state & ASC_STATE_TARGET) || + (ir & (ASC_INT_SEL|ASC_INT_SEL_ATN))) + return asc_target_intr(asc, csr, ir); + + /* + * In attempt_selection() we could not check the asc_intr + * register to see if a reselection was in progress [else + * we would cancel the interrupt, and it would not be safe + * anyways]. So we gave the select command even if sometimes + * the chip might have been reconnected already. What + * happens then is that we get an illegal command interrupt, + * which is why the second clause. Sorry, I'd do it better + * if I knew of a better way. + */ + if ((ir & ASC_INT_RESEL) || + ((ir & ASC_INT_ILL) && (regs->asc_cmd & ASC_CMD_SEL_ATN))) + return asc_reconnect(asc, csr, ir); + + /* + * Check for various errors + */ + if ((csr & (ASC_CSR_GE|ASC_CSR_PE)) || (ir & ASC_INT_ILL)) { + char *msg; +printf("{E%x,%x}", csr, ir); + if (csr & ASC_CSR_GE) + return;/* sit and prey? */ + + if (csr & ASC_CSR_PE) + msg = "SCSI bus parity error"; + if (ir & ASC_INT_ILL) + msg = "Chip sez Illegal Command"; + /* all we can do is to throw a reset on the bus */ + printf( "asc%d: %s%s", asc - asc_softc_data, msg, + ", attempting recovery.\n"); + asc_reset(asc, FALSE, asc->state & ASC_STATE_SPEC_DMA); + return; + } + + if ((scp = asc->script) == 0) /* sanity */ + return; + + LOG(6,"match"); + if (SCRIPT_MATCH(csr,ir,scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(asc, csr, ir)) { + asc->script = scp + 1; + regs->asc_cmd = scp->command; mb(); + } + } else + (void) (*asc->error_handler)(asc, csr, ir); +} + +asc_target_intr(asc, csr, ir) + register asc_softc_t asc; + +{ + register script_t scp; + + LOG(0x1e,"tmode"); + + if ((asc->state & ASC_STATE_TARGET) == 0) { + + /* + * We just got selected + */ + asc->state |= ASC_STATE_TARGET; + + /* + * See if this selection collided with our selection attempt + */ + if (asc->state & ASC_STATE_BUSY) + asc->state |= ASC_STATE_COLLISION; + asc->state |= ASC_STATE_BUSY; + + return asc_selected(asc, csr, ir); + + } + /* We must be executing a script here */ + scp = asc->script; + assert(scp != 0); + + LOG(6,"match"); + if (SCRIPT_MATCH(csr,ir,scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(asc, csr, ir)) { + asc->script = scp + 1; + asc->regs->asc_cmd = scp->command; mb(); + } + } else + (void) (*asc->error_handler)(asc, csr, ir); + +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ +boolean_t +asc_clean_fifo(asc, csr, ir) + register asc_softc_t asc; + +{ + register asc_padded_regmap_t *regs = asc->regs; + register char ff; + + ASC_TC_GET(regs,asc->dmacnt_at_end); + + ASC_TC_PUT(regs,0); /* stop dma engine */ + readback(regs->asc_cmd); + + LOG(7,"clean_fifo"); + + while (fifo_count(regs)) { + ff = get_fifo(regs); + mb(); + } + return TRUE; +} + +boolean_t +asc_end(asc, csr, ir) + register asc_softc_t asc; +{ + register target_info_t *tgt; + register io_req_t ior; + + LOG(8,"end"); + + asc->state &= ~ASC_STATE_TARGET; + asc->regs->asc_syn_p = 0; mb(); + asc->regs->asc_syn_o = 0; mb(); + + tgt = asc->active_target; + if ((tgt->done = asc->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + asc->script = 0; + + if (asc->wd.nactive-- == 1) + asc->wd.watchdog_state = SCSI_WD_INACTIVE; + + asc_release_bus(asc); + + if (ior = tgt->ior) { + /* + * WARNING: the above might have scheduled the + * DMA engine off to someone else. Keep it in + * mind in the following code + */ + (*asc->dma_ops->end_cmd)(asc->dma_state, tgt, ior); + + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + } + + return FALSE; +} + +boolean_t +asc_release_bus(asc) + register asc_softc_t asc; +{ + boolean_t ret = TRUE; + + LOG(9,"release"); + if (asc->state & ASC_STATE_COLLISION) { + + LOG(0xB,"collided"); + asc->state &= ~ASC_STATE_COLLISION; + asc_attempt_selection(asc); + + } else if (queue_empty(&asc->waiting_targets)) { + + asc->state &= ~ASC_STATE_BUSY; + asc->active_target = 0; + asc->script = 0; + ret = FALSE; + + } else { + + LOG(0xC,"dequeue"); + asc->next_target = (target_info_t *) + dequeue_head(&asc->waiting_targets); + asc_attempt_selection(asc); + } + return ret; +} + +boolean_t +asc_get_status(asc, csr, ir) + register asc_softc_t asc; +{ + register asc_padded_regmap_t *regs = asc->regs; + register scsi2_status_byte_t status; + int len; + boolean_t ret; + io_req_t ior; + register target_info_t *tgt = asc->active_target; + + LOG(0xD,"get_status"); +TRWRAP + + asc->state &= ~ASC_STATE_DMA_IN; + + if (asc->state & ASC_STATE_DO_RFB) { + tgt->transient_state.has_oddb = FALSE; + regs->asc_cnfg2 = ASC_CNFG2_SCSI2; + } + + /* + * Get the last two bytes in FIFO + */ + while (fifo_count(regs) > 2) { + status.bits = get_fifo(regs); mb(); + } + + status.bits = get_fifo(regs); mb(); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(asc->active_target, SCSI_ERR_STATUS, status.bits, 0); + asc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + asc->done = SCSI_RET_SUCCESS; + + status.bits = get_fifo(regs); /* just pop the command_complete */ + mb(); + + /* if reading, move the last piece of data in main memory */ + if (len = asc->in_count) { + register int count; + + count = asc->dmacnt_at_end; + if (count) { +#if 0 + this is incorrect and besides.. + tgt->ior->io_residual = count; +#endif + len -= count; + } + regs->asc_cmd = asc->script->command; + readback(regs->asc_cmd); + + ret = FALSE; + } else + ret = TRUE; + + asc->dmacnt_at_end = 0; + (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len); + if (!ret) + asc->script++; + return ret; +} + +boolean_t +asc_put_status(asc, csr, ir) + register asc_softc_t asc; +{ + register asc_padded_regmap_t *regs = asc->regs; + register scsi2_status_byte_t status; + register target_info_t *tgt = asc->active_target; + int len; + + LOG(0x21,"put_status"); + + asc->state &= ~ASC_STATE_DMA_IN; + + if (len = asc->in_count) { + register int count; + + ASC_TC_GET(regs,count); mb(); + if (count) + len -= count; + } + (*asc->dma_ops->end_xfer)(asc->dma_state, tgt, len); + +/* status.st.scsi_status_code = SCSI_ST_GOOD; */ + regs->asc_fifo = 0; mb(); + regs->asc_fifo = SCSI_COMMAND_COMPLETE; mb(); + + return TRUE; +} + + +boolean_t +asc_dma_in(asc, csr, ir) + register asc_softc_t asc; +{ + register target_info_t *tgt; + register asc_padded_regmap_t *regs = asc->regs; + register int count; + unsigned char ff = get_reg(regs,asc_flags); mb(); + + LOG(0xE,"dma_in"); + tgt = asc->active_target; + + /* + * This seems to be needed on certain rudimentary targets + * (such as the DEC TK50 tape) which apparently only pick + * up 6 initial bytes: when you add the initial IDENTIFY + * you are left with 1 pending byte, which is left in the + * FIFO and would otherwise show up atop the data we are + * really requesting. + * + * This is only speculation, though, based on the fact the + * sequence step value of 3 out of select means the target + * changed phase too quick and some bytes have not been + * xferred (see NCR manuals). Counter to this theory goes + * the fact that the extra byte that shows up is not alwyas + * zero, and appears to be pretty random. + * Note that asc_flags say there is one byte in the FIFO + * even in the ok case, but the sstep value is the right one. + * Note finally that this might all be a sync/async issue: + * I have only checked the ok case on synch disks so far. + * + * Indeed it seems to be an asynch issue: exabytes do it too. + */ + if ((tgt->sync_offset == 0) && ((ff & ASC_FLAGS_SEQ_STEP) != 0x80)) { + regs->asc_cmd = ASC_CMD_NOP; + wbflush(); + PRINT(("[tgt %d: %x while %d]", tgt->target_id, ff, tgt->cur_cmd)); + while ((ff & ASC_FLAGS_FIFO_CNT) != 0) { + ff = get_fifo(regs); mb(); + ff = get_reg(regs,asc_flags); mb(); + } + } + + asc->state |= ASC_STATE_DMA_IN; + + count = (*asc->dma_ops->start_datain)(asc->dma_state, tgt); + ASC_TC_PUT(regs, count); + readback(regs->asc_cmd); + + if ((asc->in_count = count) == tgt->transient_state.in_count) + return TRUE; + regs->asc_cmd = asc->script->command; mb(); + asc->script = asc_script_restart_data_in; + return FALSE; +} + +asc_dma_in_r(asc, csr, ir) + register asc_softc_t asc; +{ + register target_info_t *tgt; + register asc_padded_regmap_t *regs = asc->regs; + register int count; + boolean_t advance_script = TRUE; + + + LOG(0x1f,"dma_in_r"); + tgt = asc->active_target; + + asc->state |= ASC_STATE_DMA_IN; + + if (asc->in_count == 0) { + /* + * Got nothing yet, we just reconnected. + */ + register int avail; + + /* + * NOTE: if we have to handle the RFB (obb), + * the odd byte has been installed at reconnect + * time, before switching to data-in phase. Now + * we are already in data-in phase. + * It is up to the DMA engine to trim the dma_ptr + * down one byte. + */ + + count = (*asc->dma_ops->restart_datain_1) + (asc->dma_state, tgt); + + /* common case of 8k-or-less read ? */ + advance_script = (tgt->transient_state.in_count == count); + + } else { + + /* + * We received some data. + */ + register int offset, xferred; + + /* + * Problem: sometimes we get a 'spurious' interrupt + * right after a reconnect. It goes like disconnect, + * reconnect, dma_in_r, here but DMA is still rolling. + * Since there is no good reason we got here to begin with + * we just check for the case and dismiss it: we should + * get another interrupt when the TC goes to zero or the + * target disconnects. + */ + ASC_TC_GET(regs,xferred); mb(); + if (xferred != 0) + return FALSE; + + xferred = asc->in_count - xferred; + assert(xferred > 0); + + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + + /* + * There should NOT be any obb issues here, + * we would have no control anyways. + */ + count = (*asc->dma_ops->restart_datain_2) + (asc->dma_state, tgt, xferred); + + asc->in_count = count; + ASC_TC_PUT(regs, count); + readback(regs->asc_cmd); + regs->asc_cmd = asc->script->command; mb(); + + (*asc->dma_ops->restart_datain_3) + (asc->dma_state, tgt); + + /* last chunk ? */ + if (count == tgt->transient_state.in_count) + asc->script++; + + return FALSE; + } + + asc->in_count = count; + ASC_TC_PUT(regs, count); + readback(regs->asc_cmd); + + if (!advance_script) { + regs->asc_cmd = asc->script->command; + readback(regs->asc_cmd); + } + return advance_script; +} + + +/* send data to target. Only called to start the xfer */ + +boolean_t +asc_dma_out(asc, csr, ir) + register asc_softc_t asc; +{ + register asc_padded_regmap_t *regs = asc->regs; + register int reload_count; + register target_info_t *tgt; + int command; + + LOG(0xF,"dma_out"); + + ASC_TC_GET(regs, reload_count); mb(); + asc->extra_count = fifo_count(regs); + reload_count += asc->extra_count; + ASC_TC_PUT(regs, reload_count); + asc->state &= ~ASC_STATE_DMA_IN; + readback(regs->asc_cmd); + + tgt = asc->active_target; + + command = asc->script->command; + + if (reload_count == 0) reload_count = ASC_TC_MAX; + asc->out_count = reload_count; + + if (reload_count >= tgt->transient_state.out_count) + asc->script++; + else + asc->script = asc_script_restart_data_out; + + if ((*asc->dma_ops->start_dataout) + (asc->dma_state, tgt, (volatile unsigned *)®s->asc_cmd, + command, &asc->extra_count)) { + regs->asc_cmd = command; + readback(regs->asc_cmd); + } + + return FALSE; +} + +/* send data to target. Called in two different ways: + (a) to restart a big transfer and + (b) after reconnection + */ +boolean_t +asc_dma_out_r(asc, csr, ir) + register asc_softc_t asc; +{ + register asc_padded_regmap_t *regs = asc->regs; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count; + + + LOG(0x20,"dma_out_r"); + + tgt = asc->active_target; + asc->state &= ~ASC_STATE_DMA_IN; + + if (asc->out_count == 0) { + /* + * Nothing committed: we just got reconnected + */ + count = (*asc->dma_ops->restart_dataout_1) + (asc->dma_state, tgt); + + /* is this the last chunk ? */ + advance_script = (tgt->transient_state.out_count == count); + } else { + /* + * We sent some data. + */ + register int offset, xferred; + + ASC_TC_GET(regs,count); mb(); + + /* see comment above */ + if (count) { + return FALSE; + } + + count += fifo_count(regs); + count -= asc->extra_count; + xferred = asc->out_count - count; + assert(xferred > 0); + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + count = (*asc->dma_ops->restart_dataout_2) + (asc->dma_state, tgt, xferred); + + /* last chunk ? */ + if (tgt->transient_state.out_count == count) + goto quickie; + + asc->out_count = count; + + asc->extra_count = (*asc->dma_ops->restart_dataout_3) + (asc->dma_state, tgt, + (volatile unsigned *)®s->asc_fifo); + ASC_TC_PUT(regs, count); + readback(regs->asc_cmd); + regs->asc_cmd = asc->script->command; mb(); + + (*asc->dma_ops->restart_dataout_4)(asc->dma_state, tgt); + + return FALSE; + } + +quickie: + asc->extra_count = (*asc->dma_ops->restart_dataout_3) + (asc->dma_state, tgt, + (volatile unsigned *)®s->asc_fifo); + + asc->out_count = count; + + ASC_TC_PUT(regs, count); + readback(regs->asc_cmd); + + if (!advance_script) { + regs->asc_cmd = asc->script->command; + } + return advance_script; +} + +boolean_t +asc_dosynch(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; +{ + register asc_padded_regmap_t *regs = asc->regs; + register unsigned char c; + int i, per, offs; + register target_info_t *tgt; + + /* + * Phase is MSG_OUT here. + * Try to go synchronous, unless prohibited + */ + tgt = asc->active_target; + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + delay(1); + + per = asc_min_period; + if (BGET(scsi_no_synchronous_xfer,asc->sc->masterno,tgt->target_id)) + offs = 0; + else + offs = asc_max_offset; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb(); + regs->asc_fifo = 3; mb(); + regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb(); + regs->asc_fifo = asc_to_scsi_period(asc_min_period,asc->clk); mb(); + regs->asc_fifo = offs; mb(); + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + csr = asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); mb(); + + /* some targets might be slow to move to msg-in */ + + if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) { + + /* wait for direction bit to flip */ + csr = asc_wait(regs, SCSI_IO, 0); + ir = get_reg(regs,asc_intr); mb(); + /* Some ugly targets go stright to command phase. + "You could at least say goodbye" */ + if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) + goto did_not; + if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) + gimmeabreak(); + } + + regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); + csr = asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); mb(); + + /* some targets do not even take all the bytes out */ + while (fifo_count(regs) > 0) { + c = get_fifo(regs); /* see what it says */ + mb(); + } + + if (c == SCSI_MESSAGE_REJECT) { +did_not: + printf(" did not like SYNCH xfer "); + + /* Tk50s get in trouble with ATN, sigh. */ + regs->asc_cmd = ASC_CMD_CLR_ATN; + readback(regs->asc_cmd); + + goto cmd; + } + + /* + * Receive the rest of the message + */ + regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); + asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); mb(); + + if (c != SCSI_EXTENDED_MESSAGE) + gimmeabreak(); + + regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); + if (get_fifo(regs) != 3) + panic("asc_dosynch"); + + for (i = 0; i < 3; i++) { + regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); + + regs->asc_cmd = ASC_CMD_XFER_INFO; mb(); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr);/*ack*/ mb(); + c = get_fifo(regs); mb(); + + if (i == 1) tgt->sync_period = scsi_period_to_asc(c,asc->clk); + if (i == 2) tgt->sync_offset = c; + } + +cmd: + regs->asc_cmd = ASC_CMD_MSG_ACPT; mb(); + csr = asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); + + /* Might have to wait a bit longer for slow targets */ + for (c = 0; SCSI_PHASE(get_reg(regs,asc_csr)) == SCSI_PHASE_MSG_IN; c++) { + mb(); + delay(2); + if (c & 0x80) break; /* waited too long */ + } + csr = get_reg(regs,asc_csr); mb(); + + /* phase should normally be command here */ + if (SCSI_PHASE(csr) == SCSI_PHASE_CMD) { + register char *cmd = tgt->cmd_ptr; + + /* test unit ready or inquiry */ + for (i = 0; i < sizeof(scsi_command_group_0); i++) { + regs->asc_fifo = *cmd++; mb(); + } + ASC_TC_PUT(regs,0xff); mb(); + regs->asc_cmd = ASC_CMD_XFER_PAD; /*0x18*/ mb(); + + if (tgt->cur_cmd == SCSI_CMD_INQUIRY) { + tgt->transient_state.script = asc_script_data_in; + asc->script = tgt->transient_state.script; + regs->asc_syn_p = tgt->sync_period; + regs->asc_syn_o = tgt->sync_offset; mb(); + return FALSE; + } + + csr = asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr);/*ack*/ mb(); + } + + if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) + csr = asc_wait(regs, SCSI_IO, 1); /* direction flip */ + +status: + if (SCSI_PHASE(csr) != SCSI_PHASE_STATUS) + gimmeabreak(); + + return TRUE; +} + +/* The other side of the coin.. */ +asc_odsynch(asc, initiator) + register asc_softc_t asc; + target_info_t *initiator; +{ + register asc_padded_regmap_t *regs = asc->regs; + register unsigned char c; + int len, per, offs; + + /* + * Phase is MSG_OUT, we are the target and we have control. + * Any IDENTIFY messages have been handled already. + */ + initiator->flags |= TGT_DID_SYNCH; + initiator->flags &= ~TGT_TRY_SYNCH; + + /* + * We only understand synch negotiations + */ + c = get_fifo(regs); mb(); + if (c != SCSI_EXTENDED_MESSAGE) goto bad; + + /* + * This is not in the specs, but apparently the chip knows + * enough about scsi to receive the length automatically. + * So there were two bytes in the fifo at function call. + */ + len = get_fifo(regs); mb(); + if (len != 3) goto bad; + while (len) { + if (fifo_count(regs) == 0) { + regs->asc_cmd = ASC_CMD_RCV_MSG; + readback(regs->asc_cmd); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); + } + c = get_fifo(regs); mb(); + if (len == 1) offs = c; + if (len == 2) per = c; + len--; + } + + /* + * Adjust the proposed parameters + */ + c = scsi_period_to_asc(per,asc->clk); + initiator->sync_period = c; + per = asc_to_scsi_period(c,asc->clk); + + if (offs > asc_max_offset) offs = asc_max_offset; + initiator->sync_offset = offs; + + /* + * Tell him what the deal is + */ + regs->asc_fifo = SCSI_EXTENDED_MESSAGE; mb(); + regs->asc_fifo = 3; mb(); + regs->asc_fifo = SCSI_SYNC_XFER_REQUEST; mb(); + regs->asc_fifo = per; mb(); + regs->asc_fifo = offs; mb(); + regs->asc_cmd = ASC_CMD_SND_MSG; + readback(regs->asc_cmd); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); + + /* + * Exit conditions: fifo empty, phase undefined but non-command + */ + return; + + /* + * Something wrong, reject the message + */ +bad: + while (fifo_count(regs)) { + c = get_fifo(regs); mb(); + } + regs->asc_fifo = SCSI_MESSAGE_REJECT; mb(); + regs->asc_cmd = ASC_CMD_SND_MSG; + readback(regs->asc_cmd); + asc_wait(regs, ASC_CSR_INT, 1); + c = get_reg(regs,asc_intr); mb(); +} + +/* + * The bus was reset + */ +asc_bus_reset(asc) + register asc_softc_t asc; +{ + register asc_padded_regmap_t *regs = asc->regs; + + LOG(0x1d,"bus_reset"); + + /* + * Clear bus descriptor + */ + asc->script = 0; + asc->error_handler = 0; + asc->active_target = 0; + asc->next_target = 0; + asc->state &= ASC_STATE_SPEC_DMA | ASC_STATE_DO_RFB; + queue_init(&asc->waiting_targets); + asc->wd.nactive = 0; + asc_reset(asc, TRUE, asc->state & ASC_STATE_SPEC_DMA); + + printf("asc: (%d) bus reset ", ++asc->wd.reset_count); + + /* some targets take long to reset */ + delay( scsi_delay_after_reset + + asc->sc->initiator_id * 500000); /* if multiple initiators */ + + if (asc->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(asc->sc); +} + +/* + * Disconnect/reconnect mode ops + */ + +/* get the message in via dma */ +boolean_t +asc_msg_in(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; +{ + register target_info_t *tgt; + register asc_padded_regmap_t *regs = asc->regs; + unsigned char ff; + + LOG(0x10,"msg_in"); + /* must clean FIFO as in asc_dma_in, sigh */ + while (fifo_count(regs) != 0) { + ff = get_fifo(regs); mb(); + } + + (void) (*asc->dma_ops->start_msgin)(asc->dma_state, asc->active_target); + /* We only really expect two bytes, at tgt->cmd_ptr */ + ASC_TC_PUT(regs, sizeof(scsi_command_group_0)); + readback(regs->asc_cmd); + + return TRUE; +} + +/* check the message is indeed a DISCONNECT */ +boolean_t +asc_disconnect(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; + +{ + register char *msgs, ff; + register target_info_t *tgt; + asc_padded_regmap_t *regs; + + tgt = asc->active_target; + + (*asc->dma_ops->end_msgin)(asc->dma_state, tgt); + + /* + * Do not do this. It is most likely a reconnection + * message that sits there already by the time we + * get here. The chip is smart enough to only dma + * the bytes that correctly came in as msg_in proper, + * the identify and selection bytes are not dma-ed. + * Yes, sometimes the hardware does the right thing. + */ +#if 0 + /* First check message got out of the fifo */ + regs = asc->regs; + while (fifo_count(regs) != 0) { + *msgs++ = get_fifo(regs); + } +#endif + msgs = tgt->cmd_ptr; + + /* A SDP message preceeds it in non-completed READs */ + if ((msgs[0] == SCSI_DISCONNECT) || /* completed */ + ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* non complete */ + (msgs[1] == SCSI_DISCONNECT))) { + /* that's the ok case */ + } else + printf("[tgt %d bad SDP: x%x]", + tgt->target_id, *((unsigned short *)msgs)); + + return TRUE; +} + +/* save all relevant data, free the BUS */ +boolean_t +asc_disconnected(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; + +{ + register target_info_t *tgt; + + LOG(0x11,"disconnected"); + asc_disconnect(asc,csr,ir); + + tgt = asc->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = asc->error_handler; + /* anything else was saved in asc_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + asc_release_bus(asc); + + return FALSE; +} + +int asc_restore_ptr = 1; + +/* get reconnect message out of fifo, restore BUS */ +boolean_t +asc_reconnect(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; + +{ + register target_info_t *tgt; + asc_padded_regmap_t *regs; + unsigned int id; + + LOG(0x12,"reconnect"); + /* + * See if this reconnection collided with a selection attempt + */ + if (asc->state & ASC_STATE_BUSY) + asc->state |= ASC_STATE_COLLISION; + + asc->state |= ASC_STATE_BUSY; + + /* find tgt: first byte in fifo is (tgt_id|our_id) */ + regs = asc->regs; + while (fifo_count(regs) > 2) /* sanity */ { + id = get_fifo(regs); mb(); + } + if (fifo_count(regs) != 2) + gimmeabreak(); + + id = get_fifo(regs); /* must decode this now */ + mb(); + id &= ~(1 << asc->sc->initiator_id); + { + register int i; + for (i = 0; id > 1; i++) + id >>= 1; + id = i; + } + + tgt = asc->sc->target[id]; + if (tgt == 0) panic("asc_reconnect"); + + /* synch things*/ + regs->asc_syn_p = tgt->sync_period; + regs->asc_syn_o = tgt->sync_offset; + readback(regs->asc_syn_o); + + /* Get IDENTIFY message */ + { + register int i = get_fifo(regs); + if ((i & SCSI_IDENTIFY) == 0) + gimmeabreak(); + i &= 0x7; + /* If not LUN 0 see which target it is */ + if (i) { + target_info_t *tgt1; + for (tgt1 = tgt->next_lun; + tgt1 && tgt1 != tgt; + tgt1 = tgt1->next_lun) + if (tgt1->lun == i) { + tgt = tgt1; + break; + } + } + } + + if (asc->state & ASC_STATE_DO_RFB) { + if (tgt->transient_state.has_oddb) { + if (tgt->sync_period) { + regs->asc_cnfg2 = ASC_CNFG2_SCSI2 | ASC_CNFG2_RFB; + wbflush(); + regs->asc_rfb = tgt->transient_state.the_oddb; + } else { + regs->asc_fifo = tgt->transient_state.the_oddb; + } + tgt->transient_state.has_oddb = FALSE; + } else { + regs->asc_cnfg2 = ASC_CNFG2_SCSI2; + } + wbflush(); + } + + PRINT(("{R%d}", id)); + if (asc->state & ASC_STATE_COLLISION) + PRINT(("[B %d-%d]", asc->active_target->target_id, id)); + + LOG(0x80+id,0); + + asc->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + asc->script = tgt->transient_state.script; + asc->error_handler = tgt->transient_state.handler; + asc->in_count = 0; + asc->out_count = 0; + + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + + /* What if there is a RESTORE_PTR msgin ? */ + if (asc_restore_ptr) { +more_msgin: + csr = asc_wait(regs, ASC_CSR_INT, 1); + + if (SCSI_PHASE(csr) == SCSI_PHASE_MSG_IN) { + /* ack intr */ + id = get_reg(regs,asc_intr); mb(); + + /* Ok, get msg */ + regs->asc_cmd = ASC_CMD_XFER_INFO; + readback(regs->asc_cmd); + /* wait for xfer done */ + csr = asc_wait(regs, ASC_CSR_INT, 1); + + /* look at what we got */ + id = get_fifo(regs); + + if (id != SCSI_RESTORE_POINTERS) + printf("asc%d: uhu msg %x\n", asc->sc->masterno, id); + /* ack intr */ + id = get_reg(regs,asc_intr); mb(); + + /* move on */ + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + goto more_msgin; + } + } + + return FALSE; +} + + +/* We have been selected as target */ + +boolean_t +asc_selected(asc, csr, ir) + register asc_softc_t asc; + register unsigned csr, ir; +{ + register asc_padded_regmap_t *regs = asc->regs; + register unsigned char id; + target_info_t *self, *initiator; + unsigned int len; + + /* + * Find initiator's id: the head of the fifo is (init_id|our_id) + */ + + id = get_fifo(regs); /* must decode this now */ + mb(); + id &= ~(1 << asc->sc->initiator_id); + { + register int i; + for (i = 0; id > 1; i++) + id >>= 1; + id = i; + } + + /* + * See if we have seen him before + */ + self = asc->sc->target[asc->sc->initiator_id]; + initiator = asc->sc->target[id]; + if (initiator == 0) { + + initiator = scsi_slave_alloc(asc->sc->masterno, id, asc); + (*asc->dma_ops->new_target)(asc->dma_state, initiator); + + } + + if (! (initiator->flags & TGT_ONLINE) ) + sccpu_new_initiator(self, initiator); + + /* + * If selected with ATN the chip did the msg-out + * phase already, let us look at the message(s) + */ + if (ir & ASC_INT_SEL_ATN) { + register unsigned char m; + + m = get_fifo(regs); mb(); + if ((m & SCSI_IDENTIFY) == 0) + gimmeabreak(); + + csr = get_reg(regs,asc_csr); mb(); + if ((SCSI_PHASE(csr) == SCSI_PHASE_MSG_OUT) && + fifo_count(regs)) + asc_odsynch(asc, initiator); + + /* Get the command now, unless we have it already */ + mb(); + if (fifo_count(regs) < sizeof(scsi_command_group_0)) { + regs->asc_cmd = ASC_CMD_RCV_CMD; + readback(regs->asc_cmd); + asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); mb(); + csr = get_reg(regs,asc_csr); mb(); + } + } else { + /* + * Pop away the null byte that follows the id + */ + if (get_fifo(regs) != 0) + gimmeabreak(); + + } + + /* + * Take rest of command out of fifo + */ + self->dev_info.cpu.req_pending = TRUE; + self->dev_info.cpu.req_id = id; + self->dev_info.cpu.req_cmd = get_fifo(regs); + self->dev_info.cpu.req_lun = get_fifo(regs); + + LOG(0x80+self->dev_info.cpu.req_cmd, 0); + + switch ((self->dev_info.cpu.req_cmd & SCSI_CODE_GROUP) >> 5) { + case 0: + len = get_fifo(regs) << 16; mb(); + len |= get_fifo(regs) << 8; mb(); + len |= get_fifo(regs); mb(); + break; + case 1: + case 2: + len = get_fifo(regs); /* xxx lba1 */ mb(); + len = get_fifo(regs); /* xxx lba2 */ mb(); + len = get_fifo(regs); /* xxx lba3 */ mb(); + len = get_fifo(regs); /* xxx lba4 */ mb(); + len = get_fifo(regs); /* xxx xxx */ mb(); + len = get_fifo(regs) << 8; mb(); + len |= get_fifo(regs); mb(); + break; + case 5: + len = get_fifo(regs); /* xxx lba1 */ mb(); + len = get_fifo(regs); /* xxx lba2 */ mb(); + len = get_fifo(regs); /* xxx lba3 */ mb(); + len = get_fifo(regs); /* xxx lba4 */ mb(); + len = get_fifo(regs) << 24; mb(); + len |= get_fifo(regs) << 16; mb(); + len |= get_fifo(regs) << 8; mb(); + len |= get_fifo(regs); mb(); + if (get_fifo(regs) != 0) ; + break; + default: + panic("asc_selected"); + } + self->dev_info.cpu.req_len = len; +/*if (scsi_debug) printf("[L x%x]", len);*/ + + /* xxx pop the cntrl byte away */ + if (get_fifo(regs) != 0) + gimmeabreak(); + mb(); + + /* + * Setup state + */ + asc->active_target = self; + asc->done = SCSI_RET_IN_PROGRESS; + asc->out_count = 0; + asc->in_count = 0; + asc->extra_count = 0; + + /* + * Sync params. + */ + regs->asc_syn_p = initiator->sync_period; + regs->asc_syn_o = initiator->sync_offset; + readback(regs->asc_syn_o); + + /* + * Do the up-call + */ + LOG(0xB,"tgt-mode-restart"); + (*self->dev_ops->restart)( self, FALSE); + + /* The call above has either prepared the data, + placing an ior on self, or it handled it some + other way */ + if (self->ior == 0) + return FALSE; + + /* sanity */ + if (fifo_count(regs)) { + regs->asc_cmd = ASC_CMD_FLUSH; + readback(regs->asc_cmd); + } + + /* needed by some dma code to align things properly */ + self->transient_state.cmd_count = sizeof(scsi_command_group_0); + self->transient_state.in_count = len; /* XXX */ + + (*asc->dma_ops->map)(asc->dma_state, self); + + if (asc->wd.nactive++ == 0) + asc->wd.watchdog_state = SCSI_WD_ACTIVE; + + + { + register script_t scp; + unsigned char command; + + switch (self->dev_info.cpu.req_cmd) { + case SCSI_CMD_TEST_UNIT_READY: + scp = asc_script_t_data_in+1; break; + case SCSI_CMD_SEND: + scp = asc_script_t_data_in; break; + default: + scp = asc_script_t_data_out; break; + } + + asc->script = scp; + command = scp->command; + if (!(*scp->action)(asc, csr, ir)) + return FALSE; + +/*if (scsi_debug) printf("[F%x]", fifo_count(regs));*/ + + asc->script++; + regs->asc_cmd = command; mb(); + + } + + return FALSE; +} + + +/* + * Other utilities + */ +private void +pick_up_oddb( + target_info_t *tgt) +{ + register char *lastp; + + /* State should have been updated before we get here */ + lastp = tgt->dma_ptr + tgt->transient_state.dma_offset; + + if ((vm_offset_t) lastp & 1) { + tgt->transient_state.has_oddb = TRUE; + tgt->transient_state.the_oddb = lastp[-1]; + } else + tgt->transient_state.has_oddb = FALSE; +} + + +/* + * Error handlers + */ + +/* + * Fall-back error handler. + */ +asc_err_generic(asc, csr, ir) + register asc_softc_t asc; +{ + LOG(0x13,"err_generic"); + + /* handle non-existant or powered off devices here */ + if ((ir == ASC_INT_DISC) && + (asc_isa_select(asc->cmd_was)) && + (ASC_SS(asc->ss_was) == 0)) { + /* Powered off ? */ + target_info_t *tgt = asc->active_target; + + tgt->flags = 0; + tgt->sync_period = 0; + tgt->sync_offset = 0; mb(); + asc->done = SCSI_RET_DEVICE_DOWN; + asc_end(asc, csr, ir); + return; + } + + switch (SCSI_PHASE(csr)) { + case SCSI_PHASE_STATUS: + if (asc->script[-1].condition == SCSI_PHASE_STATUS) { + /* some are just slow to get out.. */ + } else + asc_err_to_status(asc, csr, ir); + return; + break; + case SCSI_PHASE_DATAI: + if (asc->script->condition == SCSI_PHASE_STATUS) { + /* + * Here is what I know about it. We reconnect to + * the target (csr 87, ir 0C, cmd 44), start dma in + * (81 10 12) and then get here with (81 10 90). + * Dma is rolling and keeps on rolling happily, + * the value in the counter indicates the interrupt + * was signalled right away: by the time we get + * here only 80-90 bytes have been shipped to an + * rz56 running synchronous at 3.57 Mb/sec. + */ +/* printf("{P}");*/ + return; + } + break; + case SCSI_PHASE_DATAO: + if (asc->script->condition == SCSI_PHASE_STATUS) { + /* + * See comment above. Actually seen on hitachis. + */ +/* printf("{P}");*/ + return; + } + break; + case SCSI_PHASE_CMD: + /* This can be the case with drives that are not + even scsi-1 compliant and do not like to be + selected with ATN (to do the synch negot) and + go stright to command phase, regardless */ + + if (asc->script == asc_script_try_synch) { + + target_info_t *tgt = asc->active_target; + register asc_padded_regmap_t *regs = asc->regs; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + if (tgt->cur_cmd == SCSI_CMD_INQUIRY) + tgt->transient_state.script = asc_script_data_in; + else + tgt->transient_state.script = asc_script_simple_cmd; + asc->script = tgt->transient_state.script; + regs->asc_cmd = ASC_CMD_CLR_ATN; + readback(regs->asc_cmd); + asc->regs->asc_cmd = ASC_CMD_XFER_PAD|ASC_CMD_DMA; /*0x98*/ mb(); + printf(" did not like SYNCH xfer "); + return; + } + /* fall through */ + } + gimmeabreak(); +} + +/* + * Handle generic errors that are reported as + * an unexpected change to STATUS phase + */ +asc_err_to_status(asc, csr, ir) + register asc_softc_t asc; +{ + script_t scp = asc->script; + + LOG(0x14,"err_tostatus"); + while (scp->condition != SCSI_PHASE_STATUS) + scp++; + (*scp->action)(asc, csr, ir); + asc->script = scp + 1; + asc->regs->asc_cmd = scp->command; mb(); +#if 0 + /* + * Normally, we would already be able to say the command + * is in error, e.g. the tape had a filemark or something. + * But in case we do disconnected mode WRITEs, it is quite + * common that the following happens: + * dma_out -> disconnect (xfer complete) -> reconnect + * and our script might expect at this point that the dma + * had to be restarted (it didn't notice it was completed). + * And in any event.. it is both correct and cleaner to + * declare error iff the STATUS byte says so. + */ + asc->done = SCSI_RET_NEED_SENSE; +#endif +} + +/* + * Handle disconnections as exceptions + */ +asc_err_disconn(asc, csr, ir) + register asc_softc_t asc; + register unsigned char csr, ir; +{ + register asc_padded_regmap_t *regs; + register target_info_t *tgt; + int count; + boolean_t callback = FALSE; + + LOG(0x16,"err_disconn"); + + /* + * We only do msg-in cases here + */ + if (SCSI_PHASE(csr) != SCSI_PHASE_MSG_IN) + return asc_err_generic(asc, csr, ir); + + regs = asc->regs; + tgt = asc->active_target; + + /* + * What did we expect to happen, and what did happen. + */ + switch (asc->script->condition) { + case SCSI_PHASE_DATAO: + /* + * A data out phase was either about to be started, + * or it was in progress but more had to go out later + * [e.g. a big xfer for instance, or more than the + * DMA engine can take in one shot]. + */ + LOG(0x1b,"+DATAO"); + if (asc->out_count) { + register int xferred, offset; + + /* + * Xfer in progress. See where we stopped. + */ + ASC_TC_GET(regs,xferred); /* temporary misnomer */ + /* + * Account for prefetching, in its various forms + */ + xferred += fifo_count(regs); + xferred -= asc->extra_count; + /* + * See how much went out, how much to go. + */ + xferred = asc->out_count - xferred; /* ok now */ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + callback = (*asc->dma_ops->disconn_1) + (asc->dma_state, tgt, xferred); + + } else { + + /* + * A disconnection before DMA was (re)started. + */ + callback = (*asc->dma_ops->disconn_2) + (asc->dma_state, tgt); + + } + asc->extra_count = 0; + tgt->transient_state.script = asc_script_restart_data_out; + break; + + + case SCSI_PHASE_DATAI: + /* + * Same as above, the other way around + */ + LOG(0x17,"+DATAI"); + if (asc->in_count) { + register int offset, xferred; + + /* + * How much did we expect, how much did we get + */ + ASC_TC_GET(regs,count); mb(); + xferred = asc->in_count - count; + assert(xferred > 0); + +if (regs->asc_flags & 0xf) +printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs)); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + + callback = (*asc->dma_ops->disconn_3) + (asc->dma_state, tgt, xferred); + + /* + * Handle obb if we have to. DMA code has + * updated pointers and flushed buffers. + */ + if (asc->state & ASC_STATE_DO_RFB) + pick_up_oddb(tgt); + + tgt->transient_state.script = asc_script_restart_data_in; + /* + * Some silly targets disconnect after they + * have given us all the data. + */ + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else + tgt->transient_state.script = asc_script_restart_data_in; + break; + + case SCSI_PHASE_STATUS: + /* + * Either one of the above cases here. Only diff + * the DMA engine was setup to run to completion + * and (most likely) did not. + */ + ASC_TC_GET(regs,count); mb(); + if (asc->state & ASC_STATE_DMA_IN) { + register int offset, xferred; + + LOG(0x1a,"+STATUS+R"); + + + /* + * Handle brain-dead sequence: + * 1-xfer all data, disconnect + * 2-reconnect, disconnect immediately ?? + * 3-rept 2 + * 4-reconnect,complete + */ + if (asc->in_count) { + + xferred = asc->in_count - count; + assert(xferred > 0); +if (regs->asc_flags & 0xf) +printf("{Xf %x,%x,%x}", xferred, asc->in_count, fifo_count(regs)); + + tgt->transient_state.in_count -= xferred; + + callback = (*asc->dma_ops->disconn_4) + (asc->dma_state, tgt, xferred); + } + /* + * Handle obb if we have to. DMA code has + * updated pointers and flushed buffers. + */ + if (asc->state & ASC_STATE_DO_RFB) + pick_up_oddb(tgt); + + tgt->transient_state.script = asc_script_restart_data_in; + + /* see previous note */ + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + /* + * Outgoing xfer, take care of prefetching. + */ + /* add what's left in the fifo */ + count += fifo_count(regs); + /* take back the extra we might have added */ + count -= asc->extra_count; + /* ..and drop that idea */ + asc->extra_count = 0; + + LOG(0x19,"+STATUS+W"); + + /* + * All done ? This is less silly than with + * READs: some disks will only say "done" when + * the data is down on the platter, and some + * people like it much better that way. + */ + if ((count == 0) && (tgt->transient_state.out_count == asc->out_count)) { + /* all done */ + tgt->transient_state.script = asc->script; + tgt->transient_state.out_count = 0; + } else { + register int xferred, offset; + + /* how much we xferred */ + xferred = asc->out_count - count; + + /* how much to go */ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + + callback = (*asc->dma_ops->disconn_5) + (asc->dma_state,tgt,xferred); + + tgt->transient_state.script = asc_script_restart_data_out; + } + asc->out_count = 0; + } + break; + default: + gimmeabreak(); + return; + } + asc_msg_in(asc,csr,ir); + asc->script = asc_script_disconnect; + regs->asc_cmd = ASC_CMD_XFER_INFO|ASC_CMD_DMA; + wbflush(); + /* + * Prevent a race, now. If the reselection comes quickly + * the chip will prefetch and reload the transfer counter + * register. Make sure it will stop, by reloading a zero. + */ + regs->asc_tc_lsb = 0; + regs->asc_tc_msb = 0; + if (callback) + (*asc->dma_ops->disconn_callback)(asc->dma_state, tgt); +} + +/* + * Watchdog + * + * So far I have only seen the chip get stuck in a + * select/reselect conflict: the reselection did + * win and the interrupt register showed it but.. + * no interrupt was generated. + * But we know that some (name withdrawn) disks get + * stuck in the middle of dma phases... + */ +asc_reset_scsibus(asc) + register asc_softc_t asc; +{ + register target_info_t *tgt = asc->active_target; + register asc_padded_regmap_t *regs = asc->regs; + register int ir; + + if (scsi_debug && tgt) { + int dmalen; + ASC_TC_GET(asc->regs,dmalen); mb(); + printf("Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, tgt->transient_state.out_count, + asc->in_count, asc->out_count, + dmalen); + } + ir = get_reg(regs,asc_intr); mb(); + if ((ir & ASC_INT_RESEL) && (SCSI_PHASE(regs->asc_csr) == SCSI_PHASE_MSG_IN)) { + /* getting it out of the woods is a bit tricky */ + spl_t s = splbio(); + + (void) asc_reconnect(asc, get_reg(regs,asc_csr), ir); + asc_wait(regs, ASC_CSR_INT, 1); + ir = get_reg(regs,asc_intr); mb(); + regs->asc_cmd = ASC_CMD_MSG_ACPT; + readback(regs->asc_cmd); + splx(s); + } else { + regs->asc_cmd = ASC_CMD_BUS_RESET; mb(); + delay(35); + } +} + +#endif NASC > 0 + diff --git a/scsi/adapters/scsi_7061.h b/scsi/adapters/scsi_7061.h new file mode 100644 index 0000000..8969f8b --- /dev/null +++ b/scsi/adapters/scsi_7061.h @@ -0,0 +1,230 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_7061.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Defines for the DEC DC7061 SII gate array (SCSI interface) + */ + +/* + * Register map + */ + +typedef struct { + volatile unsigned short sii_sdb; /* rw: Data bus and parity */ + volatile unsigned short sii_sc1; /* rw: scsi signals 1 */ + volatile unsigned short sii_sc2; /* rw: scsi signals 2 */ + volatile unsigned short sii_csr; /* rw: control and status */ + volatile unsigned short sii_id; /* rw: scsi bus ID */ + volatile unsigned short sii_sel_csr; /* rw: selection status */ + volatile unsigned short sii_destat; /* ro: selection detector status */ + volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */ + volatile unsigned short sii_data; /* rw: data register */ + volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */ + volatile unsigned short sii_dma_len; /* rw: length of transfer */ + volatile unsigned short sii_dma_adr_low;/* rw: low address */ + volatile unsigned short sii_dma_adr_hi; /* rw: high address */ + volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */ + volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */ + volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */ + volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */ + volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */ + volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */ + volatile unsigned short sii_data_csr; /* rc: data interrupt control */ + volatile unsigned short sii_cmd; /* rw: command register */ + volatile unsigned short sii_diag_csr; /* rw: disgnostic status */ +} sii_regmap_t; + +/* + * Data bus register (diag) + */ + +#define SII_SDB_DATA 0x00ff /* data bits, assert high */ +#define SII_SDB_PARITY 0x0100 /* parity bit */ + +/* + * Control signals one (diag) + */ + +#define SII_CS1_IO 0x0001 /* I/O bit */ +#define SII_CS1_CD 0x0002 /* Control/Data bit */ +#define SII_CS1_MSG 0x0004 /* Message bit */ +#define SII_CS1_ATN 0x0008 /* Attention bit */ +#define SII_CS1_REQ 0x0010 /* Request bit */ +#define SII_CS1_ACK 0x0020 /* Acknowledge bit */ +#define SII_CS1_RST 0x0040 /* Reset bit */ +#define SII_CS1_SEL 0x0080 /* Selection bit */ +#define SII_CS1_BSY 0x0100 /* Busy bit */ + +/* + * Control signals two (diag) + */ + +#define SII_CS2_SBE 0x0001 /* Bus enable */ +#define SII_CS2_ARB 0x0002 /* arbitration enable */ +#define SII_CS2_TGS 0x0004 /* Target role steer */ +#define SII_CS2_IGS 0x0008 /* Initiator role steer */ + +/* + * Control and status register + */ + +#define SII_CSR_IE 0x0001 /* Interrupt enable */ +#define SII_CSR_PCE 0x0002 /* Parity check enable */ +#define SII_CSR_SLE 0x0004 /* Select enable */ +#define SII_CSR_RSE 0x0008 /* Reselect enable */ +#define SII_CSR_HPM 0x0010 /* Arbitration enable */ + +/* + * SCSI bus ID register + */ + +#define SII_ID_MASK 0x0007 /* The scsi ID */ +#define SII_ID_IO 0x8000 /* ID pins are in/out */ + +/* + * Selector control and status register + */ + +#define SII_SEL_ID 0x0003 /* Destination ID */ + +/* + * Selection detector status register + */ + +#define SII_DET_ID 0x0003 /* Selector's ID */ + +/* + * Data register (silo) + */ + +#define SII_DATA_VAL 0x00ff /* Lower byte */ + +/* + * DMA control register + */ + +#define SII_DMA_SYN_OFFSET 0x0003 /* 0 -> asynch */ + +/* + * DMA counter + */ + +#define SII_DMA_COUNT_MASK 0x1fff /* in bytes */ + +/* + * DMA address registers + */ + +#define SII_DMA_LOW_MASK 0xffff /* all bits */ +#define SII_DMA_HIGH_MASK 0x0003 /* unused ones mbz */ + +/* + * DMA initial byte + */ + +#define SII_DMA_IBYTE 0x00ff /* for odd address DMAs */ + +/* + * Connection status register + */ + +#define SII_CON_LST 0x0002 /* ro: lost arbitration */ +#define SII_CON_SIP 0x0004 /* ro: selection InProgress */ +#define SII_CON_SWA 0x0008 /* rc: selected with ATN */ +#define SII_CON_TGT 0x0010 /* ro: target role */ +#define SII_CON_DST 0x0020 /* ro: sii is destination */ +#define SII_CON_CON 0x0040 /* ro: sii is connected */ +#define SII_CON_SCH 0x0080 /* rci: state change */ +#define SII_CON_LDN 0x0100 /* ??i: dssi list elm done */ +#define SII_CON_BUF 0x0200 /* ??i: dssi buffer service */ +#define SII_CON_TZ 0x0400 /* ??: dssi target zero */ +#define SII_CON_OBC 0x0800 /* ??i: dssi outen bit clr */ +#define SII_CON_BERR 0x1000 /* rci: bus error */ +#define SII_CON_RST 0x2000 /* rci: RST asserted */ +#define SII_CON_DI 0x4000 /* ro: data_csr intr */ +#define SII_CON_CI 0x8000 /* ro: con_csr intr */ + +/* + * Data transfer status register + */ + +#define SII_DTR_IO 0x0001 /* ro: I/O asserted */ +#define SII_DTR_CD 0x0002 /* ro: CD asserted */ +#define SII_DTR_MSG 0x0004 /* ro: MSG asserted */ +#define SII_DTR_ATN 0x0008 /* rc: ATN found asserted */ +#define SII_DTR_MIS 0x0010 /* roi: phase mismatch */ +#define SII_DTR_OBB 0x0100 /* ro: odd byte boundry */ +#define SII_DTR_IPE 0x0200 /* ro: incoming parity err */ +#define SII_DTR_IBF 0x0400 /* roi: input buffer full */ +#define SII_DTR_TBE 0x0800 /* roi: xmt buffer empty */ +#define SII_DTR_TCZ 0x1000 /* ro: xfer counter zero */ +#define SII_DTR_DONE 0x2000 /* rci: xfer complete */ +#define SII_DTR_DI 0x4000 /* ro: data_csr intr */ +#define SII_DTR_CI 0x8000 /* ro: con_csr intr */ + +#define SII_PHASE(dtr) SCSI_PHASE(dtr) + + +/* + * Command register + * + * Certain bits are only valid in certain roles: + * I - Initiator D - Destination T - Target + * Bits 0-3 give the 'expected phase' + * Bits 4-6 give the 'expected state' + * Bits 7-11 are the 'command' proper + */ + +#define SII_CMD_IO 0x0001 /* rw: (T) assert I/O */ +#define SII_CMD_CD 0x0002 /* rw: (T) assert CD */ +#define SII_CMD_MSG 0x0004 /* rw: (T) assert MSG */ +#define SII_CMD_ATN 0x0008 /* rw: (I) assert ATN */ + +#define SII_CMD_TGT 0x0010 /* rw: (DIT) target */ +#define SII_CMD_DST 0x0020 /* rw: (DIT) destination */ +#define SII_CMD_CON 0x0040 /* rw: (DIT) connected */ + +#define SII_CMD_RESET 0x0080 /* rw: (DIT) reset */ +#define SII_CMD_DIS 0x0100 /* rw: (DIT) disconnect */ +#define SII_CMD_REQ 0x0200 /* rw: (T) request data */ +#define SII_CMD_SEL 0x0400 /* rw: (D) select */ +#define SII_CMD_XFER 0x0800 /* rw: (IT) xfer information */ + +#define SII_CMD_RSL 0x1000 /* rw: reselect target */ +#define SII_CMD_RST 0x4000 /* zw: assert RST */ +#define SII_CMD_DMA 0x8000 /* rw: command uses DMA */ + +/* + * Diagnostic control register + */ + +#define SII_DIAG_TEST 0x0001 /* rw: test mode */ +#define SII_DIAG_DIA 0x0002 /* rw: ext loopback mode */ +#define SII_DIAG_PORT_ENB 0x0004 /* rw: enable drivers */ +#define SII_DIAG_LPB 0x0008 /* rw: loopback reg writes */ diff --git a/scsi/adapters/scsi_7061_hdw.c b/scsi/adapters/scsi_7061_hdw.c new file mode 100644 index 0000000..674e892 --- /dev/null +++ b/scsi/adapters/scsi_7061_hdw.c @@ -0,0 +1,2603 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_7061_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the DEC DC7061 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +#include <sii.h> +#if NSII > 0 + +#include <platforms.h> + +#ifdef DECSTATION +#define PAD(n) short n +#endif + +#include <machine/machspl.h> /* spl definitions */ +#include <mach/std_types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <machine/machspl.h> + +#include <sys/syslog.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#define isa_oddbb hba_dep[0] +#define oddbb hba_dep[1] + +#include <scsi/adapters/scsi_7061.h> + +#ifdef PAD + +typedef struct { + volatile unsigned short sii_sdb; /* rw: Data bus and parity */ + PAD(pad0); + volatile unsigned short sii_sc1; /* rw: scsi signals 1 */ + PAD(pad1); + volatile unsigned short sii_sc2; /* rw: scsi signals 2 */ + PAD(pad2); + volatile unsigned short sii_csr; /* rw: control and status */ + PAD(pad3); + volatile unsigned short sii_id; /* rw: scsi bus ID */ + PAD(pad4); + volatile unsigned short sii_sel_csr; /* rw: selection status */ + PAD(pad5); + volatile unsigned short sii_destat; /* ro: selection detector status */ + PAD(pad6); + volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */ + PAD(pad7); + volatile unsigned short sii_data; /* rw: data register */ + PAD(pad8); + volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */ + PAD(pad9); + volatile unsigned short sii_dma_len; /* rw: length of transfer */ + PAD(pad10); + volatile unsigned short sii_dma_adr_low;/* rw: low address */ + PAD(pad11); + volatile unsigned short sii_dma_adr_hi; /* rw: high address */ + PAD(pad12); + volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */ + PAD(pad13); + volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */ + PAD(pad14); + volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */ + PAD(pad15); + volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */ + PAD(pad16); + volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */ + PAD(pad17); + volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */ + PAD(pad18); + volatile unsigned short sii_data_csr; /* rc: data interrupt control */ + PAD(pad19); + volatile unsigned short sii_cmd; /* rw: command register */ + PAD(pad20); + volatile unsigned short sii_diag_csr; /* rw: disgnostic status */ + PAD(pad21); +} sii_padded_regmap_t; + +#else /*!PAD*/ + +typedef sii_regmap_t sii_padded_regmap_t; + +#endif /*!PAD*/ + + +#undef SII_CSR_SLE +#define SII_CSR_SLE 0 /* for now */ + +#ifdef DECSTATION +#include <mips/PMAX/kn01.h> +#define SII_OFFSET_RAM (KN01_SYS_SII_B_START-KN01_SYS_SII) +#define SII_RAM_SIZE (KN01_SYS_SII_B_END-KN01_SYS_SII_B_START) +/* 16 bits in 32 bit envelopes */ +#define SII_DMADR_LO(ptr) ((((unsigned)ptr)>>1)&SII_DMA_LOW_MASK) +#define SII_DMADR_HI(ptr) ((((unsigned)ptr)>>(16+1))&SII_DMA_HIGH_MASK) +#endif /* DECSTATION */ + +#ifndef SII_OFFSET_RAM /* cross compile check */ +#define SII_OFFSET_RAM 0 +#define SII_RAM_SIZE 0x10000 +#define SII_DMADR_LO(ptr) (((unsigned)ptr)>>16) +#define SII_DMADR_HI(ptr) (((unsigned)ptr)&0xffff) +#endif + +/* + * Statically partition the DMA buffer between targets. + * This way we will eventually be able to attach/detach + * drives on-fly. And 18k/target is enough. + */ +#define PER_TGT_DMA_SIZE ((SII_RAM_SIZE/7) & ~(sizeof(int)-1)) + +/* + * Round to 4k to make debug easier + */ +#define PER_TGT_BUFF_SIZE ((PER_TGT_DMA_SIZE >> 12) << 12) + +/* + * Macros to make certain things a little more readable + */ +#define SII_COMMAND(regs,cs,ds,cmd) \ + { \ + (regs)->sii_cmd = ((cs) & 0x70) | \ + ((ds) & 0x07) | (cmd); \ + wbflush(); \ + } +#define SII_ACK(regs,cs,ds,cmd) \ + { \ + SII_COMMAND(regs,cs,ds,cmd); \ + (regs)->sii_conn_csr = (cs); \ + (regs)->sii_data_csr = (ds); \ + } + +/* + * Deal with bogus pmax dma buffer + */ + +static char decent_buffer[NSII*8][256]; + +/* + * A script has a three parts: a pre-condition, an action, and + * an optional command to the chip. The first triggers error + * handling if not satisfied and in our case it is formed by the + * values of the sii_conn_csr and sii_data_csr register + * bits. The action part is just a function pointer, and the + * command is what the 7061 should be told to do at the end + * of the action processing. This command is only issued and the + * script proceeds if the action routine returns TRUE. + * See sii_intr() for how and where this is all done. + */ + +typedef struct script { + int condition; /* expected state at interrupt */ + int (*action)(); /* extra operations */ + int command; /* command to the chip */ +} *script_t; + +#define SCRIPT_MATCH(cs,ds) ((cs)&0x70|SCSI_PHASE((ds))) + +#define SII_PHASE_DISC 0x4 /* sort of .. */ + +/* When no command is needed */ +#define SCRIPT_END -1 + +/* forward decls of script actions */ +boolean_t + sii_script_true(), /* when nothing needed */ + sii_identify(), /* send identify msg */ + sii_dosynch(), /* negotiate synch xfer */ + sii_dma_in(), /* get data from target via dma */ + sii_dma_out(), /* send data to target via dma */ + sii_get_status(), /* get status from target */ + sii_end_transaction(), /* all come to an end */ + sii_msg_in(), /* get disconnect message(s) */ + sii_disconnected(); /* current target disconnected */ +/* forward decls of error handlers */ +boolean_t + sii_err_generic(), /* generic error handler */ + sii_err_disconn(), /* when a target disconnects */ + sii_err_rdp(), /* in reconn, handle rdp mgs */ + gimmeabreak(); /* drop into the debugger */ + +int sii_reset_scsibus(); +boolean_t sii_probe_target(); +static sii_wait(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) SCSI-7061 interface + */ +struct sii_softc { + watchdog_t wd; + sii_padded_regmap_t *regs; + volatile char *buff; + script_t script; + int (*error_handler)(); + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SII_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SII_STATE_TARGET 0x04 /* currently selected as target */ +#define SII_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SII_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char cmd_count; + + scsi_softc_t *sc; + target_info_t *active_target; + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + +} sii_softc_data[NSII]; + +typedef struct sii_softc *sii_softc_t; + +sii_softc_t sii_softc[NSII]; + +/* + * Synch xfer parameters, and timing conversions + */ +int sii_min_period = 63; /* in 4 ns units */ +int sii_max_offset = 3; /* pure number */ + +#define sii_to_scsi_period(a) (a) +#define scsi_period_to_sii(p) (((p) < sii_min_period) ? sii_min_period : (p)) + +/* + * Definition of the controller for the auto-configuration program. + */ + +int sii_probe(), scsi_slave(), sii_go(), sii_intr(); +extern void scsi_attach(); + +vm_offset_t sii_std[NSII] = { 0 }; +struct bus_device *sii_dinfo[NSII*8]; +struct bus_ctlr *sii_minfo[NSII]; +struct bus_driver sii_driver = + { sii_probe, scsi_slave, scsi_attach, sii_go, sii_std, "rz", sii_dinfo, + "sii", sii_minfo, /*BUS_INTR_B4_PROBE?*/}; + +/* + * Scripts + */ +struct script +sii_script_data_in[] = { + { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, + { SCSI_PHASE_DATAI|SII_CON_CON, sii_dma_in, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAI}, + { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} +}, + +sii_script_data_out[] = { + { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, + { SCSI_PHASE_DATAO|SII_CON_CON, sii_dma_out, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_DATAO}, + { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} +}, + +sii_script_cmd[] = { + { SCSI_PHASE_CMD|SII_CON_CON, sii_script_true, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SCSI_PHASE_CMD}, + { SCSI_PHASE_STATUS|SII_CON_CON, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON, sii_end_transaction, SCRIPT_END} +}, + +/* Same, after a disconnect */ + +sii_script_restart_data_in[] = { + { SCSI_PHASE_DATAI|SII_CON_CON|SII_CON_DST, sii_dma_in, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAI}, + { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} +}, + +sii_script_restart_data_out[] = { + { SCSI_PHASE_DATAO|SII_CON_CON|SII_CON_DST, sii_dma_out, + (SII_CMD_XFER|SII_CMD_DMA)|SII_CON_CON|SII_CON_DST|SCSI_PHASE_DATAO}, + { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} +}, + +sii_script_restart_cmd[] = { + { SCSI_PHASE_STATUS|SII_CON_CON|SII_CON_DST, sii_get_status, + SII_CMD_XFER|SII_CON_CON|SII_CON_DST|SCSI_PHASE_STATUS}, + { SCSI_PHASE_MSG_IN|SII_CON_CON|SII_CON_DST, sii_end_transaction, SCRIPT_END} +}, + +/* Synchronous transfer negotiation */ + +sii_script_try_synch[] = { + { SCSI_PHASE_MSG_OUT|SII_CON_CON, sii_dosynch, SCRIPT_END} +}, + +/* Disconnect sequence */ + +sii_script_disconnect[] = { + { SII_PHASE_DISC, sii_disconnected, SCRIPT_END} +}; + + + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + + +#define DEBUG +#ifdef DEBUG + +sii_state(regs) + sii_padded_regmap_t *regs; +{ + unsigned dmadr; + + if (regs == 0) + regs = (sii_padded_regmap_t*) 0xba000000; + + dmadr = regs->sii_dma_adr_low | (regs->sii_dma_adr_hi << 16); + db_printf("sc %x, dma %x @ x%X, cs %x, ds %x, cmd %x\n", + (unsigned) regs->sii_sc1, + (unsigned) regs->sii_dma_len, dmadr, + (unsigned) regs->sii_conn_csr, + (unsigned) regs->sii_data_csr, + (unsigned) regs->sii_cmd); + +} +sii_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = sii_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("@x%x: fl %X dma %X+%X cmd %x@%X id %X per %X off %X ior %X ret %X\n", + tgt, + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym(spt,1); + db_printf(": %X %X ", spt->condition, spt->command); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +sii_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = sii_softc[unit]->sc->target[i]; + if (tgt) + sii_target_state(tgt); + } +} + +sii_script_state(unit) +{ + script_t spt = sii_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym(spt,1); + db_printf(": %X %X ", spt->condition, spt->command); + db_printsym(spt->action,1); + db_printf(", "); + db_printsym(sii_softc[unit]->error_handler, 1); + return 0; + +} + +#define PRINT(x) if (scsi_debug) printf x + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int sii_logpt; +char sii_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x25 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + sii_log[sii_logpt++] = (e); + if (sii_logpt == LOGSIZE) sii_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +sii_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = sii_logpt; i < LOGSIZE; i++) { + c = sii_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-x%x", c & 0x7f); + } + db_printf("\n"); + return 0; +} + +sii_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /* TRACE */ +#define LOG(e,f) +#endif /* TRACE */ + +struct cnt { + unsigned int zeroes; + unsigned int usage; + unsigned int avg; + unsigned int min; + unsigned int max; +}; + +static bump(counter, value) + register struct cnt *counter; + register unsigned int value; +{ + register unsigned int n; + + if (value == 0) { + counter->zeroes++; + return; + } + n = counter->usage + 1; + counter->usage = n; + if (n == 0) { + printf("{Counter at x%x overflowed with avg x%x}", + counter, counter->avg); + return; + } else + if (n == 1) + counter->min = 0xffffffff; + + counter->avg = ((counter->avg * (n - 1)) + value) / n; + if (counter->min > value) + counter->min = value; + if (counter->max < value) + counter->max = value; +} + +struct cnt + s_cnt; + +#else /* DEBUG */ +#define PRINT(x) +#define LOG(e,f) +#define TR(x) +#define TRCHECK +#define TRWRAP +#endif /* DEBUG */ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send an identify msg to each possible target on the bus + * except of course ourselves. + */ +sii_probe(reg, ui) + unsigned reg; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + sii_softc_t sii = &sii_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register sii_padded_regmap_t *regs; + spl_t s; + boolean_t did_banner = FALSE; + char *dma_ptr; + static char *here = "sii_probe"; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + if (check_memory(reg, 0)) + return 0; + +#ifdef MACH_KERNEL + /* Mappable version side */ + SII_probe(reg, ui); +#endif /*MACH_KERNEL*/ + + /* + * Initialize hw descriptor + */ + sii_softc[unit] = sii; + sii->regs = (sii_padded_regmap_t *) (reg); + sii->buff = (volatile char*) (reg + SII_OFFSET_RAM); + + queue_init(&sii->waiting_targets); + + sc = scsi_master_alloc(unit, sii); + sii->sc = sc; + + sc->go = sii_go; + sc->watchdog = scsi_watchdog; + sc->probe = sii_probe_target; + + sii->wd.reset = sii_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; /* unlimited */ +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + regs = sii->regs; + + /* + * Clear out dma buffer + */ + blkclr(sii->buff, SII_RAM_SIZE); + + /* + * Reset chip, fully. + */ + s = splbio(); + sii_reset(regs, TRUE); + + /* + * Our SCSI id on the bus. + * The user can set this via the prom on pmaxen/3maxen. + * If this changes it is easy to fix: make a default that + * can be changed as boot arg. + */ +#ifdef unneeded + regs->sii_id = (scsi_initiator_id[unit] & SII_ID_MASK)|SII_ID_IO; +#endif + sc->initiator_id = regs->sii_id & SII_ID_MASK; + printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + for (target_id = 0, dma_ptr = (char*)sii->buff; + target_id < 8; + target_id++, dma_ptr += (PER_TGT_DMA_SIZE*2)) { + + register unsigned csr, dsr; + register scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + regs->sii_sel_csr = target_id; + wbflush(); + + /* select */ + regs->sii_cmd = SII_CMD_SEL; + wbflush(); + + /* wait for a selection timeout delay, and some more */ + delay(251000); + + dsr = regs->sii_data_csr; + csr = regs->sii_conn_csr; + if ((csr & SII_CON_CON) == 0) { + + regs->sii_conn_csr = csr;/*wtc bits*/ + + /* abort sel in progress */ + if (csr & SII_CON_SIP) { + regs->sii_cmd = SII_CMD_DIS; + wbflush(); + csr = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); + regs->sii_conn_csr = 0xffff;/*wtc bits */ + regs->sii_data_csr = 0xffff; + regs->sii_cmd = 0; + wbflush(); + } + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + /* should be command phase here */ + if (SCSI_PHASE(dsr) != SCSI_PHASE_CMD) + panic(here); + + /* acknowledge state change */ + SII_ACK(regs,csr,dsr,0); + + /* build command in (bogus) dma area */ + { + unsigned int *p = (unsigned int*) dma_ptr; + + p[0] = SCSI_CMD_TEST_UNIT_READY | (0 << 8); + p[1] = 0 | (0 << 8); + p[2] = 0 | (0 << 8); + } + + /* set up dma xfer parameters */ + regs->sii_dma_len = 6; + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + wbflush(); + + /* issue dma command */ + SII_COMMAND(regs,csr,dsr,SII_CMD_XFER|SII_CMD_DMA); + + /* wait till done */ + dsr = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_cmd &= ~(SII_CMD_XFER|SII_CMD_DMA); + regs->sii_data_csr = SII_DTR_DONE;/* clear */ + regs->sii_dma_len = 0; + + /* move on to status phase */ + dsr = sii_wait(®s->sii_data_csr, SCSI_PHASE_STATUS,1); + csr = regs->sii_conn_csr; + SII_ACK(regs,csr,dsr,0); + + if (SCSI_PHASE(dsr) != SCSI_PHASE_STATUS) + panic(here); + + /* get status byte */ + dsr = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); + csr = regs->sii_conn_csr; + + status.bits = regs->sii_data; + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + /* get cmd_complete message */ + SII_ACK(regs,csr,dsr,0); + SII_COMMAND(regs,csr,dsr,SII_CMD_XFER); + dsr = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + + dsr = sii_wait(®s->sii_data_csr, SCSI_PHASE_MSG_IN,1); + csr = regs->sii_conn_csr; + + + SII_ACK(regs,csr,dsr,0); + i = regs->sii_data; + SII_COMMAND(regs,csr,dsr,SII_CMD_XFER); + + /* check disconnected, clear all intr bits */ + csr = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); + if (regs->sii_conn_csr & SII_CON_CON) + panic(here); + + regs->sii_data_csr = 0xffff; + regs->sii_conn_csr = 0xffff; + regs->sii_cmd = 0; + + /* + * Found a target + */ + sii->ntargets++; + { + register target_info_t *tgt; + tgt = scsi_slave_alloc(sc->masterno, target_id, sii); + + tgt->dma_ptr = dma_ptr; + tgt->cmd_ptr = decent_buffer[unit*8 + target_id]; +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + } + } + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +sii_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + sii_softc_t sii = sii_softc[tgt->masterno]; + boolean_t newlywed; + int sii_probe_timeout(); + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + char *dma_ptr = (char*)sii->buff; + + dma_ptr += (PER_TGT_DMA_SIZE * tgt->target_id)*2; + tgt->dma_ptr = dma_ptr; + tgt->cmd_ptr = decent_buffer[tgt->masterno*8 + tgt->target_id]; +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + + } + + /* Unfortunately, the SII chip does not have timeout support + for selection */ + timeout(sii_probe_timeout, tgt, hz); + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + untimeout(sii_probe_timeout, tgt); + tgt->flags = TGT_ALIVE; + return TRUE; +} + +sii_probe_timeout(tgt) + target_info_t *tgt; +{ + sii_softc_t sii = (sii_softc_t)tgt->hw_state; + register sii_padded_regmap_t *regs = sii->regs; + int cs, ds; + spl_t s; + + /* cancelled ? */ + if (tgt->done != SCSI_RET_IN_PROGRESS) + return; + + s = splbio(); + + /* Someone else might be using the bus (rare) */ + switch (regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) { + case SII_CON_SIP: + /* We really timed out */ + break; + case SII_CON_SIP|SII_CON_LST: + /* Someone else is (still) using the bus */ + sii->wd.watchdog_state = SCSI_WD_ACTIVE; + /* fall-through */ + default: + /* Did not get a chance to the bus yet */ + timeout(sii_probe_timeout, tgt, hz); + goto ret; + } + regs->sii_cmd = SII_CMD_DIS; + wbflush(); + regs->sii_csr |= SII_CSR_RSE; + regs->sii_cmd = 0; + wbflush(); + + sii->done = SCSI_RET_DEVICE_DOWN; + cs = regs->sii_conn_csr; + ds = regs->sii_data_csr; + if (!sii_end(sii, cs, ds)) + (void) sii_reconnect(sii, cs, ds); +ret: + splx(s); +} + + +static sii_wait(preg, until, complain) + volatile unsigned short *preg; +{ + int timeo = 1000000; + while ((*preg & until) != until) { + delay(1); + if (!timeo--) { + if (complain) { + gimmeabreak(); + printf("sii_wait TIMEO with x%x\n", *preg); + } + break; + } + } +#ifdef DEBUG + bump(&s_cnt, 1000000-timeo); +#endif + return *preg; +} + +sii_reset(regs, quickly) + register sii_padded_regmap_t *regs; + boolean_t quickly; +{ + int my_id; + + my_id = regs->sii_id & SII_ID_MASK; + + regs->sii_cmd = SII_CMD_RESET; + wbflush(); + delay(30); + + /* clear them all random bitsy */ + regs->sii_conn_csr = SII_CON_SWA|SII_CON_SCH|SII_CON_BERR|SII_CON_RST; + regs->sii_data_csr = SII_DTR_ATN|SII_DTR_DONE; + + regs->sii_id = my_id | SII_ID_IO; + + regs->sii_dma_ctrl = 0; /* asynch */ + + regs->sii_dma_len = 0; + regs->sii_dma_adr_low = 0; + regs->sii_dma_adr_hi = 0; + + regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM; + /* later: SII_CSR_RSE */ + + regs->sii_diag_csr = SII_DIAG_PORT_ENB; + wbflush(); + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call sii_bus_reset(). + */ + regs->sii_cmd = SII_CMD_RST; + +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +sii_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + sii_softc_t sii; + register spl_t s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + + LOG(1,"go"); + + sii = (sii_softc_t)tgt->hw_state; + + /* + * We cannot do real DMA. + */ +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + if (tgt->ior) + fdma_map(&tgt->fdma, tgt->ior); +#endif /*MACH_KERNEL*/ + + copyout_gap16(tgt->cmd_ptr, tgt->dma_ptr, cmd_count); + + if ((tgt->cur_cmd == SCSI_CMD_WRITE) || + (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ + io_req_t ior = tgt->ior; + register int len = ior->io_count; + + tgt->transient_state.out_count = len; + + if (len > PER_TGT_BUFF_SIZE) + len = PER_TGT_BUFF_SIZE; + copyout_gap16( ior->io_data, + tgt->dma_ptr + (cmd_count<<1), + len); + tgt->transient_state.copy_count = len; + + /* avoid leaks */ + if (len < tgt->block_size) { + bzero_gap16(tgt->dma_ptr + ((cmd_count + len)<<1), + len - tgt->block_size); + len = tgt->block_size; + tgt->transient_state.copy_count = len; + } + + } else { + tgt->transient_state.out_count = 0; + tgt->transient_state.copy_count = 0; + } + + tgt->transient_state.cmd_count = cmd_count; + tgt->transient_state.isa_oddbb = FALSE; + + disconn = BGET(scsi_might_disconnect,tgt->masterno,tgt->target_id); + disconn = disconn && (sii->ntargets > 1); + disconn |= BGET(scsi_should_disconnect,tgt->masterno,tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? sii_err_disconn : sii_err_generic; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(0x13,"readop"); + scp = sii_script_data_in; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x14,"writeop"); + scp = sii_script_data_out; + break; + case SCSI_CMD_INQUIRY: + /* This is likely the first thing out: + do the synch neg if so */ + if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) { + scp = sii_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + break; + } + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ + case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + scp = sii_script_data_in; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ + tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd); + tgt->transient_state.out_count = + cmd_count - tgt->transient_state.cmd_count; + scp = sii_script_data_out; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_TEST_UNIT_READY: + /* + * Do the synch negotiation here, unless done already + */ + if (tgt->flags & TGT_DID_SYNCH) { + scp = sii_script_cmd; + } else { + scp = sii_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + } + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + scp = sii_script_cmd; + } + + tgt->transient_state.script = scp; + tgt->transient_state.handler = handler; + tgt->transient_state.identify = (cmd_only) ? 0xff : + (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT : + SCSI_IDENTIFY); + + if (in_count) + tgt->transient_state.in_count = + (in_count < tgt->block_size) ? tgt->block_size : in_count; + else + tgt->transient_state.in_count = 0; + + tgt->transient_state.dma_offset = 0; + + /* + * See if another target is currently selected on + * this SCSI bus, e.g. lock the sii structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + * XXX here and everywhere, locks! + */ + /* + * Protection viz reconnections makes it tricky. + */ +/* s = splbio();*/ + s = splhigh(); + + if (sii->wd.nactive++ == 0) + sii->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (sii->state & SII_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + LOG(0x80+tgt->target_id,0); + enqueue_tail(&sii->waiting_targets, (queue_entry_t) tgt); + } else { + /* + * It is down to at most two contenders now, + * we will treat reconnections same as selections + * and let the scsi-bus arbitration process decide. + */ + sii->state |= SII_STATE_BUSY; + sii->next_target = tgt; + sii_attempt_selection(sii); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +sii_attempt_selection(sii) + sii_softc_t sii; +{ + target_info_t *tgt; + register int out_count; + sii_padded_regmap_t *regs; + register int cmd; + + regs = sii->regs; + tgt = sii->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + * [They are intermixed to avoid wbflush()es] + */ + sii->active_target = tgt; + + out_count = tgt->transient_state.cmd_count; + + /* set dma pointer and counter */ + regs->sii_dma_len = out_count; + regs->sii_dma_adr_low = SII_DMADR_LO(tgt->dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(tgt->dma_ptr); + + sii->error_handler = tgt->transient_state.handler; + + regs->sii_sel_csr = tgt->target_id; + + sii->done = SCSI_RET_IN_PROGRESS; + + regs->sii_dma_ctrl = tgt->sync_offset; + + sii->cmd_count = out_count; + +/* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST))*/ + if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL)) + return; + regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE|SII_CSR_HPM; + + sii->script = tgt->transient_state.script; + sii->in_count = 0; + sii->out_count = 0; + + if (tgt->flags & TGT_DID_SYNCH) { + if (tgt->transient_state.identify == 0xff) + cmd = SII_CMD_SEL; + else { + cmd = SII_CMD_SEL | SII_CMD_ATN | + SII_CMD_CON | SII_CMD_XFER | SCSI_PHASE_MSG_OUT; + /* chain select and message out */ +/*??*/ regs->sii_dma_1st_byte = tgt->transient_state.identify; + } + } else if (tgt->flags & TGT_TRY_SYNCH) + cmd = SII_CMD_SEL | SII_CMD_ATN; + else + cmd = SII_CMD_SEL; + +/* if (regs->sii_conn_csr & (SII_CON_CON|SII_CON_DST)) { */ + if (regs->sii_sc1 & (SII_CS1_BSY|SII_CS1_SEL)) { + /* let the reconnection attempt proceed */ + regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE| + SII_CSR_HPM|SII_CSR_RSE; + sii->script = 0; + LOG(0x8c,0); + } else { + regs->sii_cmd = cmd; + wbflush(); + } +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +boolean_t sii_inside_sii_intr = FALSE; + +sii_intr(unit,spllevel) +{ + register sii_softc_t sii; + register script_t scp; + register int cs, ds; + register sii_padded_regmap_t *regs; + boolean_t try_match; +#ifdef MACH_KERNEL + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return SII_intr(unit,spllevel); +#endif /*MACH_KERNEL*/ + + /* interrupt code is NOT reentrant */ + if (sii_inside_sii_intr) { + LOG(0x22,"!!attempted to reenter sii_intr!!"); + return; + } + sii_inside_sii_intr = TRUE; + + LOG(5,"\n\tintr"); + + sii = sii_softc[unit]; + + /* collect status information */ + regs = sii->regs; + cs = regs->sii_conn_csr; + ds = regs->sii_data_csr; + +TR(cs); +TR(ds); +TR(regs->sii_cmd); +TRCHECK; + + if (cs & SII_CON_RST){ + sii_bus_reset(sii); + goto getout; + } + + /* we got an interrupt allright */ + if (sii->active_target) + sii->wd.watchdog_state = SCSI_WD_ACTIVE; + + /* rid of DONEs */ + if (ds & SII_DTR_DONE) { + regs->sii_data_csr = SII_DTR_DONE; + LOG(0x1e,"done"); + ds = regs->sii_data_csr; + cs = regs->sii_conn_csr; + } + + /* drop spurious calls, note that sometimes + * ds and cs get out-of-sync */ + if (((cs & SII_CON_CI) | (ds & SII_DTR_DI)) == 0) { + LOG(2,"SPURIOUS"); + goto getout; + } + + /* clear interrupt flags */ + + regs->sii_conn_csr = cs; + regs->sii_data_csr = cs; + + /* drop priority */ + splx(spllevel); + + if ((sii->state & SII_STATE_TARGET) || + (cs & SII_CON_TGT)) { + sii_target_intr(sii,cs,ds); + goto getout; + } + + scp = sii->script; + + /* check who got the bus */ + if ((scp == 0) || (cs & SII_CON_LST)) { + if (cs & SII_CON_DST) { + sii_reconnect(sii, cs, ds); + goto getout; + } + LOG(0x12,"no-script"); + goto getout; + } + + if (SCRIPT_MATCH(cs,ds) != scp->condition) { + if (try_match = (*sii->error_handler)(sii, cs, ds)) { + cs = regs->sii_conn_csr; + ds = regs->sii_data_csr; + } + } else + try_match = TRUE; + + /* might have been side effected */ + scp = sii->script; + + if (try_match && (SCRIPT_MATCH(cs,ds) == scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(sii, cs, ds)) { + /* might have been side effected */ + scp = sii->script; + sii->script = scp + 1; + regs->sii_cmd = scp->command; + wbflush(); + } + } +getout: + sii_inside_sii_intr = FALSE; +} + + +sii_target_intr(sii) + register sii_softc_t sii; +{ + panic("SII: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ +boolean_t +sii_script_true(sii, cs, ds) + register sii_softc_t sii; + +{ + SII_COMMAND(sii->regs,cs,ds,SII_CON_CON/*sanity*/); + LOG(7,"nop"); + return TRUE; +} + +boolean_t +sii_end_transaction( sii, cs, ds) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs = sii->regs; + + SII_COMMAND(sii->regs,cs,ds,0); + + LOG(0x1f,"end_t"); + + regs->sii_csr &= ~SII_CSR_RSE; + + /* is the fifo really clean here ? */ + ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); + + if (regs->sii_data != SCSI_COMMAND_COMPLETE) + printf("{T%x}", regs->sii_data); + + regs->sii_cmd = SII_CMD_XFER | SII_CON_CON | SCSI_PHASE_MSG_IN | + (cs & SII_CON_DST); + wbflush(); + + ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + + regs->sii_cmd = 0/*SII_PHASE_DISC*/; + wbflush(); + + cs = regs->sii_conn_csr; + + if ((cs & SII_CON_SCH) == 0) + cs = sii_wait(®s->sii_conn_csr, SII_CON_SCH,1); + regs->sii_conn_csr = SII_CON_SCH; + + regs->sii_csr |= SII_CSR_RSE; + + cs = regs->sii_conn_csr; + + if (!sii_end(sii, cs, ds)) + (void) sii_reconnect(sii, cs, ds); + return FALSE; +} + +boolean_t +sii_end( sii, cs, ds) + register sii_softc_t sii; +{ + register target_info_t *tgt; + register io_req_t ior; + register sii_padded_regmap_t *regs = sii->regs; + + LOG(6,"end"); + + tgt = sii->active_target; + + if ((tgt->done = sii->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + sii->script = 0; + + if (sii->wd.nactive-- == 1) + sii->wd.watchdog_state = SCSI_WD_INACTIVE; + + /* check reconnection not pending */ + cs = regs->sii_conn_csr; + if ((cs & SII_CON_DST) == 0) + sii_release_bus(sii); + else { + sii->active_target = 0; +/* sii->state &= ~SII_STATE_BUSY; later */ + } + + if (ior = tgt->ior) { + LOG(0xA,"ops->restart"); +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_unmap(&tgt->fdma, ior); +#endif /*MACH_KERNEL*/ + (*tgt->dev_ops->restart)(tgt, TRUE); + if (cs & SII_CON_DST) + sii->state &= ~SII_STATE_BUSY; + } + + return ((cs & SII_CON_DST) == 0); +} + +boolean_t +sii_release_bus(sii) + register sii_softc_t sii; +{ + boolean_t ret = FALSE; + + LOG(9,"release"); + + sii->script = 0; + + if (sii->state & SII_STATE_COLLISION) { + + LOG(0xB,"collided"); + sii->state &= ~SII_STATE_COLLISION; + sii_attempt_selection(sii); + + } else if (queue_empty(&sii->waiting_targets)) { + + sii->state &= ~SII_STATE_BUSY; + sii->active_target = 0; + ret = TRUE; + + } else { + + LOG(0xC,"dequeue"); + sii->next_target = (target_info_t *) + dequeue_head(&sii->waiting_targets); + sii_attempt_selection(sii); + } + return ret; +} + +boolean_t +sii_get_status( sii, cs, ds) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs = sii->regs; + register scsi2_status_byte_t status; + register target_info_t *tgt; + unsigned int len; + unsigned short cmd; + + LOG(0xD,"get_status"); +TRWRAP; + + sii->state &= ~SII_STATE_DMA_IN; + + tgt = sii->active_target; + sii->error_handler = tgt->transient_state.handler; + + if (len = sii->in_count) { + if ((tgt->cur_cmd != SCSI_CMD_READ) && + (tgt->cur_cmd != SCSI_CMD_LONG_READ)){ + len -= regs->sii_dma_len; + copyin_gap16(tgt->dma_ptr, tgt->cmd_ptr, len); + if (len & 0x1) /* odd byte, left in silo */ + tgt->cmd_ptr[len - 1] = regs->sii_data; + } else { + if (regs->sii_dma_len) { +#if 0 + this is incorrect and besides.. + tgt->ior->io_residual = regs->sii_dma_len; +#endif + len -= regs->sii_dma_len; + } + careful_copyin_gap16( tgt, tgt->transient_state.dma_offset, + len, ds & SII_DTR_OBB, + regs->sii_dma_1st_byte); + } + sii->in_count = 0; + } + + len = regs->sii_dma_len; + regs->sii_dma_len = 0;/*later?*/ + + /* if dma is still in progress we have to quiet it down */ + cmd = regs->sii_cmd; + if (cmd & SII_CMD_DMA) { + regs->sii_cmd = cmd & ~(SII_CMD_DMA|SII_CMD_XFER); + wbflush(); + /* DONE might NOT pop up. Sigh. */ + delay(10); + regs->sii_data_csr = regs->sii_data_csr; + } + + regs->sii_cmd = SCSI_PHASE_STATUS|SII_CON_CON|(cs & SII_CON_DST); + wbflush(); + + ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); + status.bits = regs->sii_data; + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(sii->active_target, SCSI_ERR_STATUS, status.bits, 0); + sii->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + sii->done = SCSI_RET_SUCCESS; + + return TRUE; +} + +boolean_t +sii_dma_in( sii, cs, ds) + register sii_softc_t sii; +{ + register target_info_t *tgt; + register sii_padded_regmap_t *regs = sii->regs; + char *dma_ptr; + register int count; + boolean_t advance_script = TRUE; + + SII_COMMAND(regs,cs,ds,0); + LOG(0xE,"dma_in"); + + tgt = sii->active_target; + sii->error_handler = tgt->transient_state.handler; + sii->state |= SII_STATE_DMA_IN; + + if (sii->in_count == 0) { + /* + * Got nothing yet: either just sent the command + * or just reconnected + */ + register int avail; + + if (tgt->transient_state.isa_oddbb) { + regs->sii_dma_1st_byte = tgt->transient_state.oddbb; + tgt->transient_state.isa_oddbb = FALSE; + } + + count = tgt->transient_state.in_count; + count = u_min(count, (SII_DMA_COUNT_MASK+1)); + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + + /* common case of 8k-or-less read ? */ + advance_script = (tgt->transient_state.in_count == count); + + } else { + + /* + * We received some data. + * Also, take care of bogus interrupts + */ + register int offset, xferred; + unsigned char obb = regs->sii_data; + + xferred = sii->in_count - regs->sii_dma_len; + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.in_count, (SII_DMA_COUNT_MASK+1)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int avail; + avail = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, avail); + } + + /* get some more */ + dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1); + sii->in_count = count; + regs->sii_dma_len = count; + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + wbflush(); + regs->sii_cmd = sii->script->command; + wbflush(); + + /* copy what we got */ + careful_copyin_gap16( tgt, offset, xferred, ds & SII_DTR_OBB, obb); + + /* last chunk ? */ + if (count == tgt->transient_state.in_count) { + sii->script++; + } + return FALSE; + } +quickie: + sii->in_count = count; + dma_ptr = tgt->dma_ptr + (tgt->transient_state.dma_offset << 1); + regs->sii_dma_len = count; + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + wbflush(); + + if (!advance_script) { + regs->sii_cmd = sii->script->command; + wbflush(); + } + return advance_script; +} + +/* send data to target. Called in three different ways: + (a) to start transfer (b) to restart a bigger-than-8k + transfer (c) after reconnection + */ +boolean_t +sii_dma_out( sii, cs, ds) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs = sii->regs; + register char *dma_ptr; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count = sii->out_count; + + SII_COMMAND(regs,cs,ds,0); + LOG(0xF,"dma_out"); + + tgt = sii->active_target; + sii->error_handler = tgt->transient_state.handler; + sii->state &= ~SII_STATE_DMA_IN; + + if (sii->out_count == 0) { + /* + * Nothing committed: either just sent the + * command or reconnected + */ + register int remains; + + count = tgt->transient_state.out_count; + count = u_min(count, (SII_DMA_COUNT_MASK+1)); + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + + /* common case of 8k-or-less write ? */ + advance_script = (tgt->transient_state.out_count == count); + } else { + /* + * We sent some data. + * Also, take care of bogus interrupts + */ + register int offset, xferred; + + xferred = sii->out_count - regs->sii_dma_len; + assert(xferred > 0); + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + count = u_min(tgt->transient_state.out_count, (SII_DMA_COUNT_MASK+1)); + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) { + tgt->transient_state.dma_offset = 0; + } else { + register int remains; + remains = PER_TGT_BUFF_SIZE - tgt->transient_state.dma_offset; + count = u_min(count, remains); + } + /* last chunk ? */ + if (tgt->transient_state.out_count == count) + goto quickie; + + /* ship some more */ + dma_ptr = tgt->dma_ptr + + ((tgt->transient_state.cmd_count + tgt->transient_state.dma_offset) << 1); + sii->out_count = count; + regs->sii_dma_len = count; + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + wbflush(); + regs->sii_cmd = sii->script->command; + + /* copy some more data */ + careful_copyout_gap16(tgt, offset, xferred); + return FALSE; + } + +quickie: + sii->out_count = count; + dma_ptr = tgt->dma_ptr + + ((tgt->transient_state.cmd_count + tgt->transient_state.dma_offset) << 1); + regs->sii_dma_len = count; + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + wbflush(); + + if (!advance_script) { + regs->sii_cmd = sii->script->command; + wbflush(); + } + return advance_script; +} + +/* disconnect-reconnect ops */ + +/* get the message in via dma */ +boolean_t +sii_msg_in(sii, cs, ds) + register sii_softc_t sii; + register unsigned char cs, ds; +{ + register target_info_t *tgt; + char *dma_ptr; + register sii_padded_regmap_t *regs = sii->regs; + + LOG(0x15,"msg_in"); + + tgt = sii->active_target; + + dma_ptr = tgt->dma_ptr; + /* We would clobber the data for READs */ + if (sii->state & SII_STATE_DMA_IN) { + register int offset; + offset = tgt->transient_state.cmd_count + tgt->transient_state.dma_offset; + if (offset & 1) offset++; + dma_ptr += (offset << 1); + } + + regs->sii_dma_adr_low = SII_DMADR_LO(dma_ptr); + regs->sii_dma_adr_hi = SII_DMADR_HI(dma_ptr); + /* We only really expect two bytes */ + regs->sii_dma_len = sizeof(scsi_command_group_0); + wbflush(); + + return TRUE; +} + +/* check the message is indeed a DISCONNECT */ +boolean_t +sii_disconnect(sii, cs, ds) + register sii_softc_t sii; + register unsigned char cs, ds; + +{ + register target_info_t *tgt; + register int len; + boolean_t ok = FALSE; + unsigned int dmsg = 0; + + tgt = sii->active_target; + + len = sizeof(scsi_command_group_0) - sii->regs->sii_dma_len; + PRINT(("{G%d}",len)); + +/* if (len == 0) ok = FALSE; */ + if (len == 1) { + dmsg = sii->regs->sii_dma_1st_byte; + ok = (dmsg == SCSI_DISCONNECT); + } else if (len == 2) { + register char *msgs; + register unsigned int offset; + register sii_padded_regmap_t *regs = sii->regs; + + /* wherever it was, take it from there */ + offset = regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16); + msgs = (char*)sii->buff + (offset << 1); + dmsg = *((unsigned short *)msgs); + + /* A SDP message preceeds it in non-completed READs */ + ok = (((dmsg & 0xff) == SCSI_DISCONNECT) || + (dmsg == ((SCSI_DISCONNECT<<8)|SCSI_SAVE_DATA_POINTER))); + } + if (!ok) + printf("[tgt %d bad msg (%d): %x]", tgt->target_id, len, dmsg); + + return TRUE; +} + +/* save all relevant data, free the BUS */ +boolean_t +sii_disconnected(sii, cs, ds) + register sii_softc_t sii; + register unsigned char cs, ds; + +{ + register target_info_t *tgt; + + SII_COMMAND(sii->regs,cs,ds,0); + + sii->regs->sii_csr = SII_CSR_IE|SII_CSR_PCE|SII_CSR_SLE| + SII_CSR_HPM|SII_CSR_RSE; + + LOG(0x16,"disconnected"); + + sii_disconnect(sii,cs,ds); + + tgt = sii->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = sii->error_handler; + /* the rest has been saved in sii_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + sii_release_bus(sii); + + return FALSE; +} + +/* get reconnect message, restore BUS */ +boolean_t +sii_reconnect(sii, cs, ds) + register sii_softc_t sii; + register unsigned char cs, ds; + +{ + register target_info_t *tgt; + sii_padded_regmap_t *regs; + int id; + + regs = sii->regs; + regs->sii_conn_csr = SII_CON_SCH; + regs->sii_cmd = SII_CON_CON|SII_CON_DST|SCSI_PHASE_MSG_IN; + wbflush(); + + LOG(0x17,"reconnect"); + + /* + * See if this reconnection collided with a selection attempt + */ + if (sii->state & SII_STATE_BUSY) + sii->state |= SII_STATE_COLLISION; + + sii->state |= SII_STATE_BUSY; + + cs = regs->sii_conn_csr; + + /* tk50s are slow */ + if ((cs & SII_CON_CON) == 0) + cs = sii_wait(®s->sii_conn_csr, SII_CON_CON,1); + + /* ?? */ + if (regs->sii_conn_csr & SII_CON_BERR) + regs->sii_conn_csr = SII_CON_BERR; + + if ((ds & SII_DTR_IBF) == 0) + ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); + + if (regs->sii_data != SCSI_IDENTIFY) + printf("{I%x %x}", regs->sii_data, regs->sii_dma_1st_byte); + + /* find tgt: id is in sii_destat */ + id = regs->sii_destat; + + tgt = sii->sc->target[id]; + if (id > 7 || tgt == 0) panic("sii_reconnect"); + + PRINT(("{R%d}", id)); + if (sii->state & SII_STATE_COLLISION) + PRINT(("[B %d-%d]", sii->active_target->target_id, id)); + + LOG(0x80+id,0); + + sii->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + /* synch params */ + regs->sii_dma_ctrl = tgt->sync_offset; + regs->sii_dma_len = 0; + + sii->script = tgt->transient_state.script; + sii->error_handler = sii_err_rdp; + sii->in_count = 0; + sii->out_count = 0; + + regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN; + wbflush(); + + (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + + return TRUE; +} + + +/* do the synch negotiation */ +boolean_t +sii_dosynch( sii, cs, ds) + register sii_softc_t sii; +{ + /* + * Phase is MSG_OUT here, cmd has not been xferred + */ + int *p, len; + unsigned short dmalo, dmahi, dmalen; + register target_info_t *tgt; + register sii_padded_regmap_t *regs = sii->regs; + unsigned char off; + + regs->sii_cmd = SCSI_PHASE_MSG_OUT|SII_CMD_ATN|SII_CON_CON; + wbflush(); + + LOG(0x11,"dosync"); + + tgt = sii->active_target; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + p = (int*) (tgt->dma_ptr + (((regs->sii_dma_len<<1) + 2) & ~3)); + p[0] = SCSI_IDENTIFY | (SCSI_EXTENDED_MESSAGE<<8); + p[1] = 3 | (SCSI_SYNC_XFER_REQUEST<<8); + if (BGET(scsi_no_synchronous_xfer,tgt->masterno,tgt->target_id)) + off = 0; + else + off = sii_max_offset; + /* but we'll ship "off" manually */ + p[2] = sii_to_scsi_period(sii_min_period) |(off << 8); + + dmalen = regs->sii_dma_len; + dmalo = regs->sii_dma_adr_low; + dmahi = regs->sii_dma_adr_hi; + regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t) /* + 1 */; + regs->sii_dma_adr_low = SII_DMADR_LO(p); + regs->sii_dma_adr_hi = SII_DMADR_HI(p); + wbflush(); + + regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CMD_ATN| + SII_CON_CON|SCSI_PHASE_MSG_OUT; + wbflush(); + + /* wait for either DONE or MIS */ + ds = sii_wait(®s->sii_data_csr, SII_DTR_DI,1); + + /* TK50s do not like xtended messages */ + /* and some others just ignore the standard */ + if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_OUT) { + /* disentangle FIFO */ + regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT; + ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + if (SCSI_PHASE(ds) == SCSI_PHASE_MSG_IN) + goto msgin; + goto got_answer; + } + + /* ack and stop dma */ + regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT|SII_CMD_ATN; + wbflush(); + ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + wbflush(); + + /* last byte of message */ + regs->sii_data = off; + wbflush(); + regs->sii_cmd = SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_OUT; + wbflush(); + + /* Race here: who will interrupt first, the DMA + controller or the status watching machine ? */ + delay(1000); + regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_OUT; + wbflush(); + + ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + + /* The standard sez there nothing else the target can do but.. */ + ds = sii_wait(®s->sii_data_csr, SCSI_PHASE_MSG_IN,0); + + /* Of course, what are standards for ? */ + if (SCSI_PHASE(ds) == SCSI_PHASE_CMD) + goto cmdp; +msgin: + /* ack */ + regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN; + wbflush(); + + /* set up dma to receive answer */ + regs->sii_dma_adr_low = SII_DMADR_LO(p); + regs->sii_dma_adr_hi = SII_DMADR_HI(p); + regs->sii_dma_len = sizeof(scsi_synch_xfer_req_t); + wbflush(); + regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SII_CON_CON|SCSI_PHASE_MSG_IN; + wbflush(); + + /* wait for the answer, and look at it */ + ds = sii_wait(®s->sii_data_csr, SII_DTR_MIS,1); + + regs->sii_cmd = SII_CON_CON|SCSI_PHASE_MSG_IN; + wbflush(); + ds = sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + +got_answer: + /* do not cancel the phase mismatch */ + regs->sii_data_csr = SII_DTR_DONE; + + if (regs->sii_dma_len || ((p[0] & 0xff) != SCSI_EXTENDED_MESSAGE)) { + /* did not like it */ + printf(" did not like SYNCH xfer "); + } else { + /* will do synch */ + tgt->sync_period = scsi_period_to_sii((p[1]>>8)&0xff); + tgt->sync_offset = regs->sii_data; /* odd xfer, in silo */ + /* sanity */ + if (tgt->sync_offset > sii_max_offset) + tgt->sync_offset = sii_max_offset; + regs->sii_dma_ctrl = tgt->sync_offset; + } + +cmdp: + /* phase should be command now */ + regs->sii_dma_len = dmalen; + regs->sii_dma_adr_low = dmalo; + regs->sii_dma_adr_hi = dmahi; + wbflush(); + + /* continue with simple command script */ + sii->error_handler = sii_err_generic; + + sii->script = (tgt->cur_cmd == SCSI_CMD_INQUIRY) ? + sii_script_data_in : sii_script_cmd; + if (SCSI_PHASE(ds) == SCSI_PHASE_CMD ) + return TRUE; + + sii->script++; + if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS ) + return TRUE; + + sii->script++; /* msgin? */ + sii->script++; + if (SCSI_PHASE(ds) == SII_PHASE_DISC) + return TRUE; + +gimmeabreak(); + panic("sii_dosynch"); + return FALSE; +} + +/* + * The bus was reset + */ +sii_bus_reset(sii) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs = sii->regs; + + LOG(0x21,"bus_reset"); + + /* + * Clear interrupt bits + */ + regs->sii_conn_csr = 0xffff; + regs->sii_data_csr = 0xffff; + + /* + * Clear bus descriptor + */ + sii->script = 0; + sii->error_handler = 0; + sii->active_target = 0; + sii->next_target = 0; + sii->state = 0; + queue_init(&sii->waiting_targets); + sii->wd.nactive = 0; + sii_reset(regs, TRUE); + + log(LOG_KERN, "sii: (%d) bus reset ", ++sii->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (sii->sc == 0) /* sanity */ + return; + + sii_inside_sii_intr = FALSE; + + scsi_bus_was_reset(sii->sc); +} + +/* + * Error handlers + */ + +/* + * Generic, default handler + */ +boolean_t +sii_err_generic(sii, cs, ds) + register sii_softc_t sii; +{ + register int cond = sii->script->condition; + + LOG(0x10,"err_generic"); + + /* + * Note to DEC hardware people. + * Dropping the notion of interrupting on + * DMA completions (or at least make it optional) + * would save TWO interrupts out of the SEVEN that + * are currently requested for a non-disconnecting + * READ or WRITE operation. + */ + if (ds & SII_DTR_DONE) + return TRUE; + + /* this is a band-aid */ + if ((SCSI_PHASE(cond) == SII_PHASE_DISC) && + (cs & SII_CON_SCH)) { + ds &= ~7; + ds |= SII_PHASE_DISC; + (void) (*sii->script->action)(sii,cs,ds); + return FALSE; + } + + /* TK50s are slow to connect, forgive em */ + if ((SCSI_PHASE(ds) == SCSI_PHASE_MSG_OUT) || + (SCSI_PHASE(cond) == SCSI_PHASE_MSG_OUT)) + return TRUE; + if ((SCSI_PHASE(cond) == SCSI_PHASE_CMD) && + ((SCSI_PHASE(ds) == 0) || (SCSI_PHASE(ds) == 4) || (SCSI_PHASE(ds) == 5))) + return TRUE; + + /* transition to status ? */ + if (SCSI_PHASE(ds) == SCSI_PHASE_STATUS) + return sii_err_to_status(sii, cs, ds); + + return sii_err_phase_mismatch(sii,cs,ds); +} + +/* + * Handle generic errors that are reported as + * an unexpected change to STATUS phase + */ +sii_err_to_status(sii, cs, ds) + register sii_softc_t sii; +{ + script_t scp = sii->script; + + LOG(0x20,"err_tostatus"); + while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS) + scp++; + sii->script = scp; +#if 0 + /* + * Normally, we would already be able to say the command + * is in error, e.g. the tape had a filemark or something. + * But in case we do disconnected mode WRITEs, it is quite + * common that the following happens: + * dma_out -> disconnect -> reconnect + * and our script might expect at this point that the dma + * had to be restarted (it didn't know it was completed + * because the tape record is shorter than we asked for). + * And in any event.. it is both correct and cleaner to + * declare error iff the STATUS byte says so. + */ + sii->done = SCSI_RET_NEED_SENSE; +#endif + return TRUE; +} + +/* + * Watch for a disconnection + */ +boolean_t +sii_err_disconn(sii, cs, ds) + register sii_softc_t sii; + register unsigned cs, ds; +{ + register sii_padded_regmap_t *regs; + register target_info_t *tgt; + int count; + int from; + unsigned char obb; + int delayed_copy = 0; + + LOG(0x18,"err_disconn"); + + if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN) + return sii_err_generic(sii, cs, ds); + + regs = sii->regs; + + if ((regs->sii_cmd & (SII_CMD_DMA|SII_CMD_XFER)) == + (SII_CMD_DMA|SII_CMD_XFER)) { + /* stop dma and wait */ + regs->sii_cmd &= ~(SII_CMD_DMA|SII_CMD_XFER); + (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); +/* later: regs->sii_data_csr = SII_DTR_DONE; */ + } + + SII_COMMAND(regs,cs,ds,0); + + tgt = sii->active_target; + switch (SCSI_PHASE(sii->script->condition)) { + case SCSI_PHASE_DATAO: + LOG(0x1b,"+DATAO"); + if (sii->out_count) { + register int xferred, offset; + + xferred = sii->out_count - regs->sii_dma_len; + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + delayed_copy = 1; + from = offset; + count = xferred; + + } + tgt->transient_state.script = sii_script_restart_data_out; + break; + + case SCSI_PHASE_DATAI: + LOG(0x19,"+DATAI"); + if (sii->in_count) { + register int offset, xferred; + + obb = regs->sii_dma_1st_byte; + + xferred = sii->in_count - regs->sii_dma_len; + assert(xferred > 0); + if (ds & SII_DTR_OBB) { + tgt->transient_state.isa_oddbb = TRUE; + tgt->transient_state.oddbb = obb; + } + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + + delayed_copy = 2; + from = offset; + count = xferred; + + } + tgt->transient_state.script = sii_script_restart_data_in; + break; + + case SCSI_PHASE_STATUS: + /* will have to restart dma */ + if (count = regs->sii_dma_len) { + (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + } + if (sii->state & SII_STATE_DMA_IN) { + register int offset, xferred; + + obb = regs->sii_dma_1st_byte; + + LOG(0x1a,"+STATUS+R"); + + xferred = sii->in_count - count; + assert(xferred > 0); + if (ds & SII_DTR_OBB) { + tgt->transient_state.isa_oddbb = TRUE; + tgt->transient_state.oddbb = obb; + } + tgt->transient_state.in_count -= xferred; +/* assert(tgt->transient_state.in_count > 0);*/ + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + /* copy what we got */ + + delayed_copy = 2; + from = offset; + count = xferred; + + tgt->transient_state.script = sii_script_restart_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + LOG(0x1d,"+STATUS+W"); + + if ((count == 0) && (tgt->transient_state.out_count == sii->out_count)) { + /* all done */ + tgt->transient_state.script = &sii_script_restart_data_out[1]; + tgt->transient_state.out_count = 0; + } else { + register int xferred, offset; + + /* how much we xferred */ + xferred = sii->out_count - count; + + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + offset = tgt->transient_state.dma_offset; + tgt->transient_state.dma_offset += xferred; + if (tgt->transient_state.dma_offset == PER_TGT_BUFF_SIZE) + tgt->transient_state.dma_offset = 0; + + delayed_copy = 1; + from = offset; + count = xferred; + + tgt->transient_state.script = sii_script_restart_data_out; + } + sii->out_count = 0; + } + break; + case SII_PHASE_DISC: /* sometimes disconnects and phase remains */ + return sii_err_generic(sii, cs, ds); + default: + gimmeabreak(); + } + regs->sii_csr &= ~SII_CSR_RSE; + sii_msg_in(sii,cs,ds); + sii->script = sii_script_disconnect; + regs->sii_cmd = SII_CMD_DMA|SII_CMD_XFER|SCSI_PHASE_MSG_IN| + SII_CON_CON|(regs->sii_conn_csr & SII_CON_DST); + wbflush(); + if (delayed_copy == 2) + careful_copyin_gap16( tgt, from, count, ds & SII_DTR_OBB, obb); + else if (delayed_copy == 1) + careful_copyout_gap16( tgt, from, count); + + return FALSE; +} + +/* + * Suppose someone reads the specs as they read the Bible. + * They would send these unnecessary restore-pointer msgs + * in reconnect phases. If this was a SCSI-2 modify-pointer + * I could understand, but. Oh well. + */ +sii_err_rdp(sii, cs, ds) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs; + register target_info_t *tgt; + + LOG(0x24,"err_drp"); + + /* One chance */ + sii->error_handler = sii->active_target->transient_state.handler; + + if (SCSI_PHASE(ds) != SCSI_PHASE_MSG_IN) + return sii_err_generic(sii, cs, ds); + + regs = sii->regs; + + if ((ds & SII_DTR_IBF) == 0) + ds = sii_wait(®s->sii_data_csr, SII_DTR_IBF,1); + + if (regs->sii_data != SCSI_RESTORE_POINTERS) + return sii_err_disconn(sii, cs, ds); + + regs->sii_cmd = SII_CMD_XFER|SII_CMD_CON|SII_CMD_DST|SCSI_PHASE_MSG_IN; + wbflush(); + + (void) sii_wait(®s->sii_data_csr, SII_DTR_DONE,1); + regs->sii_data_csr = SII_DTR_DONE; + + return FALSE; +} + +/* + * Handle strange, yet unexplained interrupts and error + * situations which eventually I will be old and wise + * enough to deal with properly with preventive care. + */ +sii_err_phase_mismatch(sii, cs, ds) + register sii_softc_t sii; +{ + register sii_padded_regmap_t *regs = sii->regs; + register int match; + + LOG(0x23,"err_mismatch"); + + match = SCSI_PHASE(sii->script->condition); + + /* dmain interrupted */ + if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAI)) { + register int xferred; + register char *p; + + if (regs->sii_dma_len <= 1) { +/*if (scsi_debug)*/ +printf("[DMAINZERO %x %x %x]", cs, ds, regs->sii_dma_len); + if (regs->sii_dma_len == 0) { + regs->sii_dma_len = sii->in_count; + wbflush(); + regs->sii_cmd = sii->script[-1].command; + } + return FALSE; + } + + /* This happens when you do not "init" the prom + and the fifo is screwed up */ + xferred = sii->in_count - regs->sii_dma_len; + p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); + p += xferred; +if (scsi_debug) +printf("[DMAIN %x %x %x]", cs, ds, xferred); + /* odd bytes are not xferred */ + if (((unsigned)p) & 0x1){ + register short *oddb; + oddb = (short*)(sii->buff) + ((unsigned)p-1);/*shifts*/ + *oddb = regs->sii_dma_1st_byte; + } + regs->sii_dma_adr_low = ((unsigned)p); + regs->sii_dma_adr_hi = ((unsigned)p) << 16; + wbflush(); + regs->sii_cmd = sii->script[-1].command; + wbflush(); + return FALSE; + } else + /* dmaout interrupted */ + if ((match == SCSI_PHASE_STATUS) && (SCSI_PHASE(ds) == SCSI_PHASE_DATAO)) { + register int xferred; + register char *p; + + if (regs->sii_dma_len <= 1) { +/*if (scsi_debug)*/ +printf("[DMAOUTZERO %x %x %x]", cs, ds, regs->sii_dma_len); +gimmeabreak(); + if (regs->sii_dma_len == 0) { + regs->sii_dma_len = sii->out_count; + wbflush(); + regs->sii_cmd = sii->script[-1].command; + } + return FALSE; + } + + xferred = sii->out_count - regs->sii_dma_len; +/*if (scsi_debug)*/ +printf("[DMAOUT %x %x %x %x]", cs, ds, regs->sii_dma_len, sii->out_count); + sii->out_count -= xferred; + p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); + p += xferred; + regs->sii_dma_adr_low = ((unsigned)p); + regs->sii_dma_adr_hi = ((unsigned)p) << 16; + wbflush(); + regs->sii_cmd = sii->script[-1].command; + wbflush(); + return FALSE; + } +#if 1 /* ?? */ + /* stuck in cmd phase */ + else if ((SCSI_PHASE(ds) == SCSI_PHASE_CMD) && + ((match == SCSI_PHASE_DATAI) || (match == SCSI_PHASE_DATAO))) { +/*if (scsi_debug)*/ +printf("[CMD %x %x %x %x]", cs, ds, sii->cmd_count, regs->sii_dma_len); + if (regs->sii_dma_len != 0) { + /* ouch, this hurts */ + register int xferred; + register char *p; + + xferred = sii->cmd_count - regs->sii_dma_len; + sii->cmd_count -= xferred; + p = (char*)( regs->sii_dma_adr_low | ((regs->sii_dma_adr_hi&3)<<16) ); + p += xferred; + regs->sii_dma_adr_low = ((unsigned)p); + regs->sii_dma_adr_hi = ((unsigned)p) << 16; + wbflush(); + regs->sii_cmd = 0x8842; + wbflush(); + return FALSE;; + + } + SII_ACK(regs,cs,ds,0/*match*/); + wbflush(); + return FALSE;; + } +#endif + else { + printf("{D%x %x}", cs, ds); +/* if (scsi_debug)*/ gimmeabreak(); + } + return FALSE; +} + +/* + * Watchdog + * + * There are two ways in which I have seen the chip + * get stuck: a target never reconnected, or the + * selection deadlocked. Both cases involved a tk50, + * but elsewhere it showed up with hitachi disks too. + */ +sii_reset_scsibus(sii) + register sii_softc_t sii; +{ + register target_info_t *tgt = sii->active_target; + register sii_padded_regmap_t *regs = sii->regs; + + /* see if SIP still --> device down or non-existant */ + if ((regs->sii_conn_csr & (SII_CON_LST|SII_CON_SIP)) == SII_CON_SIP){ + if (tgt) { + log(LOG_KERN, "Target %d went offline\n", + tgt->target_id); + tgt->flags = 0; + return sii_probe_timeout(tgt); + } + /* else fall through */ + } + + if (tgt) + log(LOG_KERN, "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, tgt->transient_state.out_count, + sii->in_count, sii->out_count, + sii->regs->sii_dma_len); + + sii->regs->sii_cmd = SII_CMD_RST; + delay(25); +} + +/* + * Copy routines that avoid odd pointers + */ +boolean_t nocopyin = FALSE; +careful_copyin_gap16(tgt, offset, len, isaobb, obb) + register target_info_t *tgt; + unsigned char obb; +{ + register char *from, *to; + register int count; + + count = tgt->transient_state.copy_count; + + from = tgt->dma_ptr + (offset << 1); + to = tgt->ior->io_data + count; + tgt->transient_state.copy_count = count + len; + if (count & 1) { + from -= (1 << 1); + to -= 1; + len += 1; + } +if (nocopyin) return;/*timing*/ + copyin_gap16( from, to, len); + /* check for last, poor little odd byte */ + if (isaobb) { + to += len; + to[-1] = obb; + } +} + +careful_copyout_gap16( tgt, offset, len) + register target_info_t *tgt; +{ + register char *from, *to; + register int count, olen; + unsigned char c; + char *p; + + count = tgt->ior->io_count - tgt->transient_state.copy_count; + if (count > 0) { + + len = u_min(count, len); + offset += tgt->transient_state.cmd_count; + + count = tgt->transient_state.copy_count; + tgt->transient_state.copy_count = count + len; + + from = tgt->ior->io_data + count; + to = tgt->dma_ptr + (offset << 1); + + /* the scsi buffer acts weirdo at times */ + if ((olen=len) & 1) { + p = tgt->dma_ptr + ((offset + olen - 1)<<1); + c = (*(unsigned short*)p) >> 8;/*!MSF*/ + } + + if (count & 1) { + from -= 1; + to -= (1 << 1); + len += 1; + } + + count = copyout_gap16(from, to, len); + + /* the scsi buffer acts weirdo at times */ + if (olen & 1) { + unsigned char cv; + cv = (*(unsigned short*)p) >> 8;/*!MSF*/ + if (c != cv) { + /* + * Scott Fahlman would say + * "Use a big plier!" + */ + unsigned short s; + volatile unsigned short *pp; + pp = (volatile unsigned short*)p; + s = (c << 8) | (from[len-1] & 0xff); + do { + *pp = s; + } while (*pp != s); + } + } + } +} + +#endif NSII > 0 + diff --git a/scsi/adapters/scsi_89352.h b/scsi/adapters/scsi_89352.h new file mode 100644 index 0000000..85c579f --- /dev/null +++ b/scsi/adapters/scsi_89352.h @@ -0,0 +1,231 @@ +/* + * Mach Operating System + * Copyright (c) 1991 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: scsi_89352.h + * Author: Daniel Stodolsky, Carnegie Mellon University + * Date: 06/91 + * + * Defines for the Fujitsu MB89352 SCSI Protocol Controller (HBA) + * The definitions herein also cover the 89351, 87035/36, 87033B + */ + +/* + * Register map, padded as needed. Matches Hardware, do not screw up. + */ + +#define vuc volatile unsigned char + +typedef struct +{ + vuc spc_bdid; /* Bus Device ID (R/W) */ +#define spc_myid spc_bdid + char pad0[3]; + vuc spc_sctl; /* SPC Control register (R/W) */ + char pad1[3]; + vuc spc_scmd; /* Command Register (R/W) */ + char pad2[3]; + vuc spc_tmod; /* Transmit Mode Register (synch models) */ + char pad3[3]; + vuc spc_ints; /* Interrupt sense (R); Interrupt Reset (w) */ + char pad4[3]; + vuc spc_psns; /* Phase Sense (R); SPC Diagnostic Control (w) */ +#define spc_phase spc_psns + char pad5[3]; + vuc spc_ssts; /* SPC status (R/O) */ + char pad6[3]; + vuc spc_serr; /* SPC error status (R/O) */ + char pad7[3]; + vuc spc_pctl; /* Phase Control (R/W) */ + char pad8[3]; + vuc spc_mbc; /* Modifed Byte Counter (R/O) */ + char pad9[3]; + vuc spc_dreg; /* Data Register (R/W) */ + char pad10[3]; + vuc spc_temp; /* Temporary Register (R/W) */ + char pad11[3]; + vuc spc_tch; /* Transfer Counter High (R/W) */ + char pad12[3]; + vuc spc_tcm; /* Transfer Counter Middle (R/W) */ + char pad13[3]; + vuc spc_tcl; /* Transfer Counter Low (R/W) */ + char pad14[3]; + vuc spc_exbf; /* External Buffer (synch models) */ + char pad15[3]; +} spc_regmap_t; + +#undef vuc + +/* + * Control register + */ + +#define SPC_SCTL_DISABLE 0x80 +#define SPC_SCTL_RESET 0x40 +#define SPC_SCTL_DIAGMODE 0x20 +#define SPC_SCTL_ARB_EBL 0x10 +#define SPC_SCTL_PAR_EBL 0x08 +#define SPC_SCTL_SEL_EBL 0x04 +#define SPC_SCTL_RSEL_EBL 0x02 +#define SPC_SCTL_IE 0x01 + +/* + * Command register + */ + +#define SPC_SCMD_CMDMASK 0xe0 +# define SPC_SCMD_C_ACKREQ_S 0xe0 +# define SPC_SCMD_C_ACKREQ_C 0xc0 +# define SPC_SCMD_C_STOP_X 0xa0 +# define SPC_SCMD_C_XFER 0x80 +# define SPC_SCMD_C_ATN_S 0x60 +# define SPC_SCMD_C_ATN_C 0x40 +# define SPC_SCMD_C_SELECT 0x20 +# define SPC_SCMD_C_BUS_RLSE 0x00 +#define SPC_SCMD_BUSRST 0x10 +#define SPC_SCMD_INTERCEPT_X 0x08 +#define SPC_SCMD_PROGRAMMED_X 0x04 +#define SPC_SCMD_PAD_X 0x01 + +/* + * Transfer mode register (MB87033B/35/36) + */ + +#define SPC_TMOD_SYNC_X 0x80 +#define SPC_TMOD_OFFSET_MASK 0x70 +# define SPC_OFFSET(x) (((x)<<4)&SPC_TMOD_OFFSET_MASK) +#define SPC_TMOD_PERIOD_MASK 0xc0 +# define SPC_PERIOD(x) (((x)<<2)&SPC_TMOD_PERIOD_MASK) +#define SPC_TMOD_EXP_COUNTER 0x01 + +/* + * Interrupt cause register + */ + +#define SPC_INTS_SELECTED 0x80 +#define SPC_INTS_RESELECTED 0x40 +#define SPC_INTS_DISC 0x20 +#define SPC_INTS_DONE 0x10 +#define SPC_INTS_BUSREQ 0x08 +#define SPC_INTS_TIMEOUT 0x04 +#define SPC_INTS_ERROR 0x02 +#define SPC_INTS_RESET 0x01 + +/* + * SCSI Bus signals ("phase") + */ + +#define SPC_BUS_REQ 0x80 /* rw */ +#define SPC_BUS_ACK 0x40 /* rw */ +#define SPC_BUS_ATN 0x20 /* ro */ +# define SPC_DIAG_ENBL_XFER 0x20 /* wo */ +#define SPC_BUS_SEL 0x10 /* ro */ +#define SPC_BUS_BSY 0x08 /* rw */ +#define SPC_BUS_MSG 0x04 /* rw */ +#define SPC_BUS_CD 0x02 /* rw */ +#define SPC_BUS_IO 0x01 /* rw */ + +#define SPC_CUR_PHASE(x) SCSI_PHASE(x) + +#define SPC_BSY(r) (r->spc_phase & SPC_BUS_BSY) + +/* + * Chip status register + */ + +#define SPC_SSTS_INI_CON 0x80 +#define SPC_SSTS_TGT_CON 0x40 +#define SPC_SSTS_BUSY 0x20 +#define SPC_SSTS_XIP 0x10 +#define SPC_SSTS_RST 0x08 +#define SPC_SSTS_TC0 0x04 +#define SPC_SSTS_FIFO_FULL 0x02 +#define SPC_SSTS_FIFO_EMPTY 0x01 + +/* + * Error register + */ + +#define SPC_SERR_SEL 0x80 /* Selected */ +#define SPC_SERR_RSEL 0x40 /* Reselected */ +#define SPC_SERR_DISC 0x20 /* Disconnected */ +#define SPC_SERR_CMDC 0x10 /* Command Complete */ +#define SPC_SERR_SRVQ 0x08 /* Service Required */ +#define SPC_SERR_TIMO 0x04 /* Timeout */ +#define SPC_SERR_HARDERR 0x02 /* SPC Hard Error */ +#define SPC_SERR_RSTC 0x01 /* Reset Condition */ + +/* + * Phase control register + * + * [use SPC_CUR_PHASE() here too] + */ + +#define SPC_PCTL_BFREE_IE 0x80 /* Bus free (disconnected) */ +#define SPC_PCTL_LST_IE 0x40 /* lost arbit (87033) */ +#define SPC_PCTL_ATN_IE 0x20 /* ATN set (87033) */ +#define SPC_PCTL_RST_DIS 0x10 /* RST asserted */ + +/* + * Modified byte counter register + */ + +#define SPC_MBC_ECNT_MASK 0xf0 /* 87033 only */ +# define SPC_MBC_ECNT_GET(x) (((x)&SPC_MBC_ECNT_MASK)>>4) +# define SPC_MBC_ECNT_PUT(x) (((x)<<4)&SPC_MBC_ECNT_MASK) +#define SPC_MBC_MBC_MASK 0x0f +# define SPC_MBC_GET(x) ((x)&SPC_MBC_MBC_MASK) +# define SPC_MBC_PUT(x) ((x)&SPC_MBC_MBC_MASK) + +/* + * Transfer counter register(s) + */ + +#define SPC_TC_PUT(ptr,val) { \ + (ptr)->spc_tch = (((val)>>16)&0xff); \ + (ptr)->spc_tcm = (((val)>> 8)&0xff); \ + (ptr)->spc_tcl = (((val) )&0xff); \ + } + +#define SPC_TC_GET(ptr,val) { \ + (val) = (((ptr)->spc_tch & 0xff )<<16) |\ + (((ptr)->spc_tcm 0 0xff )<<8) |\ + ((ptr)->spc_tcl 0xff);\ + } + +/* 87033 in expanded mode */ +#define SPC_XTC_PUT(ptr,val) { \ + (ptr)->spc_mbc = SPC_MBC_ECNT_PUT(((val)>>24));\ + (ptr)->spc_tch = (((val)>>16)&0xff); \ + (ptr)->spc_tcm = (((val)>> 8)&0xff); \ + (ptr)->spc_tcl = (((val) )&0xff); \ + } + +#define SPC_XTC_GET(ptr,val) { \ + (val) = (SPC_MBC_ECNT_GET((ptr)->spc_mbc)<<24)|\ + (((ptr)->spc_tch)<<16)|(((ptr)->spc_tcm)<<8)|\ + ((ptr)->spc_tcl);\ + } + diff --git a/scsi/adapters/scsi_89352_hdw.c b/scsi/adapters/scsi_89352_hdw.c new file mode 100644 index 0000000..5672cb6 --- /dev/null +++ b/scsi/adapters/scsi_89352_hdw.c @@ -0,0 +1,2192 @@ +/* + * Mach Operating System + * Copyright (c) 1992,1991 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: scsi_89352_hdw.c + * Author: Daniel Stodolsky, Carnegie Mellon University + * Date: 06/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the Fujitsu MB89352 + * SCSI chip (Host Bus Adapter in SCSI parlance): probing, start + * operation, and interrupt routine. + */ + +/* + * This layer works based on small simple 'scripts' that are installed + * at the start of the command and drive the chip to completion. + * The idea comes from the specs of the NCR 53C700 'script' processor. + * + * There are various reasons for this, mainly + * - Performance: identify the common (successful) path, and follow it; + * at interrupt time no code is needed to find the current status + * - Code size: it should be easy to compact common operations + * - Adaptability: the code skeleton should adapt to different chips without + * terrible complications. + * - Error handling: and it is easy to modify the actions performed + * by the scripts to cope with strange but well identified sequences + * + */ + +/* + * + * + * Known Headaches/Features with this chip. + * + * (1) After the interrupt raised by select, the phase sense (psns) + * and SPC status (ssts) registers do not display the correct values + * until the REQ line (via psns) is high. (danner@cs.cmu.edu 6/11/91) + * + * (2) After a data in phase, the command complete interrupt may be raised + * before the psns, ssts, and transfer counter registers settle. The reset + * acknowledge or request command should not be issued until they settle. + * (danner@cs.cmu.edu 6/14/91) + * + * (3) In general, an interrupt can be raised before the psns and ssts have + * meaningful values. One should wait for the psns to show the REQ bit (0x80) + * set before expecting meaningful values, with the exception of (2) above. + * Currently this is handled by spc_err_generic ("Late REQ"). (This problem + * is really a refinement of (1)). (danner@cs.cmu.edu 6/14/91) + * + * (4) When issuing a multibyte command after a select with attention, + * The chip will automatically drop ATN before sending the last byte of the + * message, in accordance with the ANSI SCSI standard. This requires, of course, + * the transfer counter be an accurate representation of the amount of data to be + * transfered. (danner@cs.cmu.edu 6/14/91) + * + */ + +#if 0 + +#include <platforms.h> + +#include <scsi.h> + +#if NSCSI > 0 + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> +#include <sys/syslog.h> +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> +#include <scsi/adapters/scsi_89352.h> + +#include <machine/db_machdep.h> /*4proto*/ +#include <ddb/db_sym.h> /*4proto*/ + +#ifdef LUNA88K +#include <luna88k/board.h> +#define SPC_DEFAULT_ADDRESS (caddr_t) SCSI_ADDR +#endif + +#ifndef SPC_DEFAULT_ADDRESS /* cross compile check */ +#define SPC_DEFAULT_ADDRESS (caddr_t) 0 +#endif + + +/* external/forward declarations */ +int spc_probe(), spc_slave(), spc_attach(), scsi_go(); +void spc_reset(), spc_attempt_selection(), spc_target_intr(), spc_bus_reset(); +/* + * Statically allocated command & temp buffers + * This way we can attach/detach drives on-fly + */ +#define PER_TGT_BUFF_DATA 256 + +static char spc_buffer[NSCSI * 8 * PER_TGT_BUFF_DATA]; + +/* + * Macros to make certain things a little more readable + */ + +/* + wait for the desired phase to appear, but make sure the REQ bit set in the psns + (otherwise the values tend to float/be garbage. +*/ + +#define SPC_WAIT_PHASE(p) while(((regs->spc_psns & (SPC_BUS_REQ|SCSI_PHASE_MASK))) \ + != (SPC_BUS_REQ|(p))) + +/* + wait until a phase different than p appears in the psns. Since it is only valid + when the REQ bit is set, don't test unless REQ bit is set. So spin until + REQ is high or the phase is not p. +*/ + +#define SPC_WAIT_PHASE_VANISH(p) while(1) { int _psns_ = regs->spc_psns; \ + if ((_psns_ & SPC_BUS_REQ) && (_psns_ & SCSI_PHASE_MASK)!=p) break; } + + + +/* ?? */ +/* #define SPC_ACK(ptr,phase) (ptr)->spc_pctl = (phase) */ + +/* + * A script has a two parts: a pre-condition and an action. + * The first triggers error handling if not satisfied and in + * our case it is formed by the current bus phase and connected + * condition as per bus status bits. The action part is just a + * function pointer, invoked in a standard way. The script + * pointer is advanced only if the action routine returns TRUE. + * See spc_intr() for how and where this is all done. + */ + +typedef struct script { + char condition; /* expected state at interrupt */ + int (*action)(); /* action routine */ +} *script_t; + +#define SCRIPT_MATCH(psns) (SPC_CUR_PHASE((psns))|((psns) & SPC_BUS_BSY)) + +/* ?? */ +#define SPC_PHASE_DISC 0x0 /* sort of .. */ + +/* The active script is in the state expected right after the issue of a select */ + +#define SCRIPT_SELECT(scp) (scp->action == spc_issue_command || \ + scp->action == spc_issue_ident_and_command) + +/* forward decls of script actions */ +boolean_t + spc_dosynch(), /* negotiate synch xfer */ + spc_xfer_in(), /* get data from target via dma */ + spc_xfer_out(), /* send data to target via dma */ + spc_get_status(), /* get status from target */ + spc_end_transaction(), /* all come to an end */ + spc_msg_in(), /* get disconnect message(s) */ + spc_issue_command(), /* spit on the bus */ + spc_issue_ident_and_command(), /* spit on the bus (with ATN) */ + spc_disconnected(); /* current target disconnected */ +/* forward decls of error handlers */ +boolean_t + spc_err_generic(), /* generic error handler */ + spc_err_disconn(); /* when a target disconnects */ +void gimmeabreak(); /* drop into the debugger */ + +void spc_reset_scsibus(); +boolean_t spc_probe_target(); + +scsi_ret_t spc_select_target(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) 89352 chip + */ +struct spc_softc { + watchdog_t wd; + spc_regmap_t *regs; /* 5380 registers */ + char *buff; /* scratch buffer memory */ + char *data_ptr; /* orig/dest memory */ + script_t script; + int (*error_handler)(); + int in_count; /* amnt we expect to receive */ + int out_count; /* amnt we are going to ship */ + + volatile char state; +#define SPC_STATE_BUSY 0x01 /* selecting or currently connected */ +#define SPC_STATE_TARGET 0x04 /* currently selected as target */ +#define SPC_STATE_COLLISION 0x08 /* lost selection attempt */ +#define SPC_STATE_DMA_IN 0x10 /* tgt --> initiator xfer */ + + unsigned char ntargets; /* how many alive on this scsibus */ + unsigned char done; + unsigned char xxxx; + + scsi_softc_t *sc; + target_info_t *active_target; + + target_info_t *next_target; /* trying to seize bus */ + queue_head_t waiting_targets;/* other targets competing for bus */ + decl_simple_lock_data(,chiplock) /* Interlock */ +} spc_softc_data[NSCSI]; + +typedef struct spc_softc *spc_softc_t; + +spc_softc_t spc_softc[NSCSI]; + +/* + * Definition of the controller for the auto-configuration program. + */ + +int spc_probe(), scsi_slave(), spc_go(); +void spc_intr(); +void scsi_attach(); + +vm_offset_t spc_std[NSCSI] = { SPC_DEFAULT_ADDRESS }; + +struct bus_device *spc_dinfo[NSCSI*8]; +struct bus_ctlr *spc_minfo[NSCSI]; +struct bus_driver spc_driver = + { spc_probe, scsi_slave, scsi_attach, spc_go, spc_std, "rz", spc_dinfo, + "spc", spc_minfo, BUS_INTR_B4_PROBE}; + +/* + * Scripts + */ + +struct script +spc_script_data_in[] = { + { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command}, + { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + +spc_script_late_data_in[] = { + { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command}, + { SCSI_PHASE_DATAI|SPC_BUS_BSY, spc_xfer_in}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + +spc_script_data_out[] = { + { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command}, + { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + + +spc_script_late_data_out[] = { + { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command}, + { SCSI_PHASE_DATAO|SPC_BUS_BSY, spc_xfer_out}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + + +spc_script_cmd[] = { + { SCSI_PHASE_CMD|SPC_BUS_BSY, spc_issue_command}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + +spc_script_late_cmd[] = { + { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_issue_ident_and_command}, + { SCSI_PHASE_STATUS|SPC_BUS_BSY, spc_get_status}, + { SCSI_PHASE_MSG_IN|SPC_BUS_BSY, spc_end_transaction} +}, + +/* Synchronous transfer neg(oti)ation */ + +spc_script_try_synch[] = { + { SCSI_PHASE_MSG_OUT|SPC_BUS_BSY, spc_dosynch} +}, + +/* Disconnect sequence */ + +spc_script_disconnect[] = { + { SPC_PHASE_DISC, spc_disconnected} +}; + + + +#define u_min(a,b) (((a) < (b)) ? (a) : (b)) + + +#define DEBUG +#ifdef DEBUG + +int spc_state(base) + vm_offset_t base; +{ + register spc_regmap_t *regs; + + if (base == 0) + base = (vm_offset_t) SPC_DEFAULT_ADDRESS; + + regs = (spc_regmap_t*) (base); + + db_printf("spc_bdid (bus device #): %x\n",regs->spc_bdid); + db_printf("spc_sctl (spc internal control): %x\n",regs->spc_sctl); + db_printf("spc_scmd (scp command): %x\n",regs->spc_scmd); + db_printf("spc_ints (spc interrupt): %x\n",regs->spc_ints); + db_printf("spc_psns (scsi bus phase): %x\n",regs->spc_psns); + db_printf("spc_ssts (spc internal status): %x\n",regs->spc_ssts); + db_printf("spc_serr (spc internal err stat): %x\n",regs->spc_serr); + db_printf("spc_pctl (scsi transfer phase): %x\n",regs->spc_pctl); + db_printf("spc_mbc (spc transfer data ct): %x\n",regs->spc_mbc); +/* db_printf("spc_dreg (spc transfer data r/w): %x\n",regs->spc_dreg);*/ + db_printf("spc_temp (scsi data bus control): %x\n",regs->spc_temp); + db_printf("spc_tch (transfer byte ct (MSB): %x\n",regs->spc_tch); + db_printf("spc_tcm (transfer byte ct (2nd): %x\n",regs->spc_tcm); + db_printf("spc_tcl (transfer byte ct (LSB): %x\n",regs->spc_tcl); + + return 0; +} + +int spc_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = spc_softc[0]->active_target; + if (tgt == 0) + return 0; + db_printf("fl %x dma %x+%x cmd %x id %x per %x off %x ior %x ret %x\n", + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + if (tgt->flags & TGT_DISCONNECTED){ + script_t spt; + + spt = tgt->transient_state.script; + db_printf("disconnected at "); + db_printsym((db_expr_t)spt,1); + db_printf(": %x ", spt->condition); + db_printsym((db_expr_t)spt->action,1); + db_printf(", "); + db_printsym((db_expr_t)tgt->transient_state.handler, 1); + db_printf("\n"); + } + + return 0; +} + +void spc_all_targets(unit) +int unit; +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = spc_softc[unit]->sc->target[i]; + if (tgt) + spc_target_state(tgt); + } +} + +int spc_script_state(unit) +int unit; +{ + script_t spt = spc_softc[unit]->script; + + if (spt == 0) return 0; + db_printsym((db_expr_t)spt,1); + db_printf(": %x ", spt->condition); + db_printsym((db_expr_t)spt->action,1); + db_printf(", "); + db_printsym((db_expr_t)spc_softc[unit]->error_handler, 1); + return 0; +} + +#define PRINT(x) if (scsi_debug) printf x + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +int spc_logpt; +int spc_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x30 +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static void LOG(e,f) + int e; + char *f; +{ + spc_log[spc_logpt++] = (e); + if (spc_logpt == LOGSIZE) spc_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +int spc_print_log(skip) + int skip; +{ + register int i, j; + register unsigned int c; + + for (i = 0, j = spc_logpt; i < LOGSIZE; i++) { + c = spc_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-0x%x", c - 0x80); + } + db_printf("\n"); + return 0; +} + +void spc_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /* TRACE */ +#define LOG(e,f) +#endif /* TRACE */ + +#else /* DEBUG */ +#define PRINT(x) +#define LOG(e,f) +#define TR(x) +#define TRCHECK +#define TRWRAP +#endif /* DEBUG */ + + +/* + * Probe/Slave/Attach functions + */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Send an identify msg to each possible target on the bus + * except of course ourselves. + */ +int spc_probe(reg, ui) + char *reg; + struct bus_ctlr *ui; +{ + int tmp; + int unit = ui->unit; + spc_softc_t spc = &spc_softc_data[unit]; + int target_id, i; + scsi_softc_t *sc; + register spc_regmap_t *regs; + int s; + boolean_t did_banner = FALSE; + char *cmd_ptr; + + /* + * We are only called if the chip is there, + * but make sure anyways.. + */ + regs = (spc_regmap_t *) (reg); + if (check_memory((unsigned)regs, 0)) + return 0; + +#if notyet + /* Mappable version side */ + SPC_probe(reg, ui); +#endif + + /* + * Initialize hw descriptor + */ + spc_softc[unit] = spc; + spc->regs = regs; + spc->buff = spc_buffer; + + queue_init(&spc->waiting_targets); + + simple_lock_init(&spc->chiplock); + + sc = scsi_master_alloc(unit, (char*)spc); + spc->sc = sc; + + sc->go = spc_go; + sc->probe = spc_probe_target; + sc->watchdog = scsi_watchdog; + spc->wd.reset = spc_reset_scsibus; + +#ifdef MACH_KERNEL + sc->max_dma_data = -1; /* unlimited */ +#else + sc->max_dma_data = scsi_per_target_virtual; +#endif + + scsi_might_disconnect[unit] = 0; /* XXX for now */ + + /* + * Reset chip + */ + s = splbio(); + spc_reset(regs, TRUE); + tmp = regs->spc_ints = regs->spc_ints; + + /* + * Our SCSI id on the bus. + */ + + sc->initiator_id = bdid_to_id(regs->spc_bdid); + printf("%s%d: my SCSI id is %d", ui->name, unit, sc->initiator_id); + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + */ + cmd_ptr = spc_buffer; + for (target_id = 0; target_id < 8; target_id++, cmd_ptr += PER_TGT_BUFF_DATA) { + + register unsigned csr, ints; + scsi_status_byte_t status; + + /* except of course ourselves */ + if (target_id == sc->initiator_id) + continue; + + if (spc_select_target( regs, sc->initiator_id, target_id, FALSE) + == SCSI_RET_DEVICE_DOWN) { + tmp = regs->spc_ints = regs->spc_ints; + continue; + } + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + /* should be command phase here: we selected wo ATN! */ + SPC_WAIT_PHASE(SCSI_PHASE_CMD); + + SPC_ACK(regs,SCSI_PHASE_CMD); + + /* build command in buffer */ + { + unsigned char *p = (unsigned char*) cmd_ptr; + + p[0] = SCSI_CMD_TEST_UNIT_READY; + p[1] = + p[2] = + p[3] = + p[4] = + p[5] = 0; + } + + spc_data_out(regs, SCSI_PHASE_CMD, 6, cmd_ptr); + + SPC_WAIT_PHASE(SCSI_PHASE_STATUS); + + /* should have recieved a Command Complete Interrupt */ + while (!(regs->spc_ints)) + delay(1); + ints = regs->spc_ints; + if (ints != (SPC_INTS_DONE)) + gimmeabreak(); + regs->spc_ints = ints; + + SPC_ACK(regs,SCSI_PHASE_STATUS); + + csr = spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + LOG(0x25,"din_count"); + LOG(0x80+csr,0); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) + scsi_error( 0, SCSI_ERR_STATUS, status.bits, 0); + + /* expect command complete interupt */ + while (!(regs->spc_ints & SPC_INTS_DONE)) + delay(1); + + /* clear all intr bits */ + tmp = regs->spc_ints; + LOG(0x26,"ints"); + LOG(0x80+tmp,0); + regs->spc_ints = SPC_INTS_DONE; + + /* get cmd_complete message */ + SPC_WAIT_PHASE(SCSI_PHASE_MSG_IN); + + SPC_ACK(regs,SCSI_PHASE_MSG_IN); + + csr = spc_data_in(regs,SCSI_PHASE_MSG_IN, 1,(unsigned char*)&i); + LOG(0x25,"din_count"); + LOG(0x80+csr,0); + + while (!(regs->spc_ints & SPC_INTS_DONE)) + delay(1); + + /* clear all done intr */ + tmp = regs->spc_ints; + LOG(0x26,"ints"); + LOG(0x80+tmp,0); + regs->spc_ints = SPC_INTS_DONE; + + SPC_ACK(regs,SPC_PHASE_DISC); + + /* release the bus */ + regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC; + /* regs->spc_scmd = 0; only in TARGET mode */ + + /* wait for disconnected interrupt */ + while (!(regs->spc_ints & SPC_INTS_DISC)) + delay(1); + + tmp = regs->spc_ints; + LOG(0x26,"ints"); + LOG(0x80+tmp,0); + regs->spc_ints = tmp; + LOG(0x29,"Probed\n"); + + /* + * Found a target + */ + spc->ntargets++; + { + register target_info_t *tgt; + + tgt = scsi_slave_alloc(unit, target_id, (char*)spc); + + /* "virtual" address for our use */ + tgt->cmd_ptr = cmd_ptr; + /* "physical" address for dma engine (??) */ + tgt->dma_ptr = 0; +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + } + } + printf(".\n"); + + splx(s); + return 1; +} + +boolean_t +spc_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + + /* "virtual" address for our use */ + tgt->cmd_ptr = &spc_buffer[PER_TGT_BUFF_DATA*tgt->target_id + + (tgt->masterno*8*PER_TGT_BUFF_DATA) ]; + /* "physical" address for dma engine */ + tgt->dma_ptr = 0; +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_init(&tgt->fdma, scsi_per_target_virtual); +#endif /*MACH_KERNEL*/ + + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + +int bdid_to_id(bdid) + register int bdid; +{ + register int i; + for (i = 0; i < 8; i++) + if (bdid == (1 << i)) break; + return i; +} + +scsi_ret_t +spc_select_target(regs, myid, id, with_atn) + register spc_regmap_t *regs; + unsigned myid, id; + boolean_t with_atn; +{ + scsi_ret_t ret = SCSI_RET_RETRY; + int mask; + + if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) +#ifdef MIPS + && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) + && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) +#endif + ) + return ret; + + /* setup for for select: + +#if 0 + (1) Toggle the Enable transfer bit (turning on the chips + SCSI bus drivers). +#endif + (2) Enable arbitration, parity, reselect display, but + disable interrupt generation to the CPU (we are polling). + (3) Disable the bus free interrupt and set I/O direction + (4) If doing a select with attention, write the Set attention command. + Then delay 1 microsecond to avoid command races. + + (5) Temp register gets 1<<target | 1<<initiator ids + (6) Timeout clocked into transfer registers + (7) Drive select (and optionally attention) onto the bus + (8) Wait 1/4 second for timeout. + */ + +#if 0 + regs->spc_psns = SPC_DIAG_ENBL_XFER; /* (1) */ +#endif + + regs->spc_sctl = SPC_SCTL_ARB_EBL| + SPC_SCTL_PAR_EBL| + SPC_SCTL_RSEL_EBL; /* (2) */ + + + + mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl; + mask &= ~1; /* set I/O direction to be out */ + + regs->spc_pctl = mask; /* (3) */ + + if (with_atn) + { + regs->spc_scmd = SPC_SCMD_C_ATN_S; /* (4) */ + delay(1); + } + + regs->spc_temp = (1<<myid) | (1<<id); /* (5) */ + + SPC_TC_PUT(regs,0xfa004); /* (6) */ + + regs->spc_scmd = (SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X); /* (7) */ + + { + int count = 2500; + + /* wait for an interrupt */ + while ((regs->spc_ints)==0) + { + if (--count > 0) + delay(100); + else + { + goto nodev; + } + } + + count = regs->spc_ints; + if (count & SPC_INTS_TIMEOUT) + { + /* sanity check. The ssts should have the busy bit set */ + if (regs->spc_ssts & SPC_SSTS_BUSY) + goto nodev; + else + panic("spc_select_target: timeout"); + } + + /* otherwise, we should have received a + command complete interrupt */ + + if (count & ~SPC_INTS_DONE) + panic("spc_select_target"); + + } /* (8) */ + + /* we got a response - now connected; bus is in COMMAND phase */ + + regs->spc_ints = regs->spc_ints; + /* regs->spc_scmd = 0; target only */ + return SCSI_RET_SUCCESS; +nodev: + SPC_TC_PUT(regs,0); /* play it safe */ + regs->spc_ints = regs->spc_ints; + /* regs->spc_scmd = 0; target only */ + ret = SCSI_RET_DEVICE_DOWN; + return ret; +} + +int spc_data_out(regs, phase, count, data) + int phase, count; + register spc_regmap_t *regs; + unsigned char *data; +{ + /* This is the one that sends data out. returns how many + bytes it did NOT xfer: */ + + if (SPC_CUR_PHASE(regs->spc_phase) != phase) + return count; + + /* check that the fifo is empty. If not, cry */ + if (!(regs->spc_ssts & SPC_SSTS_FIFO_EMPTY)) + panic("spc_data_out: junk in fifo\n"); + + SPC_TC_PUT(regs,count); + regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X; + + /* wait for the SPC to start processing the command */ + while ((regs->spc_ssts & (SPC_SSTS_INI_CON|SPC_SSTS_TGT_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP)) + != (SPC_SSTS_INI_CON|SPC_SSTS_BUSY|SPC_SSTS_XIP)) + delay(1); + + /* shovel out the data */ + + while (count) + { + /* check if interrupt is pending */ + int ints = regs->spc_ints; + int ssts; + + if (ints) /* something has gone wrong */ + break; + + ssts = regs->spc_ssts; + if (ssts & SPC_SSTS_FIFO_FULL) /* full fifo - can't write */ + delay(1); + else + { /* spit out a byte */ + regs->spc_dreg = *data; + data++; + count--; + } + } + + + if (count != 0) + { + /* need some sort of fifo cleanup if failed */ + gimmeabreak(); /* Bytes stranded in the fifo */ + } + + return count; +} + +int spc_data_in(regs, phase, count, data) + int phase, count; + register spc_regmap_t *regs; + unsigned char *data; +{ + if (SPC_CUR_PHASE(regs->spc_phase) != phase) + return count; + + SPC_TC_PUT(regs,count); + regs->spc_scmd = SPC_SCMD_C_XFER | SPC_SCMD_PROGRAMMED_X; + + /* The Fujistu code sample suggests waiting for the top nibble of the SSTS to + become 0xb (ssts & 0xf0) = 0xb. This state, however is transient. If the + message is short (say , 1 byte), it can get sucked into the fifo before + we ever get to look at the state. So instead, we are going to wait for + the fifo to become nonempty. + */ + + while ((regs->spc_ssts & SPC_SSTS_FIFO_EMPTY)) + delay(1); + + while (count) + { + int ints = regs->spc_ints; + int ssts; + + /* If there is an interrupt pending besides command complete or + phase mismatch, give up */ + + if (ints & ~(SPC_INTS_DONE|SPC_INTS_BUSREQ)) + break; + + /* see if there is any data in the fifo */ + ssts = regs->spc_ssts; + if ((ssts & SPC_SSTS_FIFO_EMPTY) == 0) + { + *data = regs->spc_dreg; + data++; + count--; + continue; + } + + /* if empty, check if phase has changed */ + if (SPC_CUR_PHASE(regs->spc_phase) != phase) + break; + + } + + if ((count==0) && (phase == SCSI_PHASE_MSG_IN)) + { + while (!(regs->spc_ints & SPC_INTS_DONE)) + delay(1); + + /* + So the command complete interrupt has arrived. Now check that the + other two conditions we expect - The psns to be in ack|busy|message_in phase + and ssts to indicate connected|xfer in progress|busy|xfer counter 0|empty fifo + are true. + */ + while (1) + { + register int psns = regs->spc_psns; + register int ssts = regs->spc_ssts; + register int sscon = ssts & (SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON); + register int ssncon = ssts & ~(SPC_SSTS_INI_CON | SPC_SSTS_TGT_CON); + + if (psns == (SPC_BUS_ACK | SPC_BUS_BSY | SCSI_PHASE_MSG_IN) && + ssncon == (SPC_SSTS_BUSY | SPC_SSTS_XIP | SPC_SSTS_TC0 | SPC_SSTS_FIFO_EMPTY) && + sscon) + break; + } + + regs->spc_scmd = SPC_SCMD_C_ACKREQ_C; + } + + return count; +} + +void spc_reset(regs, quickly) + register spc_regmap_t *regs; + boolean_t quickly; +{ + register char myid; + + /* save our id across reset */ + myid = bdid_to_id(regs->spc_bdid); + + /* wait for Reset In signal to go low */ + while (regs->spc_ssts & SPC_SSTS_RST) + delay(1); + + /* reset chip */ + regs->spc_sctl = SPC_SCTL_RESET; + delay(25); + + regs->spc_myid = myid; + regs->spc_sctl = SPC_SCTL_ARB_EBL|SPC_SCTL_PAR_EBL|SPC_SCTL_SEL_EBL| + SPC_SCTL_RSEL_EBL|SPC_SCTL_IE; + regs->spc_scmd = SPC_SCMD_C_BUS_RLSE; + /* regs->spc_tmod = 0; - SANDRO ? */ + regs->spc_ints = 0xff;/* clear off any pending */ +#if 0 + regs->spc_pctl = SPC_PCTL_LST_IE; /* useful only on 87033 */ +#else + regs->spc_pctl = 0; +#endif + regs->spc_mbc = 0; + SPC_TC_PUT(regs,0); + + if (quickly) + return; + + /* + * reset the scsi bus, the interrupt routine does the rest + * or you can call spc_bus_reset(). + */ + regs->spc_scmd = SPC_SCMD_BUSRST|SPC_SCMD_C_STOP_X;/*?*/ +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +spc_go(tgt, cmd_count, in_count, cmd_only) + int cmd_count, in_count; + target_info_t *tgt; + boolean_t cmd_only; +{ + spc_softc_t spc; + register int s; + boolean_t disconn; + script_t scp; + boolean_t (*handler)(); + int late; + + LOG(1,"\n\tgo"); + + spc = (spc_softc_t)tgt->hw_state; + + /* + * We cannot do real DMA. + */ +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + if (tgt->ior) + fdma_map(&tgt->fdma, tgt->ior); +#endif /*MACH_KERNEL*/ + + if ((tgt->cur_cmd == SCSI_CMD_WRITE) || + (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ + io_req_t ior = tgt->ior; + register int len = ior->io_count; + + tgt->transient_state.out_count = len; + tgt->transient_state.copy_count = 0; + + if (len < tgt->block_size) { + gimmeabreak(); + + /* avoid leaks */ +#if 0 +you`ll have to special case this +#endif + tgt->transient_state.out_count = tgt->block_size; + } + } else { + tgt->transient_state.out_count = 0; + tgt->transient_state.copy_count = 0; + } + + tgt->transient_state.cmd_count = cmd_count; + + disconn = + BGET(scsi_might_disconnect,(unsigned)tgt->masterno, tgt->target_id); + disconn = disconn && (spc->ntargets > 1); + disconn |= + BGET(scsi_should_disconnect,(unsigned)tgt->masterno, tgt->target_id); + + /* + * Setup target state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + handler = (disconn) ? spc_err_disconn : spc_err_generic; + + /* determine wether or not to use the late forms of the scripts */ + late = cmd_only ? FALSE : (tgt->flags & TGT_DID_SYNCH); + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(0x13,"readop"); + scp = late ? spc_script_late_data_in : spc_script_data_in; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x14,"writeop"); + scp = late ? spc_script_late_data_out : spc_script_data_out; + break; + case SCSI_CMD_INQUIRY: + /* This is likely the first thing out: + do the synch neg if so */ + if (!cmd_only && ((tgt->flags&TGT_DID_SYNCH)==0)) { + scp = spc_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + break; + } + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ + case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */ + case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */ + case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + scp = late ? spc_script_late_data_in : spc_script_data_in; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ + tgt->transient_state.cmd_count = sizeof_scsi_command(tgt->cur_cmd); + tgt->transient_state.out_count = + cmd_count - tgt->transient_state.cmd_count; + scp = late ? spc_script_late_data_out : spc_script_data_out; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + case SCSI_CMD_TEST_UNIT_READY: + /* + * Do the synch negotiation here, unless prohibited + * or done already + */ + if (tgt->flags & TGT_DID_SYNCH) { + scp = late ? spc_script_late_cmd : spc_script_cmd; + } else { + scp = spc_script_try_synch; + tgt->flags |= TGT_TRY_SYNCH; + cmd_only = FALSE; + } + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + scp = late ? spc_script_late_cmd : spc_script_cmd; + } + + tgt->transient_state.script = scp; + tgt->transient_state.handler = handler; + tgt->transient_state.identify = (cmd_only) ? 0xff : + (disconn ? SCSI_IDENTIFY|SCSI_IFY_ENABLE_DISCONNECT : + SCSI_IDENTIFY); + + if (in_count) + tgt->transient_state.in_count = + (in_count < tgt->block_size) ? tgt->block_size : in_count; + else + tgt->transient_state.in_count = 0; + tgt->transient_state.dma_offset = 0; + + /* + * See if another target is currently selected on + * this SCSI bus, e.g. lock the spc structure. + * Note that it is the strategy routine's job + * to serialize ops on the same target as appropriate. + */ +#if 0 +locking code here +#endif + s = splbio(); + + if (spc->wd.nactive++ == 0) + spc->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (spc->state & SPC_STATE_BUSY) { + /* + * Queue up this target, note that this takes care + * of proper FIFO scheduling of the scsi-bus. + */ + LOG(3,"enqueue"); + enqueue_tail(&spc->waiting_targets, (queue_entry_t) tgt); + } else { + /* + * It is down to at most two contenders now, + * we will treat reconnections same as selections + * and let the scsi-bus arbitration process decide. + */ + spc->state |= SPC_STATE_BUSY; + spc->next_target = tgt; + spc_attempt_selection(spc); + /* + * Note that we might still lose arbitration.. + */ + } + splx(s); +} + +void spc_attempt_selection(spc) + spc_softc_t spc; +{ + target_info_t *tgt; + spc_regmap_t *regs; + register int cmd; + int atn=0; + + /* This is about your select code */ + + regs = spc->regs; + tgt = spc->next_target; + + LOG(4,"select"); + LOG(0x80+tgt->target_id,0); + + /* + * Init bus state variables and set registers. + */ + spc->active_target = tgt; + + /* reselection pending ? */ + if ((regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) +#ifdef MIPS + && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) + && (regs->spc_phase & (SPC_BUS_BSY|SPC_BUS_SEL)) +#endif + ) + return; + + spc->script = tgt->transient_state.script; + spc->error_handler = tgt->transient_state.handler; + spc->done = SCSI_RET_IN_PROGRESS; + + spc->in_count = 0; + spc->out_count = 0; + + cmd = SPC_SCMD_C_SELECT | SPC_SCMD_PROGRAMMED_X; + if (tgt->flags & TGT_DID_SYNCH) + { + if (tgt->transient_state.identify != 0xff) + atn = 1; + } + else + if (tgt->flags & TGT_TRY_SYNCH) + atn = 1; + +#if 0 + regs->spc_psns = SPC_DIAG_ENBL_XFER; +#endif + + regs->spc_sctl = SPC_SCTL_ARB_EBL | SPC_SCTL_PAR_EBL | + SPC_SCTL_RSEL_EBL | SPC_SCTL_IE; + + + { int mask; + mask = ~SPC_PCTL_BFREE_IE & regs->spc_pctl; + regs->spc_pctl = mask; + } + + regs->spc_temp = (1<<(spc->sc->initiator_id)) | (1<<(tgt->target_id)); + + SPC_TC_PUT(regs,0xfa004); + + if (atn) + { + regs->spc_scmd = SPC_SCMD_C_ATN_S; + /* delay 1us to avoid races */ + delay(1); + } + + regs->spc_scmd = cmd; + return; +} + +/* + * Interrupt routine + * Take interrupts from the chip + * + * Implementation: + * Move along the current command's script if + * all is well, invoke error handler if not. + */ +void spc_intr(unit) +int unit; +{ + register spc_softc_t spc; + register script_t scp; + register unsigned ints, psns, ssts; + register spc_regmap_t *regs; + boolean_t try_match; +#if notyet + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + { + SPC_intr(unit); + return; + } +#endif + + spc = spc_softc[unit]; + regs = spc->regs; + + /* read the interrupt status register */ + ints = regs->spc_ints; + + LOG(5,"\n\tintr"); + LOG(0x80+ints,0); + +TR(ints); +TRCHECK; + + if (ints & SPC_INTS_RESET) + { + /* does its own interrupt reset when ready */ + spc_bus_reset(spc); + return; + } + + /* we got an interrupt allright */ + if (spc->active_target) + spc->wd.watchdog_state = SCSI_WD_ACTIVE; + + + if (ints == 0) + { /* no obvious cause */ + LOG(2,"SPURIOUS"); + gimmeabreak(); + return; + } + + + /* reset the interrupt */ + regs->spc_ints = ints; + + /* go get the phase, and status. We can't trust the + phase until REQ is asserted in the psns. Only do + this is we received a command complete or service + required interrupt. Otherwise, just read them once + and trust. */ + + + + if (ints & (SPC_INTS_DONE|SPC_INTS_BUSREQ)) + while(1) + { + psns = regs->spc_psns; + if (psns & SPC_BUS_REQ) + break; + delay(1); /* don't hog the bus */ + } + else + psns = regs->spc_psns; + + ssts = regs->spc_psns; + +TR(psns); +TR(ssts); +TRCHECK; + + if ((spc->state & SPC_STATE_TARGET) || + (ints & SPC_INTS_SELECTED)) + spc_target_intr(spc /**, ints, psns, ssts **/); + + scp = spc->script; + + if ((scp == 0) || (ints & SPC_INTS_RESELECTED)) + { + gimmeabreak(); + spc_reconnect(spc, ints, psns, ssts); + return; + } + + if (SCRIPT_MATCH(psns) != scp->condition) { + if (try_match = (*spc->error_handler)(spc, ints, psns, ssts)) { + psns = regs->spc_psns; + ssts = regs->spc_ssts; + } + } else + try_match = TRUE; + + + /* might have been side effected */ + scp = spc->script; + + if (try_match && (SCRIPT_MATCH(psns) == scp->condition)) { + /* + * Perform the appropriate operation, + * then proceed + */ + if ((*scp->action)(spc, ints, psns, ssts)) { + /* might have been side effected */ + scp = spc->script; + spc->script = scp + 1; + } + } +} + +void spc_target_intr(spc) + register spc_softc_t spc; +{ + panic("SPC: TARGET MODE !!!\n"); +} + +/* + * All the many little things that the interrupt + * routine might switch to + */ +boolean_t +spc_issue_command(spc, ints, psns, ssts) + spc_softc_t spc; + int ints, psns, ssts; +{ + register spc_regmap_t *regs = spc->regs; + + LOG(0x12, "cmd_issue"); + /* we have just done a select; + Bus is in CMD phase; + need to phase match */ + SPC_ACK(regs, SCSI_PHASE_CMD); + + return spc_data_out(regs, SCSI_PHASE_CMD, + spc->active_target->transient_state.cmd_count, + spc->active_target->cmd_ptr) ? FALSE : TRUE; +} + +boolean_t +spc_issue_ident_and_command(spc, ints, psns, ssts) + spc_softc_t spc; + int ints, psns, ssts; +{ + register spc_regmap_t *regs = spc->regs; + + LOG(0x22, "ident_and_cmd"); + /* we have just done a select with atn Bus is in MSG_OUT phase; + need to phase match */ + SPC_ACK(regs, SCSI_PHASE_MSG_OUT); + + spc_data_out(regs, SCSI_PHASE_MSG_OUT, 1, + &spc->active_target->transient_state.identify); + + /* wait to go to command phase */ + SPC_WAIT_PHASE(SCSI_PHASE_CMD); + + /* ack */ + SPC_ACK(regs, SCSI_PHASE_CMD); + + /* should be a command complete intr pending. Eat it */ + if (regs->spc_ints != SPC_INTS_DONE) + gimmeabreak(); + regs->spc_ints = SPC_INTS_DONE; + + /* spit */ + return spc_data_out(regs, SCSI_PHASE_CMD, + spc->active_target->transient_state.cmd_count, + spc->active_target->cmd_ptr) ? FALSE : TRUE; +} + + +boolean_t +spc_end_transaction( spc, ints, psns, serr) + register spc_softc_t spc; + int ints, psns, serr; +{ + register spc_regmap_t *regs = spc->regs; + char cmc; + int tmp; + + LOG(0x1f,"end_t"); + + SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/); + + spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &cmc); + + if (cmc != SCSI_COMMAND_COMPLETE) + printf("{T%x}", cmc); + + while (regs->spc_ints != (SPC_INTS_DONE|SPC_INTS_DISC)); + + SPC_ACK(regs,SPC_PHASE_DISC); + + /* going to disconnect */ + regs->spc_pctl = ~SPC_PCTL_BFREE_IE & SPC_PHASE_DISC; + /* regs->spc_scmd = 0; */ + + /* clear all intr bits? */ + tmp = regs->spc_ints; + regs->spc_ints = tmp; + + + if (!spc_end(spc, ints, psns, serr)) + (void) spc_reconnect(spc, ints, psns, serr); + return FALSE; +} + +boolean_t +spc_end( spc, ints, psns, serr) + register spc_softc_t spc; + int ints, psns, serr; +{ + register target_info_t *tgt; + register io_req_t ior; + register spc_regmap_t *regs = spc->regs; + int csr; + + LOG(6,"end"); + + tgt = spc->active_target; + + if ((tgt->done = spc->done) == SCSI_RET_IN_PROGRESS) + tgt->done = SCSI_RET_SUCCESS; + + spc->script = 0; + + if (spc->wd.nactive-- == 1) + spc->wd.watchdog_state = SCSI_WD_INACTIVE; + + /* check reconnection not pending */ + csr = SPC_INTS_RESELECTED & regs->spc_ints; + if (!csr) + spc_release_bus(spc); + else + { + spc->active_target = 0; + /* spc->state &= ~SPC_STATE_BUSY; later */ + } + if (ior = tgt->ior) { +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + fdma_unmap(&tgt->fdma, ior); +#endif /*MACH_KERNEL*/ + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + if (csr) + spc->state &= ~SPC_STATE_BUSY; + } + + /* return not reselected */ + return (csr & SPC_INTS_RESELECTED) ? 0 : 1; +} + +boolean_t +spc_release_bus(spc) + register spc_softc_t spc; +{ + boolean_t ret = FALSE; + + LOG(9,"release"); + + spc->script = 0; + + if (spc->state & SPC_STATE_COLLISION) { + + LOG(0xB,"collided"); + spc->state &= ~SPC_STATE_COLLISION; + spc_attempt_selection(spc); + + } else if (queue_empty(&spc->waiting_targets)) { + + spc->state &= ~SPC_STATE_BUSY; + spc->active_target = 0; + ret = TRUE; + + } else { + + LOG(0xC,"dequeue"); + spc->next_target = (target_info_t *) + dequeue_head(&spc->waiting_targets); + spc_attempt_selection(spc); + } + return ret; +} + +boolean_t +spc_get_status( spc, ints, psns, serr) + register spc_softc_t spc; + int ints, psns, serr; +{ + register spc_regmap_t *regs = spc->regs; + scsi2_status_byte_t status; + register target_info_t *tgt; + + LOG(0xD,"get_status"); +TRWRAP; + + spc->state &= ~SPC_STATE_DMA_IN; + + tgt = spc->active_target; + + SPC_ACK(regs,SCSI_PHASE_STATUS /*,1*/); + + spc_data_in(regs, SCSI_PHASE_STATUS, 1, &status.bits); + + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(spc->active_target, SCSI_ERR_STATUS, status.bits, 0); + spc->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + spc->done = SCSI_RET_SUCCESS; + + return TRUE; +} + +boolean_t +spc_xfer_in( spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register target_info_t *tgt; + register spc_regmap_t *regs = spc->regs; + register int count; + boolean_t advance_script = TRUE; + + LOG(0xE,"xfer_in"); + + tgt = spc->active_target; + spc->state |= SPC_STATE_DMA_IN; + + count = tgt->transient_state.in_count; + + SPC_ACK(regs, SCSI_PHASE_DATAI); + + if ((tgt->cur_cmd != SCSI_CMD_READ) && + (tgt->cur_cmd != SCSI_CMD_LONG_READ)) + spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->cmd_ptr); + else + { + spc_data_in(regs, SCSI_PHASE_DATAI, count, tgt->ior->io_data); + } + + return advance_script; +} + +boolean_t +spc_xfer_out( spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register spc_regmap_t *regs = spc->regs; + register target_info_t *tgt; + boolean_t advance_script = TRUE; + int count = spc->out_count; + + LOG(0xF,"xfer_out"); + + tgt = spc->active_target; + spc->state &= ~SPC_STATE_DMA_IN; + + count = tgt->transient_state.out_count; + + SPC_ACK(regs, SCSI_PHASE_DATAO); + + if ((tgt->cur_cmd != SCSI_CMD_WRITE) && + (tgt->cur_cmd != SCSI_CMD_LONG_WRITE)) + spc_data_out(regs, SCSI_PHASE_DATAO, count, + tgt->cmd_ptr + tgt->transient_state.cmd_count); + else + spc_data_out(regs, SCSI_PHASE_DATAO, count, tgt->ior->io_data); + + return advance_script; +} + +/* disconnect-reconnect ops */ + +/* get the message in via dma ?? */ +boolean_t +spc_msg_in(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register target_info_t *tgt; + + LOG(0x15,"msg_in"); + gimmeabreak(); + + tgt = spc->active_target; + +#if 0 +You can do this by hand, just leave an interrupt pending at the end +#endif + + /* We only really expect two bytes */ +#if 0 + SPC_PUT(dmar,sizeof(scsi_command_group_0)); + .... +#endif + return TRUE; +} + +/* check the message is indeed a DISCONNECT */ +boolean_t +spc_disconnect(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register int len = 0; + boolean_t ok = FALSE; + register char *msgs = 0; + + +/* SPC_TC_GET(dmar,len); */ + len = sizeof(scsi_command_group_0) - len; + +/* msgs = tgt->cmd_ptr; */ /* I think */ + + if ((len == 0) || (len > 2) || msgs == 0) + ok = FALSE; + else { + /* A SDP message preceeds it in non-completed READs */ + ok = ((msgs[0] == SCSI_DISCONNECT) || /* completed op */ + ((msgs[0] == SCSI_SAVE_DATA_POINTER) && /* incomplete */ + (msgs[1] == SCSI_DISCONNECT))); + } + if (!ok) + printf("[tgt %d bad msg (%d): %x]", + spc->active_target->target_id, len, *msgs); + + return TRUE; +} + +/* save all relevant data, free the BUS */ +boolean_t +spc_disconnected(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register target_info_t *tgt; + +/* make sure reselects will work */ + + LOG(0x16,"disconnected"); + + spc_disconnect(spc,ints, psns, ssts); + + tgt = spc->active_target; + tgt->flags |= TGT_DISCONNECTED; + tgt->transient_state.handler = spc->error_handler; + /* the rest has been saved in spc_err_disconn() */ + + PRINT(("{D%d}", tgt->target_id)); + + spc_release_bus(spc); + + return FALSE; +} + +/* get reconnect message, restore BUS */ +boolean_t +spc_reconnect(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + + LOG(0x17,"reconnect"); + + if (spc->wd.nactive == 0) { + LOG(2,"SPURIOUS"); + return FALSE; + } + +#if 0 +This is the 5380 code, for reference: + spc_regmap_t *regs = spc->regs; + register target_info_t *tgt; + register int id; + int msg; + + + id = regs->spc_data;/*parity?*/ + /* xxx check our id is in there */ + + id &= ~(1 << spc->sc->initiator_id); + { + register int i; + for (i = 0; i < 8; i++) + if (id & (1 << i)) break; +if (i == 8) {printf("{P%x}", id);return;} + id = i; + } + regs->spc_icmd = SPC_ICMD_BSY; + while (regs->spc_bus_csr & SPC_BUS_SEL) + ; + regs->spc_icmd = 0; + delay_1p2_us(); + while ( ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) && + ((regs->spc_bus_csr & SPC_BUS_BSY) == 0) && + ((regs->spc_bus_csr & SPC_BUS_BSY) == 0)) + ; + + /* Now should wait for correct phase: REQ signals it */ + while ( ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) && + ((regs->spc_bus_csr & SPC_BUS_REQ) == 0) && + ((regs->spc_bus_csr & SPC_BUS_REQ) == 0)) + ; + + regs->spc_mode |= SPC_MODE_MONBSY; + + /* + * See if this reconnection collided with a selection attempt + */ + if (spc->state & SPC_STATE_BUSY) + spc->state |= SPC_STATE_COLLISION; + + spc->state |= SPC_STATE_BUSY; + + /* Get identify msg */ + bs = regs->spc_phase; +if (SPC_CUR_PHASE(bs) != SCSI_PHASE_MSG_IN) gimmeabreak(); + SPC_ACK(regs,SCSI_PHASE_MSG_IN /*,1*/); + msg = 0; + spc_data_in(regs, SCSI_PHASE_MSG_IN, 1, &msg); + regs->spc_mode = SPC_MODE_PAR_CHK|SPC_MODE_DMA|SPC_MODE_MONBSY; + + if (msg != SCSI_IDENTIFY) + printf("{I%x %x}", id, msg); + + tgt = spc->sc->target[id]; + if (id > 7 || tgt == 0) panic("spc_reconnect"); + + PRINT(("{R%d}", id)); + if (spc->state & SPC_STATE_COLLISION) + PRINT(("[B %d-%d]", spc->active_target->target_id, id)); + + LOG(0x80+id,0); + + spc->active_target = tgt; + tgt->flags &= ~TGT_DISCONNECTED; + + spc->script = tgt->transient_state.script; + spc->error_handler = tgt->transient_state.handler; + spc->in_count = 0; + spc->out_count = 0; + + /* Should get a phase mismatch when tgt changes phase */ +#endif + return TRUE; +} + + + +/* do the synch negotiation */ +boolean_t +spc_dosynch( spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + /* + * Phase is MSG_OUT here, cmd has not been xferred + */ + int len; + register target_info_t *tgt; + register spc_regmap_t *regs = spc->regs; + unsigned char off; + unsigned char p[6]; + + LOG(0x11,"dosync"); + + /* ATN still asserted */ + SPC_ACK(regs,SCSI_PHASE_MSG_OUT); + + tgt = spc->active_target; + + tgt->flags |= TGT_DID_SYNCH; /* only one chance */ + tgt->flags &= ~TGT_TRY_SYNCH; + + /*p = some scratch buffer, on the stack */ + + p[0] = SCSI_IDENTIFY; + p[1] = SCSI_EXTENDED_MESSAGE; + p[2] = 3; + p[3] = SCSI_SYNC_XFER_REQUEST; + /* We cannot run synchronous */ +#define spc_to_scsi_period(x) 0x7 +#define scsi_period_to_spc(x) (x) + off = 0; + p[4] = spc_to_scsi_period(spc_min_period); + p[5] = off; + + /* The transfer is started with ATN still set. The + chip will automagically drop ATN before it transfers the + last byte. Pretty neat. */ + spc_data_out(regs, SCSI_PHASE_MSG_OUT, + sizeof(scsi_synch_xfer_req_t)+1, p); + + /* wait for phase change to status phase */ + SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_OUT); + + + psns = regs->spc_phase; + + /* The standard sez there nothing else the target can do but.. */ + if (SPC_CUR_PHASE(psns) != SCSI_PHASE_MSG_IN) + panic("spc_dosync");/* XXX put offline */ + + /* + msgin: + */ + /* ack */ + SPC_ACK(regs,SCSI_PHASE_MSG_IN); + + /* clear any pending interrupts */ + regs->spc_ints = regs->spc_ints; + + /* get answer */ + len = sizeof(scsi_synch_xfer_req_t); + len = spc_data_in(regs, SCSI_PHASE_MSG_IN, len, p); + + /* do not cancel the phase mismatch interrupt ! */ + + /* look at the answer and see if we like it */ + if (len || (p[0] != SCSI_EXTENDED_MESSAGE)) { + /* did not like it at all */ + printf(" did not like SYNCH xfer "); + } else { + /* will NOT do synch */ + printf(" but we cannot do SYNCH xfer "); + tgt->sync_period = scsi_period_to_spc(p[3]); + tgt->sync_offset = p[4]; + /* sanity */ + if (tgt->sync_offset != 0) + printf(" ?OFFSET %x? ", tgt->sync_offset); + } + + /* wait for phase change */ + SPC_WAIT_PHASE_VANISH(SCSI_PHASE_MSG_IN); + + psns = regs->spc_phase; + + /* phase should be command now */ + /* continue with simple command script */ + spc->error_handler = spc_err_generic; + spc->script = spc_script_cmd; + +/* Make sure you get out right here, esp the script pointer and/or pending intr */ + + if (SPC_CUR_PHASE(psns) == SCSI_PHASE_CMD ) + return FALSE; + + if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS ) /* jump to get_status */ + return TRUE; /* intr is pending */ + + spc->script++; + if (SPC_CUR_PHASE(psns) == SCSI_PHASE_MSG_IN ) + return TRUE; + + if ((psns & SPC_BUS_BSY) == 0) /* uhu? disconnected */ + return TRUE; + + gimmeabreak(); + return FALSE; +} + +/* + * The bus was reset + */ +void spc_bus_reset(spc) + register spc_softc_t spc; +{ + register spc_regmap_t *regs = spc->regs; + + LOG(0x21,"bus_reset"); + + /* + * Clear bus descriptor + */ + spc->script = 0; + spc->error_handler = 0; + spc->active_target = 0; + spc->next_target = 0; + spc->state = 0; + queue_init(&spc->waiting_targets); + spc->wd.nactive = 0; + spc_reset(regs, TRUE); + + printf("spc%d: (%d) bus reset ", spc->sc->masterno, ++spc->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (spc->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(spc->sc); +} + +/* + * Error handlers + */ + +/* + * Generic, default handler + */ +boolean_t +spc_err_generic(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + register spc_regmap_t *regs = spc->regs; + LOG(0x10,"err_generic"); + + if (ints & SPC_INTS_TIMEOUT) /* we timed out */ + if ((regs->spc_scmd & SPC_SCMD_CMDMASK) == SPC_SCMD_C_SELECT) + { + /* Powered off ? */ + if (spc->active_target->flags & TGT_FULLY_PROBED) + { + spc->active_target->flags = 0; + LOG(0x1e,"Device Down"); + } + spc->done = SCSI_RET_DEVICE_DOWN; + spc_end(spc, ints, psns, ssts); + return FALSE; /* don't retry - just report missing device */ + } + else + { /* timed out - but not on a select. What is going on? */ + gimmeabreak(); + } + + if (SPC_CUR_PHASE(psns) == SCSI_PHASE_STATUS) + return spc_err_to_status(spc, ints, psns, ssts); + gimmeabreak(); + return FALSE; +} + +/* + * Handle generic errors that are reported as + * an unexpected change to STATUS phase + */ +boolean_t +spc_err_to_status(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ + script_t scp = spc->script; + + LOG(0x20,"err_tostatus"); + while (SCSI_PHASE(scp->condition) != SCSI_PHASE_STATUS) + scp++; + spc->script = scp; +#if 0 + /* + * Normally, we would already be able to say the command + * is in error, e.g. the tape had a filemark or something. + * But in case we do disconnected mode WRITEs, it is quite + * common that the following happens: + * dma_out -> disconnect -> reconnect + * and our script might expect at this point that the dma + * had to be restarted (it didn't know it was completed + * because the tape record is shorter than we asked for). + * And in any event.. it is both correct and cleaner to + * declare error iff the STATUS byte says so. + */ + spc->done = SCSI_RET_NEED_SENSE; +#endif + return TRUE; +} + +/* + * Watch for a disconnection + */ +boolean_t +spc_err_disconn(spc, ints, psns, ssts) + register spc_softc_t spc; + int ints, psns, ssts; +{ +#if 1 +/* + * THIS ROUTINE CAN'T POSSIBLY WORK... + * FOR EXAMPLE, THE VARIABLE 'xferred' IS NEVER INITIALIZED. + */ + return FALSE; +#else + register spc_regmap_t *regs; + register target_info_t *tgt; + int xferred; + + LOG(0x18,"err_disconn"); + + if (SPC_CUR_PHASE(ints) != SCSI_PHASE_MSG_IN) + return spc_err_generic(spc, ints, psns, ssts); + + regs = spc->regs; + + tgt = spc->active_target; + + switch (SCSI_PHASE(spc->script->condition)) { + case SCSI_PHASE_DATAO: + LOG(0x1b,"+DATAO"); +/*updatecounters:*/ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + tgt->transient_state.dma_offset += xferred; + + tgt->transient_state.script = spc_script_data_out; + break; + + case SCSI_PHASE_DATAI: + LOG(0x19,"+DATAI"); + +/*update counters: */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; + assert(tgt->transient_state.in_count > 0); + tgt->transient_state.dma_offset += xferred; + + tgt->transient_state.script = spc_script_data_in; + break; + + case SCSI_PHASE_STATUS: + + if (spc->state & SPC_STATE_DMA_IN) { + + LOG(0x1a,"+STATUS+R"); + +/*same as above.. */ + assert(xferred > 0); + tgt->transient_state.in_count -= xferred; +/* assert(tgt->transient_state.in_count > 0);*/ + tgt->transient_state.dma_offset += xferred; + + tgt->transient_state.script = spc_script_data_in; + if (tgt->transient_state.in_count == 0) + tgt->transient_state.script++; + + } else { + + LOG(0x1d,"+STATUS+W"); + + if ((tgt->transient_state.out_count == spc->out_count)) { + /* all done */ + tgt->transient_state.script = &spc_script_data_out[1]; + tgt->transient_state.out_count = 0; + } else { + +/*.. */ + tgt->transient_state.out_count -= xferred; + assert(tgt->transient_state.out_count > 0); + tgt->transient_state.dma_offset += xferred; + + tgt->transient_state.script = spc_script_data_out; + } + spc->out_count = 0; + } + break; + default: + gimmeabreak(); + } + /* spc->xxx = 0; */ + +/* SPC_ACK(regs,SCSI_PHASE_MSG_IN); later */ + (void) spc_msg_in(spc, ints, psns, ssts); + + spc->script = spc_script_disconnect; + + return FALSE; +#endif +} + +/* + * Watchdog + * + */ +void spc_reset_scsibus(spc) + register spc_softc_t spc; +{ + register target_info_t *tgt = spc->active_target; + if (tgt) { + int cnt = 0; + /* SPC_TC_GET(spc->dmar,cnt); */ + log( LOG_KERN, + "Target %d was active, cmd x%x in x%x out x%x Sin x%x Sou x%x dmalen x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, tgt->transient_state.out_count, + spc->in_count, spc->out_count, cnt); + } +#if 0 + spc->regs->..... +#endif + delay(25); +} + +int SPC_ACK(regs, phase) +register spc_regmap_t *regs; +unsigned phase; +{ + /* we want to switch into the specified phase - + + The calling routine should already dismissed + any pending interrupts (spc_ints) + */ + + regs->spc_psns = 0; + regs->spc_pctl = phase | SPC_PCTL_BFREE_IE; + return 0; +} +#endif /*NSCSI > 0*/ + +#endif 0 diff --git a/scsi/adapters/scsi_aha15.h b/scsi/adapters/scsi_aha15.h new file mode 100644 index 0000000..52cd936 --- /dev/null +++ b/scsi/adapters/scsi_aha15.h @@ -0,0 +1,347 @@ +/* + * 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: scsi_aha15.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 6/91 + * + * Definitions for the Adaptec AHA-15xx family + * of Intelligent SCSI Host Adapter boards + */ + +#ifndef _SCSI_AHA15_H_ +#define _SCSI_AHA15_H_ + +/* + * Addresses/length in 24 bits + * + * BEWARE: your compiler must pack these correctly, + * e.g. without gaps between two such contiguous structs + * (GCC does) + */ +typedef struct { + unsigned char msb; + unsigned char mid; + unsigned char lsb; +} aha_address_t; + +#define AHA_ADDRESS_SET(addr,val) {\ + (addr).msb = ((val) >> 16);\ + (addr).mid = ((val) >> 8);\ + (addr).lsb = (val) ;\ + } +#define AHA_ADDRESS_GET(addr,val) {\ + (val) = ((addr).msb << 16) |\ + ((addr).mid << 8) |\ + ((addr).lsb ) ;\ + } + +#define aha_length_t aha_address_t +#define AHA_LENGTH_SET AHA_ADDRESS_SET +#define AHA_LENGTH_GET AHA_ADDRESS_GET + +/* + * Register map + */ + +typedef struct { + volatile unsigned char aha_status; /* r: Status Register */ +#define aha_ctl aha_status /* w: Control Register */ + + volatile unsigned char aha_data; /* rw: Data Port */ +#define aha_cmd aha_data /* w: Command register */ + + volatile unsigned char aha_intr; /* ro: Interrupt Register */ +} aha_regmap_t; + +/* ..but on the 386 I/O is not memory mapped */ +#define AHA_STATUS_PORT(port) ((port)) +#define AHA_CONTROL_PORT(port) ((port)) +#define AHA_COMMAND_PORT(port) ((port)+1) +#define AHA_DATA_PORT(port) ((port)+1) +#define AHA_INTR_PORT(port) ((port)+2) + +/* Status Register */ +#define AHA_CSR_CMD_ERR 0x01 /* Invalid command */ +#define AHA_CSR_xxx 0x02 /* undefined */ +#define AHA_CSR_DATAI_FULL 0x04 /* In-port full */ +#define AHA_CSR_DATAO_FULL 0x08 /* Out-port full */ +#define AHA_CSR_IDLE 0x10 /* doin nuthin */ +#define AHA_CSR_INIT_REQ 0x20 /* initialization required */ +#define AHA_CSR_DIAG_FAIL 0x40 /* selftest failed */ +#define AHA_CSR_SELF_TEST 0x80 /* selftesting */ + +/* Control Register */ +#define AHA_CTL_xxx 0x0f /* undefined */ +#define AHA_CTL_SCSI_RST 0x10 /* reset SCSIbus */ +#define AHA_CTL_INTR_CLR 0x20 /* Clear interrupt reg */ +#define AHA_CTL_SOFT_RESET 0x40 /* Board only, no selftest */ +#define AHA_CTL_HARD_RESET 0x80 /* Full reset, and SCSIbus */ + +/* Interrupt Flags register */ +#define AHA_INTR_MBI_FULL 0x01 /* scan the In mboxes */ +#define AHA_INTR_MBO_AVAIL 0x02 /* scan the Out mboxes */ +#define AHA_INTR_DONE 0x04 /* command complete */ +#define AHA_INTR_RST 0x08 /* saw a SCSIbus reset */ +#define AHA_INTR_xxx 0x70 /* undefined */ +#define AHA_INTR_PENDING 0x80 /* Any interrupt bit set */ + +/* + * Command register + */ +#define AHA_CMD_NOP 0x00 /* */ +#define AHA_CMD_INIT 0x01 /* mbox initialization */ + /* 4 bytes follow: # of Out mboxes (x2->total), and + msb, mid, lsb of mbox address */ +struct aha_init { + unsigned char mb_count; + aha_address_t mb_ptr; +}; +#define AHA_CMD_START 0x02 /* start SCSI cmd */ +#define AHA_CMD_BIOS 0x03 +#define AHA_CMD_INQUIRY 0x04 + /* returns 4 bytes: */ +struct aha_inq { + unsigned char board_id; +# define AHA_BID_1540_B16 0x00 +# define AHA_BID_1540_B64 0x30 +# define AHA_BID_1540B 0x41 +# define AHA_BID_1640 0x42 +# define AHA_BID_1740 0x43 +# define AHA_BID_1542C 0x44 +# define AHA_BID_1542CF 0x45 /* BIOS v2.0x */ + + unsigned char options; +# define AHA_BOPT_STD 0x41 /* in 154x, standard model */ + + unsigned char frl_1; /* rev level */ + unsigned char frl_2; +}; +#define AHA_CMD_MBO_IE 0x05 + /* 1 byte follows: */ +# define AHA_MBO_DISABLE 0x00 +# define AHA_MBO_ENABLE 0x01 + +#define AHA_CMD_SET_SELTO 0x06 /* select timeout */ + /* 4 bytes follow: */ +struct aha_selto { + unsigned char enable; + char xxx; + unsigned char timeo_msb; + unsigned char timeo_lsb; +}; +#define AHA_CMD_SET_BUSON 0x07 + /* 1 byte value follows: 2..15 default 11 usecs */ +#define AHA_CMD_SET_BUSOFF 0x08 + /* 1 byte value follows: 1..64 default 4 usecs */ +#define AHA_CMD_SET_XSPEED 0x09 + /* 1 byte value follows: */ +# define AHA_DMASPEED_5Mb 0x00 +# define AHA_DMASPEED_7Mb 0x01 +# define AHA_DMASPEED_8Mb 0x02 +# define AHA_DMASPEED_10Mb 0x03 +# define AHA_DMASPEED_6Mb 0x04 + /* values in the range 80..ff encoded as follows: + bit 7 on --> custom speed + bits 6..4 read pulse width + 0 100ns + 1 150 + 2 200 + 3 250 + 4 300 + 5 350 + 6 400 + 7 450 + bit 3 strobe off time + 0 100ns + 1 150ns + bits 2..0 write pulse width + <same as read pulse> + */ +#define AHA_CMD_FIND_DEVICES 0x0a + /* returns 8 bytes, each one is a bitmask of the LUNs + available for the given target ID */ +struct aha_devs { + unsigned char tgt_luns[8]; +}; +#define AHA_CMD_GET_CONFIG 0x0b + /* returns 3 bytes: */ +struct aha_conf { + unsigned char dma_arbitration;/* bit N -> channel N */ + unsigned char intr_ch;/* bit N -> intr 9+N (but 13,16)*/ + unsigned char my_scsi_id; /* both of I and T role */ +}; +#define AHA_CMD_ENB_TGT_MODE 0x0c + /* 2 bytes follow: */ +struct aha_tgt { + unsigned char enable; + unsigned char luns; /* bitmask */ +}; + +#define AHA_CMD_GET_SETUP 0x0d + /* 1 byte follows: allocation len (N) */ + /* returns N bytes, 17 significant: */ +struct aha_setup { + BITFIELD_3( unsigned char, + initiate_SDT:1, + enable_parity:1, + res:6); + unsigned char xspeed; /* see above */ + unsigned char buson; + unsigned char busoff; + unsigned char n_mboxes;/* 0 if not initialized */ + aha_address_t mb_ptr; /* garbage if not inited */ + struct { + BITFIELD_3( unsigned char, + offset: 4, + period: 3, /* 200 + 50 * N */ + negotiated: 1); + } SDT_params[8]; + unsigned char no_disconnect; /* bitmask */ +}; + +#define AHA_CMD_WRITE_CH2 0x1a + /* 3 bytes (aha_address_t) follow for the buffer pointer */ +#define AHA_CMD_READ_CH2 0x1b + /* 3 bytes (aha_address_t) follow for the buffer pointer */ +#define AHA_CMD_WRITE_FIFO 0x1c + /* 3 bytes (aha_address_t) follow for the buffer pointer */ +#define AHA_CMD_READ_FIFO 0x1d + /* 3 bytes (aha_address_t) follow for the buffer pointer */ +#define AHA_CMD_ECHO 0x1f + /* 1 byte follows, which should then be read back */ +#define AHA_CMD_DIAG 0x20 +#define AHA_CMD_SET_OPT 0x21 + /* 2+ bytes follow: */ +struct aha_diag { + unsigned char parmlen; /* bytes to follow */ + unsigned char no_disconnect; /* bitmask */ + /* rest is undefined */ +}; + +#define AHA_EXT_BIOS 0x28 /* return extended bios info */ +#define AHA_MBX_ENABLE 0x29 /* enable mail box interface */ +struct aha_extbios { + unsigned char flags; /* Bit 3 == 1 extended bios enabled */ + unsigned char mailboxlock; /* mail box lock code to unlock it */ +}; + +/* + * Command Control Block + */ +typedef struct { + unsigned char ccb_code; +# define AHA_CCB_I_CMD 0x00 +# define AHA_CCB_T_CMD 0x01 +# define AHA_CCB_I_CMD_SG 0x02 +# define AHA_CCB_ICMD_R 0x03 +# define AHA_CCB_ICMD_SG_R 0x04 +# define AHA_CCB_BDEV_RST 0x81 + BITFIELD_4( unsigned char, + ccb_lun:3, + ccb_in:1, + ccb_out:1, + ccb_scsi_id:3); + unsigned char ccb_cmd_len; + unsigned char ccb_reqsns_len; /* if 1 no automatic reqsns*/ + aha_length_t ccb_datalen; + aha_address_t ccb_dataptr; + aha_address_t ccb_linkptr; + unsigned char ccb_linkid; + unsigned char ccb_hstatus; +# define AHA_HST_SUCCESS 0x00 +# define AHA_HST_SEL_TIMEO 0x11 +# define AHA_HST_DATA_OVRUN 0x12 +# define AHA_HST_BAD_DISCONN 0x13 +# define AHA_HST_BAD_PHASE_SEQ 0x14 +# define AHA_HST_BAD_OPCODE 0x16 +# define AHA_HST_BAD_LINK_LUN 0x17 +# define AHA_HST_INVALID_TDIR 0x18 +# define AHA_HST_DUPLICATED_CCB 0x19 +# define AHA_HST_BAD_PARAM 0x1a + + scsi2_status_byte_t ccb_status; + unsigned char ccb_xxx; + unsigned char ccb_xxx1; + scsi_command_group_5 ccb_scsi_cmd; /* cast as needed */ +} aha_ccb_t; + +/* For scatter/gather use a list of (len,ptr) segments, each field + is 3 bytes (aha_address_t) long. Max 17 segments, min 1 */ + +/* + * Ring descriptor, aka Mailbox + */ +typedef union { + + struct { + volatile unsigned char mb_cmd; /* Out mbox */ +# define mb_status mb_cmd /* In mbox */ + + aha_address_t mb_ptr; +#define AHA_MB_SET_PTR(mbx,val) AHA_ADDRESS_SET((mbx)->mb.mb_ptr,(val)) +#define AHA_MB_GET_PTR(mbx,val) AHA_ADDRESS_GET((mbx)->mb.mb_ptr,(val)) + + } mb; + + struct { /* ccb required In mbox */ + volatile unsigned char mb_cmd; + BITFIELD_4( unsigned char, + mb_lun : 3, + mb_isa_send : 1, + mb_isa_recv : 1, + mb_initiator_id : 3); + unsigned char mb_data_len_msb; + unsigned char mb_data_len_mid; + } mbt; + + unsigned int bits; /* quick access */ + +} aha_mbox_t; + +/* Out mbox, values for the mb_cmd field */ +#define AHA_MBO_FREE 0x00 +#define AHA_MBO_START 0x01 +#define AHA_MBO_ABORT 0x02 + +/* In mbox, values for the mb_status field */ +#define AHA_MBI_FREE 0x00 +#define AHA_MBI_SUCCESS 0x01 +#define AHA_MBI_ABORTED 0x02 +#define AHA_MBI_NOT_FOUND 0x03 +#define AHA_MBI_ERROR 0x04 +#define AHA_MBI_NEED_CCB 0x10 + +/* + * Scatter/gather segment lists + */ +typedef struct { + aha_length_t len; + aha_address_t ptr; +} aha_seglist_t; + +#define AHA_MAX_SEGLIST 17 /* which means max 64Kb */ +#endif /*_SCSI_AHA15_H_*/ diff --git a/scsi/adapters/scsi_aha15_hdw.c b/scsi/adapters/scsi_aha15_hdw.c new file mode 100644 index 0000000..5514bc5 --- /dev/null +++ b/scsi/adapters/scsi_aha15_hdw.c @@ -0,0 +1,1467 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1992,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: scsi_aha15_hdw.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 6/91 + * + * Bottom layer of the SCSI driver: chip-dependent functions + * + * This file contains the code that is specific to the Adaptec + * AHA-15xx family of Intelligent SCSI Host Adapter boards: + * probing, start operation, and interrupt routine. + */ + +/* + * Since the board is "Intelligent" we do not need scripts like + * other simpler HBAs. Maybe. + */ +#include <cpus.h> +#include <platforms.h> + +#include <aha.h> +#if NAHA > 0 + +#include <mach/std_types.h> +#include <machine/machspl.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> + +/* #include <sys/syslog.h> */ + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#include <scsi/adapters/scsi_aha15.h> + +#ifdef AT386 +#define MACHINE_PGBYTES I386_PGBYTES +#define MAPPABLE 0 +#define gimmeabreak() asm("int3") +#include <i386/pio.h> /* inlining of outb and inb */ +#endif /*AT386*/ + +#ifdef CBUS /* For the Corollary machine, physical */ +#include <i386at/mp/mp.h> +#include <cbus/cbus.h> + +#define aha_cbus_window transient_state.hba_dep[0] + /* must use windows for phys addresses */ + /* greater than 16 megs */ + +#define kvtoAT cbus_kvtoAT +#else /* CBUS */ +#define kvtoAT kvtophys +#endif /* CBUS */ + +#ifndef MACHINE_PGBYTES /* cross compile check */ +#define MACHINE_PGBYTES 0x1000 +#define MAPPABLE 1 +#define gimmeabreak() Debugger("gimmeabreak"); +#endif + +/* + * Data structures: ring, ccbs, a per target buffer + */ + +#define AHA_NMBOXES 2 /* no need for more, I think */ +struct aha_mb_ctl { + aha_mbox_t omb[AHA_NMBOXES]; + aha_mbox_t imb[AHA_NMBOXES]; + unsigned char iidx, oidx; /* roving ptrs into */ +}; +#define next_mbx_idx(i) ((((i)+1)==AHA_NMBOXES)?0:((i)+1)) + +#define AHA_NCCB 8 /* for now */ +struct aha_ccb_raw { + target_info_t *active_target; + aha_ccb_t ccb; + char buffer[256]; /* separate out this ? */ +}; +#define rccb_to_cmdptr(rccb) ((char*)&((rccb)->ccb.ccb_scsi_cmd)) + +/* forward decls */ +int aha_reset_scsibus(); +boolean_t aha_probe_target(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) board + */ +struct aha_softc { + watchdog_t wd; + decl_simple_lock_data(, aha_lock) + unsigned int port; /* I/O port */ + + int ntargets; /* how many alive on this scsibus */ + + scsi_softc_t *sc; /* HBA-indep info */ + + struct aha_mb_ctl mb; /* mailbox structures */ + + /* This chicanery is for mapping back the phys address + of a CCB (which we get in an MBI) to its virtual */ + /* [we could use phystokv(), but it isn't standard] */ + vm_offset_t I_hold_my_phys_address; + struct aha_ccb_raw aha_ccbs[AHA_NCCB]; + +} aha_softc_data[NAHA]; + +typedef struct aha_softc *aha_softc_t; + +aha_softc_t aha_softc[NAHA]; + +struct aha_ccb_raw * +mb_to_rccb(aha, mbi) + aha_softc_t aha; + aha_mbox_t mbi; +{ + vm_offset_t addr; + + AHA_MB_GET_PTR(&mbi,addr); /* phys address of ccb */ + + /* make virtual */ + addr = ((vm_offset_t)&aha->I_hold_my_phys_address) + + (addr - aha->I_hold_my_phys_address); + + /* adjust by proper offset to get base */ + addr -= (vm_offset_t)&(((struct aha_ccb_raw *)0)->ccb); + + return (struct aha_ccb_raw *)addr; +} + +target_info_t * +aha_tgt_alloc(aha, id, sns_len, tgt) + aha_softc_t aha; + target_info_t *tgt; +{ + struct aha_ccb_raw *rccb; + + aha->ntargets++; + + if (tgt == 0) + tgt = scsi_slave_alloc(aha - aha_softc_data, id, aha); + + rccb = &(aha->aha_ccbs[id]); + rccb->ccb.ccb_reqsns_len = sns_len; + tgt->cmd_ptr = rccb_to_cmdptr(rccb); + tgt->dma_ptr = 0; +#ifdef CBUS + tgt->aha_cbus_window = 0; +#endif /* CBUS */ + return tgt; +} + +/* + * Synch xfer timing conversions + */ +#define aha_to_scsi_period(a) ((200 + ((a) * 50)) >> 2) +#define scsi_period_to_aha(p) ((((p) << 2) - 200) / 50) + +/* + * Definition of the controller for the auto-configuration program. + */ + +/* DOCUMENTATION */ +/* base ports can be: + 0x334, 0x330 (default), 0x234, 0x230, 0x134, 0x130 + possible interrupt channels are: + 9, 10, 11 (default), 12, 14, 15 + DMA channels can be: + 7, 6, 5 (default), 0 +/* DOCUMENTATION */ + +int aha_probe(), scsi_slave(), aha_go(), aha_intr(); +void scsi_attach(); + +vm_offset_t aha_std[NAHA] = { 0 }; +struct bus_device *aha_dinfo[NAHA*8]; +struct bus_ctlr *aha_minfo[NAHA]; +struct bus_driver aha_driver = + { aha_probe, scsi_slave, scsi_attach, aha_go, aha_std, "rz", aha_dinfo, + "ahac", aha_minfo, BUS_INTR_B4_PROBE}; + +#define DEBUG 1 +#if DEBUG + +#define PRINT(x) if (scsi_debug) printf x + +aha_state(port) +{ + register unsigned char st, intr; + + if (port == 0) + port = 0x330; + st = inb(AHA_STATUS_PORT(port)); + intr = inb(AHA_INTR_PORT(port)); + + printf("status %x intr %x\n", st, intr); + return 0; +} + +aha_target_state(tgt) + target_info_t *tgt; +{ + if (tgt == 0) + tgt = aha_softc[0]->sc->target[0]; + if (tgt == 0) + return 0; + printf("fl %x dma %X+%x cmd %x@%X id %x per %x off %x ior %X ret %X\n", + tgt->flags, tgt->dma_ptr, tgt->transient_state.dma_offset, tgt->cur_cmd, + tgt->cmd_ptr, tgt->target_id, tgt->sync_period, tgt->sync_offset, + tgt->ior, tgt->done); + + return 0; +} + +aha_all_targets(unit) +{ + int i; + target_info_t *tgt; + for (i = 0; i < 8; i++) { + tgt = aha_softc[unit]->sc->target[i]; + if (tgt) + aha_target_state(tgt); + } +} + +#define TRMAX 200 +int tr[TRMAX+3]; +int trpt, trpthi; +#define TR(x) tr[trpt++] = x +#define TRWRAP trpthi = trpt; trpt = 0; +#define TRCHECK if (trpt > TRMAX) {TRWRAP} + +#define TRACE + +#ifdef TRACE + +#define LOGSIZE 256 +#define LOG_KERN 0<<3 /* from syslog.h */ + +int aha_logpt; +char aha_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x1e +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG(e,f) + char *f; +{ + aha_log[aha_logpt++] = (e); + if (aha_logpt == LOGSIZE) aha_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +aha_print_log(skip) + int skip; +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = aha_logpt; i < LOGSIZE; i++) { + c = aha_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + printf(" %s", logtbl[c].name); + else + printf("-%x", c & 0x7f); + } + return 0; +} + +aha_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /*TRACE*/ +#define LOG(e,f) +#define LOGSIZE +#endif /*TRACE*/ + +#else /*DEBUG*/ +#define PRINT(x) +#define LOG(e,f) +#define LOGSIZE +#define TRCHECK +#define TR(a) + +#endif /*DEBUG*/ + +/* Utility functions at end */ + + +/* + * Probe/Slave/Attach functions + */ + +int aha_dotarget = 1; /* somehow on some boards this is trouble */ + +/* + * Probe routine: + * Should find out (a) if the controller is + * present and (b) which/where slaves are present. + * + * Implementation: + * Just ask the board to do it + */ +aha_probe(port, ui) + register port; + struct bus_ctlr *ui; +{ + int unit = ui->unit; + aha_softc_t aha = &aha_softc_data[unit]; + int target_id; + scsi_softc_t *sc; + spl_t s; + boolean_t did_banner = FALSE; + struct aha_devs installed; + struct aha_conf conf; + + /* No interrupts yet */ + s = splbio(); + + /* + * We should be called with a sensible port, but you never know. + * Send an echo command and see that we get it back properly + */ + { + register unsigned char st; + + st = inb(AHA_STATUS_PORT(port)); + + /* + * There is no board reset in case of reboot with + * no power-on/power-off sequence. Test it and do + * the reset if necessary. + */ + + if (!(st & AHA_CSR_INIT_REQ)) { + outb(AHA_CONTROL_PORT(port), + AHA_CTL_SOFT_RESET|AHA_CTL_HARD_RESET); + while ((st = inb(AHA_STATUS_PORT(port))) & + AHA_CSR_SELF_TEST); + } + if ((st & AHA_CSR_DATAO_FULL) || + !(st & AHA_CSR_INIT_REQ)) + goto fail; + + outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO); + delay(1000);/*?*/ + st = inb(AHA_STATUS_PORT(port)); + if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL)) + goto fail; + + outb(AHA_COMMAND_PORT(port), 0x5e); + delay(1000); + + st = inb(AHA_STATUS_PORT(port)); + if ((st & AHA_CSR_CMD_ERR) || + ((st & AHA_CSR_DATAI_FULL) == 0)) + goto fail; + + st = inb(AHA_DATA_PORT(port)); + if (st != 0x5e) { +fail: splx(s); + return 0; + } + /* + * augment test with check for echoing inverse and with + * test for enhanced adapter with standard ports enabled. + */ + + /* Check that 0xa1 echoed as well as 0x5e */ + + outb(AHA_COMMAND_PORT(port), AHA_CMD_ECHO); + delay(1000);/*?*/ + st = inb(AHA_STATUS_PORT(port)); + if (st & (AHA_CSR_CMD_ERR|AHA_CSR_DATAO_FULL)) + goto fail; + + outb(AHA_COMMAND_PORT(port), 0xa1); + delay(1000); + + st = inb(AHA_STATUS_PORT(port)); + if ((st & AHA_CSR_CMD_ERR) || + ((st & AHA_CSR_DATAI_FULL) == 0)) + goto fail; + + st = inb(AHA_DATA_PORT(port)); + if (st != 0xa1) + goto fail ; + + { /* Check that port isn't 174x in enhanced mode + with standard mode ports enabled. This should be + ignored because it will be caught and correctly + handled by eaha_probe(). See TRM4-11..13. + dph + */ + unsigned z ; + static unsigned port_table[] = + {0,0,0x130,0x134,0x230,0x234,0x330,0x334}; + for (z= 0x1000; z<= 0xF000; z+= 0x1000) + if (inb(z+0xC80) == 0x04 && + inb(z+0xC81) == 0x90 && + inb(z+0xCC0) & 0x80 == 0x80 && + port_table [inb(z+0xCC0) & 0x07] == port) + goto fail ; + } + outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); + } + +#if MAPPABLE + /* Mappable version side */ + AHA_probe(port, ui); +#endif /*MAPPABLE*/ + + /* + * Initialize hw descriptor, cache some pointers + */ + aha_softc[unit] = aha; + aha->port = port; + + sc = scsi_master_alloc(unit, aha); + aha->sc = sc; + + simple_lock_init(&aha->aha_lock); + sc->go = aha_go; + sc->watchdog = scsi_watchdog; + sc->probe = aha_probe_target; + aha->wd.reset = aha_reset_scsibus; + + /* Stupid limitation, no way around it */ + sc->max_dma_data = (AHA_MAX_SEGLIST-1) * MACHINE_PGBYTES; + + + /* XXX + * I'm not sure how much use this bit of code is really. + * On the 1542CF we don't really want to try and initialize + * the mailboxes before unlocking them in any case, and + * resetting the card is done above. + */ +#if 0 +#if 0 + /* + * Reset board. + */ + aha_reset(port, TRUE); +#else + /* + * Initialize mailboxes + */ + aha_init_1(aha); +#endif +#endif + + /* + * Who are we ? + */ + { + struct aha_inq inq; + struct aha_extbios extbios; + char *id; + + aha_command(port, AHA_CMD_INQUIRY, 0, 0, &inq, sizeof(inq), TRUE); + + switch (inq.board_id) { + case AHA_BID_1540_B16: + case AHA_BID_1540_B64: + id = "1540"; break; + case AHA_BID_1540B: + id = "1540B/1542B"; break; + case AHA_BID_1640: + id = "1640"; break; + case AHA_BID_1740: + id = "1740 Unsupported!!"; break; + case AHA_BID_1542C: + id = "1542C"; aha_dotarget = 0; break; + case AHA_BID_1542CF: + id = "1542CF"; break; + default: + id = 0; break; + } + + printf("Adaptec %s [id %x], rev %c%c, options x%x\n", + id ? id : "Board", + inq.board_id, inq.frl_1, inq.frl_2, inq.options); + + /* + * If we are a 1542C or 1542CF disable the extended bios + * so that the mailbox interface is unlocked. + * No need to check the extended bios flags as some of the + * extensions that cause us problems are not flagged in + * that byte. + */ + if (inq.board_id == 0x44 || inq.board_id == 0x45) { + aha_command(port, AHA_EXT_BIOS, 0, 0, &extbios, + sizeof(extbios), TRUE); +#ifdef AHADEBUG + printf("aha: extended bios flags 0x%x\n", extbios.flags); + printf("aha: mailboxlock 0x%x\n", extbios.mblock); +#endif /* AHADEBUG */ + + printf("aha: 1542C/CF detected, unlocking mailbox\n"); + + /* XXX - This sends the mailboxlock code out to the + * controller. We need to output a 0, then the + * code...so since we don't care about the flags + * anyway, we just zero out that field and re-use + * the struct. + */ + extbios.flags = 0; + aha_command(port, AHA_MBX_ENABLE, &extbios, + sizeof(extbios), 0, 0, TRUE); + } + + } +doconf: + /* + * Readin conf data + */ + aha_command(port, AHA_CMD_GET_CONFIG, 0, 0, &conf, sizeof(conf), TRUE); + + { + unsigned char args; + + /* + * Change the bus on/off times to not clash with + * other dma users. + */ + args = 7; + aha_command(port, AHA_CMD_SET_BUSON, &args, 1, 0, 0, TRUE); + args = 5; + aha_command(port, AHA_CMD_SET_BUSOFF, &args, 1, 0, 0, TRUE); + } + + /* XXX - This is _REALLY_ sickening. */ + /* + * Set up the DMA channel we'll be using. + */ + { + register int d, i; + static struct { + unsigned char port; + unsigned char init_data; + } aha_dma_init[8][2] = { + {{0x0b,0x0c}, {0x0a,0x00}}, /* channel 0 */ + {{0,0},{0,0}}, + {{0,0},{0,0}}, + {{0,0},{0,0}}, + {{0,0},{0,0}}, + {{0xd6,0xc1}, {0xd4,0x01}}, /* channel 5 (def) */ + {{0xd6,0xc2}, {0xd4,0x02}}, /* channel 6 */ + {{0xd6,0xc3}, {0xd4,0x03}} /* channel 7 */ + }; + + + for (i = 0; i < 8; i++) + if ((1 << i) & conf.intr_ch) break; + i += 9; + +#if there_was_a_way + /* + * On second unit, avoid clashes with first + */ + if ((unit > 0) && (ui->sysdep1 != i)) { + printf("Reprogramming irq and dma ch..\n"); + .... + goto doconf; + } +#endif + + /* + * Initialize the DMA controller viz the channel we'll use + */ + for (d = 0; d < 8; d++) + if ((1 << d) & conf.dma_arbitration) break; + + outb(aha_dma_init[d][0].port, aha_dma_init[d][0].init_data); + outb(aha_dma_init[d][1].port, aha_dma_init[d][1].init_data); + + /* make mapping phys->virt possible for CCBs */ + aha->I_hold_my_phys_address = + kvtoAT((vm_offset_t)&aha->I_hold_my_phys_address); + + /* + * Our SCSI ID. (xxx) On some boards this is SW programmable. + */ + sc->initiator_id = conf.my_scsi_id; + + printf("%s%d: [dma ch %d intr ch %d] my SCSI id is %d", + ui->name, unit, d, i, sc->initiator_id); + + /* Interrupt vector setup */ + ui->sysdep1 = i; + take_ctlr_irq(ui); + } + + /* + * More initializations + */ + { + register target_info_t *tgt; + + aha_init(aha); + + /* allocate a desc for tgt mode role */ + tgt = aha_tgt_alloc(aha, sc->initiator_id, 1, 0); + sccpu_new_initiator(tgt, tgt); /* self */ + + } + + /* Now we could take interrupts, BUT we do not want to + be selected as targets by some other host just yet */ + + /* + * For all possible targets, see if there is one and allocate + * a descriptor for it if it is there. + * This includes ourselves, when acting as target + */ + aha_command( port, AHA_CMD_FIND_DEVICES, 0, 0, &installed, sizeof(installed), TRUE); + for (target_id = 0; target_id < 8; target_id++) { + + if (target_id == sc->initiator_id) /* done already */ + continue; + + if (installed.tgt_luns[target_id] == 0) + continue; + + printf(",%s%d", did_banner++ ? " " : " target(s) at ", + target_id); + + /* Normally, only LUN 0 */ + if (installed.tgt_luns[target_id] != 1) + printf("(%x)", installed.tgt_luns[target_id]); + /* + * Found a target + */ + (void) aha_tgt_alloc(aha, target_id, 1/*no REQSNS*/, 0); + + } + printf(".\n"); + splx(s); + + return 1; +} + +boolean_t +aha_probe_target(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + aha_softc_t aha = aha_softc[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + (void) aha_tgt_alloc(aha,tgt->target_id, 1/*no REQSNS*/, tgt); + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + +aha_reset(port, quick) +{ + register unsigned char st; + + /* + * Reset board and wait till done + */ + outb(AHA_CONTROL_PORT(port), AHA_CTL_SOFT_RESET); + do { + delay(25); + st = inb(AHA_STATUS_PORT(port)); + } while ((st & (AHA_CSR_IDLE|AHA_CSR_INIT_REQ)) == 0); + + if (quick) return; + + /* + * reset the scsi bus. Does NOT generate an interrupt (bozos) + */ + outb(AHA_CONTROL_PORT(port), AHA_CTL_SCSI_RST); +} + +aha_init_1(aha) + aha_softc_t aha; +{ + struct aha_init a; + vm_offset_t phys; + + bzero(&aha->mb, sizeof(aha->mb)); /* also means all free */ + a.mb_count = AHA_NMBOXES; + phys = kvtoAT((vm_offset_t)&aha->mb); + AHA_ADDRESS_SET(a.mb_ptr, phys); + aha_command(aha->port, AHA_CMD_INIT, &a, sizeof(a), 0, 0, TRUE); +} + +aha_init_2(port) +{ + unsigned char disable = AHA_MBO_DISABLE; + struct aha_tgt role; + + /* Disable MBO available interrupt */ + aha_command(port, AHA_CMD_MBO_IE, &disable, 1, 0,0, FALSE); + + if (aha_dotarget) { + /* Enable target mode role */ + role.enable = 1; + role.luns = 1; /* only LUN 0 */ + aha_command(port, AHA_CMD_ENB_TGT_MODE, &role, sizeof(role), 0, 0, TRUE); + } +} + +aha_init(aha) + aha_softc_t aha; +{ + aha_init_1(aha); + aha_init_2(aha->port); +} + +/* + * Operational functions + */ + +/* + * Start a SCSI command on a target + */ +aha_go(tgt, cmd_count, in_count, cmd_only) + target_info_t *tgt; + boolean_t cmd_only; +{ + aha_softc_t aha; + spl_t s; + struct aha_ccb_raw *rccb; + int len; + vm_offset_t virt, phys; + +#if CBUS + at386_io_lock_state(); +#endif + + LOG(1,"go"); + + aha = (aha_softc_t)tgt->hw_state; + +/* XXX delay the handling of the ccb till later */ + rccb = &(aha->aha_ccbs[tgt->target_id]); + rccb->active_target = tgt; + + /* + * We can do real DMA. + */ +/* tgt->transient_state.copy_count = 0; unused */ +/* tgt->transient_state.dma_offset = 0; unused */ + + tgt->transient_state.cmd_count = cmd_count; + + if ((tgt->cur_cmd == SCSI_CMD_WRITE) || + (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ + io_req_t ior = tgt->ior; + register int len = ior->io_count; + + tgt->transient_state.out_count = len; + + /* How do we avoid leaks here ? Trust the board + will do zero-padding, for now. XXX CHECKME */ +#if 0 + if (len < tgt->block_size) { + bzero(to + len, tgt->block_size - len); + len = tgt->block_size; + tgt->transient_state.out_count = len; + } +#endif + } else { + tgt->transient_state.out_count = 0; + } + + /* See above for in_count < block_size */ + tgt->transient_state.in_count = in_count; + + /* + * Setup CCB state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(9,"readop"); + virt = (vm_offset_t)tgt->ior->io_data; + len = tgt->transient_state.in_count; + rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x1a,"writeop"); + virt = (vm_offset_t)tgt->ior->io_data; + len = tgt->transient_state.out_count; + rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1; + break; + case SCSI_CMD_INQUIRY: + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + case SCSI_CMD_READ_TOC: + case SCSI_CMD_READ_SUBCH: + case SCSI_CMD_READ_HEADER: + case 0xc4: /* despised: SCSI_CMD_DEC_PLAYBACK_STATUS */ + case 0xc6: /* despised: SCSI_CMD_TOSHIBA_READ_SUBCH_Q */ + case 0xc7: /* despised: SCSI_CMD_TOSHIBA_READ_TOC_ENTRY */ + case 0xdd: /* despised: SCSI_CMD_NEC_READ_SUBCH_Q */ + case 0xde: /* despised: SCSI_CMD_NEC_READ_TOC */ + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + virt = (vm_offset_t)tgt->cmd_ptr; + len = tgt->transient_state.in_count; + rccb->ccb.ccb_in = 1; rccb->ccb.ccb_out = 0; + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + case 0xc9: /* vendor-spec: SCSI_CMD_DEC_PLAYBACK_CONTROL */ + { register int cs = sizeof_scsi_command(tgt->cur_cmd); + tgt->transient_state.cmd_count = cs; + len = + tgt->transient_state.out_count = cmd_count - cs; + virt = (vm_offset_t)tgt->cmd_ptr + cs; + rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 1; + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + } + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + virt = 0; + len = 0; + rccb->ccb.ccb_in = 0; rccb->ccb.ccb_out = 0; + } + +#if CBUS + at386_io_lock(MP_DEV_WAIT); +#endif + aha_prepare_rccb(tgt, rccb, virt, len); + + rccb->ccb.ccb_lun = tgt->lun; + rccb->ccb.ccb_scsi_id = tgt->target_id; + +/* AHA_LENGTH_SET(rccb->ccb.ccb_linkptr, 0); unused */ +/* rccb->ccb.ccb_linkid = 0; unused */ + +#if !CBUS + s = splbio(); +#endif + + LOG(3,"enqueue"); + + aha_start_scsi(aha, &rccb->ccb); + +#if CBUS + at386_io_unlock(); +#else + splx(s); +#endif +} + +aha_prepare_rccb(tgt, rccb, virt, len) + target_info_t *tgt; + struct aha_ccb_raw *rccb; + vm_offset_t virt; + vm_size_t len; +{ + vm_offset_t phys; +#ifdef CBUS + int cbus_window; +#endif /* CBUS */ + + rccb->ccb.ccb_cmd_len = tgt->transient_state.cmd_count; + + /* this opcode is refused, grrrr. */ +/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_R; /* default common case */ + rccb->ccb.ccb_code = AHA_CCB_I_CMD; /* default common case */ + AHA_LENGTH_SET(rccb->ccb.ccb_datalen, len);/* default common case */ + +#ifdef CBUS + if (tgt->aha_cbus_window == 0) + tgt->aha_cbus_window = cbus_alloc_win(AHA_MAX_SEGLIST+1); + cbus_window = tgt->aha_cbus_window; +#endif /* CBUS */ + + if (virt == 0) { + /* no xfers */ + AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, 0); + } else if (len <= MACHINE_PGBYTES) { +/* INCORRECT: what if across two pages :INCORRECT */ + /* simple xfer */ +#ifdef CBUS + phys = cbus_kvtoAT_ww(virt, cbus_window); +#else /* CBUS */ + phys = kvtophys(virt); +#endif /* CBUS */ + AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys); + } else { + /* messy xfer */ + aha_seglist_t *seglist; + vm_offset_t ph1, off; + vm_size_t l1; + + /* this opcode does not work, grrrrr */ +/* rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG_R;*/ + rccb->ccb.ccb_code = AHA_CCB_I_CMD_SG; + + if (tgt->dma_ptr == 0) + aha_alloc_segment_list(tgt); + seglist = (aha_seglist_t *) tgt->dma_ptr; +#ifdef CBUS + phys = cbus_kvtoAT_ww(seglist, cbus_window); + cbus_window++; +#else /* CBUS */ + phys = kvtophys((vm_offset_t) seglist); +#endif /* CBUS */ + AHA_ADDRESS_SET(rccb->ccb.ccb_dataptr, phys); + + ph1 = /*i386_trunc_page*/ virt & ~(MACHINE_PGBYTES - 1); + off = virt & (MACHINE_PGBYTES - 1); +#ifdef CBUS + ph1 = cbus_kvtoAT_ww(ph1, cbus_window) + off; + cbus_window++; +#else /* CBUS */ + ph1 = kvtophys(ph1) + off; +#endif /* CBUS */ + l1 = MACHINE_PGBYTES - off; + + off = 1;/* now #pages */ + while (1) { + AHA_ADDRESS_SET(seglist->ptr, ph1); + AHA_LENGTH_SET(seglist->len, l1); + seglist++; + + if ((len -= l1) <= 0) + break; + virt += l1; off++; + +#ifdef CBUS + ph1 = cbus_kvtoAT_ww(virt, cbus_window); + cbus_window++; +#else /* CBUS */ + ph1 = kvtophys(virt); +#endif /* CBUS */ + l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len; + } + l1 = off * sizeof(*seglist); + AHA_LENGTH_SET(rccb->ccb.ccb_datalen, l1); + } +} + +aha_start_scsi(aha, ccb) + aha_softc_t aha; + aha_ccb_t *ccb; +{ + register aha_mbox_t *mb; + register idx; + vm_offset_t phys; + aha_mbox_t mbo; + spl_t s; + + LOG(4,"start"); + LOG(0x80+ccb->ccb_scsi_id,0); + + /* + * Get an MBO, spin if necessary (takes little time) + */ + s = splbio(); + phys = kvtoAT((vm_offset_t)ccb); + /* might cross pages, but should be ok (kernel is contig) */ + AHA_MB_SET_PTR(&mbo,phys); + mbo.mb.mb_cmd = AHA_MBO_START; + + simple_lock(&aha->aha_lock); + if (aha->wd.nactive++ == 0) + aha->wd.watchdog_state = SCSI_WD_ACTIVE; + idx = aha->mb.oidx; + aha->mb.oidx = next_mbx_idx(idx); + mb = &aha->mb.omb[idx]; + while (mb->mb.mb_status != AHA_MBO_FREE) + delay(1); + mb->bits = mbo.bits; + simple_unlock(&aha->aha_lock); + + /* + * Start the board going + */ + aha_command(aha->port, AHA_CMD_START, 0, 0, 0, 0, FALSE); + splx(s); +} + +/* + * Interrupt routine + * Take interrupts from the board + * + * Implementation: + * TBD + */ +aha_intr(unit) +{ + register aha_softc_t aha; + register port; + register csr, intr; +#if MAPPABLE + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) + return AHA_intr(unit); +#endif /*MAPPABLE*/ + + aha = aha_softc[unit]; + port = aha->port; + + LOG(5,"\n\tintr"); +gotintr: + /* collect ephemeral information */ + csr = inb(AHA_STATUS_PORT(port)); + intr = inb(AHA_INTR_PORT(port)); + + /* + * Check for errors + */ + if (csr & (AHA_CSR_DIAG_FAIL|AHA_CSR_CMD_ERR)) { +/* XXX */ gimmeabreak(); + } + + /* drop spurious interrupts */ + if ((intr & AHA_INTR_PENDING) == 0) { + LOG(2,"SPURIOUS"); + return; + } + outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); + +TR(csr);TR(intr);TRCHECK + + if (intr & AHA_INTR_RST) + return aha_bus_reset(aha); + + /* we got an interrupt allright */ + if (aha->wd.nactive) + aha->wd.watchdog_state = SCSI_WD_ACTIVE; + + if (intr == AHA_INTR_DONE) { + /* csr & AHA_CSR_CMD_ERR --> with error */ + LOG(6,"done"); + return; + } + +/* if (intr & AHA_INTR_MBO_AVAIL) will not happen */ + + /* Some real work today ? */ + if (intr & AHA_INTR_MBI_FULL) { + register int idx; + register aha_mbox_t *mb; + int nscan = 0; + aha_mbox_t mbi; +rescan: + simple_lock(&aha->aha_lock); + idx = aha->mb.iidx; + aha->mb.iidx = next_mbx_idx(idx); + mb = &aha->mb.imb[idx]; + mbi.bits = mb->bits; + mb->mb.mb_status = AHA_MBI_FREE; + simple_unlock(&aha->aha_lock); + + nscan++; + + switch (mbi.mb.mb_status) { + + case AHA_MBI_FREE: + if (nscan >= AHA_NMBOXES) + return; + goto rescan; + break; + + case AHA_MBI_SUCCESS: + case AHA_MBI_ERROR: + aha_initiator_intr(aha, mbi); + break; + + case AHA_MBI_NEED_CCB: + aha_target_intr(aha, mbi); + break; + +/* case AHA_MBI_ABORTED: /* this we wont see */ +/* case AHA_MBI_NOT_FOUND: /* this we wont see */ + default: + log( LOG_KERN, + "aha%d: Bogus status (x%x) in MBI\n", + unit, mbi.mb.mb_status); + break; + } + + /* peek ahead */ + if (aha->mb.imb[aha->mb.iidx].mb.mb_status != AHA_MBI_FREE) + goto rescan; + } + + /* See if more work ready */ + if (inb(AHA_INTR_PORT(port)) & AHA_INTR_PENDING) { + LOG(7,"\n\tre-intr"); + goto gotintr; + } +} + +/* + * The interrupt routine turns to one of these two + * functions, depending on the incoming mbi's role + */ +aha_target_intr(aha, mbi) + aha_softc_t aha; + aha_mbox_t mbi; +{ + target_info_t *initiator; /* this is the caller */ + target_info_t *self; /* this is us */ + int len; + + if (mbi.mbt.mb_cmd != AHA_MBI_NEED_CCB) + gimmeabreak(); + + /* If we got here this is not zero .. */ + self = aha->sc->target[aha->sc->initiator_id]; + + initiator = aha->sc->target[mbi.mbt.mb_initiator_id]; + /* ..but initiators are not required to answer to our inquiry */ + if (initiator == 0) { + /* allocate */ + initiator = aha_tgt_alloc(aha, mbi.mbt.mb_initiator_id, + sizeof(scsi_sense_data_t) + 5, 0); + + /* We do not know here wether the host was down when + we inquired, or it refused the connection. Leave + the decision on how we will talk to it to higher + level code */ + LOG(0xC, "new_initiator"); + sccpu_new_initiator(self, initiator); + } + + /* The right thing to do would be build an ior + and call the self->dev_ops->strategy routine, + but we cannot allocate it at interrupt level. + Also note that we are now disconnected from the + initiator, no way to do anything else with it + but reconnect and do what it wants us to do */ + + /* obviously, this needs both spl and MP protection */ + self->dev_info.cpu.req_pending = TRUE; + self->dev_info.cpu.req_id = mbi.mbt.mb_initiator_id; + self->dev_info.cpu.req_lun = mbi.mbt.mb_lun; + self->dev_info.cpu.req_cmd = + mbi.mbt.mb_isa_send ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE; + len = (mbi.mbt.mb_data_len_msb << 16) | + (mbi.mbt.mb_data_len_mid << 8 ); + len += 0x100;/* truncation problem */ + self->dev_info.cpu.req_len = len; + + LOG(0xB,"tgt-mode-restart"); + (*self->dev_ops->restart)( self, FALSE); + + /* The call above has either prepared the data, + placing an ior on self, or it handled it some + other way */ + if (self->ior == 0) + return; /* I guess we'll do it later */ + + { + struct aha_ccb_raw *rccb; + + rccb = &(aha->aha_ccbs[initiator->target_id]); + rccb->active_target = initiator; + if (self->dev_info.cpu.req_cmd == SCSI_CMD_SEND) { + rccb->ccb.ccb_in = 1; + rccb->ccb.ccb_out = 0; + } else { + rccb->ccb.ccb_in = 0; + rccb->ccb.ccb_out = 1; + } + + aha_prepare_rccb(initiator, rccb, + (vm_offset_t)self->ior->io_data, self->ior->io_count); + rccb->ccb.ccb_code = AHA_CCB_T_CMD; + rccb->ccb.ccb_lun = initiator->lun; + rccb->ccb.ccb_scsi_id = initiator->target_id; + + simple_lock(&aha->aha_lock); + if (aha->wd.nactive++ == 0) + aha->wd.watchdog_state = SCSI_WD_ACTIVE; + simple_unlock(&aha->aha_lock); + + aha_start_scsi(aha, &rccb->ccb); + } +} + +aha_initiator_intr(aha, mbi) + aha_softc_t aha; + aha_mbox_t mbi; +{ + struct aha_ccb_raw *rccb; + scsi2_status_byte_t status; + target_info_t *tgt; + + rccb = mb_to_rccb(aha,mbi); + tgt = rccb->active_target; + rccb->active_target = 0; + + /* shortcut (sic!) */ + if (mbi.mb.mb_status == AHA_MBI_SUCCESS) + goto allok; + + switch (rccb->ccb.ccb_hstatus) { + case AHA_HST_SUCCESS: +allok: + status = rccb->ccb.ccb_status; + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0); + tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + tgt->done = SCSI_RET_SUCCESS; + break; + case AHA_HST_SEL_TIMEO: + if (tgt->flags & TGT_FULLY_PROBED) + tgt->flags = 0; /* went offline */ + tgt->done = SCSI_RET_DEVICE_DOWN; + break; + case AHA_HST_DATA_OVRUN: + /* BUT we don't know if this is an underrun. + It is ok if we get less data than we asked + for, in a number of cases. Most boards do not + seem to generate this anyways, but some do. */ + { register int cmd = tgt->cur_cmd; + switch (cmd) { + case SCSI_CMD_INQUIRY: + case SCSI_CMD_REQUEST_SENSE: + break; + default: + printf("%sx%x\n", + "aha: U/OVRUN on scsi command x%x\n", + cmd); + gimmeabreak(); + } + } + goto allok; + case AHA_HST_BAD_DISCONN: + printf("aha: bad disconnect\n"); + tgt->done = SCSI_RET_ABORTED; + break; + case AHA_HST_BAD_PHASE_SEQ: + /* we'll get an interrupt soon */ + printf("aha: bad PHASE sequencing\n"); + tgt->done = SCSI_RET_ABORTED; + break; + case AHA_HST_BAD_OPCODE: /* fall through */ + case AHA_HST_BAD_PARAM: +printf("aha: BADCCB\n");gimmeabreak(); + tgt->done = SCSI_RET_RETRY; + break; + case AHA_HST_BAD_LINK_LUN: /* these should not happen */ + case AHA_HST_INVALID_TDIR: + case AHA_HST_DUPLICATED_CCB: + printf("aha: bad hstatus (x%x)\n", rccb->ccb.ccb_hstatus); + tgt->done = SCSI_RET_ABORTED; + break; + } + + LOG(8,"end"); + + simple_lock(&aha->aha_lock); + if (aha->wd.nactive-- == 1) + aha->wd.watchdog_state = SCSI_WD_INACTIVE; + simple_unlock(&aha->aha_lock); + + if (tgt->ior) { + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + } + + return FALSE; +} + +/* + * The bus was reset + */ +aha_bus_reset(aha) + register aha_softc_t aha; +{ + register port = aha->port; + + LOG(0x1d,"bus_reset"); + + /* + * Clear bus descriptor + */ + aha->wd.nactive = 0; + aha_reset(port, TRUE); + aha_init(aha); + + printf("aha: (%d) bus reset ", ++aha->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (aha->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(aha->sc); +} + +/* + * Watchdog + * + * We know that some (name withdrawn) disks get + * stuck in the middle of dma phases... + */ +aha_reset_scsibus(aha) + register aha_softc_t aha; +{ + register target_info_t *tgt; + register port = aha->port; + register int i; + + for (i = 0; i < AHA_NCCB; i++) { + tgt = aha->aha_ccbs[i].active_target; + if (/*scsi_debug &&*/ tgt) + printf("Target %d was active, cmd x%x in x%x out x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, + tgt->transient_state.out_count); + } + aha_reset(port, FALSE); + delay(35); + /* no interrupt will come */ + aha_bus_reset(aha); +} + +/* + * Utilities + */ + +/* + * Send a command to the board along with some + * optional parameters, optionally receive the + * results at command completion, returns how + * many bytes we did NOT get back. + */ +aha_command(port, cmd, outp, outc, inp, inc, clear_interrupt) + unsigned char *outp, *inp; +{ + register unsigned char st; + boolean_t failed = TRUE; + + do { + st = inb(AHA_STATUS_PORT(port)); + } while (st & AHA_CSR_DATAO_FULL); + + /* Output command and any data */ + outb(AHA_COMMAND_PORT(port), cmd); + while (outc--) { + do { + st = inb(AHA_STATUS_PORT(port)); + if (st & AHA_CSR_CMD_ERR) goto out; + } while (st & AHA_CSR_DATAO_FULL); + + outb(AHA_COMMAND_PORT(port), *outp++); + } + + /* get any data */ + while (inc--) { + do { + st = inb(AHA_STATUS_PORT(port)); + if (st & AHA_CSR_CMD_ERR) goto out; + } while ((st & AHA_CSR_DATAI_FULL) == 0); + + *inp++ = inb(AHA_DATA_PORT(port)); + } + ++inc; + failed = FALSE; + + /* wait command complete */ + if (clear_interrupt) do { + delay(1); + st = inb(AHA_INTR_PORT(port)); + } while ((st & AHA_INTR_DONE) == 0); + +out: + if (clear_interrupt) + outb(AHA_CONTROL_PORT(port), AHA_CTL_INTR_CLR); + if (failed) + printf("aha_command: error on (%x %x %x %x %x %x), status %x\n", + port, cmd, outp, outc, inp, inc, st); + return inc; +} + +#include <vm/vm_kern.h> + +/* + * Allocate dynamically segment lists to + * targets (for scatter/gather) + * Its a max of 17*6=102 bytes per target. + */ +vm_offset_t aha_seglist_next, aha_seglist_end; + +aha_alloc_segment_list(tgt) + target_info_t *tgt; +{ +#define ALLOC_SIZE (AHA_MAX_SEGLIST * sizeof(aha_seglist_t)) + +/* XXX locking */ + if ((aha_seglist_next + ALLOC_SIZE) > aha_seglist_end) { + (void) kmem_alloc_wired(kernel_map, &aha_seglist_next, PAGE_SIZE); + aha_seglist_end = aha_seglist_next + PAGE_SIZE; + } + tgt->dma_ptr = (char *)aha_seglist_next; + aha_seglist_next += ALLOC_SIZE; +/* XXX locking */ +} + +#endif /* NAHA > 0 */ + diff --git a/scsi/adapters/scsi_aha17_hdw.c b/scsi/adapters/scsi_aha17_hdw.c new file mode 100644 index 0000000..d8afe6a --- /dev/null +++ b/scsi/adapters/scsi_aha17_hdw.c @@ -0,0 +1,1371 @@ +/* + * Mach Operating System + * Copyright (c) 1993 Carnegie Mellon University + * Copyright (c) 1993 University of Dublin + * 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 the following 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 AND THE UNIVERSITY OF DUBLIN ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON AND THE + * UNIVERSITY OF DUBLIN DISCLAIM 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. + */ +/* + * Support for AHA-174x in enhanced mode. Dominic Herity (dherity@cs.tcd.ie) + * Will refer to "Adaptec AHA-1740A/1742A/1744 Technical Reference Manual" + * page x-y as TRMx-y in comments below. + */ + +#include <eaha.h> +#if NEAHA > 0 + +#define db_printf printf + +#include <cpus.h> +#include <platforms.h> +#include <aha.h> + +#ifdef OSF +#include <eisa.h> +#else +#include <i386at/eisa.h> +#endif + +#include <mach/std_types.h> +#include <sys/types.h> +#include <chips/busses.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#include <scsi/adapters/scsi_aha15.h> +#include <vm/vm_kern.h> + +#ifdef AT386 +#define MACHINE_PGBYTES I386_PGBYTES +#define MAPPABLE 0 +#define gimmeabreak() asm("int3") + + +#include <i386/pio.h> /* inlining of outb and inb */ +#ifdef OSF +#include <machine/mp/mp.h> +#endif +#endif /*AT386*/ + +#ifdef CBUS +#include <cbus/cbus.h> +#endif + + +#ifndef MACHINE_PGBYTES /* cross compile check */ +#define MACHINE_PGBYTES 0x1000 +#define MAPPABLE 1 +#define gimmeabreak() Debugger("gimmeabreak"); +#endif + +int eaha_probe(), scsi_slave(), eaha_go(), eaha_intr(); +void scsi_attach(); + +vm_offset_t eaha_std[NEAHA] = { 0 }; +struct bus_device *eaha_dinfo[NEAHA*8]; +struct bus_ctlr *eaha_minfo[NEAHA]; +struct bus_driver eaha_driver = + { eaha_probe, scsi_slave, scsi_attach, eaha_go, eaha_std, "rz", + eaha_dinfo, "eahac", eaha_minfo, BUS_INTR_B4_PROBE}; + + +#define TRACE +#ifdef TRACE + +#define LOGSIZE 256 +int eaha_logpt; +char eaha_log[LOGSIZE]; + +#define MAXLOG_VALUE 0x1e +struct { + char *name; + unsigned int count; +} logtbl[MAXLOG_VALUE]; + +static LOG( + int e, + char *f) +{ + eaha_log[eaha_logpt++] = (e); + if (eaha_logpt == LOGSIZE) eaha_logpt = 0; + if ((e) < MAXLOG_VALUE) { + logtbl[(e)].name = (f); + logtbl[(e)].count++; + } +} + +eaha_print_log( + int skip) +{ + register int i, j; + register unsigned char c; + + for (i = 0, j = eaha_logpt; i < LOGSIZE; i++) { + c = eaha_log[j]; + if (++j == LOGSIZE) j = 0; + if (skip-- > 0) + continue; + if (c < MAXLOG_VALUE) + db_printf(" %s", logtbl[c].name); + else + db_printf("-%x", c & 0x7f); + } + return 0; +} + +eaha_print_stat() +{ + register int i; + register char *p; + for (i = 0; i < MAXLOG_VALUE; i++) { + if (p = logtbl[i].name) + printf("%d %s\n", logtbl[i].count, p); + } +} + +#else /*TRACE*/ +#define LOG(e,f) +#define LOGSIZE +#endif /*TRACE*/ + +#ifdef DEBUG +#define ASSERT(x) { if (!(x)) gimmeabreak() ; } +#define MARK() gimmeabreak() +#else +#define ASSERT(x) +#define MARK() +#endif + +/* + * Notes : + * + * do each host command TRM6-4 + * find targets in probe + * disable SCSI writes + * matching port with structs, eaha_go with port, eaha_intr with port + * + */ + +/* eaha registers. See TRM4-11..23. dph */ + +#define HID0(z) ((z)+0xC80) +#define HID1(z) ((z)+0xC81) +#define HID2(z) ((z)+0xC82) +#define HID3(z) ((z)+0xC83) +#define EBCTRL(z) ((z)+0xC84) +#define PORTADDR(z) ((z)+0xCC0) +#define BIOSADDR(z) ((z)+0xCC1) +#define INTDEF(z) ((z)+0xCC2) +#define SCSIDEF(z) ((z)+0xCC3) +#define MBOXOUT0(z) ((z)+0xCD0) +#define MBOXOUT1(z) ((z)+0xCD1) +#define MBOXOUT2(z) ((z)+0xCD2) +#define MBOXOUT3(z) ((z)+0xCD3) +#define MBOXIN0(z) ((z)+0xCD8) +#define MBOXIN1(z) ((z)+0xCD9) +#define MBOXIN2(z) ((z)+0xCDA) +#define MBOXIN3(z) ((z)+0xCDB) +#define ATTN(z) ((z)+0xCD4) +#define G2CNTRL(z) ((z)+0xCD5) +#define G2INTST(z) ((z)+0xCD6) +#define G2STAT(z) ((z)+0xCD7) +#define G2STAT2(z) ((z)+0xCDC) + +/* + * Enhanced mode data structures: ring, enhanced ccbs, a per target buffer + */ + +#define SCSI_TARGETS 8 /* Allow for SCSI-2 */ + + +/* Extended Command Control Block Format. See TRM6-3..12. */ + +typedef struct { + unsigned short command ; +# define EAHA_CMD_NOP 0 +# define EAHA_CMD_INIT_CMD 1 +# define EAHA_CMD_DIAG 5 +# define EAHA_CMD_INIT_SCSI 6 +# define EAHA_CMD_READ_SENS 8 +# define EAHA_CMD_DOWNLOAD 9 +# define EAHA_CMD_HOST_INQ 0x0a +# define EAHA_CMD_TARG_CMD 0x10 + + /* + * It appears to be customary to tackle the endian-ness of + * bit fields as follows, so I won't deviate. However, nothing in + * K&R implies that bit fields are implemented so that the fields + * of an unsigned char are allocated lsb first. Indeed, K&R _warns_ + * _against_ using bit fields to describe storage allocation. + * This issue is separate from endian-ness. dph + * And this is exactly the reason macros are used. If your compiler + * is weird just override the macros and we will all be happy. af + */ + BITFIELD_3(unsigned char, + cne:1, + xxx0:6, + di:1) ; + BITFIELD_7(unsigned char, + xxx1:2, + ses:1, + xxx2:1, + sg:1, + xxx3:1, + dsb:1, + ars:1) ; + + BITFIELD_5(unsigned char, + lun:3, + tag:1, + tt:2, + nd:1, + xxx4:1) ; + BITFIELD_7(unsigned char, + dat:1, + dir:1, + st:1, + chk:1, + xxx5:2, + rec:1, + nbr:1) ; + + unsigned short xxx6 ; + + vm_offset_t scather ; /* scatter/gather */ + unsigned scathlen ; + vm_offset_t status ; + vm_offset_t chain ; + int xxx7 ; + + vm_offset_t sense_p ; + unsigned char sense_len ; + unsigned char cdb_len ; + unsigned short checksum ; + scsi_command_group_5 cdb ; + unsigned char buffer[256] ; /* space for data returned. */ + +} eccb ; + +#define NTARGETS (8) +#define NECCBS (NTARGETS+2) /* Targets + 2 to allow for temporaries. */ + /* Can be up to 64 (TRM6-2), but that entails lots of bss usage */ + +typedef struct { /* Status Block Format. See TRM6-13..19. */ + BITFIELD_8(unsigned char, + don:1, + du:1, + xxx0:1, + qf:1, + sc:1, + dover:1, + ch:1, + inti:1) ; + BITFIELD_8(unsigned char, + asa:1, /* Error in TRM6-15..16 says both asa and sns */ + sns:1, /* bit 9. Bits 8 and 10 are not mentioned. */ + xxx1:1, + ini:1, + me:1, + xxx2:1, + eca:1, + xxx3:1) ; + + unsigned char ha_status ; +# define HA_STATUS_SUCCESS 0x00 +# define HA_STATUS_HOST_ABORTED 0x04 +# define HA_STATUS_ADP_ABORTED 0x05 +# define HA_STATUS_NO_FIRM 0x08 +# define HA_STATUS_NOT_TARGET 0x0a +# define HA_STATUS_SEL_TIMEOUT 0x11 +# define HA_STATUS_OVRUN 0x12 +# define HA_STATUS_BUS_FREE 0x13 +# define HA_STATUS_PHASE_ERROR 0x14 +# define HA_STATUS_BAD_OPCODE 0x16 +# define HA_STATUS_INVALID_LINK 0x17 +# define HA_STATUS_BAD_CBLOCK 0x18 +# define HA_STATUS_DUP_CBLOCK 0x19 +# define HA_STATUS_BAD_SCATHER 0x1a +# define HA_STATUS_RSENSE_FAIL 0x1b +# define HA_STATUS_TAG_REJECT 0x1c +# define HA_STATUS_HARD_ERROR 0x20 +# define HA_STATUS_TARGET_NOATTN 0x21 +# define HA_STATUS_HOST_RESET 0x22 +# define HA_STATUS_OTHER_RESET 0x23 +# define HA_STATUS_PROG_BAD_SUM 0x80 + + scsi2_status_byte_t target_status ; + + unsigned residue ; + vm_offset_t residue_buffer ; + unsigned short add_stat_len ; + unsigned char sense_len ; + char xxx4[9] ; + unsigned char cdb[6] ; + +} status_block ; + +typedef struct { + vm_offset_t ptr ; + unsigned len ; +} scather_entry ; + +#define SCATHER_ENTRIES 128 /* TRM 6-11 */ + +struct erccbx { + target_info_t *active_target; + eccb _eccb; + status_block status ; + struct erccbx *next ; +} ; + +typedef struct erccbx erccb ; + +/* forward decls */ +int eaha_reset_scsibus(); +boolean_t eaha_probe_target(); + +/* + * State descriptor for this layer. There is one such structure + * per (enabled) board + */ +typedef struct { + watchdog_t wd; + decl_simple_lock_data(, aha_lock) + int port; /* I/O port */ + + int has_sense_info [NTARGETS]; + int sense_info_lun [NTARGETS]; + /* 1742 enhanced mode will hang if target has + * sense info and host doesn't request it (TRM6-34). + * This sometimes happens in the scsi driver. + * These flags indicate when a target has sense + * info to disgorge. + * If set, eaha_go reads and discards sense info + * before running any command except request sense. + * dph + */ + + scsi_softc_t *sc; /* HBA-indep info */ + + erccb _erccbs[NECCBS] ; /* mailboxes */ + erccb *toperccb ; + + /* This chicanery is for mapping back the phys address + of a CCB (which we get in an MBI) to its virtual */ + /* [we could use phystokv(), but it isn't standard] */ + vm_offset_t I_hold_my_phys_address; + + char host_inquiry_data[256] ; /* Check out ../scsi2.h */ + +} eaha_softc ; + +eaha_softc eaha_softc_data[NEAHA]; + +typedef eaha_softc *eaha_softc_t; + +eaha_softc_t eaha_softc_pool[NEAHA]; + +int eaha_quiet ; + +erccb *erccb_alloc( + eaha_softc *eaha) +{ + erccb *e ; + int x ; + + do { + while (eaha->toperccb == 0) ;/* Shouldn't be often or long, */ + /* BUT should use a semaphore */ + x = splbio() ; + e = eaha->toperccb ; + if (e == 0) + splx(x) ; + } while (!e) ; + eaha->toperccb = e->next ; + splx(x) ; + bzero(e,sizeof(*e)) ; + e->_eccb.status = kvtophys((vm_offset_t)&e->status) ; + return e ; +} + +void erccb_free( + eaha_softc *eaha, + erccb *e) +{ + int x ; + ASSERT ( e >= eaha->_erccbs && e < eaha->_erccbs+NECCBS) ; + x = splbio() ; + e->next = eaha->toperccb ; + eaha->toperccb = e ; + splx(x) ; +} + +void eaha_mboxout( + int port, + vm_offset_t phys) +{ + outb(MBOXOUT0(port),phys) ; + outb(MBOXOUT1(port),phys>>8) ; + outb(MBOXOUT2(port),phys>>16) ; + outb(MBOXOUT3(port),phys>>24) ; +} + +void eaha_command( /* start a command */ + int port, + erccb *_erccb) +{ + int s ; + vm_offset_t phys = kvtophys((vm_offset_t) &_erccb->_eccb) ; + while ((inb(G2STAT(port)) & 0x04)==0); /*While MBO busy. TRM6-1 */ + s = splbio() ; + eaha_mboxout(port,phys) ; + while (inb(G2STAT(port)) & 1) ; /* While adapter busy. TRM6-2 */ + outb(ATTN(port),0x40 | _erccb->active_target->target_id) ; /* TRM6-20 */ + /* (Should use target id for intitiator command) */ + splx(s) ; +} + +eaha_reset( + eaha_softc_t eaha, + boolean_t quick) +{ + /* + * Reset board and wait till done + */ + unsigned st ; + int target_id ; + int port = eaha->port ; + + /* Reset adapter, maybe with SCSIbus */ + eaha_mboxout(port, quick ? 0x00080080 : 0x00000080 ) ; /* TRM 6-43..45 */ + outb(ATTN(port), 0x10 | inb(SCSIDEF(port)) & 0x0f) ; + outb(G2CNTRL(port),0x20) ; /* TRM 4-22 */ + + do { + st = inb(G2INTST(port)) >> 4 ; + } while (st == 0) ; + /* TRM 4-22 implies that 1 should not be returned in G2INTST, but + in practise, it is. So this code takes 0 to mean non-completion. */ + + for (target_id = 0 ; target_id < NTARGETS; target_id++) + eaha->has_sense_info[target_id] = FALSE ; + +} + +void eaha_init( + eaha_softc_t eaha) +{ + /* Do nothing - I guess */ +} + +void eaha_bus_reset( + eaha_softc_t eaha) + +{ + LOG(0x1d,"bus_reset"); + + /* + * Clear bus descriptor + */ + eaha->wd.nactive = 0; + eaha_reset(eaha, TRUE); + eaha_init(eaha); + + printf("eaha: (%d) bus reset ", ++eaha->wd.reset_count); + delay(scsi_delay_after_reset); /* some targets take long to reset */ + + if (eaha->sc == 0) /* sanity */ + return; + + scsi_bus_was_reset(eaha->sc); +} + +#ifdef notdef + /* functions added to complete 1742 support, but not used. Untested. */ + + void eaha_download(port, data, len) + int port ; + char *data ; + unsigned len ; + { + /* 1744 firmware download. Not implemented. TRM6-21 */ + } + + void eaha_initscsi(data, len) + char *data ; + unsigned len ; + { + /* initialize SCSI subsystem. Presume BIOS does it. + Not implemented. TRM6-23 */ + } + + void eaha_noop() + { + /* Not implemented. TRM6-27 */ + } + + erccb *eaha_host_adapter_inquiry(eaha) /* Returns a promise */ + eaha_softc *eaha ; /* TRM6-31..33 */ + { + erccb *_erccb = erccb_alloc(eaha) ; + _erccb->_eccb.scather = (vm_offset_t) kvtophys(eaha->host_inquiry_data) ; + _erccb->_eccb.scathlen = sizeof(eaha->host_inquiry_data) ; + _erccb->_eccb.ses = 1 ; + _erccb->_eccb.command = EAHA_CMD_HOST_INQ ; + eaha_command(eaha->port,_erccb->_eccb,0) ; /* Is scsi_id used */ + return _erccb ; + } + + erccb *eaha_read_sense_info(eaha, target, lun) /* TRM 6-33..35 */ + eaha_softc *eaha ; + unsigned target, lun ; + { /* Don't think we need this because its done in scsi_alldevs.c */ + #ifdef notdef + erccb *_erccb = erccb_alloc(eaha) ; + _erccb->_eccb.command = EAHA_CMD_READ_SENS ; + _erccb->_eccb.lun = lun ; + eaha_command(eaha->port,_erccb->_eccb, target) ;/*Wrong # args*/ + return _erccb ; + #else + return 0 ; + #endif + } + + void eaha_diagnostic(eaha) + eaha_softc *eaha ; + { + /* Not implemented. TRM6-36..37 */ + } + + erccb *eaha_target_cmd(eaha, target, lun, data, len) /* TRM6-38..39 */ + eaha_softc *eaha ; + unsigned target, lun ; + char *data ; + unsigned len ; + { + erccb *_erccb = erccb_alloc(eaha) ; + _erccb->_eccb.command = EAHA_CMD_TARG_CMD ; + _erccb->_eccb.lun = lun ; + eaha_command(eaha->port,_erccb->_eccb,target);/*Wrong # args*/ + return _erccb ; + } + + erccb *eaha_init_cmd(port) /* SHOULD RETURN TOKEN. i.e. ptr to eccb */ + /* Need list of free eccbs */ + { /* to be continued,. possibly. */ + } + +#endif /* notdef */ + +target_info_t * +eaha_tgt_alloc( + eaha_softc_t eaha, + int id, + target_info_t *tgt) +{ + erccb *_erccb; + + if (tgt == 0) + tgt = scsi_slave_alloc(eaha - eaha_softc_data, id, eaha); + + _erccb = erccb_alloc(eaha) ; /* This is very dodgy */ + tgt->cmd_ptr = (char *)& _erccb->_eccb.cdb ; + tgt->dma_ptr = 0; + return tgt; +} + + +struct { + scsi_sense_data_t sns ; + unsigned char extra + [254-sizeof(scsi_sense_data_t)] ; +} eaha_xsns [NTARGETS] ;/*must be bss to be contiguous*/ + + +/* Enhanced adapter probe routine */ + +eaha_probe( + register int port, + struct bus_ctlr *ui) +{ + int unit = ui->unit; + eaha_softc_t eaha = &eaha_softc_data[unit] ; + int target_id ; + scsi_softc_t *sc ; + int s; + boolean_t did_banner = FALSE ; + struct aha_devs installed; + unsigned char my_scsi_id, my_interrupt ; + + if (unit >= NEAHA) + return(0); + + /* No interrupts yet */ + s = splbio(); + + /* + * Detect prescence of 174x in enhanced mode. Ignore HID2 and HID3 + * on the assumption that compatibility will be preserved. dph + */ + if (inb(HID0(port)) != 0x04 || inb(HID1(port)) != 0x90 || + (inb(PORTADDR(port)) & 0x80) != 0x80) { + splx(s); + return 0 ; + } + + /* Issue RESET in case this is a reboot */ + + outb(EBCTRL(port),0x04) ; /* Disable board. TRM4-12 */ + outb(PORTADDR(port),0x80) ; /* Disable standard mode ports. TRM4-13. */ + my_interrupt = inb(INTDEF(port)) & 0x07 ; + outb(INTDEF(port), my_interrupt | 0x00) ; + /* Disable interrupts. TRM4-15 */ + my_scsi_id = inb(SCSIDEF(port)) & 0x0f ; + outb(SCSIDEF(port), my_scsi_id | 0x10) ; + /* Force SCSI reset on hard reset. TRM4-16 */ + outb(G2CNTRL(port),0xe0) ; /* Reset board, clear interrupt */ + /* and set 'host ready'. */ + delay(10*10) ; /* HRST must remain set for 10us. TRM4-22 */ + /* (I don't believe the delay loop is slow enough.) */ + outb(G2CNTRL(port),0x60);/*Un-reset board, set 'host ready'. TRM4-22*/ + + printf("Adaptec 1740A/1742A/1744 enhanced mode\n"); + + /* Get host inquiry data */ + + eaha_softc_pool[unit] = eaha ; + bzero(eaha,sizeof(*eaha)) ; + eaha->port = port ; + + sc = scsi_master_alloc(unit, eaha) ; + eaha->sc = sc ; + sc->go = eaha_go ; + sc->watchdog = scsi_watchdog ; + sc->probe = eaha_probe_target ; + eaha->wd.reset = eaha_reset_scsibus ; + sc->max_dma_data = -1 ; /* Lets be optimistic */ + sc->initiator_id = my_scsi_id ; + eaha_reset(eaha,TRUE) ; + eaha->I_hold_my_phys_address = + kvtophys((vm_offset_t)&eaha->I_hold_my_phys_address) ; + { + erccb *e ; + eaha->toperccb = eaha->_erccbs ; + for (e=eaha->_erccbs; e < eaha->_erccbs+NECCBS; e++) { + e->next = e+1 ; + e->_eccb.status = + kvtophys((vm_offset_t) &e->status) ; + } + eaha->_erccbs[NECCBS-1].next = 0 ; + + } + + ui->sysdep1 = my_interrupt + 9 ; + take_ctlr_irq(ui) ; + + printf("%s%d: [port 0x%x intr ch %d] my SCSI id is %d", + ui->name, unit, port, my_interrupt + 9, my_scsi_id) ; + + outb(INTDEF(port), my_interrupt | 0x10) ; + /* Enable interrupts. TRM4-15 */ + outb(EBCTRL(port),0x01) ; /* Enable board. TRM4-12 */ + + { target_info_t *t = eaha_tgt_alloc(eaha, my_scsi_id, 0) ; + /* Haven't enabled target mode a la standard mode, because */ + /* it doesn't seem to be necessary. */ + sccpu_new_initiator(t, t) ; + } + + /* Find targets, incl. ourselves. */ + + for (target_id=0; target_id < SCSI_TARGETS; target_id++) + if (target_id != sc->initiator_id) { + scsi_cmd_test_unit_ready_t *cmd; + erccb *_erccb = erccb_alloc(eaha) ; + unsigned attempts = 0 ; +#define MAX_ATTEMPTS 2 + target_info_t temp_targ ; + + temp_targ.ior = 0 ; + temp_targ.hw_state = (char *) eaha ; + temp_targ.cmd_ptr = (char *) &_erccb->_eccb.cdb ; + temp_targ.target_id = target_id ; + temp_targ.lun = 0 ; + temp_targ.cur_cmd = SCSI_CMD_TEST_UNIT_READY; + + cmd = (scsi_cmd_test_unit_ready_t *) temp_targ.cmd_ptr; + + do { + cmd->scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY; + cmd->scsi_cmd_lun_and_lba1 = 0; /*assume 1 lun?*/ + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_ss_flags = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + eaha_go( &temp_targ, + sizeof(scsi_cmd_test_unit_ready_t),0,0); + /* ints disabled, so call isr yourself. */ + while (temp_targ.done == SCSI_RET_IN_PROGRESS) + if (inb(G2STAT(eaha->port)) & 0x02) { + eaha_quiet = 1 ; + eaha_intr(unit) ; + eaha_quiet = 0 ; + } + if (temp_targ.done == SCSI_RET_NEED_SENSE) { + /* MUST get sense info : TRM6-34 */ + if (eaha_retrieve_sense_info( + eaha, temp_targ.target_id, + temp_targ.lun) && + attempts == MAX_ATTEMPTS-1) { + + printf( + "\nTarget %d Check Condition : " + ,temp_targ.target_id) ; + scsi_print_sense_data(&eaha_xsns + [temp_targ.target_id]); + printf("\n") ; + } + } + } while (temp_targ.done != SCSI_RET_SUCCESS && + temp_targ.done != SCSI_RET_ABORTED && + ++attempts < MAX_ATTEMPTS) ; + + /* + * Recognize target which is present, whether or not + * it is ready, e.g. drive with removable media. + */ + if (temp_targ.done == SCSI_RET_SUCCESS || + temp_targ.done == SCSI_RET_NEED_SENSE && + _erccb->status.target_status.bits != 0) { /* Eureka */ + installed.tgt_luns[target_id]=1;/*Assume 1 lun?*/ + printf(", %s%d", + did_banner++ ? "" : "target(s) at ", + target_id); + + erccb_free(eaha, _erccb) ; + + /* Normally, only LUN 0 */ + if (installed.tgt_luns[target_id] != 1) + printf("(%x)", installed.tgt_luns[target_id]); + /* + * Found a target + */ + (void) eaha_tgt_alloc(eaha, target_id, 0); + /* Why discard ? */ + } else + installed.tgt_luns[target_id]=0; + } + + printf(".\n") ; + splx(s); + return 1 ; +} + +int eaha_retrieve_sense_info ( + eaha_softc_t eaha, + int tid, + int lun) +{ + int result ; + int s ; + target_info_t dummy_target ; /* Keeps eaha_command() happy. HACK */ + erccb *_erccb1 = erccb_alloc(eaha) ; + + _erccb1->active_target = &dummy_target ; + dummy_target.target_id = tid ; + _erccb1->_eccb.command = + EAHA_CMD_READ_SENS ; + _erccb1->_eccb.lun = lun ; + _erccb1->_eccb.sense_p = kvtophys((vm_offset_t) &eaha_xsns [tid]); + _erccb1->_eccb.sense_len = sizeof(eaha_xsns [tid]); + _erccb1->_eccb.ses = 1 ; + s = splbio() ; + eaha_command(eaha->port,_erccb1) ; + while ((inb(G2STAT(eaha->port)) & 0x02) == 0) ; + outb(G2CNTRL(eaha->port),0x40);/* Clear int */ + splx(s) ; + result = _erccb1->status.target_status.bits != 0 ; + erccb_free(eaha,_erccb1) ; + return result ; +} + +/* + * Start a SCSI command on a target (enhanced mode) + */ +eaha_go( + target_info_t *tgt, + int cmd_count, + int in_count, + boolean_t cmd_only)/*lint: unused*/ +{ + eaha_softc_t eaha; + int s; + erccb *_erccb; + int len; + vm_offset_t virt; + int tid = tgt->target_id ; + +#ifdef CBUS + at386_io_lock_state(); +#endif + LOG(1,"go"); + +#ifdef CBUS + at386_io_lock(MP_DEV_WAIT); +#endif + eaha = (eaha_softc_t)tgt->hw_state; + + if(eaha->has_sense_info[tid]) { + (void) eaha_retrieve_sense_info + (eaha, tid, eaha->sense_info_lun[tid]) ; + eaha->has_sense_info[tid] = FALSE ; + if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) { + bcopy(&eaha_xsns[tid],tgt->cmd_ptr,in_count) ; + tgt->done = SCSI_RET_SUCCESS; + tgt->transient_state.cmd_count = cmd_count; + tgt->transient_state.out_count = 0; + tgt->transient_state.in_count = in_count; + /* Fake up interrupt */ + /* Highlights from eaha_initiator_intr(), */ + /* ignoring errors */ + if (tgt->ior) + (*tgt->dev_ops->restart)( tgt, TRUE); +#ifdef CBUS + at386_io_unlock(); +#endif + return ; + } + } + +/* XXX delay the handling of the ccb till later */ + _erccb = (erccb *) + ((unsigned)tgt->cmd_ptr - (unsigned) &((erccb *) 0)->_eccb.cdb); + /* Tell *rccb about target, eg. id ? */ + _erccb->active_target = tgt; + + /* + * We can do real DMA. + */ +/* tgt->transient_state.copy_count = 0; unused */ +/* tgt->transient_state.dma_offset = 0; unused */ + + tgt->transient_state.cmd_count = cmd_count; + + if ((tgt->cur_cmd == SCSI_CMD_WRITE) || + (tgt->cur_cmd == SCSI_CMD_LONG_WRITE)){ + io_req_t ior = tgt->ior; + register int len = ior->io_count; + + tgt->transient_state.out_count = len; + + /* How do we avoid leaks here ? Trust the board + will do zero-padding, for now. XXX CHECKME */ +#if 0 + if (len < tgt->block_size) { + bzero(to + len, tgt->block_size - len); + len = tgt->block_size; + tgt->transient_state.out_count = len; + } +#endif + } else { + tgt->transient_state.out_count = 0; + } + + /* See above for in_count < block_size */ + tgt->transient_state.in_count = in_count; + + /* + * Setup CCB state + */ + tgt->done = SCSI_RET_IN_PROGRESS; + + switch (tgt->cur_cmd) { + case SCSI_CMD_READ: + case SCSI_CMD_LONG_READ: + LOG(9,"readop"); + virt = (vm_offset_t)tgt->ior->io_data; + len = tgt->transient_state.in_count; + break; + case SCSI_CMD_WRITE: + case SCSI_CMD_LONG_WRITE: + LOG(0x1a,"writeop"); + virt = (vm_offset_t)tgt->ior->io_data; + len = tgt->transient_state.out_count; + break; + case SCSI_CMD_INQUIRY: + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_MODE_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_READ_CAPACITY: + case SCSI_CMD_READ_BLOCK_LIMITS: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + virt = (vm_offset_t)tgt->cmd_ptr; + len = tgt->transient_state.in_count; + break; + case SCSI_CMD_MODE_SELECT: + case SCSI_CMD_REASSIGN_BLOCKS: + case SCSI_CMD_FORMAT_UNIT: + tgt->transient_state.cmd_count = sizeof(scsi_command_group_0); + len = + tgt->transient_state.out_count = cmd_count - sizeof(scsi_command_group_0); + virt = (vm_offset_t)tgt->cmd_ptr+sizeof(scsi_command_group_0); + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + break; + default: + LOG(0x1c,"cmdop"); + LOG(0x80+tgt->cur_cmd,0); + virt = 0; + len = 0; + } + + eaha_prepare_rccb(tgt, _erccb, virt, len); + + _erccb->_eccb.lun = tgt->lun; + + /* + * XXX here and everywhere, locks! + */ + s = splbio(); + + simple_lock(&eaha->aha_lock); + if (eaha->wd.nactive++ == 0) + eaha->wd.watchdog_state = SCSI_WD_ACTIVE; + simple_unlock(&eaha->aha_lock); + + LOG(3,"enqueue"); + + eaha_command(eaha->port, _erccb) ; + + splx(s); +#ifdef CBUS + at386_io_unlock(); +#endif +} + +eaha_prepare_rccb( + target_info_t *tgt, + erccb *_erccb, + vm_offset_t virt, + vm_size_t len) +{ + _erccb->_eccb.cdb_len = tgt->transient_state.cmd_count; + + _erccb->_eccb.command = EAHA_CMD_INIT_CMD;/* default common case */ + + if (virt == 0) { + /* no xfers */ + _erccb->_eccb.scather = 0 ; + _erccb->_eccb.scathlen = 0 ; + _erccb->_eccb.sg = 0 ; + } else { + /* messy xfer */ + scather_entry *seglist; + vm_size_t l1, off; + + _erccb->_eccb.sg = 1 ; + + if (tgt->dma_ptr == 0) + eaha_alloc_segment_list(tgt); + seglist = (scather_entry *) tgt->dma_ptr; + + _erccb->_eccb.scather = kvtophys((vm_offset_t) seglist); + + l1 = MACHINE_PGBYTES - (virt & (MACHINE_PGBYTES - 1)); + if (l1 > len) + l1 = len ; + + off = 1;/* now #pages */ + while (1) { + seglist->ptr = kvtophys(virt) ; + seglist->len = l1 ; + seglist++; + + if (len <= l1) + break ; + len-= l1 ; + virt += l1; off++; + + l1 = (len > MACHINE_PGBYTES) ? MACHINE_PGBYTES : len; + } + _erccb->_eccb.scathlen = off * sizeof(*seglist); + } +} + +/* + * Allocate dynamically segment lists to + * targets (for scatter/gather) + */ +vm_offset_t eaha_seglist_next = 0, eaha_seglist_end = 0 ; +#define EALLOC_SIZE (SCATHER_ENTRIES * sizeof(scather_entry)) + +eaha_alloc_segment_list( + target_info_t *tgt) +{ + +/* XXX locking */ +/* ? Can't spl() for unknown duration */ + if ((eaha_seglist_next + EALLOC_SIZE) > eaha_seglist_end) { + (void)kmem_alloc_wired(kernel_map,&eaha_seglist_next,PAGE_SIZE); + eaha_seglist_end = eaha_seglist_next + PAGE_SIZE; + } + tgt->dma_ptr = (char *)eaha_seglist_next; + eaha_seglist_next += EALLOC_SIZE; +/* XXX locking */ +} + +/* + * + * shameless copy from above + */ +eaha_reset_scsibus( + register eaha_softc_t eaha) +{ + register target_info_t *tgt; + register port = eaha->port; + register int i; + + for (i = 0; i < NECCBS; i++) { + tgt = eaha->_erccbs[i].active_target; + if (/*scsi_debug &&*/ tgt) + printf("Target %d was active, cmd x%x in x%x out x%x\n", + tgt->target_id, tgt->cur_cmd, + tgt->transient_state.in_count, + tgt->transient_state.out_count); + } + eaha_reset(eaha, FALSE); + delay(35); + /* no interrupt will come */ + eaha_bus_reset(eaha); +} + +boolean_t +eaha_probe_target( + target_info_t *tgt, + io_req_t ior) +{ + eaha_softc_t eaha = eaha_softc_pool[tgt->masterno]; + boolean_t newlywed; + + newlywed = (tgt->cmd_ptr == 0); + if (newlywed) { + /* desc was allocated afresh */ + (void) eaha_tgt_alloc(eaha,tgt->target_id, tgt); + } + + if (scsi_inquiry(tgt, SCSI_INQ_STD_DATA) == SCSI_RET_DEVICE_DOWN) + return FALSE; + + tgt->flags = TGT_ALIVE; + return TRUE; +} + + +/* + * Interrupt routine (enhanced mode) + * Take interrupts from the board + * + * Implementation: + * TBD + */ +eaha_intr( + int unit) +{ + register eaha_softc_t eaha; + register port; + unsigned g2intst, g2stat, g2stat2 ; + vm_offset_t mbi ; + erccb *_erccb ; + status_block *status ; + +#if MAPPABLE + extern boolean_t rz_use_mapped_interface; + + if (rz_use_mapped_interface) { + EAHA_intr(unit); + return ; + } +#endif /*MAPPABLE*/ + + eaha = eaha_softc_pool[unit]; + port = eaha->port; + + LOG(5,"\n\tintr"); +gotintr: + /* collect ephemeral information */ + + g2intst = inb(G2INTST(port)) ; /* See TRM4-22..23 */ + g2stat = inb(G2STAT(port)) ; /*lint:set,not used*/ + g2stat2 = inb(G2STAT2(port)) ; /*lint:set,not used*/ + mbi = (vm_offset_t) inb(MBOXIN0(port)) + (inb(MBOXIN1(port))<<8) + + (inb(MBOXIN2(port))<<16) + (inb(MBOXIN3(port))<<24) ; + + /* we got an interrupt allright */ + if (eaha->wd.nactive) + eaha->wd.watchdog_state = SCSI_WD_ACTIVE; + + outb(G2CNTRL(port),0x40) ; /* Clear EISA interrupt */ + + switch(g2intst>>4) { + case 0x07 : /* hardware error ? */ + case 0x0a : /* immediate command complete - don't expect */ + case 0x0e : /* ditto with failure */ + default : + printf( "aha%d: Bogus status (x%x) in MBI\n", + unit, mbi); + gimmeabreak() ; /* Any of above is disaster */ + break; + + case 0x0d : /* Asynchronous event TRM6-41 */ + if ((g2intst & 0x0f) == (inb(SCSIDEF(eaha->port)) & 0x0f)) + eaha_reset_scsibus(eaha) ; + else + eaha_target_intr(eaha, mbi, g2intst & 0x0f); + break; + + case 0x0c : /* ccb complete with error */ + case 0x01 : /* ccb completed with success */ + case 0x05 : /* ccb complete with success after retry */ + + _erccb = (erccb *) + ( ((vm_offset_t)&eaha->I_hold_my_phys_address) + + (mbi - eaha->I_hold_my_phys_address) - + (vm_offset_t)&(((erccb *)0)->_eccb) ) ; + /* That ain't necessary. As kernel (must be) */ + /* contiguous, only need delta to translate */ + + status = &_erccb->status ; + +#ifdef NOTDEF + if (!eaha_quiet && (!status->don || status->qf || + status->sc || status->dover || + status->ini || status->me)) { + printf("\nccb complete error G2INTST=%02X\n", + g2intst) ; + DUMP(*_erccb) ; + gimmeabreak() ; + } +#endif + + eaha_initiator_intr(eaha, _erccb); + break; + } + + /* See if more work ready */ + if (inb(G2STAT(port)) & 0x02) { + LOG(7,"\n\tre-intr"); + goto gotintr; + } +} + +/* + * The interrupt routine turns to one of these two + * functions, depending on the incoming mbi's role + */ +eaha_target_intr( + eaha_softc_t eaha, + unsigned int mbi, + unsigned int peer) +{ + target_info_t *initiator; /* this is the caller */ + target_info_t *self; /* this is us */ + int len; + + self = eaha->sc->target[eaha->sc->initiator_id]; + + initiator = eaha->sc->target[peer]; + + /* ..but initiators are not required to answer to our inquiry */ + if (initiator == 0) { + /* allocate */ + initiator = eaha_tgt_alloc(eaha, peer, 0); + + /* We do not know here wether the host was down when + we inquired, or it refused the connection. Leave + the decision on how we will talk to it to higher + level code */ + LOG(0xC, "new_initiator"); + sccpu_new_initiator(self, initiator); + /* Bug fix: was (aha->sc, self, initiator); dph */ + } + + /* The right thing to do would be build an ior + and call the self->dev_ops->strategy routine, + but we cannot allocate it at interrupt level. + Also note that we are now disconnected from the + initiator, no way to do anything else with it + but reconnect and do what it wants us to do */ + + /* obviously, this needs both spl and MP protection */ + self->dev_info.cpu.req_pending = TRUE; + self->dev_info.cpu.req_id = peer ; + self->dev_info.cpu.req_lun = (mbi>>24) & 0x07 ; + self->dev_info.cpu.req_cmd = + (mbi & 0x80000000) ? SCSI_CMD_SEND: SCSI_CMD_RECEIVE; + len = mbi & 0x00ffffff ; + + self->dev_info.cpu.req_len = len; + + LOG(0xB,"tgt-mode-restart"); + (*self->dev_ops->restart)( self, FALSE); + + /* The call above has either prepared the data, + placing an ior on self, or it handled it some + other way */ + if (self->ior == 0) + return; /* I guess we'll do it later */ + + { + erccb *_erccb ; + + _erccb = erccb_alloc(eaha) ; + _erccb->active_target = initiator; + _erccb->_eccb.command = EAHA_CMD_TARG_CMD ; + _erccb->_eccb.ses = 1 ; + _erccb->_eccb.dir = (self->cur_cmd == SCSI_CMD_SEND) ? 1 : 0 ; + + eaha_prepare_rccb(initiator, _erccb, + (vm_offset_t)self->ior->io_data, self->ior->io_count); + _erccb->_eccb.lun = initiator->lun; + + simple_lock(&eaha->aha_lock); + if (eaha->wd.nactive++ == 0) + eaha->wd.watchdog_state = SCSI_WD_ACTIVE; + simple_unlock(&eaha->aha_lock); + + eaha_command(eaha->port, _erccb); + } +} + +eaha_initiator_intr( + eaha_softc_t eaha, + erccb *_erccb) +{ + scsi2_status_byte_t status; + target_info_t *tgt; + + tgt = _erccb->active_target; + _erccb->active_target = 0; + + /* shortcut (sic!) */ + if (_erccb->status.ha_status == HA_STATUS_SUCCESS) + goto allok; + + switch (_erccb->status.ha_status) { /* TRM6-17 */ + case HA_STATUS_SUCCESS : +allok: + status = _erccb->status.target_status ; + if (status.st.scsi_status_code != SCSI_ST_GOOD) { + scsi_error(tgt, SCSI_ERR_STATUS, status.bits, 0); + tgt->done = (status.st.scsi_status_code == SCSI_ST_BUSY) ? + SCSI_RET_RETRY : SCSI_RET_NEED_SENSE; + } else + tgt->done = SCSI_RET_SUCCESS; + break; + + case HA_STATUS_SEL_TIMEOUT : + if (tgt->flags & TGT_FULLY_PROBED) + tgt->flags = 0; /* went offline */ + tgt->done = SCSI_RET_DEVICE_DOWN; + break; + + case HA_STATUS_OVRUN : + /* BUT we don't know if this is an underrun. + It is ok if we get less data than we asked + for, in a number of cases. Most boards do not + seem to generate this anyways, but some do. */ + { register int cmd = tgt->cur_cmd; + switch (cmd) { + case SCSI_CMD_INQUIRY: + case SCSI_CMD_REQUEST_SENSE: + case SCSI_CMD_RECEIVE_DIAG_RESULTS: + case SCSI_CMD_MODE_SENSE: + if (_erccb->status.du) /*Ignore underrun only*/ + break; + default: + printf("eaha: U/OVRUN on scsi command x%x\n",cmd); + gimmeabreak(); + } + } + goto allok; + case HA_STATUS_BUS_FREE : + printf("aha: bad disconnect\n"); + tgt->done = SCSI_RET_ABORTED; + break; + case HA_STATUS_PHASE_ERROR : + /* we'll get an interrupt soon */ + printf("aha: bad PHASE sequencing\n"); + tgt->done = SCSI_RET_ABORTED; + break; + case HA_STATUS_BAD_OPCODE : +printf("aha: BADCCB\n");gimmeabreak(); + tgt->done = SCSI_RET_RETRY; + break; + + case HA_STATUS_HOST_ABORTED : + case HA_STATUS_ADP_ABORTED : + case HA_STATUS_NO_FIRM : + case HA_STATUS_NOT_TARGET : + case HA_STATUS_INVALID_LINK : /* These aren't expected. */ + case HA_STATUS_BAD_CBLOCK : + case HA_STATUS_DUP_CBLOCK : + case HA_STATUS_BAD_SCATHER : + case HA_STATUS_RSENSE_FAIL : + case HA_STATUS_TAG_REJECT : + case HA_STATUS_HARD_ERROR : + case HA_STATUS_TARGET_NOATTN : + case HA_STATUS_HOST_RESET : + case HA_STATUS_OTHER_RESET : + case HA_STATUS_PROG_BAD_SUM : + default : + printf("aha: bad ha_status (x%x)\n", _erccb->status.ha_status); + tgt->done = SCSI_RET_ABORTED; + break; + } + + eaha->has_sense_info [tgt->target_id] = + (tgt->done == SCSI_RET_NEED_SENSE) ; + if (eaha->has_sense_info [tgt->target_id]) + eaha->sense_info_lun [tgt->target_id] = tgt->lun ; + + LOG(8,"end"); + + simple_lock(&eaha->aha_lock); + if (eaha->wd.nactive-- == 1) + eaha->wd.watchdog_state = SCSI_WD_INACTIVE; + simple_unlock(&eaha->aha_lock); + + if (tgt->ior) { + LOG(0xA,"ops->restart"); + (*tgt->dev_ops->restart)( tgt, TRUE); + } + + return FALSE;/*lint: Always returns FALSE. ignored. */ +} + +#endif /* NEAHA > 0 */ diff --git a/scsi/adapters/scsi_dma.h b/scsi/adapters/scsi_dma.h new file mode 100644 index 0000000..9401a16 --- /dev/null +++ b/scsi/adapters/scsi_dma.h @@ -0,0 +1,150 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: scsi_dma.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 7/91 + * + * DMA operations that an HBA driver might invoke. + * + */ + +/* + * This defines much more than usually needed, mainly + * to cover for the case of no DMA at all and/or only + * DMA from/to a specialized buffer ( which means the + * CPU has to copy data into/outof it ). + */ + +typedef struct { + opaque_t (*init)( + int dev_unit, + vm_offset_t base, + int *dma_bsizep, + boolean_t *oddbp); + + void (*new_target)( + opaque_t dma_state, + target_info_t *tgt); + + void (*map)( + opaque_t dma_state, + target_info_t *tgt); + + int (*start_cmd)( + opaque_t dma_state, + target_info_t *tgt); + + void (*end_xfer)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + void (*end_cmd)( + opaque_t dma_state, + target_info_t *tgt, + io_req_t ior); + + int (*start_datain)( + opaque_t dma_state, + target_info_t *tgt); + + int (*start_msgin)( + opaque_t dma_state, + target_info_t *tgt); + + void (*end_msgin)( + opaque_t dma_state, + target_info_t *tgt); + + boolean_t (*start_dataout)( + opaque_t dma_state, + target_info_t *tgt, + volatile unsigned *regp, + unsigned value, + unsigned char *prefetch_count); + + int (*restart_datain_1)( + opaque_t dma_state, + target_info_t *tgt); + + int (*restart_datain_2)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + void (*restart_datain_3)( + opaque_t dma_state, + target_info_t *tgt); + + int (*restart_dataout_1)( + opaque_t dma_state, + target_info_t *tgt); + + int (*restart_dataout_2)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + int (*restart_dataout_3)( + opaque_t dma_state, + target_info_t *tgt, + volatile unsigned *regp); + + void (*restart_dataout_4)( + opaque_t dma_state, + target_info_t *tgt); + + boolean_t (*disconn_1)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + boolean_t (*disconn_2)( + opaque_t dma_state, + target_info_t *tgt); + + boolean_t (*disconn_3)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + boolean_t (*disconn_4)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + boolean_t (*disconn_5)( + opaque_t dma_state, + target_info_t *tgt, + int xferred); + + void (*disconn_callback)( + opaque_t dma_state, + target_info_t *tgt); + +} scsi_dma_ops_t; + diff --git a/scsi/adapters/scsi_user_dma.c b/scsi/adapters/scsi_user_dma.c new file mode 100644 index 0000000..5fb98d6 --- /dev/null +++ b/scsi/adapters/scsi_user_dma.c @@ -0,0 +1,171 @@ +/* + * 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_user_dma.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Mach 2.5 compat file, to handle case of DMA to user space + * [e.g. fsck and other raw device accesses] + */ + +#ifdef MACH_KERNEL +/* We do not need this in 3.0 */ +#else /*MACH_KERNEL*/ + +#include <mach/std_types.h> +#include <scsi/adapters/scsi_user_dma.h> + +#include <kern/assert.h> + +#include <vm/vm_kern.h> +#include <mach/vm_param.h> /* round_page() */ + +/* bp -> pmap */ +#include <sys/buf.h> +#include <sys/proc.h> + +/* + * Initialization, called once per device + */ +fdma_init(fdma, size) + fdma_t fdma; + vm_size_t size; +{ + vm_offset_t addr; + + size = round_page(size); + addr = kmem_alloc_pageable(kernel_map, size); + if (addr == 0) panic("fdma_init"); + + fdma->kernel_virtual = addr; + fdma->max_data = size; + fdma->user_virtual = -1; + +} + +/* + * Remap a buffer from user space to kernel space. + * Note that physio() has already validated + * and wired the user's address range. + */ +fdma_map(fdma, bp) + fdma_t fdma; + struct buf *bp; +{ + pmap_t pmap; + vm_offset_t user_addr; + vm_size_t size; + vm_offset_t kernel_addr; + vm_offset_t off; + vm_prot_t prot; + + /* + * If this is not to user space, or no data xfer is + * involved, no need to do anything. + */ + user_addr = (vm_offset_t)bp->b_un.b_addr; + if (!(bp->b_flags & B_PHYS) || (user_addr == 0)) { + fdma->user_virtual = -1; + return; + } + /* + * We are going to clobber the buffer pointer, so + * remember what it was to restore it later. + */ + fdma->user_virtual = user_addr; + + /* + * Account for initial offset into phys page + */ + off = user_addr - trunc_page(user_addr); + + /* + * Check xfer size makes sense, note how many pages we'll remap + */ + size = bp->b_bcount + off; + assert((size <= fdma->max_data)); + fdma->xfer_size_rnd = round_page(size); + + pmap = bp->b_proc->task->map->pmap; + + /* + * Use minimal protection possible + */ + prot = VM_PROT_READ; + if (bp->b_flags & B_READ) + prot |= VM_PROT_WRITE; + + /* + * Loop through all phys pages, taking them from the + * user pmap (they are wired) and inserting them into + * the kernel pmap. + */ + user_addr -= off; + kernel_addr = fdma->kernel_virtual; + bp->b_un.b_addr = (char *)kernel_addr + off; + + for (size = fdma->xfer_size_rnd; size; size -= PAGE_SIZE) { + register vm_offset_t phys; + + phys = pmap_extract(pmap, user_addr); + pmap_enter(kernel_pmap, kernel_addr, phys, prot, TRUE); + user_addr += PAGE_SIZE; + kernel_addr += PAGE_SIZE; + } +} + +/* + * Called at end of xfer, to restore the buffer + */ +fdma_unmap(fdma, bp) + fdma_t fdma; + struct buf *bp; +{ + register vm_offset_t end_addr; + + /* + * Check we actually did remap it + */ + if (fdma->user_virtual == -1) + return; + + /* + * Restore the buffer + */ + bp->b_un.b_addr = (char *)fdma->user_virtual; + fdma->user_virtual = -1; + + /* + * Eliminate the mapping, pmap module might mess up + * the pv list otherwise. Some might actually tolerate it. + */ + end_addr = fdma->kernel_virtual + fdma->xfer_size_rnd; + pmap_remove(kernel_pmap, fdma->kernel_virtual, end_addr); + +} + +#endif /*MACH_KERNEL*/ diff --git a/scsi/adapters/scsi_user_dma.h b/scsi/adapters/scsi_user_dma.h new file mode 100644 index 0000000..ff2682c --- /dev/null +++ b/scsi/adapters/scsi_user_dma.h @@ -0,0 +1,47 @@ +/* + * 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 + * 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 the + * rights to redistribute these changes. + */ +/* + * File: scsi_user_dma.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Defines for Mach 2.5 compat, user-space DMA routines + */ + +/* There is one such structure per I/O device + that needs to xfer data to/from user space */ + +typedef struct fdma { + vm_offset_t kernel_virtual; + vm_size_t max_data; + vm_offset_t user_virtual; + int xfer_size_rnd; +} *fdma_t; + +extern int + fdma_init(/* fdma_t, vm_size_t */), + fdma_map(/* fdma_t, struct buf* */), + fdma_unmap(/* fdma_t, struct buf* */); diff --git a/scsi/compat_30.h b/scsi/compat_30.h new file mode 100644 index 0000000..988aed7 --- /dev/null +++ b/scsi/compat_30.h @@ -0,0 +1,163 @@ +/* + * 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: compat_30.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Compatibility defs to retrofit Mach 3.0 drivers + * into Mach 2.6. + */ + +#ifndef _SCSI_COMPAT_30_ +#define _SCSI_COMPAT_30_ + +#include <kern/assert.h> + +#ifdef MACH_KERNEL +/* + * Mach 3.0 compiles with these definitions + */ + +#include <device/param.h> +#include <device/io_req.h> +#include <device/device_types.h> +#include <device/disk_status.h> + +/* + * Scratch temporary in io_req structure (for error handling) + */ +#define io_temporary io_error + +#else /*MACH_KERNEL*/ +/* + * Mach 2.x compiles with these definitions + */ + +/* ??? */ +typedef int dev_mode_t; +typedef int *dev_status_t; /* Variable-length array of integers */ +/* ??? */ + +/* Buffer structures */ + +typedef int io_return_t; + +#include <sys/param.h> +#include <sys/buf.h> + +#define io_req buf +typedef struct buf *io_req_t; + +#define io_req_alloc(ior,size) ior = geteblk(size) +#define io_req_free(ior) brelse(ior) + +/* + * Redefine fields for drivers using new names + */ +#define io_op b_flags +#define io_count b_bcount +#define io_error b_error +#define io_unit b_dev +#define io_recnum b_blkno +#define io_residual b_resid +#define io_data b_un.b_addr +#define io_done b_iodone + +/* + * Redefine fields for driver request list heads, using new names. + */ +#define io_next av_forw +#define io_prev av_back +/*#define io_next b_actf*/ +/*#define io_prev b_actl*/ +#define io_link b_forw +#define io_rlink b_back +/*#define io_count b_active*/ +/*#define io_residual b_errcnt*/ +#define io_alloc_size b_bufsize + +/* + * Scratch temporary in io_req structure (for error handling) + */ +#define io_temporary b_pfcent + +/* + * Redefine flags + */ +#define IO_WRITE B_WRITE +#define IO_READ B_READ +#define IO_OPEN B_OPEN +#define IO_DONE B_DONE +#define IO_ERROR B_ERROR +#define IO_BUSY B_BUSY +#define IO_WANTED B_WANTED +#define IO_BAD B_BAD +#define IO_CALL B_CALL +#define IO_INTERNAL B_MD1 + +#define IO_SPARE_START B_MD1 + +#include <sys/disklabel.h> + +/* Error codes */ + +#include <sys/errno.h> + +#define D_SUCCESS ESUCCESS +#define D_IO_ERROR EIO +#define D_NO_SUCH_DEVICE ENXIO +#define D_INVALID_SIZE EINVAL +#define D_ALREADY_OPEN EBUSY +#define D_INVALID_OPERATION EINVAL +#define D_NO_MEMORY ENOMEM +#define D_WOULD_BLOCK EWOULDBLOCK +#define D_DEVICE_DOWN EIO +#define D_READ_ONLY EROFS + +/* + * Debugging support + */ +#define db_printf kdbprintf +#define db_printsym(s,m) kdbpsymoff(s,1,"") + +/* + * Miscellaneous utils + */ + +#define check_memory(addr,dow) ((dow) ? wbadaddr(addr,4) : badaddr(addr,4)) + +#include <sys/kernel.h> /* for hz */ +#include <scsi/adapters/scsi_user_dma.h> + +#ifdef DECSTATION +#include <mach/mips/vm_param.h> /* for page size */ +#define ULTRIX_COMPAT 1 /* support for rzdisk disk formatter */ +#endif /*DECSTATION*/ + +#endif /*MACH_KERNEL*/ + +#endif /*_SCSI_COMPAT_30_*/ diff --git a/scsi/disk_label.c b/scsi/disk_label.c new file mode 100644 index 0000000..ab20378 --- /dev/null +++ b/scsi/disk_label.c @@ -0,0 +1,692 @@ +/* + * Copyright (c) 1996 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Kevin T. Van Maren, University of Utah CSL + */ + +/* + * 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. + */ + +/* + * Copyright (c) 1994 Shantanu Goel + * 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. + * + * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + */ + +/* This file contains the partition code that is used by the Mach + * device drivers (ide & scsi). */ + +#include <scsi/compat_30.h> +#include <sys/types.h> + +#include <scsi/rz_labels.h> +#include <i386at/disk.h> /* combine & rename these... */ + +#define SECTOR_SIZE 512 /* BAD!!! */ + +#define DSLICE(dev) ((dev >> 4) & 0x3f) +#define DPART(dev) (dev & 0xf) + +/* note: 0 will supress ALL output; + 1 is 'normal' output; 2 is verbose; 3 is Very verbose */ +#define PARTITION_DEBUG 1 + +#define min(x,y) (x<y?x:y) + +/* + * Label that is filled in with extra info + */ +struct disklabel default_label = +{ + DISKMAGIC, DTYPE_SCSI, 0, + "SCSI", "", + DEV_BSIZE, 1, 1, 1, 1, 1, 0, 0, 0, + 3600, 1, 1, 1, 0, 0, 0, + {0,}, {0,}, + DISKMAGIC, 0, + 8, 8192, 8192, + {{ -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }, + { -1, 0, 1024, FS_BSDFFS, 8, 3 }} +}; + + + +/* the device driver calls this just to save some info it got from the HW */ +/* This is a bad holdover from the disklabel days, and needs to go */ +fudge_bsd_label(struct disklabel *label, int type, int total_secs, int heads, int sectors, int sectorsize, int n) +{ + *label=default_label; + + label->d_ncylinders = total_secs/(heads*sectors); + label->d_ntracks = heads; + label->d_nsectors = sectors; + + label->d_secpercyl = heads*sectors; + label->d_secperunit = total_secs; + + /* this is never used, but ... */ + label->d_partitions[MAXPARTITIONS].p_offset = 0; + label->d_partitions[MAXPARTITIONS].p_size = total_secs; + + /* ?? + */ + label->d_secsize = sectorsize; + label->d_type = type; + label->d_subtype = 0xa; /* ??? */ + + label->d_npartitions = n; /* up to 'c' */ + label->d_checksum = 0; + + /* should do a checksum on it now */ +} + + + +/* This is placed here to + a. provide comparability with existing servers + b. allow the use of FreeBSD-style slices to access ANY disk partition + c. provide an easy migration path to lites-based partition code + by only passing the drive name to get the entire disk (sd0). + + This will be called by every routine that needs to access partition info + based on a device number. It is slower than the old method of indexing + into a disklabel, but is more flexible, and reasonably fast in the (future) + case where Lites will access the whole disk. An array of disklabels + could have been used, but this is more compact and general. The underlying + structure does not limit it to 2-levels, but keeping the kernel interface + simple does. */ + + +/* this code and data structure based on conversation with Bryan Ford */ +/* Note: this is called ON EVERY read or write. It makes sense to + optimize this for the common case. Hopefully the common case + will become the '0,0' case, as partitioning is moved out of the + kernel. (Downside is kernel can't protect filesystems from each other). + It is slower than indexing into a 1-D array, but not much. */ + +struct diskpart *lookup_part(struct diskpart *array, int dev_number) +{ +/* Note: 10 bit encoding to get partitions 0-15 (0,a-h typically), and slices + * 0-63 + */ + + int slice = DSLICE(dev_number); + int part = DPART(dev_number); + struct diskpart *s; + + if (slice == 0) /* compatability slice */ + { + if (part == 0) /* whole disk */ + return &array[0]; + + if (array[0].type == DISKPART_DOS) + { + int i; + for (i = 0; i < array[0].nsubs; i++) + { + s = &array[0].subs[i]; + if ( s->type == DISKPART_BSD + || s->type == DISKPART_VTOC) + { + if (part > s->nsubs) + return 0; + return (&s->subs[part-1]); + } + } + } + + if (part > array[0].nsubs) + return 0; + return(&array[0].subs[part-1]); + } + else + { + if ( array[0].type != DISKPART_DOS + || slice > array[0].nsubs) + return 0; + + s = &array[0].subs[slice-1]; + + if (part == 0) /* whole slice */ + return (s); + if (part > s->nsubs) + return 0; + + return (&s->subs[part-1]); + } +} + + + + +static inline void fill_array(struct diskpart *array, int start, int size, + struct diskpart *subs, int nsubs, short type, short fsys) +{ + array->start=start; + array->size=size; + array->subs=subs; + array->nsubs=nsubs; + array->type=type; + array->fsys=fsys; +#if (PARTITION_DEBUG > 2) + printf("fill: type %d:%d, start %d, size %d, %d parts\n",type,fsys, + start,size,nsubs); +#endif +} + + + + +void print_array(struct diskpart *array, int level) +{ + int i,j; + struct diskpart *subs; + +#if (PARTITION_DEBUG) + subs=array[0].subs; + + for (i=0;i<array[0].nsubs;i++) { + for (j=0;j<level;j++) + printf(" "); + printf("%c: %d, %d, %d, %d (%d subparts)\n",'a'+i, + subs[i].start, subs[i].size, subs[i].fsys, + subs[i].type, subs[i].nsubs); + if (subs[i].nsubs>0) + print_array(&subs[i], level+1); + } +#endif +} + + + +/* individual routines to find the drive labels. + There needs to be a function for every different method for partitioning + much of the following code is derived from the SCSI/IDE drivers */ + +int get_dos(struct diskpart *array, char *buff, int start, + void *driver_info, int (*bottom_read_fun)(), + char *name, int max_part) +{ + + bios_label_t *mp; + struct bios_partition_info *pp; + + int count, i, j; + int pstart, psize; + int ext=-1, mystart=start, mybase; + int first=1; + + /* note: start is added, although a start != 0 is meaningless + to DOS and anything else... */ + + /* check the boot sector for a partition table. */ + (*bottom_read_fun)(driver_info, start, buff); /* always in sector 0 */ + + /* + * Check for valid partition table. + */ + mp = (bios_label_t *)&buff[BIOS_LABEL_BYTE_OFFSET]; + if (mp->magic != BIOS_LABEL_MAGIC) { +#if (PARTITION_DEBUG>1) + printf("%s invalid partition table\n", name); +#endif + return(0); /* didn't add any partitions */ + } +#if (PARTITION_DEBUG>1) + printf("DOS partition table found\n"); +#endif + + count=min(4,max_part); /* always 4 (primary) partitions */ +#if (PARTITION_DEBUG) + if (count<4) printf("More partitions than space!\n"); +#endif + + + /* fill the next 4 entries in the array */ + for (i=0, pp=(struct bios_partition_info *)mp->partitions; + i<count; i++,pp++) { + + fill_array(&array[i], pp->offset, pp->n_sectors, NULL, 0, + DISKPART_NONE, pp->systid); + if ((pp->systid == DOS_EXTENDED) &&(ext<0)) { + mystart+=pp->offset; + ext=i; + } + } + + /* if there is an extended partition, find all the logical partitions */ + /* note: logical start at '5' (extended is one of the numbered 1-4) */ + + /* logical partitions 'should' be nested inside the primary, but + then it would be impossible to NAME a disklabel inside a logical + partition, which would be nice to do */ +#if (PARTITION_DEBUG>1) + if (ext>=0) + printf("extended partition found: %d\n",ext); +#endif 0 + + while (ext>=0) { + pp = &(((struct bios_partition_info *)mp->partitions)[ext]); + + /* read the EXTENDED partition table */ + if (first) { + mybase=mystart; + first=0; + } else { + mybase=mystart+pp->offset; + } + + (*bottom_read_fun)(driver_info, mybase, buff); + + if (mp->magic != BIOS_LABEL_MAGIC) { +#if (PARTITION_DEBUG>1) + printf("%s invalid expanded magic\n", name); +#endif + return(count);/*don't add any more partitions*/ + } + + /* just in case more than one partition is there...*/ + /* search the EXTENDED partition table */ + ext=-1; + for (j=0,pp=(struct bios_partition_info *)mp->partitions; + j<4; j++,pp++) { + + if (pp->systid && (pp->systid!=DOS_EXTENDED)) { + if (count<max_part) { + fill_array(&array[count], + mybase +pp->offset, + pp->n_sectors, NULL, 0, DISKPART_NONE, + pp->systid); + count++; } + else { +#if (PARTITION_DEBUG) + printf("More partitions than space!\n"); +#endif + return(count); + } + } else if ((ext<0) &&(pp->systid==DOS_EXTENDED)) { + ext=j; + /* recursivly search the chain here */ + } + } + } +#if (PARTITION_DEBUG>1) + printf("%d dos partitions\n",count); +#endif 0 + return(count); /* number dos partitions found */ + +} + + + +/* this should work on the bare drive, or in a dos partition */ +int get_disklabel(struct diskpart *array, char *buff, int start, + void *driver_info, int (*bottom_read_fun)(), + char *name, int max_part) +{ + struct disklabel *dlp; + int mybase = start + (512 * LBLLOC)/SECTOR_SIZE, i; + int count; + + (*bottom_read_fun)(driver_info, mybase, buff); + + dlp = (struct disklabel *)buff; + if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC) { +#if (PARTITION_DEBUG>1) + printf("%s no BSD label found\n",name); +#endif + return(0); /* no partitions added */ + } +#if (PARTITION_DEBUG>1) + printf(" BSD LABEL\n"); +#endif 0 + /* note: BSD disklabel offsets are from start of DRIVE -- uuggh */ + + count=min(8,max_part); /* always 8 in a disklabel */ +#if (PARTITION_DEBUG) + if (count<8) printf("More partitions than space!\n"); +#endif + /* COPY into the array */ + for (i=0;i<count;i++) + fill_array(&array[i], /* mybase + */ + dlp->d_partitions[i].p_offset, + dlp->d_partitions[i].p_size, + NULL, 0, DISKPART_NONE, dlp->d_partitions[i].p_fstype); + + /* note: p_fstype is not the same set as the DOS types */ + + return(count); /* 'always' 8 partitions in disklabel -- if space */ + +/* UNREACHED CODE FOLLOWS: (alternative method in scsi) */ +#if 0 + (*bottom_read_fun)(driver_info, (start)+LABELSECTOR, buff); + + register int j; + boolean_t found; + + for (j = LABELOFFSET, found = FALSE; + j < (SECTOR_SIZE-sizeof(struct disklabel)); + j += sizeof(int)) { + search = (struct disklabel *)&buff[j]; + if (search->d_magic == DISKMAGIC && + search->d_magic2 == DISKMAGIC) { + found = TRUE; + break; + } + } + if (found) { +#if (PARTITION_DEBUG>1) + printf("Label found in LABELSECTOR\n"); +#endif + } else { + search = 0; + } + + +#endif 0 + +} + + +/* NOT TESTED! */ +/* VTOC in sector 29 */ +int get_vtoc(struct diskpart *array, char *buff, int start, + void *driver_info, int (*bottom_read_fun)(), + char *name, int max_part) +{ + struct evtoc *evp; + int n,i; + struct disklabel lpl; + struct disklabel *lp = &lpl; + +#if (PARTITION_DEBUG) + printf("Read VTOC.\n"); +#endif + (*bottom_read_fun)(driver_info, start +PDLOCATION, buff); + evp = (struct evtoc *)buff; + if (evp->sanity != VTOC_SANE) { +#if (PARTITION_DEBUG) + printf("%s evtoc corrupt or not found\n", name); +#endif + return(0); + } + n = min(evp->nparts,max_part); /* no longer DISKLABEL limitations... */ +#if 0 + n = (evp->nparts > MAXPARTITIONS) ? MAXPARTITIONS : evp->nparts; +#endif 0 + + for (i = 0; i < n; i++) + fill_array(&array[i], /* mybase + */ + evp->part[i].p_start, + evp->part[i].p_size, + NULL, 0, DISKPART_NONE, FS_BSDFFS); + + return(n); /* (evp->nparts) */ +} + + +/* NOT TESTED! */ +int get_omron(struct diskpart *array, char *buff, int start, + void *driver_info, int (*bottom_read_fun)(), + char *name, int max_part) +{ + + struct disklabel *label; + + /* here look for an Omron label */ + register omron_label_t *part; + int i; + +#if (PARTITION_DEBUG) + printf("Looking for Omron label...\n"); +#endif + + (*bottom_read_fun)(driver_info, start+ + OMRON_LABEL_BYTE_OFFSET/SECTOR_SIZE, buff); + + part = (omron_label_t*)&buff[OMRON_LABEL_BYTE_OFFSET%SECTOR_SIZE]; + if (part->magic == OMRON_LABEL_MAGIC) { +#if (PARTITION_DEBUG) + printf("{Using OMRON label}"); +#endif + for (i = 0; i < 8; i++) { + label->d_partitions[i].p_size = part->partitions[i].n_sectors; + label->d_partitions[i].p_offset = part->partitions[i].offset; + } + bcopy(part->packname, label->d_packname, 16); + label->d_ncylinders = part->ncyl; + label->d_acylinders = part->acyl; + label->d_ntracks = part->nhead; + label->d_nsectors = part->nsect; + /* Many disks have this wrong, therefore.. */ +#if 0 + label->d_secperunit = part->maxblk; +#else + label->d_secperunit = label->d_ncylinders * label->d_ntracks * + label->d_nsectors; +#endif 0 + + return(8); + } +#if (PARTITION_DEBUG) + printf("No Omron label found.\n"); +#endif + return(0); +} + + +/* NOT TESTED! */ +int get_dec(struct diskpart *array, char *buff, int start, + void *driver_info, int (*bottom_read_fun)(), + char *name, int max_part) +{ + struct disklabel *label; + + /* here look for a DEC label */ + register dec_label_t *part; + int i; + +#if (PARTITION_DEBUG) + printf("Checking for dec_label...\n"); +#endif + + (*bottom_read_fun)(driver_info, start + + DEC_LABEL_BYTE_OFFSET/SECTOR_SIZE, buff); + + if (part->magic == DEC_LABEL_MAGIC) { +#if (PARTITION_DEBUG) + printf("{Using DEC label}"); +#endif + for (i = 0; i < 8; i++) { + label->d_partitions[i].p_size = part->partitions[i].n_sectors; + label->d_partitions[i].p_offset = part->partitions[i].offset; + } + return(8); + } +#if (PARTITION_DEBUG) + printf("No dec label found.\n"); +#endif + + return(0); +} + + + + +/* array is a pointer to an array of partition_info structures */ +/* array_size is the number of pre-allocated entries there are */ +int get_only_partition(void *driver_info, int (*bottom_read_fun)(), + struct diskpart *array, int array_size, + int disk_size, char *drive_name) +{ + char buff[SECTOR_SIZE]; + int i,n,cnt; + int arrsize; + struct diskpart *res; + + /* first fill in the entire disk stuff */ + /* or should the calling routine do that already? */ + + fill_array(array, 0, disk_size, NULL, 0, -1, -1); + + /* while the structure does not preclude additional nestings, + additional ones make no sense currently, so they are not + checked (Mach can't handle them anyway). It might be nice + if for all partitions found, all types of sub-partitions + were looked for (unnecessary). This will be done when this + is moved out of ther kernel, and there is some way to name them */ + + arrsize = array_size -1; /* 1 for whole disk */ + + /* search for dos partition table */ + /* find all the partitions (including logical) */ + n=get_dos(&array[1], buff, 0, + driver_info, (bottom_read_fun), drive_name, + arrsize); + + if (n>0) { + fill_array(array, 0, disk_size, &array[1], n, + DISKPART_DOS, 256+DISKPART_DOS); + arrsize-=n; + + + /* search each one for a BSD disklabel (iff BSDOS) */ + /* note: searchine extended and logical partitions */ + for (i=0;i<n;i++) + if (array[i+1].fsys==BSDOS) { +#if (PARTITION_DEBUG) + printf("BSD OS slice: %d\n",i+1); +#endif + cnt=get_disklabel(&array[n+1], buff, + array[i+1].start, + driver_info, (bottom_read_fun), + drive_name,arrsize); + + if (cnt>0) { + arrsize-=cnt; + fill_array(&array[i+1],array[i+1].start, + array[i+1].size, &array[n+1], + cnt, DISKPART_BSD, + array[i+1].fsys); + } + n+=cnt; + } + + /* search for VTOC -- in a DOS partition as well */ + for (i=0;i<n;i++) + if (array[i+1].fsys==UNIXOS) { +#if (PARTITION_DEBUG) + printf("UNIXOS (vtoc) partition\n"); +#endif + cnt=get_vtoc(&array[n+1], buff, + array[i+1].start, + driver_info, (bottom_read_fun), + drive_name,arrsize); + + if (cnt>0) { + arrsize-=cnt; + fill_array(&array[i+1],array[i+1].start, + array[i+1].size, &array[n+1], + cnt, DISKPART_VTOC, + array[i+1].fsys); + } + n+=cnt; + } + } + + /* search for only disklabel */ + if (n==0) { + fill_array(array, 0, disk_size, &array[1], n, DISKPART_BSD, + 256+DISKPART_BSD); + n=get_disklabel(&array[1], buff, 0, driver_info, + (bottom_read_fun), drive_name,arrsize); + } + + /* search for only VTOC -- NOT TESTED! */ + if (n==0) { + fill_array(array, 0, disk_size, &array[1], n, DISKPART_VTOC, + 256+DISKPART_VTOC); + n=get_vtoc(&array[1], buff, 0, driver_info, (bottom_read_fun), + drive_name,arrsize); + } +#if 0 + /* search for only omron -- NOT TESTED! */ + if (n==0) { + fill_array(array, 0, disk_size, &array[1], n, DISKPART_OMRON, + 256+DISKPART_OMRON); + n=get_omron(&array[1], buff, 0,driver_info, (bottom_read_fun), + drive_name,arrsize); + } + + /* search for only dec -- NOT TESTED! */ + if (n==0) { + fill_array(array, 0, disk_size, &array[1], n, DISKPART_DEC, + 256+DISKPART_DEC); + n=get_dec(&array[1], buff, 0, driver_info, (bottom_read_fun), + drive_name,arrsize); + } +#endif 0 + +#if (PARTITION_DEBUG) /* print out what we found */ + print_array(array,0); +#endif + +} + + diff --git a/scsi/mapped_scsi.c b/scsi/mapped_scsi.c new file mode 100644 index 0000000..fe3dd77 --- /dev/null +++ b/scsi/mapped_scsi.c @@ -0,0 +1,586 @@ +/* + * 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: mapped_scsi.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * In-kernel side of the user-mapped SCSI driver. + */ + +#include <asc.h> +#include <sii.h> +#define NRZ (NASC+NSII) +#if NRZ > 0 +#include <platforms.h> + +#include <machine/machspl.h> /* spl definitions */ + +#include <device/device_types.h> +#include <device/io_req.h> +#include <chips/busses.h> + +#include <vm/vm_kern.h> +#include <kern/eventcount.h> + +#include <scsi/mapped_scsi.h> + +#include <machine/machspl.h> + +#ifdef DECSTATION + +#define machine_btop mips_btop + +#define kvctophys(v) K0SEG_TO_PHYS((v)) /* kernel virtual cached */ +#define phystokvc(p) PHYS_TO_K0SEG((p)) /* and back */ +#define kvutophys(v) K1SEG_TO_PHYS((v)) /* kernel virtual uncached */ +#define phystokvu(p) PHYS_TO_K1SEG((p)) /* and back */ + +#include <mips/mips_cpu.h> +#include <mips/PMAX/kn01.h> +#include <mips/PMAX/pmaz_aa.h> + +#define SII_REG_PHYS(self) kvutophys(self->registers.any) +#define SII_RAM_PHYS(self) (SII_REG_PHYS((self))+(KN01_SYS_SII_B_START-KN01_SYS_SII)) +#define SII_RAM_SIZE (KN01_SYS_SII_B_END-KN01_SYS_SII_B_START) + +#define ASC_REG_PHYS(self) kvutophys(self->registers.any) +#define ASC_DMAR_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_DMAR) +#define ASC_RAM_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_RAM) + +#define PAD_7061(n) short n +#define PAD_53C94(n) char n[3] + +#endif /*DECSTATION*/ + +#ifdef VAXSTATION +#define machine_btop vax_btop +#endif /*VAXSTATION*/ + +#ifdef P40 + +#define machine_btop mips_btop + +#define kvctophys(v) K0SEG_TO_PHYS((v)) /* kernel virtual cached */ +#define phystokvc(p) PHYS_TO_K0SEG((p)) /* and back */ +#define kvutophys(v) K1SEG_TO_PHYS((v)) /* kernel virtual uncached */ +#define phystokvu(p) PHYS_TO_K1SEG((p)) /* and back */ + +#include <mips/mips_cpu.h> + +#define ASC_RAM_SIZE 0 +#define ASC_OFFSET_DMAR 0 +#define ASC_OFFSET_RAM 0 + +#define ASC_REG_PHYS(self) kvutophys(self->registers.any) +#define ASC_DMAR_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_DMAR) +#define ASC_RAM_PHYS(self) (ASC_REG_PHYS((self))+ ASC_OFFSET_RAM) +#endif /* P40 */ + +/* + * Phys defines for the various supported HBAs + */ + +/* DEC7061 */ +#include <scsi/adapters/scsi_7061.h> + +#ifdef PAD_7061 + +typedef struct { + volatile unsigned short sii_sdb; /* rw: Data bus and parity */ + PAD_7061(pad0); + volatile unsigned short sii_sc1; /* rw: scsi signals 1 */ + PAD_7061(pad1); + volatile unsigned short sii_sc2; /* rw: scsi signals 2 */ + PAD_7061(pad2); + volatile unsigned short sii_csr; /* rw: control and status */ + PAD_7061(pad3); + volatile unsigned short sii_id; /* rw: scsi bus ID */ + PAD_7061(pad4); + volatile unsigned short sii_sel_csr; /* rw: selection status */ + PAD_7061(pad5); + volatile unsigned short sii_destat; /* ro: selection detector status */ + PAD_7061(pad6); + volatile unsigned short sii_dstmo; /* unsupp: dssi timeout */ + PAD_7061(pad7); + volatile unsigned short sii_data; /* rw: data register */ + PAD_7061(pad8); + volatile unsigned short sii_dma_ctrl; /* rw: dma control reg */ + PAD_7061(pad9); + volatile unsigned short sii_dma_len; /* rw: length of transfer */ + PAD_7061(pad10); + volatile unsigned short sii_dma_adr_low;/* rw: low address */ + PAD_7061(pad11); + volatile unsigned short sii_dma_adr_hi; /* rw: high address */ + PAD_7061(pad12); + volatile unsigned short sii_dma_1st_byte;/* rw: initial byte */ + PAD_7061(pad13); + volatile unsigned short sii_stlp; /* unsupp: dssi short trgt list ptr */ + PAD_7061(pad14); + volatile unsigned short sii_ltlp; /* unsupp: dssi long " " " */ + PAD_7061(pad15); + volatile unsigned short sii_ilp; /* unsupp: dssi initiator list ptr */ + PAD_7061(pad16); + volatile unsigned short sii_dssi_csr; /* unsupp: dssi control */ + PAD_7061(pad17); + volatile unsigned short sii_conn_csr; /* rc: connection interrupt control */ + PAD_7061(pad18); + volatile unsigned short sii_data_csr; /* rc: data interrupt control */ + PAD_7061(pad19); + volatile unsigned short sii_cmd; /* rw: command register */ + PAD_7061(pad20); + volatile unsigned short sii_diag_csr; /* rw: disgnostic status */ + PAD_7061(pad21); +} sii_padded_regmap_t; + +#else /*!PAD_7061*/ + +typedef sii_regmap_t sii_padded_regmap_t; + +#endif /*!PAD_7061*/ + +/* NCR 53C94 */ +#include <scsi/adapters/scsi_53C94.h> + +#ifdef PAD_53C94 +typedef struct { + volatile unsigned char asc_tc_lsb; /* rw: Transfer Counter LSB */ + PAD_53C94(pad0); + volatile unsigned char asc_tc_msb; /* rw: Transfer Counter MSB */ + PAD_53C94(pad1); + volatile unsigned char asc_fifo; /* rw: FIFO top */ + PAD_53C94(pad2); + volatile unsigned char asc_cmd; /* rw: Command */ + PAD_53C94(pad3); + volatile unsigned char asc_csr; /* r: Status */ +/*#define asc_dbus_id asc_csr /* w: Destination Bus ID */ + PAD_53C94(pad4); + volatile unsigned char asc_intr; /* r: Interrupt */ +/*#define asc_sel_timo asc_intr /* w: (re)select timeout */ + PAD_53C94(pad5); + volatile unsigned char asc_ss; /* r: Sequence Step */ +/*#define asc_syn_p asc_ss /* w: synchronous period */ + PAD_53C94(pad6); + volatile unsigned char asc_flags; /* r: FIFO flags + seq step */ +/*#define asc_syn_o asc_flags /* w: synchronous offset */ + PAD_53C94(pad7); + volatile unsigned char asc_cnfg1; /* rw: Configuration 1 */ + PAD_53C94(pad8); + volatile unsigned char asc_ccf; /* w: Clock Conv. Factor */ + PAD_53C94(pad9); + volatile unsigned char asc_test; /* w: Test Mode */ + PAD_53C94(pad10); + volatile unsigned char asc_cnfg2; /* rw: Configuration 2 */ + PAD_53C94(pad11); + volatile unsigned char asc_cnfg3; /* rw: Configuration 3 */ + PAD_53C94(pad12); + volatile unsigned char asc_rfb; /* w: Reserve FIFO byte */ + PAD_53C94(pad13); +} asc_padded_regmap_t; + +#else /* !PAD_53C94 */ + +typedef asc_regmap_t asc_padded_regmap_t; + +#endif /* !PAD_53C94 */ + +/* + * Co-existency with in-kernel drivers + */ +boolean_t rz_use_mapped_interface = FALSE; + +/* + * Status information for all HBAs + */ +/*static*/ struct RZ_status { + union { + unsigned long any; + asc_padded_regmap_t *asc; + sii_padded_regmap_t *sii; + } registers; + int (*stop)(); + vm_offset_t (*mmap)(); + mapped_scsi_info_t info; + struct evc eventcounter; +} RZ_statii[NRZ]; + +typedef struct RZ_status *RZ_status_t; + + +/* + * Probe routine for all HBAs + */ +RZ_probe(regbase, ui, hba) + unsigned long regbase; + register struct bus_device *ui; +{ + int unit = ui->unit; + vm_offset_t addr; + mapped_scsi_info_t info; + struct RZ_status *self; + + printf("[mappable] "); + + self = &RZ_statii[unit]; + + self->registers.any = regbase; + + /* + * Grab a page to be mapped later to users + */ + (void) kmem_alloc_wired(kernel_map, &addr, PAGE_SIZE); /* kseg2 */ + bzero(addr, PAGE_SIZE); + addr = pmap_extract(pmap_kernel(), addr); /* phys */ + info = (mapped_scsi_info_t) (phystokvc(addr)); + self->info = info; + + /* + * Set permanent info + */ + info->interrupt_count = 0; +/*XXX*/ info->ram_size = ASC_RAM_SIZE; + info->hba_type = hba; + + evc_init(&self->eventcounter); + info->wait_event = self->eventcounter.ev_id; + + return 1; +} + +/* + * Device open procedure + */ +RZ_open(dev, flag, ior) + io_req_t ior; +{ + int unit = dev; + register RZ_status_t self = &RZ_statii[unit]; + + + if (unit >= NRZ) + return D_NO_SUCH_DEVICE; + + /* + * Silence interface, just in case + */ + (*self->stop)(unit); + + /* + * Reset eventcounter + */ + evc_signal(&self->eventcounter); + + rz_use_mapped_interface = TRUE; + + /* + * Do not turn interrupts on. The user can do it when ready + * to take them. + */ + + return 0; +} + +/* + * Device close procedure + */ +RZ_close(dev, flag) +{ + int unit = dev; + register RZ_status_t self = &RZ_statii[unit]; + + if (unit >= NRZ) + return D_NO_SUCH_DEVICE; + + /* + * Silence interface, in case user forgot + */ + (*self->stop)(unit); + + evc_signal(&self->eventcounter); + + rz_use_mapped_interface = FALSE; + + /* XXX rz_kernel_mode(); XXX */ + + return 0; +} + + +/* + * Get status procedure. + * We need to tell that we are mappable. + */ +io_return_t +RZ_get_status(dev, flavor, status, status_count) + int dev; + int flavor; + dev_status_t status; + unsigned int status_count; +{ + return (D_SUCCESS); +} + +/* + * Should not refuse this either + */ +RZ_set_status(dev, flavor, status, status_count) + int dev; + int flavor; + dev_status_t status; + unsigned int status_count; +{ + return (D_SUCCESS); +} + +/* + * Port death notification routine + */ +RZ_portdeath(dev, dead_port) +{ +} + +/* + * Page mapping, switch off to HBA-specific for regs&ram + */ +vm_offset_t +RZ_mmap(dev, off, prot) + int dev; +{ + int unit = dev; + register RZ_status_t self = &RZ_statii[unit]; + vm_offset_t page; + vm_offset_t addr; + io_return_t ret; + + if (off < SCSI_INFO_SIZE) { + addr = kvctophys (self->info) + off; + ret = D_SUCCESS; + } else + ret = (*self->mmap)(self, off, prot, &addr); + + if (ret != D_SUCCESS) + return ret; + + page = machine_btop(addr); + + return (page); +} + + +/* + *--------------------------------------------------------------- + * The rest of the file contains HBA-specific routines + *--------------------------------------------------------------- + */ + +#if NASC > 0 +/* + * Routines for the NCR 53C94 + */ +static +ASC_stop(unit) +{ + register RZ_status_t self = &RZ_statii[unit]; + register asc_padded_regmap_t *regs = self->registers.asc; + int ack; + + ack = regs->asc_intr; /* Just acknowledge pending interrupts */ +} + +ASC_probe(reg, ui) + unsigned long reg; + register struct bus_device *ui; +{ + register RZ_status_t self = &RZ_statii[ui->unit]; + static vm_offset_t ASC_mmap(); + + self->stop = ASC_stop; + self->mmap = ASC_mmap; + return RZ_probe(reg, ui, HBA_NCR_53c94); +} + + +ASC_intr(unit,spllevel) + spl_t spllevel; +{ + register RZ_status_t self = &RZ_statii[unit]; + register asc_padded_regmap_t *regs = self->registers.asc; + register csr, intr, seq_step, cmd; + + /* + * Acknowledge interrupt request + * + * This clobbers some two other registers, therefore + * we read them beforehand. It also clears the intr + * request bit, silencing the interface for now. + */ + csr = regs->asc_csr; + + /* drop spurious interrupts */ + if ((csr & ASC_CSR_INT) == 0) + return; + seq_step = regs->asc_ss; + cmd = regs->asc_cmd; + + intr = regs->asc_intr; /* ack */ + + splx(spllevel); /* drop priority */ + + if (self->info) { + self->info->interrupt_count++; /* total interrupts */ + self->info->saved_regs.asc.csr = csr; + self->info->saved_regs.asc.isr = intr; + self->info->saved_regs.asc.seq = seq_step; + self->info->saved_regs.asc.cmd = cmd; + } + + /* Awake user thread */ + evc_signal(&self->eventcounter); +} + +/* + * Virtual->physical mapping routine for PMAZ-AA + */ +static vm_offset_t +ASC_mmap(self, off, prot, addr) + RZ_status_t self; + vm_offset_t off; + vm_prot_t prot; + vm_offset_t *addr; +{ + /* + * The offset (into the VM object) defines the following layout + * + * off size what + * 0 1pg mapping information (csr & #interrupts) + * 1pg 1pg ASC registers + * 2pg 1pg ASC dma + * 3pg 128k ASC ram buffers + */ + +#define ASC_END (ASC_RAM_BASE+ASC_RAM_SIZE) + + if (off < ASC_DMAR_BASE) + *addr = (vm_offset_t) ASC_REG_PHYS(self) + (off - SCSI_INFO_SIZE); + else if (off < ASC_RAM_BASE) + *addr = (vm_offset_t) ASC_DMAR_PHYS(self) + (off - ASC_REGS_BASE); + else if (off < ASC_END) + *addr = (vm_offset_t) ASC_RAM_PHYS(self) + (off - ASC_RAM_BASE); + else + return D_INVALID_SIZE; + + return D_SUCCESS; +} +#endif NASC > 0 + +#if NSII > 0 +SII_stop(unit) +{ + register RZ_status_t self = &RZ_statii[unit]; + register sii_padded_regmap_t *regs = self->registers.sii; + + regs->sii_csr &= ~SII_CSR_IE; /* disable interrupts */ + /* clear all wtc bits */ + regs->sii_conn_csr = regs->sii_conn_csr; + regs->sii_data_csr = regs->sii_data_csr; +} + +SII_probe(reg, ui) + unsigned long reg; + register struct bus_device *ui; +{ + register RZ_status_t self = &RZ_statii[ui->unit]; + static vm_offset_t SII_mmap(); + + self->stop = SII_stop; + self->mmap = SII_mmap; + return RZ_probe(reg, ui, HBA_DEC_7061); +} + +SII_intr(unit,spllevel) + spl_t spllevel; +{ + register RZ_status_t self = &RZ_statii[unit]; + register sii_padded_regmap_t *regs = self->registers.sii; + register unsigned short conn, data; + + /* + * Disable interrupts, saving cause(s) first. + */ + conn = regs->sii_conn_csr; + data = regs->sii_data_csr; + + /* drop spurious calls */ + if (((conn|data) & (SII_DTR_DI|SII_DTR_CI)) == 0) + return; + + regs->sii_csr &= ~SII_CSR_IE; + + regs->sii_conn_csr = conn; + regs->sii_data_csr = data; + + splx(spllevel); + + if (self->info) { + self->info->interrupt_count++; /* total interrupts */ + self->info->saved_regs.sii.sii_conn_csr = conn; + self->info->saved_regs.sii.sii_data_csr = data; + } + + /* Awake user thread */ + evc_signal(&self->eventcounter); +} + +static vm_offset_t +SII_mmap(self, off, prot, addr) + RZ_status_t self; + vm_offset_t off; + vm_prot_t prot; + vm_offset_t *addr; +{ + /* + * The offset (into the VM object) defines the following layout + * + * off size what + * 0 1pg mapping information (csr & #interrupts) + * 1pg 1pg SII registers + * 2pg 128k SII ram buffer + */ + +#define SII_END (SII_RAM_BASE+SII_RAM_SIZE) + + if (off < SII_RAM_BASE) + *addr = (vm_offset_t) SII_REG_PHYS(self) + (off - SCSI_INFO_SIZE); + else if (off < SII_END) + *addr = (vm_offset_t) SII_RAM_PHYS(self) + (off - SII_RAM_BASE); + else + return D_INVALID_SIZE; + + return D_SUCCESS; +} +#endif NSII > 0 + +#endif NRZ > 0 diff --git a/scsi/mapped_scsi.h b/scsi/mapped_scsi.h new file mode 100644 index 0000000..b9c6528 --- /dev/null +++ b/scsi/mapped_scsi.h @@ -0,0 +1,90 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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: mapped_scsi.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 11/90 + * + * Definitions for the User-level SCSI Driver + */ + +/* + * HBA chips of various sorts + */ + +/* DEC 7061 used on pmaxen */ + +typedef struct sii_volatile_regs { + unsigned short sii_conn_csr; + unsigned short sii_data_csr; +} *sii_reg_t; + +#define HBA_DEC_7061 0x00000001 + + /* layout of mapped stuff */ +#define SII_REGS_BASE (SCSI_INFO_BASE+SCSI_INFO_SIZE) +#define SII_REGS_SIZE PAGE_SIZE +#define SII_RAM_BASE (SII_REGS_BASE+SII_REGS_SIZE) + + +/* National 53C94, used on 3maxen' PMAZ-AA boards */ + +typedef struct asc_volatile_regs { + unsigned char csr; + unsigned char isr; + unsigned char seq; + unsigned char cmd; +} *asc_reg_t; + +#define HBA_NCR_53c94 0x00000002 + + /* layout of mapped stuff */ +#define ASC_REGS_BASE (SCSI_INFO_BASE+SCSI_INFO_SIZE) +#define ASC_REGS_SIZE PAGE_SIZE +#define ASC_DMAR_BASE (ASC_REGS_BASE+ASC_REGS_SIZE) +#define ASC_DMAR_SIZE PAGE_SIZE +#define ASC_RAM_BASE (ASC_DMAR_BASE+ASC_DMAR_SIZE) + +/* + * User-mapped information block, common to all + */ +#define SCSI_INFO_BASE 0 +#define SCSI_INFO_SIZE PAGE_SIZE + +#define SCSI_MAX_MAPPED_SIZE (ASC_RAM_BASE+128*1024) + +typedef struct { + int interrupt_count;/* Counter kept by kernel */ + unsigned int wait_event; /* To wait for interrupts */ + unsigned ram_size; + int hba_type; /* Tag for regs union */ + union { /* Space for regs saved on + * intr. Only few used */ + struct asc_volatile_regs asc; + struct sii_volatile_regs sii; + } saved_regs; +} *mapped_scsi_info_t; + diff --git a/scsi/pc_scsi_label.c b/scsi/pc_scsi_label.c new file mode 100644 index 0000000..9bbcbbf --- /dev/null +++ b/scsi/pc_scsi_label.c @@ -0,0 +1,196 @@ +/* + * Mach Operating System + * Copyright (c) 1993,1991,1990,1989 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. + */ +/* This goes away as soon as we move it in the Ux server */ + + + +#include <mach/std_types.h> +#include <scsi/compat_30.h> +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> +#include <scsi/rz_labels.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +#if (NSCSI > 0) +#define LABEL_DEBUG(x,y) if (label_flag&x) y + +#include <i386at/disk.h> +#include <device/device_types.h> +#include <device/disk_status.h> + + +int scsi_abs_sec = -1; +int scsi_abs_count = -1; + +scsi_rw_abs(dev, data, rw, sec, count) + dev_t dev; +{ + io_req_t ior; + io_return_t error; + + io_req_alloc(ior,0); + ior->io_next = 0; + ior->io_unit = dev & (~(MAXPARTITIONS-1)); /* sort of */ + ior->io_unit |= PARTITION_ABSOLUTE; + ior->io_data = (io_buf_ptr_t)data; + ior->io_count = count; + ior->io_recnum = sec; + ior->io_error = 0; + if (rw == IO_READ) + ior->io_op = IO_READ; + else + ior->io_op = IO_WRITE; + scdisk_strategy(ior); + iowait(ior); + error = ior->io_error; + io_req_free(ior); + return(error); +} + +io_return_t +scsi_i386_get_status(dev, tgt, flavor, status, status_count) +int dev; +target_info_t *tgt; +int flavor; +dev_status_t status; +unsigned int *status_count; +{ + + switch (flavor) { + case V_GETPARMS: { + struct disklabel *lp = &tgt->dev_info.disk.l; + struct disk_parms *dp = (struct disk_parms *)status; + extern struct disklabel default_label; + int part = rzpartition(dev); + + if (*status_count < sizeof (struct disk_parms)/sizeof(int)) + return (D_INVALID_OPERATION); + dp->dp_type = DPT_WINI; + dp->dp_secsiz = lp->d_secsize; + if (lp->d_nsectors == default_label.d_nsectors && + lp->d_ntracks == default_label.d_ntracks && + lp->d_ncylinders == default_label.d_ncylinders) { + /* I guess there is nothing there */ + /* Well, then, Adaptec's like ... */ + dp->dp_sectors = 32; + dp->dp_heads = 64; + dp->dp_cyls = lp->d_secperunit / 64 / 32 ; + } else { + dp->dp_sectors = lp->d_nsectors; + dp->dp_heads = lp->d_ntracks; + dp->dp_cyls = lp->d_ncylinders; + } + + dp->dp_dossectors = 32; + dp->dp_dosheads = 64; + dp->dp_doscyls = lp->d_secperunit / 64 / 32; + dp->dp_ptag = 0; + dp->dp_pflag = 0; +/* !!! partition changes */ +printf("USING PARTIOION TABLE\n"); + dp->dp_pstartsec = lp->d_partitions[part].p_offset; + dp->dp_pnumsec = lp->d_partitions[part].p_size; + *status_count = sizeof(struct disk_parms)/sizeof(int); + break; + } + case V_RDABS: + if (*status_count < DEV_BSIZE/sizeof (int)) { + printf("RDABS bad size %x", *status_count); + return (D_INVALID_OPERATION); + } + if (scsi_rw_abs(dev, status, IO_READ, scsi_abs_sec, DEV_BSIZE) != D_SUCCESS) + return(D_INVALID_OPERATION); + *status_count = DEV_BSIZE/sizeof(int); + break; + case V_VERIFY: { + int count = scsi_abs_count * DEV_BSIZE; + int sec = scsi_abs_sec; + char *scsi_verify_buf; +#include "vm/vm_kern.h" + + (void) kmem_alloc(kernel_map, &scsi_verify_buf, PAGE_SIZE); + + *status = 0; + while (count > 0) { + int xcount = (count < PAGE_SIZE) ? count : PAGE_SIZE; + if (scsi_rw_abs(dev, scsi_verify_buf, IO_READ, sec, xcount) != D_SUCCESS) { + *status = BAD_BLK; + break; + } else { + count -= xcount; + sec += xcount / DEV_BSIZE; + } + } + (void) kmem_free(kernel_map, scsi_verify_buf, PAGE_SIZE); + *status_count = 1; + break; + } + default: + return(D_INVALID_OPERATION); + } + return D_SUCCESS; +} + +io_return_t +scsi_i386_set_status(dev, tgt, flavor, status, status_count) +int dev; +target_info_t *tgt; +int flavor; +int *status; +unsigned int status_count; +{ + io_req_t ior; + + switch (flavor) { + case V_SETPARMS: + printf("scsdisk_set_status: invalid flavor V_SETPARMS\n"); + return(D_INVALID_OPERATION); + break; + case V_REMOUNT: + tgt->flags &= ~TGT_ONLINE; + break; + case V_ABS: + scsi_abs_sec = status[0]; + if (status_count == 2) + scsi_abs_count = status[1]; + break; + case V_WRABS: + if (status_count < DEV_BSIZE/sizeof (int)) { + printf("RDABS bad size %x", status_count); + return (D_INVALID_OPERATION); + } + if (scsi_rw_abs(dev, status, IO_WRITE, scsi_abs_sec, DEV_BSIZE) != D_SUCCESS) + return(D_INVALID_OPERATION); + break; + default: + return(D_INVALID_OPERATION); + } + return D_SUCCESS; +} +#endif /* NSCSI > 0 */ + diff --git a/scsi/rz.c b/scsi/rz.c new file mode 100644 index 0000000..febf629 --- /dev/null +++ b/scsi/rz.c @@ -0,0 +1,462 @@ +/* + * Mach Operating System + * Copyright (c) 1993-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.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Top layer of the SCSI driver: interface with the MI side. + */ + +/* + * This file contains the code that is common to all scsi devices, + * operations and/or behaviours specific to certain devices live + * in the corresponding rz_mumble files. + */ + +#include <scsi.h> + +#if (NSCSI>0) + +#include <mach/std_types.h> +#include <machine/machspl.h> /* spl definitions */ +#include <scsi/compat_30.h> + +#ifdef MACH_KERNEL +#include <kern/time_out.h> +#else /*MACH_KERNEL*/ +#include <sys/kernel.h> /* for hz */ + +static io_req_t getbp(); +#endif /*MACH_KERNEL*/ + +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + + +boolean_t +rz_check(dev, p_sc, p_tgt) + int dev; + scsi_softc_t **p_sc; + target_info_t **p_tgt; +{ + if (rzcontroller(dev) >= NSCSI || + (*p_sc = scsi_softc[rzcontroller(dev)]) == 0) + return FALSE; + + *p_tgt = (*p_sc)->target[rzslave(dev)]; + + if (!*p_tgt || + !((*p_tgt)->flags&TGT_ALIVE)) + return FALSE; + return TRUE; +} + +/* + * Open routine + * + * On tapes and other devices might have to wait a bit for + * the unit to come alive. The following patchable variable + * takes this into account + */ +int rz_open_timeout = 60;/* seconds */ + +int rz_open(dev, mode, ior) + int dev; + dev_mode_t mode; + io_req_t ior; +{ + scsi_softc_t *sc = 0; + target_info_t *tgt; + scsi_ret_t ret; + register int i; + + if (!rz_check(dev, &sc, &tgt)) { + /* + * Probe it again: might have installed a new device + */ + if (!sc || !scsi_probe(sc, &tgt, rzslave(dev), ior)) + return D_NO_SUCH_DEVICE; + } + + /* tapes do not wait for rewind to complete on close */ + if (tgt->ior && !(tgt->flags & TGT_ONLINE)) + return D_WOULD_BLOCK; + + if (scsi_debug) + printf("opening %s%d..", (*tgt->dev_ops->driver_name)(TRUE), dev&0xff); + + if (sc->watchdog) { + (*sc->watchdog)(tgt->hw_state); + sc->watchdog = 0; + } + + /* + * Bring the unit online, retrying if necessary. + * If the target is spinning up we wait for it. + */ + if ( ! (tgt->flags & TGT_ONLINE)) { + io_req_t tmp_ior; + + io_req_alloc(tmp_ior,0); + tmp_ior->io_next = 0; + tmp_ior->io_count = 0; + + for (i = 0; i < rz_open_timeout; i++) { + + tmp_ior->io_op = IO_INTERNAL; + tmp_ior->io_error = 0; + ret = scsi_test_unit_ready(tgt, tmp_ior); + + if (ret == SCSI_RET_SUCCESS) + break; + + if (ret == SCSI_RET_DEVICE_DOWN) { + i = rz_open_timeout; + break; + } + + if (ret == SCSI_RET_NEED_SENSE) { + + tmp_ior->io_op = IO_INTERNAL; + tmp_ior->io_count = 0; + tmp_ior->io_residual = 0; + tgt->ior = tmp_ior; + scsi_request_sense(tgt, tmp_ior, 0); + iowait(tmp_ior); + + } + + if (i == 5) printf("%s%d: %s\n", + (*tgt->dev_ops->driver_name)(TRUE), + tgt->target_id, + "Waiting to come online.."); + timeout(wakeup, tgt, hz); + await(tgt); + } + + /* lock on removable media */ + if ((i != rz_open_timeout) && (tgt->flags & TGT_REMOVABLE_MEDIA)) { + tmp_ior->io_op = IO_INTERNAL; + /* too many dont support it. Sigh */ + tgt->flags |= TGT_OPTIONAL_CMD; + (void) scsi_medium_removal( tgt, FALSE, tmp_ior); + tgt->flags &= ~TGT_OPTIONAL_CMD; + } + + io_req_free(tmp_ior); + if (i == rz_open_timeout) + return D_DEVICE_DOWN; + } + /* + * Perform anything open-time special on the device + */ + if (tgt->dev_ops->open != SCSI_OPEN_NULL) { + ret = (*tgt->dev_ops->open)(tgt, ior); + if (ret != SCSI_RET_SUCCESS) { + if (scsi_debug) printf("%s%d: open failed x%x\n", + (*tgt->dev_ops->driver_name)(TRUE), dev&0xff, ret); + return ret; + } + } + tgt->flags |= TGT_ONLINE; + ior->io_device->bsize = tgt->block_size; + return D_SUCCESS; +} + +int rz_close(dev) + int dev; +{ + scsi_softc_t *sc; + target_info_t *tgt; + scsi_ret_t ret; + + if (!rz_check(dev, &sc, &tgt)) + return D_NO_SUCH_DEVICE; + + if (scsi_debug) + printf("closing %s%d..", (*tgt->dev_ops->driver_name)(TRUE), dev&0xff); + + if (tgt->flags & TGT_REMOVABLE_MEDIA) { + io_req_t ior; + + io_req_alloc(ior,0); + ior->io_next = 0; + ior->io_count = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + /* too many dont support it. Sigh */ + tgt->flags |= TGT_OPTIONAL_CMD; + (void) scsi_medium_removal( tgt, TRUE, ior); + tgt->flags &= ~TGT_OPTIONAL_CMD; + io_req_free(ior); + } + + /* + * Perform anything close-time special on the device + */ + if (tgt->dev_ops->close != SCSI_CLOSE_NULL) { + ret = (*tgt->dev_ops->close)(tgt); + if (ret != SCSI_RET_SUCCESS) { + printf("%s%d: close failed x%x\n", + (*tgt->dev_ops->driver_name)(TRUE), dev&0xff, ret); + } + } + if (tgt->flags & TGT_REMOVABLE_MEDIA) + tgt->flags &= ~TGT_ONLINE; + + return D_SUCCESS; +} + +/* our own minphys */ +void rz_minphys(ior) + io_req_t ior; +{ +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + if (ior->io_count > scsi_per_target_virtual) + ior->io_count = scsi_per_target_virtual; +#endif /*MACH_KERNEL*/ +} + +int rz_read(dev, ior) + int dev; + io_req_t ior; +{ + target_info_t *tgt; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + +#ifdef MACH_KERNEL + return block_io(tgt->dev_ops->strategy, rz_minphys, ior); +#else /*MACH_KERNEL*/ + return physio(tgt->dev_ops->strategy, getbp(dev), dev, IO_READ, rz_minphys, ior); +#endif /*MACH_KERNEL*/ +} + +int rz_write(dev, ior) + int dev; + io_req_t ior; +{ + target_info_t *tgt; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + + if (tgt->flags & TGT_READONLY) + return D_INVALID_OPERATION; + +#ifdef MACH_KERNEL + return block_io(tgt->dev_ops->strategy, rz_minphys, ior); +#else /*MACH_KERNEL*/ + return physio(tgt->dev_ops->strategy, getbp(dev), dev, IO_WRITE, rz_minphys, ior); +#endif /*MACH_KERNEL*/ +} + +int rz_get_status(dev, flavor, status, status_count) + int dev; + dev_flavor_t flavor; + dev_status_t status; + natural_t *status_count; +{ + target_info_t *tgt; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + + if (scsi_debug) + printf("rz_get_status: x%x x%x x%x x%x\n", + dev, flavor, status, *status_count); + return (*tgt->dev_ops->get_status)(dev, tgt, flavor, status, status_count); +} + +int rz_set_status(dev, flavor, status, status_count) + int dev; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + target_info_t *tgt; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + + if (scsi_debug) + printf("rz_set_status: x%x x%x x%x x%x\n", + dev, flavor, status, status_count); + return (*tgt->dev_ops->set_status)(dev, tgt, flavor, status, status_count); +} + +/* + * Routine to return information to kernel. + */ +int +rz_devinfo(dev, flavor, info) + int dev; + int flavor; + char *info; +{ + register int result; + + result = D_SUCCESS; + + switch (flavor) { + /* Caller stupidity, should use device->bsize instead */ + case D_INFO_BLOCK_SIZE: + *((int *) info) = scsi_softc[rzcontroller(dev)]-> + target[rzslave(dev)]->block_size; + break; + default: + result = D_INVALID_OPERATION; + } + + return(result); +} + +void +rz_simpleq_strategy(ior, start) + io_req_t ior; + void (*start)(); +{ + target_info_t *tgt; + register scsi_softc_t *sc; + scsi_ret_t ret; + register int i = ior->io_unit; + io_req_t head, tail; + spl_t s; + + sc = scsi_softc[rzcontroller(i)]; + tgt = sc->target[rzslave(i)]; + + ior->io_next = 0; + ior->io_prev = 0; + + s = splbio(); + simple_lock(&tgt->target_lock); + if (head = tgt->ior) { + /* Queue it up at the end of the list */ + if (tail = head->io_prev) + tail->io_next = ior; + else + head->io_next = ior; + head->io_prev = ior; /* tail pointer */ + simple_unlock(&tgt->target_lock); + } else { + /* Was empty, start operation */ + tgt->ior = ior; + simple_unlock(&tgt->target_lock); + (*start)( tgt, FALSE); + } + splx(s); +} +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + +rz_strategy(ior) + io_req_t ior; +{ + target_info_t *tgt; + register int dev = ior->io_unit; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + + return (*tgt->dev_ops->strategy)(ior); +} + + +#define IOCPARM_SIZE(c) (((c)>>16)&IOCPARM_MASK) +#define IOC_WDSIZE(s) ((IOCPARM_SIZE(s))>>2) + +rz_ioctl(dev, cmd, data, flag) +{ + io_return_t error; + unsigned int count; + + count = IOC_WDSIZE(cmd); + if (cmd & (IOC_VOID|IOC_IN)) { + error = rz_set_status(dev, cmd, (dev_status_t)data, count); + if (error) + return (error); + } + if (cmd & IOC_OUT) { + error = rz_get_status(dev, cmd, (dev_status_t *)data, &count); + if (error) + return (error); + } + return (0); +} + +/* This is a very simple-minded config, + * assumes we have << 8 disks per bus */ +#define NBUF (NSCSI*8) +struct io_req rz_buffers[NBUF]; + +static io_req_t +getbp(dev) +{ + io_req_t ior; + int hash = minor(dev) >> 3; + + ior = &rz_buffers[hash]; + if (ior->io_op & IO_BUSY) { + register io_req_t ior; + for (ior = rz_buffers; ior < &rz_buffers[NBUF]; ior++) + if ((ior->io_op & IO_BUSY) == 0) + return ior; + + } + return ior; +} + +/* + * This ugliness is only needed because of the + * way the minor is encoded for tapes. + */ +tz_open(dev, mode, ior) + int dev; + dev_mode_t mode; + io_req_t ior; +{ + io_return_t error; + + error = rz_open(TAPE_UNIT(dev), mode, ior); + if(error) + return error; + if (TAPE_REWINDS(dev)) { + scsi_softc_t *sc; + target_info_t *tgt; + + rz_check(TAPE_UNIT(dev), &sc, &tgt); + tgt->flags |= TGT_REWIND_ON_CLOSE; + } + return 0; +} + +tz_close(dev) { return rz_close(TAPE_UNIT(dev));} +tz_read(dev, ior) { return rz_read(TAPE_UNIT(dev), ior);} +tz_write(dev, ior) { return rz_write(TAPE_UNIT(dev), ior);} +tz_ioctl(dev, cmd, data, flag) { return rz_ioctl(TAPE_UNIT(dev), cmd, data, flag);} + +#endif /*MACH_KERNEL*/ + +#endif (NSCSI>0) diff --git a/scsi/rz.h b/scsi/rz.h new file mode 100644 index 0000000..7fa7b88 --- /dev/null +++ b/scsi/rz.h @@ -0,0 +1,60 @@ +/* + * 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.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Mapping between U*x-like indexing and controller+slave + * Each controller handles at most 8 slaves, few controllers. + */ + +#if 0 +#define rzcontroller(dev) (((dev)>>6)&0x3) +#define rzslave(dev) (((dev)>>3)&0x7) +#endif 0 +#define rzcontroller(dev) (((dev)>>13)&0x3) +#define rzslave(dev) (((dev)>>10)&0x7) + +#if 0 +#define rzpartition(dev) ((PARTITION_TYPE(dev)==0xf)?MAXPARTITIONS:((dev)&0x7)) +#endif 0 +#define rzpartition(dev) ((dev)&0x3ff) + +/* To address the full 256 luns use upper bits 8..12 */ +/* NOTE: Under U*x this means the next major up.. what a mess */ +#define rzlun(dev) (((dev)&0x7) | (((dev)>>5)&0xf8)) + +/* note: whatever this was used for is no longer cared about -- Kevin */ +#define PARTITION_TYPE(dev) (((dev)>>24)&0xf) +#define PARTITION_ABSOLUTE (0xf<<24) + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#define tape_unit(dev) ((((dev)&0xe0)>>3)|((dev)&0x3)) +#define TAPE_UNIT(dev) ((dev)&(~0xff))|(tape_unit((dev))<<3) +#define TAPE_REWINDS(dev) (((dev)&0x1c)==0)||(((dev)&0x1c)==8) +#endif /*MACH_KERNEL*/ diff --git a/scsi/rz_audio.c b/scsi/rz_audio.c new file mode 100644 index 0000000..4d60fa1 --- /dev/null +++ b/scsi/rz_audio.c @@ -0,0 +1,1901 @@ +/* + * Mach Operating System + * Copyright (c) 1993 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_audio.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 3/93 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains operations specific to audio CD-ROM devices. + * Unlike many others, it sits on top of the rz.c module. + */ + +#include <mach/std_types.h> +#include <kern/strings.h> +#include <machine/machspl.h> /* spl definitions */ +#include <vm/vm_kern.h> +#include <device/ds_routines.h> + +#include <scsi/compat_30.h> +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + +#if (NSCSI > 0) + +#define private static + +/* some data is two BCD digits in one byte */ +#define bcd_to_decimal(b) (((b)&0xf) + 10 * (((b) >> 4) & 0xf)) +#define decimal_to_bcd(b) ((((b) / 10) << 4) | ((b) % 10)) + +/* + * Regular use of a CD-ROM is for data, and is handled + * by the default set of operations. Ours is for funtime.. + */ + +extern char *sccdrom_name(); +int cd_strategy(); +void cd_start(); + +private scsi_devsw_t scsi_audio = { + sccdrom_name, 0, 0, 0, cd_strategy, cd_start, 0, 0 +}; + +private char unsupported[] = "Device does not support it."; + +/* + * Unfortunately, none of the vendors appear to + * abide by the SCSI-2 standard and many of them + * violate or stretch even the SCSI-1 one. + * Therefore, we keep a red-list here of the worse + * offendors and how to deal with them. + * The user is notified of the problem and invited + * to solicit his vendor to upgrade the firmware. + * [They had plenty of time to do so] + */ +typedef struct red_list { + char *vendor; + char *product; + char *rev; + /* + * The standard MANDATES [par 13.1.6] the play_audio command + * at least as a way to discover if the device + * supports audio operations at all. This is the only way + * we need to use it. + */ + scsi_ret_t (*can_play_audio)( target_info_t *, char *, io_req_t); + /* + * The standard defines the use of start_stop_unit to + * cause the drive to eject the disk. + */ + scsi_ret_t (*eject)( target_info_t *, char *, io_req_t ); + /* + * The standard defines read_subchannel as a way to + * get the current playing position. + */ + scsi_ret_t (*current_position)( target_info_t *, char *, io_req_t ); + /* + * The standard defines read_table_of_content to get + * the listing of audio tracks available. + */ + scsi_ret_t (*read_toc)( target_info_t *, char *, io_req_t ); + /* + * The standard defines read_subchannel as the way to + * report the current audio status (playing/stopped/...). + */ + scsi_ret_t (*get_status)( target_info_t *, char *, io_req_t ); + /* + * The standard defines two ways to issue a play command, + * depending on the type of addressing used. + */ + scsi_ret_t (*play_msf)( target_info_t *, char *, io_req_t ); + scsi_ret_t (*play_ti)( target_info_t *, char *, io_req_t ); + /* + * The standard defines the pause_resume command to + * suspend or resume playback of audio data. + */ + scsi_ret_t (*pause_resume)( target_info_t *, char *, io_req_t ); + /* + * The standard defines the audio page among the + * mode selection options as a way to control + * both volume and connectivity of the channels + */ + scsi_ret_t (*volume_control)( target_info_t *, char *, io_req_t ); +} red_list_t; + +#define if_it_can_do(some_cmd) \ + if (tgt->dev_info.cdrom.violates_standards && \ + tgt->dev_info.cdrom.violates_standards->some_cmd) \ + rc = (*tgt->dev_info.cdrom.violates_standards->some_cmd) \ + (tgt,cmd,ior); \ + else + +/* + * So now that you know what they should have implemented :-), + * check at the end of the file what the naughty boys did instead. + */ +/* private red_list_t audio_replacements[]; / * at end */ + +/* + * Forward decls + */ +private void decode_status( char *buf, unsigned char audio_status ); +void zero_ior( io_req_t ); + +/* + * Open routine. Does some checking, sets up + * the replacement pointer. + */ +io_return_t +cd_open( + int dev, + dev_mode_t mode, + io_req_t req) +{ + scsi_softc_t *sc = 0; + target_info_t *tgt; + int ret; + scsi_ret_t rc; + io_req_t ior = 0; + vm_offset_t mem = 0; + extern boolean_t rz_check(); + + if (!rz_check(dev, &sc, &tgt)) { + /* + * Probe it again: might have installed a new device + */ + if (!sc || !scsi_probe(sc, &tgt, rzslave(dev), ior)) + return D_NO_SUCH_DEVICE; + bzero(&tgt->dev_info, sizeof(tgt->dev_info)); + } + + /* + * Check this is indeded a CD-ROM + */ + if (tgt->dev_ops != &scsi_devsw[SCSI_CDROM]) { + rz_close(dev); + return D_NO_SUCH_DEVICE; + } + + /* + * Switch to audio ops, unless some wrong + */ + tgt->dev_ops = &scsi_audio; + + /* + * Bring unit online + */ + ret = rz_open(dev, mode, req); + if (ret) goto bad; + + /* Pessimistic */ + ret = D_INVALID_OPERATION; + + /* + * Check if this device is on the red list + */ + { + scsi2_inquiry_data_t *inq; + private void check_red_list(); + + scsi_inquiry(tgt, SCSI_INQ_STD_DATA); + inq = (scsi2_inquiry_data_t*)tgt->cmd_ptr; + + check_red_list( tgt, inq ); + + } + + /* + * Allocate dynamic data + */ + if (kmem_alloc(kernel_map, &mem, PAGE_SIZE) != KERN_SUCCESS) + return D_NO_MEMORY; + tgt->dev_info.cdrom.result = (void *)mem; + tgt->dev_info.cdrom.result_available = FALSE; + + /* + * See if this CDROM can play audio data + */ + io_req_alloc(ior,0); + zero_ior( ior ); + + { + char *cmd = 0; + if_it_can_do(can_play_audio) + rc = scsi_play_audio( tgt, 0, 0, FALSE, ior); + } + + if (rc != SCSI_RET_SUCCESS) goto bad; + + io_req_free(ior); + return D_SUCCESS; + +bad: + if (ior) io_req_free(ior); + if (mem) kmem_free(kernel_map, mem, PAGE_SIZE); + tgt->dev_ops = &scsi_devsw[SCSI_CDROM]; + return ret; +} + +/* + * Close routine. + */ +io_return_t +cd_close( + int dev) +{ + scsi_softc_t *sc; + target_info_t *tgt; + vm_offset_t mem; + + if (!rz_check(dev, &sc, &tgt)) + return D_NO_SUCH_DEVICE; + if (!tgt || (tgt->dev_ops != &scsi_audio)) + return D_NO_SUCH_DEVICE; + + /* + * Cleanup state + */ + mem = (vm_offset_t) tgt->dev_info.cdrom.result; + tgt->dev_info.cdrom.result = (void *)0; + tgt->dev_info.cdrom.result_available = FALSE; + + (void) kmem_free(kernel_map, mem, PAGE_SIZE); + + (void) rz_close(dev); + + tgt->dev_ops = &scsi_devsw[SCSI_CDROM]; + return D_SUCCESS; +} + +/* + * Write routine. It is passed an ASCII string + * with the command to be executed. + */ +io_return_t +cd_write( + int dev, + io_req_t ior) +{ + register kern_return_t rc; + boolean_t wait = FALSE; + io_return_t ret; + int count; + register char *data; + vm_offset_t addr; + + data = ior->io_data; + count = ior->io_count; + if (count == 0) + return D_SUCCESS; + + if (!(ior->io_op & IO_INBAND)) { + /* + * Copy out-of-line data into kernel address space. + * Since data is copied as page list, it will be + * accessible. + */ + vm_map_copy_t copy = (vm_map_copy_t) data; + kern_return_t kr; + + kr = vm_map_copyout(device_io_map, &addr, copy); + if (kr != KERN_SUCCESS) + return kr; + data = (char *) addr; + } + + if (scsi_debug) printf("Got command '%s'\n", data); + + ret = cd_command( dev, data, count, ior); + + if (!(ior->io_op & IO_INBAND)) + (void) vm_deallocate(device_io_map, addr, ior->io_count); + return D_SUCCESS; +} + +/* + * Read routine. Returns an ASCII string with the results + * of the last command executed. + */ +io_return_t +cd_read( + int dev, + io_req_t ior) +{ + target_info_t *tgt; + kern_return_t rc; + natural_t count; + + /* + * Allocate memory for read buffer. + */ + count = (natural_t)ior->io_count; + if (count > PAGE_SIZE) + return D_INVALID_SIZE; /* sanity */ + + rc = device_read_alloc(ior, count); + if (rc != KERN_SUCCESS) + return rc; + + if (scsi_debug) printf("Got read req for %d bytes\n", count); + + /* + * See if last cmd left some to say + */ + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + if (tgt->dev_info.cdrom.result_available) { + int len; + + tgt->dev_info.cdrom.result_available = FALSE; + len = strlen(tgt->dev_info.cdrom.result)+1; + + if (count > len) + count = len; + bcopy(tgt->dev_info.cdrom.result, ior->io_data, count); + + } else { +# define noway "No results pending" + count = (count > sizeof(noway)) ? sizeof(noway) : count; + bcopy(noway, ior->io_data, count); + } + + ior->io_residual = ior->io_count - count; + return D_SUCCESS; +} + +/* + * This does all the work + */ +io_return_t +cd_command( + int dev, + char *cmd, + int count, + io_req_t req) +{ + target_info_t *tgt; + io_req_t ior; + io_return_t ret = D_INVALID_OPERATION; + scsi_ret_t rc; + char *buf; + + tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)]; + + buf = tgt->dev_info.cdrom.result; + tgt->dev_info.cdrom.result_available = FALSE; + + io_req_alloc(ior,0); + zero_ior( ior ); + + switch (cmd[0]) { + + case 'E': + /* "Eject" */ + /* too many dont support it. Sigh */ + tgt->flags |= TGT_OPTIONAL_CMD; + (void) scsi_medium_removal( tgt, TRUE, ior); + tgt->flags &= ~TGT_OPTIONAL_CMD; + + zero_ior( ior ); + + if_it_can_do(eject) + rc = scsi_start_unit(tgt, SCSI_CMD_SS_EJECT, ior); + break; + + case 'G': + switch (cmd[4]) { + + case 'P': + /* "Get Position MSF|ABS" */ + if_it_can_do(current_position) { + rc = scsi_read_subchannel(tgt, + cmd[13] == 'M', + SCSI_CMD_RS_FMT_CURPOS, + 0, + ior); + if (rc == SCSI_RET_SUCCESS) { + cdrom_chan_curpos_t *st; + st = (cdrom_chan_curpos_t *)tgt->cmd_ptr; + if (cmd[13] == 'M') + sprintf(buf, "MSF Position %d %d %d %d %d %d", + (integer_t)st->subQ.absolute_address.msf.minute, + (integer_t)st->subQ.absolute_address.msf.second, + (integer_t)st->subQ.absolute_address.msf.frame, + (integer_t)st->subQ.relative_address.msf.minute, + (integer_t)st->subQ.relative_address.msf.second, + (integer_t)st->subQ.relative_address.msf.frame); + else + sprintf(buf, "ABS Position %d %d", (integer_t) + (st->subQ.absolute_address.lba.lba1<<24)+ + (st->subQ.absolute_address.lba.lba2<<16)+ + (st->subQ.absolute_address.lba.lba3<< 8)+ + st->subQ.absolute_address.lba.lba4, + (integer_t) + (st->subQ.relative_address.lba.lba1<<24)+ + (st->subQ.relative_address.lba.lba2<<16)+ + (st->subQ.relative_address.lba.lba3<< 8)+ + st->subQ.relative_address.lba.lba4); + tgt->dev_info.cdrom.result_available = TRUE; + } + } + break; + + case 'T': + /* "Get TH" */ + if_it_can_do(read_toc) { + rc = scsi_read_toc(tgt, TRUE, 1, PAGE_SIZE, ior); + if (rc == SCSI_RET_SUCCESS) { + cdrom_toc_t *toc = (cdrom_toc_t *)tgt->cmd_ptr; + sprintf(buf, "toc header: %d %d %d", + (toc->len1 << 8) + toc->len2, + toc->first_track, + toc->last_track); + tgt->dev_info.cdrom.result_available = TRUE; + } + } + break; + + case 'S': + /* "Get Status" */ + if_it_can_do(get_status) { + rc = scsi_read_subchannel(tgt, + TRUE, + SCSI_CMD_RS_FMT_CURPOS, + 0, + ior); + if (rc == SCSI_RET_SUCCESS) { + cdrom_chan_curpos_t *st; + st = (cdrom_chan_curpos_t *)tgt->cmd_ptr; + decode_status(buf, st->audio_status); + tgt->dev_info.cdrom.result_available = TRUE; + } + } + break; + } + break; + + case 'P': + switch (cmd[5]) { + case 'A': + /* "Play A startM startS startF endM endS endF" */ + if_it_can_do(play_msf) { + + int sm, ss, sf, em, es, ef; + + sscanf(&cmd[7], "%d %d %d %d %d %d", + &sm, &ss, &sf, &em, &es, &ef); + + rc = scsi_play_audio_msf(tgt, + sm, ss, sf, + em, es, ef, + ior); + } + break; + + case 'T': + /* "Play TI startT startI endT endI" */ + if_it_can_do(play_ti) { + + int st, si, et, ei; + + sscanf(&cmd[8], "%d %d %d %d", + &st, &si, &et, &ei); + + rc = scsi_play_audio_track_index(tgt, + st, si, et, ei, ior); + } + break; + } + break; + + case 'R': + /* "Resume" */ + if_it_can_do(pause_resume) + rc = scsi_pause_resume(tgt, FALSE, ior); + break; + + case 'S': + switch (cmd[2]) { + + case 'a': + /* "Start" */ + rc = scsi_start_unit(tgt, SCSI_CMD_SS_START, ior); + break; + + case 'o': + /* "Stop" */ + if_it_can_do(pause_resume) + rc = scsi_pause_resume(tgt, TRUE, ior); + break; + + case 't': + /* "Set V chan0vol chan1vol chan2vol chan3vol" */ + if_it_can_do(volume_control) { + + int v0, v1, v2, v3; + cdrom_audio_page_t au, *aup; + + rc = scsi_mode_sense(tgt, + SCSI_CD_AUDIO_PAGE, + sizeof(au), + ior); + if (rc == SCSI_RET_SUCCESS) { + + sscanf(&cmd[6], "%d %d %d %d", + &v0, &v1, &v2, &v3); + + aup = (cdrom_audio_page_t *) tgt->cmd_ptr; + au = *aup; + /* au.h.bdesc ... */ + au.vol0 = v0; + au.vol1 = v1; + au.vol2 = v2; + au.vol3 = v3; + au.imm = 1; + au.aprv = 0; + + zero_ior( ior ); + + rc = scsi2_mode_select(tgt, FALSE, + &au, sizeof(au), ior); + } + } + break; + } + break; + + case 'T': + /* "Toc MSF|ABS trackno" */ + if_it_can_do(read_toc) { + + int t, m; + + sscanf(&cmd[8], "%d", &t); + rc = scsi_read_toc( tgt, cmd[4]=='M', t, PAGE_SIZE, ior); + + if (rc == SCSI_RET_SUCCESS) { + + cdrom_toc_t *toc = (cdrom_toc_t *)tgt->cmd_ptr; + + sprintf(buf, "TOC from track %d:\n", t); + m = (toc->len1 << 8) + toc->len2; + m -= 4; /* header */ + for (t = 0; m > 0; t++, m -= sizeof(struct cdrom_toc_desc)) { + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "%d %d %d %d %d %d\n", + toc->descs[t].control, + toc->descs[t].adr, + toc->descs[t].trackno, + (integer_t)toc->descs[t].absolute_address.msf.minute, + (integer_t)toc->descs[t].absolute_address.msf.second, + (integer_t)toc->descs[t].absolute_address.msf.frame); + else + sprintf(buf, "%d %d %d %d\n", + toc->descs[t].control, + toc->descs[t].adr, + toc->descs[t].trackno, + (toc->descs[t].absolute_address.lba.lba1<<24)+ + (toc->descs[t].absolute_address.lba.lba2<<16)+ + (toc->descs[t].absolute_address.lba.lba3<<8)+ + toc->descs[t].absolute_address.lba.lba4); + } + tgt->dev_info.cdrom.result_available = TRUE; + } + } + break; + } + + if (rc == SCSI_RET_SUCCESS) + ret = D_SUCCESS; + + /* We are stateless, but.. */ + if (rc == SCSI_RET_NEED_SENSE) { + zero_ior( ior ); + tgt->ior = ior; + scsi_request_sense(tgt, ior, 0); + iowait(ior); + if (scsi_check_sense_data(tgt, tgt->cmd_ptr)) + scsi_print_sense_data(tgt->cmd_ptr); + } + + io_req_free(ior); + return ret; +} + +private char st_invalid [] = "Drive would not say"; +private char st_playing [] = "Playing"; +private char st_paused [] = "Suspended"; +private char st_complete[] = "Done playing"; +private char st_error [] = "Stopped in error"; +private char st_nothing [] = "Idle"; + +private void +decode_status( + char *buf, + unsigned char audio_status) +{ + switch (audio_status) { + case SCSI_CDST_INVALID: + sprintf(buf, st_invalid); break; + case SCSI_CDST_PLAYING: + sprintf(buf, st_playing); break; + case SCSI_CDST_PAUSED: + sprintf(buf, st_paused); break; + case SCSI_CDST_COMPLETED: + sprintf(buf, st_complete); break; + case SCSI_CDST_ERROR: + sprintf(buf, st_error); break; + case SCSI_CDST_NO_STATUS: + sprintf(buf, st_nothing); break; + } +} + +/* some vendor specific use this instead */ +private void +decode_status_1( + char *buf, + unsigned char audio_status) +{ + switch (audio_status) { + case 0: sprintf(buf, st_playing ); break; + case 1: + case 2: sprintf(buf, st_paused ); break; + case 3: sprintf(buf, st_complete ); break; + default: + sprintf(buf, "Unknown status" ); break; + } +} + + +private void +curse_the_vendor( + red_list_t *list, + boolean_t not_really) +{ + if (not_really) return; + + printf("%s\n%s\n%s\n%s\n", + "The CDROM you use is not fully SCSI-2 compliant.", + "We invite You to contact Your vendor and ask", + "that they provide You with a firmware upgrade.", + "Here is a list of some known deficiencies"); + + printf("Vendor: %s Product: %s.. Revision: %s..\n", + list->vendor, list->product, list->rev); + +#define check(x,y,z) \ + if (list->x) printf("Command code x%x %s not supported\n", y, z); + + check(can_play_audio, SCSI_CMD_PLAY_AUDIO, "PLAY_AUDIO"); + check(eject, SCSI_CMD_START_STOP_UNIT, + "START_STOP_UNIT, flag EJECT(0x2) in byte 5"); + check(current_position, SCSI_CMD_READ_SUBCH, "READ_SUBCHANNEL"); + check(read_toc, SCSI_CMD_READ_TOC, "READ_TOC"); +/* check(get_status, ...); duplicate of current_position */ + check(play_msf, SCSI_CMD_PLAY_AUDIO_MSF, "PLAY_AUDIO_MSF"); + check(play_ti, SCSI_CMD_PLAY_AUDIO_TI, "PLAY_AUDIO_TRACK_INDEX"); + check(pause_resume, SCSI_CMD_PAUSE_RESUME, "PAUSE_RESUME"); + check(volume_control, SCSI_CMD_MODE_SELECT, + "MODE_SELECT, AUDIO page(0xe)"); + +#undef check + printf("Will work around these problems...\n"); +} + +/* + * Ancillaries + */ +cd_strategy(ior) + register io_req_t ior; +{ + return rz_simpleq_strategy( ior, cd_start); +} + +void cd_start( tgt, done) + target_info_t *tgt; + boolean_t done; +{ + io_req_t ior; + + ior = tgt->ior; + if (done && ior) { + tgt->ior = 0; + iodone(ior); + return; + } + panic("cd start"); /* uhu? */ +} + +/* + * When the hardware cannot + */ +private scsi_ret_t +op_not_supported( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + /* + * The command is not implemented, no way around it + */ + sprintf(tgt->dev_info.cdrom.result, unsupported); + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +/****************************************/ +/* Vendor Specific Operations */ +/****************************************/ + + /* DEC RRD42 */ + +#define SCSI_CMD_DEC_SET_ADDRESS_FORMAT 0xc0 +# define scsi_cmd_saf_fmt scsi_cmd_xfer_len_2 + +#define SCSI_CMD_DEC_PLAYBACK_STATUS 0xc4 +typedef struct { + unsigned char xxx; + BITFIELD_2(unsigned char, + is_msf: 1, + xxx1: 7); + unsigned char data_len1; + unsigned char data_len0; + unsigned char audio_status; + BITFIELD_2(unsigned char, + control : 4, + xxx2 : 4); + cdrom_addr_t address; + BITFIELD_2(unsigned char, + chan0_select : 4, + xxx3 : 4); + unsigned char chan0_volume; + BITFIELD_2(unsigned char, + chan1_select : 4, + xxx4 : 4); + unsigned char chan1_volume; + BITFIELD_2(unsigned char, + chan2_select : 4, + xxx5 : 4); + unsigned char chan2_volume; + BITFIELD_2(unsigned char, + chan3_select : 4, + xxx6 : 4); + unsigned char chan3_volume; +} dec_playback_status_t; + +#define SCSI_CMD_DEC_PLAYBACK_CONTROL 0xc9 +typedef struct { + unsigned char xxx0; + BITFIELD_2(unsigned char, + fmt : 1, + xxx1 : 7); + unsigned char xxx[8]; + BITFIELD_2(unsigned char, + chan0_select : 4, + xxx3 : 4); + unsigned char chan0_volume; + BITFIELD_2(unsigned char, + chan1_select : 4, + xxx4 : 4); + unsigned char chan1_volume; + BITFIELD_2(unsigned char, + chan2_select : 4, + xxx5 : 4); + unsigned char chan2_volume; + BITFIELD_2(unsigned char, + chan3_select : 4, + xxx6 : 4); + unsigned char chan3_volume; +} dec_playback_control_t; + + +#if 0 + +private scsi_ret_t +rrd42_status( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_ret_t rc; + char *buf = tgt->dev_info.cdrom.result; + scsi_command_group_2 c; + dec_playback_status_t *st; + + /* We might have to specify addressing fmt */ + if (cmd[4] == 'P') { + scsi_command_group_2 saf; + + bzero(&saf, sizeof(saf)); + saf.scsi_cmd_code = SCSI_CMD_DEC_SET_ADDRESS_FORMAT; + saf.scsi_cmd_saf_fmt = (cmd[13] == 'A') ? 0 : 1; + + rc = cdrom_vendor_specific(tgt, &saf, 0, 0, 0, ior); + + if (rc != SCSI_RET_SUCCESS) return rc; + + zero_ior( ior ); + } + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_DEC_PLAYBACK_STATUS; + c.scsi_cmd_xfer_len_2 = sizeof(*st); + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior); + + if (rc != SCSI_RET_SUCCESS) return rc; + + st = (dec_playback_status_t *) tgt->cmd_ptr; + + if (cmd[4] == 'S') + decode_status( buf, st->audio_status+0x11 ); + else { + if (st->is_msf) + sprintf(buf, "MSF Position %d %d %d", + (integer_t)st->address.msf.minute, + (integer_t)st->address.msf.second, + (integer_t)st->address.msf.frame); + else + sprintf(buf, "ABS Position %d", (integer_t) + (st->address.lba.lba1<<24)+ + (st->address.lba.lba2<<16)+ + (st->address.lba.lba3<< 8)+ + st->address.lba.lba4); + } + tgt->dev_info.cdrom.result_available = TRUE; + return rc; +} +#endif + +private scsi_ret_t +rrd42_set_volume( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + dec_playback_control_t req; + int v0, v1, v2, v3; + + sscanf(&cmd[6], "%d %d %d %d", &v0, &v1, &v2, &v3); + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_DEC_PLAYBACK_CONTROL; + c.scsi_cmd_xfer_len_2 = sizeof(req); + bzero(&req, sizeof(req)); + if (v0) { + req.chan0_select = 1; + req.chan0_volume = v0; + } + if (v1) { + req.chan1_select = 2; + req.chan1_volume = v1; + } + if (v2) { + req.chan2_select = 4; + req.chan2_volume = v2; + } + if (v3) { + req.chan3_select = 8; + req.chan3_volume = v3; + } + return cdrom_vendor_specific(tgt, &c, &req, sizeof(req), 0, ior); +} + + /* NEC CD-ROM */ + +#define SCSI_CMD_NEC_READ_TOC 0xde +typedef struct { + unsigned char xxx[9]; + unsigned char first_track; + unsigned char xxx1[9]; + unsigned char last_track; + unsigned char xxx2[9]; + unsigned char lead_out_addr[3]; + struct { + BITFIELD_2(unsigned char, + adr : 4, + ctrl : 4); + unsigned char xxx3[6]; + unsigned char address[3]; + } track_info[1]; /* VARSIZE */ +} nec_toc_data_t; + +#define SCSI_CMD_NEC_SEEK_TRK 0xd8 +#define SCSI_CMD_NEC_PLAY_AUDIO 0xd9 +#define SCSI_CMD_NEC_PAUSE 0xda +#define SCSI_CMD_NEC_EJECT 0xdc + +#define SCSI_CMD_NEC_READ_SUBCH_Q 0xdd +typedef struct { + unsigned char audio_status; /* see decode_status_1 */ + BITFIELD_2(unsigned char, + ctrl : 4, + xxx1 : 4); + unsigned char trackno; + unsigned char indexno; + unsigned char relative_address[3]; + unsigned char absolute_address[3]; +} nec_subch_data_t; + +/* + * Reserved bits in byte1 + */ +#define NEC_LR_PLAY_MODE 0x01 /* RelAdr bit overload */ +#define NEC_LR_STEREO 0x02 /* mono/stereo */ + +/* + * Vendor specific bits in the control byte. + * NEC uses them to specify the addressing mode + */ +#define NEC_CTRL_A_ABS 0x00 /* XXX not sure about this */ +#define NEC_CTRL_A_MSF 0x40 /* min/sec/frame */ +#define NEC_CTRL_A_TI 0x80 /* track/index */ +#define NEC_CTRL_A_CURRENT 0xc0 /* same as last specified */ + +private scsi_ret_t +nec_eject( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_NEC_EJECT; + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +nec_subchannel( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + nec_subch_data_t *st; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_NEC_READ_SUBCH_Q; + c.scsi_cmd_lun_and_relbit = sizeof(*st); /* Sic! */ + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + st = (nec_subch_data_t *) tgt->cmd_ptr; + + /* Status or Position ? */ + + if (cmd[4] == 'S') { + decode_status_1( buf, st->audio_status); + } else { + + /* XXX can it do ABS addressing e.g. 'logical' ? */ + + sprintf(buf, "MSF Position %d %d %d %d %d %d", + (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */ + (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */ + } + + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +private scsi_ret_t +nec_read_toc( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + nec_toc_data_t *t; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + int first, last, i; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_NEC_READ_TOC; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO; + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, 512/*XXX*/, ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + t = (nec_toc_data_t *) tgt->cmd_ptr; + + first = bcd_to_decimal(t->first_track); + last = bcd_to_decimal(t->last_track); + + /* + * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all + */ + if (cmd[0] == 'G') { + sprintf(buf, "toc header: %d %d %d", + sizeof(*t) + sizeof(t->track_info) * (last - first - 1), + first, last); + goto out; + } + + /* + * The whole shebang + */ + sscanf(&cmd[8], "%d", &i); + sprintf(buf, "TOC from track %d:\n", i); + + last -= first; + i -= first; + while ((i >= 0) && (i <= last)) { + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "%d %d %d %d %d %d\n", + t->track_info[i].ctrl, + t->track_info[i].adr, + first + i, + bcd_to_decimal(t->track_info[i].address[0]), + bcd_to_decimal(t->track_info[i].address[1]), + bcd_to_decimal(t->track_info[i].address[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "%d %d %d %d\n", + t->track_info[i].ctrl, + t->track_info[i].adr, + first + i, + bcd_to_decimal(t->track_info[i].address[0]) * 10000 + + bcd_to_decimal(t->track_info[i].address[1]) * 100 + + bcd_to_decimal(t->track_info[i].address[2])); + i++; + } + /* To know how long the last track is */ + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "%d %d %d %d %d %d\n", + 0, 1, 0xaa /* User expects this */, + bcd_to_decimal(t->lead_out_addr[0]), + bcd_to_decimal(t->lead_out_addr[1]), + bcd_to_decimal(t->lead_out_addr[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "%d %d %d %d\n", + 0, 1, 0xaa /* User expects this */, + bcd_to_decimal(t->lead_out_addr[0]) * 10000 + + bcd_to_decimal(t->lead_out_addr[1]) * 100 + + bcd_to_decimal(t->lead_out_addr[2])); +out: + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + + +private scsi_ret_t +nec_play( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + int sm, ss, sf, em, es, ef; + int st, si, et, ei; + scsi_ret_t rc; + + /* + * Seek to desired position + */ + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_NEC_SEEK_TRK; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE; + + /* + * Play_msf or Play_ti + */ + if (cmd[5] == 'A') { + /* "Play A startM startS startF endM endS endF" */ + + sscanf(&cmd[7], "%d %d %d %d %d %d", + &sm, &ss, &sf, &em, &es, &ef); + + c.scsi_cmd_lba1 = decimal_to_bcd(sm); + c.scsi_cmd_lba2 = decimal_to_bcd(ss); + c.scsi_cmd_lba3 = decimal_to_bcd(sf); + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF; + + } else { + /* "Play TI startT startI endT endI" */ + + sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei); + + c.scsi_cmd_lba1 = decimal_to_bcd(st); + c.scsi_cmd_lba2 = decimal_to_bcd(si); + c.scsi_cmd_lba3 = 0; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI; + + } + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + /* + * Now ask it to play until.. + */ + zero_ior( ior ); + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_NEC_PLAY_AUDIO; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO; + + if (cmd[5] == 'A') { + c.scsi_cmd_lba1 = decimal_to_bcd(em); + c.scsi_cmd_lba2 = decimal_to_bcd(es); + c.scsi_cmd_lba3 = decimal_to_bcd(ef); + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF; + } else { + c.scsi_cmd_lba1 = decimal_to_bcd(et); + c.scsi_cmd_lba2 = decimal_to_bcd(ei); + c.scsi_cmd_lba3 = 0; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI; + } + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +nec_pause_resume( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + /* + * "Resume" or "Stop" + */ + if (cmd[0] == 'R') { + c.scsi_cmd_code = SCSI_CMD_NEC_PLAY_AUDIO; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_CURRENT; + } else { + c.scsi_cmd_code = SCSI_CMD_NEC_PAUSE; + } + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + + /* TOSHIBA CD-ROM DRIVE:XM 3232 */ + +#define SCSI_CMD_TOSHIBA_SEEK_TRK 0xc0 +#define SCSI_CMD_TOSHIBA_PLAY_AUDIO 0xc1 +#define SCSI_CMD_TOSHIBA_PAUSE_AUDIO 0xc2 +#define SCSI_CMD_TOSHIBA_EJECT 0xc4 + +#define SCSI_CMD_TOSHIBA_READ_SUBCH_Q 0xc6 +typedef nec_subch_data_t toshiba_subch_data_t; +/* audio status -> decode_status_1 */ + +#define SCSI_CMD_TOSHIBA_READ_TOC_ENTRY 0xc7 +typedef struct { + unsigned char first_track; + unsigned char last_track; + unsigned char xxx[2]; +} toshiba_toc_header_t; +typedef struct { + unsigned char address[4]; +} toshiba_toc_data_t; + + +private scsi_ret_t +toshiba_eject( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_EJECT; + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +toshiba_subchannel( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + toshiba_subch_data_t *st; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_SUBCH_Q; + c.scsi_cmd_lun_and_relbit = sizeof(*st); /* Sic! */ + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + st = (toshiba_subch_data_t *) tgt->cmd_ptr; + + /* Status or Position ? */ + + if (cmd[4] == 'S') { + decode_status_1( buf, st->audio_status); + } else { + + /* XXX can it do ABS addressing e.g. 'logical' ? */ + + sprintf(buf, "MSF Position %d %d %d %d %d %d", + (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */ + (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */ + } + + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +private scsi_ret_t +toshiba_read_toc( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + toshiba_toc_data_t *t; + toshiba_toc_header_t *th; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + int first, last, i; + + /* TOC header first */ + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY; + c.scsi_cmd_lun_and_relbit = 0; + c.scsi_cmd_lba1 = 0; + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*th), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + th = (toshiba_toc_header_t *) tgt->cmd_ptr; + + first = bcd_to_decimal(th->first_track); + last = bcd_to_decimal(th->last_track); + + /* + * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all + */ + if (cmd[0] == 'G') { + sprintf(buf, "toc header: %d %d %d", + sizeof(*th) + sizeof(*t) * (last - first + 1), + first, last); + goto out; + } + + /* + * The whole shebang + */ + sscanf(&cmd[8], "%d", &i); + sprintf(buf, "TOC from track %d:\n", i); + + while (i <= last) { + bzero(&c, sizeof(c)); + + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY; + c.scsi_cmd_lun_and_relbit = 2; + c.scsi_cmd_lba1 = decimal_to_bcd(i); + + zero_ior( ior ); + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*t), ior); + if (rc != SCSI_RET_SUCCESS) break; + + t = (toshiba_toc_data_t *) tgt->cmd_ptr; + + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "0 0 %d %d %d %d\n", + i, + bcd_to_decimal(t->address[0]), + bcd_to_decimal(t->address[1]), + bcd_to_decimal(t->address[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "0 0 %d %d\n", + i, + bcd_to_decimal(t->address[0]) * 10000 + + bcd_to_decimal(t->address[1]) * 100 + + bcd_to_decimal(t->address[2])); + i++; + } + + /* Must simulate the lead-out track */ + bzero(&c, sizeof(c)); + + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY; + c.scsi_cmd_lun_and_relbit = 1; + c.scsi_cmd_lba1 = 0; + + zero_ior( ior ); + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*t), ior); + if (rc != SCSI_RET_SUCCESS) goto out; + + t = (toshiba_toc_data_t *) tgt->cmd_ptr; + + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "0 0 %d %d %d %d\n", + i, + bcd_to_decimal(t->address[0]), + bcd_to_decimal(t->address[1]), + bcd_to_decimal(t->address[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "0 0 %d %d\n", + i, + bcd_to_decimal(t->address[0]) * 10000 + + bcd_to_decimal(t->address[1]) * 100 + + bcd_to_decimal(t->address[2])); + i++; + +out: + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + + +private scsi_ret_t +toshiba_play( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + int sm, ss, sf, em, es, ef; + int st, si, et, ei; + scsi_ret_t rc; + + /* + * Seek to desired position + */ + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_SEEK_TRK; + + /* + * Play_msf or Play_ti + */ + if (cmd[5] == 'A') { + /* "Play A startM startS startF endM endS endF" */ + + sscanf(&cmd[7], "%d %d %d %d %d %d", + &sm, &ss, &sf, &em, &es, &ef); + + c.scsi_cmd_lba1 = decimal_to_bcd(sm); + c.scsi_cmd_lba2 = decimal_to_bcd(ss); + c.scsi_cmd_lba3 = decimal_to_bcd(sf); + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF; + + } else { + /* "Play TI startT startI endT endI" */ + + sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei); + + c.scsi_cmd_lba1 = decimal_to_bcd(st); + c.scsi_cmd_lba2 = decimal_to_bcd(si); + c.scsi_cmd_lba3 = 0; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI; + + } + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + /* + * Now ask it to play until.. + */ + zero_ior( ior ); + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PLAY_AUDIO; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO; + + if (cmd[5] == 'A') { + c.scsi_cmd_lba1 = decimal_to_bcd(em); + c.scsi_cmd_lba2 = decimal_to_bcd(es); + c.scsi_cmd_lba3 = decimal_to_bcd(ef); + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF; + } else { + c.scsi_cmd_lba1 = decimal_to_bcd(et); + c.scsi_cmd_lba2 = decimal_to_bcd(ei); + c.scsi_cmd_lba3 = 0; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI; + } + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +toshiba_pause_resume( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + /* + * "Resume" or "Stop" + */ + if (cmd[0] == 'R') { + /* ???? would have to remember last cmd ???? */ +/* broken ! */ + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PLAY_AUDIO; + c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO; + c.scsi_cmd_ctrl_byte = NEC_CTRL_A_CURRENT; + } else { + c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PAUSE_AUDIO; + } + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + + +#if 0 + /* I have info on these drives, but no drive to test */ + + /* PIONEER DRM-600 */ + +#define SCSI_CMD_PIONEER_EJECT 0xc0 + +#define SCSI_CMD_PIONEER_READ_TOC 0xc1 +typedef struct { + unsigned char first_track; + unsigned char last_track; + unsigned char xxx[2]; +} pioneer_toc_hdr_t; +typedef struct { + unsigned char ctrl; + unsigned char address[3]; +} pioneer_toc_info_t; + +#define SCSI_CMD_PIONEER_READ_SUBCH 0xc2 +typedef struct { + BITFIELD_2(unsigned char, + ctrl : 4, + xxx1 : 4); + unsigned char trackno; + unsigned char indexno; + unsigned char relative_address[3]; + unsigned char absolute_address[3]; +} pioneer_subch_data_t; + +#define SCSI_CMD_PIONEER_SEEK_TRK 0xc8 +#define SCSI_CMD_PIONEER_PLAY_AUDIO 0xc9 +#define SCSI_CMD_PIONEER_PAUSE 0xca + +#define SCSI_CMD_PIONEER_AUDIO_STATUS 0xcc +typedef struct { + unsigned char audio_status; + unsigned char xxx[5]; +} pioneer_status_t; + +/* + * Reserved bits in byte1 + */ +#define PIONEER_LR_END_ADDR 0x10 +#define PIONEER_LR_PAUSE 0x10 +#define PIONEER_LR_RESUME 0x00 + +/* + * Vendor specific bits in the control byte. + */ +#define PIONEER_CTRL_TH 0x00 /* TOC header */ +#define PIONEER_CTRL_TE 0x80 /* one TOC entry */ +#define PIONEER_CTRL_LO 0x40 /* lead-out track info */ + +#define PIONEER_CTRL_A_MSF 0x40 /* min/sec/frame addr */ + +private scsi_ret_t +pioneer_eject( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_EJECT; + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +pioneer_position( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + scsi_ret_t rc; + char *buf = tgt->dev_info.cdrom.result; + pioneer_subch_data_t *st; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_SUBCH; + c.scsi_cmd_xfer_len_2 = sizeof(pioneer_subch_data_t); /* 9 bytes */ + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_subch_data_t), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + st = (pioneer_subch_data_t *) tgt->cmd_ptr; + + /* XXX can it do ABS addressing e.g. 'logical' ? */ + + sprintf(buf, "MSF Position %d %d %d %d %d %d", + (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */ + (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */ + (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */ + (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */ + + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +private scsi_ret_t +pioneer_toc( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + pioneer_toc_hdr_t *th; + pioneer_toc_info_t *t; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + int first, last, i; + + /* Read header first */ + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC; + c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_hdr_t); + c.scsi_cmd_ctrl_byte = PIONEER_CTRL_TH; + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_hdr_t), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + th = (pioneer_toc_hdr_t *)tgt->cmd_ptr; + first = bcd_to_decimal(th->first_track); + last = bcd_to_decimal(th->last_track); + + /* + * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all + */ + if (cmd[0] == 'G') { + sprintf(buf, "toc header: %d %d %d", 0, first, last); + goto out; + } + + /* + * Must do it one track at a time + */ + sscanf(&cmd[8], "%d", &i); + sprintf(buf, "TOC from track %d:\n", i); + + for ( ; i <= last; i++) { + zero_ior(ior); + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC; + c.scsi_cmd_lba4 = decimal_to_bcd(i); + c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_info_t); + c.scsi_cmd_ctrl_byte = PIONEER_CTRL_TE; + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_info_t), ior); + if (rc != SCSI_RET_SUCCESS) break; + + t = (pioneer_toc_info_t *)tgt->cmd_ptr; + + buf += strlen(buf); + if (cmd[4] == 'M') + sprintf(buf, "%d %d %d %d %d %d\n", + t->ctrl, 0, i, + bcd_to_decimal(t->address[0]), + bcd_to_decimal(t->address[1]), + bcd_to_decimal(t->address[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "%d %d %d %d\n", + t->ctrl, 0, i, + bcd_to_decimal(t->address[0]) * 10000 + + bcd_to_decimal(t->address[1]) * 100 + + bcd_to_decimal(t->address[2])); + } + /* To know how long the last track is */ + zero_ior(ior); + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC; + c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_info_t); + c.scsi_cmd_ctrl_byte = PIONEER_CTRL_LO; + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_info_t), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + buf += strlen(buf); + t = (pioneer_toc_info_t *)tgt->cmd_ptr; + if (cmd[4] == 'M') + sprintf(buf, "%d %d %d %d %d %d\n", + t->ctrl, 0, 0xaa /* User expects this */, + bcd_to_decimal(t->address[0]), + bcd_to_decimal(t->address[1]), + bcd_to_decimal(t->address[2])); + else +/* THIS IS WRONG */ + sprintf(buf, "%d %d %d %d\n", + t->ctrl, 0, 0xaa /* User expects this */, + bcd_to_decimal(t->address[0]) * 10000 + + bcd_to_decimal(t->address[1]) * 100 + + bcd_to_decimal(t->address[2])); + +out: + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +private scsi_ret_t +pioneer_status( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + pioneer_status_t *st; + char *buf = tgt->dev_info.cdrom.result; + scsi_ret_t rc; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_AUDIO_STATUS; + c.scsi_cmd_xfer_len_2 = sizeof(pioneer_status_t); /* 6 bytes */ + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_status_t), ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + st = (pioneer_status_t*) tgt->cmd_ptr; + decode_status_1( buf, st->audio_status); + + tgt->dev_info.cdrom.result_available = TRUE; + return SCSI_RET_SUCCESS; +} + +private scsi_ret_t +pioneer_play( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + int sm, ss, sf, em, es, ef; + int st, si, et, ei; + scsi_ret_t rc; + + /* + * Seek to desired position + */ + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_SEEK_TRK; + /* + * Play_msf or Play_ti + */ + if (cmd[5] == 'A') { + /* "Play A startM startS startF endM endS endF" */ + + sscanf(&cmd[7], "%d %d %d %d %d %d", + &sm, &ss, &sf, &em, &es, &ef); + + c.scsi_cmd_lba2 = decimal_to_bcd(sm); + c.scsi_cmd_lba3 = decimal_to_bcd(ss); + c.scsi_cmd_lba4 = decimal_to_bcd(sf); + c.scsi_cmd_ctrl_byte = PIONEER_CTRL_A_MSF; + + } else { + /* "Play TI startT startI endT endI" */ + + sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei); + + c.scsi_cmd_lba3 = decimal_to_bcd(st); + c.scsi_cmd_lba4 = decimal_to_bcd(si); + c.scsi_cmd_ctrl_byte = 0x80; /* Pure speculation!! */ + + } + + rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); + if (rc != SCSI_RET_SUCCESS) return rc; + + /* + * Now ask it to play until.. + */ + zero_ior( ior ); + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_PLAY_AUDIO; + c.scsi_cmd_lun_and_relbit = PIONEER_LR_END_ADDR; + + if (cmd[5] == 'A') { + c.scsi_cmd_lba2 = decimal_to_bcd(em); + c.scsi_cmd_lba3 = decimal_to_bcd(es); + c.scsi_cmd_lba4 = decimal_to_bcd(ef); + c.scsi_cmd_ctrl_byte = PIONEER_CTRL_A_MSF; + } else { + c.scsi_cmd_lba3 = decimal_to_bcd(et); + c.scsi_cmd_lba4 = decimal_to_bcd(ei); + c.scsi_cmd_ctrl_byte = 0x80; /* Pure speculation! */ + } + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + +private scsi_ret_t +pioneer_pause_resume( + target_info_t *tgt, + char *cmd, + io_req_t ior) +{ + scsi_command_group_2 c; + + bzero(&c, sizeof(c)); + c.scsi_cmd_code = SCSI_CMD_PIONEER_PAUSE; + /* + * "Resume" or "Stop" + */ + if (cmd[0] == 'S') + c.scsi_cmd_lun_and_relbit = PIONEER_LR_PAUSE; + else + c.scsi_cmd_lun_and_relbit = PIONEER_LR_RESUME; + + return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior); +} + + /* DENON DRD-253 */ + +#define SCSI_CMD_DENON_PLAY_AUDIO 0x22 +#define SCSI_CMD_DENON_EJECT 0xe6 +#define SCSI_CMD_DENON_PAUSE_AUDIO 0xe7 +#define SCSI_CMD_DENON_READ_TOC 0xe9 +#define SCSI_CMD_DENON_READ_SUBCH 0xeb + + + /* HITACHI 1750 */ + +#define SCSI_CMD_HITACHI_PLAY_AUDIO_MSF 0xe0 +#define SCSI_CMD_HITACHI_PAUSE_AUDIO 0xe1 +#define SCSI_CMD_HITACHI_EJECT 0xe4 +#define SCSI_CMD_HITACHI_READ_SUBCH 0xe5 +#define SCSI_CMD_HITACHI_READ_TOC 0xe8 + +#endif + +/* + * Tabulate all of the above + */ +private red_list_t cdrom_exceptions[] = { + +#if 0 + For documentation purposes, here are some SCSI-2 compliant drives: + + Vendor Product Rev Comments + + "SONY " "CD-ROMCDU-541 " "2.6a" The NeXT drive +#endif + + /* vendor, product, rev */ + /* can_play_audio */ + /* eject */ + /* current_position */ + /* read_toc */ + /* get_status */ + /* play_msf */ + /* play_ti */ + /* pause_resume */ + /* volume_control */ + + /* We have seen a "RRD42(C)DEC " "4.5d" */ + { "DEC ", "RRD42", "", + 0, 0, 0, 0, 0, 0, 0, 0, rrd42_set_volume }, + + /* We have seen a "CD-ROM DRIVE:84 " "1.0 " */ + { "NEC ", "CD-ROM DRIVE:84", "", + op_not_supported, nec_eject, nec_subchannel, nec_read_toc, + nec_subchannel, nec_play, nec_play, nec_pause_resume, + op_not_supported }, + + /* We have seen a "CD-ROM DRIVE:XM " "3232" */ + { "TOSHIBA ", "CD-ROM DRIVE:XM", "32", + op_not_supported, toshiba_eject, toshiba_subchannel, toshiba_read_toc, + toshiba_subchannel, toshiba_play, toshiba_play, toshiba_pause_resume, + op_not_supported }, + + { "TOSHIBA ", "CD-ROM DRIVE:XM", "33", + op_not_supported, toshiba_eject, toshiba_subchannel, toshiba_read_toc, + toshiba_subchannel, toshiba_play, toshiba_play, toshiba_pause_resume, + op_not_supported }, + +#if 0 + { "PIONEER ", "???????DRM-6", "", + op_not_supported, pioneer_eject, pioneer_position, pioneer_toc, + pioneer_status, pioneer_play, pioneer_play, pioneer_pause_resume, + op_not_supported }, + + { "DENON ", "DRD 25X", "", ...}, + { "HITACHI ", "CDR 1750S", "", ...}, + { "HITACHI ", "CDR 1650S", "", ...}, + { "HITACHI ", "CDR 3650", "", ...}, + +#endif + + /* Zero terminate this list */ + { 0, } +}; + +private void +check_red_list( + target_info_t *tgt, + scsi2_inquiry_data_t *inq) + +{ + red_list_t *list; + + for (list = &cdrom_exceptions[0]; list->vendor; list++) { + + /* + * Prefix-Match all strings + */ + if ((strncmp(list->vendor, (const char *)inq->vendor_id, + strlen(list->vendor)) == 0) && + (strncmp(list->product, (const char *)inq->product_id, + strlen(list->product)) == 0) && + (strncmp(list->rev, (const char *)inq->product_rev, + strlen(list->rev)) == 0)) { + /* + * One of them.. + */ + if (tgt->dev_info.cdrom.violates_standards != list) { + tgt->dev_info.cdrom.violates_standards = list; + curse_the_vendor( list, TRUE ); + } + return; + } + } +} +#endif /* NSCSI > 0 */ 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 */ diff --git a/scsi/rz_disk.c b/scsi/rz_disk.c new file mode 100644 index 0000000..87a992b --- /dev/null +++ b/scsi/rz_disk.c @@ -0,0 +1,1222 @@ +/* + * 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_disk.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains operations specific to disk-like devices. + * + * Modified by Kevin T. Van Maren to use a common partition code + * with the ide driver, and support 'slices'. + */ + + +#include <scsi/scsi.h> +#if (NSCSI > 0) + +#include <device/buf.h> +#include <device/disk_status.h> +#include <device/device_types.h> +#include <device/param.h> +#include <device/errno.h> + +#include <kern/time_out.h> +#include <machine/machspl.h> /* spl definitions */ +#include <mach/std_types.h> +#include <platforms.h> + +#include <scsi/compat_30.h> +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> +#include <scsi/rz_labels.h> + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#include <sys/kernel.h> /* for hz */ +#endif /*MACH_KERNEL*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include "vm_param.h" +#include <vm/vm_kern.h> +#include <vm/pmap.h> + +extern void scdisk_read(), scdisk_write(), + scsi_long_read(), scsi_long_write(); + +void scdisk_start(); /* forwards */ +void scdisk_start_rw(); +unsigned dkcksum(); + +#if 0 +struct diskpart scsi_array[8*64]; +#endif 0 + + +/* THIS IS THE BOTTOM LAYER FOR THE SCSI PARTITION CODE */ +typedef struct scsi_driver_info { + target_info_t *tgt; + io_req_t ior; + void (*readfun)(); + int sectorsize; +} scsi_driver_info; + +int scsi_read_fun(scsi_driver_info *param, int sectornum, void *buff) +{ + char *psave; + int result = TRUE; /* SUCCESS */ + psave=param->ior->io_data; /* is this necessary ? */ + + param->ior->io_data=buff; + param->ior->io_count = param->sectorsize; + param->ior->io_op = IO_READ; + param->ior->io_error = 0; + param->tgt->ior = param->ior; + + (*param->readfun)( param->tgt, sectornum, param->ior); + iowait(param->ior); + + param->ior->io_data=psave; /* restore the io_data pointer ?? */ + return(result); +} + + + +/* + * Specialized side of the open routine for disks + */ +scsi_ret_t scdisk_open(tgt, req) + target_info_t *tgt; + io_req_t req; +{ + register int i, dev_bsize; + scsi_ret_t ret = /* SCSI_RET_SUCCESS; */ -1; + unsigned int disk_size, secs_per_cyl, sector_size; + scsi_rcap_data_t *cap; + struct disklabel *label; + io_req_t ior; + void (*readfun)() = scdisk_read; + char *data = (char *)0; + + int numpart; + + scsi_driver_info scsi_info; + char drive_name[10]; /* used for disklabel strings */ + + if (tgt->flags & TGT_ONLINE) + return SCSI_RET_SUCCESS; + + /* + * Dummy ior for proper sync purposes + */ + io_req_alloc(ior,0); + ior->io_next = 0; + ior->io_count = 0; + + /* + * Set the LBN to DEV_BSIZE with a MODE SELECT. + * If this fails we try a couple other tricks. + */ + dev_bsize = 0; + for (i = 0; i < 5; i++) { + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scdisk_mode_select(tgt, DEV_BSIZE, ior, 0, 0, 0); + if (ret == SCSI_RET_SUCCESS) { + dev_bsize = DEV_BSIZE; + break; + } + if (ret == SCSI_RET_RETRY) { + timeout(wakeup, tgt, 2*hz); + await(tgt); + } + if (ret == SCSI_RET_DEVICE_DOWN) + goto done; + } +#if 0 + if (ret != SCSI_RET_SUCCESS) { + scsi_error( tgt, SCSI_ERR_MSEL, ret, 0); + ret = D_INVALID_SIZE; + goto done; + } +#endif + /* + * Do a READ CAPACITY to get max size. Check LBN too. + */ + for (i = 0; i < 5; i++) { + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_read_capacity(tgt, 0, ior); + if (ret == SCSI_RET_SUCCESS) + break; + } + if (ret == SCSI_RET_SUCCESS) { + int val; + + cap = (scsi_rcap_data_t*) tgt->cmd_ptr; + disk_size = (cap->lba1<<24) | + (cap->lba2<<16) | + (cap->lba3<< 8) | + cap->lba4; + if (scsi_debug) + printf("rz%d holds %d blocks\n", tgt->unit_no, disk_size); + val = (cap->blen1<<24) | + (cap->blen2<<16) | + (cap->blen3<<8 ) | + cap->blen4; + if (dev_bsize == 0) + dev_bsize = val; + else + if (val != dev_bsize) panic("read capacity bad"); + + if (disk_size > SCSI_CMD_READ_MAX_LBA) + tgt->flags |= TGT_BIG; + + } else { + printf("Unknown disk capacity??\n"); + disk_size = -1; + } + /* + * Mandatory long-form commands ? + */ + if (BGET(scsi_use_long_form,(unsigned char)tgt->masterno,tgt->target_id)) + tgt->flags |= TGT_BIG; + if (tgt->flags & TGT_BIG) + readfun = scsi_long_read; + + /* + * Some CDROMS truly dislike 512 as LBN. + * Use a MODE_SENSE to cover for this case. + */ + if (dev_bsize == 0) { + scsi_mode_sense_data_t *m; + + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_mode_sense(tgt, 0/*pagecode*/, 32/*?*/, ior); + if (ret == SCSI_RET_SUCCESS) { + m = (scsi_mode_sense_data_t *) tgt->cmd_ptr; + dev_bsize = + m->bdesc[0].blen_msb << 16 | + m->bdesc[0].blen << 8 | + m->bdesc[0].blen_lsb; + } + } + + /* + * Find out about the phys disk geometry -- scsi specific + */ + + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = scsi_read_capacity( tgt, 1, ior); + if (ret == SCSI_RET_SUCCESS) { + cap = (scsi_rcap_data_t*) tgt->cmd_ptr; + secs_per_cyl = (cap->lba1<<24) | (cap->lba2<<16) | + (cap->lba3<< 8) | cap->lba4; + secs_per_cyl += 1; + sector_size = (cap->blen1<<24) | (cap->blen2<<16) | + (cap->blen3<<8 ) | cap->blen4; + } else { + sector_size = dev_bsize ? dev_bsize : DEV_BSIZE; + secs_per_cyl = disk_size; + } + if (dev_bsize == 0) + dev_bsize = sector_size; + + if (scsi_debug) + printf("rz%d: %d sect/cyl %d bytes/sec\n", tgt->unit_no, + secs_per_cyl, sector_size); + + /* + * At this point, one way or other, we are committed to + * a given disk capacity and sector size. + */ + tgt->block_size = dev_bsize; + + /* + * Get partition table off pack + */ + +#ifdef MACH_KERNEL + ior->io_data = (char *)kalloc(sector_size); +#endif /*MACH_KERNEL*/ + + scsi_info.tgt=tgt; + scsi_info.ior=ior; + scsi_info.readfun=readfun; + scsi_info.sectorsize=sector_size; + + /* label has NOT been allocated space yet! set to the tgt disklabel */ + label=&scsi_info.tgt->dev_info.disk.l; + + sprintf(drive_name, "sd%d:", tgt->unit_no); + + if (scsi_debug) + printf("Using bogus geometry: 32 sectors/track, 64 heads\n"); + + fudge_bsd_label(label, DTYPE_SCSI, disk_size /* /(32*64)*/ , + 64, 32, sector_size, 8); + + numpart=get_only_partition(&scsi_info, (*scsi_read_fun), + tgt->dev_info.disk.scsi_array, MAX_SCSI_PARTS, disk_size, drive_name); + + printf("%s %d partitions found\n",drive_name,numpart); + + ret=SCSI_RET_SUCCESS; /* if 0, return SCSI_RET_SUCCESS */ + + +done: + io_req_free(ior); + + return(ret); +} + + +/* + * Disk strategy routine + */ +int scdisk_strategy(ior) + register io_req_t ior; +{ + target_info_t *tgt; + register scsi_softc_t *sc; + register int i = ior->io_unit, part; + register unsigned rec, max; + spl_t s; + struct diskpart *label; + + sc = scsi_softc[rzcontroller(i)]; + tgt = sc->target[rzslave(i)]; + part = rzpartition(i); + + /* + * Validate request + */ + + /* readonly ? */ + if ((tgt->flags & TGT_READONLY) && + (ior->io_op & (IO_READ|IO_INTERNAL) == 0)) { + ior->io_error = D_READ_ONLY; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + + rec = ior->io_recnum; + + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) { + if (scsi_debug) + printf("sc strategy -- bad partition\n"); + ior->io_error = D_INVALID_SIZE; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + else max=label->size; + if (max == -1) /* what about 0? */ + max = tgt->dev_info.disk.l.d_secperunit - + + label->start; + + i = (ior->io_count + tgt->block_size - 1) / tgt->block_size; + if (((rec + i) > max) || (ior->io_count < 0) || +#if later + ((rec <= LABELSECTOR) && ((tgt->flags & TGT_WRITE_LABEL) == 0)) +#else + FALSE +#endif + ) { + ior->io_error = D_INVALID_SIZE; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + /* + * Find location on disk: secno and cyl (for disksort) + */ + rec += label->start; + ior->io_residual = rec / tgt->dev_info.disk.l.d_secpercyl; + + /* + * Enqueue operation + */ + s = splbio(); + simple_lock(&tgt->target_lock); + if (tgt->ior) { + disksort(tgt->ior, ior); + simple_unlock(&tgt->target_lock); + splx(s); + } else { + ior->io_next = 0; + tgt->ior = ior; + simple_unlock(&tgt->target_lock); + splx(s); + + scdisk_start(tgt,FALSE); + } + + return D_SUCCESS; +} + +/*#define CHECKSUM*/ +#ifdef CHECKSUM +int max_checksum_size = 0x2000; +#endif CHECKSUM + +/* + * Start/completion routine for disks + */ +void scdisk_start(tgt, done) + target_info_t *tgt; + boolean_t done; +{ + register io_req_t ior = tgt->ior; + register unsigned part; +#ifdef CHECKSUM + register unsigned secno; +#endif + struct diskpart *label; + + if (ior == 0) + return; + + if (tgt->flags & TGT_BBR_ACTIVE) + { + scdisk_bbr_start(tgt, done); + return; + } + + if (done) { + register unsigned int xferred; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + /* 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 ? pifff.. */ + if ((tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) && + ((ior->io_op & IO_INTERNAL) == 0)) { + if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } + goto start; + } else + /* + * Quickly check for errors: if anything goes wrong + * we do a request sense, see if that is what we did. + */ + if (tgt->cur_cmd == SCSI_CMD_REQUEST_SENSE) { + scsi_sense_data_t *sns; + unsigned int blockno; + char *outcome; + + ior->io_op = ior->io_temporary; + + sns = (scsi_sense_data_t *)tgt->cmd_ptr; + if (sns->addr_valid) + blockno = sns->u.xtended.info0 << 24 | + sns->u.xtended.info1 << 16 | + sns->u.xtended.info2 << 8 | + sns->u.xtended.info3; + else { + part = rzpartition(ior->io_unit); + label = lookup_part(tgt->dev_info.disk.scsi_array, part); + blockno = label->start; + blockno += ior->io_recnum; + if (!label) blockno=-1; + } + + if (scsi_check_sense_data(tgt, sns)) { + ior->io_error = 0; + if ((tgt->done == SCSI_RET_RETRY) && + ((ior->io_op & IO_INTERNAL) == 0)) { + delay(1000000);/*XXX*/ + goto start; + } + outcome = "Recovered"; + } else { + outcome = "Unrecoverable"; + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } + if ((tgt->flags & TGT_OPTIONAL_CMD) == 0) { + printf("%s Error, rz%d: %s%s%d\n", outcome, + tgt->target_id + (tgt->masterno * 8), + (ior->io_op & IO_READ) ? "Read" : + ((ior->io_op & IO_INTERNAL) ? "(command)" : "Write"), + " disk error, phys block no. ", blockno); + + scsi_print_sense_data(sns); + + /* + * On fatal read/write errors try replacing the bad block + * The bbr routine will return TRUE iff it took control + * over the target for all subsequent operations. In this + * event, the queue of requests is effectively frozen. + */ + if (ior->io_error && + ((sns->error_class == SCSI_SNS_XTENDED_SENSE_DATA) && + ((sns->u.xtended.sense_key == SCSI_SNS_HW_ERR) || + (sns->u.xtended.sense_key == SCSI_SNS_MEDIUM_ERR))) && + scdisk_bad_block_repl(tgt, blockno)) + return; + } + } + + /* + * See if we had errors + */ + 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; + scsi_request_sense(tgt, ior, 0); + return; + + } else if (tgt->done == SCSI_RET_DEVICE_DOWN) { + ior->io_error = D_DEVICE_DOWN; + ior->io_op |= IO_ERROR; + } else { + printf("%s%x\n", "?rz_disk Disk error, ret=x", tgt->done); + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } + } + /* + * No errors. + * See if we requested more than the max + * (We use io_residual in a flip-side way here) + */ + else if (ior->io_count > (xferred = max_dma_data)) { + ior->io_residual += xferred; + ior->io_count -= xferred; + ior->io_data += xferred; + ior->io_recnum += xferred / tgt->block_size; + goto start; + } + else if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } /* that's it */ + +#ifdef CHECKSUM + if ((ior->io_op & IO_READ) && (ior->io_count < max_checksum_size)) { + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) printf("NOT FOUND!\n"); + secno = ior->io_recnum + label->start; + scdisk_bcheck(secno, ior->io_data, ior->io_count); + } +#endif CHECKSUM + + /* dequeue next one */ + { + io_req_t next; + + simple_lock(&tgt->target_lock); + next = ior->io_next; + tgt->ior = next; + simple_unlock(&tgt->target_lock); + + iodone(ior); + if (next == 0) + return; + + ior = next; + } + +#ifdef CHECKSUM + if (((ior->io_op & IO_READ) == 0) && (ior->io_count < max_checksum_size)) { + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + secno = ior->io_recnum + label->start; + scdisk_checksum(secno, ior->io_data, ior->io_count); + } +#endif CHECKSUM + } + ior->io_residual = 0; +start: + scdisk_start_rw( tgt, ior); +} + +void scdisk_start_rw( tgt, ior) + target_info_t *tgt; + register io_req_t ior; +{ + unsigned int part, secno; + register boolean_t long_form; + struct diskpart *label; + + part = rzpartition(ior->io_unit); + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + if (!label) + printf("NOT FOUND!\n"); + secno = ior->io_recnum + label->start; + + /* Use long form if either big block addresses or + the size is more than we can fit in one byte */ + long_form = (tgt->flags & TGT_BIG) || + (ior->io_count > (256 * tgt->block_size)); + if (ior->io_op & IO_READ) + (long_form ? scsi_long_read : scdisk_read)(tgt, secno, ior); + else if ((ior->io_op & IO_INTERNAL) == 0) + (long_form ? scsi_long_write : scdisk_write)(tgt, secno, ior); +} + +#include <sys/ioctl.h> +#ifdef ULTRIX_COMPAT +#include <mips/PMAX/rzdisk.h> +#endif /*ULTRIX_COMPAT*/ + +io_return_t +scdisk_get_status(dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t *status_count; +{ + struct disklabel *lp; + struct diskpart *label; + + lp = &tgt->dev_info.disk.l; + + switch (flavor) { +#ifdef MACH_KERNEL + case DEV_GET_SIZE: + + label=lookup_part(tgt->dev_info.disk.scsi_array, rzpartition(dev)); + status[DEV_GET_SIZE_DEVICE_SIZE] = label->size * lp->d_secsize; + status[DEV_GET_SIZE_RECORD_SIZE] = tgt->block_size; + *status_count = DEV_GET_SIZE_COUNT; + break; +#endif + + case DIOCGDINFO: + *(struct disklabel *)status = *lp; +#ifdef MACH_KERNEL + *status_count = sizeof(struct disklabel)/sizeof(int); +#endif MACH_KERNEL + break; + + case DIOCGDINFO - (0x10<<16): + *(struct disklabel *)status = *lp; +#ifdef MACH_KERNEL + *status_count = sizeof(struct disklabel)/sizeof(int) - 4; +#endif MACH_KERNEL + break; + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + case SCSI_MODE_SENSE: /*_IOWR(p, 9, struct mode_sel_sns_params) */ + break; + case DIOCGETPT: /*_IOR(p, 1, struct pt) */ + case SCSI_GET_SENSE: /*_IOR(p, 10, struct extended_sense) */ + return ul_disk_ioctl(tgt, flavor, status, status_count); +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#if 0 + case DIOCRFORMAT: + break; +#endif + default: +#ifdef i386 + return(scsi_i386_get_status(dev, tgt, flavor, status, status_count)); +#else i386 + return(D_INVALID_OPERATION); +#endif i386 + } + return D_SUCCESS; +} + +io_return_t +scdisk_set_status(dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + io_return_t error = D_SUCCESS; + struct disklabel *lp; + + lp = &tgt->dev_info.disk.l; + + + switch (flavor) { + case DIOCSRETRIES: +#ifdef MACH_KERNEL + if (status_count != sizeof(int)) + return D_INVALID_SIZE; +#endif /* MACH_KERNEL */ + scsi_bbr_retries = *(int *)status; + break; + + case DIOCWLABEL: + case DIOCWLABEL - (0x10<<16): + if (*(int*)status) + tgt->flags |= TGT_WRITE_LABEL; + else + tgt->flags &= ~TGT_WRITE_LABEL; + break; + case DIOCSDINFO: + case DIOCSDINFO - (0x10<<16): + case DIOCWDINFO: + case DIOCWDINFO - (0x10<<16): +#ifdef MACH_KERNEL + if (status_count != sizeof(struct disklabel) / sizeof(int)) + return D_INVALID_SIZE; +#endif /* MACH_KERNEL */ + error = setdisklabel(lp, (struct disklabel*) status); + if (error || (flavor == DIOCSDINFO) || (flavor == DIOCSDINFO - (0x10<<16))) + return error; + error = scdisk_writelabel(tgt); + break; + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + case SCSI_FORMAT_UNIT: /*_IOW(p, 4, struct format_params) */ + case SCSI_REASSIGN_BLOCK: /*_IOW(p, 5, struct reassign_params) */ + case SCSI_READ_DEFECT_DATA: /*_IOW(p, 6, struct read_defect_params) */ + case SCSI_VERIFY_DATA: /*_IOW(p, 7, struct verify_params) */ + case SCSI_MODE_SELECT: /*_IOW(p, 8, struct mode_sel_sns_params) */ + case SCSI_MODE_SENSE: /*_IOW(p, 9, struct mode_sel_sns_params) */ + case SCSI_GET_INQUIRY_DATA: /*_IOW(p, 11, struct inquiry_info) */ + return ul_disk_ioctl(tgt, flavor, status, status_count); +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#if notyet + case DIOCWFORMAT: + case DIOCSBAD: /* ?? how ? */ +#endif + default: +#ifdef i386 + error = scsi_i386_set_status(dev, tgt, flavor, status, status_count); +#else i386 + error = D_INVALID_OPERATION; +#endif i386 + } + return error; +} + +static int grab_it(tgt, ior) + target_info_t *tgt; + io_req_t ior; +{ + spl_t s; + + s = splbio(); + simple_lock(&tgt->target_lock); + if (!tgt->ior) + tgt->ior = ior; + simple_unlock(&tgt->target_lock); + splx(s); + + if (tgt->ior != ior) + return D_ALREADY_OPEN; + return D_SUCCESS; +} + +/* Write back a label to the disk */ +io_return_t scdisk_writelabel(tgt) + target_info_t *tgt; +{ + +printf("scdisk_writelabel: NO LONGER IMPLEMENTED\n"); +#if 0 +/* Taken out at Bryan's suggestion until 'fixed' for slices */ + + io_req_t ior; + char *data = (char *)0; + struct disklabel *label; + io_return_t error; + int dev_bsize = tgt->block_size; + + io_req_alloc(ior,0); +#ifdef MACH_KERNEL + data = (char *)kalloc(dev_bsize); +#else /*MACH_KERNEL*/ + data = (char *)ior->io_data; +#endif /*MACH_KERNEL*/ + ior->io_next = 0; + ior->io_prev = 0; + ior->io_data = data; + ior->io_count = dev_bsize; + ior->io_op = IO_READ; + ior->io_error = 0; + + if (grab_it(tgt, ior) != D_SUCCESS) { + error = D_ALREADY_OPEN; + goto ret; + } + + scdisk_read( tgt, tgt->dev_info.disk.labelsector, ior); + iowait(ior); + if (error = ior->io_error) + goto ret; + + label = (struct disklabel *) &data[tgt->dev_info.disk.labeloffset]; + *label = tgt->dev_info.disk.l; + + ior->io_next = 0; + ior->io_prev = 0; + ior->io_data = data; + ior->io_count = dev_bsize; + ior->io_op = IO_WRITE; + + while (grab_it(tgt, ior) != D_SUCCESS) ; /* ahem */ + + scdisk_write( tgt, tgt->dev_info.disk.labelsector, ior); + iowait(ior); + + error = ior->io_error; +ret: +#ifdef MACH_KERNEL + if (data) kfree((int)data, dev_bsize); +#endif /*MACH_KERNEL*/ + io_req_free(ior); + return error; + +#endif 0 scdisk_writelabel +return -1; /* FAILURE ? */ +} + +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ +#if ULTRIX_COMPAT + +io_return_t ul_disk_ioctl(tgt, flavor, status, status_count) + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + io_return_t ret; + scsi_ret_t err = SCSI_RET_ABORTED;/*xxx*/ + io_req_t ior; + + if (!suser()) + return EACCES; + + ior = geteblk(sizeof(struct defect_descriptors)); + ior->io_next = 0; + ior->io_count = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ior->io_recnum = 0; + ior->io_residual = 0; + + switch (flavor) { + + case DIOCGETPT: { /*_IOR(p, 1, struct pt) */ + scsi_dec_label_t *p; + struct disklabel *lp; + int i; + + lp = &tgt->dev_info.disk.l; + p = (scsi_dec_label_t *)status; + + p->magic = DEC_PARTITION_MAGIC; + p->in_use = 1; + for (i = 0; i < 8; i++) { + label=lookup_part(tgt->dev_info.disk.scsi_array, part); + p->partitions[i].n_sectors = label->size; + p->partitions[i].offset = label->start; + } + err = SCSI_RET_SUCCESS; + } + break; + + case SCSI_GET_SENSE: { /*_IOR(p, 10, struct extended_sense) */ + scsi_sense_data_t *s; + + s = (scsi_sense_data_t*)tgt->cmd_ptr; + bcopy(s, status, sizeof(*s) + s->u.xtended.add_len - 1); + err = SCSI_RET_SUCCESS; + /* only once */ + bzero(tgt->cmd_ptr, sizeof(scsi_sense_data_t)); + } + break; + + case SCSI_GET_INQUIRY_DATA: { /*_IOR(p, 11, struct inquiry_info) */ + struct mode_sel_sns_params *ms; + + ms = (struct mode_sel_sns_params*)status; + err = scsi_inquiry( tgt, SCSI_INQ_STD_DATA); + if (copyout(tgt->cmd_ptr, ms->msp_addr, sizeof(struct inquiry_info))){ + ret = EFAULT; + goto out; + } + } + break; + + case SCSI_FORMAT_UNIT: { /*_IOW(p, 4, struct format_params) */ + struct format_params *fp; + struct defect_descriptors *df; + unsigned char mode; + unsigned int old_timeout; + + fp = (struct format_params *)status; + df = (struct defect_descriptors*)ior->io_data; + if (fp->fp_length != 0) { + if (copyin(fp->fp_addr, df, sizeof(*df))) { + ret = EFAULT; + goto out; + } + ior->io_count = sizeof(*df); + } else + ior->io_count = 0; + mode = fp->fp_format & SCSI_CMD_FMT_LIST_TYPE; + switch (fp->fp_defects) { + case VENDOR_DEFECTS: + mode |= SCSI_CMD_FMT_FMTDATA|SCSI_CMD_FMT_CMPLIST; + break; + case KNOWN_DEFECTS: + mode |= SCSI_CMD_FMT_FMTDATA; + break; + case NO_DEFECTS: + default: + break; + } + old_timeout = scsi_watchdog_period; + scsi_watchdog_period = 60*60; /* 1 hour should be enough, I hope */ + err = scsi_format_unit( tgt, mode, fp->fp_pattern, + fp->fp_interleave, ior); + scsi_watchdog_period = old_timeout; + /* Make sure we re-read all info afresh */ + tgt->flags = TGT_ALIVE | + (tgt->flags & (TGT_REMOVABLE_MEDIA|TGT_FULLY_PROBED)); + } + break; + + case SCSI_REASSIGN_BLOCK: { /*_IOW(p, 5, struct reassign_params) */ + struct reassign_params *r; + int ndef; + + r = (struct reassign_params*) status; + ndef = r->rp_header.defect_len0 | (r->rp_header.defect_len1 >> 8); + ndef >>= 2; + tgt->ior = ior; + (void) scsi_reassign_blocks( tgt, &r->rp_lbn3, ndef, ior); + iowait(ior); + err = tgt->done; + } + break; + + case SCSI_READ_DEFECT_DATA: { /*_IOW(p, 6, struct read_defect_params) */ + struct read_defect_params *dp; + + dp = (struct read_defect_params *)status; + ior->io_count = ior->io_alloc_size; + if (dp->rdp_alclen > ior->io_count) + dp->rdp_alclen = ior->io_count; + else + ior->io_count = dp->rdp_alclen; + ior->io_op |= IO_READ; + tgt->ior = ior; + err = scsi_read_defect(tgt, dp->rdp_format|0x18, ior); + if (copyout(ior->io_data, dp->rdp_addr, dp->rdp_alclen)) { + ret = EFAULT; + goto out; + } + } + break; + + case SCSI_VERIFY_DATA: { /*_IOW(p, 7, struct verify_params) */ + struct verify_params *v; + unsigned int old_timeout; + + old_timeout = scsi_watchdog_period; + scsi_watchdog_period = 5*60; /* 5 mins enough, I hope */ + v = (struct verify_params *)status; + ior->io_count = 0; + err = scdisk_verify( tgt, v->vp_lbn, v->vp_length, ior); + scsi_watchdog_period = old_timeout; + } + break; + + case SCSI_MODE_SELECT: { /*_IOW(p, 8, struct mode_sel_sns_params) */ + struct mode_sel_sns_params *ms; + + ms = (struct mode_sel_sns_params*)status; + if(copyin(ms->msp_addr, ior->io_data, ms->msp_length)) { + ret = EFAULT; + goto out; + } + err = scdisk_mode_select( tgt, DEV_BSIZE, ior, ior->io_data, + ms->msp_length, ms->msp_setps); + } + break; + + case SCSI_MODE_SENSE: { /*_IOWR(p, 9, struct mode_sel_sns_params) */ + struct mode_sel_sns_params *ms; + unsigned char pagecode; + + ms = (struct mode_sel_sns_params*)status; + pagecode = (ms->msp_pgcode & 0x3f) | (ms->msp_pgctrl << 6); + err = scsi_mode_sense( tgt, pagecode, ms->msp_length, ior); + if (copyout(tgt->cmd_ptr, ms->msp_addr, ms->msp_length)){ + ret = EFAULT; + goto out; + } + } + break; + } + + ret = (err == SCSI_RET_SUCCESS) ? D_SUCCESS : D_IO_ERROR; + if (ior->io_op & IO_ERROR) + ret = D_IO_ERROR; +out: + brelse(ior); + return ret; +} +#endif /*ULTRIX_COMPAT*/ +#endif /*!MACH_KERNEL*/ + +#ifdef CHECKSUM + +#define SUMSIZE 0x10000 +#define SUMHASH(b) (((b)>>1) & (SUMSIZE - 1)) +struct { + long blockno; + long sum; +} scdisk_checksums[SUMSIZE]; + +void scdisk_checksum(bno, addr, size) + long bno; + register unsigned int *addr; +{ + register int i = size/sizeof(int); + register unsigned int sum = -1; + + while (i-- > 0) + sum ^= *addr++; + scdisk_checksums[SUMHASH(bno)].blockno = bno; + scdisk_checksums[SUMHASH(bno)].sum = sum; +} + +void scdisk_bcheck(bno, addr, size) + long bno; + register unsigned int *addr; +{ + register int i = size/sizeof(int); + register unsigned int sum = -1; + unsigned int *start = addr; + + if (scdisk_checksums[SUMHASH(bno)].blockno != bno) { +if (scsi_debug) printf("No checksum for block x%x\n", bno); + return; + } + + while (i-- > 0) + sum ^= *addr++; + + if (scdisk_checksums[SUMHASH(bno)].sum != sum) { + printf("Bad checksum (x%x != x%x), bno x%x size x%x at x%x\n", + sum, + scdisk_checksums[bno & (SUMSIZE - 1)].sum, + bno, size, start); + gimmeabreak(); + scdisk_checksums[SUMHASH(bno)].sum = sum; + } +} + + +#endif CHECKSUM + +/*#define PERF */ +#ifdef PERF +int test_read_size = 512; +int test_read_skew = 12; +int test_read_skew_min = 0; +int test_read_nreads = 1000; +int test_read_bdev = 0; + +#include <sys/time.h> + +void test_read(max) +{ + int i, ssk, usecs; + struct timeval start, stop; + + if (max == 0) + max = test_read_skew + 1; + ssk = test_read_skew; + for (i = test_read_skew_min; i < max; i++){ + test_read_skew = i; + + start = time; + read_test(); + stop = time; + + usecs = stop.tv_usec - start.tv_usec; + if (usecs < 0) { + stop.tv_sec -= 1; + usecs += 1000000; + } + printf("Skew %3d size %d count %d time %3d sec %d us\n", + i, test_read_size, test_read_nreads, + stop.tv_sec - start.tv_sec, usecs); + } + test_read_skew = ssk; +} + +void read_test() +{ + static int buffer[(8192*2)/sizeof(int)]; + struct io_req io; + register int i, rec; + + 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*) buffer; + + for (rec = 0, i = 0; i < test_read_nreads; i++) { + io.io_op = IO_READ; + io.io_recnum = rec; + scdisk_strategy(&io); + rec += test_read_skew; + iowait(&io); + } +} + +void tur_test() +{ + struct io_req io; + register int i; + char *a, *b; + struct timeval start, stop; + + bzero(&io, sizeof(io)); + io.io_unit = test_read_bdev; + io.io_data = (char*)&io;/*unused but kernel space*/ + + start = time; + for (i = 0; i < test_read_nreads; i++) { + io.io_op = IO_INTERNAL; + rz_check(io.io_unit, &a, &b); + scsi_test_unit_ready(b,&io); + } + stop = time; + 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); +} + +#endif PERF + +/*#define WDEBUG*/ +#ifdef WDEBUG + +int buggo_write_size = 8192; +int buggo_dev = 2; /* rz0b */ /* changed by KTVM from 1 (still b) */ +int buggo_out_buffer[8192/2]; +int buggo_in_buffer[8192/2]; +int buggotest(n, pattern, verbose) +{ + struct io_req io; + register int i, rec; + + if (n <= 0) + n = 1; + + if(pattern) + for (i = 0; i < buggo_write_size/4; i++) + buggo_out_buffer[i] = i + pattern; + + for (i = 0; i < n; i++) { + register int j; + + buggo_out_buffer[0] = i + pattern; + buggo_out_buffer[(buggo_write_size/4)-1] = i + pattern; + bzero(&io, sizeof(io)); + io.io_unit = buggo_dev; + io.io_data = (char*)buggo_out_buffer; + io.io_op = IO_WRITE; + io.io_count = buggo_write_size; + io.io_recnum = i % 1024; + scdisk_strategy(&io); + + bzero(buggo_in_buffer, sizeof(buggo_in_buffer)); + iowait(&io); + + if (verbose) + printf("Done write with %x", io.io_error); + + bzero(&io, sizeof(io)); + io.io_unit = buggo_dev; + io.io_data = (char*)buggo_in_buffer; + io.io_op = IO_READ; + io.io_count = buggo_write_size; + io.io_recnum = i % 1024; + scdisk_strategy(&io); + iowait(&io); + + if (verbose) + printf("Done read with %x", io.io_error); + + for (j = 0; j < buggo_write_size/4; j++) + if (buggo_out_buffer[j] != buggo_in_buffer[j]){ + printf("Difference at %d-th word: %x %x\n", + buggo_out_buffer[j], buggo_in_buffer[j]); + return i; + } + } + printf("Test ok\n"); + return n; +} +#endif WDEBUG +#endif /* NSCSI > 0 */ diff --git a/scsi/rz_disk_bbr.c b/scsi/rz_disk_bbr.c new file mode 100644 index 0000000..9d87675 --- /dev/null +++ b/scsi/rz_disk_bbr.c @@ -0,0 +1,259 @@ +/* + * 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_disk_bbr.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 4/91 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains bad-block management functions + * (retry, replace) for disk-like devices. + */ + +#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) + +int scsi_bbr_retries = 10; + +#define BBR_ACTION_COMPLETE 1 +#define BBR_ACTION_RETRY_READ 2 +#define BBR_ACTION_REASSIGN 3 +#define BBR_ACTION_COPY 4 +#define BBR_ACTION_VERIFY 5 + +static void make_msf(); /* forward */ + +/* + * Bad block replacement routine, invoked on + * unrecovereable disk read/write errors. + */ +boolean_t +scdisk_bad_block_repl(tgt, blockno) + target_info_t *tgt; + unsigned int blockno; +{ + register io_req_t ior = tgt->ior; + + if (scsi_no_automatic_bbr || (ior->io_op & IO_INTERNAL)) + return FALSE; + + /* signal we took over */ + tgt->flags |= TGT_BBR_ACTIVE; + + printf("%s", "Attempting bad block replacement.."); + + tgt->dev_info.disk.b.badblockno = blockno; + tgt->dev_info.disk.b.retry_count = 0; + + tgt->dev_info.disk.b.save_rec = ior->io_recnum; + tgt->dev_info.disk.b.save_addr = ior->io_data; + tgt->dev_info.disk.b.save_count = ior->io_count; + tgt->dev_info.disk.b.save_resid = ior->io_residual; + + /* + * On a write all we need is to rewire the offending block. + * Note that the sense data identified precisely which 512 sector + * is bad. At the end we'll retry the entire write, so if there + * is more than one bad sector involved they will be handled one + * at a time. + */ + if ((ior->io_op & IO_READ) == 0) { + char msf[sizeof(int)]; + ior->io_temporary = BBR_ACTION_COMPLETE; + printf("%s", "just reassign.."); + make_msf(msf,blockno); + scsi_reassign_blocks( tgt, msf, 1, ior); + } else + /* + * This is more complicated. We asked for N bytes, and somewhere + * in there there is a chunk of bad data. First off, we should retry + * at least a couple of times to retrieve that data [yes the drive + * should have done its best already so what]. If that fails we + * should recover as much good data as possible (before the bad one). + */ + { + ior->io_temporary = BBR_ACTION_RETRY_READ; + printf("%s", "retry read.."); + ior->io_residual = 0; + scdisk_start_rw(tgt, ior); + } + + return TRUE; +} + +static +void make_msf(buf,val) + unsigned char *buf; + unsigned int val; +{ + *buf++ = val >> 24; + *buf++ = val >> 16; + *buf++ = val >> 8; + *buf++ = val >> 0; +} + +/* + * This effectively replaces the strategy routine during bbr. + */ +void scdisk_bbr_start( tgt, done) + target_info_t *tgt; + boolean_t done; +{ + register io_req_t ior = tgt->ior; + char *msg; + + switch (ior->io_temporary) { + + case BBR_ACTION_COMPLETE: + + /* all done, either way */ +fin: + tgt->flags &= ~TGT_BBR_ACTIVE; + ior->io_recnum = tgt->dev_info.disk.b.save_rec; + ior->io_data = tgt->dev_info.disk.b.save_addr; + ior->io_count = tgt->dev_info.disk.b.save_count; + ior->io_residual = tgt->dev_info.disk.b.save_resid; + + if (tgt->done == SCSI_RET_SUCCESS) { + /* restart normal life */ + register unsigned int xferred; + if (xferred = ior->io_residual) { + ior->io_data -= xferred; + ior->io_count += xferred; + ior->io_recnum -= xferred / tgt->block_size; + ior->io_residual = 0; + } + /* from the beginning */ + ior->io_error = 0; + msg = "done, restarting."; + } else { + /* we could not fix it. Tell user and give up */ + tgt->ior = ior->io_next; + iodone(ior); + msg = "done, but could not recover."; + } + + printf("%s\n", msg); + scdisk_start( tgt, FALSE); + return; + + case BBR_ACTION_RETRY_READ: + + /* see if retry worked, if not do it again */ + if (tgt->done == SCSI_RET_SUCCESS) { + char msf[sizeof(int)]; + + /* whew, retry worked. Now rewire that bad block + * and don't forget to copy the good data over */ + + tgt->dev_info.disk.b.retry_count = 0; + printf("%s", "ok now, reassign.."); + ior->io_temporary = BBR_ACTION_COPY; + make_msf(msf, tgt->dev_info.disk.b.badblockno); + scsi_reassign_blocks( tgt, msf, 1, ior); + return; + } + if (tgt->dev_info.disk.b.retry_count++ < scsi_bbr_retries) { + scdisk_start_rw( tgt, ior); + return; + } + /* retrying was hopeless. Leave the bad block there for maintainance */ + /* because we do not know what to write on it */ + printf("%s%d%s", "failed after ", scsi_bbr_retries, " retries.."); + goto fin; + + + case BBR_ACTION_COPY: + + /* retrying succeded and we rewired the bad block. */ + if (tgt->done == SCSI_RET_SUCCESS) { + unsigned int tmp; + struct diskpart *label; + + printf("%s", "ok, rewrite.."); + + /* writeback only the bad sector */ + + /* map blockno back to partition offset */ +/* !!! partition code changes: */ + tmp = rzpartition(ior->io_unit); +/* label=lookup_part(array, tmp +1); */ + tmp = tgt->dev_info.disk.b.badblockno - +/* label->start; */ +/* #if 0 */ + tgt->dev_info.disk.l.d_partitions[tmp].p_offset; +/* #endif 0 */ + ior->io_data += (tmp - ior->io_recnum) * tgt->block_size; + ior->io_recnum = tmp; + ior->io_count = tgt->block_size; + ior->io_op &= ~IO_READ; + + ior->io_temporary = BBR_ACTION_VERIFY; + scdisk_start_rw( tgt, ior); + } else { + + /* either unsupported command, or repl table full */ + printf("%s", "reassign failed (really needs reformatting), "); + ior->io_error = 0; + goto fin; + } + break; + + case BBR_ACTION_VERIFY: + + if (tgt->done == SCSI_RET_SUCCESS) { + ior->io_op |= IO_READ; + goto fin; + } + + if (tgt->dev_info.disk.b.retry_count++ > scsi_bbr_retries) { + printf("%s%d%s", "failed after ", + scsi_bbr_retries, " retries.."); + ior->io_op |= IO_READ; + goto fin; + } + + /* retry, we are *this* close to success.. */ + scdisk_start_rw( tgt, ior); + + break; + + case BBR_ACTION_REASSIGN: + + /* if we wanted to issue the reassign multiple times */ + /* XXX unimplemented XXX */ + + default: /* snafu */ + panic("scdisk_bbr_start"); + } +} +#endif /* NSCSI > 0 */ diff --git a/scsi/rz_host.c b/scsi/rz_host.c new file mode 100644 index 0000000..94ccd81 --- /dev/null +++ b/scsi/rz_host.c @@ -0,0 +1,108 @@ +/* + * 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_host.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 other hosts that are capable of + * sophisticated host-to-host communication protocols, we make + * them look like... you'll see. + * + * There are two sides of the coin here: when we take the initiative + * and when the other host does it. Code for handling both cases is + * provided in this one file. + */ + +#include <mach/std_types.h> +#include <machine/machspl.h> /* spl definitions */ +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + +#if (NSCSI > 0) +/* Since we have invented a new "device" this cannot go into the + the 'official' scsi_devsw table. Too bad. */ + +extern char *schost_name(); +extern scsi_ret_t + schost_open(), schost_close(); +extern int schost_strategy(); +extern void schost_start(); + +scsi_devsw_t scsi_host = { + schost_name, 0, schost_open, schost_close, schost_strategy, + schost_start, 0, 0 +}; + +char *schost_name(internal) + boolean_t internal; +{ + return internal ? "sh" : "host"; +} + +scsi_ret_t +schost_open(tgt) + target_info_t *tgt; +{ + return SCSI_RET_SUCCESS; /* XXX if this is it, drop it */ +} + +scsi_ret_t +schost_close(tgt) + target_info_t *tgt; +{ + return SCSI_RET_SUCCESS; /* XXX if this is it, drop it */ +} + +schost_strategy(ior) + register io_req_t ior; +{ + return rz_simpleq_strategy( ior, schost_start); +} + +void +schost_start( tgt, done) + target_info_t *tgt; + boolean_t done; +{ + io_req_t head, ior; + scsi_ret_t ret; + + if (done || (!tgt->dev_info.cpu.req_pending)) { + sccpu_start( tgt, done); + return; + } + + ior = tgt->ior; +} + +#endif /* NSCSI > 0 */ diff --git a/scsi/rz_labels.h b/scsi/rz_labels.h new file mode 100644 index 0000000..6ffaa11 --- /dev/null +++ b/scsi/rz_labels.h @@ -0,0 +1,243 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 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_labels.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Definitions of various vendor's disk label formats. + */ + +/* modified by Kevin T. Van Maren for the unified partition code */ + +#ifndef _RZ_LABELS_H_ +#define _RZ_LABELS_H_ + +/* + * This function looks for, and converts to BSD format + * a vendor's label. It is only called if we did not + * find a standard BSD label on the disk pack. + */ +extern boolean_t rz_vendor_label(); + +/* + * Definition of the DEC disk label, + * which is located (you guessed it) + * at the end of the 4.3 superblock. + */ + +struct dec_partition_info { + unsigned int n_sectors; /* how big the partition is */ + unsigned int offset; /* sector no. of start of part. */ +}; + +typedef struct { + int magic; +# define DEC_LABEL_MAGIC 0x032957 + int in_use; + struct dec_partition_info partitions[8]; +} dec_label_t; + +/* + * Physical location on disk. + * This is independent of the filesystem we use, + * although of course we'll be in trouble if we + * screwup the 4.3 SBLOCK.. + */ + +#define DEC_LABEL_BYTE_OFFSET ((2*8192)-sizeof(dec_label_t)) + + +/* + * Definitions for the primary boot information + * This is common, cuz the prom knows it. + */ + +typedef struct { + int pad[2]; + unsigned int magic; +# define DEC_BOOT0_MAGIC 0x2757a + int mode; + unsigned int phys_base; + unsigned int virt_base; + unsigned int n_sectors; + unsigned int start_sector; +} dec_boot0_t; + +typedef struct { + dec_boot0_t vax_boot; + /* BSD label still fits in pad */ + char pad[0x1e0-sizeof(dec_boot0_t)]; + unsigned long block_count; + unsigned long starting_lbn; + unsigned long flags; + unsigned long checksum; /* add cmpl-2 all but here */ +} alpha_boot0_t; + +/* + * Definition of the Omron disk label, + * which is located at sector 0. It + * _is_ sector 0, actually. + */ +struct omron_partition_info { + unsigned long offset; + unsigned long n_sectors; +}; + +typedef struct { + char packname[128]; /* in ascii */ + + char pad[512-(128+8*8+11*2+4)]; + + unsigned short badchk; /* checksum of bad track */ + unsigned long maxblk; /* # of total logical blocks */ + unsigned short dtype; /* disk drive type */ + unsigned short ndisk; /* # of disk drives */ + unsigned short ncyl; /* # of data cylinders */ + unsigned short acyl; /* # of alternate cylinders */ + unsigned short nhead; /* # of heads in this partition */ + unsigned short nsect; /* # of 512 byte sectors per track */ + unsigned short bhead; /* identifies proper label locations */ + unsigned short ppart; /* physical partition # */ + struct omron_partition_info + partitions[8]; + + unsigned short magic; /* identifies this label format */ +# define OMRON_LABEL_MAGIC 0xdabe + + unsigned short cksum; /* xor checksum of sector */ + +} omron_label_t; + +/* + * Physical location on disk. + */ + +#define OMRON_LABEL_BYTE_OFFSET 0 + + +/* + * Definition of the i386AT disk label, which lives inside sector 0. + * This is the info the BIOS knows about, which we use for bootstrapping. + * It is common across all disks known to BIOS. + */ + +struct bios_partition_info { + + unsigned char bootid; /* bootable or not */ +# define BIOS_BOOTABLE 128 + + unsigned char beghead;/* beginning head, sector, cylinder */ + unsigned char begsect;/* begcyl is a 10-bit number. High 2 bits */ + unsigned char begcyl; /* are in begsect. */ + + unsigned char systid; /* filesystem type */ +# define UNIXOS 99 /* GNU HURD? */ +# define BSDOS 165 /* 386BSD */ +# define LINUXSWAP 130 +# define LINUXOS 131 +# define DOS_EXTENDED 05 /* container for logical partitions */ + +# define HPFS 07 /* OS/2 Native */ +# define OS_2_BOOT 10 /* OS/2 Boot Manager */ +# define DOS_12 01 /* 12 bit FAT */ +# define DOS_16_OLD 04 /* < 32MB */ +# define DOS_16 06 /* >= 32MB (#4 not used anymore) */ + + /* these numbers can't be trusted because */ + /* of newer, larger drives */ + unsigned char endhead;/* ending head, sector, cylinder */ + unsigned char endsect;/* endcyl is a 10-bit number. High 2 bits */ + unsigned char endcyl; /* are in endsect. */ + + unsigned long offset; + unsigned long n_sectors; +}; + +typedef struct { +/* struct bios_partition_info bogus compiler alignes wrong + partitions[4]; +*/ + char partitions[4*sizeof(struct bios_partition_info)]; + unsigned short magic; +# define BIOS_LABEL_MAGIC 0xaa55 +} bios_label_t; + +/* + * Physical location on disk. + */ + +#define BIOS_LABEL_BYTE_OFFSET 446 + +/* + * Definitions for the primary boot information + * This _is_ block 0 + */ + +#define BIOS_BOOT0_SIZE BIOS_LABEL_BYTE_OFFSET + +typedef struct { + char boot0[BIOS_BOOT0_SIZE]; /* boot code */ +/* bios_label_t label; bogus compiler alignes wrong */ + char label[sizeof(bios_label_t)]; +} bios_boot0_t; + +/* Moved from i386at/nhdreg.h */ +#define PDLOCATION 29 /* VTOC sector */ + + +/* these are the partition types that can contain sub-partitions */ +/* enum types... */ +#define DISKPART_NONE 0 /* smallest piece flag !?! */ +#define DISKPART_DOS 1 +#define DISKPART_BSD 2 +#define DISKPART_VTOC 3 +#define DISKPART_OMRON 4 +#define DISKPART_DEC 5 /* VAX disks? */ +#define DISKPART_UNKNOWN 99 + + + +/* for NEW partition code */ +/* this is the basic partition structure. an array of these is + filled, with element 0 being the whole drive, element 1-n being + the n top-level partitions, followed by 0+ groups of 1+ sub-partitions. */ +typedef struct diskpart { + short type; /* DISKPART_xxx (see above) */ + short fsys; /* file system (if known) */ + int nsubs; /* number of sub-slices */ + struct diskpart *subs; /* pointer to the sub-partitions */ + int start; /* relative to the start of the DRIVE */ + int size; /* # sectors in this piece */ +} diskpart; + +int get_only_partition(void *driver_info, int (*bottom_read_fun)(), + struct diskpart *array, int array_size, + int disk_size, char *drive_name); + +struct diskpart *lookup_part(struct diskpart *array, int dev_number); +#endif _RZ_LABELS_H_ + diff --git a/scsi/rz_tape.c b/scsi/rz_tape.c new file mode 100644 index 0000000..1d27722 --- /dev/null +++ b/scsi/rz_tape.c @@ -0,0 +1,560 @@ +/* + * 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_tape.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Top layer of the SCSI driver: interface with the MI. + * This file contains operations specific to TAPE-like devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <sys/ioctl.h> +#ifdef MACH_KERNEL +#include <device/tape_status.h> +#else /*MACH_KERNEL*/ +#include <mips/PMAX/tape_status.h> +#endif /*MACH_KERNEL*/ + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> +#include <scsi/rz.h> + +#if (NSCSI > 0) + + +void sctape_start(); /* forward */ + +int scsi_tape_timeout = 5*60; /* secs, tk50 is slow when positioning far apart */ + +int sctape_open(tgt, req) + target_info_t *tgt; + io_req_t req; +{ + io_return_t ret; + io_req_t ior; + int i; + scsi_mode_sense_data_t *mod; + +#ifdef MACH_KERNEL + req->io_device->flag |= D_EXCL_OPEN; +#endif /*MACH_KERNEL*/ + + /* Preferably allow tapes to disconnect */ + if (BGET(scsi_might_disconnect,(unsigned char)tgt->masterno,tgt->target_id)) + BSET(scsi_should_disconnect,(unsigned char)tgt->masterno,tgt->target_id); + + /* + * Dummy ior for proper sync purposes + */ + io_req_alloc(ior,0); + ior->io_count = 0; + + /* + * Do a mode sense first, some drives might be picky + * about changing params [even if the standard might + * say otherwise, sigh.] + */ + do { + ior->io_op = IO_INTERNAL; + ior->io_next = 0; + ior->io_error = 0; + ret = scsi_mode_sense(tgt, 0, 32, ior); + } while (ret == SCSI_RET_RETRY); + + mod = (scsi_mode_sense_data_t *)tgt->cmd_ptr; + if (scsi_debug) { + int p[5]; + bcopy((char*)mod, (char*)p, sizeof(p)); + printf("[modsns(%x): x%x x%x x%x x%x x%x]", ret, + p[0], p[1], p[2], p[3], p[4]); + } + if (ret == SCSI_RET_DEVICE_DOWN) + goto out; + if (ret == SCSI_RET_SUCCESS) { + tgt->dev_info.tape.read_only = mod->wp; + tgt->dev_info.tape.speed = mod->speed; + tgt->dev_info.tape.density = mod->bdesc[0].density_code; + } /* else they all default sensibly, using zeroes */ + + /* Some tapes have limits on record-length */ +again: + ior->io_op = IO_INTERNAL; + ior->io_next = 0; + ior->io_error = 0; + ret = scsi_read_block_limits( tgt, ior); + if (ret == SCSI_RET_RETRY) goto again; + if (!ior->io_error && (ret == SCSI_RET_SUCCESS)) { + scsi_blimits_data_t *lim; + int maxl; + + lim = (scsi_blimits_data_t *) tgt->cmd_ptr; + + tgt->block_size = (lim->minlen_msb << 8) | + lim->minlen_lsb; + + maxl = (lim->maxlen_msb << 16) | + (lim->maxlen_sb << 8) | + lim->maxlen_lsb; + if (maxl == 0) + maxl = (unsigned)-1; + tgt->dev_info.tape.maxreclen = maxl; + tgt->dev_info.tape.fixed_size = (maxl == tgt->block_size); + } else { + /* let the user worry about it */ + /* default: tgt->block_size = 1; */ + tgt->dev_info.tape.maxreclen = (unsigned)-1; + tgt->dev_info.tape.fixed_size = FALSE; + } + + /* Try hard to do a mode select */ + for (i = 0; i < 5; i++) { + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ret = sctape_mode_select(tgt, 0, 0, FALSE, ior); + if (ret == SCSI_RET_SUCCESS) + break; + } + if (scsi_watchdog_period < scsi_tape_timeout) + scsi_watchdog_period += scsi_tape_timeout; + +#if 0 /* this might imply rewind, which we do not want, although yes, .. */ + /* we want the tape loaded */ + ior->io_op = IO_INTERNAL; + ior->io_next = 0; + ior->io_error = 0; + ret = scsi_start_unit(tgt, SCSI_CMD_SS_START, ior); +#endif + req->io_device->bsize = tgt->block_size; +out: + io_req_free(ior); + return ret; +} + + +io_return_t sctape_close(tgt) + target_info_t *tgt; +{ + io_return_t ret = SCSI_RET_SUCCESS; + io_req_t ior; + + /* + * Dummy ior for proper sync purposes + */ + io_req_alloc(ior,0); + ior->io_op = IO_INTERNAL; + ior->io_next = 0; + ior->io_count = 0; + + if (tgt->ior) printf("TAPE: Close with pending requests ?? \n"); + + /* write a filemark if we xtnded/truncated the tape */ + if (tgt->flags & TGT_WRITTEN_TO) { + tgt->ior = ior; + ior->io_error = 0; + ret = scsi_write_filemarks(tgt, 2, ior); + if (ret != SCSI_RET_SUCCESS) + printf("%s%d: wfmark failed x%x\n", + (*tgt->dev_ops->driver_name)(TRUE), tgt->target_id, ret); + /* + * Don't bother repositioning if we'll rewind it + */ + if (tgt->flags & TGT_REWIND_ON_CLOSE) + goto rew; +retry: + tgt->ior = ior; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ior->io_next = 0; + ret = scsi_space(tgt, SCSI_CMD_SP_FIL, -1, ior); + if (ret != SCSI_RET_SUCCESS) { + if (ret == SCSI_RET_RETRY) { + timeout(wakeup, tgt, hz); + await(tgt); + goto retry; + } + printf("%s%d: bspfile failed x%x\n", + (*tgt->dev_ops->driver_name)(TRUE), tgt->target_id, ret); + } + } +rew: + if (tgt->flags & TGT_REWIND_ON_CLOSE) { + /* Rewind tape */ + ior->io_error = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + tgt->ior = ior; + (void) scsi_rewind(tgt, ior, FALSE); + iowait(ior); + if (tgt->done == SCSI_RET_RETRY) { + timeout(wakeup, tgt, 5*hz); + await(tgt); + goto rew; + } + } + io_req_free(ior); + + tgt->flags &= ~(TGT_ONLINE|TGT_WRITTEN_TO|TGT_REWIND_ON_CLOSE); + return ret; +} + +int sctape_strategy(ior) + register io_req_t ior; +{ + target_info_t *tgt; + register int i = ior->io_unit; + + tgt = scsi_softc[rzcontroller(i)]->target[rzslave(i)]; + + if (((ior->io_op & IO_READ) == 0) && + tgt->dev_info.tape.read_only) { + ior->io_error = D_INVALID_OPERATION; + ior->io_op |= IO_ERROR; + ior->io_residual = ior->io_count; + iodone(ior); + return ior->io_error; + } + + return rz_simpleq_strategy( ior, sctape_start); +} + +static void +do_residue(ior, sns, bsize) + io_req_t ior; + scsi_sense_data_t *sns; + int bsize; +{ + int residue; + + /* Not an error situation */ + ior->io_error = 0; + ior->io_op &= ~IO_ERROR; + + if (!sns->addr_valid) { + ior->io_residual = ior->io_count; + return; + } + + residue = sns->u.xtended.info0 << 24 | + sns->u.xtended.info1 << 16 | + sns->u.xtended.info2 << 8 | + sns->u.xtended.info3; + /* fixed ? */ + residue *= bsize; + /* + * NOTE: residue == requested - actual + * We only care if > 0 + */ + if (residue < 0) residue = 0;/* sanity */ + ior->io_residual += residue; +} + +void sctape_start( tgt, done) + target_info_t *tgt; + boolean_t done; +{ + io_req_t head, 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 ? ouch, that hurts */ + if (tgt->done == (SCSI_RET_ABORTED|SCSI_RET_RETRY)) { + /* + * we really cannot retry because the tape position + * is lost. + */ + printf("Lost tape position\n"); + ior->io_error = D_IO_ERROR; + ior->io_op |= IO_ERROR; + } 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) { + do_residue(ior, sns, tgt->block_size); + if (scsi_debug) + printf("Tape Short Read (%d)\n", + ior->io_residual); + } + } else if (sns->u.xtended.eom) { + do_residue(ior, sns, tgt->block_size); + if (scsi_debug) + printf("End of Physical Tape!\n"); + } else if (sns->u.xtended.fm) { + do_residue(ior, sns, tgt->block_size); + if (scsi_debug) + printf("File Mark\n"); + } + } + } + + 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) { + tgt->flags &= ~TGT_WRITTEN_TO; + sctape_read( tgt, ior ); + } else if ((ior->io_op & IO_INTERNAL) == 0) { + tgt->flags |= TGT_WRITTEN_TO; + sctape_write( tgt, ior ); + } +} + +io_return_t +sctape_get_status( dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t *status_count; +{ + switch (flavor) { + case DEV_GET_SIZE: + + status[DEV_GET_SIZE_DEVICE_SIZE] = 0; + status[DEV_GET_SIZE_RECORD_SIZE] = tgt->block_size; + *status_count = DEV_GET_SIZE_COUNT; + break; + case TAPE_STATUS: { + struct tape_status *ts = (struct tape_status *) status; + + ts->mt_type = MT_ISSCSI; + ts->speed = tgt->dev_info.tape.speed; + ts->density = tgt->dev_info.tape.density; + ts->flags = (tgt->flags & TGT_REWIND_ON_CLOSE) ? + TAPE_FLG_REWIND : 0; + if (tgt->dev_info.tape.read_only) + ts->flags |= TAPE_FLG_WP; +#ifdef MACH_KERNEL + *status_count = TAPE_STATUS_COUNT; +#endif + + break; + } + /* U*x compat */ + case MTIOCGET: { + struct mtget *g = (struct mtget *) status; + + bzero(g, sizeof(struct mtget)); + g->mt_type = 0x7; /* Ultrix compat */ +#ifdef MACH_KERNEL + *status_count = sizeof(struct mtget)/sizeof(int); +#endif + break; + } + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} + +io_return_t +sctape_set_status( dev, tgt, flavor, status, status_count) + int dev; + target_info_t *tgt; + dev_flavor_t flavor; + dev_status_t status; + natural_t status_count; +{ + scsi_ret_t ret; + + switch (flavor) { + case TAPE_STATUS: { + struct tape_status *ts = (struct tape_status *) status; + if (ts->flags & TAPE_FLG_REWIND) + tgt->flags |= TGT_REWIND_ON_CLOSE; + else + tgt->flags &= ~TGT_REWIND_ON_CLOSE; + + if (ts->speed || ts->density) { + unsigned int ospeed, odensity; + io_req_t ior; + + io_req_alloc(ior,0); + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ior->io_next = 0; + ior->io_count = 0; + + ospeed = tgt->dev_info.tape.speed; + odensity = tgt->dev_info.tape.density; + tgt->dev_info.tape.speed = ts->speed; + tgt->dev_info.tape.density = ts->density; + + ret = sctape_mode_select(tgt, 0, 0, (ospeed == ts->speed), ior); + if (ret != SCSI_RET_SUCCESS) { + tgt->dev_info.tape.speed = ospeed; + tgt->dev_info.tape.density = odensity; + } + + io_req_free(ior); + } + + break; + } + /* U*x compat */ + case MTIOCTOP: { + struct tape_params *mt = (struct tape_params *) status; + io_req_t ior; + + if (scsi_debug) + printf("[sctape_sstatus: %x %x %x]\n", + flavor, mt->mt_operation, mt->mt_repeat_count); + + io_req_alloc(ior,0); +retry: + ior->io_count = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; + ior->io_next = 0; + tgt->ior = ior; + + /* compat: in U*x it is a short */ + switch ((short)(mt->mt_operation)) { + case MTWEOF: /* write an end-of-file record */ + ret = scsi_write_filemarks(tgt, mt->mt_repeat_count, ior); + break; + case MTFSF: /* forward space file */ + ret = scsi_space(tgt, SCSI_CMD_SP_FIL, mt->mt_repeat_count, ior); + break; + case MTBSF: /* backward space file */ + ret = scsi_space(tgt, SCSI_CMD_SP_FIL, -mt->mt_repeat_count,ior); + break; + case MTFSR: /* forward space record */ + ret = scsi_space(tgt, SCSI_CMD_SP_BLOCKS, mt->mt_repeat_count, ior); + break; + case MTBSR: /* backward space record */ + ret = scsi_space(tgt, SCSI_CMD_SP_BLOCKS, -mt->mt_repeat_count, ior); + break; + case MTREW: /* rewind */ + case MTOFFL: /* rewind and put the drive offline */ + ret = scsi_rewind(tgt, ior, TRUE); + iowait(ior); + if ((short)(mt->mt_operation) == MTREW) break; + ior->io_op = 0; + ior->io_next = 0; + ior->io_error = 0; + (void) scsi_start_unit(tgt, 0, ior); + break; + case MTNOP: /* no operation, sets status only */ + case MTCACHE: /* enable controller cache */ + case MTNOCACHE: /* disable controller cache */ + ret = SCSI_RET_SUCCESS; + break; + default: + tgt->ior = 0; + io_req_free(ior); + return D_INVALID_OPERATION; + } + + if (ret == SCSI_RET_RETRY) { + timeout(wakeup, ior, 5*hz); + await(ior); + goto retry; + } + + io_req_free(ior); + if (ret != SCSI_RET_SUCCESS) + return D_IO_ERROR; + break; + } + case MTIOCIEOT: + case MTIOCEEOT: + default: + return D_INVALID_OPERATION; + } + return D_SUCCESS; +} +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi.c b/scsi/scsi.c new file mode 100644 index 0000000..d4aecf6 --- /dev/null +++ b/scsi/scsi.c @@ -0,0 +1,642 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1989 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: scsi.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Middle layer of the SCSI driver: chip independent functions + * This file contains Controller and Device-independent functions + */ + +#include <scsi.h> + +#if NSCSI > 0 +#include <platforms.h> + +#include <machine/machspl.h> /* spl definitions */ + +#include <mach/std_types.h> +#include <sys/types.h> +#include <scsi/compat_30.h> + +#include <chips/busses.h> +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> +#include <machine/machspl.h> + + + +#ifdef VAXSTATION +/* We run some of this code on the interrupt stack */ +#undef spl0 +#define spl0() spl1() +#endif /*VAXSTATION*/ + +/* + * Overall driver state + */ + +target_info_t scsi_target_data[NSCSI*8]; /* per target state */ +scsi_softc_t scsi_softc_data[NSCSI]; /* per HBA state */ +scsi_softc_t *scsi_softc[NSCSI]; /* quick access&checking */ + +/* + * If a specific target should NOT be asked to go synchronous + * then its bit in this bitmap should be set. Each SCSI controller + * (Host Bus Adapter) can hold at most 8 targets --> use one + * byte per controller. A bit set to one means NO synchronous. + * Patch with adb if necessary. + */ +unsigned char scsi_no_synchronous_xfer[NSCSI]; + +/* + * For certain targets it is wise to use the long form of the + * read/write commands even if their capacity would not necessitate + * it. Same as above for usage. + */ +unsigned char scsi_use_long_form[NSCSI]; + + +/* + * Control over disconnect-reconnect mode. + */ +unsigned char scsi_might_disconnect[NSCSI] = /* do it if deemed appropriate */ + { 0xff, 0xff, 0xff, 0xff, 0xff};/* Fix by hand viz NSCSI */ +unsigned char scsi_should_disconnect[NSCSI] = /* just do it */ + { 0,}; +unsigned char scsi_initiator_id[NSCSI] = /* our id on the bus(ses) */ + { 7, 7, 7, 7, 7}; + +/* + * Miscellaneus config + */ +boolean_t scsi_exabyte_filemarks = FALSE; /* use short filemarks */ +int scsi_watchdog_period = 10; /* but exabyte needs >=30 for bspace */ +int scsi_delay_after_reset = 1000000;/* microseconds */ +boolean_t scsi_no_automatic_bbr = FALSE; /* revector bad blocks automatically */ + +#ifdef MACH_KERNEL +#else +/* This covers Exabyte's max record size */ +unsigned int scsi_per_target_virtual = 256*1024; +#endif MACH_KERNEL + + +/* + * Device-specific operations are switched off this table + */ + +extern char + *scdisk_name(), *sctape_name(), *scprt_name(), + *sccpu_name(), *scworm_name(), *sccdrom_name(), + *scscn_name(), *scmem_name(), *scjb_name(), *sccomm_name(); +extern void + sctape_optimize(); +extern scsi_ret_t + scdisk_open(), sctape_open(), sctape_close(), + sccomm_open(), sccomm_close(); +extern int + scdisk_strategy(), sctape_strategy(), sccpu_strategy(), + sccomm_strategy(); +extern void + scdisk_start(), sctape_start(), sccpu_start(), sccomm_start(); + +extern io_return_t + scdisk_set_status(), scdisk_get_status(), + sctape_set_status(), sctape_get_status(), + sccomm_set_status(), sccomm_get_status(); + +scsi_devsw_t scsi_devsw[] = { + +/* SCSI_DISK */ { scdisk_name, SCSI_OPTIMIZE_NULL, + scdisk_open, SCSI_CLOSE_NULL, + scdisk_strategy, scdisk_start, + scdisk_get_status, scdisk_set_status }, + +/* SCSI_TAPE */ { sctape_name, sctape_optimize, + sctape_open, sctape_close, + sctape_strategy, sctape_start, + sctape_get_status, sctape_set_status }, + +/* SCSI_PRINTER */ { scprt_name, SCSI_OPTIMIZE_NULL, /*XXX*/}, + +/* SCSI_CPU */ { sccpu_name, SCSI_OPTIMIZE_NULL, + SCSI_OPEN_NULL, SCSI_CLOSE_NULL, + sccpu_strategy, sccpu_start,}, + +/* SCSI_WORM */ { scworm_name, SCSI_OPTIMIZE_NULL, + scdisk_open, SCSI_CLOSE_NULL, + scdisk_strategy, scdisk_start, + scdisk_get_status, scdisk_set_status }, + +/* SCSI_CDROM */ { sccdrom_name, SCSI_OPTIMIZE_NULL, + scdisk_open, SCSI_CLOSE_NULL, + scdisk_strategy, scdisk_start, + scdisk_get_status, scdisk_set_status }, +/* scsi2 */ +/* SCSI_SCANNER */ { scscn_name, SCSI_OPTIMIZE_NULL, /*XXX*/ }, + +/* SCSI_MEMORY */ { scmem_name, SCSI_OPTIMIZE_NULL, + scdisk_open, SCSI_CLOSE_NULL, + scdisk_strategy, scdisk_start, + scdisk_get_status, scdisk_set_status }, + +/* SCSI_J_BOX */ { scjb_name, SCSI_OPTIMIZE_NULL, /*XXX*/ }, + +/* SCSI_COMM */ { sccomm_name, SCSI_OPTIMIZE_NULL, +#if (NCENDATA>0) + sccomm_open, sccomm_close, + sccomm_strategy, sccomm_start, + sccomm_get_status, sccomm_set_status +#endif + }, + 0 +}; + +/* + * Allocation routines for state structures + */ +scsi_softc_t * +scsi_master_alloc(unit, hw) + unsigned unit; + char *hw; +{ + scsi_softc_t *sc; + + if (unit < NSCSI) { + sc = &scsi_softc_data[unit]; + scsi_softc[unit] = sc; + sc->masterno = unit; + sc->hw_state = hw; + return sc; + } + return 0; +} + +target_info_t * +scsi_slave_alloc(unit, slave, hw) + unsigned unit, slave; + char *hw; +{ + target_info_t *tgt; + + tgt = &scsi_target_data[(unit<<3) + slave]; + tgt->hw_state = hw; + tgt->dev_ops = 0; /* later */ + tgt->target_id = slave; + tgt->masterno = unit; + tgt->block_size = 1; /* default */ + tgt->flags = TGT_ALIVE; + tgt->sync_period = 0; + tgt->sync_offset = 0; + simple_lock_init(&tgt->target_lock); + + scsi_softc[unit]->target[slave] = tgt; + return tgt; +} + +void +zero_ior( + io_req_t ior ) +{ + ior->io_next = ior->io_prev = 0; + ior->io_count = 0; + ior->io_op = IO_INTERNAL; + ior->io_error = 0; +} + +/* + * Slave routine: + * See if the slave description (controller, unit, ..) + * matches one of the slaves found during probe + * + * Implementation: + * Send out an INQUIRY command to see what sort of device + * the slave is. + * Notes: + * At this time the driver is fully functional and works + * off interrupts. + * TODO: + * The SCSI2 spec says what exactly must happen: see F.2.3 + */ +int scsi_slave( ui, reg) + struct bus_device *ui; + unsigned reg; +{ + scsi_softc_t *sc = scsi_softc[(unsigned char)ui->ctlr]; + target_info_t *tgt = sc->target[(unsigned char)ui->slave]; + scsi2_inquiry_data_t *inq; + int scsi_std; + int ptype, s; + + if (!tgt || !(tgt->flags & TGT_ALIVE)) + return 0; + + /* Might have scanned already */ + if (tgt->dev_ops) + goto out; + +#ifdef SCSI2 + This is what should happen: + - for all LUNs + INQUIRY + scsi_verify_state (see) + scsi_initialize (see) +#endif SCSI2 + + tgt->unit_no = ui->slave; /* incorrect, but needed early */ + + s = spl0(); /* we need interrupts */ + + if (BGET(scsi_no_synchronous_xfer,(unsigned char)sc->masterno,tgt->target_id)) + tgt->flags |= TGT_DID_SYNCH; + + /* + * Ok, it is time to see what type of device this is, + * send an INQUIRY cmd and wait till done. + * Possibly do the synch negotiation here. + */ + scsi_inquiry(tgt, SCSI_INQ_STD_DATA); + + inq = (scsi2_inquiry_data_t*)tgt->cmd_ptr; + ptype = inq->periph_type; + + switch (ptype) { + case SCSI_CDROM : + tgt->flags |= TGT_READONLY; + /* fall through */ + case SCSI_DISK : + case SCSI_TAPE : + case SCSI_PRINTER : + case SCSI_CPU : + case SCSI_WORM : + case SCSI_SCANNER : + case SCSI_MEMORY : + case SCSI_J_BOX : + case SCSI_COMM : +/* case SCSI_PREPRESS1 : reserved, really + case SCSI_PREPRESS2 : */ + tgt->dev_ops = &scsi_devsw[ptype]; + break; + default: + printf("scsi%d: %s %d (x%x). ", ui->ctlr, + "Unsupported device type at SCSI id", ui->slave, + inq->periph_type); + scsi_print_inquiry((scsi2_inquiry_data_t*)inq, + SCSI_INQ_STD_DATA, 0); + tgt->flags = 0; + splx(s); + return 0; + } + + if (inq->rmb) + tgt->flags |= TGT_REMOVABLE_MEDIA; + + /* + * Tell the user we know this target, then see if we + * can be a bit smart about it. + */ + scsi_print_inquiry((scsi2_inquiry_data_t*)inq, + SCSI_INQ_STD_DATA, tgt->tgt_name); + if (scsi_debug) + scsi_print_inquiry((scsi2_inquiry_data_t*)inq, + SCSI_INQ_STD_DATA, 0); + + /* + * The above says if it currently behaves as a scsi2, + * however scsi1 might just be the default setting. + * The spec say that even if in scsi1 mode the target + * should answer to the full scsi2 inquiry spec. + */ + scsi_std = (inq->ansi == 2 || inq->response_fmt == 2) ? 2 : 1; +#if nosey + if (scsi_std == 2) { + unsigned char supp_pages[256], i; + scsi2_impl_opdef_page_t *impl; + + scsi_inquiry(tgt, SCSI_INQ_SUPP_PAGES); + impl = (scsi2_impl_opdef_page_t *)inq; + npages = impl->page_len - 2; + bcopy(impl->supp_opdef, supp_pages, npages); + + for (i = 0; i < npages; i++) { + scsi_inquiry(tgt, supp_pages[i]); + scsi_print_inquiry(inq, supp_pages[i], 0); + } + } + + if (scsi_std == 2) { + scsi2_impl_opdef_page_t *impl; + int i; + + scsi_inquiry(tgt, SCSI_INQ_IMPL_OPDEF); + impl = (scsi2_impl_opdef_page_t *)inq; + for (i = 0; i < impl->page_len - 2; i++) + if (impl->supp_opdef[i] == SCSI2_OPDEF) { + scsi_change_definition(tgt, SCSI2_OPDEF); + /* if success .. */ + tgt->flags |= TGT_SCSI_2_MODE; + break; + } + } +#endif nosey + + splx(s); +out: + return (strcmp(ui->name, (*tgt->dev_ops->driver_name)(TRUE)) == 0); +} + +#ifdef SCSI2 +scsi_verify_state(...) +{ +verify_state: send test_unit_ready up to 3 times, each time it fails +(with check condition) send a requeste_sense. It is ok to get UNIT ATTENTION +the first time only, NOT READY the second, only GOOD the last time. +If you get BUSY or RESERVATION CONFLICT retry. +} + +scsi_initialize(...) +{ + +initialize: send start_unit with immed=0 (->disconnect), if fails +with check condition send requeste_sense and if "illegal request" +proceed anyways. Retry on BUSY. +Do a verify_state, then +disks: + - mode_sense (current) if ANSI2 or needed by vendor (!!!!) + and if check-condition&illegal-request goto capacity + - mode_sense (changeable) + - if needed do a mode_select (yes, 512) + - read_capacity +tapes: + +} +#endif SCSI2 + +/* + * Attach routine: + * Fill in all the relevant per-slave data and make + * the slave operational. + * + * Implementation: + * Get target's status, start the unit and then + * switch off to device-specific functions to gather + * as much info as possible about the slave. + */ +void scsi_attach(ui) + register struct bus_device *ui; +{ + scsi_softc_t *sc = scsi_softc[ui->mi->unit]; + target_info_t *tgt = sc->target[(unsigned char)ui->slave]; + int i; + spl_t s; + + printf(" (%s %s) ", (*tgt->dev_ops->driver_name)(FALSE),tgt->tgt_name); + + if (tgt->flags & TGT_US) { + printf(" [this cpu]"); + return; + } + + s = spl0(); + + /* sense return from inquiry */ + scsi_request_sense(tgt, 0, 0); + + /* + * Do this twice, certain targets need it + */ + if (tgt->dev_ops != &scsi_devsw[SCSI_CPU]) { + (void) scsi_start_unit(tgt, SCSI_CMD_SS_START, 0); + i = 0; + while (scsi_start_unit(tgt, SCSI_CMD_SS_START, 0) == SCSI_RET_RETRY) { + if (i++ == 5) + printf(".. not yet online .."); + delay(1000000); + if (i == 60) { + printf(" seems hopeless."); + break; + } + } + } + + /* + * See if it is up and about + */ + scsi_test_unit_ready(tgt, 0); + + if (tgt->dev_ops->optimize != SCSI_OPTIMIZE_NULL) + (*tgt->dev_ops->optimize)(tgt); + + tgt->flags |= TGT_FULLY_PROBED; + + splx(s); +} + +/* + * Probe routine: + * See if a device answers. Used AFTER autoconf. + * + * Implementation: + * First ask the HBA to see if anyone is there at all, then + * call the scsi_slave and scsi_attach routines with a fake ui. + */ +boolean_t +scsi_probe( sc, tgt_ptr, target_id, ior) + scsi_softc_t *sc; + target_info_t **tgt_ptr; + int target_id; + io_req_t ior; +{ + struct bus_device ui; + target_info_t *tgt; + + if (!sc->probe || target_id > 7 || target_id == sc->initiator_id) + return FALSE; /* sanity */ + + if (sc->target[target_id] == 0) + scsi_slave_alloc( sc->masterno, target_id, sc->hw_state); + tgt = sc->target[target_id]; + tgt->flags = 0;/* we donno yet */ + tgt->dev_ops = 0; + + /* mildly enquire */ + if (!(sc->probe)(tgt, ior)) + goto fail; + + /* There is something there, see what it is */ + bzero(&ui, sizeof(ui)); + ui.ctlr = sc->masterno; + ui.unit = + ui.slave = target_id; + ui.name = ""; + + /* this fails on the name for sure */ + (void) scsi_slave( &ui, 0 /* brrrr */); + if ((tgt->flags & TGT_ALIVE) == 0) + goto fail; + + { + struct bus_ctlr mi; + + mi.unit = sc->masterno; + ui.mi = &mi; + printf("%s at slave %d ", + (*tgt->dev_ops->driver_name)(TRUE), target_id); + scsi_attach(&ui); + } + + *tgt_ptr = tgt; + return TRUE; +fail: + tgt->flags = 0; + return FALSE; +} + + +/* + * Watchdog routine: + * Issue a SCSI bus reset if a target holds up the + * bus for too long. + * + * Implementation: + * Each HBA that wants to use this should have a + * watchdog_t structure at the head of its hardware + * descriptor. This variable is set by this periodic + * routine and reset on bus activity. If it is not reset on + * time (say some ten seconds or so) we reset the + * SCSI bus. + * NOTE: + * An HBA must be ready to accept bus reset interrupts + * properly in order to use this. + */ +void scsi_watchdog(hw) + watchdog_t *hw; +{ + spl_t s = splbio(); + + switch (hw->watchdog_state) { + case SCSI_WD_EXPIRED: + + /* double check first */ + if (hw->nactive == 0) { + hw->watchdog_state = SCSI_WD_INACTIVE; + break; + } + if (scsi_debug) + printf("SCSI Watchdog expired\n"); + hw->watchdog_state = SCSI_WD_INACTIVE; + (*hw->reset)(hw); + break; + + case SCSI_WD_ACTIVE: + + hw->watchdog_state = SCSI_WD_EXPIRED; + break; + + case SCSI_WD_INACTIVE: + + break; + } + + /* do this here, fends against powered down devices */ + if (scsi_watchdog_period != 0) + timeout((int(*)())scsi_watchdog, (char*)hw, scsi_watchdog_period * hz); + + splx(s); +} + + +/* + * BusReset Notification: + * Called when the HBA sees a BusReset interrupt + * + * Implementation: + * Go through the list of targets, redo the synch + * negotiation, and restart whatever operation was + * in progress for that target. + */ +void scsi_bus_was_reset(sc) + scsi_softc_t *sc; +{ + register target_info_t *tgt; + int i; + /* + * Redo the synch negotiation + */ + for (i = 0; i < 8; i++) { + io_req_t ior; + spl_t s; + + if (i == sc->initiator_id) + continue; + tgt = sc->target[i]; + if (!tgt || !(tgt->flags & TGT_ALIVE)) + continue; + + tgt->flags &= ~(TGT_DID_SYNCH|TGT_DISCONNECTED); +#if 0 + /* the standard does *not* imply this gets reset too */ + tgt->sync_period = 0; + tgt->sync_offset = 0; +#endif + + /* + * retry the synch negotiation + */ + ior = tgt->ior; + tgt->ior = 0; + printf(".. tgt %d ", tgt->target_id); + if (BGET(scsi_no_synchronous_xfer,(unsigned char)sc->masterno,tgt->target_id)) + tgt->flags |= TGT_DID_SYNCH; + else { + s = spl0(); + scsi_test_unit_ready(tgt, 0); + splx(s); + } + tgt->ior = ior; + } + + /* + * Notify each target of the accident + */ + for (i = 0; i < 8; i++) { + if (i == sc->initiator_id) + continue; + tgt = sc->target[i]; + if (!tgt) + continue; + tgt->done = SCSI_RET_ABORTED|SCSI_RET_RETRY; + if (tgt->ior) + (*tgt->dev_ops->restart)( tgt, TRUE); + } + + printf("%s", " reset complete\n"); +} + +#endif NSCSI > 0 diff --git a/scsi/scsi.h b/scsi/scsi.h new file mode 100644 index 0000000..9aa0bee --- /dev/null +++ b/scsi/scsi.h @@ -0,0 +1,599 @@ +/* + * 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: scsi.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Definitions of the SCSI-1 Standard + */ + +#ifndef _SCSI_SCSI_H_ +#define _SCSI_SCSI_H_ + +#include <nscsi.h> +#include <scsi/scsi_endian.h> + +/* + * Bus phases + */ + +#define SCSI_IO 0x01 /* Input/Output */ +#define SCSI_CD 0x02 /* Command/Data */ +#define SCSI_MSG 0x04 /* Message */ + +#define SCSI_PHASE_MASK 0x07 +#define SCSI_PHASE(x) ((x)&SCSI_PHASE_MASK) + +#define SCSI_PHASE_DATAO 0x00 /* 0 */ +#define SCSI_PHASE_DATAI SCSI_IO /* 1 */ +#define SCSI_PHASE_CMD SCSI_CD /* 2 */ +#define SCSI_PHASE_STATUS (SCSI_CD|SCSI_IO) /* 3 */ + /* 4..5 ANSI reserved */ +#define SCSI_PHASE_MSG_OUT (SCSI_MSG|SCSI_CD) /* 6 */ +#define SCSI_PHASE_MSG_IN (SCSI_MSG|SCSI_CD|SCSI_IO) /* 7 */ + +/* + * Single byte messages + * + * originator: I-nitiator T-arget + * T-support: M-andatory O-ptional + */ + +#define SCSI_COMMAND_COMPLETE 0x00 /* M T */ +#define SCSI_EXTENDED_MESSAGE 0x01 /* IT */ +#define SCSI_SAVE_DATA_POINTER 0x02 /* O T */ +#define SCSI_RESTORE_POINTERS 0x03 /* O T */ +#define SCSI_DISCONNECT 0x04 /* O T */ +#define SCSI_I_DETECTED_ERROR 0x05 /* M I */ +#define SCSI_ABORT 0x06 /* M I */ +#define SCSI_MESSAGE_REJECT 0x07 /* M IT */ +#define SCSI_NOP 0x08 /* M I */ +#define SCSI_MSG_PARITY_ERROR 0x09 /* M I */ +#define SCSI_LNKD_CMD_COMPLETE 0x0a /* O T */ +#define SCSI_LNKD_CMD_COMPLETE_F 0x0b /* O T */ +#define SCSI_BUS_DEVICE_RESET 0x0c /* M I */ + /* 0x0d..0x11 scsi2 */ + /* 0x12..0x1f reserved */ +#define SCSI_IDENTIFY 0x80 /* IT */ +# define SCSI_IFY_ENABLE_DISCONNECT 0x40 /* I */ +# define SCSI_IFY_LUNTAR 0x20 /* IT */ +# define SCSI_IFY_LUN_MASK 0x07 /* IT */ + + +/* Message codes 0x30..0x7f are reserved */ + +/* + * Extended messages, codes and formats + */ + +#define SCSI_MODIFY_DATA_PTR 0x00 /* T */ +typedef struct { + unsigned char xtn_msg_tag; /* const 0x01 */ + unsigned char xtn_msg_len; /* const 0x05 */ + unsigned char xtn_msg_code; /* const 0x00 */ + unsigned char xtn_msg_arg_1000; /* MSB, signed 2cmpl */ + unsigned char xtn_msg_arg_0200; + unsigned char xtn_msg_arg_0030; + unsigned char xtn_msg_arg_0004; /* LSB */ +} scsi_mod_ptr_t; + +#define SCSI_SYNC_XFER_REQUEST 0x01 /* IT */ +typedef struct { + unsigned char xtn_msg_tag; /* const 0x01 */ + unsigned char xtn_msg_len; /* const 0x03 */ + unsigned char xtn_msg_code; /* const 0x01 */ + unsigned char xtn_msg_xfer_period; /* times 4nsecs */ + unsigned char xtn_msg_xfer_offset; /* pending ack window */ +#define SCSI_SYNCH_XFER_OFFANY 0xff /* T unlimited */ +} scsi_synch_xfer_req_t; + +#define SCSI_XTN_IDENTIFY 0x02 /* IT -2 */ +typedef struct { + unsigned char xtn_msg_tag; /* const 0x01 */ + unsigned char xtn_msg_len; /* const 0x02 */ + unsigned char xtn_msg_code; /* const 0x02 */ + unsigned char xtn_msg_sublun; +} scsi_xtn_identify_t; + + /* 0x03..0x7f reserved */ + +#define SCSI_XTN_VENDOR_UQE 0x80 /* vendor unique bit */ +typedef struct { + unsigned char xtn_msg_tag; /* const 0x01 */ + unsigned char xtn_msg_len; /* args' len+1 (0-->256)*/ + unsigned char xtn_msg_code; /* const 0x80..0xff */ + unsigned char xtn_msg_args[1]; /* 0..255 bytes */ +} scsi_xtn_vedor_unique_t; + + +/* + * Commands, generic structures + */ + +/* SIX byte commands */ +typedef struct { + unsigned char scsi_cmd_code; /* group(7..5) and command(4..1) */ +#define SCSI_CODE_GROUP 0xe0 +#define SCSI_CODE_CMD 0x1f + unsigned char scsi_cmd_lun_and_lba1; /* lun(7..5) and block# msb[20..16] */ +#define SCSI_LUN_MASK 0xe0 +#define SCSI_LBA_MASK 0x1f +#define SCSI_LUN_SHIFT 5 + unsigned char scsi_cmd_lba2; /* block#[15.. 8] */ + unsigned char scsi_cmd_lba3; /* block#[ 7.. 0] */ + unsigned char scsi_cmd_xfer_len; /* if required */ + unsigned char scsi_cmd_ctrl_byte; /* contains: */ +#define SCSI_CTRL_VUQ 0xc0 /* vendor unique bits */ +#define SCSI_CTRL_RESVD 0x3c /* reserved, mbz */ +#define SCSI_CTRL_FLAG 0x02 /* send a complete_with_flag at end */ +#define SCSI_CTRL_LINK 0x01 /* link this command with next */ +} scsi_command_group_0; + +/* TEN byte commands */ +typedef struct { + unsigned char scsi_cmd_code; /* group(7..5) and command(4..1) */ + unsigned char scsi_cmd_lun_and_relbit;/* lun(7..5) and RelAdr(0) */ +#define SCSI_RELADR 0x01 + unsigned char scsi_cmd_lba1; /* block#[31..24] */ + unsigned char scsi_cmd_lba2; /* block#[23..16] */ + unsigned char scsi_cmd_lba3; /* block#[15.. 8] */ + unsigned char scsi_cmd_lba4; /* block#[ 7.. 0] */ + unsigned char scsi_cmd_xxx; /* reserved, mbz */ + unsigned char scsi_cmd_xfer_len_1; /* if required */ + unsigned char scsi_cmd_xfer_len_2; /* if required */ + unsigned char scsi_cmd_ctrl_byte; /* see above */ +} scsi_command_group_1, + scsi_command_group_2; + +/* TWELVE byte commands */ +typedef struct { + unsigned char scsi_cmd_code; /* group(7..5) and command(4..1) */ + unsigned char scsi_cmd_lun_and_relbit;/* lun(7..5) and RelAdr(0) */ + unsigned char scsi_cmd_lba1; /* block#[31..24] */ + unsigned char scsi_cmd_lba2; /* block#[23..16] */ + unsigned char scsi_cmd_lba3; /* block#[15.. 8] */ + unsigned char scsi_cmd_lba4; /* block#[ 7.. 0] */ + unsigned char scsi_cmd_xfer_len_1; /* if required */ + unsigned char scsi_cmd_xfer_len_2; /* if required */ + unsigned char scsi_cmd_xfer_len_3; /* if required */ + unsigned char scsi_cmd_xfer_len_4; /* if required */ + unsigned char scsi_cmd_xxx1; /* reserved, mbz */ + unsigned char scsi_cmd_ctrl_byte; /* see above */ +} scsi_command_group_5; + + +/* + * Commands, codes and aliases + */ + + /* GROUP 0 */ +#define SCSI_CMD_TEST_UNIT_READY 0x00 /* O all 2M all */ +#define scsi_cmd_test_unit_ready_t scsi_command_group_0 + +#define SCSI_CMD_REZERO_UNIT 0x01 /* O disk worm rom */ +#define SCSI_CMD_REWIND 0x01 /* M tape */ +#define scsi_cmd_rewind_t scsi_command_group_0 +#define scsi_cmd_rezero_t scsi_command_group_0 +# define SCSI_CMD_REW_IMMED 0x01 + + /* 0x02 vendor unique */ + +#define SCSI_CMD_REQUEST_SENSE 0x03 /* M all */ +#define scsi_cmd_request_sense_t scsi_command_group_0 +# define scsi_cmd_allocation_length scsi_cmd_xfer_len + +#define SCSI_CMD_FORMAT_UNIT 0x04 /* M disk O prin */ +#define scsi_cmd_format_t scsi_command_group_0 +# define SCSI_CMD_FMT_FMTDATA 0x10 +# define SCSI_CMD_FMT_CMPLIST 0x08 +# define SCSI_CMD_FMT_LIST_TYPE 0x07 +# define scsi_cmd_intleave1 scsi_cmd_lba3 +# define scsi_cmd_intleave2 scsi_cmd_xfer_len + +#define SCSI_CMD_READ_BLOCK_LIMITS 0x05 /* E tape */ +#define scsi_cmd_block_limits_t scsi_command_group_0 + + /* 0x06 vendor unique */ + +#define SCSI_CMD_REASSIGN_BLOCKS 0x07 /* O disk worm */ +#define scsi_cmd_reassign_blocks_t scsi_command_group_0 + +#define SCSI_CMD_READ 0x08 /* M disk tape O worm rom */ +#define SCSI_CMD_RECEIVE 0x08 /* O proc */ +#define SCSI_CMD_GET_MESSAGE 0x08 /* M comm */ +#define scsi_cmd_read_t scsi_command_group_0 +# define SCSI_CMD_TP_FIXED 0x01 /* tape */ +# define scsi_cmd_tp_len1 scsi_cmd_lba2 +# define scsi_cmd_tp_len2 scsi_cmd_lba3 +# define scsi_cmd_tp_len3 scsi_cmd_xfer_len + /* largest addressable blockno */ +#define SCSI_CMD_READ_MAX_LBA ((1 << 21) - 1) + + /* 0x09 vendor unique */ + +#define SCSI_CMD_WRITE 0x0a /* M disk tape O worm */ +#define SCSI_CMD_PRINT 0x0a /* M prin */ +#define SCSI_CMD_SEND 0x0a /* M proc */ +#define SCSI_CMD_SEND_MESSAGE 0x0a /* M comm */ +#define scsi_cmd_write_t scsi_command_group_0 + +#define SCSI_CMD_SEEK 0x0b /* O disk worm rom */ +#define SCSI_CMD_TRACK_SELECT 0x0b /* O tape */ +#define SCSI_CMD_SLEW_AND_PRINT 0x0b /* O prin */ +#define scsi_cmd_seek_t scsi_command_group_0 +# define SCSI_CMD_SLW_CHANNEL 0x01 +# define scsi_cmd_tp_trackno scsi_cmd_xfer_len +# define scsi_cmd_slew_value scsi_cmd_lba2 + + /* 0x0c..0x0e vendor unique */ + +#define SCSI_CMD_READ_REVERSE 0x0f /* O tape */ +#define scsi_cmd_rev_read_t scsi_command_group_0 + +#define SCSI_CMD_WRITE_FILEMARKS 0x10 /* M tape */ +#define SCSI_CMD_FLUSH_BUFFER 0x10 /* M prin */ +#define scsi_cmd_write_fil_t scsi_command_group_0 + +#define SCSI_CMD_SPACE 0x11 /* O tape */ +#define scsi_cmd_space_t scsi_command_group_0 +# define SCSI_CMD_SP_BLOCKS 0x00 +# define SCSI_CMD_SP_FIL 0x01 +# define SCSI_CMD_SP_SEQ_FIL 0x02 +# define SCSI_CMD_SP_EOT 0x03 + +#define SCSI_CMD_INQUIRY 0x12 /* E all (2M all) */ +#define scsi_cmd_inquiry_t scsi_command_group_0 +# define SCSI_CMD_INQ_EVPD 0x01 /* 2 */ +# define scsi_cmd_page_code scsi_cmd_lba2 /* 2 */ + +#define SCSI_CMD_VERIFY_0 0x13 /* O tape */ +#define scsi_cmd_verify_t scsi_command_group_0 +# define SCSI_CMD_VFY_BYTCMP 0x02 + +#define SCSI_CMD_RECOVER_BUFFERED_DATA 0x14 /* O tape prin */ +#define scsi_cmd_recover_buffer_t scsi_command_group_0 + +#define SCSI_CMD_MODE_SELECT 0x15 /* O disk tape prin worm rom */ +# define SCSI_CMD_MSL_PF 0x10 +# define SCSI_CMD_MSL_SP 0x01 +#define scsi_cmd_mode_select_t scsi_command_group_0 + +#define SCSI_CMD_RESERVE 0x16 /* O disk tape prin worm rom */ +#define scsi_cmd_reserve_t scsi_command_group_0 +# define SCSI_CMD_RES_3RDPTY 0x10 +# define SCSI_CMD_RES_3RDPTY_DEV 0x0e +# define SCSI_CMD_RES_EXTENT 0x01 +# define scsi_cmd_reserve_id scsi_cmd_lba2 +# define scsi_cmd_extent_llen1 scsi_cmd_lba3 +# define scsi_cmd_extent_llen2 scsi_cmd_xfer_len + +#define SCSI_CMD_RELEASE 0x17 /* O disk tape prin worm rom */ +#define scsi_cmd_release_t scsi_command_group_0 + +#define SCSI_CMD_COPY 0x18 /* O all */ +#define scsi_cmd_copy_t scsi_command_group_0 +# define SCSI_CMD_CPY_PAD 0x01 /* 2 */ +# define scsi_cmd_paraml_len0 scsi_cmd_lba2 +# define scsi_cmd_paraml_len1 scsi_cmd_lba3 +# define scsi_cmd_paraml_len2 scsi_cmd_xfer_len + +#define SCSI_CMD_ERASE 0x19 /* O tape */ +#define scsi_cmd_erase_t scsi_command_group_0 +# define SCSI_CMD_ER_LONG 0x01 + +#define SCSI_CMD_MODE_SENSE 0x1a /* O disk tape prin worm rom */ +#define scsi_cmd_mode_sense_t scsi_command_group_0 +# define scsi_cmd_ms_pagecode scsi_cmd_lba2 + +#define SCSI_CMD_START_STOP_UNIT 0x1b /* O disk prin worm rom */ +#define SCSI_CMD_LOAD_UNLOAD 0x1b /* O tape */ +#define scsi_cmd_start_t scsi_command_group_0 +# define SCSI_CMD_SS_IMMED 0x01 +# define scsi_cmd_ss_flags scsi_cmd_xfer_len +# define SCSI_CMD_SS_START 0x01 +# define SCSI_CMD_SS_RETEN 0x02 +# define SCSI_CMD_SS_RETAIN 0x01 +# define SCSI_CMD_SS_EJECT 0x02 + +#define SCSI_CMD_RECEIVE_DIAG_RESULTS 0x1c /* O all */ +#define scsi_cmd_receive_diag_t scsi_command_group_0 +# define scsi_cmd_allocation_length1 scsi_cmd_lba3 +# define scsi_cmd_allocation_length2 scsi_cmd_xfer_len + +#define SCSI_CMD_SEND_DIAGNOSTICS 0x1d /* O all */ +#define scsi_cmd_send_diag_t scsi_command_group_0 +# define SCSI_CMD_DIAG_SELFTEST 0x04 +# define SCSI_CMD_DIAG_DEVOFFL 0x02 +# define SCSI_CMD_DIAG_UNITOFFL 0x01 + +#define SCSI_CMD_PREVENT_ALLOW_REMOVAL 0x1e /* O disk tape worm rom */ +#define scsi_cmd_medium_removal_t scsi_command_group_0 +# define scsi_cmd_pa_prevent scsi_cmd_xfer_len /* 0x1 */ + + /* 0x1f reserved */ + + /* GROUP 1 */ + /* 0x20..0x24 vendor unique */ + +#define SCSI_CMD_READ_CAPACITY 0x25 /* E disk worm rom */ +#define scsi_cmd_read_capacity_t scsi_command_group_1 +# define scsi_cmd_rcap_flags scsi_cmd_xfer_len_2 +# define SCSI_CMD_RCAP_PMI 0x01 + + /* 0x26..0x27 vendor unique */ + +#define SCSI_CMD_LONG_READ 0x28 /* E disk M worm rom */ +#define scsi_cmd_long_read_t scsi_command_group_1 + + /* 0x29 vendor unique */ + +#define SCSI_CMD_LONG_WRITE 0x2a /* E disk M worm */ +#define scsi_cmd_long_write_t scsi_command_group_1 + +#define SCSI_CMD_LONG_SEEK 0x2b /* O disk worm rom */ +#define scsi_cmd_long_seek_t scsi_command_group_1 + + /* 0x2c..0x2d vendor unique */ + +#define SCSI_CMD_WRITE_AND_VERIFY 0x2e /* O disk worm */ +#define scsi_cmd_write_vfy_t scsi_command_group_1 +# define SCSI_CMD_VFY_BYTCHK 0x02 + +#define SCSI_CMD_VERIFY_1 0x2f /* O disk worm rom */ +#define scsi_cmd_verify_long_t scsi_command_group_1 +# define SCSI_CMD_VFY_BLKVFY 0x04 + +#define SCSI_CMD_SEARCH_HIGH 0x30 /* O disk worm rom */ +#define scsi_cmd_search_t scsi_command_group_1 +# define SCSI_CMD_SRCH_INVERT 0x10 +# define SCSI_CMD_SRCH_SPNDAT 0x02 + +#define SCSI_CMD_SEARCH_EQUAL 0x31 /* O disk worm rom */ +#define SCSI_CMD_SEARCH_LOW 0x32 /* O disk worm rom */ + +#define SCSI_CMD_SET_LIMITS 0x33 /* O disk worm rom */ +#define scsi_cmd_set_limits_t scsi_command_group_1 +# define SCSI_CMD_SL_RDINH 0x02 +# define SCSI_CMD_SL_WRINH 0x01 + + /* 0x34..0x38 reserved */ + +#define SCSI_CMD_COMPARE 0x39 /* O all */ +#define scsi_cmd_compare_t scsi_command_group_1 +# define scsi_cmd_1_paraml1 scsi_cmd_lba2 +# define scsi_cmd_1_paraml2 scsi_cmd_lba3 +# define scsi_cmd_1_paraml3 scsi_cmd_lba4 + +#define SCSI_CMD_COPY_AND_VERIFY 0x3a /* O all */ +#define scsi_cmd_copy_vfy_t scsi_command_group_1 +# define SCSI_CMD_CPY_BYTCHK 0x02 + + /* 0x3b..0x3f reserved */ + + /* GROUP 2 */ + /* 0x40..0x5f reserved */ + + /* GROUP 3 */ + /* 0x60..0x7f reserved */ + + /* GROUP 4 */ + /* 0x80..0x9f reserved */ + + /* GROUP 5 */ + /* 0xa0..0xaf vendor unique */ + /* 0xb0..0xbf reserved */ + + /* GROUP 6 */ + /* 0xc0..0xdf vendor unique */ + + /* GROUP 7 */ + /* 0xe0..0xff vendor unique */ + + +/* + * Command-specific results and definitions + */ + +/* inquiry data */ +typedef struct { + unsigned char periph_type; +#define SCSI_DISK 0x00 +#define SCSI_TAPE 0x01 +#define SCSI_PRINTER 0x02 +#define SCSI_CPU 0x03 +#define SCSI_WORM 0x04 +#define SCSI_CDROM 0x05 + + BITFIELD_2( unsigned char, + device_type : 7, + rmb : 1); + BITFIELD_3( unsigned char, + ansi : 3, + ecma : 3, + iso : 2); + unsigned char reserved; + unsigned char length; + unsigned char param[1]; +} scsi_inquiry_data_t; + +#define SCSI_INQ_STD_DATA -1 + +/* request sense data */ +#define SCSI_SNS_NOSENSE 0x0 +#define SCSI_SNS_RECOVERED 0x1 +#define SCSI_SNS_NOTREADY 0x2 +#define SCSI_SNS_MEDIUM_ERR 0x3 +#define SCSI_SNS_HW_ERR 0x4 +#define SCSI_SNS_ILLEGAL_REQ 0x5 +#define SCSI_SNS_UNIT_ATN 0x6 +#define SCSI_SNS_PROTECT 0x7 +#define SCSI_SNS_BLANK_CHK 0x8 +#define SCSI_SNS_VUQE 0x9 +#define SCSI_SNS_COPY_ABRT 0xa +#define SCSI_SNS_ABORTED 0xb +#define SCSI_SNS_EQUAL 0xc +#define SCSI_SNS_VOLUME_OVFL 0xd +#define SCSI_SNS_MISCOMPARE 0xe +#define SCSI_SNS_RESERVED 0xf + +typedef struct { + BITFIELD_3( unsigned char, + error_code : 4, + error_class : 3, + addr_valid : 1); +# define SCSI_SNS_XTENDED_SENSE_DATA 0x7 /* e.g. error_class=7 */ + union { + struct { + BITFIELD_2(unsigned char, + lba_msb : 5, + vuqe : 3); + unsigned char lba; + unsigned char lba_lsb; + } non_xtnded; + struct { + unsigned char segment_number; + BITFIELD_5(unsigned char, + sense_key : 4, + res : 1, + ili : 1, + eom : 1, + fm : 1); + unsigned char info0; + unsigned char info1; + unsigned char info2; + unsigned char info3; + unsigned char add_len; + unsigned char add_bytes[1];/* VARSIZE */ + } xtended; + } u; +} scsi_sense_data_t; + + +/* mode select params */ +typedef struct { + unsigned char reserved1; + unsigned char medium_type; + BITFIELD_3(unsigned char, + speed:4, /* special for tapes, reserved in SCSI-1 */ + buffer_mode:3, + reserved2:1); + unsigned char desc_len; + struct scsi_mode_parm_blockdesc { + unsigned char density_code; + unsigned char nblocks1; + unsigned char nblocks2; + unsigned char nblocks3; + unsigned char reserved; + unsigned char reclen1; + unsigned char reclen2; + unsigned char reclen3; + } descs[1]; /* VARSIZE, really */ +} scsi_mode_select_param_t; + +/* mode sense data (TAPE) */ +typedef struct { + unsigned char data_len; + unsigned char medium_type; + BITFIELD_3(unsigned char, + speed : 4, + buffered_mode : 3, + wp : 1); + unsigned char bdesc_len; + struct { + unsigned char density_code; + unsigned char no_blks_msb; + unsigned char no_blks; + unsigned char no_blks_lsb; + unsigned char reserved; + unsigned char blen_msb; + unsigned char blen; + unsigned char blen_lsb; + } bdesc[1]; /* VARSIZE */ + /* vuqe data might follow */ +} scsi_mode_sense_data_t; + +/* read capacity data */ +typedef struct { + unsigned char lba1; + unsigned char lba2; + unsigned char lba3; + unsigned char lba4; + unsigned char blen1; + unsigned char blen2; + unsigned char blen3; + unsigned char blen4; +} scsi_rcap_data_t; + +/* defect list(s) */ +typedef struct { + unsigned char res1; + unsigned char res2; + unsigned char list_len_msb; + unsigned char list_len_lsb; + struct { + unsigned char blockno_msb; + unsigned char blockno_sb1; + unsigned char blockno_sb2; + unsigned char blockno_lsb; + } defects[1]; /* VARSIZE */ +} scsi_Ldefect_data_t; + +/* block limits (tape) */ +typedef struct { + unsigned char res1; + unsigned char maxlen_msb; + unsigned char maxlen_sb; + unsigned char maxlen_lsb; + unsigned char minlen_msb; + unsigned char minlen_lsb; +} scsi_blimits_data_t; + +/* + * Status byte (a-la scsi1) + */ + +typedef union { + struct { + BITFIELD_4( unsigned char, + scsi_status_vendor_uqe1:1, + scsi_status_code:4, + scsi_status_vendor_uqe:2, + scsi_status_reserved:1); +# define SCSI_ST_GOOD 0x00 /* scsi_status_code values */ +# define SCSI_ST_CHECK_CONDITION 0x01 +# define SCSI_ST_CONDITION_MET 0x02 +# define SCSI_ST_BUSY 0x04 +# define SCSI_ST_INT_GOOD 0x08 +# define SCSI_ST_INT_MET 0x0a +# define SCSI_ST_RES_CONFLICT 0x0c + /* anything else is reserved */ + } st; + unsigned char bits; +} scsi_status_byte_t; + + +#endif _SCSI_SCSI_H_ diff --git a/scsi/scsi2.h b/scsi/scsi2.h new file mode 100644 index 0000000..93a5a76 --- /dev/null +++ b/scsi/scsi2.h @@ -0,0 +1,447 @@ +/* + * 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: scsi2.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Additions and changes of the SCSI-II standard viz SCSI-I + */ + +#ifndef _SCSI_SCSI2_H_ +#define _SCSI_SCSI2_H_ + +#include <scsi/scsi_endian.h> + +/* + * Single byte messages + * + * originator: I-nitiator T-arget + * T-support: M-andatory O-ptional + */ + +#define SCSI_ABORT_TAG 0x0d /* O I 2 */ +#define SCSI_CLEAR_QUEUE 0x0e /* O I 2 */ +#define SCSI_INITIATE_RECOVERY 0x0f /* O IT2 */ +#define SCSI_RELEASE_RECOVERY 0x10 /* O I 2 */ +#define SCSI_TERMINATE_IO_PROCESS 0x11 /* O I 2 */ + +/* + * Two byte messages + */ +#define SCSI_SIMPLE_QUEUE_TAG 0x20 /* O IT2 */ +#define SCSI_HEADOF_QUEUE_TAG 0x21 /* O I 2 */ +#define SCSI_ORDERED_QUEUE_TAG 0x22 /* O I 2 */ +#define SCSI_IGNORE_WIDE_RESIDUE 0x23 /* O T2 */ + /* 0x24..0x2f reserved */ + +/* + * Extended messages, codes and formats + */ + +#define SCSI_WIDE_XFER_REQUEST 0x03 /* IT 2 */ +typedef struct { + unsigned char xtn_msg_tag; /* const 0x01 */ + unsigned char xtn_msg_len; /* const 0x02 */ + unsigned char xtn_msg_code; /* const 0x03 */ + unsigned char xtn_msg_xfer_width; +} scsi_wide_xfer_t; + +/* + * NOTE: some command-specific mods and extensions + * are actually defined in the scsi.h file for + * readability reasons + */ + + /* GROUP 1 */ + +#define SCSI_CMD_READ_DEFECT_DATA 0x37 /* O2 disk opti */ +typedef scsi_command_group_1 scsi_cmd_read_defect_t; +# define SCSI_CMD_RDD_LIST_TYPE 0x07 +# define SCSI_CMD_RDD_GLIST 0x08 +# define SCSI_CMD_RDD_PLIST 0x10 + +#define SCSI_CMD_WRITE_BUFFER 0x3b /* O2 all */ +typedef scsi_command_group_1 scsi_cmd_write_buffer_t; +# define SCSI_CMD_BUF_MODE 0x07 +# define scsi_cmd_buf_id scs_cmd_lba1 +# define scsi_cmd_buf_offset1 scs_cmd_lba2 +# define scsi_cmd_buf_offset2 scs_cmd_lba3 +# define scsi_cmd_buf_offset3 scs_cmd_lba4 +# define scsi_cmd_buf_alloc1 scs_cmd_xxx +# define scsi_cmd_buf_alloc2 scs_cmd_xfer_len_1 +# define scsi_cmd_buf_alloc3 scs_cmd_xfer_len_2 + +#define SCSI_CMD_READ_BUFFER 0x3c /* O2 all */ +#define scsi_cmd_read_buffer_t scsi_command_group_1 + + /* GROUP 2 */ + +#define SCSI_CMD_CHANGE_DEFINITION 0x40 /* O2 all */ +#define scsi_cmd_change_def_t scsi_command_group_2 +# define scsi_cmd_chg_save scsi_cmd_lba1 +# define scsi_cmd_chg_definition scsi_cmd_lba2 +# define SCSI_CMD_CHG_CURRENT 0x00 +# define SCSI_CMD_CHG_SCSI_1 0x01 +# define SCSI_CMD_CHG_CCS 0x02 +# define SCSI_CMD_CHG_SCSI_2 0x03 + + /* 0x41 reserved */ + +#define SCSI_CMD_READ_SUBCH 0x42 /* O2 rom */ +#define scsi_cmd_read_subch_t scsi_command_group_2 +# define SCSI_CMD_CD_MSF 0x02 +# define SCSI_CMD_RS_SUBQ 0x40 +# define scsi_cmd_rs_format scsi_cmd_lba2 +# define SCSI_CMD_RS_FMT_SUBQ 0x00 +# define SCSI_CMD_RS_FMT_CURPOS 0x01 +# define SCSI_CMD_RS_FMT_CATALOG 0x02 +# define SCSI_CMD_RS_FMT_ISRC 0x03 +# define scsi_cmd_rs_trackno scsi_cmd_xxx + + +#define SCSI_CMD_READ_TOC 0x43 /* O2 rom */ +#define scsi_cmd_read_toc_t scsi_command_group_2 +# define scsi_cmd_rtoc_startT scsi_cmd_xxx + +#define SCSI_CMD_READ_HEADER 0x44 /* O2 rom */ +#define scsi_cmd_read_header_t scsi_command_group_2 + +#define SCSI_CMD_PLAY_AUDIO 0x45 /* O2 rom */ +#define scsi_cmd_play_audio_t scsi_command_group_2 + +#define SCSI_CMD_PLAY_AUDIO_MSF 0x47 /* O2 rom */ +#define scsi_cmd_play_audio_msf_t scsi_command_group_2 +# define scsi_cmd_pamsf_startM scsi_cmd_lba2 +# define scsi_cmd_pamsf_startS scsi_cmd_lba3 +# define scsi_cmd_pamsf_startF scsi_cmd_lba4 +# define scsi_cmd_pamsf_endM scsi_cmd_xxx +# define scsi_cmd_pamsf_endS scsi_cmd_xfer_len_1 +# define scsi_cmd_pamsf_endF scsi_cmd_xfer_len_2 + +#define SCSI_CMD_PLAY_AUDIO_TI 0x48 /* O2 rom */ +#define scsi_cmd_play_audio_ti_t scsi_command_group_2 +# define scsi_cmd_pati_startT scsi_cmd_lba3 +# define scsi_cmd_pati_startI scsi_cmd_lba4 +# define scsi_cmd_pati_endT scsi_cmd_xfer_len_1 +# define scsi_cmd_pati_endI scsi_cmd_xfer_len_2 + +#define SCSI_CMD_PLAY_AUDIO_TR 0x49 /* O2 rom */ +#define scsi_cmd_play_audio_tr_t scsi_command_group_2 +# define scsi_cmd_patr_startT scsi_cmd_xxx + + +#define SCSI_CMD_PAUSE_RESUME 0x4b /* O2 rom */ +#define scsi_cmd_pausres_t scsi_command_group_2 +# define SCSI_CMD_PAUSRES_RESUME 0x01 +# define scsi_cmd_pausres_res scsi_cmd_xfer_len_2 + +#define SCSI_CMD_LOG_SELECT 0x4c /* O2 all */ +#define scsi_cmd_logsel_t scsi_command_group_2 +# define SCSI_CMD_LOG_SP 0x01 +# define SCSI_CMD_LOG_PCR 0x02 +# define scsi_cmd_log_page_control scsi_cmd_lba1 + +#define SCSI_CMD_LOG_SENSE 0x4d /* O2 all */ +#define scsi_cmd_logsense_t scsi_command_group_2 +# define SCSI_CMD_LOG_PPC 0x02 +# define scsi_cmd_log_page_code scsi_cmd_lba1 +# define scsi_cmd_log_param_ptr1 scsi_cmd_lba4 +# define scsi_cmd_log_param_ptr2 scsi_cmd_xxx + + + /* 0x4e..0x54 reserved */ + +#define SCSI_CMD_MODE_SELECT_2 0x55 /* Z2 */ +#define scsi_cmd_mode_select_long_t scsi_command_group_2 +# define SCSI_CMD_MSL2_PF 0x10 +# define SCSI_CMD_MSL2_SP 0x01 + + /* 0x56..0x59 reserved */ + +#define SCSI_CMD_MODE_SENSE_2 0x5a /* Z2 */ +#define scsi_cmd_mode_sense_long_t scsi_command_group_2 +# define SCSI_CMD_MSS2_DBD 0x08 + + /* 0x5b..0x5f reserved */ + + /* GROUP 5 */ + +#define SCSI_CMD_PLAY_AUDIO_LONG 0xa5 /* O2 rom */ +#define scsi_cmd_play_audio_l_t scsi_command_group_5 + +#define SCSI_CMD_PLAY_AUDIO_TR_LONG 0xa9 /* O2 rom */ +#define scsi_cmd_play_audio_tr_l_t scsi_command_group_5 +# define scsi_cmd_patrl_startT scsi_cmd_xxx1 + + +/* + * Command specific defines + */ +typedef struct { + BITFIELD_2(unsigned char, + periph_type : 5, + periph_qual : 3); +#define SCSI_SCANNER 0x06 /* periph_type values */ +#define SCSI_MEMORY 0x07 +#define SCSI_J_BOX 0x08 +#define SCSI_COMM 0x09 +#define SCSI_PREPRESS1 0x0a +#define SCSI_PREPRESS2 0x0b + +#define SCSI_PERIPH_CONNECTED 0x00 /* periph_qual values */ +#define SCSI_PERIPH_DISCONN 0x20 +#define SCSI_PERIPH_NOTHERE 0x30 + + BITFIELD_2(unsigned char, + device_type : 7, + rmb : 1); + + BITFIELD_3( unsigned char, + ansi : 3, + ecma : 3, + iso : 2); + + BITFIELD_4( unsigned char, + response_fmt : 4, + res1 : 2, + trmIOP : 1, + aenc : 1); + unsigned char length; + unsigned char res2; + unsigned char res3; + + BITFIELD_8(unsigned char, + SftRe : 1, + CmdQue : 1, + res4 : 1, + Linked : 1, + Sync : 1, + Wbus16 : 1, + Wbus32 : 1, + RelAdr : 1); + + unsigned char vendor_id[8]; + unsigned char product_id[16]; + unsigned char product_rev[4]; + unsigned char vendor_uqe[20]; + unsigned char reserved[40]; + unsigned char vendor_uqe1[1]; /* varsize */ +} scsi2_inquiry_data_t; +#define SCSI_INQ_SUPP_PAGES 0x00 +#define SCSI_INQ_A_INFO 0x01 /* 0x01..0x1f, really */ +#define SCSI_INQ_SERIALNO 0x80 +#define SCSI_INQ_IMPL_OPDEF 0x81 +#define SCSI_INQ_A_IMPL_OPDEF 0x82 + +/* mode_select */ +typedef struct { + unsigned char data_len; + unsigned char medium_type; + unsigned char device_specific; + unsigned char desc_len; + /* block descriptors are optional, same struct as scsi1 */ + /* page info then follows, see individual pages */ +} scsi2_mode_param_t; + +/* + * CDROM thingies + */ +typedef union { + struct { + unsigned char xxx; + unsigned char minute; + unsigned char second; + unsigned char frame; + } msf; + struct { + unsigned char lba1; + unsigned char lba2; + unsigned char lba3; + unsigned char lba4; + } lba; +} cdrom_addr_t; + +typedef struct { + unsigned char len1; /* MSB */ + unsigned char len2; /* LSB */ + unsigned char first_track; + unsigned char last_track; + struct cdrom_toc_desc { + + unsigned char xxx; + + BITFIELD_2(unsigned char, + control : 4, + adr : 4); + + unsigned char trackno; + unsigned char xxx1; + cdrom_addr_t absolute_address; + } descs[1]; /* varsize */ +} cdrom_toc_t; + +typedef struct { + unsigned char xxx; + + unsigned char audio_status; +#define SCSI_CDST_INVALID 0x00 +#define SCSI_CDST_PLAYING 0x11 +#define SCSI_CDST_PAUSED 0x12 +#define SCSI_CDST_COMPLETED 0x13 +#define SCSI_CDST_ERROR 0x14 +#define SCSI_CDST_NO_STATUS 0x15 + + unsigned char len1; + unsigned char len2; + struct cdrom_chanQ { + unsigned char format; + BITFIELD_2(unsigned char, + control : 4, + adr : 4); + unsigned char trackno; + unsigned char indexno; + cdrom_addr_t absolute_address; + cdrom_addr_t relative_address; + BITFIELD_2(unsigned char, + xxx : 7, + mcv : 1); + unsigned char catalog[15]; + BITFIELD_2(unsigned char, + xxx1 : 7, + tcv : 1); + unsigned char isrc[15]; + } subQ; +} cdrom_chan_data_t; + +/* subsets */ +typedef struct { + unsigned char xxx; + unsigned char audio_status; + unsigned char len1; + unsigned char len2; + struct { + unsigned char format; + BITFIELD_2(unsigned char, + control : 4, + adr : 4); + unsigned char trackno; + unsigned char indexno; + cdrom_addr_t absolute_address; + cdrom_addr_t relative_address; + } subQ; +} cdrom_chan_curpos_t; + +typedef struct { + unsigned char xxx; + unsigned char audio_status; + unsigned char len1; + unsigned char len2; + struct { + unsigned char format; + unsigned char xxx1[3]; + BITFIELD_2(unsigned char, + xxx : 7, + mcv : 1); + unsigned char catalog[15]; + } subQ; +} cdrom_chan_catalog_t; + +typedef struct { + unsigned char xxx; + unsigned char audio_status; + unsigned char len1; + unsigned char len2; + struct { + unsigned char format; + BITFIELD_2(unsigned char, + control : 4, + adr : 4); + unsigned char trackno; + unsigned char xxx0; + BITFIELD_2(unsigned char, + xxx1 : 7, + tcv : 1); + unsigned char isrc[15]; + } subQ; +} cdrom_chan_isrc_t; + +/* Audio page */ +typedef struct { + scsi_mode_sense_data_t h; /* includes bdescs */ + unsigned char page_code; +#define SCSI_CD_AUDIO_PAGE 0x0e + unsigned char page_len; + BITFIELD_4(unsigned char, + xxx1 : 1, + sotc : 1, + imm : 1, + xxx2 : 5); + unsigned char xxx3[2]; + BITFIELD_3(unsigned char, + fmt : 4, + xxx4 : 3, + aprv : 1); + unsigned char bps1; + unsigned char bps2; + BITFIELD_2(unsigned char, + sel0 : 4, + xxx5 : 4); + unsigned char vol0; + BITFIELD_2(unsigned char, + sel1 : 4, + xxx6 : 4); + unsigned char vol1; + BITFIELD_2(unsigned char, + sel2 : 4, + xxx7 : 4); + unsigned char vol2; + BITFIELD_2(unsigned char, + sel3 : 4, + xxx8 : 4); + unsigned char vol3; +} cdrom_audio_page_t; + +/* + * Status byte (a-la scsi2) + */ + +typedef union { + struct { + BITFIELD_3( unsigned char, + scsi_status_reserved1:1, + scsi_status_code:5, + scsi_status_reserved2:2); + /* more scsi_status_code values */ + /* 00..0c as in SCSI-I */ +# define SCSI_ST2_CMD_TERMINATED 0x11 /* 2 */ +# define SCSI_ST2_QUEUE_FULL 0x14 /* 2 */ + /* anything else is reserved */ + } st; + unsigned char bits; +} scsi2_status_byte_t; + +#endif _SCSI_SCSI2_H_ diff --git a/scsi/scsi_alldevs.c b/scsi/scsi_alldevs.c new file mode 100644 index 0000000..2c4f37e --- /dev/null +++ b/scsi/scsi_alldevs.c @@ -0,0 +1,858 @@ +/* + * 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: scsi_alldevs.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * This file contains code for SCSI commands defined for all device types. + */ + +#include <mach/std_types.h> +#include <sys/types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + +void scsi_print_add_sense_keys(); /* forward */ + +/* + * Utilities + */ +void scsi_go_and_wait(tgt, insize, outsize, ior) + target_info_t *tgt; + int insize, outsize; + io_req_t ior; +{ + register scsi_softc_t *sc = scsi_softc[(unsigned char)tgt->masterno]; + + tgt->ior = ior; + + (*sc->go)(tgt, insize, outsize, ior==0); + + if (ior) + iowait(ior); + else + while (tgt->done == SCSI_RET_IN_PROGRESS); +} + +void scsi_go(tgt, insize, outsize, cmd_only) + target_info_t *tgt; + int insize, outsize, cmd_only; +{ + register scsi_softc_t *sc = scsi_softc[(unsigned char)tgt->masterno]; + + (*sc->go)(tgt, insize, outsize, cmd_only); +} + +int sizeof_scsi_command( + unsigned char cmd) +{ + switch ((cmd & SCSI_CODE_GROUP) >> 5) { + case 0: return sizeof(scsi_command_group_0); + case 1: return sizeof(scsi_command_group_1); + case 2: return sizeof(scsi_command_group_2); + /* 3,4 reserved */ + case 5: return sizeof(scsi_command_group_5); + /* 6,7 vendor specific (!!) */ + case 6: return sizeof(scsi_command_group_2); + } +} + +/* + * INQUIRY (Almost mandatory) + */ +int scsi_inquiry( tgt, pagecode) + register target_info_t *tgt; + int pagecode; +{ + scsi_cmd_inquiry_t *cmd; + boolean_t no_ify = TRUE; + +retry: + cmd = (scsi_cmd_inquiry_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_INQUIRY; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0xff; /* max len always */ + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ +/*#ifdef SCSI2*/ + if (pagecode != SCSI_INQ_STD_DATA) { + cmd->scsi_cmd_lun_and_lba1 |= SCSI_CMD_INQ_EVPD; + cmd->scsi_cmd_page_code = pagecode; + } else +/*#endif SCSI2*/ + cmd->scsi_cmd_page_code = 0; + + tgt->cur_cmd = SCSI_CMD_INQUIRY; + + /* + * Note: this is sent when we do not know much about the + * target, so we might not put an identify message upfront + */ + scsi_go(tgt, sizeof(*cmd), 0xff, no_ify); + + /* + * This spin loop is because we are called at autoconf + * time where we cannot thread_block(). Sigh. + */ + while (tgt->done == SCSI_RET_IN_PROGRESS) ; + if (tgt->done == SCSI_RET_RETRY) /* sync negotiation ? */ + goto retry; + if ((tgt->done != SCSI_RET_SUCCESS) && no_ify) { + no_ify = FALSE; + goto retry; + } + return tgt->done; +} + +void scsi_print_inquiry( inq, pagecode, result) + scsi2_inquiry_data_t *inq; + int pagecode; + char *result; +{ + static char *periph_names[10] = { + "disk", "tape", "printer", "processor", "WORM-disk", + "CD-ROM", "scanner", "memory", "jukebox", "communication" + }; + static char *periph_state[4] = { + "online", "offline", "?", "absent" + }; + + char dev[SCSI_TARGET_NAME_LEN], *devname; + register int i, j = 0; + + if (pagecode != SCSI_INQ_STD_DATA) + return; + + devname = result ? result : dev; + + if (!result) { + printf("\n\t%s%s %s (%s %x)", + (inq->rmb) ? "" : "non-", "removable SCSI", + (inq->periph_type > 10) ? + "?device?" : periph_names[inq->periph_type], + periph_state[inq->periph_qual & 0x3], + inq->device_type); + printf("\n\t%s%s%s", + inq->iso ? "ISO-compliant, " : "", + inq->ecma ? "ECMA-compliant, " : "", + inq->ansi ? "ANSI-compliant, " : ""); + if (inq->ansi) + printf("%s%d, ", "SCSI-", inq->ansi); + if (inq->response_fmt == 2) + printf("%s%s%s%s%s%s%s%s%s%s%s", "Supports: ", + inq->aenc ? "AENC, " : "", + inq->trmIOP ? "TrmIOP, " : "", + inq->RelAdr ? "RelAdr, " : "", + inq->Wbus32 ? "32 bit xfers, " : "", + inq->Wbus16 ? "16 bis xfers, " : "", + inq->Sync ? "Sync xfers, " : "", + inq->Linked ? "Linked cmds, " : "", + inq->CmdQue ? "Tagged cmd queues, " : "", + inq->SftRe ? "Soft" : "Hard", " RESET, "); + } + + for (i = 0; i < 8; i++) + if (inq->vendor_id[i] != ' ') + devname[j++] = inq->vendor_id[i]; + devname[j++] = ' '; + for (i = 0; i < 16; i++) + if (inq->product_id[i] != ' ') + devname[j++] = inq->product_id[i]; + devname[j++] = ' '; + for (i = 0; i < 4; i++) + if (inq->product_rev[i] != ' ') + devname[j++] = inq->product_rev[i]; +#if unsafe + devname[j++] = ' '; + for (i = 0; i < 8; i++) + if (inq->vendor_uqe[i] != ' ') + devname[j++] = inq->vendor_uqe[i]; +#endif + devname[j] = 0; + + if (!result) + printf("(%s, %s%s)\n", devname, "SCSI ", + (inq->periph_type > 10) ? + "?device?" : periph_names[inq->periph_type]); +} + +/* + * REQUESTE SENSE (Mandatory, All) + */ + +int scsi_request_sense(tgt, ior, data) + register target_info_t *tgt; + io_req_t ior; + char **data; +{ + scsi_cmd_request_sense_t *cmd; + + cmd = (scsi_cmd_request_sense_t *) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_REQUEST_SENSE; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_allocation_length = 0xff; /* max len always */ + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_REQUEST_SENSE; + + if (ior==0) + scsi_go_and_wait (tgt, sizeof(*cmd), 0xff, ior); + else { + scsi_go(tgt, sizeof(*cmd), 0xff, FALSE); + return tgt->done; + } + + if (data) + *data = tgt->cmd_ptr; + + (void) scsi_check_sense_data(tgt, tgt->cmd_ptr); + + return tgt->done; +} + +boolean_t +scsi_check_sense_data(tgt, sns) + register target_info_t *tgt; + scsi_sense_data_t *sns; +{ + unsigned char code; + + if (sns->error_class != SCSI_SNS_XTENDED_SENSE_DATA) { + printf("Bad sense data, vuqe class x%x code x%x\n", + sns->error_class, sns->error_code); + return FALSE; /* and good luck */ + } else { + code = sns->u.xtended.sense_key; + + switch (code) { + case SCSI_SNS_NOSENSE: + case SCSI_SNS_EQUAL: + return TRUE; + break; + case SCSI_SNS_RECOVERED: + scsi_error(tgt, SCSI_ERR_BAD | SCSI_ERR_SENSE, + code, sns->u.xtended.add_bytes); + return TRUE; + break; + case SCSI_SNS_UNIT_ATN: + scsi_error(tgt, SCSI_ERR_SENSE, + code, sns->u.xtended.add_bytes); + return TRUE; + break; + case SCSI_SNS_NOTREADY: + tgt->done = SCSI_RET_RETRY; + return TRUE; + case SCSI_SNS_ILLEGAL_REQ: + if (tgt->flags & TGT_OPTIONAL_CMD) + return TRUE; + /* fall through */ + default: +/* e.g. + case SCSI_SNS_MEDIUM_ERR: + case SCSI_SNS_HW_ERR: + case SCSI_SNS_PROTECT: + case SCSI_SNS_BLANK_CHK: + case SCSI_SNS_VUQE: + case SCSI_SNS_COPY_ABRT: + case SCSI_SNS_ABORTED: + case SCSI_SNS_VOLUME_OVFL: + case SCSI_SNS_MISCOMPARE: + case SCSI_SNS_RESERVED: +*/ + scsi_error(tgt, SCSI_ERR_GRAVE|SCSI_ERR_SENSE, + code, sns->u.xtended.add_bytes); + return FALSE; + break; + } + } +} + +/* + * START STOP UNIT (Optional, disk prin work rom tape[load/unload]) + */ +int scsi_start_unit( tgt, ss, ior) + register target_info_t *tgt; + int ss; + io_req_t ior; +{ + scsi_cmd_start_t *cmd; + + cmd = (scsi_cmd_start_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_START_STOP_UNIT; + cmd->scsi_cmd_lun_and_lba1 = SCSI_CMD_SS_IMMED;/* 0 won't work ? */ + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_ss_flags = ss; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_START_STOP_UNIT; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + return tgt->done; +} + +/* + * TEST UNIT READY (Optional, All) + * Note: this is where we do the synch negotiation at autoconf + */ +int scsi_test_unit_ready( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_test_unit_ready_t *cmd; + + cmd = (scsi_cmd_test_unit_ready_t*) (tgt->cmd_ptr); + + cmd->scsi_cmd_code = SCSI_CMD_TEST_UNIT_READY; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_ss_flags = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_TEST_UNIT_READY; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +/* + * RECEIVE DIAGNOSTIC RESULTS (Optional, All) + */ +int scsi_receive_diag( tgt, result, result_len, ior) + register target_info_t *tgt; + char *result; + int result_len; + io_req_t ior; +{ + scsi_cmd_receive_diag_t *cmd; + + cmd = (scsi_cmd_receive_diag_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_RECEIVE_DIAG_RESULTS; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = result_len >> 8 & 0xff; + cmd->scsi_cmd_xfer_len = result_len & 0xff; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_RECEIVE_DIAG_RESULTS; + + scsi_go_and_wait(tgt, sizeof(*cmd), result_len, ior); + + bcopy(tgt->cmd_ptr, (char*)result, result_len); + + return tgt->done; +} + + +int scsi_mode_sense( tgt, pagecode, len, ior) + register target_info_t *tgt; + int pagecode; + int len; + io_req_t ior; +{ + scsi_cmd_mode_sense_t *cmd; + + cmd = (scsi_cmd_mode_sense_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_MODE_SENSE; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_ms_pagecode = pagecode; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_MODE_SENSE; + + scsi_go_and_wait(tgt, sizeof(*cmd), len, ior); + + return tgt->done; +} + +#if 0 /* unused */ + +/* + * COPY (Optional, All) + */ +void scsi_copy( tgt, params, params_len, ior) + register target_info_t *tgt; + char *params; + io_req_t ior; +{ + scsi_cmd_copy_t *cmd; + + cmd = (scsi_cmd_copy_t*) (tgt->cmd_ptr; + cmd->scsi_cmd_code = SCSI_CMD_COPY; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = params_len>>16 & 0xff; + cmd->scsi_cmd_lba3 = params_len >> 8 & 0xff; + cmd->scsi_cmd_xfer_len = params_len & 0xff; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + bcopy(params, cmd + 1, params_len); + + tgt->cur_cmd = SCSI_CMD_COPY; + + scsi_go_and_wait(tgt, sizeof(*cmd) + params_len, 0, ior); +} + +/* + * SEND DIAGNOSTIC (Optional, All) + */ +void scsi_send_diag( tgt, flags, params, params_len, ior) + register target_info_t *tgt; + char *params; + io_req_t ior; +{ + scsi_cmd_send_diag_t *cmd; + + cmd = (scsi_cmd_send_diag_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SEND_DIAGNOSTICS; + cmd->scsi_cmd_lun_and_lba1 = flags & 0x7; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = params_len >> 8 & 0xff; + cmd->scsi_cmd_xfer_len = params_len & 0xff; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + bcopy(params, cmd + 1, params_len); + + tgt->cur_cmd = SCSI_CMD_SEND_DIAGNOSTICS; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +/* + * COMPARE (Optional, All) + */ +void scsi_compare( tgt, params, params_len, ior) + register target_info_t *tgt; + char *params; + io_req_t ior; +{ + scsi_cmd_compare_t *cmd; + + cmd = (scsi_cmd_compare_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_COMPARE; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_1_paraml1 = params_len >> 16 & 0xff; + cmd->scsi_cmd_1_paraml2 = params_len >> 8 & 0xff; + cmd->scsi_cmd_1_paraml3 = params_len & 0xff; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = 0; + cmd->scsi_cmd_xfer_len_2 = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + bcopy(params, cmd + 1, params_len); + + tgt->cur_cmd = SCSI_CMD_COMPARE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +/* + * COPY AND VERIFY (Optional, All) + */ +void scsi_copy_and_verify( tgt, params, params_len, bytchk, ior) + register target_info_t *tgt; + char *params; + io_req_t ior; +{ + scsi_cmd_compare_t *cmd; + + cmd = (scsi_cmd_compare_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_COMPARE; + cmd->scsi_cmd_lun_and_relbit = bytchk ? SCSI_CMD_CPY_BYTCHK : 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_1_paraml1 = params_len >> 16 & 0xff; + cmd->scsi_cmd_1_paraml2 = params_len >> 8 & 0xff; + cmd->scsi_cmd_1_paraml3 = params_len & 0xff; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = 0; + cmd->scsi_cmd_xfer_len_2 = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + bcopy(params, cmd + 1, params_len); + + tgt->cur_cmd = SCSI_CMD_COMPARE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +#endif + +#ifdef SCSI2 +scsi_change_definition +scsi_log_select +scsi_log_sense +scsi_long_mode_select +scsi_read_buffer +scsi_write_buffer +#endif SCSI2 + +/* + * Warn user of some device error + */ +int scsi_debug = 0; + +static char *sns_msg[SCSI_SNS_RESERVED+1] = { + "No Sense Data",/* shouldn't happen */ + "Recovered", + "Unit not ready", + "Medium", + "Hardware failure", + "Illegal request", + "Unit Attention Condition", + "Protection", + "Blank Check", + "Vendor Unique", + "Copy Operation Aborted", + "Aborted Command", + "Equal Comparison", + "Volume Overflow", + "Miscompare", + "Reserved" +}; + +void +scsi_error( tgt, code, info, addtl) + target_info_t *tgt; + unsigned code; + unsigned info; + char *addtl; +{ + char unit; + char *msg, *cmd; + scsi2_status_byte_t status; + if (scsi_debug) + code |= SCSI_ERR_GRAVE; + + if (tgt) + unit = tgt->unit_no + '0'; + else + unit = '?'; + + + switch (SCSI_ERR_CLASS(code)) { + case SCSI_ERR_STATUS: + cmd = "Bad status return"; + status.bits = info; + switch (status.st.scsi_status_code) { + case SCSI_ST_GOOD: + case SCSI_ST_CONDITION_MET: + case SCSI_ST_INT_GOOD: + case SCSI_ST_INT_MET: + return; /* all is fine */ + case SCSI_ST_CHECK_CONDITION: + msg = "Check condition"; break; + case SCSI_ST_RES_CONFLICT: + msg = "Reservation conflict"; break; + case SCSI_ST_BUSY: + msg = "Target busy"; break; + case SCSI_ST2_QUEUE_FULL: + msg = "Queue full"; break; + case SCSI_ST2_CMD_TERMINATED: + msg = "Command terminated"; break; + default: + msg = "Strange"; break; + } + break; + case SCSI_ERR_SENSE: + cmd = "Sensed a"; + msg = sns_msg[info & 0xf]; + break; + case SCSI_ERR_MSEL: + cmd = "Mode select broken"; msg = ""; break; + default: + cmd = "Generic"; msg = ""; + } + if (SCSI_ERR_GRAVITY(code)) { + printf("\n%s%c: %s %s %sx%x", "target ", unit, cmd, msg, + "error, code ", info); + if (addtl) { + unsigned int add[3]; + bcopy(addtl, (char*)add, 3*sizeof(int)); + printf("%s x%x x%x x%x", ", additional info ", + add[0], add[1], add[2]); + } + printf("\n"); + } +} + +void scsi_print_sense_data(sns) + scsi_sense_data_t *sns; +{ + printf("Sense data: %s%s, segment %d", + sns_msg[sns->u.xtended.sense_key], " error", + sns->u.xtended.segment_number); + if (sns->u.xtended.ili) printf(", IncorrectLengthIndicator"); + if (sns->u.xtended.eom) printf(", EndOfMedium"); + if (sns->u.xtended.fm) printf(", FileMark"); + + if (sns->addr_valid) { + unsigned int info; + info = (sns->u.xtended.info0 << 24) | + (sns->u.xtended.info1 << 16) | + (sns->u.xtended.info2 << 8) | + sns->u.xtended.info3; + printf(", Info x%x", info); + } + + if (sns->u.xtended.add_len > 6) + scsi_print_add_sense_keys(sns->u.xtended.add_bytes[4], + sns->u.xtended.add_bytes[5]); +} + +/* + * Table of the official SCSI-2 error messages + * Last update: + * X3T9.2/86-109, Revision 10c, March 9, 1990 + */ +static struct addtl_sns_keys_msg { + unsigned char byte12; + unsigned char byte13; + char *means; +} addtl_sns_msgs[] = { + { 0x00, 0x00, "No additional sense information" }, + { 0x00, 0x01, "Filemark detected" }, + { 0x00, 0x02, "End-of-partition/medium detected" }, + { 0x00, 0x03, "Setmark detected" }, + { 0x00, 0x04, "Beginning of partition/medium detected" }, + { 0x00, 0x05, "End-of-data detected" }, + { 0x00, 0x06, "I/O process terminated" }, + { 0x00, 0x11, "Audio play operation in progress" }, + { 0x00, 0x12, "Audio play operation paused" }, + { 0x00, 0x13, "Audio play operation successfully completed" }, + { 0x00, 0x14, "Audio play operation stopped due to error" }, + { 0x00, 0x15, "No current audio status to return" }, + { 0x01, 0x00, "No index/sector signal" }, + { 0x02, 0x00, "No seek complete" }, + { 0x03, 0x00, "Peripheral device write fault" }, + { 0x03, 0x01, "No write current" }, + { 0x03, 0x02, "Excessive write errors" }, + { 0x04, 0x00, "Logical unit not ready, cause not reportable" }, + { 0x04, 0x01, "Logical unit is in process of becoming ready" }, + { 0x04, 0x02, "Logical unit not ready, initializing command required" }, + { 0x04, 0x03, "Logical unit not ready, manual intervention required" }, + { 0x04, 0x04, "Logical unit not ready, format in progress" }, + { 0x05, 0x00, "Logical unit does not respond to selection" }, + { 0x06, 0x00, "No reference position found" }, + { 0x07, 0x00, "Multiple peripheral devices selected" }, + { 0x08, 0x00, "Logical unit communication failure" }, + { 0x08, 0x01, "Logical unit communication time-out" }, + { 0x08, 0x02, "Logical unit communication parity error" }, + { 0x09, 0x00, "Track following error" }, + { 0x09, 0x01, "Tracking servo failure" }, + { 0x09, 0x02, "Focus servo failure" }, + { 0x09, 0x03, "Spindle servo failure" }, + { 0x0a, 0x00, "Error log overflow" }, + { 0x0c, 0x00, "Write error" }, + { 0x0c, 0x01, "Write error recovered with auto-reallocation" }, + { 0x0c, 0x02, "Write error - auto-reallocation failed" }, + { 0x10, 0x00, "Id CRC or ECC error" }, + { 0x10, 0x04, "Recovered data with LEC" }, + { 0x11, 0x00, "Unrecovered read error" }, + { 0x11, 0x01, "Read retries exhausted" }, + { 0x11, 0x02, "Error too long to correct" }, + { 0x11, 0x03, "Multiple read errors" }, + { 0x11, 0x04, "Unrecovered read error - auto-reallocate failed" }, + { 0x11, 0x05, "L-EC uncorrectable error" }, + { 0x11, 0x06, "CIRC unrecovered error" }, + { 0x11, 0x07, "Data resynchronization error" }, + { 0x11, 0x08, "Incomplete block read" }, + { 0x11, 0x09, "No gap found" }, + { 0x11, 0x0a, "Miscorrected error" }, + { 0x11, 0x0b, "Unrecovered read error - recommend reassignment" }, + { 0x11, 0x0c, "Unrecovered read error - recommend rewrite the data" }, + { 0x12, 0x00, "Address mark not found for id field" }, + { 0x13, 0x00, "Address mark not found for data field" }, + { 0x14, 0x00, "Recorded entity not found" }, + { 0x14, 0x01, "Record not found" }, + { 0x14, 0x02, "Filemark or setmark not found" }, + { 0x14, 0x03, "End-of-data not found" }, + { 0x14, 0x04, "Block sequence error" }, + { 0x15, 0x00, "Random positioning error" }, + { 0x15, 0x01, "Mechanical positioning error" }, + { 0x15, 0x02, "Positioning error detected by read of medium" }, + { 0x16, 0x00, "Data synchronization mark error" }, + { 0x17, 0x00, "Recovered data with no error correction applied" }, + { 0x17, 0x01, "Recovered data with retries" }, + { 0x17, 0x02, "Recovered data with positive head offset" }, + { 0x17, 0x03, "Recovered data with negative head offset" }, + { 0x17, 0x04, "Recovered data with retries and/or CIRC applied" }, + { 0x17, 0x05, "Recovered data using previous sector id" }, + { 0x17, 0x06, "Recovered data without ECC - data auto-reallocated" }, + { 0x17, 0x07, "Recovered data without ECC - recommend reassignment" }, + { 0x18, 0x00, "Recovered data with error correction applied" }, + { 0x18, 0x01, "Recovered data with error correction and retries applied" }, + { 0x18, 0x02, "Recovered data - data auto-reallocated" }, + { 0x18, 0x03, "Recovered data with CIRC" }, + { 0x18, 0x05, "Recovered data - recommended reassignment" }, + { 0x19, 0x00, "Defect list error" }, + { 0x19, 0x01, "Defect list not available" }, + { 0x19, 0x02, "Defect list error in primary list" }, + { 0x19, 0x03, "Defect list error in grown list" }, + { 0x1a, 0x00, "Parameter list length error" }, + { 0x1b, 0x00, "Synchronous data transfer error" }, + { 0x1c, 0x00, "Defect list not found" }, + { 0x1c, 0x01, "Primary defect list not found" }, + { 0x1c, 0x02, "Grown defect list not found" }, + { 0x1d, 0x00, "Miscompare during verify operation" }, + { 0x1e, 0x00, "Recovered id with ECC correction" }, + { 0x20, 0x00, "Invalid command operation code" }, + { 0x21, 0x00, "Logical block address out of range" }, + { 0x21, 0x01, "Invalid element address" }, + { 0x22, 0x00, "Illegal function" }, + { 0x24, 0x00, "Invalid field in CDB" }, + { 0x24, 0x02, "Log parameters changed" }, + { 0x25, 0x00, "Logical unit not supported" }, + { 0x26, 0x00, "Invalid field in parameter list" }, + { 0x26, 0x01, "Parameter not supported" }, + { 0x26, 0x02, "Parameter value invalid" }, + { 0x26, 0x03, "Threshold parameters not supported" }, + { 0x27, 0x00, "Write protected" }, + { 0x28, 0x00, "Not ready to ready transition (medium may have changed)" }, + { 0x28, 0x01, "Import or export element accessed" }, + { 0x29, 0x00, "Power on, reset, or bus device reset occurred" }, + { 0x2a, 0x00, "Parameters changed" }, + { 0x2a, 0x01, "Mode parameters changed" }, + { 0x2b, 0x00, "Copy cannot execute since host cannot disconnect" }, + { 0x2c, 0x00, "Command sequence error" }, + { 0x2c, 0x01, "Too many windows specified" }, + { 0x2c, 0x02, "Invalid combination of windows specified" }, + { 0x2d, 0x00, "Overwrite error on update in place" }, + { 0x2f, 0x00, "Commands cleared by another initiator" }, + { 0x30, 0x00, "Incompatible medium installed" }, + { 0x30, 0x01, "Cannot read medium - unknown format" }, + { 0x30, 0x02, "Cannot read medium - incompatible format" }, + { 0x30, 0x03, "Cleaning cartridge installed" }, + { 0x31, 0x00, "Medium format corrupted" }, + { 0x31, 0x01, "Format command failed" }, + { 0x32, 0x00, "No defect spare location available" }, + { 0x32, 0x01, "Defect list update failure" }, + { 0x33, 0x00, "Tape length error" }, + { 0x36, 0x00, "Ribbon, ink, or toner failure" }, + { 0x37, 0x00, "Rounded parameter" }, + { 0x39, 0x00, "Saving parameters not supported" }, + { 0x3a, 0x00, "Medium not present" }, + { 0x3b, 0x00, "Sequential positioning error" }, + { 0x3b, 0x01, "Tape position error at beginning of medium" }, + { 0x3b, 0x02, "Tape position error at end of medium" }, + { 0x3b, 0x03, "Tape or electronic vertical forms unit not ready" }, + { 0x3b, 0x04, "Slew failure" }, + { 0x3b, 0x05, "Paper jam" }, + { 0x3b, 0x06, "Failed to sense top-of-form" }, + { 0x3b, 0x07, "Failed to sense bottom-of-form" }, + { 0x3b, 0x08, "Reposition error" }, + { 0x3b, 0x09, "Read past end of medium" }, + { 0x3b, 0x0a, "Read past beginning of medium" }, + { 0x3b, 0x0b, "Position past end of medium" }, + { 0x3b, 0x0c, "Position past beginning of medium" }, + { 0x3b, 0x0d, "Medium destination element full" }, + { 0x3b, 0x0e, "Medium source element empty" }, + { 0x3d, 0x00, "Invalid bits in identify message" }, + { 0x3e, 0x00, "Logical unit has not self-configured yet" }, + { 0x3f, 0x00, "Target operating conditions have changed" }, + { 0x3f, 0x01, "Microcode has been changed" }, + { 0x3f, 0x02, "Changed operating definition" }, + { 0x3f, 0x03, "Inquiry data has changed" }, + { 0x40, 0x00, "RAM failure" }, + { 0x40, 0xff, "Diagnostic failure on component <NN>" }, + { 0x41, 0x00, "Data path failure" }, + { 0x42, 0x00, "Power on or self-test failure" }, + { 0x43, 0x00, "Message error" }, + { 0x44, 0x00, "Internal target failure" }, + { 0x45, 0x00, "Select or reselect failure" }, + { 0x46, 0x00, "Unsuccessful soft reset" }, + { 0x47, 0x00, "SCSI parity error" }, + { 0x48, 0x00, "Initiator detected message received" }, + { 0x49, 0x00, "Invalid message error" }, + { 0x4a, 0x00, "Command phase error" }, + { 0x4b, 0x00, "Data phase error" }, + { 0x4c, 0x00, "Logical unit failed self-configuration" }, + { 0x4e, 0x00, "Overlapped commands attempted" }, + { 0x50, 0x00, "Write append error" }, + { 0x50, 0x01, "Write append position error" }, + { 0x50, 0x02, "Position error related to timing" }, + { 0x51, 0x00, "Erase failure" }, + { 0x52, 0x00, "Cartridge fault" }, + { 0x53, 0x00, "Media load or eject failed" }, + { 0x53, 0x01, "Unload tape failure" }, + { 0x53, 0x02, "Medium removal prevented" }, + { 0x54, 0x00, "SCSI to host system interface failure" }, + { 0x55, 0x00, "System resource failure" }, + { 0x57, 0x00, "Unable to recover table-of-contents" }, + { 0x58, 0x00, "Generation does not exist" }, + { 0x59, 0x00, "Updated block read" }, + { 0x5a, 0x00, "Operator request or state change input (unspecified)" }, + { 0x5a, 0x01, "Operator medium removal request" }, + { 0x5a, 0x02, "Operator selected write protect" }, + { 0x5a, 0x03, "Operator selected write permit" }, + { 0x5b, 0x00, "Log exception" }, + { 0x5b, 0x01, "Threshold condition met" }, + { 0x5b, 0x02, "Log counter at maximum" }, + { 0x5b, 0x03, "Log list codes exhausted" }, + { 0x5c, 0x00, "RPL status change" }, + { 0x5c, 0x01, "Spindles synchronized" }, + { 0x5c, 0x02, "Spindles not synchronized" }, + { 0x60, 0x00, "Lamp failure" }, + { 0x61, 0x00, "Video acquisition error" }, + { 0x61, 0x01, "Unable to acquire video" }, + { 0x61, 0x02, "Out of focus" }, + { 0x62, 0x00, "Scan head positioning error" }, + { 0x63, 0x00, "End of user area encountered on this track" }, + { 0x64, 0x00, "Illegal mode for this track" }, + { 0, 0, 0} +}; + +void scsi_print_add_sense_keys(key, qualif) + register unsigned key, qualif; +{ + register struct addtl_sns_keys_msg *msg; + + for (msg = addtl_sns_msgs; msg->means; msg++) { + if (msg->byte12 != key) continue; + if ((msg->byte12 == 0x40) && qualif) { + printf(", %s, NN=x%x", msg->means, qualif); + return; + } + if (msg->byte13 == qualif) { + printf(" %s", msg->means); + return; + } + }; + printf(", Unknown additional sense keys: 0x%x 0x%x\n", key, qualif); +} +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_comm.c b/scsi/scsi_comm.c new file mode 100644 index 0000000..1f0095a --- /dev/null +++ b/scsi/scsi_comm.c @@ -0,0 +1,115 @@ +/* + * 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: scsi_comm.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for COMMUNICATION devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + +char *sccomm_name( + boolean_t internal) +{ + return internal ? "cz" : "comm"; +} + +void scsi_get_message( + register target_info_t *tgt, + io_req_t ior) +{ + scsi_cmd_read_t *cmd; + register unsigned len, max; + + max = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max) { + ior->io_residual = len - max; + len = max; + } + + cmd = (scsi_cmd_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_GET_MESSAGE; + cmd->scsi_cmd_lun_and_lba1 = tgt->lun << SCSI_LUN_SHIFT; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_GET_MESSAGE; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +void scsi_send_message( + register target_info_t *tgt, + io_req_t ior) +{ + scsi_cmd_write_t *cmd; + register unsigned len, max; + + len = ior->io_count; + max = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + if (len > max) { + ior->io_residual = len - max; + len = max; + } + + cmd = (scsi_cmd_write_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SEND_MESSAGE; + cmd->scsi_cmd_lun_and_lba1 = tgt->lun << SCSI_LUN_SHIFT; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_SEND_MESSAGE; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + + +#if 0 +/* For now, these are not needed */ +scsi_get_message_long +scsi_get_message_vlong +scsi_send_message_long +scsi_send_message_vlong +#endif + +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_cpu.c b/scsi/scsi_cpu.c new file mode 100644 index 0000000..979847c --- /dev/null +++ b/scsi/scsi_cpu.c @@ -0,0 +1,109 @@ +/* + * 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: scsi_cpu.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for PROCESSOR devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + +char *sccpu_name(internal) + boolean_t internal; +{ + return internal ? "sc" : "cpu"; +} + +void scsi_send( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_write_t *cmd; + unsigned len; /* in bytes */ + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + + cmd = (scsi_cmd_write_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SEND; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_SEND; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + +void scsi_receive( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_read_t *cmd; + register unsigned len; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + + cmd = (scsi_cmd_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_RECEIVE; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_RECEIVE; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_defs.h b/scsi/scsi_defs.h new file mode 100644 index 0000000..c98bc85 --- /dev/null +++ b/scsi/scsi_defs.h @@ -0,0 +1,284 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1989 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: scsi_defs.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 9/90 + * + * Controller-independent definitions for the SCSI driver + */ + +#ifndef _SCSI_SCSI_DEFS_H_ +#define _SCSI_SCSI_DEFS_H_ + +#include <kern/queue.h> +#include <kern/lock.h> + +#include <rz_labels.h> + +#define await(event) sleep(event,0) +extern void wakeup(); + +typedef vm_offset_t opaque_t; /* should be elsewhere */ + +/* + * Internal error codes, and return values + * XXX use the mach/error.h scheme XXX + */ +typedef unsigned int scsi_ret_t; + +#define SCSI_ERR_GRAVITY(x) ((unsigned)(x)&0xf0000000U) +#define SCSI_ERR_GRAVE 0x80000000U +#define SCSI_ERR_BAD 0x40000000 + +#define SCSI_ERR_CLASS(x) ((unsigned)(x)&0x0fffffffU) +#define SCSI_ERR_STATUS 0x00000001 +#define SCSI_ERR_SENSE 0x00000002 +#define SCSI_ERR_MSEL 0x00000004 + +extern void scsi_error(/* target_info_t *, unsigned, unsigned */); + +#define SCSI_RET_IN_PROGRESS 0x00 +#define SCSI_RET_SUCCESS 0x01 +#define SCSI_RET_RETRY 0x02 +#define SCSI_RET_NEED_SENSE 0x04 +#define SCSI_RET_ABORTED 0x08 +#define SCSI_RET_DEVICE_DOWN 0x10 + +/* + * Device-specific information kept by driver + */ +#define MAX_SCSI_PARTS 32 /* maximum partitions on a disk;can be larger */ +typedef struct { + struct disklabel l; /* NOT accurate. partitions stored below */ + struct { + unsigned int badblockno; + unsigned int save_rec; + char *save_addr; + int save_count; + int save_resid; + int retry_count; + } b; +#if 0 /* no longer used by partition code */ + int labelsector; + int labeloffset; +#endif 0 + struct diskpart scsi_array[MAX_SCSI_PARTS]; /* new partition info */ +} scsi_disk_info_t; + +typedef struct { + boolean_t read_only; + unsigned int speed; + unsigned int density; + unsigned int maxreclen; + boolean_t fixed_size; +} scsi_tape_info_t; + +typedef struct { + char req_pending; + char req_id; + char req_lun; + char req_cmd; + unsigned int req_len; + /* more later */ +} scsi_processor_info_t; + +typedef struct { + void *result; + boolean_t result_available; + int result_size; + struct red_list *violates_standards; +} scsi_cdrom_info_t; + +typedef struct { +# define SCSI_MAX_COMM_TTYS 16 + struct tty *tty[SCSI_MAX_COMM_TTYS]; + io_req_t ior; +} scsi_comm_info_t; + +/* + * Device descriptor + */ + +#define SCSI_TARGET_NAME_LEN 8+16+4+8 /* our way to keep it */ + +typedef struct target_info { + queue_chain_t links; /* to queue for bus */ + io_req_t ior; /* what we are doing */ + + unsigned int flags; +#define TGT_DID_SYNCH 0x00000001 /* finished the synch neg */ +#define TGT_TRY_SYNCH 0x00000002 /* do the synch negotiation */ +#define TGT_FULLY_PROBED 0x00000004 /* can sleep to wait */ +#define TGT_ONLINE 0x00000008 /* did the once-only stuff */ +#define TGT_ALIVE 0x00000010 +#define TGT_BBR_ACTIVE 0x00000020 /* bad block replc in progress */ +#define TGT_DISCONNECTED 0x00000040 /* waiting for reconnect */ +#define TGT_WRITTEN_TO 0x00000080 /* tapes: needs a filemark on close */ +#define TGT_REWIND_ON_CLOSE 0x00000100 /* tapes: rewind */ +#define TGT_BIG 0x00000200 /* disks: > 1Gb, use long R/W */ +#define TGT_REMOVABLE_MEDIA 0x00000400 /* e.g. floppy, cd-rom,.. */ +#define TGT_READONLY 0x00000800 /* cd-rom, scanner, .. */ +#define TGT_OPTIONAL_CMD 0x00001000 /* optional cmd, ignore errors */ +#define TGT_WRITE_LABEL 0x00002000 /* disks: enable overwriting of label */ +#define TGT_US 0x00004000 /* our desc, when target role */ + +#define TGT_HW_SPECIFIC_BITS 0xffff0000U /* see specific HBA */ + char *hw_state; /* opaque */ + char *dma_ptr; + char *cmd_ptr; + struct scsi_devsw_struct *dev_ops; /* circularity */ + struct target_info *next_lun; /* if multi-LUN */ + char target_id; + char unit_no; + unsigned char sync_period; + unsigned char sync_offset; + decl_simple_lock_data(,target_lock) +#ifdef MACH_KERNEL +#else /*MACH_KERNEL*/ + struct fdma fdma; +#endif /*MACH_KERNEL*/ + /* + * State info kept while waiting to seize bus, either for first + * selection or while in disconnected state + */ + struct { + struct script *script; + int (*handler)(); + unsigned int out_count; + unsigned int in_count; + unsigned int copy_count; /* optional */ + unsigned int dma_offset; + unsigned char identify; + unsigned char cmd_count; + unsigned char hba_dep[2]; + } transient_state; + unsigned int block_size; + volatile char done; + unsigned char cur_cmd; + unsigned char lun; + char masterno; + char tgt_name[SCSI_TARGET_NAME_LEN]; + union { + scsi_disk_info_t disk; + scsi_tape_info_t tape; + scsi_cdrom_info_t cdrom; + scsi_processor_info_t cpu; + scsi_comm_info_t comm; + } dev_info; +} target_info_t; + + +/* + * Device-specific operations + */ +typedef struct scsi_devsw_struct { + char *(*driver_name)(boolean_t); /* my driver's name */ + void (*optimize)(target_info_t *); /* tune up internal params */ + scsi_ret_t (*open)(target_info_t *,io_req_t);/* open time ops */ + scsi_ret_t (*close)(target_info_t *); /* close time ops */ + int (*strategy)(io_req_t); /* sort/start routine */ + void (*restart)(target_info_t *, + boolean_t); /* completion routine */ + io_return_t (*get_status)(int, + target_info_t *, + dev_flavor_t, + dev_status_t, + natural_t *); /* specialization */ + io_return_t (*set_status)(int, + target_info_t *, + dev_flavor_t, + dev_status_t, + natural_t); /* specialization */ +} scsi_devsw_t; + +#define SCSI_OPTIMIZE_NULL ((void (*)(target_info_t *)) 0) +#define SCSI_OPEN_NULL ((scsi_ret_t (*)(target_info_t *,io_req_t)) 0) +#define SCSI_CLOSE_NULL ((scsi_ret_t (*)(target_info_t *)) 0) + +extern scsi_devsw_t scsi_devsw[]; + +/* + * HBA descriptor + */ + +typedef struct { + /* initiator (us) state */ + unsigned char initiator_id; + unsigned char masterno; + unsigned int max_dma_data; + char *hw_state; /* opaque */ + int (*go)(); + void (*watchdog)(); + boolean_t (*probe)(); + /* per-target state */ + target_info_t *target[8]; +} scsi_softc_t; + +extern scsi_softc_t *scsi_softc[]; +extern scsi_softc_t *scsi_master_alloc(/* int unit */); +extern target_info_t *scsi_slave_alloc(/* int unit, int slave, char *hw */); + +#define BGET(d,mid,id) (d[mid] & (1 << id)) /* bitmap ops */ +#define BSET(d,mid,id) d[mid] |= (1 << id) +#define BCLR(d,mid,id) d[mid] &= ~(1 << id) + +extern unsigned char scsi_no_synchronous_xfer[]; /* one bitmap per ctlr */ +extern unsigned char scsi_use_long_form[]; /* one bitmap per ctlr */ +extern unsigned char scsi_might_disconnect[]; /* one bitmap per ctlr */ +extern unsigned char scsi_should_disconnect[]; /* one bitmap per ctlr */ +extern unsigned char scsi_initiator_id[]; /* one id per ctlr */ + +extern boolean_t scsi_exabyte_filemarks; +extern boolean_t scsi_no_automatic_bbr; +extern int scsi_bbr_retries; +extern int scsi_watchdog_period; +extern int scsi_delay_after_reset; +extern unsigned int scsi_per_target_virtual; /* 2.5 only */ + +extern int scsi_debug; + +/* + * HBA-independent Watchdog + */ +typedef struct { + + unsigned short reset_count; + char nactive; + + char watchdog_state; + +#define SCSI_WD_INACTIVE 0 +#define SCSI_WD_ACTIVE 1 +#define SCSI_WD_EXPIRED 2 + + int (*reset)(); + +} watchdog_t; + +extern void scsi_watchdog( watchdog_t* ); + +#endif _SCSI_SCSI_DEFS_H_ diff --git a/scsi/scsi_disk.c b/scsi/scsi_disk.c new file mode 100644 index 0000000..99bbe76 --- /dev/null +++ b/scsi/scsi_disk.c @@ -0,0 +1,624 @@ +/* + * 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: scsi_disk.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for DISK devices. + */ + +#include <string.h> + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + + +char *scdisk_name(internal) + boolean_t internal; +{ + return internal ? "rz" : "disk"; +} + +/* + * SCSI commands partially specific to disks + */ +void scdisk_read( tgt, secno, ior) + register target_info_t *tgt; + register unsigned int secno; + io_req_t ior; +{ + scsi_cmd_read_t *cmd; + register unsigned len; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + + cmd = (scsi_cmd_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ; + cmd->scsi_cmd_lun_and_lba1 = (secno>>16)&SCSI_LBA_MASK; + cmd->scsi_cmd_lba2 = (secno>> 8)&0xff; + cmd->scsi_cmd_lba3 = (secno )&0xff; + cmd->scsi_cmd_xfer_len = len / tgt->block_size; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +void scdisk_write( tgt, secno, ior) + register target_info_t *tgt; + register unsigned int secno; + io_req_t ior; +{ + scsi_cmd_write_t *cmd; + unsigned len; /* in bytes */ + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + + cmd = (scsi_cmd_write_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_WRITE; + cmd->scsi_cmd_lun_and_lba1 = (secno>>16)&SCSI_LBA_MASK; + cmd->scsi_cmd_lba2 = (secno>> 8)&0xff; + cmd->scsi_cmd_lba3 = (secno )&0xff; + cmd->scsi_cmd_xfer_len = len / tgt->block_size; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_WRITE; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + + +int scdisk_mode_select(tgt, lbn, ior, mdata, mlen, save) + register target_info_t *tgt; + register int lbn; + io_req_t ior; + char *mdata; + int mlen, save; +{ + scsi_cmd_mode_select_t *cmd; + scsi_mode_select_param_t *parm; + + bzero(tgt->cmd_ptr, sizeof(*cmd) + sizeof(*parm)); + cmd = (scsi_cmd_mode_select_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_MODE_SELECT; + cmd->scsi_cmd_lun_and_lba1 = SCSI_CMD_MSL_PF; /* XXX only if... */ + cmd->scsi_cmd_xfer_len = sizeof(scsi_mode_select_param_t);/* no vuq */ + + parm = (scsi_mode_select_param_t*) (cmd + 1); + if (mdata) { + cmd->scsi_cmd_xfer_len = mlen; + bcopy(mdata, (char*)parm, mlen); + if (save) + cmd->scsi_cmd_lun_and_lba1 |= SCSI_CMD_MSL_SP; + } else { + /* parm->medium_type = if (floppy)disk.. */ + parm->desc_len = 8; + /* this really is the LBN */ + parm->descs[0].density_code = 0;/* XXX default XXX */ + parm->descs[0].reclen1 = (lbn>>16)&0xff; + parm->descs[0].reclen2 = (lbn>> 8)&0xff; + parm->descs[0].reclen3 = (lbn )&0xff; + mlen = sizeof(*parm); + } + + tgt->cur_cmd = SCSI_CMD_MODE_SELECT; + + scsi_go_and_wait(tgt, sizeof(*cmd) + mlen, 0, ior); + + return tgt->done; +} + +/* + * SCSI commands fully specific to disks + */ +int scsi_read_capacity( tgt, lbn, ior) + register target_info_t *tgt; + int lbn; + io_req_t ior; +{ + scsi_cmd_read_capacity_t *cmd; + + bzero(tgt->cmd_ptr, sizeof(*cmd)); + cmd = (scsi_cmd_read_capacity_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_CAPACITY; + /* all zeroes, unless... */ + if (lbn) { + cmd->scsi_cmd_rcap_flags = SCSI_CMD_RCAP_PMI; + cmd->scsi_cmd_lba1 = (lbn>>24); + cmd->scsi_cmd_lba2 = (lbn>>16)&0xff; + cmd->scsi_cmd_lba3 = (lbn>> 8)&0xff; + cmd->scsi_cmd_lba4 = (lbn )&0xff; + } + + tgt->cur_cmd = SCSI_CMD_READ_CAPACITY; + + scsi_go_and_wait(tgt, sizeof(*cmd), sizeof(scsi_rcap_data_t),ior); + + return tgt->done; +} + +void scsi_reassign_blocks( tgt, defect_list, n_defects, ior) + register target_info_t *tgt; + unsigned int *defect_list; /* In ascending order ! */ + int n_defects; + io_req_t ior; +{ + scsi_cmd_reassign_blocks_t *cmd; + scsi_Ldefect_data_t *parm; + + cmd = (scsi_cmd_reassign_blocks_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_REASSIGN_BLOCKS; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + parm = (scsi_Ldefect_data_t *) (cmd + 1); + parm->res1 = parm->res2 = 0; + n_defects *= 4; /* in 4-byte-ints */ + parm->list_len_msb = n_defects >> 8; + parm->list_len_lsb = n_defects; + bcopy((char*)defect_list, (char*)parm->defects, n_defects); + + tgt->cur_cmd = SCSI_CMD_REASSIGN_BLOCKS; + + scsi_go(tgt, sizeof(*cmd) + sizeof(*parm) + (n_defects - 4), 0, FALSE); +} + +void scsi_medium_removal( tgt, allow, ior) + register target_info_t *tgt; + boolean_t allow; + io_req_t ior; +{ + scsi_cmd_medium_removal_t *cmd; + + cmd = (scsi_cmd_medium_removal_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PREVENT_ALLOW_REMOVAL; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_pa_prevent = allow ? 0 : 1; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_PREVENT_ALLOW_REMOVAL; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +int scsi_format_unit( tgt, mode, vuqe, intlv, ior) + register target_info_t *tgt; + int mode, vuqe; + register unsigned int intlv; + io_req_t ior; +{ + scsi_cmd_format_t *cmd; + char *parms; + + cmd = (scsi_cmd_format_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_FORMAT_UNIT; + cmd->scsi_cmd_lun_and_lba1 = + mode & (SCSI_CMD_FMT_FMTDATA|SCSI_CMD_FMT_CMPLIST|SCSI_CMD_FMT_LIST_TYPE); + cmd->scsi_cmd_lba2 = vuqe; + cmd->scsi_cmd_lba3 = intlv >> 8; + cmd->scsi_cmd_xfer_len = intlv; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + parms = (char*) cmd + 1; + if (ior->io_count) + bcopy(ior->io_data, parms, ior->io_count); + else + bzero(parms, 0xff - sizeof(*cmd)); + + tgt->cur_cmd = SCSI_CMD_FORMAT_UNIT; + + scsi_go_and_wait(tgt, sizeof(*cmd) + ior->io_count, 0, ior); + return tgt->done; +} + + +/* Group 1 Commands */ + +void scsi_long_read( tgt, secno, ior) + register target_info_t *tgt; + register unsigned int secno; + io_req_t ior; +{ + scsi_cmd_long_read_t *cmd; + register unsigned len, n_blks; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + n_blks = len /tgt->block_size; + + cmd = (scsi_cmd_long_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_LONG_READ; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = n_blks >> 8; + cmd->scsi_cmd_xfer_len_2 = n_blks; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_LONG_READ; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +void scsi_long_write( tgt, secno, ior) + register target_info_t *tgt; + register unsigned int secno; + io_req_t ior; +{ + scsi_cmd_long_write_t *cmd; + unsigned len; /* in bytes */ + unsigned int max_dma_data, n_blks; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + n_blks = len /tgt->block_size; + + cmd = (scsi_cmd_long_write_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_LONG_WRITE; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = n_blks >> 8; + cmd->scsi_cmd_xfer_len_2 = n_blks; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_LONG_WRITE; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + +int scdisk_verify( tgt, secno, nsectrs, ior) + register target_info_t *tgt; + int secno, nsectrs; + io_req_t ior; +{ + scsi_cmd_verify_long_t *cmd; + int len; + + len = ior->io_count; + + cmd = (scsi_cmd_verify_long_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_VERIFY_1; + cmd->scsi_cmd_lun_and_relbit = len ? SCSI_CMD_VFY_BYTCHK : 0; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = (nsectrs) >> 8; + cmd->scsi_cmd_xfer_len_2 = nsectrs; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_VERIFY_1; + + scsi_go_and_wait(tgt, sizeof(*cmd) + len, 0, ior); + return tgt->done; +} + + +int scsi_read_defect( tgt, mode, ior) + register target_info_t *tgt; + register unsigned int mode; + io_req_t ior; +{ + scsi_cmd_long_read_t *cmd; + register unsigned len; + + len = ior->io_count; + if (len > 0xffff) + len = 0xffff; + + cmd = (scsi_cmd_read_defect_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_DEFECT_DATA; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = mode & 0x1f; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_lba4 = 0; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = (len) >> 8; + cmd->scsi_cmd_xfer_len_2 = (len); + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + /* ++ HACK Alert */ +/* tgt->cur_cmd = SCSI_CMD_READ_DEFECT_DATA;*/ + tgt->cur_cmd = SCSI_CMD_LONG_READ; + /* -- HACK Alert */ + + scsi_go(tgt, sizeof(*cmd), len, FALSE); + iowait(ior); + return tgt->done; +} + + +#if 0 /* unused commands */ +scsi_rezero_unit( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_rezero_t *cmd; + + cmd = (scsi_cmd_rezero_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_REZERO_UNIT; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_REZERO_UNIT; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + +} + +scsi_seek( tgt, where, ior) + register target_info_t *tgt; + register unsigned int where; + io_req_t ior; +{ + scsi_cmd_seek_t *cmd; + + cmd = (scsi_cmd_seek_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SEEK; + cmd->scsi_cmd_lun_and_lba1 = (where >> 16) & 0x1f; + cmd->scsi_cmd_lba2 = where >> 8; + cmd->scsi_cmd_lba3 = where; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_SEEK; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + +} + +scsi_reserve( tgt, len, id, mode, ior) + register target_info_t *tgt; + register unsigned int len; + unsigned char id; + io_req_t ior; +{ + scsi_cmd_reserve_t *cmd; + + cmd = (scsi_cmd_reserve_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_RESERVE; + cmd->scsi_cmd_lun_and_lba1 = mode & 0x1f; + cmd->scsi_cmd_reserve_id = id; + cmd->scsi_cmd_extent_llen1 = len >> 8; + cmd->scsi_cmd_extent_llen2 = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_RESERVE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + +} + +scsi_release( tgt, id, mode, ior) + register target_info_t *tgt; + unsigned char id, mode; + io_req_t ior; +{ + scsi_cmd_release_t *cmd; + + cmd = (scsi_cmd_release_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_RELEASE; + cmd->scsi_cmd_lun_and_lba1 = mode & 0x1f; + cmd->scsi_cmd_reserve_id = id; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_RELEASE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + +} + + +/* Group 1 Commands */ + +scsi_long_seek( tgt, secno, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_long_seek_t *cmd; + + cmd = (scsi_cmd_long_seek_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_LONG_SEEK; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = 0; + cmd->scsi_cmd_xfer_len_2 = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_LONG_SEEK; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + +scsi_write_verify( tgt, secno, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_write_vfy_t *cmd; + unsigned len; /* in bytes */ + unsigned int max_dma_data, n_blks; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + if (len < tgt->block_size) + len = tgt->block_size; + n_blks = len / tgt->block_size; + + cmd = (scsi_cmd_write_vfy_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_WRITE_AND_VERIFY; + cmd->scsi_cmd_lun_and_relbit = SCSI_CMD_VFY_BYTCHK; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = n_blks >> 8; + cmd->scsi_cmd_xfer_len_2 = n_blks; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_WRITE_AND_VERIFY; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + +scsi_search_data( tgt, secno, how, flags, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_search_t *cmd; + unsigned len; /* in bytes */ + unsigned int max_dma_data, n_blks; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + if (how != SCSI_CMD_SEARCH_HIGH && + how != SCSI_CMD_SEARCH_EQUAL && + how != SCSI_CMD_SEARCH_LOW) + panic("scsi_search_data"); + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + n_blks = len / tgt->block_size; + + cmd = (scsi_cmd_search_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = how; + cmd->scsi_cmd_lun_and_relbit = flags & 0x1e; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = n_blks >> 8; + cmd->scsi_cmd_xfer_len_2 = n_blks; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = how; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + + +scsi_set_limits( tgt, secno, nblocks, inhibit, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_set_limits_t *cmd; + + cmd = (scsi_cmd_set_limits_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SET_LIMITS; + cmd->scsi_cmd_lun_and_relbit = inhibit & 0x3; + cmd->scsi_cmd_lba1 = secno >> 24; + cmd->scsi_cmd_lba2 = secno >> 16; + cmd->scsi_cmd_lba3 = secno >> 8; + cmd->scsi_cmd_lba4 = secno; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = nblocks >> 8; + cmd->scsi_cmd_xfer_len_2 = nblocks; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_SET_LIMITS; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + + +#endif + +#ifdef SCSI2 +scsi_lock_cache +scsi_prefetch +scsi_read_defect_data +scsi_sync_cache +scsi_write_same +#endif SCSI2 +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_endian.h b/scsi/scsi_endian.h new file mode 100644 index 0000000..d298a78 --- /dev/null +++ b/scsi/scsi_endian.h @@ -0,0 +1,66 @@ +/* + * 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: scsi_endian.h + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 6/91 + * + * Byte/Bit order issues are solved here. + */ + + +#ifndef _SCSI_ENDIAN_H_ +#define _SCSI_ENDIAN_H_ 1 + +/* + * Macros to take care of bitfield placement within a byte. + * It might be possible to extend these to something that + * takes care of multibyte structures, using perhaps the + * type ("t") parameter. Someday. + */ +#if BYTE_MSF + +#define BITFIELD_2(t,a,b) t b,a +#define BITFIELD_3(t,a,b,c) t c,b,a +#define BITFIELD_4(t,a,b,c,d) t d,c,b,a +#define BITFIELD_5(t,a,b,c,d,e) t e,d,c,b,a +#define BITFIELD_6(t,a,b,c,d,e,f) t f,e,d,c,b,a +#define BITFIELD_7(t,a,b,c,d,e,f,g) t g,f,e,d,c,b,a +#define BITFIELD_8(t,a,b,c,d,e,f,g,h) t h,g,f,e,d,c,b,a + +#else /*BYTE_MSF*/ + +#define BITFIELD_2(t,a,b) t a,b +#define BITFIELD_3(t,a,b,c) t a,b,c +#define BITFIELD_4(t,a,b,c,d) t a,b,c,d +#define BITFIELD_5(t,a,b,c,d,e) t a,b,c,d,e +#define BITFIELD_6(t,a,b,c,d,e,f) t a,b,c,d,e +#define BITFIELD_7(t,a,b,c,d,e,f,g) t a,b,c,d,e,f,g +#define BITFIELD_8(t,a,b,c,d,e,f,g,h) t a,b,c,d,e,f,g,h + +#endif /*BYTE_MSF*/ + +#endif /*_SCSI_ENDIAN_H_*/ diff --git a/scsi/scsi_jukebox.c b/scsi/scsi_jukebox.c new file mode 100644 index 0000000..b62d0bf --- /dev/null +++ b/scsi/scsi_jukebox.c @@ -0,0 +1,57 @@ +/* + * 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: scsi_jukebox.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for MEDIA CHANGER devices. + */ + +#include <scsi.h> +#if (NSCSI > 0) + +#include <mach/std_types.h> + +char *scjb_name(internal) + boolean_t internal; +{ + return internal ? "jz" : "jukebox"; +} + +#if 0 +scsi_exchange_medium +scsi_init_element_status +scsi_move_medium +scsi_position_to_element +scsi_read_element_status +scsi_request_volume_address +scsi_send_volume_tag +#endif +#endif /* NSCSI > 0 */ + diff --git a/scsi/scsi_optical.c b/scsi/scsi_optical.c new file mode 100644 index 0000000..6188174 --- /dev/null +++ b/scsi/scsi_optical.c @@ -0,0 +1,57 @@ +/* + * 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: scsi_optical.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for OPTICAL MEMORY devices. + */ + +#include <scsi.h> +#if (NSCSI > 0) + +#include <mach/std_types.h> + +char *scmem_name(internal) + boolean_t internal; +{ + return internal ? "rz" : "optical disk"; +} + +#if 0 +scsi_erase_long +scsi_medium_scan +scsi_read_defect_data +scsi_read_generation +scsi_read_updated_block +scsi_update_block +scsi_write_verify_long +#endif +#endif /* NSCSI > 0 */ + diff --git a/scsi/scsi_printer.c b/scsi/scsi_printer.c new file mode 100644 index 0000000..0ffb09a --- /dev/null +++ b/scsi/scsi_printer.c @@ -0,0 +1,57 @@ +/* + * 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: scsi_printer.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for PRINTER devices. + */ + +#include <scsi.h> +#if (NSCSI > 0) + +#include <mach/std_types.h> + + +char *scprt_name(internal) + boolean_t internal; +{ + return internal ? "lz" : "printer"; +} + +void scprt_optimize() +{} + +#if 0 +scsi_print +scsi_slew_and_print +scsi_flush_buffer +scsi_stop_print +#endif +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_rom.c b/scsi/scsi_rom.c new file mode 100644 index 0000000..1a8dec9 --- /dev/null +++ b/scsi/scsi_rom.c @@ -0,0 +1,401 @@ +/* + * 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: scsi_rom.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for CD-ROM devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi2.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + +char *sccdrom_name( + boolean_t internal) +{ + return internal ? "rz" : "CD-ROM"; +} + +int scsi_pause_resume( + target_info_t *tgt, + boolean_t stop_it, + io_req_t ior) +{ + scsi_cmd_pausres_t *cmd; + + cmd = (scsi_cmd_pausres_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PAUSE_RESUME; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_lba4 = 0; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = 0; + cmd->scsi_cmd_pausres_res = stop_it ? 0 : SCSI_CMD_PAUSRES_RESUME; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PAUSE_RESUME; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio( + target_info_t *tgt, + unsigned int start, + unsigned int len, + boolean_t relative_address, + io_req_t ior) +{ + scsi_cmd_play_audio_t *cmd; + + cmd = (scsi_cmd_play_audio_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO; + cmd->scsi_cmd_lun_and_relbit = relative_address ? SCSI_RELADR : 0; + cmd->scsi_cmd_lba1 = start >> 24; + cmd->scsi_cmd_lba2 = start >> 16; + cmd->scsi_cmd_lba3 = start >> 8; + cmd->scsi_cmd_lba4 = start >> 0; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = len >> 8; + cmd->scsi_cmd_xfer_len_2 = len >> 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio_long( + target_info_t *tgt, + unsigned int start, + unsigned int len, + boolean_t relative_address, + io_req_t ior) +{ + scsi_cmd_play_audio_l_t *cmd; + + cmd = (scsi_cmd_play_audio_l_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO_LONG; + cmd->scsi_cmd_lun_and_relbit = relative_address ? SCSI_RELADR : 0; + cmd->scsi_cmd_lba1 = start >> 24; + cmd->scsi_cmd_lba2 = start >> 16; + cmd->scsi_cmd_lba3 = start >> 8; + cmd->scsi_cmd_lba4 = start >> 0; + cmd->scsi_cmd_xfer_len_1 = len >> 24; + cmd->scsi_cmd_xfer_len_2 = len >> 16; + cmd->scsi_cmd_xfer_len_3 = len >> 8; + cmd->scsi_cmd_xfer_len_4 = len >> 0; + cmd->scsi_cmd_xxx1 = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO_LONG; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio_msf( + target_info_t *tgt, + int sm, + int ss, + int sf, + int em, + int es, + int ef, + io_req_t ior) +{ + scsi_cmd_play_audio_msf_t *cmd; + + cmd = (scsi_cmd_play_audio_msf_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO_MSF; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_pamsf_startM = sm; + cmd->scsi_cmd_pamsf_startS = ss; + cmd->scsi_cmd_pamsf_startF = sf; + cmd->scsi_cmd_pamsf_endM = em; + cmd->scsi_cmd_pamsf_endS = es; + cmd->scsi_cmd_pamsf_endF = ef; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO_MSF; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio_track_index( + target_info_t *tgt, + int st, + int si, + int et, + int ei, + io_req_t ior) +{ + scsi_cmd_play_audio_ti_t *cmd; + + cmd = (scsi_cmd_play_audio_ti_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO_TI; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_pati_startT = st; + cmd->scsi_cmd_pati_startI = si; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_pati_endT = et; + cmd->scsi_cmd_pati_endI = ei; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO_TI; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio_track_relative( + target_info_t *tgt, + unsigned int lba, + int st, + unsigned int len, + io_req_t ior) +{ + scsi_cmd_play_audio_tr_t *cmd; + + cmd = (scsi_cmd_play_audio_tr_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO_TR; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = lba >> 24; + cmd->scsi_cmd_lba2 = lba >> 16; + cmd->scsi_cmd_lba3 = lba >> 8; + cmd->scsi_cmd_lba4 = lba >> 0; + cmd->scsi_cmd_patr_startT = st; + cmd->scsi_cmd_xfer_len_1 = len >> 8; + cmd->scsi_cmd_xfer_len_2 = len >> 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO_TR; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_play_audio_track_relative_long( + target_info_t *tgt, + unsigned int lba, + int st, + unsigned int len, + io_req_t ior) +{ + scsi_cmd_play_audio_tr_l_t *cmd; + + cmd = (scsi_cmd_play_audio_tr_l_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_PLAY_AUDIO_TR_LONG; + cmd->scsi_cmd_lun_and_relbit = 0; + cmd->scsi_cmd_lba1 = lba >> 24; + cmd->scsi_cmd_lba2 = lba >> 16; + cmd->scsi_cmd_lba3 = lba >> 8; + cmd->scsi_cmd_lba4 = lba >> 0; + cmd->scsi_cmd_xfer_len_1 = len >> 24; + cmd->scsi_cmd_xfer_len_2 = len >> 16; + cmd->scsi_cmd_xfer_len_3 = len >> 8; + cmd->scsi_cmd_xfer_len_4 = len >> 0; + cmd->scsi_cmd_patrl_startT = st; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_PLAY_AUDIO_TR_LONG; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +scsi_read_header( + target_info_t *tgt, + boolean_t msf_format, + unsigned int lba, + unsigned int allocsize, + io_req_t ior) +{ + scsi_cmd_read_header_t *cmd; + + cmd = (scsi_cmd_read_header_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_HEADER; + cmd->scsi_cmd_lun_and_relbit = msf_format ? SCSI_CMD_CD_MSF : 0; + cmd->scsi_cmd_lba1 = lba >> 24; + cmd->scsi_cmd_lba2 = lba >> 16; + cmd->scsi_cmd_lba3 = lba >> 8; + cmd->scsi_cmd_lba4 = lba >> 0; + cmd->scsi_cmd_xxx = 0; + cmd->scsi_cmd_xfer_len_1 = allocsize >> 8; + cmd->scsi_cmd_xfer_len_2 = allocsize >> 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ_HEADER; + + scsi_go_and_wait(tgt, sizeof(*cmd), allocsize, ior); + + return tgt->done; +} + +scsi_read_subchannel( + target_info_t *tgt, + boolean_t msf_format, + unsigned int data_format, + unsigned int trackno, + io_req_t ior) +{ + scsi_cmd_read_subch_t *cmd; + int allocsize; + + switch (data_format) { + case SCSI_CMD_RS_FMT_SUBQ: + allocsize = sizeof(cdrom_chan_data_t); + trackno = 0; break; + case SCSI_CMD_RS_FMT_CURPOS: + allocsize = sizeof(cdrom_chan_curpos_t); + trackno = 0; break; + case SCSI_CMD_RS_FMT_CATALOG: + allocsize = sizeof(cdrom_chan_catalog_t); + trackno = 0; break; + case SCSI_CMD_RS_FMT_ISRC: + allocsize = sizeof(cdrom_chan_isrc_t); break; + } + + cmd = (scsi_cmd_read_subch_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_SUBCH; + cmd->scsi_cmd_lun_and_relbit = msf_format ? SCSI_CMD_CD_MSF : 0; + cmd->scsi_cmd_lba1 = SCSI_CMD_RS_SUBQ; + cmd->scsi_cmd_rs_format = data_format; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_lba4 = 0; + cmd->scsi_cmd_rs_trackno = trackno; + cmd->scsi_cmd_xfer_len_1 = allocsize >> 8; + cmd->scsi_cmd_xfer_len_2 = allocsize >> 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ_SUBCH; + + scsi_go_and_wait(tgt, sizeof(*cmd), allocsize, ior); + + return tgt->done; +} + +scsi_read_toc( + target_info_t *tgt, + boolean_t msf_format, + int trackno, + int allocsize, + io_req_t ior) +{ + scsi_cmd_read_toc_t *cmd; + + cmd = (scsi_cmd_read_toc_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_TOC; + cmd->scsi_cmd_lun_and_relbit = msf_format ? SCSI_CMD_CD_MSF : 0; + cmd->scsi_cmd_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_lba4 = 0; + cmd->scsi_cmd_rtoc_startT = trackno; + cmd->scsi_cmd_xfer_len_1 = allocsize >> 8; + cmd->scsi_cmd_xfer_len_2 = allocsize >> 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ_TOC; + + scsi_go_and_wait(tgt, sizeof(*cmd), allocsize, ior); + + return tgt->done; +} + +/* move elsewhere ifworks */ +scsi2_mode_select( + target_info_t *tgt, + boolean_t save, + unsigned char *page, + int pagesize, + io_req_t ior) +{ + scsi_cmd_mode_select_t *cmd; + scsi2_mode_param_t *parm; + + bzero(tgt->cmd_ptr, sizeof(*cmd) + sizeof(*parm)); + cmd = (scsi_cmd_mode_select_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_MODE_SELECT; + cmd->scsi_cmd_lun_and_lba1 = SCSI_CMD_MSL_PF | (save ? SCSI_CMD_MSL_SP : 0); + cmd->scsi_cmd_xfer_len = pagesize; + + parm = (scsi2_mode_param_t*) (cmd + 1); + + bcopy(page, parm, pagesize); + + tgt->cur_cmd = SCSI_CMD_MODE_SELECT; + + scsi_go_and_wait(tgt, sizeof(*cmd) + pagesize, 0, ior); + + return tgt->done; +} + +/* + * obnoxious + */ +cdrom_vendor_specific( + target_info_t *tgt, + scsi_command_group_2 *cmd, + unsigned char *params, + int paramlen, + int retlen, + io_req_t ior) +{ + bcopy(cmd, tgt->cmd_ptr, sizeof(*cmd)); + if (paramlen) + bcopy(params, tgt->cmd_ptr + sizeof(*cmd), paramlen); + + tgt->cur_cmd = cmd->scsi_cmd_code; + + scsi_go_and_wait(tgt, sizeof(*cmd) + paramlen, retlen, ior); + + return tgt->done; +} +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_scanner.c b/scsi/scsi_scanner.c new file mode 100644 index 0000000..b6ba358 --- /dev/null +++ b/scsi/scsi_scanner.c @@ -0,0 +1,54 @@ +/* + * 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: scsi_scanner.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for SCANNER devices. + */ + +#include <scsi.h> +#if (NSCSI > 0) + +#include <mach/std_types.h> + +char *scscn_name(internal) + boolean_t internal; +{ + return internal ? "oz" : "scanner"; +} + +#if 0 +scsi_get_buffer_status +scsi_get_window +scsi_object_position +scsi_scan +scsi_set_window +#endif +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_tape.c b/scsi/scsi_tape.c new file mode 100644 index 0000000..c73f432 --- /dev/null +++ b/scsi/scsi_tape.c @@ -0,0 +1,415 @@ +/* + * 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: scsi_tape.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for SEQUENTIAL ACCESS devices. + */ + +#include <mach/std_types.h> +#include <scsi/compat_30.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_defs.h> + +#if (NSCSI > 0) + + +char *sctape_name(internal) + boolean_t internal; +{ + return internal ? "tz" : "tape"; +} + +void sctape_optimize(tgt) + target_info_t *tgt; +{ + register int i; + char result[6]; + + /* Some (DEC) tapes want to send you the self-test results */ + for (i = 0; i < 10; i++) { + if (scsi_receive_diag( tgt, result, sizeof(result), 0) + == SCSI_RET_SUCCESS) + break; + } + if (scsi_debug) + printf("[tape_rcvdiag: after %d, x%x x%x x%x x%x x%x x%x]\n", i+1, + result[0], result[1], result[2], result[3], result[4], result[5]); +} + +/* + * SCSI commands specific to sequential access devices + */ +int sctape_mode_select( tgt, vuque_data, vuque_data_len, newspeed, ior) + register target_info_t *tgt; + unsigned char *vuque_data; + int vuque_data_len; + int newspeed; + io_req_t ior; +{ + scsi_cmd_mode_select_t *cmd; + scsi_mode_select_param_t *parm; + + bzero(tgt->cmd_ptr, sizeof(*cmd) + 2 * sizeof(*parm)); + cmd = (scsi_cmd_mode_select_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_MODE_SELECT; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_xfer_len = sizeof(scsi_mode_select_param_t) + vuque_data_len; + + parm = (scsi_mode_select_param_t*) (cmd + 1); + if (newspeed) { + parm->speed = tgt->dev_info.tape.speed; + } else { + /* Standard sez 0 -> no change */ + parm->speed = 0; + } + /* parm->medium_type = 0; reserved */ + parm->descs[0].density_code = tgt->dev_info.tape.density; + parm->buffer_mode = 1; + parm->desc_len = 8; + if (tgt->dev_info.tape.fixed_size) { + register int reclen = tgt->block_size; + parm->descs[0].reclen1 = reclen >> 16; + parm->descs[0].reclen2 = reclen >> 8; + parm->descs[0].reclen3 = reclen; + } + + if (vuque_data_len) + bcopy(vuque_data, (char*)(parm+1), vuque_data_len); + + tgt->cur_cmd = SCSI_CMD_MODE_SELECT; + + scsi_go_and_wait(tgt, sizeof(*cmd) + sizeof(*parm) + vuque_data_len, 0, ior); + + return tgt->done; +} + +void sctape_read( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_read_t *cmd; + register unsigned len, max; +# define nbytes max + boolean_t fixed = FALSE; + + max = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (tgt->dev_info.tape.fixed_size) { + unsigned int bs = tgt->block_size; + fixed = TRUE; + nbytes = len; + ior->io_residual += len % bs; + len = len / bs; + } else { + if (max > tgt->dev_info.tape.maxreclen) + max = tgt->dev_info.tape.maxreclen; + if (len > max) { + ior->io_residual = len - max; + len = max; + } + if (len < tgt->block_size) + len = tgt->block_size; + nbytes = len; + } + + cmd = (scsi_cmd_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ; + cmd->scsi_cmd_lun_and_lba1 = fixed ? SCSI_CMD_TP_FIXED : 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ; + + scsi_go(tgt, sizeof(*cmd), nbytes, FALSE); +#undef nbytes +} + +void sctape_write( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_write_t *cmd; + register unsigned len, max; + boolean_t fixed = FALSE; + + len = ior->io_count; + max = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + if (tgt->dev_info.tape.fixed_size) { + unsigned int bs = tgt->block_size; + fixed = TRUE; + ior->io_residual += len % bs; + len = len / bs; + } else { + if (max > tgt->dev_info.tape.maxreclen) + max = tgt->dev_info.tape.maxreclen; + if (len > max) { + ior->io_residual = len - max; + len = max; + } + if (len < tgt->block_size) + len = tgt->block_size; + } + + cmd = (scsi_cmd_write_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_WRITE; + cmd->scsi_cmd_lun_and_lba1 = fixed ? SCSI_CMD_TP_FIXED : 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_WRITE; + + scsi_go(tgt, sizeof(*cmd), 0, FALSE); +} + +int scsi_rewind( tgt, ior, wait) + register target_info_t *tgt; + io_req_t ior; + boolean_t wait; +{ + scsi_cmd_rewind_t *cmd; + + + cmd = (scsi_cmd_rewind_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_REWIND; + cmd->scsi_cmd_lun_and_lba1 = wait ? 0 : SCSI_CMD_REW_IMMED; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_REWIND; + + scsi_go( tgt, sizeof(*cmd), 0, FALSE); + return SCSI_RET_SUCCESS; +} + +int scsi_write_filemarks( tgt, count, ior) + register target_info_t *tgt; + register unsigned int count; + io_req_t ior; +{ + scsi_cmd_write_fil_t *cmd; + + cmd = (scsi_cmd_write_fil_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_WRITE_FILEMARKS; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = count >> 16; + cmd->scsi_cmd_lba3 = count >> 8; + cmd->scsi_cmd_xfer_len = count; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_WRITE_FILEMARKS; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + +int scsi_space( tgt, mode, count, ior) + register target_info_t *tgt; + int mode; + register int count; + io_req_t ior; +{ + scsi_cmd_space_t *cmd; + + cmd = (scsi_cmd_space_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_SPACE; + cmd->scsi_cmd_lun_and_lba1 = mode & 0x3; + cmd->scsi_cmd_lba2 = count >> 16; + cmd->scsi_cmd_lba3 = count >> 8; + cmd->scsi_cmd_xfer_len = count; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_SPACE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); + + return tgt->done; +} + + +int scsi_read_block_limits( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_block_limits_t *cmd; + + cmd = (scsi_cmd_block_limits_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_BLOCK_LIMITS; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_READ_BLOCK_LIMITS; + + scsi_go_and_wait(tgt, sizeof(*cmd), sizeof(scsi_blimits_data_t), ior); + return tgt->done; +} + +#if 0 /* unused */ + +void scsi_track_select( tgt, trackno, ior) + register target_info_t *tgt; + register unsigned char trackno; + io_req_t ior; +{ + scsi_cmd_seek_t *cmd; + + cmd = (scsi_cmd_seek_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_TRACK_SELECT; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_tp_trackno = trackno; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_TRACK_SELECT; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +void scsi_read_reverse( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_rev_read_t *cmd; + register unsigned len; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + + cmd = (scsi_cmd_rev_read_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_READ_REVERSE; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_READ_REVERSE; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +void sctape_verify( tgt, len, ior) + register target_info_t *tgt; + register unsigned int len; + io_req_t ior; +{ + scsi_cmd_verify_t *cmd; + + cmd = (scsi_cmd_verify_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_VERIFY_0; + cmd->scsi_cmd_lun_and_lba1 = 0;/* XXX */ + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_VERIFY_0; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + + +void scsi_recover_buffered_data( tgt, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_recover_buffer_t *cmd; + register unsigned len; + unsigned int max_dma_data; + + max_dma_data = scsi_softc[(unsigned char)tgt->masterno]->max_dma_data; + + len = ior->io_count; + if (len > max_dma_data) + len = max_dma_data; + + cmd = (scsi_cmd_recover_buffer_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_RECOVER_BUFFERED_DATA; + cmd->scsi_cmd_lun_and_lba1 = 0; + cmd->scsi_cmd_lba2 = len >> 16; + cmd->scsi_cmd_lba3 = len >> 8; + cmd->scsi_cmd_xfer_len = len; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + tgt->cur_cmd = SCSI_CMD_RECOVER_BUFFERED_DATA; + + scsi_go(tgt, sizeof(*cmd), len, FALSE); +} + +void scsi_erase( tgt, mode, ior) + register target_info_t *tgt; + io_req_t ior; +{ + scsi_cmd_erase_t *cmd; + + cmd = (scsi_cmd_erase_t*) (tgt->cmd_ptr); + cmd->scsi_cmd_code = SCSI_CMD_ERASE; + cmd->scsi_cmd_lun_and_lba1 = mode & SCSI_CMD_ER_LONG; + cmd->scsi_cmd_lba2 = 0; + cmd->scsi_cmd_lba3 = 0; + cmd->scsi_cmd_xfer_len = 0; + cmd->scsi_cmd_ctrl_byte = 0; /* not linked */ + + + tgt->cur_cmd = SCSI_CMD_ERASE; + + scsi_go_and_wait(tgt, sizeof(*cmd), 0, ior); +} + +#endif + +#ifdef SCSI2 +scsi_locate +scsi_read_position +#endif SCSI2 +#endif /* NSCSI > 0 */ diff --git a/scsi/scsi_worm.c b/scsi/scsi_worm.c new file mode 100644 index 0000000..7490ea2 --- /dev/null +++ b/scsi/scsi_worm.c @@ -0,0 +1,51 @@ +/* + * 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: scsi_worm.c + * Author: Alessandro Forin, Carnegie Mellon University + * Date: 10/90 + * + * Middle layer of the SCSI driver: SCSI protocol implementation + * + * This file contains code for SCSI commands for WORM devices, + * e.g. disks that employ write once / read multiple media. + */ + +#include <mach/std_types.h> + + + +char *scworm_name(internal) + boolean_t internal; +{ + return internal ? "rz" : "WORM-disk"; +} + +#ifdef SCSI2 +see optical mem: + - no format + - no "update" +#endif SCSI2 |