scsi_target.c revision 73388
1/*
2 * Sample program to attach to the "targ" processor target, target mode
3 * peripheral driver and push or receive data.
4 *
5 * Copyright (c) 1998 Justin T. Gibbs.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions, and the following disclaimer,
13 *    without modification, immediately at the beginning of the file.
14 * 2. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/share/examples/scsi_target/scsi_target.c 73388 2001-03-03 21:23:31Z mjacob $
30 */
31
32#include <sys/types.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <paths.h>
36#include <poll.h>
37#include <signal.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <sysexits.h>
42#include <unistd.h>
43
44#include <cam/scsi/scsi_all.h>
45#include <cam/scsi/scsi_message.h>
46#include <cam/scsi/scsi_targetio.h>
47
48char	*appname;
49int	 ifd;
50char	*ifilename;
51int	 ofd;
52char	*ofilename;
53size_t	 bufsize = 64 * 1024;
54void	*buf;
55char	 targdevname[80];
56int	 targctlfd;
57int	 targfd;
58int	 quit;
59int      debug = 0;
60struct	 ioc_alloc_unit alloc_unit = {
61	CAM_BUS_WILDCARD,
62	CAM_TARGET_WILDCARD,
63	CAM_LUN_WILDCARD
64};
65
66static void pump_events();
67static void cleanup();
68static void handle_exception();
69static void quit_handler();
70static void usage();
71
72int
73main(int argc, char *argv[])
74{
75	int  ch;
76
77	appname = *argv;
78	while ((ch = getopt(argc, argv, "i:o:p:t:l:d")) != -1) {
79		switch(ch) {
80		case 'i':
81			if ((ifd = open(optarg, O_RDONLY)) == -1) {
82				perror(optarg);
83				exit(EX_NOINPUT);
84			}
85			ifilename = optarg;
86			break;
87		case 'o':
88			if ((ofd = open(optarg,
89					O_WRONLY|O_CREAT), 0600) == -1) {
90				perror(optarg);
91				exit(EX_CANTCREAT);
92			}
93			ofilename = optarg;
94			break;
95		case 'p':
96			alloc_unit.path_id = atoi(optarg);
97			break;
98		case 't':
99			alloc_unit.target_id = atoi(optarg);
100			break;
101		case 'l':
102			alloc_unit.lun_id = atoi(optarg);
103			break;
104		case 'd':
105			debug++;
106			break;
107		case '?':
108		default:
109			usage();
110			/* NOTREACHED */
111		}
112	}
113	argc -= optind;
114	argv += optind;
115
116	if (alloc_unit.path_id == CAM_BUS_WILDCARD
117	 || alloc_unit.target_id == CAM_TARGET_WILDCARD
118	 || alloc_unit.lun_id == CAM_LUN_WILDCARD) {
119		fprintf(stderr, "%s: Incomplete device path specifiled\n",
120			appname);
121		usage();
122		/* NOTREACHED */
123	}
124
125	if (argc != 0) {
126		fprintf(stderr, "%s: Too many arguments\n", appname);
127		usage();
128		/* NOTREACHED */
129	}
130
131	/* Allocate a new instance */
132	if ((targctlfd = open("/dev/targ.ctl", O_RDWR)) == -1) {
133		perror("/dev/targ.ctl");
134		exit(EX_UNAVAILABLE);
135	}
136
137	if (ioctl(targctlfd, TARGCTLIOALLOCUNIT, &alloc_unit) == -1) {
138		perror("TARGCTLIOALLOCUNIT");
139		exit(EX_SOFTWARE);
140	}
141
142	snprintf(targdevname, sizeof(targdevname), "%starg%d", _PATH_DEV,
143		 alloc_unit.unit);
144
145	if ((targfd = open(targdevname, O_RDWR)) == -1) {
146		perror(targdevname);
147		ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
148		exit(EX_NOINPUT);
149	}
150
151	if (ioctl(targfd, TARGIODEBUG, &debug) == -1) {
152		perror("TARGIODEBUG");
153		(void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
154		exit(EX_SOFTWARE);
155	}
156
157	buf = malloc(bufsize);
158
159	if (buf == NULL) {
160		fprintf(stderr, "%s: Could not malloc I/O buffer", appname);
161		if (debug) {
162			debug = 0;
163			(void) ioctl(targfd, TARGIODEBUG, &debug);
164		}
165		(void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
166		exit(EX_OSERR);
167	}
168
169	signal(SIGHUP, quit_handler);
170	signal(SIGINT, quit_handler);
171	signal(SIGTERM, quit_handler);
172
173	atexit(cleanup);
174
175	pump_events();
176
177	return (0);
178}
179
180static void
181cleanup()
182{
183	if (debug) {
184		debug = 0;
185		(void) ioctl(targfd, TARGIODEBUG, &debug);
186	}
187	close(targfd);
188	if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) {
189		perror("TARGCTLIOFREEUNIT");
190	}
191	close(targctlfd);
192}
193
194static void
195pump_events()
196{
197	struct pollfd targpoll;
198
199	targpoll.fd = targfd;
200	targpoll.events = POLLRDNORM|POLLWRNORM;
201
202	while (quit == 0) {
203		int retval;
204
205		retval = poll(&targpoll, 1, INFTIM);
206
207		if (retval == -1) {
208			if (errno == EINTR)
209				continue;
210			perror("Poll Failed");
211			exit(EX_SOFTWARE);
212		}
213
214		if (retval == 0) {
215			perror("Poll returned 0 although timeout infinite???");
216			exit(EX_SOFTWARE);
217		}
218
219		if (retval > 1) {
220			perror("Poll returned more fds ready than allocated");
221			exit(EX_SOFTWARE);
222		}
223
224		/* Process events */
225		if ((targpoll.revents & POLLERR) != 0) {
226			handle_exception();
227		}
228
229		if ((targpoll.revents & POLLRDNORM) != 0) {
230			retval = read(targfd, buf, bufsize);
231
232			if (retval == -1) {
233				perror("Read from targ failed");
234				/* Go look for exceptions */
235				continue;
236			} else {
237				retval = write(ofd, buf, retval);
238				if (retval == -1) {
239					perror("Write to file failed");
240				}
241			}
242		}
243
244		if ((targpoll.revents & POLLWRNORM) != 0) {
245			int amount_read;
246
247			retval = read(ifd, buf, bufsize);
248			if (retval == -1) {
249				perror("Read from file failed");
250				exit(EX_SOFTWARE);
251			}
252
253			amount_read = retval;
254			retval = write(targfd, buf, retval);
255			if (retval == -1) {
256				perror("Write to targ failed");
257				retval = 0;
258			}
259
260			/* Backup in our input stream on short writes */
261			if (retval != amount_read)
262				lseek(ifd, retval - amount_read, SEEK_CUR);
263		}
264	}
265}
266
267static void
268handle_exception()
269{
270	targ_exception exceptions;
271
272	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
273		perror("TARGIOCFETCHEXCEPTION");
274		exit(EX_SOFTWARE);
275	}
276
277	printf("Saw exceptions %x\n", exceptions);
278	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
279		/* Device went away.  Nothing more to do. */
280		printf("Device went away\n");
281		exit(0);
282	}
283
284	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
285		struct ccb_accept_tio atio;
286		struct ioc_initiator_state ioc_istate;
287		struct scsi_sense_data *sense;
288		union  ccb ccb;
289
290		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
291			perror("TARGIOCFETCHATIO");
292			exit(EX_SOFTWARE);
293		}
294
295		printf("Ignoring unhandled command 0x%x for Id %d\n",
296		       atio.cdb_io.cdb_bytes[0], atio.init_id);
297
298		ioc_istate.initiator_id = atio.init_id;
299		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
300			perror("TARGIOCGETISTATE");
301			exit(EX_SOFTWARE);
302		}
303
304		/* Send back Illegal Command code status */
305		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
306		sense = &ioc_istate.istate.sense_data;
307		bzero(sense, sizeof(*sense));
308		sense->error_code = SSD_CURRENT_ERROR;
309		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
310		sense->add_sense_code = 0x20;
311		sense->add_sense_code_qual = 0x00;
312		sense->extra_len = offsetof(struct scsi_sense_data, fru)
313				 - offsetof(struct scsi_sense_data, extra_len);
314
315		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
316			perror("TARGIOCSETISTATE");
317			exit(EX_SOFTWARE);
318		}
319
320		/* Clear the exception so the kernel will take our response */
321		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
322			perror("TARGIOCCLEAREXCEPTION");
323			exit(EX_SOFTWARE);
324		}
325
326		bzero(&ccb, sizeof(ccb));
327		cam_fill_ctio(&ccb.csio,
328			      /*retries*/2,
329			      /*cbfcnp*/NULL,
330			      CAM_DIR_NONE | CAM_SEND_STATUS,
331			      (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)?
332				MSG_SIMPLE_Q_TAG : 0,
333			      atio.tag_id,
334			      atio.init_id,
335			      SCSI_STATUS_CHECK_COND,
336			      /*data_ptr*/NULL,
337			      /*dxfer_len*/0,
338			      /*timeout*/5 * 1000);
339		/*
340		 * Make sure that periph_priv pointers are clean.
341		 */
342		bzero(&ccb.ccb_h.periph_priv, sizeof ccb.ccb_h.periph_priv);
343
344		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
345			perror("TARGIOCCOMMAND");
346			exit(EX_SOFTWARE);
347		}
348
349	} else {
350		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
351			perror("TARGIOCCLEAREXCEPTION");
352			exit(EX_SOFTWARE);
353		}
354	}
355
356}
357
358static void
359quit_handler(int signum)
360{
361	quit = 1;
362}
363
364static void
365usage()
366{
367
368	(void)fprintf(stderr,
369"usage: %-16s [ -d ] [-o output_file] [-i input_file] -p path -t target -l lun\n",
370		      appname);
371
372	exit(EX_USAGE);
373}
374
375