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