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