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