sata.c revision 0c6eaab480b44a0c790ad94e7cb6084792411de9
/*
* 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 2015 Nexenta Systems, Inc. All rights reserved.
*/
/*
* SATA Framework
* Generic SATA Host Adapter Implementation
*/
#include <sys/sysevent.h>
/*
* FMA header files
*/
/* Debug flags - defined in sata.h */
int sata_debug_flags = 0;
int sata_msg = 0;
/*
* Flags enabling selected SATA HBA framework functionality
*/
#define SATA_ENABLE_QUEUING 1
#define SATA_ENABLE_NCQ 2
#define SATA_ENABLE_PROCESS_EVENTS 4
int sata_func_enable =
/*
* Global variable setting default maximum queue depth (NCQ or TCQ)
* Note:minimum queue depth is 1
*/
/*
* initialization, using value from sata_max_queue_depth
* It is adjusted to minimum supported by the controller and by the device,
* if queueing is enabled.
*/
static int sata_current_max_qdepth;
/*
* Global variable determining the default behavior after device hotpluggin.
* If non-zero, the hotplugged device is onlined (if possible) without explicit
* IOCTL request (AP_CONFIGURE).
* If zero, hotplugged device is identified, but not onlined.
* Enabling (AP_CONNECT) device port with an attached device does not result
* in device onlining regardless of the flag setting
*/
int sata_auto_online = 0;
#ifdef SATA_DEBUG
uint64_t mbuf_count = 0;
uint64_t mbuffail_count = 0;
int sata_atapi_trace_save = 1;
static void sata_save_atapi_trace(sata_pkt_txlate_t *, int);
#else
#endif
#if 0
static void
#endif
#ifdef SATA_INJECT_FAULTS
#define SATA_INJECT_PKT_FAULT 1
uint32_t sata_fault_type = 0;
uint32_t sata_fault_cmd = 0;
static void sata_inject_pkt_fault(sata_pkt_t *, int *, int);
#endif
static char sata_rev_tag[] = {"1.46"};
/*
* 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);
/*
* SATA HBA interface functions are defined in sata_hba.h header file
*/
/* 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_pmport_link_events(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_pmdevice_detached(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_pmdevice_attached(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_cntrl_pwr_level_change(sata_hba_inst_t *);
static void sata_process_target_node_cleanup(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_device_autoonline(sata_hba_inst_t *,
/*
* 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_read_capacity16(sata_pkt_txlate_t *);
static int sata_txlt_unmap(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_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_ata_pass_thru(sata_pkt_txlate_t *);
static int sata_txlt_synchronize_cache(sata_pkt_txlate_t *);
static int sata_txlt_write_buffer(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 int sata_txlt_ata_pass_thru_illegal_cmd(sata_pkt_txlate_t *);
static int sata_txlt_unmap_nodata_cmd(sata_pkt_txlate_t *);
static void sata_txlt_rw_completion(sata_pkt_t *);
static void sata_txlt_nodata_cmd_completion(sata_pkt_t *);
static void sata_txlt_download_mcode_cmd_completion(sata_pkt_t *);
static int sata_emul_rw_completion(sata_pkt_txlate_t *);
uint8_t);
static struct scsi_extended_sense *sata_immediate_error_response(
sata_pkt_txlate_t *, int);
static int sata_txlt_atapi(sata_pkt_txlate_t *);
static void sata_txlt_atapi_completion(sata_pkt_t *);
/*
* Local functions for ioctl
*/
static int sata_ioctl_reset_all(sata_hba_inst_t *);
sata_ioctl_data_t *, int mode);
sata_ioctl_data_t *, int mode);
sata_ioctl_data_t *, int mode);
sata_ioctl_data_t *, int mode);
static int sata_ioctl_get_serialnumber_info(sata_hba_inst_t *,
/*
* Local functions
*/
static void sata_remove_hba_instance(dev_info_t *);
static void sata_probe_ports(sata_hba_inst_t *);
sata_address_t *);
static void sata_remove_target_node(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 *);
static void sata_common_free_dma_rsrcs(sata_pkt_txlate_t *);
sata_device_t *);
static void sata_reidentify_device(sata_pkt_txlate_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 *,
static int sata_set_drive_features(sata_hba_inst_t *,
sata_drive_info_t *, int flag);
uint8_t *);
struct scsi_inquiry *);
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_1a(sata_pkt_txlate_t *,
struct mode_info_power_cond *, int, int *, int *, int *);
static int sata_mode_select_page_1c(sata_pkt_txlate_t *,
struct mode_info_excpt_page *, int, int *, int *, int *);
static int sata_mode_select_page_30(sata_pkt_txlate_t *,
struct mode_acoustic_management *, int, int *, int *, int *);
sata_hba_inst_t *);
sata_hba_inst_t *);
sata_hba_inst_t *);
static void sata_set_arq_data(sata_pkt_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 *);
static void sata_decode_device_error(sata_pkt_txlate_t *,
struct scsi_extended_sense *);
static void sata_set_device_removed(dev_info_t *);
static int sata_ncq_err_ret_cmd_setup(sata_pkt_txlate_t *,
static int sata_atapi_err_ret_cmd_setup(sata_pkt_txlate_t *,
static void sata_fixed_sense_data_preset(struct scsi_extended_sense *);
static int sata_check_modser(char *, int);
/*
* FMA
*/
/*
* 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 */
"SATA Module" /* 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.
*/
/*
* Intermediate buffer device access attributes - they are required,
* but not necessarily used.
*/
static ddi_device_acc_attr_t sata_acc_attr = {
};
/*
* 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];
/*
* sata trace debug
*/
static sata_trace_rbuf_t *sata_debug_rbuf;
static sata_trace_dmsg_t *sata_trace_dmsg_alloc(void);
static void sata_trace_dmsg_free(void);
static void sata_trace_rbuf_alloc(void);
static void sata_trace_rbuf_free(void);
int dmsg_ring_size = DMSG_RING_SIZE;
/* Default write cache setting for SATA hard disks */
/* Default write cache setting for SATA ATAPI tape */
/* Default write cache setting for SATA ATAPI disk */
/*
* 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;
/*
* NCQ error recovery command
*/
static const sata_cmd_t sata_rle_cmd = {
NULL,
{
},
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
};
/*
* ATAPI error recovery CDB
*/
0, /* Only fixed RQ format is supported */
0,
0,
SATA_ATAPI_MIN_RQSENSE_LEN, /* Less data may be returned */
0
};
/* Warlock directives */
#ifdef SATA_DEBUG
#endif
/* 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;
char taskq_name[MAXPATHLEN];
"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 a task queue to handle emulated commands completion
* Use node name, dash, instance number as the queue name.
*/
taskq_name[0] = '\0';
sizeof (taskq_name));
/*
* Create events thread if not created yet.
*/
/*
* Link this hba instance into the list.
*/
if (sata_hba_list == NULL) {
/*
* The first instance of HBA is attached.
* all SATA devices. It is done here and now, to eliminate the
* possibility of the dynamic, programatic modification of the
* queue depth via global (and public) sata_max_queue_depth
* variable (this would require special handling in HBA drivers)
*/
if (sata_current_max_qdepth > 32)
sata_current_max_qdepth = 32;
else if (sata_current_max_qdepth < 1)
}
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 future phase of the development).
*/
/*
* Indicate that HBA is attached. This will enable events processing
* for this HBA.
*/
/*
* 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.
* For the ports with a supported device attached, device target nodes
* are created and devices are initialized.
*/
return (DDI_SUCCESS);
fail:
if (hba_attach_state & HBA_ATTACH_STAGE_LINKED) {
(void) sata_remove_hba_instance(dip);
if (sata_hba_list == NULL)
}
if (hba_attach_state & HBA_ATTACH_STAGE_SETUP) {
}
(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: Port multiplier is supported.
*/
int
{
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, 0);
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
"sata_hba_detach: "
"Target node not "
"removed !"));
return (DDI_FAILURE);
}
}
}
} else { /* SATA_DTYPE_PMULT */
"sata_hba_detach: Port multiplier "
"not ready yet!"));
return (DDI_FAILURE);
}
/*
* Detach would fail if removal of any of the
* target nodes is failed - albeit in that
* case some of them may have been removed.
*/
npmport);
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));
} else { /* SATA_DTYPE_PMULT */
}
}
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.
*
* Port Multiplier support is supported now.
*
* NOTE: qual should be SATA_ADDR_DCPORT or SATA_ADDR_DPMPORT
*/
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 than fail them.
*/
}
/* read devctl ioctl data */
if (cmd != DEVCTL_AP_CONTROL) {
return (EFAULT);
-1) {
if (dcp)
return (EINVAL);
}
/*
* According to SCSI_TO_SATA_ADDR_QUAL, qual should be either
* SATA_ADDR_DCPORT or SATA_ADDR_DPMPORT.
*/
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.
*
*/
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.
*/
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).
* If port activation is successful and a device is found
* attached to the port, the initialization sequence is
* executed to probe the port and attach
* a device structure to a port structure. The device is not
* set in configured state (system-wise) by this operation.
*/
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.
* If a port is in a shutdown state, sata_tran_port_activate()
* is invoked prior to any other operation.
*
* Onlining the device involves creating a new target node.
* If there is an old target node present (belonging to
* previously removed device), the operation is aborted - the
* old node has to be released and removed before configure
* operation is attempted.
*/
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 */
"sata_hba_ioctl: target port is %d:%d (%d)",
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 for configured device.
*/
break;
case SATA_CFGA_RESET_DEVICE:
/*
* There is no protection for configured device.
*/
&sata_device);
break;
case SATA_CFGA_RESET_ALL:
/*
* There is no protection for configured devices.
*/
/*
* We return here, because common return is for
* a single port operation - we have already unlocked
* all ports and no dc handle was allocated.
*/
return (rv);
/*
* Arbitrarily unconfigure attached device, if any.
* Even if the unconfigure fails, proceed with the
* port deactivation.
*/
break;
case SATA_CFGA_PORT_ACTIVATE:
break;
case SATA_CFGA_PORT_SELF_TEST:
&sata_device);
break;
break;
case SATA_CFGA_GET_AP_TYPE:
break;
case SATA_CFGA_GET_MODEL_INFO:
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);
}
/*
* Create error retrieval sata packet
*
* A sata packet is allocated and set-up to contain specified error retrieval
* command and appropriate dma-able data buffer.
* No association with any scsi packet is made and no callback routine is
* specified.
*
* Returns a pointer to sata packet upon successful packet creation.
* Returns NULL, if packet cannot be created.
*/
int pkt_type)
{
break;
}
"sata: error recovery request for non-attached device at "
return (NULL);
}
return (NULL);
}
/* address is needed now */
switch (pkt_type) {
break;
}
return (spkt);
}
break;
break;
}
return (spkt);
}
break;
default:
break;
}
return (NULL);
}
/*
* Free error retrieval sata packet
*
* Free sata packet and any associated resources allocated previously by
* sata_get_error_retrieval_pkt().
*
* Void return.
*/
void
{
}
/*
* Create READ PORT MULTIPLIER and WRITE PORT MULTIPLIER sata packet
*
* No association with any scsi packet is made and no callback routine is
* specified.
*
* Returns a pointer to sata packet upon successful packet creation.
* Returns NULL, if packet cannot be created.
*
* only lower 32 bits are available currently.
*/
{
break;
}
return (NULL);
}
/*
* NOTE: We need to send this command to the port multiplier,
* that means send to SATA_PMULT_HOSTPORT(0xf) pmport
*
* sata_device contains the address of actual target device, and the
* pmport number in the command comes from the sata_device structure.
*/
/* Fill sata_pkt */
/* Build READ PORT MULTIPLIER cmd in the sata_pkt */
if (type == SATA_RDWR_PMULT_PKT_TYPE_READ) {
} else if (type == SATA_RDWR_PMULT_PKT_TYPE_WRITE) {
}
return (spkt);
}
/*
* Free sata packet and any associated resources allocated previously by
* sata_get_rdwr_pmult_pkt().
*
* Void return.
*/
void
{
/* Free allocated resources */
}
/*
* Register a port multiplier to framework.
* 1) Store the GSCR values in the previous allocated pmult_info strctures.
* 2) Search in the blacklist and update the number of the device ports of the
* port multiplier.
*
* Void return.
*/
void
{
break;
}
/* HBA not attached? */
if (sata_hba_inst == NULL)
return;
/* Number of pmports */
/* Check the blacklist */
continue;
continue;
continue;
break;
}
/* Register the port multiplier GSCR */
"Port multiplier registered at port %d", cport);
}
}
/*
* sata_split_model splits the model ID into vendor and product IDs.
* It assumes that a vendor ID cannot be longer than 8 characters, and
* that vendor and product ID are separated by a whitespace.
*/
void
{
int i, modlen;
/*
* remove whitespace at the end of model
*/
for (i = SATA_ID_MODEL_LEN; i > 0; i--)
model[i] = '\0';
else
break;
/*
*/
break;
/*
* only use vid if it is less than 8 chars (as in SCSI)
*/
if (i < modlen && i <= 8) {
/*
* terminate vid, establish pid
*/
*pid++ = '\0';
} else {
/*
* vid will stay "ATA "
*/
/*
* model is all pid
*/
}
}
/*
* sata_name_child is for composing the name of the node
* the format of the name is "target,0".
*/
static int
{
int target;
if (target == -1)
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/* ****************** 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
/*
* Fail tran_tgt_init for .conf stub node
*/
if (ndi_dev_is_persistent_node(tgt_dip) == 0) {
return (DDI_FAILURE);
}
/* Validate scsi device address */
&sata_device) != 0)
return (DDI_FAILURE);
/* sata_device now contains a valid sata address */
return (DDI_FAILURE);
}
/*
* Check if we need to create a legacy devid (i.e cmdk style) for
* the target disks.
*
* HBA devinfo node will have the property "use-cmdk-devid-format"
* if we need to create cmdk-style devid for all the disk devices
* attached to this controller. This property may have been set
* from HBA driver's .conf file or by the HBA driver in its
* attach(9F) function.
*/
"use-cmdk-devid-format", 0) == 1)) {
/* register a legacy devid for this target node */
}
/*
* 'Identify Device Data' does not always fit in standard SCSI
* INQUIRY data, so establish INQUIRY_* properties with full-form
* of information.
*/
#ifdef _LITTLE_ENDIAN
#else /* _LITTLE_ENDIAN */
#endif /* _LITTLE_ENDIAN */
model[SATA_ID_MODEL_LEN] = 0;
fw[SATA_ID_FW_LEN] = 0;
if (vid)
if (pid)
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.
*/
"SATA 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 */
/*
* Note: tgt_free relates to the SCSA view of a device. If called, there
* was a device at this address, so even if the sata framework internal
* resources were alredy released because a device was detached,
* this function should be executed as long as its actions do
* not require the internal sata view of a device and the address
* refers to a valid sata address.
* Validating the address here means that we do not trust SCSA...
*/
&sata_device) == -1)
return;
/* sata_device now should contain a valid sata address */
return;
}
/*
* We did not allocate any resources in sata_scsi_tgt_init()
* other than few properties.
* Free them.
*/
/*
* If devid was previously created but not freed up from
* sd(7D) driver (i.e during detach(9F)) then do it here.
*/
"use-cmdk-devid-format", 0) == 1) &&
}
}
/*
* 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 data buffer
* NOTE:
* based on the premise that the transfer length specified in
* returning correct packet residue is crucial.
*/
&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.
* For DDI_DMA_NORESOURCES, we may have some of them allocated.
* Request may be repeated later - there is no real error.
*/
switch (rval) {
case DDI_DMA_NORESOURCES:
break;
case DDI_DMA_NOMAPPING:
case DDI_DMA_BADATTR:
break;
case DDI_DMA_TOOBIG:
default:
break;
}
goto fail;
}
goto fail;
}
/* Set number of bytes that are not yet accounted for */
return (pkt);
fail:
/*
* Since this is a new packet, we can clean-up
* everything
*/
} else {
/*
* This is a re-used packet. It will be target driver's
* responsibility to eventually destroy it (which
* will free allocated resources).
* Here, we just "complete" the request, leaving
* allocated resources intact, so the request may
* be retried.
*/
}
return (NULL);
}
/*
* Implementation of scsi tran_start.
* Translate scsi cmd into sata operation and return status.
* ATAPI CDBs are passed to ATAPI devices - the device determines what commands
* are supported.
* For SATA hard disks, supported scsi commands:
* SCMD_INQUIRY
* SCMD_TEST_UNIT_READY
* SCMD_START_STOP
* SCMD_READ_CAPACITY
* SCMD_SVC_ACTION_IN_G4 (READ CAPACITY (16))
* SCMD_REQUEST_SENSE
* SCMD_LOG_SENSE_G1
* SCMD_LOG_SELECT_G1
* 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_BUFFER
* SCMD_WRITE_G1
* SCMD_WRITE_G4
* SCMD_WRITE_G5
* SCMD_SEEK (noop)
* SCMD_SDIAG
*
* All other commands are rejected as unsupported.
*
* Returns:
* TRAN_ACCEPT if command was executed successfully or accepted by HBA driver
* for execution. TRAN_ACCEPT may be returned also if device was removed but
* a callback could be scheduled.
* TRAN_BADPKT if cmd was directed to invalid address.
* TRAN_FATAL_ERROR is command was rejected due to hardware error, including
* some unspecified error. TRAN_FATAL_ERROR may be also returned if a device
* was removed and there was no callback specified in scsi pkt.
* TRAN_BUSY if command could not be executed becasue HBA driver or SATA
* framework was busy performing some other operation(s).
*
*/
static int
{
int rval;
cport_tgtnode_clean == B_FALSE ||
}
} else {
pmport_tgtnode_clean == B_FALSE ||
}
}
}
/*
* The sd target driver is checking CMD_DEV_GONE pkt_reason
* only in callback function (for normal requests) and
* in the dump code path.
* So, if the callback is available, we need to do
* the callback rather than returning TRAN_FATAL_ERROR here.
*/
/* scsi callback required */
if (servicing_interrupt()) {
NULL) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
return (TRAN_ACCEPT);
}
/* No callback available */
return (TRAN_FATAL_ERROR);
}
"sata_scsi_start atapi: rval %d\n", rval);
return (rval);
}
/*
* Checking for power state, if it was on
* STOPPED state, then the drive is not capable
* of processing media access command. And
* TEST_UNIT_READY, REQUEST_SENSE has special handling
* in the function for different power state.
*/
}
/* 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_SVC_ACTION_IN_G4: /* READ CAPACITY (16) */
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_BUFFER:
break;
case SCMD_WRITE:
case SCMD_WRITE_G1:
case SCMD_WRITE_G4:
case SCMD_WRITE_G5:
break;
case SCMD_SEEK:
break;
break;
/* Other cases will be filed later */
/* postponed until phase 2 of the development */
case SPC3_CMD_UNMAP:
break;
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 of 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 */
else
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 for SATA hard disks:
* auto-rqsense (always supported)
* tagged-qing (supported if HBA supports it)
* untagged-qing (could be supported if disk supports it, but because
* caching behavior allowing untagged queuing actually
* results in reduced performance. sd tries to throttle
* back to only 3 outstanding commands, which may
* work for real SCSI disks, but with read ahead
* caching, having more than 1 outstanding command
* results in cache thrashing.)
* sector_size
* dma_max
* interconnect-type (INTERCONNECT_SATA)
*
* auto-rqsense (always supported)
* sector_size
* dma_max
* max-cdb-length
* interconnect-type (INTERCONNECT_SATA)
*
* Supported capabilities for ATAPI TAPE devices:
* auto-rqsense (always supported)
* dma_max
* max-cdb-length
*
* Supported capabilities for SATA ATAPI hard disks:
* auto-rqsense (always supported)
* interconnect-type (INTERCONNECT_SATA)
* max-cdb-length
*
* 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 (-1);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
break;
case SCSI_CAP_SECTOR_SIZE:
else rval = -1;
break;
/*
* untagged queuing cause a performance inversion because of
* the way sd operates. Because of this reason we do not
* use it when available.
*/
case SCSI_CAP_UNTAGGED_QING:
if (sdinfo->satadrv_features_enabled &
else
break;
case SCSI_CAP_TAGGED_QING:
if ((sdinfo->satadrv_features_enabled &
else
break;
case SCSI_CAP_DMA_MAX:
&adj_dma_attr);
/* We rely on the fact that dma_attr_maxxfer < 0x80000000 */
break;
break;
case SCSI_CAP_CDB_LEN:
else
rval = -1;
break;
default:
rval = -1;
break;
}
return (rval);
}
/*
* Implementation of scsi tran_setcap
*
* Only SCSI_CAP_UNTAGGED_QING and SCSI_CAP_TAGGED_QING are changeable.
*
*/
static int
{
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);
}
&sata_device)) == NULL) {
/* invalid address */
return (-1);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
case SCSI_CAP_SECTOR_SIZE:
case SCSI_CAP_DMA_MAX:
rval = 0;
break;
case SCSI_CAP_UNTAGGED_QING:
rval = 1;
if (value == 1) {
} else if (value == 0) {
} else {
rval = -1;
}
} else {
rval = 0;
}
break;
case SCSI_CAP_TAGGED_QING:
/* This can TCQ or NCQ */
if (sata_func_enable & SATA_ENABLE_QUEUING &&
rval = 1;
if (value == 1) {
} else if (value == 0) {
} else {
rval = -1;
}
} else {
rval = 0;
}
break;
default:
rval = -1;
break;
}
return (rval);
}
/*
* Implementations of scsi tran_destroy_pkt.
* Free resources allocated by sata_scsi_init_pkt()
*/
static void
{
}
/*
* Implementation of scsi tran_dmafree.
* Free DMA resources allocated by sata_scsi_init_pkt()
*/
static void
{
#ifndef __lock_lint
#endif
}
/*
* Implementation of scsi tran_sync_pkt.
*
* The assumption below is that pkt is unique - there is no need to check ap
*
* Synchronize DMA buffer and, if the intermediate buffer is used, copy data
*/
static void
{
#ifndef __lock_lint
#endif
int rval;
int direction;
/* Intermediate DMA buffer used */
if (direction & SATA_DIR_WRITE) {
}
}
/* Sync the buffer for device or for CPU */
(direction & SATA_DIR_WRITE) ?
!(direction & SATA_DIR_WRITE)) {
/* Intermediate DMA buffer used for read */
}
}
}
}
/* ******************* SATA - SCSI Translation functions **************** */
/*
* translation.
*/
/*
* Checks if a device exists and can be access and translates common
* scsi_pkt data to sata_pkt data.
*
* to HBA in arbitrary SYNC mode to execute this packet.
*
* Returns TRAN_ACCEPT and scsi pkt_reason CMD_CMPLT if device exists and
* sata_pkt was set-up.
* Returns TRAN_ACCEPT and scsi pkt_reason CMD_DEV_GONE if device does not
* exist and pkt_comp callback was scheduled.
* Returns other TRAN_XXXXX values when error occured and command should be
* rejected with the returned TRAN_XXXXX value.
*
* 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 */
};
/*
* Pkt_reason has to be set if the pkt_comp callback is invoked,
* and that implies TRAN_ACCEPT return value. Any other returned value
* indicates that the scsi packet was not accepted (the reason will not
* be checked by the scsi target driver).
* To make debugging easier, we set pkt_reason to know value here.
* It may be changed later when different completion reason is
* determined.
*/
*reason = CMD_TRAN_ERR;
/* Validate address */
case -1:
/* Invalid address or invalid device type */
return (TRAN_BADPKT);
case 2:
/*
* Valid address but device type is unknown - Chack if it is
* in the reset state and therefore in an indeterminate state.
*/
SATA_EVNT_INPROC_DEVICE_RESET)) != 0) {
if (!ddi_in_panic()) {
*reason = CMD_INCOMPLETE;
"sata_scsi_start: rejecting command "
"because of device reset state\n", NULL);
return (TRAN_BUSY);
}
}
/* FALLTHROUGH */
case 1:
/* valid address but no valid device - it has disappeared */
*reason = CMD_DEV_GONE;
/*
* The sd target driver is checking CMD_DEV_GONE pkt_reason
* only in callback function (for normal requests) and
* in the dump code path.
* So, if the callback is available, we need to do
* the callback rather than returning TRAN_FATAL_ERROR here.
*/
/* scsi callback required */
if (servicing_interrupt()) {
NULL) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
return (TRAN_ACCEPT);
}
return (TRAN_FATAL_ERROR);
default:
/* all OK; pkt reason will be overwritten later */
break;
}
/*
* If pkt is to be executed in polling mode and a command will not be
* command to HBA driver in arbitrary SYNC mode) and we are in the
* interrupt context and not in the panic dump, then reject the packet
* to avoid a possible interrupt stack overrun or hang caused by
* a potentially blocked interrupt.
*/
servicing_interrupt() && !ddi_in_panic()) {
"sata_scsi_start: rejecting synchronous command because "
"of interrupt context\n", NULL);
return (TRAN_BUSY);
}
/*
* If device is in reset condition, reject the packet with
* TRAN_BUSY, unless:
* 1. system is panicking (dumping)
* In such case only one thread is running and there is no way to
* process reset.
* 2. cfgadm operation is is progress (internal APCTL lock is set)
* Some cfgadm operations involve drive commands, so reset condition
* needs to be ignored for IOCTL operations.
*/
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) != 0) {
if (!ddi_in_panic() &&
SATA_APCTL_LOCK_PORT_BUSY) == 0)) {
*reason = CMD_INCOMPLETE;
"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.
*/
SATA_APCTL_LOCK_PORT_BUSY) != 0) {
}
/*
* At this point the generic translation routine determined that the
* scsi packet should be accepted. Packet completion reason may be
* changed later when a different completion reason is determined.
*/
/* Synchronous execution */
} else {
/* Asynchronous execution */
}
/* Convert queuing information */
/* Always limit pkt time */
else
/* Pass on scsi_pkt time */
return (TRAN_ACCEPT);
}
/*
* Translate ATA Identify Device data to SCSI Inquiry data.
* This function may be called only for ATA devices.
* This function should not be called for ATAPI devices - they
* respond directly to SCSI Inquiry command.
*
* SATA Identify Device data has to be valid in sata_drive_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.
* It could be that DTYPE_OPTICAL could also qualify in the future.
* ATAPI Inquiry may provide more data to the target driver.
*/
/* CFA type device is not a removable media device */
/*
* Queuing support - controller has to
* support some sort of command queuing.
*/
else
#ifdef _LITTLE_ENDIAN
/* Swap text fields to match SCSI format */
else
#else /* _LITTLE_ENDIAN */
else
#endif /* _LITTLE_ENDIAN */
}
/*
* 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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* Scsi response set up for check condition with special sense key
* and additional sense code.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* Scsi response setup for
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
int rval;
int reason;
return (rval);
}
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Inquiry / Identify Device
* Use cached Identify Device data for now, rather than issuing actual
* Device Identify cmd request. If device is detached and re-attached,
* asynchronous event processing should fetch and refresh Identify Device
* data.
* VPD pages supported now:
* Vital Product Data page
* Unit Serial Number page
* Block Device Characteristics Page
* ATA Information Page
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
#define INQUIRY_SUP_VPD_PAGE 0 /* Supported VPD Pages Page Code */
/* Code */
static int
{
struct scsi_extended_sense *sense;
int count;
uint8_t *p;
int i, j;
return (rval);
}
/* Reject not supported request */
goto done;
}
/* Valid Inquiry request */
/*
* Because it is fully emulated command storing data
* programatically in the specified buffer, release
* preallocated DMA resources before storing data in the buffer,
* so no unwanted DMA sync would take place.
*/
/* 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 supported Vital Product Data
* pages.
*/
page_buf[2] = 0;
/* Copy no more than requested */
break;
case INQUIRY_USN_PAGE:
/*
* Request for Unit Serial Number page.
* Set-up the page.
*/
page_buf[2] = 0;
/* remaining page length */
/*
* Copy serial number from Identify Device data
* words into the inquiry page and swap bytes
* when necessary.
*/
#ifdef _LITTLE_ENDIAN
#else
#endif
/*
* Least significant character of the serial
* number shall appear as the last byte,
* according to SBC-3 spec.
* Count trailing spaces to determine the
* necessary shift length.
*/
for (j = 0; j < SATA_ID_SERIAL_LEN; j++) {
if (*(p - j) != '\0' &&
*(p - j) != '\040')
break;
}
/*
* Shift SN string right, so that the last
* non-blank character would appear in last
* byte of SN field in the page.
* 'j' is the shift length.
*/
for (i = 0;
i < (SATA_ID_SERIAL_LEN - j) && j != 0;
i++, p--)
*p = *(p - j);
/*
* Add leading spaces - same number as the
* shift size
*/
for (; j > 0; j--)
SATA_ID_SERIAL_LEN + 4);
break;
case INQUIRY_BDC_PAGE:
/*
* Request for Block Device Characteristics
* page. Set-up the page.
*/
page_buf[2] = 0;
/* remaining page length */
page_buf[6] = 0;
ai_nomformfactor & 0xf;
SATA_ID_BDC_LEN + 4);
break;
case INQUIRY_ATA_INFO_PAGE:
/*
* Request for ATA Information page.
*/
0xff;
/* page_buf[4-7] reserved */
#ifdef _LITTLE_ENDIAN
" ", 4) == 0) {
} else {
}
#else /* _LITTLE_ENDIAN */
" ", 4) == 0) {
} else {
}
#endif /* _LITTLE_ENDIAN */
/*
* page_buf[36-55] which defines the device
* signature is not defined at this
* time.
*/
/* Set the command code */
if (sdinfo->satadrv_type ==
} else if (sdinfo->satadrv_type ==
}
/*
* If the command code, page_buf[56], is not
* zero and if one of the identify commands
* succeeds, return the identify data.
*/
if ((page_buf[56] != 0) &&
SATA_SUCCESS)) {
}
/* Need to copy out the page_buf to bp */
SATA_ID_ATA_INFO_LEN + 4);
break;
/*
* We may want to implement this page, when
* identifiers are common for SATA devices
* But not now.
*/
/*FALLTHROUGH*/
default:
/* Request for unsupported VPD page */
sense->es_add_code =
goto done;
}
}
}
done:
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Request Sense.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
* At the moment this is an emulated command (ATA version for SATA hard disks).
* May be translated into Check Power Mode command in the future.
*
* Note: There is a mismatch between already implemented Informational
* Exception Mode Select page 0x1C and this function.
* When MRIE bit is set in page 0x1C, Request Sense is supposed to return
* NO SENSE and set additional sense code to the exception code - this is not
* implemented here.
*/
static int
{
struct scsi_extended_sense sense;
return (rval);
}
/*
* when CONTROL field's NACA bit == 1
* return ILLEGAL_REQUEST
*/
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
}
switch (scmd->satacmd_sec_count_lsb) {
case SATA_PWRMODE_STANDBY: /* device in standby mode */
else {
}
break;
case SATA_PWRMODE_IDLE: /* device in idle mode */
break;
case SATA_PWRMODE_ACTIVE: /* device in active or idle mode */
default: /* 0x40, 0x41 active mode */
else {
}
break;
}
/*
* Because it is fully emulated command storing data
* programatically in the specified buffer, release
* preallocated DMA resources before storing data in the buffer,
* so no unwanted DMA sync would take place.
*/
sizeof (struct scsi_extended_sense));
/* Copy no more than requested */
switch (power_state) {
case SATA_POWER_IDLE:
case SATA_POWER_STANDBY:
break;
case SATA_POWER_STOPPED:
break;
case SATA_POWER_ACTIVE:
default:
break;
}
}
"Scsi_pkt completion reason %x\n",
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Test Unit Ready
* (ATA version for SATA hard disks).
* It is translated into the Check Power Mode command.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
struct scsi_extended_sense *sense;
int power_state;
return (rval);
}
/* send CHECK POWER MODE command */
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
}
/*
* return NOT READY when device in STOPPED mode
*/
if (power_state == SATA_PWRMODE_STANDBY &&
} else {
/*
* For other power mode, return GOOD status
*/
}
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Start Stop Unit
* Translation depends on a command:
*
* Power condition bits will be supported
* and the power level should be maintained by SATL,
* When SATL received a command, it will check the
* power level firstly, and return the status according
* to SAT2 v2.6 and SAT-2 Standby Modifications
*
* -----------------------------------------------------------------------
* SSU_PC1 Active <==> ATA Active <==> SSU:start_bit =1
* SSU_PC2 Idle <==> ATA Idle <==> N/A
* SSU_PC3 Standby <==> ATA Standby <==> N/A
* SSU_PC4 Stopped <==> ATA Standby <==> SSU:start_bit = 0
*
* Unload Media / NOT SUPPORTED YET
* Load Media / NOT SUPPROTED YET
* Immediate bit / NOT SUPPORTED YET (deferred error)
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
return (rval);
}
/* IMMED bit - not supported */
}
case 0:
goto err_out;
}
/* Start Unit */
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
} else {
/* Stop Unit */
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
/* ata standby immediate command */
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case 0x1:
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case 0x2:
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
/*
* POWER CONDITION MODIFIER bit set
* to 0x1 or larger it will be handled
* on the same way as bit = 0x1
*/
if (!(sata_id->ai_cmdset84 &
break;
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case 0x3:
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
break;
case 0x7:
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
switch (scmd->satacmd_sec_count_lsb) {
case SATA_PWRMODE_STANDBY:
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case SATA_PWRMODE_IDLE:
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case SATA_PWRMODE_ACTIVE:
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
break;
default:
goto err_out;
}
break;
case 0xb:
return (sata_txlt_check_condition(spx,
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
default:
}
/*
* Since it was a synchronous command,
* a callback function will be called directly.
*/
"synchronous execution status %x\n",
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
else
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 for SATA hard disks.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
"sata_txlt_read_capacity: ", NULL);
return (rval);
}
/*
* Because it is fully emulated command storing data
* programatically in the specified buffer, release
* preallocated DMA resources before storing data in the buffer,
* so no unwanted DMA sync would take place.
*/
/*
* As per SBC-3, the "returned LBA" is either the highest
* addressable LBA or 0xffffffff, whichever is smaller.
*/
/* 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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* SATA translate command: Read Capacity (16).
* Emulated command for SATA disks.
* Info is retrieved from cached Identify Device data.
* Implemented to SBC-3 (draft 21) and SAT-2 (final) specifications.
*
* Returns TRAN_ACCEPT and appropriate values in scsi_pkt fields.
*/
static int
{
#define TPE 0x80
#define TPRZ 0x40
"sata_txlt_read_capacity: ", NULL);
return (rval);
}
/*
* Because it is fully emulated command storing data
* programatically in the specified buffer, release
* preallocated DMA resources before storing data in the buffer,
* so no unwanted DMA sync would take place.
*/
/* Check SERVICE ACTION field */
return (sata_txlt_check_condition(spx,
}
/* Check LBA field */
return (sata_txlt_check_condition(spx,
}
/* Check PMI bit */
return (sata_txlt_check_condition(spx,
}
/* last logical block address */
/* logical to physical block size exponent */
l2p_exp = 0;
/* multiple logical sectors per phys sectors */
l2p_exp =
}
}
/* returned logical block address */
/* logical block length in bytes = 512 (for now) */
/* rbuf[8] = 0; */
/* rbuf[9] = 0; */
/* rbuf[11] = 0; */
/* p_type, prot_en, unspecified by SAT-2 */
/* rbuf[12] = 0; */
/* p_i_exponent, undefined by SAT-2 */
/* logical blocks per physical block exponent */
/* lowest aligned logical block address = 0 (for now) */
/* tpe and tprz as defined in T10/10-079 r0 */
} else {
}
}
/* rbuf[15] = 0; */
}
/*
* If a callback was requested, do it now.
*/
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* Translate command: UNMAP
*
* The function cannot be called in interrupt context since it may sleep.
*/
static int
{
int synch;
int i, x;
int bdlen = 0;
int ranges = 0;
int paramlen = 8;
#define TRIM 0x1
"sata_txlt_unmap: ", NULL);
"DSM support 0x%x, max number of 512 byte blocks of LBA "
}
return (rval);
}
/*
* Need to modify bp to have TRIM data instead of UNMAP data.
* Start by getting the block descriptor data length by subtracting
* the 8 byte parameter list header from the parameter list length.
* The block descriptor size has to be a multiple of 16 bytes.
*/
"sata_txlt_unmap: invalid block descriptor length", NULL);
}
/*
* If there are no parameter data or block descriptors, it is not
* considered an error so just complete the command without sending
* TRIM.
*/
"sata_txlt_unmap: no parameter data or block descriptors",
NULL);
return (sata_txlt_unmap_nodata_cmd(spx));
}
/*
* Loop through all the UNMAP block descriptors and convert the data
* into TRIM format.
*/
/* get range length */
/* get LBA */
ranges++;
}
/*
* The TRIM command expects the data buffer to be a multiple of
* 512-byte blocks of range entries. This means that the UNMAP buffer
* may be too small. Free the original DMA resources and create a
* local buffer.
*/
/*
* Get count of 512-byte blocks of range entries. The length
* of a range entry is 8 bytes which means one count has 64 range
* entries.
*/
/* Allocate a buffer that is a multiple of 512 bytes. */
"sata_txlt_unmap: "
"cannot allocate buffer for TRIM command", NULL);
return (TRAN_BUSY);
}
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
/* Start processing command */
} else {
}
return (rval);
}
if (synch) {
}
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 */
"sata_txlt_mode_sense, pc %x page code 0x%02x\n",
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
} else {
}
return (rval);
}
/*
* Because it is fully emulated command storing data
* programatically in the specified buffer, release
* preallocated DMA resources before storing data in the buffer,
* so no unwanted DMA sync would take place.
*/
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_ACOUSTIC_MANAG:
/* acoustic management */
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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
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: Implemented pages:
* - caching page
* - informational exception page
* - acoustic management page
* - power condition page
* Caching setup is remembered so it could be re-stored in case of
* an unexpected device reset.
*
* Returns TRAN_XXXX.
* If TRAN_ACCEPT is returned, appropriate values are set 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;
case MODEPAGE_ACOUSTIC_MANAG:
(struct mode_acoustic_management *)
/*
* 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_POWER_COND:
/*
* 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 = 0;
/*
* Following statement has to be changed if this function is
* used for devices other than SATA hard disks.
*/
&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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (rval);
}
/*
* Translate command: ATA Pass Through
* Incomplete implementation. Only supports No-Data, PIO Data-In, and
* PIO Data-Out protocols. Also supports CK_COND bit.
*
* Mapping of the incoming CDB bytes to the outgoing satacmd bytes is
* described in Table 111 of SAT-2 (Draft 9).
*/
static int
{
int extend;
return (rval);
}
/* T_DIR bit */
else
/* MULTIPLE_COUNT field. If non-zero, invalid command (for now). */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
/* OFFLINE field. If non-zero, invalid command (for now). */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
/* PROTOCOL field */
case SATL_APT_P_HW_RESET:
case SATL_APT_P_SRST:
case SATL_APT_P_DMA:
case SATL_APT_P_DMA_QUEUED:
case SATL_APT_P_DEV_DIAG:
case SATL_APT_P_DEV_RESET:
case SATL_APT_P_UDMA_IN:
case SATL_APT_P_UDMA_OUT:
case SATL_APT_P_FPDMA:
case SATL_APT_P_RET_RESP:
/* Not yet implemented */
default:
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
case SATL_APT_P_NON_DATA:
break;
case SATL_APT_P_PIO_DATA_IN:
/* If PROTOCOL disagrees with T_DIR, invalid command */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
/* if there is a buffer, release its DMA resources */
} else {
/* if there is no buffer, how do you PIO in? */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
break;
case SATL_APT_P_PIO_DATA_OUT:
/* If PROTOCOL disagrees with T_DIR, invalid command */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
/* if there is a buffer, release its DMA resources */
} else {
/* if there is no buffer, how do you PIO out? */
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
break;
}
/* Parse the ATA cmd fields, transfer some straight to the satacmd */
break;
extend = 1;
} else {
0xf0;
}
break;
}
/* CK_COND bit */
if (extend) {
}
}
/* Transfer remaining parsed ATA cmd values to the satacmd */
if (extend) {
} else {
scmd->satacmd_features_reg_ext = 0;
scmd->satacmd_sec_count_msb = 0;
scmd->satacmd_lba_low_msb = 0;
scmd->satacmd_lba_mid_msb = 0;
scmd->satacmd_lba_high_msb = 0;
}
/* Determine transfer length */
case 1:
break;
case 2:
break;
default:
t_len = 0;
break;
}
/* Adjust transfer length for the Byte Block bit */
/* Start processing command */
} else {
}
return (rval);
}
if (synch) {
}
return (TRAN_ACCEPT);
}
/*
* Translate command: Log Sense
*/
static int
{
struct scsi_extended_sense *sense;
int pc; /* Page Control code */
int page_code; /* Page code */
#define MAX_LOG_SENSE_PAGE_SIZE 512
"sata_txlt_log_sense, pc 0x%x, page code 0x%x\n",
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
} else {
}
return (rval);
}
/* Reject not supported request for all but cumulative values */
switch (pc) {
case PC_CUMULATIVE_VALUES:
break;
default:
goto done;
}
switch (page_code) {
break;
default:
goto done;
}
/*
* Because log sense uses local buffers for data retrieval from
* the devices and sets the data programatically in the
* original specified buffer, release preallocated DMA
* resources before storing data in the original buffer,
* so no unwanted DMA sync would take place.
*/
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;
sense->es_add_code =
goto done;
}
sense->es_add_code =
goto done;
}
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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
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
* for SATA hard disks.
* 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.
* In both cases the maximum queue depth is derived as minimum of:
* HBA capability,device capability and sata_max_queue_depth variable setting.
* The value passed to HBA driver is decremented by 1, because only 5 bits are
* used to pass max queue depth value, and the maximum possible queue depth
* is 32.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
int synch;
return (rval);
}
/*
* Extract LBA and sector count from scsi CDB.
*/
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));
}
/*
* For zero-length transfer, emulate good completion of the command
* (reasons for rejecting the command were already checked).
* No DMA resources were allocated.
*/
return (sata_emul_rw_completion(spx));
}
/*
* Build cmd block depending on the device capability and
* requested operation mode.
* Do not bother with non-dma mode - we are working only with
* devices supporting DMA.
*/
#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 if ((sdinfo->satadrv_features_support &
SATA_DEV_F_TCQ) &&
SATA_CTLF_QCMD)) {
/* Legacy queueing */
if (sdinfo->satadrv_features_support &
scmd->satacmd_sec_count_msb = 0;
} else {
}
} else /* NCQ nor legacy queuing not supported */
/*
* If queuing, the sector count goes in the features register
* and the secount count will contain the tag.
*/
if (using_queuing) {
scmd->satacmd_sec_count_lsb = 0;
/* Set-up maximum queue depth */
} else if (sdinfo->satadrv_features_enabled &
/*
* may be still used.
* Set-up the maximum untagged queue depth.
* Use controller's queue depth from sata_hba_tran.
* SATA HBA drivers may ignore this value and rely on
* the internal limits.For drivers that do not
* ignore untaged queue depth, limit the value to
* SATA_MAX_QUEUE_DEPTH (32), as this is the
* largest value that can be passed via
* satacmd_flags.sata_max_queue_depth.
*/
} else {
}
} else
"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
* for SATA hard disks.
* Both the device capabilities and requested operation mode are
* considered.
*
* Following scsi cdb fields are ignored:
* rwprotect, 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, write
* request will be translated to WRITE_DMA_QUEUEING or WRITE_DMA_QUEUEING_EXT
* command rather than plain WRITE_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 write
* request will be translated to WRITE_FPDMA_QUEUED command.
* In both cases the maximum queue depth is derived as minimum of:
* HBA capability,device capability and sata_max_queue_depth variable setting.
* The value passed to HBA driver is decremented by 1, because only 5 bits are
* used to pass max queue depth value, and the maximum possible queue depth
* is 32.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
int synch;
return (rval);
}
/*
* Extract LBA and sector count from scsi CDB
*/
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));
}
/*
* For zero-length transfer, emulate good completion of the command
* (reasons for rejecting the command were already checked).
* No DMA resources were allocated.
*/
return (sata_emul_rw_completion(spx));
}
/*
* Build cmd block depending on the device capability and
* requested operation mode.
* Do not bother with non-dma mode- we are working only with
* devices supporting DMA.
*/
#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 if ((sdinfo->satadrv_features_support &
SATA_DEV_F_TCQ) &&
SATA_CTLF_QCMD)) {
/* Legacy queueing */
if (sdinfo->satadrv_features_support &
scmd->satacmd_sec_count_msb = 0;
} else {
}
} else /* NCQ nor legacy queuing not supported */
if (using_queuing) {
scmd->satacmd_sec_count_lsb = 0;
/* Set-up maximum queue depth */
} else if (sdinfo->satadrv_features_enabled &
/*
* may be still used.
* Set-up the maximum untagged queue depth.
* Use controller's queue depth from sata_hba_tran.
* SATA HBA drivers may ignore this value and rely on
* the internal limits. For drivera that do not
* ignore untaged queue depth, limit the value to
* SATA_MAX_QUEUE_DEPTH (32), as this is the
* largest value that can be passed via
* satacmd_flags.sata_max_queue_depth.
*/
} else {
}
} else
"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);
}
/*
* Implements SCSI SBC WRITE BUFFER command download microcode option
*/
static int
{
#define WB_DOWNLOAD_MICROCODE_AND_REVERT_MODE 4
#define WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE 5
struct scsi_extended_sense *sense;
"sata_txlt_write_buffer, mode 0x%x\n", mode);
TRAN_ACCEPT) {
return (rval);
}
/* Use synchronous mode */
/*
* The SCSI to ATA translation specification only calls
* for WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE.
* WB_DOWNLOAD_MICROC_AND_REVERT_MODE is implemented, but
* ATA 8 (draft) got rid of download microcode for temp
* and it is even optional for ATA 7, so it may be aborted.
* WB_DOWNLOAD_MICROCODE_WITH_OFFSET is not implemented as
* it is not specified and the buffer offset for SCSI is a 16-bit
* value in bytes, but for ATA it is a 16-bit offset in 512 byte
* sectors. Thus the offset really doesn't buy us anything.
* If and when ATA 8 is stabilized and the SCSI to ATA specification
* is revised, this can be revisisted.
*/
/* Reject not supported request */
switch (mode) {
break;
break;
default:
goto bad_param;
}
goto bad_param;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_addr_type = 0;
/* Transfer command to HBA */
/* Pkt not accepted for execution */
return (rval);
}
/* Then we need synchronous check the status of the disk */
/* Download commmand succeed, so probe and identify device */
} else {
/* Something went wrong, microcode download command failed */
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:
/* 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 */
}
return (TRAN_ACCEPT);
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (rval);
}
/*
* Re-identify device after doing a firmware download.
*/
static void
{
#define DOWNLOAD_WAIT_TIME_SECS 60
#define DOWNLOAD_WAIT_INTERVAL_SECS 1
int rval;
int retry_cnt;
/*
* Before returning good status, probe device.
* Device probing will get IDENTIFY DEVICE data, if possible.
* The assumption is that the new microcode is applied by the
* device. It is a caller responsibility to verify this.
*/
for (retry_cnt = 0;
retry_cnt++) {
&sata_device);
SATA_SUCCESS) {
/* retry */
sdinfo);
if (rval == SATA_RETRY)
"SATA device at port %d pmport %d -"
" default device features could not"
" be set. Device may not operate "
"as expected.",
}
return;
} else if (rval == SATA_RETRY) {
continue;
} else /* failed - no reason to retry */
break;
}
/*
* Something went wrong, device probing failed.
*/
"Cannot probe device after downloading microcode\n"));
/* Reset device to force retrying the probe. */
(void) (*SATA_RESET_DPORT_FUNC(sata_hba_inst))
}
/*
* Translate command: Synchronize Cache.
* Translates into Flush Cache command for SATA hard disks.
*
* Returns TRAN_ACCEPT or code returned by sata_hba_start() and
* appropriate values in scsi_pkt fields.
*/
static int
{
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.
*/
static int
{
int stat;
struct sata_cmd_flags cmd_flags;
/* Clear device reset state? */
/* qual should be XXX_DPMPORT, but add XXX_PMPORT in case */
/*
* Get the pmult_info of the its parent port multiplier, all
* sub-devices share a common device reset flags on in
* pmult_info.
*/
"sata_hba_start: clearing device reset state"
"on pmult.\n", NULL);
}
} else {
if (sdinfo->satadrv_event_flags &
"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.
*/
if (stat == 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);
}
switch (stat) {
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.
*/
"SATA port %d error",
else
"SATA port %d:%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.
*/
} else {
}
*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.
*/
"sat_hba_start: cmd 0x%2x rejected "
"with SATA_TRAN_CMD_UNSUPPORTED status\n", cmd);
(void) sata_txlt_invalid_command(spx);
*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.
*/
} else {
}
}
}
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 */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
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;
return (sense);
}
/*
* ATA Pass Through support
* Sets flags indicating that an invalid value was found in some
* field in the command. It could be something illegal according to
* the SAT-2 spec or it could be a feature that is not (yet?)
* supported.
*/
static int
{
/* scsi callback required */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* The UNMAP command considers it not to be an error if the parameter length
* or block descriptor length is 0. For this case, there is nothing for TRIM
* to do so just complete the command.
*/
static int
{
/* scsi callback required */
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* This request always succedes, so in synchronous mode it always returns
* TRAN_ACCEPT, and in non-synchronous mode it may return TRAN_BUSY if the
* callback cannot be scheduled.
*/
static int
{
/* scsi callback required - have to schedule it */
if (servicing_interrupt()) {
return (TRAN_BUSY);
}
/* Scheduling the callback failed */
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
/*
* 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;
int rval;
/* Normal completion */
/* Temporary buffer was used */
rval = ddi_dma_sync(
spx->txlt_buf_dma_handle, 0, 0,
}
}
} 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:
break;
case SATA_PKT_ABORTED:
break;
case SATA_PKT_RESET:
break;
default:
"sata_txlt_rw_completion: "
"invalid packet completion reason"));
break;
}
}
/* 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
{
/* scsi callback required */
}
/*
* Completion handler for ATA Pass Through command
*/
static void
{
/* Normal completion */
/*
* If the command has CK_COND set
*/
SD_SCSI_ASC_APT_INFO_AVAIL, 0x1d);
}
/* Temporary buffer was used */
}
}
} else {
/*
* If DF or ERR was set, the HBA should have copied out the
* status and error registers to the satacmd structure.
*/
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
}
}
}
/* scsi callback required */
}
/*
* Completion handler for unmap translation command
*/
static void
{
/* Normal completion */
/* Temporary buffer was used */
}
}
} else {
/*
* If DF or ERR was set, the HBA should have copied out the
* status and error registers to the satacmd structure.
*/
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
addl_sense_qual = 0;
}
}
}
/* scsi callback required */
}
/*
*
*/
static void
{
struct sata_apt_sense_data *apt_sd =
struct scsi_ata_status_ret_sense_descr *ata_ret_desc =
&(apt_sd->apt_sd_sense);
int extend = 0;
extend = 1;
/* update the residual count */
sizeof (struct sata_apt_sense_data);
/*
* Fill in the Descriptor sense header
*/
sizeof (struct scsi_ata_status_ret_sense_descr);
/*
* Fill in the ATA Return descriptor sense data
*/
if (extend == 1) {
} else {
ata_ret_desc->ars_extend = 0;
ata_ret_desc->ars_lba_low_msb = 0;
ata_ret_desc->ars_lba_mid_msb = 0;
ata_ret_desc->ars_lba_high_msb = 0;
}
}
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:
/* 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;
}
}
}
/*
* 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
*/
if (SATA_READ_AHEAD_SUPPORTED(*sata_id) &&
}
if (SATA_WRITE_CACHE_SUPPORTED(*sata_id) &&
} else {
/* Changeable parameters */
if (SATA_READ_AHEAD_SUPPORTED(*sata_id)) {
}
}
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_page));
}
static int
{
struct mode_acoustic_management *page =
(struct mode_acoustic_management *)buf;
/*
*/
switch (pcntrl) {
case P_CNTRL_DEFAULT:
/* default paramters not supported */
return (0);
case P_CNTRL_CURRENT:
case P_CNTRL_SAVED:
/* Saved and current are supported and are identical */
/* Word 83 indicates if feature is supported */
/* If feature is not supported */
} else {
!= 0);
/* Word 94 inidicates the value */
#ifdef _LITTLE_ENDIAN
#else
#endif
}
break;
case P_CNTRL_CHANGEABLE:
/* Word 83 indicates if the feature is supported */
}
break;
}
return (PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT +
sizeof (struct mode_page));
}
/*
* Build Mode sense power condition page.
*/
static int
{
/*
* power condition page length was 0x0a
*/
if (pcntrl == P_CNTRL_DEFAULT) {
/* default paramters not supported */
return (0);
}
sizeof (uchar_t) * 4);
}
return (sizeof (struct mode_info_power_cond));
}
/*
* 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, the command will be terminated with STATUS_CHECK.
* This function fails only if the SET FEATURE command sent to
* the device fails. The page format is not verified, 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);
}
/* Current setting of Read Ahead (and Read Cache) */
if (SATA_READ_AHEAD_ENABLED(*sata_id))
dra = 0; /* 0 == not disabled */
else
dra = 1;
/* Current setting of Write Cache */
if (SATA_WRITE_CACHE_ENABLED(*sata_id))
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;
if (SATA_READ_AHEAD_SUPPORTED(*sata_id)) {
/* 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 */
}
} else {
sense->es_add_code =
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
}
/* Note that the packet is not removed, so it could be re-used */
if (SATA_WRITE_CACHE_SUPPORTED(*sata_id)) {
/* 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;
}
} else {
sense->es_add_code =
*rval = TRAN_ACCEPT;
return (SATA_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".
* This page applies to informational exceptions that report
* additional sense codes with the ADDITIONAL SENSE CODE field set to 5Dh
* (e.g.,FAILURE PREDICTION THRESHOLD EXCEEDED) or 0Bh (e.g., WARNING_).
* Informational exception conditions occur as the result of background scan
* errors, background self-test errors, or vendor specific events within a
* logical unit. An informational exception condition may occur asynchronous
* to any commands.
*
* Returns: 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.
*
* Cannot be called in the interrupt context.
*/
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);
}
/*
* Process mode select acoustic management control page 0x30
*
*
* This function has to be called with a port mutex held.
*
* Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise.
*
* Cannot be called in the interrupt context.
*/
int
{
struct scsi_extended_sense *sense;
*dmod = 0;
/* If parmlen is too short or the feature is not supported, drop it */
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
sizeof (struct mode_page);
/*
* We can enable and disable acoustice management and
* set the acoustic management level.
*/
/*
* 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;
if (page->acoustic_manag_enable) {
} else { /* disabling acoustic management */
}
/* Transfer command to HBA */
/*
* Pkt not accepted for execution.
*/
return (SATA_FAILURE);
/* Now process return */
return (SATA_FAILURE);
}
*dmod = 1;
return (SATA_SUCCESS);
}
/*
* Process mode select power condition page 0x1a
*
* This function has to be called with a port mutex held.
*
* Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise.
*
* Cannot be called in the interrupt context.
*/
int
{
struct scsi_extended_sense *sense;
int i, len;
*dmod = 0;
len = sizeof (struct mode_info_power_cond);
/* If parmlen is too short or the feature is not supported, drop it */
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
/*
* Set-up Internal STANDBY command(s)
*/
goto out;
scmd->satacmd_addr_type = 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;
/* Transfer command to HBA */
return (SATA_FAILURE);
} else {
if ((scmd->satacmd_error_reg != 0) ||
return (SATA_FAILURE);
}
}
for (i = 0; i < 4; i++) {
}
out:
*dmod = 1;
return (SATA_SUCCESS);
}
/*
* sata_build_lsense_page0() is used to create the
* SCSI LOG SENSE page 0 (supported log pages)
*
* Currently supported pages are 0, 0x10, 0x2f, 0x30 and 0x0e
* (supported log pages, self-test results, informational exceptions
* Sun vendor specific ATA SMART data, and start stop cycle counter).
*
* 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.
*
* Note: Self test and SMART data is accessible in device log pages.
* of data can be transferred by a single command), or by the General Purpose
* Logging commands (GPL) READ LOG EXT and WRITE LOG EXT (up to 65,535 sectors
* - approximately 33MB - can be transferred by a single command.
* The SCT Command response (either error or command) is the same for both
* the SMART and GPL methods of issuing commands.
* This function uses READ LOG EXT command when drive supports LBA48, and
* SMART READ command otherwise.
*
* Since above commands are executed in a synchronous mode, this function
* should not be called in an interrupt context.
*/
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, start_index;
struct smart_ext_selftest_log_entry *entry;
static const struct smart_ext_selftest_log_entry empty =
{0};
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) {
/* If this is an unused entry, we are done */
/* Broken firmware on some disks */
if (index + 1 ==
--entry;
--index;
sizeof (empty)) == 0)
goto out;
} else
goto out;
}
if (only_one_block &&
start_index == index)
goto out;
lpp->param_code[0] = 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 */
if (status != 0) {
[5];
[4];
[3];
[2];
[1];
[0];
} else { /* No bad block address */
}
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][0];
- 1][1] << 8;
--block_num;
(block_num == 0);
}
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;
static const struct smart_selftest_log_entry empty =
{ 0 };
if (index == 0)
goto done;
--index; /* Correct for 0 origin */
entry = &selftest_log->
for (count = 1;
++count) {
goto done;
lpp->param_code[0] = 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 */
if (status != 0) {
} else { /* No block address */
}
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 0x2f (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.
*
* Because it invokes function(s) that send synchronously executed command
* to the HBA, it cannot be called in the interrupt context.
*/
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 */
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));
}
/*
* sata_build_lsense_page_0e() is used to create the
* SCSI LOG SENSE page 0e (start-stop cycle counter page)
*
* Date of Manufacture (0x0001)
* YEAR = "0000"
* WEEK = "00"
* Accounting Date (0x0002)
* 6 ASCII space character(20h)
* Specified cycle count over device lifetime
* VALUE - THRESH - the delta between max and min;
* Accumulated start-stop cycles
* VALUE - WORST - the accumulated cycles;
*
*
* 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
{
struct start_stop_cycle_counter_log *log_page;
/* Now do the SMART READ DATA */
(struct smart_data *)smart_data);
if (rval == -1)
return (0);
if (id != SMART_START_STOP_COUNT_ID)
continue;
else {
break;
}
}
if (id != SMART_START_STOP_COUNT_ID)
return (0);
for (i = 0; i < 4; i++) {
if (i < 2)
}
for (i = 0; i < 4; i++) {
if (i < 2)
}
/* VALUE - THRESH - the delta between max and min */
/* WORST - THRESH - the distance from 'now' to min */
for (i = 0; i < 4; i++) {
log_page->cycle_lifetime[i] =
log_page->cycle_accumulated[i] =
}
return (sizeof (struct start_stop_cycle_counter_log));
}
/*
* This function was used for build a ATA read verify sector command
*/
static void
{
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
}
/*
* This function was used for building an ATA
* command, and only command register need to
* be defined, other register will be zero or na.
*/
static void
{
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;
}
/*
* This function was used for changing the standby
* timer format from SCSI to ATA.
*/
static uint8_t
{
for (i = 0; i < 4; i++) {
}
if (count == 0)
return (0);
ata_count = 0xfc;
ata_count = 0xff;
ata_count = 0xf1;
else
ata_count = 0xfd;
return (ata_count);
}
/* ************************** ATAPI-SPECIFIC FUNCTIONS ********************** */
/*
* Start command for ATAPI device.
* This function processes scsi_pkt requests.
* Most commands are packet without any translation into Packet Command.
* Some may be trapped and executed as SATA commands (not clear which one).
*
* Returns TRAN_ACCEPT if command is accepted for execution (or completed
* execution).
* Returns other TRAN_XXXX codes if command is not accepted or completed
* (see return values for sata_hba_start()).
*
* Note:
* Inquiry cdb format differs between transport version 2 and 3.
* However, the transport version 3 devices that were checked did not adhere
* to the specification (ignored MSB of the allocation length). Therefore,
* the transport version is not checked, but Inquiry allocation length is
* truncated to 255 bytes if the original allocation length set-up by the
* target driver is greater than 255 bytes.
*/
static int
{
int cdblen;
int synch;
return (rval);
}
/*
* ATAPI device executes some ATA commands in addition to those
* commands sent via PACKET command. These ATA commands may be
* executed by the regular SATA translation functions. None needs
* to be captured now.
*
* Commands sent via PACKET command include:
* SSC command set for ATAPI TAPE device
* SBC command set for ATAPI disk device
*
*/
/* Check the size of cdb */
case CDB_GROUPID_3: /* Reserved, per SPC-4 */
/*
* opcodes 0x7e and 0x7f identify variable-length CDBs and
* therefore require special handling. Return failure, for now.
*/
return (TRAN_BADPKT);
case CDB_GROUPID_6: /* Vendor-specific, per SPC-4 */
case CDB_GROUPID_7: /* Vendor-specific, per SPC-4 */
/* obtain length from the scsi_pkt */
break;
default:
/* CDB's length is statically known, per SPC-4 */
break;
}
"sata: invalid ATAPI cdb length %d",
cdblen);
return (TRAN_BADPKT);
}
/*
* 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;
}
/*
* scmd->satacmd_flags.sata_data_direction default -
* SATA_DIR_NODATA_XFER - is set by
* sata_txlt_generic_pkt_info().
*/
if (scmd->satacmd_bp) {
} else {
}
}
/*
* Set up ATAPI packet command.
*/
/* Copy cdb into sata_cmd */
/* See note in the command header */
}
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
"%02x %02x %02x %02x %02x %02x %02x %02x "
"%2x %02x %02x %02x %02x %02x %02x %02x",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
}
#endif
/*
* Preset request sense data to NO SENSE.
* If there is no way to get error information via Request Sense,
* the packet request sense data would not have to be modified by HBA,
* but it could be returned as is.
*/
/* 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, use the same framework callback to check
* an execution status.
*/
if (synch) {
"synchronous execution status %x\n",
}
return (TRAN_ACCEPT);
}
/*
* ATAPI Packet command completion.
*
* Failure of the command passed via Packet command are considered device
* error. SATA HBA driver would have to retrieve error data (via Request
* Sense command delivered via error retrieval sata packet) and copy it
* to satacmd_rqsense array. From there, it is moved into scsi pkt sense data.
*/
static void
{
struct scsi_extended_sense *sense;
int rval;
#ifdef SATA_DEBUG
#endif
/* Normal completion */
/* Temporary buffer was used */
rval = ddi_dma_sync(
spx->txlt_buf_dma_handle, 0, 0,
}
}
} else {
/*
* Something went wrong - analyze return
*/
/*
* pkt_reason should be CMD_CMPLT for DEVICE ERROR.
* Under this condition ERR bit is set for ATA command,
* and CHK bit set for ATAPI command.
*
* Please check st_intr & sdintr about how pkt_reason
* is used.
*/
/*
* We may not have ARQ data if there was a double
* error. But sense data in sata packet was pre-set
* with NO SENSE so it is valid even if HBA could
* not retrieve a real sense data.
* Just copy this sense data into scsi pkt sense area.
*/
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_SCSI_IF) {
"sata_txlt_atapi_completion: %02x\n"
"RQSENSE: %02x %02x %02x %02x %02x %02x "
" %02x %02x %02x %02x %02x %02x "
" %02x %02x %02x %02x %02x %02x\n",
}
#endif
} else {
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
/*
* We have no device data.
*/
break;
case SATA_PKT_TIMEOUT:
/*
* Need to check if HARDWARE_ERROR/
* TIMEOUT_ON_LOGICAL_UNIT 4/3E/2 would be more
* appropriate.
*/
break;
case SATA_PKT_ABORTED:
/* Should we set key COMMAND_ABPRTED? */
break;
case SATA_PKT_RESET:
/*
* May be we should set Unit Attention /
* Reset. Perhaps the same should be
* returned for disks....
*/
break;
default:
"sata_txlt_atapi_completion: "
"invalid packet completion reason"));
break;
}
}
}
SATAATAPITRACE(spx, 0);
/* scsi callback required */
}
}
/*
* Set up error retrieval sata command for ATAPI Packet Command error data
* recovery.
*
* Returns SATA_SUCCESS when data buffer is allocated and packet set-up,
* returns SATA_FAILURE otherwise.
*/
static int
{
/*
* Allocate dma-able buffer error data.
* Buffer allocation will take care of buffer alignment and other DMA
* attributes.
*/
"sata_get_err_retrieval_pkt: "
"cannot allocate buffer for error data", NULL);
return (SATA_FAILURE);
}
/* Operation modes are up to the caller */
/* Synchronous mode, no callback - may be changed by the caller */
/*
* Set-up acdb. Request Sense CDB (packet command content) is
* not in DMA-able buffer. Its handling is HBA-specific (how
* it is transfered into packet FIS).
*/
/* Following zeroing of pad bytes may not be necessary */
/*
* Set-up pointer to the buffer handle, so HBA can sync buffer
* before accessing it. Handle is in usual place in translate struct.
*/
/*
* Preset request sense data to NO SENSE.
* Here it is redundant, only for a symetry with scsi-originated
* packets. It should not be used for anything but debugging.
*/
return (SATA_SUCCESS);
}
/*
* Set-up ATAPI packet command.
* Data transfer direction has to be set-up in sata_cmd structure prior to
* calling this function.
*
* Returns void
*/
static void
{
/*
* We want all data to be transfered via DMA.
* But specify it only if drive supports DMA and DMA mode is
* selected - some drives are sensitive about it.
* Hopefully it wil work for all drives....
*/
/*
* Features register requires special care for devices that use
* Serial ATA bridge - they need an explicit specification of
* the data transfer direction for Packet DMA commands.
* Setting this bit is harmless if DMA is not used.
*
* spec they follow.
* We are arbitrarily following the latest SerialATA 2.6 spec,
*/
/*
* Specification of major version is valid and version 7
* is supported. It does automatically imply that all
* spec features are supported. For now, we assume that
*/
SATA_ATAPI_ID_DMADIR_REQ) != 0) {
}
}
}
#ifdef SATA_DEBUG
/* Display 18 bytes of Inquiry data */
static void
{
uint8_t *p;
"%02x %02x %02x %02x",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
"%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
"%c %c %c %c %c %c %c %c",
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
p[0], p[1], p[2], p[3]);
p[0], p[1], p[2], p[3]);
}
static void
{
return;
if (count != 0) {
/* saving cdb */
} else {
if (++sata_atapi_trace_index >= 64)
}
}
#endif
/*
* Fetch inquiry data from ATAPI device
* Returns SATA_SUCCESS if operation was successful, SATA_FAILURE otherwise.
*
* Note:
* inqb pointer does not point to a DMA-able buffer. It is a local buffer
* where the caller expects to see the inquiry data.
*
*/
static int
{
int rval;
#ifdef SATA_DEBUG
char msg_buf[MAXPATHLEN];
#endif
return (SATA_FAILURE);
}
/* address is needed now */
/* scsi_inquiry size buffer */
"sata_get_atapi_inquiry_data: "
"cannot allocate data buffer"));
return (SATA_FAILURE);
}
/* Use synchronous mode */
/* Issue inquiry command - 6 bytes cdb, data transfer, read */
/* we have to be carefull about the disapearing device */
rval = SATA_FAILURE;
goto cleanup;
}
/*
* Set-up acdb. This works for atapi transport version 2 and later.
*/
/* Transfer command to HBA */
/* Pkt not accepted for execution */
"sata_get_atapi_inquiry_data: "
"Packet not accepted for execution - ret: %02x", rval);
rval = SATA_FAILURE;
goto cleanup;
}
"sata_get_atapi_inquiry_data: "
"Packet completed successfully - ret: %02x", rval);
/*
* Sync buffer. Handle is in usual place in translate
* struct.
*/
}
rval = SATA_FAILURE;
} else {
/*
* Normal completion - copy data into caller's buffer
*/
sizeof (struct scsi_inquiry));
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
}
#endif
rval = SATA_SUCCESS;
}
} else {
/*
* Something went wrong - analyze return - check rqsense data
*/
rval = SATA_FAILURE;
/*
* ARQ data hopefull show something other than NO SENSE
*/
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
msg_buf[0] = '\0';
"ATAPI packet completion reason: %02x\n"
"RQSENSE: %02x %02x %02x %02x %02x %02x\n"
" %02x %02x %02x %02x %02x %02x\n"
" %02x %02x %02x %02x %02x %02x",
"%s", msg_buf);
}
#endif
} else {
switch (spkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
"sata_get_atapi_inquiry_data: "
"packet reason: port error", NULL);
break;
case SATA_PKT_TIMEOUT:
"sata_get_atapi_inquiry_data: "
"packet reason: timeout", NULL);
break;
case SATA_PKT_ABORTED:
"sata_get_atapi_inquiry_data: "
"packet reason: aborted", NULL);
break;
case SATA_PKT_RESET:
"sata_get_atapi_inquiry_data: "
"packet reason: reset\n", NULL);
break;
default:
"sata_get_atapi_inquiry_data: "
"invalid packet reason: %02x\n",
break;
}
}
}
return (rval);
}
#if 0
#ifdef SATA_DEBUG
/*
* Test ATAPI packet command.
* Single threaded test: send packet command in synch mode, process completion
*
*/
static void
{
int rval;
"sata_test_atapi_packet_command: "
"no device info for cport %d",
return;
}
return;
}
/* address is needed now */
/* 1024k buffer */
"sata_test_atapi_packet_command: "
"cannot allocate data buffer");
return;
}
/* Use synchronous mode */
/* Synchronous mode, no callback - may be changed by the caller */
/* Issue inquiry command - 6 bytes cdb, data transfer, read */
/* Set-up acdb. */
/* Transfer command to HBA */
/* Pkt not accepted for execution */
"sata_test_atapi_packet_command: "
"Packet not accepted for execution - ret: %02x", rval);
goto cleanup;
}
/*
* Sync buffer. Handle is in usual place in translate struct.
*/
}
"sata_test_atapi_packet_command: "
"Packet completed successfully");
/*
* Normal completion - show inquiry data
*/
} else {
/*
* Something went wrong - analyze return - check rqsense data
*/
/*
* ARQ data hopefull show something other than NO SENSE
*/
"ATAPI packet completion reason: %02x\n"
"RQSENSE: %02x %02x %02x %02x %02x %02x "
" %02x %02x %02x %02x %02x %02x "
" %02x %02x %02x %02x %02x %02x\n",
} else {
switch (spkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
"sata_test_atapi_packet_command: "
"packet reason: port error\n");
break;
case SATA_PKT_TIMEOUT:
"sata_test_atapi_packet_command: "
"packet reason: timeout\n");
break;
case SATA_PKT_ABORTED:
"sata_test_atapi_packet_command: "
"packet reason: aborted\n");
break;
case SATA_PKT_RESET:
"sata_test_atapi_packet_command: "
"packet reason: reset\n");
break;
default:
"sata_test_atapi_packet_command: "
"invalid packet reason: %02x\n",
break;
}
}
}
}
#endif /* SATA_DEBUG */
#endif /* 1 */
/* ************************** LOCAL HELPER 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_TRAN_HBA_REV is the current (highest) revision number
* of the SATA interface.
*/
"sata: invalid sata_hba_tran version %d for driver %s",
return (SATA_FAILURE);
}
"sata: inconsistent sata_tran_hba_dip "
return (SATA_FAILURE);
}
NULL) {
"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.
* 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.
*
* 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 than this thread.
* Determines if port is active and what type of the device is attached
* (if any). Allocates necessary structures for each port.
*
* An AP (Attachement Point) node is created for each SATA device port even
* when there is no device attached.
*/
static void
{
int ncport;
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
*/
ncport, 0, SATA_ADDR_CPORT);
DDI_SUCCESS) {
"cannot create SATA attachment point for port %d",
ncport);
}
/* Probe port */
start_time = ddi_get_lbolt();
(dip, &sata_device);
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
continue;
}
continue;
}
/*
* There is some device attached.
* Allocate device info structure
*/
kmem_zalloc(sizeof (sata_drive_info_t),
KM_SLEEP);
}
SATA_SUCCESS) {
/*
* Plugged device was not correctly identified.
* Retry, within a SATA_DEV_IDENTIFY_TIMEOUT
*/
cur_time = ddi_get_lbolt();
if ((cur_time - start_time) <
/* sleep for a while */
goto reprobe_cport;
}
}
} else { /* SATA_DTYPE_PMULT */
/* Allocate sata_pmult_info and sata_pmport_info */
continue;
/* Log the information of the port multiplier */
/* Probe its pmports */
}
}
}
/*
* Probe all device ports behind a port multiplier.
*
* PMult-related structure should be allocated before by sata_alloc_pmult().
*
* NOTE1: Only called from sata_probe_ports()
* NOTE2: No mutex should be hold.
*/
static void
{
int npmport;
int rval;
/* Probe Port Multiplier ports */
start_time = ddi_get_lbolt();
/* Let HBA driver probe it. */
(dip, &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
*/
}
if (rval != SATA_SUCCESS) {
/*
* Plugged device was not correctly identified.
* Retry, within the SATA_DEV_IDENTIFY_TIMEOUT
*/
cur_time = ddi_get_lbolt();
/* sleep for a while */
goto reprobe_pmport;
}
}
}
}
/*
* Add SATA device for specified HBA instance & port (SCSI target
* device nodes).
* This function is called (indirectly) only from sata_hba_attach().
* A target node is created when there is a supported type device attached,
* but may be removed if it cannot be put online.
*
* This function cannot be called from an interrupt context.
*
*
* device identification failed - adding a device could be retried.
*
*/
static int
{
int rval;
/*
* 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.
*/
case SATA_ADDR_CPORT:
/*
* Add a non-port-multiplier device at controller port.
*/
if (rval != SATA_SUCCESS ||
return (SATA_FAILURE);
/*
* Could not determine device type or
* a device is not supported.
* Degrade this device to unknown.
*/
return (SATA_SUCCESS);
}
/*
* Initialize device to the desired state. Even if it
* fails, the device will still attach but syslog
* will show the warning.
*/
/* Retry */
if (rval == SATA_RETRY)
"SATA device at port %d - "
"default device features could not be set."
" Device may not operate as expected.",
cport);
}
/*
* Attaching target node failed.
* We retain sata_drive_info structure...
*/
return (SATA_SUCCESS);
}
break;
case SATA_ADDR_PMPORT:
/* It must be a Port Multiplier at the controller port */
if (rval != SATA_SUCCESS ||
return (SATA_FAILURE);
}
/*
* Could not determine device type.
* Degrade this device to unknown.
*/
return (SATA_SUCCESS);
}
/*
* Initialize device to the desired state.
* Even if it fails, the device will still
* attach but syslog will show the warning.
*/
/* Retry */
if (rval == SATA_RETRY)
"SATA device at port %d:%d - "
"default device features could not be set."
" Device may not operate as expected.",
}
/*
* Attaching target node failed.
* We retain sata_drive_info structure...
*/
return (SATA_SUCCESS);
}
break;
default:
return (SATA_FAILURE);
}
return (SATA_SUCCESS);
}
/*
* Clean up target node at specific address.
*
* NOTE: No Mutex should be hold.
*/
static int
{
if (qual == SATA_ADDR_DCPORT) {
"sata_hba_ioctl: disconnect device at port %d", cport));
} else {
"sata_hba_ioctl: disconnect device at port %d:%d",
}
/* We are addressing attached device, not a port */
NDI_DEVI_REMOVE) != NDI_SUCCESS) {
/*
* Problem :
* The target node remained attached.
* This happens when the device file was open
* or a node was waiting for resources.
* Cannot do anything about it.
*/
if (qual == SATA_ADDR_DCPORT) {
"sata_hba_ioctl: disconnect: could "
"not unconfigure device before "
"disconnecting the SATA port %d",
cport));
} else {
"sata_hba_ioctl: disconnect: could "
"not unconfigure device before "
"disconnecting the SATA port %d:%d",
}
/*
* Set DEVICE REMOVED state in the target
* node. It will prevent access to the device
* even when a new device is attached, until
* the old target node is released, removed and
* recreated for a new device.
*/
/*
* Instruct event daemon to try the target
* node cleanup later.
*/
}
return (SATA_SUCCESS);
}
/*
* 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 or get scsi inquiry data, expected by
* scsi_hba_nodename_compatible_get()
* SATA hard disks get Identify Data translated into Inguiry Data.
* ATAPI devices respond directly to Inquiry request.
*/
} else { /* Assume supported ATAPI device */
&inq) == SATA_FAILURE)
return (NULL);
/*
* Save supported ATAPI transport version
*/
}
/* 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;
}
/*
* Add "variant" property
*/
"variant", "atapi");
if (rval != DDI_PROP_SUCCESS) {
"sata_create_target_node: variant atapi "
"property could not be created: %d", rval));
goto fail;
}
}
/* decorate the node with compatible */
"sata_create_target_node: FAIL compatible props cdip 0x%p",
(void *)cdip));
goto fail;
}
/*
* Add "sata-phy" property
*/
"sata_create_target_node: failed to create "
"\"sata-phy\" property: port %d",
}
}
/*
* 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 SATA device at port %d",
return (NULL);
}
/*
* Remove a target node.
*/
static void
{
/* Remove target node */
/*
* Target node exists. Unconfigure device
* then remove the target node (one ndi
* operation).
*/
/*
* PROBLEM - no device, but target node remained. This
* happens when the file was open or node was waiting
* for resources.
*/
"sata_remove_target_node: "
"Failed to remove target node for "
"detached SATA device."));
/*
* Set target node state to DEVI_DEVICE_REMOVED. But
* re-check first that the node still exists.
*/
/*
* Instruct event daemon to retry the cleanup
* later.
*/
}
}
if (qual == SATA_ADDR_CPORT)
"SATA device detached at port %d", cport);
else
"SATA device detached at port %d:%d",
}
#ifdef SATA_DEBUG
else {
if (qual == SATA_ADDR_CPORT)
"target node not found at port %d", cport);
else
"target node not found at port %d:%d",
}
#endif
}
/*
* Re-probe sata port, check for a device and attach 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.
*
* flag arg specifies that the function should try multiple times to identify
* device type and to initialize it, or it should return immediately on failure.
* SATA_DEV_IDENTIFY_RETRY - retry
* SATA_DEV_IDENTIFY_NORETRY - no retry
*
* SATA_FAILURE is returned if one of the operations failed.
*
* This function cannot be called in interrupt context - it may sleep.
*
* Note: Port multiplier is supported.
*/
static int
int flag)
{
int prev_device_type = SATA_DTYPE_NONE;
int prev_device_settings = 0;
int prev_device_state = 0;
int rval_probe, rval_init;
/*
* If target is pmport, sata_reprobe_pmport() will handle it.
*/
/* We only care about host sata cport for now */
/*
* If a port multiplier was previously attached (we have no idea it
* still there or not), sata_reprobe_pmult() will handle it.
*/
/* Store sata_drive_info when a non-pmult device was attached. */
/*
* We are re-probing port with a previously attached device.
* Save previous device type and settings.
*/
}
if (flag == SATA_DEV_IDENTIFY_RETRY) {
start_time = ddi_get_lbolt();
}
/* probe port */
if (rval_probe != SATA_SUCCESS) {
"SATA port %d probing 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
*/
/*
* There is no device, so remove device info structure,
* if necessary.
*/
/* Device change: Drive -> None */
"SATA device detached "
}
return (SATA_SUCCESS);
}
/* Device (may) change: Drive -> Drive */
/*
* There is some device attached, but there is
* no sata_drive_info structure - allocate one
*/
sizeof (sata_drive_info_t), KM_SLEEP);
/*
* Recheck, that the port state did not change when we
* released mutex.
*/
} else {
/*
* Port is not in ready state, we
* cannot attach a device.
*/
return (SATA_SUCCESS);
}
/*
* Since we are adding device, presumably new one,
* indicate that it should be initalized,
* as well as some internal framework states).
*/
}
} else {
/* Device change: Drive -> PMult */
"SATA device detached "
}
"SATA port multiplier detected at port %d",
return (SATA_FAILURE);
/*
* Mark all the port multiplier port behind the port
* multiplier behind with link events, so that the sata daemon
* will update their status.
*/
return (SATA_SUCCESS);
}
/*
* Figure out what kind of device we are really
* dealing with. Failure of identifying device does not fail this
* function.
*/
if (rval_probe == SATA_SUCCESS) {
/*
* If we are dealing with the same type of a device as before,
* restore its settings flags.
*/
/* Set initial device features, if necessary */
if (init_device == B_TRUE) {
sdinfo);
}
if (rval_init == SATA_SUCCESS)
return (rval_init);
/* else we will retry if retry was asked for */
} else {
/*
* If there was some device info before we probe the device,
* restore previous device setting, so we can retry from scratch
* later. Providing, of course, that device has not disapear
* during probing process.
*/
}
} else {
/* device is gone */
return (SATA_SUCCESS);
}
}
if (retry) {
/*
* A device was not successfully identified or initialized.
* Track retry time for device identification.
*/
if ((cur_time - start_time) <
/* sleep for a while */
goto retry_probe;
}
/* else no more retries */
if (rval_init == SATA_RETRY) {
/*
* Setting drive features have failed, but
* because the drive is still accessible,
* keep it and emit a warning message.
*/
"SATA device at port %d - desired "
"drive features could not be set. "
"Device may not operate as expected.",
} else {
}
}
}
return (SATA_SUCCESS);
}
/*
* Reprobe a controller port that connected to a port multiplier.
*
* NOTE: No Mutex should be hold.
*/
static int
int flag)
{
int rval_probe;
/* probe port */
if (rval_probe != SATA_SUCCESS) {
"SATA port %d probing failed", cport));
"SATA port multiplier detached at port %d", cport);
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 &
"SATA port multiplier detached at port %d", cport);
return (SATA_SUCCESS);
}
/*
* Device changed: PMult -> Non-PMult
*
* This situation is uncommon, most possibly being caused by errors
* after which the port multiplier is not correct initialized and
* recognized. In that case the new device will be marked as unknown
* and will not be automatically probed in this routine. Instead
* system administrator could manually restart it via cfgadm(1M).
*/
"SATA port multiplier detached at port %d", cport);
return (SATA_FAILURE);
}
/*
* Now we know it is a port multiplier. However, if this is not the
* previously attached port multiplier - they may have different
* pmport numbers - we need to re-allocate data structures for every
* pmport and drive.
*
* Port multipliers of the same model have identical values in these
* registers, so it is still necessary to update the information of
* all drives attached to the previous port multiplier afterwards.
*/
/* Device changed: PMult -> another PMult */
return (SATA_FAILURE);
"SATA port multiplier [changed] at port %d", cport);
"SATA port multiplier detected at port %d", cport);
/*
* Mark all the port multiplier port behind the port
* multiplier behind with link events, so that the sata daemon
* will update their status.
*/
return (SATA_SUCCESS);
}
/*
* Re-probe a port multiplier port, check for a device and attach info
* structures when necessary. Identify Device data is fetched, if possible.
* Assumption: sata address is already validated as port multiplier port.
* SATA_SUCCESS is returned if port is re-probed sucessfully, regardless of
* the presence of a device and its type.
*
* flag arg specifies that the function should try multiple times to identify
* device type and to initialize it, or it should return immediately on failure.
* SATA_DEV_IDENTIFY_RETRY - retry
* SATA_DEV_IDENTIFY_NORETRY - no retry
*
* SATA_FAILURE is returned if one of the operations failed.
*
* This function cannot be called in interrupt context - it may sleep.
*
* NOTE: Should be only called by sata_probe_port() in case target port is a
* port multiplier port.
* NOTE: No Mutex should be hold.
*/
static int
int flag)
{
int prev_device_type = SATA_DTYPE_NONE;
int prev_device_settings = 0;
int prev_device_state = 0;
int rval;
/*
* We are re-probing port with a previously attached device.
* Save previous device type and settings.
*/
}
start_time = ddi_get_lbolt();
/* check parent status */
if ((cportinfo->cport_state &
return (SATA_FAILURE);
}
/* probe port */
/* might need retry because we cannot touch registers. */
if (rval == SATA_FAILURE) {
"SATA port %d:%d probing failed",
return (SATA_FAILURE);
} else if (rval == SATA_RETRY) {
"SATA port %d:%d probing failed, retrying...",
/*
* A device was not successfully identified or initialized.
* Track retry time for device identification.
*/
if ((cur_time - start_time) <
/* sleep for a while */
goto retry_probe_pmport;
} else {
return (SATA_SUCCESS);
}
}
/*
* Sanity check - Controller port is active? Is the link active?
* Is it still a port multiplier?
*/
if ((cportinfo->cport_state &
/*
* device. Free info structure.
*/
return (SATA_FAILURE);
}
/* SATA_SUCCESS NOW */
/*
* update sata port state and set device type
*/
/*
* Sanity check - Port is active? Is the link active?
* Is there any device attached?
*/
if ((pmportinfo->pmport_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.
*/
"SATA device detached from port %d:%d",
}
return (SATA_SUCCESS);
}
/* this should not be a pmult */
/*
* There is some device attached, but there is
* no sata_drive_info structure - allocate one
*/
KM_SLEEP);
/*
* Recheck, that the port state did not change when we
* released mutex.
*/
} else {
/*
* Port is not in ready state, we
* cannot attach a device.
*/
return (SATA_SUCCESS);
}
/*
* Since we are adding device, presumably new one,
* indicate that it should be initalized,
* as well as some internal framework states).
*/
}
/*
* Figure out what kind of device we are really
* dealing with.
*/
if (rval == SATA_SUCCESS) {
/*
* If we are dealing with the same type of a device as before,
* restore its settings flags.
*/
/* Set initial device features, if necessary */
if (init_device == B_TRUE) {
}
if (rval == SATA_SUCCESS)
return (rval);
} else {
/*
* If there was some device info before we probe the device,
* restore previous device setting, so we can retry from scratch
* later. Providing, of course, that device has not disappeared
* during probing process.
*/
}
} else {
/* device is gone */
return (SATA_SUCCESS);
}
}
if (flag == SATA_DEV_IDENTIFY_RETRY) {
/*
* A device was not successfully identified or initialized.
* Track retry time for device identification.
*/
if ((cur_time - start_time) <
/* sleep for a while */
goto retry_probe_pmport;
} else {
}
}
return (SATA_SUCCESS);
}
/*
* Allocated related structure for a port multiplier and its device ports
*
* Port multiplier should be ready and probed, and related information like
* the number of the device ports should be store in sata_device_t.
*
* NOTE: No Mutex should be hold.
*/
static int
{
char name[16];
int rval;
int npmport;
/* This function might be called while a port-mult is hot-plugged. */
/* dev_type's not updated when get called from sata_reprobe_port() */
/* Create a pmult_info structure */
}
/*
* Probe the port multiplier with qualifier SATA_ADDR_PMULT_SPEC,
* The HBA driver should initialize and register the port multiplier,
* sata_register_pmult() will fill following fields,
* + sata_pmult_info.pmult_gscr
* + sata_pmult_info.pmult_num_dev_ports
*/
if (rval != SATA_SUCCESS ||
"sata_alloc_pmult: failed to initialize pmult "
"at port %d.", cport)
return (SATA_FAILURE);
}
/* Initialize pmport_info structure */
npmport++) {
/* if everything is allocated, skip */
continue;
/* Create an attachment point */
DDI_NT_SATA_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
"cannot create SATA attachment point for "
}
}
return (SATA_SUCCESS);
}
/*
* Free data structures when a port multiplier is removed.
*
* NOTE: No Mutex should be hold.
*/
static void
{
char name[16];
int npmport;
/* This function might be called while port-mult is hot plugged. */
/* Free pmport_info structure */
npmport++) {
if (pmportinfo == NULL)
continue;
/* Remove attachment point. */
name[0] = '\0';
"Remove attachment point of port %d:%d",
/*
* Rumove target node
*/
NDI_DEVI_REMOVE) != NDI_SUCCESS) {
/*
* Problem :
* The target node remained attached.
* This happens when the device file was open
* or a node was waiting for resources.
* Cannot do anything about it.
*/
"sata_free_pmult: could not unconfigure device "
"before disconnecting the SATA port %d:%d",
/*
* Set DEVICE REMOVED state in the target
* node. It will prevent access to the device
* even when a new device is attached, until
* the old target node is released, removed and
* recreated for a new device.
*/
/*
* Instruct event daemon to try the target
* node cleanup later.
*/
}
/*
* Add here differentiation for device attached or not
*/
"SATA device detached from port %d:%d",
}
}
"SATA port multiplier detached at port %d", cport);
}
/*
* Initialize device
* Specified device is initialized to a default state.
*
* Returns SATA_SUCCESS if all device features are set successfully,
* SATA_RETRY if device is accessible but device features were not set
* successfully, and SATA_FAILURE otherwise.
*/
static int
{
int rval;
/* Determine current data transfer mode */
SATA_VALIDINFO_88) != 0 &&
SATA_MDMA_SEL_MASK) != 0) {
} else
/* DMA supported, not no DMA transfer mode is selected !? */
else
return (rval);
}
/*
* Initialize write cache mode.
*
* The default write cache setting for SATA HDD is provided by sata_write_cache
* determined by sata_atapicdvd_write_cache static variable.
* ATAPI tape devices have write cache default is determined by
* sata_atapitape_write_cache static variable.
* ATAPI disk devices have write cache default is determined by
* sata_atapidisk_write_cache static variable.
* 1 - enable
* 0 - disable
* any other value - current drive setting
*
* tape devices and ATAPI disk devices, the default setting control is provided
* for the maximun flexibility.
*
* In the future, it may be overridden by the
* disk-write-cache-enable property setting, if it is defined.
* Returns SATA_SUCCESS if all device features are set successfully,
* SATA_FAILURE otherwise.
*/
static void
{
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
if (sata_write_cache == 1)
else if (sata_write_cache == 0)
/*
* When sata_write_cache value is not 0 or 1,
* a current setting of the drive's write cache is used.
*/
break;
case SATA_DTYPE_ATAPICD:
if (sata_atapicdvd_write_cache == 1)
else if (sata_atapicdvd_write_cache == 0)
/*
* When sata_atapicdvd_write_cache value is not 0 or 1,
* a current setting of the drive's write cache is used.
*/
break;
case SATA_DTYPE_ATAPITAPE:
if (sata_atapitape_write_cache == 1)
else if (sata_atapitape_write_cache == 0)
/*
* When sata_atapitape_write_cache value is not 0 or 1,
* a current setting of the drive's write cache is used.
*/
break;
case SATA_DTYPE_ATAPIDISK:
if (sata_atapidisk_write_cache == 1)
else if (sata_atapidisk_write_cache == 0)
/*
* When sata_atapidisk_write_cache value is not 0 or 1,
* a current setting of the drive's write cache is used.
*/
break;
}
}
/*
* 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 no valid device is attached,
* returns 2 if address is valid but device type is unknown (not valid device),
* returns -1 if bad address or device is of an unsupported type.
* Upon return sata_device argument is set.
*
* Port multiplier is supported now.
*/
static int
{
goto out;
goto out;
0) {
if (qual == SATA_ADDR_DCPORT) {
goto out;
rval = 2;
goto out;
}
if ((cportinfo->cport_dev_type &
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);
rval = 2;
goto out;
}
pmport) & SATA_VALID_DEV_TYPE) == 0) {
rval = -1;
goto out;
}
} else {
rval = -1;
goto out;
}
goto out;
return (0);
}
out:
if (rval > 0) {
"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 initializes 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;
* SATA_RETRY if device probe can be retried later.
* If a device cannot be identified, sata_device's dev_state and dev_type
* fields are set to unknown.
* There are no retries in this function. Any retries should be managed by
* the caller.
*/
static int
{
int rval;
(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.
*/
if (rval == SATA_RETRY) {
/* We may try to check for ATAPI device */
/*
* HBA supports ATAPI - try to issue Identify Packet
* Device command.
*/
}
}
if (rval == SATA_SUCCESS) {
/*
* Got something responding positively to ATA Identify Device
* or to Identify Packet Device cmd.
* Save last used device type.
*/
/* 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);
}
/*
* It may be SATA_RETRY or SATA_FAILURE return.
* Looks like we cannot determine the device type at this time.
*/
else {
/* SATA_ADDR_DPMPORT */
if ((SATA_PMULT_INFO(sata_hba_inst,
}
}
return (rval);
}
/*
* 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);
/* Port multiplier port not probed yet */
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:
* SATA_SUCCESS if the device was identified as a supported device,
* SATA_RETRY if the device was not identified but could be retried,
* SATA_FAILURE if the device was not identified and identify attempt
* should not be retried.
*/
static int
{
int rval;
/* fetch device identify data */
sdinfo)) != SATA_SUCCESS)
goto fail_unknown;
/* Set the correct device type */
} else if (cfg_word == SATA_CFA_TYPE) {
/* It's a Compact Flash media via CF-to-SATA HDD adapter */
switch (cfg_word & SATA_ATAPI_ID_DEV_TYPE) {
case SATA_ATAPI_CDROM_DEV:
break;
case SATA_ATAPI_SQACC_DEV:
break;
case SATA_ATAPI_DIRACC_DEV:
break;
case SATA_ATAPI_PROC_DEV:
break;
default:
}
} else {
}
if (sdinfo->satadrv_capacity == 0) {
/* Non-LBA disk. Too bad... */
"SATA disk device at port %d does not support LBA",
rval = SATA_FAILURE;
goto fail_unknown;
}
}
#if 0
/* Left for historical reason */
/*
* Some initial version of SATA spec indicated that at least
* UDMA mode 4 has to be supported. It is not metioned in
* SerialATA 2.6, so this restriction is removed.
*/
/* 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));
rval = SATA_FAILURE;
goto fail_unknown;
}
#endif
/*
* For Disk devices, if it doesn't support UDMA mode, we would
* like to return failure directly.
*/
"SATA disk device at port %d does not support UDMA",
rval = SATA_FAILURE;
goto fail_unknown;
}
return (SATA_SUCCESS);
/* Invalidate sata_drive_info ? */
return (rval);
}
/*
*/
static void
{
int valid_version;
char msg_buf[MAXPATHLEN];
int i;
/* Show HBA path */
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
break;
case SATA_DTYPE_ATAPICD:
break;
case SATA_DTYPE_ATAPITAPE:
break;
case SATA_DTYPE_ATAPIDISK:
break;
case SATA_DTYPE_ATAPIPROC:
break;
case SATA_DTYPE_UNKNOWN:
"Unsupported SATA device type (cfg 0x%x) at ",
break;
}
else
} else {
/*
* Some drives do not implement serial number and may
* violate the spec by providing spaces rather than zeros
* in serial number field. Scan the buffer to detect it.
*/
break;
}
} else {
}
}
#ifdef SATA_DEBUG
int i;
for (i = 14; i >= 2; i--) {
valid_version = i;
break;
}
}
}
#endif
/* Log some info */
msg_buf[0] = '\0';
}
if (sdinfo->satadrv_features_support &
(SATA_DEV_F_TCQ | SATA_DEV_F_NCQ)) {
msg_buf[0] = '\0';
"Supported queue depth %d",
if (!(sata_func_enable &
" - queueing disabled globally", MAXPATHLEN);
else if (sdinfo->satadrv_queue_depth >
(int)sdinfo->satadrv_max_queue_depth);
}
}
#ifdef __i386
#else
#endif
}
}
/*
* No Mutex should be hold.
*/
static void
{
char msg_buf[MAXPATHLEN];
return;
}
else
if (gscr64 & (1 << 0))
if ((gscr64 & 0xf) == 0)
}
/*
* 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.
*
* For all devices read ahead and write cache settings are saved, if the
* device supports these features at all.
* For ATAPI devices the Removable Media Status Notification setting is saved.
*/
static void
{
/* Current setting of Read Ahead (and Read Cache) */
else
/* Current setting of Write Cache */
else
}
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 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.
*/
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;
}
/*
* 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.
* To guarantee that there is always a coherent set of cookies to process
* by SATA HBA driver (observing alignment, device granularity, etc.),
* the number of slots for DMA cookies is equal to lesser of a number of
*
* Returns DDI_SUCCESS upon successful operation.
* Return failure code of a failing command or DDI_FAILURE when
* internal cleanup failed.
*/
static int
{
int rval;
int max_sg_len, req_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)
/*
* Check buffer alignment and size against dma attributes
* Consider dma_attr_align only. There may be requests
* with the size lower than device granularity, but they
* is necessary. The dma_attr_minxfer theoretically should
* be considered, but no HBA driver is checking it.
*/
} else { /* Buffer is not aligned */
int (*ddicallback)(caddr_t);
/* Check id sleeping is allowed */
"mis-aligned buffer: addr=0x%p, cnt=%lu",
/*
* CPU will need to access data in the buffer
* (for copying) so map it.
*/
/* Buffer may be padded by ddi_dma_mem_alloc()! */
&spx->txlt_tmp_buf,
&bufsz,
if (rval != DDI_SUCCESS) {
/* DMA mapping failed */
(void) ddi_dma_free_handle(
#ifdef SATA_DEBUG
#endif
"sata_dma_buf_setup: "
"buf dma mem alloc failed %x\n", rval);
return (rval);
}
#ifdef SATA_DEBUG
mbuf_count++;
/*
* This will require special handling, because
* DMA cookies will be based on the temporary
* buffer size, not the original buffer
* b_bcount, so the residue may have to
* be counted differently.
*/
"sata_dma_buf_setup: bp size %x != "
#endif
if (dma_flags & DDI_DMA_WRITE) {
/*
* Write operation - copy data into
* an aligned temporary buffer. Buffer will be
* synced for device by ddi_dma_addr_bind_handle
*/
}
NULL,
}
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);
}
"sata_dma_buf_setup: windows: %d, cookies: %d\n",
spx->txlt_cur_dma_win = 0;
break;
case DDI_DMA_MAPPED:
/* DMA fully mapped */
spx->txlt_cur_dma_win = 0;
"sata_dma_buf_setup: windows: 1 "
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++;
} 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 outstanding */
spx->txlt_curwin_processed_dma_cookies) > 0);
/* The default cookie slot was used in previous run */
spx->txlt_dma_cookie_list_len = 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;
}
/*
* Calculate lesser of number of cookies in this
* DMA window and number of s/g entries.
*/
/* Allocate new dma cookie array if necessary */
if (req_len == 1) {
/* Only one cookie - no need for a list */
} else {
/*
* More than one cookie - try to allocate space.
*/
sizeof (ddi_dma_cookie_t) * req_len,
KM_SLEEP);
"sata_dma_buf_setup: cookie list "
"allocation failed\n", NULL);
/*
* We could not allocate space for
* neccessary number of dma cookies in
* this window, so we fail this request.
* Next invocation would try again to
* allocate space for cookie list.
* Note:Packet residue was not modified.
*/
return (DDI_DMA_NORESOURCES);
} else {
}
}
}
/*
* Fetch DMA cookies into cookie list in sata_pkt_txlate.
* First cookie was already fetched.
*/
(i < spx->txlt_curwin_num_dma_cookies); i++) {
&spx->txlt_dma_cookie_list[i]);
spx->txlt_sata_pkt->
}
} else {
"sata_dma_buf_setup: sliding within DMA window, "
"cur cookie %d, total cookies %d\n",
/*
* Not all cookies from the current dma window were used because
* of s/g limitation.
* There is no need to re-size the list - it was set at
* optimal size, or only default entry is used (s/g = 1).
*/
}
/*
* Since we are processing remaining cookies in a DMA window,
* there may be less of them than the number of entries in the
* current dma cookie list.
*/
/* Fetch the next batch of cookies */
for (i = 0, cur_txfer_len = 0; i < req_len; i++) {
&spx->txlt_dma_cookie_list[i]);
spx->txlt_sata_pkt->
}
}
/* Point sata_cmd to the cookie list */
&spx->txlt_dma_cookie_list[0];
/* Remember number of DMA cookies passed in sata packet */
ASSERT(cur_txfer_len != 0);
else {
/*
* Temporary DMA buffer has been padded by
* ddi_dma_mem_alloc()!
* This requires special handling, because DMA cookies are
* based on the temporary buffer size, not the b_bcount,
* and we have extra bytes to transfer - but the packet
* residue has to stay correct because we will copy only
* the requested number of bytes.
*/
}
return (DDI_SUCCESS);
}
/*
* Common routine for releasing DMA resources
*/
static void
{
/*
* Intermediate DMA buffer was allocated.
* Free allocated buffer and associated access handle.
*/
}
/*
* Free DMA resources - cookies and handles
*/
/* ASSERT(spx->txlt_dma_cookie_list != NULL); */
if (spx->txlt_dma_cookie_list !=
&spx->txlt_dma_cookie) {
sizeof (ddi_dma_cookie_t));
}
}
}
}
/*
* Free DMA resources
* Used by the HBA driver to release DMA resources that it does not use.
*
* Returns Void
*/
void
{
return;
}
/*
* Fetch Device Identify data.
* Send DEVICE IDENTIFY or IDENTIFY PACKET DEVICE (depending on a device type)
* 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:
* SATA_SUCCESS if cmd succeeded
* SATA_RETRY if cmd was rejected and could be retried,
* SATA_FAILURE if cmd failed and should not be retried (port error)
*
* Cannot be called in an interrupt context.
*/
static int
{
int rval;
return (SATA_RETRY); /* may retry later */
}
/* address is needed now */
/*
* Allocate buffer for Identify Data return data
*/
"sata_fetch_device_identify_data: "
"cannot allocate buffer for ID"));
return (SATA_RETRY); /* may retry later */
}
/* 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 */
#ifdef SATA_INJECT_FAULTS
#endif
if (rval == SATA_TRAN_ACCEPTED &&
rval = SATA_RETRY;
goto fail;
}
}
"SATA disk device at port %d - "
"partial Identify Data",
goto fail;
}
/* Update sata_drive_info */
sizeof (sata_id_t));
/*
* Retrieve capacity (disks only) and addressing mode
*/
} else {
/*
* For ATAPI devices one would have to issue
* Get Capacity cmd for media capacity. Not here.
*/
sdinfo->satadrv_capacity = 0;
/*
* Check what cdb length is supported
*/
else
}
/* Setup supported features flags */
/* Check for SATA GEN and NCQ support */
/* SATA compliance */
} else {
}
}
/* Adjust according to controller capabilities */
/* Adjust according to global queue depth limit */
if (sdinfo->satadrv_max_queue_depth == 0)
} else
rval = SATA_SUCCESS;
} else {
/*
* Woops, no Identify Data.
*/
} else if (rval == SATA_TRAN_ACCEPTED) {
else
rval = SATA_FAILURE;
} else {
rval = SATA_FAILURE;
}
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Some devices may not come-up with default DMA mode (UDMA or MWDMA).
* UDMA mode is checked first, followed by MWDMA mode.
* set correctly, so this function is setting it to the highest supported level.
* Older SATA spec required that the device supports at least DMA 4 mode and
* UDMA mode is selected. It is not mentioned in SerialATA 2.6, so this
* restriction has been removed.
*
* Returns SATA_SUCCESS if proper DMA mode is selected or no DMA is supported.
* Returns SATA_FAILURE if proper DMA mode could not be selected.
*
* NOTE: This function should be called only if DMA mode is supported.
*/
static int
{
int i, mode;
int rval = SATA_SUCCESS;
/* Find highest Ultra DMA mode supported */
break;
}
#if 0
/* Left for historical reasons */
/*
* Some initial version of SATA spec indicated that at least
* UDMA mode 4 has to be supported. It is not mentioned in
* SerialATA 2.6, so this restriction is removed.
*/
if (mode < 4)
return (SATA_FAILURE);
#endif
/*
* For disk, we're still going to set DMA mode whatever is
* selected by default
*
* We saw an old maxtor sata drive will select Ultra DMA and
* Multi-Word DMA simultaneouly by default, which is going
* to cause DMA command timed out, so we need to select DMA
* mode even when it's already done by default
*/
/* Find UDMA mode currently selected */
for (i = 6; i >= 0; --i) {
(1 << (i + 8)))
break;
}
if (i >= mode)
/* Nothing to do */
return (SATA_SUCCESS);
}
/* Find highest MultiWord DMA mode supported */
break;
}
/*
* For disk, We're still going to set DMA mode whatever is
* selected by default
*
* We saw an old maxtor sata drive will select Ultra DMA and
* Multi-Word DMA simultaneouly by default, which is going
* to cause DMA command timed out, so we need to select DMA
* mode even when it's already done by default
*/
/* Find highest MultiWord DMA mode selected */
for (i = 2; i >= 0; --i) {
(1 << (i + 8)))
break;
}
if (i >= mode)
/* Nothing to do */
return (SATA_SUCCESS);
}
} else
return (SATA_SUCCESS);
/*
* Set DMA mode via SET FEATURES COMMAND.
* Prepare packet for SET FEATURES COMMAND.
*/
"sata_set_dma_mode: could not set DMA mode %d", mode));
rval = SATA_FAILURE;
goto done;
}
/* 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 */
rval = SATA_FAILURE;
}
done:
/* Free allocated resources */
return (rval);
}
/*
* Set device caching mode.
* One of the following operations should be specified:
* SATAC_SF_ENABLE_READ_AHEAD
* SATAC_SF_DISABLE_READ_AHEAD
* SATAC_SF_ENABLE_WRITE_CACHE
* SATAC_SF_DISABLE_WRITE_CACHE
*
* If operation fails, system log messgage is emitted.
* Returns SATA_SUCCESS when the operation succeeds, SATA_RETRY if
* command was sent but did not succeed, and SATA_FAILURE otherwise.
*/
static int
int cache_op)
{
int rval = SATA_SUCCESS;
int hba_rval;
char *infop;
/* Prepare packet for SET FEATURES COMMAND */
rval = SATA_FAILURE;
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 */
#ifdef SATA_INJECT_FAULTS
#endif
if ((hba_rval != SATA_TRAN_ACCEPTED) ||
/* Pkt execution failed */
switch (cache_op) {
infop = "enabling read ahead failed";
break;
infop = "disabling read ahead failed";
break;
infop = "enabling write cache failed";
break;
infop = "disabling write cache failed";
break;
}
rval = SATA_RETRY;
}
/* Free allocated resources */
return (rval);
}
/*
* state == 0 , disable
* state != 0 , enable
*
* If operation fails, system log messgage is emitted.
* Returns SATA_SUCCESS when the operation succeeds, SATA_FAILURE otherwise.
*/
static int
int state)
{
int rval = SATA_SUCCESS;
char *infop;
/* Prepare packet for SET FEATURES COMMAND */
rval = SATA_FAILURE;
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;
if (state == 0)
else
/* Transfer command to HBA */
if (((*SATA_START_FUNC(sata_hba_inst))(
/* Pkt execution failed */
if (state == 0)
infop = "disabling Removable Media Status "
"Notification failed";
else
infop = "enabling Removable Media Status "
"Notification failed";
rval = SATA_FAILURE;
}
/* Free allocated resources */
return (rval);
}
/*
* 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 |=
}
}
void
{
"sata_update_port_info: error address %p.",
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.
*
* Support port multiplier.
*/
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.
*
* PMult hotplug is supported now.
*
* Returns dev_info_t pointer if target device was found, NULL otherwise.
*/
static dev_info_t *
{
int circ;
/* Get target id */
if (scsi_hba_tran == NULL)
return (NULL);
if (sata_hba_inst == NULL)
return (NULL);
/* Identify a port-mult by cport_info.cport_dev_type */
else
/* Retrieve target dip */
if (tgt == -1) {
/*
* This is actually an error condition, but not
* a fatal one. Just continue the search.
*/
continue;
}
break;
}
return (cdip);
}
/*
* 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, encoded same
* way as a scsi target number.
*
* Returns dev_info_t pointer if target device was found, NULL otherwise.
*/
static dev_info_t *
{
int circ;
if (tgt == -1) {
/*
* This is actually an error condition, but not
* a fatal one. Just continue the search.
*/
continue;
}
break;
}
return (cdip);
}
/*
* Process sata port disconnect request.
* Normally, cfgadm sata plugin will try to offline (unconfigure) the device
* before this request. Nevertheless, if a device is still configured,
* we need to attempt to offline and unconfigure device.
* Regardless of the unconfigure operation results the port is marked as
* deactivated and no access to the attached device is possible.
* If the target node remains because unconfigure operation failed, its state
* will be set to DEVICE_REMOVED, preventing it to be used again when a device
* is inserted/re-inserted. The event daemon will repeatedly try to unconfigure
* the device and remove old target node.
*
* This function invokes sata_hba_inst->satahba_tran->
* sata_tran_hotplug_ops->sata_tran_port_deactivate().
* If successful, the device structure (if any) attached to the specified port
* is removed and state of the port marked appropriately.
* Failure of the port_deactivate may keep port in the physically active state,
* or may fail the port.
*
* NOTE: Port multiplier is supported.
*/
static int
{
int rval = SATA_SUCCESS;
int npmport = 0;
int rv = 0;
if (qual == SATA_ADDR_DCPORT)
else
/*
* DEVCTL_AP_DISCONNECT invokes sata_hba_inst->satahba_tran->
* sata_tran_hotplug_ops->sata_tran_port_deactivate().
* Do the sanity check.
*/
/* No physical port deactivation supported. */
return (EINVAL);
}
/* Check the current state of the port */
/*
* Processing port mulitiplier
*/
if (qual == SATA_ADDR_CPORT &&
/* Check controller port status */
if (rval != SATA_SUCCESS ||
/*
* Device port status is unknown or it is in failed
* state
*/
"sata_hba_ioctl: connect: failed to deactivate "
"SATA port %d", cport);
return (EIO);
}
/* Disconnect all sub-devices. */
continue;
&subsdevice) == SATA_SUCCESS) {
"[Remove] device at port %d:%d "
}
}
}
/* Disconnect the port multiplier */
if (rval != SATA_SUCCESS &&
} else {
}
return (rv);
}
/*
* Process non-port-multiplier device - it could be a drive connected
* to a port multiplier port or a controller port.
*/
if (qual == SATA_ADDR_PMPORT) {
if (rval != SATA_SUCCESS ||
"sata_hba_ioctl: connect: failed to deactivate "
return (EIO);
}
}
/*
* Set port's dev_state to not ready - this will disable
* an access to a potentially attached device.
*/
/* Remove and release sata_drive info structure. */
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
/*
* If a target node exists, try to offline
* a device and remove target node.
*/
(void) sata_offline_device(sata_hba_inst,
}
sizeof (sata_drive_info_t));
}
} else if (qual == SATA_ADDR_CPORT) {
if (rval != SATA_SUCCESS ||
/*
* Device port status is unknown or it is in failed
* state
*/
"sata_hba_ioctl: connect: failed to deactivate "
"SATA port %d", cport);
return (EIO);
}
}
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
/*
* If a target node exists, try to offline
* a device and remove target node.
*/
(void) sata_offline_device(sata_hba_inst,
}
sizeof (sata_drive_info_t));
}
}
/* Just ask HBA driver to deactivate port */
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint (to force listener to investivate the state).
*/
if (qual == SATA_ADDR_PMPORT) {
if (rval != SATA_SUCCESS &&
/*
* Port deactivation failure - do not change port
* state unless the state returned by HBA indicates a
* port failure.
*
* NOTE: device structures were released, so devices
* now are invisible! Port reset is needed to
* re-enumerate devices.
*/
} else {
/*
* Deactivation succeded. From now on the sata framework
* will not care what is happening to the device, until
* the port is activated again.
*/
}
} else if (qual == SATA_ADDR_CPORT) {
if (rval != SATA_SUCCESS &&
} else {
}
}
return (rv);
}
/*
* Process sata port connect request
* 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 state of the port and a device would be set
* appropriately.
* The device is not set in configured state (system-wise) by this operation.
*
* Note, that activating the port may generate link events,
* so it 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.
*
* NOTE: Port multiplier is supported.
*/
static int
{
int rv = 0;
if (qual == SATA_ADDR_DCPORT)
else
if (qual == SATA_ADDR_PMPORT)
/*
* DEVCTL_AP_CONNECT would invoke sata_hba_inst->
* satahba_tran->sata_tran_hotplug_ops->sata_tran_port_activate().
* Perform sanity check now.
*/
/* No physical port activation supported. */
return (EINVAL);
}
/* Just ask HBA driver to activate port */
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
/*
* Port activation failure.
*/
if (qual == SATA_ADDR_CPORT) {
cport)->cport_mutex);
"sata_hba_ioctl: connect: failed to "
"activate SATA port %d", cport);
}
cport)->cport_mutex);
} else { /* port multiplier device port */
"sata_hba_ioctl: connect: failed to "
}
}
return (EIO);
}
/* Virgin port state - will be updated by the port re-probe. */
if (qual == SATA_ADDR_CPORT) {
cport)->cport_mutex);
cport)->cport_mutex);
} else { /* port multiplier device port */
}
/*
* Probe the port to find its state and attached device.
*/
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint
*/
/*
* If there is a device attached to the port, emit
* a message.
*/
if (qual == SATA_ADDR_CPORT) {
"SATA port multiplier detected "
"at port %d", cport);
} else {
"SATA device detected at port %d", cport);
if (sata_device->satadev_type ==
/*
* A device was not successfully identified
*/
"Could not identify SATA "
"device at port %d", cport);
}
}
} else { /* port multiplier device port */
"SATA device detected at port %d:%d",
/*
* A device was not successfully identified
*/
"Could not identify SATA "
}
}
}
return (rv);
}
/*
* Process sata device unconfigure request.
* 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.
*/
static int
{
int rv = 0;
/* We are addressing attached device, not a port */
"sata_hba_ioctl: unconfigure: "
"failed to unconfigure device at SATA port %d:%d",
}
/*
* 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 SATA port %d:%d",
}
return (rv);
}
/*
* Process sata device configure request
* If port is in a failed state, operation is aborted - one has to use
* an explicit connect or port activate request to try to get a port into
* non-failed mode. Port reset wil also work in such situation.
* If the port is in disconnected (shutdown) state, the connect operation is
* attempted prior to any other action.
* When port is in the active state, there is a device attached and the target
* node exists, a device was most likely offlined.
* If target node does not exist, a new target node is created. In both cases
* an attempt is made to online (configure) the device.
*
* NOTE: Port multiplier is supported.
*/
static int
{
int rval;
/* Get current port state */
if (qual == SATA_ADDR_DPMPORT) {
if (rval != SATA_SUCCESS ||
/*
* Obviously, device on a failed port is not visible
*/
return (ENXIO);
}
} else {
cport)->cport_mutex);
if (rval != SATA_SUCCESS ||
/*
* Obviously, device on a failed port is not visible
*/
cport)->cport_mutex);
return (ENXIO);
}
cport)->cport_mutex);
}
/* need to activate port */
/* Sanity check */
return (ENXIO);
/* 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.
*/
if (qual == SATA_ADDR_DPMPORT) {
if (sata_device->satadev_state &
} else {
cport)->cport_mutex);
if (sata_device->satadev_state &
}
}
"sata_hba_ioctl: configure: "
"failed to activate SATA port %d:%d",
return (EIO);
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint.
*/
/* Virgin port state */
if (qual == SATA_ADDR_DPMPORT) {
pmportinfo->pmport_state = 0;
} else {
cport)-> cport_mutex);
cportinfo->cport_state = 0;
cport)->cport_mutex);
}
/*
* Always reprobe port, to get current device info.
*/
return (EIO);
if (qual == SATA_ADDR_DPMPORT) {
/*
* That's the transition from "inactive" port
* to active one with device attached.
*/
"SATA device detected at port %d:%d",
} else {
/*
* When PM is attached to the cport and cport is
* activated, every PM device port needs to be reprobed.
* We need to emit message for all devices detected
* at port multiplier's device ports.
* Add such code here.
* For now, just inform about device attached to
* cport.
*/
"SATA device detected at port %d", cport);
}
}
/*
* This is where real configuration operation starts.
*
* When PM is attached to the cport and cport is activated,
* devices attached PM device ports may have to be configured
* explicitly. This may change when port multiplier is supported.
* For now, configure only disks and other valid target devices.
*/
if (qual == SATA_ADDR_DCPORT) {
/*
* A device was not successfully identified
*/
"Could not identify SATA "
"device at port %d", cport);
}
} else { /* port multiplier device port */
/*
* A device was not successfully identified
*/
"Could not identify SATA "
}
}
return (ENXIO); /* No device to configure */
}
/*
* 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.
*/
if (qual == SATA_ADDR_DPMPORT)
else
return (ENXIO);
}
if (sdinfo->satadrv_event_flags &
sdinfo->satadrv_event_flags = 0;
}
/*
* Target node exists. Verify, that it belongs
* to existing, attached device and not to
* a removed device.
*/
if (qual == SATA_ADDR_DPMPORT)
"SATA device at port %d cannot be "
"configured. "
"Application(s) accessing "
"previously attached device "
"have to release it before newly "
"inserted device can be made accessible.",
cport);
else
"SATA device at port %d:%d cannot be"
"configured. "
"Application(s) accessing "
"previously attached device "
"have to release it before newly "
"inserted device can be made accessible.",
return (EIO);
}
/*
* Device was not removed and re-inserted.
* Try to online it.
*/
"sata_hba_ioctl: configure: "
"onlining device at SATA port "
return (EIO);
}
if (qual == SATA_ADDR_DPMPORT) {
} else {
cport)->cport_mutex);
}
} else {
/*
* No target node - need to create a new target node.
*/
if (qual == SATA_ADDR_DPMPORT) {
} else {
}
/* Configure operation failed */
"sata_hba_ioctl: configure: "
"configuring SATA device at port %d:%d "
return (EIO);
}
}
return (0);
}
/*
* Process ioctl deactivate port request.
* Arbitrarily unconfigure attached device, if any.
* Even if the unconfigure fails, proceed with the
* port deactivation.
*
* NOTE: Port Multiplier is supported now.
*/
static int
{
int npmport;
/* Sanity check */
return (ENOTSUP);
/* SCSI_TO_SATA_ADDR_QUAL() translate ap_id into a device qualifier */
if (qual == SATA_ADDR_DCPORT)
else
if (qual == SATA_ADDR_PMPORT)
/*
* Processing port multiplier
*/
if (qual == SATA_ADDR_CPORT &&
/* Deactivate all sub-deices */
"sata_hba_ioctl: deactivate: trying to "
"deactivate SATA port %d:%d",
&subsdevice) == SATA_SUCCESS) {
"[Deactivate] device at port %d:%d "
}
}
}
/* Deactivate the port multiplier now. */
if (rval != SATA_SUCCESS) {
}
} else {
}
return (rv);
}
/*
* Process non-port-multiplier device - it could be a drive connected
* to a port multiplier port or a controller port.
*/
if (qual == SATA_ADDR_CPORT) {
/* deal only with valid devices */
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) != 0)
}
} else {
/* Port multiplier device port */
}
/*
* If a target node exists, try to offline a device and
* to remove a target node.
*/
/* target node exist */
"sata_hba_ioctl: port deactivate: "
"target node exists.", NULL);
NDI_SUCCESS) {
"sata_hba_ioctl: port deactivate: "
"failed to unconfigure device at port "
"%d:%d before deactivating the port",
/*
* Set DEVICE REMOVED state in the target
* node. It will prevent an access to
* the device even when a new device is
* attached, until the old target node is
* released, removed and recreated for a new
* device.
*/
/*
* Instruct the event daemon to try the
* target node cleanup later.
*/
}
}
/*
* In any case, remove and release sata_drive_info
* structure.
*/
if (qual == SATA_ADDR_CPORT) {
} else { /* port multiplier device port */
}
}
if (qual == SATA_ADDR_CPORT) {
} else if (qual == SATA_ADDR_PMPORT) {
}
/* Just let HBA driver to deactivate port */
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* without the hint
*/
if (qual == SATA_ADDR_CPORT) {
if (rval != SATA_SUCCESS) {
/*
* Port deactivation failure - do not change port state
* unless the state returned by HBA indicates a port
* failure.
*/
}
"sata_hba_ioctl: port deactivate: "
"cannot deactivate SATA port %d", cport));
} else {
}
} else {
if (rval != SATA_SUCCESS) {
}
"sata_hba_ioctl: port deactivate: "
"cannot deactivate SATA port %d:%d",
} else {
}
}
return (rv);
}
/*
* Process ioctl port activate request.
*
* NOTE: Port multiplier is supported now.
*/
static int
{
/* Sanity check */
return (ENOTSUP);
/*
* The qual translate from ap_id (by SCSI_TO_SATA_ADDR_QUAL())
*/
if (qual == SATA_ADDR_DCPORT)
else
if (qual == SATA_ADDR_PMPORT) {
} else { /* cport */
}
/* Just let HBA driver to activate port, if necessary */
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);
if (qual == SATA_ADDR_PMPORT) {
} else
cport)->cport_mutex);
"sata_hba_ioctl: port activate: cannot activate "
return (EIO);
}
}
if (qual == SATA_ADDR_PMPORT) {
} else
/*
* 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 == B_FALSE) {
if (qual == SATA_ADDR_PMPORT &&
/*
* That's the transition from the "inactive" port state
* or the active port without a device attached to the
* active port state with a device attached.
*/
"SATA device detected at port %d:%d",
} else if (qual == SATA_ADDR_CPORT &&
/*
* That's the transition from the "inactive" port state
* or the active port without a device attached to the
* active port state with a device attached.
*/
"SATA device detected at port %d", cport);
} else {
"SATA port multiplier detected at port %d",
cport);
}
}
}
return (0);
}
/*
* Process ioctl reset port request.
*
* NOTE: Port-Multiplier is supported.
*/
static int
{
int rv = 0;
/*
* The qual translate from ap_id (by SCSI_TO_SATA_ADDR_QUAL())
*/
if (qual == SATA_ADDR_DCPORT)
else
/* Sanity check */
"sata_hba_ioctl: sata_hba_tran missing required "
"function sata_tran_reset_dport"));
return (ENOTSUP);
}
/* Ask HBA to reset port */
sata_device) != SATA_SUCCESS) {
"sata_hba_ioctl: reset port: failed %d:%d",
if (qual == SATA_ADDR_CPORT)
else {
pmport));
pmport));
}
}
return (rv);
}
/*
* Process ioctl reset device request.
*
* NOTE: Port multiplier is supported.
*/
static int
{
int rv = 0;
/* Sanity check */
"sata_hba_ioctl: sata_hba_tran missing required "
"function sata_tran_reset_dport"));
return (ENOTSUP);
}
else
} else { /* port multiplier */
}
return (EINVAL);
}
/* Ask HBA to reset device */
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
"sata_hba_ioctl: reset device: failed at port %d:%d",
/*
* Device info structure remains attached. Another device reset
* or port disconnect/connect and re-probing is
* needed to change it's state
*/
}
}
/*
* If attached device was a port multiplier, some extra processing
* may be needed to bring it back. SATA specification requies a
* mandatory software reset on host port to reliably enumerate a port
* multiplier, the HBA driver should handle that after reset
* operation.
*/
return (rv);
}
/*
* Process ioctl reset all request.
*/
static int
{
int rv = 0;
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"));
return (ENOTSUP);
}
/*
* 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.
*/
cport_event_flags) & SATA_EVNT_LOCK_PORT_BUSY) != 0) {
break;
} else {
/*
* It is enough to lock cport in command-based
* switching mode.
*/
}
}
if (rv == 0) {
/*
* All cports were successfully locked.
* Reset main SATA controller.
* Set the device address to port 0, to have a valid device
* address.
*/
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
"sata_hba_ioctl: reset controller failed"));
return (EIO);
}
}
/*
* Unlock all ports
*/
}
/*
* This operation returns EFAULT if either reset
* controller failed or a re-probing of any port failed.
*/
return (rv);
}
/*
* Process ioctl port self test request.
*
* NOTE: Port multiplier code is not completed nor tested.
*/
static int
{
int rv = 0;
/* Sanity check */
return (ENOTSUP);
/*
* There is no protection here for a configured
* device attached to this port.
*/
if ((*SATA_SELFTEST_FUNC(sata_hba_inst))
"sata_hba_ioctl: port selftest: "
if (qual == SATA_ADDR_CPORT)
else { /* port multiplier device port */
}
return (EIO);
}
/*
* Beacuse the port was reset in the course of testing, it should be
* re-probed and attached device state should be restored. 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.
*/
return (rv);
}
/*
* 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.
*
* Port multiplier is supported.
*/
static void
{
/* Check cport state */
if (port_state & SATA_PSTATE_SHUTDOWN ||
if (port_state & SATA_PSTATE_FAILED)
else
return;
}
/* cport state is okay. Now check pmport state */
/* Sanity check */
return;
if (port_state & SATA_PSTATE_SHUTDOWN ||
if (port_state & SATA_PSTATE_FAILED)
else
return;
}
}
/* Port is enabled and ready */
else
switch (dev_type) {
case SATA_DTYPE_NONE:
{
/* No device attached */
break;
}
case SATA_DTYPE_PMULT:
{
/* Need to check port multiplier state */
if (pmult_state & SATA_PSTATE_FAILED)
else
return;
}
/* Port multiplier is not configurable */
break;
}
case SATA_DTYPE_ATADISK:
case SATA_DTYPE_ATAPICD:
case SATA_DTYPE_ATAPITAPE:
case SATA_DTYPE_ATAPIDISK:
{
int circ;
if (DEVI_IS_DEVICE_REMOVED(tdip)) {
/*
* There could be the case where previously
* configured and opened device was removed
* and unknown device was plugged.
* In such case we want to show a device, and
* its configured or unconfigured state but
* indicate unusable condition untill the
* old target node is released and removed.
*/
} else {
cport));
cport);
if ((sdinfo->satadrv_state &
SATA_DSTATE_FAILED) != 0)
else
} else {
}
cport));
}
if ((DEVI_IS_DEVICE_OFFLINE(tdip)) ||
(DEVI_IS_DEVICE_DOWN(tdip))) {
} else {
}
} else {
}
break;
}
case SATA_DTYPE_ATAPIPROC:
break;
default:
/*
* This is actually internal error condition (non fatal),
* because we have already checked all defined device types.
*/
"sata_cfgadm_state: Internal error: "
"unknown device type"));
break;
}
}
/*
* Process ioctl get device path request.
*
* NOTE: Port multiplier has no target dip. Devices connected to port
* multiplier have target node attached to the HBA node. The only difference
* between them and the directly-attached device node is a target address.
*/
static int
{
char path[MAXPATHLEN];
/*
* No such device. If this is a request for a size, do not
* return EINVAL for non-existing target, because cfgadm
* will then indicate a meaningless ioctl failure.
* If this is a request for a path, indicate invalid
* argument.
*/
return (EINVAL);
} else {
}
mode) != 0)
return (EFAULT);
} else {
return (EINVAL);
mode) != 0)
return (EFAULT);
}
return (0);
}
/*
* Process ioctl get attachment point type request.
*
* NOTE: Port multiplier is supported.
*/
static int
{
const char *ap_type;
int dev_type;
else /* pmport */
switch (dev_type) {
case SATA_DTYPE_NONE:
ap_type = "port";
break;
case SATA_DTYPE_ATADISK:
case SATA_DTYPE_ATAPIDISK:
ap_type = "disk";
break;
case SATA_DTYPE_ATAPICD:
break;
case SATA_DTYPE_ATAPITAPE:
ap_type = "tape";
break;
case SATA_DTYPE_ATAPIPROC:
ap_type = "processor";
break;
case SATA_DTYPE_PMULT:
ap_type = "sata-pmult";
break;
case SATA_DTYPE_UNKNOWN:
ap_type = "unknown";
break;
default:
ap_type = "unsupported";
break;
} /* end of dev_type switch */
mode) != 0)
return (EFAULT);
} else {
return (EINVAL);
return (EFAULT);
}
return (0);
}
/*
* Process ioctl get device model info request.
* This operation should return to cfgadm the device model
* information string
*
* NOTE: Port multiplier is supported.
*/
static int
{
else /* port multiplier */
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
#else /* _LITTLE_ENDIAN */
#endif /* _LITTLE_ENDIAN */
mode) != 0)
return (EFAULT);
} else {
return (EINVAL);
mode) != 0)
return (EFAULT);
}
return (0);
}
/*
* Process ioctl get device firmware revision info request.
* This operation should return to cfgadm the device firmware revision
* information string
*
* Port multiplier is supported.
*/
static int
{
else /* port multiplier */
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
#else /* _LITTLE_ENDIAN */
#endif /* _LITTLE_ENDIAN */
mode) != 0)
return (EFAULT);
} else {
return (EINVAL);
mode) != 0)
return (EFAULT);
}
return (0);
}
/*
* Process ioctl get device serial number info request.
* This operation should return to cfgadm the device serial number string.
*
* NOTE: Port multiplier is supported.
*/
static int
{
else /* port multiplier */
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
#else /* _LITTLE_ENDIAN */
#endif /* _LITTLE_ENDIAN */
mode) != 0)
return (EFAULT);
} else {
return (EINVAL);
mode) != 0)
return (EFAULT);
}
return (0);
}
/*
* Preset scsi extended sense data (to NO SENSE)
* First 18 bytes of the sense data are preset to current valid sense
* with a key NO SENSE data.
*
* Returns void
*/
static void
{
sense->es_cmd_info[0] = 0;
sense->es_add_code = 0;
sense->es_qual_code = 0;
}
/*
* Register a legacy cmdk-style devid for the target (disk) device.
*
* Note: This function is called only when the HBA devinfo node has the
* property "use-cmdk-devid-format" set. This property indicates that
* devid compatible with old cmdk (target) driver is to be generated
* for any target device attached to this controller. This will take
* precedence over the devid generated by sd (target) driver.
* This function is derived from cmdk_devid_setup() function in cmdk.c.
*/
static void
{
char *hwid;
int modlen;
int serlen;
int rval;
/*
* device ID is a concatanation of model number, "=", serial number.
*/
if (modlen == 0)
goto err;
if (serlen == 0)
goto err;
/* initialize/register devid */
/*
* Free up the allocated devid buffer.
* NOTE: This doesn't mean unregistering devid.
*/
}
if (rval != DDI_SUCCESS)
err:
}
/*
*/
static int
{
char *s;
int i;
int tb;
char ch;
s = buf;
for (i = 0; i < buf_len; i++) {
ch = *s++;
tb = i + 1;
}
return (0); /* invalid string */
return (tb); /* return length */
}
/*
* sata_set_drive_features function compares current device features setting
* with the saved device features settings and, if there is a difference,
* it restores device features setting to the previously saved state.
* It also arbitrarily tries to select the highest supported DMA mode.
* Device Identify or Identify Packet Device data has to be current.
* At the moment read ahead and write cache are considered for all devices.
* For atapi devices, Removable Media Status Notification is set in addition
* to common features.
*
* This function cannot be called in the interrupt context (it may sleep).
*
* The input argument sdinfo should point to the drive info structure
* to be updated after features are set. Note, that only
* device (packet) identify data is updated, not the flags indicating the
* supported features.
*
* Returns SATA_SUCCESS if successful or there was nothing to do.
* Device Identify data in the drive info structure pointed to by the sdinfo
* arguments is updated even when no features were set or changed.
*
* Returns SATA_FAILURE if device features could not be set or DMA mode
* for a disk cannot be set and device identify data cannot be fetched.
*
* Returns SATA_RETRY if device features could not be set (other than disk
* DMA mode) but the device identify data was fetched successfully.
*
* Note: This function may fail the port, making it inaccessible.
* In such case the explicit port disconnect/connect or physical device
*/
static int
{
int rval = SATA_SUCCESS;
int rval_set;
char *finfo = "sata_set_drive_features: cannot";
char *finfox;
int cache_op;
/*
* Cannot get device identification - caller may retry later
*/
"%s fetch device identify data\n", finfo);
return (SATA_FAILURE);
}
" initialize device features\n";
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
/* Arbitrarily set UDMA mode */
SATA_SUCCESS) {
"%s set UDMA mode\n", finfo));
return (SATA_FAILURE);
}
break;
case SATA_DTYPE_ATAPICD:
case SATA_DTYPE_ATAPITAPE:
case SATA_DTYPE_ATAPIDISK:
/* Set Removable Media Status Notification, if necessary */
restore != 0) {
/* Current setting does not match saved one */
rval = SATA_FAILURE;
}
}
/*
* We have to set Multiword DMA or UDMA, if it is supported, as
* we want to use DMA transfer mode whenever possible.
* Some devices require explicit setting of the DMA mode.
*/
/* Set highest supported DMA mode */
SATA_SUCCESS) {
"%s set UDMA mode\n", finfo));
rval = SATA_FAILURE;
}
}
break;
}
/*
* neither READ AHEAD nor WRITE CACHE is supported
* - do nothing
*/
"settable features not supported\n", NULL);
goto update_sdinfo;
}
/*
* both READ AHEAD and WRITE CACHE are enabled
* - Nothing to do
*/
"no device features to set\n", NULL);
goto update_sdinfo;
}
cache_op = 0;
/* Enable read ahead / read cache */
"enabling read cache\n", NULL);
/* Disable read ahead / read cache */
"disabling read cache\n", NULL);
}
if (cache_op != 0) {
/* Try to set read cache mode */
&new_sdinfo, cache_op);
}
}
cache_op = 0;
/* Enable write cache */
"enabling write cache\n", NULL);
/* Disable write cache */
"disabling write cache\n", NULL);
}
if (cache_op != 0) {
/* Try to set write cache mode */
&new_sdinfo, cache_op);
}
}
if (rval != SATA_SUCCESS)
/*
* We need to fetch Device Identify data again
*/
/*
* Cannot get device identification - retry later
*/
"%s re-fetch device identify data\n", finfo));
rval = SATA_FAILURE;
}
/* Copy device sata info. */
return (rval);
}
/*
*
* Returns 1 if threshold exceeded, 0 if threshold not 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 {
rval = -1;
goto fail;
}
}
sizeof (struct smart_data));
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Used by LOG SENSE page 0x10
* Reads (in synchronous mode) the self test log data using Read Log Ext cmd.
* Note: cannot be called in the interrupt context.
*
* 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 {
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 SMART SELFTEST LOG
*/
sizeof (struct smart_selftest_log));
"sata_smart_selftest_log: "
"cannot allocate buffer"));
return (-1);
}
/* Build SMART_READ_LOG cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART DATA available
*/
rval = -1;
goto fail;
} else {
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_LOG cmd in the sata_pkt */
/* Send pkt to SATA HBA driver */
/*
* Whoops, no SMART DATA available
*/
rval = -1;
goto fail;
} else {
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 READ LOG EXTENDED command
*/
sizeof (struct read_log_ext_directory));
"sata_read_log_ext_directory: "
"cannot allocate buffer"));
return (-1);
}
/* Build READ LOG EXT w/ log directory 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 {
rval = -1;
goto fail;
}
}
sizeof (struct read_log_ext_directory));
rval = 0;
}
fail:
/* Free allocated resources */
return (rval);
}
/*
* Set up error retrieval sata command for NCQ command error data
* recovery.
*
* Returns SATA_SUCCESS when data buffer is allocated and packet set-up,
* returns SATA_FAILURE otherwise.
*/
static int
{
#ifndef __lock_lint
#endif
/* Operation modes are up to the caller */
/* Synchronous mode, no callback - may be changed by the caller */
/*
* Allocate dma_able buffer error data.
* Buffer allocation will take care of buffer alignment and other DMA
* attributes.
*/
sizeof (struct sata_ncq_error_recovery_page));
return (SATA_FAILURE);
/*
* Set-up pointer to the buffer handle, so HBA can sync buffer
* before accessing it. Handle is in usual place in translate struct.
*/
return (SATA_SUCCESS);
}
/*
* 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:
/* 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;
}
}
/*
* 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 {
} else if (sata_msg) {
}
}
/* sata trace debug */
}
/* ******** Asynchronous HBA events handling & hotplugging support ******** */
/*
* 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);
}
/*
* 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. We still want
* to process events until HBA is detached.
*/
break;
}
if (sata_hba_inst == NULL)
/* HBA not attached */
return;
/*
* Validate address before - do not proceed with invalid address.
*/
return;
/*
* If event relates to port or device, check port state.
* Port has to be initialized, or we cannot accept an event.
*/
return;
}
SATA_ADDR_DPMPORT)) != 0) {
"sata_hba_event_notify: Non-pmult device (0x%x)"
return;
}
/*
* The daemon might be processing attachment of port
* multiplier, in that case we should ignore events on its
* sub-devices.
*
* NOTE: Only pmult_state is checked in sata_hba_event_notify.
* The pmport_state is checked by sata daemon.
*/
"sata_hba_event_notify: pmult is not"
"available at port %d:%d, ignore event 0x%x",
return;
}
}
(SATA_ADDR_PMPORT | SATA_ADDR_DPMPORT)) != 0) {
"sata_hba_event_notify: invalid/"
"un-implemented port %d:%d (%d ports), "
return;
}
/* pmport is implemented/valid? */
if (pmportinfo == NULL) {
"sata_hba_event_notify: invalid/"
"un-implemented port %d:%d, ignore "
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 <
0xffffffffffffffffULL)
linkevent = 0;
} else if (linkevent & SATA_EVNT_LINK_LOST) {
} else {
if (pstats->link_established <
0xffffffffffffffffULL)
}
}
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) {
0xffffffffffffffffULL)
}
}
if ((event & ~SATA_EVNT_DEVICE_RESET) != 0) {
/* Invalid event for a device */
}
/* qualify this event */
if ((event & (SATA_EVNT_DEVICE_RESET |
SATA_EVNT_PMULT_LINK_CHANGED)) == 0) {
/* Invalid event for a port multiplier */
goto event_info;
}
if (event & SATA_EVNT_DEVICE_RESET) {
"[Reset] port-mult on cport %d", cport);
}
if (event & SATA_EVNT_PMULT_LINK_CHANGED) {
"pmult link changed on cport %d", cport);
}
} 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);
0xffffffffffffffffULL)
}
/*
* 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
*/
if (sata_hba_inst->satahba_attached == 0 ||
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 {
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.
* Port multiplier is supported now.
*/
static void
{
int ncport;
"Processing controller %d event(s)",
/*
* Process controller power change first
* HERE
*/
/*
*/
/*
* Not all ports may be processed in attach by the time we
* get an event. Check if port info is initialized.
*/
continue;
/* We have initialized controller port info */
/* 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);
}
if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) {
}
if (event_flags & SATA_EVNT_AUTOONLINE_DEVICE) {
}
}
/*
* Scan port multiplier and all its sub-ports event flags.
* The events are marked by
* (1) sata_pmult_info.pmult_event_flags
* (2) sata_pmport_info.pmport_event_flags
*/
/*
* There should be another extra check: this
* port multiplier still exists?
*/
ncport);
sata_hba_inst, ncport)));
sata_hba_inst, ncport)));
} else {
"Port-multiplier is gone. "
"Ignore all sub-device events "
"at port %d.", ncport);
}
}
SATA_DTYPE_NONE) &&
/* Have device event */
saddr);
}
}
/* Release PORT_BUSY flag */
} /* End of loop through the controller SATA ports */
}
/*
* Specific port multiplier instance event processing. At the moment, device
*
* NOTE: power management event is not supported yet.
*/
static void
{
int npmport;
int rval;
"Processing pmult event(s) on cport %d of controller %d",
/* First process events on port multiplier */
/*
* Reset event (of port multiplier) has higher priority because the
* port multiplier itself might be failed or removed after reset.
*/
if (event_flags & SATA_EVNT_DEVICE_RESET) {
/*
* The status of the sub-links are uncertain,
* so mark all sub-ports as RESET
*/
if (pmportinfo == NULL) {
/* That's weird. */
"sata_hba_event_notify: "
"port %d:%d (%d ports), ",
sata_hba_inst, cport)));
continue;
}
/* Mark all pmport to unknow state. */
/* Mark all pmports with link events. */
}
} else if (event_flags & SATA_EVNT_PMULT_LINK_CHANGED) {
/*
* We need probe the port multiplier to know what has
* happened.
*/
if (rval != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
"SATA port %d probing failed", cport));
/* PMult structure must be released. */
return;
}
/*
* Sanity check - Port is active? Is the link active?
* The device is still a port multiplier?
*/
if ((cportinfo->cport_state &
/* PMult structure must be released. */
return;
}
/* Probed succeed, set port ready. */
cportinfo->cport_state |=
}
/* Release port multiplier event flags. */
/*
* Check all sub-links.
*/
npmport ++) {
if ((event_flags &
(SATA_EVNT_PORT_EVENTS | SATA_EVNT_DRIVE_EVENTS)) != 0) {
/*
* Got port multiplier port event.
* We need some hierarchy of event processing as they
* are affecting each other:
* 2. link events - link events may trigger device
* detached or device attached events in some
* circumstances.
*/
if (event_flags & SATA_EVNT_DEVICE_DETACHED) {
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_ATTACHED) {
saddr);
}
if (event_flags & SATA_EVNT_LINK_ESTABLISHED ||
saddr);
}
if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) {
}
}
/* Checking drive event(s). */
if (event_flags & (SATA_EVNT_DEVICE_RESET |
/* Have device event */
saddr);
}
}
/* Release PORT_BUSY flag */
}
"[DONE] pmult event(s) on cport %d of controller %d",
}
/*
* 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 sequence is managed by 3 stage flags:
* - reset event reported,
* - reset event being processed,
* - request to clear device reset state.
*
* NOTE: This function has to be entered with cport mutex held. It exits with
* mutex held as well, but can release mutex during the processing.
*/
static void
{
int rval_probe, rval_set;
/* We only care about host sata cport for now */
/*
* If the port is in SHUTDOWN or FAILED state, or device is in FAILED
* state, ignore reset event.
*/
if (((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) ||
return;
}
SATA_DTYPE_PMULT)) {
/*
* Should not happened: this is already handled in
* sata_hba_event_notify()
*/
goto done;
}
SATA_VALID_DEV_TYPE) == 0) {
/*
* This should not happen - coding error.
* But we can recover, so do not panic, just clean up
* and if in debug mode, log the message.
*/
#ifdef SATA_DEBUG
"sata_process_device_reset: "
"Invalid device type with sdinfo!", NULL);
#endif
sdinfo->satadrv_event_flags = 0;
return;
}
#ifdef SATA_DEBUG
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == 0) {
/* Nothing to do */
/* Something is weird - why we are processing dev reset? */
"No device reset event!!!!", NULL);
return;
}
if ((sdinfo->satadrv_event_flags &
/* Something is weird - new device reset event */
"Overlapping device reset events!", NULL);
}
#endif
/* 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_probe != SATA_SUCCESS) {
/* Something went wrong? Fail the port */
sdinfo->satadrv_event_flags = 0;
"SATA port %d probing failed",
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.
*/
}
return;
}
return;
}
if ((sdinfo->satadrv_event_flags &
SATA_EVNT_INPROC_DEVICE_RESET) == 0) {
/*
* Start tracking time for device feature restoration and
* identification. Save current time (lbolt value).
*/
}
/* Mark device reset processing as active */
if (rval_set != SATA_SUCCESS) {
/*
* Restoring drive setting failed.
* Probe the port first, to check if the port state has changed
*/
/* probe port */
if (rval_probe == SATA_SUCCESS &&
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0 &&
/*
* We may retry this a bit later - in-process reset
* condition should be already set.
* Track retry time for device identification.
*/
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) != 0 &&
sdinfo->satadrv_reset_time != 0) {
/*
* If the retry time limit was not
* exceeded, retry.
*/
return;
}
if (rval_set == SATA_RETRY) {
/*
* Setting drive features failed, but
* the drive is still accessible,
* so emit a warning message before
* return.
*/
goto done;
}
}
/* Fail the drive */
"SATA device at port %d - device failed",
}
/*
* No point of retrying - device failed or some other event
* processing or already did or will do port info cleanup.
* To be safe (HBA may need it),
* request clearing device reset condition.
*/
sdinfo->satadrv_reset_time = 0;
return;
}
done:
/*
* If setting of drive features failed, but the drive is still
* accessible, emit a warning message.
*/
if (rval_set == SATA_RETRY) {
"SATA device at port %d - desired setting could not be "
"restored after reset. Device may not operate as expected.",
}
/*
* 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.
*/
sdinfo->satadrv_reset_time = 0;
}
}
}
/*
* Port Multiplier Port Device Reset Event processing.
*
* NOTE: This function has to be entered with pmport mutex held. It exits with
* mutex held as well, but can release mutex during the processing.
*/
static void
{
int rval;
/*
* If the port is in SHUTDOWN or FAILED state, or device is in FAILED
* state, ignore reset event.
*/
if (((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) ||
return;
}
/*
* This should not happen - coding error.
* But we can recover, so do not panic, just clean up
* and if in debug mode, log the message.
*/
#ifdef SATA_DEBUG
"sata_process_pmdevice_reset: "
"Invalid device type with sdinfo!", NULL);
#endif
sdinfo->satadrv_event_flags = 0;
return;
}
#ifdef SATA_DEBUG
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) == 0) {
/* Nothing to do */
/* Something is weird - why we are processing dev reset? */
"No device reset event!!!!", NULL);
return;
}
if ((sdinfo->satadrv_event_flags &
/* Something is weird - new device reset event */
"Overlapping device reset events!", NULL);
}
#endif
/* 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 */
sdinfo->satadrv_event_flags = 0;
"SATA port %d:%d probing failed",
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.
*/
/* must clear flags on cport */
}
return;
}
return;
}
if ((sdinfo->satadrv_event_flags &
SATA_EVNT_INPROC_DEVICE_RESET) == 0) {
/*
* Start tracking time for device feature restoration and
* identification. Save current time (lbolt value).
*/
}
/* 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 - in-process reset
* condition should be already set.
* Track retry time for device identification.
*/
if ((pmportinfo->pmport_dev_type &
SATA_VALID_DEV_TYPE) != 0 &&
sdinfo->satadrv_reset_time != 0) {
/*
* If the retry time limit was not
* exceeded, retry.
*/
return;
}
}
/* Fail the drive */
"SATA device at port %d:%d - device failed",
} 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_reset_time = 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.
*/
sdinfo->satadrv_reset_time = 0;
/* must clear flags on cport */
}
}
}
/*
* 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: Port multiplier ports events are handled by
* sata_process_pmport_link_events();
*/
static void
{
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 */
"SATA port %d probing failed",
/*
* 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) {
}
}
/*
* Port Multiplier Port Link Events processing.
*/
static void
{
int rval;
"Processing port %d:%d link event(s)",
/* Reset event flags first */
/* If the port is in SHUTDOWN or FAILED state, ignore link events. */
if ((pmportinfo->pmport_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 */
"SATA port %d:%d probing failed",
/*
* 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:%d link established event - "
"link down",
goto linklost;
}
"Processing port %d:%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 (pmportinfo->pmport_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) {
#ifdef SATA_DEBUG
if (pmportinfo->pmport_link_lost_time == 0) {
"Processing port %d:%d link lost event",
}
#endif
/* Ignore event */
"Ignoring port %d:%d link lost event - link is up",
goto done;
}
/*
* we need to track link lost time and eventually generate
* device detach event.
*/
/* We are tracking link lost time */
if (pmportinfo->pmport_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:%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: Port multiplier ports events are handled by
* sata_process_pmdevice_detached()
*/
static void
{
char name[16];
int npmport;
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 */
"SATA port %d probing failed",
/*
* 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
*/
/*
* A port-multiplier is removed.
*
* Calling sata_process_pmdevice_detached() does not work
* here. The port multiplier is gone, so we cannot probe
* sub-port any more and all pmult-related data structure must
* be de-allocated immediately. Following structure of every
* implemented sub-port behind the pmult are required to
* released.
*
* - attachment point
* - target node
* - sata_drive_info
* - sata_pmport_info
*/
"Detaching target node at port %d:%d",
/* Remove attachment point. */
name[0] = '\0';
"Remove attachment point of port %d:%d",
/* Remove target node */
/* Release sata_pmport_info & sata_drive_info. */
sizeof (sata_drive_info_t));
}
/* Release sata_pmport_info at last */
(void) kmem_free((void *) pmportinfo,
sizeof (sata_pmport_info_t));
}
/* Finally, release sata_pmult_info */
(void) kmem_free((void *)
sizeof (sata_pmult_info_t));
"SATA port-multiplier detached at port %d", cport);
} else {
sizeof (sata_drive_info_t));
}
"SATA device detached at port %d", cport);
/*
* Try to offline a device and remove target node
* if it still exists
*/
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* with the hint: SE_HINT_REMOVE
*/
}
/*
* Port Multiplier Port Device Deattached Event processing.
*
* NOTE: No Mutex should be hold.
*/
static void
{
int rval;
"Processing port %d:%d device detached",
/* Clear event flag */
/* If the port is in SHUTDOWN or FAILED state, ignore detach event. */
if ((pmportinfo->pmport_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 */
"SATA port %d:%d probing failed",
/*
* 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
*/
/*
* 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.
*
* If attached device cannot be identified or set-up, the retry for the
* attach processing is set-up. Subsequent daemon run would try again to
* identify the device, until the time limit is reached
* (SATA_DEV_IDENTIFY_TIMEOUT).
*
* This function cannot be called in interrupt context (it may sleep).
*
* NOTE: Port multiplier ports events are handled by
* sata_process_pmdevice_attached()
*/
static void
{
int rval;
int npmport;
/* Clear attach 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,
* despite the fact the device was removed and now it is re-attached,
* the old drive info structure was not removed.
* Arbitrarily release device info structure.
*/
sizeof (sata_drive_info_t));
"Arbitrarily detaching old device info.", NULL);
}
/* 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 */
"SATA port %d probing failed",
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
*/
/*
* Port reprobing will take care of the creation of the device
* info structure and determination of the device type.
*/
/* Some device is attached to the port */
/*
* A device was not successfully attached.
* Track retry time for device identification.
*/
if (cportinfo->cport_dev_attach_time != 0) {
/*
* If the retry time limit was not exceeded,
* reinstate attach event.
*/
if ((cur_time -
/* OK, restore attach event */
} else {
/* Timeout - cannot identify device */
"Could not identify SATA device "
"at port %d",
}
} else {
/*
* Start tracking time for device
* identification.
* Save current time (lbolt value).
*/
/* Restore attach event */
}
"SATA port-multiplier detected at port %d",
/* Log the info of new port multiplier */
&sata_device);
}
/* Marked all pmports with link events. */
}
/* Auto-online is not available for PMult now. */
} else {
/*
* If device was successfully attached, the subsequent
* action depends on a state of the
* sata_auto_online variable. If it is set to zero.
* an explicit 'configure' command will be needed to
* configure it. If its value is non-zero, we will
* attempt to online (configure) the device.
* First, log the message indicating that a device
* was attached.
*/
/* Log device info data */
cportinfo));
&new_sdinfo);
}
/*
* 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.
*/
#ifdef SATA_DEBUG
if ((cportinfo->cport_event_flags &
"sata_process_device_attached: "
"old device target node exists!");
#endif
/*
* target node exists - try to unconfigure
* device and remove the node.
*/
if (rval == NDI_SUCCESS) {
} else {
/*
* PROBLEM - the target node remained
* and it belongs to a previously
* attached device.
* This happens when the file was open
* or the node was waiting for
* resources at the time the
* associated device was removed.
* Instruct event daemon to retry the
* cleanup later.
*/
"Application(s) accessing "
"previously attached SATA "
"device have to release "
"it before newly inserted "
"device can be made accessible.",
}
}
if (sata_auto_online != 0) {
}
}
} else {
}
if (event_flags != 0 || pmult_event_flags != 0) {
}
}
/*
* Port Multiplier Port Device Attached Event processing.
*
* NOTE: No Mutex should be hold.
*/
static void
{
int rval;
/* Clear attach event flag first */
/* If the port is in SHUTDOWN or FAILED state, ignore event. */
if ((pmportinfo->pmport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
return;
}
/*
* If the sata_drive_info structure is found attached to the port info,
* despite the fact the device was removed and now it is re-attached,
* the old drive info structure was not removed.
* Arbitrarily release device info structure.
*/
sizeof (sata_drive_info_t));
"Arbitrarily detaching old device info.", NULL);
}
/* 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 {
/* pmport 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:%d",
return;
}
/*
* Generate sysevent - EC_DR / ESC_DR_AP_STATE_CHANGE
* with the hint: SE_HINT_INSERT
*/
/*
* Port reprobing will take care of the creation of the device
* info structure and determination of the device type.
*/
/* Some device is attached to the port */
/*
* A device was not successfully attached.
* Track retry time for device identification.
*/
if (pmportinfo->pmport_dev_attach_time != 0) {
/*
* If the retry time limit was not exceeded,
* reinstate attach event.
*/
if ((cur_time -
/* OK, restore attach event */
} else {
/* Timeout - cannot identify device */
"Could not identify SATA device "
"at port %d:%d",
}
} else {
/*
* Start tracking time for device
* identification.
* Save current time (lbolt value).
*/
/* Restore attach event */
}
} else {
/*
* If device was successfully attached, the subsequent
* action depends on a state of the
* sata_auto_online variable. If it is set to zero.
* an explicit 'configure' command will be needed to
* configure it. If its value is non-zero, we will
* attempt to online (configure) the device.
* First, log the message indicating that a device
* was attached.
*/
"SATA device detected at port %d:%d",
/* Log device info data */
pmportinfo));
&new_sdinfo);
}
/*
* 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.
*/
#ifdef SATA_DEBUG
if ((pmportinfo->pmport_event_flags &
"sata_process_device_attached: "
"old device target node exists!");
#endif
/*
* target node exists - try to unconfigure
* device and remove the node.
*/
if (rval == NDI_SUCCESS) {
} else {
/*
* PROBLEM - the target node remained
* and it belongs to a previously
* attached device.
* This happens when the file was open
* or the node was waiting for
* resources at the time the
* associated device was removed.
* Instruct event daemon to retry the
* cleanup later.
*/
"Application(s) accessing "
"previously attached SATA "
"device have to release "
"it before newly inserted "
"device can be made accessible."
"at port %d:%d",
}
}
if (sata_auto_online != 0) {
}
}
} else {
}
if (event_flags != 0) {
}
/* clear the reset_in_progress events */
/* must clear flags on cport */
}
}
}
/*
* Device Target Node Cleanup Event processing.
* If the target node associated with a sata port device is in
* DEVI_DEVICE_REMOVED state, an attempt is made to remove it.
* If the target node cannot be removed, the event flag is left intact,
* so that event daemon may re-run this function later.
*
* This function cannot be called in interrupt context (it may sleep).
*
* NOTE: Processes cport events only, not port multiplier ports.
*/
static void
{
/*
* Check if there is target node for that device and it is in the
* DEVI_DEVICE_REMOVED state. If so, release it.
*/
/*
* target node exists - check if it is target node of
* a removed device.
*/
"sata_process_target_node_cleanup: "
"old device target node exists!", NULL);
/*
* Unconfigure and remove the target node
*/
NDI_SUCCESS) {
return;
}
/*
* Event daemon will retry the cleanup later.
*/
}
} else {
} else {
/* sanity check */
return;
return;
}
}
}
/*
* Device AutoOnline Event processing.
* If attached device is to be onlined, an attempt is made to online this
* device, but only if there is no lingering (old) target node present.
* If the device cannot be onlined, the event flag is left intact,
* so that event daemon may re-run this function later.
*
* This function cannot be called in interrupt context (it may sleep).
*
* NOTE: Processes cport events only, not port multiplier ports.
*/
static void
{
/*
* Check if device is present and recognized. If not, reset event.
*/
/* Nothing to online */
return;
}
/*
* Check if there is target node for this device and if it is in the
* DEVI_DEVICE_REMOVED state. If so, abort onlining but keep
* the event for later processing.
*/
/*
* target node exists - check if it is target node of
* a removed device.
*/
"sata_process_device_autoonline: "
"old device target node exists!", NULL);
/*
* Event daemon will retry device onlining later.
*/
return;
}
/*
* If the target node is not in the 'removed" state, assume
* that it belongs to this device. There is nothing more to do,
* but reset the event.
*/
} else {
/*
* Try to online the device
* If there is any reset-related event, remove it. We are
* configuring the device and no state restoring is needed.
*/
else
if (sdinfo->satadrv_event_flags &
sdinfo->satadrv_event_flags = 0;
/* Need to create a new target node. */
/*
* Configure (onlining) failed.
* We will NOT retry
*/
"sata_process_device_autoonline: "
"configuring SATA device at port %d failed",
}
} else {
}
}
}
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));
}
}
/*
* Set DEVI_DEVICE_REMOVED state in the SATA device target node.
*/
static void
{
int circ;
}
/*
* Set internal event instructing event daemon to try
* to perform the target node cleanup.
*/
static void
{
} else {
}
}
/*
* Check if the SATA device target node is in DEVI_DEVICE_REMOVED state,
* i.e. check if the target node state indicates that it belongs to a removed
* device.
*
* Returns B_TRUE if the target node is in DEVI_DEVICE_REMOVED state,
* B_FALSE otherwise.
*/
static boolean_t
{
if (DEVI_IS_DEVICE_REMOVED(tdip))
return (B_TRUE);
else
return (B_FALSE);
}
/*
* Check for DMA error. Return B_TRUE if error, B_FALSE otherwise.
*/
static boolean_t
{
if (fm_capability & DDI_FM_DMACHK_CAPABLE) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/* ************************ FAULT INJECTTION **************************** */
#ifdef SATA_INJECT_FAULTS
static uint32_t sata_fault_count = 0;
static uint32_t sata_fault_suspend_count = 0;
/*
* Inject sata pkt fault
* It modifies returned values of the sata packet.
* It returns immediately if:
* pkt fault injection is not enabled (via sata_inject_fault,
* sata_inject_fault_count), or invalid fault is specified (sata_fault_type),
* or pkt does not contain command to be faulted (set in sata_fault_cmd), or
* pkt is not directed to specified fault controller/device
* (sata_fault_ctrl_dev and sata_fault_device).
* If fault controller is not specified, fault injection applies to all
* controllers and devices.
*
* First argument is the pointer to the executed sata packet.
* Second argument is a pointer to a value returned by the HBA tran_start
* function.
* Third argument specifies injected error. Injected sata packet faults
* are the satapkt_reason values.
* SATA_PKT_BUSY -1 Not completed, busy
* SATA_PKT_DEV_ERROR 1 Device reported error
* SATA_PKT_QUEUE_FULL 2 Not accepted, queue full
* SATA_PKT_PORT_ERROR 3 Not completed, port error
* SATA_PKT_CMD_UNSUPPORTED 4 Cmd unsupported
* SATA_PKT_ABORTED 5 Aborted by request
* SATA_PKT_TIMEOUT 6 Operation timeut
* SATA_PKT_RESET 7 Aborted by reset request
*
* Additional global variables affecting the execution:
*
* sata_inject_fault_count variable specifies number of times in row the
* error is injected. Value of -1 specifies permanent fault, ie. every time
* the fault injection point is reached, the fault is injected and a pause
* between fault injection specified by sata_inject_fault_pause_count is
* ignored). Fault injection routine decrements sata_inject_fault_count
* (if greater than zero) until it reaches 0. No fault is injected when
* sata_inject_fault_count is 0 (zero).
*
* sata_inject_fault_pause_count variable specifies number of times a fault
* injection is bypassed (pause between fault injections).
* If set to 0, a fault is injected only a number of times specified by
* sata_inject_fault_count.
*
* The fault counts are static, so for periodic errors they have to be manually
* reset to start repetition sequence from scratch.
* If the original value returned by the HBA tran_start function is not
* SATA_TRAN_ACCEPTED and pkt reason is not SATA_PKT_COMPLETED, no error
* is injected (to avoid masking real problems);
*
* NOTE: In its current incarnation, this function should be invoked only for
* commands executed in SYNCHRONOUS mode.
*/
static void
{
return;
if (sata_inject_fault_count == 0)
return;
if (fault == 0)
return;
return;
if (sata_fault_ctrl != NULL) {
return;
return;
}
/* Modify pkt return parameters */
if (*rval != SATA_TRAN_ACCEPTED ||
sata_fault_count = 0;
return;
}
if (sata_fault_count == 0 && sata_fault_suspend_count != 0) {
/* Pause in the injection */
sata_fault_suspend_count -= 1;
return;
}
if (sata_fault_count == 0 && sata_fault_suspend_count == 0) {
/*
* Init inject fault cycle. If fault count is set to -1,
* it is a permanent fault.
*/
if (sata_inject_fault_count != -1) {
if (sata_fault_suspend_count == 0)
}
}
if (sata_fault_count != 0)
sata_fault_count -= 1;
switch (fault) {
case SATA_PKT_BUSY:
*rval = SATA_TRAN_BUSY;
break;
case SATA_PKT_QUEUE_FULL:
break;
case SATA_PKT_CMD_UNSUPPORTED:
break;
case SATA_PKT_PORT_ERROR:
/* This is "rejected" command */
/* Additional error setup could be done here - port state */
break;
case SATA_PKT_DEV_ERROR:
/*
* Additional error setup could be done here
*/
break;
case SATA_PKT_ABORTED:
break;
case SATA_PKT_TIMEOUT:
/* Additional error setup could be done here */
break;
case SATA_PKT_RESET:
/*
* Additional error setup could be done here - device reset
*/
break;
default:
break;
}
}
#endif
/*
* SATA Trace Ring Buffer
* ----------------------
*
* Overview
*
* The SATA trace ring buffer is a ring buffer created and managed by
* the SATA framework module that can be used by any module or driver
* within the SATA framework to store debug messages.
*
* Ring Buffer Interfaces:
*
* sata_vtrace_debug() <-- Adds debug message to ring buffer
* sata_trace_debug() <-- Wraps varargs into sata_vtrace_debug()
*
* Note that the sata_trace_debug() interface was created to give
* consumers the flexibilty of sending debug messages to ring buffer
* as variable arguments. Consumers can send type va_list debug
* messages directly to sata_vtrace_debug(). The sata_trace_debug()
* and sata_vtrace_debug() relationship is similar to that of
* cmn_err(9F) and vcmn_err(9F).
*
* Below is a diagram of the SATA trace ring buffer interfaces and
* sample consumers:
*
* +---------------------------------+
* | o o SATA Framework Module |
* | o SATA o +------------------+ +------------------+
* |o Trace o <--|sata_vtrace_debug/|<-----|SATA HBA Driver #1|
* |o R-Buf o |sata_trace_debug |<--+ +------------------+
* | o o +------------------+ | +------------------+
* | o o ^ | +--|SATA HBA Driver #2|
* | | | +------------------+
* | +------------------+ |
* | |SATA Debug Message| |
* | +------------------+ |
* +---------------------------------+
*
* Supporting Routines:
*
* sata_trace_rbuf_alloc() <-- Initializes ring buffer
* sata_trace_rbuf_free() <-- Destroys ring buffer
* sata_trace_dmsg_alloc() <-- Creates or reuses buffer in ring buffer
* sata_trace_dmsg_free() <-- Destroys content of ring buffer
*
* The default SATA trace ring buffer size is defined by DMSG_RING_SIZE.
* The ring buffer size can be adjusted by setting dmsg_ring_size in
*
* The individual debug message size in the ring buffer is restricted
* to DMSG_BUF_SIZE.
*/
void
{
if (sata_debug_rbuf == NULL) {
return;
}
/*
* If max size of ring buffer is smaller than size
* required for one debug message then just return
* since we have no room for the debug message.
*/
return;
}
/* alloc or reuse on ring buffer */
/* resource allocation failed */
return;
}
}
void
{
}
/*
* This routine is used to manage debug messages
* on ring buffer.
*/
static sata_trace_dmsg_t *
sata_trace_dmsg_alloc(void)
{
return (sata_debug_rbuf->dmsgp);
}
/*
* If we're looping for the first time,
* connect the ring.
*/
return (sata_debug_rbuf->dmsgp);
}
/* If we've gotten this far then memory allocation is needed */
if (dmsg_alloc == NULL) {
return (dmsg_alloc);
} else {
}
return (sata_debug_rbuf->dmsgp);
} else {
/*
* We should only be here if we're initializing
* the ring buffer.
*/
} else {
/* Something is wrong */
return (NULL);
}
return (sata_debug_rbuf->dmsgp);
}
}
/*
* Free all messages on debug ring buffer.
*/
static void
sata_trace_dmsg_free(void)
{
/*
* If we've looped around the ring than we're done.
*/
break;
} else {
}
}
}
/*
* This function can block
*/
static void
sata_trace_rbuf_alloc(void)
{
if (dmsg_ring_size > 0) {
}
}
static void
sata_trace_rbuf_free(void)
{
}
/*
* If SATA_DEBUG is not defined then this routine is called instead
* of sata_log() via the SATA_LOG_D macro.
*/
static void
const char *fmt, ...)
{
#ifndef __lock_lint
#endif
if (sata_hba_inst != NULL) {
}
}