scsi_enc_ses.c revision 238739
1226586Sdim/*- 2226586Sdim * Copyright (c) 2000 Matthew Jacob 3226586Sdim * Copyright (c) 2010 Spectra Logic Corporation 4226586Sdim * All rights reserved. 5226586Sdim * 6226586Sdim * Redistribution and use in source and binary forms, with or without 7226586Sdim * modification, are permitted provided that the following conditions 8226586Sdim * are met: 9226586Sdim * 1. Redistributions of source code must retain the above copyright 10226586Sdim * notice, this list of conditions, and the following disclaimer, 11226586Sdim * without modification, immediately at the beginning of the file. 12226586Sdim * 2. The name of the author may not be used to endorse or promote products 13226586Sdim * derived from this software without specific prior written permission. 14239462Sdim * 15226586Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16239462Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17226586Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18239462Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19239462Sdim * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20239462Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21243830Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22239462Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23239462Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24226586Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25239462Sdim * SUCH DAMAGE. 26226586Sdim */ 27239462Sdim 28234353Sdim/** 29226586Sdim * \file scsi_enc_ses.c 30226586Sdim * 31226586Sdim * Structures and routines specific && private to SES only 32226586Sdim */ 33226586Sdim 34226586Sdim#include <sys/cdefs.h> 35226586Sdim__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_enc_ses.c 238739 2012-07-24 13:08:43Z mav $"); 36226586Sdim 37226586Sdim#include <sys/param.h> 38226586Sdim 39226586Sdim#include <sys/errno.h> 40226586Sdim#include <sys/kernel.h> 41226586Sdim#include <sys/lock.h> 42226586Sdim#include <sys/malloc.h> 43226586Sdim#include <sys/mutex.h> 44226586Sdim#include <sys/queue.h> 45226586Sdim#include <sys/sbuf.h> 46226586Sdim#include <sys/sx.h> 47226586Sdim#include <sys/systm.h> 48226586Sdim#include <sys/types.h> 49226586Sdim 50226586Sdim#include <cam/cam.h> 51226586Sdim#include <cam/cam_ccb.h> 52226586Sdim#include <cam/cam_xpt_periph.h> 53226586Sdim#include <cam/cam_periph.h> 54226586Sdim 55226586Sdim#include <cam/scsi/scsi_message.h> 56226586Sdim#include <cam/scsi/scsi_enc.h> 57226586Sdim#include <cam/scsi/scsi_enc_internal.h> 58226586Sdim 59226586Sdim#include <opt_enc.h> 60226586Sdim 61226586Sdim/* SES Native Type Device Support */ 62226586Sdim 63226586Sdim/* SES Diagnostic Page Codes */ 64226586Sdimtypedef enum { 65226586Sdim SesSupportedPages = 0x0, 66226586Sdim SesConfigPage = 0x1, 67226586Sdim SesControlPage = 0x2, 68226586Sdim SesStatusPage = SesControlPage, 69226586Sdim SesHelpTxt = 0x3, 70226586Sdim SesStringOut = 0x4, 71226586Sdim SesStringIn = SesStringOut, 72226586Sdim SesThresholdOut = 0x5, 73226586Sdim SesThresholdIn = SesThresholdOut, 74226586Sdim SesArrayControl = 0x6, /* Obsolete in SES v2 */ 75226586Sdim SesArrayStatus = SesArrayControl, 76226586Sdim SesElementDescriptor = 0x7, 77226586Sdim SesShortStatus = 0x8, 78226586Sdim SesEnclosureBusy = 0x9, 79243830Sdim SesAddlElementStatus = 0xa 80226586Sdim} SesDiagPageCodes; 81226586Sdim 82226586Sdimtypedef struct ses_type { 83226586Sdim const struct ses_elm_type_desc *hdr; 84226586Sdim const char *text; 85239462Sdim} ses_type_t; 86226586Sdim 87226586Sdimtypedef struct ses_comstat { 88226586Sdim uint8_t comstatus; 89226586Sdim uint8_t comstat[3]; 90226586Sdim} ses_comstat_t; 91226586Sdim 92226586Sdimtypedef union ses_addl_data { 93226586Sdim struct ses_elm_sas_device_phy *sasdev_phys; 94226586Sdim struct ses_elm_sas_expander_phy *sasexp_phys; 95226586Sdim struct ses_elm_sas_port_phy *sasport_phys; 96226586Sdim struct ses_fcobj_port *fc_ports; 97226586Sdim} ses_add_data_t; 98226586Sdim 99226586Sdimtypedef struct ses_addl_status { 100226586Sdim struct ses_elm_addlstatus_base_hdr *hdr; 101226586Sdim union { 102226586Sdim union ses_fcobj_hdr *fc; 103226586Sdim union ses_elm_sas_hdr *sas; 104226586Sdim } proto_hdr; 105226586Sdim union ses_addl_data proto_data; /* array sizes stored in header */ 106226586Sdim} ses_add_status_t; 107226586Sdim 108226586Sdimtypedef struct ses_element { 109226586Sdim uint8_t eip; /* eip bit is set */ 110226586Sdim uint16_t descr_len; /* length of the descriptor */ 111226586Sdim char *descr; /* descriptor for this object */ 112226586Sdim struct ses_addl_status addl; /* additional status info */ 113226586Sdim} ses_element_t; 114226586Sdim 115226586Sdimtypedef struct ses_control_request { 116226586Sdim int elm_idx; 117226586Sdim ses_comstat_t elm_stat; 118226586Sdim int result; 119226586Sdim TAILQ_ENTRY(ses_control_request) links; 120226586Sdim} ses_control_request_t; 121226586SdimTAILQ_HEAD(ses_control_reqlist, ses_control_request); 122226586Sdimtypedef struct ses_control_reqlist ses_control_reqlist_t; 123226586Sdimenum { 124226586Sdim SES_SETSTATUS_ENC_IDX = -1 125234353Sdim}; 126234353Sdim 127234353Sdimstatic void 128234353Sdimses_terminate_control_requests(ses_control_reqlist_t *reqlist, int result) 129234353Sdim{ 130234353Sdim ses_control_request_t *req; 131226586Sdim 132226586Sdim while ((req = TAILQ_FIRST(reqlist)) != NULL) { 133234353Sdim TAILQ_REMOVE(reqlist, req, links); 134234353Sdim req->result = result; 135234353Sdim wakeup(req); 136234353Sdim } 137234353Sdim} 138234353Sdim 139234353Sdimenum ses_iter_index_values { 140243830Sdim /** 141234353Sdim * \brief Value of an initialized but invalid index 142239462Sdim * in a ses_iterator object. 143239462Sdim * 144234353Sdim * This value is used for the individual_element_index of 145234353Sdim * overal status elements and for all index types when 146234353Sdim * an iterator is first initialized. 147234353Sdim */ 148234353Sdim ITERATOR_INDEX_INVALID = -1, 149234353Sdim 150234353Sdim /** 151234353Sdim * \brief Value of an index in a ses_iterator object 152234353Sdim * when the iterator has traversed past the last 153234353Sdim * valid element.. 154234353Sdim */ 155234353Sdim ITERATOR_INDEX_END = INT_MAX 156234353Sdim}; 157234353Sdim 158234353Sdim/** 159234353Sdim * \brief Structure encapsulating all data necessary to traverse the 160234353Sdim * elements of a SES configuration. 161234353Sdim * 162234353Sdim * The ses_iterator object simplifies the task of iterating through all 163234353Sdim * elements detected via the SES configuration page by tracking the numerous 164234353Sdim * element indexes that, instead of memoizing in the softc, we calculate 165226586Sdim * on the fly during the traversal of the element objects. The various 166226586Sdim * indexes are necessary due to the varying needs of matching objects in 167239462Sdim * the different SES pages. Some pages (e.g. Status/Control) contain all 168239462Sdim * elements, while others (e.g. Additional Element Status) only contain 169239462Sdim * individual elements (no overal status elements) of particular types. 170239462Sdim * 171239462Sdim * To use an iterator, initialize it with ses_iter_init(), and then 172239462Sdim * use ses_iter_next() to traverse the elements (including the first) in 173239462Sdim * the configuration. Once an iterator is initiailized with ses_iter_init(), 174239462Sdim * you may also seek to any particular element by either it's global or 175239462Sdim * individual element index via the ses_iter_seek_to() function. You may 176239462Sdim * also return an iterator to the position just before the first element 177239462Sdim * (i.e. the same state as after an ses_iter_init()), with ses_iter_reset(). 178239462Sdim */ 179239462Sdimstruct ses_iterator { 180239462Sdim /** 181239462Sdim * \brief Backlink to the overal software configuration structure. 182239462Sdim * 183239462Sdim * This is included for convenience so the iteration functions 184239462Sdim * need only take a single, struct ses_iterator *, argument. 185239462Sdim */ 186239462Sdim enc_softc_t *enc; 187239462Sdim 188239462Sdim enc_cache_t *cache; 189239462Sdim 190239462Sdim /** 191239462Sdim * \brief Index of the type of the current element within the 192239462Sdim * ses_cache's ses_types array. 193239462Sdim */ 194239462Sdim int type_index; 195239462Sdim 196239462Sdim /** 197239462Sdim * \brief The position (0 based) of this element relative to all other 198239462Sdim * elements of this type. 199239462Sdim * 200239462Sdim * This index resets to zero every time the iterator transitions 201239462Sdim * to elements of a new type in the configuration. 202239462Sdim */ 203239462Sdim int type_element_index; 204239462Sdim 205239462Sdim /** 206239462Sdim * \brief The position (0 based) of this element relative to all 207239462Sdim * other individual status elements in the configuration. 208239462Sdim * 209239462Sdim * This index ranges from 0 through the number of individual 210239462Sdim * elements in the configuration. When the iterator returns 211239462Sdim * an overall status element, individual_element_index is 212239462Sdim * set to ITERATOR_INDEX_INVALID, to indicate that it does 213239462Sdim * not apply to the current element. 214239462Sdim */ 215239462Sdim int individual_element_index; 216239462Sdim 217239462Sdim /** 218239462Sdim * \brief The position (0 based) of this element relative to 219239462Sdim * all elements in the configration. 220239462Sdim * 221239462Sdim * This index is appropriate for indexing into enc->ses_elm_map. 222239462Sdim */ 223239462Sdim int global_element_index; 224239462Sdim 225239462Sdim /** 226239462Sdim * \brief The last valid individual element index of this 227239462Sdim * iterator. 228239462Sdim * 229239462Sdim * When an iterator traverses an overal status element, the 230239462Sdim * individual element index is reset to ITERATOR_INDEX_INVALID 231239462Sdim * to prevent unintential use of the individual_element_index 232239462Sdim * field. The saved_individual_element_index allows the iterator 233239462Sdim * to restore it's position in the individual elements upon 234239462Sdim * reaching the next individual element. 235239462Sdim */ 236239462Sdim int saved_individual_element_index; 237239462Sdim}; 238239462Sdim 239239462Sdimtypedef enum { 240239462Sdim SES_UPDATE_NONE, 241239462Sdim SES_UPDATE_PAGES, 242239462Sdim SES_UPDATE_GETCONFIG, 243239462Sdim SES_UPDATE_GETSTATUS, 244239462Sdim SES_UPDATE_GETELMDESCS, 245239462Sdim SES_UPDATE_GETELMADDLSTATUS, 246239462Sdim SES_PROCESS_CONTROL_REQS, 247239462Sdim SES_PUBLISH_PHYSPATHS, 248239462Sdim SES_PUBLISH_CACHE, 249239462Sdim SES_NUM_UPDATE_STATES 250239462Sdim} ses_update_action; 251239462Sdim 252239462Sdimstatic enc_softc_cleanup_t ses_softc_cleanup; 253239462Sdim 254239462Sdim#define SCSZ 0x8000 255239462Sdim 256239462Sdimstatic fsm_fill_handler_t ses_fill_rcv_diag_io; 257239462Sdimstatic fsm_fill_handler_t ses_fill_control_request; 258239462Sdimstatic fsm_done_handler_t ses_process_pages; 259239462Sdimstatic fsm_done_handler_t ses_process_config; 260239462Sdimstatic fsm_done_handler_t ses_process_status; 261239462Sdimstatic fsm_done_handler_t ses_process_elm_descs; 262239462Sdimstatic fsm_done_handler_t ses_process_elm_addlstatus; 263239462Sdimstatic fsm_done_handler_t ses_process_control_request; 264239462Sdimstatic fsm_done_handler_t ses_publish_physpaths; 265239462Sdimstatic fsm_done_handler_t ses_publish_cache; 266239462Sdim 267239462Sdimstatic struct enc_fsm_state enc_fsm_states[SES_NUM_UPDATE_STATES] = 268239462Sdim{ 269239462Sdim { "SES_UPDATE_NONE", 0, 0, 0, NULL, NULL, NULL }, 270239462Sdim { 271239462Sdim "SES_UPDATE_PAGES", 272239462Sdim SesSupportedPages, 273239462Sdim SCSZ, 274239462Sdim 60 * 1000, 275239462Sdim ses_fill_rcv_diag_io, 276239462Sdim ses_process_pages, 277239462Sdim enc_error 278239462Sdim }, 279239462Sdim { 280239462Sdim "SES_UPDATE_GETCONFIG", 281239462Sdim SesConfigPage, 282239462Sdim SCSZ, 283239462Sdim 60 * 1000, 284243830Sdim ses_fill_rcv_diag_io, 285239462Sdim ses_process_config, 286239462Sdim enc_error 287239462Sdim }, 288239462Sdim { 289239462Sdim "SES_UPDATE_GETSTATUS", 290239462Sdim SesStatusPage, 291239462Sdim SCSZ, 292239462Sdim 60 * 1000, 293239462Sdim ses_fill_rcv_diag_io, 294239462Sdim ses_process_status, 295239462Sdim enc_error 296239462Sdim }, 297239462Sdim { 298239462Sdim "SES_UPDATE_GETELMDESCS", 299239462Sdim SesElementDescriptor, 300239462Sdim SCSZ, 301239462Sdim 60 * 1000, 302239462Sdim ses_fill_rcv_diag_io, 303243830Sdim ses_process_elm_descs, 304239462Sdim enc_error 305239462Sdim }, 306239462Sdim { 307239462Sdim "SES_UPDATE_GETELMADDLSTATUS", 308239462Sdim SesAddlElementStatus, 309239462Sdim SCSZ, 310239462Sdim 60 * 1000, 311239462Sdim ses_fill_rcv_diag_io, 312239462Sdim ses_process_elm_addlstatus, 313239462Sdim enc_error 314239462Sdim }, 315239462Sdim { 316239462Sdim "SES_PROCESS_CONTROL_REQS", 317239462Sdim SesControlPage, 318239462Sdim SCSZ, 319239462Sdim 60 * 1000, 320239462Sdim ses_fill_control_request, 321239462Sdim ses_process_control_request, 322239462Sdim enc_error 323239462Sdim }, 324239462Sdim { 325239462Sdim "SES_PUBLISH_PHYSPATHS", 326239462Sdim 0, 327239462Sdim 0, 328239462Sdim 0, 329239462Sdim NULL, 330239462Sdim ses_publish_physpaths, 331239462Sdim NULL 332239462Sdim }, 333239462Sdim { 334239462Sdim "SES_PUBLISH_CACHE", 335239462Sdim 0, 336239462Sdim 0, 337239462Sdim 0, 338239462Sdim NULL, 339239462Sdim ses_publish_cache, 340239462Sdim NULL 341239462Sdim } 342239462Sdim}; 343239462Sdim 344239462Sdimtypedef struct ses_cache { 345239462Sdim /* Source for all the configuration data pointers */ 346239462Sdim const struct ses_cfg_page *cfg_page; 347239462Sdim 348239462Sdim /* References into the config page. */ 349226586Sdim const struct ses_enc_desc * const *subencs; 350226586Sdim uint8_t ses_ntypes; 351226586Sdim const ses_type_t *ses_types; 352239462Sdim 353239462Sdim /* Source for all the status pointers */ 354239462Sdim const struct ses_status_page *status_page; 355239462Sdim 356239462Sdim /* Source for all the object descriptor pointers */ 357239462Sdim const struct ses_elem_descr_page *elm_descs_page; 358239462Sdim 359239462Sdim /* Source for all the additional object status pointers */ 360239462Sdim const struct ses_addl_elem_status_page *elm_addlstatus_page; 361239462Sdim 362226586Sdim} ses_cache_t; 363226586Sdim 364234353Sdimtypedef struct ses_softc { 365226586Sdim uint32_t ses_flags; 366226586Sdim#define SES_FLAG_TIMEDCOMP 0x01 367226586Sdim#define SES_FLAG_ADDLSTATUS 0x02 368226586Sdim 369226586Sdim ses_control_reqlist_t ses_requests; 370226586Sdim ses_control_reqlist_t ses_pending_requests; 371226586Sdim} ses_softc_t; 372226586Sdim 373226586Sdim/** 374234353Sdim * \brief Reset a SES iterator to just before the first element 375234353Sdim * in the configuration. 376234353Sdim * 377234353Sdim * \param iter The iterator object to reset. 378234353Sdim * 379234353Sdim * The indexes within a reset iterator are invalid and will only 380234353Sdim * become valid upon completion of a ses_iter_seek_to() or a 381226586Sdim * ses_iter_next(). 382226586Sdim */ 383226586Sdimstatic void 384239462Sdimses_iter_reset(struct ses_iterator *iter) 385239462Sdim{ 386239462Sdim /* 387239462Sdim * Set our indexes to just before the first valid element 388239462Sdim * of the first type (ITERATOR_INDEX_INVALID == -1). This 389226586Sdim * simplifies the implementation of ses_iter_next(). 390226586Sdim */ 391239462Sdim iter->type_index = 0; 392239462Sdim iter->type_element_index = ITERATOR_INDEX_INVALID; 393239462Sdim iter->global_element_index = ITERATOR_INDEX_INVALID; 394239462Sdim iter->individual_element_index = ITERATOR_INDEX_INVALID; 395243830Sdim iter->saved_individual_element_index = ITERATOR_INDEX_INVALID; 396239462Sdim} 397239462Sdim 398243830Sdim/** 399243830Sdim * \brief Initialize the storage of a SES iterator and reset it to 400239462Sdim * the position just before the first element of the 401239462Sdim * configuration. 402239462Sdim * 403226586Sdim * \param enc The SES softc for the SES instance whose configuration 404226586Sdim * will be enumerated by this iterator. 405226586Sdim * \param iter The iterator object to initialize. 406226586Sdim */ 407226586Sdimstatic void 408226586Sdimses_iter_init(enc_softc_t *enc, enc_cache_t *cache, struct ses_iterator *iter) 409226586Sdim{ 410226586Sdim iter->enc = enc; 411226586Sdim iter->cache = cache; 412226586Sdim ses_iter_reset(iter); 413226586Sdim} 414226586Sdim 415234353Sdim/** 416234353Sdim * \brief Traverse the provided SES iterator to the next element 417243830Sdim * within the configuraiton. 418234353Sdim * 419234353Sdim * \param iter The iterator to move. 420234353Sdim * 421234353Sdim * \return If a valid next element exists, a pointer to it's enc_element_t. 422239462Sdim * Otherwise NULL. 423239462Sdim */ 424239462Sdimstatic enc_element_t * 425239462Sdimses_iter_next(struct ses_iterator *iter) 426239462Sdim{ 427226586Sdim ses_cache_t *ses_cache; 428234353Sdim const ses_type_t *element_type; 429226586Sdim 430226586Sdim ses_cache = iter->cache->private; 431226586Sdim 432226586Sdim /* 433226586Sdim * Note: Treat nelms as signed, so we will hit this case 434226586Sdim * and immediately terminate the iteration if the 435226586Sdim * configuration has 0 objects. 436226586Sdim */ 437226586Sdim if (iter->global_element_index >= (int)iter->cache->nelms - 1) { 438226586Sdim 439226586Sdim /* Elements exhausted. */ 440226586Sdim iter->type_index = ITERATOR_INDEX_END; 441226586Sdim iter->type_element_index = ITERATOR_INDEX_END; 442226586Sdim iter->global_element_index = ITERATOR_INDEX_END; 443226586Sdim iter->individual_element_index = ITERATOR_INDEX_END; 444226586Sdim return (NULL); 445226586Sdim } 446226586Sdim 447226586Sdim KASSERT((iter->type_index < ses_cache->ses_ntypes), 448226586Sdim ("Corrupted element iterator. %d not less than %d", 449226586Sdim iter->type_index, ses_cache->ses_ntypes)); 450226586Sdim 451226586Sdim element_type = &ses_cache->ses_types[iter->type_index]; 452226586Sdim iter->global_element_index++; 453226586Sdim iter->type_element_index++; 454226586Sdim 455226586Sdim /* 456226586Sdim * There is an object for overal type status in addition 457226586Sdim * to one for each allowed element, but only if the element 458226586Sdim * count is non-zero. 459226586Sdim */ 460226586Sdim if (iter->type_element_index > element_type->hdr->etype_maxelt) { 461226586Sdim 462226586Sdim /* 463226586Sdim * We've exhausted the elements of this type. 464239462Sdim * This next element belongs to the next type. 465226586Sdim */ 466226586Sdim iter->type_index++; 467226586Sdim iter->type_element_index = 0; 468226586Sdim iter->saved_individual_element_index 469226586Sdim = iter->individual_element_index; 470226586Sdim iter->individual_element_index = ITERATOR_INDEX_INVALID; 471226586Sdim } 472226586Sdim 473234353Sdim if (iter->type_element_index > 0) { 474226586Sdim if (iter->type_element_index == 1) { 475226586Sdim iter->individual_element_index 476226586Sdim = iter->saved_individual_element_index; 477226586Sdim } 478239462Sdim iter->individual_element_index++; 479239462Sdim } 480239462Sdim 481226586Sdim return (&iter->cache->elm_map[iter->global_element_index]); 482226586Sdim} 483239462Sdim 484226586Sdim/** 485226586Sdim * Element index types tracked by a SES iterator. 486226586Sdim */ 487226586Sdimtypedef enum { 488226586Sdim /** 489234353Sdim * Index relative to all elements (overall and individual) 490234353Sdim * in the system. 491234353Sdim */ 492239462Sdim SES_ELEM_INDEX_GLOBAL, 493239462Sdim 494239462Sdim /** 495239462Sdim * \brief Index relative to all individual elements in the system. 496239462Sdim * 497239462Sdim * This index counts only individual elements, skipping overall 498239462Sdim * status elements. This is the index space of the additional 499239462Sdim * element status page (page 0xa). 500239462Sdim */ 501226586Sdim SES_ELEM_INDEX_INDIVIDUAL 502226586Sdim} ses_elem_index_type_t; 503226586Sdim 504226586Sdim/** 505226586Sdim * \brief Move the provided iterator forwards or backwards to the object 506226586Sdim * having the give index. 507226586Sdim * 508239462Sdim * \param iter The iterator on which to perform the seek. 509239462Sdim * \param element_index The index of the element to find. 510226586Sdim * \param index_type The type (global or individual) of element_index. 511239462Sdim * 512226586Sdim * \return If the element is found, a pointer to it's enc_element_t. 513226586Sdim * Otherwise NULL. 514226586Sdim */ 515239462Sdimstatic enc_element_t * 516239462Sdimses_iter_seek_to(struct ses_iterator *iter, int element_index, 517239462Sdim ses_elem_index_type_t index_type) 518239462Sdim{ 519239462Sdim enc_element_t *element; 520226586Sdim int *cur_index; 521226586Sdim 522226586Sdim if (index_type == SES_ELEM_INDEX_GLOBAL) 523226586Sdim cur_index = &iter->global_element_index; 524239462Sdim else 525226586Sdim cur_index = &iter->individual_element_index; 526226586Sdim 527226586Sdim if (*cur_index == element_index) { 528226586Sdim /* Already there. */ 529226586Sdim return (&iter->cache->elm_map[iter->global_element_index]); 530226586Sdim } 531226586Sdim 532239462Sdim ses_iter_reset(iter); 533239462Sdim while ((element = ses_iter_next(iter)) != NULL 534239462Sdim && *cur_index != element_index) 535239462Sdim ; 536239462Sdim 537239462Sdim if (*cur_index != element_index) 538239462Sdim return (NULL); 539239462Sdim 540239462Sdim return (element); 541239462Sdim} 542239462Sdim 543239462Sdim#if 0 544226586Sdimstatic int ses_encode(enc_softc_t *, uint8_t *, int, int, 545226586Sdim struct ses_comstat *); 546226586Sdim#endif 547226586Sdimstatic int ses_set_timed_completion(enc_softc_t *, uint8_t); 548226586Sdim#if 0 549226586Sdimstatic int ses_putstatus(enc_softc_t *, int, struct ses_comstat *); 550226586Sdim#endif 551226586Sdim 552226586Sdimstatic void ses_print_addl_data(enc_softc_t *, enc_element_t *); 553226586Sdim 554226586Sdim/*=========================== SES cleanup routines ===========================*/ 555226586Sdim 556226586Sdimstatic void 557234353Sdimses_cache_free_elm_addlstatus(enc_softc_t *enc, enc_cache_t *cache) 558234353Sdim{ 559234353Sdim ses_cache_t *ses_cache; 560243830Sdim ses_cache_t *other_ses_cache; 561243830Sdim enc_element_t *cur_elm; 562226586Sdim enc_element_t *last_elm; 563226586Sdim 564239462Sdim ENC_DLOG(enc, "%s: enter\n", __func__); 565239462Sdim ses_cache = cache->private; 566239462Sdim if (ses_cache->elm_addlstatus_page == NULL) 567239462Sdim return; 568226586Sdim 569239462Sdim for (cur_elm = cache->elm_map, 570239462Sdim last_elm = &cache->elm_map[cache->nelms - 1]; 571239462Sdim cur_elm <= last_elm; cur_elm++) { 572226586Sdim ses_element_t *elmpriv; 573226586Sdim 574226586Sdim elmpriv = cur_elm->elm_private; 575226586Sdim 576226586Sdim /* Clear references to the additional status page. */ 577239462Sdim bzero(&elmpriv->addl, sizeof(elmpriv->addl)); 578239462Sdim } 579239462Sdim 580226586Sdim other_ses_cache = enc_other_cache(enc, cache)->private; 581226586Sdim if (other_ses_cache->elm_addlstatus_page 582226586Sdim != ses_cache->elm_addlstatus_page) 583226586Sdim ENC_FREE(ses_cache->elm_addlstatus_page); 584226586Sdim ses_cache->elm_addlstatus_page = NULL; 585226586Sdim} 586226586Sdim 587226586Sdimstatic void 588226586Sdimses_cache_free_elm_descs(enc_softc_t *enc, enc_cache_t *cache) 589226586Sdim{ 590226586Sdim ses_cache_t *ses_cache; 591226586Sdim ses_cache_t *other_ses_cache; 592226586Sdim enc_element_t *cur_elm; 593226586Sdim enc_element_t *last_elm; 594226586Sdim 595239462Sdim ENC_DLOG(enc, "%s: enter\n", __func__); 596226586Sdim ses_cache = cache->private; 597226586Sdim if (ses_cache->elm_descs_page == NULL) 598226586Sdim return; 599226586Sdim 600226586Sdim for (cur_elm = cache->elm_map, 601226586Sdim last_elm = &cache->elm_map[cache->nelms - 1]; 602226586Sdim cur_elm <= last_elm; cur_elm++) { 603226586Sdim ses_element_t *elmpriv; 604226586Sdim 605226586Sdim elmpriv = cur_elm->elm_private; 606226586Sdim elmpriv->descr_len = 0; 607226586Sdim elmpriv->descr = NULL; 608226586Sdim } 609226586Sdim 610226586Sdim other_ses_cache = enc_other_cache(enc, cache)->private; 611226586Sdim if (other_ses_cache->elm_descs_page 612226586Sdim != ses_cache->elm_descs_page) 613226586Sdim ENC_FREE(ses_cache->elm_descs_page); 614226586Sdim ses_cache->elm_descs_page = NULL; 615226586Sdim} 616226586Sdim 617226586Sdimstatic void 618226586Sdimses_cache_free_status(enc_softc_t *enc, enc_cache_t *cache) 619226586Sdim{ 620226586Sdim ses_cache_t *ses_cache; 621226586Sdim ses_cache_t *other_ses_cache; 622226586Sdim 623239462Sdim ENC_DLOG(enc, "%s: enter\n", __func__); 624239462Sdim ses_cache = cache->private; 625226586Sdim if (ses_cache->status_page == NULL) 626226586Sdim return; 627226586Sdim 628226586Sdim other_ses_cache = enc_other_cache(enc, cache)->private; 629226586Sdim if (other_ses_cache->status_page != ses_cache->status_page) 630226586Sdim ENC_FREE(ses_cache->status_page); 631226586Sdim ses_cache->status_page = NULL; 632226586Sdim} 633226586Sdim 634226586Sdimstatic void 635226586Sdimses_cache_free_elm_map(enc_softc_t *enc, enc_cache_t *cache) 636226586Sdim{ 637226586Sdim enc_element_t *cur_elm; 638226586Sdim enc_element_t *last_elm; 639226586Sdim 640226586Sdim ENC_DLOG(enc, "%s: enter\n", __func__); 641226586Sdim if (cache->elm_map == NULL) 642226586Sdim return; 643239462Sdim 644 ses_cache_free_elm_descs(enc, cache); 645 ses_cache_free_elm_addlstatus(enc, cache); 646 for (cur_elm = cache->elm_map, 647 last_elm = &cache->elm_map[cache->nelms - 1]; 648 cur_elm <= last_elm; cur_elm++) { 649 650 ENC_FREE_AND_NULL(cur_elm->elm_private); 651 } 652 ENC_FREE_AND_NULL(cache->elm_map); 653 cache->nelms = 0; 654 ENC_DLOG(enc, "%s: exit\n", __func__); 655} 656 657static void 658ses_cache_free(enc_softc_t *enc, enc_cache_t *cache) 659{ 660 ses_cache_t *other_ses_cache; 661 ses_cache_t *ses_cache; 662 663 ENC_DLOG(enc, "%s: enter\n", __func__); 664 ses_cache_free_elm_addlstatus(enc, cache); 665 ses_cache_free_status(enc, cache); 666 ses_cache_free_elm_map(enc, cache); 667 668 ses_cache = cache->private; 669 ses_cache->ses_ntypes = 0; 670 671 other_ses_cache = enc_other_cache(enc, cache)->private; 672 if (other_ses_cache->subencs != ses_cache->subencs) 673 ENC_FREE(ses_cache->subencs); 674 ses_cache->subencs = NULL; 675 676 if (other_ses_cache->ses_types != ses_cache->ses_types) 677 ENC_FREE(ses_cache->ses_types); 678 ses_cache->ses_types = NULL; 679 680 if (other_ses_cache->cfg_page != ses_cache->cfg_page) 681 ENC_FREE(ses_cache->cfg_page); 682 ses_cache->cfg_page = NULL; 683 684 ENC_DLOG(enc, "%s: exit\n", __func__); 685} 686 687static void 688ses_cache_clone(enc_softc_t *enc, enc_cache_t *src, enc_cache_t *dst) 689{ 690 ses_cache_t *dst_ses_cache; 691 ses_cache_t *src_ses_cache; 692 enc_element_t *src_elm; 693 enc_element_t *dst_elm; 694 enc_element_t *last_elm; 695 696 ses_cache_free(enc, dst); 697 src_ses_cache = src->private; 698 dst_ses_cache = dst->private; 699 700 /* 701 * The cloned enclosure cache and ses specific cache are 702 * mostly identical to the source. 703 */ 704 *dst = *src; 705 *dst_ses_cache = *src_ses_cache; 706 707 /* 708 * But the ses cache storage is still independent. Restore 709 * the pointer that was clobbered by the structure copy above. 710 */ 711 dst->private = dst_ses_cache; 712 713 /* 714 * The element map is independent even though it starts out 715 * pointing to the same constant page data. 716 */ 717 dst->elm_map = ENC_MALLOCZ(dst->nelms * sizeof(enc_element_t)); 718 memcpy(dst->elm_map, src->elm_map, dst->nelms * sizeof(enc_element_t)); 719 for (dst_elm = dst->elm_map, src_elm = src->elm_map, 720 last_elm = &src->elm_map[src->nelms - 1]; 721 src_elm <= last_elm; src_elm++, dst_elm++) { 722 723 dst_elm->elm_private = ENC_MALLOCZ(sizeof(ses_element_t)); 724 memcpy(dst_elm->elm_private, src_elm->elm_private, 725 sizeof(ses_element_t)); 726 } 727} 728 729/* Structure accessors. These are strongly typed to avoid errors. */ 730 731int 732ses_elm_sas_descr_type(union ses_elm_sas_hdr *obj) 733{ 734 return ((obj)->base_hdr.byte1 >> 6); 735} 736int 737ses_elm_addlstatus_proto(struct ses_elm_addlstatus_base_hdr *hdr) 738{ 739 return ((hdr)->byte0 & 0xf); 740} 741int 742ses_elm_addlstatus_eip(struct ses_elm_addlstatus_base_hdr *hdr) 743{ 744 return ((hdr)->byte0 >> 4) & 0x1; 745} 746int 747ses_elm_addlstatus_invalid(struct ses_elm_addlstatus_base_hdr *hdr) 748{ 749 return ((hdr)->byte0 >> 7); 750} 751int 752ses_elm_sas_type0_not_all_phys(union ses_elm_sas_hdr *hdr) 753{ 754 return ((hdr)->type0_noneip.byte1 & 0x1); 755} 756int 757ses_elm_sas_dev_phy_sata_dev(struct ses_elm_sas_device_phy *phy) 758{ 759 return ((phy)->target_ports & 0x1); 760} 761int 762ses_elm_sas_dev_phy_sata_port(struct ses_elm_sas_device_phy *phy) 763{ 764 return ((phy)->target_ports >> 7); 765} 766int 767ses_elm_sas_dev_phy_dev_type(struct ses_elm_sas_device_phy *phy) 768{ 769 return (((phy)->byte0 >> 4) & 0x7); 770} 771 772/** 773 * \brief Verify that the cached configuration data in our softc 774 * is valid for processing the page data corresponding to 775 * the provided page header. 776 * 777 * \param ses_cache The SES cache to validate. 778 * \param gen_code The 4 byte generation code from a SES diagnostic 779 * page header. 780 * 781 * \return non-zero if true, 0 if false. 782 */ 783static int 784ses_config_cache_valid(ses_cache_t *ses_cache, const uint8_t *gen_code) 785{ 786 uint32_t cache_gc; 787 uint32_t cur_gc; 788 789 if (ses_cache->cfg_page == NULL) 790 return (0); 791 792 cache_gc = scsi_4btoul(ses_cache->cfg_page->hdr.gen_code); 793 cur_gc = scsi_4btoul(gen_code); 794 return (cache_gc == cur_gc); 795} 796 797/** 798 * Function signature for consumers of the ses_devids_iter() interface. 799 */ 800typedef void ses_devid_callback_t(enc_softc_t *, enc_element_t *, 801 struct scsi_vpd_id_descriptor *, void *); 802 803/** 804 * \brief Iterate over and create vpd device id records from the 805 * additional element status data for elm, passing that data 806 * to the provided callback. 807 * 808 * \param enc SES instance containing elm 809 * \param elm Element for which to extract device ID data. 810 * \param callback The callback function to invoke on each generated 811 * device id descriptor for elm. 812 * \param callback_arg Argument passed through to callback on each invocation. 813 */ 814static void 815ses_devids_iter(enc_softc_t *enc, enc_element_t *elm, 816 ses_devid_callback_t *callback, void *callback_arg) 817{ 818 ses_element_t *elmpriv; 819 struct ses_addl_status *addl; 820 u_int i; 821 size_t devid_record_size; 822 823 elmpriv = elm->elm_private; 824 addl = &(elmpriv->addl); 825 826 /* 827 * Don't assume this object has additional status information, or 828 * that it is a SAS device, or that it is a device slot device. 829 */ 830 if (addl->hdr == NULL || addl->proto_hdr.sas == NULL 831 || addl->proto_data.sasdev_phys == NULL) 832 return; 833 834 devid_record_size = SVPD_DEVICE_ID_DESC_HDR_LEN 835 + sizeof(struct scsi_vpd_id_naa_ieee_reg); 836 for (i = 0; i < addl->proto_hdr.sas->base_hdr.num_phys; i++) { 837 uint8_t devid_buf[devid_record_size]; 838 struct scsi_vpd_id_descriptor *devid; 839 uint8_t *phy_addr; 840 841 devid = (struct scsi_vpd_id_descriptor *)devid_buf; 842 phy_addr = addl->proto_data.sasdev_phys[i].phy_addr; 843 devid->proto_codeset = (SCSI_PROTO_SAS << SVPD_ID_PROTO_SHIFT) 844 | SVPD_ID_CODESET_BINARY; 845 devid->id_type = SVPD_ID_PIV 846 | SVPD_ID_ASSOC_PORT 847 | SVPD_ID_TYPE_NAA; 848 devid->reserved = 0; 849 devid->length = sizeof(struct scsi_vpd_id_naa_ieee_reg); 850 memcpy(devid->identifier, phy_addr, devid->length); 851 852 callback(enc, elm, devid, callback_arg); 853 } 854} 855 856/** 857 * Function signature for consumers of the ses_paths_iter() interface. 858 */ 859typedef void ses_path_callback_t(enc_softc_t *, enc_element_t *, 860 struct cam_path *, void *); 861 862/** 863 * Argument package passed through ses_devids_iter() by 864 * ses_paths_iter() to ses_path_iter_devid_callback(). 865 */ 866typedef struct ses_path_iter_args { 867 ses_path_callback_t *callback; 868 void *callback_arg; 869} ses_path_iter_args_t; 870 871/** 872 * ses_devids_iter() callback function used by ses_paths_iter() 873 * to map device ids to peripheral driver instances. 874 * 875 * \param enc SES instance containing elm 876 * \param elm Element on which device ID matching is active. 877 * \param periph A device ID corresponding to elm. 878 * \param arg Argument passed through to callback on each invocation. 879 */ 880static void 881ses_path_iter_devid_callback(enc_softc_t *enc, enc_element_t *elem, 882 struct scsi_vpd_id_descriptor *devid, 883 void *arg) 884{ 885 struct ccb_dev_match cdm; 886 struct dev_match_pattern match_pattern; 887 struct dev_match_result match_result; 888 struct device_match_result *device_match; 889 struct device_match_pattern *device_pattern; 890 ses_path_iter_args_t *args; 891 892 args = (ses_path_iter_args_t *)arg; 893 match_pattern.type = DEV_MATCH_DEVICE; 894 device_pattern = &match_pattern.pattern.device_pattern; 895 device_pattern->flags = DEV_MATCH_DEVID; 896 device_pattern->data.devid_pat.id_len = 897 offsetof(struct scsi_vpd_id_descriptor, identifier) 898 + devid->length; 899 memcpy(device_pattern->data.devid_pat.id, devid, 900 device_pattern->data.devid_pat.id_len); 901 902 memset(&cdm, 0, sizeof(cdm)); 903 if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, CAM_XPT_PATH_ID, 904 CAM_TARGET_WILDCARD, 905 CAM_LUN_WILDCARD) != CAM_REQ_CMP) 906 return; 907 908 cdm.ccb_h.func_code = XPT_DEV_MATCH; 909 cdm.num_patterns = 1; 910 cdm.patterns = &match_pattern; 911 cdm.pattern_buf_len = sizeof(match_pattern); 912 cdm.match_buf_len = sizeof(match_result); 913 cdm.matches = &match_result; 914 915 xpt_action((union ccb *)&cdm); 916 xpt_free_path(cdm.ccb_h.path); 917 918 if ((cdm.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP 919 || (cdm.status != CAM_DEV_MATCH_LAST 920 && cdm.status != CAM_DEV_MATCH_MORE) 921 || cdm.num_matches == 0) 922 return; 923 924 device_match = &match_result.result.device_result; 925 if (xpt_create_path(&cdm.ccb_h.path, /*periph*/NULL, 926 device_match->path_id, 927 device_match->target_id, 928 device_match->target_lun) != CAM_REQ_CMP) 929 return; 930 931 args->callback(enc, elem, cdm.ccb_h.path, args->callback_arg); 932 xpt_free_path(cdm.ccb_h.path); 933} 934 935/** 936 * \brief Iterate over and find the matching periph objects for the 937 * specified element. 938 * 939 * \param enc SES instance containing elm 940 * \param elm Element for which to perform periph object matching. 941 * \param callback The callback function to invoke with each matching 942 * periph object. 943 * \param callback_arg Argument passed through to callback on each invocation. 944 */ 945static void 946ses_paths_iter(enc_softc_t *enc, enc_element_t *elm, 947 ses_path_callback_t *callback, void *callback_arg) 948{ 949 ses_path_iter_args_t args; 950 951 args.callback = callback; 952 args.callback_arg = callback_arg; 953 ses_devids_iter(enc, elm, ses_path_iter_devid_callback, &args); 954} 955 956/** 957 * ses_paths_iter() callback function used by ses_get_elmdevname() 958 * to record periph driver instance strings corresponding to a SES 959 * element. 960 * 961 * \param enc SES instance containing elm 962 * \param elm Element on which periph matching is active. 963 * \param periph A periph instance that matches elm. 964 * \param arg Argument passed through to callback on each invocation. 965 */ 966static void 967ses_elmdevname_callback(enc_softc_t *enc, enc_element_t *elem, 968 struct cam_path *path, void *arg) 969{ 970 struct sbuf *sb; 971 972 sb = (struct sbuf *)arg; 973 cam_periph_list(path, sb); 974} 975 976/** 977 * Argument package passed through ses_paths_iter() to 978 * ses_getcampath_callback. 979 */ 980typedef struct ses_setphyspath_callback_args { 981 struct sbuf *physpath; 982 int num_set; 983} ses_setphyspath_callback_args_t; 984 985/** 986 * \brief ses_paths_iter() callback to set the physical path on the 987 * CAM EDT entries corresponding to a given SES element. 988 * 989 * \param enc SES instance containing elm 990 * \param elm Element on which periph matching is active. 991 * \param periph A periph instance that matches elm. 992 * \param arg Argument passed through to callback on each invocation. 993 */ 994static void 995ses_setphyspath_callback(enc_softc_t *enc, enc_element_t *elm, 996 struct cam_path *path, void *arg) 997{ 998 struct ccb_dev_advinfo cdai; 999 ses_setphyspath_callback_args_t *args; 1000 char *old_physpath; 1001 1002 args = (ses_setphyspath_callback_args_t *)arg; 1003 old_physpath = malloc(MAXPATHLEN, M_SCSIENC, M_WAITOK|M_ZERO); 1004 1005 xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 1006 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1007 cdai.buftype = CDAI_TYPE_PHYS_PATH; 1008 cdai.flags = 0; 1009 cdai.bufsiz = MAXPATHLEN; 1010 cdai.buf = old_physpath; 1011 xpt_action((union ccb *)&cdai); 1012 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1013 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1014 1015 if (strcmp(old_physpath, sbuf_data(args->physpath)) != 0) { 1016 1017 xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 1018 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1019 cdai.buftype = CDAI_TYPE_PHYS_PATH; 1020 cdai.flags |= CDAI_FLAG_STORE; 1021 cdai.bufsiz = sbuf_len(args->physpath); 1022 cdai.buf = sbuf_data(args->physpath); 1023 xpt_action((union ccb *)&cdai); 1024 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1025 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1026 if (cdai.ccb_h.status == CAM_REQ_CMP) 1027 args->num_set++; 1028 } 1029 free(old_physpath, M_SCSIENC); 1030} 1031 1032/** 1033 * \brief Set a device's physical path string in CAM XPT. 1034 * 1035 * \param enc SES instance containing elm 1036 * \param elm Element to publish physical path string for 1037 * \param iter Iterator whose state corresponds to elm 1038 * 1039 * \return 0 on success, errno otherwise. 1040 */ 1041static int 1042ses_set_physpath(enc_softc_t *enc, enc_element_t *elm, 1043 struct ses_iterator *iter) 1044{ 1045 struct ccb_dev_advinfo cdai; 1046 ses_setphyspath_callback_args_t args; 1047 int ret; 1048 struct sbuf sb; 1049 uint8_t *devid, *elmaddr; 1050 ses_element_t *elmpriv; 1051 1052 ret = EIO; 1053 devid = NULL; 1054 1055 /* 1056 * Assemble the components of the physical path starting with 1057 * the device ID of the enclosure itself. 1058 */ 1059 xpt_setup_ccb(&cdai.ccb_h, enc->periph->path, CAM_PRIORITY_NORMAL); 1060 cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 1061 cdai.buftype = CDAI_TYPE_SCSI_DEVID; 1062 cdai.bufsiz = CAM_SCSI_DEVID_MAXLEN; 1063 cdai.buf = devid = ENC_MALLOCZ(cdai.bufsiz); 1064 if (devid == NULL) { 1065 ret = ENOMEM; 1066 goto out; 1067 } 1068 xpt_action((union ccb *)&cdai); 1069 if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 1070 cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 1071 if (cdai.ccb_h.status != CAM_REQ_CMP) 1072 goto out; 1073 1074 elmaddr = scsi_get_devid((struct scsi_vpd_device_id *)cdai.buf, 1075 cdai.provsiz, scsi_devid_is_naa_ieee_reg); 1076 if (elmaddr == NULL) 1077 goto out; 1078 1079 if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) { 1080 ret = ENOMEM; 1081 goto out; 1082 } 1083 /* Next, generate the physical path string */ 1084 sbuf_printf(&sb, "id1,enc@n%jx/type@%x/slot@%x", 1085 scsi_8btou64(elmaddr), iter->type_index, 1086 iter->type_element_index); 1087 /* Append the element descriptor if one exists */ 1088 elmpriv = elm->elm_private; 1089 if (elmpriv->descr != NULL && elmpriv->descr_len > 0) { 1090 sbuf_cat(&sb, "/elmdesc@"); 1091 sbuf_bcat(&sb, elmpriv->descr, elmpriv->descr_len); 1092 } 1093 sbuf_finish(&sb); 1094 1095 /* 1096 * Set this physical path on any CAM devices with a device ID 1097 * descriptor that matches one created from the SES additional 1098 * status data for this element. 1099 */ 1100 args.physpath= &sb; 1101 args.num_set = 0; 1102 ses_paths_iter(enc, elm, ses_setphyspath_callback, &args); 1103 sbuf_delete(&sb); 1104 1105 ret = args.num_set == 0 ? ENOENT : 0; 1106 1107out: 1108 if (devid != NULL) 1109 ENC_FREE(devid); 1110 return (ret); 1111} 1112 1113/** 1114 * \brief Helper to set the CDB fields appropriately. 1115 * 1116 * \param cdb Buffer containing the cdb. 1117 * \param pagenum SES diagnostic page to query for. 1118 * \param dir Direction of query. 1119 */ 1120static void 1121ses_page_cdb(char *cdb, int bufsiz, SesDiagPageCodes pagenum, int dir) 1122{ 1123 1124 /* Ref: SPC-4 r25 Section 6.20 Table 223 */ 1125 if (dir == CAM_DIR_IN) { 1126 cdb[0] = RECEIVE_DIAGNOSTIC; 1127 cdb[1] = 1; /* Set page code valid bit */ 1128 cdb[2] = pagenum; 1129 } else { 1130 cdb[0] = SEND_DIAGNOSTIC; 1131 cdb[1] = 0x10; 1132 cdb[2] = pagenum; 1133 } 1134 cdb[3] = bufsiz >> 8; /* high bits */ 1135 cdb[4] = bufsiz & 0xff; /* low bits */ 1136 cdb[5] = 0; 1137} 1138 1139/** 1140 * \brief Discover whether this instance supports timed completion of a 1141 * RECEIVE DIAGNOSTIC RESULTS command requesting the Enclosure Status 1142 * page, and store the result in the softc, updating if necessary. 1143 * 1144 * \param enc SES instance to query and update. 1145 * \param tc_en Value of timed completion to set (see \return). 1146 * 1147 * \return 1 if timed completion enabled, 0 otherwise. 1148 */ 1149static int 1150ses_set_timed_completion(enc_softc_t *enc, uint8_t tc_en) 1151{ 1152 int err; 1153 union ccb *ccb; 1154 struct cam_periph *periph; 1155 struct ses_mgmt_mode_page *mgmt; 1156 uint8_t *mode_buf; 1157 size_t mode_buf_len; 1158 ses_softc_t *ses; 1159 1160 periph = enc->periph; 1161 ses = enc->enc_private; 1162 ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); 1163 1164 mode_buf_len = sizeof(struct ses_mgmt_mode_page); 1165 mode_buf = ENC_MALLOCZ(mode_buf_len); 1166 if (mode_buf == NULL) 1167 goto out; 1168 1169 scsi_mode_sense(&ccb->csio, /*retries*/4, enc_done, MSG_SIMPLE_Q_TAG, 1170 /*dbd*/FALSE, SMS_PAGE_CTRL_CURRENT, SES_MGMT_MODE_PAGE_CODE, 1171 mode_buf, mode_buf_len, SSD_FULL_SIZE, /*timeout*/60 * 1000); 1172 1173 /* 1174 * Ignore illegal request errors, as they are quite common and we 1175 * will print something out in that case anyway. 1176 */ 1177 err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, 1178 ENC_FLAGS|SF_QUIET_IR, NULL); 1179 if (ccb->ccb_h.status != CAM_REQ_CMP) { 1180 ENC_LOG(enc, "Timed Completion Unsupported\n"); 1181 goto release; 1182 } 1183 1184 /* Skip the mode select if the desired value is already set */ 1185 mgmt = (struct ses_mgmt_mode_page *)mode_buf; 1186 if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) == tc_en) 1187 goto done; 1188 1189 /* Value is not what we wanted, set it */ 1190 if (tc_en) 1191 mgmt->byte5 |= SES_MGMT_TIMED_COMP_EN; 1192 else 1193 mgmt->byte5 &= ~SES_MGMT_TIMED_COMP_EN; 1194 /* SES2r20: a completion time of zero means as long as possible */ 1195 bzero(&mgmt->max_comp_time, sizeof(mgmt->max_comp_time)); 1196 1197 scsi_mode_select(&ccb->csio, 5, enc_done, MSG_SIMPLE_Q_TAG, 1198 /*page_fmt*/FALSE, /*save_pages*/TRUE, mode_buf, mode_buf_len, 1199 SSD_FULL_SIZE, /*timeout*/60 * 1000); 1200 1201 err = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 1202 if (ccb->ccb_h.status != CAM_REQ_CMP) { 1203 ENC_LOG(enc, "Timed Completion Set Failed\n"); 1204 goto release; 1205 } 1206 1207done: 1208 if ((mgmt->byte5 & SES_MGMT_TIMED_COMP_EN) != 0) { 1209 ENC_LOG(enc, "Timed Completion Enabled\n"); 1210 ses->ses_flags |= SES_FLAG_TIMEDCOMP; 1211 } else { 1212 ENC_LOG(enc, "Timed Completion Disabled\n"); 1213 ses->ses_flags &= ~SES_FLAG_TIMEDCOMP; 1214 } 1215release: 1216 ENC_FREE(mode_buf); 1217 xpt_release_ccb(ccb); 1218out: 1219 return (ses->ses_flags & SES_FLAG_TIMEDCOMP); 1220} 1221 1222/** 1223 * \brief Process the list of supported pages and update flags. 1224 * 1225 * \param enc SES device to query. 1226 * \param buf Buffer containing the config page. 1227 * \param xfer_len Length of the config page in the buffer. 1228 * 1229 * \return 0 on success, errno otherwise. 1230 */ 1231static int 1232ses_process_pages(enc_softc_t *enc, struct enc_fsm_state *state, 1233 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1234{ 1235 ses_softc_t *ses; 1236 struct scsi_diag_page *page; 1237 int err, i, length; 1238 1239 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 1240 ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); 1241 ses = enc->enc_private; 1242 err = -1; 1243 1244 if (error != 0) { 1245 err = error; 1246 goto out; 1247 } 1248 if (xfer_len < sizeof(*page)) { 1249 ENC_LOG(enc, "Unable to parse Diag Pages List Header\n"); 1250 err = EIO; 1251 goto out; 1252 } 1253 page = (struct scsi_diag_page *)*bufp; 1254 length = scsi_2btoul(page->length); 1255 if (length + offsetof(struct scsi_diag_page, params) > xfer_len) { 1256 ENC_LOG(enc, "Diag Pages List Too Long\n"); 1257 goto out; 1258 } 1259 ENC_DLOG(enc, "%s: page length %d, xfer_len %d\n", 1260 __func__, length, xfer_len); 1261 1262 err = 0; 1263 for (i = 0; i < length; i++) { 1264 if (page->params[i] == SesAddlElementStatus) { 1265 ses->ses_flags |= SES_FLAG_ADDLSTATUS; 1266 break; 1267 } 1268 } 1269 1270out: 1271 ENC_DLOG(enc, "%s: exiting with err %d\n", __func__, err); 1272 return (err); 1273} 1274 1275/** 1276 * \brief Process the config page and update associated structures. 1277 * 1278 * \param enc SES device to query. 1279 * \param buf Buffer containing the config page. 1280 * \param xfer_len Length of the config page in the buffer. 1281 * 1282 * \return 0 on success, errno otherwise. 1283 */ 1284static int 1285ses_process_config(enc_softc_t *enc, struct enc_fsm_state *state, 1286 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1287{ 1288 struct ses_iterator iter; 1289 ses_softc_t *ses; 1290 enc_cache_t *enc_cache; 1291 ses_cache_t *ses_cache; 1292 uint8_t *buf; 1293 int length; 1294 int err; 1295 int nelm; 1296 int ntype; 1297 struct ses_cfg_page *cfg_page; 1298 struct ses_enc_desc *buf_subenc; 1299 const struct ses_enc_desc **subencs; 1300 const struct ses_enc_desc **cur_subenc; 1301 const struct ses_enc_desc **last_subenc; 1302 ses_type_t *ses_types; 1303 ses_type_t *sestype; 1304 const struct ses_elm_type_desc *cur_buf_type; 1305 const struct ses_elm_type_desc *last_buf_type; 1306 uint8_t *last_valid_byte; 1307 enc_element_t *element; 1308 const char *type_text; 1309 1310 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 1311 ("entering %s(%p, %d)\n", __func__, bufp, xfer_len)); 1312 ses = enc->enc_private; 1313 enc_cache = &enc->enc_daemon_cache; 1314 ses_cache = enc_cache->private; 1315 buf = *bufp; 1316 err = -1;; 1317 1318 if (error != 0) { 1319 err = error; 1320 goto out; 1321 } 1322 if (xfer_len < sizeof(cfg_page->hdr)) { 1323 ENC_LOG(enc, "Unable to parse SES Config Header\n"); 1324 err = EIO; 1325 goto out; 1326 } 1327 1328 cfg_page = (struct ses_cfg_page *)buf; 1329 length = ses_page_length(&cfg_page->hdr); 1330 if (length > xfer_len) { 1331 ENC_LOG(enc, "Enclosure Config Page Too Long\n"); 1332 goto out; 1333 } 1334 last_valid_byte = &buf[length - 1]; 1335 1336 ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", 1337 __func__, length, xfer_len); 1338 1339 err = 0; 1340 if (ses_config_cache_valid(ses_cache, cfg_page->hdr.gen_code)) { 1341 1342 /* Our cache is still valid. Proceed to fetching status. */ 1343 goto out; 1344 } 1345 1346 /* Cache is no longer valid. Free old data to make way for new. */ 1347 ses_cache_free(enc, enc_cache); 1348 ENC_VLOG(enc, "Generation Code 0x%x has %d SubEnclosures\n", 1349 scsi_4btoul(cfg_page->hdr.gen_code), 1350 ses_cfg_page_get_num_subenc(cfg_page)); 1351 1352 /* Take ownership of the buffer. */ 1353 ses_cache->cfg_page = cfg_page; 1354 *bufp = NULL; 1355 1356 /* 1357 * Now waltz through all the subenclosures summing the number of 1358 * types available in each. 1359 */ 1360 subencs = ENC_MALLOCZ(ses_cfg_page_get_num_subenc(cfg_page) 1361 * sizeof(*subencs)); 1362 if (subencs == NULL) { 1363 err = ENOMEM; 1364 goto out; 1365 } 1366 /* 1367 * Sub-enclosure data is const after construction (i.e. when 1368 * accessed via our cache object. 1369 * 1370 * The cast here is not required in C++ but C99 is not so 1371 * sophisticated (see C99 6.5.16.1(1)). 1372 */ 1373 ses_cache->subencs = subencs; 1374 1375 buf_subenc = cfg_page->subencs; 1376 cur_subenc = subencs; 1377 last_subenc = &subencs[ses_cfg_page_get_num_subenc(cfg_page) - 1]; 1378 ntype = 0; 1379 while (cur_subenc <= last_subenc) { 1380 1381 if (!ses_enc_desc_is_complete(buf_subenc, last_valid_byte)) { 1382 ENC_LOG(enc, "Enclosure %d Beyond End of " 1383 "Descriptors\n", cur_subenc - subencs); 1384 err = EIO; 1385 goto out; 1386 } 1387 1388 ENC_VLOG(enc, " SubEnclosure ID %d, %d Types With this ID, " 1389 "Descriptor Length %d, offset %d\n", buf_subenc->subenc_id, 1390 buf_subenc->num_types, buf_subenc->length, 1391 &buf_subenc->byte0 - buf); 1392 ENC_VLOG(enc, "WWN: %jx\n", 1393 (uintmax_t)scsi_8btou64(buf_subenc->logical_id)); 1394 1395 ntype += buf_subenc->num_types; 1396 *cur_subenc = buf_subenc; 1397 cur_subenc++; 1398 buf_subenc = ses_enc_desc_next(buf_subenc); 1399 } 1400 1401 /* Process the type headers. */ 1402 ses_types = ENC_MALLOCZ(ntype * sizeof(*ses_types)); 1403 if (ses_types == NULL) { 1404 err = ENOMEM; 1405 goto out; 1406 } 1407 /* 1408 * Type data is const after construction (i.e. when accessed via 1409 * our cache object. 1410 */ 1411 ses_cache->ses_types = ses_types; 1412 1413 cur_buf_type = (const struct ses_elm_type_desc *) 1414 (&(*last_subenc)->length + (*last_subenc)->length + 1); 1415 last_buf_type = cur_buf_type + ntype - 1; 1416 type_text = (const uint8_t *)(last_buf_type + 1); 1417 nelm = 0; 1418 sestype = ses_types; 1419 while (cur_buf_type <= last_buf_type) { 1420 if (&cur_buf_type->etype_txt_len > last_valid_byte) { 1421 ENC_LOG(enc, "Runt Enclosure Type Header %d\n", 1422 sestype - ses_types); 1423 err = EIO; 1424 goto out; 1425 } 1426 sestype->hdr = cur_buf_type; 1427 sestype->text = type_text; 1428 type_text += cur_buf_type->etype_txt_len; 1429 ENC_LOG(enc, " Type Desc[%d]: Type 0x%x, MaxElt %d, In Subenc " 1430 "%d, Text Length %d: %.*s\n", sestype - ses_types, 1431 sestype->hdr->etype_elm_type, sestype->hdr->etype_maxelt, 1432 sestype->hdr->etype_subenc, sestype->hdr->etype_txt_len, 1433 sestype->hdr->etype_txt_len, sestype->text); 1434 1435 nelm += sestype->hdr->etype_maxelt 1436 + /*overall status element*/1; 1437 sestype++; 1438 cur_buf_type++; 1439 } 1440 1441 /* Create the object map. */ 1442 enc_cache->elm_map = ENC_MALLOCZ(nelm * sizeof(enc_element_t)); 1443 if (enc_cache->elm_map == NULL) { 1444 err = ENOMEM; 1445 goto out; 1446 } 1447 ses_cache->ses_ntypes = (uint8_t)ntype; 1448 enc_cache->nelms = nelm; 1449 1450 ses_iter_init(enc, enc_cache, &iter); 1451 while ((element = ses_iter_next(&iter)) != NULL) { 1452 const struct ses_elm_type_desc *thdr; 1453 1454 ENC_DLOG(enc, "%s: checking obj %d(%d,%d)\n", __func__, 1455 iter.global_element_index, iter.type_index, nelm, 1456 iter.type_element_index); 1457 thdr = ses_cache->ses_types[iter.type_index].hdr; 1458 element->subenclosure = thdr->etype_subenc; 1459 element->enctype = thdr->etype_elm_type; 1460 element->overall_status_elem = iter.type_element_index == 0; 1461 element->elm_private = ENC_MALLOCZ(sizeof(ses_element_t)); 1462 if (element->elm_private == NULL) { 1463 err = ENOMEM; 1464 goto out; 1465 } 1466 ENC_DLOG(enc, "%s: creating elmpriv %d(%d,%d) subenc %d " 1467 "type 0x%x\n", __func__, iter.global_element_index, 1468 iter.type_index, iter.type_element_index, 1469 thdr->etype_subenc, thdr->etype_elm_type); 1470 } 1471 1472 err = 0; 1473 1474out: 1475 if (err) 1476 ses_cache_free(enc, enc_cache); 1477 else { 1478 enc_update_request(enc, SES_UPDATE_GETSTATUS); 1479 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 */ 1497static int 1498ses_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_LOG(enc, "Enclosure Status Page Too Long\n"); 1535 goto out; 1536 } 1537 /* Make sure the length contains at least one header and status */ 1538 if (length < (sizeof(*page) + sizeof(*page->elements))) { 1539 ENC_LOG(enc, "Enclosure Status Page Too Short\n"); 1540 goto out; 1541 } 1542 1543 if (!ses_config_cache_valid(ses_cache, page->hdr.gen_code)) { 1544 ENC_DLOG(enc, "%s: Generation count change detected\n", 1545 __func__); 1546 enc_update_request(enc, SES_UPDATE_GETCONFIG); 1547 goto out; 1548 } 1549 1550 ses_cache_free_status(enc, enc_cache); 1551 ses_cache->status_page = page; 1552 *bufp = NULL; 1553 1554 enc_cache->enc_status = page->hdr.page_specific_flags; 1555 1556 /* 1557 * Read in individual element status. The element order 1558 * matches the order reported in the config page (i.e. the 1559 * order of an unfiltered iteration of the config objects).. 1560 */ 1561 ses_iter_init(enc, enc_cache, &iter); 1562 cur_stat = page->elements; 1563 last_stat = (union ses_status_element *) 1564 &buf[length - sizeof(*last_stat)]; 1565 ENC_DLOG(enc, "%s: total page length %d, xfer_len %d\n", 1566 __func__, length, xfer_len); 1567 while (cur_stat <= last_stat 1568 && (element = ses_iter_next(&iter)) != NULL) { 1569 1570 ENC_DLOG(enc, "%s: obj %d(%d,%d) off=0x%tx status=%jx\n", 1571 __func__, iter.global_element_index, iter.type_index, 1572 iter.type_element_index, (uint8_t *)cur_stat - buf, 1573 scsi_4btoul(cur_stat->bytes)); 1574 1575 memcpy(&element->encstat, cur_stat, sizeof(element->encstat)); 1576 element->svalid = 1; 1577 cur_stat++; 1578 } 1579 1580 if (ses_iter_next(&iter) != NULL) { 1581 ENC_LOG(enc, "Status page, length insufficient for " 1582 "expected number of objects\n"); 1583 } else { 1584 if (cur_stat <= last_stat) 1585 ENC_LOG(enc, "Status page, exhausted objects before " 1586 "exhausing page\n"); 1587 enc_update_request(enc, SES_PUBLISH_CACHE); 1588 err = 0; 1589 } 1590out: 1591 ENC_DLOG(enc, "%s: exiting with error %d\n", __func__, err); 1592 return (err); 1593} 1594 1595typedef enum { 1596 /** 1597 * The enclosure should not provide additional element 1598 * status for this element type in page 0x0A. 1599 * 1600 * \note This status is returned for any types not 1601 * listed SES3r02. Further types added in a 1602 * future specification will be incorrectly 1603 * classified. 1604 */ 1605 TYPE_ADDLSTATUS_NONE, 1606 1607 /** 1608 * The element type provides additional element status 1609 * in page 0x0A. 1610 */ 1611 TYPE_ADDLSTATUS_MANDATORY, 1612 1613 /** 1614 * The element type may provide additional element status 1615 * in page 0x0A, but i 1616 */ 1617 TYPE_ADDLSTATUS_OPTIONAL 1618} ses_addlstatus_avail_t; 1619 1620/** 1621 * \brief Check to see whether a given type (as obtained via type headers) is 1622 * supported by the additional status command. 1623 * 1624 * \param enc SES softc to check. 1625 * \param typidx Type index to check for. 1626 * 1627 * \return An enumeration indicating if additional status is mandatory, 1628 * optional, or not required for this type. 1629 */ 1630static ses_addlstatus_avail_t 1631ses_typehasaddlstatus(enc_softc_t *enc, uint8_t typidx) 1632{ 1633 enc_cache_t *enc_cache; 1634 ses_cache_t *ses_cache; 1635 1636 enc_cache = &enc->enc_daemon_cache; 1637 ses_cache = enc_cache->private; 1638 switch(ses_cache->ses_types[typidx].hdr->etype_elm_type) { 1639 case ELMTYP_DEVICE: 1640 case ELMTYP_ARRAY_DEV: 1641 case ELMTYP_SAS_EXP: 1642 return (TYPE_ADDLSTATUS_MANDATORY); 1643 case ELMTYP_SCSI_INI: 1644 case ELMTYP_SCSI_TGT: 1645 case ELMTYP_ESCC: 1646 return (TYPE_ADDLSTATUS_OPTIONAL); 1647 default: 1648 /* No additional status information available. */ 1649 break; 1650 } 1651 return (TYPE_ADDLSTATUS_NONE); 1652} 1653 1654static int ses_get_elm_addlstatus_fc(enc_softc_t *, enc_cache_t *, 1655 uint8_t *, int); 1656static int ses_get_elm_addlstatus_sas(enc_softc_t *, enc_cache_t *, uint8_t *, 1657 int, int, int, int); 1658 1659/** 1660 * \brief Parse the additional status element data for each object. 1661 * 1662 * \param enc The SES softc to update. 1663 * \param buf The buffer containing the additional status 1664 * element response. 1665 * \param xfer_len Size of the buffer. 1666 * 1667 * \return 0 on success, errno otherwise. 1668 */ 1669static int 1670ses_process_elm_addlstatus(enc_softc_t *enc, struct enc_fsm_state *state, 1671 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1672{ 1673 struct ses_iterator iter, titer; 1674 int eip; 1675 int err; 1676 int ignore_index = 0; 1677 int length; 1678 int offset; 1679 enc_cache_t *enc_cache; 1680 ses_cache_t *ses_cache; 1681 uint8_t *buf; 1682 ses_element_t *elmpriv; 1683 const struct ses_page_hdr *hdr; 1684 enc_element_t *element, *telement; 1685 1686 enc_cache = &enc->enc_daemon_cache; 1687 ses_cache = enc_cache->private; 1688 buf = *bufp; 1689 err = -1; 1690 1691 if (error != 0) { 1692 err = error; 1693 goto out; 1694 } 1695 ses_cache_free_elm_addlstatus(enc, enc_cache); 1696 ses_cache->elm_addlstatus_page = 1697 (struct ses_addl_elem_status_page *)buf; 1698 *bufp = NULL; 1699 1700 /* 1701 * The objects appear in the same order here as in Enclosure Status, 1702 * which itself is ordered by the Type Descriptors from the Config 1703 * page. However, it is necessary to skip elements that are not 1704 * supported by this page when counting them. 1705 */ 1706 hdr = &ses_cache->elm_addlstatus_page->hdr; 1707 length = ses_page_length(hdr); 1708 ENC_DLOG(enc, "Additional Element Status Page Length 0x%x\n", length); 1709 /* Make sure the length includes at least one header. */ 1710 if (length < sizeof(*hdr)+sizeof(struct ses_elm_addlstatus_base_hdr)) { 1711 ENC_LOG(enc, "Runt Additional Element Status Page\n"); 1712 goto out; 1713 } 1714 if (length > xfer_len) { 1715 ENC_LOG(enc, "Additional Element Status Page Too Long\n"); 1716 goto out; 1717 } 1718 1719 if (!ses_config_cache_valid(ses_cache, hdr->gen_code)) { 1720 ENC_DLOG(enc, "%s: Generation count change detected\n", 1721 __func__); 1722 enc_update_request(enc, SES_UPDATE_GETCONFIG); 1723 goto out; 1724 } 1725 1726 offset = sizeof(struct ses_page_hdr); 1727 ses_iter_init(enc, enc_cache, &iter); 1728 while (offset < length 1729 && (element = ses_iter_next(&iter)) != NULL) { 1730 struct ses_elm_addlstatus_base_hdr *elm_hdr; 1731 int proto_info_len; 1732 ses_addlstatus_avail_t status_type; 1733 1734 /* 1735 * Additional element status is only provided for 1736 * individual elements (i.e. overal status elements 1737 * are excluded) and those of the types specified 1738 * in the SES spec. 1739 */ 1740 status_type = ses_typehasaddlstatus(enc, iter.type_index); 1741 if (iter.individual_element_index == ITERATOR_INDEX_INVALID 1742 || status_type == TYPE_ADDLSTATUS_NONE) 1743 continue; 1744 1745 elm_hdr = (struct ses_elm_addlstatus_base_hdr *)&buf[offset]; 1746 eip = ses_elm_addlstatus_eip(elm_hdr); 1747 if (eip && !ignore_index) { 1748 struct ses_elm_addlstatus_eip_hdr *eip_hdr; 1749 int expected_index; 1750 1751 eip_hdr = (struct ses_elm_addlstatus_eip_hdr *)elm_hdr; 1752 expected_index = iter.individual_element_index; 1753 titer = iter; 1754 telement = ses_iter_seek_to(&titer, 1755 eip_hdr->element_index, 1756 SES_ELEM_INDEX_INDIVIDUAL); 1757 if (telement != NULL && 1758 (ses_typehasaddlstatus(enc, titer.type_index) != 1759 TYPE_ADDLSTATUS_NONE || 1760 titer.type_index > ELMTYP_SAS_CONN)) { 1761 iter = titer; 1762 element = telement; 1763 } else 1764 ignore_index = 1; 1765 1766 if (iter.individual_element_index > expected_index 1767 && status_type == TYPE_ADDLSTATUS_MANDATORY) { 1768 ENC_LOG(enc, "%s: provided element " 1769 "index %d skips mandatory status " 1770 " element at index %d\n", 1771 __func__, eip_hdr->element_index, 1772 expected_index); 1773 } 1774 } 1775 elmpriv = element->elm_private; 1776 elmpriv->addl.hdr = elm_hdr; 1777 ENC_DLOG(enc, "%s: global element index=%d, type index=%d " 1778 "type element index=%d, offset=0x%x, " 1779 "byte0=0x%x, length=0x%x\n", __func__, 1780 iter.global_element_index, iter.type_index, 1781 iter.type_element_index, offset, elmpriv->addl.hdr->byte0, 1782 elmpriv->addl.hdr->length); 1783 1784 /* Skip to after the length field */ 1785 offset += sizeof(struct ses_elm_addlstatus_base_hdr); 1786 1787 /* Make sure the descriptor is within bounds */ 1788 if ((offset + elmpriv->addl.hdr->length) > length) { 1789 ENC_LOG(enc, "Element %d Beyond End " 1790 "of Additional Element Status Descriptors\n", 1791 iter.global_element_index); 1792 err = EIO; 1793 goto out; 1794 } 1795 1796 /* Advance to the protocol data, skipping eip bytes if needed */ 1797 offset += (eip * SES_EIP_HDR_EXTRA_LEN); 1798 proto_info_len = elmpriv->addl.hdr->length 1799 - (eip * SES_EIP_HDR_EXTRA_LEN); 1800 1801 /* Errors in this block are ignored as they are non-fatal */ 1802 switch(ses_elm_addlstatus_proto(elmpriv->addl.hdr)) { 1803 case SPSP_PROTO_FC: 1804 if (elmpriv->addl.hdr->length == 0) 1805 break; 1806 ses_get_elm_addlstatus_fc(enc, enc_cache, 1807 &buf[offset], proto_info_len); 1808 break; 1809 case SPSP_PROTO_SAS: 1810 if (elmpriv->addl.hdr->length <= 2) 1811 break; 1812 ses_get_elm_addlstatus_sas(enc, enc_cache, 1813 &buf[offset], 1814 proto_info_len, 1815 eip, iter.type_index, 1816 iter.global_element_index); 1817 break; 1818 default: 1819 ENC_LOG(enc, "Element %d: Unknown Additional Element " 1820 "Protocol 0x%x\n", iter.global_element_index, 1821 ses_elm_addlstatus_proto(elmpriv->addl.hdr)); 1822 goto out; 1823 } 1824 1825 offset += proto_info_len; 1826 } 1827 err = 0; 1828out: 1829 if (err) 1830 ses_cache_free_elm_addlstatus(enc, enc_cache); 1831 enc_update_request(enc, SES_PUBLISH_PHYSPATHS); 1832 enc_update_request(enc, SES_PUBLISH_CACHE); 1833 return (err); 1834} 1835 1836static int 1837ses_process_control_request(enc_softc_t *enc, struct enc_fsm_state *state, 1838 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1839{ 1840 ses_softc_t *ses; 1841 1842 ses = enc->enc_private; 1843 /* 1844 * Possible errors: 1845 * o Generation count wrong. 1846 * o Some SCSI status error. 1847 */ 1848 ses_terminate_control_requests(&ses->ses_pending_requests, error); 1849 enc_update_request(enc, SES_UPDATE_GETSTATUS); 1850 return (0); 1851} 1852 1853static int 1854ses_publish_physpaths(enc_softc_t *enc, struct enc_fsm_state *state, 1855 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1856{ 1857 struct ses_iterator iter; 1858 enc_cache_t *enc_cache; 1859 ses_cache_t *ses_cache; 1860 enc_element_t *element; 1861 1862 enc_cache = &enc->enc_daemon_cache; 1863 ses_cache = enc_cache->private; 1864 1865 ses_iter_init(enc, enc_cache, &iter); 1866 while ((element = ses_iter_next(&iter)) != NULL) { 1867 /* 1868 * ses_set_physpath() returns success if we changed 1869 * the physpath of any element. This allows us to 1870 * only announce devices once regardless of how 1871 * many times we process additional element status. 1872 */ 1873 if (ses_set_physpath(enc, element, &iter) == 0) 1874 ses_print_addl_data(enc, element); 1875 } 1876 1877 return (0); 1878} 1879 1880static int 1881ses_publish_cache(enc_softc_t *enc, struct enc_fsm_state *state, 1882 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1883{ 1884 1885 sx_xlock(&enc->enc_cache_lock); 1886 ses_cache_clone(enc, /*src*/&enc->enc_daemon_cache, 1887 /*dst*/&enc->enc_cache); 1888 sx_xunlock(&enc->enc_cache_lock); 1889 1890 return (0); 1891} 1892 1893/** 1894 * \brief Parse the descriptors for each object. 1895 * 1896 * \param enc The SES softc to update. 1897 * \param buf The buffer containing the descriptor list response. 1898 * \param xfer_len Size of the buffer. 1899 * 1900 * \return 0 on success, errno otherwise. 1901 */ 1902static int 1903ses_process_elm_descs(enc_softc_t *enc, struct enc_fsm_state *state, 1904 union ccb *ccb, uint8_t **bufp, int error, int xfer_len) 1905{ 1906 ses_softc_t *ses; 1907 struct ses_iterator iter; 1908 enc_element_t *element; 1909 int err; 1910 int offset; 1911 u_long length, plength; 1912 enc_cache_t *enc_cache; 1913 ses_cache_t *ses_cache; 1914 uint8_t *buf; 1915 ses_element_t *elmpriv; 1916 const struct ses_page_hdr *phdr; 1917 const struct ses_elm_desc_hdr *hdr; 1918 1919 ses = enc->enc_private; 1920 enc_cache = &enc->enc_daemon_cache; 1921 ses_cache = enc_cache->private; 1922 buf = *bufp; 1923 err = -1; 1924 1925 if (error != 0) { 1926 err = error; 1927 goto out; 1928 } 1929 ses_cache_free_elm_descs(enc, enc_cache); 1930 ses_cache->elm_descs_page = (struct ses_elem_descr_page *)buf; 1931 *bufp = NULL; 1932 1933 phdr = &ses_cache->elm_descs_page->hdr; 1934 plength = ses_page_length(phdr); 1935 if (xfer_len < sizeof(struct ses_page_hdr)) { 1936 ENC_LOG(enc, "Runt Element Descriptor Page\n"); 1937 goto out; 1938 } 1939 if (plength > xfer_len) { 1940 ENC_LOG(enc, "Element Descriptor Page Too Long\n"); 1941 goto out; 1942 } 1943 1944 if (!ses_config_cache_valid(ses_cache, phdr->gen_code)) { 1945 ENC_VLOG(enc, "%s: Generation count change detected\n", 1946 __func__); 1947 enc_update_request(enc, SES_UPDATE_GETCONFIG); 1948 goto out; 1949 } 1950 1951 offset = sizeof(struct ses_page_hdr); 1952 1953 ses_iter_init(enc, enc_cache, &iter); 1954 while (offset < plength 1955 && (element = ses_iter_next(&iter)) != NULL) { 1956 1957 if ((offset + sizeof(struct ses_elm_desc_hdr)) > plength) { 1958 ENC_LOG(enc, "Element %d Descriptor Header Past " 1959 "End of Buffer\n", iter.global_element_index); 1960 goto out; 1961 } 1962 hdr = (struct ses_elm_desc_hdr *)&buf[offset]; 1963 length = scsi_2btoul(hdr->length); 1964 ENC_DLOG(enc, "%s: obj %d(%d,%d) length=%d off=%d\n", __func__, 1965 iter.global_element_index, iter.type_index, 1966 iter.type_element_index, length, offset); 1967 if ((offset + sizeof(*hdr) + length) > plength) { 1968 ENC_LOG(enc, "Element%d Descriptor Past " 1969 "End of Buffer\n", iter.global_element_index); 1970 goto out; 1971 } 1972 offset += sizeof(*hdr); 1973 1974 if (length > 0) { 1975 elmpriv = element->elm_private; 1976 elmpriv->descr_len = length; 1977 elmpriv->descr = &buf[offset]; 1978 } 1979 1980 /* skip over the descriptor itself */ 1981 offset += length; 1982 } 1983 1984 err = 0; 1985out: 1986 if (err == 0) { 1987 if (ses->ses_flags & SES_FLAG_ADDLSTATUS) 1988 enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); 1989 } 1990 enc_update_request(enc, SES_PUBLISH_CACHE); 1991 return (err); 1992} 1993 1994static int 1995ses_fill_rcv_diag_io(enc_softc_t *enc, struct enc_fsm_state *state, 1996 union ccb *ccb, uint8_t *buf) 1997{ 1998 1999 if (enc->enc_type == ENC_SEMB_SES) { 2000 semb_receive_diagnostic_results(&ccb->ataio, /*retries*/5, 2001 enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1, 2002 state->page_code, buf, state->buf_size, 2003 state->timeout); 2004 } else { 2005 scsi_receive_diagnostic_results(&ccb->csio, /*retries*/5, 2006 enc_done, MSG_SIMPLE_Q_TAG, /*pcv*/1, 2007 state->page_code, buf, state->buf_size, 2008 SSD_FULL_SIZE, state->timeout); 2009 } 2010 return (0); 2011} 2012 2013/** 2014 * \brief Encode the object status into the response buffer, which is 2015 * expected to contain the current enclosure status. This function 2016 * turns off all the 'select' bits for the objects except for the 2017 * object specified, then sends it back to the enclosure. 2018 * 2019 * \param enc SES enclosure the change is being applied to. 2020 * \param buf Buffer containing the current enclosure status response. 2021 * \param amt Length of the response in the buffer. 2022 * \param req The control request to be applied to buf. 2023 * 2024 * \return 0 on success, errno otherwise. 2025 */ 2026static int 2027ses_encode(enc_softc_t *enc, uint8_t *buf, int amt, ses_control_request_t *req) 2028{ 2029 struct ses_iterator iter; 2030 enc_element_t *element; 2031 int offset; 2032 struct ses_control_page_hdr *hdr; 2033 2034 ses_iter_init(enc, &enc->enc_cache, &iter); 2035 hdr = (struct ses_control_page_hdr *)buf; 2036 if (req->elm_idx == -1) { 2037 /* for enclosure status, at least 2 bytes are needed */ 2038 if (amt < 2) 2039 return EIO; 2040 hdr->control_flags = 2041 req->elm_stat.comstatus & SES_SET_STATUS_MASK; 2042 ENC_DLOG(enc, "Set EncStat %x\n", hdr->control_flags); 2043 return (0); 2044 } 2045 2046 element = ses_iter_seek_to(&iter, req->elm_idx, SES_ELEM_INDEX_GLOBAL); 2047 if (element == NULL) 2048 return (ENXIO); 2049 2050 /* 2051 * Seek to the type set that corresponds to the requested object. 2052 * The +1 is for the overall status element for the type. 2053 */ 2054 offset = sizeof(struct ses_control_page_hdr) 2055 + (iter.global_element_index * sizeof(struct ses_comstat)); 2056 2057 /* Check for buffer overflow. */ 2058 if (offset + sizeof(struct ses_comstat) > amt) 2059 return (EIO); 2060 2061 /* Set the status. */ 2062 memcpy(&buf[offset], &req->elm_stat, sizeof(struct ses_comstat)); 2063 2064 ENC_DLOG(enc, "Set Type 0x%x Obj 0x%x (offset %d) with %x %x %x %x\n", 2065 iter.type_index, iter.global_element_index, offset, 2066 req->elm_stat.comstatus, req->elm_stat.comstat[0], 2067 req->elm_stat.comstat[1], req->elm_stat.comstat[2]); 2068 2069 return (0); 2070} 2071 2072static int 2073ses_fill_control_request(enc_softc_t *enc, struct enc_fsm_state *state, 2074 union ccb *ccb, uint8_t *buf) 2075{ 2076 ses_softc_t *ses; 2077 enc_cache_t *enc_cache; 2078 ses_cache_t *ses_cache; 2079 struct ses_control_page_hdr *hdr; 2080 ses_control_request_t *req; 2081 size_t plength; 2082 size_t offset; 2083 2084 ses = enc->enc_private; 2085 enc_cache = &enc->enc_daemon_cache; 2086 ses_cache = enc_cache->private; 2087 hdr = (struct ses_control_page_hdr *)buf; 2088 2089 if (ses_cache->status_page == NULL) { 2090 ses_terminate_control_requests(&ses->ses_requests, EIO); 2091 return (EIO); 2092 } 2093 2094 plength = ses_page_length(&ses_cache->status_page->hdr); 2095 memcpy(buf, ses_cache->status_page, plength); 2096 2097 /* Disable the select bits in all status entries. */ 2098 offset = sizeof(struct ses_control_page_hdr); 2099 for (offset = sizeof(struct ses_control_page_hdr); 2100 offset < plength; offset += sizeof(struct ses_comstat)) { 2101 buf[offset] &= ~SESCTL_CSEL; 2102 } 2103 2104 /* And make sure the INVOP bit is clear. */ 2105 hdr->control_flags &= ~SES_ENCSTAT_INVOP; 2106 2107 /* Apply incoming requests. */ 2108 while ((req = TAILQ_FIRST(&ses->ses_requests)) != NULL) { 2109 2110 TAILQ_REMOVE(&ses->ses_requests, req, links); 2111 req->result = ses_encode(enc, buf, plength, req); 2112 if (req->result != 0) { 2113 wakeup(req); 2114 continue; 2115 } 2116 TAILQ_INSERT_TAIL(&ses->ses_pending_requests, req, links); 2117 } 2118 2119 if (TAILQ_EMPTY(&ses->ses_pending_requests) != 0) 2120 return (ENOENT); 2121 2122 /* Fill out the ccb */ 2123 if (enc->enc_type == ENC_SEMB_SES) { 2124 semb_send_diagnostic(&ccb->ataio, /*retries*/5, enc_done, 2125 MSG_SIMPLE_Q_TAG, 2126 buf, ses_page_length(&ses_cache->status_page->hdr), 2127 state->timeout); 2128 } else { 2129 scsi_send_diagnostic(&ccb->csio, /*retries*/5, enc_done, 2130 MSG_SIMPLE_Q_TAG, /*unit_offline*/0, 2131 /*device_offline*/0, /*self_test*/0, 2132 /*page_format*/1, /*self_test_code*/0, 2133 buf, ses_page_length(&ses_cache->status_page->hdr), 2134 SSD_FULL_SIZE, state->timeout); 2135 } 2136 return (0); 2137} 2138 2139static int 2140ses_get_elm_addlstatus_fc(enc_softc_t *enc, enc_cache_t *enc_cache, 2141 uint8_t *buf, int bufsiz) 2142{ 2143 ENC_LOG(enc, "FC Device Support Stubbed in Additional Status Page\n"); 2144 return (ENODEV); 2145} 2146 2147#define SES_PRINT_PORTS(p, type) do { \ 2148 sbuf_printf(sbp, " %s(", type); \ 2149 if (((p) & SES_SASOBJ_DEV_PHY_PROTOMASK) == 0) \ 2150 sbuf_printf(sbp, " None"); \ 2151 else { \ 2152 if ((p) & SES_SASOBJ_DEV_PHY_SMP) \ 2153 sbuf_printf(sbp, " SMP"); \ 2154 if ((p) & SES_SASOBJ_DEV_PHY_STP) \ 2155 sbuf_printf(sbp, " STP"); \ 2156 if ((p) & SES_SASOBJ_DEV_PHY_SSP) \ 2157 sbuf_printf(sbp, " SSP"); \ 2158 } \ 2159 sbuf_printf(sbp, " )"); \ 2160} while(0) 2161 2162/** 2163 * \brief Print the additional element status data for this object, for SAS 2164 * type 0 objects. See SES2 r20 Section 6.1.13.3.2. 2165 * 2166 * \param sesname SES device name associated with the object. 2167 * \param sbp Sbuf to print to. 2168 * \param obj The object to print the data for. 2169 * \param periph_name Peripheral string associated with the object. 2170 */ 2171static void 2172ses_print_addl_data_sas_type0(char *sesname, struct sbuf *sbp, 2173 enc_element_t *obj, char *periph_name) 2174{ 2175 int i; 2176 ses_element_t *elmpriv; 2177 struct ses_addl_status *addl; 2178 struct ses_elm_sas_device_phy *phy; 2179 2180 elmpriv = obj->elm_private; 2181 addl = &(elmpriv->addl); 2182 if (addl->proto_hdr.sas == NULL) 2183 return; 2184 sbuf_printf(sbp, "%s: %s: SAS Device Slot Element:", 2185 sesname, periph_name); 2186 sbuf_printf(sbp, " %d Phys", addl->proto_hdr.sas->base_hdr.num_phys); 2187 if (ses_elm_addlstatus_eip(addl->hdr)) 2188 sbuf_printf(sbp, " at Slot %d", 2189 addl->proto_hdr.sas->type0_eip.dev_slot_num); 2190 if (ses_elm_sas_type0_not_all_phys(addl->proto_hdr.sas)) 2191 sbuf_printf(sbp, ", Not All Phys"); 2192 sbuf_printf(sbp, "\n"); 2193 if (addl->proto_data.sasdev_phys == NULL) 2194 return; 2195 for (i = 0;i < addl->proto_hdr.sas->base_hdr.num_phys;i++) { 2196 phy = &addl->proto_data.sasdev_phys[i]; 2197 sbuf_printf(sbp, "%s: phy %d:", sesname, i); 2198 if (ses_elm_sas_dev_phy_sata_dev(phy)) 2199 /* Spec says all other fields are specific values */ 2200 sbuf_printf(sbp, " SATA device\n"); 2201 else { 2202 sbuf_printf(sbp, " SAS device type %d id %d\n", 2203 ses_elm_sas_dev_phy_dev_type(phy), phy->phy_id); 2204 sbuf_printf(sbp, "%s: phy %d: protocols:", sesname, i); 2205 SES_PRINT_PORTS(phy->initiator_ports, "Initiator"); 2206 SES_PRINT_PORTS(phy->target_ports, "Target"); 2207 sbuf_printf(sbp, "\n"); 2208 } 2209 sbuf_printf(sbp, "%s: phy %d: parent %jx addr %jx\n", 2210 sesname, i, 2211 (uintmax_t)scsi_8btou64(phy->parent_addr), 2212 (uintmax_t)scsi_8btou64(phy->phy_addr)); 2213 } 2214} 2215#undef SES_PRINT_PORTS 2216 2217/** 2218 * \brief Report whether a given enclosure object is an expander. 2219 * 2220 * \param enc SES softc associated with object. 2221 * \param obj Enclosure object to report for. 2222 * 2223 * \return 1 if true, 0 otherwise. 2224 */ 2225static int 2226ses_obj_is_expander(enc_softc_t *enc, enc_element_t *obj) 2227{ 2228 return (obj->enctype == ELMTYP_SAS_EXP); 2229} 2230 2231/** 2232 * \brief Print the additional element status data for this object, for SAS 2233 * type 1 objects. See SES2 r20 Sections 6.1.13.3.3 and 6.1.13.3.4. 2234 * 2235 * \param enc SES enclosure, needed for type identification. 2236 * \param sesname SES device name associated with the object. 2237 * \param sbp Sbuf to print to. 2238 * \param obj The object to print the data for. 2239 * \param periph_name Peripheral string associated with the object. 2240 */ 2241static void 2242ses_print_addl_data_sas_type1(enc_softc_t *enc, char *sesname, 2243 struct sbuf *sbp, enc_element_t *obj, char *periph_name) 2244{ 2245 int i, num_phys; 2246 ses_element_t *elmpriv; 2247 struct ses_addl_status *addl; 2248 struct ses_elm_sas_expander_phy *exp_phy; 2249 struct ses_elm_sas_port_phy *port_phy; 2250 2251 elmpriv = obj->elm_private; 2252 addl = &(elmpriv->addl); 2253 if (addl->proto_hdr.sas == NULL) 2254 return; 2255 sbuf_printf(sbp, "%s: %s: SAS ", sesname, periph_name); 2256 if (ses_obj_is_expander(enc, obj)) { 2257 num_phys = addl->proto_hdr.sas->base_hdr.num_phys; 2258 sbuf_printf(sbp, "Expander: %d Phys", num_phys); 2259 if (addl->proto_data.sasexp_phys == NULL) 2260 return; 2261 for (i = 0;i < num_phys;i++) { 2262 exp_phy = &addl->proto_data.sasexp_phys[i]; 2263 sbuf_printf(sbp, "%s: phy %d: connector %d other %d\n", 2264 sesname, i, exp_phy->connector_index, 2265 exp_phy->other_index); 2266 } 2267 } else { 2268 num_phys = addl->proto_hdr.sas->base_hdr.num_phys; 2269 sbuf_printf(sbp, "Port: %d Phys", num_phys); 2270 if (addl->proto_data.sasport_phys == NULL) 2271 return; 2272 for (i = 0;i < num_phys;i++) { 2273 port_phy = &addl->proto_data.sasport_phys[i]; 2274 sbuf_printf(sbp, 2275 "%s: phy %d: id %d connector %d other %d\n", 2276 sesname, i, port_phy->phy_id, 2277 port_phy->connector_index, port_phy->other_index); 2278 sbuf_printf(sbp, "%s: phy %d: addr %jx\n", sesname, i, 2279 (uintmax_t)scsi_8btou64(port_phy->phy_addr)); 2280 } 2281 } 2282} 2283 2284/** 2285 * \brief Print the additional element status data for this object. 2286 * 2287 * \param enc SES softc associated with the object. 2288 * \param obj The object to print the data for. 2289 */ 2290static void 2291ses_print_addl_data(enc_softc_t *enc, enc_element_t *obj) 2292{ 2293 ses_element_t *elmpriv; 2294 struct ses_addl_status *addl; 2295 struct sbuf sesname, name, out; 2296 2297 elmpriv = obj->elm_private; 2298 if (elmpriv == NULL) 2299 return; 2300 2301 addl = &(elmpriv->addl); 2302 if (addl->hdr == NULL) 2303 return; 2304 2305 sbuf_new(&sesname, NULL, 16, SBUF_AUTOEXTEND); 2306 sbuf_new(&name, NULL, 16, SBUF_AUTOEXTEND); 2307 sbuf_new(&out, NULL, 512, SBUF_AUTOEXTEND); 2308 ses_paths_iter(enc, obj, ses_elmdevname_callback, &name); 2309 if (sbuf_len(&name) == 0) 2310 sbuf_printf(&name, "(none)"); 2311 sbuf_finish(&name); 2312 sbuf_printf(&sesname, "%s%d", enc->periph->periph_name, 2313 enc->periph->unit_number); 2314 sbuf_finish(&sesname); 2315 if (elmpriv->descr != NULL) 2316 sbuf_printf(&out, "%s: %s: Element descriptor: '%s'\n", 2317 sbuf_data(&sesname), sbuf_data(&name), elmpriv->descr); 2318 switch(ses_elm_addlstatus_proto(addl->hdr)) { 2319 case SPSP_PROTO_SAS: 2320 switch(ses_elm_sas_descr_type(addl->proto_hdr.sas)) { 2321 case SES_SASOBJ_TYPE_SLOT: 2322 ses_print_addl_data_sas_type0(sbuf_data(&sesname), 2323 &out, obj, sbuf_data(&name)); 2324 break; 2325 case SES_SASOBJ_TYPE_OTHER: 2326 ses_print_addl_data_sas_type1(enc, sbuf_data(&sesname), 2327 &out, obj, sbuf_data(&name)); 2328 break; 2329 default: 2330 break; 2331 } 2332 break; 2333 case SPSP_PROTO_FC: /* stubbed for now */ 2334 break; 2335 default: 2336 break; 2337 } 2338 sbuf_finish(&out); 2339 printf("%s", sbuf_data(&out)); 2340 sbuf_delete(&out); 2341 sbuf_delete(&name); 2342 sbuf_delete(&sesname); 2343} 2344 2345/** 2346 * \brief Update the softc with the additional element status data for this 2347 * object, for SAS type 0 objects. 2348 * 2349 * \param enc SES softc to be updated. 2350 * \param buf The additional element status response buffer. 2351 * \param bufsiz Size of the response buffer. 2352 * \param eip The EIP bit value. 2353 * \param nobj Number of objects attached to the SES softc. 2354 * 2355 * \return 0 on success, errno otherwise. 2356 */ 2357static int 2358ses_get_elm_addlstatus_sas_type0(enc_softc_t *enc, enc_cache_t *enc_cache, 2359 uint8_t *buf, int bufsiz, int eip, int nobj) 2360{ 2361 int err, offset, physz; 2362 enc_element_t *obj; 2363 ses_element_t *elmpriv; 2364 struct ses_addl_status *addl; 2365 2366 err = offset = 0; 2367 2368 /* basic object setup */ 2369 obj = &(enc_cache->elm_map[nobj]); 2370 elmpriv = obj->elm_private; 2371 addl = &(elmpriv->addl); 2372 2373 addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; 2374 2375 /* Don't assume this object has any phys */ 2376 bzero(&addl->proto_data, sizeof(addl->proto_data)); 2377 if (addl->proto_hdr.sas->base_hdr.num_phys == 0) 2378 goto out; 2379 2380 /* Skip forward to the phy list */ 2381 if (eip) 2382 offset += sizeof(struct ses_elm_sas_type0_eip_hdr); 2383 else 2384 offset += sizeof(struct ses_elm_sas_type0_base_hdr); 2385 2386 /* Make sure the phy list fits in the buffer */ 2387 physz = addl->proto_hdr.sas->base_hdr.num_phys; 2388 physz *= sizeof(struct ses_elm_sas_device_phy); 2389 if (physz > (bufsiz - offset + 4)) { 2390 ENC_LOG(enc, "Element %d Device Phy List Beyond End Of Buffer\n", 2391 nobj); 2392 err = EIO; 2393 goto out; 2394 } 2395 2396 /* Point to the phy list */ 2397 addl->proto_data.sasdev_phys = 2398 (struct ses_elm_sas_device_phy *)&buf[offset]; 2399 2400out: 2401 return (err); 2402} 2403 2404/** 2405 * \brief Update the softc with the additional element status data for this 2406 * object, for SAS type 1 objects. 2407 * 2408 * \param enc SES softc to be updated. 2409 * \param buf The additional element status response buffer. 2410 * \param bufsiz Size of the response buffer. 2411 * \param eip The EIP bit value. 2412 * \param nobj Number of objects attached to the SES softc. 2413 * 2414 * \return 0 on success, errno otherwise. 2415 */ 2416static int 2417ses_get_elm_addlstatus_sas_type1(enc_softc_t *enc, enc_cache_t *enc_cache, 2418 uint8_t *buf, int bufsiz, int eip, int nobj) 2419{ 2420 int err, offset, physz; 2421 enc_element_t *obj; 2422 ses_element_t *elmpriv; 2423 struct ses_addl_status *addl; 2424 2425 err = offset = 0; 2426 2427 /* basic object setup */ 2428 obj = &(enc_cache->elm_map[nobj]); 2429 elmpriv = obj->elm_private; 2430 addl = &(elmpriv->addl); 2431 2432 addl->proto_hdr.sas = (union ses_elm_sas_hdr *)&buf[offset]; 2433 2434 /* Don't assume this object has any phys */ 2435 bzero(&addl->proto_data, sizeof(addl->proto_data)); 2436 if (addl->proto_hdr.sas->base_hdr.num_phys == 0) 2437 goto out; 2438 2439 /* Process expanders differently from other type1 cases */ 2440 if (ses_obj_is_expander(enc, obj)) { 2441 offset += sizeof(struct ses_elm_sas_type1_expander_hdr); 2442 physz = addl->proto_hdr.sas->base_hdr.num_phys * 2443 sizeof(struct ses_elm_sas_expander_phy); 2444 if (physz > (bufsiz - offset)) { 2445 ENC_LOG(enc, "Element %d: Expander Phy List Beyond " 2446 "End Of Buffer\n", nobj); 2447 err = EIO; 2448 goto out; 2449 } 2450 addl->proto_data.sasexp_phys = 2451 (struct ses_elm_sas_expander_phy *)&buf[offset]; 2452 } else { 2453 offset += sizeof(struct ses_elm_sas_type1_nonexpander_hdr); 2454 physz = addl->proto_hdr.sas->base_hdr.num_phys * 2455 sizeof(struct ses_elm_sas_port_phy); 2456 if (physz > (bufsiz - offset + 4)) { 2457 ENC_LOG(enc, "Element %d: Port Phy List Beyond End " 2458 "Of Buffer\n", nobj); 2459 err = EIO; 2460 goto out; 2461 } 2462 addl->proto_data.sasport_phys = 2463 (struct ses_elm_sas_port_phy *)&buf[offset]; 2464 } 2465 2466out: 2467 return (err); 2468} 2469 2470/** 2471 * \brief Update the softc with the additional element status data for this 2472 * object, for SAS objects. 2473 * 2474 * \param enc SES softc to be updated. 2475 * \param buf The additional element status response buffer. 2476 * \param bufsiz Size of the response buffer. 2477 * \param eip The EIP bit value. 2478 * \param tidx Type index for this object. 2479 * \param nobj Number of objects attached to the SES softc. 2480 * 2481 * \return 0 on success, errno otherwise. 2482 */ 2483static int 2484ses_get_elm_addlstatus_sas(enc_softc_t *enc, enc_cache_t *enc_cache, 2485 uint8_t *buf, int bufsiz, int eip, int tidx, 2486 int nobj) 2487{ 2488 int dtype, err; 2489 ses_cache_t *ses_cache; 2490 union ses_elm_sas_hdr *hdr; 2491 2492 /* Need to be able to read the descriptor type! */ 2493 if (bufsiz < sizeof(union ses_elm_sas_hdr)) { 2494 err = EIO; 2495 goto out; 2496 } 2497 2498 ses_cache = enc_cache->private; 2499 2500 hdr = (union ses_elm_sas_hdr *)buf; 2501 dtype = ses_elm_sas_descr_type(hdr); 2502 switch(dtype) { 2503 case SES_SASOBJ_TYPE_SLOT: 2504 switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { 2505 case ELMTYP_DEVICE: 2506 case ELMTYP_ARRAY_DEV: 2507 break; 2508 default: 2509 ENC_LOG(enc, "Element %d has Additional Status type 0, " 2510 "invalid for SES element type 0x%x\n", nobj, 2511 ses_cache->ses_types[tidx].hdr->etype_elm_type); 2512 err = ENODEV; 2513 goto out; 2514 } 2515 err = ses_get_elm_addlstatus_sas_type0(enc, enc_cache, 2516 buf, bufsiz, eip, 2517 nobj); 2518 break; 2519 case SES_SASOBJ_TYPE_OTHER: 2520 switch(ses_cache->ses_types[tidx].hdr->etype_elm_type) { 2521 case ELMTYP_SAS_EXP: 2522 case ELMTYP_SCSI_INI: 2523 case ELMTYP_SCSI_TGT: 2524 case ELMTYP_ESCC: 2525 break; 2526 default: 2527 ENC_LOG(enc, "Element %d has Additional Status type 1, " 2528 "invalid for SES element type 0x%x\n", nobj, 2529 ses_cache->ses_types[tidx].hdr->etype_elm_type); 2530 err = ENODEV; 2531 goto out; 2532 } 2533 err = ses_get_elm_addlstatus_sas_type1(enc, enc_cache, buf, 2534 bufsiz, eip, nobj); 2535 break; 2536 default: 2537 ENC_LOG(enc, "Element %d of type 0x%x has Additional Status " 2538 "of unknown type 0x%x\n", nobj, 2539 ses_cache->ses_types[tidx].hdr->etype_elm_type, dtype); 2540 err = ENODEV; 2541 break; 2542 } 2543 2544out: 2545 return (err); 2546} 2547 2548static void 2549ses_softc_invalidate(enc_softc_t *enc) 2550{ 2551 ses_softc_t *ses; 2552 2553 ses = enc->enc_private; 2554 ses_terminate_control_requests(&ses->ses_requests, ENXIO); 2555} 2556 2557static void 2558ses_softc_cleanup(enc_softc_t *enc) 2559{ 2560 2561 ses_cache_free(enc, &enc->enc_cache); 2562 ses_cache_free(enc, &enc->enc_daemon_cache); 2563 ENC_FREE_AND_NULL(enc->enc_private); 2564 ENC_FREE_AND_NULL(enc->enc_cache.private); 2565 ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); 2566} 2567 2568static int 2569ses_init_enc(enc_softc_t *enc) 2570{ 2571 return (0); 2572} 2573 2574static int 2575ses_get_enc_status(enc_softc_t *enc, int slpflag) 2576{ 2577 /* Automatically updated, caller checks enc_cache->encstat itself */ 2578 return (0); 2579} 2580 2581static int 2582ses_set_enc_status(enc_softc_t *enc, uint8_t encstat, int slpflag) 2583{ 2584 ses_control_request_t req; 2585 ses_softc_t *ses; 2586 2587 ses = enc->enc_private; 2588 req.elm_idx = SES_SETSTATUS_ENC_IDX; 2589 req.elm_stat.comstatus = encstat & 0xf; 2590 2591 TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); 2592 enc_update_request(enc, SES_PROCESS_CONTROL_REQS); 2593 cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); 2594 2595 return (req.result); 2596} 2597 2598static int 2599ses_get_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) 2600{ 2601 unsigned int i = elms->elm_idx; 2602 2603 memcpy(elms->cstat, &enc->enc_cache.elm_map[i].encstat, 4); 2604 return (0); 2605} 2606 2607static int 2608ses_set_elm_status(enc_softc_t *enc, encioc_elm_status_t *elms, int slpflag) 2609{ 2610 ses_control_request_t req; 2611 ses_softc_t *ses; 2612 2613 /* If this is clear, we don't do diddly. */ 2614 if ((elms->cstat[0] & SESCTL_CSEL) == 0) 2615 return (0); 2616 2617 ses = enc->enc_private; 2618 req.elm_idx = elms->elm_idx; 2619 memcpy(&req.elm_stat, elms->cstat, sizeof(req.elm_stat)); 2620 2621 TAILQ_INSERT_TAIL(&ses->ses_requests, &req, links); 2622 enc_update_request(enc, SES_PROCESS_CONTROL_REQS); 2623 cam_periph_sleep(enc->periph, &req, PUSER, "encstat", 0); 2624 2625 return (req.result); 2626} 2627 2628static int 2629ses_get_elm_desc(enc_softc_t *enc, encioc_elm_desc_t *elmd) 2630{ 2631 int i = (int)elmd->elm_idx; 2632 ses_element_t *elmpriv; 2633 2634 /* Assume caller has already checked obj_id validity */ 2635 elmpriv = enc->enc_cache.elm_map[i].elm_private; 2636 /* object might not have a descriptor */ 2637 if (elmpriv == NULL || elmpriv->descr == NULL) { 2638 elmd->elm_desc_len = 0; 2639 return (0); 2640 } 2641 if (elmd->elm_desc_len > elmpriv->descr_len) 2642 elmd->elm_desc_len = elmpriv->descr_len; 2643 copyout(elmpriv->descr, elmd->elm_desc_str, elmd->elm_desc_len); 2644 return (0); 2645} 2646 2647/** 2648 * \brief Respond to ENCIOC_GETELMDEVNAME, providing a device name for the 2649 * given object id if one is available. 2650 * 2651 * \param enc SES softc to examine. 2652 * \param objdn ioctl structure to read/write device name info. 2653 * 2654 * \return 0 on success, errno otherwise. 2655 */ 2656static int 2657ses_get_elm_devnames(enc_softc_t *enc, encioc_elm_devnames_t *elmdn) 2658{ 2659 struct sbuf sb; 2660 int len; 2661 2662 len = elmdn->elm_names_size; 2663 if (len < 0) 2664 return (EINVAL); 2665 2666 sbuf_new(&sb, elmdn->elm_devnames, len, 0); 2667 2668 cam_periph_unlock(enc->periph); 2669 ses_paths_iter(enc, &enc->enc_cache.elm_map[elmdn->elm_idx], 2670 ses_elmdevname_callback, &sb); 2671 sbuf_finish(&sb); 2672 elmdn->elm_names_len = sbuf_len(&sb); 2673 cam_periph_lock(enc->periph); 2674 return (elmdn->elm_names_len > 0 ? 0 : ENODEV); 2675} 2676 2677/** 2678 * \brief Send a string to the primary subenclosure using the String Out 2679 * SES diagnostic page. 2680 * 2681 * \param enc SES enclosure to run the command on. 2682 * \param sstr SES string structure to operate on 2683 * \param ioc Ioctl being performed 2684 * 2685 * \return 0 on success, errno otherwise. 2686 */ 2687static int 2688ses_handle_string(enc_softc_t *enc, encioc_string_t *sstr, int ioc) 2689{ 2690 int amt, payload, ret; 2691 char cdb[6]; 2692 uint8_t *buf; 2693 2694 /* Implement SES2r20 6.1.6 */ 2695 if (sstr->bufsiz > 0xffff) 2696 return (EINVAL); /* buffer size too large */ 2697 2698 if (ioc == ENCIOC_SETSTRING) { 2699 payload = sstr->bufsiz + 4; /* header for SEND DIAGNOSTIC */ 2700 amt = 0 - payload; 2701 buf = ENC_MALLOC(payload); 2702 if (buf == NULL) 2703 return ENOMEM; 2704 2705 ses_page_cdb(cdb, payload, 0, CAM_DIR_OUT); 2706 /* Construct the page request */ 2707 buf[0] = SesStringOut; 2708 buf[1] = 0; 2709 buf[2] = sstr->bufsiz >> 8; 2710 buf[3] = sstr->bufsiz & 0xff; 2711 memcpy(&buf[4], sstr->buf, sstr->bufsiz); 2712 } else if (ioc == ENCIOC_GETSTRING) { 2713 payload = sstr->bufsiz; 2714 amt = payload; 2715 ses_page_cdb(cdb, payload, SesStringIn, CAM_DIR_IN); 2716 buf = sstr->buf; 2717 } else 2718 return EINVAL; 2719 2720 ret = enc_runcmd(enc, cdb, 6, buf, &amt); 2721 if (ioc == ENCIOC_SETSTRING) 2722 ENC_FREE(buf); 2723 return ret; 2724} 2725 2726/** 2727 * \invariant Called with cam_periph mutex held. 2728 */ 2729static void 2730ses_poll_status(enc_softc_t *enc) 2731{ 2732 ses_softc_t *ses; 2733 2734 ses = enc->enc_private; 2735 enc_update_request(enc, SES_UPDATE_GETSTATUS); 2736 if (ses->ses_flags & SES_FLAG_ADDLSTATUS) 2737 enc_update_request(enc, SES_UPDATE_GETELMADDLSTATUS); 2738} 2739 2740/** 2741 * \brief Notification received when CAM detects a new device in the 2742 * SCSI domain in which this SEP resides. 2743 * 2744 * \param enc SES enclosure instance. 2745 */ 2746static void 2747ses_device_found(enc_softc_t *enc) 2748{ 2749 ses_poll_status(enc); 2750 enc_update_request(enc, SES_PUBLISH_PHYSPATHS); 2751} 2752 2753static struct enc_vec ses_enc_vec = 2754{ 2755 .softc_invalidate = ses_softc_invalidate, 2756 .softc_cleanup = ses_softc_cleanup, 2757 .init_enc = ses_init_enc, 2758 .get_enc_status = ses_get_enc_status, 2759 .set_enc_status = ses_set_enc_status, 2760 .get_elm_status = ses_get_elm_status, 2761 .set_elm_status = ses_set_elm_status, 2762 .get_elm_desc = ses_get_elm_desc, 2763 .get_elm_devnames = ses_get_elm_devnames, 2764 .handle_string = ses_handle_string, 2765 .device_found = ses_device_found, 2766 .poll_status = ses_poll_status 2767}; 2768 2769/** 2770 * \brief Initialize a new SES instance. 2771 * 2772 * \param enc SES softc structure to set up the instance in. 2773 * \param doinit Do the initialization (see main driver). 2774 * 2775 * \return 0 on success, errno otherwise. 2776 */ 2777int 2778ses_softc_init(enc_softc_t *enc) 2779{ 2780 ses_softc_t *ses_softc; 2781 2782 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 2783 ("entering enc_softc_init(%p)\n", enc)); 2784 2785 enc->enc_vec = ses_enc_vec; 2786 enc->enc_fsm_states = enc_fsm_states; 2787 2788 if (enc->enc_private == NULL) 2789 enc->enc_private = ENC_MALLOCZ(sizeof(ses_softc_t)); 2790 if (enc->enc_cache.private == NULL) 2791 enc->enc_cache.private = ENC_MALLOCZ(sizeof(ses_cache_t)); 2792 if (enc->enc_daemon_cache.private == NULL) 2793 enc->enc_daemon_cache.private = 2794 ENC_MALLOCZ(sizeof(ses_cache_t)); 2795 2796 if (enc->enc_private == NULL 2797 || enc->enc_cache.private == NULL 2798 || enc->enc_daemon_cache.private == NULL) { 2799 ENC_FREE_AND_NULL(enc->enc_private); 2800 ENC_FREE_AND_NULL(enc->enc_cache.private); 2801 ENC_FREE_AND_NULL(enc->enc_daemon_cache.private); 2802 return (ENOMEM); 2803 } 2804 2805 ses_softc = enc->enc_private; 2806 TAILQ_INIT(&ses_softc->ses_requests); 2807 TAILQ_INIT(&ses_softc->ses_pending_requests); 2808 2809 enc_update_request(enc, SES_UPDATE_PAGES); 2810 2811 // XXX: Move this to the FSM so it doesn't hang init 2812 if (0) (void) ses_set_timed_completion(enc, 1); 2813 2814 return (0); 2815} 2816 2817