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