scsi_target.c revision 63375
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 63375 2000-07-18 04:39:36Z mjacob $
30 */
31
32#include <sys/types.h>
33#include <errno.h>
34#include <fcntl.h>
35#include <poll.h>
36#include <signal.h>
37#include <stddef.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <sysexits.h>
41#include <unistd.h>
42
43#include <cam/scsi/scsi_all.h>
44#include <cam/scsi/scsi_message.h>
45#include <cam/scsi/scsi_targetio.h>
46
47char	*appname;
48int	 ifd;
49char	*ifilename;
50int	 ofd;
51char	*ofilename;
52size_t	 bufsize = 64 * 1024;
53void	*buf;
54char	 targdevname[80];
55int	 targctlfd;
56int	 targfd;
57int	 quit;
58int      debug = 0;
59struct	 ioc_alloc_unit alloc_unit = {
60	CAM_BUS_WILDCARD,
61	CAM_TARGET_WILDCARD,
62	CAM_LUN_WILDCARD
63};
64
65static void pump_events();
66static void cleanup();
67static void handle_exception();
68static void quit_handler();
69static void usage();
70
71int
72main(int argc, char *argv[])
73{
74	int  ch;
75
76	appname = *argv;
77	while ((ch = getopt(argc, argv, "i:o:p:t:l:d")) != -1) {
78		switch(ch) {
79		case 'i':
80			if ((ifd = open(optarg, O_RDONLY)) == -1) {
81				perror(optarg);
82				exit(EX_NOINPUT);
83			}
84			ifilename = optarg;
85			break;
86		case 'o':
87			if ((ofd = open(optarg,
88					O_WRONLY|O_CREAT), 0600) == -1) {
89				perror(optarg);
90				exit(EX_CANTCREAT);
91			}
92			ofilename = optarg;
93			break;
94		case 'p':
95			alloc_unit.path_id = atoi(optarg);
96			break;
97		case 't':
98			alloc_unit.target_id = atoi(optarg);
99			break;
100		case 'l':
101			alloc_unit.lun_id = atoi(optarg);
102			break;
103		case 'd':
104			debug++;
105			break;
106		case '?':
107		default:
108			usage();
109			/* NOTREACHED */
110		}
111	}
112	argc -= optind;
113	argv += optind;
114
115	if (alloc_unit.path_id == CAM_BUS_WILDCARD
116	 || alloc_unit.target_id == CAM_TARGET_WILDCARD
117	 || alloc_unit.lun_id == CAM_LUN_WILDCARD) {
118		fprintf(stderr, "%s: Incomplete device path specifiled\n",
119			appname);
120		usage();
121		/* NOTREACHED */
122	}
123
124	if (argc != 0) {
125		fprintf(stderr, "%s: Too many arguments\n", appname);
126		usage();
127		/* NOTREACHED */
128	}
129
130	/* Allocate a new instance */
131	if ((targctlfd = open("/dev/targ.ctl", O_RDWR)) == -1) {
132		perror("/dev/targ.ctl");
133		exit(EX_UNAVAILABLE);
134	}
135
136	if (ioctl(targctlfd, TARGCTLIOALLOCUNIT, &alloc_unit) == -1) {
137		perror("TARGCTLIOALLOCUNIT");
138		exit(EX_SOFTWARE);
139	}
140
141	snprintf(targdevname, sizeof(targdevname), "/dev/targ%d",
142		 alloc_unit.unit);
143
144	if ((targfd = open(targdevname, O_RDWR)) == -1) {
145		perror(targdevname);
146		ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
147		exit(EX_NOINPUT);
148	}
149
150	if (ioctl(targfd, TARGIODEBUG, &debug) == -1) {
151		perror("TARGIODEBUG");
152		(void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
153		exit(EX_SOFTWARE);
154	}
155
156	buf = malloc(bufsize);
157
158	if (buf == NULL) {
159		fprintf(stderr, "%s: Could not malloc I/O buffer", appname);
160		if (debug) {
161			debug = 0;
162			(void) ioctl(targfd, TARGIODEBUG, &debug);
163		}
164		(void) ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
165		exit(EX_OSERR);
166	}
167
168	signal(SIGHUP, quit_handler);
169	signal(SIGINT, quit_handler);
170	signal(SIGTERM, quit_handler);
171
172	atexit(cleanup);
173
174	pump_events();
175
176	return (0);
177}
178
179static void
180cleanup()
181{
182	if (debug) {
183		debug = 0;
184		(void) ioctl(targfd, TARGIODEBUG, &debug);
185	}
186	close(targfd);
187	if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) {
188		perror("TARGCTLIOFREEUNIT");
189	}
190	close(targctlfd);
191}
192
193static void
194pump_events()
195{
196	struct pollfd targpoll;
197
198	targpoll.fd = targfd;
199	targpoll.events = POLLRDNORM|POLLWRNORM;
200
201	while (quit == 0) {
202		int retval;
203
204		retval = poll(&targpoll, 1, INFTIM);
205
206		if (retval == -1) {
207			if (errno == EINTR)
208				continue;
209			perror("Poll Failed");
210			exit(EX_SOFTWARE);
211		}
212
213		if (retval == 0) {
214			perror("Poll returned 0 although timeout infinite???");
215			exit(EX_SOFTWARE);
216		}
217
218		if (retval > 1) {
219			perror("Poll returned more fds ready than allocated");
220			exit(EX_SOFTWARE);
221		}
222
223		/* Process events */
224		if ((targpoll.revents & POLLERR) != 0) {
225			handle_exception();
226		}
227
228		if ((targpoll.revents & POLLRDNORM) != 0) {
229			retval = read(targfd, buf, bufsize);
230
231			if (retval == -1) {
232				perror("Read from targ failed");
233				/* Go look for exceptions */
234				continue;
235			} else {
236				retval = write(ofd, buf, retval);
237				if (retval == -1) {
238					perror("Write to file failed");
239				}
240			}
241		}
242
243		if ((targpoll.revents & POLLWRNORM) != 0) {
244			int amount_read;
245
246			retval = read(ifd, buf, bufsize);
247			if (retval == -1) {
248				perror("Read from file failed");
249				exit(EX_SOFTWARE);
250			}
251
252			amount_read = retval;
253			retval = write(targfd, buf, retval);
254			if (retval == -1) {
255				perror("Write to targ failed");
256				retval = 0;
257			}
258
259			/* Backup in our input stream on short writes */
260			if (retval != amount_read)
261				lseek(ifd, retval - amount_read, SEEK_CUR);
262		}
263	}
264}
265
266static void
267handle_exception()
268{
269	targ_exception exceptions;
270
271	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
272		perror("TARGIOCFETCHEXCEPTION");
273		exit(EX_SOFTWARE);
274	}
275
276	printf("Saw exceptions %x\n", exceptions);
277	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
278		/* Device went away.  Nothing more to do. */
279		printf("Device went away\n");
280		exit(0);
281	}
282
283	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
284		struct ccb_accept_tio atio;
285		struct ioc_initiator_state ioc_istate;
286		struct scsi_sense_data *sense;
287		union  ccb ccb;
288
289		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
290			perror("TARGIOCFETCHATIO");
291			exit(EX_SOFTWARE);
292		}
293
294		printf("Ignoring unhandled command 0x%x for Id %d\n",
295		       atio.cdb_io.cdb_bytes[0], atio.init_id);
296
297		ioc_istate.initiator_id = atio.init_id;
298		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
299			perror("TARGIOCGETISTATE");
300			exit(EX_SOFTWARE);
301		}
302
303		/* Send back Illegal Command code status */
304		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
305		sense = &ioc_istate.istate.sense_data;
306		bzero(sense, sizeof(*sense));
307		sense->error_code = SSD_CURRENT_ERROR;
308		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
309		sense->add_sense_code = 0x20;
310		sense->add_sense_code_qual = 0x00;
311		sense->extra_len = offsetof(struct scsi_sense_data, fru)
312				 - offsetof(struct scsi_sense_data, extra_len);
313
314		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
315			perror("TARGIOCSETISTATE");
316			exit(EX_SOFTWARE);
317		}
318
319		/* Clear the exception so the kernel will take our response */
320		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
321			perror("TARGIOCCLEAREXCEPTION");
322			exit(EX_SOFTWARE);
323		}
324
325		bzero(&ccb, sizeof(ccb));
326		cam_fill_ctio(&ccb.csio,
327			      /*retries*/2,
328			      /*cbfcnp*/NULL,
329			      CAM_DIR_NONE | CAM_SEND_STATUS,
330			      (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)?
331				MSG_SIMPLE_Q_TAG : 0,
332			      atio.tag_id,
333			      atio.init_id,
334			      SCSI_STATUS_CHECK_COND,
335			      /*data_ptr*/NULL,
336			      /*dxfer_len*/0,
337			      /*timeout*/5 * 1000);
338		/*
339		 * Make sure that periph_priv pointers are clean.
340		 */
341		bzero(&ccb.ccb_h.periph_priv, sizeof ccb.ccb_h.periph_priv);
342
343		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
344			perror("TARGIOCCOMMAND");
345			exit(EX_SOFTWARE);
346		}
347
348	} else {
349		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
350			perror("TARGIOCCLEAREXCEPTION");
351			exit(EX_SOFTWARE);
352		}
353	}
354
355}
356
357static void
358quit_handler(int signum)
359{
360	quit = 1;
361}
362
363static void
364usage()
365{
366
367	(void)fprintf(stderr,
368"usage: %-16s [ -d ] [-o output_file] [-i input_file] -p path -t target -l lun\n",
369		      appname);
370
371	exit(EX_USAGE);
372}
373
374