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 /**
57  * @file
58  *
59  * @brief
60  */
61 
62 #include <dev/isci/scil/scic_remote_device.h>
63 
64 #include <dev/isci/scil/scif_sas_remote_device.h>
65 #include <dev/isci/scil/scif_sas_domain.h>
66 #include <dev/isci/scil/scif_sas_logger.h>
67 
68 
69 /**
70  * This constant indicates the number of milliseconds to wait for the core
71  * to start/stop it's remote device object.
72  */
73 //#define SCIF_SAS_REMOTE_DEVICE_CORE_OP_TIMEOUT 1000
74 
75 //******************************************************************************
76 //* P R O T E C T E D   M E T H O D S
77 //******************************************************************************
78 
79 /**
80  * @brief This method implements the actions taken when entering the
81  *        INITIAL state.  This basically, causes an immediate transition
82  *        into the STOPPED state.
83  *
84  * @param[in]  object This parameter specifies the base object for which
85  *             the state transition is occurring.  This is cast into a
86  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
87  *
88  * @return none
89  */
90 static
scif_sas_remote_device_initial_state_enter(SCI_BASE_OBJECT_T * object)91 void scif_sas_remote_device_initial_state_enter(
92    SCI_BASE_OBJECT_T *object
93 )
94 {
95    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
96 
97    SET_STATE_HANDLER(
98       fw_device,
99       scif_sas_remote_device_state_handler_table,
100       SCI_BASE_REMOTE_DEVICE_STATE_INITIAL
101    );
102 
103    // Initial state is a transitional state to the stopped state
104    sci_base_state_machine_change_state(
105       &fw_device->parent.state_machine,
106       SCI_BASE_REMOTE_DEVICE_STATE_STOPPED
107    );
108 }
109 
110 /**
111  * @brief This method implements the actions taken when entering the
112  *        STOPPED state.  This method updates the domains count of started
113  *        devices and will invoke the destruct method if this entrance into
114  *        the STOPPED state was due to a scif_remote_device_destruct()
115  *        call by the user.
116  *
117  * @param[in]  object This parameter specifies the base object for which
118  *             the state transition is occurring.  This is cast into a
119  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
120  *
121  * @return none
122  */
123 static
scif_sas_remote_device_stopped_state_enter(SCI_BASE_OBJECT_T * object)124 void scif_sas_remote_device_stopped_state_enter(
125    SCI_BASE_OBJECT_T *object
126 )
127 {
128    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
129 
130    SET_STATE_HANDLER(
131       fw_device,
132       scif_sas_remote_device_state_handler_table,
133       SCI_BASE_REMOTE_DEVICE_STATE_STOPPED
134    );
135 
136    // There should be no outstanding requests for this device in the
137    // stopped state.
138    ASSERT(fw_device->request_count == 0);
139 
140    // If we are entering the stopped state as a result of a destruct
141    // request, then let's perform the actual destruct operation now.
142    if (fw_device->destruct_when_stopped == TRUE)
143       fw_device->operation_status
144          = fw_device->state_handlers->parent.destruct_handler(
145               &fw_device->parent
146            );
147 
148    /// @todo What should we do if this call fails?
149    fw_device->domain->state_handlers->device_stop_complete_handler(
150       &fw_device->domain->parent, &fw_device->parent
151    );
152 }
153 
154 /**
155  * @brief This method implements the actions taken when entering the
156  *        STARTING state.  This method will attempt to start the core
157  *        remote device and will kick-start the starting sub-state machine
158  *        if no errors are encountered.
159  *
160  * @param[in]  object This parameter specifies the base object for which
161  *             the state transition is occurring.  This is cast into a
162  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
163  *
164  * @return none
165  */
166 static
scif_sas_remote_device_starting_state_enter(SCI_BASE_OBJECT_T * object)167 void scif_sas_remote_device_starting_state_enter(
168    SCI_BASE_OBJECT_T *object
169 )
170 {
171    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
172 
173    SET_STATE_HANDLER(
174       fw_device,
175       scif_sas_remote_device_state_handler_table,
176       SCI_BASE_REMOTE_DEVICE_STATE_STARTING
177    );
178 
179    SCIF_LOG_INFO((
180       sci_base_object_get_logger(fw_device),
181       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_REMOTE_DEVICE_CONFIG,
182       "RemoteDevice:0x%x starting/configuring\n",
183       fw_device
184    ));
185 
186    fw_device->destination_state =
187       SCIF_SAS_REMOTE_DEVICE_DESTINATION_STATE_READY;
188 
189    sci_base_state_machine_start(&fw_device->starting_substate_machine);
190 
191    fw_device->operation_status = scic_remote_device_start(
192                                     fw_device->core_object,
193                                     SCIF_SAS_REMOTE_DEVICE_CORE_OP_TIMEOUT
194                                  );
195 
196    if (fw_device->operation_status != SCI_SUCCESS)
197    {
198       fw_device->state_handlers->parent.fail_handler(&fw_device->parent);
199 
200       // Something is seriously wrong.  Starting the core remote device
201       // shouldn't fail in anyway in this state.
202       scif_cb_controller_error(fw_device->domain->controller,
203               SCI_CONTROLLER_REMOTE_DEVICE_ERROR);
204    }
205 }
206 
207 /**
208  * @brief This method implements the actions taken when exiting the
209  *        STARTING state.  Currently this method simply stops the
210  *        sub-state machine.
211  *
212  * @param[in]  object This parameter specifies the base object for which
213  *             the state transition is occurring.  This is cast into a
214  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
215  *
216  * @return none
217  */
218 static
scif_sas_remote_device_starting_state_exit(SCI_BASE_OBJECT_T * object)219 void scif_sas_remote_device_starting_state_exit(
220    SCI_BASE_OBJECT_T *object
221 )
222 {
223    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
224 
225    fw_device->destination_state =
226       SCIF_SAS_REMOTE_DEVICE_DESTINATION_STATE_UNSPECIFIED;
227 
228    // Transition immediately into the operational sub-state.
229    sci_base_state_machine_stop(&fw_device->starting_substate_machine);
230 }
231 
232 /**
233  * @brief This method implements the actions taken when entering the
234  *        READY state.  Currently this method simply starts the
235  *        sub-state machine.
236  *
237  * @param[in]  object This parameter specifies the base object for which
238  *             the state transition is occurring.  This is cast into a
239  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
240  *
241  * @return none
242  */
243 static
scif_sas_remote_device_ready_state_enter(SCI_BASE_OBJECT_T * object)244 void scif_sas_remote_device_ready_state_enter(
245    SCI_BASE_OBJECT_T *object
246 )
247 {
248    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
249 
250    // Transition immediately into the operational sub-state.
251    sci_base_state_machine_start(&fw_device->ready_substate_machine);
252 
253 #if defined(DISABLE_WIDE_PORTED_TARGETS)
254    scif_sas_domain_remote_device_start_complete(fw_device->domain,fw_device);
255 #endif
256 }
257 
258 /**
259  * @brief This method implements the actions taken when exiting the
260  *        READY state.  Currently this method simply stops the
261  *        sub-state machine.
262  *
263  * @param[in]  object This parameter specifies the base object for which
264  *             the state transition is occurring.  This is cast into a
265  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
266  *
267  * @return none
268  */
269 static
scif_sas_remote_device_ready_state_exit(SCI_BASE_OBJECT_T * object)270 void scif_sas_remote_device_ready_state_exit(
271    SCI_BASE_OBJECT_T *object
272 )
273 {
274    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
275 
276    // Transition immediately into the operational sub-state.
277    sci_base_state_machine_stop(&fw_device->ready_substate_machine);
278 }
279 
280 /**
281  * @brief This method implements the actions taken when entering the
282  *        STOPPING state.  This includes: stopping the core remote device
283  *        and handling any errors that may occur.
284  *
285  * @param[in]  object This parameter specifies the base object for which
286  *             the state transition is occurring.  This is cast into a
287  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
288  *
289  * @return none
290  */
291 static
scif_sas_remote_device_stopping_state_enter(SCI_BASE_OBJECT_T * object)292 void scif_sas_remote_device_stopping_state_enter(
293    SCI_BASE_OBJECT_T *object
294 )
295 {
296    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
297 
298    SET_STATE_HANDLER(
299       fw_device,
300       scif_sas_remote_device_state_handler_table,
301       SCI_BASE_REMOTE_DEVICE_STATE_STOPPING
302    );
303 
304    fw_device->operation_status = scic_remote_device_stop(
305                                     fw_device->core_object,
306                                     SCIF_SAS_REMOTE_DEVICE_CORE_OP_TIMEOUT
307                                  );
308 
309    // If there was a failure, then transition directly to the stopped state.
310    if (fw_device->operation_status != SCI_SUCCESS)
311    {
312       /**
313        * @todo We may want to consider adding handling to reset the
314        *       structure data for the framework and core devices here
315        *       in order to help aid recovery.
316        */
317 
318       fw_device->state_handlers->stop_complete_handler(
319          fw_device, fw_device->operation_status
320       );
321    }
322 }
323 
324 /**
325  * @brief This method implements the actions taken when exiting the
326  *        STOPPING state.
327  *
328  * @param[in]  object This parameter specifies the base object for which
329  *             the state transition is occurring.  This is cast into a
330  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
331  *
332  * @return none
333  */
334 static
scif_sas_remote_device_stopping_state_exit(SCI_BASE_OBJECT_T * object)335 void scif_sas_remote_device_stopping_state_exit(
336    SCI_BASE_OBJECT_T *object
337 )
338 {
339    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
340 
341    // Let the domain know that the device has stopped
342    fw_device->domain->device_start_count--;
343 }
344 
345 /**
346  * @brief This method implements the actions taken when entering the
347  *        FAILED state.  This includes setting the state handler methods
348  *        and issuing a scif_cb_remote_device_failed() notification to
349  *        the user.
350  *
351  * @param[in]  object This parameter specifies the base object for which
352  *             the state transition is occurring.  This is cast into a
353  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
354  *
355  * @return none
356  */
357 static
scif_sas_remote_device_failed_state_enter(SCI_BASE_OBJECT_T * object)358 void scif_sas_remote_device_failed_state_enter(
359    SCI_BASE_OBJECT_T *object
360 )
361 {
362    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
363 
364    SET_STATE_HANDLER(
365       fw_device,
366       scif_sas_remote_device_state_handler_table,
367       SCI_BASE_REMOTE_DEVICE_STATE_FAILED
368    );
369 
370    SCIF_LOG_INFO((
371       sci_base_object_get_logger(fw_device),
372       SCIF_LOG_OBJECT_REMOTE_DEVICE | SCIF_LOG_OBJECT_REMOTE_DEVICE_CONFIG,
373       "Domain:0x%x Device:0x%x Status:0x%x device failed\n",
374       fw_device->domain, fw_device, fw_device->operation_status
375    ));
376 
377    // Notify the user that the device has failed.
378    scif_cb_remote_device_failed(
379       fw_device->domain->controller,
380       fw_device->domain,
381       fw_device,
382       fw_device->operation_status
383    );
384 
385    // Only call start_complete for the remote device if the device failed
386    // from the STARTING state.
387    if (fw_device->parent.state_machine.previous_state_id
388        == SCI_BASE_REMOTE_DEVICE_STATE_STARTING)
389       scif_sas_domain_remote_device_start_complete(fw_device->domain,fw_device);
390 }
391 
392 /**
393  * @brief This method implements the actions taken when entering the RESETTING
394  *        state.
395  *
396  * @param[in]  object This parameter specifies the base object for which
397  *             the state transition is occurring.  This is cast into a
398  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
399  *
400  * @return none
401  */
402 static
scif_sas_remote_device_resetting_state_enter(SCI_BASE_OBJECT_T * object)403 void scif_sas_remote_device_resetting_state_enter(
404    SCI_BASE_OBJECT_T *object
405 )
406 {
407 }
408 
409 #if !defined(DISABLE_WIDE_PORTED_TARGETS)
410 /**
411  * @brief This method implements the actions taken when entering the UPDATING
412  *        PORT WIDTH state.
413  *
414  * @param[in]  object This parameter specifies the base object for which
415  *             the state transition is occurring.  This is cast into a
416  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
417  *
418  * @return none
419  */
420 static
scif_sas_remote_device_updating_port_width_state_enter(SCI_BASE_OBJECT_T * object)421 void scif_sas_remote_device_updating_port_width_state_enter(
422    SCI_BASE_OBJECT_T *object
423 )
424 {
425    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
426 
427    SET_STATE_HANDLER(
428       fw_device,
429       scif_sas_remote_device_state_handler_table,
430       SCI_BASE_REMOTE_DEVICE_STATE_UPDATING_PORT_WIDTH
431    );
432 
433    fw_device->destination_state = SCIF_SAS_REMOTE_DEVICE_DESTINATION_STATE_READY;
434 
435    //If the request count is zero, go ahead to update the RNC.
436    //If not, don't do anything for now. The IO complete handler of this state
437    //will update the RNC whenever the request count goes down to zero.
438    if (fw_device->request_count == 0)
439    {
440       //stop the device, upon the stop complete callback, start the device again
441       //with the updated port width.
442       scic_remote_device_stop(
443          fw_device->core_object, SCIF_SAS_REMOTE_DEVICE_CORE_OP_TIMEOUT);
444    }
445 }
446 
447 
448 /**
449  * @brief This method implements the actions taken when exiting the
450  *        STOPPING state.
451  *
452  * @param[in]  object This parameter specifies the base object for which
453  *             the state transition is occurring.  This is cast into a
454  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
455  *
456  * @return none
457  */
458 static
scif_sas_remote_device_updating_port_width_state_exit(SCI_BASE_OBJECT_T * object)459 void scif_sas_remote_device_updating_port_width_state_exit(
460    SCI_BASE_OBJECT_T *object
461 )
462 {
463    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
464 
465    fw_device->destination_state =
466       SCIF_SAS_REMOTE_DEVICE_DESTINATION_STATE_UNSPECIFIED;
467 }
468 
469 
470 #endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
471 
472 /**
473  * @brief This method implements the actions taken when entering the
474  *        FINAL state.  This includes setting the FINAL state handler
475  *        methods.
476  *
477  * @param[in]  object This parameter specifies the base object for which
478  *             the state transition is occurring.  This is cast into a
479  *             SCIF_SAS_REMOTE_DEVICE object in the method implementation.
480  *
481  * @return none
482  */
483 static
scif_sas_remote_device_final_state_enter(SCI_BASE_OBJECT_T * object)484 void scif_sas_remote_device_final_state_enter(
485    SCI_BASE_OBJECT_T *object
486 )
487 {
488    SCIF_SAS_REMOTE_DEVICE_T * fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)object;
489 
490    SET_STATE_HANDLER(
491       fw_device,
492       scif_sas_remote_device_state_handler_table,
493       SCI_BASE_REMOTE_DEVICE_STATE_FINAL
494    );
495 }
496 
497 
498 SCI_BASE_STATE_T
499    scif_sas_remote_device_state_table[SCI_BASE_REMOTE_DEVICE_MAX_STATES] =
500 {
501    {
502       SCI_BASE_REMOTE_DEVICE_STATE_INITIAL,
503       scif_sas_remote_device_initial_state_enter,
504       NULL
505    },
506    {
507       SCI_BASE_REMOTE_DEVICE_STATE_STOPPED,
508       scif_sas_remote_device_stopped_state_enter,
509       NULL
510    },
511    {
512       SCI_BASE_REMOTE_DEVICE_STATE_STARTING,
513       scif_sas_remote_device_starting_state_enter,
514       scif_sas_remote_device_starting_state_exit
515    },
516    {
517       SCI_BASE_REMOTE_DEVICE_STATE_READY,
518       scif_sas_remote_device_ready_state_enter,
519       scif_sas_remote_device_ready_state_exit
520    },
521    {
522       SCI_BASE_REMOTE_DEVICE_STATE_STOPPING,
523       scif_sas_remote_device_stopping_state_enter,
524       scif_sas_remote_device_stopping_state_exit
525    },
526    {
527       SCI_BASE_REMOTE_DEVICE_STATE_FAILED,
528       scif_sas_remote_device_failed_state_enter,
529       NULL
530    },
531    {
532       SCI_BASE_REMOTE_DEVICE_STATE_RESETTING,
533       scif_sas_remote_device_resetting_state_enter,
534       NULL
535    },
536 #if !defined(DISABLE_WIDE_PORTED_TARGETS)
537    {
538       SCI_BASE_REMOTE_DEVICE_STATE_UPDATING_PORT_WIDTH,
539       scif_sas_remote_device_updating_port_width_state_enter,
540       scif_sas_remote_device_updating_port_width_state_exit
541    },
542 #endif //#if !defined(DISABLE_WIDE_PORTED_TARGETS)
543    {
544       SCI_BASE_REMOTE_DEVICE_STATE_FINAL,
545       scif_sas_remote_device_final_state_enter,
546       NULL
547    },
548 };
549 
550