1 /* $OpenBSD: scsi_ioctl.c,v 1.64 2020/07/16 14:44:55 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/scsiconf.h> 49 50 #include <sys/scsiio.h> 51 #include <sys/ataio.h> 52 53 int scsi_ioc_cmd(struct scsi_link *, scsireq_t *); 54 int scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *); 55 56 const unsigned char scsi_readsafe_cmd[256] = { 57 [0x00] = 1, /* TEST UNIT READY */ 58 [0x03] = 1, /* REQUEST SENSE */ 59 [0x08] = 1, /* READ(6) */ 60 [0x12] = 1, /* INQUIRY */ 61 [0x1a] = 1, /* MODE SENSE */ 62 [0x1b] = 1, /* START STOP */ 63 [0x23] = 1, /* READ FORMAT CAPACITIES */ 64 [0x25] = 1, /* READ CDVD CAPACITY */ 65 [0x28] = 1, /* READ(10) */ 66 [0x2b] = 1, /* SEEK */ 67 [0x2f] = 1, /* VERIFY(10) */ 68 [0x3c] = 1, /* READ BUFFER */ 69 [0x3e] = 1, /* READ LONG */ 70 [0x42] = 1, /* READ SUBCHANNEL */ 71 [0x43] = 1, /* READ TOC PMA ATIP */ 72 [0x44] = 1, /* READ HEADER */ 73 [0x45] = 1, /* PLAY AUDIO(10) */ 74 [0x46] = 1, /* GET CONFIGURATION */ 75 [0x47] = 1, /* PLAY AUDIO MSF */ 76 [0x48] = 1, /* PLAY AUDIO TI */ 77 [0x4a] = 1, /* GET EVENT STATUS NOTIFICATION */ 78 [0x4b] = 1, /* PAUSE RESUME */ 79 [0x4e] = 1, /* STOP PLAY SCAN */ 80 [0x51] = 1, /* READ DISC INFO */ 81 [0x52] = 1, /* READ TRACK RZONE INFO */ 82 [0x5a] = 1, /* MODE SENSE(10) */ 83 [0x88] = 1, /* READ(16) */ 84 [0x8f] = 1, /* VERIFY(16) */ 85 [0xa4] = 1, /* REPORT KEY */ 86 [0xa5] = 1, /* PLAY AUDIO(12) */ 87 [0xa8] = 1, /* READ(12) */ 88 [0xac] = 1, /* GET PERFORMANCE */ 89 [0xad] = 1, /* READ DVD STRUCTURE */ 90 [0xb9] = 1, /* READ CD MSF */ 91 [0xba] = 1, /* SCAN */ 92 [0xbc] = 1, /* PLAY CD */ 93 [0xbd] = 1, /* MECHANISM STATUS */ 94 [0xbe] = 1 /* READ CD */ 95 }; 96 97 int 98 scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq) 99 { 100 struct scsi_xfer *xs; 101 int err = 0; 102 103 if (screq->cmdlen > sizeof(struct scsi_generic)) 104 return EFAULT; 105 if (screq->datalen > MAXPHYS) 106 return EINVAL; 107 108 xs = scsi_xs_get(link, 0); 109 if (xs == NULL) 110 return ENOMEM; 111 112 memcpy(xs->cmd, screq->cmd, screq->cmdlen); 113 xs->cmdlen = screq->cmdlen; 114 115 if (screq->datalen > 0) { 116 xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO); 117 if (xs->data == NULL) { 118 err = ENOMEM; 119 goto err; 120 } 121 xs->datalen = screq->datalen; 122 } 123 124 if (ISSET(screq->flags, SCCMD_READ)) 125 SET(xs->flags, SCSI_DATA_IN); 126 if (ISSET(screq->flags, SCCMD_WRITE)) { 127 if (screq->datalen > 0) { 128 err = copyin(screq->databuf, xs->data, screq->datalen); 129 if (err != 0) 130 goto err; 131 } 132 133 SET(xs->flags, SCSI_DATA_OUT); 134 } 135 136 SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */ 137 xs->timeout = screq->timeout; 138 xs->retries = 0; /* user must do the retries *//* ignored */ 139 140 scsi_xs_sync(xs); 141 142 screq->retsts = 0; 143 screq->status = xs->status; 144 switch (xs->error) { 145 case XS_NOERROR: 146 /* probably rubbish */ 147 screq->datalen_used = xs->datalen - xs->resid; 148 screq->retsts = SCCMD_OK; 149 break; 150 case XS_SENSE: 151 SC_DEBUG_SENSE(xs); 152 screq->senselen_used = min(sizeof(xs->sense), 153 sizeof(screq->sense)); 154 memcpy(screq->sense, &xs->sense, screq->senselen_used); 155 screq->retsts = SCCMD_SENSE; 156 break; 157 case XS_SHORTSENSE: 158 SC_DEBUG_SENSE(xs); 159 printf("XS_SHORTSENSE\n"); 160 screq->senselen_used = min(sizeof(xs->sense), 161 sizeof(screq->sense)); 162 memcpy(screq->sense, &xs->sense, screq->senselen_used); 163 screq->retsts = SCCMD_UNKNOWN; 164 break; 165 case XS_DRIVER_STUFFUP: 166 screq->retsts = SCCMD_UNKNOWN; 167 break; 168 case XS_TIMEOUT: 169 screq->retsts = SCCMD_TIMEOUT; 170 break; 171 case XS_BUSY: 172 screq->retsts = SCCMD_BUSY; 173 break; 174 default: 175 screq->retsts = SCCMD_UNKNOWN; 176 break; 177 } 178 179 if (screq->datalen > 0 && ISSET(screq->flags, SCCMD_READ)) { 180 err = copyout(xs->data, screq->databuf, screq->datalen); 181 if (err != 0) 182 goto err; 183 } 184 185 err: 186 if (xs->data) 187 dma_free(xs->data, screq->datalen); 188 scsi_xs_put(xs); 189 190 return err; 191 } 192 193 int 194 scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq) 195 { 196 struct scsi_xfer *xs; 197 struct scsi_ata_passthru_12 *cdb; 198 int err = 0; 199 200 if (atareq->datalen > MAXPHYS) 201 return EINVAL; 202 203 xs = scsi_xs_get(link, 0); 204 if (xs == NULL) 205 return ENOMEM; 206 207 cdb = (struct scsi_ata_passthru_12 *)xs->cmd; 208 cdb->opcode = ATA_PASSTHRU_12; 209 210 if (atareq->datalen > 0) { 211 if (ISSET(atareq->flags, ATACMD_READ)) { 212 cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN; 213 cdb->flags = ATA_PASSTHRU_T_DIR_READ; 214 } else { 215 cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT; 216 cdb->flags = ATA_PASSTHRU_T_DIR_WRITE; 217 } 218 SET(cdb->flags, ATA_PASSTHRU_T_LEN_SECTOR_COUNT); 219 } else { 220 cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA; 221 cdb->flags = ATA_PASSTHRU_T_LEN_NONE; 222 } 223 cdb->features = atareq->features; 224 cdb->sector_count = atareq->sec_count; 225 cdb->lba_low = atareq->sec_num; 226 cdb->lba_mid = atareq->cylinder; 227 cdb->lba_high = atareq->cylinder >> 8; 228 cdb->device = atareq->head & 0x0f; 229 cdb->command = atareq->command; 230 231 xs->cmdlen = sizeof(*cdb); 232 233 if (atareq->datalen > 0) { 234 xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO); 235 if (xs->data == NULL) { 236 err = ENOMEM; 237 goto err; 238 } 239 xs->datalen = atareq->datalen; 240 } 241 242 if (ISSET(atareq->flags, ATACMD_READ)) 243 SET(xs->flags, SCSI_DATA_IN); 244 if (ISSET(atareq->flags, ATACMD_WRITE)) { 245 if (atareq->datalen > 0) { 246 err = copyin(atareq->databuf, xs->data, 247 atareq->datalen); 248 if (err != 0) 249 goto err; 250 } 251 252 SET(xs->flags, SCSI_DATA_OUT); 253 } 254 255 SET(xs->flags, SCSI_SILENT); /* User is responsible for errors. */ 256 xs->retries = 0; /* user must do the retries *//* ignored */ 257 258 scsi_xs_sync(xs); 259 260 atareq->retsts = ATACMD_ERROR; 261 switch (xs->error) { 262 case XS_SENSE: 263 case XS_SHORTSENSE: 264 SC_DEBUG_SENSE(xs); 265 /* XXX this is not right */ 266 case XS_NOERROR: 267 atareq->retsts = ATACMD_OK; 268 break; 269 default: 270 atareq->retsts = ATACMD_ERROR; 271 break; 272 } 273 274 if (atareq->datalen > 0 && ISSET(atareq->flags, ATACMD_READ)) { 275 err = copyout(xs->data, atareq->databuf, atareq->datalen); 276 if (err != 0) 277 goto err; 278 } 279 280 err: 281 if (xs->data) 282 dma_free(xs->data, atareq->datalen); 283 scsi_xs_put(xs); 284 285 return err; 286 } 287 288 /* 289 * Something (e.g. another driver) has called us 290 * with a scsi_link for a target/lun/adapter, and a scsi 291 * specific ioctl to perform, better try. 292 */ 293 int 294 scsi_do_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag) 295 { 296 SC_DEBUG(link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd)); 297 298 switch(cmd) { 299 case SCIOCIDENTIFY: { 300 struct scsi_addr *sca = (struct scsi_addr *)addr; 301 302 if ((link->flags & (SDEV_ATAPI | SDEV_UMASS)) == 0) 303 /* A 'real' SCSI target. */ 304 sca->type = TYPE_SCSI; 305 else 306 /* An 'emulated' SCSI target. */ 307 sca->type = TYPE_ATAPI; 308 sca->scbus = link->bus->sc_dev.dv_unit; 309 sca->target = link->target; 310 sca->lun = link->lun; 311 return 0; 312 } 313 case SCIOCCOMMAND: 314 if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]]) 315 break; 316 /* FALLTHROUGH */ 317 case ATAIOCCOMMAND: 318 case SCIOCDEBUG: 319 if (!ISSET(flag, FWRITE)) 320 return EPERM; 321 break; 322 default: 323 if (link->bus->sb_adapter->ioctl) 324 return (link->bus->sb_adapter->ioctl)(link, cmd, addr, flag); 325 else 326 return ENOTTY; 327 } 328 329 switch(cmd) { 330 case SCIOCCOMMAND: 331 return scsi_ioc_cmd(link, (scsireq_t *)addr); 332 case ATAIOCCOMMAND: 333 return scsi_ioc_ata_cmd(link, (atareq_t *)addr); 334 case SCIOCDEBUG: { 335 int level = *((int *)addr); 336 337 SC_DEBUG(link, SDEV_DB3, ("debug set to %d\n", level)); 338 CLR(link->flags, SDEV_DBX); /* clear debug bits */ 339 if (level & 1) 340 SET(link->flags, SDEV_DB1); 341 if (level & 2) 342 SET(link->flags, SDEV_DB2); 343 if (level & 4) 344 SET(link->flags, SDEV_DB3); 345 if (level & 8) 346 SET(link->flags, SDEV_DB4); 347 return 0; 348 } 349 default: 350 #ifdef DIAGNOSTIC 351 panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd); 352 #endif /* DIAGNOSTIC */ 353 return 0; 354 } 355 } 356