scsi_target.c revision 41643
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
46char  *appname;
47int    ifd;
48char  *ifilename;
49int    ofd;
50char  *ofilename;
51size_t bufsize = 64 * 1024;
52void  *buf;
53char  *targdevname;
54int    targfd;
55
56static void pump_events();
57static void handle_exception();
58static void usage();
59
60int
61main(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
116static void
117pump_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
185static void
186handle_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
262static void
263usage()
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