sata.c revision 2c9565cfcd87a2045c2e4b76f31ac4e978903589
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* SATA Framework
* Generic SATA Host Adapter Implementation
*
* NOTE: THIS VERSION DOES NOT SUPPORT ATAPI DEVICES,
* although there is some code related to these devices.
*
*/
#include <sys/sysevent.h>
/* Debug flags - defined in sata.h */
int sata_debug_flags = 0;
/*
* Flags enabling selected SATA HBA framework functionality
*/
#define SATA_ENABLE_QUEUING 1
#define SATA_ENABLE_NCQ 2
#define SATA_ENABLE_PROCESS_EVENTS 4
static int sata_func_enable = SATA_ENABLE_PROCESS_EVENTS;
#ifdef SATA_DEBUG
#else
#define SATA_LOG_D(arg)
#endif
/*
* SATA cb_ops functions
*/
/*
* SCSA required entry points
*/
scsi_hba_tran_t *, struct scsi_device *);
static int sata_scsi_tgt_probe(struct scsi_device *,
int (*callback)(void));
scsi_hba_tran_t *, struct scsi_device *);
static int sata_scsi_reset(struct scsi_address *, int);
static int sata_scsi_getcap(struct scsi_address *, char *, int);
static int sata_scsi_setcap(struct scsi_address *, char *, int, int);
caddr_t);
static int sata_scsi_get_name(struct scsi_device *, char *, int);
/*
* Local functions
*/
static void sata_remove_hba_instance(dev_info_t *);
static void sata_probe_ports(sata_hba_inst_t *);
sata_address_t *);
static int sata_validate_scsi_address(sata_hba_inst_t *,
struct scsi_address *, sata_device_t *);
static int sata_validate_sata_address(sata_hba_inst_t *, int, int, int);
static void sata_pkt_free(sata_pkt_txlate_t *);
caddr_t, ddi_dma_attr_t *);
sata_device_t *);
static void sata_free_local_buffer(sata_pkt_txlate_t *);
ddi_dma_attr_t *);
static int sata_fetch_device_identify_data(sata_hba_inst_t *,
/* Event processing functions */
static void sata_event_daemon(void *);
static void sata_event_thread_control(int);
static void sata_process_port_failed_event(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_port_link_events(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_cntrl_pwr_level_change(sata_hba_inst_t *);
static int sata_restore_drive_settings(sata_hba_inst_t *,
/* Local functions for ioctl */
/* Local translation functions */
static int sata_txlt_inquiry(sata_pkt_txlate_t *);
static int sata_txlt_test_unit_ready(sata_pkt_txlate_t *);
static int sata_txlt_start_stop_unit(sata_pkt_txlate_t *);
static int sata_txlt_read_capacity(sata_pkt_txlate_t *);
static int sata_txlt_request_sense(sata_pkt_txlate_t *);
static int sata_txlt_read(sata_pkt_txlate_t *);
static int sata_txlt_write(sata_pkt_txlate_t *);
static int sata_txlt_atapi(sata_pkt_txlate_t *);
static int sata_txlt_log_sense(sata_pkt_txlate_t *);
static int sata_txlt_log_select(sata_pkt_txlate_t *);
static int sata_txlt_mode_sense(sata_pkt_txlate_t *);
static int sata_txlt_mode_select(sata_pkt_txlate_t *);
static int sata_txlt_synchronize_cache(sata_pkt_txlate_t *);
static int sata_txlt_nodata_cmd_immediate(sata_pkt_txlate_t *);
static int sata_hba_start(sata_pkt_txlate_t *, int *);
static int sata_txlt_invalid_command(sata_pkt_txlate_t *);
static int sata_txlt_lba_out_of_range(sata_pkt_txlate_t *);
static void sata_txlt_rw_completion(sata_pkt_t *);
static void sata_txlt_atapi_completion(sata_pkt_t *);
static void sata_txlt_nodata_cmd_completion(sata_pkt_t *);
static struct scsi_extended_sense *sata_immediate_error_response(
sata_pkt_txlate_t *, int);
/* Local functions */
uint8_t *);
static int sata_mode_select_page_8(sata_pkt_txlate_t *,
struct mode_cache_scsi3 *, int, int *, int *, int *);
static int sata_mode_select_page_1c(sata_pkt_txlate_t *,
struct mode_info_excpt_page *, int, int *, int *, int *);
sata_hba_inst_t *);
sata_hba_inst_t *);
sata_hba_inst_t *);
static void sata_save_drive_settings(sata_drive_info_t *);
static int sata_fetch_smart_return_status(sata_hba_inst_t *,
struct smart_data *);
static int sata_smart_selftest_log(sata_hba_inst_t *,
struct smart_selftest_log *);
static int sata_ext_smart_selftest_read_log(sata_hba_inst_t *,
struct read_log_ext_directory *);
static void sata_xlate_errors(sata_pkt_txlate_t *);
/*
* SATA Framework will ignore SATA HBA driver cb_ops structure and
* register following one with SCSA framework.
* Open & close are provided, so scsi framework will not use its own
*/
static struct cb_ops sata_cb_ops = {
sata_hba_open, /* open */
sata_hba_close, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
sata_hba_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* chpoll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* rev */
nodev, /* aread */
nodev /* awrite */
};
extern struct mod_ops mod_miscops;
extern uchar_t scsi_cdb_size[];
&mod_miscops, /* Type of module */
"Generic SATA Driver v%I%" /* module name */
};
static struct modlinkage modlinkage = {
(void *)&modlmisc,
};
/*
* Default sata pkt timeout. Used when a target driver scsi_pkt time is zero,
* i.e. when scsi_pkt has not timeout specified.
*/
/*
* Mutexes protecting structures in multithreaded operations.
* Because events are relatively rare, a single global mutex protecting
* data structures should be sufficient. To increase performance, add
* separate mutex per each sata port and use global mutex only to protect
* common data structures.
*/
static char sata_log_buf[256];
/*
* Linked list of HBA instances
*/
/*
* Pointer to per-instance SATA HBA soft structure is stored in sata_hba_tran
* structure and in sata soft state.
*/
/*
* Event daemon related variables
*/
static kmutex_t sata_event_mutex;
static kcondvar_t sata_event_cv;
static int sata_event_thread_terminate = 0;
static int sata_event_pending = 0;
static int sata_event_thread_active = 0;
extern pri_t minclsyspri;
/* Warlock directives */
/* End of warlock directives */
/* ************** loadable module configuration functions ************** */
int
_init()
{
int rval;
#ifdef SATA_DEBUG
#endif
}
return (rval);
}
int
_fini()
{
int rval;
return (rval);
return (rval);
}
int
{
}
/* ********************* SATA HBA entry points ********************* */
/*
* Called by SATA HBA from _init().
* calling scsi_hba_init().
*
* SATA HBA driver cb_ops are ignored - SATA HBA framework cb_ops are used
* instead. SATA HBA framework cb_ops pointer overwrites SATA HBA driver
* cb_ops pointer in SATA HBA driver dev_ops structure.
* SATA HBA framework cb_ops supplies cb_open cb_close and cb_ioctl vectors.
*
* Return status of the scsi_hba_init() is returned to a calling SATA HBA
* driver.
*/
int
{
int rval;
"sata_hba_init: name %s \n",
/*
* Fill-up cb_ops and dev_ops when necessary
*/
/*
* Provide pointer to SATA dev_ops
*/
/*
* Register SATA HBA with SCSI framework
*/
"sata_hba_init: scsi hba init failed\n", NULL);
return (rval);
}
return (0);
}
/* HBA attach stages */
#define HBA_ATTACH_STAGE_SATA_HBA_INST 1
#define HBA_ATTACH_STAGE_SCSI_ATTACHED 2
#define HBA_ATTACH_STAGE_SETUP 4
#define HBA_ATTACH_STAGE_LINKED 8
/*
*
* Called from SATA HBA driver's attach routine to attach an instance of
* the HBA.
*
* For DDI_ATTACH command:
* sata_hba_inst structure is allocated here and initialized with pointers to
* SATA framework implementation of required scsi tran functions.
* The scsi_tran's tran_hba_private field is used by SATA Framework to point
* to the soft structure (sata_hba_inst) allocated by SATA framework for
* SATA HBA instance related data.
* The scsi_tran's tran_hba_private field is used by SATA framework to
* store a pointer to per-HBA-instance of sata_hba_inst structure.
* The sata_hba_inst structure is cross-linked to scsi tran structure.
* Among other info, a pointer to sata_hba_tran structure is stored in
* sata_hba_inst. The sata_hba_inst structures for different HBA instances are
* linked together into the list, pointed to by sata_hba_list.
* On the first HBA instance attach the sata event thread is initialized.
* Attachment points are created for all SATA ports of the HBA being attached.
* All HBA instance's SATA ports are probed and type of plugged devices is
* determined. For each device of a supported type, a target node is created.
*
* DDI_SUCCESS is returned when attachment process is successful,
* DDI_FAILURE is returned otherwise.
*
* For DDI_RESUME command:
* Not implemented at this time (postponed until phase 2 of the development).
*/
int
{
int hba_attach_state = 0;
"sata_hba_attach: node %s (%s%d)\n",
if (cmd == DDI_RESUME) {
/*
* Postponed until phase 2 of the development
*/
return (DDI_FAILURE);
}
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
/* cmd == DDI_ATTACH */
"sata_hba_attach: invalid sata_hba_tran"));
return (DDI_FAILURE);
}
/*
* Allocate and initialize SCSI tran structure.
* SATA copy of tran_bus_config is provided to create port nodes.
*/
return (DDI_FAILURE);
/*
* Allocate soft structure for SATA HBA instance.
* There is a separate softstate for each HBA instance.
*/
/*
* scsi_trans's tran_hba_private is used by SATA Framework to point to
* soft structure allocated by SATA framework for
* SATA HBA instance related data.
*/
scsi_tran, 0) != DDI_SUCCESS) {
#ifdef SATA_DEBUG
#endif
goto fail;
}
"failed to create hba sata prop"));
goto fail;
}
}
/*
* Save pointers in hba instance soft state.
*/
/*
* Create events thread if not created yet.
*/
/*
* Link this hba instance into the list.
*/
if (sata_hba_list == NULL) {
}
if (sata_hba_list_tail != NULL) {
}
/*
* Create SATA HBA devctl minor node for sata_hba_open, close, ioctl
*
* Make sure that instance number doesn't overflow
* when forming minor numbers.
*/
DDI_NT_SATA_NEXUS, 0) != DDI_SUCCESS) {
#ifdef SATA_DEBUG
"cannot create devctl minor node");
#endif
goto fail;
}
/*
* Set-up kstats here, if necessary.
* (postponed until phase 2 of the development).
*/
/*
* Probe controller ports. This operation will describe a current
* controller/port/multipliers/device configuration and will create
* attachment points.
* We may end-up with just a controller with no devices attached.
*/
/*
* Create child nodes for all possible target devices currently
* attached to controller's ports and port multiplier device ports.
*/
return (DDI_SUCCESS);
fail:
if (hba_attach_state & HBA_ATTACH_STAGE_LINKED) {
(void) sata_remove_hba_instance(dip);
if (sata_hba_list == NULL)
}
(void) scsi_hba_detach(dip);
kmem_free((void *)sata_hba_inst,
sizeof (struct sata_hba_inst));
}
return (DDI_FAILURE);
}
/*
* Called by SATA HBA from to detach an instance of the driver.
*
* For DDI_DETACH command:
* Free local structures allocated for SATA HBA instance during
* sata_hba_attach processing.
*
* Returns DDI_SUCCESS when HBA was detached, DDI_FAILURE otherwise.
*
* For DDI_SUSPEND command:
* Not implemented at this time (postponed until phase 2 of the development)
* Returnd DDI_SUCCESS.
*
* When the last HBA instance is detached, the event daemon is terminated.
*
* NOTE: cport support only, no port multiplier support.
*/
int
{
int ncport;
switch (cmd) {
case DDI_DETACH:
return (DDI_FAILURE);
if (sata_hba_inst == NULL)
return (DDI_FAILURE);
return (DDI_FAILURE);
}
/*
* Free all target nodes - at this point
* devices should be at least offlined
* otherwise scsi_hba_detach() should not be called.
*/
ncport++) {
ncport);
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
"sata_hba_detach: "
"Target node not "
"removed !"));
return (DDI_FAILURE);
}
}
}
}
}
/*
* Disable sata event daemon processing for this HBA
*/
/*
* Remove event daemon thread, if it is last HBA instance.
*/
}
/* Remove this HBA instance from the HBA list */
/*
* At this point there should be no target nodes attached.
* Detach and destroy device and port info structures.
*/
ncport++) {
sdinfo =
/* Release device structure */
sizeof (sata_drive_info_t));
}
/* Release cport info */
sizeof (sata_cport_info_t));
}
}
kmem_free((void *)sata_hba_inst,
sizeof (struct sata_hba_inst));
return (DDI_SUCCESS);
case DDI_SUSPEND:
/*
* Postponed until phase 2
*/
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
/*
* Called by an HBA drive from _fini() routine.
*/
void
{
"sata_hba_fini: name %s\n",
}
/*
* Default open and close routine for sata_hba framework.
*
*/
/*
* Open devctl node.
*
* Returns:
* 0 if node was open successfully, error code otherwise.
*
*
*/
static int
{
#ifndef __lock_lint
#endif
int rv = 0;
return (EINVAL);
return (ENXIO);
return (ENXIO);
return (ENXIO);
if (sata_hba_inst->satahba_open_flag != 0) {
} else {
}
} else {
} else {
}
}
return (rv);
}
/*
* Close devctl node.
* Returns:
* 0 if node was closed successfully, error code otherwise.
*
*/
static int
{
#ifndef __lock_lint
#endif
return (EINVAL);
return (ENXIO);
return (ENXIO);
return (ENXIO);
return (0);
}
/*
* Standard IOCTL commands for SATA hotplugging.
* Implemented DEVCTL_AP commands:
* DEVCTL_AP_CONNECT
* DEVCTL_AP_DISCONNECT
* DEVCTL_AP_CONFIGURE
* DEVCTL_UNCONFIGURE
* DEVCTL_AP_CONTROL
*
* Commands passed to default ndi ioctl handler:
* DEVCTL_DEVICE_GETSTATE
* DEVCTL_DEVICE_ONLINE
* DEVCTL_DEVICE_OFFLINE
* DEVCTL_DEVICE_REMOVE
* DEVCTL_DEVICE_INSERT
* DEVCTL_BUS_GETSTATE
*
* All other cmds are passed to HBA if it provide ioctl handler, or failed
* if not.
*
* Returns:
* 0 if successful,
* error code if operation failed.
*
* NOTE: Port Multiplier is not supported.
*
*/
static int
int *rvalp)
{
#ifndef __lock_lint
#endif
int rv = 0;
int rval = SATA_SUCCESS;
return (ENXIO);
return (ENXIO);
if (sata_hba_inst == NULL)
return (ENXIO);
return (ENXIO);
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_DEVICE_REMOVE:
case DEVCTL_BUS_GETSTATE:
/*
* There may be more cases that we want to pass to default
* handler rather then fail them.
*/
}
/* read devctl ioctl data */
if (cmd != DEVCTL_AP_CONTROL) {
return (EFAULT);
-1) {
if (dcp)
return (EINVAL);
}
/* Only cport is considered now, i.e. SATA_ADDR_CPORT */
qual) != 0) {
return (EINVAL);
}
/*
* Cannot process ioctl request now. Come back later.
*/
return (EBUSY);
}
/* Block event processing for this port */
}
switch (cmd) {
case DEVCTL_AP_DISCONNECT:
/*
* Normally, cfgadm sata plugin will try to offline
* (unconfigure) device before this request. Nevertheless,
* if a device is still configured, we need to
* attempt to offline and unconfigure device first, and we will
* deactivate the port regardless of the unconfigure
* operation results.
*
* DEVCTL_AP_DISCONNECT invokes
* sata_hba_inst->satahba_tran->
* sata_tran_hotplug_ops->sata_tran_port_deactivate().
* If successful, the device structure (if any) attached
* to a port is removed and state of the port marked
* appropriately.
* Failure of the port_deactivate may keep port in
* the active state, or may fail the port.
*/
/* Check the current state of the port */
SATA_SUCCESS) {
break;
}
if (cportinfo->cport_state &
break;
}
/* Sanity check */
/* No physical port deactivation supported. */
break;
}
/*
* set port's dev_state to not ready - this will disable
* an access to an attached device.
*/
if ((sdinfo->satadrv_type &
(SATA_VALID_DEV_TYPE))) {
/*
* If a target node exists, try to offline
* a device and remove target node.
*/
cport)->cport_mutex);
/* target node exist */
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) != NDI_SUCCESS) {
/*
* Problem
* A target node remained
* attached. This happens when
* the file was open or a node
* was waiting for resources.
* Cannot do anything about it.
*/
"sata_hba_ioctl: "
"disconnect: cannot "
"remove target node!!!"));
}
}
cport)->cport_mutex);
/*
* Remove and release sata_drive_info
* structure.
*/
if (SATA_CPORTINFO_DRV_INFO(cportinfo) !=
NULL) {
NULL;
sizeof (sata_drive_info_t));
}
}
/*
* Note: PMult info requires different handling.
* Put PMult handling code here, when PMult is
* supported.
*/
}
/* Just ask HBA driver to deactivate port */
(dip, &sata_device);
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint.
*/
if (rval != SATA_SUCCESS) {
/*
* Port deactivation failure - do not
* change port state unless the state
* returned by HBA indicates a port failure.
*/
} else {
/*
* Deactivation succeded. From now on the framework
* will not know what is happening to the device, until
* the port is activated again.
*/
}
break;
case DEVCTL_AP_UNCONFIGURE:
/*
* The unconfigure operation uses generic nexus operation to
* offline a device. It leaves a target device node attached.
* and obviously sata_drive_info attached as well, because
* from the hardware point of view nothing has changed.
*/
NDI_SUCCESS) {
"sata_hba_ioctl: unconfigure: "
"failed to unconfigure "
"device at cport %d", cport));
}
/*
* The target node devi_state should be marked with
* DEVI_DEVICE_OFFLINE by ndi_devi_offline().
* This would be the indication for cfgadm that
* the AP node occupant state is 'unconfigured'.
*/
} else {
/*
* This would indicate a failure on the part of cfgadm
* to detect correct state of the node prior to this
* call - one cannot unconfigure non-existing device.
*/
"sata_hba_ioctl: unconfigure: "
"attempt to unconfigure non-existing device "
"at cport %d", cport));
}
break;
case DEVCTL_AP_CONNECT:
{
/*
* The sata cfgadm pluging will invoke this operation only if
* port was found in the disconnect state (failed state
* is also treated as the disconnected state).
* DEVCTL_AP_CONNECT would invoke
* sata_hba_inst->satahba_tran->
* sata_tran_hotplug_ops->sata_tran_port_activate().
* If successful and a device is found attached to the port,
* the initialization sequence is executed to attach
* a device structure to a port structure. The device is not
* set in configured state (system-wise) by this operation.
* The state of the port and a device would be set
* appropriately.
*
* Note, that activating the port may generate link events,
* so is is important that following processing and the
* event processing does not interfere with each other!
*
* This operation may remove port failed state and will
* try to make port active and in good standing.
*/
/* We only care about host sata cport for now */
/* Just let HBA driver to activate port */
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
/*
* Port activation failure.
*/
cport)->cport_mutex);
&sata_device);
if (sata_device.satadev_state &
}
cport)->cport_mutex);
"sata_hba_ioctl: connect: "
"failed to activate SATA cport %d",
cport));
break;
}
}
/* Virgin port state - will be updated by the port re-probe. */
cport)->cport_mutex);
cportinfo->cport_state = 0;
cport)->cport_mutex);
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint
*/
/*
* If there is a device attached to the port, emit
* a message.
*/
"SATA device detected at port %d", cport);
}
break;
}
case DEVCTL_AP_CONFIGURE:
{
/*
* A port may be in an active or shutdown state.
* If port is in a failed state, operation is aborted - one
* has to use explicit connect or port activate request
* to try to get a port into non-failed mode.
*
* If a port is in a shutdown state, arbitrarily invoke
* sata_tran_port_activate() prior to any other operation.
*
* Verify that port state is READY and there is a device
* of a supported type attached to this port.
* If target node exists, a device was most likely offlined.
* If target node does not exist, create a target node an
* attempt to online it.
* *
* NO PMult or devices beyond PMult are supported yet.
*/
/* We only care about host controller's sata cport for now. */
break;
}
/* Check the current state of the port */
(dip, &sata_device);
if (rval != SATA_SUCCESS ||
break;
}
/* Just let HBA driver to activate port */
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
/*
* Port activation failure - do not
* change port state unless the state
* returned by HBA indicates a port
* failure.
*/
&sata_device);
if (sata_device.satadev_state &
}
"sata_hba_ioctl: configure: "
"failed to activate SATA cport %d",
cport));
break;
}
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint.
*/
/* Virgin port state */
cportinfo->cport_state = 0;
}
/*
* Always reprobe port, to get current device info.
*/
SATA_SUCCESS) {
break;
}
/*
* That's the transition from "inactive" port
* to active one with device attached.
*/
"SATA device detected at port %d",
cport);
}
/*
* This is where real configure starts.
* Change following check for PMult support.
*/
/* No device to configure */
break;
}
/*
* Here we may have a device in reset condition,
* but because we are just configuring it, there is
* no need to process the reset other than just
* to clear device reset condition in the HBA driver.
* Setting the flag SATA_EVNT_CLEAR_DEVICE_RESET will
* cause a first command sent the HBA driver with the request
* to clear device reset condition.
*/
break;
}
if (sdinfo->satadrv_event_flags &
sdinfo->satadrv_event_flags = 0;
/* target node still exists */
"sata_hba_ioctl: configure: "
"onlining device at cport %d failed",
cport));
break;
}
} else {
/*
* No target node - need to create a new target node.
*/
/* configure failed */
"sata_hba_ioctl: configure: "
"configuring device at cport %d "
"failed", cport));
break;
}
}
break;
}
case DEVCTL_AP_GETSTATE:
ap_state.ap_error_code = 0;
ap_state.ap_in_transition = 0;
/* Copy the return AP-state information to the user space */
}
break;
case DEVCTL_AP_CONTROL:
{
/*
* Generic devctl for hardware specific functionality
*/
/* Copy in user ioctl data first */
#ifdef _MULTI_DATAMODEL
break;
}
} else
#endif /* _MULTI_DATAMODEL */
mode) != 0) {
return (EFAULT);
}
"sata_hba_ioctl: DEVCTL_AP_CONTROL "
/*
* a 32-bit number.
*/
return (EINVAL);
}
/* validate address */
/* Override address qualifier - handle cport only for now */
return (EINVAL);
/* Is the port locked by event processing daemon ? */
/*
* Cannot process ioctl request now. Come back later
*/
return (EBUSY);
}
/* Block event processing for this port */
case SATA_CFGA_RESET_PORT:
/*
* There is no protection here for configured
* device.
*/
/* Sanity check */
"sata_hba_ioctl: "
"sata_hba_tran missing required "
"function sata_tran_reset_dport"));
break;
}
/* handle cport only for now */
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
"sata_hba_ioctl: reset port: "
"failed cport %d pmport %d",
cport)->cport_mutex);
&sata_device);
cport)->cport_mutex);
}
/*
* Since the port was reset, it should be probed and
* attached device reinitialized. At this point the
* port state is unknown - it's state is HBA-specific.
* Re-probe port to get its state.
*/
SATA_SUCCESS) {
break;
}
break;
case SATA_CFGA_RESET_DEVICE:
/*
* There is no protection here for configured
* device.
*/
/* Sanity check */
"sata_hba_ioctl: "
"sata_hba_tran missing required "
"function sata_tran_reset_dport"));
break;
}
/* handle only device attached to cports, for now */
&sata_device);
cport)->cport_mutex);
break;
}
/* only handle cport for now */
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
"sata_hba_ioctl: reset device: failed "
cport)->cport_mutex);
&sata_device);
/*
* Device info structure remains
* attached. Another device reset or
* port disconnect/connect and re-probing is
* needed to change it's state
*/
sdinfo->satadrv_state |=
cport)->cport_mutex);
}
/*
* Since the device was reset, we expect reset event
* to be reported and processed.
*/
break;
case SATA_CFGA_RESET_ALL:
{
int tcport;
/*
* There is no protection here for configured
* devices.
*/
/* Sanity check */
"sata_hba_ioctl: "
"sata_hba_tran missing required "
"function sata_tran_reset_dport"));
break;
}
/*
* Need to lock all ports, not just one.
* If any port is locked by event processing, fail
* the whole operation.
* One port is already locked, but for simplicity
* lock it again.
*/
for (tcport = 0;
tcport++) {
tcport)->cport_mutex);
SATA_EVNT_LOCK_PORT_BUSY) != 0) {
tcport)->cport_mutex);
break;
} else {
tcport)->cport_event_flags |=
}
tcport)->cport_mutex);
}
if (rv == 0) {
/*
* All cports successfully locked.
* Reset main SATA controller only for now -
* no PMult.
*/
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
"sata_hba_ioctl: reset controller "
"failed"));
}
/*
* Since ports were reset, they should be
* re-probed and attached devices
* reinitialized.
* At this point port states are unknown,
* Re-probe ports to get their state -
* cports only for now.
*/
for (tcport = 0;
tcport++) {
&sata_device) != SATA_SUCCESS)
}
}
/*
* Unlock all ports
*/
for (tcport = 0;
tcport++) {
tcport)->cport_mutex);
tcport)->cport_mutex);
}
/*
* This operation returns EFAULT if either reset
* controller failed or a re-probbing of any ports
* failed.
* We return here, because common return is for
* a single cport operation.
*/
return (rv);
}
/* Sanity check */
break;
}
/*
* Arbitrarily unconfigure attached device, if any.
* Even if the unconfigure fails, proceed with the
* port deactivation.
*/
/* Handle only device attached to cports, for now */
/*
* Handle only device attached to cports,
* for now
*/
&sata_device);
(sdinfo->satadrv_type &
/*
* If a target node exists, try to
* offline a device and remove target
* node.
*/
/* target node exist */
"sata_hba_ioctl: "
"port deactivate: "
"target node exists.",
NULL);
if (ndi_devi_offline(tdip,
NDI_UNCONFIG) !=
NDI_SUCCESS) {
"sata_hba_ioctl:"
"port deactivate: "
"failed to "
"unconfigure "
"device at cport "
"%d", cport));
}
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
/*
* Problem;
* target node remained
* attached.
* Too bad...
*/
"sata_hba_ioctl: "
"port deactivate: "
"failed to "
"unconfigure "
"device at "
"cport %d",
cport));
}
}
/*
* In any case,
* remove and release sata_drive_info
* structure.
* (cport attached device ony, for now)
*/
NULL;
sizeof (sata_drive_info_t));
}
/*
* Note: PMult info requires different
* handling. This comment is a placeholder for
* a code handling PMult, to be implemented
* in phase 2.
*/
}
/* handle cport only for now */
/* Just let HBA driver to deactivate port */
(dip, &sata_device);
/*
* Generate sysevent -
* EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint
*/
if (rval != SATA_SUCCESS) {
/*
* Port deactivation failure - do not
* change port state unless the state
* returned by HBA indicates a port failure.
*/
if (sata_device.satadev_state &
}
"sata_hba_ioctl: port deactivate: "
"cannot deactivate SATA cport %d",
cport));
} else {
}
break;
case SATA_CFGA_PORT_ACTIVATE:
{
/* Sanity check */
break;
}
/* handle cport only for now */
dev_existed = FALSE;
/* Just let HBA driver to activate port */
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
/*
* Port activation failure - do not
* change port state unless the state
* returned by HBA indicates a port failure.
*/
cport)->cport_mutex);
&sata_device);
if (sata_device.satadev_state &
}
cport)->cport_mutex);
"sata_hba_ioctl: port activate: "
"cannot activate SATA cport %d",
cport));
break;
}
/*
* Re-probe port to find its current state and
* possibly attached device.
* Port re-probing may change the cportinfo device
* type if device is found attached.
* If port probing failed, the device type would be
* set to SATA_DTYPE_NONE.
*/
/*
* Generate sysevent -
* EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint.
*/
if (dev_existed == FALSE &&
/*
* That's the transition from "inactive" port
* state or active port without a device
* attached to the active port state with
* a device attached.
*/
"SATA device detected at port %d", cport);
}
break;
}
case SATA_CFGA_PORT_SELF_TEST:
/* Sanity check */
break;
}
/*
* There is no protection here for a configured
* device attached to this port.
*/
/* only handle cport for now */
if ((*SATA_SELFTEST_FUNC(sata_hba_inst))
"sata_hba_ioctl: port selftest: "
"failed cport %d pmport %d",
cport)->cport_mutex);
&sata_device);
cport)->cport_mutex);
break;
}
/*
* Since the port was reset, it should be probed and
* attached device reinitialized. At this point the
* port state is unknown - it's state is HBA-specific.
* Force port re-probing to get it into a known state.
*/
SATA_SUCCESS) {
break;
}
break;
{
char path[MAXPATHLEN];
NULL) {
/*
* No such device.
* If this is a request for a size, do not
* return EINVAL for non-exisiting target,
* because cfgadm will indicate a meaningless
* ioctl failure.
* If this is a real request for a path,
* indicate invalid argument.
*/
break;
}
} else {
}
if (ddi_copyout((void *)&size,
}
} else {
} else if (ddi_copyout((void *)&path,
}
}
break;
}
case SATA_CFGA_GET_AP_TYPE:
{
const char *ap_type;
/* cport only, no port multiplier support */
case SATA_DTYPE_NONE:
ap_type = "port";
break;
case SATA_DTYPE_ATADISK:
ap_type = "disk";
break;
case SATA_DTYPE_ATAPICD:
break;
case SATA_DTYPE_PMULT:
ap_type = "pmult";
break;
case SATA_DTYPE_UNKNOWN:
ap_type = "unknown";
break;
default:
ap_type = "unsupported";
break;
} /* end of dev_type switch */
if (ddi_copyout((void *)&type_len,
break;
}
} else {
break;
}
break;
}
}
break;
}
case SATA_CFGA_GET_MODEL_INFO:
{
/*
* This operation should return to cfgadm the
* device model information string
*/
/* only handle device connected to cport for now */
&sata_device);
cport)->cport_mutex);
break;
}
if (ddi_copyout((void *)&info_len,
break;
}
} else {
break;
}
break;
}
}
break;
}
{
char ap_info[
/*
* This operation should return to cfgadm the
* device firmware revision information string
*/
/* only handle device connected to cport for now */
&sata_device);
cport)->cport_mutex);
break;
}
if (ddi_copyout((void *)&info_len,
break;
}
} else {
break;
}
break;
}
}
break;
}
{
char ap_info[
/*
* This operation should return to cfgadm the
* device serial number information string
*/
/* only handle device connected to cport for now */
&sata_device);
cport)->cport_mutex);
break;
}
if (ddi_copyout((void *)&info_len,
break;
}
} else {
break;
}
break;
}
}
break;
}
default:
break;
} /* End of DEVCTL_AP_CONTROL cmd switch */
break;
}
default:
{
/*
* If we got here, we got an IOCTL that SATA HBA Framework
* does not recognize. Pass ioctl to HBA driver, in case
* it could process it.
*/
"IOCTL 0x%2x not supported in SATA framework, "
"passthrough to HBA", cmd);
break;
}
if (rval != 0) {
"IOCTL 0x%2x failed in HBA", cmd);
}
break;
}
} /* End of main IOCTL switch */
if (dcp) {
}
return (rv);
}
/* ****************** SCSA required entry points *********************** */
/*
* Implementation of scsi tran_tgt_init.
* sata_scsi_tgt_init() initializes scsi_device structure
*
* If successful, DDI_SUCCESS is returned.
* DDI_FAILURE is returned if addressed device does not exist
*/
static int
{
#ifndef __lock_lint
#endif
/* Validate scsi device address */
&sata_device) != 0)
return (DDI_FAILURE);
/* sata_device now contains a valid sata address */
return (DDI_FAILURE);
}
"sata_scsi_tgt_init: variant atapi "
"property could not be created"));
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
/*
* Implementation of scsi tran_tgt_probe.
* Probe target, by calling default scsi routine scsi_hba_probe()
*/
static int
{
int rval;
if (rval == SCSIPROBE_EXISTS) {
/*
* Set property "pm-capable" on the target device node, so that
* the target driver will not try to fetch scsi cycle counters
* before enabling device power-management.
*/
"device at port %d: will not be power-managed ",
"failure updating pm-capable property"));
}
}
return (rval);
}
/*
* Implementation of scsi tran_tgt_free.
* Release all resources allocated for scsi_device
*/
static void
{
#ifndef __lock_lint
#endif
/* Validate scsi device address */
&sata_device) != 0)
return;
/* sata_device now should contain a valid sata address */
return;
}
/*
* We did not allocate any resources in sata_scsi_tgt_init()
* other than property for ATAPI device, if any
*/
"sata_scsi_tgt_free: variant atapi "
"property could not be removed"));
} else {
}
}
/*
* Implementation of scsi tran_init_pkt
* Upon successful return, scsi pkt buffer has DMA resources allocated.
*
* It seems that we should always allocate pkt, even if the address is
* for non-existing device - just use some default for dma_attr.
* The reason is that there is no way to communicate this to a caller here.
* Subsequent call to sata_scsi_start may fail appropriately.
* Simply returning NULL does not seem to discourage a target driver...
*
* Returns a pointer to initialized scsi_pkt, or NULL otherwise.
*/
static struct scsi_pkt *
{
int rval;
/*
* We need to translate the address, even if it could be
* a bogus one, for a non-existing device
*/
/*
* Have to allocate a brand new scsi packet.
* We need to operate with auto request sense enabled.
*/
return (NULL);
/* Fill scsi packet structure */
pkt->pkt_statistics = 0;
pkt->pkt_reason = 0;
/*
* pkt_hba_private will point to sata pkt txlate structure
*/
/* Allocate sata_pkt */
/* Could not allocate sata pkt */
return (NULL);
}
/* Set sata address */
return (pkt);
} else {
/*
* Packet was preallocated/initialized by previous call
*/
return (pkt);
}
/* Pkt is available already: spx->txlt_scsi_pkt == pkt; */
}
/*
* We use an adjusted version of the dma_attr, to account
* for device addressing limitations.
* sata_adjust_dma_attr() will handle sdinfo == NULL which may
* happen when a device is not yet configured.
*/
/* NULL sdinfo may be passsed to sata_adjust_dma_attr() */
/*
* Allocate necessary DMA resources for the packet's buffer
*/
&cur_dma_attr)) != DDI_SUCCESS) {
/*
* If a DMA allocation request fails with
* DDI_DMA_NOMAPPING, indicate the error by calling
* bioerror(9F) with bp and an error code of EFAULT.
* If a DMA allocation request fails with
* DDI_DMA_TOOBIG, indicate the error by calling
* bioerror(9F) with bp and an error code of EINVAL.
*/
switch (rval) {
case DDI_DMA_NORESOURCES:
break;
case DDI_DMA_NOMAPPING:
case DDI_DMA_BADATTR:
break;
case DDI_DMA_TOOBIG:
default:
break;
}
}
return (NULL);
}
/* Set number of bytes that are not yet accounted for */
return (pkt);
}
/*
* Implementation of scsi tran_start.
* Translate scsi cmd into sata operation and return status.
* Supported scsi commands:
* SCMD_INQUIRY
* SCMD_TEST_UNIT_READY
* SCMD_START_STOP
* SCMD_READ_CAPACITY
* SCMD_REQUEST_SENSE
* SCMD_LOG_SENSE_G1 (unimplemented)
* SCMD_LOG_SELECT_G1 (unimplemented)
* SCMD_MODE_SENSE (specific pages)
* SCMD_MODE_SENSE_G1 (specific pages)
* SCMD_MODE_SELECT (specific pages)
* SCMD_MODE_SELECT_G1 (specific pages)
* SCMD_SYNCHRONIZE_CACHE
* SCMD_SYNCHRONIZE_CACHE_G1
* SCMD_READ
* SCMD_READ_G1
* SCMD_READ_G4
* SCMD_READ_G5
* SCMD_WRITE
* SCMD_WRITE_G1
* SCMD_WRITE_G4
* SCMD_WRITE_G5
* SCMD_SEEK (noop)
*
* All other commands are rejected as unsupported.
*
* Returns:
* TRAN_ACCEPT if command was executed successfully or accepted by HBA driver
* for execution.
* TRAN_BADPKT if cmd was directed to invalid address.
* TRAN_FATAL_ERROR is command was rejected due to hardware error, including
* unexpected removal of a device or some other unspecified error.
* TRAN_BUSY if command could not be executed becasue HBA driver or SATA
* framework was busy performing some other operation(s).
*
*/
static int
{
int cport;
int rval;
/*
* Mutex-protected section below is just to identify device type
* and switch to ATAPI processing, if necessary
*/
return (TRAN_FATAL_ERROR);
}
"sata_scsi_start atapi: rval %d\n", rval);
return (rval);
}
/* ATA Disk commands processing starts here */
case SCMD_INQUIRY:
/* Mapped to identify device */
break;
case SCMD_TEST_UNIT_READY:
/*
* SAT "SATA to ATA Translation" doc specifies translation
* to ATA CHECK POWER MODE.
*/
break;
case SCMD_START_STOP:
/* Mapping depends on the command */
break;
case SCMD_READ_CAPACITY:
break;
case SCMD_REQUEST_SENSE:
/*
* Always No Sense, since we force ARQ
*/
break;
case SCMD_LOG_SENSE_G1:
break;
case SCMD_LOG_SELECT_G1:
break;
case SCMD_MODE_SENSE:
case SCMD_MODE_SENSE_G1:
break;
case SCMD_MODE_SELECT:
case SCMD_MODE_SELECT_G1:
break;
case SCMD_SYNCHRONIZE_CACHE:
break;
case SCMD_READ:
case SCMD_READ_G1:
case SCMD_READ_G4:
case SCMD_READ_G5:
break;
case SCMD_WRITE:
case SCMD_WRITE_G1:
case SCMD_WRITE_G4:
case SCMD_WRITE_G5:
break;
case SCMD_SEEK:
break;
/* Other cases will be filed later */
/* postponed until phase 2 of the development */
default:
break;
}
"sata_scsi_start: rval %d\n", rval);
return (rval);
}
/*
* Implementation of scsi tran_abort.
* Abort specific pkt or all packets.
*
* Returns 1 if one or more packets were aborted, returns 0 otherwise
*
* May be called from an interrupt level.
*/
static int
{
"sata_scsi_abort: %s at target: 0x%x\n",
/* Validate address */
/* Invalid address */
return (0);
/* invalid address */
return (0);
}
/*
* Abort all packets.
* Although we do not have specific packet, we still need
* dummy packet structure to pass device address to HBA.
* Allocate one, without sleeping. Fail if pkt cannot be
* allocated.
*/
"could not allocate sata_pkt"));
return (0);
}
} else {
return (0); /* Bad scsi pkt */
}
/* extract pointer to sata pkt */
}
/* Send abort request to HBA */
if ((*SATA_ABORT_FUNC(sata_hba_inst))
SATA_SUCCESS) {
/* Success */
return (1);
}
/* Else, something did not go right */
/* Failure */
return (0);
}
/*
* Implementation os scsi tran_reset.
* RESET_ALL request is translated into port reset.
* RESET_TARGET requests is translated into a device reset,
* RESET_LUN request is accepted only for LUN 0 and translated into
* device reset.
* The target reset should cause all HBA active and queued packets to
* be terminated and returned with pkt reason SATA_PKT_RESET prior to
* the return. HBA should report reset event for the device.
*
* Returns 1 upon success, 0 upon failure.
*/
static int
{
int val;
"sata_scsi_reset: level %d target: 0x%x\n",
/* Validate address */
if (val == -1)
/* Invalid address */
return (0);
/* invalid address */
return (0);
}
/* port reset - cport only */
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
return (1);
else
return (0);
} else if (val == 0 &&
/* reset device (device attached) */
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
return (1);
else
return (0);
}
return (0);
}
/*
* Supported capabilities:
* auto-rqsense (always supported)
* tagged-qing (supported if HBA supports it)
* dma_max
* interconnect-type (INTERCONNECT_SATA)
*
* Request for other capabilities is rejected as unsupported.
*
* Returns supported capability value, or -1 if capability is unsuppported or
* the address is invalid (no device).
*/
static int
{
int rval;
"sata_scsi_getcap: target: 0x%x, cap: %s\n",
/*
* We want to process the capabilities on per port granularity.
* So, we are specifically restricting ourselves to whom != 0
* to exclude the controller wide handling.
*/
return (-1);
/* Invalid address */
return (-1);
}
NULL) {
/* invalid address */
return (0);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
break;
case SCSI_CAP_SECTOR_SIZE:
else rval = -1;
break;
case SCSI_CAP_TAGGED_QING:
/*
* It is enough if the controller supports queuing, regardless
* of the device. NCQ support is an internal implementation
* feature used between HBA and the device.
*/
else
break;
case SCSI_CAP_DMA_MAX:
&adj_dma_attr);
/* We rely on the fact that dma_attr_maxxfer < 0x80000000 */
break;
break;
default:
rval = -1;
break;
}
return (rval);
}
/*
* Implementation of scsi tran_setcap
*
* All supported capabilities are fixed/unchangeable.
* Returns 0 for all supported capabilities and valid device, -1 otherwise.
*/
static int
{
#ifndef __lock_lint
#endif
int rval;
/*
* We want to process the capabilities on per port granularity.
* So, we are specifically restricting ourselves to whom != 0
* to exclude the controller wide handling.
*/
return (-1);
}
/* Invalid address */
return (-1);
}
/* invalid address */
return (0);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
case SCSI_CAP_SECTOR_SIZE:
case SCSI_CAP_TAGGED_QING:
case SCSI_CAP_DMA_MAX:
rval = 0; /* Capability cannot be changed */
break;
default:
rval = -1;
break;
}
return (rval);
}
/*
* Implementations of scsi tran_destroy_pkt.
* Free resources allocated by sata_scsi_init_pkt()
*/
static void
{
/*
* Free DMA resources - cookies and handles
*/
}
}
/*
* Implementation of scsi tran_dmafree.
* Free DMA resources allocated by sata_scsi_init_pkt()
*/
static void
{
#ifndef __lock_lint
#endif
/*
* Free DMA resources - cookies and handles
*/
}
}
/*
* Implementation of scsi tran_sync_pkt.
*
* The assumption below is that pkt is unique - there is no need to check ap
*/
static void
{
#ifndef __lock_lint
#endif
int rval;
if (rval == DDI_SUCCESS) {
"sata_scsi_sync_pkt: sync pkt failed"));
}
}
}
}
/* ******************* SATA - SCSI Translation functions **************** */
/*
* translation.
*/
/*
* Checks if a device exists and can be access and translates common
* scsi_pkt data to sata_pkt data.
*
* Returns TRAN_ACCEPT if device exists and sata_pkt was set-up.
* Returns other TRAN_XXXXX values when error occured.
*
* This function should be called with port mutex held.
*/
static int
{
const struct sata_cmd_flags sata_initial_cmd_flags = {
/* all other values to 0/FALSE */
};
/* Validate address */
case -1:
/* Invalid address or invalid device type */
return (TRAN_BADPKT);
case 1:
/* valid address but no device - it has disappeared ? */
return (TRAN_FATAL_ERROR);
default:
/* all OK */
break;
}
/*
* If device is in reset condition, reject the packet with
* TRAN_BUSY
*/
if (sdinfo->satadrv_event_flags &
"sata_scsi_start: rejecting command because "
"of device reset state\n", NULL);
return (TRAN_BUSY);
}
/*
* Fix the dev_type in the sata_pkt->satapkt_device. It was not set by
* sata_scsi_pkt_init() because pkt init had to work also with
* non-existing devices.
* Now we know that the packet was set-up for a real device, so its
* type is known.
*/
/* Synchronous execution */
} else {
/* Asynchronous execution */
}
/* Convert queuing information */
/* Always limit pkt time */
else
/* Pass on scsi_pkt time */
return (TRAN_ACCEPT);
}
/*
* Translate ATA(ATAPI) Identify (Packet) Device data to SCSI Inquiry data.
* SATA Identify Device data has to be valid in sata_rive_info.
* Buffer has to accomodate the inquiry length (36 bytes).
*
* This function should be called with a port mutex held.
*/
static void
{
/* Start with a nice clean slate */
/* Rely on the dev_type for setting paripheral qualifier */
/*
* Queuing support - controller has to
* support some sort of command queuing.
*/
else
#ifdef _LITTLE_ENDIAN
/* Swap text fields to match SCSI format */
else
#else
else
#endif
}
/*
* Scsi response set up for invalid command (command not supported)
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* Scsi response setup for
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
return (rval);
}
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Inquiry / Identify Device
* Use cached Identify Device data for now, rather then issuing actual
* Device Identify cmd request. If device is detached and re-attached,
* asynchromous event processing should fetch and refresh Identify Device
* data.
* Two VPD pages are supported now:
* Vital Product Data page
* Unit Serial Number page
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
#define INQUIRY_SUP_VPD_PAGE 0 /* Supported VDP Pages Page COde */
static int
{
struct scsi_extended_sense *sense;
int count;
uint8_t *p;
int i, j;
int rval;
return (rval);
}
/* Reject not supported request */
goto done;
}
/* Valid Inquiry request */
/* Standard Inquiry Data request */
struct scsi_inquiry inq;
unsigned int bufsize;
/* Copy no more than requested */
sizeof (struct scsi_inquiry));
} else {
/*
* peripheral_qualifier = 0;
*
* We are dealing only with HD and will be
*/
case INQUIRY_SUP_VPD_PAGE:
/*
* Request for suported Vital Product Data
* pages - assuming only 2 page codes
* supported
*/
page_buf[2] = 0;
/* Copy no more than requested */
break;
case INQUIRY_USN_PAGE:
/*
* Request for Unit Serial Number page
*/
page_buf[2] = 0;
#ifdef _LITTLE_ENDIAN
#else
#endif
for (i = 0; i < 20; i++) {
break;
}
}
/*
* 'i' contains string length.
*
* Least significant character of the serial
* number shall appear as the last byte,
* according to SBC-3 spec.
*/
for (j = i; j > 0; j--, p--) {
*p = *(p - 20 + i);
}
p = &page_buf[4];
for (j = 20 - i; j > 0; j--) {
*p++ = '\040';
}
break;
/*
* We may want to implement this page, when
* identifiers are common for SATA devices
* But not now.
*/
/*FALLTHRU*/
default:
/* Request for unsupported VPD page */
sense->es_add_code =
goto done;
}
}
}
done:
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Request Sense
* emulated command (ATA version so far, no ATAPI)
* Always NO SENSE, because any sense data should be reported by ARQ sense.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense sense;
int rval;
return (rval);
}
sizeof (struct scsi_extended_sense));
/* Copy no more than requested */
}
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Test Unit Ready
* At the moment this is an emulated command (ATA version so far, no ATAPI).
* May be translated into Check Power Mode command in the future
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
int power_state;
int rval;
return (rval);
}
/* At this moment, emulate it rather than execute anything */
switch (power_state) {
case SATA_PWRMODE_ACTIVE:
case SATA_PWRMODE_IDLE:
break;
default:
/* PWR mode standby */
break;
}
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Start Stop Unit
* Translation depends on a command:
* Start Unit translated into Idle Immediate
* Stop Unit translated into Standby Immediate
* Unload Media / NOT SUPPORTED YET
* Load Media / NOT SUPPROTED YET
* Power condition bits are ignored, so is Immediate bit
* Requesting synchronous execution.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
int rval;
int synch;
return (rval);
}
/* scsi callback required */
return (TRAN_ACCEPT);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_features_reg = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
/* Start Unit */
} else {
/* Stop Unit */
}
/* Need to set-up a callback function */
} else {
}
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/*
* If execution is non-synchronous,
* a callback function will handle potential errors, translate
* the response and will do a callback to a target driver.
* If it was synchronous, check execution status using the same
* framework callback.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Read Capacity.
* Emulated command for SATA disks.
* Capacity is retrieved from cached Idenifty Device data.
* Identify Device data shows effective disk capacity, not the native
* capacity, which may be limitted by Set Max Address command.
* This is ATA version (non-ATAPI).
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
"sata_txlt_read_capacity: ", NULL);
return (rval);
}
/* Last logical block address */
/* Need to swap endians to match scsi format */
/* block size - always 512 bytes, for now */
rbuf[4] = 0;
rbuf[5] = 0;
rbuf[7] = 0;
}
/*
* If a callback was requested, do it now.
*/
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Mode Sense.
* Translated into appropriate SATA command or emulated.
* Saved Values Page Control (03) are not supported.
*
* NOTE: only caching mode sense page is currently implemented.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
int pc; /* Page Control code */
int rval;
"sata_txlt_mode_sense, pc %x page code 0x%02x\n",
return (rval);
}
len = 0;
bdlen = 0;
bdlen = 16;
else
bdlen = 8;
}
/* Build mode parameter header */
/* 4-byte mode parameter header */
} else {
/* 8-byte mode parameter header */
if (bdlen == 16)
else
}
/* Build block descriptor only if not disabled (DBD) */
/* Block descriptor - direct-access device format */
if (bdlen == 8) {
/* build regular block descriptor */
if (sdinfo->satadrv_type ==
else
/* ATAPI */
} else if (bdlen == 16) {
/* Long LBA Accepted */
/* build long lba block descriptor */
#ifndef __lock_lint
#endif
if (sdinfo->satadrv_type ==
else
/* ATAPI */
}
}
/*
* Add requested pages.
* Page 3 and 4 are obsolete and we are not supporting them.
* We deal now with:
* We should eventually deal with following mode pages:
* error recovery (0x01),
* power condition (0x1a),
* exception control page (enables SMART) (0x1c),
* enclosure management (ses),
* protocol-specific port mode (port control).
*/
case MODEPAGE_RW_ERRRECOV:
/* DAD_MODE_ERR_RECOV */
/* R/W recovery */
break;
case MODEPAGE_CACHING:
/* DAD_MODE_CACHE */
/* Reject not supported request for saved parameters */
if (pc == 3) {
sense->es_add_code =
goto done;
}
/* caching */
break;
case MODEPAGE_INFO_EXCPT:
/* exception cntrl */
}
else
goto err;
break;
case MODEPAGE_POWER_COND:
/* DAD_MODE_POWER_COND */
/* power condition */
break;
case MODEPAGE_ALLPAGES:
/* all pages */
}
break;
default:
err:
/* Invalid request */
goto done;
}
/* fix total mode data length */
/* 4-byte mode parameter header */
} else {
}
/* Check allocation length */
} else {
}
/*
* We do not check for possible parameters truncation
* (alc_len < len) assuming that the target driver works
* correctly. Just avoiding overrun.
* Copy no more than requested and possible, buffer-wise.
*/
}
done:
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Mode Select.
* Translated into appropriate SATA command or emulated.
* Saving parameters is not supported.
* Changing device capacity is not supported (although theoretically
*
* Assumption is that the target driver is working correctly.
*
* More than one SATA command may be executed to perform operations specified
* by mode select pages. The first error terminates further execution.
* Operations performed successully are not backed-up in such case.
*
* NOTE: only caching mode select page is implemented.
* Caching setup is remembered so it could be re-stored in case of
* an unexpected device reset.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
int dmod = 0;
"sata_txlt_mode_select, pc %x page code 0x%02x\n",
return (rval);
}
rval = TRAN_ACCEPT;
/* Reject not supported request */
goto done;
}
} else {
}
/*
* Check the header to skip the block descriptor(s) - we
* do not support setting device capacity.
* Existing macros do not recognize long LBA dscriptor,
* hence manual calculation.
*/
/* 6-bytes CMD, 4 bytes header */
if (count <= 4)
goto done; /* header only */
} else {
/* 10-bytes CMD, 8 bytes header */
if (count <= 8)
goto done; /* header only */
}
goto done; /* header + descriptor(s) only */
/*
* We may be executing SATA command and want to execute it
* in SYNCH mode, regardless of scsi_pkt setting.
* Save scsi_pkt setting and indicate SYNCH mode
*/
}
/*
* len is now the offset to a first mode select page
* Process all pages
*/
while (pllen > 0) {
case MODEPAGE_CACHING:
/* No support for SP (saving) */
sense->es_add_code =
goto done;
}
/*
* The pagelen value indicates the number of
* parameter bytes already processed.
* The rval is the return value from
* sata_tran_start().
* The stat indicates the overall status of
* the operation(s).
*/
if (stat != SATA_SUCCESS)
/*
* Page processing did not succeed -
* all error info is already set-up,
* just return
*/
pllen = 0; /* this breaks the loop */
else {
}
break;
case MODEPAGE_INFO_EXCPT:
/*
* The pagelen value indicates the number of
* parameter bytes already processed.
* The rval is the return value from
* sata_tran_start().
* The stat indicates the overall status of
* the operation(s).
*/
if (stat != SATA_SUCCESS)
/*
* Page processing did not succeed -
* all error info is already set-up,
* just return
*/
pllen = 0; /* this breaks the loop */
else {
}
break;
default:
sense->es_add_code =
goto done;
}
}
}
done:
/*
* If device parameters were modified, fetch and store the new
* Identify Device data. Since port mutex could have been released
* for accessing HBA driver, we need to re-check device existence.
*/
if (dmod != 0) {
int rv;
&new_sdinfo);
/*
* Since port mutex could have been released when
* accessing HBA driver, we need to re-check that the
* framework still holds the device info structure.
*/
/*
* Device still has info structure in the
* sata framework. Copy newly fetched info
*/
if (rv == 0) {
} else {
/*
* Could not fetch new data - invalidate
* sata_drive_info. That makes device
* unusable.
*/
}
}
/*
* This changes the overall mode select completion
* reason to a failed one !!!!!
*/
rval = TRAN_ACCEPT;
}
}
/* Restore the scsi pkt flags */
/* scsi callback required */
return (rval);
}
/*
* Translate command: Log Sense
*/
static int
{
struct scsi_extended_sense *sense;
int pc; /* Page Control code */
int page_code; /* Page code */
int rval;
#define MAX_LOG_SENSE_PAGE_SIZE 512
"sata_txlt_log_sense, pc 0x%x, page code 0x%x\n",
return (rval);
}
/* Reject not supported request for all but cummulative values */
switch (pc) {
case PC_CUMMULATIVE_VALUES:
break;
default:
goto done;
}
switch (page_code) {
break;
default:
goto done;
}
len = 0;
/* Build log parameter header */
/*
* Add requested pages.
*/
switch (page_code) {
break;
if ((! (sata_id->ai_cmdset84 &
(! (sata_id->ai_features87 &
sense->es_add_code =
goto done;
}
break;
sense->es_add_code =
goto done;
}
sense->es_add_code =
goto done;
}
break;
sense->es_add_code =
goto done;
}
sense->es_add_code =
goto done;
}
/* This page doesn't include a page header */
goto no_header;
default:
/* Invalid request */
goto done;
}
/* set parameter log sense data length */
/* Check allocation length */
/*
* We do not check for possible parameters truncation
* (alc_len < len) assuming that the target driver works
* correctly. Just avoiding overrun.
* Copy no more than requested and possible, buffer-wise.
*/
}
done:
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* Translate command: Log Select
* Not implemented at this time - returns invalid command response.
*/
static int
{
"sata_txlt_log_select\n", NULL);
return (sata_txlt_invalid_command(spx));
}
/*
* Translate command: Read (various types).
* Translated into appropriate type of ATA READ command
* (NO ATAPI implementation yet).
* Both the device capabilities and requested operation mode are
* considered.
*
* Following scsi cdb fields are ignored:
* rdprotect, dpo, fua, fua_nv, group_number.
*
* If SATA_ENABLE_QUEUING flag is set (in the global SATA HBA framework
* enable variable sata_func_enable), the capability of the controller and
* capability of a device are checked and if both support queueing, read
* request will be translated to READ_DMA_QUEUEING or READ_DMA_QUEUEING_EXT
* command rather than plain READ_XXX command.
* If SATA_ENABLE_NCQ flag is set in addition to SATA_ENABLE_QUEUING flag and
* both the controller and device suport such functionality, the read
* request will be translated to READ_FPDMA_QUEUED command.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
int synch;
return (rval);
}
/*
* Build cmd block depending on the device capability and
* requested operation mode.
* Do not bother with non-dma mode.
*/
case SCMD_READ:
/* 6-byte scsi read cmd : 0x08 */
/* sec_count 0 will be interpreted as 256 by a device */
break;
case SCMD_READ_G1:
/* 10-bytes scsi read command : 0x28 */
break;
case SCMD_READ_G5:
/* 12-bytes scsi read command : 0xA8 */
break;
case SCMD_READ_G4:
/* 16-bytes scsi read command : 0x88 */
break;
default:
/* Unsupported command */
return (sata_txlt_invalid_command(spx));
}
/*
* Check if specified address exceeds device capacity
*/
/* LBA out of range */
return (sata_txlt_lba_out_of_range(spx));
}
#ifndef __lock_lint
#endif
}
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/*
* Check if queueing commands should be used and switch
* to appropriate command if possible
*/
if (sata_func_enable & SATA_ENABLE_QUEUING) {
/* Queuing supported by controller and device */
if ((sata_func_enable & SATA_ENABLE_NCQ) &&
SATA_DEV_F_NCQ) &&
SATA_CTLF_NCQ)) {
/* NCQ supported - use FPDMA READ */
scmd->satacmd_sec_count_msb = 0;
} else {
/* Legacy queueing */
if (sdinfo->satadrv_features_support &
scmd->satacmd_sec_count_msb = 0;
} else {
}
}
scmd->satacmd_sec_count_lsb = 0;
}
}
"sata_txlt_read cmd 0x%2x, lba %llx, sec count %x\n",
/* Need callback function */
} else
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/*
* If execution is non-synchronous,
* a callback function will handle potential errors, translate
* the response and will do a callback to a target driver.
* If it was synchronous, check execution status using the same
* framework callback.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Write (various types)
* Translated into appropriate type of ATA WRITE command
* (NO ATAPI implementation yet).
* Both the device capabilities and requested operation mode are
* considered.
*
* Following scsi cdb fields are ignored:
* rwprotect, dpo, fua, fua_nv, group_number.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
int synch;
return (rval);
}
/*
* Build cmd block depending on the device capability and
* requested operation mode.
* Do not bother with non-dma mode.
*/
case SCMD_WRITE:
/* 6-byte scsi read cmd : 0x0A */
/* sec_count 0 will be interpreted as 256 by a device */
break;
case SCMD_WRITE_G1:
/* 10-bytes scsi write command : 0x2A */
break;
case SCMD_WRITE_G5:
/* 12-bytes scsi read command : 0xAA */
break;
case SCMD_WRITE_G4:
/* 16-bytes scsi write command : 0x8A */
break;
default:
/* Unsupported command */
return (sata_txlt_invalid_command(spx));
}
/*
* Check if specified address and length exceeds device capacity
*/
/* LBA out of range */
return (sata_txlt_lba_out_of_range(spx));
}
#ifndef __lock_lint
#endif
}
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/*
* Check if queueing commands should be used and switch
* to appropriate command if possible
*/
if (sata_func_enable & SATA_ENABLE_QUEUING) {
/* Queuing supported by controller and device */
if ((sata_func_enable & SATA_ENABLE_NCQ) &&
SATA_DEV_F_NCQ) &&
SATA_CTLF_NCQ)) {
/* NCQ supported - use FPDMA WRITE */
scmd->satacmd_sec_count_msb = 0;
} else {
/* Legacy queueing */
if (sdinfo->satadrv_features_support &
scmd->satacmd_sec_count_msb = 0;
} else {
}
}
scmd->satacmd_sec_count_lsb = 0;
}
}
"sata_txlt_write cmd 0x%2x, lba %llx, sec count %x\n",
/* Need callback function */
} else
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/*
* If execution is non-synchronous,
* a callback function will handle potential errors, translate
* the response and will do a callback to a target driver.
* If it was synchronous, check execution status using the same
* framework callback.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* NOTE: NOT FUNCTIONAL IMPLEMENTATION. THIS IS A PLACEHOLDER for the function
* that will be fixed in phase 2 of the development.
* Currently ATAPI is not supported. ATAPI devices are threated as not-valid
* devices.
* This function is not called, since scsi_sata_start() will bail-out prior
* to calling it.
*/
static int
{
int rval;
int synch;
return (rval);
}
/*
* scmd->satacmd_flags.sata_data_direction default -
* SATA_DIR_NODATA_XFER - is set by
* sata_txlt_generic_pkt_info().
*/
if (scmd->satacmd_bp) {
} else {
}
}
/*
* map buffer
*/
case SCMD_READ:
case SCMD_READ_G1:
case SCMD_READ_G5:
case SCMD_READ_G4:
case SCMD_WRITE:
case SCMD_WRITE_G1:
case SCMD_WRITE_G5:
case SCMD_WRITE_G4:
break;
default:
break;
}
/* Need callback function */
} else
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/*
* If execution is non-synchronous,
* a callback function will handle potential errors, translate
* the response and will do a callback to a target driver.
* If it was synchronous, check execution status using the same
* framework callback.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* Translate command: Synchronize Cache.
* Translates into Flush Cache command.
* (NO ATAPI implementation yet).
*
* NOTE: We should check if Flush Cache is supported by the device (ATAPI
* devices)
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
int synch;
return (rval);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
"sata_txlt_synchronize_cache\n", NULL);
/* Need to set-up a callback function */
} else
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/*
* If execution non-synchronous, it had to be completed
* a callback function will handle potential errors, translate
* the response and will do a callback to a target driver.
* If it was synchronous, check status, using the same
* framework callback.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* Send pkt to SATA HBA driver
*
* This function may be called only if the operation is requested by scsi_pkt,
* i.e. scsi_pkt is not NULL.
*
* This function has to be called with cport mutex held. It does release
* the mutex when it calls HBA driver sata_tran_start function and
* re-acquires it afterwards.
*
* If return value is 0, pkt was accepted, -1 otherwise
* rval is set to appropriate sata_scsi_start return value.
*
* Note 1:If HBA driver returns value other than TRAN_ACCEPT, it should not
* have called the sata_pkt callback function for this packet.
*
* The scsi callback has to be performed by the caller of this routine.
*
* Note 2: No port multiplier support for now.
*/
static int
{
int stat;
struct sata_cmd_flags cmd_flags;
/* Clear device reset state? */
"sata_hba_start: clearing device reset state\n", NULL);
}
"Sata cmd 0x%2x\n", cmd);
spx->txlt_sata_pkt);
/*
* If sata pkt was accepted and executed in asynchronous mode, i.e.
* with the sata callback, the sata_pkt could be already destroyed
* by the time we check ther return status from the hba_start()
* function, because sata_scsi_destroy_pkt() could have been already
* called (perhaps in the interrupt context). So, in such case, there
* should be no references to it. In other cases, sata_pkt still
* exists.
*/
switch (stat) {
case SATA_TRAN_ACCEPTED:
/*
* pkt accepted for execution.
* If it was executed synchronously, it is already completed
* and pkt completion_reason indicates completion status.
*/
*rval = TRAN_ACCEPT;
return (0);
case SATA_TRAN_QUEUE_FULL:
/*
* Controller detected queue full condition.
*/
"sata_hba_start: queue full\n", NULL);
break;
case SATA_TRAN_PORT_ERROR:
/*
* Communication/link with device or general port error
* detected before pkt execution begun.
*/
"port %d error",
else
"port %d pmport %d error\n",
/*
* sata_pkt should be still valid. Since port error is
* returned, sata_device content should reflect port
* state - it means, that sata address have been changed,
* because original packet's sata address refered to a device
* attached to some port.
*/
*rval = TRAN_FATAL_ERROR;
break;
/*
* Command rejected by HBA as unsupported. It was HBA driver
* that rejected the command, command was not sent to
* an attached device.
*/
(void) sata_txlt_invalid_command(spx);
"sat_hba_start: cmd 0x%2x rejected "
"with SATA_TRAN_CMD_UNSUPPORTED status\n", cmd);
*rval = TRAN_ACCEPT;
break;
case SATA_TRAN_BUSY:
/*
* Command rejected by HBA because other operation prevents
* accepting the packet, or device is in RESET condition.
*/
"sata_hba_start: cmd 0x%2x rejected "
"because of device reset condition\n",
cmd);
} else {
"sata_hba_start: cmd 0x%2x rejected "
"with SATA_TRAN_BUSY status\n",
cmd);
}
}
break;
default:
/* Unrecognized HBA response */
"sata_hba_start: unrecognized HBA response "
*rval = TRAN_FATAL_ERROR;
break;
}
/*
* If we got here, the packet was rejected.
* Check if we need to remember reset state clearing request
*/
if (cmd_flags.sata_clear_dev_reset) {
/*
* Check if device is still configured - it may have
* disapeared from the configuration
*/
/*
* Restore the flag that requests clearing of
* the device reset state,
* so the next sata packet may carry it to HBA.
*/
}
}
return (-1);
}
/*
* Scsi response setup for invalid LBA
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
/* scsi callback required */
return (TRAN_ACCEPT);
}
/*
* Analyze device status and error registers and translate them into
* appropriate scsi sense codes.
* NOTE: non-packet commands only for now
*/
static void
struct scsi_extended_sense *sense)
{
if (err_reg & SATA_ERROR_ICRC) {
return;
}
if (err_reg & SATA_ERROR_UNC) {
/* Information bytes (LBA) need to be set by a caller */
return;
}
return;
}
if (err_reg & SATA_ERROR_IDNF) {
if (err_reg & SATA_ERROR_ABORT) {
} else {
}
return;
}
if (err_reg & SATA_ERROR_ABORT) {
return;
}
}
/*
* Extract error LBA from sata_pkt.satapkt_cmd register fields
*/
static void
{
*lba = 0;
}
}
/*
* This is fixed sense format - if LBA exceeds the info field size,
* no valid info will be returned (valid bit in extended sense will
* be set to 0).
*/
static struct scsi_extended_sense *
{
struct scsi_arq_status *arqs;
struct scsi_extended_sense *sense;
/* Fill ARQ sense data */
arqs->sts_rqpkt_resid = 0;
sense->es_cmd_info[0] = 0;
sense->es_add_code = 0;
sense->es_qual_code = 0;
return (sense);
}
/*
* pkt completion_reason is checked to determine the completion status.
* Do scsi callback if necessary.
*
* Note: this function may be called also for synchronously executed
* commands.
* This function may be used only if scsi_pkt is non-NULL.
*/
static void
{
struct scsi_extended_sense *sense;
/* Normal completion */
} else {
/*
* Something went wrong - analyze return
*/
/*
* SATA_PKT_DEV_ERROR is the only case where we may be able to
* extract from device registers the failing LBA.
*/
(scmd->satacmd_lba_mid_msb != 0 ||
scmd->satacmd_lba_high_msb != 0)) {
/*
* We have problem reporting this cmd LBA
* in fixed sense data format, because of
* the size of the scsi LBA fields.
*/
} else {
}
} else {
/* Invalid extended sense info */
}
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
/* We may want to handle DEV GONE state as well */
/*
* We have no device data. Assume no data transfered.
*/
break;
case SATA_PKT_DEV_ERROR:
/*
* determine dev error reason from error
* reg content
*/
switch (scmd->satacmd_cmd_reg) {
case SATAC_READ_DMA:
case SATAC_READ_DMA_EXT:
case SATAC_READ_DMA_QUEUED:
case SATAC_READ_FPDMA_QUEUED:
/* Unrecovered read error */
sense->es_add_code =
break;
case SATAC_WRITE_DMA:
case SATAC_WRITE_DMA_EXT:
case SATAC_WRITE_DMA_QUEUED:
case SATAC_WRITE_FPDMA_QUEUED:
/* Write error */
sense->es_add_code =
break;
default:
/* Internal error */
"sata_txlt_rw_completion :"
"internal error - invalid "
"command 0x%2x",
scmd->satacmd_cmd_reg));
break;
}
}
break;
}
/* No extended sense key - no info available */
break;
case SATA_PKT_TIMEOUT:
/* scsipkt->pkt_reason = CMD_TIMEOUT; */
/* No extended sense key ? */
break;
case SATA_PKT_ABORTED:
/* No extended sense key ? */
break;
case SATA_PKT_RESET:
break;
default:
"sata_txlt_rw_completion: "
"invalid packet completion reason"));
break;
}
}
/* scsi callback required */
}
/*
* NON FUNCTIONAL IMPLEMENTATION. THIS IS A PLACE HOLDER.
* ATAPI devices are not supported currently (are not be attached recognized
* as valid devices).
* Will be fixed in phase 2 of the development.
*/
static void
{
struct scsi_arq_status *arqs;
/* Normal completion */
} else {
/*
* Something went wrong - analyze return
*/
arqs->sts_rqpkt_resid = 0;
}
/* scsi callback required */
}
}
/*
* Translate completion status of non-data commands (i.e. commands returning
* no data).
* pkt completion_reason is checked to determine the completion status.
* Do scsi callback if necessary (FLAG_NOINTR == 0)
*
* Note: this function may be called also for synchronously executed
* commands.
* This function may be used only if scsi_pkt is non-NULL.
*/
static void
{
struct scsi_extended_sense *sense;
/* Normal completion */
} else {
/* Something went wrong */
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
/*
* We have no device data. Assume no data transfered.
*/
break;
case SATA_PKT_DEV_ERROR:
/*
* determine dev error reason from error
* reg content
*/
break;
}
/* No extended sense key - no info available */
break;
case SATA_PKT_TIMEOUT:
/* scsipkt->pkt_reason = CMD_TIMEOUT; */
/* No extended sense key ? */
break;
case SATA_PKT_ABORTED:
/* No extended sense key ? */
break;
case SATA_PKT_RESET:
/* pkt aborted by an explicit reset from a host */
break;
default:
"sata_txlt_nodata_cmd_completion: "
"invalid packet completion reason %d",
break;
}
}
/* scsi callback required */
}
/*
* Build Mode sense R/W recovery page
* NOT IMPLEMENTED
*/
static int
{
#ifndef __lock_lint
#endif
return (0);
}
/*
* Build Mode sense caching page - scsi-3 implementation.
* Page length distinguishes previous format from scsi-3 format.
* buf must have space for 0x12 bytes.
* Only DRA (disable read ahead ) and WCE (write cache enable) are changeable.
*
*/
static int
{
/*
*/
/* Saved paramters not supported */
if (pcntrl == 3)
return (0);
/*
* For now treat current and default parameters as same
* That may have to change, if target driver will complain
*/
}
} else {
/* Changeable parameters */
}
}
return (PAGELENGTH_DAD_MODE_CACHE_SCSI3 +
sizeof (struct mode_page));
}
/*
* Build Mode sense exception cntrl page
*/
static int
{
/*
*/
/* Indicate that this is page is saveable */
/*
* We will return the same data for default, current and saved page.
* The only changeable bit is dexcpt and that bit is required
* by the ATA specification to be preserved across power cycles.
*/
if (pcntrl != 1) {
}
else
return (PAGELENGTH_INFO_EXCPT + sizeof (struct mode_info_excpt_page));
}
/*
* Build Mode sense power condition page
* NOT IMPLEMENTED.
*/
static int
{
#ifndef __lock_lint
#endif
return (0);
}
/*
* Process mode select caching page 8 (scsi3 format only).
* Read Ahead (same as read cache) and Write Cache may be turned on and off
* if these features are supported by the device. If these features are not
* supported, quietly ignore them.
* This function fails only if the SET FEATURE command sent to
* the device fails. The page format is not varified, assuming that the
* target driver operates correctly - if parameters length is too short,
* we just drop the page.
* setting have to be changed.
* SET FEATURE command is executed synchronously, i.e. we wait here until
* it is completed, regardless of the scsi pkt directives.
*
* Note: Mode Select Caching page RCD and DRA bits are tied together, i.e.
* changing DRA will change RCD.
*
* More than one SATA command may be executed to perform operations specified
* by mode select pages. The first error terminates further execution.
* Operations performed successully are not backed-up in such case.
*
* Return SATA_SUCCESS if operation succeeded, SATA_FAILURE otherwise.
* If operation resulted in changing device setup, dmod flag should be set to
* one (1). If parameters were not changed, dmod flag should be set to 0.
* Upon return, if operation required sending command to the device, the rval
* should be set to the value returned by sata_hba_start. If operation
* did not require device access, rval should be set to TRAN_ACCEPT.
* The pagelen should be set to the length of the page.
*
* This function has to be called with a port mutex held.
*
* Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise.
*/
int
{
struct scsi_extended_sense *sense;
*dmod = 0;
/* Verify parameters length. If too short, drop it */
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
/*
* We can manipulate only write cache and read ahead
* (read cache) setting.
*/
/*
* None of the features is supported - ignore
*/
*rval = TRAN_ACCEPT;
return (SATA_SUCCESS);
}
/* Current setting of Read Ahead (and Read Cache) */
dra = 0; /* 0 == not disabled */
else
dra = 1;
/* Current setting of Write Cache */
wce = 1;
else
wce = 0;
/* nothing to do */
*rval = TRAN_ACCEPT;
return (SATA_SUCCESS);
}
/*
* Need to flip some setting
* Set-up Internal SET FEATURES command(s)
*/
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/* Need to flip read ahead setting */
if (dra == 0)
/* Disable read ahead / read cache */
else
/* Enable read ahead / read cache */
/* Transfer command to HBA */
/*
* Pkt not accepted for execution.
*/
return (SATA_FAILURE);
*dmod = 1;
/* Now process return */
goto failure; /* Terminate */
}
}
/* Note that the packet is not removed, so it could be re-used */
/* Need to flip Write Cache setting */
/* Enable write cache */
else
/* Disable write cache */
/* Transfer command to HBA */
/*
* Pkt not accepted for execution.
*/
return (SATA_FAILURE);
*dmod = 1;
/* Now process return */
goto failure;
}
}
return (SATA_SUCCESS);
return (SATA_FAILURE);
}
/*
* Process mode select informational exceptions control page 0x1c
*
* The only changeable bit is dexcpt (disable exceptions).
* MRIE (method of reporting informational exceptions) must be
* "only on request".
*
* Return SATA_SUCCESS if operation succeeded, SATA_FAILURE otherwise.
* If operation resulted in changing device setup, dmod flag should be set to
* one (1). If parameters were not changed, dmod flag should be set to 0.
* Upon return, if operation required sending command to the device, the rval
* should be set to the value returned by sata_hba_start. If operation
* did not require device access, rval should be set to TRAN_ACCEPT.
* The pagelen should be set to the length of the page.
*
* This function has to be called with a port mutex held.
*
* Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise.
*/
static int
struct mode_info_excpt_page *page,
int parmlen,
int *pagelen,
int *rval,
int *dmod)
{
struct scsi_extended_sense *sense;
*dmod = 0;
/* Verify parameters length. If too short, drop it */
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
/* If already in the state requested, we are done */
/* nothing to do */
*rval = TRAN_ACCEPT;
return (SATA_SUCCESS);
}
/* Build SMART_ENABLE or SMART_DISABLE command */
/* Transfer command to HBA */
/*
* Pkt not accepted for execution.
*/
return (SATA_FAILURE);
/* Now process return */
return (SATA_SUCCESS);
/* Packet did not complete successfully */
return (SATA_FAILURE);
}
/*
* sata_build_lsense_page0() is used to create the
* SCSI LOG SENSE page 0 (supported log pages)
*
* Currently supported pages are 0, 0x10, 0x2f and 0x30
* (supported log pages, self-test results, informational exceptions
* and Sun vendor specific ATA SMART data).
*
* Takes a sata_drive_info t * and the address of a buffer
* in which to create the page information.
*
* Returns the number of bytes valid in the buffer.
*/
static int
{
lpp->param_code[0] = 0;
}
}
}
/*
* sata_build_lsense_page_10() is used to create the
* SCSI LOG SENSE page 0x10 (self-test results)
*
* Takes a sata_drive_info t * and the address of a buffer
* in which to create the page information as well as a sata_hba_inst_t *.
*
* Returns the number of bytes valid in the buffer.
*/
static int
{
int rval;
struct smart_ext_selftest_log *ext_selftest_log;
sizeof (struct smart_ext_selftest_log), KM_SLEEP);
ext_selftest_log, 0);
if (rval == 0) {
int index;
struct smart_ext_selftest_log_entry *entry;
int count;
index |= ext_selftest_log->
if (index == 0)
goto out;
--index; /* Correct for 0 origin */
if (block_num != 0) {
if (rval != 0)
goto out;
}
entry =
for (count = 1;
++count) {
lpp->param_code[0] = 0;
lpp->param_ctrl_flags = 0;
status >>= 4;
switch (status) {
case 0:
default:
add_sense_code_qual = 0;
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
}
code = 0; /* unspecified */
lpp = (struct log_parameter *)
--index; /* Back up to previous entry */
if (index < 0) {
if (block_num > 0) {
--block_num;
} else {
struct read_log_ext_directory
rval =
&logdir);
if (rval == -1)
goto out;
if ((logdir.read_log_ext_vers
[0] == 0) &&
[1] == 0))
goto out;
- 1];
- 1] << 8;
--block_num;
}
if (rval != 0)
goto out;
index =
1;
}
entry = &ext_selftest_log->
}
}
out:
sizeof (struct smart_ext_selftest_log));
} else {
struct smart_selftest_log *selftest_log;
KM_SLEEP);
if (rval == 0) {
int index;
int count;
struct smart_selftest_log_entry *entry;
if (index == 0)
goto done;
--index; /* Correct for 0 origin */
entry = &selftest_log->
for (count = 1;
++count) {
lpp->param_code[0] = 0;
lpp->param_ctrl_flags = 0;
status >>= 4;
switch (status) {
case 0:
default:
break;
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 7:
break;
case 8:
break;
}
code = 0; /* unspecified */
lpp = (struct log_parameter *)
--index; /* back up to previous entry */
if (index < 0) {
index =
}
entry = &selftest_log->
}
}
done:
}
return ((SCSI_LOG_PARAM_HDR_LEN + SCSI_LOG_SENSE_SELFTEST_PARAM_LEN) *
}
/*
* sata_build_lsense_page_2f() is used to create the
* SCSI LOG SENSE page 0x10 (informational exceptions)
*
* Takes a sata_drive_info t * and the address of a buffer
* in which to create the page information as well as a sata_hba_inst_t *.
*
* Returns the number of bytes valid in the buffer.
*/
static int
{
int rval;
#define SMART_NO_TEMP 0xff
lpp->param_code[0] = 0;
/* Now get the SMART status w.r.t. threshold exceeded */
switch (rval) {
case 1:
break;
case 0:
case -1: /* failed to get data */
lpp->param_values[0] = 0;
break;
#if defined(SATA_DEBUG)
default:
/* NOTREACHED */
#endif
}
else {
/* Now get the temperature */
SCT_STATUS_LOG_PAGE, 1);
if (rval == -1)
else {
if (temp & 0x80) {
if (temp & 0x7f)
temp = 0;
else
}
}
}
return (SCSI_INFO_EXCEPTIONS_PARAM_LEN + SCSI_LOG_PARAM_HDR_LEN);
}
/*
* sata_build_lsense_page_30() is used to create the
* SCSI LOG SENSE page 0x30 (Sun's vendor specific page for ATA SMART data).
*
* Takes a sata_drive_info t * and the address of a buffer
* in which to create the page information as well as a sata_hba_inst_t *.
*
* Returns the number of bytes valid in the buffer.
*/
static int
{
int rval;
/* Now do the SMART READ DATA */
if (rval == -1)
return (0);
return (sizeof (struct smart_data));
}
/* ************************** LOCAL FUNCTIONS ************************** */
/*
* Validate sata_tran info
* SATA_FAILURE returns if structure is inconsistent or structure revision
* does not match one used by the framework.
*
* Returns SATA_SUCCESS if sata_hba_tran has matching revision and contains
* required function pointers.
* Returns SATA_FAILURE otherwise.
*/
static int
{
"sata: invalid sata_hba_tran version %d for driver %s",
return (SATA_FAILURE);
}
"sata: inconsistent sata_tran_hba_dip "
return (SATA_FAILURE);
}
"required functions"));
}
return (SATA_SUCCESS);
}
/*
* Remove HBA instance from sata_hba_list.
*/
static void
{
for (sata_hba_inst = sata_hba_list;
break;
}
#ifdef SATA_DEBUG
"unknown HBA instance\n");
#endif
}
if (sata_hba_inst == sata_hba_list) {
if (sata_hba_list) {
(struct sata_hba_inst *)NULL;
}
if (sata_hba_inst == sata_hba_list_tail) {
}
} else if (sata_hba_inst == sata_hba_list_tail) {
if (sata_hba_list_tail) {
(struct sata_hba_inst *)NULL;
}
} else {
}
}
/*
* Probe all SATA ports of the specified HBA instance.
* This function is called only from sata_hba_attach(). It does not have to
* be protected by controller mutex, because the hba_attached flag is not set
* yet and no one would be touching this HBA instance other then this thread.
* Determines if port is active and what type of the device is attached
* (if any). Allocates necessary structures for each port.
* Creates attachment point minor node for each non-failed port.
*/
static void
{
int rval;
char name[16];
/*
* Probe controller ports first, to find port status and
* any port multiplier attached.
*/
/* allocate cport structure */
/*
* Regardless if a port is usable or not, create
* an attachment point
*/
DDI_SUCCESS) {
"cannot create sata attachment point for port %d",
ncport);
}
/* Probe port */
(dip, &sata_device);
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
continue;
}
continue;
}
/*
* There is some device attached.
* Allocate device info structure
*/
KM_SLEEP);
} else {
KM_SLEEP);
NULL);
/* Probe Port Multiplier ports */
for (npmport = 0;
npmport++) {
sizeof (sata_pmport_info_t), KM_SLEEP);
MUTEX_DRIVER, NULL);
/* Create an attachment point */
0) != DDI_SUCCESS) {
"sata_hba_attach: "
"cannot create sata attachment "
"point for port %d pmult port %d",
}
(dip, &sata_device);
/* sata_update_port_info() */
&sata_device);
if (rval != SATA_SUCCESS) {
continue;
}
if (pmportinfo->pmport_dev_type ==
continue;
/* Port multipliers cannot be chained */
/*
* There is something attached to Port
* Multiplier device port
* Allocate device info structure
*/
drive = kmem_zalloc(
sizeof (sata_drive_info_t), KM_SLEEP);
(dip, &sata_device);
/* sata_update_port_info() */
&sata_device);
}
}
}
}
/*
* Create SATA device nodes for specified HBA instance (SCSI target
* device nodes).
* This function is called only from sata_hba_attach(). The hba_attached flag
* is not set yet, so no ports or device data structures would be touched
* by anyone other then this thread, therefore per-port mutex protection is
* not needed.
* The assumption is that there are no target and attachment point minor nodes
* created by the boot subsystems, so we do not need to prune device tree.
* An AP (Attachement Point) node is created for each SATA device port even
* when there is no device attached.
* A target node is created when there is a supported type of device attached,
* but may be removed if it cannot be put online.
*
* This function cannot be called from an interrupt context.
*
* ONLY DISK TARGET NODES ARE CREATED NOW
*/
static void
{
int rval;
/*
* Walk through pre-probed sata ports info in sata_scsi
*/
continue;
}
continue;
}
/* No device attached to the controller port */
continue;
}
/*
* Some device is attached to a controller port.
* We rely on controllers distinquishing between no-device,
* attached port multiplier and other kind of attached device.
* We need to get Identify Device data and determine
* positively the dev type before trying to attach
* the target driver.
*/
/*
* Not port multiplier.
*/
if (rval != SATA_SUCCESS ||
continue;
if ((sata_device.satadev_type &
SATA_VALID_DEV_TYPE) == 0) {
/*
* Could not determine device type or
* a device is not supported.
* Degrade this device to unknown.
*/
continue;
}
/*
* Attaching target node failed.
* We retain sata_drive_info structure...
*/
continue;
}
} else {
/* This must be Port Multiplier type */
"sata_make_device_nodes: "
"unknown dev type %x",
continue;
}
for (npmport = 0;
npmport++) {
if (pmportinfo->pmport_state &
continue;
}
if (pmportinfo->pmport_dev_type &
/* No device attached */
continue;
&sata_device);
if (rval != SATA_SUCCESS ||
continue;
}
if ((sata_device.satadev_type &
SATA_VALID_DEV_TYPE) == 0) {
/*
* Could not determine device type.
* Degrade this device to unknown.
*/
continue;
}
/*
* Attaching target node failed.
* We retain sata_drive_info
* structure...
*/
continue;
}
}
}
}
}
/*
* Create scsi target node for attached device, create node properties and
* attach the node.
* The node could be removed if the device onlining fails.
*
* A dev_info_t pointer is returned if operation is successful, NULL is
* returned otherwise.
*/
static dev_info_t *
{
int rval;
char **compatible = NULL;
int ncompatible;
struct scsi_inquiry inq;
int target;
int i;
"sata_create_target_node: no sdinfo for target %x",
target));
return (NULL);
}
/*
* create scsi inquiry data, expected by
* scsi_hba_nodename_compatible_get()
*/
/* determine the node name and compatible */
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_NODES) {
"cannot determine nodename for target %d\n",
target);
} else {
}
if (compatible == NULL) {
"sata_create_target_node: no compatible name\n");
} else {
for (i = 0; i < ncompatible; i++) {
"compatible name: %s\n", compatible[i]);
}
}
}
#endif
/* if nodename can't be determined, log error and exit */
"sata_create_target_node: cannot determine nodename "
"for target %d\n", target));
return (NULL);
}
/*
* Create scsi target node
*/
"device-type", "scsi");
if (rval != DDI_PROP_SUCCESS) {
"updating device_type prop failed %d", rval));
goto fail;
}
/*
* Create target node properties: target & lun
*/
if (rval != DDI_PROP_SUCCESS) {
"updating target prop failed %d", rval));
goto fail;
}
if (rval != DDI_PROP_SUCCESS) {
"updating target prop failed %d", rval));
goto fail;
}
/* decorate the node with compatible */
"sata_create_target_node: FAIL compatible props cdip 0x%p",
(void *)cdip));
goto fail;
}
/*
* Now, try to attach the driver. If probing of the device fails,
* the target node may be removed
*/
if (rval == NDI_SUCCESS)
return (cdip);
/* target node was removed - are we sure? */
return (NULL);
fail:
if (rval != NDI_SUCCESS) {
"node removal failed %d", rval));
}
"cannot create target node for device at port %d",
return (NULL);
}
/*
* Re-probe sata port, check for a device and attach necessary info
* structures when necessary. Identify Device data is fetched, if possible.
* Assumption: sata address is already validated.
* SATA_SUCCESS is returned if port is re-probed sucessfully, regardless of
* the presence of a device and its type.
* SATA_FAILURE is returned if one of the operations failed.
*/
static int
{
int rval;
/* We only care about host sata cport for now */
/* probe port */
if (rval != SATA_SUCCESS) {
"connect: port probbing failed"));
return (SATA_FAILURE);
}
/*
* update sata port state and set device type
*/
/*
* Sanity check - Port is active? Is the link active?
* Is there any device attached?
*/
if ((cportinfo->cport_state &
/*
* Free info structure if necessary (direct attached drive
* only, for now!
*/
/* Add here differentiation for device attached or not */
return (SATA_SUCCESS);
}
/*
* If we are re-probing the port, there may be
* sata_drive_info structure attached
* (or sata_pm_info, if PMult is supported).
*/
/*
* There is no device, so remove device info structure,
* if necessary. Direct attached drive only!
*/
"SATA device detached "
}
return (SATA_SUCCESS);
}
/*
* There is some device attached, but there is
* no sata_drive_info structure - allocate one
*/
sizeof (sata_drive_info_t), KM_SLEEP);
/*
* Recheck, if port state did not change when we
* released mutex.
*/
} else {
/*
* Port is not in ready state, we
* cannot attach a device.
*/
return (SATA_SUCCESS);
}
}
} else {
return (SATA_SUCCESS);
}
/*
* Figure out what kind of device we are really
* dealing with.
*/
}
/*
* Validate sata address.
* Specified cport, pmport and qualifier has to match
* passed sata_scsi configuration info.
* The presence of an attached device is not verified.
*
* Returns 0 when address is valid, -1 otherwise.
*/
static int
{
goto invalid_address;
goto invalid_address;
goto invalid_address;
return (0);
return (-1);
}
/*
* Validate scsi address
* Returns 0 if a scsi target refers to an attached device,
* returns 1 if address is valid but device is not attached,
* returns -1 if bad address or device is of an unsupported type.
* Upon return sata_device argument is set.
*/
static int
{
goto out;
goto out;
0) {
if (qual == SATA_ADDR_DCPORT) {
goto out;
SATA_VALID_DEV_TYPE) == 0) {
rval = -1;
goto out;
}
} else if (qual == SATA_ADDR_DPMPORT) {
rval = -1;
goto out;
}
NULL ||
pmport) == SATA_DTYPE_NONE)
goto out;
pmport);
} else {
rval = -1;
goto out;
}
goto out;
return (0);
}
out:
if (rval == 1) {
"sata_validate_scsi_address: no valid target %x lun %x",
}
return (rval);
}
/*
* Find dip corresponding to passed device number
*
* Returns NULL if invalid device number is passed or device cannot be found,
* Returns dip is device is found.
*/
static dev_info_t *
{
#ifndef __lock_lint
return (NULL);
}
#endif
return (dip);
}
/*
* Probe device.
* This function issues Identify Device command and initialize local
* sata_drive_info structure if the device can be identified.
* The device type is determined by examining Identify Device
* command response.
* If the sata_hba_inst has linked drive info structure for this
* device address, the Identify Device data is stored into sata_drive_info
* structure linked to the port info structure.
*
* sata_device has to refer to the valid sata port(s) for HBA described
* by sata_hba_inst structure.
*
* Returns: SATA_SUCCESS if device type was successfully probed and port-linked
* drive info structure was updated;
* SATA_FAILURE if there is no device, or device was not probed
* successully.
* If a device cannot be identified, sata_device's dev_state and dev_type
* fields are set to unknown.
*
*/
static int
{
int retry_cnt;
(SATA_STATE_PROBED | SATA_STATE_READY)) != 0);
/* Get pointer to port-linked sata device info structure */
sdinfo->satadrv_state &=
} else {
/* No device to probe */
return (SATA_FAILURE);
}
/*
* Need to issue both types of identify device command and
* First, ATA Identify Device.
*/
retry_cnt++) {
/* Got something responding to ATA Identify Device */
SATA_SUCCESS) {
/* Try one more time */
&new_sdinfo) != SATA_SUCCESS)
goto failure;
}
break;
}
/*
* HBA supports ATAPI - try to issue Identify Packet
* Device command.
*/
&new_sdinfo) == 0) {
/*
* Got something responding to Identify Packet
* Device cmd.
*/
/* Set UDMA mode here as well ? - phase 2 */
break;
}
}
}
if (retry_cnt <= SATA_DEVICE_IDENTIFY_RETRY) {
/* save device info, if possible */
return (SATA_FAILURE);
}
/*
* Copy drive info into the port-linked drive info structure.
*/
*sdinfo = new_sdinfo;
else /* SATA_ADDR_DPMPORT */
return (SATA_SUCCESS);
}
/*
* Looks like we cannot determine the device type.
*/
else {
/* SATA_ADDR_DPMPORT */
if ((SATA_PMULT_INFO(sata_hba_inst,
}
}
return (SATA_FAILURE);
}
/*
* Get pointer to sata_drive_info structure.
*
* The sata_device has to contain address (cport, pmport and qualifier) for
* specified sata_scsi structure.
*
* Returns NULL if device address is not valid for this HBA configuration.
* Otherwise, returns a pointer to sata_drive_info structure.
*
* This function should be called with a port mutex held.
*/
static sata_drive_info_t *
{
return (NULL);
/* Port not probed yet */
return (NULL);
return (NULL);
if (qual == SATA_ADDR_DCPORT) {
/* Request for a device on a controller port */
/* Port multiplier attached */
return (NULL);
}
if (qual == SATA_ADDR_DPMPORT) {
return (NULL);
return (NULL);
}
/* we should not get here */
return (NULL);
}
/*
* sata_identify_device.
* Send Identify Device command to SATA HBA driver.
* If command executes successfully, update sata_drive_info structure pointed
* to by sdinfo argument, including Identify Device data.
* If command fails, invalidate data in sata_drive_info.
*
* Cannot be called from interrupt level.
*
* Returns 0 if device was identified as supported device, -1 otherwise.
*/
static int
{
int i;
/* fetch device identify data */
goto fail_unknown;
/* Change device type to reflect Identify Device data */
if (((cfg_word & SATA_ATAPI_TYPE_MASK) ==
SATA_ATAPI_TYPE) &&
((cfg_word & SATA_ATAPI_ID_DEV_TYPE) ==
} else {
}
/* Change device type to reflect Identify Device data ! */
} else {
}
}
if (sdinfo->satadrv_capacity == 0) {
/* Non-LBA disk. Too bad... */
"SATA disk device at port %d does not support LBA",
goto fail_unknown;
}
}
/* Check for Ultra DMA modes 6 through 0 being supported */
for (i = 6; i >= 0; --i) {
break;
}
/*
* At least UDMA 4 mode has to be supported. If mode 4 or
* higher are not supported by the device, fail this
* device.
*/
if (i < 4) {
/* No required Ultra DMA mode supported */
"SATA disk device at port %d does not support UDMA "
"mode 4 or higher required, %d supported", i));
goto fail_unknown;
}
return (0);
/* Invalidate sata_drive_info ? */
return (-1);
}
/*
*/
static void
{
int valid_version;
char msg_buf[MAXPATHLEN];
/* Show HBA path */
"Unsupported SATA device type (cfg 0x%x) at ",
} else {
}
else
#ifdef SATA_DEBUG
int i;
for (i = 14; i >= 2; i--) {
valid_version = i;
break;
}
}
}
#endif
/* Log some info */
msg_buf[0] = '\0';
#ifdef __i386
#else
#endif
}
/*
* sata_save_drive_settings extracts current setting of the device and stores
* it for future reference, in case the device setup would need to be restored
* after the device reset.
*
* At the moment only read ahead and write cache settings are saved, if the
* device supports these features at all.
*/
static void
{
/* None of the features is supported - do nothing */
return;
}
/* Current setting of Read Ahead (and Read Cache) */
else
/* Current setting of Write Cache */
else
}
/*
* sata_check_capacity function determines a disk capacity
*
* NOTE: CHS mode is not supported! If a device does not support LBA,
* this function is not called.
*
* Returns device capacity in number of blocks, i.e. largest addressable LBA+1
*/
static uint64_t
{
int i;
/* Capacity valid only for LBA-addressable disk devices */
return (0);
/* LBA48 mode supported and enabled */
for (i = 3; i >= 0; --i) {
capacity <<= 16;
}
} else {
capacity <<= 16;
if (capacity >= 0x1000000)
/* LBA28 mode */
}
return (capacity);
}
/*
* Allocate consistent buffer for DMA transfer
*
* Cannot be called from interrupt level or with mutex held - it may sleep.
*
* Returns pointer to allocated buffer structure, or NULL if allocation failed.
*/
static struct buf *
{
struct scsi_address ap;
/* Allocate DMA resources for this buffer */
/*
* We use a local version of the dma_attr, to account
* for a device addressing limitations.
* sata_adjust_dma_attr() will handle sdinfo == NULL which
* will cause dma attributes to be adjusted to a lowest
* acceptable level.
*/
}
}
return (bp);
}
/*
* Release local buffer (consistent buffer for DMA transfer) allocated
* via sata_alloc_local_buffer().
*/
static void
{
/* Free DMA resources */
spx->txlt_buf_dma_handle = 0;
spx->txlt_dma_cookie_list_len = 0;
/* Free buffer */
}
/*
* Allocate sata_pkt
* Pkt structure version and embedded strcutures version are initialized.
* sata_pkt and sata_pkt_txlate structures are cross-linked.
*
* Since this may be called in interrupt context by sata_scsi_init_pkt,
* callback argument determines if it can sleep or not.
* Hence, it should not be called from interrupt context.
*
* If successful, non-NULL pointer to a sata pkt is returned.
* Upon failure, NULL pointer is returned.
*/
static sata_pkt_t *
{
int kmsflag;
"sata_pkt_alloc: failed"));
return (NULL);
}
return (spkt);
}
/*
* Free sata pkt allocated via sata_pkt_alloc()
*/
static void
{
}
/*
* Adjust DMA attributes.
* SCSI cmds block count is up to 24 bits, SATA cmd block count vary
* from 8 bits to 16 bits, depending on a command being used.
* commands may affects performance, so check both the device and
* controller capability before adjusting dma attributes.
* because these devices support block size of 2k rather
* then 512 bytes.
*/
void
{
/* Copy original attributes */
*adj_dma_attr = *dma_attr;
/*
* Things to consider: device addressing capability,
* "excessive" controller DMA capabilities.
* If a device is being probed/initialized, there are
* no device info - use default limits then.
*/
return;
}
/* arbitrarily modify controller dma granularity */
}
/*
* 16-bit sector count may be used - we rely on
* the assumption that only read and write cmds
* will request more than 256 sectors worth of data
*/
} else {
/*
* 8-bit sector count will be used - default limits
* for dma attributes
*/
}
/*
* Adjust controler dma attributes, if necessary
*/
}
/*
* Allocate DMA resources for the buffer
* This function handles initial DMA resource allocation as well as
* DMA window shift and may be called repeatedly for the same DMA window
* until all DMA cookies in the DMA window are processed.
*
* Returns DDI_SUCCESS upon successful operation,
* returns failure code returned by failing commands or DDI_FAILURE when
* internal cleanup failed.
*/
static int
{
int rval;
int max_sg_len, req_sg_len, i;
/*
* No DMA resources allocated so far - this is a first call
* for this sata pkt.
*/
if (rval != DDI_SUCCESS) {
"sata_dma_buf_setup: no buf DMA resources %x",
rval));
return (rval);
}
else
if (flags & PKT_CONSISTENT)
if (flags & PKT_DMA_PARTIAL)
switch (rval) {
case DDI_DMA_PARTIAL_MAP:
"sata_dma_buf_setup: DMA Partial Map\n", NULL);
/*
* Partial DMA mapping.
* Retrieve number of DMA windows for this request.
*/
(void) ddi_dma_unbind_handle(
(void) ddi_dma_free_handle(
"sata_dma_buf_setup: numwin failed\n"));
return (DDI_FAILURE);
}
spx->txlt_cur_dma_win = 0;
break;
case DDI_DMA_MAPPED:
/* DMA fully mapped */
spx->txlt_cur_dma_win = 0;
break;
default:
/* DMA mapping failed */
"sata_dma_buf_setup: buf dma handle binding "
"failed %x\n", rval));
return (rval);
}
} else {
/*
* DMA setup is reused. Check if we need to process more
* cookies in current window, or to get next window, if any.
*/
if (spx->txlt_curwin_processed_dma_cookies ==
/*
* All cookies from current DMA window were processed.
* Get next DMA window.
*/
spx->txlt_cur_dma_win++;
&cookie,
} else {
/* No more windows! End of request! */
/* What to do? - panic for now */
spx->txlt_sata_pkt->
return (DDI_SUCCESS);
}
}
}
/* There better be at least one DMA cookie */
spx->txlt_curwin_processed_dma_cookies) > 0);
if (spx->txlt_curwin_processed_dma_cookies == 0) {
/*
* Processing a new DMA window - set-up dma cookies list.
* We may reuse previously allocated cookie array if it is
* possible.
*/
/*
* New DMA window contains more cookies than
* the previous one. We need larger cookie list - free
* the old one.
*/
sizeof (ddi_dma_cookie_t));
spx->txlt_dma_cookie_list_len = 0;
}
/* Allocate new dma cookie array */
sizeof (ddi_dma_cookie_t) *
}
/*
* Copy all DMA cookies into local list, so we will know their
* dma_size in advance of setting the sata_pkt.
* One cookie was already fetched, so copy it.
*/
}
} else {
"sata_dma_buf_setup: sliding within DMA window, "
"cur cookie %d, total cookies %d\n",
}
/*
* Set-up sata_pkt cookie list.
* No single cookie transfer size would exceed max transfer size of
* an ATA command used for addressed device (tha adjustment of the dma
* attributes took care of this). But there may be more
* then one cookie, so the cmd cookie list has to be
* constrained by both a maximum scatter gather list length and
* a maximum transfer size restriction of an ATA command.
*/
ASSERT(req_sg_len > 0);
/* One cookie should be always available */
/* Add more cookies to the scatter-gather list */
for (i = 1; i < req_sg_len; i++) {
if (cur_txfer_len < max_txfer_len) {
/*
* Check if the next cookie could be used by
* this sata_pkt.
*/
if ((cur_txfer_len +
dmac_size) <= max_txfer_len) {
/* Yes, transfer lenght is within bounds */
spx->txlt_sata_pkt->
} else {
/* No, transfer would exceed max lenght. */
"ncookies %d, size 0x%lx, "
"max_size 0x%lx\n",
spx->txlt_sata_pkt->
break;
}
} else {
/* Cmd max transfer length reached */
"Max transfer length? "
"ncookies %d, size 0x%lx, max_size 0x%lx\n",
spx->txlt_sata_pkt->
break;
}
}
ASSERT(cur_txfer_len != 0);
return (DDI_SUCCESS);
}
/*
* Fetch Device Identify data.
* Send DEVICE IDENTIFY command to a device and get the device identify data.
* The device_info structure has to be set to device type (for selecting proper
* device identify command).
*
* Returns 0 if success, -1 otherwise.
*
* Cannot be called in an interrupt context.
*/
static int
{
int rval;
return (-1);
}
/* address is needed now */
/*
* Allocate buffer for Identify Data return data
*/
"sata_fetch_device_identify_data: "
"cannot allocate buffer for ID"));
return (-1);
}
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/* Build Identify Device cmd in the sata_pkt */
/* Identify Packet Device cmd */
} else {
/* Identify Device cmd - mandatory for all other devices */
}
/* Send pkt to SATA HBA driver */
/*
* Woops, no Identify Data.
* Invalidate sata_drive_info ?
*/
rval = -1;
} else {
/* Update sata_drive_info */
if (rval != DDI_SUCCESS) {
"sata_fetch_device_identify_data: "
"sync pkt failed"));
rval = -1;
goto fail;
}
sizeof (sata_id_t));
/*
* Retrieve capacity (disks only) and addressing mode
*/
} else {
/*
* For ATAPI devices one has to issue Get Capacity cmd
* (not needed at the moment)
*/
sdinfo->satadrv_capacity = 0;
}
/* Setup supported features flags */
/* Check for NCQ support */
/* SATA compliance */
(SATA_1_SPEED | SATA_2_SPEED)) {
} else {
}
}
if (sdinfo->satadrv_queue_depth == 0)
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* SATA spec requires that the device supports at least UDMA 4 mode and
* UDMA mode is selected.
* Some devices (bridged devices) may not come-up with default UDMA mode
* set correctly, so this function is setting it.
*
* Returns SATA_SUCCESS if proper UDMA mode is selected.
* Returns SATA_FAILURE if proper UDMA mode could not be selected.
*/
static int
{
int result = SATA_SUCCESS;
int i, mode;
/* Find highest Ultra DMA mode supported */
break;
}
if (mode < 4)
return (SATA_FAILURE);
/* Find UDMA mode currently selected */
for (i = 6; i >= 0; --i) {
break;
}
if (i < mode) {
/* Set UDMA mode via SET FEATURES COMMAND */
/* Prepare packet for SET FEATURES COMMAND */
goto failure;
}
/* Fill sata_pkt */
/* Timeout 30s */
/* Synchronous mode, no callback, interrupts */
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/* Transfer command to HBA */
spkt) != SATA_TRAN_ACCEPTED ||
/* Pkt execution failed */
}
if (result == SATA_FAILURE)
"sata_set_udma_mode: could not set UDMA "
"mode %", mode));
/* Free allocated resources */
}
return (result);
}
/*
* Update port SCR block
*/
static void
{
}
/*
* Update state and copy port ss* values from passed sata_device structure.
* sata_address is validated - if not valid, nothing is changed in sata_scsi
* configuration struct.
*
* SATA_PSTATE_SHUTDOWN in port state is not reset to 0 by this function
* regardless of the state in device argument.
*
* Port mutex should be held while calling this function.
*/
static void
{
if (SATA_NUM_CPORTS(sata_hba_inst) <=
return;
/* Preserve SATA_PSTATE_SHUTDOWN flag */
cportinfo->cport_state |=
} else {
return;
/* Preserve SATA_PSTATE_SHUTDOWN flag */
}
}
/*
* Extract SATA port specification from an IOCTL argument.
*
* This function return the port the user land send us as is, unless it
* cannot retrieve port spec, then -1 is returned.
*
* Note: Only cport - no port multiplier port.
*/
static int32_t
{
/* Extract port number from nvpair in dca structure */
"sata_get_port_num: invalid port spec 0x%x in ioctl",
port));
port = -1;
}
return (port);
}
/*
* Get dev_info_t pointer to the device node pointed to by port argument.
* NOTE: target argument is a value used in ioctls to identify
* the AP - it is not a sata_address.
* It is a combination of cport, pmport and address qualifier, encodded same
* way as a scsi target number.
* At this moment it carries only cport number.
*
* No PMult hotplug support.
*
* Returns dev_info_t pointer if target device was found, NULL otherwise.
*/
static dev_info_t *
{
int ncport;
int circ;
if (tgt == -1) {
/*
* This is actually an error condition, but not
* a fatal one. Just continue the search.
*/
continue;
}
break;
}
return (cdip);
}
/*
* sata_cfgadm_state:
* Use the sata port state and state of the target node to figure out
* the cfgadm_state.
*
* The port argument is a value with encoded cport,
* pmport and address qualifier, in the same manner as a scsi target number.
* SCSI_TO_SATA_CPORT macro extracts cport number,
* SCSI_TO_SATA_PMPORT extracts pmport number and
* SCSI_TO_SATA_ADDR_QUAL extracts port mulitplier qualifier flag.
*
* For now, support is for cports only - no pmultiplier ports.
*/
static void
{
int port_state;
/* Cport only */
if (port_state & SATA_PSTATE_SHUTDOWN ||
if (port_state & SATA_PSTATE_FAILED)
else
return;
}
/* Need to check pmult device port here as well, when supported */
/* Port is enabled and ready */
case SATA_DTYPE_NONE:
{
/* No device attached */
break;
}
case SATA_DTYPE_UNKNOWN:
case SATA_DTYPE_ATAPINONCD:
case SATA_DTYPE_PMULT: /* Until PMult is supported */
{
/* Unknown device attached */
break;
}
case SATA_DTYPE_ATADISK:
case SATA_DTYPE_ATAPICD:
{
int circ;
if ((DEVI_IS_DEVICE_OFFLINE(tdip)) ||
(DEVI_IS_DEVICE_DOWN(tdip))) {
} else {
}
} else {
}
break;
}
default:
/*
* This is actually internal error condition (non fatal),
* beacuse we already checked all defined device types.
*/
"sata_cfgadm_state: Internal error: "
"unknown device type"));
break;
}
}
/*
* Start or terminate the thread, depending on flag arg and current state
*/
static void
{
static int sata_event_thread_terminating = 0;
static int sata_event_thread_starting = 0;
int i;
sata_event_thread_terminating == 1)) {
return;
}
return;
}
/* wait til terminate operation completes */
while (sata_event_thread_terminating == 1) {
if (i-- <= 0) {
#ifdef SATA_DEBUG
"timeout waiting for thread to terminate");
#endif
return;
}
}
}
if (startstop == 1) {
if (sata_event_thread == NULL) {
(void (*)())sata_event_daemon,
}
return;
}
/*
* If we got here, thread may need to be terminated
*/
if (sata_event_thread != NULL) {
int i;
/* Signal event thread to go away */
/*
* Wait til daemon terminates.
*/
while (sata_event_thread_terminate == 1) {
if (i-- <= 0) {
/* Daemon did not go away !!! */
#ifdef SATA_DEBUG
"cannot terminate event daemon thread");
#endif
break;
}
}
}
ASSERT(sata_event_thread_starting == 0);
}
/*
* Log sata message
* dev pathname msg line preceeds the logged message.
*/
static void
{
char pathname[128];
if (sata_hba_inst != NULL) {
} else {
pathname[0] = 0;
}
if (sata_debug_flags == 0)
else
} else
}
/* ******** Asynchronous HBA events handling & hotplugging support ******** */
/*
* SATA HBA event notification function.
* Events reported by SATA HBA drivers per HBA instance relate to a change in
* A warning message is generated for each event type.
* Events are not processed by this function, so only the
* event flag(s)is set for an affected entity and the event thread is
* waken up. Event daemon thread processes all events.
*
* NOTE: Since more than one event may be reported at the same time, one
* cannot determine a sequence of events when opposite event are reported, eg.
* LINK_LOST and LINK_ESTABLISHED. Actual port status during event processing
* is taking precedence over reported events, i.e. may cause ignoring some
* events.
*/
#define SATA_EVENT_MAX_MSG_LENGTH 79
void
{
char *lcp;
static char *err_msg_evnt_1 =
"sata_hba_event_notify: invalid port event 0x%x ";
static char *err_msg_evnt_2 =
"sata_hba_event_notify: invalid device event 0x%x ";
int linkevent;
/*
* There is a possibility that an event will be generated on HBA
* that has not completed attachment or is detaching.
* HBA driver should prevent this, but just in case it does not,
* we need to ignore events for such HBA.
*/
break;
}
if (sata_hba_inst == NULL)
/* HBA not attached */
return;
/*
* Validate address before - do not proceed with invalid address.
*/
return;
/* Port Multiplier not supported yet */
return;
/*
* Events refer to devices, ports and controllers - each has
* unique address. Events for different addresses cannot be combined.
*/
/* qualify this event(s) */
if ((event & SATA_EVNT_PORT_EVENTS) == 0) {
/* Invalid event for the device port */
goto event_info;
}
/* Controller's device port event */
pstats =
} else {
/* Port multiplier's device port event */
pstats =
}
/*
* Add to statistics and log the message. We have to do it
* here rather than in the event daemon, because there may be
* multiple events occuring before they are processed.
*/
if (linkevent) {
if (linkevent == (SATA_EVNT_LINK_LOST |
/* This is likely event combination */
if (pstats->link_established <
0xffffffffffffffff)
linkevent = 0;
} else if (linkevent & SATA_EVNT_LINK_LOST) {
} else {
if (pstats->link_established <
0xffffffffffffffff)
}
}
if (event & SATA_EVNT_DEVICE_ATTACHED) {
}
if (event & SATA_EVNT_DEVICE_DETACHED) {
}
if (event & SATA_EVNT_PWR_LEVEL_CHANGED) {
"port %d power level changed", cport);
}
if ((event & ~SATA_EVNT_PORT_EVENTS) != 0) {
/* There should be no other events for this address */
}
/* qualify this event */
if ((event & SATA_EVNT_DEVICE_RESET) == 0) {
/* Invalid event for a device */
goto event_info;
}
/* drive event */
if (event & SATA_EVNT_DEVICE_RESET) {
0xffffffffffffffff)
}
}
if ((event & ~SATA_EVNT_DEVICE_RESET) != 0) {
/* Invalid event for a device */
}
} else {
/* Wrong address qualifier */
"sata_hba_event_notify: invalid address 0x%x",
return;
}
if ((event & SATA_EVNT_CONTROLLER_EVENTS) == 0 ||
(event & ~SATA_EVNT_CONTROLLER_EVENTS) != 0) {
/* Invalid event for the controller */
"sata_hba_event_notify: invalid event 0x%x for "
"controller",
return;
}
buf1[0] = '\0';
/* This may be a frequent and not interesting event */
"controller power level changed\n", NULL);
0xffffffffffffffff)
}
/*
* If we got here, there is something to do with this HBA
* instance.
*/
/* Tickle event thread */
if (sata_event_thread_active == 0)
if (buf1[0] != '\0') {
*lcp = '\0';
}
if (buf1[0] != '\0') {
}
if (buf2[0] != '\0') {
}
if (buf1[0] != '\0') {
}
if (buf2[0] != '\0') {
}
}
}
/*
* Event processing thread.
* Arg is a pointer to the sata_hba_list pointer.
* It is not really needed, because sata_hba_list is global and static
*/
static void
sata_event_daemon(void *arg)
{
#ifndef __lock_lint
#endif
"SATA event daemon started\n", NULL);
loop:
/*
* Process events here. Walk through all registered HBAs
*/
SATA_EVNT_SKIP) != 0) {
continue;
}
/* Got the controller with pending event */
/*
* Since global mutex was released, there is a
* possibility that HBA list has changed, so start
* over from the top. Just processed controller
* will be passed-over because of the SKIP flag.
*/
goto loop;
}
}
/* Clear SKIP flag in all controllers */
}
"SATA EVENT DAEMON suspending itself", NULL);
#ifdef SATA_DEBUG
if ((sata_func_enable & SATA_ENABLE_PROCESS_EVENTS) == 0) {
"SATA EVENTS PROCESSING DISABLED\n");
thread_exit(); /* Daemon will not run again */
}
#endif
/*
* wait timeout. Exit if there is a termination request (driver
* unload).
*/
do {
lbolt = ddi_get_lbolt();
if (sata_event_thread_active != 0) {
continue;
}
/* Check if it is time to go away */
if (sata_event_thread_terminate == 1) {
/*
* It is up to the thread setting above flag to make
* sure that this thread is not killed prematurely.
*/
"SATA_EVENT_DAEMON_TERMINATING", NULL);
}
} while (!(sata_event_pending & SATA_EVNT_MAIN));
"SATA EVENT DAEMON READY TO PROCESS EVENT", NULL);
goto loop;
}
/*
* Specific HBA instance event processing.
*
* NOTE: At the moment, device event processing is limited to hard disks
* only.
* cports only are supported - no pmports.
*/
static void
{
int ncport;
"Processing controller %d event(s)",
/*
* Process controller power change first
* HERE
*/
/*
*/
/* Check if port was locked by IOCTL processing */
if (event_flags & SATA_APCTL_LOCK_PORT_BUSY) {
/*
* We ignore port events because port is busy
* with AP control processing. Set again
* controller and main event flag, so that
* events may be processed by the next daemon
* run.
*/
"Event processing postponed until "
"AP control processing completes",
NULL);
/* Check other ports */
continue;
} else {
/*
* Set BSY flag so that AP control would not
* interfere with events processing for
* this port.
*/
}
if ((event_flags &
(SATA_EVNT_PORT_EVENTS | SATA_EVNT_DRIVE_EVENTS)) != 0) {
/*
* Got port event.
* We need some hierarchy of event processing as they
* are affecting each other:
* 1. port failed
* 3. link events - link events may trigger device
* detached or device attached events in some
* circumstances.
* 4. port power level changed
*/
if (event_flags & SATA_EVNT_PORT_FAILED) {
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_DETACHED) {
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_ATTACHED) {
saddr);
}
if (event_flags &
saddr);
}
if (event_flags & SATA_EVNT_PWR_LEVEL_CHANGED) {
saddr);
}
}
/* May have device event */
}
/* Release PORT_BUSY flag */
} /* End of loop through the controller SATA ports */
}
/*
* Process HBA power level change reported by HBA driver.
* Not implemented at this time - event is ignored.
*/
static void
{
"Processing controller power level change", NULL);
/* Ignoring it for now */
}
/*
* Process port power level change reported by HBA driver.
* Not implemented at this time - event is ignored.
*/
static void
{
"Processing port power level change", NULL);
/* Reset event flag */
}
/*
* Process port failure reported by HBA driver.
* cports support only - no pmports.
*/
static void
{
/* Reset event flag first */
/* If the port is in SHUTDOWN or FAILED state, ignore this event. */
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0) {
return;
}
/* Fail the port */
}
/*
* Device Reset Event processing.
* The seqeunce is managed by 3 stage flags:
* - reset event reported,
* - reset event being processed,
* - request to clear device reset state.
*/
static void
{
int rval;
/* We only care about host sata cport for now */
/* If the port is in SHUTDOWN or FAILED state, ignore reset event. */
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
return;
}
SATA_VALID_DEV_TYPE) == 0) {
return;
}
return;
}
/* Nothing to do */
return;
}
/* Something is weird - new device reset event */
"Overlapping device reset events!", NULL);
/* Just leave */
return;
}
/* Clear event flag */
/* It seems that we always need to check the port state first */
/*
* We have to exit mutex, because the HBA probe port function may
* block on its own mutex.
*/
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
return;
}
/*
* No device to process, anymore. Some other event processing
* would or have already performed port info cleanup.
* To be safe (HBA may need it), request clearing device
* reset condition.
*/
sdinfo->satadrv_event_flags = 0;
return;
}
/* Mark device reset processing as active */
SATA_FAILURE) {
/*
* Restoring drive setting failed.
* Probe the port first, to check if the port state has changed
*/
/* probe port */
if (rval == SATA_SUCCESS &&
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0 &&
/*
* We may retry this a bit later - reinstate reset
* condition
*/
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) != 0 &&
return;
}
} else {
/*
* No point of retrying - some other event processing
* would or already did port info cleanup.
* To be safe (HBA may need it),
* request clearing device reset condition.
*/
sdinfo->satadrv_event_flags = 0;
}
return;
}
/*
* Raise the flag indicating that the next sata command could
* be sent with SATA_CLEAR_DEV_RESET_STATE flag, if no new device
* reset is reported.
*/
}
}
/*
* Port Link Events processing.
* Every link established event may involve device reset (due to
* COMRESET signal, equivalent of the hard reset) so arbitrarily
* set device reset event for an attached device (if any).
* If the port is in SHUTDOWN or FAILED state, ignore link events.
*
* The link established event processing varies, depending on the state
* of the target node, HBA hotplugging capabilities, state of the port.
* If the link is not active, the link established event is ignored.
* If HBA cannot detect device attachment and there is no target node,
* the link established event triggers device attach event processing.
* Else, link established event triggers device reset event processing.
*
* The link lost event processing varies, depending on a HBA hotplugging
* capability and the state of the port (link active or not active).
* If the link is active, the lost link event is ignored.
* If HBA cannot detect device removal, the lost link event triggers
* device detached event processing after link lost timeout.
* Else, the event is ignored.
*
* NOTE: Only cports are processed for now, i.e. no port multiplier ports
*/
static void
{
int event_flags;
int rval;
/* Reset event flags first */
/* If the port is in SHUTDOWN or FAILED state, ignore link events. */
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
return;
}
/*
* For the sanity sake get current port state.
* Set device address only. Other sata_device fields should be
* set by HBA driver.
*/
/*
* We have to exit mutex, because the HBA probe port function may
* block on its own mutex.
*/
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
/*
* We may want to release device info structure, but
* it is not necessary.
*/
return;
} else {
/* port probed successfully */
}
if (event_flags & SATA_EVNT_LINK_ESTABLISHED) {
/* Ignore event */
"Ignoring port %d link established event - "
"link down",
goto linklost;
}
"Processing port %d link established event",
/*
* For the sanity sake check if a device is attached - check
* return state of a port probing.
*/
/*
* HBA port probe indicated that there is a device
* attached. Check if the framework had device info
* structure attached for this device.
*/
NULL);
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
/*
* Dev info structure is present.
* If dev_type is set to known type in
* the framework's drive info struct
* then the device existed before and
* the link was probably lost
* momentarily - in such case
* we may want to check device
* identity.
* Identity check is not supported now.
*
* Link established event
* triggers device reset event.
*/
}
} else if (cportinfo->cport_dev_type ==
/*
* We got new device attached! If HBA does not
* generate device attached events, trigger it
* here.
*/
if (!(SATA_FEATURES(sata_hba_inst) &
}
}
/* Reset link lost timeout */
}
}
if (event_flags & SATA_EVNT_LINK_LOST) {
/* Ignore event */
"Ignoring port %d link lost event - link is up",
goto done;
}
#ifdef SATA_DEBUG
if (cportinfo->cport_link_lost_time == 0) {
"Processing port %d link lost event",
}
#endif
/*
* we need to track link lost time and eventually generate
* device detach event.
*/
/* We are tracking link lost time */
if (cportinfo->cport_link_lost_time == 0) {
/* save current time (lbolt value) */
/* just keep link lost event */
} else {
if ((cur_time -
/* trigger device detach event */
"Triggering port %d "
"device detached event",
} else {
/* keep link lost event */
}
}
}
/*
* the attached device until the link is recovered.
*/
}
done:
if (event_flags != 0) {
}
}
/*
* Device Detached Event processing.
* Port is probed to find if a device is really gone. If so,
* the device info structure is detached from the SATA port info structure
* and released.
* Port status is updated.
*
* NOTE: Process cports event only, no port multiplier ports.
*/
static void
{
int rval;
/* Clear event flag */
/* If the port is in SHUTDOWN or FAILED state, ignore detach event. */
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
return;
}
/* For sanity, re-probe the port */
/*
* We have to exit mutex, because the HBA probe port function may
* block on its own mutex.
*/
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
/*
* We may want to release device info structure, but
* it is not necessary.
*/
return;
} else {
/* port probed successfully */
}
/*
* Check if a device is still attached. For sanity, check also
* link status - if no link, there is no device.
*/
/*
* Device is still attached - ignore detach event.
*/
"Ignoring detach - device still attached to port %d",
return;
}
/*
* We need to detach and release device info structure here
*/
sizeof (sata_drive_info_t));
}
/*
* Device cannot be reached anymore, even if the target node may be
* still present.
*/
/*
* Try to offline a device and remove target node if it still exists
*/
/*
* target node exist - unconfigure device first, then remove
* the node
*/
/*
* PROBLEM - no device, but target node remained
* This happens when the file was open or node was
* waiting for resources.
*/
"sata_process_device_detached: "
"Failed to unconfigure removed device."));
}
/*
* PROBLEM - no device, but target node remained
* This happens when the file was open or node was
* waiting for resources.
*/
"sata_process_device_detached: "
"Failed to remove target node for "
"removed device."));
}
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* with the hint: SE_HINT_REMOVE
*/
}
/*
* Device Attached Event processing.
* Port state is checked to verify that a device is really attached. If so,
* the device info structure is created and attached to the SATA port info
* structure.
*
* This function cannot be called in interrupt context (it may sleep).
*
* NOTE: Process cports event only, no port multiplier ports.
*/
static void
{
int rval;
/* Clear event flag first */
/* If the port is in SHUTDOWN or FAILED state, ignore event. */
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
return;
}
/*
* If the sata_drive_info structure is found attached to the port info,
* something went wrong in the event reporting and processing sequence.
* To recover, arbitrarily release device info structure and issue
* a warning.
*/
sizeof (sata_drive_info_t));
"Arbitrarily detaching old device info."));
}
/* For sanity, re-probe the port */
/*
* We have to exit mutex, because the HBA probe port function may
* block on its own mutex.
*/
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
return;
} else {
/* port probed successfully */
}
/*
* Check if a device is still attached. For sanity, check also
* link status - if no link, there is no device.
*/
/*
* No device - ignore attach event.
*/
"Ignoring attach - no device connected to port %d",
return;
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* with the hint: SE_HINT_INSERT
*/
/*
* Make sure that there is no target node for that device.
* If so, release it. It should not happen, unless we had problem
* removing the node when device was detached.
*/
"sata_process_device_attached: "
"old device target node exists!!!"));
/*
* target node exist - unconfigure device first, then remove
* the node
*/
/*
* PROBLEM - no device, but target node remained
* This happens when the file was open or node was
* waiting for resources.
*/
"sata_process_device_attached: "
"Failed to unconfigure old target node!"));
}
/* Following call will retry node offlining and removing it */
/* PROBLEM - no device, but target node remained */
"sata_process_device_attached: "
"Failed to remove old target node!"));
/*
* It is not clear, what should be done here.
* For now, we will not attach a new device
*/
return;
}
}
/*
* Reprobing port will take care of the creation of the device info
* structure and determination of the device type.
*/
/*
* If device was successfully attached, an explicit
* 'configure' command is needed to configure it.
*/
/* Log device info data */
}
}
}
/*
* sata_restore_drive_settings function compares current device setting
* with the saved device setting and, if there is a difference, restores
* device setting to the stored state.
* Device Identify data has to be current.
* At the moment only read ahead and write cache settings are considered.
*
* This function cannot be called in the interrupt context (it may sleep).
*
* Returns TRUE if successful or there was nothing to do.
* Returns FALSE if device setting could not be restored.
*
* Note: This function may fail the port, making it inaccessible.
* Explicit port disconnect/connect or physical device
*/
static int
{
int rval = SATA_SUCCESS;
/*
* Cannot get device identification - retry later
*/
"sata_restore_drive_settings: "
"cannot fetch device identify data\n"));
return (SATA_FAILURE);
}
/* Arbitrarily set UDMA mode */
"sata_restore_drive_settings: cannot set UDMA mode\n"));
return (SATA_FAILURE);
}
/* None of the features is supported - do nothing */
"restorable features not supported\n", NULL);
return (SATA_SUCCESS);
}
/* Nothing to do */
"nothing to restore\n", NULL);
return (SATA_SUCCESS);
}
/* Prepare packet for SET FEATURES COMMAND */
"sata_restore_drive_settings: could not "
"restore device settings\n"));
return (SATA_FAILURE);
}
/* Fill sata_pkt */
/* Timeout 30s */
/* Synchronous mode, no callback */
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/* Enable read ahead / read cache */
else
/* Disable read ahead / read cache */
/* Transfer command to HBA */
if (((*SATA_START_FUNC(sata_hba_inst))
/* Pkt execution failed */
"sata_restore_drive_settings: could not "
"restore device settings\n"));
rval = SATA_FAILURE;
}
}
/* Note that the sata packet is not removed, so it could be re-used */
/* Enable write cache */
else
/* Disable write cache */
/* Transfer command to HBA */
if (((*SATA_START_FUNC(sata_hba_inst))(
/* Pkt execution failed */
"sata_restore_drive_settings: could not "
"restore device settings\n"));
rval = SATA_FAILURE;
}
}
/* Free allocated resources */
/*
* We need to fetch Device Identify data again
*/
/*
* Cannot get device identification - retry later
*/
"sata_restore_drive_settings: "
"cannot re-fetch device identify data\n"));
rval = SATA_FAILURE;
}
/* Copy device sata info. */
return (rval);
}
/*
*
* Returns 1 if threshold exceeded, 0 if threshold no exceeded, -1 if
* unable to determine.
*
* Cannot be called in an interrupt context.
*
* Called by sata_build_lsense_page_2f()
*/
static int
{
int rval;
return (-1);
}
/* address is needed now */
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/* Set up which registers need to be returned */
/* Build SMART_RETURN_STATUS cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART RETURN STATUS
*/
rval = -1;
} else {
rval = -1;
goto fail;
}
rval = -1;
goto fail;
}
rval = 0;
rval = 1;
else {
rval = -1;
goto fail;
}
}
fail:
/* Free allocated resources */
return (rval);
}
/*
*
* Returns 0 if succeeded, -1 otherwise
*
* Cannot be called in an interrupt context.
*
*/
static int
struct smart_data *smart_data)
{
int rval;
#if ! defined(lint)
#endif
return (-1);
}
/* address is needed now */
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/*
* Allocate buffer for SMART data
*/
sizeof (struct smart_data));
"sata_fetch_smart_data: "
"cannot allocate buffer"));
return (-1);
}
/* Build SMART_READ_DATA cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART DATA available
*/
rval = -1;
goto fail;
} else {
if (rval != DDI_SUCCESS) {
"sata_fetch_smart_data: "
"sync pkt failed"));
rval = -1;
goto fail;
}
sizeof (struct smart_data));
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Used by LOG SENSE page 0x10
*
* return 0 for success, -1 otherwise
*
*/
static int
struct smart_ext_selftest_log *ext_selftest_log,
{
int rval;
#if ! defined(lint)
#endif
return (-1);
}
/* address is needed now */
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/*
* Allocate buffer for SMART extended self-test log
*/
sizeof (struct smart_ext_selftest_log));
"sata_ext_smart_selftest_log: "
"cannot allocate buffer"));
return (-1);
}
/* Build READ LOG EXT w/ extended self-test log cmd in the sata_pkt */
scmd->satacmd_lba_low_msb = 0;
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART selftest log info available
*/
rval = -1;
goto fail;
} else {
if (rval != DDI_SUCCESS) {
"sata_ext_smart_selftest_log: "
"sync pkt failed"));
rval = -1;
goto fail;
}
sizeof (struct smart_ext_selftest_log));
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Returns 0 for success, -1 otherwise
*
* SMART self-test log data is returned in buffer pointed to by selftest_log
*/
static int
struct smart_selftest_log *selftest_log)
{
int rval;
#if ! defined(lint)
#endif
return (-1);
}
/* address is needed now */
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/*
* Allocate buffer for Identify Data return data
*/
sizeof (struct smart_selftest_log));
"sata_smart_selftest_log: "
"cannot allocate buffer"));
return (-1);
}
/* Build SMART_READ_DATA cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART DATA available
*/
rval = -1;
goto fail;
} else {
if (rval != DDI_SUCCESS) {
"sata_smart_selftest_log: "
"sync pkt failed"));
rval = -1;
goto fail;
}
sizeof (struct smart_selftest_log));
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Returns 0 for success, -1 otherwise
*
* SMART READ LOG data is returned in buffer pointed to by smart_log
*/
static int
{
int rval;
return (-1);
}
/* address is needed now */
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/*
* Allocate buffer for SMART READ LOG
*/
"sata_smart_read_log: " "cannot allocate buffer"));
return (-1);
}
/* Build SMART_READ_DATA cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART DATA available
*/
rval = -1;
goto fail;
} else {
if (rval != DDI_SUCCESS) {
"sata_smart_read_log: " "sync pkt failed"));
rval = -1;
goto fail;
}
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Used by LOG SENSE page 0x10
*
* return 0 for success, -1 otherwise
*
*/
static int
struct read_log_ext_directory *logdir)
{
int rval;
#if ! defined(lint)
#endif
return (-1);
}
/* Fill sata_pkt */
/* Synchronous mode, no callback */
/* Timeout 30s */
/*
* Allocate buffer for SMART extended self-test log
*/
sizeof (struct read_log_ext_directory));
"sata_read_log_ext_directory: "
"cannot allocate buffer"));
return (-1);
}
/* Build READ LOG EXT w/ extended self-test log cmd in the sata_pkt */
scmd->satacmd_lba_low_msb = 0;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_mid_msb = 0;
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART selftest log info available
*/
rval = -1;
goto fail;
} else {
if (rval != DDI_SUCCESS) {
"sata_read_log_ext_directory: "
"sync pkt failed"));
rval = -1;
goto fail;
}
sizeof (struct read_log_ext_directory));
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
static void
int hint)
{
char ap[MAXPATHLEN];
int err;
/* Allocate and build sysevent attribute list */
if (err != 0) {
"sata_gen_sysevent: "
"cannot allocate memory for sysevent attributes\n"));
return;
}
/* Add hint attribute */
if (err != 0) {
"sata_gen_sysevent: "
"failed to add DR_HINT attr for sysevent"));
return;
}
/*
* Add AP attribute.
* Get controller pathname and convert it into AP pathname by adding
* a target number.
*/
if (err != 0) {
"sata_gen_sysevent: "
"failed to add DR_AP_ID attr for sysevent"));
return;
}
if (err != DDI_SUCCESS) {
"sata_gen_sysevent: "
"cannot log sysevent, err code %x\n", err));
}
}
/*
* sata_xlate_errors() is used to translate (S)ATA error
* information to SCSI information returned in the SCSI
* packet.
*/
static void
{
struct scsi_extended_sense *sense;
case SATA_PKT_PORT_ERROR:
/*
* We have no device data. Assume no data transfered.
*/
break;
case SATA_PKT_DEV_ERROR:
/*
* determine dev error reason from error
* reg content
*/
break;
}
/* No extended sense key - no info available */
break;
case SATA_PKT_TIMEOUT:
/*
* scsipkt->pkt_reason = CMD_TIMEOUT; This causes problems.
*/
/* No extended sense key */
break;
case SATA_PKT_ABORTED:
/* No extended sense key */
break;
case SATA_PKT_RESET:
/*
* pkt aborted either by an explicit reset request from
* a host, or due to error recovery
*/
break;
default:
break;
}
}