1 | /* $NetBSD: st_scsi.c,v 1.37 2015/08/24 23:13:15 pooka Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2004 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 | * |
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. |
30 | */ |
31 | |
32 | /* |
33 | * Originally written by Julian Elischer (julian@tfs.com) |
34 | * for TRW Financial Systems for use under the MACH(2.5) operating system. |
35 | * |
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. |
41 | * |
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. |
45 | * |
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 |
48 | * |
49 | * A lot of rewhacking done by mjacob (mjacob@nas.nasa.gov). |
50 | */ |
51 | |
52 | #include <sys/cdefs.h> |
53 | __KERNEL_RCSID(0, "$NetBSD: st_scsi.c,v 1.37 2015/08/24 23:13:15 pooka Exp $" ); |
54 | |
55 | #ifdef _KERNEL_OPT |
56 | #include "opt_scsi.h" |
57 | #endif |
58 | |
59 | #include <sys/param.h> |
60 | #include <sys/device.h> |
61 | #include <sys/buf.h> |
62 | #include <sys/bufq.h> |
63 | #include <sys/conf.h> |
64 | #include <sys/kernel.h> |
65 | #include <sys/systm.h> |
66 | |
67 | #include <dev/scsipi/scsi_all.h> |
68 | #include <dev/scsipi/scsi_tape.h> |
69 | #include <dev/scsipi/stvar.h> |
70 | |
71 | static int st_scsibus_match(device_t, cfdata_t, void *); |
72 | static void st_scsibus_attach(device_t, device_t, void *); |
73 | static int st_scsibus_ops(struct st_softc *, int, int); |
74 | static int st_scsibus_read_block_limits(struct st_softc *, int); |
75 | static int st_scsibus_mode_sense(struct st_softc *, int); |
76 | static int st_scsibus_cmprss(struct st_softc *, int, int); |
77 | |
78 | CFATTACH_DECL_NEW( |
79 | st_scsibus, |
80 | sizeof(struct st_softc), |
81 | st_scsibus_match, |
82 | st_scsibus_attach, |
83 | stdetach, |
84 | NULL |
85 | ); |
86 | |
87 | static const struct scsipi_inquiry_pattern st_scsibus_patterns[] = { |
88 | {T_SEQUENTIAL, T_REMOV, |
89 | "" , "" , "" }, |
90 | }; |
91 | |
92 | static int |
93 | st_scsibus_match(device_t parent, cfdata_t match, void *aux) |
94 | { |
95 | struct scsipibus_attach_args *sa = aux; |
96 | int priority; |
97 | |
98 | if (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(sa->sa_periph)) != |
99 | SCSIPI_BUSTYPE_SCSI) |
100 | return 0; |
101 | |
102 | (void)scsipi_inqmatch(&sa->sa_inqbuf, |
103 | st_scsibus_patterns, |
104 | sizeof(st_scsibus_patterns)/sizeof(st_scsibus_patterns[0]), |
105 | sizeof(st_scsibus_patterns[0]), &priority); |
106 | return priority; |
107 | } |
108 | |
109 | static void |
110 | st_scsibus_attach(device_t parent, device_t self, void *aux) |
111 | { |
112 | struct st_softc *st = device_private(self); |
113 | |
114 | st->ops = st_scsibus_ops; |
115 | stattach(parent, self, aux); |
116 | } |
117 | |
118 | static int |
119 | st_scsibus_ops(struct st_softc *st, int op, int flags) |
120 | { |
121 | switch(op) { |
122 | case ST_OPS_RBL: |
123 | return st_scsibus_read_block_limits(st, flags); |
124 | case ST_OPS_MODESENSE: |
125 | return st_scsibus_mode_sense(st, flags); |
126 | case ST_OPS_MODESELECT: |
127 | return st_mode_select(st, flags); |
128 | case ST_OPS_CMPRSS_ON: |
129 | case ST_OPS_CMPRSS_OFF: |
130 | return st_scsibus_cmprss(st, flags, |
131 | (op == ST_OPS_CMPRSS_ON) ? 1 : 0); |
132 | default: |
133 | panic("st_scsibus_ops: invalid op" ); |
134 | return 0; /* XXX to appease gcc */ |
135 | } |
136 | /* NOTREACHED */ |
137 | } |
138 | |
139 | /* |
140 | * Ask the drive what its min and max blk sizes are. |
141 | */ |
142 | static int |
143 | st_scsibus_read_block_limits(struct st_softc *st, int flags) |
144 | { |
145 | struct scsi_block_limits cmd; |
146 | struct scsi_block_limits_data block_limits; |
147 | struct scsipi_periph *periph = st->sc_periph; |
148 | int error; |
149 | |
150 | /* do a 'Read Block Limits' */ |
151 | memset(&cmd, 0, sizeof(cmd)); |
152 | cmd.opcode = READ_BLOCK_LIMITS; |
153 | |
154 | /* do the command, update the global values */ |
155 | error = scsipi_command(periph, (void *)&cmd, sizeof(cmd), |
156 | (void *)&block_limits, sizeof(block_limits), |
157 | ST_RETRIES, ST_CTL_TIME, NULL, flags | XS_CTL_DATA_IN); |
158 | if (error) |
159 | return error; |
160 | |
161 | st->blkmin = _2btol(block_limits.min_length); |
162 | st->blkmax = _3btol(block_limits.max_length); |
163 | |
164 | SC_DEBUG(periph, SCSIPI_DB3, |
165 | ("(%d <= blksize <= %d)\n" , st->blkmin, st->blkmax)); |
166 | return 0; |
167 | } |
168 | |
169 | /* |
170 | * Get the scsi driver to send a full inquiry to the |
171 | * device and use the results to fill out the global |
172 | * parameter structure. |
173 | * |
174 | * called from: |
175 | * attach |
176 | * open |
177 | * ioctl (to reset original blksize) |
178 | */ |
179 | static int |
180 | st_scsibus_mode_sense(struct st_softc *st, int flags) |
181 | { |
182 | u_int scsipi_sense_len; |
183 | int error; |
184 | struct scsipi_sense { |
185 | struct scsi_mode_parameter_header_6 ; |
186 | struct scsi_general_block_descriptor blk_desc; |
187 | u_char sense_data[MAX_PAGE_0_SIZE]; |
188 | } scsipi_sense; |
189 | struct scsipi_periph *periph = st->sc_periph; |
190 | |
191 | scsipi_sense_len = sizeof(scsipi_sense.header) + |
192 | sizeof(scsipi_sense.blk_desc) + |
193 | st->page_0_size; |
194 | |
195 | /* |
196 | * Set up a mode sense |
197 | * We don't need the results. Just print them for our interest's sake, |
198 | * if asked, or if we need it as a template for the mode select store |
199 | * it away. |
200 | */ |
201 | error = scsipi_mode_sense(st->sc_periph, 0, SMS_PCTRL_CURRENT, |
202 | &scsipi_sense.header, scsipi_sense_len, flags, |
203 | ST_RETRIES, ST_CTL_TIME); |
204 | if (error) |
205 | return error; |
206 | |
207 | st->numblks = _3btol(scsipi_sense.blk_desc.nblocks); |
208 | st->media_blksize = _3btol(scsipi_sense.blk_desc.blklen); |
209 | st->media_density = scsipi_sense.blk_desc.density; |
210 | if (scsipi_sense.header.dev_spec & SMH_DSP_WRITE_PROT) |
211 | st->flags |= ST_READONLY; |
212 | else |
213 | st->flags &= ~ST_READONLY; |
214 | SC_DEBUG(periph, SCSIPI_DB3, |
215 | ("density code %d, %d-byte blocks, write-%s, " , |
216 | st->media_density, st->media_blksize, |
217 | st->flags & ST_READONLY ? "protected" : "enabled" )); |
218 | SC_DEBUG(periph, SCSIPI_DB3, |
219 | ("%sbuffered\n" , |
220 | scsipi_sense.header.dev_spec & SMH_DSP_BUFF_MODE ? "" : "un" )); |
221 | if (st->page_0_size) |
222 | memcpy(st->sense_data, scsipi_sense.sense_data, |
223 | st->page_0_size); |
224 | periph->periph_flags |= PERIPH_MEDIA_LOADED; |
225 | return 0; |
226 | } |
227 | |
228 | static int |
229 | (struct st_softc *st, int flags, int onoff) |
230 | { |
231 | u_int scsi_dlen; |
232 | int byte2, page; |
233 | struct scsi_select { |
234 | struct scsi_mode_parameter_header_6 ; |
235 | struct scsi_general_block_descriptor blk_desc; |
236 | u_char pdata[MAX(sizeof(struct scsi_tape_dev_conf_page), |
237 | sizeof(struct scsi_tape_dev_compression_page))]; |
238 | } scsi_pdata; |
239 | struct scsi_tape_dev_conf_page *ptr; |
240 | struct scsi_tape_dev_compression_page *cptr; |
241 | struct scsipi_periph *periph = st->sc_periph; |
242 | int error, ison; |
243 | |
244 | scsi_dlen = sizeof(scsi_pdata); |
245 | /* Do DATA COMPRESSION page first. */ |
246 | page = SMS_PCTRL_CURRENT | 0xf; |
247 | byte2 = 0; |
248 | |
249 | /* Do the MODE SENSE command... */ |
250 | again: |
251 | memset(&scsi_pdata, 0, scsi_dlen); |
252 | error = scsipi_mode_sense(periph, byte2, page, |
253 | &scsi_pdata.header, scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME); |
254 | |
255 | if (error) { |
256 | if (byte2 != SMS_DBD) { |
257 | byte2 = SMS_DBD; |
258 | goto again; |
259 | } |
260 | /* Try a different page? */ |
261 | if (page == (SMS_PCTRL_CURRENT | 0xf)) { |
262 | page = SMS_PCTRL_CURRENT | 0x10; |
263 | byte2 = 0; |
264 | goto again; |
265 | } |
266 | return error; |
267 | } |
268 | |
269 | if (scsi_pdata.header.blk_desc_len) |
270 | ptr = (struct scsi_tape_dev_conf_page *) scsi_pdata.pdata; |
271 | else |
272 | ptr = (struct scsi_tape_dev_conf_page *) &scsi_pdata.blk_desc; |
273 | |
274 | if ((page & SMS_PAGE_MASK) == 0xf) { |
275 | cptr = (struct scsi_tape_dev_compression_page *) ptr; |
276 | ison = (cptr->dce_dcc & DCP_DCE) != 0; |
277 | if (onoff) |
278 | cptr->dce_dcc |= DCP_DCE; |
279 | else |
280 | cptr->dce_dcc &= ~DCP_DCE; |
281 | cptr->pagecode &= ~0x80; |
282 | } else { |
283 | ison = (ptr->sel_comp_alg != 0); |
284 | if (onoff) |
285 | ptr->sel_comp_alg = 1; |
286 | else |
287 | ptr->sel_comp_alg = 0; |
288 | ptr->pagecode &= ~0x80; |
289 | ptr->byte2 = 0; |
290 | ptr->active_partition = 0; |
291 | ptr->wb_full_ratio = 0; |
292 | ptr->rb_empty_ratio = 0; |
293 | ptr->byte8 &= ~0x30; |
294 | ptr->gap_size = 0; |
295 | ptr->byte10 &= ~0xe7; |
296 | ptr->ew_bufsize[0] = 0; |
297 | ptr->ew_bufsize[1] = 0; |
298 | ptr->ew_bufsize[2] = 0; |
299 | ptr->reserved = 0; |
300 | } |
301 | /* |
302 | * There might be a virtue in actually doing the MODE SELECTS, |
303 | * but let's not clog the bus over it. |
304 | */ |
305 | if (onoff == ison) |
306 | return 0; |
307 | |
308 | /* Set up for a mode select */ |
309 | scsi_pdata.header.data_length = 0; |
310 | scsi_pdata.header.medium_type = 0; |
311 | if ((st->flags & ST_DONTBUFFER) == 0) |
312 | scsi_pdata.header.dev_spec = SMH_DSP_BUFF_MODE_ON; |
313 | else |
314 | scsi_pdata.header.dev_spec = 0; |
315 | |
316 | if (scsi_pdata.header.blk_desc_len) { |
317 | scsi_pdata.blk_desc.density = 0; |
318 | scsi_pdata.blk_desc.nblocks[0] = 0; |
319 | scsi_pdata.blk_desc.nblocks[1] = 0; |
320 | scsi_pdata.blk_desc.nblocks[2] = 0; |
321 | } |
322 | |
323 | /* Do the command */ |
324 | error = scsipi_mode_select(periph, SMS_PF, &scsi_pdata.header, |
325 | scsi_dlen, flags, ST_RETRIES, ST_CTL_TIME); |
326 | |
327 | if (error && (page & SMS_PAGE_MASK) == 0xf) { |
328 | /* Try DEVICE CONFIGURATION page. */ |
329 | page = SMS_PCTRL_CURRENT | 0x10; |
330 | goto again; |
331 | } |
332 | return error; |
333 | } |
334 | |