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