1 /* $NetBSD: st_scsi.c,v 1.31 2009/10/21 21:12:06 rmind Exp $ */
4 * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Originally written by Julian Elischer (julian@tfs.com)
34 * for TRW Financial Systems for use under the MACH(2.5) operating system.
36 * TRW Financial Systems, in accordance with their agreement with Carnegie
37 * Mellon University, makes this software available to CMU to distribute
38 * or use in any manner that they see fit as long as this message is kept with
39 * the software. For this reason TFS also grants any other persons or
40 * organisations permission to use or modify this software.
42 * TFS supplies this software to be publicly redistributed
43 * on the understanding that TFS is not responsible for the correct
44 * functioning of this software in any circumstances.
46 * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
47 * major changes by Julian Elischer (julian@jules.dialix.oz.au) May 1993
49 * A lot of rewhacking done by mjacob (mjacob@nas.nasa.gov).
52 #include <sys/cdefs.h>
53 __KERNEL_RCSID(0, "$NetBSD: st_scsi.c,v 1.31 2009/10/21 21:12:06 rmind Exp $");
58 #include <sys/param.h>
59 #include <sys/device.h>
63 #include <sys/kernel.h>
64 #include <sys/systm.h>
66 #include <dev/scsipi/scsi_all.h>
67 #include <dev/scsipi/scsi_tape.h>
68 #include <dev/scsipi/stvar.h>
70 static int st_scsibus_match(device_t
, cfdata_t
, void *);
71 static void st_scsibus_attach(device_t
, device_t
, void *);
72 static int st_scsibus_ops(struct st_softc
*, int, int);
73 static int st_scsibus_read_block_limits(struct st_softc
*, int);
74 static int st_scsibus_mode_sense(struct st_softc
*, int);
75 static int st_scsibus_cmprss(struct st_softc
*, int, int);
77 CFATTACH_DECL(st_scsibus
, sizeof(struct st_softc
),
78 st_scsibus_match
, st_scsibus_attach
, stdetach
, NULL
);
80 static const struct scsipi_inquiry_pattern st_scsibus_patterns
[] = {
81 {T_SEQUENTIAL
, T_REMOV
,
86 st_scsibus_match(device_t parent
, cfdata_t match
,
89 struct scsipibus_attach_args
*sa
= aux
;
92 if (scsipi_periph_bustype(sa
->sa_periph
) != SCSIPI_BUSTYPE_SCSI
)
95 (void)scsipi_inqmatch(&sa
->sa_inqbuf
,
97 sizeof(st_scsibus_patterns
)/sizeof(st_scsibus_patterns
[0]),
98 sizeof(st_scsibus_patterns
[0]), &priority
);
103 st_scsibus_attach(device_t parent
, device_t self
, void *aux
)
105 struct st_softc
*st
= device_private(self
);
107 st
->ops
= st_scsibus_ops
;
108 stattach(parent
, st
, aux
);
112 st_scsibus_ops(struct st_softc
*st
, int op
, int flags
)
116 return st_scsibus_read_block_limits(st
, flags
);
117 case ST_OPS_MODESENSE
:
118 return st_scsibus_mode_sense(st
, flags
);
119 case ST_OPS_MODESELECT
:
120 return st_mode_select(st
, flags
);
121 case ST_OPS_CMPRSS_ON
:
122 case ST_OPS_CMPRSS_OFF
:
123 return st_scsibus_cmprss(st
, flags
,
124 (op
== ST_OPS_CMPRSS_ON
) ? 1 : 0);
126 panic("st_scsibus_ops: invalid op");
127 return 0; /* XXX to appease gcc */
133 * Ask the drive what it's min and max blk sizes are.
136 st_scsibus_read_block_limits(struct st_softc
*st
, int flags
)
138 struct scsi_block_limits cmd
;
139 struct scsi_block_limits_data block_limits
;
140 struct scsipi_periph
*periph
= st
->sc_periph
;
144 * do a 'Read Block Limits'
146 memset(&cmd
, 0, sizeof(cmd
));
147 cmd
.opcode
= READ_BLOCK_LIMITS
;
150 * do the command, update the global values
152 error
= scsipi_command(periph
, (void *)&cmd
, sizeof(cmd
),
153 (void *)&block_limits
, sizeof(block_limits
),
154 ST_RETRIES
, ST_CTL_TIME
, NULL
, flags
| XS_CTL_DATA_IN
);
158 st
->blkmin
= _2btol(block_limits
.min_length
);
159 st
->blkmax
= _3btol(block_limits
.max_length
);
161 SC_DEBUG(periph
, SCSIPI_DB3
,
162 ("(%d <= blksize <= %d)\n", st
->blkmin
, st
->blkmax
));
167 * Get the scsi driver to send a full inquiry to the
168 * device and use the results to fill out the global
169 * parameter structure.
174 * ioctl (to reset original blksize)
177 st_scsibus_mode_sense(struct st_softc
*st
, int flags
)
179 u_int scsipi_sense_len
;
181 struct scsipi_sense
{
182 struct scsi_mode_parameter_header_6 header
;
183 struct scsi_general_block_descriptor blk_desc
;
184 u_char sense_data
[MAX_PAGE_0_SIZE
];
186 struct scsipi_periph
*periph
= st
->sc_periph
;
188 scsipi_sense_len
= sizeof(scsipi_sense
.header
) +
189 sizeof(scsipi_sense
.blk_desc
) +
193 * Set up a mode sense
194 * We don't need the results. Just print them for our interest's sake,
195 * if asked, or if we need it as a template for the mode select store
198 error
= scsipi_mode_sense(st
->sc_periph
, 0, SMS_PCTRL_CURRENT
,
199 &scsipi_sense
.header
, scsipi_sense_len
, flags
,
200 ST_RETRIES
, ST_CTL_TIME
);
204 st
->numblks
= _3btol(scsipi_sense
.blk_desc
.nblocks
);
205 st
->media_blksize
= _3btol(scsipi_sense
.blk_desc
.blklen
);
206 st
->media_density
= scsipi_sense
.blk_desc
.density
;
207 if (scsipi_sense
.header
.dev_spec
& SMH_DSP_WRITE_PROT
)
208 st
->flags
|= ST_READONLY
;
210 st
->flags
&= ~ST_READONLY
;
211 SC_DEBUG(periph
, SCSIPI_DB3
,
212 ("density code %d, %d-byte blocks, write-%s, ",
213 st
->media_density
, st
->media_blksize
,
214 st
->flags
& ST_READONLY
? "protected" : "enabled"));
215 SC_DEBUG(periph
, SCSIPI_DB3
,
217 scsipi_sense
.header
.dev_spec
& SMH_DSP_BUFF_MODE
? "" : "un"));
219 memcpy(st
->sense_data
, scsipi_sense
.sense_data
,
221 periph
->periph_flags
|= PERIPH_MEDIA_LOADED
;
226 st_scsibus_cmprss(struct st_softc
*st
, int flags
, int onoff
)
231 struct scsi_mode_parameter_header_6 header
;
232 struct scsi_general_block_descriptor blk_desc
;
233 u_char pdata
[MAX(sizeof(struct scsi_tape_dev_conf_page
),
234 sizeof(struct scsi_tape_dev_compression_page
))];
236 struct scsi_tape_dev_conf_page
*ptr
;
237 struct scsi_tape_dev_compression_page
*cptr
;
238 struct scsipi_periph
*periph
= st
->sc_periph
;
241 scsi_dlen
= sizeof(scsi_pdata
);
243 * Do DATA COMPRESSION page first.
245 page
= SMS_PCTRL_CURRENT
| 0xf;
249 * Do the MODE SENSE command...
252 memset(&scsi_pdata
, 0, scsi_dlen
);
253 error
= scsipi_mode_sense(periph
, byte2
, page
,
254 &scsi_pdata
.header
, scsi_dlen
, flags
, ST_RETRIES
, ST_CTL_TIME
);
257 if (byte2
!= SMS_DBD
) {
262 * Try a different page?
264 if (page
== (SMS_PCTRL_CURRENT
| 0xf)) {
265 page
= SMS_PCTRL_CURRENT
| 0x10;
272 if (scsi_pdata
.header
.blk_desc_len
)
273 ptr
= (struct scsi_tape_dev_conf_page
*) scsi_pdata
.pdata
;
275 ptr
= (struct scsi_tape_dev_conf_page
*) &scsi_pdata
.blk_desc
;
277 if ((page
& SMS_PAGE_MASK
) == 0xf) {
278 cptr
= (struct scsi_tape_dev_compression_page
*) ptr
;
279 ison
= (cptr
->dce_dcc
& DCP_DCE
) != 0;
281 cptr
->dce_dcc
|= DCP_DCE
;
283 cptr
->dce_dcc
&= ~DCP_DCE
;
284 cptr
->pagecode
&= ~0x80;
286 ison
= (ptr
->sel_comp_alg
!= 0);
288 ptr
->sel_comp_alg
= 1;
290 ptr
->sel_comp_alg
= 0;
291 ptr
->pagecode
&= ~0x80;
293 ptr
->active_partition
= 0;
294 ptr
->wb_full_ratio
= 0;
295 ptr
->rb_empty_ratio
= 0;
298 ptr
->byte10
&= ~0xe7;
299 ptr
->ew_bufsize
[0] = 0;
300 ptr
->ew_bufsize
[1] = 0;
301 ptr
->ew_bufsize
[2] = 0;
305 * There might be a virtue in actually doing the MODE SELECTS,
306 * but let's not clog the bus over it.
312 * Set up for a mode select
315 scsi_pdata
.header
.data_length
= 0;
316 scsi_pdata
.header
.medium_type
= 0;
317 if ((st
->flags
& ST_DONTBUFFER
) == 0)
318 scsi_pdata
.header
.dev_spec
= SMH_DSP_BUFF_MODE_ON
;
320 scsi_pdata
.header
.dev_spec
= 0;
322 if (scsi_pdata
.header
.blk_desc_len
) {
323 scsi_pdata
.blk_desc
.density
= 0;
324 scsi_pdata
.blk_desc
.nblocks
[0] = 0;
325 scsi_pdata
.blk_desc
.nblocks
[1] = 0;
326 scsi_pdata
.blk_desc
.nblocks
[2] = 0;
332 error
= scsipi_mode_select(periph
, SMS_PF
, &scsi_pdata
.header
,
333 scsi_dlen
, flags
, ST_RETRIES
, ST_CTL_TIME
);
335 if (error
&& (page
& SMS_PAGE_MASK
) == 0xf) {
337 * Try DEVICE CONFIGURATION page.
339 page
= SMS_PCTRL_CURRENT
| 0x10;