cac.c revision 1.44 1 /* $NetBSD: cac.c,v 1.44 2008/03/14 03:30:19 mhitch Exp $ */
2
3 /*-
4 * Copyright (c) 2000, 2006, 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 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * Driver for Compaq array controllers.
41 */
42
43 #include <sys/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.44 2008/03/14 03:30:19 mhitch Exp $");
45
46 #include "bio.h"
47
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/device.h>
52 #include <sys/queue.h>
53 #include <sys/proc.h>
54 #include <sys/buf.h>
55 #include <sys/endian.h>
56 #include <sys/malloc.h>
57 #include <sys/pool.h>
58
59 #include <uvm/uvm_extern.h>
60
61 #include <sys/bswap.h>
62 #include <sys/bus.h>
63
64 #include <dev/ic/cacreg.h>
65 #include <dev/ic/cacvar.h>
66
67 #if NBIO > 0
68 #include <dev/biovar.h>
69 #endif /* NBIO > 0 */
70
71 #include "locators.h"
72
73 static struct cac_ccb *cac_ccb_alloc(struct cac_softc *, int);
74 static void cac_ccb_done(struct cac_softc *, struct cac_ccb *);
75 static void cac_ccb_free(struct cac_softc *, struct cac_ccb *);
76 static int cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int);
77 static int cac_ccb_start(struct cac_softc *, struct cac_ccb *);
78 static int cac_print(void *, const char *);
79 static void cac_shutdown(void *);
80
81 static struct cac_ccb *cac_l0_completed(struct cac_softc *);
82 static int cac_l0_fifo_full(struct cac_softc *);
83 static void cac_l0_intr_enable(struct cac_softc *, int);
84 static int cac_l0_intr_pending(struct cac_softc *);
85 static void cac_l0_submit(struct cac_softc *, struct cac_ccb *);
86
87 static void *cac_sdh; /* shutdown hook */
88
89 #if NBIO > 0
90 int cac_ioctl(struct device *, u_long, void *);
91 int cac_ioctl_vol(struct cac_softc *, struct bioc_vol *);
92 int cac_create_sensors(struct cac_softc *);
93 void cac_sensor_refresh(struct sysmon_envsys *, envsys_data_t *);
94 #endif /* NBIO > 0 */
95
96 const struct cac_linkage cac_l0 = {
97 cac_l0_completed,
98 cac_l0_fifo_full,
99 cac_l0_intr_enable,
100 cac_l0_intr_pending,
101 cac_l0_submit
102 };
103
104 /*
105 * Initialise our interface to the controller.
106 */
107 int
108 cac_init(struct cac_softc *sc, const char *intrstr, int startfw)
109 {
110 struct cac_controller_info cinfo;
111 struct cac_attach_args caca;
112 int error, rseg, size, i;
113 bus_dma_segment_t seg;
114 struct cac_ccb *ccb;
115 int locs[CACCF_NLOCS];
116 char firm[8];
117
118 if (intrstr != NULL)
119 aprint_normal("%s: interrupting at %s\n", sc->sc_dv.dv_xname,
120 intrstr);
121
122 SIMPLEQ_INIT(&sc->sc_ccb_free);
123 SIMPLEQ_INIT(&sc->sc_ccb_queue);
124 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM);
125 cv_init(&sc->sc_ccb_cv, "cacccb");
126
127 size = sizeof(struct cac_ccb) * CAC_MAX_CCBS;
128
129 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1,
130 &rseg, BUS_DMA_NOWAIT)) != 0) {
131 aprint_error("%s: unable to allocate CCBs, error = %d\n",
132 sc->sc_dv.dv_xname, error);
133 return (-1);
134 }
135
136 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size,
137 (void **)&sc->sc_ccbs,
138 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
139 aprint_error("%s: unable to map CCBs, error = %d\n",
140 sc->sc_dv.dv_xname, error);
141 return (-1);
142 }
143
144 if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
145 BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
146 aprint_error("%s: unable to create CCB DMA map, error = %d\n",
147 sc->sc_dv.dv_xname, error);
148 return (-1);
149 }
150
151 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs,
152 size, NULL, BUS_DMA_NOWAIT)) != 0) {
153 aprint_error("%s: unable to load CCB DMA map, error = %d\n",
154 sc->sc_dv.dv_xname, error);
155 return (-1);
156 }
157
158 sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr;
159 memset(sc->sc_ccbs, 0, size);
160 ccb = (struct cac_ccb *)sc->sc_ccbs;
161
162 for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) {
163 /* Create the DMA map for this CCB's data */
164 error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER,
165 CAC_SG_SIZE, CAC_MAX_XFER, 0,
166 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
167 &ccb->ccb_dmamap_xfer);
168
169 if (error) {
170 aprint_error("%s: can't create ccb dmamap (%d)\n",
171 sc->sc_dv.dv_xname, error);
172 break;
173 }
174
175 ccb->ccb_flags = 0;
176 ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb);
177 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain);
178 }
179
180 /* Start firmware background tasks, if needed. */
181 if (startfw) {
182 if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo),
183 0, 0, CAC_CCB_DATA_IN, NULL)) {
184 aprint_error("%s: CAC_CMD_START_FIRMWARE failed\n",
185 sc->sc_dv.dv_xname);
186 return (-1);
187 }
188 }
189
190 if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0,
191 CAC_CCB_DATA_IN, NULL)) {
192 aprint_error("%s: CAC_CMD_GET_CTRL_INFO failed\n",
193 sc->sc_dv.dv_xname);
194 return (-1);
195 }
196
197 strlcpy(firm, cinfo.firm_rev, 4+1);
198 printf("%s: %d channels, firmware <%s>\n", sc->sc_dv.dv_xname,
199 cinfo.scsi_chips, firm);
200
201 sc->sc_nunits = cinfo.num_drvs;
202 for (i = 0; i < cinfo.num_drvs; i++) {
203 caca.caca_unit = i;
204
205 locs[CACCF_UNIT] = i;
206
207 config_found_sm_loc(&sc->sc_dv, "cac", locs, &caca,
208 cac_print, config_stdsubmatch);
209 }
210
211 /* Set our `shutdownhook' before we start any device activity. */
212 if (cac_sdh == NULL)
213 cac_sdh = shutdownhook_establish(cac_shutdown, NULL);
214
215 mutex_enter(&sc->sc_mutex);
216 (*sc->sc_cl.cl_intr_enable)(sc, CAC_INTR_ENABLE);
217 mutex_exit(&sc->sc_mutex);
218
219 #if NBIO > 0
220 if (bio_register(&sc->sc_dv, cac_ioctl) != 0)
221 printf("%s: controller registration failed",
222 sc->sc_dv.dv_xname);
223 else
224 sc->sc_ioctl = cac_ioctl;
225 if (cac_create_sensors(sc) != 0)
226 aprint_error("%s: unable to create sensors\n", sc->sc_dv.dv_xname);
227 #endif
228
229 return (0);
230 }
231
232 /*
233 * Shut down all `cac' controllers.
234 */
235 static void
236 cac_shutdown(void *cookie)
237 {
238 extern struct cfdriver cac_cd;
239 struct cac_softc *sc;
240 u_int8_t tbuf[512];
241 int i;
242
243 for (i = 0; i < cac_cd.cd_ndevs; i++) {
244 if ((sc = device_lookup(&cac_cd, i)) == NULL)
245 continue;
246 memset(tbuf, 0, sizeof(tbuf));
247 tbuf[0] = 1;
248 cac_cmd(sc, CAC_CMD_FLUSH_CACHE, tbuf, sizeof(tbuf), 0, 0,
249 CAC_CCB_DATA_OUT, NULL);
250 }
251 }
252
253 /*
254 * Print autoconfiguration message for a sub-device.
255 */
256 static int
257 cac_print(void *aux, const char *pnp)
258 {
259 struct cac_attach_args *caca;
260
261 caca = (struct cac_attach_args *)aux;
262
263 if (pnp != NULL)
264 aprint_normal("block device at %s", pnp);
265 aprint_normal(" unit %d", caca->caca_unit);
266 return (UNCONF);
267 }
268
269 /*
270 * Handle an interrupt from the controller: process finished CCBs and
271 * dequeue any waiting CCBs.
272 */
273 int
274 cac_intr(void *cookie)
275 {
276 struct cac_softc *sc;
277 struct cac_ccb *ccb;
278 int rv;
279
280 sc = (struct cac_softc *)cookie;
281
282 mutex_enter(&sc->sc_mutex);
283
284 if ((*sc->sc_cl.cl_intr_pending)(sc)) {
285 while ((ccb = (*sc->sc_cl.cl_completed)(sc)) != NULL) {
286 cac_ccb_done(sc, ccb);
287 cac_ccb_start(sc, NULL);
288 }
289 rv = 1;
290 } else
291 rv = 0;
292
293 mutex_exit(&sc->sc_mutex);
294
295 return (rv);
296 }
297
298 /*
299 * Execute a [polled] command.
300 */
301 int
302 cac_cmd(struct cac_softc *sc, int command, void *data, int datasize,
303 int drive, int blkno, int flags, struct cac_context *context)
304 {
305 struct cac_ccb *ccb;
306 struct cac_sgb *sgb;
307 int i, rv, size, nsegs;
308
309 size = 0;
310
311 if ((ccb = cac_ccb_alloc(sc, 1)) == NULL) {
312 printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname);
313 return (EAGAIN);
314 }
315
316 if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
317 bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer,
318 (void *)data, datasize, NULL, BUS_DMA_NOWAIT |
319 BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ?
320 BUS_DMA_READ : BUS_DMA_WRITE));
321
322 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize,
323 (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD :
324 BUS_DMASYNC_PREWRITE);
325
326 sgb = ccb->ccb_seg;
327 nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE);
328
329 for (i = 0; i < nsegs; i++, sgb++) {
330 size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len;
331 sgb->length =
332 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len);
333 sgb->addr =
334 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr);
335 }
336 } else {
337 size = datasize;
338 nsegs = 0;
339 }
340
341 ccb->ccb_hdr.drive = drive;
342 ccb->ccb_hdr.priority = 0;
343 ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) +
344 sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2);
345
346 ccb->ccb_req.next = 0;
347 ccb->ccb_req.error = 0;
348 ccb->ccb_req.reserved = 0;
349 ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE));
350 ccb->ccb_req.command = command;
351 ccb->ccb_req.sgcount = nsegs;
352 ccb->ccb_req.blkno = htole32(blkno);
353
354 ccb->ccb_flags = flags;
355 ccb->ccb_datasize = size;
356
357 mutex_enter(&sc->sc_mutex);
358
359 if (context == NULL) {
360 memset(&ccb->ccb_context, 0, sizeof(struct cac_context));
361
362 /* Synchronous commands musn't wait. */
363 if ((*sc->sc_cl.cl_fifo_full)(sc)) {
364 cac_ccb_free(sc, ccb);
365 rv = EAGAIN;
366 } else {
367 #ifdef DIAGNOSTIC
368 ccb->ccb_flags |= CAC_CCB_ACTIVE;
369 #endif
370 (*sc->sc_cl.cl_submit)(sc, ccb);
371 rv = cac_ccb_poll(sc, ccb, 2000);
372 cac_ccb_free(sc, ccb);
373 }
374 } else {
375 memcpy(&ccb->ccb_context, context, sizeof(struct cac_context));
376 (void)cac_ccb_start(sc, ccb);
377 rv = 0;
378 }
379
380 mutex_exit(&sc->sc_mutex);
381 return (rv);
382 }
383
384 /*
385 * Wait for the specified CCB to complete.
386 */
387 static int
388 cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo)
389 {
390 struct cac_ccb *ccb;
391
392 KASSERT(mutex_owned(&sc->sc_mutex));
393
394 timo *= 1000;
395
396 do {
397 for (; timo != 0; timo--) {
398 ccb = (*sc->sc_cl.cl_completed)(sc);
399 if (ccb != NULL)
400 break;
401 DELAY(1);
402 }
403
404 if (timo == 0) {
405 printf("%s: timeout\n", sc->sc_dv.dv_xname);
406 return (EBUSY);
407 }
408 cac_ccb_done(sc, ccb);
409 } while (ccb != wantccb);
410
411 return (0);
412 }
413
414 /*
415 * Enqueue the specified command (if any) and attempt to start all enqueued
416 * commands.
417 */
418 static int
419 cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb)
420 {
421
422 KASSERT(mutex_owned(&sc->sc_mutex));
423
424 if (ccb != NULL)
425 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain);
426
427 while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) {
428 if ((*sc->sc_cl.cl_fifo_full)(sc))
429 return (EAGAIN);
430 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb_chain);
431 #ifdef DIAGNOSTIC
432 ccb->ccb_flags |= CAC_CCB_ACTIVE;
433 #endif
434 (*sc->sc_cl.cl_submit)(sc, ccb);
435 }
436
437 return (0);
438 }
439
440 /*
441 * Process a finished CCB.
442 */
443 static void
444 cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb)
445 {
446 struct device *dv;
447 void *context;
448 int error;
449
450 error = 0;
451
452 KASSERT(mutex_owned(&sc->sc_mutex));
453
454 #ifdef DIAGNOSTIC
455 if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0)
456 panic("cac_ccb_done: CCB not active");
457 ccb->ccb_flags &= ~CAC_CCB_ACTIVE;
458 #endif
459
460 if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
461 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0,
462 ccb->ccb_datasize, ccb->ccb_flags & CAC_CCB_DATA_IN ?
463 BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
464 bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer);
465 }
466
467 error = ccb->ccb_req.error;
468 if (ccb->ccb_context.cc_handler != NULL) {
469 dv = ccb->ccb_context.cc_dv;
470 context = ccb->ccb_context.cc_context;
471 cac_ccb_free(sc, ccb);
472 (*ccb->ccb_context.cc_handler)(dv, context, error);
473 } else {
474 if ((error & CAC_RET_SOFT_ERROR) != 0)
475 printf("%s: soft error; array may be degraded\n",
476 sc->sc_dv.dv_xname);
477 if ((error & CAC_RET_HARD_ERROR) != 0)
478 printf("%s: hard error\n", sc->sc_dv.dv_xname);
479 if ((error & CAC_RET_CMD_REJECTED) != 0) {
480 error = 1;
481 printf("%s: invalid request\n", sc->sc_dv.dv_xname);
482 }
483 }
484 }
485
486 /*
487 * Allocate a CCB.
488 */
489 static struct cac_ccb *
490 cac_ccb_alloc(struct cac_softc *sc, int nosleep)
491 {
492 struct cac_ccb *ccb;
493
494 mutex_enter(&sc->sc_mutex);
495
496 for (;;) {
497 if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) {
498 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_chain);
499 break;
500 }
501 if (nosleep) {
502 ccb = NULL;
503 break;
504 }
505 cv_wait(&sc->sc_ccb_cv, &sc->sc_mutex);
506 }
507
508 mutex_exit(&sc->sc_mutex);
509 return (ccb);
510 }
511
512 /*
513 * Put a CCB onto the freelist.
514 */
515 static void
516 cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb)
517 {
518
519 KASSERT(mutex_owned(&sc->sc_mutex));
520
521 ccb->ccb_flags = 0;
522 if (SIMPLEQ_EMPTY(&sc->sc_ccb_free))
523 cv_signal(&sc->sc_ccb_cv);
524 SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
525 }
526
527 /*
528 * Board specific linkage shared between multiple bus types.
529 */
530
531 static int
532 cac_l0_fifo_full(struct cac_softc *sc)
533 {
534
535 KASSERT(mutex_owned(&sc->sc_mutex));
536
537 return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0);
538 }
539
540 static void
541 cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb)
542 {
543
544 KASSERT(mutex_owned(&sc->sc_mutex));
545
546 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
547 (char *)ccb - (char *)sc->sc_ccbs,
548 sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
549 cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr);
550 }
551
552 static struct cac_ccb *
553 cac_l0_completed(struct cac_softc *sc)
554 {
555 struct cac_ccb *ccb;
556 paddr_t off;
557
558 KASSERT(mutex_owned(&sc->sc_mutex));
559
560 if ((off = cac_inl(sc, CAC_REG_DONE_FIFO)) == 0)
561 return (NULL);
562
563 if ((off & 3) != 0)
564 printf("%s: failed command list returned: %lx\n",
565 sc->sc_dv.dv_xname, (long)off);
566
567 off = (off & ~3) - sc->sc_ccbs_paddr;
568 ccb = (struct cac_ccb *)((char *)sc->sc_ccbs + off);
569
570 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb),
571 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
572
573 if ((off & 3) != 0 && ccb->ccb_req.error == 0)
574 ccb->ccb_req.error = CAC_RET_CMD_REJECTED;
575
576 return (ccb);
577 }
578
579 static int
580 cac_l0_intr_pending(struct cac_softc *sc)
581 {
582
583 KASSERT(mutex_owned(&sc->sc_mutex));
584
585 return (cac_inl(sc, CAC_REG_INTR_PENDING) & CAC_INTR_ENABLE);
586 }
587
588 static void
589 cac_l0_intr_enable(struct cac_softc *sc, int state)
590 {
591
592 KASSERT(mutex_owned(&sc->sc_mutex));
593
594 cac_outl(sc, CAC_REG_INTR_MASK,
595 state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE);
596 }
597
598 #if NBIO > 0
599 const int cac_level[] = { 0, 4, 1, 5, 51, 7 };
600 const int cac_stat[] = { BIOC_SVONLINE, BIOC_SVOFFLINE, BIOC_SVOFFLINE,
601 BIOC_SVDEGRADED, BIOC_SVREBUILD, BIOC_SVREBUILD, BIOC_SVDEGRADED,
602 BIOC_SVDEGRADED, BIOC_SVINVALID, BIOC_SVINVALID, BIOC_SVBUILDING,
603 BIOC_SVOFFLINE, BIOC_SVBUILDING };
604
605 int
606 cac_ioctl(struct device *dev, u_long cmd, void *addr)
607 {
608 struct cac_softc *sc = (struct cac_softc *)dev;
609 struct bioc_inq *bi;
610 struct bioc_disk *bd;
611 cac_lock_t lock;
612 int error = 0;
613
614 lock = CAC_LOCK(sc);
615 switch (cmd) {
616 case BIOCINQ:
617 bi = (struct bioc_inq *)addr;
618 strlcpy(bi->bi_dev, sc->sc_dv.dv_xname, sizeof(bi->bi_dev));
619 bi->bi_novol = sc->sc_nunits;
620 bi->bi_nodisk = 0;
621 break;
622
623 case BIOCVOL:
624 error = cac_ioctl_vol(sc, (struct bioc_vol *)addr);
625 break;
626
627 case BIOCDISK:
628 case BIOCDISK_NOVOL:
629 bd = (struct bioc_disk *)addr;
630 if (bd->bd_volid > sc->sc_nunits) {
631 error = EINVAL;
632 break;
633 }
634 /* No disk information yet */
635 break;
636
637 case BIOCBLINK:
638 case BIOCALARM:
639 case BIOCSETSTATE:
640 default:
641 error = EINVAL;
642 }
643 CAC_UNLOCK(sc, lock);
644
645 return (error);
646 }
647
648 int
649 cac_ioctl_vol(struct cac_softc *sc, struct bioc_vol *bv)
650 {
651 struct cac_drive_info dinfo;
652 struct cac_drive_status dstatus;
653 u_int32_t blks;
654
655 if (bv->bv_volid > sc->sc_nunits) {
656 return EINVAL;
657 }
658 if (cac_cmd(sc, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo),
659 bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) {
660 return EIO;
661 }
662 if (cac_cmd(sc, CAC_CMD_SENSE_DRV_STATUS, &dstatus, sizeof(dstatus),
663 bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) {
664 return EIO;
665 }
666 blks = CAC_GET2(dinfo.ncylinders) * CAC_GET1(dinfo.nheads) *
667 CAC_GET1(dinfo.nsectors);
668 bv->bv_size = (off_t)blks * CAC_GET2(dinfo.secsize);
669 bv->bv_level = cac_level[CAC_GET1(dinfo.mirror)]; /*XXX limit check */
670 bv->bv_nodisk = 0; /* XXX */
671 bv->bv_status = 0; /* XXX */
672 bv->bv_percent = -1;
673 bv->bv_seconds = 0;
674 if (dstatus.stat < sizeof(cac_stat)/sizeof(cac_stat[0]))
675 bv->bv_status = cac_stat[dstatus.stat];
676 if (bv->bv_status == BIOC_SVREBUILD ||
677 bv->bv_status == BIOC_SVBUILDING)
678 bv->bv_percent = ((blks - CAC_GET4(dstatus.prog)) * 1000ULL) /
679 blks;
680 return 0;
681 }
682
683 int
684 cac_create_sensors(struct cac_softc *sc)
685 {
686 int i;
687 int nsensors = sc->sc_nunits;
688
689 sc->sc_sme = sysmon_envsys_create();
690 sc->sc_sensor = malloc(sizeof(envsys_data_t) * nsensors,
691 M_DEVBUF, M_NOWAIT | M_ZERO);
692 if (sc->sc_sensor == NULL) {
693 aprint_error("%s: can't allocate envsys_data_t\n",
694 sc->sc_dv.dv_xname);
695 return(ENOMEM);
696 }
697
698 for (i = 0; i < nsensors; i++) {
699 sc->sc_sensor[i].units = ENVSYS_DRIVE;
700 sc->sc_sensor[i].monitor = true;
701 /* Enable monitoring for drive state changes */
702 sc->sc_sensor[i].flags |= ENVSYS_FMONSTCHANGED;
703 /* logical drives */
704 snprintf(sc->sc_sensor[i].desc,
705 sizeof(sc->sc_sensor[i].desc), "%s:%d",
706 sc->sc_dv.dv_xname, i);
707 if (sysmon_envsys_sensor_attach(sc->sc_sme,
708 &sc->sc_sensor[i]))
709 goto out;
710 }
711 sc->sc_sme->sme_name = sc->sc_dv.dv_xname;
712 sc->sc_sme->sme_cookie = sc;
713 sc->sc_sme->sme_refresh = cac_sensor_refresh;
714 if (sysmon_envsys_register(sc->sc_sme)) {
715 printf("%s: unable to register with sysmon\n", sc->sc_dv.dv_xname);
716 return(1);
717 }
718 return (0);
719
720 out:
721 free(sc->sc_sensor, M_DEVBUF);
722 sysmon_envsys_destroy(sc->sc_sme);
723 return EINVAL;
724 }
725
726 void
727 cac_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
728 {
729 struct cac_softc *sc = sme->sme_cookie;
730 struct bioc_vol bv;
731 int s;
732
733 if (edata->sensor >= sc->sc_nunits)
734 return;
735
736 bzero(&bv, sizeof(bv));
737 bv.bv_volid = edata->sensor;
738 s = splbio();
739 if (cac_ioctl_vol(sc, &bv)) {
740 splx(s);
741 return;
742 }
743 splx(s);
744
745 switch(bv.bv_status) {
746 case BIOC_SVOFFLINE:
747 edata->value_cur = ENVSYS_DRIVE_FAIL;
748 edata->state = ENVSYS_SCRITICAL;
749 break;
750
751 case BIOC_SVDEGRADED:
752 edata->value_cur = ENVSYS_DRIVE_PFAIL;
753 edata->state = ENVSYS_SCRITICAL;
754 break;
755
756 case BIOC_SVSCRUB:
757 case BIOC_SVONLINE:
758 edata->value_cur = ENVSYS_DRIVE_ONLINE;
759 edata->state = ENVSYS_SVALID;
760 break;
761
762 case BIOC_SVREBUILD:
763 case BIOC_SVBUILDING:
764 edata->value_cur = ENVSYS_DRIVE_REBUILD;
765 edata->state = ENVSYS_SVALID;
766 break;
767
768 case BIOC_SVINVALID:
769 /* FALLTRHOUGH */
770 default:
771 edata->value_cur = 0; /* unknown */
772 edata->state = ENVSYS_SINVALID;
773 }
774 }
775 #endif /* NBIO > 0 */
776