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