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