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