1 /* $NetBSD: st_scsi.c,v 1.7 2001/12/07 11:26:31 yamt 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. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Originally written by Julian Elischer (julian@tfs.com) 41 * for TRW Financial Systems for use under the MACH(2.5) operating system. 42 * 43 * TRW Financial Systems, in accordance with their agreement with Carnegie 44 * Mellon University, makes this software available to CMU to distribute 45 * or use in any manner that they see fit as long as this message is kept with 46 * the software. For this reason TFS also grants any other persons or 47 * organisations permission to use or modify this software. 48 * 49 * TFS supplies this software to be publicly redistributed 50 * on the understanding that TFS is not responsible for the correct 51 * functioning of this software in any circumstances. 52 * 53 * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 54 * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993 55 * 56 * A lot of rewhacking done by mjacob (mjacob@nas.nasa.gov). 57 */ 58 59 #include <sys/cdefs.h> 60 __KERNEL_RCSID(0, "$NetBSD: st_scsi.c,v 1.7 2001/12/07 11:26:31 yamt Exp $"); 61 62 #include "opt_scsi.h" 63 #include "rnd.h" 64 65 #include <sys/param.h> 66 #include <sys/device.h> 67 #include <sys/buf.h> 68 #include <sys/conf.h> 69 #include <sys/kernel.h> 70 #include <sys/systm.h> 71 72 #include <dev/scsipi/stvar.h> 73 #include <dev/scsipi/scsi_tape.h> 74 #include <dev/scsipi/scsi_all.h> 75 76 int st_scsibus_match __P((struct device *, struct cfdata *, void *)); 77 void st_scsibus_attach __P((struct device *, struct device *, void *)); 78 int st_scsibus_ops __P((struct st_softc *, int, int)); 79 int st_scsibus_read_block_limits __P((struct st_softc *, int)); 80 int st_scsibus_mode_sense __P((struct st_softc *, int)); 81 int st_scsibus_mode_select __P((struct st_softc *, int)); 82 int st_scsibus_cmprss __P((struct st_softc *, int, int)); 83 84 struct cfattach st_scsibus_ca = { 85 sizeof(struct st_softc), st_scsibus_match, st_scsibus_attach, 86 stdetach, stactivate 87 }; 88 89 const struct scsipi_inquiry_pattern st_scsibus_patterns[] = { 90 {T_SEQUENTIAL, T_REMOV, 91 "", "", ""}, 92 }; 93 94 int 95 st_scsibus_match(parent, match, aux) 96 struct device *parent; 97 struct cfdata *match; 98 void *aux; 99 { 100 struct scsipibus_attach_args *sa = aux; 101 int priority; 102 103 if (scsipi_periph_bustype(sa->sa_periph) != SCSIPI_BUSTYPE_SCSI) 104 return (0); 105 106 (void)scsipi_inqmatch(&sa->sa_inqbuf, 107 (caddr_t)st_scsibus_patterns, 108 sizeof(st_scsibus_patterns)/sizeof(st_scsibus_patterns[0]), 109 sizeof(st_scsibus_patterns[0]), &priority); 110 return (priority); 111 } 112 113 void 114 st_scsibus_attach(parent, self, aux) 115 struct device *parent, *self; 116 void *aux; 117 { 118 struct st_softc *st = (void *)self; 119 120 st->ops = st_scsibus_ops; 121 stattach(parent, st, aux); 122 } 123 124 int 125 st_scsibus_ops(st, op, flags) 126 struct st_softc *st; 127 int op; 128 int flags; 129 { 130 switch(op) { 131 case ST_OPS_RBL: 132 return st_scsibus_read_block_limits(st, flags); 133 case ST_OPS_MODESENSE: 134 return st_scsibus_mode_sense(st, flags); 135 case ST_OPS_MODESELECT: 136 return st_scsibus_mode_select(st, flags); 137 case ST_OPS_CMPRSS_ON: 138 case ST_OPS_CMPRSS_OFF: 139 return st_scsibus_cmprss(st, flags, 140 (op == ST_OPS_CMPRSS_ON) ? 1 : 0); 141 default: 142 panic("st_scsibus_ops: invalid op"); 143 return 0; /* XXX to appease gcc */ 144 } 145 /* NOTREACHED */ 146 } 147 148 /* 149 * Ask the drive what it's min and max blk sizes are. 150 */ 151 int 152 st_scsibus_read_block_limits(st, flags) 153 struct st_softc *st; 154 int flags; 155 { 156 struct scsi_block_limits cmd; 157 struct scsi_block_limits_data block_limits; 158 struct scsipi_periph *periph = st->sc_periph; 159 int error; 160 161 /* 162 * do a 'Read Block Limits' 163 */ 164 memset(&cmd, 0, sizeof(cmd)); 165 cmd.opcode = READ_BLOCK_LIMITS; 166 167 /* 168 * do the command, update the global values 169 */ 170 error = scsipi_command(periph, (struct scsipi_generic *)&cmd, 171 sizeof(cmd), (u_char *)&block_limits, sizeof(block_limits), 172 ST_RETRIES, ST_CTL_TIME, NULL, 173 flags | XS_CTL_DATA_IN | XS_CTL_DATA_ONSTACK); 174 if (error) 175 return (error); 176 177 st->blkmin = _2btol(block_limits.min_length); 178 st->blkmax = _3btol(block_limits.max_length); 179 180 SC_DEBUG(periph, SCSIPI_DB3, 181 ("(%d <= blksize <= %d)\n", st->blkmin, st->blkmax)); 182 return (0); 183 } 184 185 /* 186 * Get the scsi driver to send a full inquiry to the 187 * device and use the results to fill out the global 188 * parameter structure. 189 * 190 * called from: 191 * attach 192 * open 193 * ioctl (to reset original blksize) 194 */ 195 int 196 st_scsibus_mode_sense(st, flags) 197 struct st_softc *st; 198 int flags; 199 { 200 u_int scsipi_sense_len; 201 int error; 202 struct scsipi_sense { 203 struct scsipi_mode_header header; 204 struct scsi_blk_desc blk_desc; 205 u_char sense_data[MAX_PAGE_0_SIZE]; 206 } scsipi_sense; 207 struct scsipi_periph *periph = st->sc_periph; 208 209 scsipi_sense_len = 12 + st->page_0_size; 210 211 /* 212 * Set up a mode sense 213 * We don't need the results. Just print them for our interest's sake, 214 * if asked, or if we need it as a template for the mode select store 215 * it away. 216 */ 217 error = scsipi_mode_sense(st->sc_periph, 0, SMS_PAGE_CTRL_CURRENT, 218 &scsipi_sense.header, scsipi_sense_len, flags | XS_CTL_DATA_ONSTACK, 219 ST_RETRIES, ST_CTL_TIME); 220 if (error) 221 return (error); 222 223 st->numblks = _3btol(scsipi_sense.blk_desc.nblocks); 224 st->media_blksize = _3btol(scsipi_sense.blk_desc.blklen); 225 st->media_density = scsipi_sense.blk_desc.density; 226 if (scsipi_sense.header.dev_spec & SMH_DSP_WRITE_PROT) 227 st->flags |= ST_READONLY; 228 else 229 st->flags &= ~ST_READONLY; 230 SC_DEBUG(periph, SCSIPI_DB3, 231 ("density code %d, %d-byte blocks, write-%s, ", 232 st->media_density, st->media_blksize, 233 st->flags & ST_READONLY ? "protected" : "enabled")); 234 SC_DEBUG(periph, SCSIPI_DB3, 235 ("%sbuffered\n", 236 scsipi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un")); 237 if (st->page_0_size) 238 memcpy(st->sense_data, scsipi_sense.sense_data, 239 st->page_0_size); 240 periph->periph_flags |= PERIPH_MEDIA_LOADED; 241 return (0); 242 } 243 244 /* 245 * Send a filled out parameter structure to the drive to 246 * set it into the desire modes etc. 247 */ 248 int 249 st_scsibus_mode_select(st, flags) 250 struct st_softc *st; 251 int flags; 252 { 253 u_int scsi_select_len; 254 struct scsi_select { 255 struct scsipi_mode_header header; 256 struct scsi_blk_desc blk_desc; 257 u_char sense_data[MAX_PAGE_0_SIZE]; 258 } scsi_select; 259 struct scsipi_periph *periph = st->sc_periph; 260 261 scsi_select_len = 12 + st->page_0_size; 262 263 /* 264 * This quirk deals with drives that have only one valid mode 265 * and think this gives them license to reject all mode selects, 266 * even if the selected mode is the one that is supported. 267 */ 268 if (st->quirks & ST_Q_UNIMODAL) { 269 SC_DEBUG(periph, SCSIPI_DB3, 270 ("not setting density 0x%x blksize 0x%x\n", 271 st->density, st->blksize)); 272 return (0); 273 } 274 275 /* 276 * Set up for a mode select 277 */ 278 memset(&scsi_select, 0, scsi_select_len); 279 scsi_select.header.blk_desc_len = sizeof(struct scsi_blk_desc); 280 scsi_select.header.dev_spec &= ~SMH_DSP_BUFF_MODE; 281 scsi_select.blk_desc.density = st->density; 282 if (st->flags & ST_DONTBUFFER) 283 scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_OFF; 284 else 285 scsi_select.header.dev_spec |= SMH_DSP_BUFF_MODE_ON; 286 if (st->flags & ST_FIXEDBLOCKS) 287 _lto3b(st->blksize, scsi_select.blk_desc.blklen); 288 if (st->page_0_size) 289 memcpy(scsi_select.sense_data, st->sense_data, st->page_0_size); 290 291 /* 292 * do the command 293 */ 294 return scsipi_mode_select(periph, 0, &scsi_select.header, 295 scsi_select_len, flags | XS_CTL_DATA_ONSTACK, 296 ST_RETRIES, ST_CTL_TIME); 297 } 298 299 int 300 st_scsibus_cmprss(st, flags, onoff) 301 struct st_softc *st; 302 int flags; 303 int onoff; 304 { 305 u_int scsi_dlen; 306 int byte2, page; 307 struct scsi_select { 308 struct scsipi_mode_header header; 309 struct scsi_blk_desc blk_desc; 310 u_char pdata[MAX(sizeof(struct scsi_tape_dev_conf_page), 311 sizeof(struct scsi_tape_dev_compression_page))]; 312 } scsi_pdata; 313 struct scsi_tape_dev_conf_page *ptr; 314 struct scsi_tape_dev_compression_page *cptr; 315 struct scsipi_periph *periph = st->sc_periph; 316 int error, ison; 317 318 scsi_dlen = sizeof(scsi_pdata); 319 /* 320 * Do DATA COMPRESSION page first. 321 */ 322 page = SMS_PAGE_CTRL_CURRENT | 0xf; 323 byte2 = 0; 324 325 /* 326 * Do the MODE SENSE command... 327 */ 328 again: 329 memset(&scsi_pdata, 0, scsi_dlen); 330 error = scsipi_mode_sense(periph, byte2, page, 331 &scsi_pdata.header, scsi_dlen, flags | XS_CTL_DATA_ONSTACK, 332 ST_RETRIES, ST_CTL_TIME); 333 334 if (error) { 335 if (byte2 != SMS_DBD) { 336 byte2 = SMS_DBD; 337 goto again; 338 } 339 /* 340 * Try a different page? 341 */ 342 if (page == (SMS_PAGE_CTRL_CURRENT | 0xf)) { 343 page = SMS_PAGE_CTRL_CURRENT | 0x10; 344 byte2 = 0; 345 goto again; 346 } 347 return (error); 348 } 349 350 if (scsi_pdata.header.blk_desc_len) 351 ptr = (struct scsi_tape_dev_conf_page *) scsi_pdata.pdata; 352 else 353 ptr = (struct scsi_tape_dev_conf_page *) &scsi_pdata.blk_desc; 354 355 if ((page & SMS_PAGE_CODE) == 0xf) { 356 cptr = (struct scsi_tape_dev_compression_page *) ptr; 357 ison = (cptr->dce_dcc & DCP_DCE) != 0; 358 if (onoff) 359 cptr->dce_dcc |= DCP_DCE; 360 else 361 cptr->dce_dcc &= ~DCP_DCE; 362 cptr->pagecode &= ~0x80; 363 } else { 364 ison = (ptr->sel_comp_alg != 0); 365 if (onoff) 366 ptr->sel_comp_alg = 1; 367 else 368 ptr->sel_comp_alg = 0; 369 ptr->pagecode &= ~0x80; 370 ptr->byte2 = 0; 371 ptr->active_partition = 0; 372 ptr->wb_full_ratio = 0; 373 ptr->rb_empty_ratio = 0; 374 ptr->byte8 &= ~0x30; 375 ptr->gap_size = 0; 376 ptr->byte10 &= ~0xe7; 377 ptr->ew_bufsize[0] = 0; 378 ptr->ew_bufsize[1] = 0; 379 ptr->ew_bufsize[2] = 0; 380 ptr->reserved = 0; 381 } 382 /* 383 * There might be a virtue in actually doing the MODE SELECTS, 384 * but let's not clog the bus over it. 385 */ 386 if (onoff == ison) 387 return (0); 388 389 /* 390 * Set up for a mode select 391 */ 392 393 scsi_pdata.header.data_length = 0; 394 scsi_pdata.header.medium_type = 0; 395 if ((st->flags & ST_DONTBUFFER) == 0) 396 scsi_pdata.header.dev_spec = SMH_DSP_BUFF_MODE_ON; 397 else 398 scsi_pdata.header.dev_spec = 0; 399 400 if (scsi_pdata.header.blk_desc_len) { 401 scsi_pdata.blk_desc.density = 0; 402 scsi_pdata.blk_desc.nblocks[0] = 0; 403 scsi_pdata.blk_desc.nblocks[1] = 0; 404 scsi_pdata.blk_desc.nblocks[2] = 0; 405 } 406 407 /* 408 * Do the command 409 */ 410 error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header, 411 scsi_dlen, flags | XS_CTL_DATA_ONSTACK, ST_RETRIES, ST_CTL_TIME); 412 413 if (error && (page & SMS_PAGE_CODE) == 0xf) { 414 /* 415 * Try DEVICE CONFIGURATION page. 416 */ 417 page = SMS_PAGE_CTRL_CURRENT | 0x10; 418 goto again; 419 } 420 return (error); 421 } 422