scsi_target.c revision 63185
139215Sgibbs/*
239215Sgibbs * Sample program to attach to the "targ" processor target, target mode
339215Sgibbs * peripheral driver and push or receive data.
439215Sgibbs *
539215Sgibbs * Copyright (c) 1998 Justin T. Gibbs.
639215Sgibbs * All rights reserved.
739215Sgibbs *
839215Sgibbs * Redistribution and use in source and binary forms, with or without
939215Sgibbs * modification, are permitted provided that the following conditions
1039215Sgibbs * are met:
1139215Sgibbs * 1. Redistributions of source code must retain the above copyright
1239215Sgibbs *    notice, this list of conditions, and the following disclaimer,
1339215Sgibbs *    without modification, immediately at the beginning of the file.
1439215Sgibbs * 2. The name of the author may not be used to endorse or promote products
1539215Sgibbs *    derived from this software without specific prior written permission.
1639215Sgibbs *
1739215Sgibbs * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1839215Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1939215Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2039215Sgibbs * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2139215Sgibbs * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2239215Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2339215Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2439215Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2539215Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2639215Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2739215Sgibbs * SUCH DAMAGE.
2839215Sgibbs *
2950476Speter * $FreeBSD: head/share/examples/scsi_target/scsi_target.c 63185 2000-07-14 20:26:59Z mjacob $
3039215Sgibbs */
3139215Sgibbs
3239215Sgibbs#include <sys/types.h>
3339215Sgibbs
3444498Sgibbs#include <errno.h>
3539215Sgibbs#include <fcntl.h>
3639215Sgibbs#include <poll.h>
3744498Sgibbs#include <signal.h>
3839215Sgibbs#include <stddef.h>
3939215Sgibbs#include <stdio.h>
4039215Sgibbs#include <stdlib.h>
4139215Sgibbs#include <sysexits.h>
4239215Sgibbs#include <unistd.h>
4339215Sgibbs
4439215Sgibbs#include <cam/scsi/scsi_all.h>
4539215Sgibbs#include <cam/scsi/scsi_message.h>
4639215Sgibbs#include <cam/scsi/scsi_targetio.h>
4739215Sgibbs
4844498Sgibbschar	*appname;
4944498Sgibbsint	 ifd;
5044498Sgibbschar	*ifilename;
5144498Sgibbsint	 ofd;
5244498Sgibbschar	*ofilename;
5344498Sgibbssize_t	 bufsize = 64 * 1024;
5444498Sgibbsvoid	*buf;
5544498Sgibbschar	 targdevname[80];
5644498Sgibbsint	 targctlfd;
5744498Sgibbsint	 targfd;
5844498Sgibbsint	 quit;
5963185Smjacobint      debug = 0;
6044498Sgibbsstruct	 ioc_alloc_unit alloc_unit = {
6144498Sgibbs	CAM_BUS_WILDCARD,
6244498Sgibbs	CAM_TARGET_WILDCARD,
6344498Sgibbs	CAM_LUN_WILDCARD
6444498Sgibbs};
6539215Sgibbs
6639215Sgibbsstatic void pump_events();
6749935Sgibbsstatic void cleanup();
6839215Sgibbsstatic void handle_exception();
6944498Sgibbsstatic void quit_handler();
7039215Sgibbsstatic void usage();
7139215Sgibbs
7239215Sgibbsint
7339215Sgibbsmain(int argc, char *argv[])
7439215Sgibbs{
7539215Sgibbs	int  ch;
7639215Sgibbs
7739215Sgibbs	appname = *argv;
7863185Smjacob	while ((ch = getopt(argc, argv, "i:o:p:t:l:d")) != -1) {
7939215Sgibbs		switch(ch) {
8039215Sgibbs		case 'i':
8139215Sgibbs			if ((ifd = open(optarg, O_RDONLY)) == -1) {
8239215Sgibbs				perror(optarg);
8339215Sgibbs				exit(EX_NOINPUT);
8439215Sgibbs			}
8539215Sgibbs			ifilename = optarg;
8639215Sgibbs			break;
8739215Sgibbs		case 'o':
8839215Sgibbs			if ((ofd = open(optarg,
8939215Sgibbs					O_WRONLY|O_CREAT), 0600) == -1) {
9039215Sgibbs				perror(optarg);
9139215Sgibbs				exit(EX_CANTCREAT);
9239215Sgibbs			}
9339215Sgibbs			ofilename = optarg;
9439215Sgibbs			break;
9544498Sgibbs		case 'p':
9644498Sgibbs			alloc_unit.path_id = atoi(optarg);
9744498Sgibbs			break;
9844498Sgibbs		case 't':
9944498Sgibbs			alloc_unit.target_id = atoi(optarg);
10044498Sgibbs			break;
10144498Sgibbs		case 'l':
10244498Sgibbs			alloc_unit.lun_id = atoi(optarg);
10344498Sgibbs			break;
10463185Smjacob		case 'd':
10563185Smjacob			debug++;
10663185Smjacob			break;
10739215Sgibbs		case '?':
10839215Sgibbs		default:
10939215Sgibbs			usage();
11039215Sgibbs			/* NOTREACHED */
11139215Sgibbs		}
11239215Sgibbs	}
11339215Sgibbs	argc -= optind;
11439215Sgibbs	argv += optind;
11539215Sgibbs
11644498Sgibbs	if (alloc_unit.path_id == CAM_BUS_WILDCARD
11744498Sgibbs	 || alloc_unit.target_id == CAM_TARGET_WILDCARD
11844498Sgibbs	 || alloc_unit.lun_id == CAM_LUN_WILDCARD) {
11944498Sgibbs		fprintf(stderr, "%s: Incomplete device path specifiled\n",
12044498Sgibbs			appname);
12139215Sgibbs		usage();
12239215Sgibbs		/* NOTREACHED */
12339215Sgibbs	}
12439215Sgibbs
12544498Sgibbs	if (argc != 0) {
12644498Sgibbs		fprintf(stderr, "%s: Too many arguments\n", appname);
12744498Sgibbs		usage();
12844498Sgibbs		/* NOTREACHED */
12944498Sgibbs	}
13044498Sgibbs
13144498Sgibbs	/* Allocate a new instance */
13244498Sgibbs	if ((targctlfd = open("/dev/targ.ctl", O_RDWR)) == -1) {
13344498Sgibbs		perror("/dev/targ.ctl");
13444498Sgibbs		exit(EX_UNAVAILABLE);
13544498Sgibbs	}
13644498Sgibbs
13744498Sgibbs	if (ioctl(targctlfd, TARGCTLIOALLOCUNIT, &alloc_unit) == -1) {
13844498Sgibbs		perror("TARGCTLIOALLOCUNIT");
13944498Sgibbs		exit(EX_SOFTWARE);
14044498Sgibbs	}
14144498Sgibbs
14244498Sgibbs	snprintf(targdevname, sizeof(targdevname), "/dev/targ%d",
14344498Sgibbs		 alloc_unit.unit);
14444498Sgibbs
14539215Sgibbs	if ((targfd = open(targdevname, O_RDWR)) == -1) {
14639215Sgibbs		perror(targdevname);
14744498Sgibbs		ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit);
14839215Sgibbs		exit(EX_NOINPUT);
14939215Sgibbs	}
15039215Sgibbs
15163185Smjacob	if (ioctl(targfd, TARGIODEBUG, &debug) == -1) {
15263185Smjacob		perror("TARGIODEBUG");
15363185Smjacob		exit(EX_SOFTWARE);
15463185Smjacob	}
15563185Smjacob
15639215Sgibbs	buf = malloc(bufsize);
15739215Sgibbs
15839215Sgibbs	if (buf == NULL) {
15939215Sgibbs		fprintf(stderr, "%s: Could not malloc I/O buffer", appname);
16039215Sgibbs		exit(EX_OSERR);
16139215Sgibbs	}
16239215Sgibbs
16344498Sgibbs	signal(SIGHUP, quit_handler);
16444498Sgibbs	signal(SIGINT, quit_handler);
16544498Sgibbs	signal(SIGTERM, quit_handler);
16644498Sgibbs
16749935Sgibbs	atexit(cleanup);
16849935Sgibbs
16939215Sgibbs	pump_events();
17039215Sgibbs
17149935Sgibbs	return (0);
17249935Sgibbs}
17349935Sgibbs
17449935Sgibbsstatic void
17549935Sgibbscleanup()
17649935Sgibbs{
17744498Sgibbs	close(targfd);
17844498Sgibbs
17944498Sgibbs	if (ioctl(targctlfd, TARGCTLIOFREEUNIT, &alloc_unit) == -1) {
18044498Sgibbs		perror("TARGCTLIOFREEUNIT");
18144498Sgibbs		exit(EX_SOFTWARE);
18244498Sgibbs	}
18344498Sgibbs
18444498Sgibbs	close(targctlfd);
18539215Sgibbs}
18639215Sgibbs
18739215Sgibbsstatic void
18839215Sgibbspump_events()
18939215Sgibbs{
19039215Sgibbs	struct pollfd targpoll;
19139215Sgibbs
19239215Sgibbs	targpoll.fd = targfd;
19339215Sgibbs	targpoll.events = POLLRDNORM|POLLWRNORM;
19439215Sgibbs
19544498Sgibbs	while (quit == 0) {
19639215Sgibbs		int retval;
19739215Sgibbs
19839215Sgibbs		retval = poll(&targpoll, 1, INFTIM);
19939215Sgibbs
20039215Sgibbs		if (retval == -1) {
20144498Sgibbs			if (errno == EINTR)
20244498Sgibbs				continue;
20339215Sgibbs			perror("Poll Failed");
20439215Sgibbs			exit(EX_SOFTWARE);
20539215Sgibbs		}
20639215Sgibbs
20739215Sgibbs		if (retval == 0) {
20839215Sgibbs			perror("Poll returned 0 although timeout infinite???");
20939215Sgibbs			exit(EX_SOFTWARE);
21039215Sgibbs		}
21139215Sgibbs
21239215Sgibbs		if (retval > 1) {
21339215Sgibbs			perror("Poll returned more fds ready than allocated");
21439215Sgibbs			exit(EX_SOFTWARE);
21539215Sgibbs		}
21639215Sgibbs
21739215Sgibbs		/* Process events */
21839215Sgibbs		if ((targpoll.revents & POLLERR) != 0) {
21939215Sgibbs			handle_exception();
22039215Sgibbs		}
22139215Sgibbs
22239215Sgibbs		if ((targpoll.revents & POLLRDNORM) != 0) {
22339215Sgibbs			retval = read(targfd, buf, bufsize);
22439215Sgibbs
22539215Sgibbs			if (retval == -1) {
22639215Sgibbs				perror("Read from targ failed");
22749935Sgibbs				/* Go look for exceptions */
22849935Sgibbs				continue;
22939215Sgibbs			} else {
23039215Sgibbs				retval = write(ofd, buf, retval);
23139215Sgibbs				if (retval == -1) {
23239215Sgibbs					perror("Write to file failed");
23339215Sgibbs				}
23439215Sgibbs			}
23539215Sgibbs		}
23639215Sgibbs
23739215Sgibbs		if ((targpoll.revents & POLLWRNORM) != 0) {
23839215Sgibbs			int amount_read;
23939215Sgibbs
24039215Sgibbs			retval = read(ifd, buf, bufsize);
24139215Sgibbs			if (retval == -1) {
24239215Sgibbs				perror("Read from file failed");
24339215Sgibbs				exit(EX_SOFTWARE);
24439215Sgibbs			}
24539215Sgibbs
24639215Sgibbs			amount_read = retval;
24739215Sgibbs			retval = write(targfd, buf, retval);
24839215Sgibbs			if (retval == -1) {
24939215Sgibbs				perror("Write to targ failed");
25039215Sgibbs				retval = 0;
25139215Sgibbs			}
25239215Sgibbs
25339215Sgibbs			/* Backup in our input stream on short writes */
25439215Sgibbs			if (retval != amount_read)
25539215Sgibbs				lseek(ifd, retval - amount_read, SEEK_CUR);
25639215Sgibbs		}
25739215Sgibbs	}
25839215Sgibbs}
25939215Sgibbs
26039215Sgibbsstatic void
26139215Sgibbshandle_exception()
26239215Sgibbs{
26339215Sgibbs	targ_exception exceptions;
26439215Sgibbs
26539215Sgibbs	if (ioctl(targfd, TARGIOCFETCHEXCEPTION, &exceptions) == -1) {
26639215Sgibbs		perror("TARGIOCFETCHEXCEPTION");
26739215Sgibbs		exit(EX_SOFTWARE);
26839215Sgibbs	}
26939215Sgibbs
27049935Sgibbs	printf("Saw exceptions %x\n", exceptions);
27139215Sgibbs	if ((exceptions & TARG_EXCEPT_DEVICE_INVALID) != 0) {
27239215Sgibbs		/* Device went away.  Nothing more to do. */
27349935Sgibbs		printf("Device went away\n");
27439215Sgibbs		exit(0);
27539215Sgibbs	}
27639215Sgibbs
27739215Sgibbs	if ((exceptions & TARG_EXCEPT_UNKNOWN_ATIO) != 0) {
27839215Sgibbs		struct ccb_accept_tio atio;
27939215Sgibbs		struct ioc_initiator_state ioc_istate;
28039215Sgibbs		struct scsi_sense_data *sense;
28139215Sgibbs		union  ccb ccb;
28239215Sgibbs
28339215Sgibbs		if (ioctl(targfd, TARGIOCFETCHATIO, &atio) == -1) {
28439215Sgibbs			perror("TARGIOCFETCHATIO");
28539215Sgibbs			exit(EX_SOFTWARE);
28639215Sgibbs		}
28739215Sgibbs
28839215Sgibbs		printf("Ignoring unhandled command 0x%x for Id %d\n",
28939215Sgibbs		       atio.cdb_io.cdb_bytes[0], atio.init_id);
29039215Sgibbs
29139215Sgibbs		ioc_istate.initiator_id = atio.init_id;
29239215Sgibbs		if (ioctl(targfd, TARGIOCGETISTATE, &ioc_istate) == -1) {
29339215Sgibbs			perror("TARGIOCGETISTATE");
29439215Sgibbs			exit(EX_SOFTWARE);
29539215Sgibbs		}
29639215Sgibbs
29739215Sgibbs		/* Send back Illegal Command code status */
29839215Sgibbs		ioc_istate.istate.pending_ca |= CA_CMD_SENSE;
29939215Sgibbs		sense = &ioc_istate.istate.sense_data;
30039215Sgibbs		bzero(sense, sizeof(*sense));
30139215Sgibbs		sense->error_code = SSD_CURRENT_ERROR;
30239215Sgibbs		sense->flags = SSD_KEY_ILLEGAL_REQUEST;
30339215Sgibbs		sense->add_sense_code = 0x20;
30439215Sgibbs		sense->add_sense_code_qual = 0x00;
30539215Sgibbs		sense->extra_len = offsetof(struct scsi_sense_data, fru)
30639215Sgibbs				 - offsetof(struct scsi_sense_data, extra_len);
30739215Sgibbs
30839215Sgibbs		if (ioctl(targfd, TARGIOCSETISTATE, &ioc_istate) == -1) {
30939215Sgibbs			perror("TARGIOCSETISTATE");
31039215Sgibbs			exit(EX_SOFTWARE);
31139215Sgibbs		}
31239215Sgibbs
31363185Smjacob		/* Clear the exception so the kernel will take our response */
31463185Smjacob		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
31563185Smjacob			perror("TARGIOCCLEAREXCEPTION");
31663185Smjacob			exit(EX_SOFTWARE);
31763185Smjacob		}
31863185Smjacob
31939215Sgibbs		bzero(&ccb, sizeof(ccb));
32039215Sgibbs		cam_fill_ctio(&ccb.csio, /*retries*/2,
32139215Sgibbs			      /*cbfcnp*/NULL,
32239215Sgibbs			      /*flags*/CAM_DIR_NONE
32339215Sgibbs			     | (atio.ccb_h.flags & CAM_TAG_ACTION_VALID)
32439215Sgibbs			     | CAM_SEND_STATUS,
32539215Sgibbs                              /*tag_action*/MSG_SIMPLE_Q_TAG,
32639215Sgibbs			      atio.tag_id,
32739215Sgibbs			      atio.init_id,
32839215Sgibbs			      SCSI_STATUS_CHECK_COND,
32939215Sgibbs			      /*data_ptr*/NULL,
33039215Sgibbs			      /*dxfer_len*/0,
33139215Sgibbs			      /*timeout*/5 * 1000);
33263185Smjacob
33339215Sgibbs		if (ioctl(targfd, TARGIOCCOMMAND, &ccb) == -1) {
33439215Sgibbs			perror("TARGIOCCOMMAND");
33539215Sgibbs			exit(EX_SOFTWARE);
33639215Sgibbs		}
33739215Sgibbs
33863185Smjacob	} else {
33963185Smjacob		if (ioctl(targfd, TARGIOCCLEAREXCEPTION, &exceptions) == -1) {
34063185Smjacob			perror("TARGIOCCLEAREXCEPTION");
34163185Smjacob			exit(EX_SOFTWARE);
34263185Smjacob		}
34339215Sgibbs	}
34439215Sgibbs
34539215Sgibbs}
34639215Sgibbs
34739215Sgibbsstatic void
34844498Sgibbsquit_handler(int signum)
34944498Sgibbs{
35044498Sgibbs	quit = 1;
35144498Sgibbs}
35244498Sgibbs
35344498Sgibbsstatic void
35439215Sgibbsusage()
35539215Sgibbs{
35639215Sgibbs
35739215Sgibbs	(void)fprintf(stderr,
35863185Smjacob"usage: %-16s [ -d ] [-o output_file] [-i input_file] -p path -t target -l lun\n",
35944498Sgibbs		      appname);
36039215Sgibbs
36139215Sgibbs	exit(EX_USAGE);
36239215Sgibbs}
36339215Sgibbs
364