xref: /netbsd-src/sys/dev/scsipi/scsi_base.c (revision 422da90206af4aa93240398e2e9a776a68390dba)
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