xref: /freebsd/sys/dev/isci/scil/scif_sas_smp_remote_device.c (revision 56e53cb8ef000c3ef72337a4095987a932cdedef)
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 methods for the SCIF_SAS_SMP_REMOTE_DEVICE object.
62  */
63 #include <dev/isci/scil/sci_controller.h>
64 #include <dev/isci/scil/scif_sas_controller.h>
65 #include <dev/isci/scil/scif_sas_remote_device.h>
66 #include <dev/isci/scil/scif_sas_logger.h>
67 
68 #include <dev/isci/scil/scif_sas_smp_remote_device.h>
69 #include <dev/isci/scil/scif_sas_smp_io_request.h>
70 #include <dev/isci/scil/intel_sas.h>
71 #include <dev/isci/scil/scic_io_request.h>
72 #include <dev/isci/scil/scic_remote_device.h>
73 #include <dev/isci/scil/scif_sas_smp_phy.h>
74 
75 
76 /**
77  * @brief This method resets all fields for a smp remote device. This is a
78  *        private method.
79  *
80  * @param[in] fw_device the framework SMP device that is being
81  *            constructed.
82  *
83  * @return none
84  */
85 void scif_sas_smp_remote_device_clear(
86    SCIF_SAS_REMOTE_DEVICE_T * fw_device
87 )
88 {
89    //reset all fields in smp_device, indicate that the smp device is not
90    //in discovery process.
91    fw_device->protocol_device.smp_device.current_activity =
92       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
93 
94    fw_device->protocol_device.smp_device.current_smp_request =
95       NOT_IN_SMP_ACTIVITY;
96 
97    fw_device->protocol_device.smp_device.current_activity_phy_index = 0;
98 
99    fw_device->protocol_device.smp_device.curr_config_route_index = 0;
100 
101    fw_device->protocol_device.smp_device.config_route_smp_phy_anchor = NULL;
102 
103    fw_device->protocol_device.smp_device.is_route_table_cleaned = FALSE;
104 
105    fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy = NULL;
106 
107    fw_device->protocol_device.smp_device.scheduled_activity =
108       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
109 
110    fw_device->protocol_device.smp_device.io_retry_count = 0;
111 
112    fw_device->protocol_device.smp_device.curr_clear_affiliation_phy = NULL;
113 
114    if (fw_device->protocol_device.smp_device.smp_activity_timer != NULL)
115    {
116       //stop the timer
117       scif_cb_timer_stop(
118          fw_device->domain->controller,
119          fw_device->protocol_device.smp_device.smp_activity_timer
120       );
121 
122       //destroy the timer
123       scif_cb_timer_destroy(
124          fw_device->domain->controller,
125          fw_device->protocol_device.smp_device.smp_activity_timer
126       );
127 
128       fw_device->protocol_device.smp_device.smp_activity_timer = NULL;
129    }
130 }
131 
132 
133 /**
134  * @brief This method intializes a smp remote device.
135  *
136  * @param[in] fw_device the framework SMP device that is being
137  *            constructed.
138  *
139  * @return none
140  */
141 void scif_sas_smp_remote_device_construct(
142    SCIF_SAS_REMOTE_DEVICE_T * fw_device
143 )
144 {
145    SCIF_LOG_TRACE((
146       sci_base_object_get_logger(fw_device),
147       SCIF_LOG_OBJECT_REMOTE_DEVICE,
148       "scif_sas_smp_remote_device_construct(0x%x) enter\n",
149       fw_device
150    ));
151 
152    fw_device->protocol_device.smp_device.number_of_phys = 0;
153    fw_device->protocol_device.smp_device.expander_route_indexes = 0;
154    fw_device->protocol_device.smp_device.is_table_to_table_supported = FALSE;
155    fw_device->protocol_device.smp_device.is_externally_configurable  = FALSE;
156    fw_device->protocol_device.smp_device.is_able_to_config_others    = FALSE;
157 
158    sci_fast_list_init(&fw_device->protocol_device.smp_device.smp_phy_list);
159 
160    scif_sas_smp_remote_device_clear(fw_device);
161 }
162 
163 
164 /**
165  * @brief This method decodes a smp response to this smp device and then
166  *        continue the smp discover process.
167  *
168  * @param[in] fw_device The framework device that a SMP response targets to.
169  * @param[in] fw_request The pointer to an smp request whose response
170  *       is to be decoded.
171  * @param[in] response_data The response data passed in.
172  *
173  * @return none
174  */
175 SCI_STATUS scif_sas_smp_remote_device_decode_smp_response(
176    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
177    SCIF_SAS_REQUEST_T       * fw_request,
178    void                     * response_data,
179    SCI_IO_STATUS              completion_status
180 )
181 {
182    SMP_RESPONSE_T * smp_response = (SMP_RESPONSE_T *)response_data;
183    SCI_STATUS       status       = SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE;
184 
185    if (fw_device->protocol_device.smp_device.smp_activity_timer != NULL)
186    {
187       //if there is a timer being used, recycle it now. Since we may
188       //use the timer for other purpose next.
189       scif_cb_timer_destroy(
190          fw_device->domain->controller,
191          fw_device->protocol_device.smp_device.smp_activity_timer
192       );
193 
194       fw_device->protocol_device.smp_device.smp_activity_timer = NULL;
195    }
196 
197    //if Core set the status of this io to be RETRY_REQUIRED, we should
198    //retry the IO without even decode the response.
199    if (completion_status == SCI_FAILURE_RETRY_REQUIRED)
200    {
201       scif_sas_smp_remote_device_continue_current_activity(
202          fw_device, fw_request, SCI_FAILURE_RETRY_REQUIRED
203       );
204 
205       return SCI_FAILURE_RETRY_REQUIRED;
206    }
207 
208    //check the current smp request, decide what's next smp request to issue.
209    switch (fw_device->protocol_device.smp_device.current_smp_request)
210    {
211       case SMP_FUNCTION_REPORT_GENERAL:
212       {
213          //interpret REPORT GENERAL response.
214          status = scif_sas_smp_remote_device_decode_report_general_response(
215             fw_device, smp_response
216          );
217 
218          break;
219       }
220 
221       case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
222       {
223          // No need to perform any parsing.  Just want to see
224          // the information in a trace if necessary.
225          status = SCI_SUCCESS;
226          break;
227       }
228 
229       case SMP_FUNCTION_DISCOVER:
230       {
231          if (fw_device->protocol_device.smp_device.current_activity ==
232                 SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
233          {
234             //decode discover response
235             status = scif_sas_smp_remote_device_decode_initial_discover_response(
236                         fw_device, smp_response
237                      );
238          }
239          else if (fw_device->protocol_device.smp_device.current_activity ==
240                   SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
241          {
242             //decode discover response as a polling result for a remote device
243             //target reset.
244             status =
245                scif_sas_smp_remote_device_decode_target_reset_discover_response(
246                   fw_device, smp_response
247                );
248          }
249          else if (fw_device->protocol_device.smp_device.current_activity ==
250                 SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE)
251          {
252             //decode discover response
253             status =
254                scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(
255                   fw_device, smp_response
256                );
257          }
258          else
259             ASSERT(0);
260          break;
261       }
262 
263       case SMP_FUNCTION_REPORT_PHY_SATA:
264       {
265          //decode the report phy sata response.
266          status = scif_sas_smp_remote_device_decode_report_phy_sata_response(
267             fw_device, smp_response
268          );
269 
270          break;
271       }
272 
273       case SMP_FUNCTION_PHY_CONTROL:
274       {
275          if (fw_device->protocol_device.smp_device.current_activity ==
276                 SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
277          {
278             //decode the phy control response.
279             status = scif_sas_smp_remote_device_decode_discover_phy_control_response(
280                         fw_device, smp_response
281                      );
282          }
283          else if (fw_device->protocol_device.smp_device.current_activity ==
284                      SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
285          {
286             //decode discover response as a polling result for a remote device
287             //target reset.
288             status = scif_sas_smp_remote_device_decode_target_reset_phy_control_response(
289                         fw_device, smp_response
290                      );
291          }
292          else if (fw_device->protocol_device.smp_device.current_activity ==
293                      SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION)
294          {
295             //currently don't care about the status.
296             status = SCI_SUCCESS;
297          }
298          else
299             ASSERT(0);
300          break;
301       }
302 
303       case SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION:
304       {
305          //Note, currently we don't expect any abnormal status from config route info response,
306          //but there is a possibility that we exceed the maximum route index. We will take care
307          //of errors later.
308          status = scif_sas_smp_remote_device_decode_config_route_info_response(
309                      fw_device, smp_response
310                   );
311          break;
312       }
313 
314       default:
315          //unsupported case, TBD
316          status = SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE;
317          break;
318    } //end of switch
319 
320    //Continue current activity based on response's decoding status.
321    scif_sas_smp_remote_device_continue_current_activity(
322       fw_device, fw_request, status
323    );
324 
325    return status;
326 }
327 
328 
329 /**
330  * @brief This method decodes a smp Report Genernal response to this smp device
331  *        and then continue the smp discover process.
332  *
333  * @param[in] fw_device The framework device that the REPORT GENERAL command
334  *       targets to.
335  * @param[in] report_general_response The pointer to a report general response
336  *
337  * @return none
338  */
339 SCI_STATUS scif_sas_smp_remote_device_decode_report_general_response(
340    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
341    SMP_RESPONSE_T           * smp_response
342 )
343 {
344    SMP_RESPONSE_REPORT_GENERAL_T * report_general_response =
345       &smp_response->response.report_general;
346 
347    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
348 
349    SCIF_LOG_TRACE((
350       sci_base_object_get_logger(fw_device),
351       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
352       "scif_sas_smp_remote_device_decode_report_general_response(0x%x, 0x%x) enter\n",
353       fw_device, smp_response
354    ));
355 
356    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
357    {
358       /// @todo: more decoding work needed when the function_result is not
359       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
360       /// function result.
361       SCIF_LOG_ERROR((
362          sci_base_object_get_logger(fw_device),
363          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
364          "Report General function result(0x%x)\n",
365          response_header->function_result
366       ));
367 
368       return SCI_FAILURE;
369    }
370 
371    //get info from report general response.
372    fw_device->protocol_device.smp_device.number_of_phys =
373       (U8)report_general_response->number_of_phys;
374 
375    //currently there is byte swap issue in U16 data.
376    fw_device->protocol_device.smp_device.expander_route_indexes =
377       ((report_general_response->expander_route_indexes & 0xff) << 8) |
378       ((report_general_response->expander_route_indexes & 0xff00) >> 8);
379 
380    fw_device->protocol_device.smp_device.is_table_to_table_supported =
381       (BOOL)report_general_response->table_to_table_supported;
382 
383    fw_device->protocol_device.smp_device.is_externally_configurable =
384       (BOOL)report_general_response->configurable_route_table;
385 
386    fw_device->protocol_device.smp_device.is_able_to_config_others =
387       (BOOL)report_general_response->configures_others;
388 
389    //If the top level expander of a domain is able to configure others,
390    //no config route table is needed in the domain. Or else,
391    //we'll let all the externally configurable expanders in the damain
392    //configure route table.
393    if (fw_device->containing_device == NULL
394        && ! fw_device->protocol_device.smp_device.is_able_to_config_others)
395       fw_device->domain->is_config_route_table_needed = TRUE;
396 
397    //knowing number of phys this expander has, we can allocate all the smp phys for
398    //this expander now if it is not done already.
399    if (fw_device->protocol_device.smp_device.smp_phy_list.element_count == 0)
400       scif_sas_smp_remote_device_populate_smp_phy_list(fw_device);
401 
402    if (report_general_response->configuring)
403       return SCI_FAILURE_RETRY_REQUIRED;
404 
405    return SCI_SUCCESS;
406 }
407 
408 
409 /**
410  * @brief This method decodes a smp Discover response to this smp device
411  *        and then continue the smp discover process. This is only ever
412  *        called for the very first discover stage during a given domain
413  *        discovery process.
414  *
415  * @param[in] fw_device The framework device that the DISCOVER command
416  *       targets to.
417  * @param[in] discover_response The pointer to a DISCOVER response
418  *
419  * @return none
420  */
421 SCI_STATUS scif_sas_smp_remote_device_decode_initial_discover_response(
422    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
423    SMP_RESPONSE_T           * smp_response
424 )
425 {
426    SCIF_SAS_DOMAIN_T        * fw_domain = fw_device->domain;
427    SCI_SAS_ADDRESS_T          attached_device_address;
428    SCIF_SAS_REMOTE_DEVICE_T * attached_remote_device;
429    SMP_RESPONSE_DISCOVER_T  * discover_response =
430       &smp_response->response.discover;
431    SMP_RESPONSE_HEADER_T    * response_header = &smp_response->header;
432 
433    SCIF_LOG_TRACE((
434       sci_base_object_get_logger(fw_device),
435       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
436       "scif_sas_smp_remote_device_decode_initial_discover_response(0x%x, 0x%x) enter\n",
437       fw_device, smp_response
438    ));
439 
440    if (response_header->function_result == SMP_RESULT_PHY_VACANT)
441    {
442       return SCI_SUCCESS;
443    }
444    else if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
445    {
446       /// @todo: more decoding work needed when the function_result is not
447       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
448       /// function result.
449       SCIF_LOG_ERROR((
450          sci_base_object_get_logger(fw_device),
451          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
452          "Discover function result(0x%x)\n",
453          response_header->function_result
454       ));
455 
456       return SCI_FAILURE;
457    }
458 
459    //only if there is target device attached. We don't add device that is
460    //initiator only.
461    if ( ( discover_response->u2.sas1_1.attached_device_type
462              != SMP_NO_DEVICE_ATTACHED )
463        && ( discover_response->protocols.u.bits.attached_ssp_target
464            || discover_response->protocols.u.bits.attached_stp_target
465            || discover_response->protocols.u.bits.attached_smp_target
466            || discover_response->protocols.u.bits.attached_sata_device ) )
467    {
468       attached_device_address = discover_response->attached_sas_address;
469 
470       attached_remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
471          scif_domain_get_device_by_sas_address(
472             fw_domain, &attached_device_address
473          );
474 
475       //need to check if the device already existed in the domian.
476       if (attached_remote_device != SCI_INVALID_HANDLE)
477       {
478 #if !defined(DISABLE_WIDE_PORTED_TARGETS)
479          if ( attached_remote_device->is_currently_discovered == TRUE
480              && attached_remote_device != fw_device->containing_device )
481          {
482             //a downstream wide port target is found.
483             attached_remote_device->device_port_width++;
484          }
485          else
486 #endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
487          {
488             //The device already existed. Mark the device as discovered.
489             attached_remote_device->is_currently_discovered = TRUE;
490          }
491 
492 #if !defined(DISABLE_WIDE_PORTED_TARGETS)
493          if (attached_remote_device->device_port_width !=
494                 scic_remote_device_get_port_width(attached_remote_device->core_object)
495              && discover_response->protocols.u.bits.attached_ssp_target
496             )
497          {
498             scif_sas_remote_device_update_port_width(
499                attached_remote_device, attached_remote_device->device_port_width);
500          }
501 #endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
502 
503          if ( discover_response->protocols.u.bits.attached_smp_target
504              && attached_remote_device != fw_device->containing_device)
505          {
506             //another expander device is discovered. Its own smp discover will starts after
507             //this discover finishes.
508             attached_remote_device->protocol_device.smp_device.scheduled_activity =
509                SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
510          }
511       }
512       else
513       {
514          //report the discovery of a disk for all types of end device.
515          scif_cb_domain_ea_device_added(
516             fw_domain->controller, fw_domain, fw_device, discover_response
517          );
518 
519          //get info from discover response to see what we found. And do
520          //extra work according to end device's protocol type.
521          if ( discover_response->protocols.u.bits.attached_ssp_target
522              || discover_response->protocols.u.bits.attached_smp_target)
523          {
524             //for SSP or SMP target, no extra work.
525             ;
526          }
527          else if (  (discover_response->protocols.u.bits.attached_stp_target)
528                  || (discover_response->protocols.u.bits.attached_sata_device) )
529          {
530             // We treat a SATA Device bit the same as an attached STP
531             // target.
532             discover_response->protocols.u.bits.attached_stp_target = 1;
533 
534             //kick off REPORT PHY SATA to the same phy.
535             fw_device->protocol_device.smp_device.current_smp_request =
536                SMP_FUNCTION_REPORT_PHY_SATA;
537          }
538       }
539    }
540    else if( (discover_response->u2.sas1_1.negotiated_physical_link_rate == SCI_SATA_SPINUP_HOLD
541              || discover_response->u4.sas2.negotiated_physical_link_rate == SCI_SATA_SPINUP_HOLD)
542           &&(discover_response->protocols.u.bits.attached_stp_target
543              || discover_response->protocols.u.bits.attached_sata_device)
544           )
545    {
546       attached_remote_device = scif_sas_domain_get_device_by_containing_device(
547                                   fw_domain,
548                                   fw_device,
549                                   discover_response->phy_identifier
550                                );
551 
552       if (attached_remote_device != SCI_INVALID_HANDLE)
553       {
554          //Here, the only reason a device already existed in domain but
555          //the initial discover rersponse shows it in SPINUP_HOLD, is that
556          //a device has been removed and coming back in SPINUP_HOLD before
557          //we detected. The possibility of this situation is very very rare.
558          //we need to remove the device then add it back using the new
559          //discover response.
560          scif_cb_domain_device_removed(
561             fw_domain->controller, fw_domain, attached_remote_device
562          );
563       }
564 
565       discover_response->protocols.u.bits.attached_stp_target = 1;
566 
567       //still report ea_device_added(). But this device will not be
568       //started during scif_remote_device_ea_construct().
569       scif_cb_domain_ea_device_added(
570          fw_domain->controller, fw_domain, fw_device, discover_response
571       );
572 
573       //need to send Phy Control (RESET) to release the phy from spinup hold
574       //condition.
575       fw_device->protocol_device.smp_device.current_smp_request =
576          SMP_FUNCTION_PHY_CONTROL;
577    }
578 
579    //update the smp phy info based on this DISCOVER response.
580    return scif_sas_smp_remote_device_save_smp_phy_info(
581              fw_device, discover_response);
582 }
583 
584 
585 /**
586  * @brief This method decodes a smp Report Phy Sata response to this
587  *        smp device and then continue the smp discover process.
588  *
589  * @param[in] fw_device The framework device that the REPORT PHY SATA
590  *       command targets to.
591  * @param[in] report_phy_sata_response The pointer to a REPORT PHY
592  *       SATA response
593  *
594  * @return none
595  */
596 SCI_STATUS scif_sas_smp_remote_device_decode_report_phy_sata_response(
597    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
598    SMP_RESPONSE_T           * smp_response
599 )
600 {
601    SMP_RESPONSE_REPORT_PHY_SATA_T * report_phy_sata_response =
602       &smp_response->response.report_phy_sata;
603 
604    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
605 
606    SCIF_LOG_TRACE((
607       sci_base_object_get_logger(fw_device),
608       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
609       "scif_sas_smp_remote_device_decode_report_phy_sata_response(0x%x, 0x%x) enter\n",
610       fw_device, smp_response
611    ));
612 
613    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
614    {
615       /// @todo: more decoding work needed when the function_result is not
616       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
617       /// function result.
618       SCIF_LOG_ERROR((
619          sci_base_object_get_logger(fw_device),
620          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
621          "Report Phy Sata function result(0x%x)\n",
622          response_header->function_result
623       ));
624 
625       return SCI_FAILURE;
626    }
627 
628    scif_sas_remote_device_save_report_phy_sata_information(
629       report_phy_sata_response
630    );
631 
632    // continue the discover process.
633    fw_device->protocol_device.smp_device.current_smp_request =
634       SMP_FUNCTION_DISCOVER;
635 
636    return SCI_SUCCESS;
637 }
638 
639 
640 /**
641  * @brief This method decodes a smp Phy Control response to this smp device and
642  *        then continue the smp TARGET RESET process.
643  *
644  * @param[in] fw_device The framework device that the Phy Control command
645  *       targets to.
646  * @param[in] smp_response The pointer to a Phy Control response
647  * @param[in] fw_io The scif IO request that associates to this smp response.
648  *
649  * @return none
650  */
651 SCI_STATUS scif_sas_smp_remote_device_decode_target_reset_phy_control_response(
652    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
653    SMP_RESPONSE_T           * smp_response
654 )
655 {
656    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
657 
658    SCI_STATUS status = SCI_SUCCESS;
659 
660    SCIF_LOG_TRACE((
661       sci_base_object_get_logger(fw_device),
662       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
663       "scif_sas_smp_remote_device_decode_target_reset_phy_control_response(0x%x, 0x%x) enter\n",
664       fw_device, smp_response
665    ));
666 
667    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
668    {
669       /// @todo: more decoding work needed when the function_result is not
670       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
671       /// function result.
672       SCIF_LOG_ERROR((
673          sci_base_object_get_logger(fw_device),
674          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
675          "Phy Control function unaccepted result(0x%x)\n",
676          response_header->function_result
677       ));
678 
679       status = SCI_FAILURE_RETRY_REQUIRED;
680    }
681 
682    // phy Control succeeded.
683    return status;
684 }
685 
686 /**
687  * @brief This method decodes a smp Phy Control response to this smp device and
688  *        then continue the smp DISCOVER process.
689  *
690  * @param[in] fw_device The framework device that the Phy Control command
691  *       targets to.
692  * @param[in] smp_response The pointer to a Phy Control response
693  *
694  * @return Almost always SCI_SUCCESS
695  */
696 SCI_STATUS scif_sas_smp_remote_device_decode_discover_phy_control_response(
697    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
698    SMP_RESPONSE_T           * smp_response
699 )
700 {
701    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
702 
703    SCI_STATUS status = SCI_SUCCESS;
704 
705    SCIF_LOG_TRACE((
706       sci_base_object_get_logger(fw_device),
707       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
708       "scif_sas_smp_remote_device_decode_discover_phy_control_response(0x%x, 0x%x) enter\n",
709       fw_device, smp_response
710    ));
711 
712    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
713    {
714       /// @todo: more decoding work needed when the function_result is not
715       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
716       /// function result.
717       SCIF_LOG_ERROR((
718          sci_base_object_get_logger(fw_device),
719          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
720          "Phy Control function unaccepted result(0x%x)\n",
721          response_header->function_result
722       ));
723 
724       return SCI_FAILURE_RETRY_REQUIRED;
725    }
726 
727    // continue the discover process.
728    fw_device->protocol_device.smp_device.current_smp_request =
729       SMP_FUNCTION_DISCOVER;
730 
731    // phy Control succeeded.
732    return status;
733 }
734 
735 
736 /**
737  * @brief This method decodes a smp Discover response to this smp device
738  *        and then continue the smp discover process.
739  *
740  * @param[in] fw_device The framework device that the DISCOVER command
741  *       targets to.
742  * @param[in] discover_response The pointer to a DISCOVER response
743  *
744  * @return none
745  */
746 SCI_STATUS scif_sas_smp_remote_device_decode_target_reset_discover_response(
747    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
748    SMP_RESPONSE_T           * smp_response
749 )
750 {
751    SCIF_SAS_DOMAIN_T  * fw_domain;
752    SCI_SAS_ADDRESS_T attached_device_address;
753    SMP_RESPONSE_DISCOVER_T * discover_response =
754       &smp_response->response.discover;
755 
756    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
757 
758    SCIF_LOG_TRACE((
759       sci_base_object_get_logger(fw_device),
760       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
761       "scif_sas_smp_remote_device_decode_target_reset_discover_response(0x%x, 0x%x) enter\n",
762       fw_device, smp_response
763    ));
764 
765    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
766    {
767       /// @todo: more decoding work needed when the function_result is not
768       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
769       /// function result.
770       SCIF_LOG_ERROR((
771          sci_base_object_get_logger(fw_device),
772          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
773          "Discover function result(0x%x)\n",
774          response_header->function_result
775       ));
776 
777       return SCI_FAILURE_RETRY_REQUIRED;
778    }
779 
780    //only if there is device attached.
781    if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
782    {
783       fw_domain = fw_device->domain;
784       attached_device_address = discover_response->attached_sas_address;
785 
786       // the device should have already existed in the domian.
787       ASSERT(scif_domain_get_device_by_sas_address(
788                 fw_domain,
789                 &attached_device_address
790              ) != SCI_INVALID_HANDLE);
791       return SCI_SUCCESS;
792    }
793    else
794       return SCI_FAILURE_RETRY_REQUIRED;
795 }
796 
797 /**
798  * @brief This method decodes a smp Discover response to this smp device
799  *        for SPINUP_HOLD_RELEASE activity. If a DISCOVER response says
800  *        SATA DEVICE ATTACHED and has a valid NPL value, we call fw_device's
801  *        start_handler(). But if a DISCOVER response still shows SPINUP
802  *        in NPL state, we need to return retry_required status
803  *
804  * @param[in] fw_device The framework device that the DISCOVER command
805  *       targets to.
806  * @param[in] discover_response The pointer to a DISCOVER response
807  *
808  * @return SCI_SUCCESS
809  *         SCI_FAILURE_RETRY_REQUIRED
810  */
811 SCI_STATUS scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(
812    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
813    SMP_RESPONSE_T           * smp_response
814 )
815 {
816    SMP_RESPONSE_DISCOVER_T * discover_response = &smp_response->response.discover;
817 
818    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
819 
820    SCIF_LOG_TRACE((
821       sci_base_object_get_logger(fw_device),
822       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
823       "scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(0x%x, 0x%x) enter\n",
824       fw_device, smp_response
825    ));
826 
827    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
828    {
829       /// @todo: more decoding work needed when the function_result is not
830       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
831       /// function result.
832       SCIF_LOG_ERROR((
833          sci_base_object_get_logger(fw_device),
834          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
835          "Discover function result(0x%x)\n",
836          response_header->function_result
837       ));
838 
839       return SCI_FAILURE;
840    }
841 
842    if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
843    {
844       if (discover_response->u2.sas1_1.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
845           && discover_response->u4.sas2.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
846           && ( discover_response->protocols.u.bits.attached_stp_target
847              ||discover_response->protocols.u.bits.attached_sata_device )
848          )
849       {
850          SCIF_SAS_REMOTE_DEVICE_T * target_device =
851             scif_sas_domain_get_device_by_containing_device(
852                fw_device->domain,
853                fw_device,
854                fw_device->protocol_device.smp_device.current_activity_phy_index
855             );
856 
857          //Need to update the device's connection rate. Its connection rate was SPINIP_HOLD.
858          scic_remote_device_set_max_connection_rate(
859             target_device->core_object,
860             discover_response->u2.sas1_1.negotiated_physical_link_rate
861          );
862 
863          //Need to update the smp phy info too.
864          scif_sas_smp_remote_device_save_smp_phy_info(
865              fw_device, discover_response);
866 
867          //This device has already constructed, only need to call start_handler
868          //of this device here.
869          return target_device->state_handlers->parent.start_handler(
870                    &target_device->parent );
871       }
872       else
873          return SCI_FAILURE_RETRY_REQUIRED;
874    }
875    else
876       return SCI_FAILURE_RETRY_REQUIRED;
877 }
878 
879 
880 /**
881  * @brief This method decodes a smp CONFIG ROUTE INFO response to this smp
882  *        device and then continue to config route table.
883  *
884  * @param[in] fw_device The framework device that the CONFIG ROUTE INFO command
885  *       targets to.
886  * @param[in] smp_response The pointer to a CONFIG ROUTE INFO response
887  *
888  * @return none
889  */
890 SCI_STATUS scif_sas_smp_remote_device_decode_config_route_info_response(
891    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
892    SMP_RESPONSE_T           * smp_response
893 )
894 {
895    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
896 
897    SCIF_LOG_TRACE((
898       sci_base_object_get_logger(fw_device),
899       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
900       "scif_sas_smp_remote_device_decode_config_route_info_response(0x%x, 0x%x) enter\n",
901       fw_device, smp_response
902    ));
903 
904    if (response_header->function_result == SMP_RESULT_INDEX_DOES_NOT_EXIST)
905    {
906       //case of exceeding max route index. We need to remove the devices that are not
907       //able to be edit to route table. The destination config route smp phy
908       //is used to remove devices.
909       scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
910 
911       return SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX;
912    }
913    else if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
914    {
915       /// @todo: more decoding work needed when the function_result is not
916       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
917       /// function result.
918       SCIF_LOG_ERROR((
919          sci_base_object_get_logger(fw_device),
920          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
921          "Discover function result(0x%x)\n",
922          response_header->function_result
923       ));
924 
925       return SCI_FAILURE;
926    }
927 
928    return SCI_SUCCESS;
929 }
930 
931 
932 /**
933  * @brief This method starts the smp Discover process for an expander by
934  *        sending Report General request.
935  *
936  * @param[in] fw_device The framework smp device that a  command
937  *       targets to.
938  *
939  * @return none
940  */
941 void scif_sas_smp_remote_device_start_discover(
942    SCIF_SAS_REMOTE_DEVICE_T * fw_device
943 )
944 {
945    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
946 
947    SCIF_LOG_TRACE((
948       sci_base_object_get_logger(fw_device),
949       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
950       "scif_sas_smp_remote_device_start_discover(0x%x) enter\n",
951       fw_device
952    ));
953 
954    //For safety, clear the device again, there may be some config route table
955    //related info are not cleared yet.
956    scif_sas_smp_remote_device_clear(fw_device);
957 
958    //set current activity
959    fw_device->protocol_device.smp_device.current_activity =
960       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
961 
962    //Set current_smp_request to REPORT GENERAL.
963    fw_device->protocol_device.smp_device.current_smp_request =
964       SMP_FUNCTION_REPORT_GENERAL;
965 
966    //reset discover_to_start flag.
967    fw_device->protocol_device.smp_device.scheduled_activity =
968       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
969 
970    //build the first smp request Report Genernal.
971    scif_sas_smp_request_construct_report_general(fw_controller, fw_device);
972 
973    //issue DPC to start this request.
974    scif_cb_start_internal_io_task_schedule(
975       fw_controller,
976       scif_sas_controller_start_high_priority_io,
977       fw_controller
978    );
979 }
980 
981 
982 /**
983  * @brief This method continues the smp Discover process.
984  *
985  * @param[in] fw_device The framework smp device that a DISCOVER command
986  *       targets to.
987  * @param[in] fw_request The pointer to an smp request whose response
988  *       has been decoded.
989  * @param[in] status The decoding status of the smp request's response
990  *
991  * @return none
992  */
993 void scif_sas_smp_remote_device_continue_current_activity(
994    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
995    SCIF_SAS_REQUEST_T       * fw_request,
996    SCI_STATUS                 status
997 )
998 {
999    SCIF_SAS_IO_REQUEST_T * fw_io = (SCIF_SAS_IO_REQUEST_T *)fw_request;
1000    // save the retry count.
1001    U8 io_retry_count = fw_io->retry_count;
1002 
1003    if (fw_request->is_internal)
1004    {
1005       // Complete this internal io request now. We want to free this io before
1006       // we create another SMP request, which is going to happen soon.
1007       scif_sas_internal_io_request_complete(
1008          fw_device->domain->controller,
1009          (SCIF_SAS_INTERNAL_IO_REQUEST_T *)fw_request,
1010          SCI_SUCCESS
1011       );
1012    }
1013 
1014    if (fw_device->protocol_device.smp_device.current_activity ==
1015       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
1016    {
1017       if (status == SCI_SUCCESS)
1018       {   //continue the discover process.
1019          scif_sas_smp_remote_device_continue_discover(fw_device);
1020       }
1021       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1022       {
1023          //Retry the smp request. Since we are in the middle of Discover
1024          //process, all the smp requests are internal. A new smp request
1025          //will be created for retry.
1026          U32 retry_wait_duration = (SCIF_DOMAIN_DISCOVER_TIMEOUT / 2) / SCIF_SAS_IO_RETRY_LIMIT;
1027 
1028          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1029             scif_sas_smp_remote_device_retry_internal_io (
1030                fw_device, io_retry_count, retry_wait_duration);
1031          else
1032             scif_sas_smp_remote_device_fail_discover(fw_device);
1033       }
1034       else if (status == SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION)
1035       {
1036          //remove this expander device and its child devices. No need to
1037          //continue the discover on this device.
1038          scif_sas_domain_remove_expander_device(fw_device->domain, fw_device);
1039 
1040          //continue the domain's smp discover.
1041          scif_sas_domain_continue_discover(fw_device->domain);
1042       }
1043       else
1044       {  //terminate the discover process.
1045          scif_sas_smp_remote_device_fail_discover(fw_device);
1046       }
1047    }
1048    else if (fw_device->protocol_device.smp_device.current_activity ==
1049       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
1050    {
1051       if (status == SCI_SUCCESS)
1052       {   //continue the target reset process.
1053          scif_sas_smp_remote_device_continue_target_reset(
1054             fw_device, fw_request);
1055       }
1056       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1057       {
1058          //Retry the same smp request. Since we are in the middle of Target
1059          //reset process, all the smp requests are using external resource.
1060          //We will use the exactly same memory to retry.
1061          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1062          {
1063             if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1064             {
1065                //create the timer to wait before retry.
1066                fw_device->protocol_device.smp_device.smp_activity_timer =
1067                   scif_cb_timer_create(
1068                   (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1069                   (SCI_TIMER_CALLBACK_T)scif_sas_smp_external_request_retry,
1070                   (void*)fw_request
1071                );
1072             }
1073             else
1074             {
1075                ASSERT(0);
1076             }
1077 
1078             //start the timer to wait
1079             scif_cb_timer_start(
1080                (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1081                fw_device->protocol_device.smp_device.smp_activity_timer,
1082                SMP_REQUEST_RETRY_WAIT_DURATION  //20 miliseconds
1083             );
1084          }
1085          else
1086             scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
1087       }
1088       else
1089          //terminate the discover process.
1090          scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
1091    }
1092    else if (fw_device->protocol_device.smp_device.current_activity ==
1093       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE)
1094    {
1095       SCIF_SAS_REMOTE_DEVICE_T * target_device =
1096          scif_sas_domain_get_device_by_containing_device(
1097             fw_device->domain,
1098             fw_device,
1099             fw_device->protocol_device.smp_device.current_activity_phy_index
1100          );
1101 
1102       if (status == SCI_SUCCESS)
1103       {
1104          //move on to next round of SPINUP_HOLD_REALSE activity.
1105          scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
1106       }
1107       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1108       {
1109          U32 delay =
1110             (scic_remote_device_get_suggested_reset_timeout(target_device->core_object) /
1111                 SCIF_SAS_IO_RETRY_LIMIT);
1112 
1113          //Retry the smp request. Since we are in the middle of Discover
1114          //process, all the smp requests are internal. A new smp request
1115          //will be created for retry.
1116          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1117          {
1118             scif_sas_smp_remote_device_retry_internal_io(
1119                fw_device, io_retry_count, delay);
1120          }
1121          else //give up on this target device.
1122          {
1123             scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1124                fw_device , target_device);
1125          }
1126       }
1127       else //give up on this target device.
1128         scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1129            fw_device, target_device);
1130    }
1131    else if (fw_device->protocol_device.smp_device.current_activity ==
1132       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
1133    {
1134       SCI_FAST_LIST_ELEMENT_T * next_phy_element = sci_fast_list_get_next(
1135          &(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element) );
1136 
1137       SCI_FAST_LIST_T * destination_smp_phy_list =
1138           fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element.owning_list;
1139 
1140       SCIF_SAS_SMP_PHY_T * next_phy_in_wide_port = NULL;
1141 
1142       if (next_phy_element != NULL
1143           && status != SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX)
1144       {
1145          fw_device->protocol_device.smp_device.curr_config_route_index++;
1146 
1147          fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
1148             (SCIF_SAS_SMP_PHY_T *)sci_fast_list_get_object(next_phy_element);
1149 
1150          // Update the anchor for config route index.
1151          fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor =
1152             fw_device->protocol_device.smp_device.curr_config_route_index;
1153 
1154          scif_sas_smp_remote_device_configure_route_table(fw_device);
1155       }
1156       else if ( scif_sas_smp_remote_device_get_config_route_table_method(fw_device)
1157                    == SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS
1158                 && (next_phy_in_wide_port = scif_sas_smp_phy_find_next_phy_in_wide_port(
1159                        fw_device->protocol_device.smp_device.config_route_smp_phy_anchor)
1160                    )!= NULL
1161               )
1162       {
1163          //config the other phy in the same wide port
1164          fw_device->protocol_device.smp_device.config_route_smp_phy_anchor =
1165             next_phy_in_wide_port;
1166 
1167          fw_device->protocol_device.smp_device.current_activity_phy_index =
1168             fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
1169 
1170          fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
1171             sci_fast_list_get_head(destination_smp_phy_list);
1172 
1173          if (fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor != 0)
1174             fw_device->protocol_device.smp_device.curr_config_route_index =
1175                fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor + 1;
1176          else
1177             fw_device->protocol_device.smp_device.curr_config_route_index = 0;
1178 
1179          scif_sas_smp_remote_device_configure_route_table(fw_device);
1180       }
1181       else if ( fw_device->protocol_device.smp_device.is_route_table_cleaned == FALSE)
1182       {
1183          fw_device->protocol_device.smp_device.current_activity =
1184             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE;
1185 
1186          scif_sas_smp_remote_device_clean_route_table(fw_device);
1187       }
1188       else
1189       {
1190          //set this device's activity to NON.
1191          fw_device->protocol_device.smp_device.current_activity =
1192             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
1193 
1194          //we need to notify domain that this device finished config route table, domain
1195          //may pick up other activities (i.e. Discover) for other expanders.
1196          scif_sas_domain_continue_discover(fw_device->domain);
1197       }
1198    }
1199    else if (fw_device->protocol_device.smp_device.current_activity ==
1200                SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE)
1201    {
1202       scif_sas_smp_remote_device_clean_route_table(fw_device);
1203    }
1204    else if (fw_device->protocol_device.smp_device.current_activity ==
1205                SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION)
1206    {
1207       scif_sas_smp_remote_device_continue_clear_affiliation(fw_device);
1208    }
1209 }
1210 
1211 
1212 /**
1213  * @brief This method continues the smp Discover process.
1214  *
1215  * @param[in] fw_device The framework smp device that a DISCOVER command
1216  *       targets to.
1217  *
1218  * @return none
1219  */
1220 void scif_sas_smp_remote_device_continue_discover(
1221    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1222 )
1223 {
1224    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1225 
1226    SCIF_LOG_TRACE((
1227       sci_base_object_get_logger(fw_device),
1228       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1229       "scif_sas_smp_remote_device_continue_discover(0x%x) enter\n",
1230       fw_device
1231    ));
1232 
1233    switch (fw_device->protocol_device.smp_device.current_smp_request)
1234    {
1235       case SMP_FUNCTION_REPORT_GENERAL:
1236          // send the REPORT MANUFACTURER_INFO request
1237          fw_device->protocol_device.smp_device.current_smp_request =
1238             SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION;
1239 
1240          scif_sas_smp_request_construct_report_manufacturer_info(
1241             fw_domain->controller, fw_device
1242          );
1243 
1244          break;
1245 
1246       case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
1247          //send the first SMP DISCOVER request.
1248          fw_device->protocol_device.smp_device.current_activity_phy_index = 0;
1249          fw_device->protocol_device.smp_device.current_smp_request =
1250             SMP_FUNCTION_DISCOVER;
1251 
1252          scif_sas_smp_request_construct_discover(
1253             fw_domain->controller,
1254             fw_device,
1255             fw_device->protocol_device.smp_device.current_activity_phy_index,
1256             NULL, NULL
1257          );
1258          break;
1259 
1260 
1261       case SMP_FUNCTION_DISCOVER:
1262          fw_device->protocol_device.smp_device.current_activity_phy_index++;
1263 
1264          if ( (fw_device->protocol_device.smp_device.current_activity_phy_index <
1265                   fw_device->protocol_device.smp_device.number_of_phys) )
1266          {
1267             scif_sas_smp_request_construct_discover(
1268                fw_domain->controller,
1269                fw_device,
1270                fw_device->protocol_device.smp_device.current_activity_phy_index,
1271                NULL, NULL
1272             );
1273          }
1274          else
1275             scif_sas_smp_remote_device_finish_initial_discover(fw_device);
1276          break;
1277 
1278 
1279       case SMP_FUNCTION_REPORT_PHY_SATA:
1280          scif_sas_smp_request_construct_report_phy_sata(
1281             fw_device->domain->controller,
1282             fw_device,
1283             fw_device->protocol_device.smp_device.current_activity_phy_index
1284          );
1285 
1286          break;
1287 
1288 
1289       case SMP_FUNCTION_PHY_CONTROL:
1290          scif_sas_smp_request_construct_phy_control(
1291             fw_device->domain->controller,
1292             fw_device,
1293             PHY_OPERATION_HARD_RESET,
1294             fw_device->protocol_device.smp_device.current_activity_phy_index,
1295             NULL,
1296             NULL
1297          );
1298 
1299          break;
1300 
1301       default:
1302          break;
1303    }
1304 }
1305 
1306 /**
1307  * @brief This method finishes the initial smp DISCOVER process. There
1308  *        may be a spinup_hold release phase following of initial discover,
1309  *        depending on whether there are SATA device in the domain
1310  *        in SATA_SPINUP_HOLD condition.
1311  *
1312  * @param[in] fw_device The framework smp device that finishes all the
1313  *       DISCOVER requests.
1314  *
1315  * @return none
1316  */
1317 void scif_sas_smp_remote_device_finish_initial_discover(
1318    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1319 )
1320 {
1321    SCIF_SAS_REMOTE_DEVICE_T * device_in_sata_spinup_hold =
1322       scif_sas_domain_find_device_in_spinup_hold(fw_device->domain);
1323 
1324    SCIF_LOG_TRACE((
1325       sci_base_object_get_logger(fw_device),
1326       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1327       "scif_sas_smp_remote_device_finish_initial_discover(0x%x) enter\n",
1328       fw_device
1329    ));
1330 
1331    if ( device_in_sata_spinup_hold != NULL )
1332    {
1333      //call the common private routine to reset all fields of this smp device.
1334      scif_sas_smp_remote_device_clear(fw_device);
1335 
1336      //Move on to next activity SPINUP_HOLD_RELEASE
1337      fw_device->protocol_device.smp_device.current_activity =
1338         SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE;
1339 
1340       //create the timer to delay a little bit before going to
1341       //sata spinup hold release activity.
1342       if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1343       {
1344       fw_device->protocol_device.smp_device.smp_activity_timer =
1345          scif_cb_timer_create(
1346             (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1347             (SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_sata_spinup_hold_release,
1348             (void*)fw_device
1349          );
1350       }
1351       else
1352       {
1353          ASSERT (0);
1354       }
1355 
1356       scif_cb_timer_start(
1357          (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1358          fw_device->protocol_device.smp_device.smp_activity_timer,
1359          SMP_SPINUP_HOLD_RELEASE_WAIT_DURATION
1360       );
1361    }
1362    else
1363       scif_sas_smp_remote_device_finish_discover(fw_device);
1364 }
1365 
1366 
1367 /**
1368  * @brief This method finishes the smp DISCOVER process.
1369  *
1370  * @param[in] fw_device The framework smp device that finishes all the
1371  *       DISCOVER requests.
1372  *
1373  * @return none
1374  */
1375 void scif_sas_smp_remote_device_finish_discover(
1376    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1377 )
1378 {
1379    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1380 
1381    SCIF_LOG_TRACE((
1382       sci_base_object_get_logger(fw_device),
1383       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1384       "scif_sas_smp_remote_device_finish_discover(0x%x) enter\n",
1385       fw_device
1386    ));
1387 
1388    if ( fw_domain->is_config_route_table_needed
1389        && fw_device->protocol_device.smp_device.smp_phy_list.list_head != NULL)
1390       scif_sas_smp_remote_device_configure_upstream_expander_route_info(fw_device);
1391 
1392    //call the common private routine to reset all fields of this smp device.
1393    scif_sas_smp_remote_device_clear(fw_device);
1394 
1395 #ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
1396    scif_sas_smp_remote_device_print_smp_phy_list(fw_device);
1397 #endif
1398 
1399    //notify domain this smp device's discover finishes, it's up to domain
1400    //to continue the discover process in a bigger scope.
1401    scif_sas_domain_continue_discover(fw_domain);
1402 }
1403 
1404 
1405 /**
1406  * @brief This method continues the smp Target Reset (Phy Control) process.
1407  *
1408  * @param[in] fw_device The framework smp device that a smp reset targets to.
1409  *
1410  * @return none
1411  */
1412 void scif_sas_smp_remote_device_continue_target_reset(
1413    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1414    SCIF_SAS_REQUEST_T       * fw_request
1415 )
1416 {
1417    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
1418    SCIF_SAS_REMOTE_DEVICE_T * target_device =
1419       scif_sas_domain_get_device_by_containing_device(
1420          fw_device->domain,
1421          fw_device,
1422          fw_device->protocol_device.smp_device.current_activity_phy_index
1423       );
1424 
1425    SCIF_LOG_TRACE((
1426       sci_base_object_get_logger(fw_device),
1427       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1428       "scif_sas_smp_remote_device_continue_target_reset(0x%x, 0x%x) enter\n",
1429       fw_device, fw_request
1430    ));
1431 
1432    if (fw_device->protocol_device.smp_device.current_smp_request ==
1433           SMP_FUNCTION_PHY_CONTROL)
1434    {
1435       //query the core remote device to get suggested reset timeout value
1436       //then scale down by factor of 8 to get the duration of the pause
1437       //before sending out Discover command to poll.
1438       U32 delay =
1439          (scic_remote_device_get_suggested_reset_timeout(target_device->core_object)/8);
1440 
1441       //create the timer to send Discover command polling target device's
1442       //coming back.
1443       if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1444       {
1445          fw_device->protocol_device.smp_device.smp_activity_timer =
1446             scif_cb_timer_create(
1447                (SCI_CONTROLLER_HANDLE_T *)fw_controller,
1448                (SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_target_reset_poll,
1449                (void*)fw_request
1450             );
1451       }
1452       else
1453       {
1454          ASSERT(0);
1455       }
1456 
1457       //start the timer
1458       scif_cb_timer_start(
1459          (SCI_CONTROLLER_HANDLE_T)fw_controller,
1460          fw_device->protocol_device.smp_device.smp_activity_timer,
1461          delay
1462       );
1463    }
1464    else if (fw_device->protocol_device.smp_device.current_smp_request ==
1465           SMP_FUNCTION_DISCOVER)
1466    {
1467       //tell target reset successful
1468       scif_sas_remote_device_target_reset_complete(
1469          target_device, fw_request, SCI_SUCCESS);
1470    }
1471 }
1472 
1473 /**
1474  * @brief This routine is invoked by timer or when 2 BCN are received
1475  *        after Phy Control command. This routine will construct a
1476  *        Discover command to the same expander phy to poll the target
1477  *        device's coming back. This new request is then put into
1478  *        high priority queue and will be started by a DPC soon.
1479  *
1480  * @param[in] fw_request The scif request for smp activities.
1481  */
1482 void scif_sas_smp_remote_device_target_reset_poll(
1483    SCIF_SAS_REQUEST_T       * fw_request
1484 )
1485 {
1486    SCIF_SAS_REMOTE_DEVICE_T * fw_device = fw_request->device;
1487    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
1488    void * new_command_handle;
1489 
1490    SCIF_LOG_TRACE((
1491       sci_base_object_get_logger(fw_device),
1492       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1493       "scif_sas_smp_remote_device_target_reset_poll(0x%x) enter\n",
1494       fw_request
1495    ));
1496 
1497    // Before we construct new io using the same memory, we need to
1498    // remove the IO from the list of outstanding requests on the domain
1499    // so that we don't damage the domain's fast list of request.
1500    sci_fast_list_remove_element(&fw_request->list_element);
1501 
1502    fw_device->protocol_device.smp_device.current_smp_request =
1503       SMP_FUNCTION_DISCOVER;
1504 
1505    //sent smp discover request to poll on remote device's coming back.
1506    //construct Discover command using the same memory as fw_request.
1507    new_command_handle = scif_sas_smp_request_construct_discover(
1508       fw_device->domain->controller,
1509       fw_device,
1510       fw_device->protocol_device.smp_device.current_activity_phy_index,
1511       (void *)sci_object_get_association(fw_request),
1512       (void *)fw_request
1513    );
1514 
1515    //put into the high priority queue.
1516    sci_pool_put(fw_controller->hprq.pool, (POINTER_UINT) new_command_handle);
1517 
1518    //schedule the DPC to start new Discover command.
1519    scif_cb_start_internal_io_task_schedule(
1520       fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
1521    );
1522 }
1523 
1524 
1525 /**
1526  * @brief This method fails discover process.
1527  *
1528  * @param[in] fw_device The framework smp device that failed at current
1529  *       activity.
1530  *
1531  * @return none
1532  */
1533 void scif_sas_smp_remote_device_fail_discover(
1534    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1535 )
1536 {
1537    SCIF_LOG_TRACE((
1538       sci_base_object_get_logger(fw_device),
1539       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1540       "scif_sas_smp_remote_device_fail_discover(0x%x) enter\n",
1541       fw_device
1542    ));
1543 
1544    switch (fw_device->protocol_device.smp_device.current_smp_request)
1545    {
1546       case SMP_FUNCTION_REPORT_GENERAL:
1547       case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
1548          scif_sas_smp_remote_device_finish_discover(fw_device);
1549          break;
1550 
1551       case SMP_FUNCTION_DISCOVER:
1552       case SMP_FUNCTION_REPORT_PHY_SATA:
1553          //Retry limit reached, we will continue to send DISCOVER to next phy.
1554          fw_device->protocol_device.smp_device.current_smp_request =
1555             SMP_FUNCTION_DISCOVER;
1556 
1557          scif_sas_smp_remote_device_continue_discover(fw_device);
1558          break;
1559 
1560       default:
1561          break;
1562    }
1563 }
1564 
1565 
1566 /**
1567  * @brief This method fails Target Reset.
1568  *
1569  * @param[in] fw_device The framework smp device that failed at current
1570  *       activity.
1571  * @param[in] fw_request The smp request created for target reset
1572  *       using external resource.
1573  *
1574  * @return none
1575  */
1576 void scif_sas_smp_remote_device_fail_target_reset(
1577    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1578    SCIF_SAS_REQUEST_T       * fw_request
1579 )
1580 {
1581    SCIF_SAS_REMOTE_DEVICE_T * target_device =
1582       scif_sas_domain_get_device_by_containing_device(
1583          fw_device->domain,
1584          fw_device,
1585          fw_device->protocol_device.smp_device.current_activity_phy_index
1586       );
1587 
1588    SCIF_LOG_TRACE((
1589       sci_base_object_get_logger(fw_device),
1590       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1591       "scif_sas_smp_remote_device_fail_target_reset(0x%x, 0x%x, 0x%x) enter\n",
1592       fw_device, target_device, fw_request
1593    ));
1594 
1595    //tell target reset failed
1596    scif_sas_remote_device_target_reset_complete(
1597       target_device, fw_request, SCI_FAILURE);
1598 }
1599 
1600 /**
1601  * @brief This method init or continue the SATA SPINUP_HOLD RELEASE activity.
1602  * This function searches domain's device list, find a device in STOPPED STATE
1603  * and its connection_rate is SPINIP, then send DISCOVER command to its expander
1604  * phy id to poll. But if searching the domain's device list for SATA devices on
1605  * SPINUP_HOLD finds no device, the activity SPINUP_HOLD_RELEASE is finished.
1606  * We then call fw_domain->device_start_complete_handler() for this smp-device.
1607  *
1608  * @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
1609  *       activity.
1610  *
1611  * @return none
1612  */
1613 void scif_sas_smp_remote_device_sata_spinup_hold_release(
1614    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1615 )
1616 {
1617    SCIF_SAS_DOMAIN_T        * fw_domain = fw_device->domain;
1618    SCIF_SAS_CONTROLLER_T    * fw_controller = fw_domain->controller;
1619    SCIF_SAS_REMOTE_DEVICE_T * device_to_poll = NULL;
1620 
1621    SCIF_LOG_TRACE((
1622       sci_base_object_get_logger(fw_device),
1623       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1624       "scif_sas_smp_remote_device_sata_spinup_hold_release(0x%x) enter\n",
1625       fw_device
1626    ));
1627 
1628    //search throught domain's device list to find a sata device on spinup_hold
1629    //state to poll.
1630    device_to_poll = scif_sas_domain_find_device_in_spinup_hold(fw_domain);
1631 
1632    if (device_to_poll != NULL)
1633    {
1634       //send DISCOVER command to this device's expaner phy.
1635       fw_device->protocol_device.smp_device.current_smp_request =
1636          SMP_FUNCTION_DISCOVER;
1637 
1638       fw_device->protocol_device.smp_device.current_activity_phy_index =
1639         device_to_poll->expander_phy_identifier;
1640 
1641       scif_sas_smp_request_construct_discover(
1642          fw_domain->controller,
1643          fw_device,
1644          fw_device->protocol_device.smp_device.current_activity_phy_index,
1645          NULL, NULL
1646       );
1647 
1648       //schedule the DPC to start new Discover command.
1649       scif_cb_start_internal_io_task_schedule(
1650          fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
1651       );
1652    }
1653    else //SATA SPINUP HOLD RELEASE activity is done.
1654       scif_sas_smp_remote_device_finish_discover (fw_device);
1655 }
1656 
1657 
1658 /**
1659  * @brief This method fail an action of SATA SPINUP_HOLD RELEASE on a single EA
1660  *        SATA device. It will remove a remote_device object for a sata device
1661  *        that fails to come out of spinup_hold.
1662  *
1663  * @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
1664  *       activity.
1665  * @param[in] target_device The expander attached device failed being brought out
1666  *       of SPINUP_HOLD state.
1667  *
1668  * @return none
1669  */
1670 void scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1671    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1672    SCIF_SAS_REMOTE_DEVICE_T * target_device
1673 )
1674 {
1675    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1676 
1677    SCIF_LOG_TRACE((
1678       sci_base_object_get_logger(fw_device),
1679       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1680       "scif_sas_smp_remote_device_fail_target_spinup_hold_release(0x%x, 0x%x) enter\n",
1681       fw_device, target_device
1682    ));
1683 
1684    //need to remove the device, since we have to give up on spinup_hold_release
1685    //activity on this device.
1686    scif_cb_domain_device_removed(
1687       fw_domain->controller, fw_domain, target_device
1688    );
1689 
1690    //move on to next round of SPINUP_HOLD_REALSE activity.
1691    scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
1692 }
1693 
1694 
1695 /**
1696  * @brief This method retry only internal IO for the smp device.
1697  *
1698  * @param[in] fw_device The framework smp device that has an smp request to retry.
1699  * @param[in] io_retry_count current count for times the IO being retried.
1700  * @param[in] delay The time delay before the io gets retried.
1701  *
1702  * @return none
1703  */
1704 void scif_sas_smp_remote_device_retry_internal_io(
1705    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1706    U8                         io_retry_count,
1707    U32                        delay
1708 )
1709 {
1710    SCIF_LOG_TRACE((
1711       sci_base_object_get_logger(fw_device),
1712       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1713       "scif_sas_smp_remote_device_retry_internal_io(0x%x, 0x%x, 0x%x) enter\n",
1714       fw_device, io_retry_count, delay
1715    ));
1716 
1717    fw_device->protocol_device.smp_device.io_retry_count =
1718       io_retry_count;
1719 
1720    //create the timer for poll target device's coming back.
1721    if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1722    {
1723       fw_device->protocol_device.smp_device.smp_activity_timer =
1724          scif_cb_timer_create(
1725             (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1726             (SCI_TIMER_CALLBACK_T)scif_sas_smp_internal_request_retry,
1727             (void*)fw_device
1728          );
1729    }
1730    else
1731    {
1732       ASSERT(0);
1733    }
1734    //start the timer for a purpose of waiting.
1735    scif_cb_timer_start(
1736       (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1737       fw_device->protocol_device.smp_device.smp_activity_timer,
1738       delay
1739    );
1740 }
1741 
1742 
1743 /**
1744  * @brief This method indicates whether an expander device is in Discover
1745  *        process.
1746  *
1747  * @param[in] fw_device The framework smp device.
1748  *
1749  * @return Whether an expander device is in the middle of discovery process.
1750  */
1751 BOOL scif_sas_smp_remote_device_is_in_activity(
1752    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1753 )
1754 {
1755    return(fw_device->protocol_device.smp_device.current_activity
1756           != SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE);
1757 }
1758 
1759 /**
1760  * @brief This method search through the smp phy list of an expander to
1761  *        find a smp phy by its phy id of the expander.
1762  *
1763  * @param[in] phy_identifier The search criteria.
1764  * @param[in] smp_remote_device The expander that owns the smp phy list.
1765  *
1766  * @return The found smp phy or a NULL pointer to indicate no smp phy is found.
1767  */
1768 SCIF_SAS_SMP_PHY_T * scif_sas_smp_remote_device_find_smp_phy_by_id(
1769    U8                             phy_identifier,
1770    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device
1771 )
1772 {
1773    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1774    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1775 
1776    ASSERT(phy_identifier < smp_remote_device->smp_phy_list.number_of_phys);
1777 
1778    while (element != NULL)
1779    {
1780       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1781       element = sci_fast_list_get_next(element);
1782 
1783       if (curr_smp_phy->phy_identifier == phy_identifier)
1784          return curr_smp_phy;
1785    }
1786 
1787    return NULL;
1788 }
1789 
1790 /**
1791  * @brief This method takes care of removing smp phy list of a smp devcie, which is
1792  *           about to be removed.
1793  *
1794  * @param[in] fw_device The expander device that is about to be removed.
1795  *
1796  * @return none.
1797  */
1798 void scif_sas_smp_remote_device_removed(
1799    SCIF_SAS_REMOTE_DEVICE_T * this_device
1800 )
1801 {
1802    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
1803       &this_device->protocol_device.smp_device;
1804 
1805    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1806    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1807 
1808    SCIF_LOG_TRACE((
1809       sci_base_object_get_logger(this_device),
1810       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1811       "scif_sas_smp_remote_device_removed(0x%x) enter\n",
1812       this_device
1813    ));
1814 
1815    //remove all the smp phys in this device's smp_phy_list, and the conterpart smp phys
1816    //in phy connections.
1817    while (element != NULL)
1818    {
1819       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1820       element = sci_fast_list_get_next(element);
1821 
1822       scif_sas_smp_phy_destruct(curr_smp_phy);
1823    }
1824 
1825    this_device->protocol_device.smp_device.number_of_phys = 0;
1826    this_device->protocol_device.smp_device.expander_route_indexes = 0;
1827    this_device->protocol_device.smp_device.is_table_to_table_supported = FALSE;
1828    this_device->protocol_device.smp_device.is_externally_configurable  = FALSE;
1829    this_device->protocol_device.smp_device.is_able_to_config_others    = FALSE;
1830 
1831    scif_sas_smp_remote_device_clear(this_device);
1832 }
1833 
1834 
1835 /**
1836  * @brief This method takes care of terminated smp request to a smp device. The
1837  *        terminated smp request is most likely timeout and being aborted. A timeout
1838  *        maybe due to OPEN REJECT (NO DESTINATION).
1839  *
1840  * @param[in] fw_device The expander device that a timed out smp request towards to.
1841  * @param[in] fw_request A failed smp request that is terminated by scic.
1842  *
1843  * @return none.
1844  */
1845 void scif_sas_smp_remote_device_terminated_request_handler(
1846    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1847    SCIF_SAS_REQUEST_T       * fw_request
1848 )
1849 {
1850    SCIF_LOG_TRACE((
1851       sci_base_object_get_logger(fw_device),
1852       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1853       "scif_sas_smp_remote_device_terminated_request_handler(0x%x, 0x%x) enter\n",
1854       fw_device, fw_request
1855    ));
1856 
1857    scif_sas_smp_remote_device_decode_smp_response(
1858       fw_device, fw_request, NULL, SCI_IO_FAILURE_RETRY_REQUIRED
1859    );
1860 }
1861 
1862 
1863 /**
1864  * @brief This method allocates and populates the smp phy list of a expander device.
1865  *
1866  * @param[in] fw_device The expander device, whose smp phy list is to be populated after
1867  *                      getting REPORT GENERAL response.
1868  *
1869  * @return none.
1870  */
1871 void scif_sas_smp_remote_device_populate_smp_phy_list(
1872    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1873 )
1874 {
1875    SCIF_SAS_SMP_PHY_T * this_smp_phy = NULL;
1876    U8                   expander_phy_id = 0;
1877 
1878    SCIF_LOG_TRACE((
1879       sci_base_object_get_logger(fw_device),
1880       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1881       "scif_sas_smp_remote_device_populate_smp_phy_list(0x%x) enter\n",
1882       fw_device
1883    ));
1884 
1885    for ( expander_phy_id = 0;
1886          expander_phy_id < fw_device->protocol_device.smp_device.number_of_phys;
1887          expander_phy_id++ )
1888    {
1889       this_smp_phy =
1890          scif_sas_controller_allocate_smp_phy(fw_device->domain->controller);
1891 
1892       ASSERT( this_smp_phy != NULL );
1893 
1894       if ( this_smp_phy != NULL )
1895          scif_sas_smp_phy_construct(this_smp_phy, fw_device, expander_phy_id);
1896    }
1897 }
1898 
1899 
1900 /**
1901  * @brief This method updates a smp phy of a expander device based on DISCOVER response.
1902  *
1903  * @param[in] fw_device The expander device, one of whose smp phys is to be updated.
1904  * @param[in] discover_response The smp DISCOVER response.
1905  *
1906  * @return SCI_STATUS If a smp phy pair between expanders has invalid routing attribute,
1907  *                    return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION, otherwise,
1908  *                    return SCI_SUCCESS
1909  */
1910 SCI_STATUS scif_sas_smp_remote_device_save_smp_phy_info(
1911    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1912    SMP_RESPONSE_DISCOVER_T  * discover_response
1913 )
1914 {
1915    SCI_STATUS status = SCI_SUCCESS;
1916    SCIF_SAS_SMP_PHY_T * smp_phy = NULL;
1917    SCIF_SAS_REMOTE_DEVICE_T * attached_device = NULL;
1918 
1919     SCIF_LOG_TRACE((
1920       sci_base_object_get_logger(fw_device),
1921       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1922       "scif_sas_smp_remote_device_save_smp_phy_info(0x%x, 0x%x) enter\n",
1923       fw_device, discover_response
1924    ));
1925 
1926    smp_phy = scif_sas_smp_remote_device_find_smp_phy_by_id(
1927                 discover_response->phy_identifier,
1928                 &fw_device->protocol_device.smp_device
1929              );
1930 
1931    ASSERT( smp_phy != NULL );
1932 
1933    //Note, attached_device could be NULL, not all the smp phy have to connected to a device.
1934    attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1935       scif_domain_get_device_by_sas_address(
1936          fw_device->domain, &discover_response->attached_sas_address);
1937 
1938    scif_sas_smp_phy_save_information(
1939       smp_phy, attached_device, discover_response);
1940 
1941    //handle the special case of smp phys between expanders.
1942    if ( discover_response->protocols.u.bits.attached_smp_target )
1943    {
1944        //this fw_device is a child expander, just found its parent expander.
1945        //And there is no smp_phy constructed yet, record this phy connection.
1946        if ( attached_device != NULL
1947            && attached_device == fw_device->containing_device )
1948        {
1949           //record the smp phy info, for this phy connects to a upstream smp device.
1950           //the connection of a pair of smp phys are completed.
1951           status = scif_sas_smp_phy_set_attached_phy(
1952                       smp_phy,
1953                       discover_response->attached_phy_identifier,
1954                       attached_device
1955                    );
1956 
1957           if (status == SCI_SUCCESS)
1958           {
1959              //check the routing attribute for this phy and its containing device's
1960              //expander_phy_routing_attribute.
1961              if ( scif_sas_smp_phy_verify_routing_attribute(
1962                      smp_phy, smp_phy->u.attached_phy) != SCI_SUCCESS )
1963                 return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION;
1964           }
1965        }
1966     }
1967 
1968     return status;
1969 }
1970 
1971 #ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
1972 void scif_sas_smp_remote_device_print_smp_phy_list(
1973    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1974 )
1975 {
1976    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device = &fw_device->protocol_device.smp_device;
1977    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1978    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1979 
1980    SCIF_LOG_ERROR((
1981       sci_base_object_get_logger(fw_device),
1982       SCIF_LOG_OBJECT_REMOTE_DEVICE,
1983       "==========EXPANDER DEVICE (0x%x) smp phy list========== \n",
1984       fw_device
1985    ));
1986 
1987    while (element != NULL)
1988    {
1989       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1990       element = sci_fast_list_get_next(element);
1991 
1992       //print every thing about a smp phy
1993       SCIF_LOG_ERROR((
1994          sci_base_object_get_logger(fw_device),
1995          SCIF_LOG_OBJECT_REMOTE_DEVICE,
1996          "SMP_PHY_%d (0x%x), attached device(0x%x), attached_sas_address(%x%x) attached_device_type(%d), routing_attribute(%d)\n",
1997          curr_smp_phy->phy_identifier, curr_smp_phy,
1998          curr_smp_phy->u.end_device,
1999          curr_smp_phy->attached_sas_address.high, curr_smp_phy->attached_sas_address.low,
2000          curr_smp_phy->attached_device_type,
2001          curr_smp_phy->routing_attribute
2002       ));
2003    }
2004 }
2005 #endif
2006 
2007 
2008 /**
2009  * @brief This method configure upstream expander(s)' (if there is any) route info.
2010  *
2011  * @param[in] this_device The expander device that is currently in discover process.
2012  *
2013  * @return none.
2014  */
2015 void scif_sas_smp_remote_device_configure_upstream_expander_route_info(
2016    SCIF_SAS_REMOTE_DEVICE_T * this_device
2017 )
2018 {
2019    SCIF_SAS_REMOTE_DEVICE_T * curr_child_expander = this_device;
2020    SCIF_SAS_REMOTE_DEVICE_T * curr_parent_expander =
2021       scif_sas_remote_device_find_upstream_expander(this_device);
2022 
2023    SCIF_SAS_REMOTE_DEVICE_T * curr_config_route_info_expander = NULL;
2024 
2025    SCIF_LOG_TRACE((
2026       sci_base_object_get_logger(this_device),
2027       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2028       "scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
2029       this_device
2030    ));
2031 
2032    //traverse back to find root device.
2033    while(curr_parent_expander != NULL )
2034    {
2035       //must set destination_smp_phy outside of find_upstream_expander() using the device
2036       //that is just about to finish the discovery.
2037       curr_parent_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy =
2038          (SCIF_SAS_SMP_PHY_T*)sci_fast_list_get_object(
2039              this_device->protocol_device.smp_device.smp_phy_list.list_head);
2040 
2041       curr_child_expander = curr_parent_expander;
2042       curr_parent_expander = scif_sas_remote_device_find_upstream_expander(curr_child_expander);
2043    }
2044 
2045    //found the root device: curr_child_expander. configure it and its downstream expander(s) till
2046    //this_device or a self-configuring expander that configures others;
2047    curr_config_route_info_expander = curr_child_expander;
2048 
2049    while ( curr_config_route_info_expander != NULL
2050           && curr_config_route_info_expander != this_device
2051           && curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity
2052                 == SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE
2053          )
2054    {
2055       if (curr_config_route_info_expander->protocol_device.smp_device.is_externally_configurable)
2056       {
2057          SCIF_SAS_SMP_PHY_T * phy_being_config =
2058             curr_config_route_info_expander->protocol_device.smp_device.config_route_smp_phy_anchor;
2059 
2060          curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index =
2061             phy_being_config->config_route_table_index_anchor;
2062 
2063          if (curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index != 0)
2064             curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index++;
2065 
2066          curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity =
2067             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
2068 
2069          //Find a downstream expander that has curr_config_route_destination_smp_phy.owning device
2070          //same as curr_config_route_info_expander.
2071          curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
2072             curr_config_route_info_expander);
2073       }
2074       else if (curr_config_route_info_expander->protocol_device.smp_device.is_able_to_config_others)
2075       {
2076          //no need to config route table to this expander and its children.
2077          //find its downstream expander and clear the planned config route table activity.
2078          SCIF_SAS_REMOTE_DEVICE_T * curr_downstream_expander =
2079             scif_sas_remote_device_find_downstream_expander(
2080                curr_config_route_info_expander);
2081 
2082          scif_sas_smp_remote_device_clear(curr_config_route_info_expander);
2083 
2084          while ( curr_downstream_expander != NULL
2085                 && curr_downstream_expander != this_device )
2086          {
2087             scif_sas_smp_remote_device_clear(curr_downstream_expander);
2088             curr_downstream_expander =
2089                scif_sas_remote_device_find_downstream_expander(
2090                   curr_config_route_info_expander);
2091          }
2092 
2093          break;
2094       }
2095       else
2096       {
2097          // current expander is a self-configuring expander, which is not externally
2098          // configurable, and doesn't config others. we need to simply skip this expander.
2099          curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
2100             curr_config_route_info_expander);
2101       }
2102    }
2103 }
2104 
2105 /**
2106  * @brief This method finds the immediate upstream expander of a given expander device.
2107  *
2108  * @param[in] this_device The given expander device, whose upstream expander is to be found.
2109  *
2110  * @return The immediate upstream expander. Or a NULL pointer if this_device is root already.
2111  */
2112 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_upstream_expander(
2113    SCIF_SAS_REMOTE_DEVICE_T * this_device
2114 )
2115 {
2116    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2117       &this_device->protocol_device.smp_device;
2118 
2119    SCIF_SAS_REMOTE_DEVICE_T    * upstream_expander = NULL;
2120 
2121    SCI_FAST_LIST_ELEMENT_T     * element = smp_remote_device->smp_phy_list.list_head;
2122    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2123 
2124    SCIF_LOG_TRACE((
2125       sci_base_object_get_logger(this_device),
2126       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2127       "scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
2128       this_device
2129    ));
2130 
2131    while (element != NULL)
2132    {
2133       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2134       element = sci_fast_list_get_next(element);
2135 
2136       if ( curr_smp_phy->routing_attribute == SUBTRACTIVE_ROUTING_ATTRIBUTE
2137           && ( curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
2138               || curr_smp_phy->attached_device_type == SMP_FANOUT_EXPANDER_DEVICE)
2139           && curr_smp_phy->u.attached_phy != NULL
2140           && curr_smp_phy->u.attached_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE )
2141       {
2142          //set the current_activity and current_config_route_index for that
2143          //upstream expander.
2144          upstream_expander = curr_smp_phy->u.attached_phy->owning_device;
2145 
2146          upstream_expander->protocol_device.smp_device.current_smp_request =
2147             SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION;
2148 
2149          //if the upstream_expander's config route table method is config phy0 only or
2150          //config all phys, the current activity phy is found.
2151          upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2152             scif_sas_smp_remote_device_find_smp_phy_by_id(
2153                curr_smp_phy->u.attached_phy->phy_identifier,
2154                &(curr_smp_phy->u.attached_phy->owning_device->protocol_device.smp_device)
2155             );
2156 
2157          //if the upstream_expander's config route table method is config middle phy only
2158          //config highest phy only, the current activity phy needs a update.
2159          if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
2160                  == SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY )
2161          {
2162             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2163                scif_sas_smp_phy_find_middle_phy_in_wide_port (
2164                   upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
2165                );
2166          }
2167          else if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
2168                       == SCIF_SAS_CONFIG_ROUTE_TABLE_HIGHEST_PHY_ONLY )
2169          {
2170             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2171                scif_sas_smp_phy_find_highest_phy_in_wide_port (
2172                   upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
2173                );
2174          }
2175 
2176          upstream_expander->protocol_device.smp_device.current_activity_phy_index =
2177             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
2178 
2179          return upstream_expander;
2180       }
2181    }
2182 
2183    return NULL;
2184 }
2185 
2186 
2187 /**
2188  * @brief This method finds the immediate downstream expander of a given expander device.
2189  *
2190  * @param[in] this_device The given expander device, whose downstream expander is to be found.
2191  *
2192  * @return The immediate downstream expander. Or a NULL pointer if there is none.
2193  */
2194 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_downstream_expander(
2195    SCIF_SAS_REMOTE_DEVICE_T * this_device
2196 )
2197 {
2198    SCIF_SAS_SMP_REMOTE_DEVICE_T * this_smp_remote_device =
2199       &this_device->protocol_device.smp_device;
2200 
2201    SCIF_SAS_REMOTE_DEVICE_T    * downstream_expander = NULL;
2202 
2203    SCI_FAST_LIST_ELEMENT_T     * element = this_smp_remote_device->smp_phy_list.list_head;
2204    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2205 
2206    SCIF_LOG_TRACE((
2207       sci_base_object_get_logger(this_device),
2208       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2209       "scif_sas_remote_device_find_downstream_expander(0x%x) enter\n",
2210       this_device
2211    ));
2212 
2213    while (element != NULL)
2214    {
2215       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2216       element = sci_fast_list_get_next(element);
2217 
2218       if ( curr_smp_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE
2219           && curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
2220           && curr_smp_phy->u.attached_phy != NULL)
2221       {
2222          //set the current_activity and current_config_route_index for that
2223          //upstream expander.
2224          downstream_expander = curr_smp_phy->u.attached_phy->owning_device;
2225 
2226          if ( downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy != NULL
2227              && downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy->owning_device ==
2228                 this_smp_remote_device->curr_config_route_destination_smp_phy->owning_device )
2229             return downstream_expander;
2230       }
2231    }
2232 
2233    return NULL;
2234 }
2235 
2236 
2237 /**
2238  * @brief This method follows route table optimization rule to check if a destination_device
2239  *        should be recorded in the device_being_config's route table
2240  *
2241  * @param[in] device_being_config The upstream expander device, whose route table is being configured.
2242  * @param[in] destination_smp_phy A smp phy whose attached device is potentially to be
2243  *               recorded in route table.
2244  *
2245  * @return BOOL This method returns TRUE if a destination_device should be recorded in route table.
2246  *              This method returns FALSE if a destination_device need not to be recorded
2247  *              in route table.
2248  */
2249 BOOL scif_sas_smp_remote_device_do_config_route_info(
2250    SCIF_SAS_REMOTE_DEVICE_T * device_being_config,
2251    SCIF_SAS_SMP_PHY_T       * destination_smp_phy
2252 )
2253 {
2254    SCI_SAS_ADDRESS_T device_being_config_sas_address;
2255 
2256    SCIF_LOG_TRACE((
2257       sci_base_object_get_logger(device_being_config),
2258       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2259       "scif_sas_smp_remote_device_do_config_route_info(0x%x, 0x%x) enter\n",
2260       device_being_config, destination_smp_phy
2261    ));
2262 
2263    scic_remote_device_get_sas_address(
2264       device_being_config->core_object, &device_being_config_sas_address
2265    );
2266 
2267    //refer to SAS-2 spec 4.8.3, rule (b)
2268    if ((destination_smp_phy->attached_sas_address.low == 0
2269         && destination_smp_phy->attached_sas_address.high == 0)
2270        && (destination_smp_phy->attached_device_type == SMP_NO_DEVICE_ATTACHED))
2271    {
2272       return FALSE;
2273    }
2274 
2275    //refer to SAS-2 spec 4.8.3, rule (c), self-referencing.
2276    if (destination_smp_phy->attached_sas_address.high ==
2277           device_being_config_sas_address.high
2278        && destination_smp_phy->attached_sas_address.low ==
2279              device_being_config_sas_address.low)
2280    {
2281       return FALSE;
2282    }
2283 
2284    //There will be no cases that falling into rule (a), (d), (e) to be excluded,
2285    //based on our current mechanism of cofig route table.
2286 
2287    return TRUE;
2288 }
2289 
2290 
2291 /**
2292  * @brief This method configures device_being_config's route table for all the enclosed devices in
2293  *           a downstream smp device, destination_device.
2294  *
2295  * @param[in] device_being_config The upstream expander device, whose route table is being configured.
2296  *
2297  * @return None
2298  */
2299 void scif_sas_smp_remote_device_configure_route_table(
2300    SCIF_SAS_REMOTE_DEVICE_T * device_being_config
2301 )
2302 {
2303    //go through the smp phy list of this_device.
2304    SCI_FAST_LIST_ELEMENT_T     * element =
2305       &(device_being_config->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
2306    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2307 
2308    SCIF_LOG_TRACE((
2309       sci_base_object_get_logger(device_being_config),
2310       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2311       "scif_sas_smp_remote_device_configure_route_table(0x%x) enter\n",
2312       device_being_config
2313    ));
2314 
2315    device_being_config->protocol_device.smp_device.current_activity =
2316       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
2317 
2318    while (element != NULL)
2319    {
2320       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2321       element = sci_fast_list_get_next(element);
2322 
2323       //check if this phy needs to be added to the expander's route table.
2324       if (scif_sas_smp_remote_device_do_config_route_info(
2325              device_being_config, curr_smp_phy) == TRUE )
2326       {
2327          SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2328             &device_being_config->protocol_device.smp_device;
2329 
2330          smp_remote_device->curr_config_route_destination_smp_phy =
2331             curr_smp_phy;
2332 
2333          //Then config this_device's route table entry at the phy and next route_index.
2334          //send config_route_info using curr_smp_phy.phy_identifier and sas_address.
2335          scif_sas_smp_request_construct_config_route_info(
2336             device_being_config->domain->controller,
2337             device_being_config,
2338             smp_remote_device->current_activity_phy_index,
2339             smp_remote_device->curr_config_route_index,
2340             curr_smp_phy->attached_sas_address,
2341             FALSE
2342          );
2343 
2344          //schedule the DPC.
2345          scif_cb_start_internal_io_task_schedule(
2346             device_being_config->domain->controller,
2347             scif_sas_controller_start_high_priority_io,
2348             device_being_config->domain->controller
2349          );
2350 
2351          //stop here, we need to wait for config route info's response then send
2352          //the next one.
2353          break;
2354       }
2355    }
2356 }
2357 
2358 
2359 /**
2360  * @brief This method walks through an expander's route table to clean table
2361  *           attribute phys' route entries. This routine finds one table entry
2362  *           to clean and will be called repeatly till it finishes cleanning the
2363  *           whole table.
2364  *
2365  * @param[in] fw_device The expander device, whose route table entry is to be cleaned.
2366  *
2367  * @return None.
2368  */
2369 void scif_sas_smp_remote_device_clean_route_table(
2370    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2371 )
2372 {
2373    SCIF_SAS_SMP_PHY_T * smp_phy_being_config;
2374 
2375    SCIF_LOG_TRACE((
2376       sci_base_object_get_logger(fw_device),
2377       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2378       "scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
2379       fw_device
2380    ));
2381 
2382    //from anchors, start to clean all the other route table entries.
2383    fw_device->protocol_device.smp_device.curr_config_route_index++;
2384 
2385    if ( fw_device->protocol_device.smp_device.curr_config_route_index >=
2386            fw_device->protocol_device.smp_device.expander_route_indexes )
2387    {
2388       fw_device->protocol_device.smp_device.curr_config_route_index = 0;
2389 
2390       do //find next table attribute PHY.
2391       {
2392          fw_device->protocol_device.smp_device.current_activity_phy_index++;
2393          if (fw_device->protocol_device.smp_device.current_activity_phy_index ==
2394                 fw_device->protocol_device.smp_device.number_of_phys)
2395             fw_device->protocol_device.smp_device.current_activity_phy_index=0;
2396 
2397          //phy_index changed, so update the smp_phy_being_config.
2398          smp_phy_being_config =
2399             scif_sas_smp_remote_device_find_smp_phy_by_id(
2400                fw_device->protocol_device.smp_device.current_activity_phy_index,
2401                &(fw_device->protocol_device.smp_device)
2402             );
2403       } while( smp_phy_being_config->routing_attribute != TABLE_ROUTING_ATTRIBUTE );
2404 
2405       if ( smp_phy_being_config->phy_identifier !=
2406               fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier)
2407       {
2408          if (smp_phy_being_config->config_route_table_index_anchor != 0)
2409             fw_device->protocol_device.smp_device.curr_config_route_index =
2410                smp_phy_being_config->config_route_table_index_anchor + 1;
2411          else
2412             fw_device->protocol_device.smp_device.curr_config_route_index = 0;
2413       }
2414    }
2415 
2416    if ( !(fw_device->protocol_device.smp_device.current_activity_phy_index ==
2417              fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier
2418           && fw_device->protocol_device.smp_device.curr_config_route_index == 0)
2419       )
2420    {
2421       //clean this route entry.
2422       scif_sas_smp_remote_device_clean_route_table_entry(fw_device);
2423    }
2424    else
2425    {
2426       fw_device->protocol_device.smp_device.is_route_table_cleaned = TRUE;
2427 
2428       //set this device's activity to NON.
2429       fw_device->protocol_device.smp_device.current_activity =
2430          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
2431 
2432       //we need to notify domain that this device finished config route table, domain
2433       //may pick up other activities (i.e. Discover) for other expanders.
2434       scif_sas_domain_continue_discover(fw_device->domain);
2435    }
2436 }
2437 
2438 /**
2439  * @brief This method cleans a device's route table antry.
2440  *
2441  * @param[in] fw_device The expander device, whose route table entry is to be cleaned.
2442  *
2443  * @return None.
2444  */
2445 void scif_sas_smp_remote_device_clean_route_table_entry(
2446    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2447 )
2448 {
2449    SCI_SAS_ADDRESS_T empty_sas_address;
2450    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2451       &(fw_device->protocol_device.smp_device);
2452 
2453    SCIF_LOG_TRACE((
2454       sci_base_object_get_logger(fw_device),
2455       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2456       "scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
2457       fw_device
2458    ));
2459 
2460    empty_sas_address.high = 0;
2461    empty_sas_address.low = 0;
2462 
2463    scif_sas_smp_request_construct_config_route_info(
2464       fw_device->domain->controller,
2465       fw_device,
2466       smp_remote_device->current_activity_phy_index,
2467       smp_remote_device->curr_config_route_index,
2468       empty_sas_address,
2469       TRUE
2470    );
2471 
2472    //schedule the DPC.
2473    scif_cb_start_internal_io_task_schedule(
2474       fw_device->domain->controller,
2475       scif_sas_controller_start_high_priority_io,
2476       fw_device->domain->controller
2477    );
2478 }
2479 
2480 
2481 /**
2482  * @brief This method handles the case of exceeding route index when config route table
2483  *           for a device, by removing the attached device of current config route
2484  *           destination smp phy and the rest of smp phys in the same smp phy list.
2485  *
2486  * @param[in] fw_device The expander device, whose route table to be edited but failed
2487  *               with a SMP function result of INDEX DOES NOT EXIST.
2488  *
2489  * @return None.
2490  */
2491 void scif_sas_smp_remote_device_cancel_config_route_table_activity(
2492    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2493 )
2494 {
2495    //go through the rest of the smp phy list of destination device.
2496    SCI_FAST_LIST_ELEMENT_T     * element =
2497       &(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
2498    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2499    SCIF_SAS_REMOTE_DEVICE_T    * curr_attached_device = NULL;
2500 
2501    SCIF_LOG_TRACE((
2502       sci_base_object_get_logger(fw_device),
2503       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2504       "scif_sas_smp_remote_device_cancel_config_route_table_activity(0x%x) enter\n",
2505       fw_device
2506    ));
2507 
2508    while (element != NULL)
2509    {
2510       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2511       element = sci_fast_list_get_next(element);
2512 
2513       //check if this phy needs to be added to the expander's route table but can't due to
2514       //exceeding max route index.
2515       if (scif_sas_smp_remote_device_do_config_route_info(
2516              fw_device, curr_smp_phy) == TRUE )
2517       {
2518          //set the is_currently_discovered to FALSE for attached device. Then when
2519          //domain finish discover, domain will remove this device.
2520          curr_attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
2521             scif_domain_get_device_by_sas_address(
2522                fw_device->domain, &(curr_smp_phy->attached_sas_address));
2523 
2524          if (curr_attached_device != NULL)
2525             curr_attached_device->is_currently_discovered = FALSE;
2526       }
2527    }
2528 }
2529 
2530 
2531 /**
2532  * @brief This method cancel current activity and terminate the outstanding internal IO
2533  *           if there is one.
2534  *
2535  * @param[in] fw_device The expander device, whose smp activity is to be canceled.
2536  *
2537  * @return None.
2538  */
2539 void scif_sas_smp_remote_device_cancel_smp_activity(
2540    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2541 )
2542 {
2543    SCIF_LOG_TRACE((
2544       sci_base_object_get_logger(fw_device),
2545       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2546       "scif_sas_smp_remote_device_cancel_smp_activity(0x%x) enter\n",
2547       fw_device
2548    ));
2549 
2550    //Terminate all of the requests in the silicon for this device.
2551    scif_sas_domain_terminate_requests(
2552       fw_device->domain, fw_device, NULL, NULL
2553    );
2554 
2555    if (fw_device->protocol_device.smp_device.current_activity ==
2556           SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
2557       scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
2558 
2559    //Clear the device to stop the smp sctivity.
2560    scif_sas_smp_remote_device_clear(fw_device);
2561 }
2562 
2563 
2564 /**
2565  * @brief This method tells the way to configure route table for a expander. The
2566  *          possible ways are: configure phy 0's route table, configure middle
2567  *          phy's route table, configure highest order phy's route table,
2568  *          configure all phys.
2569  *
2570  * @param[in] fw_device The expander device, whose config route table method is
2571  *               to be chosen.
2572  *
2573  * @return one in 4 possible options.
2574  */
2575 U8 scif_sas_smp_remote_device_get_config_route_table_method(
2576    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2577 )
2578 {
2579    U8 config_route_table_method;
2580 
2581    //config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY;
2582    config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS;
2583 
2584    return config_route_table_method;
2585 }
2586 
2587 
2588 /**
2589  * @brief This method starts the EA target reset process by constructing
2590  *           and starting a PHY CONTROL (hard reset) smp request.
2591  *
2592  * @param[in] expander_device The expander device, to which a PHY Control smp command is
2593  *               sent.
2594  * @param[in] target_device The expander attahced target device, to which the target reset
2595  *               request is sent.
2596  * @param[in] fw_request The target reset task request.
2597  *
2598  * @return none
2599  */
2600 void scif_sas_smp_remote_device_start_target_reset(
2601    SCIF_SAS_REMOTE_DEVICE_T * expander_device,
2602    SCIF_SAS_REMOTE_DEVICE_T * target_device,
2603    SCIF_SAS_REQUEST_T       * fw_request
2604 )
2605 {
2606    SCIF_SAS_CONTROLLER_T * fw_controller = expander_device->domain->controller;
2607 
2608    //set current_activity and current_smp_request to expander device.
2609    expander_device->protocol_device.smp_device.current_activity =
2610       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET;
2611    expander_device->protocol_device.smp_device.current_smp_request =
2612       SMP_FUNCTION_PHY_CONTROL;
2613    expander_device->protocol_device.smp_device.current_activity_phy_index =
2614       target_device->expander_phy_identifier;
2615 
2616    //A Phy Control smp request has been constructed towards parent device.
2617    //Walk the high priority io path.
2618    fw_controller->state_handlers->start_high_priority_io_handler(
2619       (SCI_BASE_CONTROLLER_T*) fw_controller,
2620       (SCI_BASE_REMOTE_DEVICE_T*) expander_device,
2621       (SCI_BASE_REQUEST_T*) fw_request,
2622       SCI_CONTROLLER_INVALID_IO_TAG
2623    );
2624 }
2625 
2626 
2627