scsi_enc.c revision 235911
1235911Smav/*-
2235911Smav * Copyright (c) 2000 Matthew Jacob
3235911Smav * All rights reserved.
4235911Smav *
5235911Smav * Redistribution and use in source and binary forms, with or without
6235911Smav * modification, are permitted provided that the following conditions
7235911Smav * are met:
8235911Smav * 1. Redistributions of source code must retain the above copyright
9235911Smav *    notice, this list of conditions, and the following disclaimer,
10235911Smav *    without modification, immediately at the beginning of the file.
11235911Smav * 2. The name of the author may not be used to endorse or promote products
12235911Smav *    derived from this software without specific prior written permission.
13235911Smav *
14235911Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15235911Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16235911Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17235911Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18235911Smav * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19235911Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20235911Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21235911Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22235911Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23235911Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24235911Smav * SUCH DAMAGE.
25235911Smav */
26235911Smav
27235911Smav#include <sys/cdefs.h>
28235911Smav__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_enc.c 235911 2012-05-24 14:07:44Z mav $");
29235911Smav
30235911Smav#include <sys/param.h>
31235911Smav
32235911Smav#include <sys/conf.h>
33235911Smav#include <sys/errno.h>
34235911Smav#include <sys/fcntl.h>
35235911Smav#include <sys/kernel.h>
36235911Smav#include <sys/kthread.h>
37235911Smav#include <sys/lock.h>
38235911Smav#include <sys/malloc.h>
39235911Smav#include <sys/mutex.h>
40235911Smav#include <sys/queue.h>
41235911Smav#include <sys/sx.h>
42235911Smav#include <sys/systm.h>
43235911Smav#include <sys/sysctl.h>
44235911Smav#include <sys/types.h>
45235911Smav
46235911Smav#include <machine/stdarg.h>
47235911Smav
48235911Smav#include <cam/cam.h>
49235911Smav#include <cam/cam_ccb.h>
50235911Smav#include <cam/cam_debug.h>
51235911Smav#include <cam/cam_periph.h>
52235911Smav#include <cam/cam_xpt_periph.h>
53235911Smav
54235911Smav#include <cam/scsi/scsi_all.h>
55235911Smav#include <cam/scsi/scsi_message.h>
56235911Smav#include <cam/scsi/scsi_enc.h>
57235911Smav#include <cam/scsi/scsi_enc_internal.h>
58235911Smav
59235911Smav#include <opt_enc.h>
60235911Smav
61235911SmavMALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers");
62235911Smav
63235911Smav/* Enclosure type independent driver */
64235911Smav
65235911Smav#define	SEN_ID		"UNISYS           SUN_SEN"
66235911Smav#define	SEN_ID_LEN	24
67235911Smav
68235911Smavstatic	d_open_t	enc_open;
69235911Smavstatic	d_close_t	enc_close;
70235911Smavstatic	d_ioctl_t	enc_ioctl;
71235911Smavstatic	periph_init_t	enc_init;
72235911Smavstatic  periph_ctor_t	enc_ctor;
73235911Smavstatic	periph_oninv_t	enc_oninvalidate;
74235911Smavstatic  periph_dtor_t   enc_dtor;
75235911Smavstatic  periph_start_t  enc_start;
76235911Smav
77235911Smavstatic void enc_async(void *, uint32_t, struct cam_path *, void *);
78235911Smavstatic enctyp enc_type(struct ccb_getdev *);
79235911Smav
80235911SmavSYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0,
81235911Smav            "CAM Enclosure Services driver");
82235911Smav
83235911Smavstatic struct periph_driver encdriver = {
84235911Smav	enc_init, "ses",
85235911Smav	TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0
86235911Smav};
87235911Smav
88235911SmavPERIPHDRIVER_DECLARE(enc, encdriver);
89235911Smav
90235911Smavstatic struct cdevsw enc_cdevsw = {
91235911Smav	.d_version =	D_VERSION,
92235911Smav	.d_open =	enc_open,
93235911Smav	.d_close =	enc_close,
94235911Smav	.d_ioctl =	enc_ioctl,
95235911Smav	.d_name =	"ses",
96235911Smav	.d_flags =	0,
97235911Smav};
98235911Smav
99235911Smavstatic void
100235911Smavenc_init(void)
101235911Smav{
102235911Smav	cam_status status;
103235911Smav
104235911Smav	/*
105235911Smav	 * Install a global async callback.  This callback will
106235911Smav	 * receive async callbacks like "new device found".
107235911Smav	 */
108235911Smav	status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL);
109235911Smav
110235911Smav	if (status != CAM_REQ_CMP) {
111235911Smav		printf("enc: Failed to attach master async callback "
112235911Smav		       "due to status 0x%x!\n", status);
113235911Smav	}
114235911Smav}
115235911Smav
116235911Smavstatic void
117235911Smavenc_oninvalidate(struct cam_periph *periph)
118235911Smav{
119235911Smav	struct enc_softc *enc;
120235911Smav
121235911Smav	enc = periph->softc;
122235911Smav
123235911Smav	enc->enc_flags |= ENC_FLAG_INVALID;
124235911Smav
125235911Smav	/* If the sub-driver has an invalidate routine, call it */
126235911Smav	if (enc->enc_vec.softc_invalidate != NULL)
127235911Smav		enc->enc_vec.softc_invalidate(enc);
128235911Smav
129235911Smav	/*
130235911Smav	 * Unregister any async callbacks.
131235911Smav	 */
132235911Smav	xpt_register_async(0, enc_async, periph, periph->path);
133235911Smav
134235911Smav	/*
135235911Smav	 * Shutdown our daemon.
136235911Smav	 */
137235911Smav	enc->enc_flags |= ENC_FLAG_SHUTDOWN;
138235911Smav	if (enc->enc_daemon != NULL) {
139235911Smav		/* Signal and wait for the ses daemon to terminate. */
140235911Smav		wakeup(enc->enc_daemon);
141235911Smav		/*
142235911Smav		 * We're called with the SIM mutex held, but we're dropping
143235911Smav		 * the update mutex here on sleep.  So we have to manually
144235911Smav		 * drop the SIM mutex.
145235911Smav		 */
146235911Smav		cam_periph_sleep(enc->periph, enc->enc_daemon,
147235911Smav				 PUSER, "thtrm", 0);
148235911Smav	}
149235911Smav	callout_drain(&enc->status_updater);
150235911Smav
151235911Smav	xpt_print(periph->path, "lost device\n");
152235911Smav}
153235911Smav
154235911Smavstatic void
155235911Smavenc_dtor(struct cam_periph *periph)
156235911Smav{
157235911Smav	struct enc_softc *enc;
158235911Smav
159235911Smav	enc = periph->softc;
160235911Smav
161235911Smav	xpt_print(periph->path, "removing device entry\n");
162235911Smav	cam_periph_unlock(periph);
163235911Smav	destroy_dev(enc->enc_dev);
164235911Smav	cam_periph_lock(periph);
165235911Smav
166235911Smav	/* If the sub-driver has a cleanup routine, call it */
167235911Smav	if (enc->enc_vec.softc_cleanup != NULL)
168235911Smav		enc->enc_vec.softc_cleanup(enc);
169235911Smav
170235911Smav	if (enc->enc_boot_hold_ch.ich_func != NULL) {
171235911Smav		config_intrhook_disestablish(&enc->enc_boot_hold_ch);
172235911Smav		enc->enc_boot_hold_ch.ich_func = NULL;
173235911Smav	}
174235911Smav
175235911Smav	ENC_FREE(enc);
176235911Smav}
177235911Smav
178235911Smavstatic void
179235911Smavenc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg)
180235911Smav{
181235911Smav	struct cam_periph *periph;
182235911Smav
183235911Smav	periph = (struct cam_periph *)callback_arg;
184235911Smav
185235911Smav	switch(code) {
186235911Smav	case AC_FOUND_DEVICE:
187235911Smav	{
188235911Smav		struct ccb_getdev *cgd;
189235911Smav		cam_status status;
190235911Smav		path_id_t path_id;
191235911Smav
192235911Smav		cgd = (struct ccb_getdev *)arg;
193235911Smav		if (arg == NULL) {
194235911Smav			break;
195235911Smav		}
196235911Smav
197235911Smav		if (enc_type(cgd) == ENC_NONE) {
198235911Smav			/*
199235911Smav			 * Schedule announcement of the ENC bindings for
200235911Smav			 * this device if it is managed by a SEP.
201235911Smav			 */
202235911Smav			path_id = xpt_path_path_id(path);
203235911Smav			xpt_lock_buses();
204235911Smav			TAILQ_FOREACH(periph, &encdriver.units, unit_links) {
205235911Smav				struct enc_softc *softc;
206235911Smav
207235911Smav				softc = (struct enc_softc *)periph->softc;
208235911Smav				if (xpt_path_path_id(periph->path) != path_id
209235911Smav				 || softc == NULL
210235911Smav				 || (softc->enc_flags & ENC_FLAG_INITIALIZED)
211235911Smav				  == 0
212235911Smav				 || softc->enc_vec.device_found == NULL)
213235911Smav					continue;
214235911Smav
215235911Smav				softc->enc_vec.device_found(softc);
216235911Smav			}
217235911Smav			xpt_unlock_buses();
218235911Smav			return;
219235911Smav		}
220235911Smav
221235911Smav		status = cam_periph_alloc(enc_ctor, enc_oninvalidate,
222235911Smav		    enc_dtor, enc_start, "ses", CAM_PERIPH_BIO,
223235911Smav		    cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd);
224235911Smav
225235911Smav		if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) {
226235911Smav			printf("enc_async: Unable to probe new device due to "
227235911Smav			    "status 0x%x\n", status);
228235911Smav		}
229235911Smav		break;
230235911Smav	}
231235911Smav	default:
232235911Smav		cam_periph_async(periph, code, path, arg);
233235911Smav		break;
234235911Smav	}
235235911Smav}
236235911Smav
237235911Smavstatic int
238235911Smavenc_open(struct cdev *dev, int flags, int fmt, struct thread *td)
239235911Smav{
240235911Smav	struct cam_periph *periph;
241235911Smav	struct enc_softc *softc;
242235911Smav	int error = 0;
243235911Smav
244235911Smav	periph = (struct cam_periph *)dev->si_drv1;
245235911Smav	if (periph == NULL) {
246235911Smav		return (ENXIO);
247235911Smav	}
248235911Smav
249235911Smav	if (cam_periph_acquire(periph) != CAM_REQ_CMP)
250235911Smav		return (ENXIO);
251235911Smav
252235911Smav	cam_periph_lock(periph);
253235911Smav
254235911Smav	softc = (struct enc_softc *)periph->softc;
255235911Smav
256235911Smav	if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
257235911Smav		error = ENXIO;
258235911Smav		goto out;
259235911Smav	}
260235911Smav	if (softc->enc_flags & ENC_FLAG_INVALID) {
261235911Smav		error = ENXIO;
262235911Smav		goto out;
263235911Smav	}
264235911Smav
265235911Smavout:
266235911Smav	cam_periph_unlock(periph);
267235911Smav	if (error) {
268235911Smav		cam_periph_release(periph);
269235911Smav	}
270235911Smav	return (error);
271235911Smav}
272235911Smav
273235911Smavstatic int
274235911Smavenc_close(struct cdev *dev, int flag, int fmt, struct thread *td)
275235911Smav{
276235911Smav	struct cam_periph *periph;
277235911Smav	struct enc_softc *softc;
278235911Smav
279235911Smav	periph = (struct cam_periph *)dev->si_drv1;
280235911Smav	if (periph == NULL)
281235911Smav		return (ENXIO);
282235911Smav
283235911Smav	cam_periph_lock(periph);
284235911Smav
285235911Smav	softc = (struct enc_softc *)periph->softc;
286235911Smav
287235911Smav	cam_periph_unlock(periph);
288235911Smav	cam_periph_release(periph);
289235911Smav
290235911Smav	return (0);
291235911Smav}
292235911Smav
293235911Smavstatic void
294235911Smavenc_start(struct cam_periph *p, union ccb *sccb)
295235911Smav{
296235911Smav	struct enc_softc *enc;
297235911Smav
298235911Smav	enc = p->softc;
299235911Smav	ENC_DLOG(enc, "%s enter imm=%d prio=%d\n",
300235911Smav	    __func__, p->immediate_priority, p->pinfo.priority);
301235911Smav	if (p->immediate_priority <= p->pinfo.priority) {
302235911Smav		SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle);
303235911Smav		p->immediate_priority = CAM_PRIORITY_NONE;
304235911Smav		wakeup(&p->ccb_list);
305235911Smav	} else
306235911Smav		xpt_release_ccb(sccb);
307235911Smav	ENC_DLOG(enc, "%s exit\n", __func__);
308235911Smav}
309235911Smav
310235911Smavvoid
311235911Smavenc_done(struct cam_periph *periph, union ccb *dccb)
312235911Smav{
313235911Smav	wakeup(&dccb->ccb_h.cbfcnp);
314235911Smav}
315235911Smav
316235911Smavint
317235911Smavenc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags)
318235911Smav{
319235911Smav	struct enc_softc *softc;
320235911Smav	struct cam_periph *periph;
321235911Smav
322235911Smav	periph = xpt_path_periph(ccb->ccb_h.path);
323235911Smav	softc = (struct enc_softc *)periph->softc;
324235911Smav
325235911Smav	return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb));
326235911Smav}
327235911Smav
328235911Smavstatic int
329235911Smavenc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag,
330235911Smav	 struct thread *td)
331235911Smav{
332235911Smav	struct cam_periph *periph;
333235911Smav	encioc_enc_status_t tmp;
334235911Smav	encioc_string_t sstr;
335235911Smav	encioc_elm_status_t elms;
336235911Smav	encioc_elm_desc_t elmd;
337235911Smav	encioc_elm_devnames_t elmdn;
338235911Smav	encioc_element_t *uelm;
339235911Smav	enc_softc_t *enc;
340235911Smav	enc_cache_t *cache;
341235911Smav	void *addr;
342235911Smav	int error, i;
343235911Smav
344235911Smav
345235911Smav	if (arg_addr)
346235911Smav		addr = *((caddr_t *) arg_addr);
347235911Smav	else
348235911Smav		addr = NULL;
349235911Smav
350235911Smav	periph = (struct cam_periph *)dev->si_drv1;
351235911Smav	if (periph == NULL)
352235911Smav		return (ENXIO);
353235911Smav
354235911Smav	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n"));
355235911Smav
356235911Smav	cam_periph_lock(periph);
357235911Smav	enc = (struct enc_softc *)periph->softc;
358235911Smav	cache = &enc->enc_cache;
359235911Smav
360235911Smav	/*
361235911Smav	 * Now check to see whether we're initialized or not.
362235911Smav	 * This actually should never fail as we're not supposed
363235911Smav	 * to get past enc_open w/o successfully initializing
364235911Smav	 * things.
365235911Smav	 */
366235911Smav	if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) {
367235911Smav		cam_periph_unlock(periph);
368235911Smav		return (ENXIO);
369235911Smav	}
370235911Smav	cam_periph_unlock(periph);
371235911Smav
372235911Smav	error = 0;
373235911Smav
374235911Smav	CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
375235911Smav	    ("trying to do ioctl %#lx\n", cmd));
376235911Smav
377235911Smav	/*
378235911Smav	 * If this command can change the device's state,
379235911Smav	 * we must have the device open for writing.
380235911Smav	 *
381235911Smav	 * For commands that get information about the
382235911Smav	 * device- we don't need to lock the peripheral
383235911Smav	 * if we aren't running a command.  The periph
384235911Smav	 * also can't go away while a user process has
385235911Smav	 * it open.
386235911Smav	 */
387235911Smav	switch (cmd) {
388235911Smav	case ENCIOC_GETNELM:
389235911Smav	case ENCIOC_GETELMMAP:
390235911Smav	case ENCIOC_GETENCSTAT:
391235911Smav	case ENCIOC_GETELMSTAT:
392235911Smav	case ENCIOC_GETELMDESC:
393235911Smav	case ENCIOC_GETELMDEVNAMES:
394235911Smav		break;
395235911Smav	default:
396235911Smav		if ((flag & FWRITE) == 0) {
397235911Smav			return (EBADF);
398235911Smav		}
399235911Smav	}
400235911Smav
401235911Smav	/*
402235911Smav	 * XXX The values read here are only valid for the current
403235911Smav	 *     configuration generation.  We need these ioctls
404235911Smav	 *     to also pass in/out a generation number.
405235911Smav	 */
406235911Smav	sx_slock(&enc->enc_cache_lock);
407235911Smav	switch (cmd) {
408235911Smav	case ENCIOC_GETNELM:
409235911Smav		error = copyout(&cache->nelms, addr, sizeof (cache->nelms));
410235911Smav		break;
411235911Smav
412235911Smav	case ENCIOC_GETELMMAP:
413235911Smav		for (uelm = addr, i = 0; i != cache->nelms; i++) {
414235911Smav			encioc_element_t kelm;
415235911Smav			kelm.elm_idx = i;
416235911Smav			kelm.elm_subenc_id = cache->elm_map[i].subenclosure;
417235911Smav			kelm.elm_type = cache->elm_map[i].enctype;
418235911Smav			error = copyout(&kelm, &uelm[i], sizeof(kelm));
419235911Smav			if (error)
420235911Smav				break;
421235911Smav		}
422235911Smav		break;
423235911Smav
424235911Smav	case ENCIOC_GETENCSTAT:
425235911Smav		cam_periph_lock(periph);
426235911Smav		error = enc->enc_vec.get_enc_status(enc, 1);
427235911Smav		if (error) {
428235911Smav			cam_periph_unlock(periph);
429235911Smav			break;
430235911Smav		}
431235911Smav		tmp = cache->enc_status;
432235911Smav		cam_periph_unlock(periph);
433235911Smav		error = copyout(&tmp, addr, sizeof(tmp));
434235911Smav		cache->enc_status = tmp;
435235911Smav		break;
436235911Smav
437235911Smav	case ENCIOC_SETENCSTAT:
438235911Smav		error = copyin(addr, &tmp, sizeof(tmp));
439235911Smav		if (error)
440235911Smav			break;
441235911Smav		cam_periph_lock(periph);
442235911Smav		error = enc->enc_vec.set_enc_status(enc, tmp, 1);
443235911Smav		cam_periph_unlock(periph);
444235911Smav		break;
445235911Smav
446235911Smav	case ENCIOC_GETSTRING:
447235911Smav	case ENCIOC_SETSTRING:
448235911Smav		if (enc->enc_vec.handle_string == NULL) {
449235911Smav			error = EINVAL;
450235911Smav			break;
451235911Smav		}
452235911Smav		error = copyin(addr, &sstr, sizeof(sstr));
453235911Smav		if (error)
454235911Smav			break;
455235911Smav		cam_periph_lock(periph);
456235911Smav		error = enc->enc_vec.handle_string(enc, &sstr, cmd);
457235911Smav		cam_periph_unlock(periph);
458235911Smav		break;
459235911Smav
460235911Smav	case ENCIOC_GETELMSTAT:
461235911Smav		error = copyin(addr, &elms, sizeof(elms));
462235911Smav		if (error)
463235911Smav			break;
464235911Smav		if (elms.elm_idx >= cache->nelms) {
465235911Smav			error = EINVAL;
466235911Smav			break;
467235911Smav		}
468235911Smav		cam_periph_lock(periph);
469235911Smav		error = enc->enc_vec.get_elm_status(enc, &elms, 1);
470235911Smav		cam_periph_unlock(periph);
471235911Smav		if (error)
472235911Smav			break;
473235911Smav		error = copyout(&elms, addr, sizeof(elms));
474235911Smav		break;
475235911Smav
476235911Smav	case ENCIOC_GETELMDESC:
477235911Smav		error = copyin(addr, &elmd, sizeof(elmd));
478235911Smav		if (error)
479235911Smav			break;
480235911Smav		if (elmd.elm_idx >= cache->nelms) {
481235911Smav			error = EINVAL;
482235911Smav			break;
483235911Smav		}
484235911Smav		if (enc->enc_vec.get_elm_desc != NULL) {
485235911Smav			error = enc->enc_vec.get_elm_desc(enc, &elmd);
486235911Smav			if (error)
487235911Smav				break;
488235911Smav		} else
489235911Smav			elmd.elm_desc_len = 0;
490235911Smav		error = copyout(&elmd, addr, sizeof(elmd));
491235911Smav		break;
492235911Smav
493235911Smav	case ENCIOC_GETELMDEVNAMES:
494235911Smav		if (enc->enc_vec.get_elm_devnames == NULL) {
495235911Smav			error = EINVAL;
496235911Smav			break;
497235911Smav		}
498235911Smav		error = copyin(addr, &elmdn, sizeof(elmdn));
499235911Smav		if (error)
500235911Smav			break;
501235911Smav		if (elmdn.elm_idx >= cache->nelms) {
502235911Smav			error = EINVAL;
503235911Smav			break;
504235911Smav		}
505235911Smav		cam_periph_lock(periph);
506235911Smav		error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn);
507235911Smav		cam_periph_unlock(periph);
508235911Smav		if (error)
509235911Smav			break;
510235911Smav		error = copyout(&elmdn, addr, sizeof(elmdn));
511235911Smav		break;
512235911Smav
513235911Smav	case ENCIOC_SETELMSTAT:
514235911Smav		error = copyin(addr, &elms, sizeof(elms));
515235911Smav		if (error)
516235911Smav			break;
517235911Smav
518235911Smav		if (elms.elm_idx >= cache->nelms) {
519235911Smav			error = EINVAL;
520235911Smav			break;
521235911Smav		}
522235911Smav		cam_periph_lock(periph);
523235911Smav		error = enc->enc_vec.set_elm_status(enc, &elms, 1);
524235911Smav		cam_periph_unlock(periph);
525235911Smav
526235911Smav		break;
527235911Smav
528235911Smav	case ENCIOC_INIT:
529235911Smav
530235911Smav		cam_periph_lock(periph);
531235911Smav		error = enc->enc_vec.init_enc(enc);
532235911Smav		cam_periph_unlock(periph);
533235911Smav		break;
534235911Smav
535235911Smav	default:
536235911Smav		cam_periph_lock(periph);
537235911Smav		error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error);
538235911Smav		cam_periph_unlock(periph);
539235911Smav		break;
540235911Smav	}
541235911Smav	sx_sunlock(&enc->enc_cache_lock);
542235911Smav	return (error);
543235911Smav}
544235911Smav
545235911Smavint
546235911Smavenc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp)
547235911Smav{
548235911Smav	int error, dlen, tdlen;
549235911Smav	ccb_flags ddf;
550235911Smav	union ccb *ccb;
551235911Smav
552235911Smav	CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE,
553235911Smav	    ("entering enc_runcmd\n"));
554235911Smav	if (dptr) {
555235911Smav		if ((dlen = *dlenp) < 0) {
556235911Smav			dlen = -dlen;
557235911Smav			ddf = CAM_DIR_OUT;
558235911Smav		} else {
559235911Smav			ddf = CAM_DIR_IN;
560235911Smav		}
561235911Smav	} else {
562235911Smav		dlen = 0;
563235911Smav		ddf = CAM_DIR_NONE;
564235911Smav	}
565235911Smav
566235911Smav	if (cdbl > IOCDBLEN) {
567235911Smav		cdbl = IOCDBLEN;
568235911Smav	}
569235911Smav
570235911Smav	ccb = cam_periph_getccb(enc->periph, 1);
571235911Smav	if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) {
572235911Smav		tdlen = min(dlen, 1020);
573235911Smav		tdlen = (tdlen + 3) & ~3;
574235911Smav		cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen,
575235911Smav		    30 * 1000);
576235911Smav		if (cdb[0] == RECEIVE_DIAGNOSTIC)
577235911Smav			ata_28bit_cmd(&ccb->ataio,
578235911Smav			    ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4);
579235911Smav		else if (cdb[0] == SEND_DIAGNOSTIC)
580235911Smav			ata_28bit_cmd(&ccb->ataio,
581235911Smav			    ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
582235911Smav			    0x82, tdlen / 4);
583235911Smav		else if (cdb[0] == READ_BUFFER)
584235911Smav			ata_28bit_cmd(&ccb->ataio,
585235911Smav			    ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4);
586235911Smav		else
587235911Smav			ata_28bit_cmd(&ccb->ataio,
588235911Smav			    ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0,
589235911Smav			    0x80, tdlen / 4);
590235911Smav	} else {
591235911Smav		tdlen = dlen;
592235911Smav		cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG,
593235911Smav		    dptr, dlen, sizeof (struct scsi_sense_data), cdbl,
594235911Smav		    60 * 1000);
595235911Smav		bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl);
596235911Smav	}
597235911Smav
598235911Smav	error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
599235911Smav	if (error) {
600235911Smav		if (dptr) {
601235911Smav			*dlenp = dlen;
602235911Smav		}
603235911Smav	} else {
604235911Smav		if (dptr) {
605235911Smav			if (ccb->ccb_h.func_code == XPT_ATA_IO)
606235911Smav				*dlenp = ccb->ataio.resid;
607235911Smav			else
608235911Smav				*dlenp = ccb->csio.resid;
609235911Smav			*dlenp += tdlen - dlen;
610235911Smav		}
611235911Smav	}
612235911Smav	xpt_release_ccb(ccb);
613235911Smav	CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
614235911Smav	    ("exiting enc_runcmd: *dlenp = %d\n", *dlenp));
615235911Smav	return (error);
616235911Smav}
617235911Smav
618235911Smavvoid
619235911Smavenc_log(struct enc_softc *enc, const char *fmt, ...)
620235911Smav{
621235911Smav	va_list ap;
622235911Smav
623235911Smav	printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number);
624235911Smav	va_start(ap, fmt);
625235911Smav	vprintf(fmt, ap);
626235911Smav	va_end(ap);
627235911Smav}
628235911Smav
629235911Smav/*
630235911Smav * The code after this point runs on many platforms,
631235911Smav * so forgive the slightly awkward and nonconforming
632235911Smav * appearance.
633235911Smav */
634235911Smav
635235911Smav/*
636235911Smav * Is this a device that supports enclosure services?
637235911Smav *
638235911Smav * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's
639235911Smav * an ENC device. If it happens to be an old UNISYS SEN device, we can
640235911Smav * handle that too.
641235911Smav */
642235911Smav
643235911Smav#define	SAFTE_START	44
644235911Smav#define	SAFTE_END	50
645235911Smav#define	SAFTE_LEN	SAFTE_END-SAFTE_START
646235911Smav
647235911Smavstatic enctyp
648235911Smavenc_type(struct ccb_getdev *cgd)
649235911Smav{
650235911Smav	int buflen;
651235911Smav	unsigned char *iqd;
652235911Smav
653235911Smav	if (cgd->protocol == PROTO_SEMB) {
654235911Smav		iqd = (unsigned char *)&cgd->ident_data;
655235911Smav		if (STRNCMP(iqd + 43, "S-E-S", 5) == 0)
656235911Smav			return (ENC_SEMB_SES);
657235911Smav		else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0)
658235911Smav			return (ENC_SEMB_SAFT);
659235911Smav		return (ENC_NONE);
660235911Smav
661235911Smav	} else if (cgd->protocol != PROTO_SCSI)
662235911Smav		return (ENC_NONE);
663235911Smav
664235911Smav	iqd = (unsigned char *)&cgd->inq_data;
665235911Smav	buflen = min(sizeof(cgd->inq_data),
666235911Smav	    SID_ADDITIONAL_LENGTH(&cgd->inq_data));
667235911Smav	if (buflen < 8+SEN_ID_LEN)
668235911Smav		return (ENC_NONE);
669235911Smav
670235911Smav	if ((iqd[0] & 0x1f) == T_ENCLOSURE) {
671235911Smav		if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) {
672235911Smav			return (ENC_SEN);
673235911Smav		} else if ((iqd[2] & 0x7) > 2) {
674235911Smav			return (ENC_SES);
675235911Smav		} else {
676235911Smav			return (ENC_SES_SCSI2);
677235911Smav		}
678235911Smav		return (ENC_NONE);
679235911Smav	}
680235911Smav
681235911Smav#ifdef	ENC_ENABLE_PASSTHROUGH
682235911Smav	if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) {
683235911Smav		/*
684235911Smav		 * PassThrough Device.
685235911Smav		 */
686235911Smav		return (ENC_ENC_PASSTHROUGH);
687235911Smav	}
688235911Smav#endif
689235911Smav
690235911Smav	/*
691235911Smav	 * The comparison is short for a reason-
692235911Smav	 * some vendors were chopping it short.
693235911Smav	 */
694235911Smav
695235911Smav	if (buflen < SAFTE_END - 2) {
696235911Smav		return (ENC_NONE);
697235911Smav	}
698235911Smav
699235911Smav	if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) {
700235911Smav		return (ENC_SAFT);
701235911Smav	}
702235911Smav	return (ENC_NONE);
703235911Smav}
704235911Smav
705235911Smav/*================== Enclosure Monitoring/Processing Daemon ==================*/
706235911Smav/**
707235911Smav * \brief Queue an update request for a given action, if needed.
708235911Smav *
709235911Smav * \param enc		SES softc to queue the request for.
710235911Smav * \param action	Action requested.
711235911Smav */
712235911Smavvoid
713235911Smavenc_update_request(enc_softc_t *enc, uint32_t action)
714235911Smav{
715235911Smav	if ((enc->pending_actions & (0x1 << action)) == 0) {
716235911Smav		enc->pending_actions |= (0x1 << action);
717235911Smav		ENC_DLOG(enc, "%s: queing requested action %d\n",
718235911Smav		    __func__, action);
719235911Smav		if (enc->current_action == ENC_UPDATE_NONE)
720235911Smav			wakeup(enc->enc_daemon);
721235911Smav	} else {
722235911Smav		ENC_DLOG(enc, "%s: ignoring requested action %d - "
723235911Smav		    "Already queued\n", __func__, action);
724235911Smav	}
725235911Smav}
726235911Smav
727235911Smav/**
728235911Smav * \brief Invoke the handler of the highest priority pending
729235911Smav *	  state in the SES state machine.
730235911Smav *
731235911Smav * \param enc  The SES instance invoking the state machine.
732235911Smav */
733235911Smavstatic void
734235911Smavenc_fsm_step(enc_softc_t *enc)
735235911Smav{
736235911Smav	union ccb            *ccb;
737235911Smav	uint8_t              *buf;
738235911Smav	struct enc_fsm_state *cur_state;
739235911Smav	int		      error;
740235911Smav	uint32_t	      xfer_len;
741235911Smav
742235911Smav	ENC_DLOG(enc, "%s enter %p\n", __func__, enc);
743235911Smav
744235911Smav	enc->current_action   = ffs(enc->pending_actions) - 1;
745235911Smav	enc->pending_actions &= ~(0x1 << enc->current_action);
746235911Smav
747235911Smav	cur_state = &enc->enc_fsm_states[enc->current_action];
748235911Smav
749235911Smav	buf = NULL;
750235911Smav	if (cur_state->buf_size != 0) {
751235911Smav		cam_periph_unlock(enc->periph);
752235911Smav		buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO);
753235911Smav		cam_periph_lock(enc->periph);
754235911Smav	}
755235911Smav
756235911Smav	error = 0;
757235911Smav	ccb   = NULL;
758235911Smav	if (cur_state->fill != NULL) {
759235911Smav		ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL);
760235911Smav
761235911Smav		error = cur_state->fill(enc, cur_state, ccb, buf);
762235911Smav		if (error != 0)
763235911Smav			goto done;
764235911Smav
765235911Smav		error = cam_periph_runccb(ccb, cur_state->error,
766235911Smav					  ENC_CFLAGS,
767235911Smav					  ENC_FLAGS|SF_QUIET_IR, NULL);
768235911Smav	}
769235911Smav
770235911Smav	if (ccb != NULL) {
771235911Smav		if (ccb->ccb_h.func_code == XPT_ATA_IO)
772235911Smav			xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
773235911Smav		else
774235911Smav			xfer_len = ccb->csio.dxfer_len - ccb->csio.resid;
775235911Smav	} else
776235911Smav		xfer_len = 0;
777235911Smav
778235911Smav	cam_periph_unlock(enc->periph);
779235911Smav	cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len);
780235911Smav	cam_periph_lock(enc->periph);
781235911Smav
782235911Smavdone:
783235911Smav	ENC_DLOG(enc, "%s exit - result %d\n", __func__, error);
784235911Smav	ENC_FREE_AND_NULL(buf);
785235911Smav	if (ccb != NULL)
786235911Smav		xpt_release_ccb(ccb);
787235911Smav}
788235911Smav
789235911Smav/**
790235911Smav * \invariant Called with cam_periph mutex held.
791235911Smav */
792235911Smavstatic void
793235911Smavenc_status_updater(void *arg)
794235911Smav{
795235911Smav	enc_softc_t *enc;
796235911Smav
797235911Smav	enc = arg;
798235911Smav	if (enc->enc_vec.poll_status != NULL)
799235911Smav		enc->enc_vec.poll_status(enc);
800235911Smav}
801235911Smav
802235911Smavstatic void
803235911Smavenc_daemon(void *arg)
804235911Smav{
805235911Smav	enc_softc_t *enc;
806235911Smav
807235911Smav	enc = arg;
808235911Smav
809235911Smav	cam_periph_lock(enc->periph);
810235911Smav	while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) {
811235911Smav		if (enc->pending_actions == 0) {
812235911Smav			struct intr_config_hook *hook;
813235911Smav
814235911Smav			/*
815235911Smav			 * Reset callout and msleep, or
816235911Smav			 * issue timed task completion
817235911Smav			 * status command.
818235911Smav			 */
819235911Smav			enc->current_action = ENC_UPDATE_NONE;
820235911Smav
821235911Smav			/*
822235911Smav			 * We've been through our state machine at least
823235911Smav			 * once.  Allow the transition to userland.
824235911Smav			 */
825235911Smav			hook = &enc->enc_boot_hold_ch;
826235911Smav			if (hook->ich_func != NULL) {
827235911Smav				config_intrhook_disestablish(hook);
828235911Smav				hook->ich_func = NULL;
829235911Smav			}
830235911Smav
831235911Smav			callout_reset(&enc->status_updater, 60*hz,
832235911Smav				      enc_status_updater, enc);
833235911Smav
834235911Smav			cam_periph_sleep(enc->periph, enc->enc_daemon,
835235911Smav					 PUSER, "idle", 0);
836235911Smav		} else {
837235911Smav			enc_fsm_step(enc);
838235911Smav		}
839235911Smav	}
840235911Smav	enc->enc_daemon = NULL;
841235911Smav	cam_periph_unlock(enc->periph);
842235911Smav	kproc_exit(0);
843235911Smav}
844235911Smav
845235911Smavstatic int
846235911Smavenc_kproc_init(enc_softc_t *enc)
847235911Smav{
848235911Smav	int result;
849235911Smav
850235911Smav	callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0);
851235911Smav
852235911Smav	result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0,
853235911Smav			      /*stackpgs*/0, "enc_daemon%d",
854235911Smav			      enc->periph->unit_number);
855235911Smav	if (result == 0) {
856235911Smav		/* Do an initial load of all page data. */
857235911Smav		cam_periph_lock(enc->periph);
858235911Smav		enc->enc_vec.poll_status(enc);
859235911Smav		cam_periph_unlock(enc->periph);
860235911Smav	}
861235911Smav	return (result);
862235911Smav}
863235911Smav
864235911Smav/**
865235911Smav * \brief Interrupt configuration hook callback associated with
866235911Smav *        enc_boot_hold_ch.
867235911Smav *
868235911Smav * Since interrupts are always functional at the time of enclosure
869235911Smav * configuration, there is nothing to be done when the callback occurs.
870235911Smav * This hook is only registered to hold up boot processing while initial
871235911Smav * eclosure processing occurs.
872235911Smav *
873235911Smav * \param arg  The enclosure softc, but currently unused in this callback.
874235911Smav */
875235911Smavstatic void
876235911Smavenc_nop_confighook_cb(void *arg __unused)
877235911Smav{
878235911Smav}
879235911Smav
880235911Smavstatic cam_status
881235911Smavenc_ctor(struct cam_periph *periph, void *arg)
882235911Smav{
883235911Smav	cam_status status = CAM_REQ_CMP_ERR;
884235911Smav	int err;
885235911Smav	enc_softc_t *enc;
886235911Smav	struct ccb_getdev *cgd;
887235911Smav	char *tname;
888235911Smav
889235911Smav	cgd = (struct ccb_getdev *)arg;
890235911Smav	if (periph == NULL) {
891235911Smav		printf("enc_ctor: periph was NULL!!\n");
892235911Smav		goto out;
893235911Smav	}
894235911Smav
895235911Smav	if (cgd == NULL) {
896235911Smav		printf("enc_ctor: no getdev CCB, can't register device\n");
897235911Smav		goto out;
898235911Smav	}
899235911Smav
900235911Smav	enc = ENC_MALLOCZ(sizeof(*enc));
901235911Smav	if (enc == NULL) {
902235911Smav		printf("enc_ctor: Unable to probe new device. "
903235911Smav		       "Unable to allocate enc\n");
904235911Smav		goto out;
905235911Smav	}
906235911Smav	enc->periph = periph;
907235911Smav	enc->current_action = ENC_UPDATE_INVALID;
908235911Smav
909235911Smav	enc->enc_type = enc_type(cgd);
910235911Smav	sx_init(&enc->enc_cache_lock, "enccache");
911235911Smav
912235911Smav	switch (enc->enc_type) {
913235911Smav	case ENC_SES:
914235911Smav	case ENC_SES_SCSI2:
915235911Smav	case ENC_SES_PASSTHROUGH:
916235911Smav	case ENC_SEMB_SES:
917235911Smav		err = ses_softc_init(enc);
918235911Smav		break;
919235911Smav	case ENC_SAFT:
920235911Smav	case ENC_SEMB_SAFT:
921235911Smav		err = safte_softc_init(enc);
922235911Smav		break;
923235911Smav	case ENC_SEN:
924235911Smav	case ENC_NONE:
925235911Smav	default:
926235911Smav		ENC_FREE(enc);
927235911Smav		return (CAM_REQ_CMP_ERR);
928235911Smav	}
929235911Smav
930235911Smav	if (err) {
931235911Smav		xpt_print(periph->path, "error %d initializing\n", err);
932235911Smav		goto out;
933235911Smav	}
934235911Smav
935235911Smav	/*
936235911Smav	 * Hold off userland until we have made at least one pass
937235911Smav	 * through our state machine so that physical path data is
938235911Smav	 * present.
939235911Smav	 */
940235911Smav	if (enc->enc_vec.poll_status != NULL) {
941235911Smav		enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb;
942235911Smav		enc->enc_boot_hold_ch.ich_arg = enc;
943235911Smav		config_intrhook_establish(&enc->enc_boot_hold_ch);
944235911Smav	}
945235911Smav
946235911Smav	/*
947235911Smav	 * The softc field is set only once the enc is fully initialized
948235911Smav	 * so that we can rely on this field to detect partially
949235911Smav	 * initialized periph objects in the AC_FOUND_DEVICE handler.
950235911Smav	 */
951235911Smav	periph->softc = enc;
952235911Smav
953235911Smav	cam_periph_unlock(periph);
954235911Smav	if (enc->enc_vec.poll_status != NULL) {
955235911Smav		err = enc_kproc_init(enc);
956235911Smav		if (err) {
957235911Smav			xpt_print(periph->path,
958235911Smav				  "error %d string enc_daemon\n", err);
959235911Smav			goto out;
960235911Smav		}
961235911Smav	}
962235911Smav	enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number,
963235911Smav	    UID_ROOT, GID_OPERATOR, 0600, "%s%d",
964235911Smav	    periph->periph_name, periph->unit_number);
965235911Smav	cam_periph_lock(periph);
966235911Smav	enc->enc_dev->si_drv1 = periph;
967235911Smav
968235911Smav	enc->enc_flags |= ENC_FLAG_INITIALIZED;
969235911Smav
970235911Smav	/*
971235911Smav	 * Add an async callback so that we get notified if this
972235911Smav	 * device goes away.
973235911Smav	 */
974235911Smav	xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path);
975235911Smav
976235911Smav	switch (enc->enc_type) {
977235911Smav	default:
978235911Smav	case ENC_NONE:
979235911Smav		tname = "No ENC device";
980235911Smav		break;
981235911Smav	case ENC_SES_SCSI2:
982235911Smav		tname = "SCSI-2 ENC Device";
983235911Smav		break;
984235911Smav	case ENC_SES:
985235911Smav		tname = "SCSI-3 ENC Device";
986235911Smav		break;
987235911Smav        case ENC_SES_PASSTHROUGH:
988235911Smav		tname = "ENC Passthrough Device";
989235911Smav		break;
990235911Smav        case ENC_SEN:
991235911Smav		tname = "UNISYS SEN Device (NOT HANDLED YET)";
992235911Smav		break;
993235911Smav        case ENC_SAFT:
994235911Smav		tname = "SAF-TE Compliant Device";
995235911Smav		break;
996235911Smav	case ENC_SEMB_SES:
997235911Smav		tname = "SEMB SES Device";
998235911Smav		break;
999235911Smav	case ENC_SEMB_SAFT:
1000235911Smav		tname = "SEMB SAF-TE Device";
1001235911Smav		break;
1002235911Smav	}
1003235911Smav	xpt_announce_periph(periph, tname);
1004235911Smav	status = CAM_REQ_CMP;
1005235911Smav
1006235911Smavout:
1007235911Smav	if (status != CAM_REQ_CMP)
1008235911Smav		enc_dtor(periph);
1009235911Smav	return (status);
1010235911Smav}
1011235911Smav
1012