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