xref: /freebsd/sys/dev/isci/scil/scif_sas_domain.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0
3  *
4  * This file is provided under a dual BSD/GPLv2 license.  When using or
5  * redistributing this file, you may do so under either license.
6  *
7  * GPL LICENSE SUMMARY
8  *
9  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of version 2 of the GNU General Public License as
13  * published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
23  * The full GNU General Public License is included in this distribution
24  * in the file called LICENSE.GPL.
25  *
26  * BSD LICENSE
27  *
28  * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions
33  * are met:
34  *
35  *   * Redistributions of source code must retain the above copyright
36  *     notice, this list of conditions and the following disclaimer.
37  *   * Redistributions in binary form must reproduce the above copyright
38  *     notice, this list of conditions and the following disclaimer in
39  *     the documentation and/or other materials provided with the
40  *     distribution.
41  *
42  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
43  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
44  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
45  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
46  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
47  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
48  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
49  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
50  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
51  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
52  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
53  */
54 
55 #include <sys/cdefs.h>
56 /**
57  * @file
58  *
59  * @brief This file contains the implementation of the SCIF_SAS_DOMAIN
60  *        object.
61  */
62 
63 #include <dev/isci/scil/intel_sas.h>
64 #include <dev/isci/scil/sci_fast_list.h>
65 #include <dev/isci/scil/scic_controller.h>
66 #include <dev/isci/scil/scic_port.h>
67 #include <dev/isci/scil/scic_remote_device.h>
68 #include <dev/isci/scil/scic_io_request.h>
69 #include <dev/isci/scil/scic_user_callback.h>
70 #include <dev/isci/scil/scif_user_callback.h>
71 #include <dev/isci/scil/sci_abstract_list.h>
72 #include <dev/isci/scil/sci_base_iterator.h>
73 
74 #include <dev/isci/scil/scif_sas_logger.h>
75 #include <dev/isci/scil/scif_sas_domain.h>
76 #include <dev/isci/scil/scif_sas_controller.h>
77 #include <dev/isci/scil/scif_sas_remote_device.h>
78 #include <dev/isci/scil/scif_sas_smp_remote_device.h>
79 #include <dev/isci/scil/sci_util.h>
80 
81 //******************************************************************************
82 //* P R I V A T E   M E T H O D S
83 //******************************************************************************
84 
85 /**
86  * @brief This method will attempt to handle an operation timeout (i.e.
87  *        discovery or reset).
88  *
89  * @param[in]  cookie This parameter specifies the domain in which the
90  *             timeout occurred.
91  *
92  * @return none
93  */
94 static
95 void scif_sas_domain_operation_timeout_handler(
96    void * cookie
97 )
98 {
99    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*) cookie;
100    U32                 state;
101 
102    state = sci_base_state_machine_get_state(&fw_domain->parent.state_machine);
103 
104    // Based upon the state of the domain, we know whether we were in the
105    // process of performing discovery or a reset.
106    if (state == SCI_BASE_DOMAIN_STATE_DISCOVERING)
107    {
108       SCIF_LOG_WARNING((
109          sci_base_object_get_logger(fw_domain),
110          SCIF_LOG_OBJECT_DOMAIN,
111          "Domain:0x%x State:0x%x DISCOVER timeout!\n",
112          fw_domain, state
113       ));
114 
115       fw_domain->operation.status = SCI_FAILURE_TIMEOUT;
116 
117       //search all the smp devices in the domain and cancel their activities
118       //if there is any outstanding activity remained. The smp devices will terminate
119       //all the started internal IOs.
120       scif_sas_domain_cancel_smp_activities(fw_domain);
121 
122       scif_sas_domain_continue_discover(fw_domain);
123    }
124    else
125    {
126       SCIF_LOG_ERROR((
127          sci_base_object_get_logger(fw_domain),
128          SCIF_LOG_OBJECT_DOMAIN,
129          "Domain:0x%x State:0x%x operation timeout in invalid state\n",
130          fw_domain, state
131       ));
132    }
133 }
134 
135 //******************************************************************************
136 //* P U B L I C   M E T H O D S
137 //******************************************************************************
138 
139 SCI_PORT_HANDLE_T scif_domain_get_scic_port_handle(
140    SCI_DOMAIN_HANDLE_T  domain
141 )
142 {
143    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*) domain;
144 
145    if ( (fw_domain == NULL) || (fw_domain->core_object == SCI_INVALID_HANDLE) )
146       return SCI_INVALID_HANDLE;
147 
148    SCIF_LOG_WARNING((
149       sci_base_object_get_logger(fw_domain),
150       SCIF_LOG_OBJECT_DOMAIN,
151       "Domain:0x%x no associated core port found\n",
152       fw_domain
153    ));
154 
155    return fw_domain->core_object;
156 }
157 
158 // ---------------------------------------------------------------------------
159 
160 SCI_REMOTE_DEVICE_HANDLE_T scif_domain_get_device_by_sas_address(
161    SCI_DOMAIN_HANDLE_T   domain,
162    SCI_SAS_ADDRESS_T   * sas_address
163 )
164 {
165    SCIF_SAS_DOMAIN_T        * fw_domain = (SCIF_SAS_DOMAIN_T*) domain;
166    SCI_ABSTRACT_ELEMENT_T   * element   = sci_abstract_list_get_front(
167                                              &fw_domain->remote_device_list
168                                           );
169    SCIF_SAS_REMOTE_DEVICE_T * fw_device;
170    SCI_SAS_ADDRESS_T          fw_device_address;
171 
172    SCIF_LOG_TRACE((
173       sci_base_object_get_logger(domain),
174       SCIF_LOG_OBJECT_DOMAIN,
175       "scif_domain_get_device_by_sas_address(0x%x, 0x%x) enter\n",
176       domain, sas_address
177    ));
178 
179    // Search the abstract list to see if there is a remote device with the
180    // same SAS address.
181    while (element != NULL)
182    {
183       fw_device = (SCIF_SAS_REMOTE_DEVICE_T*)
184                   sci_abstract_list_get_object(element);
185 
186       scic_remote_device_get_sas_address(
187          fw_device->core_object, &fw_device_address
188       );
189 
190       // Check to see if this is the device for which we are searching.
191       if (  (fw_device_address.low == sas_address->low)
192          && (fw_device_address.high == sas_address->high) )
193       {
194          return fw_device;
195       }
196 
197       element = sci_abstract_list_get_next(element);
198    }
199 
200    return SCI_INVALID_HANDLE;
201 }
202 
203 // ---------------------------------------------------------------------------
204 
205 #if !defined(DISABLE_SCI_ITERATORS)
206 
207 SCI_ITERATOR_HANDLE_T scif_domain_get_remote_device_iterator(
208    SCI_DOMAIN_HANDLE_T   domain,
209    void                * iterator_buffer
210 )
211 {
212    SCI_ITERATOR_HANDLE_T iterator = (SCI_ITERATOR_HANDLE_T *)iterator_buffer;
213 
214    sci_base_iterator_construct(
215       iterator, &((SCIF_SAS_DOMAIN_T*) domain)->remote_device_list
216    );
217 
218 
219    return iterator;
220 }
221 
222 #endif // !defined(DISABLE_SCI_ITERATORS)
223 
224 // ---------------------------------------------------------------------------
225 
226 SCI_STATUS scif_domain_discover(
227    SCI_DOMAIN_HANDLE_T   domain,
228    U32                   discover_timeout,
229    U32                   device_timeout
230 )
231 {
232    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*) domain;
233    SCI_STATUS          status    = SCI_SUCCESS;
234    SCI_STATUS          op_status = SCI_SUCCESS;
235 
236    SCIF_LOG_TRACE((
237       sci_base_object_get_logger(domain),
238       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
239       "scif_domain_discover(0x%x, 0x%x, 0x%x) enter\n",
240       domain, discover_timeout, device_timeout
241    ));
242 
243    // Check to make sure the size of the domain doesn't cause potential issues
244    // with the remote device timer and the domain timer.
245    if ((device_timeout * sci_abstract_list_size(&fw_domain->remote_device_list))
246         > discover_timeout)
247       status = SCI_WARNING_TIMER_CONFLICT;
248 
249    op_status = fw_domain->state_handlers->discover_handler(
250                   &fw_domain->parent, discover_timeout, device_timeout
251                );
252 
253    // The status of the discover operation takes priority.
254    if (  (status == SCI_SUCCESS)
255       || (status != SCI_SUCCESS && op_status != SCI_SUCCESS) )
256    {
257       status = op_status;
258    }
259 
260    return status;
261 }
262 
263 // ---------------------------------------------------------------------------
264 
265 U32 scif_domain_get_suggested_discover_timeout(
266    SCI_DOMAIN_HANDLE_T   domain
267 )
268 {
269    U32 suggested_timeout = SCIF_DOMAIN_DISCOVER_TIMEOUT; //milli-seconds
270    return suggested_timeout;
271 }
272 
273 // ---------------------------------------------------------------------------
274 
275 void scic_cb_port_stop_complete(
276    SCI_CONTROLLER_HANDLE_T  controller,
277    SCI_PORT_HANDLE_T        port,
278    SCI_STATUS               completion_status
279 )
280 {
281    SCIF_LOG_TRACE((
282       sci_base_object_get_logger((SCIF_SAS_DOMAIN_T*)sci_object_get_association(port)),
283       SCIF_LOG_OBJECT_DOMAIN,
284       "scic_cb_port_stop_complete(0x%x, 0x%x, 0x%x) enter\n",
285       controller, port, completion_status
286    ));
287 }
288 
289 // ---------------------------------------------------------------------------
290 
291 void scic_cb_port_ready(
292    SCI_CONTROLLER_HANDLE_T  controller,
293    SCI_PORT_HANDLE_T        port
294 )
295 {
296    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*)
297                                    sci_object_get_association(port);
298 
299    SCIF_LOG_TRACE((
300       sci_base_object_get_logger(fw_domain),
301       SCIF_LOG_OBJECT_DOMAIN,
302       "scic_cb_port_ready(0x%x, 0x%x) enter\n",
303       controller, port
304    ));
305 
306    // The controller supplied with the port should match the controller
307    // saved in the domain.
308    ASSERT(sci_object_get_association(controller) == fw_domain->controller);
309 
310    fw_domain->is_port_ready = TRUE;
311 
312    fw_domain->state_handlers->port_ready_handler(&fw_domain->parent);
313 }
314 
315 // ---------------------------------------------------------------------------
316 
317 void scic_cb_port_not_ready(
318    SCI_CONTROLLER_HANDLE_T  controller,
319    SCI_PORT_HANDLE_T        port,
320    U32                      reason_code
321 )
322 {
323    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*)
324                                    sci_object_get_association(port);
325 
326    SCIF_LOG_TRACE((
327       sci_base_object_get_logger(fw_domain),
328       SCIF_LOG_OBJECT_DOMAIN,
329       "scic_cb_port_not_ready(0x%x, 0x%x) enter\n",
330       controller, port
331    ));
332 
333    // The controller supplied with the port should match the controller
334    // saved in the domain.
335    ASSERT(sci_object_get_association(controller) == fw_domain->controller);
336 
337    // There is no need to take action on the port reconfiguring since it is
338    // just a change of the port width.
339    if (reason_code != SCIC_PORT_NOT_READY_RECONFIGURING)
340    {
341       fw_domain->is_port_ready = FALSE;
342 
343       fw_domain->state_handlers->port_not_ready_handler(
344                                     &fw_domain->parent, reason_code);
345    }
346 }
347 
348 // ---------------------------------------------------------------------------
349 
350 void scic_cb_port_hard_reset_complete(
351    SCI_CONTROLLER_HANDLE_T  controller,
352    SCI_PORT_HANDLE_T        port,
353    SCI_STATUS               completion_status
354 )
355 {
356    SCIF_SAS_DOMAIN_T        * fw_domain = (SCIF_SAS_DOMAIN_T*)
357                                    sci_object_get_association(port);
358    SCIF_SAS_REMOTE_DEVICE_T * fw_device;
359    SCI_FAST_LIST_ELEMENT_T  * element = fw_domain->request_list.list_head;
360    SCIF_SAS_TASK_REQUEST_T  * task_request = NULL;
361 
362    SCIF_LOG_TRACE((
363       sci_base_object_get_logger(fw_domain),
364       SCIF_LOG_OBJECT_DOMAIN,
365       "scic_cb_port_hard_reset_complete(0x%x, 0x%x, 0x%x) enter\n",
366       controller, port, completion_status
367    ));
368 
369    while (element != NULL)
370    {
371       task_request = (SCIF_SAS_TASK_REQUEST_T*) sci_fast_list_get_object(element);
372       element = sci_fast_list_get_next(element);
373 
374       if (scif_sas_task_request_get_function(task_request)
375              == SCI_SAS_HARD_RESET)
376       {
377          fw_device = task_request->parent.device;
378 
379          if (fw_device->domain == fw_domain)
380          {
381             scic_remote_device_reset_complete(fw_device->core_object);
382 
383             scif_cb_task_request_complete(
384                sci_object_get_association(controller),
385                fw_device,
386                task_request,
387                (SCI_TASK_STATUS) completion_status
388             );
389 
390             break;
391          }
392       }
393    }
394 }
395 
396 // ---------------------------------------------------------------------------
397 
398 void scic_cb_port_bc_change_primitive_recieved(
399    SCI_CONTROLLER_HANDLE_T  controller,
400    SCI_PORT_HANDLE_T        port,
401    SCI_PHY_HANDLE_T         phy
402 )
403 {
404    SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T*)
405                                    sci_object_get_association(port);
406 
407    SCIF_SAS_CONTROLLER_T * fw_controller = (SCIF_SAS_CONTROLLER_T *)
408                                            sci_object_get_association(controller);
409 
410    SCIF_LOG_TRACE((
411       sci_base_object_get_logger(fw_domain),
412       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
413       "scic_cb_port_bc_change_primitive_recieved(0x%x, 0x%x, 0x%x) enter\n",
414       controller, port, phy
415    ));
416 
417    if (fw_domain->broadcast_change_count == 0)
418    {  // Enable the BCN detection only if the bcn_count is zero. If bcn_count is
419       // not zero at this time, we won't enable BCN detection since all non-zero
420       // BCN_count means same to us. Furthermore, we avoid BCN storm by not
421       // always enabling the BCN_detection.
422       scic_port_enable_broadcast_change_notification(fw_domain->core_object);
423    }
424 
425    fw_domain->broadcast_change_count++;
426 
427    //if there is smp device on this domain that is in the middle of discover
428    //process or smp target reset, don't notify the driver layer.
429    if( ! scif_sas_domain_is_in_smp_activity(fw_domain) )
430       // Notify the user that there is, potentially, a change to the domain.
431       scif_cb_domain_change_notification(fw_controller, fw_domain);
432 }
433 
434 // ---------------------------------------------------------------------------
435 
436 void scic_cb_port_bc_ses_primitive_recieved(
437    SCI_CONTROLLER_HANDLE_T  controller,
438    SCI_PORT_HANDLE_T        port,
439    SCI_PHY_HANDLE_T         phy
440 )
441 {
442    SCIF_LOG_TRACE((
443       sci_base_object_get_logger(sci_object_get_association(port)),
444       SCIF_LOG_OBJECT_DOMAIN,
445       "scic_cb_port_bc_ses_primitive_received(0x%x, 0x%x, 0x%x) enter\n",
446       controller, port, phy
447    ));
448 }
449 
450 // ---------------------------------------------------------------------------
451 
452 void scic_cb_port_bc_expander_primitive_recieved(
453    SCI_CONTROLLER_HANDLE_T  controller,
454    SCI_PORT_HANDLE_T        port,
455    SCI_PHY_HANDLE_T         phy
456 )
457 {
458    SCIF_LOG_TRACE((
459       sci_base_object_get_logger(sci_object_get_association(port)),
460       SCIF_LOG_OBJECT_DOMAIN,
461       "scic_cb_port_bc_expander_primitive_received(0x%x, 0x%x, 0x%x) enter\n",
462       controller, port, phy
463    ));
464 }
465 
466 // ---------------------------------------------------------------------------
467 
468 void scic_cb_port_bc_aen_primitive_recieved(
469    SCI_CONTROLLER_HANDLE_T  controller,
470    SCI_PORT_HANDLE_T        port,
471    SCI_PHY_HANDLE_T         phy
472 )
473 {
474    SCIF_LOG_TRACE((
475       sci_base_object_get_logger(sci_object_get_association(port)),
476       SCIF_LOG_OBJECT_DOMAIN,
477       "scic_cb_port_bc_aen_primitive_received(0x%x, 0x%x, 0x%x) enter\n",
478       controller, port, phy
479    ));
480 }
481 
482 // ---------------------------------------------------------------------------
483 
484 void scic_cb_port_link_up(
485    SCI_CONTROLLER_HANDLE_T  controller,
486    SCI_PORT_HANDLE_T        port,
487    SCI_PHY_HANDLE_T         phy
488 )
489 {
490    SCIF_SAS_DOMAIN_T        * fw_domain = (SCIF_SAS_DOMAIN_T*)
491                                  sci_object_get_association(port);
492 
493    SCIF_LOG_TRACE((
494       sci_base_object_get_logger(sci_object_get_association(port)),
495       SCIF_LOG_OBJECT_DOMAIN,
496       "scic_cb_port_link_up(0x%x, 0x%x, 0x%x) enter\n",
497       controller, port, phy
498    ));
499 
500    scif_sas_domain_update_device_port_width(fw_domain, port);
501 }
502 
503 // ---------------------------------------------------------------------------
504 
505 void scic_cb_port_link_down(
506    SCI_CONTROLLER_HANDLE_T  controller,
507    SCI_PORT_HANDLE_T        port,
508    SCI_PHY_HANDLE_T         phy
509 )
510 {
511    SCIF_SAS_DOMAIN_T        * fw_domain = (SCIF_SAS_DOMAIN_T*)
512                                  sci_object_get_association(port);
513 
514    SCIF_LOG_TRACE((
515       sci_base_object_get_logger(sci_object_get_association(port)),
516       SCIF_LOG_OBJECT_DOMAIN,
517       "scic_cb_port_link_down(0x%x, 0x%x, 0x%x) enter\n",
518       controller, port, phy
519    ));
520 
521    scif_sas_domain_update_device_port_width(fw_domain, port);
522 }
523 
524 //******************************************************************************
525 //* P R O T E C T E D   M E T H O D S
526 //******************************************************************************
527 
528 /**
529  * @brief This method constructs the framework's SAS domain object.  During
530  *        the construction process a linkage to the corresponding core port
531  *        object.
532  *
533  * @param[in]  domain This parameter specifies the domain object to be
534  *             constructed.
535  * @param[in]  domain_id This parameter specifies the ID for the domain
536  *             object.
537  * @param[in]  fw_controller This parameter specifies the controller managing
538  *             the domain being constructed.
539  *
540  * @return none
541  */
542 void scif_sas_domain_construct(
543    SCIF_SAS_DOMAIN_T     * fw_domain,
544    U8                      domain_id,
545    SCIF_SAS_CONTROLLER_T * fw_controller
546 )
547 {
548    SCIF_LOG_TRACE((
549       sci_base_object_get_logger(fw_controller),
550       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_INITIALIZATION,
551       "scif_sas_domain_construct(0x%x, 0x%x, 0x%x) enter\n",
552       fw_domain, domain_id, fw_controller
553    ));
554 
555    sci_base_domain_construct(
556       &fw_domain->parent,
557       sci_base_object_get_logger(fw_controller),
558       scif_sas_domain_state_table
559    );
560 
561    scif_sas_domain_initialize_state_logging(fw_domain);
562 
563    sci_abstract_list_construct(
564       &fw_domain->remote_device_list, &fw_controller->free_remote_device_pool
565    );
566 
567    // Retrieve the core's port object that directly corresponds to this
568    // domain.
569    scic_controller_get_port_handle(
570       fw_controller->core_object, domain_id, &fw_domain->core_object
571    );
572 
573    // Set the association in the core port to this framework domain object.
574    sci_object_set_association(
575       (SCI_OBJECT_HANDLE_T) fw_domain->core_object, fw_domain
576    );
577 
578    sci_fast_list_init(&fw_domain->request_list);
579 
580    fw_domain->operation.timer = NULL;
581 
582    fw_domain->is_port_ready      = FALSE;
583    fw_domain->device_start_count = 0;
584    fw_domain->controller         = fw_controller;
585    fw_domain->operation.status   = SCI_SUCCESS;
586    fw_domain->is_config_route_table_needed = FALSE;
587 }
588 
589 /**
590  * @brief This method will terminate the requests outstanding in the core
591  *        based on the supplied criteria.
592  *        - if the all three parameters are specified then only the single
593  *          SCIF_SAS_REQUEST object is terminated.
594  *        - if only the SCIF_SAS_DOMAIN and SCIF_SAS_REMOTE_DEVICE are
595  *          specified, then all SCIF_SAS_REQUEST objects outstanding at
596  *          the device are terminated.  The one exclusion to this rule is
597  *          that the fw_requestor is not terminated.
598  *        - if only the SCIF_SAS_DOMAIN object is specified, then all
599  *          SCIF_SAS_REQUEST objects outstanding in the domain are
600  *          terminated.
601  *
602  * @param[in]  fw_domain This parameter specifies the domain in which to
603  *             terminate requests.
604  * @param[in]  fw_device This parameter specifies the remote device in
605  *             which to terminate requests.  This parameter can be NULL
606  *             as long as the fw_request parameter is NULL.  It is a
607  *             required parameter if the fw_request parameter is not NULL.
608  * @param[in]  fw_request This parameter specifies the request object to
609  *             be terminated.  This parameter can be NULL.
610  * @param[in]  fw_requestor This parameter specifies the task management
611  *             request that is responsible for the termination of requests.
612  *
613  * @return none
614  */
615 void scif_sas_domain_terminate_requests(
616    SCIF_SAS_DOMAIN_T        * fw_domain,
617    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
618    SCIF_SAS_REQUEST_T       * fw_request,
619    SCIF_SAS_TASK_REQUEST_T  * fw_requestor
620 )
621 {
622    SCIF_LOG_TRACE((
623       sci_base_object_get_logger(fw_domain),
624       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_TASK_MANAGEMENT,
625       "scif_sas_domain_terminate_requests(0x%x, 0x%x, 0x%x, 0x%x) enter\n",
626       fw_domain, fw_device, fw_request, fw_requestor
627    ));
628 
629    if (fw_request != NULL)
630    {
631       fw_request->terminate_requestor = fw_requestor;
632       fw_request->state_handlers->abort_handler(&fw_request->parent);
633    }
634    else
635    {
636       SCI_FAST_LIST_ELEMENT_T * element = fw_domain->request_list.list_head;
637       SCIF_SAS_REQUEST_T      * request = NULL;
638 
639       // Cycle through the fast list of IO requests.  Terminate each
640       // outstanding requests that matches the criteria supplied by the
641       // caller.
642       while (element != NULL)
643       {
644          request = (SCIF_SAS_REQUEST_T*) sci_fast_list_get_object(element);
645          // The current element may be deleted from the list because of
646          // IO completion so advance to the next element early
647          element = sci_fast_list_get_next(element);
648 
649          // Ensure we pass the supplied criteria before terminating the
650          // request.
651          if (
652                (fw_device == NULL)
653             || (
654                   (request->device == fw_device)
655                && (fw_requestor != (SCIF_SAS_TASK_REQUEST_T*) request)
656                )
657             )
658          {
659             if (
660                   (request->is_waiting_for_abort_task_set == FALSE) ||
661                   (request->terminate_requestor == NULL)
662                )
663             {
664                request->terminate_requestor = fw_requestor;
665                request->state_handlers->abort_handler(&request->parent);
666             }
667          }
668       }
669    }
670 }
671 
672 /**
673  * @brief This method searches the domain object to find a
674  *        SCIF_SAS_REQUEST object associated with the supplied IO tag.
675  *
676  * @param[in]  fw_domain This parameter specifies the domain in which to
677  *             to find the request object.
678  * @param[in]  io_tag This parameter specifies the IO tag value for which
679  *             to locate the corresponding request.
680  *
681  * @return This method returns a pointer to the SCIF_SAS_REQUEST object
682  *         associated with the supplied IO tag.
683  * @retval NULL This value is returned if the IO tag does not resolve to
684  *         a request.
685  */
686 SCIF_SAS_REQUEST_T * scif_sas_domain_get_request_by_io_tag(
687    SCIF_SAS_DOMAIN_T * fw_domain,
688    U16                 io_tag
689 )
690 {
691    SCI_FAST_LIST_ELEMENT_T * element    = fw_domain->request_list.list_head;
692    SCIF_SAS_IO_REQUEST_T   * io_request = NULL;
693 
694    SCIF_LOG_TRACE((
695       sci_base_object_get_logger(fw_domain),
696       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_TASK_MANAGEMENT,
697       "scif_sas_domain_get_request_by_io_tag(0x%x, 0x%x) enter\n",
698       fw_domain, io_tag
699    ));
700 
701    while (element != NULL)
702    {
703       io_request = (SCIF_SAS_IO_REQUEST_T*) sci_fast_list_get_object(element);
704 
705       // Check to see if we located the request with an identical IO tag.
706       if (scic_io_request_get_io_tag(io_request->parent.core_object) == io_tag)
707          return &io_request->parent;
708 
709       element = sci_fast_list_get_next(element);
710    }
711 
712    return NULL;
713 }
714 
715 /**
716  * @brief This method performs domain object initialization to be done
717  *        when the scif_controller_initialize() method is invoked.
718  *        This includes operation timeout creation.
719  *
720  * @param[in]  fw_domain This parameter specifies the domain object for
721  *             which to perform initialization.
722  *
723  * @return none
724  */
725 void scif_sas_domain_initialize(
726    SCIF_SAS_DOMAIN_T * fw_domain
727 )
728 {
729    SCIF_LOG_TRACE((
730       sci_base_object_get_logger(fw_domain),
731       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_INITIALIZATION,
732       "scif_sas_domain_initialize(0x%x) enter\n",
733       fw_domain
734    ));
735 
736    // Create the timer for each domain.  It is too early in the process
737    // to allocate this during construction since the user didn't have
738    // a chance to set it's association.
739    if (fw_domain->operation.timer == 0)
740    {
741       fw_domain->operation.timer = scif_cb_timer_create(
742                                       fw_domain->controller,
743                                       scif_sas_domain_operation_timeout_handler,
744                                       fw_domain
745                                    );
746    }
747 }
748 
749 /**
750  * @brief This method performs domain object handling for core remote
751  *        device start complete notifications.  Core remote device starts
752  *        and start completes are only done during discovery.  This could
753  *        ultimately be wrapped into a handler method on the domain (they
754  *        actually already exist).  This method will decrement the number
755  *        of device start operations ongoing and attempt to determine if
756  *        discovery is complete.
757  *
758  * @param[in]  fw_domain This parameter specifies the domain object for
759  *             which to perform initialization.
760  *
761  * @return none
762  */
763 void scif_sas_domain_remote_device_start_complete(
764    SCIF_SAS_DOMAIN_T        * fw_domain,
765    SCIF_SAS_REMOTE_DEVICE_T * fw_device
766 )
767 {
768    SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
769 
770    SCIF_LOG_TRACE((
771       sci_base_object_get_logger(fw_domain),
772       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
773       "scif_sas_domain_remote_device_start_complete(0x%x, 0x%x) enter\n",
774       fw_domain, fw_device
775    ));
776 
777    // If a device is being started/start completed, then we must be
778    // during discovery.
779    ASSERT(fw_domain->parent.state_machine.current_state_id
780           == SCI_BASE_DOMAIN_STATE_DISCOVERING);
781 
782    scic_remote_device_get_protocols(fw_device->core_object, &dev_protocols);
783 
784    // Decrement the number of devices being started and check to see
785    // if all have finished being started or failed as the case may be.
786    fw_domain->device_start_in_progress_count--;
787 
788    if ( dev_protocols.u.bits.attached_smp_target )
789    {
790       if ( fw_device->containing_device == NULL )
791          //kick off the smp discover process if this expander is direct attached.
792          scif_sas_smp_remote_device_start_discover(fw_device);
793       else
794          //mark this device, the discover process of this device will start after
795          //its containing smp device finish discover.
796          fw_device->protocol_device.smp_device.scheduled_activity =
797             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
798    }
799    else
800    {
801       fw_domain->state_handlers->device_start_complete_handler(
802          &fw_domain->parent, &fw_device->parent
803       );
804    }
805 }
806 
807 
808 /**
809  * @brief This methods check each smp device in this domain. If there is at
810  *        least one smp device in discover or target reset activity, this
811  *        domain is considered in smp activity. Note this routine is not
812  *        called on fast IO path.
813  *
814  * @param[in] fw_domain The framework domain object
815  *
816  * @return BOOL value to indicate whether a domain is in SMP activity.
817  */
818 BOOL scif_sas_domain_is_in_smp_activity(
819    SCIF_SAS_DOMAIN_T        * fw_domain
820 )
821 {
822    SCI_ABSTRACT_ELEMENT_T * current_element =
823       sci_abstract_list_get_front(&fw_domain->remote_device_list);
824 
825    SCIF_SAS_REMOTE_DEVICE_T * current_device;
826 
827    while ( current_element != NULL )
828    {
829       SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
830 
831       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
832                        sci_abstract_list_get_object(current_element);
833 
834       scic_remote_device_get_protocols(current_device->core_object,
835                                        &dev_protocols
836       );
837 
838       if (dev_protocols.u.bits.attached_smp_target &&
839           scif_sas_smp_remote_device_is_in_activity(current_device))
840          return TRUE;
841 
842       current_element =
843          sci_abstract_list_get_next(current_element);
844    }
845 
846    return FALSE;
847 }
848 
849 
850 /**
851  * @brief This methods finds a expander attached device by searching the domain's
852  *        device list using connected expander device and expander phy id.
853  *
854  * @param[in] fw_domain The framework domain object
855  * @param[in] parent_device The expander device the target device attaches to.
856  * @param[in] expander_phy_id The expander phy id that the target device owns.
857  *
858  * @return found remote device or a NULL value if no device found.
859  */
860 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_domain_get_device_by_containing_device(
861    SCIF_SAS_DOMAIN_T        * fw_domain,
862    SCIF_SAS_REMOTE_DEVICE_T * containing_device,
863    U8                         expander_phy_id
864 )
865 {
866    SCIF_SAS_REMOTE_DEVICE_T * fw_device;
867    SCI_ABSTRACT_ELEMENT_T * element = sci_abstract_list_get_front(
868                                          &fw_domain->remote_device_list
869                                       );
870 
871    //parent device must not be NULL.
872    ASSERT(containing_device != NULL);
873 
874    // Search the abstract list to see if there is a remote device meets the
875    // search condition.
876    while (element != NULL)
877    {
878       fw_device = (SCIF_SAS_REMOTE_DEVICE_T*)
879                   sci_abstract_list_get_object(element);
880 
881       // Check to see if this is the device for which we are searching.
882       if (
883             (fw_device->containing_device == containing_device)
884          && (fw_device->expander_phy_identifier == expander_phy_id)
885          )
886       {
887          return fw_device;
888       }
889 
890       element = sci_abstract_list_get_next(element);
891    }
892 
893    return SCI_INVALID_HANDLE;
894 }
895 
896 
897 /**
898  * @brief This methods finds the first device that is in STOPPED state and its
899  *        connection_rate is still in SPINUP_HOLD(value 3).
900  *
901  * @param[in] fw_domain The framework domain object
902  *
903  * @return SCIF_SAS_REMOTE_DEVICE_T The device that is in SPINUP_HOLD or NULL.
904  */
905 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_domain_find_device_in_spinup_hold(
906    SCIF_SAS_DOMAIN_T        * fw_domain
907 )
908 {
909    SCI_ABSTRACT_ELEMENT_T   * current_element;
910    SCIF_SAS_REMOTE_DEVICE_T * current_device;
911 
912    SCIF_LOG_TRACE((
913       sci_base_object_get_logger(fw_domain),
914       SCIF_LOG_OBJECT_DOMAIN,
915       "scif_sas_domain_find_device_in_spinup_hold(0x%x) enter\n",
916       fw_domain
917    ));
918 
919    //search throught domain's device list to find the first sata device on spinup_hold
920    current_element = sci_abstract_list_get_front(&fw_domain->remote_device_list);
921    while (current_element != NULL )
922    {
923       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
924                        sci_abstract_list_get_object(current_element);
925 
926       //We must get the next element before we remove the current
927       //device. Or else, we will get wrong next_element, since the erased
928       //element has been put into free pool.
929       current_element = sci_abstract_list_get_next(current_element);
930 
931       if ( sci_base_state_machine_get_state(&current_device->parent.state_machine) ==
932               SCI_BASE_REMOTE_DEVICE_STATE_STOPPED
933           && scic_remote_device_get_connection_rate(current_device->core_object) ==
934                 SCI_SATA_SPINUP_HOLD )
935       {
936          return current_device;
937       }
938    }
939 
940    return NULL;
941 }
942 
943 
944 /**
945  * @brief This methods finds the first device that has specific activity scheduled.
946  *
947  * @param[in] fw_domain The framework domain object
948  * @param[in] smp_activity A specified smp activity. The valid range is [1,5].
949  *
950  * @return SCIF_SAS_REMOTE_DEVICE_T The device that has specified smp activity scheduled.
951  */
952 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_domain_find_device_has_scheduled_activity(
953    SCIF_SAS_DOMAIN_T        * fw_domain,
954    U8                         smp_activity
955 )
956 {
957    SCI_ABSTRACT_ELEMENT_T * current_element =
958       sci_abstract_list_get_front(&fw_domain->remote_device_list);
959 
960    SCIF_SAS_REMOTE_DEVICE_T * current_device;
961    SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
962 
963    //config route table activity has higher priority than discover activity.
964    while ( current_element != NULL )
965    {
966       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
967                        sci_abstract_list_get_object(current_element);
968 
969       scic_remote_device_get_protocols(current_device->core_object,
970                                        &dev_protocols);
971 
972       current_element =
973          sci_abstract_list_get_next(current_element);
974 
975       if ( dev_protocols.u.bits.attached_smp_target
976           && current_device->protocol_device.smp_device.scheduled_activity ==
977                 smp_activity)
978       {
979          return current_device;
980       }
981    }
982 
983    return NULL;
984 }
985 
986 
987 /**
988  * @brief This methods finds the smp device that has is_config_route_table_scheduled
989  *        flag set to TRUE, and start config route table on it. If there is no
990  *        smp device scheduled to config route table, find the smp device has
991  *        is_discover_scheduled and start the smp discover process on them.
992  *
993  * @param[in] fw_domain The framework domain that to start smp discover process.
994  *
995  * @return NONE
996  */
997 void scif_sas_domain_start_smp_activity(
998   SCIF_SAS_DOMAIN_T        * fw_domain
999 )
1000 {
1001    SCIF_SAS_REMOTE_DEVICE_T * device_has_scheduled_activity = NULL;
1002 
1003    //first, find device that has config route table activity scheduled.
1004    //config route table activity has higher priority than Discover.
1005    device_has_scheduled_activity =
1006       scif_sas_domain_find_device_has_scheduled_activity(
1007          fw_domain,
1008          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE
1009       );
1010 
1011    if (device_has_scheduled_activity != NULL)
1012    {
1013       scif_sas_smp_remote_device_configure_route_table(device_has_scheduled_activity);
1014       device_has_scheduled_activity->protocol_device.smp_device.scheduled_activity =
1015          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
1016       return;
1017    }
1018 
1019    //if no device has config route table activity scheduled, search again, find
1020    //device has discover activity scheduled.
1021    device_has_scheduled_activity =
1022       scif_sas_domain_find_device_has_scheduled_activity(
1023          fw_domain,
1024          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER
1025       );
1026 
1027    if (device_has_scheduled_activity != NULL)
1028       scif_sas_smp_remote_device_start_discover(device_has_scheduled_activity);
1029 }
1030 
1031 
1032 /**
1033  * @brief This method starts domain's smp discover process from the top level expander.
1034  *
1035  * @param[in] fw_domain The framework domain that to start smp discover process.
1036  @ @param[in] top_expander The top level expander device to start smp discover process.
1037  *
1038  * @return None
1039  */
1040 void scif_sas_domain_start_smp_discover(
1041    SCIF_SAS_DOMAIN_T        * fw_domain,
1042    SCIF_SAS_REMOTE_DEVICE_T * top_expander
1043 )
1044 {
1045    SCI_ABSTRACT_ELEMENT_T * current_element =
1046        sci_abstract_list_get_front(&fw_domain->remote_device_list);
1047 
1048    SCIF_SAS_REMOTE_DEVICE_T * current_device;
1049 
1050    // something changed behind expander
1051    // mark all the device behind expander to be NOT
1052    // is_currently_discovered.
1053    while ( current_element != NULL )
1054    {
1055       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1056                            sci_abstract_list_get_object(current_element);
1057 
1058       current_device->is_currently_discovered = FALSE;
1059 
1060       //reset all the devices' port witdh except the top expander.
1061       if (current_device->containing_device != NULL)
1062          current_device->device_port_width = 1;
1063 
1064       current_element = sci_abstract_list_get_next(current_element);
1065    }
1066 
1067    //expander device itself should be set to is_currently_discovered.
1068    top_expander->is_currently_discovered = TRUE;
1069 
1070    //kick off the smp discover process.
1071    scif_sas_smp_remote_device_start_discover(top_expander);
1072 }
1073 
1074 
1075 /**
1076  * @brief This method continues domain's smp discover process and
1077  *        may transit to READY state if all smp activities are done.
1078  *
1079  * @param[in] fw_domain The framework domain that to start smp discover process.
1080  *
1081  * @return None
1082  */
1083 void scif_sas_domain_continue_discover(
1084    SCIF_SAS_DOMAIN_T * fw_domain
1085 )
1086 {
1087    SCIF_LOG_TRACE((
1088       sci_base_object_get_logger(fw_domain),
1089       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1090       "scif_sas_domain_continue_discover(0x%x) enter\n",
1091       fw_domain
1092    ));
1093 
1094    if ( fw_domain->device_start_in_progress_count == 0
1095        && !scif_sas_domain_is_in_smp_activity(fw_domain) )
1096    {
1097       //domain scrub the remote device list to see if there is a need
1098       //to start smp discover on expander device. There may be no
1099       //need to start any smp discover.
1100       scif_sas_domain_start_smp_activity(fw_domain);
1101 
1102       //In domain discovery timeout case, we cancel all
1103       //the smp activities, and terminate all the smp requests, then
1104       //this routine is called. But the smp request may not done
1105       //terminated. We want to guard the domain trasitting to READY
1106       //by checking outstanding smp request count. If there is outstanding
1107       //smp request, the domain will not transit to READY. Later when
1108       //the smp request is terminated at smp remote device, this routine
1109       //will be called then the domain will transit to READY state.
1110       if ( ! scif_sas_domain_is_in_smp_activity(fw_domain)
1111           && scif_sas_domain_get_smp_request_count(fw_domain) == 0)
1112       {
1113          //before domain transit to READY state, domain has some clean up
1114          //work to do, such like update domain's remote devcie list.
1115          scif_sas_domain_finish_discover(fw_domain);
1116       }
1117    }
1118 }
1119 
1120 
1121 /**
1122  * @brief This method finishes domain's smp discover process and
1123  *        update domain's remote device list.
1124  *
1125  * @param[in] fw_domain The framework domain that's to finish smp discover process.
1126  *
1127  * @return None
1128  */
1129 void scif_sas_domain_finish_discover(
1130    SCIF_SAS_DOMAIN_T * fw_domain
1131 )
1132 {
1133    SCIF_SAS_REMOTE_DEVICE_T * current_device = NULL;
1134    SCI_ABSTRACT_ELEMENT_T   * current_element = NULL;
1135 
1136    SCIF_LOG_TRACE((
1137       sci_base_object_get_logger(fw_domain),
1138       SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1139       "scif_sas_domain_finish_discover(0x%x) enter\n",
1140       fw_domain
1141    ));
1142 
1143    //need to scrub all the devices behind the expander. Check each
1144    //device's discover_status. if the is_currently_discovered is FALSE, means
1145    //the device is not been rediscovered. this device needs to be removed.
1146    current_element = sci_abstract_list_get_front(&fw_domain->remote_device_list);
1147    while (current_element != NULL )
1148    {
1149       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1150                           sci_abstract_list_get_object(current_element);
1151 
1152       //We must get the next element before we remove the current
1153       //device. Or else, we will get wrong next_element, since the erased
1154       //element has been put into free pool.
1155       current_element = sci_abstract_list_get_next(current_element);
1156 
1157       if ( current_device->is_currently_discovered == FALSE )
1158       {
1159          // Notify the framework user of the device removal.
1160          scif_cb_domain_device_removed(
1161             fw_domain->controller, fw_domain, current_device
1162          );
1163       }
1164    }
1165 
1166    sci_base_state_machine_change_state(
1167       &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_READY
1168    );
1169 }
1170 
1171 
1172 
1173 /**
1174  * @brief This method remove an expander device and its child devices, in order to
1175  *        deal with a detected illeagal phy connection.
1176  *
1177  * @param[in] fw_domain The domain that a expander belongs to.
1178  * @param[in] fw_device The expander device to be removed.
1179  *
1180  * @return none.
1181  */
1182 void scif_sas_domain_remove_expander_device(
1183    SCIF_SAS_DOMAIN_T        * fw_domain,
1184    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1185 )
1186 {
1187    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
1188       &fw_device->protocol_device.smp_device;
1189 
1190    SCI_FAST_LIST_ELEMENT_T     * element = smp_remote_device->smp_phy_list.list_head;
1191    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
1192    SCIF_SAS_REMOTE_DEVICE_T    * current_device = NULL;
1193 
1194    while (element != NULL)
1195    {
1196       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1197       element = sci_fast_list_get_next(element);
1198 
1199       if ( curr_smp_phy->attached_device_type != SMP_NO_DEVICE_ATTACHED
1200           && curr_smp_phy->u.end_device != NULL )
1201       {
1202          if (curr_smp_phy->attached_device_type == SMP_END_DEVICE_ONLY)
1203             current_device = curr_smp_phy->u.end_device;
1204          else
1205             current_device = curr_smp_phy->u.attached_phy->owning_device;
1206 
1207          scif_cb_domain_device_removed(fw_domain->controller, fw_domain, current_device);
1208       }
1209    }
1210 
1211    //remove device itself
1212    scif_cb_domain_device_removed(fw_domain->controller, fw_domain, fw_device);
1213 }
1214 
1215 
1216 /**
1217  * @brief This method searches the whole domain and finds all the smp devices to
1218  *        cancel their smp activities if there is any.
1219  *
1220  * @param[in] fw_domain The domain that its smp activities are to be canceled.
1221  *
1222  * @return none.
1223  */
1224 void scif_sas_domain_cancel_smp_activities(
1225    SCIF_SAS_DOMAIN_T * fw_domain
1226 )
1227 {
1228    SCI_ABSTRACT_ELEMENT_T * current_element =
1229       sci_abstract_list_get_front(&fw_domain->remote_device_list);
1230 
1231    SCIF_SAS_REMOTE_DEVICE_T * current_device;
1232 
1233    //purge all the outstanding internal IOs in HPQ.
1234    scif_sas_high_priority_request_queue_purge_domain(
1235       &fw_domain->controller->hprq, fw_domain
1236    );
1237 
1238    while ( current_element != NULL )
1239    {
1240       SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
1241 
1242       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1243                        sci_abstract_list_get_object(current_element);
1244 
1245       scic_remote_device_get_protocols(current_device->core_object,
1246                                        &dev_protocols
1247       );
1248 
1249       if (dev_protocols.u.bits.attached_smp_target)
1250       {
1251          scif_sas_smp_remote_device_cancel_smp_activity(current_device);
1252       }
1253 
1254       current_element =
1255          sci_abstract_list_get_next(current_element);
1256    }
1257 }
1258 
1259 
1260 /**
1261  * @brief This method searches the domain's request list and counts outstanding
1262  *           smp IOs.
1263  *
1264  * @param[in] fw_domain The domain that its request list is to be searched.
1265  *
1266  * @return U8 The possible return value of this routine is 0 or 1.
1267  */
1268 U8 scif_sas_domain_get_smp_request_count(
1269    SCIF_SAS_DOMAIN_T * fw_domain
1270 )
1271 {
1272    SCI_FAST_LIST_ELEMENT_T * element = fw_domain->request_list.list_head;
1273    SCIF_SAS_REQUEST_T      * request = NULL;
1274    U8                        count = 0;
1275    SCIC_TRANSPORT_PROTOCOL   protocol;
1276 
1277    // Cycle through the fast list of IO requests.  Terminate each
1278    // outstanding requests that matches the criteria supplied by the
1279    // caller.
1280    while (element != NULL)
1281    {
1282       request = (SCIF_SAS_REQUEST_T*) sci_fast_list_get_object(element);
1283       // The current element may be deleted from the list because of
1284       // IO completion so advance to the next element early
1285       element = sci_fast_list_get_next(element);
1286 
1287       protocol = scic_io_request_get_protocol(request->core_object);
1288 
1289       if ( protocol == SCIC_SMP_PROTOCOL)
1290          count++;
1291    }
1292 
1293    return count;
1294 }
1295 
1296 
1297 /**
1298  * @brief This method start clear affiliation activities for smp devices in
1299  *           this domain.
1300  *
1301  * @param[in] fw_domain The domain that its smp devices are scheduled to clear
1302  *                affiliation for all the EA SATA devices.
1303  *
1304  * @return none.
1305  */
1306 void scif_sas_domain_start_clear_affiliation(
1307    SCIF_SAS_DOMAIN_T * fw_domain
1308 )
1309 {
1310    scif_sas_domain_schedule_clear_affiliation(fw_domain);
1311    scif_sas_domain_continue_clear_affiliation(fw_domain);
1312 }
1313 
1314 
1315 /**
1316  * @brief This method schedule clear affiliation activities for smp devices in
1317  *           this domain.
1318  *
1319  * @param[in] fw_domain The domain that its smp devices are scheduled to clear
1320  *                affiliation for all the EA SATA devices.
1321  *
1322  * @return none.
1323  */
1324 void scif_sas_domain_schedule_clear_affiliation(
1325    SCIF_SAS_DOMAIN_T * fw_domain
1326 )
1327 {
1328    SCI_ABSTRACT_ELEMENT_T * current_element =
1329       sci_abstract_list_get_front(&fw_domain->remote_device_list);
1330 
1331    SCIF_SAS_REMOTE_DEVICE_T * current_device;
1332    SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
1333 
1334    //config route table activity has higher priority than discover activity.
1335    while ( current_element != NULL )
1336    {
1337       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1338                        sci_abstract_list_get_object(current_element);
1339 
1340       scic_remote_device_get_protocols(current_device->core_object,
1341                                        &dev_protocols);
1342 
1343       current_element =
1344          sci_abstract_list_get_next(current_element);
1345 
1346       if ( dev_protocols.u.bits.attached_smp_target )
1347       {
1348          current_device->protocol_device.smp_device.scheduled_activity =
1349             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION;
1350       }
1351    }
1352 }
1353 
1354 
1355 /**
1356  * @brief This method carries clear affiliation activities for a smp devices in
1357  *           this domain during controller stop process.
1358  *
1359  * @param[in] fw_domain The domain that its smp devices are to clear
1360  *                affiliation for all the EA SATA devices.
1361  *
1362  * @return none.
1363  */
1364 void scif_sas_domain_continue_clear_affiliation(
1365    SCIF_SAS_DOMAIN_T * fw_domain
1366 )
1367 {
1368    SCIF_SAS_REMOTE_DEVICE_T * smp_device =
1369       scif_sas_domain_find_device_has_scheduled_activity(
1370          fw_domain,
1371          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION
1372       );
1373 
1374    if (smp_device != NULL)
1375       scif_sas_smp_remote_device_start_clear_affiliation(smp_device);
1376    else
1377    {
1378       //This domain has done clear affiliation.
1379       SCIF_SAS_CONTROLLER_T * fw_controller = fw_domain->controller;
1380       fw_controller->current_domain_to_clear_affiliation++;
1381 
1382       //let controller continue to clear affiliation on other domains.
1383       scif_sas_controller_clear_affiliation(fw_domain->controller);
1384    }
1385 }
1386 
1387 
1388 /**
1389  * @brief This method releases resource for a framework domain.
1390  *
1391  * @param[in] fw_controller This parameter specifies the framework
1392  *            controller, its associated domain's resources are to be released.
1393  * @param[in] fw_domain This parameter specifies the framework
1394  *            domain whose resources are to be released.
1395  */
1396 void scif_sas_domain_release_resource(
1397    SCIF_SAS_CONTROLLER_T * fw_controller,
1398    SCIF_SAS_DOMAIN_T     * fw_domain
1399 )
1400 {
1401    if (fw_domain->operation.timer != NULL)
1402    {
1403       scif_cb_timer_destroy(fw_controller, fw_domain->operation.timer);
1404       fw_domain->operation.timer = NULL;
1405    }
1406 }
1407 
1408 
1409 /**
1410  * @brief This method finds the a EA device that has target reset scheduled.
1411  *
1412  * @param[in] fw_domain The framework domain object
1413  *
1414  * @return SCIF_SAS_REMOTE_DEVICE_T The EA device that has target reset scheduled.
1415  */
1416 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_domain_find_next_ea_target_reset(
1417    SCIF_SAS_DOMAIN_T     * fw_domain
1418 )
1419 {
1420    SCI_ABSTRACT_ELEMENT_T   * current_element;
1421    SCIF_SAS_REMOTE_DEVICE_T * current_device;
1422 
1423    SCIF_LOG_TRACE((
1424       sci_base_object_get_logger(fw_domain),
1425       SCIF_LOG_OBJECT_DOMAIN,
1426       "scif_sas_domain_find_next_ea_target_reset(0x%x) enter\n",
1427       fw_domain
1428    ));
1429 
1430    //search through domain's device list to find the first sata device on spinup_hold
1431    current_element = sci_abstract_list_get_front(&fw_domain->remote_device_list);
1432    while (current_element != NULL )
1433    {
1434       current_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1435                        sci_abstract_list_get_object(current_element);
1436 
1437       current_element = sci_abstract_list_get_next(current_element);
1438 
1439       if ( current_device->ea_target_reset_request_scheduled != NULL )
1440       {
1441          return current_device;
1442       }
1443    }
1444 
1445    return NULL;
1446 }
1447 
1448 #if !defined(DISABLE_WIDE_PORTED_TARGETS)
1449 /**
1450  * @brief This method update the direct attached device port width.
1451  *
1452  * @param[in] fw_domain The framework domain object
1453  * @param[in] port The associated port object which recently has link up/down
1454  *                 event happened.
1455  *
1456  * @return none
1457  */
1458 void scif_sas_domain_update_device_port_width(
1459    SCIF_SAS_DOMAIN_T * fw_domain,
1460    SCI_PORT_HANDLE_T   port
1461 )
1462 {
1463    SCIF_SAS_REMOTE_DEVICE_T * fw_device;
1464    SCIC_PORT_PROPERTIES_T     properties;
1465    U8                         new_port_width = 0;
1466 
1467    SCIF_LOG_TRACE((
1468       sci_base_object_get_logger(fw_domain),
1469       SCIF_LOG_OBJECT_DOMAIN,
1470       "scif_sas_domain_update_device_port_width(0x%x, 0x%x) enter\n",
1471       fw_domain, port
1472    ));
1473 
1474    scic_port_get_properties(port, &properties);
1475 
1476    fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1477                   scif_domain_get_device_by_sas_address(
1478                   fw_domain, &properties.remote.sas_address
1479                );
1480 
1481    // If the device already existed in the domain, it is a wide port SSP target,
1482    // we need to update its port width.
1483    if (fw_device != SCI_INVALID_HANDLE)
1484    {
1485       SMP_DISCOVER_RESPONSE_PROTOCOLS_T  dev_protocols;
1486       scic_remote_device_get_protocols(fw_device->core_object, &dev_protocols);
1487 
1488       if (dev_protocols.u.bits.attached_ssp_target)
1489       {
1490          //Get accurate port width from port's phy mask for a DA device.
1491          SCI_GET_BITS_SET_COUNT(properties.phy_mask, new_port_width);
1492 
1493          scif_sas_remote_device_update_port_width(fw_device, new_port_width);
1494       }
1495    }
1496 }
1497 #endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
1498 
1499 
1500 #ifdef SCI_LOGGING
1501 /**
1502  * This method will turn on logging of domain state changes.
1503  *
1504  * @param[in] fw_domain The domain for which the state logging is to be turned
1505  *       on.
1506  */
1507 void scif_sas_domain_initialize_state_logging(
1508    SCIF_SAS_DOMAIN_T *fw_domain
1509 )
1510 {
1511    sci_base_state_machine_logger_initialize(
1512       &fw_domain->parent.state_machine_logger,
1513       &fw_domain->parent.state_machine,
1514       &fw_domain->parent.parent,
1515       scif_cb_logger_log_states,
1516       "SCIF_SAS_DOMAIN_T", "base state machine",
1517       SCIF_LOG_OBJECT_DOMAIN
1518    );
1519 }
1520 
1521 /**
1522  * This method will turn off logging of domain state changes.
1523  *
1524  * @param[in] fw_domain The domain for which the state logging is to be turned
1525  *       off.
1526  */
1527 void scif_sas_domain_deinitialize_state_logging(
1528    SCIF_SAS_DOMAIN_T *fw_domain
1529 )
1530 {
1531    sci_base_state_machine_logger_deinitialize(
1532       &fw_domain->parent.state_machine_logger,
1533       &fw_domain->parent.state_machine
1534    );
1535 }
1536 #endif
1537