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