scsi_target.c revision 63185
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 63185 2000-07-14 20:26:59Z mjacob $
30 */
31
32#include <sys/types.h>
33
34#include <errno.h>
35#include <fcntl.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), "/dev/targ%d",
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		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		exit(EX_OSERR);
161	}
162
163	signal(SIGHUP, quit_handler);
164	signal(SIGINT, quit_handler);
165	signal(SIGTERM, quit_handler);
166
167	atexit(cleanup);
168
169	pump_events();
170
171	return (0);
172}
173
174static void
175cleanup()
176{
177	close(targfd);
178
179	if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) {
180		perror("TARGCTLIOFREEUNIT");
181		exit(EX_SOFTWARE);
182	}
183
184	close(targctlfd);
185}
186
187static void
188pump_events()
189{
190	struct pollfd targpoll;
191
192	targpoll.fd = targfd;
193	targpoll.events = POLLRDNORM|POLLWRNORM;
194
195	while (quit == 0) {
196		int retval;
197
198		retval = poll(&targpoll, 1, INFTIM);
199
200		if (retval == -1) {
201			if (errno == EINTR)
202				continue;
203			perror("Poll Failed");
204			exit(EX_SOFTWARE);
205		}
206
207		if (retval == 0) {
208			perror("Poll returned 0 although timeout infinite???");
209			exit(EX_SOFTWARE);
210		}
211
212		if (retval > 1) {
213			perror("Poll returned more fds ready than allocated");
214			exit(EX_SOFTWARE);
215		}
216
217		/* Process events */
218		if ((targpoll.revents & POLLERR) != 0) {
219			handle_exception();
220		}
221
222		if ((targpoll.revents & POLLRDNORM) != 0) {
223			retval = read(targfd, buf, bufsize);
224
225			if (retval == -1) {
226				perror("Read from targ failed");
227				/* Go look for exceptions */
228				continue;
229			} else {
230				retval = write(ofd, buf, retval);
231				if (retval == -1) {
232					perror("Write to file failed");
233				}
234			}
235		}
236
237		if ((targpoll.revents & POLLWRNORM) != 0) {
238			int amount_read;
239
240			retval = read(ifd, buf, bufsize);
241			if (retval == -1) {
242				perror("Read from file failed");
243				exit(EX_SOFTWARE);
244			}
245
246			amount_read = retval;
247			retval = write(targfd, buf, retval);
248			if (retval == -1) {
249				perror("Write to targ failed");
250				retval = 0;
251			}
252
253			/* Backup in our input stream on short writes */
254			if (retval != amount_read)
255				lseek(ifd, retval - amount_read, SEEK_CUR);
256		}
257	}
258}
259
260static void
261handle_exception()
262{
263	targ_exception exceptions;
264
265	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
266		perror("TARGIOCFETCHEXCEPTION");
267		exit(EX_SOFTWARE);
268	}
269
270	printf("Saw exceptions %x\n", exceptions);
271	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
272		/* Device went away.  Nothing more to do. */
273		printf("Device went away\n");
274		exit(0);
275	}
276
277	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
278		struct ccb_accept_tio atio;
279		struct ioc_initiator_state ioc_istate;
280		struct scsi_sense_data *sense;
281		union  ccb ccb;
282
283		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
284			perror("TARGIOCFETCHATIO");
285			exit(EX_SOFTWARE);
286		}
287
288		printf("Ignoring unhandled command 0x%x for Id %d\n",
289		       atio.cdb_io.cdb_bytes[0], atio.init_id);
290
291		ioc_istate.initiator_id = atio.init_id;
292		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
293			perror("TARGIOCGETISTATE");
294			exit(EX_SOFTWARE);
295		}
296
297		/* Send back Illegal Command code status */
298		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
299		sense = &ioc_istate.istate.sense_data;
300		bzero(sense, sizeof(*sense));
301		sense->error_code = SSD_CURRENT_ERROR;
302		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
303		sense->add_sense_code = 0x20;
304		sense->add_sense_code_qual = 0x00;
305		sense->extra_len = offsetof(struct scsi_sense_data, fru)
306				 - offsetof(struct scsi_sense_data, extra_len);
307
308		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
309			perror("TARGIOCSETISTATE");
310			exit(EX_SOFTWARE);
311		}
312
313		/* Clear the exception so the kernel will take our response */
314		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
315			perror("TARGIOCCLEAREXCEPTION");
316			exit(EX_SOFTWARE);
317		}
318
319		bzero(&ccb, sizeof(ccb));
320		cam_fill_ctio(&ccb.csio, /*retries*/2,
321			      /*cbfcnp*/NULL,
322			      /*flags*/CAM_DIR_NONE
323			     | (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)
324			     | CAM_SEND_STATUS,
325                              /*tag_action*/MSG_SIMPLE_Q_TAG,
326			      atio.tag_id,
327			      atio.init_id,
328			      SCSI_STATUS_CHECK_COND,
329			      /*data_ptr*/NULL,
330			      /*dxfer_len*/0,
331			      /*timeout*/5 * 1000);
332
333		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
334			perror("TARGIOCCOMMAND");
335			exit(EX_SOFTWARE);
336		}
337
338	} else {
339		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
340			perror("TARGIOCCLEAREXCEPTION");
341			exit(EX_SOFTWARE);
342		}
343	}
344
345}
346
347static void
348quit_handler(int signum)
349{
350	quit = 1;
351}
352
353static void
354usage()
355{
356
357	(void)fprintf(stderr,
358"usage: %-16s [ -d ] [-o output_file] [-i input_file] -p path -t target -l lun\n",
359		      appname);
360
361	exit(EX_USAGE);
362}
363
364