1/*-
2 * This file is provided under a dual BSD/GPLv2 license.  When using or
3 * redistributing this file, you may do so under either license.
4 *
5 * GPL LICENSE SUMMARY
6 *
7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of version 2 of the GNU General Public License as
11 * published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21 * The full GNU General Public License is included in this distribution
22 * in the file called LICENSE.GPL.
23 *
24 * BSD LICENSE
25 *
26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27 * All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 *
33 *   * Redistributions of source code must retain the above copyright
34 *     notice, this list of conditions and the following disclaimer.
35 *   * Redistributions in binary form must reproduce the above copyright
36 *     notice, this list of conditions and the following disclaimer in
37 *     the documentation and/or other materials provided with the
38 *     distribution.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
41 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
42 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
43 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
44 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
46 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
50 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 */
52
53#include <sys/cdefs.h>
54__FBSDID("$FreeBSD$");
55
56/**
57 * @file
58 *
59 * @brief
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 */
90static
91void 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 */
123static
124void 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 */
166static
167void 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 */
218static
219void 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 */
243static
244void 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 */
269static
270void 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 */
291static
292void 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 */
334static
335void 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 */
357static
358void 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 */
402static
403void 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 */
420static
421void 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 */
458static
459void 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 */
483static
484void 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
498SCI_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