scsi_enc_ses.c revision 300589
1/*-
2 * Copyright (c) 2000 Matthew Jacob
3 * Copyright (c) 2010 Spectra Logic Corporation
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer,
11 *    without modification, immediately at the beginning of the file.
12 * 2. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28/**
29 * \file scsi_enc_ses.c
30 *
31 * Structures and routines specific && private to SES only
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: stable/10/sys/cam/scsi/scsi_enc_ses.c 300589 2016-05-24 07:21:23Z mav $");
36
37#include <sys/param.h>
38
39#include <sys/ctype.h>
40#include <sys/errno.h>
41#include <sys/kernel.h>
42#include <sys/lock.h>
43#include <sys/malloc.h>
44#include <sys/mutex.h>
45#include <sys/queue.h>
46#include <sys/sbuf.h>
47#include <sys/sx.h>
48#include <sys/systm.h>
49#include <sys/types.h>
50
51#include <cam/cam.h>
52#include <cam/cam_ccb.h>
53#include <cam/cam_xpt_periph.h>
54#include <cam/cam_periph.h>
55
56#include <cam/scsi/scsi_message.h>
57#include <cam/scsi/scsi_enc.h>
58#include <cam/scsi/scsi_enc_internal.h>
59
60/* SES Native Type Device Support */
61
62/* SES Diagnostic Page Codes */
63typedef enum {
64	SesSupportedPages	= 0x0,
65	SesConfigPage		= 0x1,
66	SesControlPage		= 0x2,
67	SesStatusPage		= SesControlPage,
68	SesHelpTxt		= 0x3,
69	SesStringOut		= 0x4,
70	SesStringIn		= SesStringOut,
71	SesThresholdOut		= 0x5,
72	SesThresholdIn		= SesThresholdOut,
73	SesArrayControl		= 0x6,	/* Obsolete in SES v2 */
74	SesArrayStatus		= SesArrayControl,
75	SesElementDescriptor	= 0x7,
76	SesShortStatus		= 0x8,
77	SesEnclosureBusy	= 0x9,
78	SesAddlElementStatus	= 0xa
79} SesDiagPageCodes;
80
81typedef struct ses_type {
82	const struct ses_elm_type_desc  *hdr;
83	const char			*text;
84} ses_type_t;
85
86typedef struct ses_comstat {
87	uint8_t	comstatus;
88	uint8_t	comstat[3];
89} ses_comstat_t;
90
91typedef union ses_addl_data {
92	struct ses_elm_sas_device_phy *sasdev_phys;
93	struct ses_elm_sas_expander_phy *sasexp_phys;
94	struct ses_elm_sas_port_phy *sasport_phys;
95	struct ses_fcobj_port *fc_ports;
96} ses_add_data_t;
97
98typedef struct ses_addl_status {
99	struct ses_elm_addlstatus_base_hdr *hdr;
100	union {
101		union ses_fcobj_hdr *fc;
102		union ses_elm_sas_hdr *sas;
103	} proto_hdr;
104	union ses_addl_data proto_data;	/* array sizes stored in header */
105} ses_add_status_t;
106
107typedef struct ses_element {
108	uint8_t eip;			/* eip bit is set */
109	uint16_t descr_len;		/* length of the descriptor */
110	char *descr;			/* descriptor for this object */
111	struct ses_addl_status addl;	/* additional status info */
112} ses_element_t;
113
114typedef struct ses_control_request {
115	int	      elm_idx;
116	ses_comstat_t elm_stat;
117	int	      result;
118	TAILQ_ENTRY(ses_control_request) links;
119} ses_control_request_t;
120TAILQ_HEAD(ses_control_reqlist, ses_control_request);
121typedef struct ses_control_reqlist ses_control_reqlist_t;
122enum {
123	SES_SETSTATUS_ENC_IDX = -1
124};
125
126static void
127ses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result)
128{
129	ses_control_request_t *req;
130
131	while ((req = TAILQ_FIRST(reqlist)) != NULL) {
132		TAILQ_REMOVE(reqlist, req, links);
133		req->result = result;
134		wakeup(req);
135	}
136}
137
138enum ses_iter_index_values {
139	/**
140	 * \brief  Value of an initialized but invalid index
141	 *         in a ses_iterator object.
142	 *
143	 * This value is used for the  individual_element_index of
144	 * overal status elements and for all index types when
145	 * an iterator is first initialized.
146	 */
147	ITERATOR_INDEX_INVALID = -1,
148
149	/**
150	 * \brief  Value of an index in a ses_iterator object
151	 *	   when the iterator has traversed past the last
152	 *	   valid element..
153	 */
154	ITERATOR_INDEX_END     = INT_MAX
155};
156
157/**
158 * \brief Structure encapsulating all data necessary to traverse the
159 *        elements of a SES configuration.
160 *
161 * The ses_iterator object simplifies the task of iterating through all
162 * elements detected via the SES configuration page by tracking the numerous
163 * element indexes that, instead of memoizing in the softc, we calculate
164 * on the fly during the traversal of the element objects.  The various
165 * indexes are necessary due to the varying needs of matching objects in
166 * the different SES pages.  Some pages (e.g. Status/Control) contain all
167 * elements, while others (e.g. Additional Element Status) only contain
168 * individual elements (no overal status elements) of particular types.
169 *
170 * To use an iterator, initialize it with ses_iter_init(), and then
171 * use ses_iter_next() to traverse the elements (including the first) in
172 * the configuration.  Once an iterator is initiailized with ses_iter_init(),
173 * you may also seek to any particular element by either it's global or
174 * individual element index via the ses_iter_seek_to() function.  You may
175 * also return an iterator to the position just before the first element
176 * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset().
177 */
178struct ses_iterator {
179	/**
180	 * \brief Backlink to the overal software configuration structure.
181	 *
182	 * This is included for convenience so the iteration functions
183	 * need only take a single, struct ses_iterator *, argument.
184	 */
185	enc_softc_t *enc;
186
187	enc_cache_t *cache;
188
189	/**
190	 * \brief Index of the type of the current element within the
191	 *        ses_cache's ses_types array.
192	 */
193	int	          type_index;
194
195	/**
196	 * \brief The position (0 based) of this element relative to all other
197	 *        elements of this type.
198	 *
199	 * This index resets to zero every time the iterator transitions
200	 * to elements of a new type in the configuration.
201	 */
202	int	          type_element_index;
203
204	/**
205	 * \brief The position (0 based) of this element relative to all
206	 *        other individual status elements in the configuration.
207	 *
208	 * This index ranges from 0 through the number of individual
209	 * elements in the configuration.  When the iterator returns
210	 * an overall status element, individual_element_index is
211	 * set to ITERATOR_INDEX_INVALID, to indicate that it does
212	 * not apply to the current element.
213	 */
214	int	          individual_element_index;
215
216	/**
217	 * \brief The position (0 based) of this element relative to
218	 *        all elements in the configration.
219	 *
220	 * This index is appropriate for indexing into enc->ses_elm_map.
221	 */
222	int	          global_element_index;
223
224	/**
225	 * \brief The last valid individual element index of this
226	 *        iterator.
227	 *
228	 * When an iterator traverses an overal status element, the
229	 * individual element index is reset to ITERATOR_INDEX_INVALID
230	 * to prevent unintential use of the individual_element_index
231	 * field.  The saved_individual_element_index allows the iterator
232	 * to restore it's position in the individual elements upon
233	 * reaching the next individual element.
234	 */
235	int	          saved_individual_element_index;
236};
237
238typedef enum {
239	SES_UPDATE_NONE,
240	SES_UPDATE_PAGES,
241	SES_UPDATE_GETCONFIG,
242	SES_UPDATE_GETSTATUS,
243	SES_UPDATE_GETELMDESCS,
244	SES_UPDATE_GETELMADDLSTATUS,
245	SES_PROCESS_CONTROL_REQS,
246	SES_PUBLISH_PHYSPATHS,
247	SES_PUBLISH_CACHE,
248	SES_NUM_UPDATE_STATES
249} ses_update_action;
250
251static enc_softc_cleanup_t ses_softc_cleanup;
252
253#define	SCSZ	0x8000
254
255static fsm_fill_handler_t ses_fill_rcv_diag_io;
256static fsm_fill_handler_t ses_fill_control_request;
257static fsm_done_handler_t ses_process_pages;
258static fsm_done_handler_t ses_process_config;
259static fsm_done_handler_t ses_process_status;
260static fsm_done_handler_t ses_process_elm_descs;
261static fsm_done_handler_t ses_process_elm_addlstatus;
262static fsm_done_handler_t ses_process_control_request;
263static fsm_done_handler_t ses_publish_physpaths;
264static fsm_done_handler_t ses_publish_cache;
265
266static struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] =
267{
268	{ "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL },
269	{
270		"SES_UPDATE_PAGES",
271		SesSupportedPages,
272		SCSZ,
273		60 * 1000,
274		ses_fill_rcv_diag_io,
275		ses_process_pages,
276		enc_error
277	},
278	{
279		"SES_UPDATE_GETCONFIG",
280		SesConfigPage,
281		SCSZ,
282		60 * 1000,
283		ses_fill_rcv_diag_io,
284		ses_process_config,
285		enc_error
286	},
287	{
288		"SES_UPDATE_GETSTATUS",
289		SesStatusPage,
290		SCSZ,
291		60 * 1000,
292		ses_fill_rcv_diag_io,
293		ses_process_status,
294		enc_error
295	},
296	{
297		"SES_UPDATE_GETELMDESCS",
298		SesElementDescriptor,
299		SCSZ,
300		60 * 1000,
301		ses_fill_rcv_diag_io,
302		ses_process_elm_descs,
303		enc_error
304	},
305	{
306		"SES_UPDATE_GETELMADDLSTATUS",
307		SesAddlElementStatus,
308		SCSZ,
309		60 * 1000,
310		ses_fill_rcv_diag_io,
311		ses_process_elm_addlstatus,
312		enc_error
313	},
314	{
315		"SES_PROCESS_CONTROL_REQS",
316		SesControlPage,
317		SCSZ,
318		60 * 1000,
319		ses_fill_control_request,
320		ses_process_control_request,
321		enc_error
322	},
323	{
324		"SES_PUBLISH_PHYSPATHS",
325		0,
326		0,
327		0,
328		NULL,
329		ses_publish_physpaths,
330		NULL
331	},
332	{
333		"SES_PUBLISH_CACHE",
334		0,
335		0,
336		0,
337		NULL,
338		ses_publish_cache,
339		NULL
340	}
341};
342
343typedef struct ses_cache {
344	/* Source for all the configuration data pointers */
345	const struct ses_cfg_page		*cfg_page;
346
347	/* References into the config page. */
348	int					 ses_nsubencs;
349	const struct ses_enc_desc * const	*subencs;
350	int					 ses_ntypes;
351	const ses_type_t			*ses_types;
352
353	/* Source for all the status pointers */
354	const struct ses_status_page		*status_page;
355
356	/* Source for all the object descriptor pointers */
357	const struct ses_elem_descr_page	*elm_descs_page;
358
359	/* Source for all the additional object status pointers */
360	const struct ses_addl_elem_status_page  *elm_addlstatus_page;
361
362} ses_cache_t;
363
364typedef struct ses_softc {
365	uint32_t		ses_flags;
366#define	SES_FLAG_TIMEDCOMP	0x01
367#define	SES_FLAG_ADDLSTATUS	0x02
368#define	SES_FLAG_DESC		0x04
369
370	ses_control_reqlist_t	ses_requests;
371	ses_control_reqlist_t	ses_pending_requests;
372} ses_softc_t;
373
374/**
375 * \brief Reset a SES iterator to just before the first element
376 *        in the configuration.
377 *
378 * \param iter  The iterator object to reset.
379 *
380 * The indexes within a reset iterator are invalid and will only
381 * become valid upon completion of a ses_iter_seek_to() or a
382 * ses_iter_next().
383 */
384static void
385ses_iter_reset(struct ses_iterator *iter)
386{
387	/*
388	 * Set our indexes to just before the first valid element
389	 * of the first type (ITERATOR_INDEX_INVALID == -1).  This
390	 * simplifies the implementation of ses_iter_next().
391	 */
392	iter->type_index                     = 0;
393	iter->type_element_index             = ITERATOR_INDEX_INVALID;
394	iter->global_element_index           = ITERATOR_INDEX_INVALID;
395	iter->individual_element_index       = ITERATOR_INDEX_INVALID;
396	iter->saved_individual_element_index = ITERATOR_INDEX_INVALID;
397}
398
399/**
400 * \brief Initialize the storage of a SES iterator and reset it to
401 *        the position just before the first element of the
402 *        configuration.
403 *
404 * \param enc	The SES softc for the SES instance whose configuration
405 *              will be enumerated by this iterator.
406 * \param iter  The iterator object to initialize.
407 */
408static void
409ses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter)
410{
411	iter->enc = enc;
412	iter->cache = cache;
413	ses_iter_reset(iter);
414}
415
416/**
417 * \brief Traverse the provided SES iterator to the next element
418 *        within the configuraiton.
419 *
420 * \param iter  The iterator to move.
421 *
422 * \return  If a valid next element exists, a pointer to it's enc_element_t.
423 *          Otherwise NULL.
424 */
425static enc_element_t *
426ses_iter_next(struct ses_iterator *iter)
427{
428	ses_cache_t	 *ses_cache;
429	const ses_type_t *element_type;
430
431	ses_cache = iter->cache->private;
432
433	/*
434	 * Note: Treat nelms as signed, so we will hit this case
435	 *       and immediately terminate the iteration if the
436	 *	 configuration has 0 objects.
437	 */
438	if (iter->global_element_index >= (int)iter->cache->nelms - 1) {
439
440		/* Elements exhausted. */
441		iter->type_index	       = ITERATOR_INDEX_END;
442		iter->type_element_index       = ITERATOR_INDEX_END;
443		iter->global_element_index     = ITERATOR_INDEX_END;
444		iter->individual_element_index = ITERATOR_INDEX_END;
445		return (NULL);
446	}
447
448	KASSERT((iter->type_index < ses_cache->ses_ntypes),
449		("Corrupted element iterator. %d not less than %d",
450		 iter->type_index, ses_cache->ses_ntypes));
451
452	element_type = &ses_cache->ses_types[iter->type_index];
453	iter->global_element_index++;
454	iter->type_element_index++;
455
456	/*
457	 * There is an object for overal type status in addition
458	 * to one for each allowed element, but only if the element
459	 * count is non-zero.
460	 */
461	if (iter->type_element_index > element_type->hdr->etype_maxelt) {
462
463		/*
464		 * We've exhausted the elements of this type.
465		 * This next element belongs to the next type.
466		 */
467		iter->type_index++;
468		iter->type_element_index = 0;
469		iter->saved_individual_element_index
470		    = iter->individual_element_index;
471		iter->individual_element_index = ITERATOR_INDEX_INVALID;
472	}
473
474	if (iter->type_element_index > 0) {
475		if (iter->type_element_index == 1) {
476			iter->individual_element_index
477			    = iter->saved_individual_element_index;
478		}
479		iter->individual_element_index++;
480	}
481
482	return (&iter->cache->elm_map[iter->global_element_index]);
483}
484
485/**
486 * Element index types tracked by a SES iterator.
487 */
488typedef enum {
489	/**
490	 * Index relative to all elements (overall and individual)
491	 * in the system.
492	 */
493	SES_ELEM_INDEX_GLOBAL,
494
495	/**
496	 * \brief Index relative to all individual elements in the system.
497	 *
498	 * This index counts only individual elements, skipping overall
499	 * status elements.  This is the index space of the additional
500	 * element status page (page 0xa).
501	 */
502	SES_ELEM_INDEX_INDIVIDUAL
503} ses_elem_index_type_t;
504
505/**
506 * \brief Move the provided iterator forwards or backwards to the object
507 *        having the give index.
508 *
509 * \param iter           The iterator on which to perform the seek.
510 * \param element_index  The index of the element to find.
511 * \param index_type     The type (global or individual) of element_index.
512 *
513 * \return  If the element is found, a pointer to it's enc_element_t.
514 *          Otherwise NULL.
515 */
516static enc_element_t *
517ses_iter_seek_to(struct ses_iterator *iter, int element_index,
518		 ses_elem_index_type_t index_type)
519{
520	enc_element_t	*element;
521	int		*cur_index;
522
523	if (index_type == SES_ELEM_INDEX_GLOBAL)
524		cur_index = &iter->global_element_index;
525	else
526		cur_index = &iter->individual_element_index;
527
528	if (*cur_index == element_index) {
529		/* Already there. */
530		return (&iter->cache->elm_map[iter->global_element_index]);
531	}
532
533	ses_iter_reset(iter);
534	while ((element = ses_iter_next(iter)) != NULL
535	    && *cur_index != element_index)
536		;
537
538	if (*cur_index != element_index)
539		return (NULL);
540
541	return (element);
542}
543
544#if 0
545static int ses_encode(enc_softc_t *, uint8_t *, int, int,
546    struct ses_comstat *);
547#endif
548static int ses_set_timed_completion(enc_softc_t *, uint8_t);
549#if 0
550static int ses_putstatus(enc_softc_t *, int, struct ses_comstat *);
551#endif
552
553static void ses_print_addl_data(enc_softc_t *, enc_element_t *);
554
555/*=========================== SES cleanup routines ===========================*/
556
557static void
558ses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache)
559{
560	ses_cache_t   *ses_cache;
561	ses_cache_t   *other_ses_cache;
562	enc_element_t *cur_elm;
563	enc_element_t *last_elm;
564
565	ENC_DLOG(enc, "%s: enter\n", __func__);
566	ses_cache = cache->private;
567	if (ses_cache->elm_addlstatus_page == NULL)
568		return;
569
570	for (cur_elm = cache->elm_map,
571	     last_elm = &cache->elm_map[cache->nelms];
572	     cur_elm != last_elm; cur_elm++) {
573		ses_element_t *elmpriv;
574
575		elmpriv = cur_elm->elm_private;
576
577		/* Clear references to the additional status page. */
578		bzero(&elmpriv->addl, sizeof(elmpriv->addl));
579	}
580
581	other_ses_cache = enc_other_cache(enc, cache)->private;
582	if (other_ses_cache->elm_addlstatus_page
583	 != ses_cache->elm_addlstatus_page)
584		ENC_FREE(ses_cache->elm_addlstatus_page);
585	ses_cache->elm_addlstatus_page = NULL;
586}
587
588static void
589ses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache)
590{
591	ses_cache_t   *ses_cache;
592	ses_cache_t   *other_ses_cache;
593	enc_element_t *cur_elm;
594	enc_element_t *last_elm;
595
596	ENC_DLOG(enc, "%s: enter\n", __func__);
597	ses_cache = cache->private;
598	if (ses_cache->elm_descs_page == NULL)
599		return;
600
601	for (cur_elm = cache->elm_map,
602	     last_elm = &cache->elm_map[cache->nelms];
603	     cur_elm != last_elm; cur_elm++) {
604		ses_element_t *elmpriv;
605
606		elmpriv = cur_elm->elm_private;
607		elmpriv->descr_len = 0;
608		elmpriv->descr = NULL;
609	}
610
611	other_ses_cache = enc_other_cache(enc, cache)->private;
612	if (other_ses_cache->elm_descs_page
613	 != ses_cache->elm_descs_page)
614		ENC_FREE(ses_cache->elm_descs_page);
615	ses_cache->elm_descs_page = NULL;
616}
617
618static void
619ses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache)
620{
621	ses_cache_t *ses_cache;
622	ses_cache_t *other_ses_cache;
623
624	ENC_DLOG(enc, "%s: enter\n", __func__);
625	ses_cache   = cache->private;
626	if (ses_cache->status_page == NULL)
627		return;
628
629	other_ses_cache = enc_other_cache(enc, cache)->private;
630	if (other_ses_cache->status_page != ses_cache->status_page)
631		ENC_FREE(ses_cache->status_page);
632	ses_cache->status_page = NULL;
633}
634
635static void
636ses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache)
637{
638	enc_element_t *cur_elm;
639	enc_element_t *last_elm;
640
641	ENC_DLOG(enc, "%s: enter\n", __func__);
642	if (cache->elm_map == NULL)
643		return;
644
645	ses_cache_free_elm_descs(enc, cache);
646	ses_cache_free_elm_addlstatus(enc, cache);
647	for (cur_elm = cache->elm_map,
648	     last_elm = &cache->elm_map[cache->nelms];
649	     cur_elm != last_elm; cur_elm++) {
650
651		ENC_FREE_AND_NULL(cur_elm->elm_private);
652	}
653	ENC_FREE_AND_NULL(cache->elm_map);
654	cache->nelms = 0;
655	ENC_DLOG(enc, "%s: exit\n", __func__);
656}
657
658static void
659ses_cache_free(enc_softc_t *enc, enc_cache_t *cache)
660{
661	ses_cache_t *other_ses_cache;
662	ses_cache_t *ses_cache;
663
664	ENC_DLOG(enc, "%s: enter\n", __func__);
665	ses_cache_free_elm_addlstatus(enc, cache);
666	ses_cache_free_status(enc, cache);
667	ses_cache_free_elm_map(enc, cache);
668
669	ses_cache = cache->private;
670	ses_cache->ses_ntypes = 0;
671
672	other_ses_cache = enc_other_cache(enc, cache)->private;
673	if (other_ses_cache->subencs != ses_cache->subencs)
674		ENC_FREE(ses_cache->subencs);
675	ses_cache->subencs = NULL;
676
677	if (other_ses_cache->ses_types != ses_cache->ses_types)
678		ENC_FREE(ses_cache->ses_types);
679	ses_cache->ses_types = NULL;
680
681	if (other_ses_cache->cfg_page != ses_cache->cfg_page)
682		ENC_FREE(ses_cache->cfg_page);
683	ses_cache->cfg_page = NULL;
684
685	ENC_DLOG(enc, "%s: exit\n", __func__);
686}
687
688static void
689ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst)
690{
691	ses_cache_t   *dst_ses_cache;
692	ses_cache_t   *src_ses_cache;
693	enc_element_t *src_elm;
694	enc_element_t *dst_elm;
695	enc_element_t *last_elm;
696
697	ses_cache_free(enc, dst);
698	src_ses_cache = src->private;
699	dst_ses_cache = dst->private;
700
701	/*
702	 * The cloned enclosure cache and ses specific cache are
703	 * mostly identical to the source.
704	 */
705	*dst = *src;
706	*dst_ses_cache = *src_ses_cache;
707
708	/*
709	 * But the ses cache storage is still independent.  Restore
710	 * the pointer that was clobbered by the structure copy above.
711	 */
712	dst->private = dst_ses_cache;
713
714	/*
715	 * The element map is independent even though it starts out
716	 * pointing to the same constant page data.
717	 */
718	dst->elm_map = malloc(dst->nelms * sizeof(enc_element_t),
719	    M_SCSIENC, M_WAITOK);
720	memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t));
721	for (dst_elm = dst->elm_map, src_elm = src->elm_map,
722	     last_elm = &src->elm_map[src->nelms];
723	     src_elm != last_elm; src_elm++, dst_elm++) {
724
725		dst_elm->elm_private = malloc(sizeof(ses_element_t),
726		    M_SCSIENC, M_WAITOK);
727		memcpy(dst_elm->elm_private, src_elm->elm_private,
728		       sizeof(ses_element_t));
729	}
730}
731
732/* Structure accessors.  These are strongly typed to avoid errors. */
733
734int
735ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj)
736{
737	return ((obj)->base_hdr.byte1 >> 6);
738}
739int
740ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr)
741{
742	return ((hdr)->byte0 & 0xf);
743}
744int
745ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr)
746{
747	return ((hdr)->byte0 >> 4) & 0x1;
748}
749int
750ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr)
751{
752	return ((hdr)->byte0 >> 7);
753}
754int
755ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr)
756{
757	return ((hdr)->type0_noneip.byte1 & 0x1);
758}
759int
760ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy)
761{
762	return ((phy)->target_ports & 0x1);
763}
764int
765ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy)
766{
767	return ((phy)->target_ports >> 7);
768}
769int
770ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy)
771{
772	return (((phy)->byte0 >> 4) & 0x7);
773}
774
775/**
776 * \brief Verify that the cached configuration data in our softc
777 *        is valid for processing the page data corresponding to
778 *        the provided page header.
779 *
780 * \param ses_cache The SES cache to validate.
781 * \param gen_code  The 4 byte generation code from a SES diagnostic
782 *		    page header.
783 *
784 * \return  non-zero if true, 0 if false.
785 */
786static int
787ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code)
788{
789	uint32_t cache_gc;
790	uint32_t cur_gc;
791
792	if (ses_cache->cfg_page == NULL)
793		return (0);
794
795	cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code);
796	cur_gc   = scsi_4btoul(gen_code);
797	return (cache_gc == cur_gc);
798}
799
800/**
801 * Function signature for consumers of the ses_devids_iter() interface.
802 */
803typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *,
804				  struct scsi_vpd_id_descriptor *, void *);
805
806/**
807 * \brief Iterate over and create vpd device id records from the
808 *        additional element status data for elm, passing that data
809 *        to the provided callback.
810 *
811 * \param enc	        SES instance containing elm
812 * \param elm	        Element for which to extract device ID data.
813 * \param callback      The callback function to invoke on each generated
814 *                      device id descriptor for elm.
815 * \param callback_arg  Argument passed through to callback on each invocation.
816 */
817static void
818ses_devids_iter(enc_softc_t *enc, enc_element_t *elm,
819		ses_devid_callback_t *callback, void *callback_arg)
820{
821	ses_element_t           *elmpriv;
822	struct ses_addl_status *addl;
823	u_int                   i;
824	size_t			devid_record_size;
825
826	elmpriv = elm->elm_private;
827	addl = &(elmpriv->addl);
828
829	/*
830	 * Don't assume this object has additional status information, or
831	 * that it is a SAS device, or that it is a device slot device.
832	 */
833	if (addl->hdr == NULL || addl->proto_hdr.sas == NULL
834	 || addl->proto_data.sasdev_phys == NULL)
835		return;
836
837	devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN
838			  + sizeof(struct scsi_vpd_id_naa_ieee_reg);
839	for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) {
840		uint8_t			       devid_buf[devid_record_size];
841		struct scsi_vpd_id_descriptor *devid;
842		uint8_t			      *phy_addr;
843
844		devid = (struct scsi_vpd_id_descriptor *)devid_buf;
845		phy_addr = addl->proto_data.sasdev_phys[i].phy_addr;
846		devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT)
847				     | SVPD_ID_CODESET_BINARY;
848		devid->id_type       = SVPD_ID_PIV
849				     | SVPD_ID_ASSOC_PORT
850				     | SVPD_ID_TYPE_NAA;
851		devid->reserved	     = 0;
852		devid->length	     = sizeof(struct scsi_vpd_id_naa_ieee_reg);
853		memcpy(devid->identifier, phy_addr, devid->length);
854
855		callback(enc, elm, devid, callback_arg);
856	}
857}
858
859/**
860 * Function signature for consumers of the ses_paths_iter() interface.
861 */
862typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *,
863				 struct cam_path *, void *);
864
865/**
866 * Argument package passed through ses_devids_iter() by
867 * ses_paths_iter() to ses_path_iter_devid_callback().
868 */
869typedef struct ses_path_iter_args {
870	ses_path_callback_t *callback;
871	void		    *callback_arg;
872} ses_path_iter_args_t;
873
874/**
875 * ses_devids_iter() callback function used by ses_paths_iter()
876 * to map device ids to peripheral driver instances.
877 *
878 * \param enc	  SES instance containing elm
879 * \param elm	  Element on which device ID matching is active.
880 * \param periph  A device ID corresponding to elm.
881 * \param arg     Argument passed through to callback on each invocation.
882 */
883static void
884ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem,
885			       struct scsi_vpd_id_descriptor *devid,
886			       void *arg)
887{
888	struct ccb_dev_match         cdm;
889	struct dev_match_pattern     match_pattern;
890	struct dev_match_result      match_result;
891	struct device_match_result  *device_match;
892	struct device_match_pattern *device_pattern;
893	ses_path_iter_args_t	    *args;
894
895	args = (ses_path_iter_args_t *)arg;
896	match_pattern.type = DEV_MATCH_DEVICE;
897	device_pattern = &match_pattern.pattern.device_pattern;
898	device_pattern->flags = DEV_MATCH_DEVID;
899	device_pattern->data.devid_pat.id_len =
900	    offsetof(struct scsi_vpd_id_descriptor, identifier)
901	  + devid->length;
902	memcpy(device_pattern->data.devid_pat.id, devid,
903	       device_pattern->data.devid_pat.id_len);
904
905	memset(&cdm, 0, sizeof(cdm));
906	if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL,
907			     CAM_XPT_PATH_ID,
908			     CAM_TARGET_WILDCARD,
909			     CAM_LUN_WILDCARD) != CAM_REQ_CMP)
910		return;
911
912	cdm.ccb_h.func_code = XPT_DEV_MATCH;
913	cdm.num_patterns    = 1;
914	cdm.patterns        = &match_pattern;
915	cdm.pattern_buf_len = sizeof(match_pattern);
916	cdm.match_buf_len   = sizeof(match_result);
917	cdm.matches         = &match_result;
918
919	xpt_action((union ccb *)&cdm);
920	xpt_free_path(cdm.ccb_h.path);
921
922	if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP
923	 || (cdm.status != CAM_DEV_MATCH_LAST
924	  && cdm.status != CAM_DEV_MATCH_MORE)
925	 || cdm.num_matches == 0)
926		return;
927
928	device_match = &match_result.result.device_result;
929	if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL,
930			     device_match->path_id,
931			     device_match->target_id,
932			     device_match->target_lun) != CAM_REQ_CMP)
933		return;
934
935	args->callback(enc, elem, cdm.ccb_h.path, args->callback_arg);
936
937	xpt_free_path(cdm.ccb_h.path);
938}
939
940/**
941 * \brief Iterate over and find the matching periph objects for the
942 *        specified element.
943 *
944 * \param enc	        SES instance containing elm
945 * \param elm	        Element for which to perform periph object matching.
946 * \param callback      The callback function to invoke with each matching
947 *                      periph object.
948 * \param callback_arg  Argument passed through to callback on each invocation.
949 */
950static void
951ses_paths_iter(enc_softc_t *enc, enc_element_t *elm,
952	       ses_path_callback_t *callback, void *callback_arg)
953{
954	ses_path_iter_args_t args;
955
956	args.callback     = callback;
957	args.callback_arg = callback_arg;
958	ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args);
959}
960
961/**
962 * ses_paths_iter() callback function used by ses_get_elmdevname()
963 * to record periph driver instance strings corresponding to a SES
964 * element.
965 *
966 * \param enc	  SES instance containing elm
967 * \param elm	  Element on which periph matching is active.
968 * \param periph  A periph instance that matches elm.
969 * \param arg     Argument passed through to callback on each invocation.
970 */
971static void
972ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem,
973			struct cam_path *path, void *arg)
974{
975	struct sbuf *sb;
976
977	sb = (struct sbuf *)arg;
978	cam_periph_list(path, sb);
979}
980
981/**
982 * Argument package passed through ses_paths_iter() to
983 * ses_getcampath_callback.
984 */
985typedef struct ses_setphyspath_callback_args {
986	struct sbuf *physpath;
987	int          num_set;
988} ses_setphyspath_callback_args_t;
989
990/**
991 * \brief ses_paths_iter() callback to set the physical path on the
992 *        CAM EDT entries corresponding to a given SES element.
993 *
994 * \param enc	  SES instance containing elm
995 * \param elm	  Element on which periph matching is active.
996 * \param periph  A periph instance that matches elm.
997 * \param arg     Argument passed through to callback on each invocation.
998 */
999static void
1000ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm,
1001			 struct cam_path *path, void *arg)
1002{
1003	struct ccb_dev_advinfo cdai;
1004	ses_setphyspath_callback_args_t *args;
1005	char *old_physpath;
1006
1007	args = (ses_setphyspath_callback_args_t *)arg;
1008	old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO);
1009	cam_periph_lock(enc->periph);
1010	xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
1011	cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
1012	cdai.buftype = CDAI_TYPE_PHYS_PATH;
1013	cdai.flags = CDAI_FLAG_NONE;
1014	cdai.bufsiz = MAXPATHLEN;
1015	cdai.buf = old_physpath;
1016	xpt_action((union ccb *)&cdai);
1017	if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
1018		cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
1019
1020	if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) {
1021
1022		xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
1023		cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
1024		cdai.buftype = CDAI_TYPE_PHYS_PATH;
1025		cdai.flags = CDAI_FLAG_STORE;
1026		cdai.bufsiz = sbuf_len(args->physpath);
1027		cdai.buf = sbuf_data(args->physpath);
1028		xpt_action((union ccb *)&cdai);
1029		if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
1030			cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
1031		if (cdai.ccb_h.status == CAM_REQ_CMP)
1032			args->num_set++;
1033	}
1034	cam_periph_unlock(enc->periph);
1035	free(old_physpath, M_SCSIENC);
1036}
1037
1038/**
1039 * \brief Set a device's physical path string in CAM XPT.
1040 *
1041 * \param enc	SES instance containing elm
1042 * \param elm	Element to publish physical path string for
1043 * \param iter	Iterator whose state corresponds to elm
1044 *
1045 * \return	0 on success, errno otherwise.
1046 */
1047static int
1048ses_set_physpath(enc_softc_t *enc, enc_element_t *elm,
1049		 struct ses_iterator *iter)
1050{
1051	struct ccb_dev_advinfo cdai;
1052	ses_setphyspath_callback_args_t args;
1053	int i, ret;
1054	struct sbuf sb;
1055	struct scsi_vpd_id_descriptor *idd;
1056	uint8_t *devid;
1057	ses_element_t *elmpriv;
1058	const char *c;
1059
1060	ret = EIO;
1061	devid = NULL;
1062
1063	/*
1064	 * Assemble the components of the physical path starting with
1065	 * the device ID of the enclosure itself.
1066	 */
1067	xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL);
1068	cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
1069	cdai.buftype = CDAI_TYPE_SCSI_DEVID;
1070	cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN;
1071	cdai.buf = devid = malloc(cdai.bufsiz, M_SCSIENC, M_WAITOK|M_ZERO);
1072	cam_periph_lock(enc->periph);
1073	xpt_action((union ccb *)&cdai);
1074	if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
1075		cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
1076	cam_periph_unlock(enc->periph);
1077	if (cdai.ccb_h.status != CAM_REQ_CMP)
1078		goto out;
1079
1080	idd = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf,
1081	    cdai.provsiz, scsi_devid_is_naa_ieee_reg);
1082	if (idd == NULL)
1083		goto out;
1084
1085	if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) {
1086		ret = ENOMEM;
1087		goto out;
1088	}
1089	/* Next, generate the physical path string */
1090	sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x",
1091	    scsi_8btou64(idd->identifier), iter->type_index,
1092	    iter->type_element_index);
1093	/* Append the element descriptor if one exists */
1094	elmpriv = elm->elm_private;
1095	if (elmpriv->descr != NULL && elmpriv->descr_len > 0) {
1096		sbuf_cat(&sb, "/elmdesc@");
1097		for (i = 0, c = elmpriv->descr; i < elmpriv->descr_len;
1098		    i++, c++) {
1099			if (!isprint(*c) || isspace(*c) || *c == '/')
1100				sbuf_putc(&sb, '_');
1101			else
1102				sbuf_putc(&sb, *c);
1103		}
1104	}
1105	sbuf_finish(&sb);
1106
1107	/*
1108	 * Set this physical path on any CAM devices with a device ID
1109	 * descriptor that matches one created from the SES additional
1110	 * status data for this element.
1111	 */
1112	args.physpath= &sb;
1113	args.num_set = 0;
1114	ses_paths_iter(enc, elm, ses_setphyspath_callback, &args);
1115	sbuf_delete(&sb);
1116
1117	ret = args.num_set == 0 ? ENOENT : 0;
1118
1119out:
1120	if (devid != NULL)
1121		ENC_FREE(devid);
1122	return (ret);
1123}
1124
1125/**
1126 * \brief Helper to set the CDB fields appropriately.
1127 *
1128 * \param cdb		Buffer containing the cdb.
1129 * \param pagenum	SES diagnostic page to query for.
1130 * \param dir		Direction of query.
1131 */
1132static void
1133ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir)
1134{
1135
1136	/* Ref: SPC-4 r25 Section 6.20 Table 223 */
1137	if (dir == CAM_DIR_IN) {
1138		cdb[0] = RECEIVE_DIAGNOSTIC;
1139		cdb[1] = 1; /* Set page code valid bit */
1140		cdb[2] = pagenum;
1141	} else {
1142		cdb[0] = SEND_DIAGNOSTIC;
1143		cdb[1] = 0x10;
1144		cdb[2] = pagenum;
1145	}
1146	cdb[3] = bufsiz >> 8;	/* high bits */
1147	cdb[4] = bufsiz & 0xff;	/* low bits */
1148	cdb[5] = 0;
1149}
1150
1151/**
1152 * \brief Discover whether this instance supports timed completion of a
1153 * 	  RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status
1154 * 	  page, and store the result in the softc, updating if necessary.
1155 *
1156 * \param enc	SES instance to query and update.
1157 * \param tc_en	Value of timed completion to set (see \return).
1158 *
1159 * \return	1 if timed completion enabled, 0 otherwise.
1160 */
1161static int
1162ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en)
1163{
1164	int err;
1165	union ccb *ccb;
1166	struct cam_periph *periph;
1167	struct ses_mgmt_mode_page *mgmt;
1168	uint8_t *mode_buf;
1169	size_t mode_buf_len;
1170	ses_softc_t *ses;
1171
1172	periph = enc->periph;
1173	ses = enc->enc_private;
1174	ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL);
1175
1176	mode_buf_len = sizeof(struct ses_mgmt_mode_page);
1177	mode_buf = ENC_MALLOCZ(mode_buf_len);
1178	if (mode_buf == NULL)
1179		goto out;
1180
1181	scsi_mode_sense(&ccb->csio, /*retries*/4, NULL, MSG_SIMPLE_Q_TAG,
1182	    /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE,
1183	    mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000);
1184
1185	/*
1186	 * Ignore illegal request errors, as they are quite common and we
1187	 * will print something out in that case anyway.
1188	 */
1189	err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS,
1190	    ENC_FLAGS|SF_QUIET_IR, NULL);
1191	if (ccb->ccb_h.status != CAM_REQ_CMP) {
1192		ENC_VLOG(enc, "Timed Completion Unsupported\n");
1193		goto release;
1194	}
1195
1196	/* Skip the mode select if the desired value is already set */
1197	mgmt = (struct ses_mgmt_mode_page *)mode_buf;
1198	if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en)
1199		goto done;
1200
1201	/* Value is not what we wanted, set it */
1202	if (tc_en)
1203		mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN;
1204	else
1205		mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN;
1206	/* SES2r20: a completion time of zero means as long as possible */
1207	bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time));
1208
1209	scsi_mode_select(&ccb->csio, 5, NULL, MSG_SIMPLE_Q_TAG,
1210	    /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len,
1211	    SSD_FULL_SIZE, /*timeout*/60 * 1000);
1212
1213	err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL);
1214	if (ccb->ccb_h.status != CAM_REQ_CMP) {
1215		ENC_VLOG(enc, "Timed Completion Set Failed\n");
1216		goto release;
1217	}
1218
1219done:
1220	if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) {
1221		ENC_LOG(enc, "Timed Completion Enabled\n");
1222		ses->ses_flags |= SES_FLAG_TIMEDCOMP;
1223	} else {
1224		ENC_LOG(enc, "Timed Completion Disabled\n");
1225		ses->ses_flags &= ~SES_FLAG_TIMEDCOMP;
1226	}
1227release:
1228	ENC_FREE(mode_buf);
1229	xpt_release_ccb(ccb);
1230out:
1231	return (ses->ses_flags & SES_FLAG_TIMEDCOMP);
1232}
1233
1234/**
1235 * \brief Process the list of supported pages and update flags.
1236 *
1237 * \param enc       SES device to query.
1238 * \param buf       Buffer containing the config page.
1239 * \param xfer_len  Length of the config page in the buffer.
1240 *
1241 * \return  0 on success, errno otherwise.
1242 */
1243static int
1244ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state,
1245    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1246{
1247	ses_softc_t *ses;
1248	struct scsi_diag_page *page;
1249	int err, i, length;
1250
1251	CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
1252	    ("entering %s(%p, %d)\n", __func__, bufp, xfer_len));
1253	ses = enc->enc_private;
1254	err = -1;
1255
1256	if (error != 0) {
1257		err = error;
1258		goto out;
1259	}
1260	if (xfer_len < sizeof(*page)) {
1261		ENC_VLOG(enc, "Unable to parse Diag Pages List Header\n");
1262		err = EIO;
1263		goto out;
1264	}
1265	page = (struct scsi_diag_page *)*bufp;
1266	length = scsi_2btoul(page->length);
1267	if (length + offsetof(struct scsi_diag_page, params) > xfer_len) {
1268		ENC_VLOG(enc, "Diag Pages List Too Long\n");
1269		goto out;
1270	}
1271	ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n",
1272		 __func__, length, xfer_len);
1273
1274	err = 0;
1275	for (i = 0; i < length; i++) {
1276		if (page->params[i] == SesElementDescriptor)
1277			ses->ses_flags |= SES_FLAG_DESC;
1278		else if (page->params[i] == SesAddlElementStatus)
1279			ses->ses_flags |= SES_FLAG_ADDLSTATUS;
1280	}
1281
1282out:
1283	ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err);
1284	return (err);
1285}
1286
1287/**
1288 * \brief Process the config page and update associated structures.
1289 *
1290 * \param enc       SES device to query.
1291 * \param buf       Buffer containing the config page.
1292 * \param xfer_len  Length of the config page in the buffer.
1293 *
1294 * \return  0 on success, errno otherwise.
1295 */
1296static int
1297ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state,
1298    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1299{
1300	struct ses_iterator iter;
1301	ses_softc_t *ses;
1302	enc_cache_t *enc_cache;
1303	ses_cache_t *ses_cache;
1304	uint8_t *buf;
1305	int length;
1306	int err;
1307	int nelm;
1308	int ntype;
1309	struct ses_cfg_page *cfg_page;
1310	struct ses_enc_desc *buf_subenc;
1311	const struct ses_enc_desc **subencs;
1312	const struct ses_enc_desc **cur_subenc;
1313	const struct ses_enc_desc **last_subenc;
1314	ses_type_t *ses_types;
1315	ses_type_t *sestype;
1316	const struct ses_elm_type_desc *cur_buf_type;
1317	const struct ses_elm_type_desc *last_buf_type;
1318	uint8_t *last_valid_byte;
1319	enc_element_t *element;
1320	const char *type_text;
1321
1322	CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
1323	    ("entering %s(%p, %d)\n", __func__, bufp, xfer_len));
1324	ses = enc->enc_private;
1325	enc_cache = &enc->enc_daemon_cache;
1326	ses_cache = enc_cache->private;
1327	buf = *bufp;
1328	err = -1;
1329
1330	if (error != 0) {
1331		err = error;
1332		goto out;
1333	}
1334	if (xfer_len < sizeof(cfg_page->hdr)) {
1335		ENC_VLOG(enc, "Unable to parse SES Config Header\n");
1336		err = EIO;
1337		goto out;
1338	}
1339
1340	cfg_page = (struct ses_cfg_page *)buf;
1341	length = ses_page_length(&cfg_page->hdr);
1342	if (length > xfer_len) {
1343		ENC_VLOG(enc, "Enclosure Config Page Too Long\n");
1344		goto out;
1345	}
1346	last_valid_byte = &buf[length - 1];
1347
1348	ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n",
1349		 __func__, length, xfer_len);
1350
1351	err = 0;
1352	if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) {
1353
1354		/* Our cache is still valid.  Proceed to fetching status. */
1355		goto out;
1356	}
1357
1358	/* Cache is no longer valid.  Free old data to make way for new. */
1359	ses_cache_free(enc, enc_cache);
1360	ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n",
1361	    scsi_4btoul(cfg_page->hdr.gen_code),
1362	    ses_cfg_page_get_num_subenc(cfg_page));
1363
1364	/* Take ownership of the buffer. */
1365	ses_cache->cfg_page = cfg_page;
1366	*bufp = NULL;
1367
1368	/*
1369	 * Now waltz through all the subenclosures summing the number of
1370	 * types available in each.
1371	 */
1372	subencs = malloc(ses_cfg_page_get_num_subenc(cfg_page)
1373	    * sizeof(*subencs), M_SCSIENC, M_WAITOK|M_ZERO);
1374	/*
1375	 * Sub-enclosure data is const after construction (i.e. when
1376	 * accessed via our cache object.
1377	 *
1378	 * The cast here is not required in C++ but C99 is not so
1379	 * sophisticated (see C99 6.5.16.1(1)).
1380	 */
1381	ses_cache->ses_nsubencs = ses_cfg_page_get_num_subenc(cfg_page);
1382	ses_cache->subencs = subencs;
1383
1384	buf_subenc = cfg_page->subencs;
1385	cur_subenc = subencs;
1386	last_subenc = &subencs[ses_cache->ses_nsubencs - 1];
1387	ntype = 0;
1388	while (cur_subenc <= last_subenc) {
1389
1390		if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) {
1391			ENC_VLOG(enc, "Enclosure %d Beyond End of "
1392			    "Descriptors\n", cur_subenc - subencs);
1393			err = EIO;
1394			goto out;
1395		}
1396
1397		ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, "
1398		    "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id,
1399		    buf_subenc->num_types, buf_subenc->length,
1400		    &buf_subenc->byte0 - buf);
1401		ENC_VLOG(enc, "WWN: %jx\n",
1402		    (uintmax_t)scsi_8btou64(buf_subenc->logical_id));
1403
1404		ntype += buf_subenc->num_types;
1405		*cur_subenc = buf_subenc;
1406		cur_subenc++;
1407		buf_subenc = ses_enc_desc_next(buf_subenc);
1408	}
1409
1410	/* Process the type headers. */
1411	ses_types = malloc(ntype * sizeof(*ses_types),
1412	    M_SCSIENC, M_WAITOK|M_ZERO);
1413	/*
1414	 * Type data is const after construction (i.e. when accessed via
1415	 * our cache object.
1416	 */
1417	ses_cache->ses_ntypes = ntype;
1418	ses_cache->ses_types = ses_types;
1419
1420	cur_buf_type = (const struct ses_elm_type_desc *)
1421	    (&(*last_subenc)->length + (*last_subenc)->length + 1);
1422	last_buf_type = cur_buf_type + ntype - 1;
1423	type_text = (const uint8_t *)(last_buf_type + 1);
1424	nelm = 0;
1425	sestype = ses_types;
1426	while (cur_buf_type <= last_buf_type) {
1427		if (&cur_buf_type->etype_txt_len > last_valid_byte) {
1428			ENC_VLOG(enc, "Runt Enclosure Type Header %d\n",
1429			    sestype - ses_types);
1430			err = EIO;
1431			goto out;
1432		}
1433		sestype->hdr  = cur_buf_type;
1434		sestype->text = type_text;
1435		type_text += cur_buf_type->etype_txt_len;
1436		ENC_VLOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc "
1437		    "%d, Text Length %d: %.*s\n", sestype - ses_types,
1438		    sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt,
1439		    sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len,
1440		    sestype->hdr->etype_txt_len, sestype->text);
1441
1442		nelm += sestype->hdr->etype_maxelt
1443		      + /*overall status element*/1;
1444		sestype++;
1445		cur_buf_type++;
1446	}
1447
1448	/* Create the object map. */
1449	enc_cache->elm_map = malloc(nelm * sizeof(enc_element_t),
1450	    M_SCSIENC, M_WAITOK|M_ZERO);
1451	enc_cache->nelms = nelm;
1452
1453	ses_iter_init(enc, enc_cache, &iter);
1454	while ((element = ses_iter_next(&iter)) != NULL) {
1455		const struct ses_elm_type_desc *thdr;
1456
1457		ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__,
1458		    iter.global_element_index, iter.type_index, nelm,
1459		    iter.type_element_index);
1460		thdr = ses_cache->ses_types[iter.type_index].hdr;
1461		element->subenclosure = thdr->etype_subenc;
1462		element->enctype = thdr->etype_elm_type;
1463		element->overall_status_elem = iter.type_element_index == 0;
1464		element->elm_private = malloc(sizeof(ses_element_t),
1465		    M_SCSIENC, M_WAITOK|M_ZERO);
1466		ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d "
1467		    "type 0x%x\n", __func__, iter.global_element_index,
1468		    iter.type_index, iter.type_element_index,
1469		    thdr->etype_subenc, thdr->etype_elm_type);
1470	}
1471
1472	err = 0;
1473
1474out:
1475	if (err)
1476		ses_cache_free(enc, enc_cache);
1477	else {
1478		enc_update_request(enc, SES_UPDATE_GETSTATUS);
1479		if (ses->ses_flags & SES_FLAG_DESC)
1480			enc_update_request(enc, SES_UPDATE_GETELMDESCS);
1481		if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
1482			enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
1483		enc_update_request(enc, SES_PUBLISH_CACHE);
1484	}
1485	ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err);
1486	return (err);
1487}
1488
1489/**
1490 * \brief Update the status page and associated structures.
1491 *
1492 * \param enc   SES softc to update for.
1493 * \param buf   Buffer containing the status page.
1494 * \param bufsz	Amount of data in the buffer.
1495 *
1496 * \return	0 on success, errno otherwise.
1497 */
1498static int
1499ses_process_status(enc_softc_t *enc, struct enc_fsm_state *state,
1500    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1501{
1502	struct ses_iterator iter;
1503	enc_element_t *element;
1504	ses_softc_t *ses;
1505	enc_cache_t *enc_cache;
1506	ses_cache_t *ses_cache;
1507	uint8_t *buf;
1508	int err = -1;
1509	int length;
1510	struct ses_status_page *page;
1511	union ses_status_element *cur_stat;
1512	union ses_status_element *last_stat;
1513
1514	ses = enc->enc_private;
1515	enc_cache = &enc->enc_daemon_cache;
1516	ses_cache = enc_cache->private;
1517	buf = *bufp;
1518
1519	ENC_DLOG(enc, "%s: enter (%p, %p, %d)\n", __func__, enc, buf, xfer_len);
1520	page = (struct ses_status_page *)buf;
1521	length = ses_page_length(&page->hdr);
1522
1523	if (error != 0) {
1524		err = error;
1525		goto out;
1526	}
1527	/*
1528	 * Make sure the length fits in the buffer.
1529	 *
1530	 * XXX all this means is that the page is larger than the space
1531	 * we allocated.  Since we use a statically sized buffer, this
1532	 * could happen... Need to use dynamic discovery of the size.
1533	 */
1534	if (length > xfer_len) {
1535		ENC_VLOG(enc, "Enclosure Status Page Too Long\n");
1536		goto out;
1537	}
1538
1539	/* Check for simple enclosure reporting short enclosure status. */
1540	if (length >= 4 && page->hdr.page_code == SesShortStatus) {
1541		ENC_DLOG(enc, "Got Short Enclosure Status page\n");
1542		ses->ses_flags &= ~(SES_FLAG_ADDLSTATUS | SES_FLAG_DESC);
1543		ses_cache_free(enc, enc_cache);
1544		enc_cache->enc_status = page->hdr.page_specific_flags;
1545		enc_update_request(enc, SES_PUBLISH_CACHE);
1546		err = 0;
1547		goto out;
1548	}
1549
1550	/* Make sure the length contains at least one header and status */
1551	if (length < (sizeof(*page) + sizeof(*page->elements))) {
1552		ENC_VLOG(enc, "Enclosure Status Page Too Short\n");
1553		goto out;
1554	}
1555
1556	if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) {
1557		ENC_DLOG(enc, "%s: Generation count change detected\n",
1558		    __func__);
1559		enc_update_request(enc, SES_UPDATE_GETCONFIG);
1560		goto out;
1561	}
1562
1563	ses_cache_free_status(enc, enc_cache);
1564	ses_cache->status_page = page;
1565	*bufp = NULL;
1566
1567	enc_cache->enc_status = page->hdr.page_specific_flags;
1568
1569	/*
1570	 * Read in individual element status.  The element order
1571	 * matches the order reported in the config page (i.e. the
1572	 * order of an unfiltered iteration of the config objects)..
1573	 */
1574	ses_iter_init(enc, enc_cache, &iter);
1575	cur_stat  = page->elements;
1576	last_stat = (union ses_status_element *)
1577	    &buf[length - sizeof(*last_stat)];
1578	ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n",
1579		__func__, length, xfer_len);
1580	while (cur_stat <= last_stat
1581	    && (element = ses_iter_next(&iter)) != NULL) {
1582
1583		ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n",
1584		    __func__, iter.global_element_index, iter.type_index,
1585		    iter.type_element_index, (uint8_t *)cur_stat - buf,
1586		    scsi_4btoul(cur_stat->bytes));
1587
1588		memcpy(&element->encstat, cur_stat, sizeof(element->encstat));
1589		element->svalid = 1;
1590		cur_stat++;
1591	}
1592
1593	if (ses_iter_next(&iter) != NULL) {
1594		ENC_VLOG(enc, "Status page, length insufficient for "
1595			"expected number of objects\n");
1596	} else {
1597		if (cur_stat <= last_stat)
1598			ENC_VLOG(enc, "Status page, exhausted objects before "
1599				"exhausing page\n");
1600		enc_update_request(enc, SES_PUBLISH_CACHE);
1601		err = 0;
1602	}
1603out:
1604	ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err);
1605	return (err);
1606}
1607
1608typedef enum {
1609	/**
1610	 * The enclosure should not provide additional element
1611	 * status for this element type in page 0x0A.
1612	 *
1613	 * \note  This status is returned for any types not
1614	 *        listed SES3r02.  Further types added in a
1615	 *        future specification will be incorrectly
1616	 *        classified.
1617	 */
1618	TYPE_ADDLSTATUS_NONE,
1619
1620	/**
1621	 * The element type provides additional element status
1622	 * in page 0x0A.
1623	 */
1624	TYPE_ADDLSTATUS_MANDATORY,
1625
1626	/**
1627	 * The element type may provide additional element status
1628	 * in page 0x0A, but i
1629	 */
1630	TYPE_ADDLSTATUS_OPTIONAL
1631} ses_addlstatus_avail_t;
1632
1633/**
1634 * \brief Check to see whether a given type (as obtained via type headers) is
1635 *	  supported by the additional status command.
1636 *
1637 * \param enc     SES softc to check.
1638 * \param typidx  Type index to check for.
1639 *
1640 * \return  An enumeration indicating if additional status is mandatory,
1641 *          optional, or not required for this type.
1642 */
1643static ses_addlstatus_avail_t
1644ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx)
1645{
1646	enc_cache_t *enc_cache;
1647	ses_cache_t *ses_cache;
1648
1649	enc_cache = &enc->enc_daemon_cache;
1650	ses_cache = enc_cache->private;
1651	switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) {
1652	case ELMTYP_DEVICE:
1653	case ELMTYP_ARRAY_DEV:
1654	case ELMTYP_SAS_EXP:
1655		return (TYPE_ADDLSTATUS_MANDATORY);
1656	case ELMTYP_SCSI_INI:
1657	case ELMTYP_SCSI_TGT:
1658	case ELMTYP_ESCC:
1659		return (TYPE_ADDLSTATUS_OPTIONAL);
1660	default:
1661		/* No additional status information available. */
1662		break;
1663	}
1664	return (TYPE_ADDLSTATUS_NONE);
1665}
1666
1667static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *,
1668				     uint8_t *, int);
1669static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *,
1670				      int, int, int, int);
1671
1672/**
1673 * \brief Parse the additional status element data for each object.
1674 *
1675 * \param enc       The SES softc to update.
1676 * \param buf       The buffer containing the additional status
1677 *                  element response.
1678 * \param xfer_len  Size of the buffer.
1679 *
1680 * \return  0 on success, errno otherwise.
1681 */
1682static int
1683ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state,
1684    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1685{
1686	struct ses_iterator iter, titer;
1687	int eip;
1688	int err;
1689	int ignore_index = 0;
1690	int length;
1691	int offset;
1692	enc_cache_t *enc_cache;
1693	ses_cache_t *ses_cache;
1694	uint8_t *buf;
1695	ses_element_t *elmpriv;
1696	const struct ses_page_hdr *hdr;
1697	enc_element_t *element, *telement;
1698
1699	enc_cache = &enc->enc_daemon_cache;
1700	ses_cache = enc_cache->private;
1701	buf = *bufp;
1702	err = -1;
1703
1704	if (error != 0) {
1705		err = error;
1706		goto out;
1707	}
1708	ses_cache_free_elm_addlstatus(enc, enc_cache);
1709	ses_cache->elm_addlstatus_page =
1710	    (struct ses_addl_elem_status_page *)buf;
1711	*bufp = NULL;
1712
1713	/*
1714	 * The objects appear in the same order here as in Enclosure Status,
1715	 * which itself is ordered by the Type Descriptors from the Config
1716	 * page.  However, it is necessary to skip elements that are not
1717	 * supported by this page when counting them.
1718	 */
1719	hdr = &ses_cache->elm_addlstatus_page->hdr;
1720	length = ses_page_length(hdr);
1721	ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length);
1722	/* Make sure the length includes at least one header. */
1723	if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) {
1724		ENC_VLOG(enc, "Runt Additional Element Status Page\n");
1725		goto out;
1726	}
1727	if (length > xfer_len) {
1728		ENC_VLOG(enc, "Additional Element Status Page Too Long\n");
1729		goto out;
1730	}
1731
1732	if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) {
1733		ENC_DLOG(enc, "%s: Generation count change detected\n",
1734		    __func__);
1735		enc_update_request(enc, SES_UPDATE_GETCONFIG);
1736		goto out;
1737	}
1738
1739	offset = sizeof(struct ses_page_hdr);
1740	ses_iter_init(enc, enc_cache, &iter);
1741	while (offset < length
1742	    && (element = ses_iter_next(&iter)) != NULL) {
1743		struct ses_elm_addlstatus_base_hdr *elm_hdr;
1744		int proto_info_len;
1745		ses_addlstatus_avail_t status_type;
1746
1747		/*
1748		 * Additional element status is only provided for
1749		 * individual elements (i.e. overal status elements
1750		 * are excluded) and those of the types specified
1751		 * in the SES spec.
1752		 */
1753		status_type = ses_typehasaddlstatus(enc, iter.type_index);
1754		if (iter.individual_element_index == ITERATOR_INDEX_INVALID
1755		 || status_type == TYPE_ADDLSTATUS_NONE)
1756			continue;
1757
1758		elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset];
1759		eip = ses_elm_addlstatus_eip(elm_hdr);
1760		if (eip && !ignore_index) {
1761			struct ses_elm_addlstatus_eip_hdr *eip_hdr;
1762			int expected_index;
1763
1764			eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr;
1765			expected_index = iter.individual_element_index;
1766			titer = iter;
1767			telement = ses_iter_seek_to(&titer,
1768						   eip_hdr->element_index,
1769						   SES_ELEM_INDEX_INDIVIDUAL);
1770			if (telement != NULL &&
1771			    (ses_typehasaddlstatus(enc, titer.type_index) !=
1772			     TYPE_ADDLSTATUS_NONE ||
1773			     titer.type_index > ELMTYP_SAS_CONN)) {
1774				iter = titer;
1775				element = telement;
1776			} else
1777				ignore_index = 1;
1778
1779			if (iter.individual_element_index > expected_index
1780			 && status_type == TYPE_ADDLSTATUS_MANDATORY) {
1781				ENC_VLOG(enc, "%s: provided element "
1782					"index %d skips mandatory status "
1783					" element at index %d\n",
1784					__func__, eip_hdr->element_index,
1785					expected_index);
1786			}
1787		}
1788		elmpriv = element->elm_private;
1789		elmpriv->addl.hdr = elm_hdr;
1790		ENC_DLOG(enc, "%s: global element index=%d, type index=%d "
1791		    "type element index=%d, offset=0x%x, "
1792		    "byte0=0x%x, length=0x%x\n", __func__,
1793		    iter.global_element_index, iter.type_index,
1794		    iter.type_element_index, offset, elmpriv->addl.hdr->byte0,
1795		    elmpriv->addl.hdr->length);
1796
1797		/* Skip to after the length field */
1798		offset += sizeof(struct ses_elm_addlstatus_base_hdr);
1799
1800		/* Make sure the descriptor is within bounds */
1801		if ((offset + elmpriv->addl.hdr->length) > length) {
1802			ENC_VLOG(enc, "Element %d Beyond End "
1803			    "of Additional Element Status Descriptors\n",
1804			    iter.global_element_index);
1805			break;
1806		}
1807
1808		/* Advance to the protocol data, skipping eip bytes if needed */
1809		offset += (eip * SES_EIP_HDR_EXTRA_LEN);
1810		proto_info_len = elmpriv->addl.hdr->length
1811			       - (eip * SES_EIP_HDR_EXTRA_LEN);
1812
1813		/* Errors in this block are ignored as they are non-fatal */
1814		switch(ses_elm_addlstatus_proto(elmpriv->addl.hdr)) {
1815		case SPSP_PROTO_FC:
1816			if (elmpriv->addl.hdr->length == 0)
1817				break;
1818			ses_get_elm_addlstatus_fc(enc, enc_cache,
1819						  &buf[offset], proto_info_len);
1820			break;
1821		case SPSP_PROTO_SAS:
1822			if (elmpriv->addl.hdr->length <= 2)
1823				break;
1824			ses_get_elm_addlstatus_sas(enc, enc_cache,
1825						   &buf[offset],
1826						   proto_info_len,
1827						   eip, iter.type_index,
1828						   iter.global_element_index);
1829			break;
1830		default:
1831			ENC_VLOG(enc, "Element %d: Unknown Additional Element "
1832			    "Protocol 0x%x\n", iter.global_element_index,
1833			    ses_elm_addlstatus_proto(elmpriv->addl.hdr));
1834			break;
1835		}
1836
1837		offset += proto_info_len;
1838	}
1839	err = 0;
1840out:
1841	if (err)
1842		ses_cache_free_elm_addlstatus(enc, enc_cache);
1843	enc_update_request(enc, SES_PUBLISH_PHYSPATHS);
1844	enc_update_request(enc, SES_PUBLISH_CACHE);
1845	return (err);
1846}
1847
1848static int
1849ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
1850    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1851{
1852	ses_softc_t *ses;
1853
1854	ses = enc->enc_private;
1855	/*
1856	 * Possible errors:
1857	 *  o Generation count wrong.
1858	 *  o Some SCSI status error.
1859	 */
1860	ses_terminate_control_requests(&ses->ses_pending_requests, error);
1861	enc_update_request(enc, SES_UPDATE_GETSTATUS);
1862	return (0);
1863}
1864
1865static int
1866ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state,
1867    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1868{
1869	struct ses_iterator iter;
1870	enc_cache_t *enc_cache;
1871	ses_cache_t *ses_cache;
1872	enc_element_t *element;
1873
1874	enc_cache = &enc->enc_daemon_cache;
1875	ses_cache = enc_cache->private;
1876
1877	ses_iter_init(enc, enc_cache, &iter);
1878	while ((element = ses_iter_next(&iter)) != NULL) {
1879		/*
1880		 * ses_set_physpath() returns success if we changed
1881		 * the physpath of any element.  This allows us to
1882		 * only announce devices once regardless of how
1883		 * many times we process additional element status.
1884		 */
1885		if (ses_set_physpath(enc, element, &iter) == 0)
1886			ses_print_addl_data(enc, element);
1887	}
1888
1889	return (0);
1890}
1891
1892static int
1893ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state,
1894    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1895{
1896
1897	sx_xlock(&enc->enc_cache_lock);
1898	ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache,
1899			/*dst*/&enc->enc_cache);
1900	sx_xunlock(&enc->enc_cache_lock);
1901
1902	return (0);
1903}
1904
1905/**
1906 * \brief Parse the descriptors for each object.
1907 *
1908 * \param enc       The SES softc to update.
1909 * \param buf       The buffer containing the descriptor list response.
1910 * \param xfer_len  Size of the buffer.
1911 *
1912 * \return	0 on success, errno otherwise.
1913 */
1914static int
1915ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state,
1916    union ccb *ccb, uint8_t **bufp, int error, int xfer_len)
1917{
1918	ses_softc_t *ses;
1919	struct ses_iterator iter;
1920	enc_element_t *element;
1921	int err;
1922	int offset;
1923	u_long length, plength;
1924	enc_cache_t *enc_cache;
1925	ses_cache_t *ses_cache;
1926	uint8_t *buf;
1927	ses_element_t *elmpriv;
1928	const struct ses_page_hdr *phdr;
1929	const struct ses_elm_desc_hdr *hdr;
1930
1931	ses = enc->enc_private;
1932	enc_cache = &enc->enc_daemon_cache;
1933	ses_cache = enc_cache->private;
1934	buf = *bufp;
1935	err = -1;
1936
1937	if (error != 0) {
1938		err = error;
1939		goto out;
1940	}
1941	ses_cache_free_elm_descs(enc, enc_cache);
1942	ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf;
1943	*bufp = NULL;
1944
1945	phdr = &ses_cache->elm_descs_page->hdr;
1946	plength = ses_page_length(phdr);
1947	if (xfer_len < sizeof(struct ses_page_hdr)) {
1948		ENC_VLOG(enc, "Runt Element Descriptor Page\n");
1949		goto out;
1950	}
1951	if (plength > xfer_len) {
1952		ENC_VLOG(enc, "Element Descriptor Page Too Long\n");
1953		goto out;
1954	}
1955
1956	if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) {
1957		ENC_VLOG(enc, "%s: Generation count change detected\n",
1958		    __func__);
1959		enc_update_request(enc, SES_UPDATE_GETCONFIG);
1960		goto out;
1961	}
1962
1963	offset = sizeof(struct ses_page_hdr);
1964
1965	ses_iter_init(enc, enc_cache, &iter);
1966	while (offset < plength
1967	    && (element = ses_iter_next(&iter)) != NULL) {
1968
1969		if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) {
1970			ENC_VLOG(enc, "Element %d Descriptor Header Past "
1971			    "End of Buffer\n", iter.global_element_index);
1972			goto out;
1973		}
1974		hdr = (struct ses_elm_desc_hdr *)&buf[offset];
1975		length = scsi_2btoul(hdr->length);
1976		ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__,
1977		    iter.global_element_index, iter.type_index,
1978		    iter.type_element_index, length, offset);
1979		if ((offset + sizeof(*hdr) + length) > plength) {
1980			ENC_VLOG(enc, "Element%d Descriptor Past "
1981			    "End of Buffer\n", iter.global_element_index);
1982			goto out;
1983		}
1984		offset += sizeof(*hdr);
1985
1986		if (length > 0) {
1987			elmpriv = element->elm_private;
1988			elmpriv->descr_len = length;
1989			elmpriv->descr = &buf[offset];
1990		}
1991
1992		/* skip over the descriptor itself */
1993		offset += length;
1994	}
1995
1996	err = 0;
1997out:
1998	if (err == 0) {
1999		if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
2000			enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
2001	}
2002	enc_update_request(enc, SES_PUBLISH_CACHE);
2003	return (err);
2004}
2005
2006static int
2007ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state,
2008		       union ccb *ccb, uint8_t *buf)
2009{
2010
2011	if (enc->enc_type == ENC_SEMB_SES) {
2012		semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5,
2013					NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1,
2014					state->page_code, buf, state->buf_size,
2015					state->timeout);
2016	} else {
2017		scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5,
2018					NULL, MSG_SIMPLE_Q_TAG, /*pcv*/1,
2019					state->page_code, buf, state->buf_size,
2020					SSD_FULL_SIZE, state->timeout);
2021	}
2022	return (0);
2023}
2024
2025/**
2026 * \brief Encode the object status into the response buffer, which is
2027 *	  expected to contain the current enclosure status.  This function
2028 *	  turns off all the 'select' bits for the objects except for the
2029 *	  object specified, then sends it back to the enclosure.
2030 *
2031 * \param enc	SES enclosure the change is being applied to.
2032 * \param buf	Buffer containing the current enclosure status response.
2033 * \param amt	Length of the response in the buffer.
2034 * \param req	The control request to be applied to buf.
2035 *
2036 * \return	0 on success, errno otherwise.
2037 */
2038static int
2039ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req)
2040{
2041	struct ses_iterator iter;
2042	enc_element_t *element;
2043	int offset;
2044	struct ses_control_page_hdr *hdr;
2045
2046	ses_iter_init(enc, &enc->enc_cache, &iter);
2047	hdr = (struct ses_control_page_hdr *)buf;
2048	if (req->elm_idx == -1) {
2049		/* for enclosure status, at least 2 bytes are needed */
2050		if (amt < 2)
2051			return EIO;
2052		hdr->control_flags =
2053		    req->elm_stat.comstatus & SES_SET_STATUS_MASK;
2054		ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags);
2055		return (0);
2056	}
2057
2058	element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL);
2059	if (element == NULL)
2060		return (ENXIO);
2061
2062	/*
2063	 * Seek to the type set that corresponds to the requested object.
2064	 * The +1 is for the overall status element for the type.
2065	 */
2066	offset = sizeof(struct ses_control_page_hdr)
2067	       + (iter.global_element_index * sizeof(struct ses_comstat));
2068
2069	/* Check for buffer overflow. */
2070	if (offset + sizeof(struct ses_comstat) > amt)
2071		return (EIO);
2072
2073	/* Set the status. */
2074	memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat));
2075
2076	ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n",
2077	    iter.type_index, iter.global_element_index, offset,
2078	    req->elm_stat.comstatus, req->elm_stat.comstat[0],
2079	    req->elm_stat.comstat[1], req->elm_stat.comstat[2]);
2080
2081	return (0);
2082}
2083
2084static int
2085ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state,
2086			 union ccb *ccb, uint8_t *buf)
2087{
2088	ses_softc_t			*ses;
2089	enc_cache_t			*enc_cache;
2090	ses_cache_t			*ses_cache;
2091	struct ses_control_page_hdr	*hdr;
2092	ses_control_request_t		*req;
2093	size_t				 plength;
2094	size_t				 offset;
2095
2096	ses = enc->enc_private;
2097	enc_cache = &enc->enc_daemon_cache;
2098	ses_cache = enc_cache->private;
2099	hdr = (struct ses_control_page_hdr *)buf;
2100
2101	if (ses_cache->status_page == NULL) {
2102		ses_terminate_control_requests(&ses->ses_requests, EIO);
2103		return (EIO);
2104	}
2105
2106	plength = ses_page_length(&ses_cache->status_page->hdr);
2107	memcpy(buf, ses_cache->status_page, plength);
2108
2109	/* Disable the select bits in all status entries.  */
2110	offset = sizeof(struct ses_control_page_hdr);
2111	for (offset = sizeof(struct ses_control_page_hdr);
2112	     offset < plength; offset += sizeof(struct ses_comstat)) {
2113		buf[offset] &= ~SESCTL_CSEL;
2114	}
2115
2116	/* And make sure the INVOP bit is clear.  */
2117	hdr->control_flags &= ~SES_ENCSTAT_INVOP;
2118
2119	/* Apply incoming requests. */
2120	while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) {
2121
2122		TAILQ_REMOVE(&ses->ses_requests, req, links);
2123		req->result = ses_encode(enc, buf, plength, req);
2124		if (req->result != 0) {
2125			wakeup(req);
2126			continue;
2127		}
2128		TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links);
2129	}
2130
2131	if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0)
2132		return (ENOENT);
2133
2134	/* Fill out the ccb */
2135	if (enc->enc_type == ENC_SEMB_SES) {
2136		semb_send_diagnostic(&ccb->ataio, /*retries*/5, NULL,
2137			     MSG_SIMPLE_Q_TAG,
2138			     buf, ses_page_length(&ses_cache->status_page->hdr),
2139			     state->timeout);
2140	} else {
2141		scsi_send_diagnostic(&ccb->csio, /*retries*/5, NULL,
2142			     MSG_SIMPLE_Q_TAG, /*unit_offline*/0,
2143			     /*device_offline*/0, /*self_test*/0,
2144			     /*page_format*/1, /*self_test_code*/0,
2145			     buf, ses_page_length(&ses_cache->status_page->hdr),
2146			     SSD_FULL_SIZE, state->timeout);
2147	}
2148	return (0);
2149}
2150
2151static int
2152ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache,
2153			  uint8_t *buf, int bufsiz)
2154{
2155	ENC_VLOG(enc, "FC Device Support Stubbed in Additional Status Page\n");
2156	return (ENODEV);
2157}
2158
2159#define	SES_PRINT_PORTS(p, type) do {					\
2160	sbuf_printf(sbp, " %s(", type);					\
2161	if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) == 0)			\
2162		sbuf_printf(sbp, " None");				\
2163	else {								\
2164		if ((p) & SES_SASOBJ_DEV_PHY_SMP)			\
2165			sbuf_printf(sbp, " SMP");			\
2166		if ((p) & SES_SASOBJ_DEV_PHY_STP)			\
2167			sbuf_printf(sbp, " STP");			\
2168		if ((p) & SES_SASOBJ_DEV_PHY_SSP)			\
2169			sbuf_printf(sbp, " SSP");			\
2170	}								\
2171	sbuf_printf(sbp, " )");						\
2172} while(0)
2173
2174/**
2175 * \brief Print the additional element status data for this object, for SAS
2176 * 	  type 0 objects.  See SES2 r20 Section 6.1.13.3.2.
2177 *
2178 * \param sesname	SES device name associated with the object.
2179 * \param sbp		Sbuf to print to.
2180 * \param obj		The object to print the data for.
2181 * \param periph_name	Peripheral string associated with the object.
2182 */
2183static void
2184ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp,
2185			      enc_element_t *obj, char *periph_name)
2186{
2187	int i;
2188	ses_element_t *elmpriv;
2189	struct ses_addl_status *addl;
2190	struct ses_elm_sas_device_phy *phy;
2191
2192	elmpriv = obj->elm_private;
2193	addl = &(elmpriv->addl);
2194	if (addl->proto_hdr.sas == NULL)
2195		return;
2196	sbuf_printf(sbp, "%s: %s: SAS Device Slot Element:",
2197	    sesname, periph_name);
2198	sbuf_printf(sbp, " %d Phys", addl->proto_hdr.sas->base_hdr.num_phys);
2199	if (ses_elm_addlstatus_eip(addl->hdr))
2200		sbuf_printf(sbp, " at Slot %d",
2201		    addl->proto_hdr.sas->type0_eip.dev_slot_num);
2202	if (ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas))
2203		sbuf_printf(sbp, ", Not All Phys");
2204	sbuf_printf(sbp, "\n");
2205	if (addl->proto_data.sasdev_phys == NULL)
2206		return;
2207	for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) {
2208		phy = &addl->proto_data.sasdev_phys[i];
2209		sbuf_printf(sbp, "%s:  phy %d:", sesname, i);
2210		if (ses_elm_sas_dev_phy_sata_dev(phy))
2211			/* Spec says all other fields are specific values */
2212			sbuf_printf(sbp, " SATA device\n");
2213		else {
2214			sbuf_printf(sbp, " SAS device type %d id %d\n",
2215			    ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id);
2216			sbuf_printf(sbp, "%s:  phy %d: protocols:", sesname, i);
2217			SES_PRINT_PORTS(phy->initiator_ports, "Initiator");
2218			SES_PRINT_PORTS(phy->target_ports, "Target");
2219			sbuf_printf(sbp, "\n");
2220		}
2221		sbuf_printf(sbp, "%s:  phy %d: parent %jx addr %jx\n",
2222		    sesname, i,
2223		    (uintmax_t)scsi_8btou64(phy->parent_addr),
2224		    (uintmax_t)scsi_8btou64(phy->phy_addr));
2225	}
2226}
2227#undef SES_PRINT_PORTS
2228
2229/**
2230 * \brief Report whether a given enclosure object is an expander.
2231 *
2232 * \param enc	SES softc associated with object.
2233 * \param obj	Enclosure object to report for.
2234 *
2235 * \return	1 if true, 0 otherwise.
2236 */
2237static int
2238ses_obj_is_expander(enc_softc_t *enc, enc_element_t *obj)
2239{
2240	return (obj->enctype == ELMTYP_SAS_EXP);
2241}
2242
2243/**
2244 * \brief Print the additional element status data for this object, for SAS
2245 *	  type 1 objects.  See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4.
2246 *
2247 * \param enc		SES enclosure, needed for type identification.
2248 * \param sesname	SES device name associated with the object.
2249 * \param sbp		Sbuf to print to.
2250 * \param obj		The object to print the data for.
2251 * \param periph_name	Peripheral string associated with the object.
2252 */
2253static void
2254ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname,
2255    struct sbuf *sbp, enc_element_t *obj, char *periph_name)
2256{
2257	int i, num_phys;
2258	ses_element_t *elmpriv;
2259	struct ses_addl_status *addl;
2260	struct ses_elm_sas_expander_phy *exp_phy;
2261	struct ses_elm_sas_port_phy *port_phy;
2262
2263	elmpriv = obj->elm_private;
2264	addl = &(elmpriv->addl);
2265	if (addl->proto_hdr.sas == NULL)
2266		return;
2267	sbuf_printf(sbp, "%s: %s: SAS ", sesname, periph_name);
2268	if (ses_obj_is_expander(enc, obj)) {
2269		num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
2270		sbuf_printf(sbp, "Expander: %d Phys", num_phys);
2271		if (addl->proto_data.sasexp_phys == NULL)
2272			return;
2273		for (i = 0;i < num_phys;i++) {
2274			exp_phy = &addl->proto_data.sasexp_phys[i];
2275			sbuf_printf(sbp, "%s:  phy %d: connector %d other %d\n",
2276			    sesname, i, exp_phy->connector_index,
2277			    exp_phy->other_index);
2278		}
2279	} else {
2280		num_phys = addl->proto_hdr.sas->base_hdr.num_phys;
2281		sbuf_printf(sbp, "Port: %d Phys", num_phys);
2282		if (addl->proto_data.sasport_phys == NULL)
2283			return;
2284		for (i = 0;i < num_phys;i++) {
2285			port_phy = &addl->proto_data.sasport_phys[i];
2286			sbuf_printf(sbp,
2287			    "%s:  phy %d: id %d connector %d other %d\n",
2288			    sesname, i, port_phy->phy_id,
2289			    port_phy->connector_index, port_phy->other_index);
2290			sbuf_printf(sbp, "%s:  phy %d: addr %jx\n", sesname, i,
2291			    (uintmax_t)scsi_8btou64(port_phy->phy_addr));
2292		}
2293	}
2294}
2295
2296/**
2297 * \brief Print the additional element status data for this object.
2298 *
2299 * \param enc		SES softc associated with the object.
2300 * \param obj		The object to print the data for.
2301 */
2302static void
2303ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj)
2304{
2305	ses_element_t *elmpriv;
2306	struct ses_addl_status *addl;
2307	struct sbuf sesname, name, out;
2308
2309	elmpriv = obj->elm_private;
2310	if (elmpriv == NULL)
2311		return;
2312
2313	addl = &(elmpriv->addl);
2314	if (addl->hdr == NULL)
2315		return;
2316
2317	sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND);
2318	sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND);
2319	sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND);
2320	ses_paths_iter(enc, obj, ses_elmdevname_callback, &name);
2321	if (sbuf_len(&name) == 0)
2322		sbuf_printf(&name, "(none)");
2323	sbuf_finish(&name);
2324	sbuf_printf(&sesname, "%s%d", enc->periph->periph_name,
2325	    enc->periph->unit_number);
2326	sbuf_finish(&sesname);
2327	if (elmpriv->descr != NULL)
2328		sbuf_printf(&out, "%s: %s: Element descriptor: '%s'\n",
2329		    sbuf_data(&sesname), sbuf_data(&name), elmpriv->descr);
2330	switch(ses_elm_addlstatus_proto(addl->hdr)) {
2331	case SPSP_PROTO_SAS:
2332		switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) {
2333		case SES_SASOBJ_TYPE_SLOT:
2334			ses_print_addl_data_sas_type0(sbuf_data(&sesname),
2335			    &out, obj, sbuf_data(&name));
2336			break;
2337		case SES_SASOBJ_TYPE_OTHER:
2338			ses_print_addl_data_sas_type1(enc, sbuf_data(&sesname),
2339			    &out, obj, sbuf_data(&name));
2340			break;
2341		default:
2342			break;
2343		}
2344		break;
2345	case SPSP_PROTO_FC:	/* stubbed for now */
2346		break;
2347	default:
2348		break;
2349	}
2350	sbuf_finish(&out);
2351	printf("%s", sbuf_data(&out));
2352	sbuf_delete(&out);
2353	sbuf_delete(&name);
2354	sbuf_delete(&sesname);
2355}
2356
2357/**
2358 * \brief Update the softc with the additional element status data for this
2359 * 	  object, for SAS type 0 objects.
2360 *
2361 * \param enc		SES softc to be updated.
2362 * \param buf		The additional element status response buffer.
2363 * \param bufsiz	Size of the response buffer.
2364 * \param eip		The EIP bit value.
2365 * \param nobj		Number of objects attached to the SES softc.
2366 *
2367 * \return		0 on success, errno otherwise.
2368 */
2369static int
2370ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache,
2371				 uint8_t *buf, int bufsiz, int eip, int nobj)
2372{
2373	int err, offset, physz;
2374	enc_element_t *obj;
2375	ses_element_t *elmpriv;
2376	struct ses_addl_status *addl;
2377
2378	err = offset = 0;
2379
2380	/* basic object setup */
2381	obj = &(enc_cache->elm_map[nobj]);
2382	elmpriv = obj->elm_private;
2383	addl = &(elmpriv->addl);
2384
2385	addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset];
2386
2387	/* Don't assume this object has any phys */
2388	bzero(&addl->proto_data, sizeof(addl->proto_data));
2389	if (addl->proto_hdr.sas->base_hdr.num_phys == 0)
2390		goto out;
2391
2392	/* Skip forward to the phy list */
2393	if (eip)
2394		offset += sizeof(struct ses_elm_sas_type0_eip_hdr);
2395	else
2396		offset += sizeof(struct ses_elm_sas_type0_base_hdr);
2397
2398	/* Make sure the phy list fits in the buffer */
2399	physz = addl->proto_hdr.sas->base_hdr.num_phys;
2400	physz *= sizeof(struct ses_elm_sas_device_phy);
2401	if (physz > (bufsiz - offset + 4)) {
2402		ENC_VLOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n",
2403		    nobj);
2404		err = EIO;
2405		goto out;
2406	}
2407
2408	/* Point to the phy list */
2409	addl->proto_data.sasdev_phys =
2410	    (struct ses_elm_sas_device_phy *)&buf[offset];
2411
2412out:
2413	return (err);
2414}
2415
2416/**
2417 * \brief Update the softc with the additional element status data for this
2418 * 	  object, for SAS type 1 objects.
2419 *
2420 * \param enc		SES softc to be updated.
2421 * \param buf		The additional element status response buffer.
2422 * \param bufsiz	Size of the response buffer.
2423 * \param eip		The EIP bit value.
2424 * \param nobj		Number of objects attached to the SES softc.
2425 *
2426 * \return		0 on success, errno otherwise.
2427 */
2428static int
2429ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache,
2430			         uint8_t *buf, int bufsiz, int eip, int nobj)
2431{
2432	int err, offset, physz;
2433	enc_element_t *obj;
2434	ses_element_t *elmpriv;
2435	struct ses_addl_status *addl;
2436
2437	err = offset = 0;
2438
2439	/* basic object setup */
2440	obj = &(enc_cache->elm_map[nobj]);
2441	elmpriv = obj->elm_private;
2442	addl = &(elmpriv->addl);
2443
2444	addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset];
2445
2446	/* Don't assume this object has any phys */
2447	bzero(&addl->proto_data, sizeof(addl->proto_data));
2448	if (addl->proto_hdr.sas->base_hdr.num_phys == 0)
2449		goto out;
2450
2451	/* Process expanders differently from other type1 cases */
2452	if (ses_obj_is_expander(enc, obj)) {
2453		offset += sizeof(struct ses_elm_sas_type1_expander_hdr);
2454		physz = addl->proto_hdr.sas->base_hdr.num_phys *
2455		    sizeof(struct ses_elm_sas_expander_phy);
2456		if (physz > (bufsiz - offset)) {
2457			ENC_VLOG(enc, "Element %d: Expander Phy List Beyond "
2458			    "End Of Buffer\n", nobj);
2459			err = EIO;
2460			goto out;
2461		}
2462		addl->proto_data.sasexp_phys =
2463		    (struct ses_elm_sas_expander_phy *)&buf[offset];
2464	} else {
2465		offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr);
2466		physz = addl->proto_hdr.sas->base_hdr.num_phys *
2467		    sizeof(struct ses_elm_sas_port_phy);
2468		if (physz > (bufsiz - offset + 4)) {
2469			ENC_VLOG(enc, "Element %d: Port Phy List Beyond End "
2470			    "Of Buffer\n", nobj);
2471			err = EIO;
2472			goto out;
2473		}
2474		addl->proto_data.sasport_phys =
2475		    (struct ses_elm_sas_port_phy *)&buf[offset];
2476	}
2477
2478out:
2479	return (err);
2480}
2481
2482/**
2483 * \brief Update the softc with the additional element status data for this
2484 * 	  object, for SAS objects.
2485 *
2486 * \param enc		SES softc to be updated.
2487 * \param buf		The additional element status response buffer.
2488 * \param bufsiz	Size of the response buffer.
2489 * \param eip		The EIP bit value.
2490 * \param tidx		Type index for this object.
2491 * \param nobj		Number of objects attached to the SES softc.
2492 *
2493 * \return		0 on success, errno otherwise.
2494 */
2495static int
2496ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache,
2497			   uint8_t *buf, int bufsiz, int eip, int tidx,
2498			   int nobj)
2499{
2500	int dtype, err;
2501	ses_cache_t *ses_cache;
2502	union ses_elm_sas_hdr *hdr;
2503
2504	/* Need to be able to read the descriptor type! */
2505	if (bufsiz < sizeof(union ses_elm_sas_hdr)) {
2506		err = EIO;
2507		goto out;
2508	}
2509
2510	ses_cache = enc_cache->private;
2511
2512	hdr = (union ses_elm_sas_hdr *)buf;
2513	dtype = ses_elm_sas_descr_type(hdr);
2514	switch(dtype) {
2515	case SES_SASOBJ_TYPE_SLOT:
2516		switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) {
2517		case ELMTYP_DEVICE:
2518		case ELMTYP_ARRAY_DEV:
2519			break;
2520		default:
2521			ENC_VLOG(enc, "Element %d has Additional Status type 0, "
2522			    "invalid for SES element type 0x%x\n", nobj,
2523			    ses_cache->ses_types[tidx].hdr->etype_elm_type);
2524			err = ENODEV;
2525			goto out;
2526		}
2527		err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache,
2528						       buf, bufsiz, eip,
2529		    nobj);
2530		break;
2531	case SES_SASOBJ_TYPE_OTHER:
2532		switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) {
2533		case ELMTYP_SAS_EXP:
2534		case ELMTYP_SCSI_INI:
2535		case ELMTYP_SCSI_TGT:
2536		case ELMTYP_ESCC:
2537			break;
2538		default:
2539			ENC_VLOG(enc, "Element %d has Additional Status type 1, "
2540			    "invalid for SES element type 0x%x\n", nobj,
2541			    ses_cache->ses_types[tidx].hdr->etype_elm_type);
2542			err = ENODEV;
2543			goto out;
2544		}
2545		err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf,
2546						       bufsiz, eip, nobj);
2547		break;
2548	default:
2549		ENC_VLOG(enc, "Element %d of type 0x%x has Additional Status "
2550		    "of unknown type 0x%x\n", nobj,
2551		    ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype);
2552		err = ENODEV;
2553		break;
2554	}
2555
2556out:
2557	return (err);
2558}
2559
2560static void
2561ses_softc_invalidate(enc_softc_t *enc)
2562{
2563	ses_softc_t *ses;
2564
2565	ses = enc->enc_private;
2566	ses_terminate_control_requests(&ses->ses_requests, ENXIO);
2567}
2568
2569static void
2570ses_softc_cleanup(enc_softc_t *enc)
2571{
2572
2573	ses_cache_free(enc, &enc->enc_cache);
2574	ses_cache_free(enc, &enc->enc_daemon_cache);
2575	ENC_FREE_AND_NULL(enc->enc_private);
2576	ENC_FREE_AND_NULL(enc->enc_cache.private);
2577	ENC_FREE_AND_NULL(enc->enc_daemon_cache.private);
2578}
2579
2580static int
2581ses_init_enc(enc_softc_t *enc)
2582{
2583	return (0);
2584}
2585
2586static int
2587ses_get_enc_status(enc_softc_t *enc, int slpflag)
2588{
2589	/* Automatically updated, caller checks enc_cache->encstat itself */
2590	return (0);
2591}
2592
2593static int
2594ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag)
2595{
2596	ses_control_request_t req;
2597	ses_softc_t	     *ses;
2598
2599	ses = enc->enc_private;
2600	req.elm_idx = SES_SETSTATUS_ENC_IDX;
2601	req.elm_stat.comstatus = encstat & 0xf;
2602
2603	TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links);
2604	enc_update_request(enc, SES_PROCESS_CONTROL_REQS);
2605	cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
2606
2607	return (req.result);
2608}
2609
2610static int
2611ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag)
2612{
2613	unsigned int i = elms->elm_idx;
2614
2615	memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4);
2616	return (0);
2617}
2618
2619static int
2620ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag)
2621{
2622	ses_control_request_t req;
2623	ses_softc_t	     *ses;
2624
2625	/* If this is clear, we don't do diddly.  */
2626	if ((elms->cstat[0] & SESCTL_CSEL) == 0)
2627		return (0);
2628
2629	ses = enc->enc_private;
2630	req.elm_idx = elms->elm_idx;
2631	memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat));
2632
2633	TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links);
2634	enc_update_request(enc, SES_PROCESS_CONTROL_REQS);
2635	cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0);
2636
2637	return (req.result);
2638}
2639
2640static int
2641ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd)
2642{
2643	int i = (int)elmd->elm_idx;
2644	ses_element_t *elmpriv;
2645
2646	/* Assume caller has already checked obj_id validity */
2647	elmpriv = enc->enc_cache.elm_map[i].elm_private;
2648	/* object might not have a descriptor */
2649	if (elmpriv == NULL || elmpriv->descr == NULL) {
2650		elmd->elm_desc_len = 0;
2651		return (0);
2652	}
2653	if (elmd->elm_desc_len > elmpriv->descr_len)
2654		elmd->elm_desc_len = elmpriv->descr_len;
2655	copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len);
2656	return (0);
2657}
2658
2659/**
2660 * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the
2661 *	  given object id if one is available.
2662 *
2663 * \param enc	SES softc to examine.
2664 * \param objdn	ioctl structure to read/write device name info.
2665 *
2666 * \return	0 on success, errno otherwise.
2667 */
2668static int
2669ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn)
2670{
2671	struct sbuf sb;
2672	int len;
2673
2674	len = elmdn->elm_names_size;
2675	if (len < 0)
2676		return (EINVAL);
2677
2678	sbuf_new(&sb, elmdn->elm_devnames, len, 0);
2679
2680	cam_periph_unlock(enc->periph);
2681	ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx],
2682		       ses_elmdevname_callback, &sb);
2683	sbuf_finish(&sb);
2684	elmdn->elm_names_len = sbuf_len(&sb);
2685	cam_periph_lock(enc->periph);
2686	return (elmdn->elm_names_len > 0 ? 0 : ENODEV);
2687}
2688
2689/**
2690 * \brief Send a string to the primary subenclosure using the String Out
2691 * 	  SES diagnostic page.
2692 *
2693 * \param enc	SES enclosure to run the command on.
2694 * \param sstr	SES string structure to operate on
2695 * \param ioc	Ioctl being performed
2696 *
2697 * \return	0 on success, errno otherwise.
2698 */
2699static int
2700ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc)
2701{
2702	ses_softc_t *ses;
2703	enc_cache_t *enc_cache;
2704	ses_cache_t *ses_cache;
2705	const struct ses_enc_desc *enc_desc;
2706	int amt, payload, ret;
2707	char cdb[6];
2708	char str[32];
2709	char vendor[9];
2710	char product[17];
2711	char rev[5];
2712	uint8_t *buf;
2713	size_t size, rsize;
2714
2715	ses = enc->enc_private;
2716	enc_cache = &enc->enc_daemon_cache;
2717	ses_cache = enc_cache->private;
2718
2719	/* Implement SES2r20 6.1.6 */
2720	if (sstr->bufsiz > 0xffff)
2721		return (EINVAL); /* buffer size too large */
2722
2723	if (ioc == ENCIOC_SETSTRING) {
2724		payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */
2725		amt = 0 - payload;
2726		buf = ENC_MALLOC(payload);
2727		if (buf == NULL)
2728			return ENOMEM;
2729
2730		ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT);
2731		/* Construct the page request */
2732		buf[0] = SesStringOut;
2733		buf[1] = 0;
2734		buf[2] = sstr->bufsiz >> 8;
2735		buf[3] = sstr->bufsiz & 0xff;
2736		memcpy(&buf[4], sstr->buf, sstr->bufsiz);
2737	} else if (ioc == ENCIOC_GETSTRING) {
2738		payload = sstr->bufsiz;
2739		amt = payload;
2740		ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN);
2741		buf = sstr->buf;
2742	} else if (ioc == ENCIOC_GETENCNAME) {
2743		if (ses_cache->ses_nsubencs < 1)
2744			return (ENODEV);
2745		enc_desc = ses_cache->subencs[0];
2746		cam_strvis(vendor, enc_desc->vendor_id,
2747		    sizeof(enc_desc->vendor_id), sizeof(vendor));
2748		cam_strvis(product, enc_desc->product_id,
2749		    sizeof(enc_desc->product_id), sizeof(product));
2750		cam_strvis(rev, enc_desc->product_rev,
2751		    sizeof(enc_desc->product_rev), sizeof(rev));
2752		rsize = snprintf(str, sizeof(str), "%s %s %s",
2753		    vendor, product, rev) + 1;
2754		if (rsize > sizeof(str))
2755			rsize = sizeof(str);
2756		copyout(&rsize, &sstr->bufsiz, sizeof(rsize));
2757		size = rsize;
2758		if (size > sstr->bufsiz)
2759			size = sstr->bufsiz;
2760		copyout(str, sstr->buf, size);
2761		return (size == rsize ? 0 : ENOMEM);
2762	} else if (ioc == ENCIOC_GETENCID) {
2763		if (ses_cache->ses_nsubencs < 1)
2764			return (ENODEV);
2765		enc_desc = ses_cache->subencs[0];
2766		rsize = snprintf(str, sizeof(str), "%16jx",
2767		    scsi_8btou64(enc_desc->logical_id)) + 1;
2768		if (rsize > sizeof(str))
2769			rsize = sizeof(str);
2770		copyout(&rsize, &sstr->bufsiz, sizeof(rsize));
2771		size = rsize;
2772		if (size > sstr->bufsiz)
2773			size = sstr->bufsiz;
2774		copyout(str, sstr->buf, size);
2775		return (size == rsize ? 0 : ENOMEM);
2776	} else
2777		return EINVAL;
2778
2779	ret = enc_runcmd(enc, cdb, 6, buf, &amt);
2780	if (ioc == ENCIOC_SETSTRING)
2781		ENC_FREE(buf);
2782	return ret;
2783}
2784
2785/**
2786 * \invariant Called with cam_periph mutex held.
2787 */
2788static void
2789ses_poll_status(enc_softc_t *enc)
2790{
2791	ses_softc_t *ses;
2792
2793	ses = enc->enc_private;
2794	enc_update_request(enc, SES_UPDATE_GETSTATUS);
2795	if (ses->ses_flags & SES_FLAG_ADDLSTATUS)
2796		enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS);
2797}
2798
2799/**
2800 * \brief Notification received when CAM detects a new device in the
2801 *        SCSI domain in which this SEP resides.
2802 *
2803 * \param enc	SES enclosure instance.
2804 */
2805static void
2806ses_device_found(enc_softc_t *enc)
2807{
2808	ses_poll_status(enc);
2809	enc_update_request(enc, SES_PUBLISH_PHYSPATHS);
2810}
2811
2812static struct enc_vec ses_enc_vec =
2813{
2814	.softc_invalidate	= ses_softc_invalidate,
2815	.softc_cleanup		= ses_softc_cleanup,
2816	.init_enc		= ses_init_enc,
2817	.get_enc_status		= ses_get_enc_status,
2818	.set_enc_status		= ses_set_enc_status,
2819	.get_elm_status		= ses_get_elm_status,
2820	.set_elm_status		= ses_set_elm_status,
2821	.get_elm_desc		= ses_get_elm_desc,
2822	.get_elm_devnames	= ses_get_elm_devnames,
2823	.handle_string		= ses_handle_string,
2824	.device_found		= ses_device_found,
2825	.poll_status		= ses_poll_status
2826};
2827
2828/**
2829 * \brief Initialize a new SES instance.
2830 *
2831 * \param enc		SES softc structure to set up the instance in.
2832 * \param doinit	Do the initialization (see main driver).
2833 *
2834 * \return		0 on success, errno otherwise.
2835 */
2836int
2837ses_softc_init(enc_softc_t *enc)
2838{
2839	ses_softc_t *ses_softc;
2840
2841	CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE,
2842	    ("entering enc_softc_init(%p)\n", enc));
2843
2844	enc->enc_vec = ses_enc_vec;
2845	enc->enc_fsm_states = enc_fsm_states;
2846
2847	if (enc->enc_private == NULL)
2848		enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t));
2849	if (enc->enc_cache.private == NULL)
2850		enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t));
2851	if (enc->enc_daemon_cache.private == NULL)
2852		enc->enc_daemon_cache.private =
2853		     ENC_MALLOCZ(sizeof(ses_cache_t));
2854
2855	if (enc->enc_private == NULL
2856	 || enc->enc_cache.private == NULL
2857	 || enc->enc_daemon_cache.private == NULL) {
2858		ENC_FREE_AND_NULL(enc->enc_private);
2859		ENC_FREE_AND_NULL(enc->enc_cache.private);
2860		ENC_FREE_AND_NULL(enc->enc_daemon_cache.private);
2861		return (ENOMEM);
2862	}
2863
2864	ses_softc = enc->enc_private;
2865	TAILQ_INIT(&ses_softc->ses_requests);
2866	TAILQ_INIT(&ses_softc->ses_pending_requests);
2867
2868	enc_update_request(enc, SES_UPDATE_PAGES);
2869
2870	// XXX: Move this to the FSM so it doesn't hang init
2871	if (0) (void) ses_set_timed_completion(enc, 1);
2872
2873	return (0);
2874}
2875
2876