scsi_target.c revision 44498
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 *      $Id: scsi_target.c,v 1.2 1998/12/10 04:00:03 gibbs Exp $
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;
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 handle_exception();
67static void quit_handler();
68static void usage();
69
70int
71main(int argc, char *argv[])
72{
73	int  ch;
74
75	appname = *argv;
76	while ((ch = getopt(argc, argv, "i:o:p:t:l:")) != -1) {
77		switch(ch) {
78		case 'i':
79			if ((ifd = open(optarg, O_RDONLY)) == -1) {
80				perror(optarg);
81				exit(EX_NOINPUT);
82			}
83			ifilename = optarg;
84			break;
85		case 'o':
86			if ((ofd = open(optarg,
87					O_WRONLY|O_CREAT), 0600) == -1) {
88				perror(optarg);
89				exit(EX_CANTCREAT);
90			}
91			ofilename = optarg;
92			break;
93		case 'p':
94			alloc_unit.path_id = atoi(optarg);
95			break;
96		case 't':
97			alloc_unit.target_id = atoi(optarg);
98			break;
99		case 'l':
100			alloc_unit.lun_id = atoi(optarg);
101			break;
102		case '?':
103		default:
104			usage();
105			/* NOTREACHED */
106		}
107	}
108	argc -= optind;
109	argv += optind;
110
111	if (alloc_unit.path_id == CAM_BUS_WILDCARD
112	 || alloc_unit.target_id == CAM_TARGET_WILDCARD
113	 || alloc_unit.lun_id == CAM_LUN_WILDCARD) {
114		fprintf(stderr, "%s: Incomplete device path specifiled\n",
115			appname);
116		usage();
117		/* NOTREACHED */
118	}
119
120	if (argc != 0) {
121		fprintf(stderr, "%s: Too many arguments\n", appname);
122		usage();
123		/* NOTREACHED */
124	}
125
126	/* Allocate a new instance */
127	if ((targctlfd = open("/dev/targ.ctl", O_RDWR)) == -1) {
128		perror("/dev/targ.ctl");
129		exit(EX_UNAVAILABLE);
130	}
131
132	if (ioctl(targctlfd, TARGCTLIOALLOCUNIT, &alloc_unit) == -1) {
133		perror("TARGCTLIOALLOCUNIT");
134		exit(EX_SOFTWARE);
135	}
136
137	snprintf(targdevname, sizeof(targdevname), "/dev/targ%d",
138		 alloc_unit.unit);
139
140	if ((targfd = open(targdevname, O_RDWR)) == -1) {
141		perror(targdevname);
142		ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
143		exit(EX_NOINPUT);
144	}
145
146	buf = malloc(bufsize);
147
148	if (buf == NULL) {
149		fprintf(stderr, "%s: Could not malloc I/O buffer", appname);
150		exit(EX_OSERR);
151	}
152
153	signal(SIGHUP, quit_handler);
154	signal(SIGINT, quit_handler);
155	signal(SIGTERM, quit_handler);
156
157	pump_events();
158
159	close(targfd);
160
161	if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) {
162		perror("TARGCTLIOFREEUNIT");
163		exit(EX_SOFTWARE);
164	}
165
166	close(targctlfd);
167	return (0);
168}
169
170static void
171pump_events()
172{
173	struct pollfd targpoll;
174
175	targpoll.fd = targfd;
176	targpoll.events = POLLRDNORM|POLLWRNORM;
177
178	while (quit == 0) {
179		int retval;
180
181		retval = poll(&targpoll, 1, INFTIM);
182
183		if (retval == -1) {
184			if (errno == EINTR)
185				continue;
186			perror("Poll Failed");
187			exit(EX_SOFTWARE);
188		}
189
190		if (retval == 0) {
191			perror("Poll returned 0 although timeout infinite???");
192			exit(EX_SOFTWARE);
193		}
194
195		if (retval > 1) {
196			perror("Poll returned more fds ready than allocated");
197			exit(EX_SOFTWARE);
198		}
199
200		/* Process events */
201		if ((targpoll.revents & POLLERR) != 0) {
202			handle_exception();
203		}
204
205		if ((targpoll.revents & POLLRDNORM) != 0) {
206			retval = read(targfd, buf, bufsize);
207
208			if (retval == -1) {
209				perror("Read from targ failed");
210			} else {
211				retval = write(ofd, buf, retval);
212				if (retval == -1) {
213					perror("Write to file failed");
214				}
215			}
216		}
217
218		if ((targpoll.revents & POLLWRNORM) != 0) {
219			int amount_read;
220
221			retval = read(ifd, buf, bufsize);
222			if (retval == -1) {
223				perror("Read from file failed");
224				exit(EX_SOFTWARE);
225			}
226
227			amount_read = retval;
228			retval = write(targfd, buf, retval);
229			if (retval == -1) {
230				perror("Write to targ failed");
231				retval = 0;
232			}
233
234			/* Backup in our input stream on short writes */
235			if (retval != amount_read)
236				lseek(ifd, retval - amount_read, SEEK_CUR);
237		}
238	}
239}
240
241static void
242handle_exception()
243{
244	targ_exception exceptions;
245
246	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
247		perror("TARGIOCFETCHEXCEPTION");
248		exit(EX_SOFTWARE);
249	}
250
251	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
252		/* Device went away.  Nothing more to do. */
253		exit(0);
254	}
255
256	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
257		struct ccb_accept_tio atio;
258		struct ioc_initiator_state ioc_istate;
259		struct scsi_sense_data *sense;
260		union  ccb ccb;
261
262		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
263			perror("TARGIOCFETCHATIO");
264			exit(EX_SOFTWARE);
265		}
266
267		printf("Ignoring unhandled command 0x%x for Id %d\n",
268		       atio.cdb_io.cdb_bytes[0], atio.init_id);
269
270		ioc_istate.initiator_id = atio.init_id;
271		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
272			perror("TARGIOCGETISTATE");
273			exit(EX_SOFTWARE);
274		}
275
276		/* Send back Illegal Command code status */
277		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
278		sense = &ioc_istate.istate.sense_data;
279		bzero(sense, sizeof(*sense));
280		sense->error_code = SSD_CURRENT_ERROR;
281		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
282		sense->add_sense_code = 0x20;
283		sense->add_sense_code_qual = 0x00;
284		sense->extra_len = offsetof(struct scsi_sense_data, fru)
285				 - offsetof(struct scsi_sense_data, extra_len);
286
287		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
288			perror("TARGIOCSETISTATE");
289			exit(EX_SOFTWARE);
290		}
291
292		bzero(&ccb, sizeof(ccb));
293		cam_fill_ctio(&ccb.csio, /*retries*/2,
294			      /*cbfcnp*/NULL,
295			      /*flags*/CAM_DIR_NONE
296			     | (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)
297			     | CAM_SEND_STATUS,
298                              /*tag_action*/MSG_SIMPLE_Q_TAG,
299			      atio.tag_id,
300			      atio.init_id,
301			      SCSI_STATUS_CHECK_COND,
302			      /*data_ptr*/NULL,
303			      /*dxfer_len*/0,
304			      /*timeout*/5 * 1000);
305		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
306			perror("TARGIOCCOMMAND");
307			exit(EX_SOFTWARE);
308		}
309
310	}
311
312	if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
313		perror("TARGIOCCLEAREXCEPTION");
314		exit(EX_SOFTWARE);
315	}
316}
317
318static void
319quit_handler(int signum)
320{
321	quit = 1;
322}
323
324static void
325usage()
326{
327
328	(void)fprintf(stderr,
329"usage: %-16s [-o output_file] [-i input_file] -p path -t target -l lun\n",
330		      appname);
331
332	exit(EX_USAGE);
333}
334
335