1 /* $NetBSD: scsitest.c,v 1.2 2014/04/25 00:24:39 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.2 2014/04/25 00:24:39 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 55 #include "scsitest.h" 56 57 int scsitest_match(device_t, cfdata_t, void *); 58 void scsitest_attach(device_t, device_t, void *); 59 60 struct scsitest { 61 struct scsipi_channel sc_channel; 62 struct scsipi_adapter sc_adapter; 63 }; 64 65 CFATTACH_DECL_NEW(scsitest, sizeof(struct scsitest), scsitest_match, 66 scsitest_attach, NULL, NULL); 67 68 /* 69 * tosi.iso can be used to deliver CD requests to a host file with the 70 * name in USE_TOSI_ISO (yes, it's extrasimplistic). 71 */ 72 //#define USE_TOSI_ISO 73 74 #define CDBLOCKSIZE 2048 75 static uint32_t mycdsize = 2048; 76 static int isofd; 77 78 #define MYCDISO "tosi.iso" 79 80 unsigned rump_scsitest_err[RUMP_SCSITEST_MAXERROR]; 81 82 static void 83 sense_notready(struct scsipi_xfer *xs) 84 { 85 struct scsi_sense_data *sense = &xs->sense.scsi_sense; 86 87 xs->error = XS_SENSE; 88 89 sense->response_code = 0x70; 90 sense->flags = SKEY_NOT_READY; 91 sense->asc = 0x3A; 92 sense->ascq = 0x00; 93 sense->extra_len = 6; 94 } 95 96 /* 97 * This is pretty much a CD target for now 98 */ 99 static void 100 scsitest_request(struct scsipi_channel *chan, 101 scsipi_adapter_req_t req, void *arg) 102 { 103 struct scsipi_xfer *xs = arg; 104 struct scsipi_generic *cmd = xs->cmd; 105 #ifdef USE_TOSI_ISO 106 int error; 107 #endif 108 109 if (req != ADAPTER_REQ_RUN_XFER) 110 return; 111 112 //show_scsipi_xs(xs); 113 114 switch (cmd->opcode) { 115 case SCSI_TEST_UNIT_READY: 116 if (isofd == -1) 117 sense_notready(xs); 118 119 break; 120 case INQUIRY: { 121 struct scsipi_inquiry_data *inqbuf = (void *)xs->data; 122 123 memset(inqbuf, 0, sizeof(*inqbuf)); 124 inqbuf->device = T_CDROM; 125 inqbuf->dev_qual2 = SID_REMOVABLE; 126 strcpy(inqbuf->vendor, "RUMPHOBO"); 127 strcpy(inqbuf->product, "It's a LIE"); 128 strcpy(inqbuf->revision, "0.00"); 129 break; 130 } 131 case READ_CD_CAPACITY: { 132 struct scsipi_read_cd_cap_data *ret = (void *)xs->data; 133 134 _lto4b(CDBLOCKSIZE, ret->length); 135 _lto4b(mycdsize, ret->addr); 136 137 break; 138 } 139 case READ_DISCINFO: { 140 struct scsipi_read_discinfo_data *ret = (void *)xs->data; 141 142 memset(ret, 0, sizeof(*ret)); 143 break; 144 } 145 case READ_TRACKINFO: { 146 struct scsipi_read_trackinfo_data *ret = (void *)xs->data; 147 148 _lto4b(mycdsize, ret->track_size); 149 break; 150 } 151 case READ_TOC: { 152 struct scsipi_toc_header *ret = (void *)xs->data; 153 154 memset(ret, 0, sizeof(*ret)); 155 break; 156 } 157 case START_STOP: { 158 struct scsipi_start_stop *param = (void *)cmd; 159 160 if (param->how & SSS_LOEJ) { 161 #ifdef USE_TOSI_ISO 162 rumpuser_close(isofd, &error); 163 #endif 164 isofd = -1; 165 } 166 break; 167 } 168 case SCSI_SYNCHRONIZE_CACHE_10: { 169 if (isofd == -1) { 170 if ((xs->xs_control & XS_CTL_SILENT) == 0) 171 atomic_inc_uint(&rump_scsitest_err 172 [RUMP_SCSITEST_NOISYSYNC]); 173 174 sense_notready(xs); 175 } 176 177 break; 178 } 179 case GET_CONFIGURATION: { 180 memset(xs->data, 0, sizeof(struct scsipi_get_conf_data)); 181 break; 182 } 183 case SCSI_READ_6_COMMAND: { 184 #ifdef USE_TOSI_ISO 185 struct scsi_rw_6 *param = (void *)cmd; 186 187 printf("reading %d bytes from %d\n", 188 param->length * CDBLOCKSIZE, 189 _3btol(param->addr) * CDBLOCKSIZE); 190 rumpuser_pread(isofd, xs->data, 191 param->length * CDBLOCKSIZE, 192 _3btol(param->addr) * CDBLOCKSIZE, 193 &error); 194 #endif 195 196 break; 197 } 198 case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL: 199 /* hardcoded for now */ 200 break; 201 default: 202 printf("unhandled opcode 0x%x\n", cmd->opcode); 203 break; 204 } 205 206 scsipi_done(xs); 207 } 208 209 int 210 scsitest_match(device_t parent, cfdata_t match, void *aux) 211 { 212 #ifdef USE_TOSI_ISO 213 uint64_t fsize; 214 int error, ft; 215 216 if (rumpuser_getfileinfo(MYCDISO, &fsize, &ft, &error)) 217 return 0; 218 if (ft != RUMPUSER_FT_REG) 219 return 0; 220 mycdsize = fsize / CDBLOCKSIZE; 221 222 if ((isofd = rumpuser_open(MYCDISO, RUMPUSER_OPEN_RDWR, &error)) == -1) 223 return 0; 224 #else 225 /* 226 * We pretend to have a medium present initially, so != -1. 227 */ 228 isofd = -2; 229 #endif 230 231 return 1; 232 } 233 234 void 235 scsitest_attach(device_t parent, device_t self, void *aux) 236 { 237 struct scsitest *sc = device_private(self); 238 239 aprint_naive("\n"); 240 aprint_normal("\n"); 241 242 memset(&sc->sc_adapter, 0, sizeof(sc->sc_adapter)); 243 sc->sc_adapter.adapt_nchannels = 1; 244 sc->sc_adapter.adapt_request = scsitest_request; 245 sc->sc_adapter.adapt_minphys = minphys; 246 sc->sc_adapter.adapt_dev = self; 247 sc->sc_adapter.adapt_max_periph = 1; 248 sc->sc_adapter.adapt_openings = 1; 249 250 memset(&sc->sc_channel, 0, sizeof(sc->sc_channel)); 251 sc->sc_channel.chan_bustype = &scsi_bustype; 252 sc->sc_channel.chan_ntargets = 2; 253 sc->sc_channel.chan_nluns = 1; 254 sc->sc_channel.chan_id = 0; 255 sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; 256 sc->sc_channel.chan_adapter = &sc->sc_adapter; 257 258 config_found_ia(self, "scsi", &sc->sc_channel, scsiprint); 259 } 260