1/* $OpenBSD: scsi_ioctl.c,v 1.67 2020/09/22 19:32:53 krw Exp $ */ 2/* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */ 3 4/* 5 * Copyright (c) 1994 Charles Hannum. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Charles Hannum. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33/* 34 * Contributed by HD Associates (hd@world.std.com). 35 * Copyright (c) 1992, 1993 HD Associates 36 * 37 * Berkeley style copyright. 38 */ 39 40#include <sys/param.h> 41#include <sys/errno.h> 42#include <sys/systm.h> 43#include <sys/pool.h> 44#include <sys/device.h> 45#include <sys/fcntl.h> 46 47#include <scsi/scsi_all.h> 48#include <scsi/scsi_debug.h> 49#include <scsi/scsiconf.h> 50 51#include <sys/scsiio.h> 52#include <sys/ataio.h> 53 54int scsi_ioc_cmd(struct scsi_link *, scsireq_t *); 55int scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *); 56 57const unsigned char scsi_readsafe_cmd[256] = { 58 [0x00] = 1, /* TEST UNIT READY */ 59 [0x03] = 1, /* REQUEST SENSE */ 60 [0x08] = 1, /* READ(6) */ 61 [0x12] = 1, /* INQUIRY */ 62 [0x1a] = 1, /* MODE SENSE */ 63 [0x1b] = 1, /* START STOP */ 64 [0x23] = 1, /* READ FORMAT CAPACITIES */ 65 [0x25] = 1, /* READ CDVD CAPACITY */ 66 [0x28] = 1, /* READ(10) */ 67 [0x2b] = 1, /* SEEK */ 68 [0x2f] = 1, /* VERIFY(10) */ 69 [0x3c] = 1, /* READ BUFFER */ 70 [0x3e] = 1, /* READ LONG */ 71 [0x42] = 1, /* READ SUBCHANNEL */ 72 [0x43] = 1, /* READ TOC PMA ATIP */ 73 [0x44] = 1, /* READ HEADER */ 74 [0x45] = 1, /* PLAY AUDIO(10) */ 75 [0x46] = 1, /* GET CONFIGURATION */ 76 [0x47] = 1, /* PLAY AUDIO MSF */ 77 [0x48] = 1, /* PLAY AUDIO TI */ 78 [0x4a] = 1, /* GET EVENT STATUS NOTIFICATION */ 79 [0x4b] = 1, /* PAUSE RESUME */ 80 [0x4e] = 1, /* STOP PLAY SCAN */ 81 [0x51] = 1, /* READ DISC INFO */ 82 [0x52] = 1, /* READ TRACK RZONE INFO */ 83 [0x5a] = 1, /* MODE SENSE(10) */ 84 [0x88] = 1, /* READ(16) */ 85 [0x8f] = 1, /* VERIFY(16) */ 86 [0xa4] = 1, /* REPORT KEY */ 87 [0xa5] = 1, /* PLAY AUDIO(12) */ 88 [0xa8] = 1, /* READ(12) */ 89 [0xac] = 1, /* GET PERFORMANCE */ 90 [0xad] = 1, /* READ DVD STRUCTURE */ 91 [0xb9] = 1, /* READ CD MSF */ 92 [0xba] = 1, /* SCAN */ 93 [0xbc] = 1, /* PLAY CD */ 94 [0xbd] = 1, /* MECHANISM STATUS */ 95 [0xbe] = 1 /* READ CD */ 96}; 97 98int 99scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq) 100{ 101 struct scsi_xfer *xs; 102 int err = 0; 103 104 if (screq->cmdlen > sizeof(struct scsi_generic)) 105 return EFAULT; 106 if (screq->datalen > MAXPHYS) 107 return EINVAL; 108 109 xs = scsi_xs_get(link, 0); 110 if (xs == NULL) 111 return ENOMEM; 112 113 memcpy(&xs->cmd, screq->cmd, screq->cmdlen); 114 xs->cmdlen = screq->cmdlen; 115 116 if (screq->datalen > 0) { 117 xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO); 118 if (xs->data == NULL) { 119 err = ENOMEM; 120 goto err; 121 } 122 xs->datalen = screq->datalen; 123 } 124 125 if (ISSET(screq->flags, SCCMD_READ)) 126 SET(xs->flags, SCSI_DATA_IN); 127 if (ISSET(screq->flags, SCCMD_WRITE)) { 128 if (screq->datalen > 0) { 129 err = copyin(screq->databuf, xs->data, screq->datalen); 130 if (err != 0) 131 goto err; 132 } 133 134 SET(xs->flags, SCSI_DATA_OUT); 135 } 136 137 SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */ 138 xs->timeout = screq->timeout; 139 xs->retries = 0; /* user must do the retries *//* ignored */ 140 141 scsi_xs_sync(xs); 142 143 screq->retsts = 0; 144 screq->status = xs->status; 145 switch (xs->error) { 146 case XS_NOERROR: 147 /* probably rubbish */ 148 screq->datalen_used = xs->datalen - xs->resid; 149 screq->retsts = SCCMD_OK; 150 break; 151 case XS_SENSE: 152 SC_DEBUG_SENSE(xs); 153 screq->senselen_used = min(sizeof(xs->sense), 154 sizeof(screq->sense)); 155 memcpy(screq->sense, &xs->sense, screq->senselen_used); 156 screq->retsts = SCCMD_SENSE; 157 break; 158 case XS_SHORTSENSE: 159 SC_DEBUG_SENSE(xs); 160 printf("XS_SHORTSENSE\n"); 161 screq->senselen_used = min(sizeof(xs->sense), 162 sizeof(screq->sense)); 163 memcpy(screq->sense, &xs->sense, screq->senselen_used); 164 screq->retsts = SCCMD_UNKNOWN; 165 break; 166 case XS_DRIVER_STUFFUP: 167 screq->retsts = SCCMD_UNKNOWN; 168 break; 169 case XS_TIMEOUT: 170 screq->retsts = SCCMD_TIMEOUT; 171 break; 172 case XS_BUSY: 173 screq->retsts = SCCMD_BUSY; 174 break; 175 default: 176 screq->retsts = SCCMD_UNKNOWN; 177 break; 178 } 179 180 if (screq->datalen > 0 && ISSET(screq->flags, SCCMD_READ)) { 181 err = copyout(xs->data, screq->databuf, screq->datalen); 182 if (err != 0) 183 goto err; 184 } 185 186err: 187 if (xs->data) 188 dma_free(xs->data, screq->datalen); 189 scsi_xs_put(xs); 190 191 return err; 192} 193 194int 195scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq) 196{ 197 struct scsi_xfer *xs; 198 struct scsi_ata_passthru_12 *cdb; 199 int err = 0; 200 201 if (atareq->datalen > MAXPHYS) 202 return EINVAL; 203 204 xs = scsi_xs_get(link, 0); 205 if (xs == NULL) 206 return ENOMEM; 207 208 cdb = (struct scsi_ata_passthru_12 *)&xs->cmd; 209 cdb->opcode = ATA_PASSTHRU_12; 210 211 if (atareq->datalen > 0) { 212 if (ISSET(atareq->flags, ATACMD_READ)) { 213 cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN; 214 cdb->flags = ATA_PASSTHRU_T_DIR_READ; 215 } else { 216 cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT; 217 cdb->flags = ATA_PASSTHRU_T_DIR_WRITE; 218 } 219 SET(cdb->flags, ATA_PASSTHRU_T_LEN_SECTOR_COUNT); 220 } else { 221 cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA; 222 cdb->flags = ATA_PASSTHRU_T_LEN_NONE; 223 } 224 cdb->features = atareq->features; 225 cdb->sector_count = atareq->sec_count; 226 cdb->lba_low = atareq->sec_num; 227 cdb->lba_mid = atareq->cylinder; 228 cdb->lba_high = atareq->cylinder >> 8; 229 cdb->device = atareq->head & 0x0f; 230 cdb->command = atareq->command; 231 232 xs->cmdlen = sizeof(*cdb); 233 234 if (atareq->datalen > 0) { 235 xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO); 236 if (xs->data == NULL) { 237 err = ENOMEM; 238 goto err; 239 } 240 xs->datalen = atareq->datalen; 241 } 242 243 if (ISSET(atareq->flags, ATACMD_READ)) 244 SET(xs->flags, SCSI_DATA_IN); 245 if (ISSET(atareq->flags, ATACMD_WRITE)) { 246 if (atareq->datalen > 0) { 247 err = copyin(atareq->databuf, xs->data, 248 atareq->datalen); 249 if (err != 0) 250 goto err; 251 } 252 253 SET(xs->flags, SCSI_DATA_OUT); 254 } 255 256 SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */ 257 xs->retries = 0; /* user must do the retries *//* ignored */ 258 259 scsi_xs_sync(xs); 260 261 atareq->retsts = ATACMD_ERROR; 262 switch (xs->error) { 263 case XS_SENSE: 264 case XS_SHORTSENSE: 265 SC_DEBUG_SENSE(xs); 266 /* XXX this is not right */ 267 case XS_NOERROR: 268 atareq->retsts = ATACMD_OK; 269 break; 270 default: 271 atareq->retsts = ATACMD_ERROR; 272 break; 273 } 274 275 if (atareq->datalen > 0 && ISSET(atareq->flags, ATACMD_READ)) { 276 err = copyout(xs->data, atareq->databuf, atareq->datalen); 277 if (err != 0) 278 goto err; 279 } 280 281err: 282 if (xs->data) 283 dma_free(xs->data, atareq->datalen); 284 scsi_xs_put(xs); 285 286 return err; 287} 288 289/* 290 * Something (e.g. another driver) has called us 291 * with a scsi_link for a target/lun/adapter, and a scsi 292 * specific ioctl to perform, better try. 293 */ 294int 295scsi_do_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag) 296{ 297 SC_DEBUG(link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd)); 298 299 switch(cmd) { 300 case SCIOCIDENTIFY: { 301 struct scsi_addr *sca = (struct scsi_addr *)addr; 302 303 if (!ISSET(link->flags, (SDEV_ATAPI | SDEV_UMASS))) 304 /* A 'real' SCSI target. */ 305 sca->type = TYPE_SCSI; 306 else 307 /* An 'emulated' SCSI target. */ 308 sca->type = TYPE_ATAPI; 309 sca->scbus = link->bus->sc_dev.dv_unit; 310 sca->target = link->target; 311 sca->lun = link->lun; 312 return 0; 313 } 314 case SCIOCCOMMAND: 315 if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]]) 316 break; 317 /* FALLTHROUGH */ 318 case ATAIOCCOMMAND: 319 case SCIOCDEBUG: 320 if (!ISSET(flag, FWRITE)) 321 return EPERM; 322 break; 323 default: 324 if (link->bus->sb_adapter->ioctl) 325 return (link->bus->sb_adapter->ioctl)(link, cmd, addr, flag); 326 else 327 return ENOTTY; 328 } 329 330 switch(cmd) { 331 case SCIOCCOMMAND: 332 return scsi_ioc_cmd(link, (scsireq_t *)addr); 333 case ATAIOCCOMMAND: 334 return scsi_ioc_ata_cmd(link, (atareq_t *)addr); 335 case SCIOCDEBUG: { 336 int level = *((int *)addr); 337 338 SC_DEBUG(link, SDEV_DB3, ("debug set to %d\n", level)); 339 CLR(link->flags, SDEV_DBX); /* clear debug bits */ 340 if (level & 1) 341 SET(link->flags, SDEV_DB1); 342 if (level & 2) 343 SET(link->flags, SDEV_DB2); 344 if (level & 4) 345 SET(link->flags, SDEV_DB3); 346 if (level & 8) 347 SET(link->flags, SDEV_DB4); 348 return 0; 349 } 350 default: 351#ifdef DIAGNOSTIC 352 panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd); 353#endif /* DIAGNOSTIC */ 354 return 0; 355 } 356} 357