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