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