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