1/* $NetBSD: uscsi_subr.c,v 1.7 2002/10/08 20:17:06 soren Exp $ */ 2 3/*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum; Jason R. Thorpe of the Numerical Aerospace 9 * Simulation Facility, NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 * 32 * Small changes, generalisations and Linux support by Reinoud Zandijk 33 * <reinoud@netbsd.org>. 34 * 35 */ 36 37 38/* 39 * SCSI support subroutines. 40 */ 41 42#include <sys/param.h> 43#include <sys/ioctl.h> 44#include <err.h> 45#include <errno.h> 46#include <stdio.h> 47#include <stdlib.h> 48#include <string.h> 49#include <unistd.h> 50#include <fcntl.h> 51#include <sys/types.h> 52#include <inttypes.h> 53#include <assert.h> 54 55#include "uscsilib.h" 56 57 58int uscsilib_verbose = 0; 59 60 61#ifdef USCSI_SCSIPI 62 /* 63 * scsipi is a integrated SCSI and ATAPI layer under NetBSD and exists 64 * in a modified form under OpenBSD and possibly also under other 65 * operating systems. 66 */ 67 68 69#include <sys/scsiio.h> 70#ifdef __OpenBSD__ 71#include <scsi/uscsi_all.h> 72#else 73#include <dev/scsipi/scsipi_all.h> 74#endif 75 76 77int 78uscsi_open(struct uscsi_dev *disc) 79{ 80 struct stat dstat; 81 82 disc->fhandle = open(disc->dev_name, O_RDWR, 0); /* no create */ 83 if (disc->fhandle<0) { 84 perror("Failure to open device or file"); 85 return ENODEV; 86 } 87 88 if (fstat(disc->fhandle, &dstat) < 0) { 89 perror("Can't stat device or file"); 90 uscsi_close(disc); 91 return ENODEV; 92 } 93 94 return 0; 95} 96 97 98int 99uscsi_close(struct uscsi_dev * disc) 100{ 101 close(disc->fhandle); 102 disc->fhandle = -1; 103 104 return 0; 105} 106 107 108int 109uscsi_command(int flags, struct uscsi_dev *disc, 110 void *cmd, size_t cmdlen, void *data, size_t datalen, 111 uint32_t timeout, struct uscsi_sense *uscsi_sense) 112{ 113 scsireq_t req; 114 115 memset(&req, 0, sizeof(req)); 116 if (uscsi_sense) 117 bzero(uscsi_sense, sizeof(struct uscsi_sense)); 118 119 memcpy(req.cmd, cmd, cmdlen); 120 req.cmdlen = cmdlen; 121 req.databuf = data; 122 req.datalen = datalen; 123 req.timeout = timeout; 124 req.flags = flags; 125 req.senselen = SENSEBUFLEN; 126 127 if (ioctl(disc->fhandle, SCIOCCOMMAND, &req) == -1) 128 err(1, "SCIOCCOMMAND"); 129 130 if (req.retsts == SCCMD_OK) 131 return 0; 132 133 /* Some problem; report it and exit. */ 134 if (req.retsts == SCCMD_TIMEOUT) { 135 if (uscsilib_verbose) 136 fprintf(stderr, "%s: SCSI command timed out\n", 137 disc->dev_name); 138 return EAGAIN; 139 } else if (req.retsts == SCCMD_BUSY) { 140 if (uscsilib_verbose) 141 fprintf(stderr, "%s: device is busy\n", 142 disc->dev_name); 143 return EBUSY; 144 } else if (req.retsts == SCCMD_SENSE) { 145 if (uscsi_sense) { 146 uscsi_sense->asc = req.sense[12]; 147 uscsi_sense->ascq = req.sense[13]; 148 uscsi_sense->skey_valid = req.sense[15] & 128; 149 uscsi_sense->sense_key = (req.sense[16] << 8) | 150 (req.sense[17]); 151 } 152 if (uscsilib_verbose) 153 uscsi_print_sense((char *) disc->dev_name, 154 req.cmd, req.cmdlen, 155 req.sense, req.senselen_used, 1); 156 return EIO; 157 } else 158 if (uscsilib_verbose) 159 fprintf(stderr, "%s: device had unknown status %x\n", 160 disc->dev_name, 161 req.retsts); 162 163 return EFAULT; 164} 165 166 167/* 168 * The reasoning behind this explicit copy is for compatibility with changes 169 * in our uscsi_addr structure. 170 */ 171int 172uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) 173{ 174 struct scsi_addr raddr; 175 int error; 176 177 bzero(saddr, sizeof(struct scsi_addr)); 178 error = ioctl(disc->fhandle, SCIOCIDENTIFY, &raddr); 179 if (error) return error; 180 181#ifdef __NetBSD__ 182 /* scsi and atapi are split up like in uscsi_addr */ 183 if (raddr.type == 0) { 184 saddr->type = USCSI_TYPE_SCSI; 185 saddr->addr.scsi.scbus = raddr.addr.scsi.scbus; 186 saddr->addr.scsi.target = raddr.addr.scsi.target; 187 saddr->addr.scsi.lun = raddr.addr.scsi.lun; 188 } else { 189 saddr->type = USCSI_TYPE_ATAPI; 190 saddr->addr.atapi.atbus = raddr.addr.atapi.atbus; 191 saddr->addr.atapi.drive = raddr.addr.atapi.drive; 192 } 193#endif 194#ifdef __OpenBSD__ 195 /* atapi's are shown as SCSI devices */ 196 if (raddr.type == 0) { 197 saddr->type = USCSI_TYPE_SCSI; 198 saddr->addr.scsi.scbus = raddr.scbus; 199 saddr->addr.scsi.target = raddr.target; 200 saddr->addr.scsi.lun = raddr.lun; 201 } else { 202 saddr->type = USCSI_TYPE_ATAPI; 203 saddr->addr.atapi.atbus = raddr.scbus; /* overload */ 204 saddr->addr.atapi.drive = raddr.target; /* overload */ 205 } 206#endif 207 208 return 0; 209} 210 211 212int 213uscsi_check_for_scsi(struct uscsi_dev *disc) 214{ 215 struct uscsi_addr saddr; 216 217 return uscsi_identify(disc, &saddr); 218} 219#endif /* SCSILIB_SCSIPI */ 220 221 222 223 224#ifdef USCSI_LINUX_SCSI 225 /* 226 * Support code for Linux SCSI code. It uses the ioctl() way of 227 * communicating since this is more close to the origional NetBSD 228 * scsipi implementation. 229 */ 230#include <scsi/sg.h> 231#include <scsi/scsi.h> 232 233#define SENSEBUFLEN 48 234 235 236int 237uscsi_open(struct uscsi_dev * disc) 238{ 239 int flags; 240 struct stat stat; 241 242 /* in Linux we are NOT allowed to open it blocking */ 243 /* no create! */ 244 disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0); 245 if (disc->fhandle<0) { 246 perror("Failure to open device or file"); 247 return ENODEV; 248 } 249 250 /* explicitly mark it non blocking (again) (silly Linux) */ 251 flags = fcntl(disc->fhandle, F_GETFL); 252 flags &= ~O_NONBLOCK; 253 fcntl(disc->fhandle, F_SETFL, flags); 254 255 if (fstat(disc->fhandle, &stat) < 0) { 256 perror("Can't stat device or file"); 257 uscsi_close(disc); 258 return ENODEV; 259 } 260 261 return 0; 262} 263 264 265int 266uscsi_close(struct uscsi_dev * disc) 267{ 268 close(disc->fhandle); 269 disc->fhandle = -1; 270 271 return 0; 272} 273 274 275int 276uscsi_command(int flags, struct uscsi_dev *disc, 277 void *cmd, size_t cmdlen, 278 void *data, size_t datalen, 279 uint32_t timeout, struct uscsi_sense *uscsi_sense) 280{ 281 struct sg_io_hdr req; 282 uint8_t sense_buffer[SENSEBUFLEN]; 283 int error; 284 285 bzero(&req, sizeof(req)); 286 if (flags == SG_DXFER_FROM_DEV) bzero(data, datalen); 287 288 req.interface_id = 'S'; 289 req.dxfer_direction = flags; 290 req.cmd_len = cmdlen; 291 req.mx_sb_len = SENSEBUFLEN; 292 req.iovec_count = 0; 293 req.dxfer_len = datalen; 294 req.dxferp = data; 295 req.cmdp = cmd; 296 req.sbp = sense_buffer; 297 req.flags = 0; 298 req.timeout = timeout; 299 300 error = ioctl(disc->fhandle, SG_IO, &req); 301 302 if (req.status) { 303 /* Is this OK? */ 304 if (uscsi_sense) { 305 uscsi_sense->asc = sense_buffer[12]; 306 uscsi_sense->ascq = sense_buffer[13]; 307 uscsi_sense->skey_valid = sense_buffer[15] & 128; 308 uscsi_sense->sense_key = (sense_buffer[16] << 8) | 309 (sense_buffer[17]); 310 } 311 if (uscsilib_verbose) { 312 uscsi_print_sense((char *) disc->dev_name, 313 cmd, cmdlen, sense_buffer, req.sb_len_wr, 1); 314 } 315 } 316 317 return error; 318} 319 320 321int 322uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) 323{ 324 struct sg_scsi_id sg_scsi_id; 325 struct sg_id { 326 /* target | lun << 8 | channel << 16 | low_ino << 24 */ 327 uint32_t tlci; 328 uint32_t uniq_id; 329 } sg_id; 330 int emulated; 331 int error; 332 333 /* clean result */ 334 bzero(saddr, sizeof(struct uscsi_addr)); 335 336 /* check if its really SCSI or emulated SCSI (ATAPI f.e.) */ 337 saddr->type = USCSI_TYPE_SCSI; 338 ioctl(disc->fhandle, SG_EMULATED_HOST, &emulated); 339 if (emulated) saddr->type = USCSI_TYPE_ATAPI; 340 341 /* try 2.4 kernel or older */ 342 error = ioctl(disc->fhandle, SG_GET_SCSI_ID, &sg_scsi_id); 343 if (!error) { 344 saddr->addr.scsi.target = sg_scsi_id.scsi_id; 345 saddr->addr.scsi.lun = sg_scsi_id.lun; 346 saddr->addr.scsi.scbus = sg_scsi_id.channel; 347 348 return 0; 349 } 350 351 /* 2.6 kernel or newer */ 352 error = ioctl(disc->fhandle, SCSI_IOCTL_GET_IDLUN, &sg_id); 353 if (error) return error; 354 355 saddr->addr.scsi.target = (sg_id.tlci ) & 0xff; 356 saddr->addr.scsi.lun = (sg_id.tlci >> 8) & 0xff; 357 saddr->addr.scsi.scbus = (sg_id.tlci >> 16) & 0xff; 358 359 return 0; 360} 361 362 363int uscsi_check_for_scsi(struct uscsi_dev *disc) { 364 struct uscsi_addr saddr; 365 366 return uscsi_identify(disc, &saddr); 367} 368#endif /* USCSI_LINUX_SCSI */ 369 370 371 372 373#ifdef USCSI_FREEBSD_CAM 374 375int 376uscsi_open(struct uscsi_dev *disc) 377{ 378 disc->devhandle = cam_open_device(disc->dev_name, O_RDWR); 379 380 if (disc->devhandle == NULL) { 381 disc->fhandle = open(disc->dev_name, O_RDWR | O_NONBLOCK, 0); 382 if (disc->fhandle < 0) { 383 perror("Failure to open device or file"); 384 return ENODEV; 385 } 386 } 387 388 return 0; 389} 390 391 392int 393uscsi_close(struct uscsi_dev *disc) 394{ 395 if (disc->devhandle != NULL) { 396 cam_close_device(disc->devhandle); 397 disc->devhandle = NULL; 398 } else { 399 close(disc->fhandle); 400 disc->fhandle = -1; 401 } 402 403 return 0; 404} 405 406 407int 408uscsi_command(int flags, struct uscsi_dev *disc, 409 void *cmd, size_t cmdlen, 410 void *data, size_t datalen, 411 uint32_t timeout, struct uscsi_sense *uscsi_sense) 412{ 413 struct cam_device *cam_dev; 414 struct scsi_sense_data *cam_sense_data; 415 union ccb ccb; 416 uint32_t cam_sense; 417 uint8_t *keypos; 418 int camflags; 419 420 memset(&ccb, 0, sizeof(ccb)); 421 cam_dev = (struct cam_device *) disc->devhandle; 422 423 if (datalen == 0) flags = SCSI_NODATACMD; 424 /* optional : */ 425 /* if (data) assert(flags == SCSI_NODATACMD); */ 426 427 camflags = CAM_DIR_NONE; 428 if (flags & SCSI_READCMD) 429 camflags = CAM_DIR_IN; 430 if (flags & SCSI_WRITECMD) 431 camflags = CAM_DIR_OUT; 432 433 cam_fill_csio( 434 &ccb.csio, 435 0, /* retries */ 436 NULL, /* cbfcnp */ 437 camflags, /* flags */ 438 MSG_SIMPLE_Q_TAG, /* tag_action */ 439 (u_int8_t *) data, /* data_ptr */ 440 datalen, /* dxfer_len */ 441 SSD_FULL_SIZE, /* sense_len */ 442 cmdlen, /* cdb_len */ 443 timeout /* timeout */ 444 ); 445 446 /* Disable freezing the device queue */ 447 ccb.ccb_h.flags |= CAM_DEV_QFRZDIS; 448 449 memcpy(ccb.csio.cdb_io.cdb_bytes, cmd, cmdlen); 450 451 /* Send the command down via the CAM interface */ 452 if (cam_send_ccb(cam_dev, &ccb) < 0) { 453 err(1, "cam_send_ccb"); 454 } 455 456 if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) 457 return 0; 458 459 /* print error using the uscsi_sense routines? */ 460 461 cam_sense = (ccb.ccb_h.status & (CAM_STATUS_MASK | CAM_AUTOSNS_VALID)); 462 if (cam_sense != (CAM_SCSI_STATUS_ERROR | CAM_AUTOSNS_VALID)) 463 return EFAULT; 464 465 /* drive responds with sense information */ 466 if (!uscsilib_verbose) 467 return EFAULT; 468 469 /* print sense info */ 470 cam_sense_data = &ccb.csio.sense_data; 471 if (uscsi_sense) { 472 uscsi_sense->asc = cam_sense_data->add_sense_code; 473 uscsi_sense->ascq = cam_sense_data->add_sense_code_qual; 474 keypos = cam_sense_data->sense_key_spec; 475 uscsi_sense->skey_valid = keypos[0] & 128; 476 uscsi_sense->sense_key = (keypos[1] << 8) | (keypos[2]); 477 } 478 479 uscsi_print_sense((char *) disc->dev_name, 480 cmd, cmdlen, 481 (uint8_t *) cam_sense_data, 8 + cam_sense_data->extra_len, 1); 482 483 return EFAULT; 484} 485 486 487int 488uscsi_identify(struct uscsi_dev *disc, struct uscsi_addr *saddr) 489{ 490 struct cam_device *cam_dev; 491 492 /* clean result */ 493 bzero(saddr, sizeof(struct uscsi_addr)); 494 495 cam_dev = (struct cam_device *) disc->devhandle; 496 if (!cam_dev) return ENODEV; 497 498 /* check if its really SCSI or emulated SCSI (ATAPI f.e.) ? */ 499 saddr->type = USCSI_TYPE_SCSI; 500 saddr->addr.scsi.target = cam_dev->target_id; 501 saddr->addr.scsi.lun = cam_dev->target_lun; 502 saddr->addr.scsi.scbus = cam_dev->bus_id; 503 504 return 0; 505} 506 507 508int 509uscsi_check_for_scsi(struct uscsi_dev *disc) 510{ 511 struct uscsi_addr saddr; 512 513 return uscsi_identify(disc, &saddr); 514} 515 516#endif /* USCSI_FREEBSD_CAM */ 517 518 519 520/* 521 * Generic SCSI funtions also used by the sense printing functionality. 522 * FreeBSD support has it allready asked for by the CAM. 523 */ 524 525int 526uscsi_mode_sense(struct uscsi_dev *dev, 527 uint8_t pgcode, uint8_t pctl, void *buf, size_t len) 528{ 529 scsicmd cmd; 530 531 bzero(buf, len); /* initialise recieving buffer */ 532 533 bzero(cmd, SCSI_CMD_LEN); 534 cmd[ 0] = 0x1a; /* MODE SENSE */ 535 cmd[ 1] = 0; /* - */ 536 cmd[ 2] = pgcode | pctl; /* page code and control flags */ 537 cmd[ 3] = 0; /* - */ 538 cmd[ 4] = len; /* length of recieve buffer */ 539 cmd[ 5] = 0; /* control */ 540 541 return uscsi_command(SCSI_READCMD, dev, &cmd, 6, buf, len, 10000, NULL); 542} 543 544 545int 546uscsi_mode_select(struct uscsi_dev *dev, 547 uint8_t byte2, void *buf, size_t len) 548{ 549 scsicmd cmd; 550 551 bzero(cmd, SCSI_CMD_LEN); 552 cmd[ 0] = 0x15; /* MODE SELECT */ 553 cmd[ 1] = 0x10 | byte2; /* SCSI-2 page format select */ 554 cmd[ 4] = len; /* length of page settings */ 555 cmd[ 5] = 0; /* control */ 556 557 return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len, 558 10000, NULL); 559} 560 561 562int 563uscsi_request_sense(struct uscsi_dev *dev, void *buf, size_t len) 564{ 565 scsicmd cmd; 566 567 bzero(buf, len); /* initialise recieving buffer */ 568 569 bzero(cmd, SCSI_CMD_LEN); 570 cmd[ 0] = 0x03; /* REQUEST SENSE */ 571 cmd[ 4] = len; /* length of data to be read */ 572 cmd[ 5] = 0; /* control */ 573 574 return uscsi_command(SCSI_WRITECMD, dev, &cmd, 6, buf, len, 575 10000, NULL); 576} 577 578 579/* end of uscsi_subr.c */ 580 581