1*1a2899ffSandvar /* $NetBSD: iopsp.c,v 1.41 2022/05/04 07:48:34 andvar Exp $ */
2d49fd135Sad
3d49fd135Sad /*-
4e5bca80aSad * Copyright (c) 2000, 2001, 2007 The NetBSD Foundation, Inc.
5d49fd135Sad * All rights reserved.
6d49fd135Sad *
7d49fd135Sad * This code is derived from software contributed to The NetBSD Foundation
8d49fd135Sad * by Andrew Doran.
9d49fd135Sad *
10d49fd135Sad * Redistribution and use in source and binary forms, with or without
11d49fd135Sad * modification, are permitted provided that the following conditions
12d49fd135Sad * are met:
13d49fd135Sad * 1. Redistributions of source code must retain the above copyright
14d49fd135Sad * notice, this list of conditions and the following disclaimer.
15d49fd135Sad * 2. Redistributions in binary form must reproduce the above copyright
16d49fd135Sad * notice, this list of conditions and the following disclaimer in the
17d49fd135Sad * documentation and/or other materials provided with the distribution.
18d49fd135Sad *
19d49fd135Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d49fd135Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d49fd135Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d49fd135Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d49fd135Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d49fd135Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d49fd135Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d49fd135Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d49fd135Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d49fd135Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d49fd135Sad * POSSIBILITY OF SUCH DAMAGE.
30d49fd135Sad */
31d49fd135Sad
32d49fd135Sad /*
33ebf51109Sad * Raw SCSI device support for I2O. IOPs present SCSI devices individually;
34ebf51109Sad * we group them by controlling port.
35d49fd135Sad */
36d49fd135Sad
3705b019baSlukem #include <sys/cdefs.h>
38*1a2899ffSandvar __KERNEL_RCSID(0, "$NetBSD: iopsp.c,v 1.41 2022/05/04 07:48:34 andvar Exp $");
39d49fd135Sad
40d49fd135Sad #include <sys/param.h>
41d49fd135Sad #include <sys/systm.h>
42d49fd135Sad #include <sys/kernel.h>
43d49fd135Sad #include <sys/device.h>
44d49fd135Sad #include <sys/queue.h>
45d49fd135Sad #include <sys/proc.h>
46d49fd135Sad #include <sys/buf.h>
47d49fd135Sad #include <sys/endian.h>
48d49fd135Sad #include <sys/malloc.h>
49d49fd135Sad #include <sys/scsiio.h>
50d49fd135Sad
516f0f9f87Sdsl #include <sys/bswap.h>
52a2a38285Sad #include <sys/bus.h>
53d49fd135Sad
54d49fd135Sad #include <dev/scsipi/scsi_all.h>
55d49fd135Sad #include <dev/scsipi/scsi_disk.h>
56d49fd135Sad #include <dev/scsipi/scsipi_all.h>
57d49fd135Sad #include <dev/scsipi/scsiconf.h>
58937a7a3eSbouyer #include <dev/scsipi/scsi_message.h>
59d49fd135Sad
60d49fd135Sad #include <dev/i2o/i2o.h>
61ebf51109Sad #include <dev/i2o/iopio.h>
62d49fd135Sad #include <dev/i2o/iopvar.h>
63d49fd135Sad #include <dev/i2o/iopspvar.h>
64d49fd135Sad
65529e91fcScegger static void iopsp_adjqparam(device_t, int);
66529e91fcScegger static void iopsp_attach(device_t, device_t, void *);
67529e91fcScegger static void iopsp_intr(device_t, struct iop_msg *, void *);
68937a7a3eSbouyer static int iopsp_ioctl(struct scsipi_channel *, u_long,
6953524e44Schristos void *, int, struct proc *);
70529e91fcScegger static int iopsp_match(device_t, cfdata_t, void *);
71d49fd135Sad static int iopsp_rescan(struct iopsp_softc *);
72529e91fcScegger static int iopsp_reconfig(device_t);
73937a7a3eSbouyer static void iopsp_scsipi_request(struct scsipi_channel *,
74937a7a3eSbouyer scsipi_adapter_req_t, void *);
75d49fd135Sad
76cbab9cadSchs CFATTACH_DECL_NEW(iopsp, sizeof(struct iopsp_softc),
77c9b3657cSthorpej iopsp_match, iopsp_attach, NULL, NULL);
78d49fd135Sad
79d49fd135Sad /*
80ebf51109Sad * Match a supported device.
81d49fd135Sad */
82d49fd135Sad static int
iopsp_match(device_t parent,cfdata_t match,void * aux)83529e91fcScegger iopsp_match(device_t parent, cfdata_t match, void *aux)
84d49fd135Sad {
85d49fd135Sad struct iop_attach_args *ia;
8633a7e100Smsaitoh struct iop_softc *iop;
87d49fd135Sad struct {
88d49fd135Sad struct i2o_param_op_results pr;
89d49fd135Sad struct i2o_param_read_results prr;
90d49fd135Sad struct i2o_param_hba_ctlr_info ci;
910de5da96Sgmcgarry } __packed param;
92d49fd135Sad
93d49fd135Sad ia = aux;
9433a7e100Smsaitoh iop = device_private(parent);
95d49fd135Sad
96d49fd135Sad if (ia->ia_class != I2O_CLASS_BUS_ADAPTER_PORT)
97d49fd135Sad return (0);
98d49fd135Sad
9933a7e100Smsaitoh if (iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO, ¶m,
10033a7e100Smsaitoh sizeof(param), NULL) != 0)
101d49fd135Sad return (0);
102d49fd135Sad
103d49fd135Sad return (param.ci.bustype == I2O_HBA_BUS_SCSI ||
104d49fd135Sad param.ci.bustype == I2O_HBA_BUS_FCA);
105d49fd135Sad }
106d49fd135Sad
107d49fd135Sad /*
108d49fd135Sad * Attach a supported device.
109d49fd135Sad */
110d49fd135Sad static void
iopsp_attach(device_t parent,device_t self,void * aux)111529e91fcScegger iopsp_attach(device_t parent, device_t self, void *aux)
112d49fd135Sad {
113d49fd135Sad struct iop_attach_args *ia;
114d49fd135Sad struct iopsp_softc *sc;
115d49fd135Sad struct iop_softc *iop;
116d49fd135Sad struct {
117d49fd135Sad struct i2o_param_op_results pr;
118d49fd135Sad struct i2o_param_read_results prr;
119d49fd135Sad union {
120d49fd135Sad struct i2o_param_hba_ctlr_info ci;
121d49fd135Sad struct i2o_param_hba_scsi_ctlr_info sci;
122af9d4c4fSad struct i2o_param_hba_scsi_port_info spi;
123d49fd135Sad } p;
1240de5da96Sgmcgarry } __packed param;
12503e46dc8Sad int fc, rv;
126d49fd135Sad int size;
127d49fd135Sad
128d49fd135Sad ia = (struct iop_attach_args *)aux;
1298fc35725Sthorpej sc = device_private(self);
1308fc35725Sthorpej iop = device_private(parent);
131d49fd135Sad
132d49fd135Sad /* Register us as an initiator. */
133d49fd135Sad sc->sc_ii.ii_dv = self;
134d49fd135Sad sc->sc_ii.ii_intr = iopsp_intr;
135d49fd135Sad sc->sc_ii.ii_flags = 0;
136af9d4c4fSad sc->sc_ii.ii_tid = ia->ia_tid;
137af9d4c4fSad sc->sc_ii.ii_reconfig = iopsp_reconfig;
138ebf51109Sad sc->sc_ii.ii_adjqparam = iopsp_adjqparam;
139ebf51109Sad iop_initiator_register(iop, &sc->sc_ii);
140d49fd135Sad
1411d6329bbSad rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO,
1421d6329bbSad ¶m, sizeof(param), NULL);
1431d6329bbSad if (rv != 0)
144d49fd135Sad goto bad;
145d49fd135Sad
14603e46dc8Sad fc = (param.p.ci.bustype == I2O_HBA_BUS_FCA);
147d49fd135Sad
148d49fd135Sad /*
149*1a2899ffSandvar * Say what the device is. If we can find out what the controlling
150d49fd135Sad * device is, say what that is too.
151d49fd135Sad */
1528ef79919Sad aprint_normal(": SCSI port");
153ebf51109Sad iop_print_ident(iop, ia->ia_tid);
1548ef79919Sad aprint_normal("\n");
155d49fd135Sad
1561d6329bbSad rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_SCSI_CTLR_INFO,
1571d6329bbSad ¶m, sizeof(param), NULL);
1581d6329bbSad if (rv != 0)
159d49fd135Sad goto bad;
160d49fd135Sad
161cbab9cadSchs aprint_normal_dev(sc->sc_dev, "");
16203e46dc8Sad if (fc)
1638ef79919Sad aprint_normal("FC");
16403e46dc8Sad else
1658ef79919Sad aprint_normal("%d-bit", param.p.sci.maxdatawidth);
1668ef79919Sad aprint_normal(", max sync rate %dMHz, initiator ID %d\n",
167d49fd135Sad (u_int32_t)le64toh(param.p.sci.maxsyncrate) / 1000,
168d49fd135Sad le32toh(param.p.sci.initiatorid));
169d49fd135Sad
170434a4243Sthorpej sc->sc_openings = 1;
171434a4243Sthorpej
172cbab9cadSchs sc->sc_adapter.adapt_dev = sc->sc_dev;
173937a7a3eSbouyer sc->sc_adapter.adapt_nchannels = 1;
174937a7a3eSbouyer sc->sc_adapter.adapt_openings = 1;
175937a7a3eSbouyer sc->sc_adapter.adapt_max_periph = 1;
176937a7a3eSbouyer sc->sc_adapter.adapt_ioctl = iopsp_ioctl;
177937a7a3eSbouyer sc->sc_adapter.adapt_minphys = minphys;
178937a7a3eSbouyer sc->sc_adapter.adapt_request = iopsp_scsipi_request;
179d49fd135Sad
180937a7a3eSbouyer memset(&sc->sc_channel, 0, sizeof(sc->sc_channel));
181937a7a3eSbouyer sc->sc_channel.chan_adapter = &sc->sc_adapter;
182937a7a3eSbouyer sc->sc_channel.chan_bustype = &scsi_bustype;
183937a7a3eSbouyer sc->sc_channel.chan_channel = 0;
18403e46dc8Sad sc->sc_channel.chan_ntargets = fc ?
18503e46dc8Sad IOPSP_MAX_FC_TARGET : param.p.sci.maxdatawidth;
186937a7a3eSbouyer sc->sc_channel.chan_nluns = IOPSP_MAX_LUN;
187937a7a3eSbouyer sc->sc_channel.chan_id = le32toh(param.p.sci.initiatorid);
188937a7a3eSbouyer sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE;
189d49fd135Sad
190d49fd135Sad /*
191d49fd135Sad * Allocate the target map. Currently used for informational
192d49fd135Sad * purposes only.
193d49fd135Sad */
194937a7a3eSbouyer size = sc->sc_channel.chan_ntargets * sizeof(struct iopsp_target);
195d47bcd29Schs sc->sc_targetmap = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
196d49fd135Sad
197d49fd135Sad /* Build the two maps, and attach to scsipi. */
198af9d4c4fSad if (iopsp_reconfig(self) != 0) {
199cbab9cadSchs aprint_error_dev(sc->sc_dev, "configure failed\n");
200af9d4c4fSad goto bad;
201af9d4c4fSad }
202c7fb772bSthorpej config_found(self, &sc->sc_channel, scsiprint, CFARGS_NONE);
203d49fd135Sad return;
204d49fd135Sad
205d49fd135Sad bad:
206d49fd135Sad iop_initiator_unregister(iop, &sc->sc_ii);
207d49fd135Sad }
208d49fd135Sad
209d49fd135Sad /*
210af9d4c4fSad * Scan the LCT to determine which devices we control, and enter them into
211af9d4c4fSad * the maps.
212d49fd135Sad */
213af9d4c4fSad static int
iopsp_reconfig(device_t dv)214529e91fcScegger iopsp_reconfig(device_t dv)
215d49fd135Sad {
216af9d4c4fSad struct iopsp_softc *sc;
217d49fd135Sad struct iop_softc *iop;
218d49fd135Sad struct i2o_lct_entry *le;
219937a7a3eSbouyer struct scsipi_channel *sc_chan;
220d49fd135Sad struct {
221d49fd135Sad struct i2o_param_op_results pr;
222d49fd135Sad struct i2o_param_read_results prr;
223d49fd135Sad struct i2o_param_scsi_device_info sdi;
2240de5da96Sgmcgarry } __packed param;
225e5bca80aSad u_int tid, nent, i, targ, lun, size, rv, bptid;
226d49fd135Sad u_short *tidmap;
227e5bca80aSad void *tofree;
228d49fd135Sad struct iopsp_target *it;
229d49fd135Sad int syncrate;
230d49fd135Sad
231cbab9cadSchs sc = device_private(dv);
232cbab9cadSchs iop = device_private(device_parent(sc->sc_dev));
233937a7a3eSbouyer sc_chan = &sc->sc_channel;
234d49fd135Sad
235e5bca80aSad KASSERT(mutex_owned(&iop->sc_conflock));
236e5bca80aSad
237af9d4c4fSad /* Anything to do? */
238ebf51109Sad if (iop->sc_chgind == sc->sc_chgind)
239af9d4c4fSad return (0);
240af9d4c4fSad
241d49fd135Sad /*
242d49fd135Sad * Allocate memory for the target/LUN -> TID map. Use zero to
243d49fd135Sad * denote absent targets (zero is the TID of the I2O executive,
244d49fd135Sad * and we never address that here).
245d49fd135Sad */
246937a7a3eSbouyer size = sc_chan->chan_ntargets * (IOPSP_MAX_LUN) * sizeof(u_short);
247c12b5c18Stsutsui if ((tidmap = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO)) == NULL)
248af9d4c4fSad return (ENOMEM);
249d49fd135Sad
250937a7a3eSbouyer for (i = 0; i < sc_chan->chan_ntargets; i++)
251d49fd135Sad sc->sc_targetmap[i].it_flags &= ~IT_PRESENT;
252d49fd135Sad
253ebf51109Sad /*
254ebf51109Sad * A quick hack to handle Intel's stacked bus port arrangement.
255ebf51109Sad */
256ebf51109Sad bptid = sc->sc_ii.ii_tid;
257ebf51109Sad nent = iop->sc_nlctent;
258ebf51109Sad for (le = iop->sc_lct->entry; nent != 0; nent--, le++)
259ebf51109Sad if ((le16toh(le->classid) & 4095) ==
260ebf51109Sad I2O_CLASS_BUS_ADAPTER_PORT &&
261ebf51109Sad (le32toh(le->usertid) & 4095) == bptid) {
26203e46dc8Sad bptid = le16toh(le->localtid) & 4095;
263ebf51109Sad break;
264ebf51109Sad }
265ebf51109Sad
266d49fd135Sad nent = iop->sc_nlctent;
267d49fd135Sad for (i = 0, le = iop->sc_lct->entry; i < nent; i++, le++) {
268ebf51109Sad if ((le16toh(le->classid) & 4095) != I2O_CLASS_SCSI_PERIPHERAL)
269d49fd135Sad continue;
270ebf51109Sad if (((le32toh(le->usertid) >> 12) & 4095) != bptid)
271d49fd135Sad continue;
27203e46dc8Sad tid = le16toh(le->localtid) & 4095;
273d49fd135Sad
2741d6329bbSad rv = iop_field_get_all(iop, tid, I2O_PARAM_SCSI_DEVICE_INFO,
2751d6329bbSad ¶m, sizeof(param), NULL);
2761d6329bbSad if (rv != 0)
277d49fd135Sad continue;
278d49fd135Sad targ = le32toh(param.sdi.identifier);
279d49fd135Sad lun = param.sdi.luninfo[1];
280ebf51109Sad #if defined(DIAGNOSTIC) || defined(I2ODEBUG)
281937a7a3eSbouyer if (targ >= sc_chan->chan_ntargets ||
282937a7a3eSbouyer lun >= sc_chan->chan_nluns) {
283cbab9cadSchs aprint_error_dev(sc->sc_dev, "target %d,%d (tid %d): "
2840e50a946Scegger "bad target/LUN\n", targ, lun, tid);
285d49fd135Sad continue;
286d49fd135Sad }
287ebf51109Sad #endif
288d49fd135Sad
289d49fd135Sad /*
290d49fd135Sad * If we've already described this target, and nothing has
291d49fd135Sad * changed, then don't describe it again.
292d49fd135Sad */
293d49fd135Sad it = &sc->sc_targetmap[targ];
294d49fd135Sad it->it_flags |= IT_PRESENT;
295d49fd135Sad syncrate = ((int)le64toh(param.sdi.negsyncrate) + 500) / 1000;
29657d787eeSad if (it->it_width != param.sdi.negdatawidth ||
29757d787eeSad it->it_offset != param.sdi.negoffset ||
29857d787eeSad it->it_syncrate != syncrate) {
299d49fd135Sad it->it_width = param.sdi.negdatawidth;
300d49fd135Sad it->it_offset = param.sdi.negoffset;
301d49fd135Sad it->it_syncrate = syncrate;
302d49fd135Sad
303cbab9cadSchs aprint_verbose_dev(sc->sc_dev, "target %d (tid %d): %d-bit, ",
3040e50a946Scegger targ, tid, it->it_width);
305d49fd135Sad if (it->it_syncrate == 0)
3068ef79919Sad aprint_verbose("asynchronous\n");
307d49fd135Sad else
3088ef79919Sad aprint_verbose("synchronous at %dMHz, "
3098ef79919Sad "offset 0x%x\n", it->it_syncrate,
3108ef79919Sad it->it_offset);
31157d787eeSad }
312ebf51109Sad
313ebf51109Sad /* Ignore the device if it's in use by somebody else. */
314ebf51109Sad if ((le32toh(le->usertid) & 4095) != I2O_TID_NONE) {
315ebf51109Sad if (sc->sc_tidmap == NULL ||
316ebf51109Sad IOPSP_TIDMAP(sc->sc_tidmap, targ, lun) !=
3178ef79919Sad IOPSP_TID_INUSE) {
318cbab9cadSchs aprint_verbose_dev(sc->sc_dev, "target %d,%d (tid %d): "
3190e50a946Scegger "in use by tid %d\n",
320ebf51109Sad targ, lun, tid,
321ebf51109Sad le32toh(le->usertid) & 4095);
3228ef79919Sad }
323ebf51109Sad IOPSP_TIDMAP(tidmap, targ, lun) = IOPSP_TID_INUSE;
324ebf51109Sad } else
325ebf51109Sad IOPSP_TIDMAP(tidmap, targ, lun) = (u_short)tid;
326d49fd135Sad }
327d49fd135Sad
328937a7a3eSbouyer for (i = 0; i < sc_chan->chan_ntargets; i++)
329d49fd135Sad if ((sc->sc_targetmap[i].it_flags & IT_PRESENT) == 0)
330d49fd135Sad sc->sc_targetmap[i].it_width = 0;
331d49fd135Sad
332d49fd135Sad /* Swap in the new map and return. */
333e5bca80aSad mutex_spin_enter(&iop->sc_intrlock);
334e5bca80aSad tofree = sc->sc_tidmap;
335d49fd135Sad sc->sc_tidmap = tidmap;
336e5bca80aSad mutex_spin_exit(&iop->sc_intrlock);
337e5bca80aSad
338e5bca80aSad if (tofree != NULL)
339e5bca80aSad free(tofree, M_DEVBUF);
340ebf51109Sad sc->sc_chgind = iop->sc_chgind;
341af9d4c4fSad return (0);
342d49fd135Sad }
343d49fd135Sad
344d49fd135Sad /*
345af9d4c4fSad * Re-scan the bus; to be called from a higher level (e.g. scsipi).
346d49fd135Sad */
347d49fd135Sad static int
iopsp_rescan(struct iopsp_softc * sc)348d49fd135Sad iopsp_rescan(struct iopsp_softc *sc)
349d49fd135Sad {
350d49fd135Sad struct iop_softc *iop;
351d49fd135Sad struct iop_msg *im;
352ebf51109Sad struct i2o_hba_bus_scan mf;
353d49fd135Sad int rv;
354d49fd135Sad
355cbab9cadSchs iop = device_private(device_parent(sc->sc_dev));
356d49fd135Sad
3579abeea58Sad mutex_enter(&iop->sc_conflock);
35803e46dc8Sad im = iop_msg_alloc(iop, IM_WAIT);
359d49fd135Sad
360ebf51109Sad mf.msgflags = I2O_MSGFLAGS(i2o_hba_bus_scan);
361ebf51109Sad mf.msgfunc = I2O_MSGFUNC(sc->sc_ii.ii_tid, I2O_HBA_BUS_SCAN);
362ebf51109Sad mf.msgictx = sc->sc_ii.ii_ictx;
363ebf51109Sad mf.msgtctx = im->im_tctx;
364d49fd135Sad
365ebf51109Sad rv = iop_msg_post(iop, im, &mf, 5*60*1000);
366ebf51109Sad iop_msg_free(iop, im);
367d49fd135Sad if (rv != 0)
368cbab9cadSchs aprint_error_dev(sc->sc_dev, "bus rescan failed (error %d)\n",
3690e50a946Scegger rv);
370af9d4c4fSad
37199187cddSad if ((rv = iop_lct_get(iop)) == 0)
372cbab9cadSchs rv = iopsp_reconfig(sc->sc_dev);
37399187cddSad
3749abeea58Sad mutex_exit(&iop->sc_conflock);
375d49fd135Sad return (rv);
376d49fd135Sad }
377d49fd135Sad
378d49fd135Sad /*
379d49fd135Sad * Start a SCSI command.
380d49fd135Sad */
381937a7a3eSbouyer static void
iopsp_scsipi_request(struct scsipi_channel * chan,scsipi_adapter_req_t req,void * arg)382937a7a3eSbouyer iopsp_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
383937a7a3eSbouyer void *arg)
384d49fd135Sad {
385937a7a3eSbouyer struct scsipi_xfer *xs;
386937a7a3eSbouyer struct scsipi_periph *periph;
387d49fd135Sad struct iopsp_softc *sc;
388d49fd135Sad struct iop_msg *im;
389d49fd135Sad struct iop_softc *iop;
390ebf51109Sad struct i2o_scsi_scb_exec *mf;
391434a4243Sthorpej int error, flags, tid;
392ebf51109Sad u_int32_t mb[IOP_MAX_MSG_SIZE / sizeof(u_int32_t)];
393d49fd135Sad
394cbab9cadSchs sc = device_private(chan->chan_adapter->adapt_dev);
395cbab9cadSchs iop = device_private(device_parent(sc->sc_dev));
396d49fd135Sad
397937a7a3eSbouyer switch (req) {
398937a7a3eSbouyer case ADAPTER_REQ_RUN_XFER:
399937a7a3eSbouyer xs = arg;
400937a7a3eSbouyer periph = xs->xs_periph;
401937a7a3eSbouyer flags = xs->xs_control;
402937a7a3eSbouyer
40357d787eeSad SC_DEBUG(periph, SCSIPI_DB2, ("iopsp_scsi_request run_xfer\n"));
40457d787eeSad
405937a7a3eSbouyer tid = IOPSP_TIDMAP(sc->sc_tidmap, periph->periph_target,
406937a7a3eSbouyer periph->periph_lun);
407d49fd135Sad if (tid == IOPSP_TID_ABSENT || tid == IOPSP_TID_INUSE) {
408d49fd135Sad xs->error = XS_SELTIMEOUT;
409937a7a3eSbouyer scsipi_done(xs);
410937a7a3eSbouyer return;
411d49fd135Sad }
412d49fd135Sad
413d49fd135Sad /* Need to reset the target? */
414d49fd135Sad if ((flags & XS_CTL_RESET) != 0) {
415d49fd135Sad if (iop_simple_cmd(iop, tid, I2O_SCSI_DEVICE_RESET,
41699187cddSad sc->sc_ii.ii_ictx, 1, 30*1000) != 0) {
417cbab9cadSchs aprint_error_dev(sc->sc_dev, "reset failed\n");
418d49fd135Sad xs->error = XS_DRIVER_STUFFUP;
419937a7a3eSbouyer } else
420937a7a3eSbouyer xs->error = XS_NOERROR;
421937a7a3eSbouyer
422937a7a3eSbouyer scsipi_done(xs);
423937a7a3eSbouyer return;
424d49fd135Sad }
425d49fd135Sad
426d49fd135Sad #if defined(I2ODEBUG) || defined(SCSIDEBUG)
427ebf51109Sad if (xs->cmdlen > sizeof(mf->cdb))
428cbab9cadSchs panic("%s: CDB too large", device_xname(sc->sc_dev));
429d49fd135Sad #endif
430d49fd135Sad
43103e46dc8Sad im = iop_msg_alloc(iop, IM_POLL_INTR |
432937a7a3eSbouyer IM_NOSTATUS | ((flags & XS_CTL_POLL) != 0 ? IM_POLL : 0));
433d49fd135Sad im->im_dvcontext = xs;
434d49fd135Sad
435ebf51109Sad mf = (struct i2o_scsi_scb_exec *)mb;
436ebf51109Sad mf->msgflags = I2O_MSGFLAGS(i2o_scsi_scb_exec);
437ebf51109Sad mf->msgfunc = I2O_MSGFUNC(tid, I2O_SCSI_SCB_EXEC);
438ebf51109Sad mf->msgictx = sc->sc_ii.ii_ictx;
439ebf51109Sad mf->msgtctx = im->im_tctx;
440ebf51109Sad mf->flags = xs->cmdlen | I2O_SCB_FLAG_ENABLE_DISCONNECT |
441d49fd135Sad I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE;
442ebf51109Sad mf->datalen = xs->datalen;
443ebf51109Sad memcpy(mf->cdb, xs->cmd, xs->cmdlen);
444d49fd135Sad
445937a7a3eSbouyer switch (xs->xs_tag_type) {
446937a7a3eSbouyer case MSG_ORDERED_Q_TAG:
447ebf51109Sad mf->flags |= I2O_SCB_FLAG_ORDERED_QUEUE_TAG;
448937a7a3eSbouyer break;
449937a7a3eSbouyer case MSG_SIMPLE_Q_TAG:
450ebf51109Sad mf->flags |= I2O_SCB_FLAG_SIMPLE_QUEUE_TAG;
451937a7a3eSbouyer break;
452937a7a3eSbouyer case MSG_HEAD_OF_Q_TAG:
453937a7a3eSbouyer mf->flags |= I2O_SCB_FLAG_HEAD_QUEUE_TAG;
454937a7a3eSbouyer break;
455937a7a3eSbouyer default:
456937a7a3eSbouyer break;
457d49fd135Sad }
458d49fd135Sad
459d49fd135Sad if (xs->datalen != 0) {
460937a7a3eSbouyer error = iop_msg_map_bio(iop, im, mb, xs->data,
461937a7a3eSbouyer xs->datalen, (flags & XS_CTL_DATA_OUT) == 0);
462d49fd135Sad if (error) {
463d49fd135Sad xs->error = XS_DRIVER_STUFFUP;
464ebf51109Sad iop_msg_free(iop, im);
465937a7a3eSbouyer scsipi_done(xs);
466937a7a3eSbouyer return;
467d49fd135Sad }
468d49fd135Sad if ((flags & XS_CTL_DATA_IN) == 0)
469ebf51109Sad mf->flags |= I2O_SCB_FLAG_XFER_TO_DEVICE;
470d49fd135Sad else
471ebf51109Sad mf->flags |= I2O_SCB_FLAG_XFER_FROM_DEVICE;
472d49fd135Sad }
473d49fd135Sad
47499187cddSad if (iop_msg_post(iop, im, mb, xs->timeout)) {
475ebf51109Sad if (xs->datalen != 0)
476ebf51109Sad iop_msg_unmap(iop, im);
477ebf51109Sad iop_msg_free(iop, im);
478ebf51109Sad xs->error = XS_DRIVER_STUFFUP;
479937a7a3eSbouyer scsipi_done(xs);
480ebf51109Sad }
481937a7a3eSbouyer break;
48299187cddSad
483937a7a3eSbouyer case ADAPTER_REQ_GROW_RESOURCES:
484937a7a3eSbouyer /*
485937a7a3eSbouyer * Not supported.
486937a7a3eSbouyer */
487937a7a3eSbouyer break;
488937a7a3eSbouyer
489937a7a3eSbouyer case ADAPTER_REQ_SET_XFER_MODE:
490937a7a3eSbouyer /*
491937a7a3eSbouyer * The DDM takes care of this, and we can't modify its
492937a7a3eSbouyer * behaviour.
493937a7a3eSbouyer */
494937a7a3eSbouyer break;
495937a7a3eSbouyer }
496d49fd135Sad }
497d49fd135Sad
49899187cddSad #ifdef notyet
499d49fd135Sad /*
500d49fd135Sad * Abort the specified I2O_SCSI_SCB_EXEC message and its associated SCB.
501d49fd135Sad */
502d49fd135Sad static int
iopsp_scsi_abort(struct iopsp_softc * sc,int atid,struct iop_msg * aim)503af9d4c4fSad iopsp_scsi_abort(struct iopsp_softc *sc, int atid, struct iop_msg *aim)
504d49fd135Sad {
505d49fd135Sad struct iop_msg *im;
506ebf51109Sad struct i2o_scsi_scb_abort mf;
507d49fd135Sad struct iop_softc *iop;
508ebf51109Sad int rv, s;
509d49fd135Sad
510cbab9cadSchs iop = device_private(device_parent(sc->sc_dev));
51103e46dc8Sad im = iop_msg_alloc(iop, IM_POLL);
512d49fd135Sad
513ebf51109Sad mf.msgflags = I2O_MSGFLAGS(i2o_scsi_scb_abort);
514ebf51109Sad mf.msgfunc = I2O_MSGFUNC(atid, I2O_SCSI_SCB_ABORT);
515ebf51109Sad mf.msgictx = sc->sc_ii.ii_ictx;
516ebf51109Sad mf.msgtctx = im->im_tctx;
517ebf51109Sad mf.tctxabort = aim->im_tctx;
518d49fd135Sad
519ebf51109Sad rv = iop_msg_post(iop, im, &mf, 30000);
520ebf51109Sad iop_msg_free(iop, im);
521e5bca80aSad
522d49fd135Sad return (rv);
523d49fd135Sad }
52499187cddSad #endif
525d49fd135Sad
526d49fd135Sad /*
527d49fd135Sad * We have a message which has been processed and replied to by the IOP -
528d49fd135Sad * deal with it.
529d49fd135Sad */
530d49fd135Sad static void
iopsp_intr(device_t dv,struct iop_msg * im,void * reply)531529e91fcScegger iopsp_intr(device_t dv, struct iop_msg *im, void *reply)
532d49fd135Sad {
533d49fd135Sad struct scsipi_xfer *xs;
534d49fd135Sad struct iopsp_softc *sc;
535d49fd135Sad struct i2o_scsi_reply *rb;
536d49fd135Sad struct iop_softc *iop;
537ebf51109Sad u_int sl;
538d49fd135Sad
539cbab9cadSchs sc = device_private(dv);
540cbab9cadSchs xs = im->im_dvcontext;
541cbab9cadSchs iop = device_private(device_parent(dv));
54299187cddSad rb = reply;
543d49fd135Sad
544f47d097cSbouyer SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("iopsp_intr\n"));
545d49fd135Sad
546ebf51109Sad if ((rb->msgflags & I2O_MSGFLAGS_FAIL) != 0) {
547ebf51109Sad xs->error = XS_DRIVER_STUFFUP;
548ebf51109Sad xs->resid = xs->datalen;
549ebf51109Sad } else {
550ebf51109Sad if (rb->hbastatus != I2O_SCSI_DSC_SUCCESS) {
551ebf51109Sad switch (rb->hbastatus) {
552d49fd135Sad case I2O_SCSI_DSC_ADAPTER_BUSY:
553d49fd135Sad case I2O_SCSI_DSC_SCSI_BUS_RESET:
554d49fd135Sad case I2O_SCSI_DSC_BUS_BUSY:
555d49fd135Sad xs->error = XS_BUSY;
556d49fd135Sad break;
557d49fd135Sad case I2O_SCSI_DSC_SELECTION_TIMEOUT:
558d49fd135Sad xs->error = XS_SELTIMEOUT;
559d49fd135Sad break;
560d49fd135Sad case I2O_SCSI_DSC_COMMAND_TIMEOUT:
561d49fd135Sad case I2O_SCSI_DSC_DEVICE_NOT_PRESENT:
562d49fd135Sad case I2O_SCSI_DSC_LUN_INVALID:
563d49fd135Sad case I2O_SCSI_DSC_SCSI_TID_INVALID:
564d49fd135Sad xs->error = XS_TIMEOUT;
565d49fd135Sad break;
566d49fd135Sad default:
567d49fd135Sad xs->error = XS_DRIVER_STUFFUP;
568d49fd135Sad break;
569d49fd135Sad }
570cbab9cadSchs aprint_error_dev(sc->sc_dev, "HBA status 0x%02x\n",
5710e50a946Scegger rb->hbastatus);
572ebf51109Sad } else if (rb->scsistatus != SCSI_OK) {
573ebf51109Sad switch (rb->scsistatus) {
574d49fd135Sad case SCSI_CHECK:
575d49fd135Sad xs->error = XS_SENSE;
576d49fd135Sad sl = le32toh(rb->senselen);
577d49fd135Sad if (sl > sizeof(xs->sense.scsi_sense))
578ebf51109Sad sl = sizeof(xs->sense.scsi_sense);
579d49fd135Sad memcpy(&xs->sense.scsi_sense, rb->sense, sl);
580d49fd135Sad break;
581937a7a3eSbouyer case SCSI_QUEUE_FULL:
582d49fd135Sad case SCSI_BUSY:
583d49fd135Sad xs->error = XS_BUSY;
584d49fd135Sad break;
585d49fd135Sad default:
586d49fd135Sad xs->error = XS_DRIVER_STUFFUP;
587d49fd135Sad break;
588d49fd135Sad }
589d49fd135Sad } else
590d49fd135Sad xs->error = XS_NOERROR;
591d49fd135Sad
592ebf51109Sad xs->resid = xs->datalen - le32toh(rb->datalen);
593ebf51109Sad xs->status = rb->scsistatus;
594d49fd135Sad }
595d49fd135Sad
596d49fd135Sad /* Free the message wrapper and pass the news to scsipi. */
597ebf51109Sad if (xs->datalen != 0)
598d49fd135Sad iop_msg_unmap(iop, im);
599ebf51109Sad iop_msg_free(iop, im);
600937a7a3eSbouyer
601d49fd135Sad scsipi_done(xs);
602d49fd135Sad }
603d49fd135Sad
604d49fd135Sad /*
605d49fd135Sad * ioctl hook; used here only to initiate low-level rescans.
606d49fd135Sad */
607d49fd135Sad static int
iopsp_ioctl(struct scsipi_channel * chan,u_long cmd,void * data,int flag,struct proc * p)60853524e44Schristos iopsp_ioctl(struct scsipi_channel *chan, u_long cmd, void *data,
609168cd830Schristos int flag, struct proc *p)
610d49fd135Sad {
611d49fd135Sad int rv;
612d49fd135Sad
613d49fd135Sad switch (cmd) {
614d49fd135Sad case SCBUSIOLLSCAN:
615ebf51109Sad /*
616ebf51109Sad * If it's boot time, the bus will have been scanned and the
617ebf51109Sad * maps built. Locking would stop re-configuration, but we
618ebf51109Sad * want to fake success.
619ebf51109Sad */
620e5bca80aSad if (curlwp != &lwp0)
621937a7a3eSbouyer rv = iopsp_rescan(
622cbab9cadSchs device_private(chan->chan_adapter->adapt_dev));
623ebf51109Sad else
624ebf51109Sad rv = 0;
625d49fd135Sad break;
626ebf51109Sad
627d49fd135Sad default:
628ebf51109Sad rv = ENOTTY;
629d49fd135Sad break;
630d49fd135Sad }
631d49fd135Sad
632d49fd135Sad return (rv);
633d49fd135Sad }
634ebf51109Sad
635ebf51109Sad /*
636ebf51109Sad * The number of openings available to us has changed, so inform scsipi.
637ebf51109Sad */
638ebf51109Sad static void
iopsp_adjqparam(device_t dv,int mpi)639529e91fcScegger iopsp_adjqparam(device_t dv, int mpi)
640ebf51109Sad {
641937a7a3eSbouyer struct iopsp_softc *sc;
642e5bca80aSad struct iop_softc *iop;
643ebf51109Sad
644e5bca80aSad sc = device_private(dv);
645e5bca80aSad iop = device_private(device_parent(dv));
646937a7a3eSbouyer
647e5bca80aSad mutex_spin_enter(&iop->sc_intrlock);
648434a4243Sthorpej sc->sc_adapter.adapt_openings += mpi - sc->sc_openings;
649434a4243Sthorpej sc->sc_openings = mpi;
650e5bca80aSad mutex_spin_exit(&iop->sc_intrlock);
651ebf51109Sad }
652