1 | /* $NetBSD: iopsp.c,v 1.37 2015/08/16 19:22:33 msaitoh Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2000, 2001, 2007 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Andrew Doran. |
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 | * Raw SCSI device support for I2O. IOPs present SCSI devices individually; |
34 | * we group them by controlling port. |
35 | */ |
36 | |
37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: iopsp.c,v 1.37 2015/08/16 19:22:33 msaitoh Exp $" ); |
39 | |
40 | #include <sys/param.h> |
41 | #include <sys/systm.h> |
42 | #include <sys/kernel.h> |
43 | #include <sys/device.h> |
44 | #include <sys/queue.h> |
45 | #include <sys/proc.h> |
46 | #include <sys/buf.h> |
47 | #include <sys/endian.h> |
48 | #include <sys/malloc.h> |
49 | #include <sys/scsiio.h> |
50 | |
51 | #include <sys/bswap.h> |
52 | #include <sys/bus.h> |
53 | |
54 | #include <dev/scsipi/scsi_all.h> |
55 | #include <dev/scsipi/scsi_disk.h> |
56 | #include <dev/scsipi/scsipi_all.h> |
57 | #include <dev/scsipi/scsiconf.h> |
58 | #include <dev/scsipi/scsi_message.h> |
59 | |
60 | #include <dev/i2o/i2o.h> |
61 | #include <dev/i2o/iopio.h> |
62 | #include <dev/i2o/iopvar.h> |
63 | #include <dev/i2o/iopspvar.h> |
64 | |
65 | static void iopsp_adjqparam(device_t, int); |
66 | static void iopsp_attach(device_t, device_t, void *); |
67 | static void iopsp_intr(device_t, struct iop_msg *, void *); |
68 | static int iopsp_ioctl(struct scsipi_channel *, u_long, |
69 | void *, int, struct proc *); |
70 | static int iopsp_match(device_t, cfdata_t, void *); |
71 | static int iopsp_rescan(struct iopsp_softc *); |
72 | static int iopsp_reconfig(device_t); |
73 | static void iopsp_scsipi_request(struct scsipi_channel *, |
74 | scsipi_adapter_req_t, void *); |
75 | |
76 | CFATTACH_DECL_NEW(iopsp, sizeof(struct iopsp_softc), |
77 | iopsp_match, iopsp_attach, NULL, NULL); |
78 | |
79 | /* |
80 | * Match a supported device. |
81 | */ |
82 | static int |
83 | iopsp_match(device_t parent, cfdata_t match, void *aux) |
84 | { |
85 | struct iop_attach_args *ia; |
86 | struct iop_softc *iop; |
87 | struct { |
88 | struct i2o_param_op_results pr; |
89 | struct i2o_param_read_results prr; |
90 | struct i2o_param_hba_ctlr_info ci; |
91 | } __packed param; |
92 | |
93 | ia = aux; |
94 | iop = device_private(parent); |
95 | |
96 | if (ia->ia_class != I2O_CLASS_BUS_ADAPTER_PORT) |
97 | return (0); |
98 | |
99 | if (iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO, ¶m, |
100 | sizeof(param), NULL) != 0) |
101 | return (0); |
102 | |
103 | return (param.ci.bustype == I2O_HBA_BUS_SCSI || |
104 | param.ci.bustype == I2O_HBA_BUS_FCA); |
105 | } |
106 | |
107 | /* |
108 | * Attach a supported device. |
109 | */ |
110 | static void |
111 | iopsp_attach(device_t parent, device_t self, void *aux) |
112 | { |
113 | struct iop_attach_args *ia; |
114 | struct iopsp_softc *sc; |
115 | struct iop_softc *iop; |
116 | struct { |
117 | struct i2o_param_op_results pr; |
118 | struct i2o_param_read_results prr; |
119 | union { |
120 | struct i2o_param_hba_ctlr_info ci; |
121 | struct i2o_param_hba_scsi_ctlr_info sci; |
122 | struct i2o_param_hba_scsi_port_info spi; |
123 | } p; |
124 | } __packed param; |
125 | int fc, rv; |
126 | int size; |
127 | |
128 | ia = (struct iop_attach_args *)aux; |
129 | sc = device_private(self); |
130 | iop = device_private(parent); |
131 | |
132 | /* Register us as an initiator. */ |
133 | sc->sc_ii.ii_dv = self; |
134 | sc->sc_ii.ii_intr = iopsp_intr; |
135 | sc->sc_ii.ii_flags = 0; |
136 | sc->sc_ii.ii_tid = ia->ia_tid; |
137 | sc->sc_ii.ii_reconfig = iopsp_reconfig; |
138 | sc->sc_ii.ii_adjqparam = iopsp_adjqparam; |
139 | iop_initiator_register(iop, &sc->sc_ii); |
140 | |
141 | rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_CTLR_INFO, |
142 | ¶m, sizeof(param), NULL); |
143 | if (rv != 0) |
144 | goto bad; |
145 | |
146 | fc = (param.p.ci.bustype == I2O_HBA_BUS_FCA); |
147 | |
148 | /* |
149 | * Say what the device is. If we can find out what the controling |
150 | * device is, say what that is too. |
151 | */ |
152 | aprint_normal(": SCSI port" ); |
153 | iop_print_ident(iop, ia->ia_tid); |
154 | aprint_normal("\n" ); |
155 | |
156 | rv = iop_field_get_all(iop, ia->ia_tid, I2O_PARAM_HBA_SCSI_CTLR_INFO, |
157 | ¶m, sizeof(param), NULL); |
158 | if (rv != 0) |
159 | goto bad; |
160 | |
161 | aprint_normal_dev(sc->sc_dev, "" ); |
162 | if (fc) |
163 | aprint_normal("FC" ); |
164 | else |
165 | aprint_normal("%d-bit" , param.p.sci.maxdatawidth); |
166 | aprint_normal(", max sync rate %dMHz, initiator ID %d\n" , |
167 | (u_int32_t)le64toh(param.p.sci.maxsyncrate) / 1000, |
168 | le32toh(param.p.sci.initiatorid)); |
169 | |
170 | sc->sc_openings = 1; |
171 | |
172 | sc->sc_adapter.adapt_dev = sc->sc_dev; |
173 | sc->sc_adapter.adapt_nchannels = 1; |
174 | sc->sc_adapter.adapt_openings = 1; |
175 | sc->sc_adapter.adapt_max_periph = 1; |
176 | sc->sc_adapter.adapt_ioctl = iopsp_ioctl; |
177 | sc->sc_adapter.adapt_minphys = minphys; |
178 | sc->sc_adapter.adapt_request = iopsp_scsipi_request; |
179 | |
180 | memset(&sc->sc_channel, 0, sizeof(sc->sc_channel)); |
181 | sc->sc_channel.chan_adapter = &sc->sc_adapter; |
182 | sc->sc_channel.chan_bustype = &scsi_bustype; |
183 | sc->sc_channel.chan_channel = 0; |
184 | sc->sc_channel.chan_ntargets = fc ? |
185 | IOPSP_MAX_FC_TARGET : param.p.sci.maxdatawidth; |
186 | sc->sc_channel.chan_nluns = IOPSP_MAX_LUN; |
187 | sc->sc_channel.chan_id = le32toh(param.p.sci.initiatorid); |
188 | sc->sc_channel.chan_flags = SCSIPI_CHAN_NOSETTLE; |
189 | |
190 | /* |
191 | * Allocate the target map. Currently used for informational |
192 | * purposes only. |
193 | */ |
194 | size = sc->sc_channel.chan_ntargets * sizeof(struct iopsp_target); |
195 | sc->sc_targetmap = malloc(size, M_DEVBUF, M_NOWAIT|M_ZERO); |
196 | |
197 | /* Build the two maps, and attach to scsipi. */ |
198 | if (iopsp_reconfig(self) != 0) { |
199 | aprint_error_dev(sc->sc_dev, "configure failed\n" ); |
200 | goto bad; |
201 | } |
202 | config_found(self, &sc->sc_channel, scsiprint); |
203 | return; |
204 | |
205 | bad: |
206 | iop_initiator_unregister(iop, &sc->sc_ii); |
207 | } |
208 | |
209 | /* |
210 | * Scan the LCT to determine which devices we control, and enter them into |
211 | * the maps. |
212 | */ |
213 | static int |
214 | iopsp_reconfig(device_t dv) |
215 | { |
216 | struct iopsp_softc *sc; |
217 | struct iop_softc *iop; |
218 | struct i2o_lct_entry *le; |
219 | struct scsipi_channel *sc_chan; |
220 | struct { |
221 | struct i2o_param_op_results pr; |
222 | struct i2o_param_read_results prr; |
223 | struct i2o_param_scsi_device_info sdi; |
224 | } __packed param; |
225 | u_int tid, nent, i, targ, lun, size, rv, bptid; |
226 | u_short *tidmap; |
227 | void *tofree; |
228 | struct iopsp_target *it; |
229 | int syncrate; |
230 | |
231 | sc = device_private(dv); |
232 | iop = device_private(device_parent(sc->sc_dev)); |
233 | sc_chan = &sc->sc_channel; |
234 | |
235 | KASSERT(mutex_owned(&iop->sc_conflock)); |
236 | |
237 | /* Anything to do? */ |
238 | if (iop->sc_chgind == sc->sc_chgind) |
239 | return (0); |
240 | |
241 | /* |
242 | * Allocate memory for the target/LUN -> TID map. Use zero to |
243 | * denote absent targets (zero is the TID of the I2O executive, |
244 | * and we never address that here). |
245 | */ |
246 | size = sc_chan->chan_ntargets * (IOPSP_MAX_LUN) * sizeof(u_short); |
247 | if ((tidmap = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO)) == NULL) |
248 | return (ENOMEM); |
249 | |
250 | for (i = 0; i < sc_chan->chan_ntargets; i++) |
251 | sc->sc_targetmap[i].it_flags &= ~IT_PRESENT; |
252 | |
253 | /* |
254 | * A quick hack to handle Intel's stacked bus port arrangement. |
255 | */ |
256 | bptid = sc->sc_ii.ii_tid; |
257 | nent = iop->sc_nlctent; |
258 | for (le = iop->sc_lct->entry; nent != 0; nent--, le++) |
259 | if ((le16toh(le->classid) & 4095) == |
260 | I2O_CLASS_BUS_ADAPTER_PORT && |
261 | (le32toh(le->usertid) & 4095) == bptid) { |
262 | bptid = le16toh(le->localtid) & 4095; |
263 | break; |
264 | } |
265 | |
266 | nent = iop->sc_nlctent; |
267 | for (i = 0, le = iop->sc_lct->entry; i < nent; i++, le++) { |
268 | if ((le16toh(le->classid) & 4095) != I2O_CLASS_SCSI_PERIPHERAL) |
269 | continue; |
270 | if (((le32toh(le->usertid) >> 12) & 4095) != bptid) |
271 | continue; |
272 | tid = le16toh(le->localtid) & 4095; |
273 | |
274 | rv = iop_field_get_all(iop, tid, I2O_PARAM_SCSI_DEVICE_INFO, |
275 | ¶m, sizeof(param), NULL); |
276 | if (rv != 0) |
277 | continue; |
278 | targ = le32toh(param.sdi.identifier); |
279 | lun = param.sdi.luninfo[1]; |
280 | #if defined(DIAGNOSTIC) || defined(I2ODEBUG) |
281 | if (targ >= sc_chan->chan_ntargets || |
282 | lun >= sc_chan->chan_nluns) { |
283 | aprint_error_dev(sc->sc_dev, "target %d,%d (tid %d): " |
284 | "bad target/LUN\n" , targ, lun, tid); |
285 | continue; |
286 | } |
287 | #endif |
288 | |
289 | /* |
290 | * If we've already described this target, and nothing has |
291 | * changed, then don't describe it again. |
292 | */ |
293 | it = &sc->sc_targetmap[targ]; |
294 | it->it_flags |= IT_PRESENT; |
295 | syncrate = ((int)le64toh(param.sdi.negsyncrate) + 500) / 1000; |
296 | if (it->it_width != param.sdi.negdatawidth || |
297 | it->it_offset != param.sdi.negoffset || |
298 | it->it_syncrate != syncrate) { |
299 | it->it_width = param.sdi.negdatawidth; |
300 | it->it_offset = param.sdi.negoffset; |
301 | it->it_syncrate = syncrate; |
302 | |
303 | aprint_verbose_dev(sc->sc_dev, "target %d (tid %d): %d-bit, " , |
304 | targ, tid, it->it_width); |
305 | if (it->it_syncrate == 0) |
306 | aprint_verbose("asynchronous\n" ); |
307 | else |
308 | aprint_verbose("synchronous at %dMHz, " |
309 | "offset 0x%x\n" , it->it_syncrate, |
310 | it->it_offset); |
311 | } |
312 | |
313 | /* Ignore the device if it's in use by somebody else. */ |
314 | if ((le32toh(le->usertid) & 4095) != I2O_TID_NONE) { |
315 | if (sc->sc_tidmap == NULL || |
316 | IOPSP_TIDMAP(sc->sc_tidmap, targ, lun) != |
317 | IOPSP_TID_INUSE) { |
318 | aprint_verbose_dev(sc->sc_dev, "target %d,%d (tid %d): " |
319 | "in use by tid %d\n" , |
320 | targ, lun, tid, |
321 | le32toh(le->usertid) & 4095); |
322 | } |
323 | IOPSP_TIDMAP(tidmap, targ, lun) = IOPSP_TID_INUSE; |
324 | } else |
325 | IOPSP_TIDMAP(tidmap, targ, lun) = (u_short)tid; |
326 | } |
327 | |
328 | for (i = 0; i < sc_chan->chan_ntargets; i++) |
329 | if ((sc->sc_targetmap[i].it_flags & IT_PRESENT) == 0) |
330 | sc->sc_targetmap[i].it_width = 0; |
331 | |
332 | /* Swap in the new map and return. */ |
333 | mutex_spin_enter(&iop->sc_intrlock); |
334 | tofree = sc->sc_tidmap; |
335 | sc->sc_tidmap = tidmap; |
336 | mutex_spin_exit(&iop->sc_intrlock); |
337 | |
338 | if (tofree != NULL) |
339 | free(tofree, M_DEVBUF); |
340 | sc->sc_chgind = iop->sc_chgind; |
341 | return (0); |
342 | } |
343 | |
344 | /* |
345 | * Re-scan the bus; to be called from a higher level (e.g. scsipi). |
346 | */ |
347 | static int |
348 | iopsp_rescan(struct iopsp_softc *sc) |
349 | { |
350 | struct iop_softc *iop; |
351 | struct iop_msg *im; |
352 | struct i2o_hba_bus_scan mf; |
353 | int rv; |
354 | |
355 | iop = device_private(device_parent(sc->sc_dev)); |
356 | |
357 | mutex_enter(&iop->sc_conflock); |
358 | im = iop_msg_alloc(iop, IM_WAIT); |
359 | |
360 | mf.msgflags = I2O_MSGFLAGS(i2o_hba_bus_scan); |
361 | mf.msgfunc = I2O_MSGFUNC(sc->sc_ii.ii_tid, I2O_HBA_BUS_SCAN); |
362 | mf.msgictx = sc->sc_ii.ii_ictx; |
363 | mf.msgtctx = im->im_tctx; |
364 | |
365 | rv = iop_msg_post(iop, im, &mf, 5*60*1000); |
366 | iop_msg_free(iop, im); |
367 | if (rv != 0) |
368 | aprint_error_dev(sc->sc_dev, "bus rescan failed (error %d)\n" , |
369 | rv); |
370 | |
371 | if ((rv = iop_lct_get(iop)) == 0) |
372 | rv = iopsp_reconfig(sc->sc_dev); |
373 | |
374 | mutex_exit(&iop->sc_conflock); |
375 | return (rv); |
376 | } |
377 | |
378 | /* |
379 | * Start a SCSI command. |
380 | */ |
381 | static void |
382 | iopsp_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req, |
383 | void *arg) |
384 | { |
385 | struct scsipi_xfer *xs; |
386 | struct scsipi_periph *periph; |
387 | struct iopsp_softc *sc; |
388 | struct iop_msg *im; |
389 | struct iop_softc *iop; |
390 | struct i2o_scsi_scb_exec *mf; |
391 | int error, flags, tid; |
392 | u_int32_t mb[IOP_MAX_MSG_SIZE / sizeof(u_int32_t)]; |
393 | |
394 | sc = device_private(chan->chan_adapter->adapt_dev); |
395 | iop = device_private(device_parent(sc->sc_dev)); |
396 | |
397 | switch (req) { |
398 | case ADAPTER_REQ_RUN_XFER: |
399 | xs = arg; |
400 | periph = xs->xs_periph; |
401 | flags = xs->xs_control; |
402 | |
403 | SC_DEBUG(periph, SCSIPI_DB2, ("iopsp_scsi_request run_xfer\n" )); |
404 | |
405 | tid = IOPSP_TIDMAP(sc->sc_tidmap, periph->periph_target, |
406 | periph->periph_lun); |
407 | if (tid == IOPSP_TID_ABSENT || tid == IOPSP_TID_INUSE) { |
408 | xs->error = XS_SELTIMEOUT; |
409 | scsipi_done(xs); |
410 | return; |
411 | } |
412 | |
413 | /* Need to reset the target? */ |
414 | if ((flags & XS_CTL_RESET) != 0) { |
415 | if (iop_simple_cmd(iop, tid, I2O_SCSI_DEVICE_RESET, |
416 | sc->sc_ii.ii_ictx, 1, 30*1000) != 0) { |
417 | aprint_error_dev(sc->sc_dev, "reset failed\n" ); |
418 | xs->error = XS_DRIVER_STUFFUP; |
419 | } else |
420 | xs->error = XS_NOERROR; |
421 | |
422 | scsipi_done(xs); |
423 | return; |
424 | } |
425 | |
426 | #if defined(I2ODEBUG) || defined(SCSIDEBUG) |
427 | if (xs->cmdlen > sizeof(mf->cdb)) |
428 | panic("%s: CDB too large" , device_xname(sc->sc_dev)); |
429 | #endif |
430 | |
431 | im = iop_msg_alloc(iop, IM_POLL_INTR | |
432 | IM_NOSTATUS | ((flags & XS_CTL_POLL) != 0 ? IM_POLL : 0)); |
433 | im->im_dvcontext = xs; |
434 | |
435 | mf = (struct i2o_scsi_scb_exec *)mb; |
436 | mf->msgflags = I2O_MSGFLAGS(i2o_scsi_scb_exec); |
437 | mf->msgfunc = I2O_MSGFUNC(tid, I2O_SCSI_SCB_EXEC); |
438 | mf->msgictx = sc->sc_ii.ii_ictx; |
439 | mf->msgtctx = im->im_tctx; |
440 | mf->flags = xs->cmdlen | I2O_SCB_FLAG_ENABLE_DISCONNECT | |
441 | I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE; |
442 | mf->datalen = xs->datalen; |
443 | memcpy(mf->cdb, xs->cmd, xs->cmdlen); |
444 | |
445 | switch (xs->xs_tag_type) { |
446 | case MSG_ORDERED_Q_TAG: |
447 | mf->flags |= I2O_SCB_FLAG_ORDERED_QUEUE_TAG; |
448 | break; |
449 | case MSG_SIMPLE_Q_TAG: |
450 | mf->flags |= I2O_SCB_FLAG_SIMPLE_QUEUE_TAG; |
451 | break; |
452 | case MSG_HEAD_OF_Q_TAG: |
453 | mf->flags |= I2O_SCB_FLAG_HEAD_QUEUE_TAG; |
454 | break; |
455 | default: |
456 | break; |
457 | } |
458 | |
459 | if (xs->datalen != 0) { |
460 | error = iop_msg_map_bio(iop, im, mb, xs->data, |
461 | xs->datalen, (flags & XS_CTL_DATA_OUT) == 0); |
462 | if (error) { |
463 | xs->error = XS_DRIVER_STUFFUP; |
464 | iop_msg_free(iop, im); |
465 | scsipi_done(xs); |
466 | return; |
467 | } |
468 | if ((flags & XS_CTL_DATA_IN) == 0) |
469 | mf->flags |= I2O_SCB_FLAG_XFER_TO_DEVICE; |
470 | else |
471 | mf->flags |= I2O_SCB_FLAG_XFER_FROM_DEVICE; |
472 | } |
473 | |
474 | if (iop_msg_post(iop, im, mb, xs->timeout)) { |
475 | if (xs->datalen != 0) |
476 | iop_msg_unmap(iop, im); |
477 | iop_msg_free(iop, im); |
478 | xs->error = XS_DRIVER_STUFFUP; |
479 | scsipi_done(xs); |
480 | } |
481 | break; |
482 | |
483 | case ADAPTER_REQ_GROW_RESOURCES: |
484 | /* |
485 | * Not supported. |
486 | */ |
487 | break; |
488 | |
489 | case ADAPTER_REQ_SET_XFER_MODE: |
490 | /* |
491 | * The DDM takes care of this, and we can't modify its |
492 | * behaviour. |
493 | */ |
494 | break; |
495 | } |
496 | } |
497 | |
498 | #ifdef notyet |
499 | /* |
500 | * Abort the specified I2O_SCSI_SCB_EXEC message and its associated SCB. |
501 | */ |
502 | static int |
503 | iopsp_scsi_abort(struct iopsp_softc *sc, int atid, struct iop_msg *aim) |
504 | { |
505 | struct iop_msg *im; |
506 | struct i2o_scsi_scb_abort mf; |
507 | struct iop_softc *iop; |
508 | int rv, s; |
509 | |
510 | iop = device_private(device_parent(sc->sc_dev)); |
511 | im = iop_msg_alloc(iop, IM_POLL); |
512 | |
513 | mf.msgflags = I2O_MSGFLAGS(i2o_scsi_scb_abort); |
514 | mf.msgfunc = I2O_MSGFUNC(atid, I2O_SCSI_SCB_ABORT); |
515 | mf.msgictx = sc->sc_ii.ii_ictx; |
516 | mf.msgtctx = im->im_tctx; |
517 | mf.tctxabort = aim->im_tctx; |
518 | |
519 | rv = iop_msg_post(iop, im, &mf, 30000); |
520 | iop_msg_free(iop, im); |
521 | |
522 | return (rv); |
523 | } |
524 | #endif |
525 | |
526 | /* |
527 | * We have a message which has been processed and replied to by the IOP - |
528 | * deal with it. |
529 | */ |
530 | static void |
531 | iopsp_intr(device_t dv, struct iop_msg *im, void *reply) |
532 | { |
533 | struct scsipi_xfer *xs; |
534 | struct iopsp_softc *sc; |
535 | struct i2o_scsi_reply *rb; |
536 | struct iop_softc *iop; |
537 | u_int sl; |
538 | |
539 | sc = device_private(dv); |
540 | xs = im->im_dvcontext; |
541 | iop = device_private(device_parent(dv)); |
542 | rb = reply; |
543 | |
544 | SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("iopsp_intr\n" )); |
545 | |
546 | if ((rb->msgflags & I2O_MSGFLAGS_FAIL) != 0) { |
547 | xs->error = XS_DRIVER_STUFFUP; |
548 | xs->resid = xs->datalen; |
549 | } else { |
550 | if (rb->hbastatus != I2O_SCSI_DSC_SUCCESS) { |
551 | switch (rb->hbastatus) { |
552 | case I2O_SCSI_DSC_ADAPTER_BUSY: |
553 | case I2O_SCSI_DSC_SCSI_BUS_RESET: |
554 | case I2O_SCSI_DSC_BUS_BUSY: |
555 | xs->error = XS_BUSY; |
556 | break; |
557 | case I2O_SCSI_DSC_SELECTION_TIMEOUT: |
558 | xs->error = XS_SELTIMEOUT; |
559 | break; |
560 | case I2O_SCSI_DSC_COMMAND_TIMEOUT: |
561 | case I2O_SCSI_DSC_DEVICE_NOT_PRESENT: |
562 | case I2O_SCSI_DSC_LUN_INVALID: |
563 | case I2O_SCSI_DSC_SCSI_TID_INVALID: |
564 | xs->error = XS_TIMEOUT; |
565 | break; |
566 | default: |
567 | xs->error = XS_DRIVER_STUFFUP; |
568 | break; |
569 | } |
570 | aprint_error_dev(sc->sc_dev, "HBA status 0x%02x\n" , |
571 | rb->hbastatus); |
572 | } else if (rb->scsistatus != SCSI_OK) { |
573 | switch (rb->scsistatus) { |
574 | case SCSI_CHECK: |
575 | xs->error = XS_SENSE; |
576 | sl = le32toh(rb->senselen); |
577 | if (sl > sizeof(xs->sense.scsi_sense)) |
578 | sl = sizeof(xs->sense.scsi_sense); |
579 | memcpy(&xs->sense.scsi_sense, rb->sense, sl); |
580 | break; |
581 | case SCSI_QUEUE_FULL: |
582 | case SCSI_BUSY: |
583 | xs->error = XS_BUSY; |
584 | break; |
585 | default: |
586 | xs->error = XS_DRIVER_STUFFUP; |
587 | break; |
588 | } |
589 | } else |
590 | xs->error = XS_NOERROR; |
591 | |
592 | xs->resid = xs->datalen - le32toh(rb->datalen); |
593 | xs->status = rb->scsistatus; |
594 | } |
595 | |
596 | /* Free the message wrapper and pass the news to scsipi. */ |
597 | if (xs->datalen != 0) |
598 | iop_msg_unmap(iop, im); |
599 | iop_msg_free(iop, im); |
600 | |
601 | scsipi_done(xs); |
602 | } |
603 | |
604 | /* |
605 | * ioctl hook; used here only to initiate low-level rescans. |
606 | */ |
607 | static int |
608 | iopsp_ioctl(struct scsipi_channel *chan, u_long cmd, void *data, |
609 | int flag, struct proc *p) |
610 | { |
611 | int rv; |
612 | |
613 | switch (cmd) { |
614 | case SCBUSIOLLSCAN: |
615 | /* |
616 | * If it's boot time, the bus will have been scanned and the |
617 | * maps built. Locking would stop re-configuration, but we |
618 | * want to fake success. |
619 | */ |
620 | if (curlwp != &lwp0) |
621 | rv = iopsp_rescan( |
622 | device_private(chan->chan_adapter->adapt_dev)); |
623 | else |
624 | rv = 0; |
625 | break; |
626 | |
627 | default: |
628 | rv = ENOTTY; |
629 | break; |
630 | } |
631 | |
632 | return (rv); |
633 | } |
634 | |
635 | /* |
636 | * The number of openings available to us has changed, so inform scsipi. |
637 | */ |
638 | static void |
639 | iopsp_adjqparam(device_t dv, int mpi) |
640 | { |
641 | struct iopsp_softc *sc; |
642 | struct iop_softc *iop; |
643 | |
644 | sc = device_private(dv); |
645 | iop = device_private(device_parent(dv)); |
646 | |
647 | mutex_spin_enter(&iop->sc_intrlock); |
648 | sc->sc_adapter.adapt_openings += mpi - sc->sc_openings; |
649 | sc->sc_openings = mpi; |
650 | mutex_spin_exit(&iop->sc_intrlock); |
651 | } |
652 | |