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