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