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