xref: /freebsd/share/examples/scsi_target/scsi_target.c (revision 2ad872c5794e4c26fdf6ed219ad3f09ca0d5304a)
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.1 1998/09/15 06:46:32 gibbs Exp $
30  */
31 
32 #include <sys/types.h>
33 
34 #include <fcntl.h>
35 #include <poll.h>
36 #include <stddef.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <sysexits.h>
40 #include <unistd.h>
41 
42 #include <cam/scsi/scsi_all.h>
43 #include <cam/scsi/scsi_message.h>
44 #include <cam/scsi/scsi_targetio.h>
45 
46 char  *appname;
47 int    ifd;
48 char  *ifilename;
49 int    ofd;
50 char  *ofilename;
51 size_t bufsize = 64 * 1024;
52 void  *buf;
53 char  *targdevname;
54 int    targfd;
55 
56 static void pump_events();
57 static void handle_exception();
58 static void usage();
59 
60 int
61 main(int argc, char *argv[])
62 {
63 	int  ch;
64 
65 	appname = *argv;
66 	while ((ch = getopt(argc, argv, "i:o:")) != -1) {
67 		switch(ch) {
68 		case 'i':
69 			if ((ifd = open(optarg, O_RDONLY)) == -1) {
70 				perror(optarg);
71 				exit(EX_NOINPUT);
72 			}
73 			ifilename = optarg;
74 			break;
75 		case 'o':
76 			if ((ofd = open(optarg,
77 					O_WRONLY|O_CREAT), 0600) == -1) {
78 				perror(optarg);
79 				exit(EX_CANTCREAT);
80 			}
81 			ofilename = optarg;
82 			break;
83 		case '?':
84 		default:
85 			usage();
86 			/* NOTREACHED */
87 		}
88 	}
89 	argc -= optind;
90 	argv += optind;
91 
92 	if (argc != 1) {
93 		fprintf(stderr, "%s: No target device specifiled\n", appname);
94 		usage();
95 		/* NOTREACHED */
96 	}
97 
98 	targdevname = *argv;
99 	if ((targfd = open(targdevname, O_RDWR)) == -1) {
100 		perror(targdevname);
101 		exit(EX_NOINPUT);
102 	}
103 
104 	buf = malloc(bufsize);
105 
106 	if (buf == NULL) {
107 		fprintf(stderr, "%s: Could not malloc I/O buffer", appname);
108 		exit(EX_OSERR);
109 	}
110 
111 	pump_events();
112 
113 	return (0);
114 }
115 
116 static void
117 pump_events()
118 {
119 	struct pollfd targpoll;
120 
121 	targpoll.fd = targfd;
122 	targpoll.events = POLLRDNORM|POLLWRNORM;
123 
124 	while (1) {
125 		int retval;
126 
127 		retval = poll(&targpoll, 1, INFTIM);
128 
129 		if (retval == -1) {
130 			perror("Poll Failed");
131 			exit(EX_SOFTWARE);
132 		}
133 
134 		if (retval == 0) {
135 			perror("Poll returned 0 although timeout infinite???");
136 			exit(EX_SOFTWARE);
137 		}
138 
139 		if (retval > 1) {
140 			perror("Poll returned more fds ready than allocated");
141 			exit(EX_SOFTWARE);
142 		}
143 
144 		/* Process events */
145 		if ((targpoll.revents & POLLERR) != 0) {
146 			handle_exception();
147 		}
148 
149 		if ((targpoll.revents & POLLRDNORM) != 0) {
150 			retval = read(targfd, buf, bufsize);
151 
152 			if (retval == -1) {
153 				perror("Read from targ failed");
154 			} else {
155 				retval = write(ofd, buf, retval);
156 				if (retval == -1) {
157 					perror("Write to file failed");
158 				}
159 			}
160 		}
161 
162 		if ((targpoll.revents & POLLWRNORM) != 0) {
163 			int amount_read;
164 
165 			retval = read(ifd, buf, bufsize);
166 			if (retval == -1) {
167 				perror("Read from file failed");
168 				exit(EX_SOFTWARE);
169 			}
170 
171 			amount_read = retval;
172 			retval = write(targfd, buf, retval);
173 			if (retval == -1) {
174 				perror("Write to targ failed");
175 				retval = 0;
176 			}
177 
178 			/* Backup in our input stream on short writes */
179 			if (retval != amount_read)
180 				lseek(ifd, retval - amount_read, SEEK_CUR);
181 		}
182 	}
183 }
184 
185 static void
186 handle_exception()
187 {
188 	targ_exception exceptions;
189 
190 	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
191 		perror("TARGIOCFETCHEXCEPTION");
192 		exit(EX_SOFTWARE);
193 	}
194 
195 	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
196 		/* Device went away.  Nothing more to do. */
197 		exit(0);
198 	}
199 
200 	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
201 		struct ccb_accept_tio atio;
202 		struct ioc_initiator_state ioc_istate;
203 		struct scsi_sense_data *sense;
204 		union  ccb ccb;
205 
206 		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
207 			perror("TARGIOCFETCHATIO");
208 			exit(EX_SOFTWARE);
209 		}
210 
211 		printf("Ignoring unhandled command 0x%x for Id %d\n",
212 		       atio.cdb_io.cdb_bytes[0], atio.init_id);
213 
214 		ioc_istate.initiator_id = atio.init_id;
215 		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
216 			perror("TARGIOCGETISTATE");
217 			exit(EX_SOFTWARE);
218 		}
219 
220 		/* Send back Illegal Command code status */
221 		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
222 		sense = &ioc_istate.istate.sense_data;
223 		bzero(sense, sizeof(*sense));
224 		sense->error_code = SSD_CURRENT_ERROR;
225 		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
226 		sense->add_sense_code = 0x20;
227 		sense->add_sense_code_qual = 0x00;
228 		sense->extra_len = offsetof(struct scsi_sense_data, fru)
229 				 - offsetof(struct scsi_sense_data, extra_len);
230 
231 		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
232 			perror("TARGIOCSETISTATE");
233 			exit(EX_SOFTWARE);
234 		}
235 
236 		bzero(&ccb, sizeof(ccb));
237 		cam_fill_ctio(&ccb.csio, /*retries*/2,
238 			      /*cbfcnp*/NULL,
239 			      /*flags*/CAM_DIR_NONE
240 			     | (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)
241 			     | CAM_SEND_STATUS,
242                               /*tag_action*/MSG_SIMPLE_Q_TAG,
243 			      atio.tag_id,
244 			      atio.init_id,
245 			      SCSI_STATUS_CHECK_COND,
246 			      /*data_ptr*/NULL,
247 			      /*dxfer_len*/0,
248 			      /*timeout*/5 * 1000);
249 		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
250 			perror("TARGIOCCOMMAND");
251 			exit(EX_SOFTWARE);
252 		}
253 
254 	}
255 
256 	if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
257 		perror("TARGIOCCLEAREXCEPTION");
258 		exit(EX_SOFTWARE);
259 	}
260 }
261 
262 static void
263 usage()
264 {
265 
266 	(void)fprintf(stderr,
267 "usage: %-16s [-o output_file] [-i input_file] /dev/targ?\n", appname);
268 
269 	exit(EX_USAGE);
270 }
271 
272