xref: /freebsd/share/examples/scsi_target/scsi_target.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
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$
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 
48 char	*appname;
49 int	 ifd;
50 char	*ifilename;
51 int	 ofd;
52 char	*ofilename;
53 size_t	 bufsize = 64 * 1024;
54 void	*buf;
55 char	 targdevname[80];
56 int	 targctlfd;
57 int	 targfd;
58 int	 quit;
59 int      debug = 0;
60 struct	 ioc_alloc_unit alloc_unit = {
61 	CAM_BUS_WILDCARD,
62 	CAM_TARGET_WILDCARD,
63 	CAM_LUN_WILDCARD
64 };
65 
66 static void pump_events();
67 static void cleanup();
68 static void handle_exception();
69 static void quit_handler();
70 static void usage();
71 
72 int
73 main(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 
180 static void
181 cleanup()
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 
194 static void
195 pump_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 
267 static void
268 handle_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 
358 static void
359 quit_handler(int signum)
360 {
361 	quit = 1;
362 }
363 
364 static void
365 usage()
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