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