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.
7 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
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.
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.
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.
26 * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved.
27 * All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
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
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.
53 #include <sys/cdefs.h>
54 __FBSDID("$FreeBSD$");
59 * @brief This file contains all of the entrance and exit methods for each
60 * of the domain states defined by the SCI_BASE_DOMAIN state
64 #include <dev/isci/scil/intel_sas.h>
65 #include <dev/isci/scil/scic_port.h>
67 #include <dev/isci/scil/scif_sas_logger.h>
68 #include <dev/isci/scil/scif_sas_domain.h>
69 #include <dev/isci/scil/scif_sas_controller.h>
70 #include <dev/isci/scil/scic_controller.h>
72 //******************************************************************************
73 //* P R O T E C T E D M E T H O D S
74 //******************************************************************************
77 * @brief This method will attempt to transition to the stopped state.
78 * The transition will only occur if the criteria for transition is
79 * met (i.e. all IOs are complete and all devices are stopped).
81 * @param[in] fw_domain This parameter specifies the domain in which to
82 * to attempt to perform the transition.
86 void scif_sas_domain_transition_to_stopped_state(
87 SCIF_SAS_DOMAIN_T * fw_domain
91 sci_base_object_get_logger(fw_domain),
92 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
93 "scif_sas_domain_transition_to_stopped_state(0x%x) enter\n",
97 // If IOs are quiesced, and all remote devices are stopped,
98 // then transition directly to the STOPPED state.
99 if ( (fw_domain->request_list.element_count == 0)
100 && (fw_domain->device_start_count == 0) )
103 sci_base_object_get_logger(fw_domain),
104 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
105 "Domain:0x%x immediate transition to STOPPED\n",
109 sci_base_state_machine_change_state(
110 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STOPPED
117 * @brief This method is called upon entrance to all states where the
118 * previous state may have been the DISCOVERING state.
119 * We issue the scif_cb_domain_discovery_complete() notification
120 * from this method, assuming pre-requisites are met, as opposed
121 * to in the exit handler of the DISCOVERING state, so that the
122 * appropriate state handlers are in place should the user decide
123 * to call scif_domain_discover() again.
125 * @param[in] fw_domain This parameter specifies the domain for which
126 * the state transition has occurred.
131 void scif_sas_domain_transition_from_discovering_state(
132 SCIF_SAS_DOMAIN_T * fw_domain
136 sci_base_object_get_logger(fw_domain),
137 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
138 "scif_sas_domain_transition_from_discovering_state(0x%x) enter\n",
142 if (fw_domain->parent.state_machine.previous_state_id
143 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
145 scif_sas_controller_restore_interrupt_coalescence(fw_domain->controller);
147 scif_cb_timer_stop(fw_domain->controller, fw_domain->operation.timer);
149 scif_cb_domain_discovery_complete(
150 fw_domain->controller, fw_domain, fw_domain->operation.status
157 * @brief This method is called upon entrance to DISCOVERING state. Right before
158 * transitioning to DISCOVERING state, we temporarily change interrupt
159 * coalescence scheme.
161 * @param[in] fw_domain This parameter specifies the domain for which
162 * the state transition has occurred.
166 void scif_sas_domain_transition_to_discovering_state(
167 SCIF_SAS_DOMAIN_T * fw_domain
170 scif_sas_controller_save_interrupt_coalescence(fw_domain->controller);
172 sci_base_state_machine_change_state(
173 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
179 * @brief This method implements the actions taken when entering the
182 * @param[in] object This parameter specifies the base object for which
183 * the state transition is occurring. This is cast into a
184 * SCIF_SAS_DOMAIN object in the method implementation.
189 void scif_sas_domain_initial_state_enter(
190 SCI_BASE_OBJECT_T * object
193 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
197 scif_sas_domain_state_handler_table,
198 SCI_BASE_DOMAIN_STATE_INITIAL
202 sci_base_object_get_logger(fw_domain),
203 SCIF_LOG_OBJECT_DOMAIN,
204 "scif_sas_domain_initial_state_enter(0x%x) enter\n",
210 * @brief This method implements the actions taken when entering the
211 * STARTING state. This includes setting the state handlers and
212 * checking to see if the core port has already become READY.
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_DOMAIN object in the method implementation.
221 void scif_sas_domain_starting_state_enter(
222 SCI_BASE_OBJECT_T * object
225 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
229 scif_sas_domain_state_handler_table,
230 SCI_BASE_DOMAIN_STATE_STARTING
234 sci_base_object_get_logger(fw_domain),
235 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
236 "scif_sas_domain_starting_state_enter(0x%x) enter\n",
240 scif_sas_domain_transition_from_discovering_state(fw_domain);
242 // If we entered the STARTING state and the core port is actually ready,
243 // then directly transition into the READY state. This can occur
244 // if we were in the middle of discovery when the port failed
245 // (causing a transition to STOPPING), then before reaching STOPPED
246 // the port becomes ready again.
247 if (fw_domain->is_port_ready == TRUE)
249 sci_base_state_machine_change_state(
250 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_READY
256 * @brief This method implements the actions taken when entering the
257 * READY state. If the transition into this state came from:
258 * - the STARTING state, then alert the user via a
259 * scif_cb_domain_change_notification() that the domain
260 * has at least 1 device ready for discovery.
261 * - the DISCOVERING state, then alert the user that
262 * discovery is complete via the
263 * scif_cb_domain_discovery_complete() notification that
264 * discovery is finished.
266 * @param[in] object This parameter specifies the base object for which
267 * the state transition is occurring. This is cast into a
268 * SCIF_SAS_DOMAIN object in the method implementation.
273 void scif_sas_domain_ready_state_enter(
274 SCI_BASE_OBJECT_T * object
277 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
281 scif_sas_domain_state_handler_table,
282 SCI_BASE_DOMAIN_STATE_READY
286 sci_base_object_get_logger(fw_domain),
287 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
288 "scif_sas_domain_ready_state_enter(0x%x) enter\n",
292 if (fw_domain->parent.state_machine.previous_state_id
293 == SCI_BASE_DOMAIN_STATE_STARTING)
295 scif_cb_domain_ready(fw_domain->controller, fw_domain);
297 // Only indicate the domain change notification if the previous
298 // state was the STARTING state. We issue the notification here
299 // as opposed to exit of the STARTING state so that the appropriate
300 // state handlers are in place should the user call
301 // scif_domain_discover() from scif_cb_domain_change_notification()
302 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
304 else if (fw_domain->parent.state_machine.previous_state_id
305 == SCI_BASE_DOMAIN_STATE_DISCOVERING)
307 //if domain discovery timed out, we will NOT go back to discover even
308 //the broadcast change count is not zero. Instead we finish the discovery
309 //back to user. User can check the operation status and decide to
310 //retry discover all over again.
311 if (fw_domain->operation.status == SCI_FAILURE_TIMEOUT)
312 fw_domain->broadcast_change_count = 0;
314 // Check the broadcast change count to determine if discovery
315 // is indeed complete.
316 if (fw_domain->broadcast_change_count == 0)
318 scif_sas_domain_transition_from_discovering_state(fw_domain);
319 scif_cb_domain_ready(fw_domain->controller, fw_domain);
323 // The broadcast change count indicates something my have
324 // changed in the domain, while a discovery was ongoing.
325 // Thus, we should start discovery over again.
326 sci_base_state_machine_change_state(
327 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_DISCOVERING
331 // Enable the BCN because underneath hardware may disabled any further
333 scic_port_enable_broadcast_change_notification(fw_domain->core_object);
338 * @brief This method implements the actions taken when exiting the
341 * @param[in] object This parameter specifies the base object for which
342 * the state transition is occurring. This is cast into a
343 * SCIF_SAS_DOMAIN object in the method implementation.
348 void scif_sas_domain_ready_state_exit(
349 SCI_BASE_OBJECT_T * object
352 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
355 sci_base_object_get_logger(fw_domain),
356 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
357 "scif_sas_domain_ready_state_exit(0x%x) enter\n",
361 scif_cb_domain_not_ready(fw_domain->controller, fw_domain);
365 * @brief This method implements the actions taken when entering the
368 * @param[in] object This parameter specifies the base object for which
369 * the state transition is occurring. This is cast into a
370 * SCIF_SAS_DOMAIN object in the method implementation.
375 void scif_sas_domain_stopping_state_enter(
376 SCI_BASE_OBJECT_T * object
379 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
380 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
381 SCI_ABSTRACT_ELEMENT_T * element = sci_abstract_list_get_front(
382 &fw_domain->remote_device_list
387 scif_sas_domain_state_handler_table,
388 SCI_BASE_DOMAIN_STATE_STOPPING
391 // This must be invoked after the state handlers are set to ensure
392 // appropriate processing will occur if the user attempts to perform
393 // additional actions.
394 scif_sas_domain_transition_from_discovering_state(fw_domain);
397 sci_base_object_get_logger(fw_domain),
398 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
399 "scif_sas_domain_stopping_state_enter(0x%x) enter\n",
403 scif_sas_high_priority_request_queue_purge_domain(
404 &fw_domain->controller->hprq, fw_domain
407 // Search the domain's list of devices and put them all in the STOPPING
409 while (element != NULL)
411 fw_device = (SCIF_SAS_REMOTE_DEVICE_T*)
412 sci_abstract_list_get_object(element);
414 // This method will stop the core device. The core will terminate
415 // all IO requests currently outstanding.
416 fw_device->state_handlers->parent.stop_handler(&fw_device->parent);
418 element = sci_abstract_list_get_next(element);
421 // Attempt to transition to the stopped state.
422 scif_sas_domain_transition_to_stopped_state(fw_domain);
426 * @brief This method implements the actions taken when entering the
429 * @param[in] object This parameter specifies the base object for which
430 * the state transition is occurring. This is cast into a
431 * SCIF_SAS_DOMAIN object in the method implementation.
436 void scif_sas_domain_stopped_state_enter(
437 SCI_BASE_OBJECT_T * object
440 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
444 scif_sas_domain_state_handler_table,
445 SCI_BASE_DOMAIN_STATE_STOPPED
449 sci_base_object_get_logger(fw_domain),
450 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
451 "scif_sas_domain_stopped_state_enter(0x%x) enter\n",
455 // A hot unplug of the direct attached device has occurred. Thus,
456 // notify the user. Note, if the controller is not in READY state,
457 // mostly likely the controller is in STOPPING or STOPPED state,
458 // meaning the controller is in the process of stopping, we should
459 // not call back to user in the middle of controller stopping.
460 if(fw_domain->controller->parent.state_machine.current_state_id
461 == SCI_BASE_CONTROLLER_STATE_READY)
462 scif_cb_domain_change_notification(fw_domain->controller, fw_domain);
466 * @brief This method implements the actions taken when entering the
467 * DISCOVERING state. This includes determining from which
468 * state we entered. If we entered from stopping that some sort
469 * of hot-remove of the port occurred. In the hot-remove case
470 * all devices should be in the STOPPED state already and, as
471 * a result, are removed from the domain with a notification sent
472 * to the framework user.
474 * @note This method currently only handles hot-insert/hot-remove of
475 * direct attached SSP devices.
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_DOMAIN object in the method implementation.
484 void scif_sas_domain_discovering_state_enter(
485 SCI_BASE_OBJECT_T * object
488 SCIF_SAS_DOMAIN_T * fw_domain = (SCIF_SAS_DOMAIN_T *)object;
492 scif_sas_domain_state_handler_table,
493 SCI_BASE_DOMAIN_STATE_DISCOVERING
497 sci_base_object_get_logger(fw_domain),
498 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
499 "scif_sas_domain_discovering_state_enter(0x%x) enter\n",
503 fw_domain->broadcast_change_count = 0;
505 // Did the domain just go through a port not ready action? If it did,
506 // then we will be entering from the STOPPED state.
507 if (fw_domain->parent.state_machine.previous_state_id
508 != SCI_BASE_DOMAIN_STATE_STOPPED)
510 SCIF_SAS_REMOTE_DEVICE_T * remote_device;
511 SCIC_PORT_PROPERTIES_T properties;
513 scic_port_get_properties(fw_domain->core_object, &properties);
515 // If the device has not yet been added to the domain, then
516 // inform the user that the device is new.
517 remote_device = (SCIF_SAS_REMOTE_DEVICE_T *)
518 scif_domain_get_device_by_sas_address(
519 fw_domain, &properties.remote.sas_address
521 if (remote_device == SCI_INVALID_HANDLE)
523 // simply notify the user of the new DA device and be done
525 scif_cb_domain_da_device_added(
526 fw_domain->controller,
528 &properties.remote.sas_address,
529 &properties.remote.protocols
534 if(properties.remote.protocols.u.bits.smp_target)
535 //kick off the smp discover process.
536 scif_sas_domain_start_smp_discover(fw_domain, remote_device);
539 else //entered from STOPPED state.
541 SCI_ABSTRACT_ELEMENT_T * current_element =
542 sci_abstract_list_get_front(&(fw_domain->remote_device_list) );
544 SCIF_SAS_REMOTE_DEVICE_T * fw_device;
546 while (current_element != NULL)
548 fw_device = (SCIF_SAS_REMOTE_DEVICE_T *)
549 sci_abstract_list_get_object(current_element);
551 ASSERT(fw_device->parent.state_machine.current_state_id
552 == SCI_BASE_REMOTE_DEVICE_STATE_STOPPED);
555 sci_abstract_list_get_next(current_element);
558 sci_base_object_get_logger(fw_domain),
559 SCIF_LOG_OBJECT_DOMAIN | SCIF_LOG_OBJECT_DOMAIN_DISCOVERY,
560 "Controller:0x%x Domain:0x%x Device:0x%x removed\n",
561 fw_domain->controller, fw_domain, fw_device
564 // Notify the framework user of the device removal.
565 scif_cb_domain_device_removed(
566 fw_domain->controller, fw_domain, fw_device
570 ASSERT(fw_domain->request_list.element_count == 0);
571 ASSERT(sci_abstract_list_size(&fw_domain->remote_device_list) == 0);
573 sci_base_state_machine_change_state(
574 &fw_domain->parent.state_machine, SCI_BASE_DOMAIN_STATE_STARTING
579 SCI_BASE_STATE_T scif_sas_domain_state_table[SCI_BASE_DOMAIN_MAX_STATES] =
582 SCI_BASE_DOMAIN_STATE_INITIAL,
583 scif_sas_domain_initial_state_enter,
587 SCI_BASE_DOMAIN_STATE_STARTING,
588 scif_sas_domain_starting_state_enter,
592 SCI_BASE_DOMAIN_STATE_READY,
593 scif_sas_domain_ready_state_enter,
594 scif_sas_domain_ready_state_exit,
597 SCI_BASE_DOMAIN_STATE_STOPPING,
598 scif_sas_domain_stopping_state_enter,
602 SCI_BASE_DOMAIN_STATE_STOPPED,
603 scif_sas_domain_stopped_state_enter,
607 SCI_BASE_DOMAIN_STATE_DISCOVERING,
608 scif_sas_domain_discovering_state_enter,