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