1 /* $NetBSD: scsitest.c,v 1.1 2014/04/24 21:46:44 pooka Exp $ */ 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: scsitest.c,v 1.1 2014/04/24 21:46:44 pooka Exp $"); 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 56 int scsitest_match(device_t, cfdata_t, void *); 57 void scsitest_attach(device_t, device_t, void *); 58 59 struct scsitest { 60 struct scsipi_channel sc_channel; 61 struct scsipi_adapter sc_adapter; 62 }; 63 64 CFATTACH_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 74 static uint32_t mycdsize = 2048; 75 static int isofd; 76 77 #define MYCDISO "tosi.iso" 78 79 unsigned rump_scsitest_err[RUMP_SCSITEST_MAXERROR]; 80 81 static void 82 sense_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 */ 98 static void 99 scsitest_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 memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); 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 208 int 209 scsitest_match(device_t parent, cfdata_t 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, RUMPUSER_OPEN_RDWR, &error)) == -1) 222 return 0; 223 #else 224 /* 225 * We pretend to have a medium present initially, so != -1. 226 */ 227 isofd = -2; 228 #endif 229 230 return 1; 231 } 232 233 void 234 scsitest_attach(device_t parent, device_t self, void *aux) 235 { 236 struct scsitest *sc = device_private(self); 237 238 aprint_naive("\n"); 239 aprint_normal("\n"); 240 241 memset(&sc->sc_adapter, 0, sizeof(sc->sc_adapter)); 242 sc->sc_adapter.adapt_nchannels = 1; 243 sc->sc_adapter.adapt_request = scsitest_request; 244 sc->sc_adapter.adapt_minphys = minphys; 245 sc->sc_adapter.adapt_dev = self; 246 sc->sc_adapter.adapt_max_periph = 1; 247 sc->sc_adapter.adapt_openings = 1; 248 249 memset(&sc->sc_channel, 0, sizeof(sc->sc_channel)); 250 sc->sc_channel.chan_bustype = &scsi_bustype; 251 sc->sc_channel.chan_ntargets = 2; 252 sc->sc_channel.chan_nluns = 1; 253 sc->sc_channel.chan_id = 0; 254 sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; 255 sc->sc_channel.chan_adapter = &sc->sc_adapter; 256 257 config_found_ia(self, "scsi", &sc->sc_channel, scsiprint); 258 } 259