1/* $NetBSD$ */ 2 3/* 4 * Copyright (c) 2010 Antti Kantee. All Rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28/* 29 * A SCSI target which is useful for debugging our scsipi driver stack. 30 * Currently it pretends to be a single CD. 31 * 32 * Freely add the necessary features for your tests. Just remember to 33 * run the atf test suite to make sure you didn't cause regressions to 34 * other tests. 35 */ 36 37#include <sys/cdefs.h> 38__KERNEL_RCSID(0, "$NetBSD$"); 39 40#include <sys/param.h> 41#include <sys/atomic.h> 42#include <sys/buf.h> 43#include <sys/device.h> 44#include <sys/malloc.h> 45#include <sys/fcntl.h> 46 47#include <dev/scsipi/scsiconf.h> 48#include <dev/scsipi/scsipiconf.h> 49#include <dev/scsipi/scsi_disk.h> 50#include <dev/scsipi/scsipi_cd.h> 51#include <dev/scsipi/scsipi_all.h> 52 53#include <rump/rumpuser.h> 54#include <rump/scsitest.h> 55 56int scsitest_match(struct device *, struct cfdata *, void *); 57void scsitest_attach(struct device *, struct device *, void *); 58 59struct scsitest { 60 struct scsipi_channel sc_channel; 61 struct scsipi_adapter sc_adapter; 62}; 63 64CFATTACH_DECL_NEW(scsitest, sizeof(struct scsitest), scsitest_match, 65 scsitest_attach, NULL, NULL); 66 67/* 68 * tosi.iso can be used to deliver CD requests to a host file with the 69 * name in USE_TOSI_ISO (yes, it's extrasimplistic). 70 */ 71//#define USE_TOSI_ISO 72 73#define CDBLOCKSIZE 2048 74static uint32_t mycdsize = 2048; 75static int isofd; 76 77#define MYCDISO "tosi.iso" 78 79unsigned rump_scsitest_err[RUMP_SCSITEST_MAXERROR]; 80 81static void 82sense_notready(struct scsipi_xfer *xs) 83{ 84 struct scsi_sense_data *sense = &xs->sense.scsi_sense; 85 86 xs->error = XS_SENSE; 87 88 sense->response_code = 0x70; 89 sense->flags = SKEY_NOT_READY; 90 sense->asc = 0x3A; 91 sense->ascq = 0x00; 92 sense->extra_len = 6; 93} 94 95/* 96 * This is pretty much a CD target for now 97 */ 98static void 99scsitest_request(struct scsipi_channel *chan, 100 scsipi_adapter_req_t req, void *arg) 101{ 102 struct scsipi_xfer *xs = arg; 103 struct scsipi_generic *cmd = xs->cmd; 104#ifdef USE_TOSI_ISO 105 int error; 106#endif 107 108 if (req != ADAPTER_REQ_RUN_XFER) 109 return; 110 111 //show_scsipi_xs(xs); 112 113 switch (cmd->opcode) { 114 case SCSI_TEST_UNIT_READY: 115 if (isofd == -1) 116 sense_notready(xs); 117 118 break; 119 case INQUIRY: { 120 struct scsipi_inquiry_data *inqbuf = (void *)xs->data; 121 122 memset(inqbuf, 0, sizeof(*inqbuf)); 123 inqbuf->device = T_CDROM; 124 inqbuf->dev_qual2 = SID_REMOVABLE; 125 strcpy(inqbuf->vendor, "RUMPHOBO"); 126 strcpy(inqbuf->product, "It's a LIE"); 127 strcpy(inqbuf->revision, "0.00"); 128 break; 129 } 130 case READ_CD_CAPACITY: { 131 struct scsipi_read_cd_cap_data *ret = (void *)xs->data; 132 133 _lto4b(CDBLOCKSIZE, ret->length); 134 _lto4b(mycdsize, ret->addr); 135 136 break; 137 } 138 case READ_DISCINFO: { 139 struct scsipi_read_discinfo_data *ret = (void *)xs->data; 140 141 memset(ret, 0, sizeof(*ret)); 142 break; 143 } 144 case READ_TRACKINFO: { 145 struct scsipi_read_trackinfo_data *ret = (void *)xs->data; 146 147 _lto4b(mycdsize, ret->track_size); 148 break; 149 } 150 case READ_TOC: { 151 struct scsipi_toc_header *ret = (void *)xs->data; 152 153 memset(ret, 0, sizeof(*ret)); 154 break; 155 } 156 case START_STOP: { 157 struct scsipi_start_stop *param = (void *)cmd; 158 159 if (param->how & SSS_LOEJ) { 160#ifdef USE_TOSI_ISO 161 rumpuser_close(isofd, &error); 162#endif 163 isofd = -1; 164 } 165 break; 166 } 167 case SCSI_SYNCHRONIZE_CACHE_10: { 168 if (isofd == -1) { 169 if ((xs->xs_control & XS_CTL_SILENT) == 0) 170 atomic_inc_uint(&rump_scsitest_err 171 [RUMP_SCSITEST_NOISYSYNC]); 172 173 sense_notready(xs); 174 } 175 176 break; 177 } 178 case GET_CONFIGURATION: { 179 180 break; 181 } 182 case SCSI_READ_6_COMMAND: { 183#ifdef USE_TOSI_ISO 184 struct scsi_rw_6 *param = (void *)cmd; 185 186 printf("reading %d bytes from %d\n", 187 param->length * CDBLOCKSIZE, 188 _3btol(param->addr) * CDBLOCKSIZE); 189 rumpuser_pread(isofd, xs->data, 190 param->length * CDBLOCKSIZE, 191 _3btol(param->addr) * CDBLOCKSIZE, 192 &error); 193#endif 194 195 break; 196 } 197 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: 198 /* hardcoded for now */ 199 break; 200 default: 201 printf("unhandled opcode 0x%x\n", cmd->opcode); 202 break; 203 } 204 205 scsipi_done(xs); 206} 207 208int 209scsitest_match(struct device *parent, struct cfdata *match, void *aux) 210{ 211#ifdef USE_TOSI_ISO 212 uint64_t fsize; 213 int error, ft; 214 215 if (rumpuser_getfileinfo(MYCDISO, &fsize, &ft, &error)) 216 return 0; 217 if (ft != RUMPUSER_FT_REG) 218 return 0; 219 mycdsize = fsize / CDBLOCKSIZE; 220 221 if ((isofd = rumpuser_open(MYCDISO, O_RDWR, &error)) == -1) 222 return 0; 223#else 224 isofd = -2; 225#endif 226 227 return 1; 228} 229 230void 231scsitest_attach(struct device *parent, struct device *self, void *aux) 232{ 233 struct scsitest *sc = device_private(self); 234 235 aprint_naive("\n"); 236 aprint_normal("\n"); 237 238 memset(&sc->sc_adapter, 0, sizeof(sc->sc_adapter)); 239 sc->sc_adapter.adapt_nchannels = 1; 240 sc->sc_adapter.adapt_request = scsitest_request; 241 sc->sc_adapter.adapt_minphys = minphys; 242 sc->sc_adapter.adapt_dev = self; 243 sc->sc_adapter.adapt_max_periph = 1; 244 sc->sc_adapter.adapt_openings = 1; 245 246 memset(&sc->sc_channel, 0, sizeof(sc->sc_channel)); 247 sc->sc_channel.chan_bustype = &scsi_bustype; 248 sc->sc_channel.chan_ntargets = 2; 249 sc->sc_channel.chan_nluns = 1; 250 sc->sc_channel.chan_id = 0; 251 sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; 252 sc->sc_channel.chan_adapter = &sc->sc_adapter; 253 254 config_found_ia(self, "scsi", &sc->sc_channel, scsiprint); 255} 256