1 /*-
2 * Copyright (c) 2000 Matthew Jacob
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions, and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. The name of the author may not be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD: releng/10.0/sys/cam/scsi/scsi_enc.c 257049 2013-10-24 10:33:31Z mav $");
29
30 #include <sys/param.h>
31
32 #include <sys/conf.h>
33 #include <sys/errno.h>
34 #include <sys/fcntl.h>
35 #include <sys/kernel.h>
36 #include <sys/kthread.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/queue.h>
41 #include <sys/sx.h>
42 #include <sys/systm.h>
43 #include <sys/sysctl.h>
44 #include <sys/types.h>
45
46 #include <machine/stdarg.h>
47
48 #include <cam/cam.h>
49 #include <cam/cam_ccb.h>
50 #include <cam/cam_debug.h>
51 #include <cam/cam_periph.h>
52 #include <cam/cam_xpt_periph.h>
53
54 #include <cam/scsi/scsi_all.h>
55 #include <cam/scsi/scsi_message.h>
56 #include <cam/scsi/scsi_enc.h>
57 #include <cam/scsi/scsi_enc_internal.h>
58
59 #include <opt_ses.h>
60
61 MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
62
63 /* Enclosure type independent driver */
64
65 static d_open_t enc_open;
66 static d_close_t enc_close;
67 static d_ioctl_t enc_ioctl;
68 static periph_init_t enc_init;
69 static periph_ctor_t enc_ctor;
70 static periph_oninv_t enc_oninvalidate;
71 static periph_dtor_t enc_dtor;
72 static periph_start_t enc_start;
73
74 static void enc_async(void *, uint32_t, struct cam_path *, void *);
75 static enctyp enc_type(struct ccb_getdev *);
76
77 SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0,
78 "CAM Enclosure Services driver");
79
80 static struct periph_driver encdriver = {
81 enc_init, "ses",
82 TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
83 };
84
85 PERIPHDRIVER_DECLARE(enc, encdriver);
86
87 static struct cdevsw enc_cdevsw = {
88 .d_version = D_VERSION,
89 .d_open = enc_open,
90 .d_close = enc_close,
91 .d_ioctl = enc_ioctl,
92 .d_name = "ses",
93 .d_flags = D_TRACKCLOSE,
94 };
95
96 static void
97 enc_init(void)
98 {
99 cam_status status;
100
101 /*
102 * Install a global async callback. This callback will
103 * receive async callbacks like "new device found".
104 */
105 status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL);
106
107 if (status != CAM_REQ_CMP) {
108 printf("enc: Failed to attach master async callback "
109 "due to status 0x%x!\n", status);
110 }
111 }
112
113 static void
114 enc_devgonecb(void *arg)
115 {
116 struct cam_sim *sim;
117 struct cam_periph *periph;
118 struct enc_softc *enc;
119 int i;
120
121 periph = (struct cam_periph *)arg;
122 sim = periph->sim;
123 enc = (struct enc_softc *)periph->softc;
124
125 mtx_lock(sim->mtx);
126
127 /*
128 * When we get this callback, we will get no more close calls from
129 * devfs. So if we have any dangling opens, we need to release the
130 * reference held for that particular context.
131 */
132 for (i = 0; i < enc->open_count; i++)
133 cam_periph_release_locked(periph);
134
135 enc->open_count = 0;
136
137 /*
138 * Release the reference held for the device node, it is gone now.
139 */
140 cam_periph_release_locked(periph);
141
142 /*
143 * We reference the SIM lock directly here, instead of using
144 * cam_periph_unlock(). The reason is that the final call to
145 * cam_periph_release_locked() above could result in the periph
146 * getting freed. If that is the case, dereferencing the periph
147 * with a cam_periph_unlock() call would cause a page fault.
148 */
149 mtx_unlock(sim->mtx);
150 }
151
152 static void
153 enc_oninvalidate(struct cam_periph *periph)
154 {
155 struct enc_softc *enc;
156
157 enc = periph->softc;
158
159 enc->enc_flags |= ENC_FLAG_INVALID;
160
161 /* If the sub-driver has an invalidate routine, call it */
162 if (enc->enc_vec.softc_invalidate != NULL)
163 enc->enc_vec.softc_invalidate(enc);
164
165 /*
166 * Unregister any async callbacks.
167 */
168 xpt_register_async(0, enc_async, periph, periph->path);
169
170 /*
171 * Shutdown our daemon.
172 */
173 enc->enc_flags |= ENC_FLAG_SHUTDOWN;
174 if (enc->enc_daemon != NULL) {
175 /* Signal the ses daemon to terminate. */
176 wakeup(enc->enc_daemon);
177 }
178 callout_drain(&enc->status_updater);
179
180 destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph);
181 }
182
183 static void
184 enc_dtor(struct cam_periph *periph)
185 {
186 struct enc_softc *enc;
187
188 enc = periph->softc;
189
190 /* If the sub-driver has a cleanup routine, call it */
191 if (enc->enc_vec.softc_cleanup != NULL)
192 enc->enc_vec.softc_cleanup(enc);
193
194 if (enc->enc_boot_hold_ch.ich_func != NULL) {
195 config_intrhook_disestablish(&enc->enc_boot_hold_ch);
196 enc->enc_boot_hold_ch.ich_func = NULL;
197 }
198
199 ENC_FREE(enc);
200 }
201
202 static void
203 enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
204 {
205 struct cam_periph *periph;
206
207 periph = (struct cam_periph *)callback_arg;
208
209 switch(code) {
210 case AC_FOUND_DEVICE:
211 {
212 struct ccb_getdev *cgd;
213 cam_status status;
214 path_id_t path_id;
215
216 cgd = (struct ccb_getdev *)arg;
217 if (arg == NULL) {
218 break;
219 }
220
221 if (enc_type(cgd) == ENC_NONE) {
222 /*
223 * Schedule announcement of the ENC bindings for
224 * this device if it is managed by a SEP.
225 */
226 path_id = xpt_path_path_id(path);
227 xpt_lock_buses();
228 TAILQ_FOREACH(periph, &encdriver.units, unit_links) {
229 struct enc_softc *softc;
230
231 softc = (struct enc_softc *)periph->softc;
232 if (xpt_path_path_id(periph->path) != path_id
233 || softc == NULL
234 || (softc->enc_flags & ENC_FLAG_INITIALIZED)
235 == 0
236 || softc->enc_vec.device_found == NULL)
237 continue;
238
239 softc->enc_vec.device_found(softc);
240 }
241 xpt_unlock_buses();
242 return;
243 }
244
245 status = cam_periph_alloc(enc_ctor, enc_oninvalidate,
246 enc_dtor, enc_start, "ses", CAM_PERIPH_BIO,
247 cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd);
248
249 if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
250 printf("enc_async: Unable to probe new device due to "
251 "status 0x%x\n", status);
252 }
253 break;
254 }
255 default:
256 cam_periph_async(periph, code, path, arg);
257 break;
258 }
259 }
260
261 static int
262 enc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
263 {
264 struct cam_periph *periph;
265 struct enc_softc *softc;
266 int error = 0;
267
268 periph = (struct cam_periph *)dev->si_drv1;
269 if (periph == NULL) {
270 return (ENXIO);
271 }
272
273 if (cam_periph_acquire(periph) != CAM_REQ_CMP)
274 return (ENXIO);
275
276 cam_periph_lock(periph);
277
278 softc = (struct enc_softc *)periph->softc;
279
280 if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
281 error = ENXIO;
282 goto out;
283 }
284 if (softc->enc_flags & ENC_FLAG_INVALID) {
285 error = ENXIO;
286 goto out;
287 }
288 out:
289 if (error != 0)
290 cam_periph_release_locked(periph);
291 else
292 softc->open_count++;
293
294 cam_periph_unlock(periph);
295
296 return (error);
297 }
298
299 static int
300 enc_close(struct cdev *dev, int flag, int fmt, struct thread *td)
301 {
302 struct cam_sim *sim;
303 struct cam_periph *periph;
304 struct enc_softc *enc;
305
306 periph = (struct cam_periph *)dev->si_drv1;
307 if (periph == NULL)
308 return (ENXIO);
309
310 sim = periph->sim;
311 enc = periph->softc;
312
313 mtx_lock(sim->mtx);
314
315 enc->open_count--;
316
317 cam_periph_release_locked(periph);
318
319 /*
320 * We reference the SIM lock directly here, instead of using
321 * cam_periph_unlock(). The reason is that the call to
322 * cam_periph_release_locked() above could result in the periph
323 * getting freed. If that is the case, dereferencing the periph
324 * with a cam_periph_unlock() call would cause a page fault.
325 *
326 * cam_periph_release() avoids this problem using the same method,
327 * but we're manually acquiring and dropping the lock here to
328 * protect the open count and avoid another lock acquisition and
329 * release.
330 */
331 mtx_unlock(sim->mtx);
332
333 return (0);
334 }
335
336 static void
337 enc_start(struct cam_periph *p, union ccb *sccb)
338 {
339 struct enc_softc *enc;
340
341 enc = p->softc;
342 ENC_DLOG(enc, "%s enter imm=%d prio=%d\n",
343 __func__, p->immediate_priority, p->pinfo.priority);
344 if (p->immediate_priority <= p->pinfo.priority) {
345 SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
346 p->immediate_priority = CAM_PRIORITY_NONE;
347 wakeup(&p->ccb_list);
348 } else
349 xpt_release_ccb(sccb);
350 ENC_DLOG(enc, "%s exit\n", __func__);
351 }
352
353 void
354 enc_done(struct cam_periph *periph, union ccb *dccb)
355 {
356 wakeup(&dccb->ccb_h.cbfcnp);
357 }
358
359 int
360 enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags)
361 {
362 struct enc_softc *softc;
363 struct cam_periph *periph;
364
365 periph = xpt_path_periph(ccb->ccb_h.path);
366 softc = (struct enc_softc *)periph->softc;
367
368 return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb));
369 }
370
371 static int
372 enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
373 struct thread *td)
374 {
375 struct cam_periph *periph;
376 encioc_enc_status_t tmp;
377 encioc_string_t sstr;
378 encioc_elm_status_t elms;
379 encioc_elm_desc_t elmd;
380 encioc_elm_devnames_t elmdn;
381 encioc_element_t *uelm;
382 enc_softc_t *enc;
383 enc_cache_t *cache;
384 void *addr;
385 int error, i;
386
387
388 if (arg_addr)
389 addr = *((caddr_t *) arg_addr);
390 else
391 addr = NULL;
392
393 periph = (struct cam_periph *)dev->si_drv1;
394 if (periph == NULL)
395 return (ENXIO);
396
397 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n"));
398
399 cam_periph_lock(periph);
400 enc = (struct enc_softc *)periph->softc;
401 cache = &enc->enc_cache;
402
403 /*
404 * Now check to see whether we're initialized or not.
405 * This actually should never fail as we're not supposed
406 * to get past enc_open w/o successfully initializing
407 * things.
408 */
409 if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
410 cam_periph_unlock(periph);
411 return (ENXIO);
412 }
413 cam_periph_unlock(periph);
414
415 error = 0;
416
417 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
418 ("trying to do ioctl %#lx\n", cmd));
419
420 /*
421 * If this command can change the device's state,
422 * we must have the device open for writing.
423 *
424 * For commands that get information about the
425 * device- we don't need to lock the peripheral
426 * if we aren't running a command. The periph
427 * also can't go away while a user process has
428 * it open.
429 */
430 switch (cmd) {
431 case ENCIOC_GETNELM:
432 case ENCIOC_GETELMMAP:
433 case ENCIOC_GETENCSTAT:
434 case ENCIOC_GETELMSTAT:
435 case ENCIOC_GETELMDESC:
436 case ENCIOC_GETELMDEVNAMES:
437 break;
438 default:
439 if ((flag & FWRITE) == 0) {
440 return (EBADF);
441 }
442 }
443
444 /*
445 * XXX The values read here are only valid for the current
446 * configuration generation. We need these ioctls
447 * to also pass in/out a generation number.
448 */
449 sx_slock(&enc->enc_cache_lock);
450 switch (cmd) {
451 case ENCIOC_GETNELM:
452 error = copyout(&cache->nelms, addr, sizeof (cache->nelms));
453 break;
454
455 case ENCIOC_GETELMMAP:
456 for (uelm = addr, i = 0; i != cache->nelms; i++) {
457 encioc_element_t kelm;
458 kelm.elm_idx = i;
459 kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
460 kelm.elm_type = cache->elm_map[i].enctype;
461 error = copyout(&kelm, &uelm[i], sizeof(kelm));
462 if (error)
463 break;
464 }
465 break;
466
467 case ENCIOC_GETENCSTAT:
468 cam_periph_lock(periph);
469 error = enc->enc_vec.get_enc_status(enc, 1);
470 if (error) {
471 cam_periph_unlock(periph);
472 break;
473 }
474 tmp = cache->enc_status;
475 cam_periph_unlock(periph);
476 error = copyout(&tmp, addr, sizeof(tmp));
477 cache->enc_status = tmp;
478 break;
479
480 case ENCIOC_SETENCSTAT:
481 error = copyin(addr, &tmp, sizeof(tmp));
482 if (error)
483 break;
484 cam_periph_lock(periph);
485 error = enc->enc_vec.set_enc_status(enc, tmp, 1);
486 cam_periph_unlock(periph);
487 break;
488
489 case ENCIOC_GETSTRING:
490 case ENCIOC_SETSTRING:
491 if (enc->enc_vec.handle_string == NULL) {
492 error = EINVAL;
493 break;
494 }
495 error = copyin(addr, &sstr, sizeof(sstr));
496 if (error)
497 break;
498 cam_periph_lock(periph);
499 error = enc->enc_vec.handle_string(enc, &sstr, cmd);
500 cam_periph_unlock(periph);
501 break;
502
503 case ENCIOC_GETELMSTAT:
504 error = copyin(addr, &elms, sizeof(elms));
505 if (error)
506 break;
507 if (elms.elm_idx >= cache->nelms) {
508 error = EINVAL;
509 break;
510 }
511 cam_periph_lock(periph);
512 error = enc->enc_vec.get_elm_status(enc, &elms, 1);
513 cam_periph_unlock(periph);
514 if (error)
515 break;
516 error = copyout(&elms, addr, sizeof(elms));
517 break;
518
519 case ENCIOC_GETELMDESC:
520 error = copyin(addr, &elmd, sizeof(elmd));
521 if (error)
522 break;
523 if (elmd.elm_idx >= cache->nelms) {
524 error = EINVAL;
525 break;
526 }
527 if (enc->enc_vec.get_elm_desc != NULL) {
528 error = enc->enc_vec.get_elm_desc(enc, &elmd);
529 if (error)
530 break;
531 } else
532 elmd.elm_desc_len = 0;
533 error = copyout(&elmd, addr, sizeof(elmd));
534 break;
535
536 case ENCIOC_GETELMDEVNAMES:
537 if (enc->enc_vec.get_elm_devnames == NULL) {
538 error = EINVAL;
539 break;
540 }
541 error = copyin(addr, &elmdn, sizeof(elmdn));
542 if (error)
543 break;
544 if (elmdn.elm_idx >= cache->nelms) {
545 error = EINVAL;
546 break;
547 }
548 cam_periph_lock(periph);
549 error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn);
550 cam_periph_unlock(periph);
551 if (error)
552 break;
553 error = copyout(&elmdn, addr, sizeof(elmdn));
554 break;
555
556 case ENCIOC_SETELMSTAT:
557 error = copyin(addr, &elms, sizeof(elms));
558 if (error)
559 break;
560
561 if (elms.elm_idx >= cache->nelms) {
562 error = EINVAL;
563 break;
564 }
565 cam_periph_lock(periph);
566 error = enc->enc_vec.set_elm_status(enc, &elms, 1);
567 cam_periph_unlock(periph);
568
569 break;
570
571 case ENCIOC_INIT:
572
573 cam_periph_lock(periph);
574 error = enc->enc_vec.init_enc(enc);
575 cam_periph_unlock(periph);
576 break;
577
578 default:
579 cam_periph_lock(periph);
580 error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error);
581 cam_periph_unlock(periph);
582 break;
583 }
584 sx_sunlock(&enc->enc_cache_lock);
585 return (error);
586 }
587
588 int
589 enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp)
590 {
591 int error, dlen, tdlen;
592 ccb_flags ddf;
593 union ccb *ccb;
594
595 CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE,
596 ("entering enc_runcmd\n"));
597 if (dptr) {
598 if ((dlen = *dlenp) < 0) {
599 dlen = -dlen;
600 ddf = CAM_DIR_OUT;
601 } else {
602 ddf = CAM_DIR_IN;
603 }
604 } else {
605 dlen = 0;
606 ddf = CAM_DIR_NONE;
607 }
608
609 if (cdbl > IOCDBLEN) {
610 cdbl = IOCDBLEN;
611 }
612
613 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
614 if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) {
615 tdlen = min(dlen, 1020);
616 tdlen = (tdlen + 3) & ~3;
617 cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen,
618 30 * 1000);
619 if (cdb[0] == RECEIVE_DIAGNOSTIC)
620 ata_28bit_cmd(&ccb->ataio,
621 ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4);
622 else if (cdb[0] == SEND_DIAGNOSTIC)
623 ata_28bit_cmd(&ccb->ataio,
624 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
625 0x82, tdlen / 4);
626 else if (cdb[0] == READ_BUFFER)
627 ata_28bit_cmd(&ccb->ataio,
628 ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4);
629 else
630 ata_28bit_cmd(&ccb->ataio,
631 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
632 0x80, tdlen / 4);
633 } else {
634 tdlen = dlen;
635 cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG,
636 dptr, dlen, sizeof (struct scsi_sense_data), cdbl,
637 60 * 1000);
638 bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
639 }
640
641 error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
642 if (error) {
643 if (dptr) {
644 *dlenp = dlen;
645 }
646 } else {
647 if (dptr) {
648 if (ccb->ccb_h.func_code == XPT_ATA_IO)
649 *dlenp = ccb->ataio.resid;
650 else
651 *dlenp = ccb->csio.resid;
652 *dlenp += tdlen - dlen;
653 }
654 }
655 xpt_release_ccb(ccb);
656 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
657 ("exiting enc_runcmd: *dlenp = %d\n", *dlenp));
658 return (error);
659 }
660
661 void
662 enc_log(struct enc_softc *enc, const char *fmt, ...)
663 {
664 va_list ap;
665
666 printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number);
667 va_start(ap, fmt);
668 vprintf(fmt, ap);
669 va_end(ap);
670 }
671
672 /*
673 * The code after this point runs on many platforms,
674 * so forgive the slightly awkward and nonconforming
675 * appearance.
676 */
677
678 /*
679 * Is this a device that supports enclosure services?
680 *
681 * It's a pretty simple ruleset- if it is device type
682 * 0x0D (13), it's an ENCLOSURE device.
683 */
684
685 #define SAFTE_START 44
686 #define SAFTE_END 50
687 #define SAFTE_LEN SAFTE_END-SAFTE_START
688
689 static enctyp
690 enc_type(struct ccb_getdev *cgd)
691 {
692 int buflen;
693 unsigned char *iqd;
694
695 if (cgd->protocol == PROTO_SEMB) {
696 iqd = (unsigned char *)&cgd->ident_data;
697 if (STRNCMP(iqd + 43, "S-E-S", 5) == 0)
698 return (ENC_SEMB_SES);
699 else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0)
700 return (ENC_SEMB_SAFT);
701 return (ENC_NONE);
702
703 } else if (cgd->protocol != PROTO_SCSI)
704 return (ENC_NONE);
705
706 iqd = (unsigned char *)&cgd->inq_data;
707 buflen = min(sizeof(cgd->inq_data),
708 SID_ADDITIONAL_LENGTH(&cgd->inq_data));
709
710 if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
711 if ((iqd[2] & 0x7) > 2) {
712 return (ENC_SES);
713 } else {
714 return (ENC_SES_SCSI2);
715 }
716 return (ENC_NONE);
717 }
718
719 #ifdef SES_ENABLE_PASSTHROUGH
720 if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
721 /*
722 * PassThrough Device.
723 */
724 return (ENC_SES_PASSTHROUGH);
725 }
726 #endif
727
728 /*
729 * The comparison is short for a reason-
730 * some vendors were chopping it short.
731 */
732
733 if (buflen < SAFTE_END - 2) {
734 return (ENC_NONE);
735 }
736
737 if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
738 return (ENC_SAFT);
739 }
740 return (ENC_NONE);
741 }
742
743 /*================== Enclosure Monitoring/Processing Daemon ==================*/
744 /**
745 * \brief Queue an update request for a given action, if needed.
746 *
747 * \param enc SES softc to queue the request for.
748 * \param action Action requested.
749 */
750 void
751 enc_update_request(enc_softc_t *enc, uint32_t action)
752 {
753 if ((enc->pending_actions & (0x1 << action)) == 0) {
754 enc->pending_actions |= (0x1 << action);
755 ENC_DLOG(enc, "%s: queing requested action %d\n",
756 __func__, action);
757 if (enc->current_action == ENC_UPDATE_NONE)
758 wakeup(enc->enc_daemon);
759 } else {
760 ENC_DLOG(enc, "%s: ignoring requested action %d - "
761 "Already queued\n", __func__, action);
762 }
763 }
764
765 /**
766 * \brief Invoke the handler of the highest priority pending
767 * state in the SES state machine.
768 *
769 * \param enc The SES instance invoking the state machine.
770 */
771 static void
772 enc_fsm_step(enc_softc_t *enc)
773 {
774 union ccb *ccb;
775 uint8_t *buf;
776 struct enc_fsm_state *cur_state;
777 int error;
778 uint32_t xfer_len;
779
780 ENC_DLOG(enc, "%s enter %p\n", __func__, enc);
781
782 enc->current_action = ffs(enc->pending_actions) - 1;
783 enc->pending_actions &= ~(0x1 << enc->current_action);
784
785 cur_state = &enc->enc_fsm_states[enc->current_action];
786
787 buf = NULL;
788 if (cur_state->buf_size != 0) {
789 cam_periph_unlock(enc->periph);
790 buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO);
791 cam_periph_lock(enc->periph);
792 }
793
794 error = 0;
795 ccb = NULL;
796 if (cur_state->fill != NULL) {
797 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
798
799 error = cur_state->fill(enc, cur_state, ccb, buf);
800 if (error != 0)
801 goto done;
802
803 error = cam_periph_runccb(ccb, cur_state->error,
804 ENC_CFLAGS,
805 ENC_FLAGS|SF_QUIET_IR, NULL);
806 }
807
808 if (ccb != NULL) {
809 if (ccb->ccb_h.func_code == XPT_ATA_IO)
810 xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
811 else
812 xfer_len = ccb->csio.dxfer_len - ccb->csio.resid;
813 } else
814 xfer_len = 0;
815
816 cam_periph_unlock(enc->periph);
817 cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len);
818 cam_periph_lock(enc->periph);
819
820 done:
821 ENC_DLOG(enc, "%s exit - result %d\n", __func__, error);
822 ENC_FREE_AND_NULL(buf);
823 if (ccb != NULL)
824 xpt_release_ccb(ccb);
825 }
826
827 /**
828 * \invariant Called with cam_periph mutex held.
829 */
830 static void
831 enc_status_updater(void *arg)
832 {
833 enc_softc_t *enc;
834
835 enc = arg;
836 if (enc->enc_vec.poll_status != NULL)
837 enc->enc_vec.poll_status(enc);
838 }
839
840 static void
841 enc_daemon(void *arg)
842 {
843 enc_softc_t *enc;
844
845 enc = arg;
846
847 cam_periph_lock(enc->periph);
848 while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) {
849 if (enc->pending_actions == 0) {
850 struct intr_config_hook *hook;
851
852 /*
853 * Reset callout and msleep, or
854 * issue timed task completion
855 * status command.
856 */
857 enc->current_action = ENC_UPDATE_NONE;
858
859 /*
860 * We've been through our state machine at least
861 * once. Allow the transition to userland.
862 */
863 hook = &enc->enc_boot_hold_ch;
864 if (hook->ich_func != NULL) {
865 config_intrhook_disestablish(hook);
866 hook->ich_func = NULL;
867 }
868
869 callout_reset(&enc->status_updater, 60*hz,
870 enc_status_updater, enc);
871
872 cam_periph_sleep(enc->periph, enc->enc_daemon,
873 PUSER, "idle", 0);
874 } else {
875 enc_fsm_step(enc);
876 }
877 }
878 enc->enc_daemon = NULL;
879 cam_periph_unlock(enc->periph);
880 cam_periph_release(enc->periph);
881 kproc_exit(0);
882 }
883
884 static int
885 enc_kproc_init(enc_softc_t *enc)
886 {
887 int result;
888
889 callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0);
890
891 if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP)
892 return (ENXIO);
893
894 result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0,
895 /*stackpgs*/0, "enc_daemon%d",
896 enc->periph->unit_number);
897 if (result == 0) {
898 /* Do an initial load of all page data. */
899 cam_periph_lock(enc->periph);
900 enc->enc_vec.poll_status(enc);
901 cam_periph_unlock(enc->periph);
902 } else
903 cam_periph_release(enc->periph);
904 return (result);
905 }
906
907 /**
908 * \brief Interrupt configuration hook callback associated with
909 * enc_boot_hold_ch.
910 *
911 * Since interrupts are always functional at the time of enclosure
912 * configuration, there is nothing to be done when the callback occurs.
913 * This hook is only registered to hold up boot processing while initial
914 * eclosure processing occurs.
915 *
916 * \param arg The enclosure softc, but currently unused in this callback.
917 */
918 static void
919 enc_nop_confighook_cb(void *arg __unused)
920 {
921 }
922
923 static cam_status
924 enc_ctor(struct cam_periph *periph, void *arg)
925 {
926 cam_status status = CAM_REQ_CMP_ERR;
927 int err;
928 enc_softc_t *enc;
929 struct ccb_getdev *cgd;
930 char *tname;
931
932 cgd = (struct ccb_getdev *)arg;
933 if (cgd == NULL) {
934 printf("enc_ctor: no getdev CCB, can't register device\n");
935 goto out;
936 }
937
938 enc = ENC_MALLOCZ(sizeof(*enc));
939 if (enc == NULL) {
940 printf("enc_ctor: Unable to probe new device. "
941 "Unable to allocate enc\n");
942 goto out;
943 }
944 enc->periph = periph;
945 enc->current_action = ENC_UPDATE_INVALID;
946
947 enc->enc_type = enc_type(cgd);
948 sx_init(&enc->enc_cache_lock, "enccache");
949
950 switch (enc->enc_type) {
951 case ENC_SES:
952 case ENC_SES_SCSI2:
953 case ENC_SES_PASSTHROUGH:
954 case ENC_SEMB_SES:
955 err = ses_softc_init(enc);
956 break;
957 case ENC_SAFT:
958 case ENC_SEMB_SAFT:
959 err = safte_softc_init(enc);
960 break;
961 case ENC_NONE:
962 default:
963 ENC_FREE(enc);
964 return (CAM_REQ_CMP_ERR);
965 }
966
967 if (err) {
968 xpt_print(periph->path, "error %d initializing\n", err);
969 goto out;
970 }
971
972 /*
973 * Hold off userland until we have made at least one pass
974 * through our state machine so that physical path data is
975 * present.
976 */
977 if (enc->enc_vec.poll_status != NULL) {
978 enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb;
979 enc->enc_boot_hold_ch.ich_arg = enc;
980 config_intrhook_establish(&enc->enc_boot_hold_ch);
981 }
982
983 /*
984 * The softc field is set only once the enc is fully initialized
985 * so that we can rely on this field to detect partially
986 * initialized periph objects in the AC_FOUND_DEVICE handler.
987 */
988 periph->softc = enc;
989
990 cam_periph_unlock(periph);
991 if (enc->enc_vec.poll_status != NULL) {
992 err = enc_kproc_init(enc);
993 if (err) {
994 xpt_print(periph->path,
995 "error %d starting enc_daemon\n", err);
996 goto out;
997 }
998 }
999
1000 /*
1001 * Acquire a reference to the periph before we create the devfs
1002 * instance for it. We'll release this reference once the devfs
1003 * instance has been freed.
1004 */
1005 if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
1006 xpt_print(periph->path, "%s: lost periph during "
1007 "registration!\n", __func__);
1008 cam_periph_lock(periph);
1009
1010 return (CAM_REQ_CMP_ERR);
1011 }
1012
1013 enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number,
1014 UID_ROOT, GID_OPERATOR, 0600, "%s%d",
1015 periph->periph_name, periph->unit_number);
1016
1017 cam_periph_lock(periph);
1018 enc->enc_dev->si_drv1 = periph;
1019
1020 enc->enc_flags |= ENC_FLAG_INITIALIZED;
1021
1022 /*
1023 * Add an async callback so that we get notified if this
1024 * device goes away.
1025 */
1026 xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path);
1027
1028 switch (enc->enc_type) {
1029 default:
1030 case ENC_NONE:
1031 tname = "No ENC device";
1032 break;
1033 case ENC_SES_SCSI2:
1034 tname = "SCSI-2 ENC Device";
1035 break;
1036 case ENC_SES:
1037 tname = "SCSI-3 ENC Device";
1038 break;
1039 case ENC_SES_PASSTHROUGH:
1040 tname = "ENC Passthrough Device";
1041 break;
1042 case ENC_SAFT:
1043 tname = "SAF-TE Compliant Device";
1044 break;
1045 case ENC_SEMB_SES:
1046 tname = "SEMB SES Device";
1047 break;
1048 case ENC_SEMB_SAFT:
1049 tname = "SEMB SAF-TE Device";
1050 break;
1051 }
1052 xpt_announce_periph(periph, tname);
1053 status = CAM_REQ_CMP;
1054
1055 out:
1056 if (status != CAM_REQ_CMP)
1057 enc_dtor(periph);
1058 return (status);
1059 }
1060
Cache object: 38b6799e8897dac225c4073dcf091db5
|