xref: /freebsd/sys/dev/isci/scil/scif_sas_smp_remote_device.c (revision b7c60aadbbd5c846a250c05791fe7406d6d78bf4)
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    SCIF_SAS_REMOTE_DEVICE_T * attached_remote_device;
752    SMP_RESPONSE_DISCOVER_T * discover_response =
753       &smp_response->response.discover;
754 
755    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
756 
757    SCIF_LOG_TRACE((
758       sci_base_object_get_logger(fw_device),
759       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
760       "scif_sas_smp_remote_device_decode_target_reset_discover_response(0x%x, 0x%x) enter\n",
761       fw_device, smp_response
762    ));
763 
764    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
765    {
766       /// @todo: more decoding work needed when the function_result is not
767       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
768       /// function result.
769       SCIF_LOG_ERROR((
770          sci_base_object_get_logger(fw_device),
771          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
772          "Discover function result(0x%x)\n",
773          response_header->function_result
774       ));
775 
776       return SCI_FAILURE_RETRY_REQUIRED;
777    }
778 
779    //only if there is device attached.
780    if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
781    {
782       fw_domain = fw_device->domain;
783       attached_device_address = discover_response->attached_sas_address;
784 
785       attached_remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
786          scif_domain_get_device_by_sas_address(
787          fw_domain, &attached_device_address
788       );
789 
790       // the device should have already existed in the domian.
791       ASSERT (attached_remote_device != SCI_INVALID_HANDLE);
792       return SCI_SUCCESS;
793    }
794    else
795       return SCI_FAILURE_RETRY_REQUIRED;
796 }
797 
798 /**
799  * @brief This method decodes a smp Discover response to this smp device
800  *        for SPINUP_HOLD_RELEASE activity. If a DISCOVER response says
801  *        SATA DEVICE ATTACHED and has a valid NPL value, we call fw_device's
802  *        start_handler(). But if a DISCOVER response still shows SPINUP
803  *        in NPL state, we need to return retry_required status
804  *
805  * @param[in] fw_device The framework device that the DISCOVER command
806  *       targets to.
807  * @param[in] discover_response The pointer to a DISCOVER response
808  *
809  * @return SCI_SUCCESS
810  *         SCI_FAILURE_RETRY_REQUIRED
811  */
812 SCI_STATUS scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(
813    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
814    SMP_RESPONSE_T           * smp_response
815 )
816 {
817    SMP_RESPONSE_DISCOVER_T * discover_response = &smp_response->response.discover;
818 
819    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
820 
821    SCIF_LOG_TRACE((
822       sci_base_object_get_logger(fw_device),
823       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
824       "scif_sas_smp_remote_device_decode_spinup_hold_release_discover_response(0x%x, 0x%x) enter\n",
825       fw_device, smp_response
826    ));
827 
828    if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
829    {
830       /// @todo: more decoding work needed when the function_result is not
831       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
832       /// function result.
833       SCIF_LOG_ERROR((
834          sci_base_object_get_logger(fw_device),
835          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
836          "Discover function result(0x%x)\n",
837          response_header->function_result
838       ));
839 
840       return SCI_FAILURE;
841    }
842 
843    if ( discover_response->u2.sas1_1.attached_device_type != SMP_NO_DEVICE_ATTACHED )
844    {
845       if (discover_response->u2.sas1_1.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
846           && discover_response->u4.sas2.negotiated_physical_link_rate != SCI_SATA_SPINUP_HOLD
847           && ( discover_response->protocols.u.bits.attached_stp_target
848              ||discover_response->protocols.u.bits.attached_sata_device )
849          )
850       {
851          SCIF_SAS_REMOTE_DEVICE_T * target_device =
852             scif_sas_domain_get_device_by_containing_device(
853                fw_device->domain,
854                fw_device,
855                fw_device->protocol_device.smp_device.current_activity_phy_index
856             );
857 
858          //Need to update the device's connection rate. Its connection rate was SPINIP_HOLD.
859          scic_remote_device_set_max_connection_rate(
860             target_device->core_object,
861             discover_response->u2.sas1_1.negotiated_physical_link_rate
862          );
863 
864          //Need to update the smp phy info too.
865          scif_sas_smp_remote_device_save_smp_phy_info(
866              fw_device, discover_response);
867 
868          //This device has already constructed, only need to call start_handler
869          //of this device here.
870          return target_device->state_handlers->parent.start_handler(
871                    &target_device->parent );
872       }
873       else
874          return SCI_FAILURE_RETRY_REQUIRED;
875    }
876    else
877       return SCI_FAILURE_RETRY_REQUIRED;
878 }
879 
880 
881 /**
882  * @brief This method decodes a smp CONFIG ROUTE INFO response to this smp
883  *        device and then continue to config route table.
884  *
885  * @param[in] fw_device The framework device that the CONFIG ROUTE INFO command
886  *       targets to.
887  * @param[in] smp_response The pointer to a CONFIG ROUTE INFO response
888  *
889  * @return none
890  */
891 SCI_STATUS scif_sas_smp_remote_device_decode_config_route_info_response(
892    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
893    SMP_RESPONSE_T           * smp_response
894 )
895 {
896    SMP_RESPONSE_HEADER_T * response_header = &smp_response->header;
897 
898    SCIF_LOG_TRACE((
899       sci_base_object_get_logger(fw_device),
900       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
901       "scif_sas_smp_remote_device_decode_config_route_info_response(0x%x, 0x%x) enter\n",
902       fw_device, smp_response
903    ));
904 
905    if (response_header->function_result == SMP_RESULT_INDEX_DOES_NOT_EXIST)
906    {
907       //case of exceeding max route index. We need to remove the devices that are not
908       //able to be edit to route table. The destination config route smp phy
909       //is used to remove devices.
910       scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
911 
912       return SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX;
913    }
914    else if (response_header->function_result != SMP_RESULT_FUNCTION_ACCEPTED)
915    {
916       /// @todo: more decoding work needed when the function_result is not
917       /// SMP_RESULT_FUNCTION_ACCEPTED. Retry might be the option for some
918       /// function result.
919       SCIF_LOG_ERROR((
920          sci_base_object_get_logger(fw_device),
921          SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
922          "Discover function result(0x%x)\n",
923          response_header->function_result
924       ));
925 
926       return SCI_FAILURE;
927    }
928 
929    return SCI_SUCCESS;
930 }
931 
932 
933 /**
934  * @brief This method starts the smp Discover process for an expander by
935  *        sending Report General request.
936  *
937  * @param[in] fw_device The framework smp device that a  command
938  *       targets to.
939  *
940  * @return none
941  */
942 void scif_sas_smp_remote_device_start_discover(
943    SCIF_SAS_REMOTE_DEVICE_T * fw_device
944 )
945 {
946    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
947 
948    SCIF_LOG_TRACE((
949       sci_base_object_get_logger(fw_device),
950       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
951       "scif_sas_smp_remote_device_start_discover(0x%x) enter\n",
952       fw_device
953    ));
954 
955    //For safety, clear the device again, there may be some config route table
956    //related info are not cleared yet.
957    scif_sas_smp_remote_device_clear(fw_device);
958 
959    //set current activity
960    fw_device->protocol_device.smp_device.current_activity =
961       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER;
962 
963    //Set current_smp_request to REPORT GENERAL.
964    fw_device->protocol_device.smp_device.current_smp_request =
965       SMP_FUNCTION_REPORT_GENERAL;
966 
967    //reset discover_to_start flag.
968    fw_device->protocol_device.smp_device.scheduled_activity =
969       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
970 
971    //build the first smp request Report Genernal.
972    scif_sas_smp_request_construct_report_general(fw_controller, fw_device);
973 
974    //issue DPC to start this request.
975    scif_cb_start_internal_io_task_schedule(
976       fw_controller,
977       scif_sas_controller_start_high_priority_io,
978       fw_controller
979    );
980 }
981 
982 
983 /**
984  * @brief This method continues the smp Discover process.
985  *
986  * @param[in] fw_device The framework smp device that a DISCOVER command
987  *       targets to.
988  * @param[in] fw_request The pointer to an smp request whose response
989  *       has been decoded.
990  * @param[in] status The decoding status of the smp request's response
991  *
992  * @return none
993  */
994 void scif_sas_smp_remote_device_continue_current_activity(
995    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
996    SCIF_SAS_REQUEST_T       * fw_request,
997    SCI_STATUS                 status
998 )
999 {
1000    SCIF_SAS_IO_REQUEST_T * fw_io = (SCIF_SAS_IO_REQUEST_T *)fw_request;
1001    // save the retry count.
1002    U8 io_retry_count = fw_io->retry_count;
1003 
1004    if (fw_request->is_internal)
1005    {
1006       // Complete this internal io request now. We want to free this io before
1007       // we create another SMP request, which is going to happen soon.
1008       scif_sas_internal_io_request_complete(
1009          fw_device->domain->controller,
1010          (SCIF_SAS_INTERNAL_IO_REQUEST_T *)fw_request,
1011          SCI_SUCCESS
1012       );
1013    }
1014 
1015    if (fw_device->protocol_device.smp_device.current_activity ==
1016       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_DISCOVER)
1017    {
1018       if (status == SCI_SUCCESS)
1019       {   //continue the discover process.
1020          scif_sas_smp_remote_device_continue_discover(fw_device);
1021       }
1022       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1023       {
1024          //Retry the smp request. Since we are in the middle of Discover
1025          //process, all the smp requests are internal. A new smp request
1026          //will be created for retry.
1027          U32 retry_wait_duration = (SCIF_DOMAIN_DISCOVER_TIMEOUT / 2) / SCIF_SAS_IO_RETRY_LIMIT;
1028 
1029          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1030             scif_sas_smp_remote_device_retry_internal_io (
1031                fw_device, io_retry_count, retry_wait_duration);
1032          else
1033             scif_sas_smp_remote_device_fail_discover(fw_device);
1034       }
1035       else if (status == SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION)
1036       {
1037          //remove this expander device and its child devices. No need to
1038          //continue the discover on this device.
1039          scif_sas_domain_remove_expander_device(fw_device->domain, fw_device);
1040 
1041          //continue the domain's smp discover.
1042          scif_sas_domain_continue_discover(fw_device->domain);
1043       }
1044       else
1045       {  //terminate the discover process.
1046          scif_sas_smp_remote_device_fail_discover(fw_device);
1047       }
1048    }
1049    else if (fw_device->protocol_device.smp_device.current_activity ==
1050       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET)
1051    {
1052       if (status == SCI_SUCCESS)
1053       {   //continue the target reset process.
1054          scif_sas_smp_remote_device_continue_target_reset(
1055             fw_device, fw_request);
1056       }
1057       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1058       {
1059          //Retry the same smp request. Since we are in the middle of Target
1060          //reset process, all the smp requests are using external resource.
1061          //We will use the exactly same memory to retry.
1062          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1063          {
1064             if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1065             {
1066                //create the timer to wait before retry.
1067                fw_device->protocol_device.smp_device.smp_activity_timer =
1068                   scif_cb_timer_create(
1069                   (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1070                   (SCI_TIMER_CALLBACK_T)scif_sas_smp_external_request_retry,
1071                   (void*)fw_request
1072                );
1073             }
1074             else
1075             {
1076                ASSERT(0);
1077             }
1078 
1079             //start the timer to wait
1080             scif_cb_timer_start(
1081                (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1082                fw_device->protocol_device.smp_device.smp_activity_timer,
1083                SMP_REQUEST_RETRY_WAIT_DURATION  //20 miliseconds
1084             );
1085          }
1086          else
1087             scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
1088       }
1089       else
1090          //terminate the discover process.
1091          scif_sas_smp_remote_device_fail_target_reset(fw_device, fw_request);
1092    }
1093    else if (fw_device->protocol_device.smp_device.current_activity ==
1094       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE)
1095    {
1096       SCIF_SAS_REMOTE_DEVICE_T * target_device =
1097          scif_sas_domain_get_device_by_containing_device(
1098             fw_device->domain,
1099             fw_device,
1100             fw_device->protocol_device.smp_device.current_activity_phy_index
1101          );
1102 
1103       if (status == SCI_SUCCESS)
1104       {
1105          //move on to next round of SPINUP_HOLD_REALSE activity.
1106          scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
1107       }
1108       else if (status == SCI_FAILURE_RETRY_REQUIRED)
1109       {
1110          U32 delay =
1111             (scic_remote_device_get_suggested_reset_timeout(target_device->core_object) /
1112                 SCIF_SAS_IO_RETRY_LIMIT);
1113 
1114          //Retry the smp request. Since we are in the middle of Discover
1115          //process, all the smp requests are internal. A new smp request
1116          //will be created for retry.
1117          if (io_retry_count < SCIF_SAS_IO_RETRY_LIMIT)
1118          {
1119             scif_sas_smp_remote_device_retry_internal_io(
1120                fw_device, io_retry_count, delay);
1121          }
1122          else //give up on this target device.
1123          {
1124             scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1125                fw_device , target_device);
1126          }
1127       }
1128       else //give up on this target device.
1129         scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1130            fw_device, target_device);
1131    }
1132    else if (fw_device->protocol_device.smp_device.current_activity ==
1133       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
1134    {
1135       SCI_FAST_LIST_ELEMENT_T * next_phy_element = sci_fast_list_get_next(
1136          &(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element) );
1137 
1138       SCI_FAST_LIST_T * destination_smp_phy_list =
1139           fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element.owning_list;
1140 
1141       SCIF_SAS_SMP_PHY_T * next_phy_in_wide_port = NULL;
1142 
1143       if (next_phy_element != NULL
1144           && status != SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX)
1145       {
1146          fw_device->protocol_device.smp_device.curr_config_route_index++;
1147 
1148          fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
1149             (SCIF_SAS_SMP_PHY_T *)sci_fast_list_get_object(next_phy_element);
1150 
1151          // Update the anchor for config route index.
1152          fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor =
1153             fw_device->protocol_device.smp_device.curr_config_route_index;
1154 
1155          scif_sas_smp_remote_device_configure_route_table(fw_device);
1156       }
1157       else if ( scif_sas_smp_remote_device_get_config_route_table_method(fw_device)
1158                    == SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS
1159                 && (next_phy_in_wide_port = scif_sas_smp_phy_find_next_phy_in_wide_port(
1160                        fw_device->protocol_device.smp_device.config_route_smp_phy_anchor)
1161                    )!= NULL
1162               )
1163       {
1164          //config the other phy in the same wide port
1165          fw_device->protocol_device.smp_device.config_route_smp_phy_anchor =
1166             next_phy_in_wide_port;
1167 
1168          fw_device->protocol_device.smp_device.current_activity_phy_index =
1169             fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
1170 
1171          fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy =
1172             sci_fast_list_get_head(destination_smp_phy_list);
1173 
1174          if (fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor != 0)
1175             fw_device->protocol_device.smp_device.curr_config_route_index =
1176                fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->config_route_table_index_anchor + 1;
1177          else
1178             fw_device->protocol_device.smp_device.curr_config_route_index = 0;
1179 
1180          scif_sas_smp_remote_device_configure_route_table(fw_device);
1181       }
1182       else if ( fw_device->protocol_device.smp_device.is_route_table_cleaned == FALSE)
1183       {
1184          fw_device->protocol_device.smp_device.current_activity =
1185             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE;
1186 
1187          scif_sas_smp_remote_device_clean_route_table(fw_device);
1188       }
1189       else
1190       {
1191          //set this device's activity to NON.
1192          fw_device->protocol_device.smp_device.current_activity =
1193             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
1194 
1195          //we need to notify domain that this device finished config route table, domain
1196          //may pick up other activities (i.e. Discover) for other expanders.
1197          scif_sas_domain_continue_discover(fw_device->domain);
1198       }
1199    }
1200    else if (fw_device->protocol_device.smp_device.current_activity ==
1201                SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAN_ROUTE_TABLE)
1202    {
1203       scif_sas_smp_remote_device_clean_route_table(fw_device);
1204    }
1205    else if (fw_device->protocol_device.smp_device.current_activity ==
1206                SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CLEAR_AFFILIATION)
1207    {
1208       scif_sas_smp_remote_device_continue_clear_affiliation(fw_device);
1209    }
1210 }
1211 
1212 
1213 /**
1214  * @brief This method continues the smp Discover process.
1215  *
1216  * @param[in] fw_device The framework smp device that a DISCOVER command
1217  *       targets to.
1218  *
1219  * @return none
1220  */
1221 void scif_sas_smp_remote_device_continue_discover(
1222    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1223 )
1224 {
1225    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1226 
1227    SCIF_LOG_TRACE((
1228       sci_base_object_get_logger(fw_device),
1229       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1230       "scif_sas_smp_remote_device_continue_discover(0x%x) enter\n",
1231       fw_device
1232    ));
1233 
1234    switch (fw_device->protocol_device.smp_device.current_smp_request)
1235    {
1236       case SMP_FUNCTION_REPORT_GENERAL:
1237          // send the REPORT MANUFACTURER_INFO request
1238          fw_device->protocol_device.smp_device.current_smp_request =
1239             SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION;
1240 
1241          scif_sas_smp_request_construct_report_manufacturer_info(
1242             fw_domain->controller, fw_device
1243          );
1244 
1245          break;
1246 
1247       case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
1248          //send the first SMP DISCOVER request.
1249          fw_device->protocol_device.smp_device.current_activity_phy_index = 0;
1250          fw_device->protocol_device.smp_device.current_smp_request =
1251             SMP_FUNCTION_DISCOVER;
1252 
1253          scif_sas_smp_request_construct_discover(
1254             fw_domain->controller,
1255             fw_device,
1256             fw_device->protocol_device.smp_device.current_activity_phy_index,
1257             NULL, NULL
1258          );
1259          break;
1260 
1261 
1262       case SMP_FUNCTION_DISCOVER:
1263          fw_device->protocol_device.smp_device.current_activity_phy_index++;
1264 
1265          if ( (fw_device->protocol_device.smp_device.current_activity_phy_index <
1266                   fw_device->protocol_device.smp_device.number_of_phys) )
1267          {
1268             scif_sas_smp_request_construct_discover(
1269                fw_domain->controller,
1270                fw_device,
1271                fw_device->protocol_device.smp_device.current_activity_phy_index,
1272                NULL, NULL
1273             );
1274          }
1275          else
1276             scif_sas_smp_remote_device_finish_initial_discover(fw_device);
1277          break;
1278 
1279 
1280       case SMP_FUNCTION_REPORT_PHY_SATA:
1281          scif_sas_smp_request_construct_report_phy_sata(
1282             fw_device->domain->controller,
1283             fw_device,
1284             fw_device->protocol_device.smp_device.current_activity_phy_index
1285          );
1286 
1287          break;
1288 
1289 
1290       case SMP_FUNCTION_PHY_CONTROL:
1291          scif_sas_smp_request_construct_phy_control(
1292             fw_device->domain->controller,
1293             fw_device,
1294             PHY_OPERATION_HARD_RESET,
1295             fw_device->protocol_device.smp_device.current_activity_phy_index,
1296             NULL,
1297             NULL
1298          );
1299 
1300          break;
1301 
1302       default:
1303          break;
1304    }
1305 }
1306 
1307 /**
1308  * @brief This method finishes the initial smp DISCOVER process. There
1309  *        may be a spinup_hold release phase following of initial discover,
1310  *        depending on whether there are SATA device in the domain
1311  *        in SATA_SPINUP_HOLD condition.
1312  *
1313  * @param[in] fw_device The framework smp device that finishes all the
1314  *       DISCOVER requests.
1315  *
1316  * @return none
1317  */
1318 void scif_sas_smp_remote_device_finish_initial_discover(
1319    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1320 )
1321 {
1322    SCIF_SAS_REMOTE_DEVICE_T * device_in_sata_spinup_hold =
1323       scif_sas_domain_find_device_in_spinup_hold(fw_device->domain);
1324 
1325    SCIF_LOG_TRACE((
1326       sci_base_object_get_logger(fw_device),
1327       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1328       "scif_sas_smp_remote_device_finish_initial_discover(0x%x) enter\n",
1329       fw_device
1330    ));
1331 
1332    if ( device_in_sata_spinup_hold != NULL )
1333    {
1334      //call the common private routine to reset all fields of this smp device.
1335      scif_sas_smp_remote_device_clear(fw_device);
1336 
1337      //Move on to next activity SPINUP_HOLD_RELEASE
1338      fw_device->protocol_device.smp_device.current_activity =
1339         SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_SATA_SPINUP_HOLD_RELEASE;
1340 
1341       //create the timer to delay a little bit before going to
1342       //sata spinup hold release activity.
1343       if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1344       {
1345       fw_device->protocol_device.smp_device.smp_activity_timer =
1346          scif_cb_timer_create(
1347             (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1348             (SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_sata_spinup_hold_release,
1349             (void*)fw_device
1350          );
1351       }
1352       else
1353       {
1354          ASSERT (0);
1355       }
1356 
1357       scif_cb_timer_start(
1358          (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1359          fw_device->protocol_device.smp_device.smp_activity_timer,
1360          SMP_SPINUP_HOLD_RELEASE_WAIT_DURATION
1361       );
1362    }
1363    else
1364       scif_sas_smp_remote_device_finish_discover(fw_device);
1365 }
1366 
1367 
1368 /**
1369  * @brief This method finishes the smp DISCOVER process.
1370  *
1371  * @param[in] fw_device The framework smp device that finishes all the
1372  *       DISCOVER requests.
1373  *
1374  * @return none
1375  */
1376 void scif_sas_smp_remote_device_finish_discover(
1377    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1378 )
1379 {
1380    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1381 
1382    SCIF_LOG_TRACE((
1383       sci_base_object_get_logger(fw_device),
1384       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1385       "scif_sas_smp_remote_device_finish_discover(0x%x) enter\n",
1386       fw_device
1387    ));
1388 
1389    if ( fw_domain->is_config_route_table_needed
1390        && fw_device->protocol_device.smp_device.smp_phy_list.list_head != NULL)
1391       scif_sas_smp_remote_device_configure_upstream_expander_route_info(fw_device);
1392 
1393    //call the common private routine to reset all fields of this smp device.
1394    scif_sas_smp_remote_device_clear(fw_device);
1395 
1396 #ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
1397    scif_sas_smp_remote_device_print_smp_phy_list(fw_device);
1398 #endif
1399 
1400    //notify domain this smp device's discover finishes, it's up to domain
1401    //to continue the discover process in a bigger scope.
1402    scif_sas_domain_continue_discover(fw_domain);
1403 }
1404 
1405 
1406 /**
1407  * @brief This method continues the smp Target Reset (Phy Control) process.
1408  *
1409  * @param[in] fw_device The framework smp device that a smp reset targets to.
1410  *
1411  * @return none
1412  */
1413 void scif_sas_smp_remote_device_continue_target_reset(
1414    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1415    SCIF_SAS_REQUEST_T       * fw_request
1416 )
1417 {
1418    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
1419    SCIF_SAS_REMOTE_DEVICE_T * target_device =
1420       scif_sas_domain_get_device_by_containing_device(
1421          fw_device->domain,
1422          fw_device,
1423          fw_device->protocol_device.smp_device.current_activity_phy_index
1424       );
1425 
1426    SCIF_LOG_TRACE((
1427       sci_base_object_get_logger(fw_device),
1428       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1429       "scif_sas_smp_remote_device_continue_target_reset(0x%x, 0x%x) enter\n",
1430       fw_device, fw_request
1431    ));
1432 
1433    if (fw_device->protocol_device.smp_device.current_smp_request ==
1434           SMP_FUNCTION_PHY_CONTROL)
1435    {
1436       //query the core remote device to get suggested reset timeout value
1437       //then scale down by factor of 8 to get the duration of the pause
1438       //before sending out Discover command to poll.
1439       U32 delay =
1440          (scic_remote_device_get_suggested_reset_timeout(target_device->core_object)/8);
1441 
1442       //create the timer to send Discover command polling target device's
1443       //coming back.
1444       if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1445       {
1446          fw_device->protocol_device.smp_device.smp_activity_timer =
1447             scif_cb_timer_create(
1448                (SCI_CONTROLLER_HANDLE_T *)fw_controller,
1449                (SCI_TIMER_CALLBACK_T)scif_sas_smp_remote_device_target_reset_poll,
1450                (void*)fw_request
1451             );
1452       }
1453       else
1454       {
1455          ASSERT(0);
1456       }
1457 
1458       //start the timer
1459       scif_cb_timer_start(
1460          (SCI_CONTROLLER_HANDLE_T)fw_controller,
1461          fw_device->protocol_device.smp_device.smp_activity_timer,
1462          delay
1463       );
1464    }
1465    else if (fw_device->protocol_device.smp_device.current_smp_request ==
1466           SMP_FUNCTION_DISCOVER)
1467    {
1468       //tell target reset successful
1469       scif_sas_remote_device_target_reset_complete(
1470          target_device, fw_request, SCI_SUCCESS);
1471    }
1472 }
1473 
1474 /**
1475  * @brief This routine is invoked by timer or when 2 BCN are received
1476  *        after Phy Control command. This routine will construct a
1477  *        Discover command to the same expander phy to poll the target
1478  *        device's coming back. This new request is then put into
1479  *        high priority queue and will be started by a DPC soon.
1480  *
1481  * @param[in] fw_request The scif request for smp activities.
1482  */
1483 void scif_sas_smp_remote_device_target_reset_poll(
1484    SCIF_SAS_REQUEST_T       * fw_request
1485 )
1486 {
1487    SCIF_SAS_REMOTE_DEVICE_T * fw_device = fw_request->device;
1488    SCIF_SAS_CONTROLLER_T * fw_controller = fw_device->domain->controller;
1489    void * new_command_handle;
1490 
1491    SCIF_LOG_TRACE((
1492       sci_base_object_get_logger(fw_device),
1493       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1494       "scif_sas_smp_remote_device_target_reset_poll(0x%x) enter\n",
1495       fw_request
1496    ));
1497 
1498    // Before we construct new io using the same memory, we need to
1499    // remove the IO from the list of outstanding requests on the domain
1500    // so that we don't damage the domain's fast list of request.
1501    sci_fast_list_remove_element(&fw_request->list_element);
1502 
1503    fw_device->protocol_device.smp_device.current_smp_request =
1504       SMP_FUNCTION_DISCOVER;
1505 
1506    //sent smp discover request to poll on remote device's coming back.
1507    //construct Discover command using the same memory as fw_request.
1508    new_command_handle = scif_sas_smp_request_construct_discover(
1509       fw_device->domain->controller,
1510       fw_device,
1511       fw_device->protocol_device.smp_device.current_activity_phy_index,
1512       (void *)sci_object_get_association(fw_request),
1513       (void *)fw_request
1514    );
1515 
1516    //put into the high priority queue.
1517    sci_pool_put(fw_controller->hprq.pool, (POINTER_UINT) new_command_handle);
1518 
1519    //schedule the DPC to start new Discover command.
1520    scif_cb_start_internal_io_task_schedule(
1521       fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
1522    );
1523 }
1524 
1525 
1526 /**
1527  * @brief This method fails discover process.
1528  *
1529  * @param[in] fw_device The framework smp device that failed at current
1530  *       activity.
1531  *
1532  * @return none
1533  */
1534 void scif_sas_smp_remote_device_fail_discover(
1535    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1536 )
1537 {
1538    SCIF_LOG_TRACE((
1539       sci_base_object_get_logger(fw_device),
1540       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1541       "scif_sas_smp_remote_device_fail_discover(0x%x) enter\n",
1542       fw_device
1543    ));
1544 
1545    switch (fw_device->protocol_device.smp_device.current_smp_request)
1546    {
1547       case SMP_FUNCTION_REPORT_GENERAL:
1548       case SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION:
1549          scif_sas_smp_remote_device_finish_discover(fw_device);
1550          break;
1551 
1552       case SMP_FUNCTION_DISCOVER:
1553       case SMP_FUNCTION_REPORT_PHY_SATA:
1554          //Retry limit reached, we will continue to send DISCOVER to next phy.
1555          fw_device->protocol_device.smp_device.current_smp_request =
1556             SMP_FUNCTION_DISCOVER;
1557 
1558          scif_sas_smp_remote_device_continue_discover(fw_device);
1559          break;
1560 
1561       default:
1562          break;
1563    }
1564 }
1565 
1566 
1567 /**
1568  * @brief This method fails Target Reset.
1569  *
1570  * @param[in] fw_device The framework smp device that failed at current
1571  *       activity.
1572  * @param[in] fw_request The smp request created for target reset
1573  *       using external resource.
1574  *
1575  * @return none
1576  */
1577 void scif_sas_smp_remote_device_fail_target_reset(
1578    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1579    SCIF_SAS_REQUEST_T       * fw_request
1580 )
1581 {
1582    SCIF_SAS_REMOTE_DEVICE_T * target_device =
1583       scif_sas_domain_get_device_by_containing_device(
1584          fw_device->domain,
1585          fw_device,
1586          fw_device->protocol_device.smp_device.current_activity_phy_index
1587       );
1588 
1589    SCIF_LOG_TRACE((
1590       sci_base_object_get_logger(fw_device),
1591       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1592       "scif_sas_smp_remote_device_fail_target_reset(0x%x, 0x%x, 0x%x) enter\n",
1593       fw_device, target_device, fw_request
1594    ));
1595 
1596    //tell target reset failed
1597    scif_sas_remote_device_target_reset_complete(
1598       target_device, fw_request, SCI_FAILURE);
1599 }
1600 
1601 /**
1602  * @brief This method init or continue the SATA SPINUP_HOLD RELEASE activity.
1603  * This function searches domain's device list, find a device in STOPPED STATE
1604  * and its connection_rate is SPINIP, then send DISCOVER command to its expander
1605  * phy id to poll. But if searching the domain's device list for SATA devices on
1606  * SPINUP_HOLD finds no device, the activity SPINUP_HOLD_RELEASE is finished.
1607  * We then call fw_domain->device_start_complete_handler() for this smp-device.
1608  *
1609  * @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
1610  *       activity.
1611  *
1612  * @return none
1613  */
1614 void scif_sas_smp_remote_device_sata_spinup_hold_release(
1615    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1616 )
1617 {
1618    SCIF_SAS_DOMAIN_T        * fw_domain = fw_device->domain;
1619    SCIF_SAS_CONTROLLER_T    * fw_controller = fw_domain->controller;
1620    SCIF_SAS_REMOTE_DEVICE_T * device_to_poll = NULL;
1621 
1622    SCIF_LOG_TRACE((
1623       sci_base_object_get_logger(fw_device),
1624       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1625       "scif_sas_smp_remote_device_sata_spinup_hold_release(0x%x) enter\n",
1626       fw_device
1627    ));
1628 
1629    //search throught domain's device list to find a sata device on spinup_hold
1630    //state to poll.
1631    device_to_poll = scif_sas_domain_find_device_in_spinup_hold(fw_domain);
1632 
1633    if (device_to_poll != NULL)
1634    {
1635       //send DISCOVER command to this device's expaner phy.
1636       fw_device->protocol_device.smp_device.current_smp_request =
1637          SMP_FUNCTION_DISCOVER;
1638 
1639       fw_device->protocol_device.smp_device.current_activity_phy_index =
1640         device_to_poll->expander_phy_identifier;
1641 
1642       scif_sas_smp_request_construct_discover(
1643          fw_domain->controller,
1644          fw_device,
1645          fw_device->protocol_device.smp_device.current_activity_phy_index,
1646          NULL, NULL
1647       );
1648 
1649       //schedule the DPC to start new Discover command.
1650       scif_cb_start_internal_io_task_schedule(
1651          fw_controller, scif_sas_controller_start_high_priority_io, fw_controller
1652       );
1653    }
1654    else //SATA SPINUP HOLD RELEASE activity is done.
1655       scif_sas_smp_remote_device_finish_discover (fw_device);
1656 }
1657 
1658 
1659 /**
1660  * @brief This method fail an action of SATA SPINUP_HOLD RELEASE on a single EA
1661  *        SATA device. It will remove a remote_device object for a sata device
1662  *        that fails to come out of spinup_hold.
1663  *
1664  * @param[in] fw_device The framework smp device that is on SATA SPINUP_HOLD_RELEASE
1665  *       activity.
1666  * @param[in] target_device The expander attached device failed being brought out
1667  *       of SPINUP_HOLD state.
1668  *
1669  * @return none
1670  */
1671 void scif_sas_smp_remote_device_fail_target_spinup_hold_release(
1672    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1673    SCIF_SAS_REMOTE_DEVICE_T * target_device
1674 )
1675 {
1676    SCIF_SAS_DOMAIN_T * fw_domain = fw_device->domain;
1677 
1678    SCIF_LOG_TRACE((
1679       sci_base_object_get_logger(fw_device),
1680       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1681       "scif_sas_smp_remote_device_fail_target_spinup_hold_release(0x%x, 0x%x) enter\n",
1682       fw_device, target_device
1683    ));
1684 
1685    //need to remove the device, since we have to give up on spinup_hold_release
1686    //activity on this device.
1687    scif_cb_domain_device_removed(
1688       fw_domain->controller, fw_domain, target_device
1689    );
1690 
1691    //move on to next round of SPINUP_HOLD_REALSE activity.
1692    scif_sas_smp_remote_device_sata_spinup_hold_release(fw_device);
1693 }
1694 
1695 
1696 /**
1697  * @brief This method retry only internal IO for the smp device.
1698  *
1699  * @param[in] fw_device The framework smp device that has an smp request to retry.
1700  * @param[in] io_retry_count current count for times the IO being retried.
1701  * @param[in] delay The time delay before the io gets retried.
1702  *
1703  * @return none
1704  */
1705 void scif_sas_smp_remote_device_retry_internal_io(
1706    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1707    U8                         io_retry_count,
1708    U32                        delay
1709 )
1710 {
1711    SCIF_LOG_TRACE((
1712       sci_base_object_get_logger(fw_device),
1713       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1714       "scif_sas_smp_remote_device_retry_internal_io(0x%x, 0x%x, 0x%x) enter\n",
1715       fw_device, io_retry_count, delay
1716    ));
1717 
1718    fw_device->protocol_device.smp_device.io_retry_count =
1719       io_retry_count;
1720 
1721    //create the timer for poll target device's coming back.
1722    if (fw_device->protocol_device.smp_device.smp_activity_timer == NULL)
1723    {
1724       fw_device->protocol_device.smp_device.smp_activity_timer =
1725          scif_cb_timer_create(
1726             (SCI_CONTROLLER_HANDLE_T *)fw_device->domain->controller,
1727             (SCI_TIMER_CALLBACK_T)scif_sas_smp_internal_request_retry,
1728             (void*)fw_device
1729          );
1730    }
1731    else
1732    {
1733       ASSERT(0);
1734    }
1735    //start the timer for a purpose of waiting.
1736    scif_cb_timer_start(
1737       (SCI_CONTROLLER_HANDLE_T)fw_device->domain->controller,
1738       fw_device->protocol_device.smp_device.smp_activity_timer,
1739       delay
1740    );
1741 }
1742 
1743 
1744 /**
1745  * @brief This method indicates whether an expander device is in Discover
1746  *        process.
1747  *
1748  * @param[in] fw_device The framework smp device.
1749  *
1750  * @return Whether an expander device is in the middle of discovery process.
1751  */
1752 BOOL scif_sas_smp_remote_device_is_in_activity(
1753    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1754 )
1755 {
1756    return(fw_device->protocol_device.smp_device.current_activity
1757           != SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE);
1758 }
1759 
1760 /**
1761  * @brief This method search through the smp phy list of an expander to
1762  *        find a smp phy by its phy id of the expander.
1763  *
1764  * @param[in] phy_identifier The search criteria.
1765  * @param[in] smp_remote_device The expander that owns the smp phy list.
1766  *
1767  * @return The found smp phy or a NULL pointer to indicate no smp phy is found.
1768  */
1769 SCIF_SAS_SMP_PHY_T * scif_sas_smp_remote_device_find_smp_phy_by_id(
1770    U8                             phy_identifier,
1771    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device
1772 )
1773 {
1774    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1775    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1776 
1777    while (element != NULL)
1778    {
1779       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1780       element = sci_fast_list_get_next(element);
1781 
1782       if (curr_smp_phy->phy_identifier == phy_identifier)
1783          return curr_smp_phy;
1784    }
1785 
1786    return NULL;
1787 }
1788 
1789 /**
1790  * @brief This method takes care of removing smp phy list of a smp devcie, which is
1791  *           about to be removed.
1792  *
1793  * @param[in] fw_device The expander device that is about to be removed.
1794  *
1795  * @return none.
1796  */
1797 void scif_sas_smp_remote_device_removed(
1798    SCIF_SAS_REMOTE_DEVICE_T * this_device
1799 )
1800 {
1801    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
1802       &this_device->protocol_device.smp_device;
1803 
1804    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1805    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1806 
1807    SCIF_LOG_TRACE((
1808       sci_base_object_get_logger(this_device),
1809       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1810       "scif_sas_smp_remote_device_removed(0x%x) enter\n",
1811       this_device
1812    ));
1813 
1814    //remove all the smp phys in this device's smp_phy_list, and the conterpart smp phys
1815    //in phy connections.
1816    while (element != NULL)
1817    {
1818       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1819       element = sci_fast_list_get_next(element);
1820 
1821       scif_sas_smp_phy_destruct(curr_smp_phy);
1822    }
1823 
1824    this_device->protocol_device.smp_device.number_of_phys = 0;
1825    this_device->protocol_device.smp_device.expander_route_indexes = 0;
1826    this_device->protocol_device.smp_device.is_table_to_table_supported = FALSE;
1827    this_device->protocol_device.smp_device.is_externally_configurable  = FALSE;
1828    this_device->protocol_device.smp_device.is_able_to_config_others    = FALSE;
1829 
1830    scif_sas_smp_remote_device_clear(this_device);
1831 }
1832 
1833 
1834 /**
1835  * @brief This method takes care of terminated smp request to a smp device. The
1836  *        terminated smp request is most likely timeout and being aborted. A timeout
1837  *        maybe due to OPEN REJECT (NO DESTINATION).
1838  *
1839  * @param[in] fw_device The expander device that a timed out smp request towards to.
1840  * @param[in] fw_request A failed smp request that is terminated by scic.
1841  *
1842  * @return none.
1843  */
1844 void scif_sas_smp_remote_device_terminated_request_handler(
1845    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1846    SCIF_SAS_REQUEST_T       * fw_request
1847 )
1848 {
1849    SCIF_LOG_TRACE((
1850       sci_base_object_get_logger(fw_device),
1851       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1852       "scif_sas_smp_remote_device_terminated_request_handler(0x%x, 0x%x) enter\n",
1853       fw_device, fw_request
1854    ));
1855 
1856    scif_sas_smp_remote_device_decode_smp_response(
1857       fw_device, fw_request, NULL, SCI_FAILURE_RETRY_REQUIRED
1858    );
1859 }
1860 
1861 
1862 /**
1863  * @brief This method allocates and populates the smp phy list of a expander device.
1864  *
1865  * @param[in] fw_device The expander device, whose smp phy list is to be populated after
1866  *                      getting REPORT GENERAL response.
1867  *
1868  * @return none.
1869  */
1870 void scif_sas_smp_remote_device_populate_smp_phy_list(
1871    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1872 )
1873 {
1874    SCIF_SAS_SMP_PHY_T * this_smp_phy = NULL;
1875    U8                   expander_phy_id = 0;
1876 
1877    SCIF_LOG_TRACE((
1878       sci_base_object_get_logger(fw_device),
1879       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1880       "scif_sas_smp_remote_device_populate_smp_phy_list(0x%x) enter\n",
1881       fw_device
1882    ));
1883 
1884    for ( expander_phy_id = 0;
1885          expander_phy_id < fw_device->protocol_device.smp_device.number_of_phys;
1886          expander_phy_id++ )
1887    {
1888       this_smp_phy =
1889          scif_sas_controller_allocate_smp_phy(fw_device->domain->controller);
1890 
1891       ASSERT( this_smp_phy != NULL );
1892 
1893       if ( this_smp_phy != NULL )
1894          scif_sas_smp_phy_construct(this_smp_phy, fw_device, expander_phy_id);
1895    }
1896 }
1897 
1898 
1899 /**
1900  * @brief This method updates a smp phy of a expander device based on DISCOVER response.
1901  *
1902  * @param[in] fw_device The expander device, one of whose smp phys is to be updated.
1903  * @param[in] discover_response The smp DISCOVER response.
1904  *
1905  * @return SCI_STATUS If a smp phy pair between expanders has invalid routing attribute,
1906  *                    return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION, otherwise,
1907  *                    return SCI_SUCCESS
1908  */
1909 SCI_STATUS scif_sas_smp_remote_device_save_smp_phy_info(
1910    SCIF_SAS_REMOTE_DEVICE_T * fw_device,
1911    SMP_RESPONSE_DISCOVER_T  * discover_response
1912 )
1913 {
1914    SCI_STATUS status = SCI_SUCCESS;
1915    SCIF_SAS_SMP_PHY_T * smp_phy = NULL;
1916    SCIF_SAS_REMOTE_DEVICE_T * attached_device = NULL;
1917 
1918     SCIF_LOG_TRACE((
1919       sci_base_object_get_logger(fw_device),
1920       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
1921       "scif_sas_smp_remote_device_save_smp_phy_info(0x%x, 0x%x) enter\n",
1922       fw_device, discover_response
1923    ));
1924 
1925    smp_phy = scif_sas_smp_remote_device_find_smp_phy_by_id(
1926                 discover_response->phy_identifier,
1927                 &fw_device->protocol_device.smp_device
1928              );
1929 
1930    ASSERT( smp_phy != NULL );
1931 
1932    //Note, attached_device could be NULL, not all the smp phy have to connected to a device.
1933    attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
1934       scif_domain_get_device_by_sas_address(
1935          fw_device->domain, &discover_response->attached_sas_address);
1936 
1937    if (smp_phy != NULL)
1938    {
1939       scif_sas_smp_phy_save_information(
1940          smp_phy, attached_device, discover_response);
1941    }
1942 
1943    //handle the special case of smp phys between expanders.
1944    if ( discover_response->protocols.u.bits.attached_smp_target )
1945    {
1946        //this fw_device is a child expander, just found its parent expander.
1947        //And there is no smp_phy constructed yet, record this phy connection.
1948        if ( attached_device != NULL
1949            && attached_device == fw_device->containing_device )
1950        {
1951           //record the smp phy info, for this phy connects to a upstream smp device.
1952           //the connection of a pair of smp phys are completed.
1953           status = scif_sas_smp_phy_set_attached_phy(
1954                       smp_phy,
1955                       discover_response->attached_phy_identifier,
1956                       attached_device
1957                    );
1958 
1959           if (status == SCI_SUCCESS)
1960           {
1961              //check the routing attribute for this phy and its containing device's
1962              //expander_phy_routing_attribute.
1963              if ( scif_sas_smp_phy_verify_routing_attribute(
1964                      smp_phy, smp_phy->u.attached_phy) != SCI_SUCCESS )
1965                 return SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION;
1966           }
1967        }
1968     }
1969 
1970     return status;
1971 }
1972 
1973 #ifdef SCI_SMP_PHY_LIST_DEBUG_PRINT
1974 void scif_sas_smp_remote_device_print_smp_phy_list(
1975    SCIF_SAS_REMOTE_DEVICE_T * fw_device
1976 )
1977 {
1978    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device = &fw_device->protocol_device.smp_device;
1979    SCI_FAST_LIST_ELEMENT_T  * element = smp_remote_device->smp_phy_list.list_head;
1980    SCIF_SAS_SMP_PHY_T * curr_smp_phy = NULL;
1981 
1982    SCIF_LOG_ERROR((
1983       sci_base_object_get_logger(fw_device),
1984       SCIF_LOG_OBJECT_REMOTE_DEVICE,
1985       "==========EXPANDER DEVICE (0x%x) smp phy list========== \n",
1986       fw_device
1987    ));
1988 
1989    while (element != NULL)
1990    {
1991       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
1992       element = sci_fast_list_get_next(element);
1993 
1994       //print every thing about a smp phy
1995       SCIF_LOG_ERROR((
1996          sci_base_object_get_logger(fw_device),
1997          SCIF_LOG_OBJECT_REMOTE_DEVICE,
1998          "SMP_PHY_%d (0x%x), attached device(0x%x), attached_sas_address(%x%x) attached_device_type(%d), routing_attribute(%d)\n",
1999          curr_smp_phy->phy_identifier, curr_smp_phy,
2000          curr_smp_phy->u.end_device,
2001          curr_smp_phy->attached_sas_address.high, curr_smp_phy->attached_sas_address.low,
2002          curr_smp_phy->attached_device_type,
2003          curr_smp_phy->routing_attribute
2004       ));
2005    }
2006 }
2007 #endif
2008 
2009 
2010 /**
2011  * @brief This method configure upstream expander(s)' (if there is any) route info.
2012  *
2013  * @param[in] this_device The expander device that is currently in discover process.
2014  *
2015  * @return none.
2016  */
2017 void scif_sas_smp_remote_device_configure_upstream_expander_route_info(
2018    SCIF_SAS_REMOTE_DEVICE_T * this_device
2019 )
2020 {
2021    SCIF_SAS_REMOTE_DEVICE_T * curr_child_expander = this_device;
2022    SCIF_SAS_REMOTE_DEVICE_T * curr_parent_expander =
2023       scif_sas_remote_device_find_upstream_expander(this_device);
2024 
2025    SCIF_SAS_REMOTE_DEVICE_T * curr_config_route_info_expander = NULL;
2026 
2027    SCIF_LOG_TRACE((
2028       sci_base_object_get_logger(this_device),
2029       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2030       "scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
2031       this_device
2032    ));
2033 
2034    //traverse back to find root device.
2035    while(curr_parent_expander != NULL )
2036    {
2037       //must set destination_smp_phy outside of find_upstream_expander() using the device
2038       //that is just about to finish the discovery.
2039       curr_parent_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy =
2040          (SCIF_SAS_SMP_PHY_T*)sci_fast_list_get_object(
2041              this_device->protocol_device.smp_device.smp_phy_list.list_head);
2042 
2043       curr_child_expander = curr_parent_expander;
2044       curr_parent_expander = scif_sas_remote_device_find_upstream_expander(curr_child_expander);
2045    }
2046 
2047    //found the root device: curr_child_expander. configure it and its downstream expander(s) till
2048    //this_device or a self-configuring expander that configures others;
2049    curr_config_route_info_expander = curr_child_expander;
2050 
2051    while ( curr_config_route_info_expander != NULL
2052           && curr_config_route_info_expander != this_device
2053           && curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity
2054                 == SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE
2055          )
2056    {
2057       if (curr_config_route_info_expander->protocol_device.smp_device.is_externally_configurable)
2058       {
2059          SCIF_SAS_SMP_PHY_T * phy_being_config =
2060             curr_config_route_info_expander->protocol_device.smp_device.config_route_smp_phy_anchor;
2061 
2062          curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index =
2063             phy_being_config->config_route_table_index_anchor;
2064 
2065          if (curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index != 0)
2066             curr_config_route_info_expander->protocol_device.smp_device.curr_config_route_index++;
2067 
2068          curr_config_route_info_expander->protocol_device.smp_device.scheduled_activity =
2069             SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
2070 
2071          //Find a downstream expander that has curr_config_route_destination_smp_phy.owning device
2072          //same as curr_config_route_info_expander.
2073          curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
2074             curr_config_route_info_expander);
2075       }
2076       else if (curr_config_route_info_expander->protocol_device.smp_device.is_able_to_config_others)
2077       {
2078          //no need to config route table to this expander and its children.
2079          //find its downstream expander and clear the planned config route table activity.
2080          SCIF_SAS_REMOTE_DEVICE_T * curr_downstream_expander =
2081             scif_sas_remote_device_find_downstream_expander(
2082                curr_config_route_info_expander);
2083 
2084          scif_sas_smp_remote_device_clear(curr_config_route_info_expander);
2085 
2086          while ( curr_downstream_expander != NULL
2087                 && curr_downstream_expander != this_device )
2088          {
2089             scif_sas_smp_remote_device_clear(curr_downstream_expander);
2090             curr_downstream_expander =
2091                scif_sas_remote_device_find_downstream_expander(
2092                   curr_config_route_info_expander);
2093          }
2094 
2095          break;
2096       }
2097       else
2098       {
2099          // current expander is a self-configuring expander, which is not externally
2100          // configurable, and doesn't config others. we need to simply skip this expander.
2101          curr_config_route_info_expander = scif_sas_remote_device_find_downstream_expander(
2102             curr_config_route_info_expander);
2103       }
2104    }
2105 }
2106 
2107 /**
2108  * @brief This method finds the immediate upstream expander of a given expander device.
2109  *
2110  * @param[in] this_device The given expander device, whose upstream expander is to be found.
2111  *
2112  * @return The immediate upstream expander. Or a NULL pointer if this_device is root already.
2113  */
2114 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_upstream_expander(
2115    SCIF_SAS_REMOTE_DEVICE_T * this_device
2116 )
2117 {
2118    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2119       &this_device->protocol_device.smp_device;
2120 
2121    SCIF_SAS_REMOTE_DEVICE_T    * upstream_expander = NULL;
2122 
2123    SCI_FAST_LIST_ELEMENT_T     * element = smp_remote_device->smp_phy_list.list_head;
2124    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2125 
2126    SCIF_LOG_TRACE((
2127       sci_base_object_get_logger(this_device),
2128       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2129       "scif_sas_smp_remote_device_configure_upstream_expander_route_info(0x%x) enter\n",
2130       this_device
2131    ));
2132 
2133    while (element != NULL)
2134    {
2135       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2136       element = sci_fast_list_get_next(element);
2137 
2138       if ( curr_smp_phy->routing_attribute == SUBTRACTIVE_ROUTING_ATTRIBUTE
2139           && ( curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
2140               || curr_smp_phy->attached_device_type == SMP_FANOUT_EXPANDER_DEVICE)
2141           && curr_smp_phy->u.attached_phy != NULL
2142           && curr_smp_phy->u.attached_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE )
2143       {
2144          //set the current_activity and current_config_route_index for that
2145          //upstream expander.
2146          upstream_expander = curr_smp_phy->u.attached_phy->owning_device;
2147 
2148          upstream_expander->protocol_device.smp_device.current_smp_request =
2149             SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION;
2150 
2151          //if the upstream_expander's config route table method is config phy0 only or
2152          //config all phys, the current activity phy is found.
2153          upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2154             scif_sas_smp_remote_device_find_smp_phy_by_id(
2155                curr_smp_phy->u.attached_phy->phy_identifier,
2156                &(curr_smp_phy->u.attached_phy->owning_device->protocol_device.smp_device)
2157             );
2158 
2159          //if the upstream_expander's config route table method is config middle phy only
2160          //config highest phy only, the current activity phy needs a update.
2161          if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
2162                  == SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY )
2163          {
2164             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2165                scif_sas_smp_phy_find_middle_phy_in_wide_port (
2166                   upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
2167                );
2168          }
2169          else if ( scif_sas_smp_remote_device_get_config_route_table_method(upstream_expander)
2170                       == SCIF_SAS_CONFIG_ROUTE_TABLE_HIGHEST_PHY_ONLY )
2171          {
2172             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor =
2173                scif_sas_smp_phy_find_highest_phy_in_wide_port (
2174                   upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor
2175                );
2176          }
2177 
2178          upstream_expander->protocol_device.smp_device.current_activity_phy_index =
2179             upstream_expander->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier;
2180 
2181          return upstream_expander;
2182       }
2183    }
2184 
2185    return NULL;
2186 }
2187 
2188 
2189 /**
2190  * @brief This method finds the immediate downstream expander of a given expander device.
2191  *
2192  * @param[in] this_device The given expander device, whose downstream expander is to be found.
2193  *
2194  * @return The immediate downstream expander. Or a NULL pointer if there is none.
2195  */
2196 SCIF_SAS_REMOTE_DEVICE_T * scif_sas_remote_device_find_downstream_expander(
2197    SCIF_SAS_REMOTE_DEVICE_T * this_device
2198 )
2199 {
2200    SCIF_SAS_SMP_REMOTE_DEVICE_T * this_smp_remote_device =
2201       &this_device->protocol_device.smp_device;
2202 
2203    SCIF_SAS_REMOTE_DEVICE_T    * downstream_expander = NULL;
2204 
2205    SCI_FAST_LIST_ELEMENT_T     * element = this_smp_remote_device->smp_phy_list.list_head;
2206    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2207 
2208    SCIF_LOG_TRACE((
2209       sci_base_object_get_logger(this_device),
2210       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2211       "scif_sas_remote_device_find_downstream_expander(0x%x) enter\n",
2212       this_device
2213    ));
2214 
2215    while (element != NULL)
2216    {
2217       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2218       element = sci_fast_list_get_next(element);
2219 
2220       if ( curr_smp_phy->routing_attribute == TABLE_ROUTING_ATTRIBUTE
2221           && curr_smp_phy->attached_device_type == SMP_EDGE_EXPANDER_DEVICE
2222           && curr_smp_phy->u.attached_phy != NULL)
2223       {
2224          //set the current_activity and current_config_route_index for that
2225          //upstream expander.
2226          downstream_expander = curr_smp_phy->u.attached_phy->owning_device;
2227 
2228          if ( downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy != NULL
2229              && downstream_expander->protocol_device.smp_device.curr_config_route_destination_smp_phy->owning_device ==
2230                 this_smp_remote_device->curr_config_route_destination_smp_phy->owning_device )
2231             return downstream_expander;
2232       }
2233    }
2234 
2235    return NULL;
2236 }
2237 
2238 
2239 /**
2240  * @brief This method follows route table optimization rule to check if a destination_device
2241  *        should be recorded in the device_being_config's route table
2242  *
2243  * @param[in] device_being_config The upstream expander device, whose route table is being configured.
2244  * @param[in] destination_smp_phy A smp phy whose attached device is potentially to be
2245  *               recorded in route table.
2246  *
2247  * @return BOOL This method returns TRUE if a destination_device should be recorded in route table.
2248  *              This method returns FALSE if a destination_device need not to be recorded
2249  *              in route table.
2250  */
2251 BOOL scif_sas_smp_remote_device_do_config_route_info(
2252    SCIF_SAS_REMOTE_DEVICE_T * device_being_config,
2253    SCIF_SAS_SMP_PHY_T       * destination_smp_phy
2254 )
2255 {
2256    SCI_SAS_ADDRESS_T device_being_config_sas_address;
2257 
2258    SCIF_LOG_TRACE((
2259       sci_base_object_get_logger(device_being_config),
2260       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2261       "scif_sas_smp_remote_device_do_config_route_info(0x%x, 0x%x) enter\n",
2262       device_being_config, destination_smp_phy
2263    ));
2264 
2265    scic_remote_device_get_sas_address(
2266       device_being_config->core_object, &device_being_config_sas_address
2267    );
2268 
2269    //refer to SAS-2 spec 4.8.3, rule (b)
2270    if ((destination_smp_phy->attached_sas_address.low == 0
2271         && destination_smp_phy->attached_sas_address.high == 0)
2272        && (destination_smp_phy->attached_device_type == SMP_NO_DEVICE_ATTACHED))
2273    {
2274       return FALSE;
2275    }
2276 
2277    //refer to SAS-2 spec 4.8.3, rule (c), self-referencing.
2278    if (destination_smp_phy->attached_sas_address.high ==
2279           device_being_config_sas_address.high
2280        && destination_smp_phy->attached_sas_address.low ==
2281              device_being_config_sas_address.low)
2282    {
2283       return FALSE;
2284    }
2285 
2286    //There will be no cases that falling into rule (a), (d), (e) to be excluded,
2287    //based on our current mechanism of cofig route table.
2288 
2289    return TRUE;
2290 }
2291 
2292 
2293 /**
2294  * @brief This method configures device_being_config's route table for all the enclosed devices in
2295  *           a downstream smp device, destination_device.
2296  *
2297  * @param[in] device_being_config The upstream expander device, whose route table is being configured.
2298  *
2299  * @return None
2300  */
2301 void scif_sas_smp_remote_device_configure_route_table(
2302    SCIF_SAS_REMOTE_DEVICE_T * device_being_config
2303 )
2304 {
2305    //go through the smp phy list of this_device.
2306    SCI_FAST_LIST_ELEMENT_T     * element =
2307       &(device_being_config->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
2308    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2309 
2310    SCIF_LOG_TRACE((
2311       sci_base_object_get_logger(device_being_config),
2312       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2313       "scif_sas_smp_remote_device_configure_route_table(0x%x) enter\n",
2314       device_being_config
2315    ));
2316 
2317    device_being_config->protocol_device.smp_device.current_activity =
2318       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE;
2319 
2320    while (element != NULL)
2321    {
2322       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2323       element = sci_fast_list_get_next(element);
2324 
2325       //check if this phy needs to be added to the expander's route table.
2326       if (scif_sas_smp_remote_device_do_config_route_info(
2327              device_being_config, curr_smp_phy) == TRUE )
2328       {
2329          SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2330             &device_being_config->protocol_device.smp_device;
2331 
2332          smp_remote_device->curr_config_route_destination_smp_phy =
2333             curr_smp_phy;
2334 
2335          //Then config this_device's route table entry at the phy and next route_index.
2336          //send config_route_info using curr_smp_phy.phy_identifier and sas_address.
2337          scif_sas_smp_request_construct_config_route_info(
2338             device_being_config->domain->controller,
2339             device_being_config,
2340             smp_remote_device->current_activity_phy_index,
2341             smp_remote_device->curr_config_route_index,
2342             curr_smp_phy->attached_sas_address,
2343             FALSE
2344          );
2345 
2346          //schedule the DPC.
2347          scif_cb_start_internal_io_task_schedule(
2348             device_being_config->domain->controller,
2349             scif_sas_controller_start_high_priority_io,
2350             device_being_config->domain->controller
2351          );
2352 
2353          //stop here, we need to wait for config route info's response then send
2354          //the next one.
2355          break;
2356       }
2357    }
2358 }
2359 
2360 
2361 /**
2362  * @brief This method walks through an expander's route table to clean table
2363  *           attribute phys' route entries. This routine finds one table entry
2364  *           to clean and will be called repeatly till it finishes cleanning the
2365  *           whole table.
2366  *
2367  * @param[in] fw_device The expander device, whose route table entry is to be cleaned.
2368  *
2369  * @return None.
2370  */
2371 void scif_sas_smp_remote_device_clean_route_table(
2372    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2373 )
2374 {
2375    SCIF_SAS_SMP_PHY_T * smp_phy_being_config =
2376       scif_sas_smp_remote_device_find_smp_phy_by_id(
2377          fw_device->protocol_device.smp_device.current_activity_phy_index,
2378          &(fw_device->protocol_device.smp_device)
2379       );
2380 
2381    SCIF_LOG_TRACE((
2382       sci_base_object_get_logger(fw_device),
2383       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2384       "scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
2385       fw_device
2386    ));
2387 
2388    //from anchors, start to clean all the other route table entries.
2389    fw_device->protocol_device.smp_device.curr_config_route_index++;
2390 
2391    if ( fw_device->protocol_device.smp_device.curr_config_route_index >=
2392            fw_device->protocol_device.smp_device.expander_route_indexes )
2393    {
2394       fw_device->protocol_device.smp_device.curr_config_route_index = 0;
2395 
2396       do //find next table attribute PHY.
2397       {
2398          fw_device->protocol_device.smp_device.current_activity_phy_index++;
2399          if (fw_device->protocol_device.smp_device.current_activity_phy_index ==
2400                 fw_device->protocol_device.smp_device.number_of_phys)
2401             fw_device->protocol_device.smp_device.current_activity_phy_index=0;
2402 
2403          //phy_index changed, so update the smp_phy_being_config.
2404          smp_phy_being_config =
2405             scif_sas_smp_remote_device_find_smp_phy_by_id(
2406                fw_device->protocol_device.smp_device.current_activity_phy_index,
2407                &(fw_device->protocol_device.smp_device)
2408             );
2409       } while( smp_phy_being_config->routing_attribute != TABLE_ROUTING_ATTRIBUTE );
2410 
2411       if ( smp_phy_being_config->phy_identifier !=
2412               fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier)
2413       {
2414          if (smp_phy_being_config->config_route_table_index_anchor != 0)
2415             fw_device->protocol_device.smp_device.curr_config_route_index =
2416                smp_phy_being_config->config_route_table_index_anchor + 1;
2417          else
2418             fw_device->protocol_device.smp_device.curr_config_route_index = 0;
2419       }
2420    }
2421 
2422    if ( !(fw_device->protocol_device.smp_device.current_activity_phy_index ==
2423              fw_device->protocol_device.smp_device.config_route_smp_phy_anchor->phy_identifier
2424           && fw_device->protocol_device.smp_device.curr_config_route_index == 0)
2425       )
2426    {
2427       //clean this route entry.
2428       scif_sas_smp_remote_device_clean_route_table_entry(fw_device);
2429    }
2430    else
2431    {
2432       fw_device->protocol_device.smp_device.is_route_table_cleaned = TRUE;
2433 
2434       //set this device's activity to NON.
2435       fw_device->protocol_device.smp_device.current_activity =
2436          SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_NONE;
2437 
2438       //we need to notify domain that this device finished config route table, domain
2439       //may pick up other activities (i.e. Discover) for other expanders.
2440       scif_sas_domain_continue_discover(fw_device->domain);
2441    }
2442 }
2443 
2444 /**
2445  * @brief This method cleans a device's route table antry.
2446  *
2447  * @param[in] fw_device The expander device, whose route table entry is to be cleaned.
2448  *
2449  * @return None.
2450  */
2451 void scif_sas_smp_remote_device_clean_route_table_entry(
2452    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2453 )
2454 {
2455    SCI_SAS_ADDRESS_T empty_sas_address;
2456    SCIF_SAS_SMP_REMOTE_DEVICE_T * smp_remote_device =
2457       &(fw_device->protocol_device.smp_device);
2458 
2459    SCIF_LOG_TRACE((
2460       sci_base_object_get_logger(fw_device),
2461       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2462       "scif_sas_smp_remote_device_clean_route_table(0x%x) enter\n",
2463       fw_device
2464    ));
2465 
2466    empty_sas_address.high = 0;
2467    empty_sas_address.low = 0;
2468 
2469    scif_sas_smp_request_construct_config_route_info(
2470       fw_device->domain->controller,
2471       fw_device,
2472       smp_remote_device->current_activity_phy_index,
2473       smp_remote_device->curr_config_route_index,
2474       empty_sas_address,
2475       TRUE
2476    );
2477 
2478    //schedule the DPC.
2479    scif_cb_start_internal_io_task_schedule(
2480       fw_device->domain->controller,
2481       scif_sas_controller_start_high_priority_io,
2482       fw_device->domain->controller
2483    );
2484 }
2485 
2486 
2487 /**
2488  * @brief This method handles the case of exceeding route index when config route table
2489  *           for a device, by removing the attached device of current config route
2490  *           destination smp phy and the rest of smp phys in the same smp phy list.
2491  *
2492  * @param[in] fw_device The expander device, whose route table to be edited but failed
2493  *               with a SMP function result of INDEX DOES NOT EXIST.
2494  *
2495  * @return None.
2496  */
2497 void scif_sas_smp_remote_device_cancel_config_route_table_activity(
2498    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2499 )
2500 {
2501    //go through the rest of the smp phy list of destination device.
2502    SCI_FAST_LIST_ELEMENT_T     * element =
2503       &(fw_device->protocol_device.smp_device.curr_config_route_destination_smp_phy->list_element);
2504    SCIF_SAS_SMP_PHY_T          * curr_smp_phy = NULL;
2505    SCIF_SAS_REMOTE_DEVICE_T    * curr_attached_device = NULL;
2506 
2507    SCIF_LOG_TRACE((
2508       sci_base_object_get_logger(fw_device),
2509       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2510       "scif_sas_smp_remote_device_cancel_config_route_table_activity(0x%x) enter\n",
2511       fw_device
2512    ));
2513 
2514    while (element != NULL)
2515    {
2516       curr_smp_phy = (SCIF_SAS_SMP_PHY_T*) sci_fast_list_get_object(element);
2517       element = sci_fast_list_get_next(element);
2518 
2519       //check if this phy needs to be added to the expander's route table but can't due to
2520       //exceeding max route index.
2521       if (scif_sas_smp_remote_device_do_config_route_info(
2522              fw_device, curr_smp_phy) == TRUE )
2523       {
2524          //set the is_currently_discovered to FALSE for attached device. Then when
2525          //domain finish discover, domain will remove this device.
2526          curr_attached_device = (SCIF_SAS_REMOTE_DEVICE_T *)
2527             scif_domain_get_device_by_sas_address(
2528                fw_device->domain, &(curr_smp_phy->attached_sas_address));
2529 
2530          if (curr_attached_device != NULL)
2531             curr_attached_device->is_currently_discovered = FALSE;
2532       }
2533    }
2534 }
2535 
2536 
2537 /**
2538  * @brief This method cancel current activity and terminate the outstanding internal IO
2539  *           if there is one.
2540  *
2541  * @param[in] fw_device The expander device, whose smp activity is to be canceled.
2542  *
2543  * @return None.
2544  */
2545 void scif_sas_smp_remote_device_cancel_smp_activity(
2546    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2547 )
2548 {
2549    SCIF_LOG_TRACE((
2550       sci_base_object_get_logger(fw_device),
2551       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
2552       "scif_sas_smp_remote_device_cancel_smp_activity(0x%x) enter\n",
2553       fw_device
2554    ));
2555 
2556    //Terminate all of the requests in the silicon for this device.
2557    scif_sas_domain_terminate_requests(
2558       fw_device->domain, fw_device, NULL, NULL
2559    );
2560 
2561    if (fw_device->protocol_device.smp_device.current_activity ==
2562           SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_CONFIG_ROUTE_TABLE)
2563       scif_sas_smp_remote_device_cancel_config_route_table_activity(fw_device);
2564 
2565    //Clear the device to stop the smp sctivity.
2566    scif_sas_smp_remote_device_clear(fw_device);
2567 }
2568 
2569 
2570 /**
2571  * @brief This method tells the way to configure route table for a expander. The
2572  *          possible ways are: configure phy 0's route table, configure middle
2573  *          phy's route table, configure highest order phy's route table,
2574  *          configure all phys.
2575  *
2576  * @param[in] fw_device The expander device, whose config route table method is
2577  *               to be chosen.
2578  *
2579  * @return one in 4 possible options.
2580  */
2581 U8 scif_sas_smp_remote_device_get_config_route_table_method(
2582    SCIF_SAS_REMOTE_DEVICE_T * fw_device
2583 )
2584 {
2585    U8 config_route_table_method;
2586 
2587    //config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_MIDDLE_PHY_ONLY;
2588    config_route_table_method = SCIF_SAS_CONFIG_ROUTE_TABLE_ALL_PHYS;
2589 
2590    return config_route_table_method;
2591 }
2592 
2593 
2594 /**
2595  * @brief This method starts the EA target reset process by constructing
2596  *           and starting a PHY CONTROL (hard reset) smp request.
2597  *
2598  * @param[in] expander_device The expander device, to which a PHY Control smp command is
2599  *               sent.
2600  * @param[in] target_device The expander attahced target device, to which the target reset
2601  *               request is sent.
2602  * @param[in] fw_request The target reset task request.
2603  *
2604  * @return none
2605  */
2606 void scif_sas_smp_remote_device_start_target_reset(
2607    SCIF_SAS_REMOTE_DEVICE_T * expander_device,
2608    SCIF_SAS_REMOTE_DEVICE_T * target_device,
2609    SCIF_SAS_REQUEST_T       * fw_request
2610 )
2611 {
2612    SCIF_SAS_CONTROLLER_T * fw_controller = expander_device->domain->controller;
2613 
2614    //set current_activity and current_smp_request to expander device.
2615    expander_device->protocol_device.smp_device.current_activity =
2616       SCIF_SAS_SMP_REMOTE_DEVICE_ACTIVITY_TARGET_RESET;
2617    expander_device->protocol_device.smp_device.current_smp_request =
2618       SMP_FUNCTION_PHY_CONTROL;
2619    expander_device->protocol_device.smp_device.current_activity_phy_index =
2620       target_device->expander_phy_identifier;
2621 
2622    //A Phy Control smp request has been constructed towards parent device.
2623    //Walk the high priority io path.
2624    fw_controller->state_handlers->start_high_priority_io_handler(
2625       (SCI_BASE_CONTROLLER_T*) fw_controller,
2626       (SCI_BASE_REMOTE_DEVICE_T*) expander_device,
2627       (SCI_BASE_REQUEST_T*) fw_request,
2628       SCI_CONTROLLER_INVALID_IO_TAG
2629    );
2630 }
2631 
2632 
2633