1 1.37 pooka /* $NetBSD: st_scsi.c,v 1.37 2015/08/24 23:13:15 pooka Exp $ */ 2 1.1 bouyer 3 1.1 bouyer /*- 4 1.13 mycroft * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. 5 1.1 bouyer * All rights reserved. 6 1.1 bouyer * 7 1.1 bouyer * This code is derived from software contributed to The NetBSD Foundation 8 1.1 bouyer * by Charles M. Hannum. 9 1.1 bouyer * 10 1.1 bouyer * Redistribution and use in source and binary forms, with or without 11 1.1 bouyer * modification, are permitted provided that the following conditions 12 1.1 bouyer * are met: 13 1.1 bouyer * 1. Redistributions of source code must retain the above copyright 14 1.1 bouyer * notice, this list of conditions and the following disclaimer. 15 1.1 bouyer * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 bouyer * notice, this list of conditions and the following disclaimer in the 17 1.1 bouyer * documentation and/or other materials provided with the distribution. 18 1.1 bouyer * 19 1.1 bouyer * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 bouyer * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 bouyer * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 bouyer * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 bouyer * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 bouyer * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 bouyer * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 bouyer * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 bouyer * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 bouyer * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 bouyer * POSSIBILITY OF SUCH DAMAGE. 30 1.1 bouyer */ 31 1.1 bouyer 32 1.1 bouyer /* 33 1.1 bouyer * Originally written by Julian Elischer (julian (at) tfs.com) 34 1.1 bouyer * for TRW Financial Systems for use under the MACH(2.5) operating system. 35 1.1 bouyer * 36 1.1 bouyer * TRW Financial Systems, in accordance with their agreement with Carnegie 37 1.1 bouyer * Mellon University, makes this software available to CMU to distribute 38 1.1 bouyer * or use in any manner that they see fit as long as this message is kept with 39 1.1 bouyer * the software. For this reason TFS also grants any other persons or 40 1.1 bouyer * organisations permission to use or modify this software. 41 1.1 bouyer * 42 1.1 bouyer * TFS supplies this software to be publicly redistributed 43 1.1 bouyer * on the understanding that TFS is not responsible for the correct 44 1.1 bouyer * functioning of this software in any circumstances. 45 1.1 bouyer * 46 1.1 bouyer * Ported to run under 386BSD by Julian Elischer (julian (at) tfs.com) Sept 1992 47 1.1 bouyer * major changes by Julian Elischer (julian (at) jules.dialix.oz.au) May 1993 48 1.1 bouyer * 49 1.1 bouyer * A lot of rewhacking done by mjacob (mjacob (at) nas.nasa.gov). 50 1.1 bouyer */ 51 1.5 lukem 52 1.5 lukem #include <sys/cdefs.h> 53 1.37 pooka __KERNEL_RCSID(0, "$NetBSD: st_scsi.c,v 1.37 2015/08/24 23:13:15 pooka Exp $"); 54 1.1 bouyer 55 1.37 pooka #ifdef _KERNEL_OPT 56 1.1 bouyer #include "opt_scsi.h" 57 1.37 pooka #endif 58 1.33 tls 59 1.1 bouyer #include <sys/param.h> 60 1.1 bouyer #include <sys/device.h> 61 1.1 bouyer #include <sys/buf.h> 62 1.16 yamt #include <sys/bufq.h> 63 1.1 bouyer #include <sys/conf.h> 64 1.1 bouyer #include <sys/kernel.h> 65 1.2 bouyer #include <sys/systm.h> 66 1.1 bouyer 67 1.15 mycroft #include <dev/scsipi/scsi_all.h> 68 1.15 mycroft #include <dev/scsipi/scsi_tape.h> 69 1.1 bouyer #include <dev/scsipi/stvar.h> 70 1.1 bouyer 71 1.29 cegger static int st_scsibus_match(device_t, cfdata_t, void *); 72 1.29 cegger static void st_scsibus_attach(device_t, device_t, void *); 73 1.11 thorpej static int st_scsibus_ops(struct st_softc *, int, int); 74 1.11 thorpej static int st_scsibus_read_block_limits(struct st_softc *, int); 75 1.11 thorpej static int st_scsibus_mode_sense(struct st_softc *, int); 76 1.11 thorpej static int st_scsibus_cmprss(struct st_softc *, int, int); 77 1.1 bouyer 78 1.34 mbalmer CFATTACH_DECL_NEW( 79 1.34 mbalmer st_scsibus, 80 1.34 mbalmer sizeof(struct st_softc), 81 1.34 mbalmer st_scsibus_match, 82 1.34 mbalmer st_scsibus_attach, 83 1.34 mbalmer stdetach, 84 1.34 mbalmer NULL 85 1.34 mbalmer ); 86 1.1 bouyer 87 1.11 thorpej static const struct scsipi_inquiry_pattern st_scsibus_patterns[] = { 88 1.1 bouyer {T_SEQUENTIAL, T_REMOV, 89 1.1 bouyer "", "", ""}, 90 1.1 bouyer }; 91 1.1 bouyer 92 1.11 thorpej static int 93 1.34 mbalmer st_scsibus_match(device_t parent, cfdata_t match, void *aux) 94 1.1 bouyer { 95 1.1 bouyer struct scsipibus_attach_args *sa = aux; 96 1.1 bouyer int priority; 97 1.1 bouyer 98 1.35 bouyer if (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(sa->sa_periph)) != 99 1.35 bouyer SCSIPI_BUSTYPE_SCSI) 100 1.34 mbalmer return 0; 101 1.2 bouyer 102 1.1 bouyer (void)scsipi_inqmatch(&sa->sa_inqbuf, 103 1.22 christos st_scsibus_patterns, 104 1.1 bouyer sizeof(st_scsibus_patterns)/sizeof(st_scsibus_patterns[0]), 105 1.1 bouyer sizeof(st_scsibus_patterns[0]), &priority); 106 1.34 mbalmer return priority; 107 1.2 bouyer } 108 1.2 bouyer 109 1.11 thorpej static void 110 1.29 cegger st_scsibus_attach(device_t parent, device_t self, void *aux) 111 1.2 bouyer { 112 1.24 thorpej struct st_softc *st = device_private(self); 113 1.2 bouyer 114 1.2 bouyer st->ops = st_scsibus_ops; 115 1.34 mbalmer stattach(parent, self, aux); 116 1.2 bouyer } 117 1.2 bouyer 118 1.11 thorpej static int 119 1.11 thorpej st_scsibus_ops(struct st_softc *st, int op, int flags) 120 1.2 bouyer { 121 1.2 bouyer switch(op) { 122 1.2 bouyer case ST_OPS_RBL: 123 1.2 bouyer return st_scsibus_read_block_limits(st, flags); 124 1.2 bouyer case ST_OPS_MODESENSE: 125 1.2 bouyer return st_scsibus_mode_sense(st, flags); 126 1.2 bouyer case ST_OPS_MODESELECT: 127 1.30 pgoyette return st_mode_select(st, flags); 128 1.2 bouyer case ST_OPS_CMPRSS_ON: 129 1.2 bouyer case ST_OPS_CMPRSS_OFF: 130 1.2 bouyer return st_scsibus_cmprss(st, flags, 131 1.2 bouyer (op == ST_OPS_CMPRSS_ON) ? 1 : 0); 132 1.2 bouyer default: 133 1.2 bouyer panic("st_scsibus_ops: invalid op"); 134 1.2 bouyer return 0; /* XXX to appease gcc */ 135 1.2 bouyer } 136 1.2 bouyer /* NOTREACHED */ 137 1.2 bouyer } 138 1.2 bouyer 139 1.2 bouyer /* 140 1.36 snj * Ask the drive what its min and max blk sizes are. 141 1.2 bouyer */ 142 1.11 thorpej static int 143 1.11 thorpej st_scsibus_read_block_limits(struct st_softc *st, int flags) 144 1.2 bouyer { 145 1.2 bouyer struct scsi_block_limits cmd; 146 1.2 bouyer struct scsi_block_limits_data block_limits; 147 1.2 bouyer struct scsipi_periph *periph = st->sc_periph; 148 1.2 bouyer int error; 149 1.2 bouyer 150 1.34 mbalmer /* do a 'Read Block Limits' */ 151 1.3 thorpej memset(&cmd, 0, sizeof(cmd)); 152 1.2 bouyer cmd.opcode = READ_BLOCK_LIMITS; 153 1.2 bouyer 154 1.34 mbalmer /* do the command, update the global values */ 155 1.14 mycroft error = scsipi_command(periph, (void *)&cmd, sizeof(cmd), 156 1.14 mycroft (void *)&block_limits, sizeof(block_limits), 157 1.31 rmind ST_RETRIES, ST_CTL_TIME, NULL, flags | XS_CTL_DATA_IN); 158 1.2 bouyer if (error) 159 1.34 mbalmer return error; 160 1.2 bouyer 161 1.2 bouyer st->blkmin = _2btol(block_limits.min_length); 162 1.2 bouyer st->blkmax = _3btol(block_limits.max_length); 163 1.2 bouyer 164 1.2 bouyer SC_DEBUG(periph, SCSIPI_DB3, 165 1.2 bouyer ("(%d <= blksize <= %d)\n", st->blkmin, st->blkmax)); 166 1.34 mbalmer return 0; 167 1.2 bouyer } 168 1.2 bouyer 169 1.2 bouyer /* 170 1.2 bouyer * Get the scsi driver to send a full inquiry to the 171 1.2 bouyer * device and use the results to fill out the global 172 1.2 bouyer * parameter structure. 173 1.2 bouyer * 174 1.2 bouyer * called from: 175 1.2 bouyer * attach 176 1.2 bouyer * open 177 1.2 bouyer * ioctl (to reset original blksize) 178 1.2 bouyer */ 179 1.11 thorpej static int 180 1.11 thorpej st_scsibus_mode_sense(struct st_softc *st, int flags) 181 1.2 bouyer { 182 1.19 reinoud u_int scsipi_sense_len; 183 1.2 bouyer int error; 184 1.2 bouyer struct scsipi_sense { 185 1.20 thorpej struct scsi_mode_parameter_header_6 header; 186 1.20 thorpej struct scsi_general_block_descriptor blk_desc; 187 1.19 reinoud u_char sense_data[MAX_PAGE_0_SIZE]; 188 1.2 bouyer } scsipi_sense; 189 1.2 bouyer struct scsipi_periph *periph = st->sc_periph; 190 1.2 bouyer 191 1.30 pgoyette scsipi_sense_len = sizeof(scsipi_sense.header) + 192 1.30 pgoyette sizeof(scsipi_sense.blk_desc) + 193 1.30 pgoyette st->page_0_size; 194 1.2 bouyer 195 1.2 bouyer /* 196 1.2 bouyer * Set up a mode sense 197 1.2 bouyer * We don't need the results. Just print them for our interest's sake, 198 1.2 bouyer * if asked, or if we need it as a template for the mode select store 199 1.2 bouyer * it away. 200 1.2 bouyer */ 201 1.20 thorpej error = scsipi_mode_sense(st->sc_periph, 0, SMS_PCTRL_CURRENT, 202 1.31 rmind &scsipi_sense.header, scsipi_sense_len, flags, 203 1.2 bouyer ST_RETRIES, ST_CTL_TIME); 204 1.2 bouyer if (error) 205 1.34 mbalmer return error; 206 1.2 bouyer 207 1.2 bouyer st->numblks = _3btol(scsipi_sense.blk_desc.nblocks); 208 1.2 bouyer st->media_blksize = _3btol(scsipi_sense.blk_desc.blklen); 209 1.2 bouyer st->media_density = scsipi_sense.blk_desc.density; 210 1.2 bouyer if (scsipi_sense.header.dev_spec & SMH_DSP_WRITE_PROT) 211 1.2 bouyer st->flags |= ST_READONLY; 212 1.2 bouyer else 213 1.2 bouyer st->flags &= ~ST_READONLY; 214 1.2 bouyer SC_DEBUG(periph, SCSIPI_DB3, 215 1.2 bouyer ("density code %d, %d-byte blocks, write-%s, ", 216 1.2 bouyer st->media_density, st->media_blksize, 217 1.2 bouyer st->flags & ST_READONLY ? "protected" : "enabled")); 218 1.2 bouyer SC_DEBUG(periph, SCSIPI_DB3, 219 1.2 bouyer ("%sbuffered\n", 220 1.2 bouyer scsipi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un")); 221 1.2 bouyer if (st->page_0_size) 222 1.4 thorpej memcpy(st->sense_data, scsipi_sense.sense_data, 223 1.2 bouyer st->page_0_size); 224 1.2 bouyer periph->periph_flags |= PERIPH_MEDIA_LOADED; 225 1.34 mbalmer return 0; 226 1.2 bouyer } 227 1.2 bouyer 228 1.11 thorpej static int 229 1.11 thorpej st_scsibus_cmprss(struct st_softc *st, int flags, int onoff) 230 1.2 bouyer { 231 1.19 reinoud u_int scsi_dlen; 232 1.2 bouyer int byte2, page; 233 1.2 bouyer struct scsi_select { 234 1.20 thorpej struct scsi_mode_parameter_header_6 header; 235 1.20 thorpej struct scsi_general_block_descriptor blk_desc; 236 1.19 reinoud u_char pdata[MAX(sizeof(struct scsi_tape_dev_conf_page), 237 1.2 bouyer sizeof(struct scsi_tape_dev_compression_page))]; 238 1.2 bouyer } scsi_pdata; 239 1.2 bouyer struct scsi_tape_dev_conf_page *ptr; 240 1.2 bouyer struct scsi_tape_dev_compression_page *cptr; 241 1.2 bouyer struct scsipi_periph *periph = st->sc_periph; 242 1.2 bouyer int error, ison; 243 1.2 bouyer 244 1.2 bouyer scsi_dlen = sizeof(scsi_pdata); 245 1.34 mbalmer /* Do DATA COMPRESSION page first. */ 246 1.20 thorpej page = SMS_PCTRL_CURRENT | 0xf; 247 1.2 bouyer byte2 = 0; 248 1.2 bouyer 249 1.34 mbalmer /* Do the MODE SENSE command... */ 250 1.2 bouyer again: 251 1.3 thorpej memset(&scsi_pdata, 0, scsi_dlen); 252 1.2 bouyer error = scsipi_mode_sense(periph, byte2, page, 253 1.31 rmind &scsi_pdata.header, scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME); 254 1.2 bouyer 255 1.2 bouyer if (error) { 256 1.2 bouyer if (byte2 != SMS_DBD) { 257 1.2 bouyer byte2 = SMS_DBD; 258 1.2 bouyer goto again; 259 1.2 bouyer } 260 1.34 mbalmer /* Try a different page? */ 261 1.20 thorpej if (page == (SMS_PCTRL_CURRENT | 0xf)) { 262 1.20 thorpej page = SMS_PCTRL_CURRENT | 0x10; 263 1.2 bouyer byte2 = 0; 264 1.2 bouyer goto again; 265 1.2 bouyer } 266 1.34 mbalmer return error; 267 1.2 bouyer } 268 1.2 bouyer 269 1.2 bouyer if (scsi_pdata.header.blk_desc_len) 270 1.2 bouyer ptr = (struct scsi_tape_dev_conf_page *) scsi_pdata.pdata; 271 1.2 bouyer else 272 1.2 bouyer ptr = (struct scsi_tape_dev_conf_page *) &scsi_pdata.blk_desc; 273 1.2 bouyer 274 1.20 thorpej if ((page & SMS_PAGE_MASK) == 0xf) { 275 1.2 bouyer cptr = (struct scsi_tape_dev_compression_page *) ptr; 276 1.2 bouyer ison = (cptr->dce_dcc & DCP_DCE) != 0; 277 1.2 bouyer if (onoff) 278 1.2 bouyer cptr->dce_dcc |= DCP_DCE; 279 1.2 bouyer else 280 1.2 bouyer cptr->dce_dcc &= ~DCP_DCE; 281 1.2 bouyer cptr->pagecode &= ~0x80; 282 1.2 bouyer } else { 283 1.2 bouyer ison = (ptr->sel_comp_alg != 0); 284 1.2 bouyer if (onoff) 285 1.2 bouyer ptr->sel_comp_alg = 1; 286 1.2 bouyer else 287 1.2 bouyer ptr->sel_comp_alg = 0; 288 1.2 bouyer ptr->pagecode &= ~0x80; 289 1.2 bouyer ptr->byte2 = 0; 290 1.2 bouyer ptr->active_partition = 0; 291 1.2 bouyer ptr->wb_full_ratio = 0; 292 1.2 bouyer ptr->rb_empty_ratio = 0; 293 1.2 bouyer ptr->byte8 &= ~0x30; 294 1.2 bouyer ptr->gap_size = 0; 295 1.2 bouyer ptr->byte10 &= ~0xe7; 296 1.2 bouyer ptr->ew_bufsize[0] = 0; 297 1.2 bouyer ptr->ew_bufsize[1] = 0; 298 1.2 bouyer ptr->ew_bufsize[2] = 0; 299 1.2 bouyer ptr->reserved = 0; 300 1.2 bouyer } 301 1.2 bouyer /* 302 1.2 bouyer * There might be a virtue in actually doing the MODE SELECTS, 303 1.2 bouyer * but let's not clog the bus over it. 304 1.2 bouyer */ 305 1.2 bouyer if (onoff == ison) 306 1.34 mbalmer return 0; 307 1.2 bouyer 308 1.34 mbalmer /* Set up for a mode select */ 309 1.2 bouyer scsi_pdata.header.data_length = 0; 310 1.2 bouyer scsi_pdata.header.medium_type = 0; 311 1.2 bouyer if ((st->flags & ST_DONTBUFFER) == 0) 312 1.2 bouyer scsi_pdata.header.dev_spec = SMH_DSP_BUFF_MODE_ON; 313 1.2 bouyer else 314 1.2 bouyer scsi_pdata.header.dev_spec = 0; 315 1.2 bouyer 316 1.2 bouyer if (scsi_pdata.header.blk_desc_len) { 317 1.2 bouyer scsi_pdata.blk_desc.density = 0; 318 1.2 bouyer scsi_pdata.blk_desc.nblocks[0] = 0; 319 1.2 bouyer scsi_pdata.blk_desc.nblocks[1] = 0; 320 1.2 bouyer scsi_pdata.blk_desc.nblocks[2] = 0; 321 1.2 bouyer } 322 1.2 bouyer 323 1.34 mbalmer /* Do the command */ 324 1.2 bouyer error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header, 325 1.31 rmind scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME); 326 1.2 bouyer 327 1.20 thorpej if (error && (page & SMS_PAGE_MASK) == 0xf) { 328 1.34 mbalmer /* Try DEVICE CONFIGURATION page. */ 329 1.20 thorpej page = SMS_PCTRL_CURRENT | 0x10; 330 1.2 bouyer goto again; 331 1.2 bouyer } 332 1.34 mbalmer return error; 333 1.1 bouyer } 334