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 */ 90 static 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 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 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 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 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 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 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 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 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 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 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 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 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