1*422da902Smlelstv /* $NetBSD: scsi_base.c,v 1.93 2019/05/03 16:06:56 mlelstv Exp $ */
2fccfa11aScgd
36dc90320Smycroft /*-
4ba781da1Smycroft * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc.
56dc90320Smycroft * All rights reserved.
66dc90320Smycroft *
76dc90320Smycroft * This code is derived from software contributed to The NetBSD Foundation
86dc90320Smycroft * by Charles M. Hannum.
92e5a2815Smycroft *
102e5a2815Smycroft * Redistribution and use in source and binary forms, with or without
112e5a2815Smycroft * modification, are permitted provided that the following conditions
122e5a2815Smycroft * are met:
132e5a2815Smycroft * 1. Redistributions of source code must retain the above copyright
142e5a2815Smycroft * notice, this list of conditions and the following disclaimer.
152e5a2815Smycroft * 2. Redistributions in binary form must reproduce the above copyright
162e5a2815Smycroft * notice, this list of conditions and the following disclaimer in the
172e5a2815Smycroft * documentation and/or other materials provided with the distribution.
182e5a2815Smycroft *
196dc90320Smycroft * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
206dc90320Smycroft * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
216dc90320Smycroft * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
226dc90320Smycroft * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
236dc90320Smycroft * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
246dc90320Smycroft * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
256dc90320Smycroft * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
266dc90320Smycroft * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
276dc90320Smycroft * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
286dc90320Smycroft * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
296dc90320Smycroft * POSSIBILITY OF SUCH DAMAGE.
3037548328Smycroft */
3137548328Smycroft
327ba10b35Slukem #include <sys/cdefs.h>
33*422da902Smlelstv __KERNEL_RCSID(0, "$NetBSD: scsi_base.c,v 1.93 2019/05/03 16:06:56 mlelstv Exp $");
347ba10b35Slukem
3537548328Smycroft #include <sys/param.h>
36249cddaaSthorpej #include <sys/systm.h>
37c01cb5c4Smycroft #include <sys/kernel.h>
3837548328Smycroft #include <sys/buf.h>
3937548328Smycroft #include <sys/uio.h>
4037548328Smycroft #include <sys/malloc.h>
4137548328Smycroft #include <sys/errno.h>
4237548328Smycroft #include <sys/device.h>
430bc63f48Schristos #include <sys/proc.h>
44e985f404Smycroft
456f3bab1fSbouyer #include <dev/scsipi/scsipi_all.h>
466f3bab1fSbouyer #include <dev/scsipi/scsi_all.h>
476f3bab1fSbouyer #include <dev/scsipi/scsi_disk.h>
486f3bab1fSbouyer #include <dev/scsipi/scsiconf.h>
496f3bab1fSbouyer #include <dev/scsipi/scsipi_base.h>
5037548328Smycroft
51976962adSbouyer static void scsi_print_xfer_mode(struct scsipi_periph *);
5237548328Smycroft /*
5337548328Smycroft * Do a scsi operation, asking a device to run as SCSI-II if it can.
5437548328Smycroft */
55e985f404Smycroft int
scsi_change_def(struct scsipi_periph * periph,int flags)56be6339d2Sthorpej scsi_change_def(struct scsipi_periph *periph, int flags)
5737548328Smycroft {
588f93b96fSmycroft struct scsi_changedef cmd;
5937548328Smycroft
608f93b96fSmycroft memset(&cmd, 0, sizeof(cmd));
618f93b96fSmycroft cmd.opcode = SCSI_CHANGE_DEFINITION;
628f93b96fSmycroft cmd.how = SC_SCSI_2;
6337548328Smycroft
648f93b96fSmycroft return (scsipi_command(periph, (void *)&cmd, sizeof(cmd), 0, 0,
658f93b96fSmycroft SCSIPIRETRIES, 100000, NULL, flags));
668fd13520Smycroft }
678fd13520Smycroft
6837548328Smycroft /*
698fd13520Smycroft * ask the scsi driver to perform a command for us.
708fd13520Smycroft * tell it where to read/write the data, and how
718fd13520Smycroft * long the data is supposed to be. If we have a buf
728fd13520Smycroft * to associate with the transfer, we need that too.
7337548328Smycroft */
74ba781da1Smycroft void
scsi_scsipi_cmd(struct scsipi_xfer * xs)75ba781da1Smycroft scsi_scsipi_cmd(struct scsipi_xfer *xs)
768fd13520Smycroft {
77ba781da1Smycroft struct scsipi_periph *periph = xs->xs_periph;
788fd13520Smycroft
79937a7a3eSbouyer SC_DEBUG(periph, SCSIPI_DB2, ("scsi_scsipi_cmd\n"));
808fd13520Smycroft
813b207eafScgd /*
823b207eafScgd * Set the LUN in the CDB if we have an older device. We also
83937a7a3eSbouyer * set it for more modern SCSI-2 devices "just in case".
843b207eafScgd */
85937a7a3eSbouyer if (periph->periph_version <= 2)
863b207eafScgd xs->cmd->bytes[0] |=
87937a7a3eSbouyer ((periph->periph_lun << SCSI_CMD_LUN_SHIFT) &
883b207eafScgd SCSI_CMD_LUN_MASK);
898fd13520Smycroft }
908fd13520Smycroft
9137548328Smycroft /*
9237548328Smycroft * Utility routines often used in SCSI stuff
9337548328Smycroft */
9437548328Smycroft
9537548328Smycroft /*
96937a7a3eSbouyer * Print out the periph's address info.
9737548328Smycroft */
9837548328Smycroft void
scsi_print_addr(struct scsipi_periph * periph)99be6339d2Sthorpej scsi_print_addr(struct scsipi_periph *periph)
10037548328Smycroft {
101937a7a3eSbouyer struct scsipi_channel *chan = periph->periph_channel;
102937a7a3eSbouyer struct scsipi_adapter *adapt = chan->chan_adapter;
10337548328Smycroft
104937a7a3eSbouyer printf("%s(%s:%d:%d:%d): ", periph->periph_dev != NULL ?
10513783bfdScegger device_xname(periph->periph_dev) : "probe",
10613783bfdScegger device_xname(adapt->adapt_dev),
107937a7a3eSbouyer chan->chan_channel, periph->periph_target,
108937a7a3eSbouyer periph->periph_lun);
10937548328Smycroft }
110a2369d8eSenami
111a2369d8eSenami /*
112937a7a3eSbouyer * Kill off all pending xfers for a periph.
113a2369d8eSenami *
114b6a6db8fSmlelstv * Must be called with channel lock held
115a2369d8eSenami */
116a2369d8eSenami void
scsi_kill_pending(struct scsipi_periph * periph)117168cd830Schristos scsi_kill_pending(struct scsipi_periph *periph)
118a2369d8eSenami {
1193c54deadSmlelstv struct scsipi_xfer *xs;
1203c54deadSmlelstv
1213c54deadSmlelstv TAILQ_FOREACH(xs, &periph->periph_xferq, device_q) {
1223c54deadSmlelstv callout_stop(&xs->xs_callout);
1233c54deadSmlelstv scsi_print_addr(periph);
1243c54deadSmlelstv printf("killed ");
1253c54deadSmlelstv scsipi_print_cdb(xs->cmd);
1263c54deadSmlelstv xs->error = XS_DRIVER_STUFFUP;
1273c54deadSmlelstv scsipi_done(xs);
1283c54deadSmlelstv }
129a2369d8eSenami }
130976962adSbouyer
131976962adSbouyer /*
132976962adSbouyer * scsi_print_xfer_mode:
133976962adSbouyer *
134976962adSbouyer * Print a parallel SCSI periph's capabilities.
135976962adSbouyer */
136976962adSbouyer static void
scsi_print_xfer_mode(struct scsipi_periph * periph)137976962adSbouyer scsi_print_xfer_mode(struct scsipi_periph *periph)
138976962adSbouyer {
139*422da902Smlelstv struct scsipi_channel *chan = periph->periph_channel;
140*422da902Smlelstv struct scsipi_adapter *adapt = chan->chan_adapter;
141976962adSbouyer int period, freq, speed, mbs;
142976962adSbouyer
143*422da902Smlelstv if (periph->periph_dev)
144976962adSbouyer aprint_normal_dev(periph->periph_dev, "");
145*422da902Smlelstv else
146*422da902Smlelstv aprint_normal("probe(%s:%d:%d:%d): ",
147*422da902Smlelstv device_xname(adapt->adapt_dev),
148*422da902Smlelstv chan->chan_channel, periph->periph_target,
149*422da902Smlelstv periph->periph_lun);
150976962adSbouyer if (periph->periph_mode & (PERIPH_CAP_SYNC | PERIPH_CAP_DT)) {
151976962adSbouyer period = scsipi_sync_factor_to_period(periph->periph_period);
152976962adSbouyer aprint_normal("sync (%d.%02dns offset %d)",
153976962adSbouyer period / 100, period % 100, periph->periph_offset);
154976962adSbouyer } else
155976962adSbouyer aprint_normal("async");
156976962adSbouyer
157976962adSbouyer if (periph->periph_mode & PERIPH_CAP_WIDE32)
158976962adSbouyer aprint_normal(", 32-bit");
159976962adSbouyer else if (periph->periph_mode & (PERIPH_CAP_WIDE16 | PERIPH_CAP_DT))
160976962adSbouyer aprint_normal(", 16-bit");
161976962adSbouyer else
162976962adSbouyer aprint_normal(", 8-bit");
163976962adSbouyer
164976962adSbouyer if (periph->periph_mode & (PERIPH_CAP_SYNC | PERIPH_CAP_DT)) {
165976962adSbouyer freq = scsipi_sync_factor_to_freq(periph->periph_period);
166976962adSbouyer speed = freq;
167976962adSbouyer if (periph->periph_mode & PERIPH_CAP_WIDE32)
168976962adSbouyer speed *= 4;
169976962adSbouyer else if (periph->periph_mode &
170976962adSbouyer (PERIPH_CAP_WIDE16 | PERIPH_CAP_DT))
171976962adSbouyer speed *= 2;
172976962adSbouyer mbs = speed / 1000;
173976962adSbouyer if (mbs > 0) {
174976962adSbouyer aprint_normal(" (%d.%03dMB/s)", mbs,
175976962adSbouyer speed % 1000);
176976962adSbouyer } else
177976962adSbouyer aprint_normal(" (%dKB/s)", speed % 1000);
178976962adSbouyer }
179976962adSbouyer
180976962adSbouyer aprint_normal(" transfers");
181976962adSbouyer
182976962adSbouyer if (periph->periph_mode & PERIPH_CAP_TQING)
183976962adSbouyer aprint_normal(", tagged queueing");
184976962adSbouyer
185976962adSbouyer aprint_normal("\n");
186976962adSbouyer }
187976962adSbouyer
188976962adSbouyer /*
189976962adSbouyer * scsi_async_event_xfer_mode:
190976962adSbouyer *
191976962adSbouyer * Update the xfer mode for all parallel SCSI periphs sharing the
192976962adSbouyer * specified I_T Nexus.
193976962adSbouyer */
194976962adSbouyer void
scsi_async_event_xfer_mode(struct scsipi_channel * chan,void * arg)195976962adSbouyer scsi_async_event_xfer_mode(struct scsipi_channel *chan, void *arg)
196976962adSbouyer {
197976962adSbouyer struct scsipi_xfer_mode *xm = arg;
198976962adSbouyer struct scsipi_periph *periph;
199976962adSbouyer int lun, announce, mode, period, offset;
200976962adSbouyer
201976962adSbouyer for (lun = 0; lun < chan->chan_nluns; lun++) {
202b6a6db8fSmlelstv periph = scsipi_lookup_periph_locked(chan, xm->xm_target, lun);
203976962adSbouyer if (periph == NULL)
204976962adSbouyer continue;
205976962adSbouyer announce = 0;
206976962adSbouyer
207976962adSbouyer /*
208976962adSbouyer * Clamp the xfer mode down to this periph's capabilities.
209976962adSbouyer */
210976962adSbouyer mode = xm->xm_mode & periph->periph_cap;
211976962adSbouyer if (mode & PERIPH_CAP_SYNC) {
212976962adSbouyer period = xm->xm_period;
213976962adSbouyer offset = xm->xm_offset;
214976962adSbouyer } else {
215976962adSbouyer period = 0;
216976962adSbouyer offset = 0;
217976962adSbouyer }
218976962adSbouyer
219976962adSbouyer /*
220976962adSbouyer * If we do not have a valid xfer mode yet, or the parameters
221976962adSbouyer * are different, announce them.
222976962adSbouyer */
223976962adSbouyer if ((periph->periph_flags & PERIPH_MODE_VALID) == 0 ||
224976962adSbouyer periph->periph_mode != mode ||
225976962adSbouyer periph->periph_period != period ||
226976962adSbouyer periph->periph_offset != offset)
227976962adSbouyer announce = 1;
228976962adSbouyer
229976962adSbouyer periph->periph_mode = mode;
230976962adSbouyer periph->periph_period = period;
231976962adSbouyer periph->periph_offset = offset;
232976962adSbouyer periph->periph_flags |= PERIPH_MODE_VALID;
233976962adSbouyer
234976962adSbouyer if (announce)
235976962adSbouyer scsi_print_xfer_mode(periph);
236976962adSbouyer }
237976962adSbouyer }
238976962adSbouyer
239976962adSbouyer /*
240976962adSbouyer * scsipi_async_event_xfer_mode:
241976962adSbouyer *
242976962adSbouyer * Update the xfer mode for all SAS/FC periphs sharing the
243976962adSbouyer * specified I_T Nexus.
244976962adSbouyer */
245976962adSbouyer void
scsi_fc_sas_async_event_xfer_mode(struct scsipi_channel * chan,void * arg)246976962adSbouyer scsi_fc_sas_async_event_xfer_mode(struct scsipi_channel *chan, void *arg)
247976962adSbouyer {
248976962adSbouyer struct scsipi_xfer_mode *xm = arg;
249976962adSbouyer struct scsipi_periph *periph;
250976962adSbouyer int lun, announce, mode;
251976962adSbouyer
252976962adSbouyer for (lun = 0; lun < chan->chan_nluns; lun++) {
253b6a6db8fSmlelstv periph = scsipi_lookup_periph_locked(chan, xm->xm_target, lun);
254976962adSbouyer if (periph == NULL)
255976962adSbouyer continue;
256976962adSbouyer announce = 0;
257976962adSbouyer
258976962adSbouyer /*
259976962adSbouyer * Clamp the xfer mode down to this periph's capabilities.
260976962adSbouyer */
261976962adSbouyer mode = xm->xm_mode & periph->periph_cap;
262976962adSbouyer /*
263976962adSbouyer * If we do not have a valid xfer mode yet, or the parameters
264976962adSbouyer * are different, announce them.
265976962adSbouyer */
266976962adSbouyer if ((periph->periph_flags & PERIPH_MODE_VALID) == 0 ||
267976962adSbouyer periph->periph_mode != mode)
268976962adSbouyer announce = 1;
269976962adSbouyer
270976962adSbouyer periph->periph_mode = mode;
271976962adSbouyer periph->periph_flags |= PERIPH_MODE_VALID;
272976962adSbouyer
273976962adSbouyer if (announce &&
274976962adSbouyer (periph->periph_mode & PERIPH_CAP_TQING) != 0) {
275976962adSbouyer aprint_normal_dev(periph->periph_dev,
276976962adSbouyer "tagged queueing\n");
277976962adSbouyer }
278976962adSbouyer }
279976962adSbouyer }
280