#include <sys/conf.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/thread.h>
#include <sys/kstat.h>
#include <sys/note.h>
#include <sys/sysevent.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/dr.h>
#include <sys/taskq.h>
#include <sys/disp.h>
#include <sys/sdt.h>
#include <sys/sata/impl/sata.h>
#include <sys/sata/sata_hba.h>
#include <sys/sata/sata_defs.h>
#include <sys/sata/sata_cfgadm.h>
#include <sys/sata/sata_blacklist.h>
#include <sys/sata/sata_satl.h>
#include <sys/scsi/impl/spc3_types.h>
#include <sys/ddifm.h>
#include <sys/fm/protocol.h>
#include <sys/fm/util.h>
#include <sys/fm/io/ddi.h>
int sata_debug_flags = 0;
int sata_msg = 0;
#define SATA_ENABLE_QUEUING 1
#define SATA_ENABLE_NCQ 2
#define SATA_ENABLE_PROCESS_EVENTS 4
#define SATA_ENABLE_PMULT_FBS 8
int sata_func_enable =
SATA_ENABLE_PROCESS_EVENTS | SATA_ENABLE_QUEUING | SATA_ENABLE_NCQ;
int sata_max_queue_depth = SATA_MAX_QUEUE_DEPTH;
static int sata_current_max_qdepth;
int sata_auto_online = 0;
#ifdef SATA_DEBUG
#define SATA_LOG_D(args) sata_log args
uint64_t mbuf_count = 0;
uint64_t mbuffail_count = 0;
sata_atapi_cmd_t sata_atapi_trace[64];
uint32_t sata_atapi_trace_index = 0;
int sata_atapi_trace_save = 1;
static void sata_save_atapi_trace(sata_pkt_txlate_t *, int);
#define SATAATAPITRACE(spx, count) \
if (sata_atapi_trace_save) \
sata_save_atapi_trace(spx, count)
#else
#define SATA_LOG_D(args) sata_trace_log args
#define SATAATAPITRACE(spx, count)
#endif
#if 0
static void
sata_test_atapi_packet_command(sata_hba_inst_t *, int);
#endif
#ifdef SATA_INJECT_FAULTS
#define SATA_INJECT_PKT_FAULT 1
uint32_t sata_inject_fault = 0;
uint32_t sata_inject_fault_count = 0;
uint32_t sata_inject_fault_pause_count = 0;
uint32_t sata_fault_type = 0;
uint32_t sata_fault_cmd = 0;
dev_info_t *sata_fault_ctrl = NULL;
sata_device_t sata_fault_device;
static void sata_inject_pkt_fault(sata_pkt_t *, int *, int);
#endif
#define LEGACY_HWID_LEN 64
static int sata_hba_open(dev_t *, int, int, cred_t *);
static int sata_hba_close(dev_t, int, int, cred_t *);
static int sata_hba_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int sata_scsi_tgt_init(dev_info_t *, dev_info_t *,
scsi_hba_tran_t *, struct scsi_device *);
static int sata_scsi_tgt_probe(struct scsi_device *,
int (*callback)(void));
static void sata_scsi_tgt_free(dev_info_t *, dev_info_t *,
scsi_hba_tran_t *, struct scsi_device *);
static int sata_scsi_start(struct scsi_address *, struct scsi_pkt *);
static int sata_scsi_abort(struct scsi_address *, struct scsi_pkt *);
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);
static struct scsi_pkt *sata_scsi_init_pkt(struct scsi_address *,
struct scsi_pkt *, struct buf *, int, int, int, int, int (*)(caddr_t),
caddr_t);
static void sata_scsi_destroy_pkt(struct scsi_address *, struct scsi_pkt *);
static void sata_scsi_dmafree(struct scsi_address *, struct scsi_pkt *);
static void sata_scsi_sync_pkt(struct scsi_address *, struct scsi_pkt *);
static void sata_event_daemon(void *);
static void sata_event_thread_control(int);
static void sata_process_controller_events(sata_hba_inst_t *sata_hba_inst);
static void sata_process_pmult_events(sata_hba_inst_t *, uint8_t);
static void sata_process_device_reset(sata_hba_inst_t *, sata_address_t *);
static void sata_process_pmdevice_reset(sata_hba_inst_t *, sata_address_t *);
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_device_detached(sata_hba_inst_t *, sata_address_t *);
static void sata_process_pmdevice_detached(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_device_attached(sata_hba_inst_t *, sata_address_t *);
static void sata_process_pmdevice_attached(sata_hba_inst_t *,
sata_address_t *);
static void sata_process_port_pwr_change(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 *,
sata_address_t *saddr);
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 boolean_t sata_txlt_unmap_supported(sata_pkt_txlate_t *,
sata_drive_info_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_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_txlt_supported_ops(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_check_condition(sata_pkt_txlate_t *, uchar_t, uchar_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_apt_completion(sata_pkt_t *sata_pkt);
static void sata_txlt_unmap_completion(sata_pkt_t *sata_pkt);
static int sata_emul_rw_completion(sata_pkt_txlate_t *);
static void sata_fill_ata_return_desc(sata_pkt_t *, uint8_t, uint8_t,
uint8_t);
static struct scsi_extended_sense *sata_arq_sense(sata_pkt_txlate_t *);
static int sata_txlt_atapi(sata_pkt_txlate_t *);
static void sata_txlt_atapi_completion(sata_pkt_t *);
static int32_t sata_get_port_num(sata_hba_inst_t *, struct devctl_iocdata *);
static void sata_cfgadm_state(sata_hba_inst_t *, int32_t,
devctl_ap_state_t *);
static dev_info_t *sata_get_target_dip(dev_info_t *, uint8_t, uint8_t);
static dev_info_t *sata_get_scsi_target_dip(dev_info_t *, sata_address_t *);
static dev_info_t *sata_devt_to_devinfo(dev_t);
static int sata_ioctl_connect(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_disconnect(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_configure(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_unconfigure(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_activate(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_deactivate(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_reset_port(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_reset_device(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_reset_all(sata_hba_inst_t *);
static int sata_ioctl_port_self_test(sata_hba_inst_t *, sata_device_t *);
static int sata_ioctl_get_device_path(sata_hba_inst_t *, sata_device_t *,
sata_ioctl_data_t *, int mode);
static int sata_ioctl_get_ap_type(sata_hba_inst_t *, sata_device_t *,
sata_ioctl_data_t *, int mode);
static int sata_ioctl_get_model_info(sata_hba_inst_t *, sata_device_t *,
sata_ioctl_data_t *, int mode);
static int sata_ioctl_get_revfirmware_info(sata_hba_inst_t *, sata_device_t *,
sata_ioctl_data_t *, int mode);
static int sata_ioctl_get_serialnumber_info(sata_hba_inst_t *,
sata_device_t *, sata_ioctl_data_t *, int mode);
static void sata_remove_hba_instance(dev_info_t *);
static int sata_validate_sata_hba_tran(dev_info_t *, sata_hba_tran_t *);
static void sata_probe_ports(sata_hba_inst_t *);
static void sata_probe_pmports(sata_hba_inst_t *, uint8_t);
static int sata_reprobe_port(sata_hba_inst_t *, sata_device_t *, int);
static int sata_reprobe_pmult(sata_hba_inst_t *, sata_device_t *, int);
static int sata_reprobe_pmport(sata_hba_inst_t *, sata_device_t *, int);
static int sata_alloc_pmult(sata_hba_inst_t *, sata_device_t *);
static void sata_free_pmult(sata_hba_inst_t *, sata_device_t *);
static int sata_add_device(dev_info_t *, sata_hba_inst_t *, sata_device_t *);
static int sata_offline_device(sata_hba_inst_t *, sata_device_t *,
sata_drive_info_t *);
static dev_info_t *sata_create_target_node(dev_info_t *, 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 sata_pkt_t *sata_pkt_alloc(sata_pkt_txlate_t *, int (*)(caddr_t));
static void sata_pkt_free(sata_pkt_txlate_t *);
static int sata_dma_buf_setup(sata_pkt_txlate_t *, int, int (*)(caddr_t),
caddr_t, ddi_dma_attr_t *);
static void sata_common_free_dma_rsrcs(sata_pkt_txlate_t *);
static int sata_probe_device(sata_hba_inst_t *, sata_device_t *);
static sata_drive_info_t *sata_get_device_info(sata_hba_inst_t *,
sata_device_t *);
static int sata_identify_device(sata_hba_inst_t *, sata_drive_info_t *);
static void sata_reidentify_device(sata_pkt_txlate_t *);
static struct buf *sata_alloc_local_buffer(sata_pkt_txlate_t *, size_t);
static void sata_free_local_buffer(sata_pkt_txlate_t *);
static uint64_t sata_check_capacity(sata_drive_info_t *);
void sata_adjust_dma_attr(sata_drive_info_t *, ddi_dma_attr_t *,
ddi_dma_attr_t *);
static int sata_fetch_device_identify_data(sata_hba_inst_t *,
sata_drive_info_t *);
static void sata_update_port_info(sata_hba_inst_t *, sata_device_t *);
static void sata_update_pmport_info(sata_hba_inst_t *, sata_device_t *);
static int sata_set_dma_mode(sata_hba_inst_t *, sata_drive_info_t *);
static int sata_set_cache_mode(sata_hba_inst_t *, sata_drive_info_t *, int);
static int sata_set_rmsn(sata_hba_inst_t *, sata_drive_info_t *, int);
static int sata_set_drive_features(sata_hba_inst_t *,
sata_drive_info_t *, int flag);
static void sata_init_write_cache_mode(sata_drive_info_t *sdinfo);
static int sata_initialize_device(sata_hba_inst_t *, sata_drive_info_t *);
static void sata_identdev_to_inquiry(sata_hba_inst_t *, sata_drive_info_t *,
uint8_t *);
static int sata_get_atapi_inquiry_data(sata_hba_inst_t *, sata_address_t *,
struct scsi_inquiry *);
static int sata_build_msense_page_1(sata_drive_info_t *, int, uint8_t *);
static int sata_build_msense_page_8(sata_drive_info_t *, int, uint8_t *);
static int sata_build_msense_page_1a(sata_drive_info_t *, int, uint8_t *);
static int sata_build_msense_page_1c(sata_drive_info_t *, int, uint8_t *);
static int sata_build_msense_page_30(sata_drive_info_t *, int, uint8_t *);
static int sata_mode_select_page_8(sata_pkt_txlate_t *,
struct mode_cache_scsi3 *, int, int *, int *, int *);
static int sata_mode_select_page_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 *);
static int sata_build_lsense_page_0(sata_drive_info_t *, uint8_t *);
static int sata_build_lsense_page_03(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_0d(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_0e(sata_drive_info_t *, uint8_t *,
sata_pkt_txlate_t *);
static int sata_build_lsense_page_10(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_11(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_19(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_2f(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static int sata_build_lsense_page_30(sata_drive_info_t *, uint8_t *,
sata_hba_inst_t *);
static void sata_set_arq_data(sata_pkt_t *);
static void sata_build_read_verify_cmd(sata_cmd_t *, uint16_t, uint64_t);
static void sata_build_generic_cmd(sata_cmd_t *, uint8_t);
static uint8_t sata_get_standby_timer(uint8_t *timer);
static void sata_save_drive_settings(sata_drive_info_t *);
static void sata_show_drive_info(sata_hba_inst_t *, sata_drive_info_t *);
static void sata_show_pmult_info(sata_hba_inst_t *, sata_device_t *);
static void sata_log(sata_hba_inst_t *, uint_t, char *fmt, ...);
#ifndef SATA_DEBUG
static void sata_trace_log(sata_hba_inst_t *, uint_t, const char *fmt, ...);
#endif
static int sata_fetch_smart_return_status(sata_hba_inst_t *,
sata_drive_info_t *);
static int sata_fetch_smart_data(sata_hba_inst_t *, sata_drive_info_t *,
struct smart_data *);
static int sata_smart_selftest_log(sata_hba_inst_t *,
sata_drive_info_t *,
struct smart_selftest_log *);
static int sata_ext_smart_selftest_read_log(sata_hba_inst_t *,
sata_drive_info_t *, struct smart_ext_selftest_log *, uint16_t);
static int sata_read_log_ext(sata_hba_inst_t *, sata_drive_info_t *, uint8_t,
uint16_t, void *, uint16_t);
static int sata_smart_read_log(sata_hba_inst_t *, sata_drive_info_t *,
uint8_t *, uint8_t, uint8_t);
static int sata_read_log_ext_directory(sata_hba_inst_t *, sata_drive_info_t *,
struct read_log_ext_directory *);
static void sata_gen_sysevent(sata_hba_inst_t *, sata_address_t *, int);
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 boolean_t sata_check_device_removed(dev_info_t *);
static void sata_set_target_node_cleanup(sata_hba_inst_t *, sata_address_t *);
static int sata_ncq_err_ret_cmd_setup(sata_pkt_txlate_t *,
sata_drive_info_t *);
static int sata_atapi_err_ret_cmd_setup(sata_pkt_txlate_t *,
sata_drive_info_t *);
static void sata_atapi_packet_cmd_setup(sata_cmd_t *, sata_drive_info_t *);
static void sata_fixed_sense_data_preset(struct scsi_extended_sense *);
static void sata_target_devid_register(dev_info_t *, sata_drive_info_t *);
static int sata_check_modser(char *, int);
static boolean_t sata_check_for_dma_error(dev_info_t *, sata_pkt_txlate_t *);
static struct cb_ops sata_cb_ops = {
sata_hba_open,
sata_hba_close,
nodev,
nodev,
nodev,
nodev,
nodev,
sata_hba_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_NEW | D_MP,
CB_REV,
nodev,
nodev
};
extern struct mod_ops mod_miscops;
extern uchar_t scsi_cdb_size[];
static struct modlmisc modlmisc = {
&mod_miscops,
"SATA Module"
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlmisc,
NULL
};
static int sata_default_pkt_time = 60;
static ddi_device_acc_attr_t sata_acc_attr = {
DDI_DEVICE_ATTR_V0,
DDI_STRUCTURE_LE_ACC,
DDI_STRICTORDER_ACC
};
static kmutex_t sata_mutex;
static kmutex_t sata_log_mutex;
static char sata_log_buf[256];
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;
int sata_write_cache = 1;
int sata_atapicdvd_write_cache = 1;
int sata_atapitape_write_cache = 1;
int sata_atapidisk_write_cache = 1;
static sata_hba_inst_t *sata_hba_list = NULL;
static sata_hba_inst_t *sata_hba_list_tail = NULL;
static kmutex_t sata_event_mutex;
static kcondvar_t sata_event_cv;
static kthread_t *sata_event_thread = NULL;
static int sata_event_thread_terminate = 0;
static int sata_event_pending = 0;
static int sata_event_thread_active = 0;
extern pri_t minclsyspri;
static const sata_cmd_t sata_rle_cmd = {
SATA_CMD_REV,
NULL,
{
SATA_DIR_READ
},
ATA_ADDR_LBA48,
0,
0,
0,
0,
0,
1,
READ_LOG_EXT_NCQ_ERROR_RECOVERY,
0,
0,
0,
SATAC_READ_LOG_EXT,
0,
0,
0,
};
static const uint8_t sata_rqsense_cdb[SATA_ATAPI_RQSENSE_CDB_LEN] = {
SCMD_REQUEST_SENSE,
0,
0,
0,
SATA_ATAPI_MIN_RQSENSE_LEN,
0
};
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", scsi_hba_tran))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", scsi_device))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", dev_ops))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", scsi_extended_sense))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", scsi_arq_status))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", ddi_dma_attr))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", ddi_dma_cookie_t))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", devctl_ap_state))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", dev_info::devi_state))
_NOTE(MUTEX_PROTECTS_DATA(sata_mutex, sata_hba_list))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_hba_list))
_NOTE(MUTEX_PROTECTS_DATA(sata_mutex, sata_hba_inst::satahba_next))
_NOTE(MUTEX_PROTECTS_DATA(sata_mutex, sata_hba_inst::satahba_prev))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", \
sata_hba_inst::satahba_scsi_tran))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", sata_hba_inst::satahba_tran))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", sata_hba_inst::satahba_dip))
_NOTE(SCHEME_PROTECTS_DATA("Scheme", sata_hba_inst::satahba_attached))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_hba_inst::satahba_dev_port))
_NOTE(MUTEX_PROTECTS_DATA(sata_hba_inst::satahba_mutex,
sata_hba_inst::satahba_event_flags))
_NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \
sata_cport_info::cport_devp))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_cport_info::cport_devp))
_NOTE(SCHEME_PROTECTS_DATA("Scheme", sata_cport_info::cport_addr))
_NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \
sata_cport_info::cport_dev_type))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_cport_info::cport_dev_type))
_NOTE(MUTEX_PROTECTS_DATA(sata_cport_info::cport_mutex, \
sata_cport_info::cport_state))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_cport_info::cport_state))
_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \
sata_pmport_info::pmport_state))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_state))
_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \
sata_pmport_info::pmport_dev_type))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_dev_type))
_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \
sata_pmport_info::pmport_sata_drive))
_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \
sata_pmport_info::pmport_tgtnode_clean))
_NOTE(MUTEX_PROTECTS_DATA(sata_pmport_info::pmport_mutex, \
sata_pmport_info::pmport_event_flags))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmport_info::pmport_sata_drive))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmult_info::pmult_dev_port))
_NOTE(DATA_READABLE_WITHOUT_LOCK(sata_pmult_info::pmult_num_dev_ports))
#ifdef SATA_DEBUG
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", mbuf_count))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", mbuffail_count))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", sata_atapi_trace))
_NOTE(SCHEME_PROTECTS_DATA("No Mutex Needed", sata_atapi_trace_index))
#endif
struct sata_txlt_buf {
uint8_t *stb_ptr;
uint32_t stb_idx;
uint32_t stb_len;
};
static inline void
sbuf_init(struct sata_txlt_buf *sbuf, struct buf *bp, uint32_t alc_len)
{
sbuf->stb_ptr = (uint8_t *)bp->b_un.b_addr;
sbuf->stb_idx = 0;
sbuf->stb_len = MIN(bp->b_bcount, alc_len);
}
static inline void
sbuf_put8(struct sata_txlt_buf *sb, uint8_t val)
{
if (sb->stb_idx >= sb->stb_len) {
sb->stb_idx++;
return;
}
sb->stb_ptr[sb->stb_idx++] = val;
}
static inline void
sbuf_put16(struct sata_txlt_buf *sb, uint16_t val)
{
sbuf_put8(sb, val >> 8);
sbuf_put8(sb, val & 0xff);
}
static inline void
sbuf_put32(struct sata_txlt_buf *sb, uint32_t val)
{
sbuf_put8(sb, val >> 24);
sbuf_put8(sb, (val >> 16) & 0xff);
sbuf_put8(sb, (val >> 8) & 0xff);
sbuf_put8(sb, val & 0xff);
}
static inline void
sbuf_copy(struct sata_txlt_buf *sb, const void *src, size_t len)
{
ssize_t max = sb->stb_len - sb->stb_idx;
if (len == 0)
return;
if (max <= 0) {
sb->stb_idx += len;
return;
}
size_t amt = MIN(max, len);
ASSERT3U(sb->stb_idx + amt, <=, sb->stb_len);
bcopy(src, sb->stb_ptr + sb->stb_idx, amt);
sb->stb_idx += len;
}
static inline void
sbuf_set_len(struct sata_txlt_buf *sb, uint32_t offset, uint32_t llen,
uint32_t adj)
{
uint_t shift = (llen - 1) * NBBY;
uint64_t val = sb->stb_idx - adj;
ASSERT3U(adj, <=, sb->stb_idx);
ASSERT3U(llen, >, 0);
for (uint_t i = 0; i < llen; i++) {
if (offset >= sb->stb_len)
return;
sb->stb_ptr[offset++] = (val >> shift) & 0xff;
shift -= NBBY;
}
}
static inline size_t
sbuf_resid(const struct sata_txlt_buf *sb, const struct buf *bp,
int32_t alc_len)
{
const size_t expected = MIN(alc_len, sb->stb_idx);
const size_t written = MIN(bp->b_bcount, sb->stb_idx);
ASSERT3U(written, <=, expected);
return ((written <= expected) ? 0 : expected - written);
}
int
_init()
{
int rval;
mutex_init(&sata_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&sata_event_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&sata_log_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&sata_event_cv, NULL, CV_DRIVER, NULL);
sata_trace_rbuf_alloc();
if ((rval = mod_install(&modlinkage)) != 0) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata: _init: mod_install failed\n");
#endif
sata_trace_rbuf_free();
mutex_destroy(&sata_log_mutex);
cv_destroy(&sata_event_cv);
mutex_destroy(&sata_event_mutex);
mutex_destroy(&sata_mutex);
}
return (rval);
}
int
_fini()
{
int rval;
if ((rval = mod_remove(&modlinkage)) != 0)
return (rval);
sata_trace_rbuf_free();
mutex_destroy(&sata_log_mutex);
cv_destroy(&sata_event_cv);
mutex_destroy(&sata_event_mutex);
mutex_destroy(&sata_mutex);
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
sata_hba_init(struct modlinkage *modlp)
{
int rval;
struct dev_ops *hba_ops;
SATADBG1(SATA_DBG_HBA_IF, NULL,
"sata_hba_init: name %s \n",
((struct modldrv *)(modlp->ml_linkage[0]))->drv_linkinfo);
hba_ops = ((struct modldrv *)(modlp->ml_linkage[0]))->drv_dev_ops;
hba_ops->devo_cb_ops = &sata_cb_ops;
if ((rval = scsi_hba_init(modlp)) != 0) {
SATADBG1(SATA_DBG_HBA_IF, NULL,
"sata_hba_init: scsi hba init failed\n", NULL);
return (rval);
}
return (0);
}
#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
int
sata_hba_attach(dev_info_t *dip, sata_hba_tran_t *sata_tran,
ddi_attach_cmd_t cmd)
{
sata_hba_inst_t *sata_hba_inst;
scsi_hba_tran_t *scsi_tran = NULL;
int hba_attach_state = 0;
char taskq_name[MAXPATHLEN];
SATADBG3(SATA_DBG_HBA_IF, NULL,
"sata_hba_attach: node %s (%s%d)\n",
ddi_node_name(dip), ddi_driver_name(dip),
ddi_get_instance(dip));
if (cmd == DDI_RESUME) {
return (DDI_FAILURE);
}
if (cmd != DDI_ATTACH) {
return (DDI_FAILURE);
}
if (sata_validate_sata_hba_tran(dip, sata_tran) != SATA_SUCCESS) {
SATA_LOG_D((NULL, CE_WARN,
"sata_hba_attach: invalid sata_hba_tran"));
return (DDI_FAILURE);
}
scsi_tran = scsi_hba_tran_alloc(dip, SCSI_HBA_CANSLEEP);
if (scsi_tran == NULL)
return (DDI_FAILURE);
sata_hba_inst = kmem_zalloc(sizeof (struct sata_hba_inst), KM_SLEEP);
ASSERT(sata_hba_inst != NULL);
mutex_init(&sata_hba_inst->satahba_mutex, NULL, MUTEX_DRIVER, NULL);
hba_attach_state |= HBA_ATTACH_STAGE_SATA_HBA_INST;
scsi_tran->tran_hba_private = sata_hba_inst;
scsi_tran->tran_tgt_private = NULL;
scsi_tran->tran_tgt_init = sata_scsi_tgt_init;
scsi_tran->tran_tgt_probe = sata_scsi_tgt_probe;
scsi_tran->tran_tgt_free = sata_scsi_tgt_free;
scsi_tran->tran_start = sata_scsi_start;
scsi_tran->tran_reset = sata_scsi_reset;
scsi_tran->tran_abort = sata_scsi_abort;
scsi_tran->tran_getcap = sata_scsi_getcap;
scsi_tran->tran_setcap = sata_scsi_setcap;
scsi_tran->tran_init_pkt = sata_scsi_init_pkt;
scsi_tran->tran_destroy_pkt = sata_scsi_destroy_pkt;
scsi_tran->tran_dmafree = sata_scsi_dmafree;
scsi_tran->tran_sync_pkt = sata_scsi_sync_pkt;
scsi_tran->tran_reset_notify = NULL;
scsi_tran->tran_get_bus_addr = NULL;
scsi_tran->tran_quiesce = NULL;
scsi_tran->tran_unquiesce = NULL;
scsi_tran->tran_bus_reset = NULL;
if (scsi_hba_attach_setup(dip, sata_tran->sata_tran_hba_dma_attr,
scsi_tran, 0) != DDI_SUCCESS) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "?SATA: %s%d hba scsi attach failed",
ddi_driver_name(dip), ddi_get_instance(dip));
#endif
goto fail;
}
hba_attach_state |= HBA_ATTACH_STAGE_SCSI_ATTACHED;
if (!ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "sata")) {
if (ddi_prop_update_int(DDI_DEV_T_NONE, dip,
"sata", 1) != DDI_PROP_SUCCESS) {
SATA_LOG_D((NULL, CE_WARN, "sata_hba_attach: "
"failed to create hba sata prop"));
goto fail;
}
}
sata_hba_inst->satahba_scsi_tran = scsi_tran;
sata_hba_inst->satahba_tran = sata_tran;
sata_hba_inst->satahba_dip = dip;
taskq_name[0] = '\0';
(void) strlcat(taskq_name, DEVI(dip)->devi_node_name,
sizeof (taskq_name));
(void) snprintf(taskq_name + strlen(taskq_name),
sizeof (taskq_name) - strlen(taskq_name),
"-%d", DEVI(dip)->devi_instance);
sata_hba_inst->satahba_taskq = taskq_create(taskq_name, 1,
minclsyspri, 1, sata_tran->sata_tran_hba_num_cports * 4,
TASKQ_DYNAMIC);
hba_attach_state |= HBA_ATTACH_STAGE_SETUP;
sata_event_thread_control(1);
mutex_enter(&sata_mutex);
if (sata_hba_list == NULL) {
sata_current_max_qdepth = sata_max_queue_depth;
if (sata_current_max_qdepth > 32)
sata_current_max_qdepth = 32;
else if (sata_current_max_qdepth < 1)
sata_current_max_qdepth = 1;
}
sata_hba_inst->satahba_next = NULL;
sata_hba_inst->satahba_prev = sata_hba_list_tail;
if (sata_hba_list == NULL) {
sata_hba_list = sata_hba_inst;
}
if (sata_hba_list_tail != NULL) {
sata_hba_list_tail->satahba_next = sata_hba_inst;
}
sata_hba_list_tail = sata_hba_inst;
mutex_exit(&sata_mutex);
hba_attach_state |= HBA_ATTACH_STAGE_LINKED;
ASSERT(ddi_get_instance(dip) <= (L_MAXMIN >> INST_MINOR_SHIFT));
if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
INST2DEVCTL(ddi_get_instance(dip)),
DDI_NT_SATA_NEXUS, 0) != DDI_SUCCESS) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata_hba_attach: "
"cannot create devctl minor node");
#endif
goto fail;
}
sata_hba_inst->satahba_attached = 1;
sata_probe_ports(sata_hba_inst);
return (DDI_SUCCESS);
fail:
if (hba_attach_state & HBA_ATTACH_STAGE_LINKED) {
(void) sata_remove_hba_instance(dip);
if (sata_hba_list == NULL)
sata_event_thread_control(0);
}
if (hba_attach_state & HBA_ATTACH_STAGE_SETUP) {
(void) ddi_prop_remove(DDI_DEV_T_ANY, dip, "sata");
taskq_destroy(sata_hba_inst->satahba_taskq);
}
if (hba_attach_state & HBA_ATTACH_STAGE_SCSI_ATTACHED)
(void) scsi_hba_detach(dip);
if (hba_attach_state & HBA_ATTACH_STAGE_SATA_HBA_INST) {
mutex_destroy(&sata_hba_inst->satahba_mutex);
kmem_free((void *)sata_hba_inst,
sizeof (struct sata_hba_inst));
scsi_hba_tran_free(scsi_tran);
}
sata_log(NULL, CE_WARN, "?SATA: %s%d hba attach failed",
ddi_driver_name(dip), ddi_get_instance(dip));
return (DDI_FAILURE);
}
int
sata_hba_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
dev_info_t *tdip;
sata_hba_inst_t *sata_hba_inst;
scsi_hba_tran_t *scsi_hba_tran;
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pminfo;
sata_drive_info_t *sdinfo;
sata_device_t sdevice;
int ncport, npmport;
SATADBG3(SATA_DBG_HBA_IF, NULL, "sata_hba_detach: node %s (%s%d)\n",
ddi_node_name(dip), ddi_driver_name(dip), ddi_get_instance(dip));
switch (cmd) {
case DDI_DETACH:
if ((scsi_hba_tran = ddi_get_driver_private(dip)) == NULL)
return (DDI_FAILURE);
sata_hba_inst = scsi_hba_tran->tran_hba_private;
if (sata_hba_inst == NULL)
return (DDI_FAILURE);
if (scsi_hba_detach(dip) == DDI_FAILURE) {
sata_hba_inst->satahba_attached = 1;
return (DDI_FAILURE);
}
for (ncport = 0; ncport < SATA_NUM_CPORTS(sata_hba_inst);
ncport++) {
cportinfo = SATA_CPORT_INFO(sata_hba_inst, ncport);
if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) {
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
if (sdinfo != NULL) {
tdip = sata_get_target_dip(dip,
ncport, 0);
if (tdip != NULL) {
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
SATA_LOG_D((
sata_hba_inst,
CE_WARN,
"sata_hba_detach: "
"Target node not "
"removed !"));
return (DDI_FAILURE);
}
}
}
} else {
mutex_enter(&cportinfo->cport_mutex);
pminfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
if (pminfo == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_detach: Port multiplier "
"not ready yet!"));
mutex_exit(&cportinfo->cport_mutex);
return (DDI_FAILURE);
}
for (npmport = 0; npmport < SATA_NUM_PMPORTS(
sata_hba_inst, ncport); npmport++) {
tdip = sata_get_target_dip(dip, ncport,
npmport);
if (tdip != NULL) {
if (ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
SATA_LOG_D((
sata_hba_inst,
CE_WARN,
"sata_hba_detach: "
"Target node not "
"removed !"));
mutex_exit(&cportinfo->
cport_mutex);
return (DDI_FAILURE);
}
}
}
mutex_exit(&cportinfo->cport_mutex);
}
}
sata_hba_inst->satahba_attached = 0;
mutex_enter(&sata_mutex);
if (sata_hba_list->satahba_next == NULL) {
mutex_exit(&sata_mutex);
sata_event_thread_control(0);
mutex_enter(&sata_mutex);
}
mutex_exit(&sata_mutex);
sata_remove_hba_instance(dip);
for (ncport = 0; ncport < SATA_NUM_CPORTS(sata_hba_inst);
ncport++) {
cportinfo = SATA_CPORT_INFO(sata_hba_inst, ncport);
if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) {
sdinfo =
cportinfo->cport_devp.cport_sata_drive;
if (sdinfo != NULL) {
kmem_free(sdinfo,
sizeof (sata_drive_info_t));
}
mutex_destroy(&cportinfo->cport_mutex);
kmem_free(cportinfo,
sizeof (sata_cport_info_t));
} else {
sdevice.satadev_addr.cport = (uint8_t)ncport;
sdevice.satadev_addr.qual = SATA_ADDR_PMULT;
sata_free_pmult(sata_hba_inst, &sdevice);
}
}
scsi_hba_tran_free(sata_hba_inst->satahba_scsi_tran);
(void) ddi_prop_remove(DDI_DEV_T_ANY, dip, "sata");
taskq_destroy(sata_hba_inst->satahba_taskq);
mutex_destroy(&sata_hba_inst->satahba_mutex);
kmem_free((void *)sata_hba_inst,
sizeof (struct sata_hba_inst));
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
}
void
sata_hba_fini(struct modlinkage *modlp)
{
SATADBG1(SATA_DBG_HBA_IF, NULL,
"sata_hba_fini: name %s\n",
((struct modldrv *)(modlp->ml_linkage[0]))->drv_linkinfo);
scsi_hba_fini(modlp);
}
static int
sata_hba_open(dev_t *devp, int flags, int otyp, cred_t *credp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(credp))
#endif
int rv = 0;
dev_info_t *dip;
scsi_hba_tran_t *scsi_hba_tran;
sata_hba_inst_t *sata_hba_inst;
SATADBG1(SATA_DBG_IOCTL_IF, NULL, "sata_hba_open: entered", NULL);
if (otyp != OTYP_CHR)
return (EINVAL);
dip = sata_devt_to_devinfo(*devp);
if (dip == NULL)
return (ENXIO);
if ((scsi_hba_tran = ddi_get_driver_private(dip)) == NULL)
return (ENXIO);
sata_hba_inst = scsi_hba_tran->tran_hba_private;
if (sata_hba_inst == NULL || sata_hba_inst->satahba_attached == 0)
return (ENXIO);
mutex_enter(&sata_mutex);
if (flags & FEXCL) {
if (sata_hba_inst->satahba_open_flag != 0) {
rv = EBUSY;
} else {
sata_hba_inst->satahba_open_flag =
SATA_DEVCTL_EXOPENED;
}
} else {
if (sata_hba_inst->satahba_open_flag == SATA_DEVCTL_EXOPENED) {
rv = EBUSY;
} else {
sata_hba_inst->satahba_open_flag =
SATA_DEVCTL_SOPENED;
}
}
mutex_exit(&sata_mutex);
return (rv);
}
static int
sata_hba_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(credp))
_NOTE(ARGUNUSED(flag))
#endif
dev_info_t *dip;
scsi_hba_tran_t *scsi_hba_tran;
sata_hba_inst_t *sata_hba_inst;
SATADBG1(SATA_DBG_IOCTL_IF, NULL, "sata_hba_close: entered", NULL);
if (otyp != OTYP_CHR)
return (EINVAL);
dip = sata_devt_to_devinfo(dev);
if (dip == NULL)
return (ENXIO);
if ((scsi_hba_tran = ddi_get_driver_private(dip)) == NULL)
return (ENXIO);
sata_hba_inst = scsi_hba_tran->tran_hba_private;
if (sata_hba_inst == NULL || sata_hba_inst->satahba_attached == 0)
return (ENXIO);
mutex_enter(&sata_mutex);
sata_hba_inst->satahba_open_flag = 0;
mutex_exit(&sata_mutex);
return (0);
}
static int
sata_hba_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(credp))
_NOTE(ARGUNUSED(rvalp))
#endif
int rv = 0;
int32_t comp_port = -1;
dev_info_t *dip;
devctl_ap_state_t ap_state;
struct devctl_iocdata *dcp = NULL;
scsi_hba_tran_t *scsi_hba_tran;
sata_hba_inst_t *sata_hba_inst;
sata_device_t sata_device;
sata_cport_info_t *cportinfo;
int cport, pmport, qual;
int rval = SATA_SUCCESS;
dip = sata_devt_to_devinfo(dev);
if (dip == NULL)
return (ENXIO);
if ((scsi_hba_tran = ddi_get_driver_private(dip)) == NULL)
return (ENXIO);
sata_hba_inst = scsi_hba_tran->tran_hba_private;
if (sata_hba_inst == NULL)
return (ENXIO);
if (sata_hba_inst->satahba_tran == NULL)
return (ENXIO);
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_DEVICE_REMOVE:
case DEVCTL_BUS_GETSTATE:
return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
}
cport = pmport = qual = 0;
cportinfo = NULL;
if (cmd != DEVCTL_AP_CONTROL && IS_DEVCTL(cmd)) {
if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
return (EFAULT);
if ((comp_port = sata_get_port_num(sata_hba_inst, dcp)) ==
-1) {
if (dcp)
ndi_dc_freehdl(dcp);
return (EINVAL);
}
cport = SCSI_TO_SATA_CPORT(comp_port);
pmport = SCSI_TO_SATA_PMPORT(comp_port);
qual = SCSI_TO_SATA_ADDR_QUAL(comp_port);
if (sata_validate_sata_address(sata_hba_inst, cport, pmport,
qual) != 0) {
ndi_dc_freehdl(dcp);
return (EINVAL);
}
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
if (cportinfo->cport_event_flags & SATA_EVNT_LOCK_PORT_BUSY) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
ndi_dc_freehdl(dcp);
return (EBUSY);
}
cportinfo->cport_event_flags |= SATA_APCTL_LOCK_PORT_BUSY;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
sata_device.satadev_addr.cport = cport;
sata_device.satadev_addr.pmport = pmport;
sata_device.satadev_addr.qual = qual;
sata_device.satadev_rev = SATA_DEVICE_REV;
}
switch (cmd) {
case DEVCTL_AP_DISCONNECT:
rv = sata_ioctl_disconnect(sata_hba_inst, &sata_device);
break;
case DEVCTL_AP_UNCONFIGURE:
rv = sata_ioctl_unconfigure(sata_hba_inst, &sata_device);
break;
case DEVCTL_AP_CONNECT:
{
rv = sata_ioctl_connect(sata_hba_inst, &sata_device);
break;
}
case DEVCTL_AP_CONFIGURE:
{
rv = sata_ioctl_configure(sata_hba_inst, &sata_device);
break;
}
case DEVCTL_AP_GETSTATE:
sata_cfgadm_state(sata_hba_inst, comp_port, &ap_state);
ap_state.ap_last_change = (time_t)-1;
ap_state.ap_error_code = 0;
ap_state.ap_in_transition = 0;
if (ndi_dc_return_ap_state(&ap_state, dcp) != NDI_SUCCESS) {
rv = EFAULT;
}
break;
case DEVCTL_AP_CONTROL:
{
sata_ioctl_data_t ioc;
ASSERT(dcp == NULL);
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(mode & FMODELS) ==
DDI_MODEL_ILP32) {
sata_ioctl_data_32_t ioc32;
if (ddi_copyin((void *)arg, (void *)&ioc32,
sizeof (ioc32), mode) != 0) {
rv = EFAULT;
break;
}
ioc.cmd = (uint_t)ioc32.cmd;
ioc.port = (uint_t)ioc32.port;
ioc.get_size = (uint_t)ioc32.get_size;
ioc.buf = (caddr_t)(uintptr_t)ioc32.buf;
ioc.bufsiz = (uint_t)ioc32.bufsiz;
ioc.misc_arg = (uint_t)ioc32.misc_arg;
} else
#endif
if (ddi_copyin((void *)arg, (void *)&ioc, sizeof (ioc),
mode) != 0) {
return (EFAULT);
}
SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: DEVCTL_AP_CONTROL "
"cmd 0x%x, port 0x%x", ioc.cmd, ioc.port);
if (ioc.get_size != 0 && ioc.bufsiz != (sizeof (uint32_t))) {
return (EINVAL);
}
cport = SCSI_TO_SATA_CPORT(ioc.port);
pmport = SCSI_TO_SATA_PMPORT(ioc.port);
qual = SCSI_TO_SATA_ADDR_QUAL(ioc.port);
SATADBG3(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: target port is %d:%d (%d)",
cport, pmport, qual);
if (sata_validate_sata_address(sata_hba_inst, cport,
pmport, qual) != 0)
return (EINVAL);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
if (cportinfo->cport_event_flags & SATA_EVNT_LOCK_PORT_BUSY) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
return (EBUSY);
}
cportinfo->cport_event_flags |= SATA_APCTL_LOCK_PORT_BUSY;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
sata_device.satadev_addr.cport = cport;
sata_device.satadev_addr.pmport = pmport;
sata_device.satadev_addr.qual = qual;
sata_device.satadev_rev = SATA_DEVICE_REV;
switch (ioc.cmd) {
case SATA_CFGA_RESET_PORT:
rv = sata_ioctl_reset_port(sata_hba_inst, &sata_device);
break;
case SATA_CFGA_RESET_DEVICE:
rv = sata_ioctl_reset_device(sata_hba_inst,
&sata_device);
break;
case SATA_CFGA_RESET_ALL:
rv = sata_ioctl_reset_all(sata_hba_inst);
return (rv);
case SATA_CFGA_PORT_DEACTIVATE:
rv = sata_ioctl_deactivate(sata_hba_inst, &sata_device);
break;
case SATA_CFGA_PORT_ACTIVATE:
rv = sata_ioctl_activate(sata_hba_inst, &sata_device);
break;
case SATA_CFGA_PORT_SELF_TEST:
rv = sata_ioctl_port_self_test(sata_hba_inst,
&sata_device);
break;
case SATA_CFGA_GET_DEVICE_PATH:
rv = sata_ioctl_get_device_path(sata_hba_inst,
&sata_device, &ioc, mode);
break;
case SATA_CFGA_GET_AP_TYPE:
rv = sata_ioctl_get_ap_type(sata_hba_inst,
&sata_device, &ioc, mode);
break;
case SATA_CFGA_GET_MODEL_INFO:
rv = sata_ioctl_get_model_info(sata_hba_inst,
&sata_device, &ioc, mode);
break;
case SATA_CFGA_GET_REVFIRMWARE_INFO:
rv = sata_ioctl_get_revfirmware_info(sata_hba_inst,
&sata_device, &ioc, mode);
break;
case SATA_CFGA_GET_SERIALNUMBER_INFO:
rv = sata_ioctl_get_serialnumber_info(sata_hba_inst,
&sata_device, &ioc, mode);
break;
default:
rv = EINVAL;
break;
}
break;
}
default:
{
sata_hba_tran_t *sata_tran = sata_hba_inst->satahba_tran;
dev_info_t *mydip = SATA_DIP(sata_hba_inst);
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"IOCTL 0x%2x not supported in SATA framework, "
"passthrough to HBA", cmd);
if (sata_tran->sata_tran_ioctl == NULL) {
rv = EINVAL;
break;
}
rval = (*sata_tran->sata_tran_ioctl)(mydip, cmd, arg);
if (rval != 0) {
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"IOCTL 0x%2x failed in HBA", cmd);
rv = rval;
}
break;
}
}
if (dcp) {
ndi_dc_freehdl(dcp);
}
if (IS_DEVCTL(cmd)) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_APCTL_LOCK_PORT_BUSY;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
}
return (rv);
}
sata_pkt_t *
sata_get_error_retrieval_pkt(dev_info_t *dip, sata_device_t *sata_device,
int pkt_type)
{
sata_hba_inst_t *sata_hba_inst;
sata_pkt_txlate_t *spx;
sata_pkt_t *spkt;
sata_drive_info_t *sdinfo;
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
if (SATA_DIP(sata_hba_inst) == dip)
break;
}
mutex_exit(&sata_mutex);
ASSERT(sata_hba_inst != NULL);
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo == NULL) {
sata_log(sata_hba_inst, CE_WARN,
"sata: error recovery request for non-attached device at "
"cport %d", sata_device->satadev_addr.cport);
return (NULL);
}
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, NULL);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (NULL);
}
spkt->satapkt_device.satadev_addr = sata_device->satadev_addr;
switch (pkt_type) {
case SATA_ERR_RETR_PKT_TYPE_NCQ:
if (sata_ncq_err_ret_cmd_setup(spx, sdinfo) == SATA_SUCCESS) {
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
break;
}
return (spkt);
}
break;
case SATA_ERR_RETR_PKT_TYPE_ATAPI:
if (sata_atapi_err_ret_cmd_setup(spx, sdinfo) == SATA_SUCCESS) {
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
break;
}
return (spkt);
}
break;
default:
break;
}
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (NULL);
}
void
sata_free_error_retrieval_pkt(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
ASSERT(sata_pkt != NULL);
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
}
sata_pkt_t *
sata_get_rdwr_pmult_pkt(dev_info_t *dip, sata_device_t *sd,
uint16_t regn, uint32_t regv, uint32_t type)
{
sata_hba_inst_t *sata_hba_inst;
sata_pkt_txlate_t *spx;
sata_pkt_t *spkt;
sata_cmd_t *scmd;
ASSERT(type == SATA_RDWR_PMULT_PKT_TYPE_READ ||
type == SATA_RDWR_PMULT_PKT_TYPE_WRITE);
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
if (SATA_DIP(sata_hba_inst) == dip)
break;
}
mutex_exit(&sata_mutex);
ASSERT(sata_hba_inst != NULL);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (NULL);
}
spkt->satapkt_device.satadev_addr = sd->satadev_addr;
spkt->satapkt_device.satadev_addr.pmport = SATA_PMULT_HOSTPORT;
spkt->satapkt_device.satadev_addr.qual = SATA_ADDR_PMULT;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_POLLING;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = 10;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_features_reg = regn & 0xff;
scmd->satacmd_features_reg_ext = (regn >> 8) & 0xff;
scmd->satacmd_device_reg = sd->satadev_addr.pmport;
scmd->satacmd_addr_type = 0;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
if (type == SATA_RDWR_PMULT_PKT_TYPE_READ) {
scmd->satacmd_cmd_reg = SATAC_READ_PORTMULT;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_flags.sata_special_regs = 1;
scmd->satacmd_flags.sata_copy_out_lba_high_lsb = 1;
scmd->satacmd_flags.sata_copy_out_lba_mid_lsb = 1;
scmd->satacmd_flags.sata_copy_out_lba_low_lsb = 1;
scmd->satacmd_flags.sata_copy_out_sec_count_lsb = 1;
} else if (type == SATA_RDWR_PMULT_PKT_TYPE_WRITE) {
scmd->satacmd_cmd_reg = SATAC_WRITE_PORTMULT;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
scmd->satacmd_sec_count_lsb = regv & 0xff;
scmd->satacmd_lba_low_lsb = regv >> 8 & 0xff;
scmd->satacmd_lba_mid_lsb = regv >> 16 & 0xff;
scmd->satacmd_lba_high_lsb = regv >> 24 & 0xff;
}
return (spkt);
}
void
sata_free_rdwr_pmult_pkt(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
}
void
sata_register_pmult(dev_info_t *dip, sata_device_t *sd, sata_pmult_gscr_t *sg)
{
sata_hba_inst_t *sata_hba_inst = NULL;
sata_pmult_info_t *pmultinfo;
sata_pmult_bl_t *blp;
int cport = sd->satadev_addr.cport;
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
if (SATA_DIP(sata_hba_inst) == dip)
if (sata_hba_inst->satahba_attached == 1)
break;
}
mutex_exit(&sata_mutex);
if (sata_hba_inst == NULL)
return;
sd->satadev_add_info = sg->gscr2 & SATA_PMULT_PORTNUM_MASK;
for (blp = sata_pmult_blacklist; blp->bl_gscr0; blp++) {
if (sg->gscr0 != blp->bl_gscr0 && blp->bl_gscr0)
continue;
if (sg->gscr1 != blp->bl_gscr1 && blp->bl_gscr1)
continue;
if (sg->gscr2 != blp->bl_gscr2 && blp->bl_gscr2)
continue;
cmn_err(CE_WARN, "!Port multiplier is on the blacklist.");
sd->satadev_add_info = blp->bl_flags;
break;
}
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
if (pmultinfo != NULL) {
pmultinfo->pmult_gscr = *sg;
pmultinfo->pmult_num_dev_ports =
sd->satadev_add_info & SATA_PMULT_PORTNUM_MASK;
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"Port multiplier registered at port %d", cport);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
}
void
sata_split_model(char *model, char **vendor, char **product)
{
int i, modlen;
char *vid, *pid;
for (i = SATA_ID_MODEL_LEN; i > 0; i--)
if (model[i] == ' ' || model[i] == '\t' || model[i] == '\0')
model[i] = '\0';
else
break;
modlen = strlen(model);
for (i = 0, pid = model; i < modlen; i++, pid++)
if ((*pid == ' ') || (*pid == '\t'))
break;
if (i < modlen && i <= 8) {
vid = model;
*pid++ = '\0';
} else {
vid = NULL;
pid = model;
}
*vendor = vid;
*product = pid;
}
static int
sata_name_child(dev_info_t *dip, char *name, int namelen)
{
int target;
target = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "target", -1);
if (target == -1)
return (DDI_FAILURE);
(void) snprintf(name, namelen, "%x,0", target);
return (DDI_SUCCESS);
}
static int
sata_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(hba_dip))
_NOTE(ARGUNUSED(tgt_dip))
#endif
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
struct sata_id *sid;
sata_hba_inst_t *sata_hba_inst;
char model[SATA_ID_MODEL_LEN + 1];
char fw[SATA_ID_FW_LEN + 1];
char *vid, *pid;
if (ndi_dev_is_persistent_node(tgt_dip) == 0) {
(void) ndi_merge_node(tgt_dip, sata_name_child);
ddi_set_name_addr(tgt_dip, NULL);
return (DDI_FAILURE);
}
sata_hba_inst = (sata_hba_inst_t *)(hba_tran->tran_hba_private);
if (sata_validate_scsi_address(sata_hba_inst, &sd->sd_address,
&sata_device) != 0)
return (DDI_FAILURE);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
sdinfo = sata_get_device_info(sata_hba_inst, &sata_device);
if (sdinfo == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (DDI_FAILURE);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if ((sdinfo->satadrv_type == SATA_DTYPE_ATADISK) &&
(ddi_getprop(DDI_DEV_T_ANY, hba_dip, DDI_PROP_DONTPASS,
"use-cmdk-devid-format", 0) == 1)) {
sata_target_devid_register(tgt_dip, sdinfo);
}
sid = &sdinfo->satadrv_id;
#ifdef _LITTLE_ENDIAN
swab(sid->ai_model, model, SATA_ID_MODEL_LEN);
swab(sid->ai_fw, fw, SATA_ID_FW_LEN);
#else
bcopy(sid->ai_model, model, SATA_ID_MODEL_LEN);
bcopy(sid->ai_fw, fw, SATA_ID_FW_LEN);
#endif
model[SATA_ID_MODEL_LEN] = 0;
fw[SATA_ID_FW_LEN] = 0;
sata_split_model(model, &vid, &pid);
if (vid)
(void) scsi_device_prop_update_inqstring(sd, INQUIRY_VENDOR_ID,
vid, strlen(vid));
if (pid)
(void) scsi_device_prop_update_inqstring(sd, INQUIRY_PRODUCT_ID,
pid, strlen(pid));
(void) scsi_device_prop_update_inqstring(sd, INQUIRY_REVISION_ID,
fw, strlen(fw));
return (DDI_SUCCESS);
}
static int
sata_scsi_tgt_probe(struct scsi_device *sd, int (*callback)(void))
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(sd->sd_address.a_hba_tran->tran_hba_private);
int rval;
uint32_t pm_cap;
rval = scsi_hba_probe(sd, callback);
pm_cap = SATA_CAP_POWER_CONDITON | SATA_CAP_SMART_PAGE |
SATA_CAP_LOG_SENSE;
if (rval == SCSIPROBE_EXISTS) {
if ((ddi_prop_update_int(DDI_DEV_T_NONE, sd->sd_dev,
"pm-capable", pm_cap)) != DDI_PROP_SUCCESS) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d: "
"will not be power-managed ",
SCSI_TO_SATA_CPORT(sd->sd_address.a_target));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"failure updating pm-capable property"));
}
}
return (rval);
}
static void
sata_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(hba_dip))
#endif
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
sata_hba_inst_t *sata_hba_inst;
ddi_devid_t devid;
sata_hba_inst = (sata_hba_inst_t *)(hba_tran->tran_hba_private);
if (sata_validate_scsi_address(sata_hba_inst, &sd->sd_address,
&sata_device) == -1)
return;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
sdinfo = sata_get_device_info(sata_hba_inst, &sata_device);
if (sdinfo == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return;
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
(void) ndi_prop_remove(DDI_DEV_T_NONE, tgt_dip, "pm-capable");
if ((sdinfo->satadrv_type == SATA_DTYPE_ATADISK) &&
(ddi_getprop(DDI_DEV_T_ANY, hba_dip, DDI_PROP_DONTPASS,
"use-cmdk-devid-format", 0) == 1) &&
(ddi_devid_get(tgt_dip, &devid) == DDI_SUCCESS)) {
ddi_devid_unregister(tgt_dip);
ddi_devid_free(devid);
}
}
static struct scsi_pkt *
sata_scsi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags,
int (*callback)(caddr_t), caddr_t arg)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
dev_info_t *dip = SATA_DIP(sata_hba_inst);
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
sata_pkt_txlate_t *spx;
ddi_dma_attr_t cur_dma_attr;
int rval;
boolean_t new_pkt = B_TRUE;
ASSERT(ap->a_hba_tran->tran_hba_dip == dip);
sata_device.satadev_addr.qual = SCSI_TO_SATA_ADDR_QUAL(ap->a_target);
sata_device.satadev_addr.cport = SCSI_TO_SATA_CPORT(ap->a_target);
sata_device.satadev_addr.pmport = SCSI_TO_SATA_PMPORT(ap->a_target);
sata_device.satadev_rev = SATA_DEVICE_REV;
if (pkt == NULL) {
pkt = scsi_hba_pkt_alloc(dip, ap, cmdlen,
MAX(statuslen, SATA_MAX_SENSE_LEN),
tgtlen, sizeof (sata_pkt_txlate_t), callback, arg);
if (pkt == NULL)
return (NULL);
pkt->pkt_comp = (void (*)())NULL;
pkt->pkt_time = 0;
pkt->pkt_resid = 0;
pkt->pkt_statistics = 0;
pkt->pkt_reason = 0;
spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
bzero(spx, sizeof (sata_pkt_txlate_t));
spx->txlt_scsi_pkt = pkt;
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_sata_pkt = sata_pkt_alloc(spx, callback);
if (spx->txlt_sata_pkt == NULL) {
scsi_hba_pkt_free(ap, pkt);
return (NULL);
}
spx->txlt_sata_pkt->satapkt_device.satadev_addr =
sata_device.satadev_addr;
spx->txlt_sata_pkt->satapkt_device.satadev_rev =
sata_device.satadev_rev;
if ((bp == NULL) || (bp->b_bcount == 0))
return (pkt);
spx->txlt_total_residue = bp->b_bcount;
} else {
new_pkt = B_FALSE;
spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
if ((bp == NULL) || (bp->b_bcount == 0)) {
return (pkt);
}
}
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = bp;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_adjust_dma_attr(sdinfo,
SATA_DMA_ATTR(spx->txlt_sata_hba_inst), &cur_dma_attr);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if ((rval = sata_dma_buf_setup(spx, flags, callback, arg,
&cur_dma_attr)) != DDI_SUCCESS) {
switch (rval) {
case DDI_DMA_NORESOURCES:
bioerror(bp, 0);
break;
case DDI_DMA_NOMAPPING:
case DDI_DMA_BADATTR:
bioerror(bp, EFAULT);
break;
case DDI_DMA_TOOBIG:
default:
bioerror(bp, EINVAL);
break;
}
goto fail;
}
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip, DDI_SERVICE_UNAFFECTED);
bioerror(bp, EFAULT);
goto fail;
}
pkt->pkt_resid = spx->txlt_total_residue;
ASSERT(pkt->pkt_resid >= 0);
return (pkt);
fail:
if (new_pkt == B_TRUE) {
sata_scsi_destroy_pkt(ap, pkt);
} else {
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
sata_pkt_free(spx);
}
return (NULL);
}
typedef enum sata_cmd_info_flags {
SCF_NONE = 0,
SCF_SVC_ACT = (1 << 0),
SCF_MAPIN = (1 << 1),
} sata_cmd_info_flags_t;
#define CDB_MAXLEN 16
static const struct sata_cmd_info {
uint8_t sci_op;
uint16_t sci_svcact;
sata_cmd_info_flags_t sci_flags;
int (*sci_cmd)(sata_pkt_txlate_t *spx);
boolean_t (*sci_supported)(sata_pkt_txlate_t *,
sata_drive_info_t *);
uint8_t sci_cdbusage[CDB_MAXLEN];
} sata_cmd_info[] = {
{ SCMD_INQUIRY, 0, SCF_MAPIN, sata_txlt_inquiry, NULL,
{ SCMD_INQUIRY, 0x01, 0xff, 0xff, 0xff, 0x00 } },
{ SCMD_TEST_UNIT_READY, 0, SCF_NONE, sata_txlt_test_unit_ready, NULL,
{ SCMD_TEST_UNIT_READY, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ SCMD_START_STOP, 0, SCF_NONE, sata_txlt_start_stop_unit, NULL,
{ SCMD_START_STOP, 0x01, 0x00, 0x0f, 0xf7, 0x00 } },
{ SCMD_READ_CAPACITY, 0, SCF_MAPIN, sata_txlt_read_capacity, NULL,
{ SCMD_READ_CAPACITY, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ SCMD_SVC_ACTION_IN_G4, SSVC_ACTION_READ_CAPACITY_G4,
SCF_SVC_ACT|SCF_MAPIN, sata_txlt_read_capacity16, NULL,
{ SCMD_SVC_ACTION_IN_G4, SSVC_ACTION_READ_CAPACITY_G4, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
0x00 } },
{ SCMD_REQUEST_SENSE, 0, SCF_MAPIN, sata_txlt_request_sense, NULL,
{ SCMD_REQUEST_SENSE, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ SCMD_LOG_SENSE_G1, 0, SCF_MAPIN, sata_txlt_log_sense, NULL,
{ SCMD_LOG_SENSE_G1, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff,
0xff } },
{ SCMD_MODE_SENSE, 0, SCF_MAPIN, sata_txlt_mode_sense, NULL,
{ SCMD_MODE_SENSE, 0x08, 0xff, 0xff, 0xff, 0x00 } },
{ SCMD_MODE_SENSE_G1, 0, SCF_MAPIN, sata_txlt_mode_sense, NULL,
{ SCMD_MODE_SENSE_G1, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff,
0xff, 0x00 } },
{ SCMD_MODE_SELECT, 0, SCF_MAPIN, sata_txlt_mode_select, NULL,
{ SCMD_MODE_SELECT, 0x00, 0x00, 0x00, 0xff, 0x00 } },
{ SCMD_MODE_SELECT_G1, 0, SCF_MAPIN, sata_txlt_mode_select, NULL,
{ SCMD_MODE_SELECT_G1, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00 } },
{ SCMD_SYNCHRONIZE_CACHE, 0, SCF_NONE, sata_txlt_synchronize_cache,
NULL, { SCMD_SYNCHRONIZE_CACHE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00 } },
{ SCMD_SYNCHRONIZE_CACHE_G1, 0, SCF_NONE,
sata_txlt_synchronize_cache, NULL,
{ SCMD_SYNCHRONIZE_CACHE_G1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ SCMD_READ, 0, SCF_NONE, sata_txlt_read, NULL,
{ SCMD_READ, 0x1f, 0xff, 0xff, 0xff, 0x00 } },
{ SCMD_READ_G1, 0, SCF_NONE, sata_txlt_read, NULL,
{ SCMD_READ_G1, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00 } },
{ SCMD_READ_G4, 0, SCF_NONE, sata_txlt_read, NULL,
{ SCMD_READ_G4, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } },
{ SCMD_READ_G5, 0, SCF_NONE, sata_txlt_read, NULL,
{ SCMD_READ_G5, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x00 } },
{ SCMD_WRITE_BUFFER, 0, SCF_MAPIN, sata_txlt_write_buffer, NULL,
{ SCMD_WRITE_BUFFER, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 } },
{ SCMD_WRITE, 0, SCF_NONE, sata_txlt_write, NULL,
{ SCMD_WRITE, 0x1f, 0xff, 0xff, 0xff, 0x00, 0x00 } },
{ SCMD_WRITE_G1, 0, SCF_NONE, sata_txlt_write, NULL,
{ SCMD_WRITE_G1, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
0x00 } },
{ SCMD_WRITE_G4, 0, SCF_NONE, sata_txlt_write, NULL,
{ SCMD_WRITE_G4, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } },
{ SCMD_WRITE_G5, 0, SCF_NONE, sata_txlt_write, NULL,
{ SCMD_WRITE_G5, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x00 } },
{ SCMD_SEEK, 0, SCF_NONE, sata_txlt_nodata_cmd_immediate, NULL,
{ SCMD_SEEK, 0x00, 0x00, 0x00, 0x00, 0x00 } },
{ SPC3_CMD_ATA_COMMAND_PASS_THROUGH12, 0, SCF_MAPIN,
sata_txlt_ata_pass_thru, NULL,
{ SPC3_CMD_ATA_COMMAND_PASS_THROUGH12, 0x1e, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } },
{ SPC3_CMD_ATA_COMMAND_PASS_THROUGH16, 0, SCF_MAPIN,
sata_txlt_ata_pass_thru, NULL,
{ SPC3_CMD_ATA_COMMAND_PASS_THROUGH16, 0x1f, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } },
{ SPC3_CMD_UNMAP, 0, SCF_MAPIN, sata_txlt_unmap,
sata_txlt_unmap_supported, { SPC3_CMD_UNMAP, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0xff, 0xff, 0x00 } },
{ SCMD_MAINTENANCE_IN, SSVC_ACTION_GET_SUPPORTED_OPERATIONS,
SCF_SVC_ACT|SCF_MAPIN, sata_txlt_supported_ops, NULL,
{ SCMD_MAINTENANCE_IN, SSVC_ACTION_GET_SUPPORTED_OPERATIONS, 0x07,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00 } },
};
static const struct sata_cmd_info scmd_invalid = {
.sci_op = 0,
.sci_svcact = 0,
.sci_flags = SCF_NONE,
.sci_cmd = sata_txlt_invalid_command,
.sci_supported = NULL,
};
static inline uint16_t
sata_cmd_cdblen(const struct sata_cmd_info *cmd)
{
switch (CDB_GROUPID(cmd->sci_op)) {
case CDB_GROUPID_0:
return (CDB_GROUP0);
case CDB_GROUPID_1:
return (CDB_GROUP1);
case CDB_GROUPID_2:
return (CDB_GROUP2);
case CDB_GROUPID_3:
return (CDB_GROUP3);
case CDB_GROUPID_4:
return (CDB_GROUP4);
case CDB_GROUPID_5:
return (CDB_GROUP5);
case CDB_GROUPID_6:
return (CDB_GROUP6);
case CDB_GROUPID_7:
return (CDB_GROUP7);
default:
cmn_err(CE_PANIC, "invalid CDB size for op %x\n", cmd->sci_op);
#ifndef __CHECKER__
return (0);
#endif
}
}
static int
sata_scsi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
sata_pkt_txlate_t *spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
sata_device_t *sdevice = &spx->txlt_sata_pkt->satapkt_device;
sata_drive_info_t *sdinfo = NULL;
struct buf *bp;
uint8_t cport, pmport;
boolean_t dev_gone = B_FALSE;
int rval;
SATADBG1(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_start: cmd 0x%02x\n", pkt->pkt_cdbp[0]);
ASSERT3P(spx, !=, NULL);
ASSERT3P(spx->txlt_scsi_pkt, ==, pkt);
ASSERT3P(spx->txlt_scsi_pkt, !=, NULL);
cport = SCSI_TO_SATA_CPORT(ap->a_target);
pmport = SCSI_TO_SATA_PMPORT(ap->a_target);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if (sdevice->satadev_addr.qual == SATA_ADDR_DCPORT) {
sdinfo = sata_get_device_info(sata_hba_inst, sdevice);
if (sdinfo == NULL ||
SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_tgtnode_clean == B_FALSE ||
(sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) {
dev_gone = B_TRUE;
}
} else if (sdevice->satadev_addr.qual == SATA_ADDR_DPMPORT) {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) !=
SATA_DTYPE_PMULT || SATA_PMULT_INFO(sata_hba_inst,
cport) == NULL) {
dev_gone = B_TRUE;
} else if (SATA_PMPORT_INFO(sata_hba_inst, cport,
pmport) == NULL) {
dev_gone = B_TRUE;
} else {
mutex_enter(&(SATA_PMPORT_MUTEX(sata_hba_inst,
cport, pmport)));
sdinfo = sata_get_device_info(sata_hba_inst, sdevice);
if (sdinfo == NULL ||
SATA_PMPORT_INFO(sata_hba_inst, cport, pmport)->
pmport_tgtnode_clean == B_FALSE ||
(sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) {
dev_gone = B_TRUE;
}
mutex_exit(&(SATA_PMPORT_MUTEX(sata_hba_inst,
cport, pmport)));
}
}
if (dev_gone == B_TRUE) {
taskq_t *tq = SATA_TXLT_TASKQ(spx);
task_func_t *func = (task_func_t *)pkt->pkt_comp;
uint_t flags = servicing_interrupt() ?
TQ_NOSLEEP : TQ_SLEEP;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
pkt->pkt_reason = CMD_DEV_GONE;
if (pkt->pkt_comp == NULL)
return (TRAN_FATAL_ERROR);
if (taskq_dispatch(tq, func, pkt, flags) == TASKQID_INVALID)
return (TRAN_BUSY);
return (TRAN_ACCEPT);
}
if (sdinfo->satadrv_type & SATA_DTYPE_ATAPI) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
rval = sata_txlt_atapi(spx);
SATADBG1(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_start atapi: rval %d\n", rval);
return (rval);
}
if (((sdinfo->satadrv_power_level == SATA_POWER_STANDBY) ||
(sdinfo->satadrv_power_level == SATA_POWER_STOPPED)) &&
(SATA_IS_MEDIUM_ACCESS_CMD(pkt->pkt_cdbp[0]))) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
return (sata_txlt_check_condition(spx, KEY_NOT_READY,
SD_SCSI_ASC_LU_NOT_READY));
}
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
const struct sata_cmd_info *cmd = &scmd_invalid;
for (uint_t i = 0; i < ARRAY_SIZE(sata_cmd_info); i++) {
if (pkt->pkt_cdbp[0] != sata_cmd_info[i].sci_op)
continue;
if ((sata_cmd_info[i].sci_flags & SCF_SVC_ACT) != 0 &&
(pkt->pkt_cdbp[1] & 0x1f) != sata_cmd_info[i].sci_svcact) {
continue;
}
cmd = &sata_cmd_info[i];
break;
}
if (cmd->sci_supported != NULL && !cmd->sci_supported(spx, sdinfo)) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
return (sata_txlt_invalid_command(spx));
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if ((cmd->sci_flags & SCF_MAPIN) && bp != NULL &&
(bp->b_flags & (B_PHYS | B_PAGEIO))) {
bp_mapin(bp);
}
rval = cmd->sci_cmd(spx);
SATADBG1(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_start: rval %d\n", rval);
return (rval);
}
static int
sata_scsi_abort(struct scsi_address *ap, struct scsi_pkt *scsi_pkt)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
sata_device_t sata_device;
sata_pkt_t *sata_pkt;
SATADBG2(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_abort: %s at target: 0x%x\n",
scsi_pkt == NULL ? "all packets" : "one pkt", ap->a_target);
if (sata_validate_scsi_address(sata_hba_inst, ap, &sata_device) != 0)
return (0);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if (sata_get_device_info(sata_hba_inst, &sata_device) == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (0);
}
if (scsi_pkt == NULL) {
sata_pkt = kmem_zalloc(sizeof (sata_pkt_t), KM_NOSLEEP);
if (sata_pkt == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_pkt_abort: "
"could not allocate sata_pkt"));
return (0);
}
sata_pkt->satapkt_rev = SATA_PKT_REV;
sata_pkt->satapkt_device = sata_device;
sata_pkt->satapkt_device.satadev_rev = SATA_DEVICE_REV;
} else {
if (scsi_pkt->pkt_ha_private == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (0);
}
sata_pkt = ((sata_pkt_txlate_t *)scsi_pkt->pkt_ha_private)->
txlt_sata_pkt;
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if ((*SATA_ABORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_pkt,
scsi_pkt == NULL ? SATA_ABORT_ALL_PACKETS : SATA_ABORT_PACKET) ==
SATA_SUCCESS) {
if (scsi_pkt == NULL)
kmem_free(sata_pkt, sizeof (sata_pkt_t));
return (1);
}
if (scsi_pkt == NULL)
kmem_free(sata_pkt, sizeof (sata_pkt_t));
return (0);
}
static int
sata_scsi_reset(struct scsi_address *ap, int level)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
sata_device_t sata_device;
int val;
SATADBG2(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_reset: level %d target: 0x%x\n",
level, ap->a_target);
val = sata_validate_scsi_address(sata_hba_inst, ap, &sata_device);
if (val == -1)
return (0);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if (sata_get_device_info(sata_hba_inst, &sata_device) == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (0);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if (level == RESET_ALL) {
if (sata_device.satadev_addr.qual == SATA_ADDR_DCPORT)
sata_device.satadev_addr.qual = SATA_ADDR_CPORT;
else
sata_device.satadev_addr.qual = SATA_ADDR_PMPORT;
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device) == SATA_SUCCESS)
return (1);
else
return (0);
} else if (val == 0 &&
(level == RESET_TARGET || level == RESET_LUN)) {
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device) == SATA_SUCCESS)
return (1);
else
return (0);
}
return (0);
}
static int
sata_scsi_getcap(struct scsi_address *ap, char *cap, int whom)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
ddi_dma_attr_t adj_dma_attr;
int rval;
SATADBG2(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_getcap: target: 0x%x, cap: %s\n",
ap->a_target, cap);
if (cap == NULL || whom == 0)
return (-1);
if (sata_validate_scsi_address(sata_hba_inst, ap, &sata_device) != 0) {
return (-1);
}
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if ((sdinfo = sata_get_device_info(sata_hba_inst, &sata_device)) ==
NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (-1);
}
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
rval = 1;
break;
case SCSI_CAP_SECTOR_SIZE:
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK)
rval = SATA_DISK_SECTOR_SIZE;
else if (sdinfo->satadrv_type == SATA_DTYPE_ATAPICD)
rval = SATA_ATAPI_SECTOR_SIZE;
else rval = -1;
break;
case SCSI_CAP_UNTAGGED_QING:
if (sdinfo->satadrv_features_enabled &
SATA_DEV_F_E_UNTAGGED_QING)
rval = 1;
else
rval = -1;
break;
case SCSI_CAP_TAGGED_QING:
if ((sdinfo->satadrv_features_enabled &
SATA_DEV_F_E_TAGGED_QING) &&
(sdinfo->satadrv_max_queue_depth > 1))
rval = 1;
else
rval = -1;
break;
case SCSI_CAP_DMA_MAX:
sata_adjust_dma_attr(sdinfo, SATA_DMA_ATTR(sata_hba_inst),
&adj_dma_attr);
rval = (int)adj_dma_attr.dma_attr_maxxfer;
break;
case SCSI_CAP_INTERCONNECT_TYPE:
rval = INTERCONNECT_SATA;
break;
case SCSI_CAP_CDB_LEN:
if (sdinfo->satadrv_type & SATA_DTYPE_ATAPI)
rval = sdinfo->satadrv_atapi_cdb_len;
else
rval = -1;
break;
default:
rval = -1;
break;
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (rval);
}
static int
sata_scsi_setcap(struct scsi_address *ap, char *cap, int value, int whom)
{
sata_hba_inst_t *sata_hba_inst =
(sata_hba_inst_t *)(ap->a_hba_tran->tran_hba_private);
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
int rval;
SATADBG2(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_scsi_setcap: target: 0x%x, cap: %s\n", ap->a_target, cap);
if (cap == NULL || whom == 0) {
return (-1);
}
if (sata_validate_scsi_address(sata_hba_inst, ap, &sata_device) != 0) {
return (-1);
}
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
if ((sdinfo = sata_get_device_info(sata_hba_inst,
&sata_device)) == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
return (-1);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device.satadev_addr.cport)));
switch (scsi_hba_lookup_capstr(cap)) {
case SCSI_CAP_ARQ:
case SCSI_CAP_SECTOR_SIZE:
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_INTERCONNECT_TYPE:
rval = 0;
break;
case SCSI_CAP_UNTAGGED_QING:
if (SATA_QDEPTH(sata_hba_inst) > 1) {
rval = 1;
if (value == 1) {
sdinfo->satadrv_features_enabled |=
SATA_DEV_F_E_UNTAGGED_QING;
} else if (value == 0) {
sdinfo->satadrv_features_enabled &=
~SATA_DEV_F_E_UNTAGGED_QING;
} else {
rval = -1;
}
} else {
rval = 0;
}
break;
case SCSI_CAP_TAGGED_QING:
if (sata_func_enable & SATA_ENABLE_QUEUING &&
((sdinfo->satadrv_features_support & SATA_DEV_F_TCQ &&
SATA_FEATURES(sata_hba_inst) & SATA_CTLF_QCMD) ||
(sata_func_enable & SATA_ENABLE_NCQ &&
sdinfo->satadrv_features_support & SATA_DEV_F_NCQ &&
SATA_FEATURES(sata_hba_inst) & SATA_CTLF_NCQ)) &&
(sdinfo->satadrv_max_queue_depth > 1)) {
rval = 1;
if (value == 1) {
sdinfo->satadrv_features_enabled |=
SATA_DEV_F_E_TAGGED_QING;
} else if (value == 0) {
sdinfo->satadrv_features_enabled &=
~SATA_DEV_F_E_TAGGED_QING;
} else {
rval = -1;
}
} else {
rval = 0;
}
break;
default:
rval = -1;
break;
}
return (rval);
}
static void
sata_scsi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
{
sata_pkt_txlate_t *spx;
spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
sata_common_free_dma_rsrcs(spx);
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
sata_pkt_free(spx);
scsi_hba_pkt_free(ap, pkt);
}
static void
sata_scsi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(ap))
#endif
sata_pkt_txlate_t *spx;
ASSERT(pkt != NULL);
spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
sata_common_free_dma_rsrcs(spx);
}
static void
sata_scsi_sync_pkt(struct scsi_address *ap __unused, struct scsi_pkt *pkt)
{
sata_pkt_txlate_t *spx = (sata_pkt_txlate_t *)pkt->pkt_ha_private;
struct buf *bp;
int direction;
int rval;
ASSERT(spx != NULL);
if (spx->txlt_buf_dma_handle == NULL)
return;
if (spx->txlt_sata_pkt == NULL)
return;
direction = spx->txlt_sata_pkt->
satapkt_cmd.satacmd_flags.sata_data_direction;
if (direction == SATA_DIR_NODATA_XFER)
return;
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
if (spx->txlt_tmp_buf != NULL && (direction & SATA_DIR_WRITE) != 0) {
bcopy(bp->b_un.b_addr, spx->txlt_tmp_buf, bp->b_bcount);
}
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
(direction & SATA_DIR_WRITE) ?
DDI_DMA_SYNC_FORDEV : DDI_DMA_SYNC_FORCPU);
ASSERT3S(rval, ==, DDI_SUCCESS);
if (spx->txlt_tmp_buf != NULL && !(direction & SATA_DIR_WRITE)) {
bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr, bp->b_bcount);
}
}
static int
sata_txlt_generic_pkt_info(sata_pkt_txlate_t *spx, int *reason, int flag)
{
sata_drive_info_t *sdinfo;
sata_device_t sata_device;
const struct sata_cmd_flags sata_initial_cmd_flags = {
SATA_DIR_NODATA_XFER,
};
spx->txlt_scsi_pkt->pkt_reason = CMD_TRAN_ERR;
*reason = CMD_TRAN_ERR;
switch (sata_validate_scsi_address(spx->txlt_sata_hba_inst,
&spx->txlt_scsi_pkt->pkt_address, &sata_device)) {
case -1:
return (TRAN_BADPKT);
case 2:
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
if (sdinfo != NULL && (sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET |
SATA_EVNT_INPROC_DEVICE_RESET)) != 0) {
if (!ddi_in_panic()) {
spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
*reason = CMD_INCOMPLETE;
SATADBG1(SATA_DBG_SCSI_IF,
spx->txlt_sata_hba_inst,
"sata_scsi_start: rejecting command "
"because of device reset state\n", NULL);
return (TRAN_BUSY);
}
}
case 1:
spx->txlt_scsi_pkt->pkt_reason = CMD_DEV_GONE;
*reason = CMD_DEV_GONE;
if (spx->txlt_scsi_pkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
return (TRAN_ACCEPT);
}
return (TRAN_FATAL_ERROR);
default:
break;
}
if (((spx->txlt_scsi_pkt->pkt_flags & FLAG_NOINTR) != 0 || flag != 0) &&
servicing_interrupt() && !ddi_in_panic()) {
SATADBG1(SATA_DBG_INTR_CTX, spx->txlt_sata_hba_inst,
"sata_scsi_start: rejecting synchronous command because "
"of interrupt context\n", NULL);
return (TRAN_BUSY);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) != 0) {
if (!ddi_in_panic() &&
((SATA_CPORT_EVENT_FLAGS(spx->txlt_sata_hba_inst,
sata_device.satadev_addr.cport) &
SATA_APCTL_LOCK_PORT_BUSY) == 0)) {
spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
*reason = CMD_INCOMPLETE;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_scsi_start: rejecting command because "
"of device reset state\n", NULL);
return (TRAN_BUSY);
}
}
spx->txlt_sata_pkt->satapkt_device.satadev_type = sdinfo->satadrv_type;
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags = sata_initial_cmd_flags;
if ((SATA_CPORT_INFO(spx->txlt_sata_hba_inst,
sata_device.satadev_addr.cport)->cport_event_flags &
SATA_APCTL_LOCK_PORT_BUSY) != 0) {
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.
sata_ignore_dev_reset = B_TRUE;
}
spx->txlt_scsi_pkt->pkt_reason = CMD_CMPLT;
*reason = CMD_CMPLT;
if ((spx->txlt_scsi_pkt->pkt_flags & FLAG_NOINTR) != 0) {
spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH |
SATA_OPMODE_POLLING;
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.
sata_ignore_dev_reset = ddi_in_panic();
} else {
spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_ASYNCH |
SATA_OPMODE_INTERRUPTS;
}
if (spx->txlt_scsi_pkt->pkt_flags & FLAG_STAG)
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.sata_queue_stag =
B_TRUE;
else if (spx->txlt_scsi_pkt->pkt_flags &
(FLAG_OTAG | FLAG_HTAG | FLAG_HEAD))
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.sata_queue_otag =
B_TRUE;
if (spx->txlt_scsi_pkt->pkt_time == 0)
spx->txlt_sata_pkt->satapkt_time = sata_default_pkt_time;
else
spx->txlt_sata_pkt->satapkt_time =
spx->txlt_scsi_pkt->pkt_time;
return (TRAN_ACCEPT);
}
static void
sata_identdev_to_inquiry(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo, uint8_t *buf)
{
struct scsi_inquiry *inq = (struct scsi_inquiry *)buf;
struct sata_id *sid = &sdinfo->satadrv_id;
bzero((void *)inq, sizeof (struct scsi_inquiry));
inq->inq_dtype = sdinfo->satadrv_type == SATA_DTYPE_ATADISK ?
DTYPE_DIRECT : DTYPE_RODIRECT;
inq->inq_rmb = ((sid->ai_config != SATA_CFA_TYPE) &&
(sid->ai_config & SATA_REM_MEDIA)) ? 1 : 0;
inq->inq_qual = 0;
inq->inq_iso = 0;
inq->inq_ecma = 0;
inq->inq_ansi = 3;
inq->inq_aenc = 0;
inq->inq_trmiop = 0;
inq->inq_normaca = 0;
inq->inq_rdf = RDF_SCSI2;
inq->inq_len = 31;
inq->inq_dualp = 0;
inq->inq_reladdr = 0;
inq->inq_sync = 0;
inq->inq_linked = 0;
if (SATA_QDEPTH(sata_hba_inst) > 1)
inq->inq_cmdque = 1;
else
inq->inq_cmdque = 0;
inq->inq_sftre = 0;
inq->inq_wbus32 = 0;
inq->inq_wbus16 = 0;
#ifdef _LITTLE_ENDIAN
bcopy("ATA ", inq->inq_vid, 8);
swab(sid->ai_model, inq->inq_pid, 16);
if (strncmp(&sid->ai_fw[4], " ", 4) == 0)
swab(sid->ai_fw, inq->inq_revision, 4);
else
swab(&sid->ai_fw[4], inq->inq_revision, 4);
#else
bcopy("ATA ", inq->inq_vid, 8);
bcopy(sid->ai_model, inq->inq_pid, 16);
if (strncmp(&sid->ai_fw[4], " ", 4) == 0)
bcopy(sid->ai_fw, inq->inq_revision, 4);
else
bcopy(&sid->ai_fw[4], inq->inq_revision, 4);
#endif
}
static int
sata_txlt_invalid_command(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_COMMAND_CODE;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_check_condition(sata_pkt_txlate_t *spx, uchar_t key, uchar_t code)
{
sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx);
int cport = SATA_TXLT_CPORT(spx);
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
mutex_enter(&SATA_CPORT_MUTEX(shi, cport));
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = key;
sense->es_add_code = code;
mutex_exit(&SATA_CPORT_MUTEX(shi, cport));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_nodata_cmd_immediate(sata_pkt_txlate_t *spx)
{
int rval;
int reason;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
spx->txlt_scsi_pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
spx->txlt_scsi_pkt->pkt_reason = CMD_CMPLT;
*(spx->txlt_scsi_pkt->pkt_scbp) = STATUS_GOOD;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n",
spx->txlt_scsi_pkt->pkt_reason);
if ((spx->txlt_scsi_pkt->pkt_flags & FLAG_NOINTR) == 0 &&
spx->txlt_scsi_pkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
#define EVPD 1
#define CMDDT 2
#define INQUIRY_SUP_VPD_PAGE 0
#define INQUIRY_USN_PAGE 0x80
#define INQUIRY_BDC_PAGE 0xB1
#define INQUIRY_ATA_INFO_PAGE 0x89
#define INQUIRY_DEV_IDENTIFICATION_PAGE 0x83
static int
sata_txlt_inquiry(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
struct scsi_extended_sense *sense;
int count;
uint8_t *p;
int i, j;
uint8_t page_buf[1024];
int rval, reason;
ushort_t rate;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
ASSERT(sdinfo != NULL);
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if (scsipkt->pkt_cdbp[1] & CMDDT) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
*scsipkt->pkt_scbp = STATUS_GOOD;
if (bp == NULL || bp->b_un.b_addr == NULL || bp->b_bcount == 0)
goto done;
sata_scsi_dmafree(NULL, scsipkt);
if (!(scsipkt->pkt_cdbp[1] & EVPD)) {
struct scsi_inquiry inq;
unsigned int bufsize;
sata_identdev_to_inquiry(spx->txlt_sata_hba_inst,
sdinfo, (uint8_t *)&inq);
count = MIN(bp->b_bcount, sizeof (struct scsi_inquiry));
bufsize = scsipkt->pkt_cdbp[4];
bufsize |= scsipkt->pkt_cdbp[3] << 8;
count = MIN(count, bufsize);
bcopy(&inq, bp->b_un.b_addr, count);
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = scsipkt->pkt_cdbp[4] > count ?
bufsize - count : 0;
goto done;
}
uint8_t peripheral_device_type =
sdinfo->satadrv_type == SATA_DTYPE_ATADISK ?
DTYPE_DIRECT : DTYPE_RODIRECT;
bzero(page_buf, sizeof (page_buf));
switch ((uint_t)scsipkt->pkt_cdbp[2]) {
case INQUIRY_SUP_VPD_PAGE:
page_buf[0] = peripheral_device_type;
page_buf[1] = INQUIRY_SUP_VPD_PAGE;
page_buf[2] = 0;
page_buf[4] = INQUIRY_SUP_VPD_PAGE;
page_buf[5] = INQUIRY_USN_PAGE;
page_buf[6] = INQUIRY_BDC_PAGE;
if (sdinfo->satadrv_id.ai_naa_ieee_oui != 0) {
page_buf[3] = 5;
page_buf[7] = INQUIRY_DEV_IDENTIFICATION_PAGE;
page_buf[8] = INQUIRY_ATA_INFO_PAGE;
count = 9;
} else {
page_buf[3] = 4;
page_buf[7] = INQUIRY_ATA_INFO_PAGE;
count = 8;
}
count = MIN(bp->b_bcount, count);
bcopy(page_buf, bp->b_un.b_addr, count);
break;
case INQUIRY_USN_PAGE:
page_buf[0] = peripheral_device_type;
page_buf[1] = INQUIRY_USN_PAGE;
page_buf[2] = 0;
page_buf[3] = SATA_ID_SERIAL_LEN;
p = (uint8_t *)(sdinfo->satadrv_id.ai_drvser);
#ifdef _LITTLE_ENDIAN
swab(p, &page_buf[4], SATA_ID_SERIAL_LEN);
#else
bcopy(p, &page_buf[4], SATA_ID_SERIAL_LEN);
#endif
p = &page_buf[SATA_ID_SERIAL_LEN + 4 - 1];
for (j = 0; j < SATA_ID_SERIAL_LEN; j++) {
if (*(p - j) != '\0' && *(p - j) != '\040')
break;
}
for (i = 0; i < (SATA_ID_SERIAL_LEN - j) && j != 0; i++, p--)
*p = *(p - j);
for (; j > 0; j--)
page_buf[4 + j - 1] = '\040';
count = MIN(bp->b_bcount, SATA_ID_SERIAL_LEN + 4);
bcopy(page_buf, bp->b_un.b_addr, count);
break;
case INQUIRY_BDC_PAGE:
page_buf[0] = peripheral_device_type;
page_buf[1] = INQUIRY_BDC_PAGE;
page_buf[2] = 0;
page_buf[3] = SATA_ID_BDC_LEN;
rate = sdinfo->satadrv_id.ai_medrotrate;
page_buf[4] = (rate >> 8) & 0xff;
page_buf[5] = rate & 0xff;
page_buf[6] = 0;
page_buf[7] = sdinfo->satadrv_id.ai_nomformfactor & 0xf;
count = MIN(bp->b_bcount, SATA_ID_BDC_LEN + 4);
bcopy(page_buf, bp->b_un.b_addr, count);
break;
case INQUIRY_ATA_INFO_PAGE:
page_buf[0] = peripheral_device_type;
page_buf[1] = INQUIRY_ATA_INFO_PAGE;
page_buf[2] = (SATA_ID_ATA_INFO_LEN >> 8) & 0xff;
page_buf[3] = SATA_ID_ATA_INFO_LEN & 0xff;
#ifdef _LITTLE_ENDIAN
bcopy("ATA ", &page_buf[8], 8);
swab(sdinfo->satadrv_id.ai_model, &page_buf[16], 16);
if (strncmp(&sdinfo->satadrv_id.ai_fw[4], " ", 4) == 0) {
swab(sdinfo->satadrv_id.ai_fw, &page_buf[32], 4);
} else {
swab(&sdinfo->satadrv_id.ai_fw[4], &page_buf[32], 4);
}
#else
bcopy("ATA ", &page_buf[8], 8);
bcopy(sdinfo->satadrv_id.ai_model, &page_buf[16], 16);
if (strncmp(&sdinfo->satadrv_id.ai_fw[4], " ", 4) == 0) {
bcopy(sdinfo->satadrv_id.ai_fw, &page_buf[32], 4);
} else {
bcopy(&sdinfo->satadrv_id.ai_fw[4], &page_buf[32], 4);
}
#endif
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
page_buf[56] = SATAC_ID_DEVICE;
} else if (sdinfo->satadrv_type == SATA_DTYPE_ATAPI) {
page_buf[56] = SATAC_ID_PACKET_DEVICE;
}
if (page_buf[56] != 0) {
sata_drive_info_t temp_info = {
.satadrv_addr = sdinfo->satadrv_addr,
.satadrv_type = sdinfo->satadrv_type,
};
mutex_exit(cport_mutex);
(void) sata_fetch_device_identify_data(
spx->txlt_sata_hba_inst, &temp_info);
mutex_enter(cport_mutex);
bcopy(&temp_info.satadrv_id, &page_buf[60],
sizeof (sata_id_t));
}
count = MIN(bp->b_bcount, SATA_ID_ATA_INFO_LEN + 4);
bcopy(page_buf, bp->b_un.b_addr, count);
break;
case INQUIRY_DEV_IDENTIFICATION_PAGE:
if (sdinfo->satadrv_id.ai_naa_ieee_oui != 0) {
page_buf[0] = peripheral_device_type;
page_buf[1] = INQUIRY_DEV_IDENTIFICATION_PAGE;
page_buf[2] = 0;
page_buf[3] = 12;
page_buf[4] = 0x01;
page_buf[5] = 0x03;
page_buf[6] = 0;
page_buf[7] = 0x08;
#ifdef _LITTLE_ENDIAN
swab(&sdinfo->satadrv_id.ai_naa_ieee_oui, &page_buf[8],
8);
#else
bcopy(&sdinfo->satadrv_id.ai_naa_ieee_oui,
&page_buf[8], 8);
#endif
count = MIN(bp->b_bcount, 12 + 4);
bcopy(page_buf, bp->b_un.b_addr, count);
break;
}
default:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = scsipkt->pkt_cdbp[4] > count ?
scsipkt->pkt_cdbp[4] - count : 0;
done:
mutex_exit(cport_mutex);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n",
scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_request_sense(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense sense;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
int rval, reason, power_state = 0;
kmutex_t *cport_mutex;
cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (scsipkt->pkt_cdbp[5] & CTL_BYTE_NACA_MASK) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_CMD_SEQUENCE_ERR));
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
ASSERT(sdinfo != NULL);
spx->txlt_sata_pkt->satapkt_op_mode |= SATA_OPMODE_SYNCH;
sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE);
scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE;
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx, KEY_NO_SENSE,
SD_SCSI_ASC_NO_ADD_SENSE));
}
switch (scmd->satacmd_sec_count_lsb) {
case SATA_PWRMODE_STANDBY:
if (sdinfo->satadrv_power_level == SATA_POWER_STOPPED)
power_state = SATA_POWER_STOPPED;
else {
power_state = SATA_POWER_STANDBY;
sdinfo->satadrv_power_level = SATA_POWER_STANDBY;
}
break;
case SATA_PWRMODE_IDLE:
power_state = SATA_POWER_IDLE;
sdinfo->satadrv_power_level = SATA_POWER_IDLE;
break;
case SATA_PWRMODE_ACTIVE:
default:
if (sdinfo->satadrv_power_level == SATA_POWER_IDLE)
power_state = SATA_POWER_IDLE;
else {
power_state = SATA_POWER_ACTIVE;
sdinfo->satadrv_power_level = SATA_POWER_ACTIVE;
}
break;
}
mutex_exit(cport_mutex);
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
int count = MIN(bp->b_bcount,
sizeof (struct scsi_extended_sense));
sata_scsi_dmafree(NULL, scsipkt);
bzero(&sense, sizeof (struct scsi_extended_sense));
sense.es_valid = 0;
sense.es_class = 7;
sense.es_key = KEY_NO_SENSE;
sense.es_add_len = 6;
bcopy(&sense, bp->b_un.b_addr, count);
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = 0;
switch (power_state) {
case SATA_POWER_IDLE:
case SATA_POWER_STANDBY:
sense.es_add_code =
SD_SCSI_ASC_LOW_POWER_CONDITION_ON;
break;
case SATA_POWER_STOPPED:
sense.es_add_code = SD_SCSI_ASC_NO_ADD_SENSE;
break;
case SATA_POWER_ACTIVE:
default:
break;
}
}
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n",
scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_test_unit_ready(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_drive_info_t *sdinfo;
int power_state;
int rval, reason;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
ASSERT(sdinfo != NULL);
spx->txlt_sata_pkt->satapkt_op_mode |= SATA_OPMODE_SYNCH;
sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE);
scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE;
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx, KEY_NOT_READY,
SD_SCSI_ASC_LU_NOT_RESPONSE));
}
power_state = scmd->satacmd_sec_count_lsb;
if (power_state == SATA_PWRMODE_STANDBY &&
sdinfo->satadrv_power_level == SATA_POWER_STOPPED) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_NOT_READY;
sense->es_add_code = SD_SCSI_ASC_LU_NOT_READY;
} else {
*scsipkt->pkt_scbp = STATUS_GOOD;
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
mutex_exit(cport_mutex);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_start_stop_unit(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
int rval, reason;
sata_drive_info_t *sdinfo;
sata_id_t *sata_id;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_start_stop_unit: %d\n", scsipkt->pkt_scbp[4] & 1);
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
if (scsipkt->pkt_cdbp[1] & START_STOP_IMMED_MASK) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
spx->txlt_sata_pkt->satapkt_op_mode |= SATA_OPMODE_SYNCH;
spx->txlt_sata_pkt->satapkt_comp = NULL;
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
ASSERT(sdinfo != NULL);
sata_id = &sdinfo->satadrv_id;
switch ((scsipkt->pkt_cdbp[4] & START_STOP_POWER_COND_MASK) >> 4) {
case 0:
if (scsipkt->pkt_cdbp[4] & START_STOP_LOEJ_MASK) {
goto err_out;
}
if (scsipkt->pkt_cdbp[4] & START_STOP_START_MASK) {
sata_build_read_verify_cmd(scmd, 1, 5);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sdinfo->satadrv_power_level = SATA_POWER_ACTIVE;
} else {
sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
sata_build_generic_cmd(scmd, SATAC_STANDBY_IM);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sdinfo->satadrv_power_level = SATA_POWER_STOPPED;
}
break;
case 0x1:
sata_build_generic_cmd(scmd, SATAC_IDLE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sata_build_read_verify_cmd(scmd, 1, 5);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
sdinfo->satadrv_power_level = SATA_POWER_ACTIVE;
break;
case 0x2:
sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) {
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
sata_build_generic_cmd(scmd, SATAC_IDLE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
if ((scsipkt->pkt_cdbp[3] & START_STOP_MODIFIER_MASK)) {
if (!(sata_id->ai_cmdset84 &
SATA_IDLE_UNLOAD_SUPPORTED)) {
sdinfo->satadrv_power_level = SATA_POWER_IDLE;
break;
}
sata_build_generic_cmd(scmd, SATAC_IDLE_IM);
scmd->satacmd_features_reg = 0x44;
scmd->satacmd_lba_low_lsb = 0x4c;
scmd->satacmd_lba_mid_lsb = 0x4e;
scmd->satacmd_lba_high_lsb = 0x55;
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
sdinfo->satadrv_power_level = SATA_POWER_IDLE;
break;
case 0x3:
sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) {
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
sata_build_generic_cmd(scmd, SATAC_STANDBY);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sdinfo->satadrv_power_level = SATA_POWER_STANDBY;
break;
case 0x7:
sata_build_generic_cmd(scmd, SATAC_CHECK_POWER_MODE);
scmd->satacmd_flags.sata_copy_out_sec_count_lsb = B_TRUE;
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
switch (scmd->satacmd_sec_count_lsb) {
case SATA_PWRMODE_STANDBY:
sata_build_generic_cmd(scmd, SATAC_STANDBY);
scmd->satacmd_sec_count_msb = sata_get_standby_timer(
sdinfo->satadrv_standby_timer);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case SATA_PWRMODE_IDLE:
sata_build_generic_cmd(scmd, SATAC_IDLE);
scmd->satacmd_sec_count_msb = sata_get_standby_timer(
sdinfo->satadrv_standby_timer);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
} else {
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
break;
case SATA_PWRMODE_ACTIVE_SPINDOWN:
case SATA_PWRMODE_ACTIVE_SPINUP:
case SATA_PWRMODE_ACTIVE:
sata_build_generic_cmd(scmd, SATAC_IDLE);
scmd->satacmd_sec_count_msb = sata_get_standby_timer(
sdinfo->satadrv_standby_timer);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sata_build_read_verify_cmd(scmd, 1, 5);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
break;
default:
goto err_out;
}
break;
case 0xb:
if ((sata_get_standby_timer(sdinfo->satadrv_standby_timer) ==
0) || (!(sata_id->ai_cap & SATA_STANDBYTIMER))) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx,
KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
sata_build_generic_cmd(scmd, SATAC_FLUSH_CACHE);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (!(scsipkt->pkt_cdbp[4] & START_STOP_NOFLUSH_MASK)) {
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
sata_build_generic_cmd(scmd, SATAC_STANDBY_IM);
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
if (scmd->satacmd_error_reg != 0) {
goto err_out;
}
}
bzero(sdinfo->satadrv_standby_timer, sizeof (uchar_t) * 4);
break;
default:
err_out:
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
mutex_exit(cport_mutex);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"synchronous execution status %x\n",
spx->txlt_sata_pkt->satapkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
sata_set_arq_data(spx->txlt_sata_pkt);
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
else
sata_txlt_nodata_cmd_completion(spx->txlt_sata_pkt);
return (TRAN_ACCEPT);
}
static int
sata_txlt_read_capacity(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
uint64_t val;
uint32_t lbsize = DEV_BSIZE;
uchar_t *rbuf;
int rval, reason;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_read_capacity: ", NULL);
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
sata_scsi_dmafree(NULL, scsipkt);
sdinfo = sata_get_device_info(
spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
val = MIN(sdinfo->satadrv_capacity - 1, UINT32_MAX);
if (sdinfo->satadrv_id.ai_phys_sect_sz & SATA_L2PS_CHECK_BIT) {
if (sdinfo->satadrv_id.ai_phys_sect_sz &
SATA_L2PS_BIG_SECTORS) {
lbsize = sdinfo->satadrv_id.ai_words_lsec[0] |
(sdinfo->satadrv_id.ai_words_lsec[1] << 16);
lbsize <<= 1;
}
}
rbuf = (uchar_t *)bp->b_un.b_addr;
rbuf[0] = (val >> 24) & 0xff;
rbuf[1] = (val >> 16) & 0xff;
rbuf[2] = (val >> 8) & 0xff;
rbuf[3] = val & 0xff;
rbuf[4] = (lbsize >> 24) & 0xff;
rbuf[5] = (lbsize >> 16) & 0xff;
rbuf[6] = (lbsize >> 8) & 0xff;
rbuf[7] = lbsize & 0xff;
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = 0;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "%d\n",
sdinfo->satadrv_capacity -1);
}
mutex_exit(cport_mutex);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_read_capacity16(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
uint64_t val;
uint16_t l2p_exp;
uint32_t lbsize = DEV_BSIZE;
uchar_t *rbuf;
int rval, reason;
#define TPE 0x80
#define TPRZ 0x40
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_read_capacity: ", NULL);
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
sata_scsi_dmafree(NULL, scsipkt);
if ((scsipkt->pkt_cdbp[1] & 0x1f) !=
SSVC_ACTION_READ_CAPACITY_G4) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx,
KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
if ((scsipkt->pkt_cdbp[2] != 0) ||
(scsipkt->pkt_cdbp[3] != 0) ||
(scsipkt->pkt_cdbp[4] != 0) ||
(scsipkt->pkt_cdbp[5] != 0) ||
(scsipkt->pkt_cdbp[6] != 0) ||
(scsipkt->pkt_cdbp[7] != 0) ||
(scsipkt->pkt_cdbp[8] != 0) ||
(scsipkt->pkt_cdbp[9] != 0)) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx,
KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
if (scsipkt->pkt_cdbp[14] & 0x1) {
mutex_exit(cport_mutex);
return (sata_txlt_check_condition(spx,
KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB));
}
*scsipkt->pkt_scbp = STATUS_GOOD;
sdinfo = sata_get_device_info(
spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
val = MIN(sdinfo->satadrv_capacity - 1,
SCSI_READ_CAPACITY16_MAX_LBA);
l2p_exp = 0;
if (sdinfo->satadrv_id.ai_phys_sect_sz & SATA_L2PS_CHECK_BIT) {
if (sdinfo->satadrv_id.ai_phys_sect_sz &
SATA_L2PS_HAS_MULT) {
l2p_exp =
sdinfo->satadrv_id.ai_phys_sect_sz &
SATA_L2PS_EXP_MASK;
}
if (sdinfo->satadrv_id.ai_phys_sect_sz &
SATA_L2PS_BIG_SECTORS) {
lbsize = sdinfo->satadrv_id.ai_words_lsec[0] |
(sdinfo->satadrv_id.ai_words_lsec[1] << 16);
lbsize <<= 1;
}
}
rbuf = (uchar_t *)bp->b_un.b_addr;
bzero(rbuf, bp->b_bcount);
rbuf[0] = (val >> 56) & 0xff;
rbuf[1] = (val >> 48) & 0xff;
rbuf[2] = (val >> 40) & 0xff;
rbuf[3] = (val >> 32) & 0xff;
rbuf[4] = (val >> 24) & 0xff;
rbuf[5] = (val >> 16) & 0xff;
rbuf[6] = (val >> 8) & 0xff;
rbuf[7] = val & 0xff;
rbuf[8] = (lbsize >> 24) & 0xff;
rbuf[9] = (lbsize >> 16) & 0xff;
rbuf[10] = (lbsize >> 8) & 0xff;
rbuf[11] = lbsize & 0xff;
rbuf[13] = l2p_exp;
if (sdinfo->satadrv_id.ai_dsm & SATA_DSM_TRIM) {
rbuf[14] |= TPE;
if ((sdinfo->satadrv_id.ai_addsupported &
SATA_DETERMINISTIC_READ) &&
(sdinfo->satadrv_id.ai_addsupported &
SATA_READ_ZERO)) {
rbuf[14] |= TPRZ;
}
}
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = 0;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst, "%llu\n",
sdinfo->satadrv_capacity -1);
}
mutex_exit(cport_mutex);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static boolean_t
sata_txlt_unmap_supported(sata_pkt_txlate_t *spx, sata_drive_info_t *sdinfo)
{
const sata_id_t *id = &sdinfo->satadrv_id;
ASSERT(MUTEX_HELD(&SATA_TXLT_CPORT_MUTEX(spx)));
if (!(id->ai_dsm & SATA_DSM_TRIM) ||
!(id->ai_addsupported & SATA_DETERMINISTIC_READ)) {
return (B_FALSE);
}
return (B_TRUE);
}
static int
sata_txlt_unmap(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
uint16_t count = 0;
int synch;
int rval, reason;
int i, x;
int bdlen = 0;
int ranges = 0;
int paramlen = 8;
uint8_t *data, *tmpbd;
sata_drive_info_t *sdinfo;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
#define TRIM 0x1
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_unmap: ", NULL);
mutex_enter(cport_mutex);
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
if (sdinfo != NULL) {
SATADBG2(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"DSM support 0x%x, max number of 512 byte blocks of LBA "
"range entries 0x%x\n", sdinfo->satadrv_id.ai_dsm,
sdinfo->satadrv_id.ai_maxcount);
}
rval = sata_txlt_generic_pkt_info(spx, &reason, 1);
if ((rval != TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
if (!sata_txlt_unmap_supported(spx, sdinfo)) {
mutex_exit(cport_mutex);
return (sata_txlt_invalid_command(spx));
}
bdlen = scsipkt->pkt_cdbp[7];
bdlen = (bdlen << 8) + scsipkt->pkt_cdbp[8] - paramlen;
if ((bdlen < 0) || ((bdlen % 16) != 0) ||
((bp != NULL) && (bdlen > (bp->b_bcount - paramlen)))) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_unmap: invalid block descriptor length", NULL);
mutex_exit(cport_mutex);
return ((sata_txlt_check_condition(spx, KEY_ILLEGAL_REQUEST,
SD_SCSI_ASC_INVALID_FIELD_IN_CDB)));
}
if ((bdlen == 0) || (bp == NULL) || (bp->b_un.b_addr == NULL) ||
(bp->b_bcount == 0)) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_unmap: no parameter data or block descriptors",
NULL);
mutex_exit(cport_mutex);
return (sata_txlt_unmap_nodata_cmd(spx));
}
tmpbd = (uint8_t *)bp->b_un.b_addr + paramlen;
data = kmem_zalloc(bdlen, KM_SLEEP);
for (i = 0, x = 0; i < bdlen; i += 16, x += 8) {
data[x] = tmpbd[i+7];
data[x+1] = tmpbd[i+6];
data[x+2] = tmpbd[i+5];
data[x+3] = tmpbd[i+4];
data[x+4] = tmpbd[i+3];
data[x+5] = tmpbd[i+2];
data[x+6] = tmpbd[i+11];
data[x+7] = tmpbd[i+10];
ranges++;
}
sata_common_free_dma_rsrcs(spx);
count = (ranges + 63)/64;
mutex_exit(cport_mutex);
bp = sata_alloc_local_buffer(spx, (size_t)count * 512);
if (bp == NULL) {
SATADBG1(SATA_DBG_ATAPI, spx->txlt_sata_hba_inst,
"sata_txlt_unmap: "
"cannot allocate buffer for TRIM command", NULL);
kmem_free(data, bdlen);
return (TRAN_BUSY);
}
bp_mapin(bp);
mutex_enter(cport_mutex);
bzero(bp->b_un.b_addr, bp->b_bcount);
bcopy(data, bp->b_un.b_addr, x);
kmem_free(data, bdlen);
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORDEV);
ASSERT(rval == DDI_SUCCESS);
scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
scmd->satacmd_addr_type = ATA_ADDR_LBA48;
scmd->satacmd_cmd_reg = SATAC_DSM;
scmd->satacmd_sec_count_msb = (count >> 8) & 0xff;
scmd->satacmd_sec_count_lsb = count & 0xff;
scmd->satacmd_features_reg = TRIM;
scmd->satacmd_device_reg = SATA_ADH_LBA;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp =
sata_txlt_unmap_completion;
synch = FALSE;
} else {
synch = TRUE;
}
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
sata_txlt_unmap_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
#define LLBAA 0x10
static int
sata_txlt_mode_sense(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
sata_id_t *sata_id;
struct scsi_extended_sense *sense;
int len, bdlen, count, alc_len;
int pc;
uint8_t *buf;
int rval, reason;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG2(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_mode_sense, pc %x page code 0x%02x\n",
spx->txlt_scsi_pkt->pkt_cdbp[2] >> 6,
spx->txlt_scsi_pkt->pkt_cdbp[2] & 0x3f);
if (servicing_interrupt()) {
buf = kmem_zalloc(1024, KM_NOSLEEP);
if (buf == NULL) {
return (TRAN_BUSY);
}
} else {
buf = kmem_zalloc(1024, KM_SLEEP);
}
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
kmem_free(buf, 1024);
return (rval);
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
pc = scsipkt->pkt_cdbp[2] >> 6;
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
sata_scsi_dmafree(NULL, scsipkt);
len = 0;
bdlen = 0;
if (!(scsipkt->pkt_cdbp[1] & 8)) {
if (scsipkt->pkt_cdbp[0] == SCMD_MODE_SENSE_G1 &&
(scsipkt->pkt_cdbp[1] & LLBAA))
bdlen = 16;
else
bdlen = 8;
}
if (spx->txlt_scsi_pkt->pkt_cdbp[0] == SCMD_MODE_SENSE) {
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = bdlen;
} else {
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
if (bdlen == 16)
buf[len++] = 1;
else
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = bdlen;
}
sdinfo = sata_get_device_info(
spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
if ((scsipkt->pkt_cdbp[1] & 0x08) == 0) {
if (bdlen == 8) {
buf[len++] =
(sdinfo->satadrv_capacity >> 24) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 16) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 8) & 0xff;
buf[len++] = sdinfo->satadrv_capacity & 0xff;
buf[len++] = 0;
buf[len++] = 0;
if (sdinfo->satadrv_type ==
SATA_DTYPE_ATADISK)
buf[len++] = 2;
else
buf[len++] = 8;
buf[len++] = 0;
} else if (bdlen == 16) {
#ifndef __lock_lint
buf[len++] =
(sdinfo->satadrv_capacity >> 56) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 48) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 40) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 32) & 0xff;
#endif
buf[len++] =
(sdinfo->satadrv_capacity >> 24) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 16) & 0xff;
buf[len++] =
(sdinfo->satadrv_capacity >> 8) & 0xff;
buf[len++] = sdinfo->satadrv_capacity & 0xff;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
if (sdinfo->satadrv_type ==
SATA_DTYPE_ATADISK)
buf[len++] = 2;
else
buf[len++] = 8;
buf[len++] = 0;
}
}
sata_id = &sdinfo->satadrv_id;
switch (scsipkt->pkt_cdbp[2] & 0x3f) {
case MODEPAGE_RW_ERRRECOV:
len += sata_build_msense_page_1(sdinfo, pc, buf+len);
break;
case MODEPAGE_CACHING:
if (pc == 3) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_SAVING_PARAMS_NOT_SUPPORTED;
goto done;
}
len += sata_build_msense_page_8(sdinfo, pc, buf+len);
break;
case MODEPAGE_INFO_EXCPT:
if (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED) {
len += sata_build_msense_page_1c(sdinfo, pc,
buf+len);
}
else
goto err;
break;
case MODEPAGE_POWER_COND:
len += sata_build_msense_page_1a(sdinfo, pc, buf+len);
break;
case MODEPAGE_ACOUSTIC_MANAG:
len += sata_build_msense_page_30(sdinfo, pc, buf+len);
break;
case MODEPAGE_ALLPAGES:
len += sata_build_msense_page_1(sdinfo, pc, buf+len);
len += sata_build_msense_page_8(sdinfo, pc, buf+len);
len += sata_build_msense_page_1a(sdinfo, pc, buf+len);
if (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED) {
len += sata_build_msense_page_1c(sdinfo, pc,
buf+len);
}
len += sata_build_msense_page_30(sdinfo, pc, buf+len);
break;
default:
err:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (spx->txlt_scsi_pkt->pkt_cdbp[0] == SCMD_MODE_SENSE) {
buf[0] = len - 1;
} else {
buf[0] = (len -2) >> 8;
buf[1] = (len -2) & 0xff;
}
if (scsipkt->pkt_cdbp[0] == SCMD_MODE_SENSE) {
alc_len = scsipkt->pkt_cdbp[4];
} else {
alc_len = scsipkt->pkt_cdbp[7];
alc_len = (alc_len << 8) | scsipkt->pkt_cdbp[8];
}
count = MIN(alc_len, len);
count = MIN(bp->b_bcount, count);
bcopy(buf, bp->b_un.b_addr, count);
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = alc_len > count ? alc_len - count : 0;
}
*scsipkt->pkt_scbp = STATUS_GOOD;
done:
mutex_exit(cport_mutex);
(void) kmem_free(buf, 1024);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_mode_select(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
struct scsi_extended_sense *sense;
int len, pagelen, count, pllen;
uint8_t *buf;
int rval, stat, reason;
uint_t nointr_flag;
int dmod = 0;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG2(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_mode_select, pc %x page code 0x%02x\n",
spx->txlt_scsi_pkt->pkt_cdbp[2] >> 6,
spx->txlt_scsi_pkt->pkt_cdbp[2] & 0x3f);
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
rval = TRAN_ACCEPT;
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
nointr_flag = scsipkt->pkt_flags & FLAG_NOINTR;
if (! (scsipkt->pkt_cdbp[1] & 0x10)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (scsipkt->pkt_cdbp[0] == SCMD_MODE_SELECT) {
pllen = scsipkt->pkt_cdbp[4];
} else {
pllen = scsipkt->pkt_cdbp[7];
pllen = (pllen << 8) | scsipkt->pkt_cdbp[7];
}
*scsipkt->pkt_scbp = STATUS_GOOD;
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount && pllen != 0) {
buf = (uint8_t *)bp->b_un.b_addr;
count = MIN(bp->b_bcount, pllen);
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = 0;
pllen = count;
if (scsipkt->pkt_cdbp[0] == SCMD_MODE_SELECT) {
if (count <= 4)
goto done;
len = buf[3] + 4;
} else {
if (count <= 8)
goto done;
len = buf[6];
len = (len << 8) + buf[7] + 8;
}
if (len >= count)
goto done;
pllen -= len;
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
scsipkt->pkt_flags |= FLAG_NOINTR;
}
spx->txlt_sata_pkt->satapkt_op_mode = SATA_OPMODE_SYNCH;
while (pllen > 0) {
switch ((int)buf[len]) {
case MODEPAGE_CACHING:
if (scsipkt->pkt_cdbp[1] & 0x01) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
stat = sata_mode_select_page_8(spx,
(struct mode_cache_scsi3 *)&buf[len],
pllen, &pagelen, &rval, &dmod);
if (stat != SATA_SUCCESS)
pllen = 0;
else {
len += pagelen;
pllen -= pagelen;
}
break;
case MODEPAGE_INFO_EXCPT:
stat = sata_mode_select_page_1c(spx,
(struct mode_info_excpt_page *)&buf[len],
pllen, &pagelen, &rval, &dmod);
if (stat != SATA_SUCCESS)
pllen = 0;
else {
len += pagelen;
pllen -= pagelen;
}
break;
case MODEPAGE_ACOUSTIC_MANAG:
stat = sata_mode_select_page_30(spx,
(struct mode_acoustic_management *)
&buf[len], pllen, &pagelen, &rval, &dmod);
if (stat != SATA_SUCCESS)
pllen = 0;
else {
len += pagelen;
pllen -= pagelen;
}
break;
case MODEPAGE_POWER_COND:
stat = sata_mode_select_page_1a(spx,
(struct mode_info_power_cond *)&buf[len],
pllen, &pagelen, &rval, &dmod);
if (stat != SATA_SUCCESS)
pllen = 0;
else {
len += pagelen;
pllen -= pagelen;
}
break;
default:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
goto done;
}
}
}
done:
mutex_exit(cport_mutex);
if (dmod != 0) {
sata_drive_info_t new_sdinfo, *sdinfo;
int rv = 0;
new_sdinfo.satadrv_type = SATA_DTYPE_ATADISK;
new_sdinfo.satadrv_addr =
spx->txlt_sata_pkt->satapkt_device.satadev_addr;
rv = sata_fetch_device_identify_data(spx->txlt_sata_hba_inst,
&new_sdinfo);
mutex_enter(cport_mutex);
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
if (sdinfo != NULL) {
if (rv == 0) {
sdinfo->satadrv_id = new_sdinfo.satadrv_id;
sata_save_drive_settings(sdinfo);
} else {
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_state = SATA_STATE_UNKNOWN;
}
}
if (rv != 0 || sdinfo == NULL) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
scsipkt->pkt_reason = CMD_INCOMPLETE;
rval = TRAN_ACCEPT;
}
mutex_exit(cport_mutex);
}
scsipkt->pkt_flags &= ~FLAG_NOINTR;
scsipkt->pkt_flags |= nointr_flag;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (rval);
}
static int
sata_txlt_ata_pass_thru(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
uint32_t xfer_len;
int extend = 0;
int synch, rval, reason;
mutex_enter(cport_mutex);
rval = sata_txlt_generic_pkt_info(spx, &reason, 1);
if ((rval != TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
if (scsipkt->pkt_cdbp[2] & SATL_APT_BM_T_DIR)
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
else
scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
if (((scsipkt->pkt_cdbp[1] >> 5) & 0x7) != 0) {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
if (((scsipkt->pkt_cdbp[2] >> 6) & 0x3) != 0) {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
switch ((scsipkt->pkt_cdbp[1] >> 1) & 0xf) {
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:
default:
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
case SATL_APT_P_NON_DATA:
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
break;
case SATL_APT_P_PIO_DATA_IN:
if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_WRITE) {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
if ((bp != NULL) && bp->b_un.b_addr && bp->b_bcount) {
sata_scsi_dmafree(NULL, scsipkt);
} else {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
break;
case SATL_APT_P_PIO_DATA_OUT:
if (scmd->satacmd_flags.sata_data_direction == SATA_DIR_READ) {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
if ((bp != NULL) && bp->b_un.b_addr && bp->b_bcount) {
sata_scsi_dmafree(NULL, scsipkt);
} else {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
break;
}
scmd->satacmd_addr_type = ATA_ADDR_LBA28;
scmd->satacmd_lba_low_msb = 0;
scmd->satacmd_lba_mid_msb = 0;
scmd->satacmd_lba_high_msb = 0;
scmd->satacmd_features_reg_ext = 0;
scmd->satacmd_sec_count_msb = 0;
switch ((uint_t)scsipkt->pkt_cdbp[0]) {
case SPC3_CMD_ATA_COMMAND_PASS_THROUGH12:
scmd->satacmd_lba_low_lsb = scsipkt->pkt_cdbp[5];
scmd->satacmd_lba_mid_lsb = scsipkt->pkt_cdbp[6];
scmd->satacmd_lba_high_lsb = scsipkt->pkt_cdbp[7];
scmd->satacmd_features_reg = scsipkt->pkt_cdbp[3];
scmd->satacmd_sec_count_lsb = scsipkt->pkt_cdbp[4];
scmd->satacmd_device_reg = scsipkt->pkt_cdbp[8];
scmd->satacmd_cmd_reg = scsipkt->pkt_cdbp[9];
break;
case SPC3_CMD_ATA_COMMAND_PASS_THROUGH16:
scmd->satacmd_device_reg = scsipkt->pkt_cdbp[13];
scmd->satacmd_cmd_reg = scsipkt->pkt_cdbp[14];
scmd->satacmd_lba_low_lsb = scsipkt->pkt_cdbp[8];
scmd->satacmd_lba_mid_lsb = scsipkt->pkt_cdbp[10];
scmd->satacmd_lba_high_lsb = scsipkt->pkt_cdbp[12];
scmd->satacmd_features_reg = scsipkt->pkt_cdbp[4];
scmd->satacmd_sec_count_lsb = scsipkt->pkt_cdbp[6];
if (scsipkt->pkt_cdbp[1] & SATL_APT_BM_EXTEND) {
extend = 1;
scmd->satacmd_addr_type = ATA_ADDR_LBA48;
scmd->satacmd_lba_low_msb = scsipkt->pkt_cdbp[7];
scmd->satacmd_lba_mid_msb = scsipkt->pkt_cdbp[9];
scmd->satacmd_lba_high_msb = scsipkt->pkt_cdbp[11];
scmd->satacmd_features_reg_ext = scsipkt->pkt_cdbp[3];
scmd->satacmd_sec_count_msb = scsipkt->pkt_cdbp[5];
}
break;
default:
cmn_err(CE_PANIC, "unexpected ATA pass-thru cmd %x",
scsipkt->pkt_cdbp[0]);
}
if (scsipkt->pkt_cdbp[2] & SATL_APT_BM_CK_COND) {
if (extend) {
scmd->satacmd_flags.sata_copy_out_sec_count_msb = 1;
scmd->satacmd_flags.sata_copy_out_lba_low_msb = 1;
scmd->satacmd_flags.sata_copy_out_lba_mid_msb = 1;
scmd->satacmd_flags.sata_copy_out_lba_high_msb = 1;
}
scmd->satacmd_flags.sata_copy_out_sec_count_lsb = 1;
scmd->satacmd_flags.sata_copy_out_lba_low_lsb = 1;
scmd->satacmd_flags.sata_copy_out_lba_mid_lsb = 1;
scmd->satacmd_flags.sata_copy_out_lba_high_lsb = 1;
scmd->satacmd_flags.sata_copy_out_device_reg = 1;
scmd->satacmd_flags.sata_copy_out_error_reg = 1;
}
switch (scsipkt->pkt_cdbp[2] & 0x03) {
case 1:
xfer_len = (uint32_t)scmd->satacmd_features_reg_ext << 8 |
scmd->satacmd_features_reg;
if (((scsipkt->pkt_cdbp[2] >> 2) & 1) == 0)
xfer_len *= SATA_DISK_SECTOR_SIZE;
break;
case 2:
xfer_len = (uint32_t)scmd->satacmd_sec_count_msb << 8 |
scmd->satacmd_sec_count_lsb;
if (((scsipkt->pkt_cdbp[2] >> 2) & 1) == 0)
xfer_len *= SATA_DISK_SECTOR_SIZE;
break;
case 3:
xfer_len = bp->b_bcount;
break;
default:
xfer_len = 0;
}
if (xfer_len > bp->b_bcount) {
mutex_exit(cport_mutex);
return (sata_txlt_ata_pass_thru_illegal_cmd(spx));
}
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp = sata_txlt_apt_completion;
synch = FALSE;
} else {
synch = TRUE;
}
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
sata_txlt_apt_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_log_sense(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
struct scsi_extended_sense *sense;
int len, count, alc_len;
int pc;
int page_code;
uint8_t *buf;
int rval, reason;
#define MAX_LOG_SENSE_PAGE_SIZE 512
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
SATADBG2(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_log_sense, pc 0x%x, page code 0x%x\n",
spx->txlt_scsi_pkt->pkt_cdbp[2] >> 6,
spx->txlt_scsi_pkt->pkt_cdbp[2] & 0x3f);
if (servicing_interrupt()) {
buf = kmem_zalloc(MAX_LOG_SENSE_PAGE_SIZE, KM_NOSLEEP);
if (buf == NULL) {
return (TRAN_BUSY);
}
} else {
buf = kmem_zalloc(MAX_LOG_SENSE_PAGE_SIZE, KM_SLEEP);
}
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
kmem_free(buf, MAX_LOG_SENSE_PAGE_SIZE);
return (rval);
}
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
pc = scsipkt->pkt_cdbp[2] >> 6;
page_code = scsipkt->pkt_cdbp[2] & 0x3f;
switch (pc) {
case PC_CUMULATIVE_VALUES:
break;
default:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
switch (page_code) {
case PAGE_CODE_GET_SUPPORTED_LOG_PAGES:
case PAGE_CODE_SELF_TEST_RESULTS:
case PAGE_CODE_INFORMATION_EXCEPTIONS:
case PAGE_CODE_SMART_READ_DATA:
case PAGE_CODE_START_STOP_CYCLE_COUNTER:
case PAGE_CODE_TEMPERATURE:
case PAGE_CODE_SOLID_STATE_MEDIA:
case PAGE_CODE_READ_ERRORS:
case PAGE_CODE_GENERAL_STATS:
break;
default:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (bp != NULL && bp->b_un.b_addr && bp->b_bcount) {
sata_id_t *sata_id;
sata_scsi_dmafree(NULL, scsipkt);
len = 0;
buf[len++] = page_code;
buf[len++] = 0;
buf[len++] = 0;
buf[len++] = 0;
sdinfo = sata_get_device_info(
spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_id = &sdinfo->satadrv_id;
switch (page_code) {
case PAGE_CODE_GET_SUPPORTED_LOG_PAGES:
len = sata_build_lsense_page_0(sdinfo, buf + len);
break;
case PAGE_CODE_SELF_TEST_RESULTS:
if ((! (sata_id->ai_cmdset84 &
SATA_SMART_SELF_TEST_SUPPORTED)) ||
(! (sata_id->ai_features87 &
SATA_SMART_SELF_TEST_SUPPORTED))) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
len = sata_build_lsense_page_10(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
case PAGE_CODE_INFORMATION_EXCEPTIONS:
if (! (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (! (sata_id->ai_features85 & SATA_SMART_ENABLED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ABORTED_COMMAND;
sense->es_add_code =
SCSI_ASC_ATA_DEV_FEAT_NOT_ENABLED;
sense->es_qual_code =
SCSI_ASCQ_ATA_DEV_FEAT_NOT_ENABLED;
goto done;
}
len = sata_build_lsense_page_2f(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
case PAGE_CODE_SMART_READ_DATA:
if (! (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (! (sata_id->ai_features85 & SATA_SMART_ENABLED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ABORTED_COMMAND;
sense->es_add_code =
SCSI_ASC_ATA_DEV_FEAT_NOT_ENABLED;
sense->es_qual_code =
SCSI_ASCQ_ATA_DEV_FEAT_NOT_ENABLED;
goto done;
}
len = sata_build_lsense_page_30(sdinfo, buf,
spx->txlt_sata_hba_inst);
goto no_header;
case PAGE_CODE_START_STOP_CYCLE_COUNTER:
if (! (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (! (sata_id->ai_features85 & SATA_SMART_ENABLED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ABORTED_COMMAND;
sense->es_add_code =
SCSI_ASC_ATA_DEV_FEAT_NOT_ENABLED;
sense->es_qual_code =
SCSI_ASCQ_ATA_DEV_FEAT_NOT_ENABLED;
goto done;
}
len = sata_build_lsense_page_0e(sdinfo, buf, spx);
goto no_header;
case PAGE_CODE_TEMPERATURE:
len = sata_build_lsense_page_0d(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
case PAGE_CODE_SOLID_STATE_MEDIA:
len = sata_build_lsense_page_11(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
case PAGE_CODE_READ_ERRORS:
len = sata_build_lsense_page_03(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
case PAGE_CODE_GENERAL_STATS:
len = sata_build_lsense_page_19(sdinfo, buf + len,
spx->txlt_sata_hba_inst);
break;
default:
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (len < 0) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
buf[2] = len >> 8;
buf[3] = len & 0xff;
len += SCSI_LOG_PAGE_HDR_LEN;
ASSERT(len <= MAX_LOG_SENSE_PAGE_SIZE);
no_header:
alc_len = scsipkt->pkt_cdbp[7];
alc_len = (alc_len << 8) | scsipkt->pkt_cdbp[8];
count = MIN(alc_len, len);
count = MIN(bp->b_bcount, count);
bcopy(buf, bp->b_un.b_addr, count);
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_resid = alc_len > count ? alc_len - count : 0;
}
*scsipkt->pkt_scbp = STATUS_GOOD;
done:
mutex_exit(cport_mutex);
(void) kmem_free(buf, MAX_LOG_SENSE_PAGE_SIZE);
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_read(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_drive_info_t *sdinfo;
sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx);
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
uint16_t sec_count;
uint64_t lba;
int rval, reason;
int synch;
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
switch ((uint_t)scsipkt->pkt_cdbp[0]) {
case SCMD_READ:
lba = (scsipkt->pkt_cdbp[1] & 0x1f);
lba = (lba << 8) | scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
sec_count = scsipkt->pkt_cdbp[4];
break;
case SCMD_READ_G1:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
sec_count = scsipkt->pkt_cdbp[7];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[8];
break;
case SCMD_READ_G5:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
sec_count = scsipkt->pkt_cdbp[6];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[7];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[8];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[9];
break;
case SCMD_READ_G4:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
lba = (lba << 8) | scsipkt->pkt_cdbp[6];
lba = (lba << 8) | scsipkt->pkt_cdbp[7];
lba = (lba << 8) | scsipkt->pkt_cdbp[8];
lba = (lba << 8) | scsipkt->pkt_cdbp[9];
sec_count = scsipkt->pkt_cdbp[10];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[11];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[12];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[13];
break;
default:
mutex_exit(cport_mutex);
return (sata_txlt_invalid_command(spx));
}
if ((lba >= sdinfo->satadrv_capacity) ||
((lba + sec_count) > sdinfo->satadrv_capacity)) {
mutex_exit(cport_mutex);
return (sata_txlt_lba_out_of_range(spx));
}
if (spx->txlt_dma_cookie_list == NULL) {
mutex_exit(cport_mutex);
return (sata_emul_rw_completion(spx));
}
scmd->satacmd_addr_type = ATA_ADDR_LBA;
scmd->satacmd_device_reg = SATA_ADH_LBA;
scmd->satacmd_cmd_reg = SATAC_READ_DMA;
if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA48) {
scmd->satacmd_addr_type = ATA_ADDR_LBA48;
scmd->satacmd_cmd_reg = SATAC_READ_DMA_EXT;
scmd->satacmd_sec_count_msb = sec_count >> 8;
#ifndef __lock_lint
scmd->satacmd_lba_low_msb = (lba >> 24) & 0xff;
scmd->satacmd_lba_mid_msb = (lba >> 32) & 0xff;
scmd->satacmd_lba_high_msb = lba >> 40;
#endif
} else if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA28) {
scmd->satacmd_addr_type = ATA_ADDR_LBA28;
scmd->satacmd_device_reg = SATA_ADH_LBA | ((lba >> 24) & 0xf);
}
scmd->satacmd_sec_count_lsb = sec_count & 0xff;
scmd->satacmd_lba_low_lsb = lba & 0xff;
scmd->satacmd_lba_mid_lsb = (lba >> 8) & 0xff;
scmd->satacmd_lba_high_lsb = (lba >> 16) & 0xff;
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
if (sata_func_enable & SATA_ENABLE_QUEUING) {
boolean_t using_queuing;
if ((sata_func_enable & SATA_ENABLE_NCQ) &&
(sdinfo->satadrv_features_support &
SATA_DEV_F_NCQ) &&
(SATA_FEATURES(spx->txlt_sata_hba_inst) &
SATA_CTLF_NCQ)) {
using_queuing = B_TRUE;
scmd->satacmd_cmd_reg =
SATAC_READ_FPDMA_QUEUED;
scmd->satacmd_features_reg_ext =
scmd->satacmd_sec_count_msb;
scmd->satacmd_sec_count_msb = 0;
} else if ((sdinfo->satadrv_features_support &
SATA_DEV_F_TCQ) &&
(SATA_FEATURES(spx->txlt_sata_hba_inst) &
SATA_CTLF_QCMD)) {
using_queuing = B_TRUE;
if (sdinfo->satadrv_features_support &
SATA_DEV_F_LBA48) {
scmd->satacmd_cmd_reg =
SATAC_READ_DMA_QUEUED_EXT;
scmd->satacmd_features_reg_ext =
scmd->satacmd_sec_count_msb;
scmd->satacmd_sec_count_msb = 0;
} else {
scmd->satacmd_cmd_reg =
SATAC_READ_DMA_QUEUED;
}
} else
using_queuing = B_FALSE;
if (using_queuing) {
scmd->satacmd_features_reg =
scmd->satacmd_sec_count_lsb;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_flags.sata_queued = B_TRUE;
scmd->satacmd_flags.sata_max_queue_depth =
sdinfo->satadrv_max_queue_depth - 1;
} else if (sdinfo->satadrv_features_enabled &
SATA_DEV_F_E_UNTAGGED_QING) {
scmd->satacmd_flags.sata_max_queue_depth =
SATA_QDEPTH(shi) <= SATA_MAX_QUEUE_DEPTH ?
SATA_QDEPTH(shi) - 1: SATA_MAX_QUEUE_DEPTH - 1;
} else {
scmd->satacmd_flags.sata_max_queue_depth = 0;
}
} else
scmd->satacmd_flags.sata_max_queue_depth = 0;
SATADBG3(SATA_DBG_HBA_IF, spx->txlt_sata_hba_inst,
"sata_txlt_read cmd 0x%2x, lba %llx, sec count %x\n",
scmd->satacmd_cmd_reg, lba, sec_count);
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp = sata_txlt_rw_completion;
synch = FALSE;
} else
synch = TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"synchronous execution status %x\n",
spx->txlt_sata_pkt->satapkt_reason);
sata_txlt_rw_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_write(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_drive_info_t *sdinfo;
sata_hba_inst_t *shi = SATA_TXLT_HBA_INST(spx);
uint16_t sec_count;
uint64_t lba;
int rval, reason;
int synch;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
switch ((uint_t)scsipkt->pkt_cdbp[0]) {
case SCMD_WRITE:
lba = (scsipkt->pkt_cdbp[1] & 0x1f);
lba = (lba << 8) | scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
sec_count = scsipkt->pkt_cdbp[4];
break;
case SCMD_WRITE_G1:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
sec_count = scsipkt->pkt_cdbp[7];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[8];
break;
case SCMD_WRITE_G5:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
sec_count = scsipkt->pkt_cdbp[6];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[7];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[8];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[9];
break;
case SCMD_WRITE_G4:
lba = scsipkt->pkt_cdbp[2];
lba = (lba << 8) | scsipkt->pkt_cdbp[3];
lba = (lba << 8) | scsipkt->pkt_cdbp[4];
lba = (lba << 8) | scsipkt->pkt_cdbp[5];
lba = (lba << 8) | scsipkt->pkt_cdbp[6];
lba = (lba << 8) | scsipkt->pkt_cdbp[7];
lba = (lba << 8) | scsipkt->pkt_cdbp[8];
lba = (lba << 8) | scsipkt->pkt_cdbp[9];
sec_count = scsipkt->pkt_cdbp[10];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[11];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[12];
sec_count = (sec_count << 8) | scsipkt->pkt_cdbp[13];
break;
default:
mutex_exit(cport_mutex);
return (sata_txlt_invalid_command(spx));
}
if ((lba >= sdinfo->satadrv_capacity) ||
((lba + sec_count) > sdinfo->satadrv_capacity)) {
mutex_exit(cport_mutex);
return (sata_txlt_lba_out_of_range(spx));
}
if (spx->txlt_dma_cookie_list == NULL) {
mutex_exit(cport_mutex);
return (sata_emul_rw_completion(spx));
}
scmd->satacmd_addr_type = ATA_ADDR_LBA;
scmd->satacmd_device_reg = SATA_ADH_LBA;
scmd->satacmd_cmd_reg = SATAC_WRITE_DMA;
if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA48) {
scmd->satacmd_addr_type = ATA_ADDR_LBA48;
scmd->satacmd_cmd_reg = SATAC_WRITE_DMA_EXT;
scmd->satacmd_sec_count_msb = sec_count >> 8;
scmd->satacmd_lba_low_msb = (lba >> 24) & 0xff;
#ifndef __lock_lint
scmd->satacmd_lba_mid_msb = (lba >> 32) & 0xff;
scmd->satacmd_lba_high_msb = lba >> 40;
#endif
} else if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA28) {
scmd->satacmd_addr_type = ATA_ADDR_LBA28;
scmd->satacmd_device_reg = SATA_ADH_LBA | ((lba >> 24) & 0xf);
}
scmd->satacmd_sec_count_lsb = sec_count & 0xff;
scmd->satacmd_lba_low_lsb = lba & 0xff;
scmd->satacmd_lba_mid_lsb = (lba >> 8) & 0xff;
scmd->satacmd_lba_high_lsb = (lba >> 16) & 0xff;
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
if (sata_func_enable & SATA_ENABLE_QUEUING) {
boolean_t using_queuing;
if ((sata_func_enable & SATA_ENABLE_NCQ) &&
(sdinfo->satadrv_features_support &
SATA_DEV_F_NCQ) &&
(SATA_FEATURES(spx->txlt_sata_hba_inst) &
SATA_CTLF_NCQ)) {
using_queuing = B_TRUE;
scmd->satacmd_cmd_reg =
SATAC_WRITE_FPDMA_QUEUED;
scmd->satacmd_features_reg_ext =
scmd->satacmd_sec_count_msb;
scmd->satacmd_sec_count_msb = 0;
} else if ((sdinfo->satadrv_features_support &
SATA_DEV_F_TCQ) &&
(SATA_FEATURES(spx->txlt_sata_hba_inst) &
SATA_CTLF_QCMD)) {
using_queuing = B_TRUE;
if (sdinfo->satadrv_features_support &
SATA_DEV_F_LBA48) {
scmd->satacmd_cmd_reg =
SATAC_WRITE_DMA_QUEUED_EXT;
scmd->satacmd_features_reg_ext =
scmd->satacmd_sec_count_msb;
scmd->satacmd_sec_count_msb = 0;
} else {
scmd->satacmd_cmd_reg =
SATAC_WRITE_DMA_QUEUED;
}
} else
using_queuing = B_FALSE;
if (using_queuing) {
scmd->satacmd_features_reg =
scmd->satacmd_sec_count_lsb;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_flags.sata_queued = B_TRUE;
scmd->satacmd_flags.sata_max_queue_depth =
sdinfo->satadrv_max_queue_depth - 1;
} else if (sdinfo->satadrv_features_enabled &
SATA_DEV_F_E_UNTAGGED_QING) {
scmd->satacmd_flags.sata_max_queue_depth =
SATA_QDEPTH(shi) <= SATA_MAX_QUEUE_DEPTH ?
SATA_QDEPTH(shi) - 1: SATA_MAX_QUEUE_DEPTH - 1;
} else {
scmd->satacmd_flags.sata_max_queue_depth = 0;
}
} else
scmd->satacmd_flags.sata_max_queue_depth = 0;
SATADBG3(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_write cmd 0x%2x, lba %llx, sec count %x\n",
scmd->satacmd_cmd_reg, lba, sec_count);
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp = sata_txlt_rw_completion;
synch = FALSE;
} else
synch = TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"synchronous execution status %x\n",
spx->txlt_sata_pkt->satapkt_reason);
sata_txlt_rw_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_write_buffer(sata_pkt_txlate_t *spx)
{
#define WB_DOWNLOAD_MICROCODE_AND_REVERT_MODE 4
#define WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE 5
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct sata_pkt *sata_pkt = spx->txlt_sata_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
struct scsi_extended_sense *sense;
int rval, mode, sector_count, reason;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
mode = scsipkt->pkt_cdbp[1] & 0x1f;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_write_buffer, mode 0x%x\n", mode);
mutex_enter(cport_mutex);
if ((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) {
mutex_exit(cport_mutex);
return (rval);
}
spx->txlt_sata_pkt->satapkt_op_mode
|= SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_WRITE;
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
switch (mode) {
case WB_DOWNLOAD_MICROCODE_AND_REVERT_MODE:
scmd->satacmd_features_reg = SATA_DOWNLOAD_MCODE_TEMP;
break;
case WB_DOWNLOAD_MICROCODE_AND_SAVE_MODE:
scmd->satacmd_features_reg = SATA_DOWNLOAD_MCODE_SAVE;
break;
default:
goto bad_param;
}
*scsipkt->pkt_scbp = STATUS_GOOD;
scmd->satacmd_cmd_reg = SATAC_DOWNLOAD_MICROCODE;
if ((bp->b_bcount % SATA_DISK_SECTOR_SIZE) != 0)
goto bad_param;
sector_count = bp->b_bcount / SATA_DISK_SECTOR_SIZE;
scmd->satacmd_sec_count_lsb = (uint8_t)sector_count;
scmd->satacmd_lba_low_lsb = ((uint16_t)sector_count) >> 8;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_device_reg = 0;
spx->txlt_sata_pkt->satapkt_comp = NULL;
scmd->satacmd_addr_type = 0;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
scsipkt->pkt_reason = CMD_CMPLT;
sata_reidentify_device(spx);
} else {
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
sense->es_key = KEY_HARDWARE_ERROR;
break;
case SATA_PKT_DEV_ERROR:
if (sata_pkt->satapkt_cmd.satacmd_status_reg &
SATA_STATUS_ERR) {
sata_decode_device_error(spx, sense);
break;
}
break;
case SATA_PKT_TIMEOUT:
scsipkt->pkt_reason = CMD_TIMEOUT;
scsipkt->pkt_statistics |=
STAT_TIMEOUT | STAT_DEV_RESET;
break;
case SATA_PKT_ABORTED:
scsipkt->pkt_reason = CMD_ABORTED;
scsipkt->pkt_statistics |= STAT_ABORTED;
break;
case SATA_PKT_RESET:
scsipkt->pkt_reason = CMD_RESET;
scsipkt->pkt_statistics |= STAT_DEV_RESET;
break;
default:
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_txlt_nodata_cmd_completion: "
"invalid packet completion reason %d",
sata_pkt->satapkt_reason));
scsipkt->pkt_reason = CMD_TRAN_ERR;
break;
}
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
return (TRAN_ACCEPT);
bad_param:
mutex_exit(cport_mutex);
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (rval);
}
static void
sata_reidentify_device(sata_pkt_txlate_t *spx)
{
#define DOWNLOAD_WAIT_TIME_SECS 60
#define DOWNLOAD_WAIT_INTERVAL_SECS 1
int rval;
int retry_cnt;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_hba_inst_t *sata_hba_inst = spx->txlt_sata_hba_inst;
sata_device_t sata_device = spx->txlt_sata_pkt->satapkt_device;
sata_drive_info_t *sdinfo;
for (retry_cnt = 0;
retry_cnt < DOWNLOAD_WAIT_TIME_SECS / DOWNLOAD_WAIT_INTERVAL_SECS;
retry_cnt++) {
rval = sata_probe_device(sata_hba_inst, &sata_device);
if (rval == SATA_SUCCESS) {
sdinfo = sata_get_device_info(sata_hba_inst,
&sata_device);
if (sata_initialize_device(sata_hba_inst, sdinfo) !=
SATA_SUCCESS) {
rval = sata_initialize_device(sata_hba_inst,
sdinfo);
if (rval == SATA_RETRY)
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d pmport %d -"
" default device features could not"
" be set. Device may not operate "
"as expected.",
sata_device.satadev_addr.cport,
sata_device.satadev_addr.pmport);
}
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
return;
} else if (rval == SATA_RETRY) {
delay(drv_usectohz(1000000 *
DOWNLOAD_WAIT_INTERVAL_SECS));
continue;
} else
break;
}
SATA_LOG_D((sata_hba_inst, CE_WARN,
"Cannot probe device after downloading microcode\n"));
(void) (*SATA_RESET_DPORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
static int
sata_txlt_synchronize_cache(sata_pkt_txlate_t *spx)
{
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
int rval, reason;
int synch;
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 1)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_cmd_reg = SATAC_FLUSH_CACHE;
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;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"sata_txlt_synchronize_cache\n", NULL);
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp =
sata_txlt_nodata_cmd_completion;
synch = FALSE;
} else
synch = TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"synchronous execution status %x\n",
spx->txlt_sata_pkt->satapkt_reason);
sata_txlt_nodata_cmd_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
#define RCTD(pkt) (pkt->pkt_cdbp[1] & 0x80)
static int
sata_txlt_supported_ops(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *pkt = spx->txlt_scsi_pkt;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_drive_info_t *sdinfo;
const struct sata_cmd_info *sci = NULL;
struct sata_txlt_buf sbuf;
uint32_t alc_len;
uint_t i;
int reason, rval;
uint16_t svcact;
uint8_t op, reporting_opts;
if (bp == NULL || bp->b_un.b_addr == 0 || bp->b_bcount == 0) {
*pkt->pkt_scbp = STATUS_GOOD;
goto done;
}
mutex_enter(&SATA_TXLT_CPORT_MUTEX(spx));
rval = sata_txlt_generic_pkt_info(spx, &reason, 1);
if (rval != TRAN_ACCEPT || reason == CMD_DEV_GONE) {
mutex_exit(&SATA_TXLT_CPORT_MUTEX(spx));
return (rval);
}
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_scsi_dmafree(NULL, pkt);
pkt->pkt_reason = CMD_CMPLT;
pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | STATE_SENT_CMD |
STATE_GOT_STATUS;
op = pkt->pkt_cdbp[3];
svcact = BE_IN16(&pkt->pkt_cdbp[4]);
alc_len = BE_IN32(&pkt->pkt_cdbp[6]);
reporting_opts = pkt->pkt_cdbp[2] & 0x07;
if (reporting_opts > 0x03) {
struct scsi_extended_sense *sense;
*pkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
sbuf_init(&sbuf, bp, alc_len);
if (reporting_opts == 0)
sbuf_put32(&sbuf, 0);
for (i = 0, sci = &sata_cmd_info[0]; i < ARRAY_SIZE(sata_cmd_info);
i++, sci++) {
const boolean_t has_svc_act = (sci->sci_flags & SCF_SVC_ACT) ?
B_TRUE : B_FALSE;
if (reporting_opts == 0) {
uint8_t flags = 0;
if (sci->sci_supported != NULL &&
!sci->sci_supported(spx, sdinfo)) {
continue;
}
if (has_svc_act)
flags |= 0x01;
if (RCTD(pkt))
flags |= 0x02;
sbuf_put8(&sbuf, sci->sci_op);
sbuf_put8(&sbuf, 0);
sbuf_put16(&sbuf, sci->sci_svcact);
sbuf_put8(&sbuf, 0);
sbuf_put8(&sbuf, flags);
sbuf_put16(&sbuf, sata_cmd_cdblen(sci));
if (RCTD(pkt)) {
sbuf_put16(&sbuf, 0x0a);
sbuf_put8(&sbuf, 0);
sbuf_put8(&sbuf, 0);
sbuf_put32(&sbuf, 0);
sbuf_put32(&sbuf, 0);
}
continue;
}
if (sci->sci_op != op)
continue;
if (has_svc_act) {
if (reporting_opts == 0x01) {
struct scsi_extended_sense *sense;
*pkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
if (sci->sci_svcact != svcact)
continue;
} else {
if (reporting_opts == 0x02) {
struct scsi_extended_sense *sense;
*pkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
goto done;
}
}
break;
}
if (reporting_opts > 0) {
uint16_t cdblen;
uint8_t support;
if (i < ARRAY_SIZE(sata_cmd_info)) {
cdblen = sata_cmd_cdblen(sci);
if (sci->sci_supported == NULL ||
sci->sci_supported(spx, sdinfo)) {
support = 0x03;
} else {
support = 0x01;
}
} else {
cdblen = 0;
support = 0x01;
}
if (RCTD(pkt))
support |= 0x80;
sbuf_put8(&sbuf, 0);
sbuf_put8(&sbuf, support);
sbuf_put16(&sbuf, cdblen);
sbuf_copy(&sbuf, sci->sci_cdbusage, cdblen);
if (RCTD(pkt)) {
sbuf_put16(&sbuf, 0x0a);
sbuf_put8(&sbuf, 0);
sbuf_put8(&sbuf, 0);
sbuf_put32(&sbuf, 0);
sbuf_put32(&sbuf, 0);
}
}
if (reporting_opts == 0)
sbuf_set_len(&sbuf, 0, sizeof (uint32_t), 4);
pkt->pkt_state |= STATE_XFERRED_DATA;
pkt->pkt_resid = sbuf_resid(&sbuf, bp, alc_len);
*pkt->pkt_scbp = STATUS_GOOD;
done:
mutex_exit(&SATA_TXLT_CPORT_MUTEX(spx));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", pkt->pkt_reason);
taskq_t *tq = SATA_TXLT_TASKQ(spx);
task_func_t *func = (task_func_t *)pkt->pkt_comp;
uint_t tq_flags = servicing_interrupt() ? TQ_NOSLEEP : TQ_SLEEP;
if ((pkt->pkt_flags & FLAG_NOINTR) != 0 || pkt->pkt_comp == NULL)
return (TRAN_ACCEPT);
if (taskq_dispatch(tq, func, pkt, tq_flags) == TASKQID_INVALID)
return (TRAN_BUSY);
return (TRAN_ACCEPT);
}
static int
sata_hba_start(sata_pkt_txlate_t *spx, int *rval)
{
int stat;
uint8_t cport = SATA_TXLT_CPORT(spx);
uint8_t pmport = SATA_TXLT_PMPORT(spx);
sata_hba_inst_t *sata_hba_inst = spx->txlt_sata_hba_inst;
sata_drive_info_t *sdinfo;
sata_pmult_info_t *pminfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_device_t *sata_device = NULL;
uint8_t cmd;
struct sata_cmd_flags cmd_flags;
ASSERT(spx->txlt_sata_pkt != NULL);
ASSERT(mutex_owned(&SATA_CPORT_MUTEX(sata_hba_inst, cport)));
sdinfo = sata_get_device_info(sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
ASSERT(sdinfo != NULL);
if (sdinfo->satadrv_addr.qual == SATA_ADDR_DPMPORT ||
sdinfo->satadrv_addr.qual == SATA_ADDR_PMPORT) {
pminfo = SATA_PMULT_INFO(sata_hba_inst, cport);
pmportinfo = pminfo->pmult_dev_port[pmport];
ASSERT(pminfo != NULL);
if (pminfo->pmult_event_flags & SATA_EVNT_CLEAR_DEVICE_RESET) {
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.
sata_clear_dev_reset = B_TRUE;
pminfo->pmult_event_flags &=
~SATA_EVNT_CLEAR_DEVICE_RESET;
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"sata_hba_start: clearing device reset state"
"on pmult.\n", NULL);
}
} else {
if (sdinfo->satadrv_event_flags &
SATA_EVNT_CLEAR_DEVICE_RESET) {
spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags.
sata_clear_dev_reset = B_TRUE;
sdinfo->satadrv_event_flags &=
~SATA_EVNT_CLEAR_DEVICE_RESET;
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"sata_hba_start: clearing device reset state\n",
NULL);
}
}
cmd = spx->txlt_sata_pkt->satapkt_cmd.satacmd_cmd_reg;
cmd_flags = spx->txlt_sata_pkt->satapkt_cmd.satacmd_flags;
sata_device = &spx->txlt_sata_pkt->satapkt_device;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Sata cmd 0x%2x\n", cmd);
stat = (*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst),
spx->txlt_sata_pkt);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if (stat == SATA_TRAN_ACCEPTED) {
*rval = TRAN_ACCEPT;
return (0);
}
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
switch (stat) {
case SATA_TRAN_QUEUE_FULL:
SATADBG1(SATA_DBG_HBA_IF, sata_hba_inst,
"sata_hba_start: queue full\n", NULL);
spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
*spx->txlt_scsi_pkt->pkt_scbp = STATUS_QFULL;
*rval = TRAN_BUSY;
break;
case SATA_TRAN_PORT_ERROR:
if (spx->txlt_sata_pkt->satapkt_device.satadev_addr.qual ==
SATA_ADDR_CPORT ||
spx->txlt_sata_pkt->satapkt_device.satadev_addr.qual ==
SATA_ADDR_DCPORT)
sata_log(sata_hba_inst, CE_CONT,
"SATA port %d error",
sata_device->satadev_addr.cport);
else
sata_log(sata_hba_inst, CE_CONT,
"SATA port %d:%d error\n",
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
if (sata_device->satadev_addr.qual == SATA_ADDR_DPMPORT ||
sata_device->satadev_addr.qual == SATA_ADDR_PMPORT) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
mutex_exit(&pmportinfo->pmport_mutex);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
} else {
sata_update_port_info(sata_hba_inst, sata_device);
}
spx->txlt_scsi_pkt->pkt_reason = CMD_TRAN_ERR;
*rval = TRAN_FATAL_ERROR;
break;
case SATA_TRAN_CMD_UNSUPPORTED:
if ((sdinfo != NULL) &&
(sdinfo->satadrv_state & SATA_DSTATE_RESET))
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"sat_hba_start: cmd 0x%2x rejected "
"with SATA_TRAN_CMD_UNSUPPORTED status\n", cmd);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
(void) sata_txlt_invalid_command(spx);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
*rval = TRAN_ACCEPT;
break;
case SATA_TRAN_BUSY:
if (sdinfo != NULL) {
sdinfo->satadrv_state =
spx->txlt_sata_pkt->satapkt_device.satadev_state;
if (sdinfo->satadrv_state & SATA_DSTATE_RESET) {
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"sata_hba_start: cmd 0x%2x rejected "
"because of device reset condition\n",
cmd);
} else {
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"sata_hba_start: cmd 0x%2x rejected "
"with SATA_TRAN_BUSY status\n",
cmd);
}
}
spx->txlt_scsi_pkt->pkt_reason = CMD_INCOMPLETE;
*rval = TRAN_BUSY;
break;
default:
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_start: unrecognized HBA response "
"to cmd : 0x%2x resp 0x%x", cmd, rval));
spx->txlt_scsi_pkt->pkt_reason = CMD_TRAN_ERR;
*rval = TRAN_FATAL_ERROR;
break;
}
if (cmd_flags.sata_clear_dev_reset) {
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo != NULL) {
if (sdinfo->satadrv_addr.qual == SATA_ADDR_PMPORT ||
sdinfo->satadrv_addr.qual == SATA_ADDR_DPMPORT) {
pminfo->pmult_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
} else {
sdinfo->satadrv_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
}
}
return (-1);
}
static int
sata_txlt_lba_out_of_range(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
scsipkt->pkt_reason = CMD_CMPLT;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
*scsipkt->pkt_scbp = STATUS_CHECK;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_LBA_OUT_OF_RANGE;
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static void
sata_decode_device_error(sata_pkt_txlate_t *spx,
struct scsi_extended_sense *sense)
{
uint8_t err_reg = spx->txlt_sata_pkt->satapkt_cmd.satacmd_error_reg;
ASSERT(sense != NULL);
ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_status_reg &
SATA_STATUS_ERR);
if (err_reg & SATA_ERROR_ICRC) {
sense->es_key = KEY_ABORTED_COMMAND;
sense->es_add_code = 0x08;
return;
}
if (err_reg & SATA_ERROR_UNC) {
sense->es_key = KEY_MEDIUM_ERROR;
return;
}
if (err_reg & (SATA_ERROR_MCR | SATA_ERROR_NM)) {
sense->es_key = KEY_UNIT_ATTENTION;
sense->es_add_code = 0x3a;
return;
}
if (err_reg & SATA_ERROR_IDNF) {
if (err_reg & SATA_ERROR_ABORT) {
sense->es_key = KEY_ABORTED_COMMAND;
} else {
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = 0x21;
}
return;
}
if (err_reg & SATA_ERROR_ABORT) {
ASSERT(spx->txlt_sata_pkt != NULL);
sense->es_key = KEY_ABORTED_COMMAND;
return;
}
}
static void
sata_extract_error_lba(sata_pkt_txlate_t *spx, uint64_t *lba)
{
sata_cmd_t *sata_cmd = &spx->txlt_sata_pkt->satapkt_cmd;
*lba = 0;
if (sata_cmd->satacmd_addr_type == ATA_ADDR_LBA48) {
*lba = sata_cmd->satacmd_lba_high_msb;
*lba = (*lba << 8) | sata_cmd->satacmd_lba_mid_msb;
*lba = (*lba << 8) | sata_cmd->satacmd_lba_low_msb;
} else if (sata_cmd->satacmd_addr_type == ATA_ADDR_LBA28) {
*lba = sata_cmd->satacmd_device_reg & 0xf;
}
*lba = (*lba << 8) | sata_cmd->satacmd_lba_high_lsb;
*lba = (*lba << 8) | sata_cmd->satacmd_lba_mid_lsb;
*lba = (*lba << 8) | sata_cmd->satacmd_lba_low_lsb;
}
static struct scsi_extended_sense *
sata_arq_sense(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_arq_status *arqs;
struct scsi_extended_sense *sense;
scsipkt->pkt_state |= STATE_ARQ_DONE;
arqs = (struct scsi_arq_status *)scsipkt->pkt_scbp;
*(uchar_t *)&arqs->sts_status = STATUS_CHECK;
*(uchar_t *)&arqs->sts_rqpkt_status = STATUS_GOOD;
arqs->sts_rqpkt_reason = CMD_CMPLT;
arqs->sts_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_XFERRED_DATA | STATE_SENT_CMD | STATE_GOT_STATUS;
arqs->sts_rqpkt_resid = 0;
sense = &arqs->sts_sensedata;
bzero(sense, sizeof (struct scsi_extended_sense));
sata_fixed_sense_data_preset(sense);
return (sense);
}
static int
sata_txlt_ata_pass_thru_illegal_cmd(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense = sata_arq_sense(spx);
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_CHECK;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_txlt_unmap_nodata_cmd(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static int
sata_emul_rw_completion(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
if (servicing_interrupt()) {
if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_NOSLEEP) ==
TASKQID_INVALID) {
return (TRAN_BUSY);
}
} else if (taskq_dispatch(SATA_TXLT_TASKQ(spx),
(task_func_t *)spx->txlt_scsi_pkt->pkt_comp,
(void *)spx->txlt_scsi_pkt, TQ_SLEEP) == TASKQID_INVALID) {
return (TRAN_BUSY);
}
}
return (TRAN_ACCEPT);
}
static void
sata_txlt_rw_completion(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_cmd_t *scmd = &sata_pkt->satapkt_cmd;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
uint64_t lba;
struct buf *bp;
int rval;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (spx->txlt_tmp_buf != NULL) {
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
if (bp->b_flags & B_READ) {
rval = ddi_dma_sync(
spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORCPU);
ASSERT(rval == DDI_SUCCESS);
bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr,
bp->b_bcount);
}
}
} else {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
ASSERT(sense != NULL);
if (sata_pkt->satapkt_reason == SATA_PKT_DEV_ERROR) {
if ((scmd->satacmd_addr_type == ATA_ADDR_LBA48) &&
(scmd->satacmd_lba_mid_msb != 0 ||
scmd->satacmd_lba_high_msb != 0)) {
sense->es_valid = 0;
} else {
sata_extract_error_lba(spx, &lba);
sense->es_info_1 = (lba & 0xFF000000) >> 24;
sense->es_info_2 = (lba & 0xFF0000) >> 16;
sense->es_info_3 = (lba & 0xFF00) >> 8;
sense->es_info_4 = lba & 0xFF;
}
} else {
sense->es_valid = 0;
}
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
sense->es_key = KEY_HARDWARE_ERROR;
break;
case SATA_PKT_DEV_ERROR:
if (sata_pkt->satapkt_cmd.satacmd_status_reg &
SATA_STATUS_ERR) {
sata_decode_device_error(spx, sense);
if (sense->es_key == KEY_MEDIUM_ERROR) {
switch (scmd->satacmd_cmd_reg) {
case SATAC_READ_DMA:
case SATAC_READ_DMA_EXT:
case SATAC_READ_DMA_QUEUED:
case SATAC_READ_DMA_QUEUED_EXT:
case SATAC_READ_FPDMA_QUEUED:
sense->es_add_code =
SD_SCSI_ASC_UNREC_READ_ERR;
break;
case SATAC_WRITE_DMA:
case SATAC_WRITE_DMA_EXT:
case SATAC_WRITE_DMA_QUEUED:
case SATAC_WRITE_DMA_QUEUED_EXT:
case SATAC_WRITE_FPDMA_QUEUED:
sense->es_add_code =
SD_SCSI_ASC_WRITE_ERR;
break;
default:
SATA_LOG_D((
spx->txlt_sata_hba_inst,
CE_WARN,
"sata_txlt_rw_completion :"
"internal error - invalid "
"command 0x%2x",
scmd->satacmd_cmd_reg));
break;
}
}
break;
}
scsipkt->pkt_reason = CMD_INCOMPLETE;
break;
case SATA_PKT_TIMEOUT:
scsipkt->pkt_reason = CMD_TIMEOUT;
scsipkt->pkt_statistics |=
STAT_TIMEOUT | STAT_DEV_RESET;
sense->es_key = KEY_ABORTED_COMMAND;
break;
case SATA_PKT_ABORTED:
scsipkt->pkt_reason = CMD_ABORTED;
scsipkt->pkt_statistics |= STAT_ABORTED;
sense->es_key = KEY_ABORTED_COMMAND;
break;
case SATA_PKT_RESET:
scsipkt->pkt_reason = CMD_RESET;
scsipkt->pkt_statistics |= STAT_DEV_RESET;
sense->es_key = KEY_ABORTED_COMMAND;
break;
default:
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_txlt_rw_completion: "
"invalid packet completion reason"));
scsipkt->pkt_reason = CMD_TRAN_ERR;
break;
}
}
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
static void
sata_txlt_nodata_cmd_completion(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_set_arq_data(sata_pkt);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
static void
sata_txlt_apt_completion(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_cmd_t *scmd = &sata_pkt->satapkt_cmd;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp;
uint8_t sense_key = 0, addl_sense_code = 0, addl_sense_qual = 0;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (scsipkt->pkt_cdbp[2] & SATL_APT_BM_CK_COND) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sata_fill_ata_return_desc(sata_pkt,
KEY_RECOVERABLE_ERROR,
SD_SCSI_ASC_APT_INFO_AVAIL, 0x1d);
}
if (spx->txlt_tmp_buf != NULL) {
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
if (bp->b_flags & B_READ) {
bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr,
bp->b_bcount);
}
}
} else {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
if (scmd->satacmd_status_reg & SATA_STATUS_DF) {
sense_key = KEY_HARDWARE_ERROR;
addl_sense_code = SD_SCSI_ASC_INTERNAL_TARGET_FAILURE;
addl_sense_qual = 0;
} else if (scmd->satacmd_status_reg & SATA_STATUS_ERR) {
if (scmd->satacmd_error_reg & SATA_ERROR_NM) {
sense_key = KEY_NOT_READY;
addl_sense_code =
SD_SCSI_ASC_MEDIUM_NOT_PRESENT;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_UNC) {
sense_key = KEY_MEDIUM_ERROR;
addl_sense_code = SD_SCSI_ASC_UNREC_READ_ERR;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ILI) {
sense_key = KEY_DATA_PROTECT;
addl_sense_code = SD_SCSI_ASC_WRITE_PROTECTED;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_IDNF) {
sense_key = KEY_ILLEGAL_REQUEST;
addl_sense_code = SD_SCSI_ASC_LBA_OUT_OF_RANGE;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ABORT) {
sense_key = KEY_ABORTED_COMMAND;
addl_sense_code = SD_SCSI_ASC_NO_ADD_SENSE;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_MC) {
sense_key = KEY_UNIT_ATTENTION;
addl_sense_code =
SD_SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_MCR) {
sense_key = KEY_UNIT_ATTENTION;
addl_sense_code = SD_SCSI_ASC_OP_MEDIUM_REM_REQ;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ICRC) {
sense_key = KEY_ABORTED_COMMAND;
addl_sense_code =
SD_SCSI_ASC_INFO_UNIT_IUCRC_ERR;
addl_sense_qual = 0;
}
}
sata_fill_ata_return_desc(sata_pkt, sense_key, addl_sense_code,
addl_sense_qual);
}
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
static void
sata_txlt_unmap_completion(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_cmd_t *scmd = &sata_pkt->satapkt_cmd;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct buf *bp;
uint8_t sense_key = 0, addl_sense_code = 0, addl_sense_qual = 0;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_XFERRED_DATA | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (spx->txlt_tmp_buf != NULL) {
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
if (bp->b_flags & B_READ) {
bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr,
bp->b_bcount);
}
}
} else {
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
if (scmd->satacmd_status_reg & SATA_STATUS_DF) {
sense_key = KEY_HARDWARE_ERROR;
addl_sense_code = SD_SCSI_ASC_INTERNAL_TARGET_FAILURE;
addl_sense_qual = 0;
} else if (scmd->satacmd_status_reg & SATA_STATUS_ERR) {
if (scmd->satacmd_error_reg & SATA_ERROR_NM) {
sense_key = KEY_NOT_READY;
addl_sense_code =
SD_SCSI_ASC_MEDIUM_NOT_PRESENT;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_UNC) {
sense_key = KEY_MEDIUM_ERROR;
addl_sense_code = SD_SCSI_ASC_WRITE_ERR;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ILI) {
sense_key = KEY_DATA_PROTECT;
addl_sense_code = SD_SCSI_ASC_WRITE_PROTECTED;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_IDNF) {
sense_key = KEY_ILLEGAL_REQUEST;
addl_sense_code = SD_SCSI_ASC_LBA_OUT_OF_RANGE;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ABORT) {
sense_key = KEY_ABORTED_COMMAND;
addl_sense_code = SD_SCSI_ASC_NO_ADD_SENSE;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_MC) {
sense_key = KEY_UNIT_ATTENTION;
addl_sense_code =
SD_SCSI_ASC_MEDIUM_MAY_HAVE_CHANGED;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_MCR) {
sense_key = KEY_UNIT_ATTENTION;
addl_sense_code = SD_SCSI_ASC_OP_MEDIUM_REM_REQ;
addl_sense_qual = 0;
} else if (scmd->satacmd_error_reg & SATA_ERROR_ICRC) {
sense_key = KEY_ABORTED_COMMAND;
addl_sense_code =
SD_SCSI_ASC_INFO_UNIT_IUCRC_ERR;
addl_sense_qual = 0;
}
}
sata_fill_ata_return_desc(sata_pkt, sense_key, addl_sense_code,
addl_sense_qual);
}
sata_free_local_buffer(spx);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0)
scsi_hba_pkt_comp(scsipkt);
}
static void
sata_fill_ata_return_desc(sata_pkt_t *sata_pkt, uint8_t sense_key,
uint8_t addl_sense_code, uint8_t addl_sense_qual)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_cmd_t *scmd = &sata_pkt->satapkt_cmd;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct sata_apt_sense_data *apt_sd =
(struct sata_apt_sense_data *)scsipkt->pkt_scbp;
struct scsi_descr_sense_hdr *sds = &(apt_sd->apt_sd_hdr);
struct scsi_ata_status_ret_sense_descr *ata_ret_desc =
&(apt_sd->apt_sd_sense);
int extend = 0;
if ((scsipkt->pkt_cdbp[0] == SPC3_CMD_ATA_COMMAND_PASS_THROUGH16) &&
(scsipkt->pkt_cdbp[2] & SATL_APT_BM_EXTEND))
extend = 1;
scsipkt->pkt_state |= STATE_ARQ_DONE;
*(uchar_t *)&apt_sd->apt_status = STATUS_CHECK;
*(uchar_t *)&apt_sd->apt_rqpkt_status = STATUS_GOOD;
apt_sd->apt_rqpkt_reason = CMD_CMPLT;
apt_sd->apt_rqpkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_XFERRED_DATA | STATE_SENT_CMD | STATE_GOT_STATUS;
apt_sd->apt_rqpkt_resid = scsipkt->pkt_scblen -
sizeof (struct sata_apt_sense_data);
bzero(sds, sizeof (struct scsi_descr_sense_hdr));
sds->ds_code = CODE_FMT_DESCR_CURRENT;
sds->ds_class = CLASS_EXTENDED_SENSE;
sds->ds_key = sense_key & 0xf;
sds->ds_add_code = addl_sense_code;
sds->ds_qual_code = addl_sense_qual;
sds->ds_addl_sense_length =
sizeof (struct scsi_ata_status_ret_sense_descr);
bzero(ata_ret_desc, sizeof (struct scsi_ata_status_ret_sense_descr));
ata_ret_desc->ars_descr_type = DESCR_ATA_STATUS_RETURN;
ata_ret_desc->ars_addl_length = 0xc;
ata_ret_desc->ars_error = scmd->satacmd_error_reg;
ata_ret_desc->ars_sec_count_lsb = scmd->satacmd_sec_count_lsb;
ata_ret_desc->ars_lba_low_lsb = scmd->satacmd_lba_low_lsb;
ata_ret_desc->ars_lba_mid_lsb = scmd->satacmd_lba_mid_lsb;
ata_ret_desc->ars_lba_high_lsb = scmd->satacmd_lba_high_lsb;
ata_ret_desc->ars_device = scmd->satacmd_device_reg;
ata_ret_desc->ars_status = scmd->satacmd_status_reg;
if (extend == 1) {
ata_ret_desc->ars_extend = 1;
ata_ret_desc->ars_sec_count_msb = scmd->satacmd_sec_count_msb;
ata_ret_desc->ars_lba_low_msb = scmd->satacmd_lba_low_msb;
ata_ret_desc->ars_lba_mid_msb = scmd->satacmd_lba_mid_msb;
ata_ret_desc->ars_lba_high_msb = scmd->satacmd_lba_high_msb;
} else {
ata_ret_desc->ars_extend = 0;
ata_ret_desc->ars_sec_count_msb = 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
sata_set_arq_data(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
} else {
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
sense->es_key = KEY_HARDWARE_ERROR;
break;
case SATA_PKT_DEV_ERROR:
if (sata_pkt->satapkt_cmd.satacmd_status_reg &
SATA_STATUS_ERR) {
sata_decode_device_error(spx, sense);
break;
}
break;
case SATA_PKT_TIMEOUT:
scsipkt->pkt_reason = CMD_TIMEOUT;
scsipkt->pkt_statistics |=
STAT_TIMEOUT | STAT_DEV_RESET;
break;
case SATA_PKT_ABORTED:
scsipkt->pkt_reason = CMD_ABORTED;
scsipkt->pkt_statistics |= STAT_ABORTED;
break;
case SATA_PKT_RESET:
scsipkt->pkt_reason = CMD_RESET;
scsipkt->pkt_statistics |= STAT_DEV_RESET;
break;
default:
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_txlt_nodata_cmd_completion: "
"invalid packet completion reason %d",
sata_pkt->satapkt_reason));
scsipkt->pkt_reason = CMD_TRAN_ERR;
break;
}
}
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"Scsi_pkt completion reason %x\n", scsipkt->pkt_reason);
}
static int
sata_build_msense_page_1(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(sdinfo))
_NOTE(ARGUNUSED(pcntrl))
_NOTE(ARGUNUSED(buf))
#endif
return (0);
}
static int
sata_build_msense_page_8(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf)
{
struct mode_cache_scsi3 *page = (struct mode_cache_scsi3 *)buf;
sata_id_t *sata_id = &sdinfo->satadrv_id;
bzero(buf, PAGELENGTH_DAD_MODE_CACHE_SCSI3);
if (pcntrl == 3)
return (0);
if (pcntrl == 0 || pcntrl == 2) {
page->mode_page.code = MODEPAGE_CACHING;
page->mode_page.length = PAGELENGTH_DAD_MODE_CACHE_SCSI3;
if (SATA_READ_AHEAD_SUPPORTED(*sata_id) &&
!SATA_READ_AHEAD_ENABLED(*sata_id)) {
page->dra = 1;
page->rcd = 1;
}
if (SATA_WRITE_CACHE_SUPPORTED(*sata_id) &&
SATA_WRITE_CACHE_ENABLED(*sata_id))
page->wce = 1;
} else {
page->mode_page.code = MODEPAGE_CACHING;
page->mode_page.length = PAGELENGTH_DAD_MODE_CACHE_SCSI3;
if (SATA_READ_AHEAD_SUPPORTED(*sata_id)) {
page->dra = 1;
page->rcd = 1;
}
if (SATA_WRITE_CACHE_SUPPORTED(*sata_id))
page->wce = 1;
}
return (PAGELENGTH_DAD_MODE_CACHE_SCSI3 +
sizeof (struct mode_page));
}
static int
sata_build_msense_page_1c(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf)
{
struct mode_info_excpt_page *page = (struct mode_info_excpt_page *)buf;
sata_id_t *sata_id = &sdinfo->satadrv_id;
bzero(buf, PAGELENGTH_INFO_EXCPT);
page->mode_page.code = MODEPAGE_INFO_EXCPT;
page->mode_page.length = PAGELENGTH_INFO_EXCPT;
page->mode_page.ps = 1;
if (pcntrl != 1) {
page->dexcpt = !(sata_id->ai_features85 & SATA_SMART_SUPPORTED);
page->mrie = MRIE_ONLY_ON_REQUEST;
}
else
page->dexcpt = 1;
return (PAGELENGTH_INFO_EXCPT + sizeof (struct mode_page));
}
static int
sata_build_msense_page_30(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf)
{
struct mode_acoustic_management *page =
(struct mode_acoustic_management *)buf;
sata_id_t *sata_id = &sdinfo->satadrv_id;
bzero(buf, PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT);
switch (pcntrl) {
case P_CNTRL_DEFAULT:
return (0);
case P_CNTRL_CURRENT:
case P_CNTRL_SAVED:
page->mode_page.code = MODEPAGE_ACOUSTIC_MANAG;
page->mode_page.length =
PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT;
page->mode_page.ps = 1;
if (!(sata_id->ai_cmdset83 & SATA_ACOUSTIC_MGMT)) {
page->acoustic_manag_enable =
ACOUSTIC_DISABLED;
} else {
page->acoustic_manag_enable =
((sata_id->ai_features86 & SATA_ACOUSTIC_MGMT)
!= 0);
#ifdef _LITTLE_ENDIAN
page->acoustic_manag_level =
(uchar_t)sata_id->ai_acoustic;
page->vendor_recommended_value =
sata_id->ai_acoustic >> 8;
#else
page->acoustic_manag_level =
sata_id->ai_acoustic >> 8;
page->vendor_recommended_value =
(uchar_t)sata_id->ai_acoustic;
#endif
}
break;
case P_CNTRL_CHANGEABLE:
page->mode_page.code = MODEPAGE_ACOUSTIC_MANAG;
page->mode_page.length =
PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT;
page->mode_page.ps = 1;
if (sata_id->ai_cmdset83 & SATA_ACOUSTIC_MGMT) {
page->acoustic_manag_enable =
ACOUSTIC_ENABLED;
page->acoustic_manag_level = 0xff;
}
break;
}
return (PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT +
sizeof (struct mode_page));
}
static int
sata_build_msense_page_1a(sata_drive_info_t *sdinfo, int pcntrl, uint8_t *buf)
{
struct mode_info_power_cond *page = (struct mode_info_power_cond *)buf;
sata_id_t *sata_id = &sdinfo->satadrv_id;
bzero(buf, sizeof (struct mode_info_power_cond));
if (pcntrl == P_CNTRL_DEFAULT) {
return (0);
}
page->mode_page.code = MODEPAGE_POWER_COND;
page->mode_page.length = sizeof (struct mode_info_power_cond);
if (sata_id->ai_cap & SATA_STANDBYTIMER) {
page->standby = 1;
bcopy(sdinfo->satadrv_standby_timer, page->standby_cond_timer,
sizeof (uchar_t) * 4);
}
return (sizeof (struct mode_info_power_cond));
}
int
sata_mode_select_page_8(sata_pkt_txlate_t *spx, struct mode_cache_scsi3 *page,
int parmlen, int *pagelen, int *rval, int *dmod)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_id_t *sata_id;
struct scsi_extended_sense *sense;
int wce, dra;
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_id = &sdinfo->satadrv_id;
*dmod = 0;
if ((PAGELENGTH_DAD_MODE_CACHE_SCSI3 +
sizeof (struct mode_page)) > parmlen) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
*pagelen = PAGELENGTH_DAD_MODE_CACHE_SCSI3 + sizeof (struct mode_page);
if (SATA_READ_AHEAD_ENABLED(*sata_id))
dra = 0;
else
dra = 1;
if (SATA_WRITE_CACHE_ENABLED(*sata_id))
wce = 1;
else
wce = 0;
if (page->dra == dra && page->wce == wce && page->rcd == dra) {
*rval = TRAN_ACCEPT;
return (SATA_SUCCESS);
}
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SET_FEATURES;
if (page->dra != dra || page->rcd != dra) {
if (SATA_READ_AHEAD_SUPPORTED(*sata_id)) {
if (dra == 0)
scmd->satacmd_features_reg =
SATAC_SF_DISABLE_READ_AHEAD;
else
scmd->satacmd_features_reg =
SATAC_SF_ENABLE_READ_AHEAD;
if (sata_hba_start(spx, rval) != 0)
return (SATA_FAILURE);
*dmod = 1;
if (spx->txlt_sata_pkt->satapkt_reason !=
SATA_PKT_COMPLETED) {
goto failure;
}
} else {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
}
if (page->wce != wce) {
if (SATA_WRITE_CACHE_SUPPORTED(*sata_id)) {
if (page->wce == 1)
scmd->satacmd_features_reg =
SATAC_SF_ENABLE_WRITE_CACHE;
else
scmd->satacmd_features_reg =
SATAC_SF_DISABLE_WRITE_CACHE;
if (sata_hba_start(spx, rval) != 0)
return (SATA_FAILURE);
*dmod = 1;
if (spx->txlt_sata_pkt->satapkt_reason !=
SATA_PKT_COMPLETED) {
goto failure;
}
} else {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code =
SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
}
return (SATA_SUCCESS);
failure:
sata_xlate_errors(spx);
return (SATA_FAILURE);
}
static int
sata_mode_select_page_1c(
sata_pkt_txlate_t *spx,
struct mode_info_excpt_page *page,
int parmlen,
int *pagelen,
int *rval,
int *dmod)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_drive_info_t *sdinfo;
sata_id_t *sata_id;
struct scsi_extended_sense *sense;
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_id = &sdinfo->satadrv_id;
*dmod = 0;
if (((PAGELENGTH_INFO_EXCPT + sizeof (struct mode_page)) > parmlen) ||
page->perf || page->test || (page->mrie != MRIE_ONLY_ON_REQUEST)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
*pagelen = PAGELENGTH_INFO_EXCPT + sizeof (struct mode_page);
if (! (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_CDB;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
if (page->dexcpt == ! (sata_id->ai_features85 & SATA_SMART_ENABLED)) {
*rval = TRAN_ACCEPT;
return (SATA_SUCCESS);
}
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_addr_type = 0;
scmd->satacmd_lba_mid_lsb = SMART_MAGIC_VAL_1;
scmd->satacmd_lba_high_lsb = SMART_MAGIC_VAL_2;
scmd->satacmd_features_reg = page->dexcpt ?
SATA_SMART_DISABLE_OPS : SATA_SMART_ENABLE_OPS;
scmd->satacmd_device_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SMART;
if (sata_hba_start(spx, rval) != 0)
return (SATA_FAILURE);
*dmod = 1;
if (spx->txlt_sata_pkt->satapkt_reason == SATA_PKT_COMPLETED)
return (SATA_SUCCESS);
sata_xlate_errors(spx);
return (SATA_FAILURE);
}
int
sata_mode_select_page_30(sata_pkt_txlate_t *spx, struct
mode_acoustic_management *page, int parmlen, int *pagelen,
int *rval, int *dmod)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_id_t *sata_id;
struct scsi_extended_sense *sense;
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_id = &sdinfo->satadrv_id;
*dmod = 0;
if (((PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT +
sizeof (struct mode_page)) > parmlen) ||
(! (sata_id->ai_cmdset83 & SATA_ACOUSTIC_MGMT))) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
*pagelen = PAGELENGTH_DAD_MODE_ACOUSTIC_MANAGEMENT +
sizeof (struct mode_page);
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SET_FEATURES;
if (page->acoustic_manag_enable) {
scmd->satacmd_features_reg = SATAC_SF_ENABLE_ACOUSTIC;
scmd->satacmd_sec_count_lsb = page->acoustic_manag_level;
} else {
scmd->satacmd_features_reg = SATAC_SF_DISABLE_ACOUSTIC;
}
if (sata_hba_start(spx, rval) != 0)
return (SATA_FAILURE);
if (spx->txlt_sata_pkt->satapkt_reason != SATA_PKT_COMPLETED) {
sata_xlate_errors(spx);
return (SATA_FAILURE);
}
*dmod = 1;
return (SATA_SUCCESS);
}
int
sata_mode_select_page_1a(sata_pkt_txlate_t *spx, struct
mode_info_power_cond *page, int parmlen, int *pagelen,
int *rval, int *dmod)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
sata_id_t *sata_id;
struct scsi_extended_sense *sense;
uint8_t ata_count;
int i, len;
sdinfo = sata_get_device_info(spx->txlt_sata_hba_inst,
&spx->txlt_sata_pkt->satapkt_device);
sata_id = &sdinfo->satadrv_id;
*dmod = 0;
len = sizeof (struct mode_info_power_cond);
len += sizeof (struct mode_page);
if ((len < parmlen) || (page->idle == 1) ||
(!(sata_id->ai_cap & SATA_STANDBYTIMER) && page->standby == 1)) {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
sense->es_key = KEY_ILLEGAL_REQUEST;
sense->es_add_code = SD_SCSI_ASC_INVALID_FIELD_IN_PARAMS_LIST;
*pagelen = parmlen;
*rval = TRAN_ACCEPT;
return (SATA_FAILURE);
}
*pagelen = len;
if (page->standby == 0)
goto out;
ata_count = sata_get_standby_timer(page->standby_cond_timer);
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = ata_count;
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;
scmd->satacmd_cmd_reg = SATAC_STANDBY;
scmd->satacmd_flags.sata_special_regs = B_TRUE;
scmd->satacmd_flags.sata_copy_out_error_reg = B_TRUE;
if (sata_hba_start(spx, rval) != 0) {
return (SATA_FAILURE);
} else {
if ((scmd->satacmd_error_reg != 0) ||
(spx->txlt_sata_pkt->satapkt_reason !=
SATA_PKT_COMPLETED)) {
sata_xlate_errors(spx);
return (SATA_FAILURE);
}
}
for (i = 0; i < 4; i++) {
sdinfo->satadrv_standby_timer[i] = page->standby_cond_timer[i];
}
out:
*dmod = 1;
return (SATA_SUCCESS);
}
CTASSERT(sizeof (struct log_parameter) == 4);
static inline struct log_parameter *
log_param_next(struct log_parameter *lpp)
{
uint8_t *ptr = (uint8_t *)lpp;
ptr += sizeof (*lpp) + lpp->param_len;
return ((struct log_parameter *)ptr);
}
static inline int
log_param_size(const struct log_parameter *last, const void *startp)
{
uintptr_t b = (uintptr_t)last;
uintptr_t a = (uintptr_t)startp;
ASSERT3U(b, >=, a);
return ((int)(b - a));
}
static int
sata_build_lsense_page_0(sata_drive_info_t *sdinfo, uint8_t *buf)
{
uint8_t *ptr = buf;
sata_id_t *sata_id = &sdinfo->satadrv_id;
*ptr++ = PAGE_CODE_GET_SUPPORTED_LOG_PAGES;
if (sata_id->ai_cmdset84 & SATA_GPL_SUPPORTED) {
*ptr++ = PAGE_CODE_READ_ERRORS;
*ptr++ = PAGE_CODE_TEMPERATURE;
}
if (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED) {
*ptr++ = PAGE_CODE_START_STOP_CYCLE_COUNTER;
if (sata_id->ai_cmdset84 & SATA_SMART_SELF_TEST_SUPPORTED) {
*ptr++ = PAGE_CODE_SELF_TEST_RESULTS;
}
}
if (sata_id->ai_medrotrate == 0x01 &&
(sata_id->ai_cmdset84 & SATA_GPL_SUPPORTED))
*ptr++ = PAGE_CODE_SOLID_STATE_MEDIA;
if (sata_id->ai_cmdset84 & SATA_GPL_SUPPORTED) {
*ptr++ = PAGE_CODE_GENERAL_STATS;
}
if (sata_id->ai_cmdset82 & SATA_SMART_SUPPORTED) {
*ptr++ = PAGE_CODE_INFORMATION_EXCEPTIONS;
*ptr++ = PAGE_CODE_SMART_READ_DATA;
}
return ((int)((uintptr_t)ptr - (uintptr_t)buf));
}
static int
sata_build_lsense_page_03(sata_drive_info_t *sdinfo, uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct log_parameter *lpp = (struct log_parameter *)buf;
uint64_t *lbuf;
uint64_t param;
int rval;
if (!(sdinfo->satadrv_id.ai_cmdset84 & SATA_GPL_SUPPORTED))
return (-1);
lbuf = kmem_zalloc(512, KM_SLEEP);
rval = sata_read_log_ext(sata_hba_inst, sdinfo, DEVICE_STATS_LOG,
DEVSTAT_ROTATING_MEDIA_PAGE, lbuf, 1);
if (rval == 0) {
param = LE_64(lbuf[5]);
if (SATA_STAT_SUPPORTED(param) && SATA_STAT_VALID(param)) {
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x04;
lpp->param_ctrl_flags = LOG_CTRL_LBIN;
lpp->param_len = sizeof (uint32_t);
BE_OUT32(&lpp->param_values[0],
SATA_STAT_VALUE(param) & 0xffffffff);
lpp = log_param_next(lpp);
}
}
bzero(lbuf, 512);
rval = sata_read_log_ext(sata_hba_inst, sdinfo, DEVICE_STATS_LOG,
DEVSTAT_GENERAL_ERRORS_PAGE, lbuf, 1);
if (rval == 0) {
param = LE_64(lbuf[1]);
if (SATA_STAT_SUPPORTED(param) && SATA_STAT_VALID(param)) {
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x06;
lpp->param_ctrl_flags = LOG_CTRL_LBIN;
lpp->param_len = sizeof (uint32_t);
BE_OUT32(&lpp->param_values[0],
SATA_STAT_VALUE(param) & 0xffffffff);
lpp = log_param_next(lpp);
}
}
kmem_free(lbuf, 512);
return (log_param_size(lpp, buf) > 0 ? log_param_size(lpp, buf) : -1);
}
static int
sata_build_lsense_page_10(
sata_drive_info_t *sdinfo,
uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct log_parameter *lpp = (struct log_parameter *)buf;
int rval;
if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA48) {
struct smart_ext_selftest_log *ext_selftest_log;
ext_selftest_log = kmem_zalloc(
sizeof (struct smart_ext_selftest_log), KM_SLEEP);
rval = sata_ext_smart_selftest_read_log(sata_hba_inst, sdinfo,
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};
uint16_t block_num;
int count;
boolean_t only_one_block = B_FALSE;
index = ext_selftest_log->
smart_ext_selftest_log_index[0];
index |= ext_selftest_log->
smart_ext_selftest_log_index[1] << 8;
if (index == 0)
goto out;
--index;
start_index = index;
block_num = index / ENTRIES_PER_EXT_SELFTEST_LOG_BLK;
if (block_num != 0) {
rval = sata_ext_smart_selftest_read_log(
sata_hba_inst, sdinfo, ext_selftest_log,
block_num);
if (rval != 0)
goto out;
}
index %= ENTRIES_PER_EXT_SELFTEST_LOG_BLK;
entry =
&ext_selftest_log->
smart_ext_selftest_log_entries[index];
for (count = 1;
count <= SCSI_ENTRIES_IN_LOG_SENSE_SELFTEST_RESULTS;
++count) {
uint8_t status;
uint8_t code;
uint8_t sense_key;
uint8_t add_sense_code;
uint8_t add_sense_code_qual;
if (bcmp(entry, &empty, sizeof (empty)) == 0) {
if (index + 1 ==
ENTRIES_PER_EXT_SELFTEST_LOG_BLK) {
--entry;
--index;
if (bcmp(entry, &empty,
sizeof (empty)) == 0)
goto out;
} else
goto out;
}
if (only_one_block &&
start_index == index)
goto out;
lpp->param_code[0] = 0;
lpp->param_code[1] = count;
lpp->param_ctrl_flags =
LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len =
SCSI_LOG_SENSE_SELFTEST_PARAM_LEN;
status = entry->smart_ext_selftest_log_status;
status >>= 4;
switch (status) {
case 0:
default:
sense_key = KEY_NO_SENSE;
add_sense_code =
SD_SCSI_ASC_NO_ADD_SENSE;
add_sense_code_qual = 0;
break;
case 1:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_81;
break;
case 2:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_82;
break;
case 3:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_83;
break;
case 4:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_84;
break;
case 5:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_85;
break;
case 6:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_86;
break;
case 7:
sense_key = KEY_MEDIUM_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_87;
break;
case 8:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_88;
break;
}
code = 0;
status |= (code << 4);
lpp->param_values[0] = status;
lpp->param_values[1] = 0;
lpp->param_values[2] = entry->
smart_ext_selftest_log_timestamp[1];
lpp->param_values[3] = entry->
smart_ext_selftest_log_timestamp[0];
if (status != 0) {
lpp->param_values[4] = 0;
lpp->param_values[5] = 0;
lpp->param_values[6] = entry->
smart_ext_selftest_log_failing_lba
[5];
lpp->param_values[7] = entry->
smart_ext_selftest_log_failing_lba
[4];
lpp->param_values[8] = entry->
smart_ext_selftest_log_failing_lba
[3];
lpp->param_values[9] = entry->
smart_ext_selftest_log_failing_lba
[2];
lpp->param_values[10] = entry->
smart_ext_selftest_log_failing_lba
[1];
lpp->param_values[11] = entry->
smart_ext_selftest_log_failing_lba
[0];
} else {
lpp->param_values[4] = 0xff;
lpp->param_values[5] = 0xff;
lpp->param_values[6] = 0xff;
lpp->param_values[7] = 0xff;
lpp->param_values[8] = 0xff;
lpp->param_values[9] = 0xff;
lpp->param_values[10] = 0xff;
lpp->param_values[11] = 0xff;
}
lpp->param_values[12] = sense_key;
lpp->param_values[13] = add_sense_code;
lpp->param_values[14] = add_sense_code_qual;
lpp->param_values[15] = 0;
lpp = (struct log_parameter *)
(((uint8_t *)lpp) +
SCSI_LOG_PARAM_HDR_LEN +
SCSI_LOG_SENSE_SELFTEST_PARAM_LEN);
--index;
if (index < 0) {
if (block_num > 0) {
--block_num;
} else {
struct read_log_ext_directory
logdir;
rval =
sata_read_log_ext_directory(
sata_hba_inst, sdinfo,
&logdir);
if (rval == -1)
goto out;
if ((logdir.read_log_ext_vers
[0] == 0) &&
(logdir.read_log_ext_vers
[1] == 0))
goto out;
block_num =
logdir.read_log_ext_nblks
[EXT_SMART_SELFTEST_LOG_PAGE
- 1][0];
block_num |= logdir.
read_log_ext_nblks
[EXT_SMART_SELFTEST_LOG_PAGE
- 1][1] << 8;
--block_num;
only_one_block =
(block_num == 0);
}
rval = sata_ext_smart_selftest_read_log(
sata_hba_inst, sdinfo,
ext_selftest_log, block_num);
if (rval != 0)
goto out;
index =
ENTRIES_PER_EXT_SELFTEST_LOG_BLK -
1;
}
index %= ENTRIES_PER_EXT_SELFTEST_LOG_BLK;
entry = &ext_selftest_log->
smart_ext_selftest_log_entries[index];
}
}
out:
kmem_free(ext_selftest_log,
sizeof (struct smart_ext_selftest_log));
} else {
struct smart_selftest_log *selftest_log;
selftest_log = kmem_zalloc(sizeof (struct smart_selftest_log),
KM_SLEEP);
rval = sata_smart_selftest_log(sata_hba_inst, sdinfo,
selftest_log);
if (rval == 0) {
int index;
int count;
struct smart_selftest_log_entry *entry;
static const struct smart_selftest_log_entry empty =
{ 0 };
index = selftest_log->smart_selftest_log_index;
if (index == 0)
goto done;
--index;
entry = &selftest_log->
smart_selftest_log_entries[index];
for (count = 1;
count <= SCSI_ENTRIES_IN_LOG_SENSE_SELFTEST_RESULTS;
++count) {
uint8_t status;
uint8_t code;
uint8_t sense_key;
uint8_t add_sense_code;
uint8_t add_sense_code_qual = 0;
if (bcmp(entry, &empty, sizeof (empty)) == 0)
goto done;
lpp->param_code[0] = 0;
lpp->param_code[1] = count;
lpp->param_ctrl_flags =
LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len =
SCSI_LOG_SENSE_SELFTEST_PARAM_LEN;
status = entry->smart_selftest_log_status;
status >>= 4;
switch (status) {
case 0:
default:
sense_key = KEY_NO_SENSE;
add_sense_code =
SD_SCSI_ASC_NO_ADD_SENSE;
break;
case 1:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_81;
break;
case 2:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_82;
break;
case 3:
sense_key = KEY_ABORTED_COMMAND;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_83;
break;
case 4:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_84;
break;
case 5:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_85;
break;
case 6:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_86;
break;
case 7:
sense_key = KEY_MEDIUM_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_87;
break;
case 8:
sense_key = KEY_HARDWARE_ERROR;
add_sense_code =
DIAGNOSTIC_FAILURE_ON_COMPONENT;
add_sense_code_qual = SCSI_COMPONENT_88;
break;
}
code = 0;
status |= (code << 4);
lpp->param_values[0] = status;
lpp->param_values[1] = 0;
lpp->param_values[2] = entry->
smart_selftest_log_timestamp[1];
lpp->param_values[3] = entry->
smart_selftest_log_timestamp[0];
if (status != 0) {
lpp->param_values[4] = 0;
lpp->param_values[5] = 0;
lpp->param_values[6] = 0;
lpp->param_values[7] = 0;
lpp->param_values[8] = entry->
smart_selftest_log_failing_lba[3];
lpp->param_values[9] = entry->
smart_selftest_log_failing_lba[2];
lpp->param_values[10] = entry->
smart_selftest_log_failing_lba[1];
lpp->param_values[11] = entry->
smart_selftest_log_failing_lba[0];
} else {
lpp->param_values[4] = 0xff;
lpp->param_values[5] = 0xff;
lpp->param_values[6] = 0xff;
lpp->param_values[7] = 0xff;
lpp->param_values[8] = 0xff;
lpp->param_values[9] = 0xff;
lpp->param_values[10] = 0xff;
lpp->param_values[11] = 0xff;
}
lpp->param_values[12] = sense_key;
lpp->param_values[13] = add_sense_code;
lpp->param_values[14] = add_sense_code_qual;
lpp->param_values[15] = 0;
lpp = (struct log_parameter *)
(((uint8_t *)lpp) +
SCSI_LOG_PARAM_HDR_LEN +
SCSI_LOG_SENSE_SELFTEST_PARAM_LEN);
--index;
if (index < 0) {
index =
NUM_SMART_SELFTEST_LOG_ENTRIES - 1;
}
entry = &selftest_log->
smart_selftest_log_entries[index];
}
}
done:
kmem_free(selftest_log, sizeof (struct smart_selftest_log));
}
return ((SCSI_LOG_PARAM_HDR_LEN + SCSI_LOG_SENSE_SELFTEST_PARAM_LEN) *
SCSI_ENTRIES_IN_LOG_SENSE_SELFTEST_RESULTS);
}
static uint8_t
sata_sct_temp(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo,
void *p, size_t lbufsz)
{
sata_id_t *sata_id = &sdinfo->satadrv_id;
uint8_t *lbuf = p;
int rval;
uint8_t temp;
ASSERT3U(lbufsz, >=, 512);
if ((sata_id->ai_sctsupport & SATA_SCT_CMD_TRANS_SUP) == 0)
return (SCSI_NO_TEMP);
bzero(lbuf, lbufsz);
rval = sata_smart_read_log(sata_hba_inst, sdinfo, lbuf,
SCT_STATUS_LOG_PAGE, 1);
if (rval == -1)
return (SCSI_NO_TEMP);
temp = lbuf[200];
if (temp == 0x80)
return (SCSI_NO_TEMP);
if ((temp & 0x80) != 0)
return (0);
return (temp);
}
static int
sata_build_lsense_page_2f(
sata_drive_info_t *sdinfo,
uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct log_parameter *lpp = (struct log_parameter *)buf;
int rval;
uint8_t *smart_data;
uint8_t temp;
sata_id_t *sata_id;
lpp->param_code[0] = 0;
lpp->param_code[1] = 0;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
rval = sata_fetch_smart_return_status(sata_hba_inst, sdinfo);
switch (rval) {
case 1:
lpp->param_values[0] = SCSI_PREDICTED_FAILURE;
lpp->param_values[1] = SCSI_GENERAL_HD_FAILURE;
break;
case 0:
case -1:
lpp->param_values[0] = 0;
lpp->param_values[1] = 0;
break;
#if defined(SATA_DEBUG)
default:
cmn_err(CE_PANIC, "sata_build_lsense_page_2f bad return value");
#endif
}
sata_id = &sdinfo->satadrv_id;
if (! (sata_id->ai_sctsupport & SATA_SCT_CMD_TRANS_SUP)) {
temp = SCSI_NO_TEMP;
} else {
smart_data = kmem_zalloc(512, KM_SLEEP);
temp = sata_sct_temp(sata_hba_inst, sdinfo, smart_data, 512);
kmem_free(smart_data, 512);
}
lpp->param_values[2] = temp;
lpp->param_values[3] = 0;
lpp->param_len = SCSI_INFO_EXCEPTIONS_PARAM_LEN;
return (SCSI_INFO_EXCEPTIONS_PARAM_LEN + SCSI_LOG_PARAM_HDR_LEN);
}
static int
sata_build_lsense_page_0d(sata_drive_info_t *sdinfo, uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct log_parameter *lpp = (struct log_parameter *)buf;
uint64_t *lbuf;
uint64_t param;
int rval;
uint8_t temp, ref_temp, sct_temp;
if (!(sdinfo->satadrv_id.ai_sctsupport & SATA_SCT_CMD_TRANS_SUP) &&
!(sdinfo->satadrv_id.ai_cmdset84 & SATA_GPL_SUPPORTED))
return (-1);
temp = ref_temp = sct_temp = SCSI_NO_TEMP;
lbuf = kmem_zalloc(512, KM_SLEEP);
sct_temp = sata_sct_temp(sata_hba_inst, sdinfo, lbuf, 512);
bzero(lbuf, 512);
rval = sata_read_log_ext(sata_hba_inst, sdinfo, DEVICE_STATS_LOG,
DEVSTAT_TEMP_PAGE, lbuf, 1);
if (rval == -1)
goto done;
param = LE_64(lbuf[1]);
if (SATA_STAT_SUPPORTED(param) && SATA_STAT_VALID(param)) {
temp = SATA_STAT_VALUE(param) & 0xff;
if ((temp & 0x80) != 0)
temp = SCSI_NO_TEMP;
}
param = LE_64(lbuf[11]);
if (SATA_STAT_SUPPORTED(param) && SATA_STAT_VALID(param)) {
int8_t val = (int8_t)(SATA_STAT_VALUE(param) & 0xff);
ref_temp = (val < 0) ? 0 : val;
}
rval = 0;
done:
kmem_free(lbuf, 512);
lpp->param_code[0] = 0;
lpp->param_code[1] = 0;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len = 2;
lpp->param_values[0] = 0;
lpp->param_values[1] = (sct_temp != SCSI_NO_TEMP) ? sct_temp : temp;
lpp = log_param_next(lpp);
if (ref_temp != SCSI_NO_TEMP) {
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x01;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len = 2;
lpp->param_values[0] = 0;
lpp->param_values[1] = ref_temp;
lpp = log_param_next(lpp);
}
return (log_param_size(lpp, buf));
}
static int
sata_build_lsense_page_30(
sata_drive_info_t *sdinfo,
uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct smart_data *smart_data = (struct smart_data *)buf;
int rval;
rval = sata_fetch_smart_data(sata_hba_inst, sdinfo, smart_data);
if (rval == -1)
return (0);
return (sizeof (struct smart_data));
}
static int
sata_build_lsense_page_0e(sata_drive_info_t *sdinfo, uint8_t *buf,
sata_pkt_txlate_t *spx)
{
struct start_stop_cycle_counter_log *log_page;
int i, rval, index;
uint8_t smart_data[512], id, value, worst, thresh;
uint32_t max_count, cycles;
rval = sata_fetch_smart_data(spx->txlt_sata_hba_inst, sdinfo,
(struct smart_data *)smart_data);
if (rval == -1)
return (0);
for (i = 0, id = 0; i < SMART_START_STOP_COUNT_ID * 2; i++) {
index = (i * 12) + 2;
id = smart_data[index];
if (id != SMART_START_STOP_COUNT_ID)
continue;
else {
thresh = smart_data[index + 2];
value = smart_data[index + 3];
worst = smart_data[index + 4];
break;
}
}
if (id != SMART_START_STOP_COUNT_ID)
return (0);
max_count = value - thresh;
cycles = value - worst;
log_page = (struct start_stop_cycle_counter_log *)buf;
bzero(log_page, sizeof (struct start_stop_cycle_counter_log));
log_page->code = 0x0e;
log_page->page_len_low = 0x24;
log_page->manufactor_date_low = 0x1;
log_page->param_1.fmt_link = 0x1;
log_page->param_len_1 = 0x06;
for (i = 0; i < 4; i++) {
log_page->year_manu[i] = 0x30;
if (i < 2)
log_page->week_manu[i] = 0x30;
}
log_page->account_date_low = 0x02;
log_page->param_2.fmt_link = 0x01;
log_page->param_len_2 = 0x06;
for (i = 0; i < 4; i++) {
log_page->year_account[i] = 0x20;
if (i < 2)
log_page->week_account[i] = 0x20;
}
log_page->lifetime_code_low = 0x03;
log_page->param_3.fmt_link = 0x03;
log_page->param_len_3 = 0x04;
log_page->cycle_code_low = 0x04;
log_page->param_4.fmt_link = 0x03;
log_page->param_len_4 = 0x04;
for (i = 0; i < 4; i++) {
log_page->cycle_lifetime[i] =
(max_count >> (8 * (3 - i))) & 0xff;
log_page->cycle_accumulated[i] =
(cycles >> (8 * (3 - i))) & 0xff;
}
return (sizeof (struct start_stop_cycle_counter_log));
}
static int
sata_build_lsense_page_11(sata_drive_info_t *sdinfo, uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
struct log_parameter *lpp = (struct log_parameter *)buf;
uint64_t *lbuf;
uint64_t param;
int rval = 0;
if (sdinfo->satadrv_id.ai_medrotrate != 0x01 ||
!(sdinfo->satadrv_id.ai_cmdset84 & SATA_GPL_SUPPORTED)) {
return (-1);
}
lbuf = kmem_zalloc(512, KM_SLEEP);
rval = sata_read_log_ext(sata_hba_inst, sdinfo, DEVICE_STATS_LOG,
DEVSTAT_SSD_PAGE, lbuf, 1);
if (rval == -1)
goto done;
param = LE_64(lbuf[1]);
if (!SATA_STAT_SUPPORTED(param) || !SATA_STAT_VALID(param)) {
rval = -1;
goto done;
}
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x01;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len = 4;
BE_OUT32(&lpp->param_values[0], SATA_STAT_VALUE(param) & 0xffffffff);
lpp = log_param_next(lpp);
done:
kmem_free(lbuf, 512);
return ((rval < 0) ? -1 : log_param_size(lpp, buf));
}
static int
sata_build_lsense_page_19(sata_drive_info_t *sdinfo, uint8_t *buf,
sata_hba_inst_t *sata_hba_inst)
{
static const int stat_idx[] = {
6,
4,
3,
5,
-1, -1, -1, -1
};
struct log_parameter *lpp = (struct log_parameter *)buf;
uint64_t *lbuf;
uint64_t *paramp;
uint64_t param;
uint_t nvalid;
int rval;
if (!(sdinfo->satadrv_id.ai_cmdset84 & SATA_GPL_SUPPORTED))
return (-1);
nvalid = 0;
lbuf = kmem_zalloc(512, KM_SLEEP);
rval = sata_read_log_ext(sata_hba_inst, sdinfo, DEVICE_STATS_LOG,
DEVSTAT_GENERAL_STATS, lbuf, 1);
if (rval == -1) {
kmem_free(lbuf, 512);
return (-1);
}
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x01;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len = 0x40;
paramp = (uint64_t *)&lpp->param_values[0];
bzero(paramp, 0x40);
#define PARAM_VAL(x) ((x) & ((1ULL << 48) - 1))
for (uint_t i = 0; i < ARRAY_SIZE(stat_idx); i++, paramp++) {
if (stat_idx[i] == -1) {
continue;
}
param = LE_64(lbuf[stat_idx[i]]);
if (SATA_STAT_SUPPORTED(param) && SATA_STAT_VALID(param)) {
BE_OUT64(paramp, PARAM_VAL(param));
nvalid++;
}
}
#undef PARAM_VAL
kmem_free(lbuf, 512);
if (nvalid == 0)
return (-1);
lpp = log_param_next(lpp);
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x02;
lpp->param_ctrl_flags = LOG_CTRL_LP;
lpp->param_len = 0x08;
lpp->param_values[0] = lpp->param_values[1] = lpp->param_values[2] =
lpp->param_values[3] = lpp->param_values[4] =
lpp->param_values[5] = lpp->param_values[6] =
lpp->param_values[7] = 0;
lpp = log_param_next(lpp);
lpp->param_code[0] = 0x00;
lpp->param_code[1] = 0x03;
lpp->param_ctrl_flags = LOG_CTRL_LP | LOG_CTRL_LBIN;
lpp->param_len = 0x08;
uint32_t *vp = (uint32_t *)&lpp->param_values;
BE_OUT32(vp, 3);
vp++;
BE_OUT32(vp, 1);
lpp = log_param_next(lpp);
return (log_param_size(lpp, buf));
}
static void
sata_build_read_verify_cmd(sata_cmd_t *scmd, uint16_t sec, uint64_t lba)
{
scmd->satacmd_cmd_reg = SATAC_RDVER;
scmd->satacmd_addr_type = ATA_ADDR_LBA28;
scmd->satacmd_flags.sata_special_regs = B_TRUE;
scmd->satacmd_sec_count_lsb = sec & 0xff;
scmd->satacmd_lba_low_lsb = lba & 0xff;
scmd->satacmd_lba_mid_lsb = (lba >> 8) & 0xff;
scmd->satacmd_lba_high_lsb = (lba >> 16) & 0xff;
scmd->satacmd_device_reg = (SATA_ADH_LBA | ((lba >> 24) & 0xf));
scmd->satacmd_features_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
}
static void
sata_build_generic_cmd(sata_cmd_t *scmd, uint8_t cmd)
{
scmd->satacmd_addr_type = 0;
scmd->satacmd_cmd_reg = cmd;
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;
scmd->satacmd_flags.sata_special_regs = B_TRUE;
}
static uint8_t
sata_get_standby_timer(uint8_t *timer)
{
uint32_t i = 0, count = 0;
uint8_t ata_count;
for (i = 0; i < 4; i++) {
count = count << 8 | timer[i];
}
if (count == 0)
return (0);
if (count >= 1 && count <= 12000)
ata_count = (count -1) / 50 + 1;
else if (count > 12000 && count <= 12600)
ata_count = 0xfc;
else if (count > 12601 && count <= 12750)
ata_count = 0xff;
else if (count > 12750 && count <= 17999)
ata_count = 0xf1;
else if (count > 18000 && count <= 198000)
ata_count = count / 18000 + 240;
else
ata_count = 0xfd;
return (ata_count);
}
static int
sata_txlt_atapi(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
sata_cmd_t *scmd = &spx->txlt_sata_pkt->satapkt_cmd;
struct buf *bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
sata_hba_inst_t *sata_hba = SATA_TXLT_HBA_INST(spx);
sata_drive_info_t *sdinfo = sata_get_device_info(sata_hba,
&spx->txlt_sata_pkt->satapkt_device);
kmutex_t *cport_mutex = &(SATA_TXLT_CPORT_MUTEX(spx));
int cdblen;
int rval, reason;
int synch;
union scsi_cdb *cdbp = (union scsi_cdb *)scsipkt->pkt_cdbp;
mutex_enter(cport_mutex);
if (((rval = sata_txlt_generic_pkt_info(spx, &reason, 0)) !=
TRAN_ACCEPT) || (reason == CMD_DEV_GONE)) {
mutex_exit(cport_mutex);
return (rval);
}
switch (GETGROUP(cdbp)) {
case CDB_GROUPID_3:
mutex_exit(cport_mutex);
return (TRAN_BADPKT);
case CDB_GROUPID_6:
case CDB_GROUPID_7:
cdblen = scsipkt->pkt_cdblen;
break;
default:
cdblen = scsi_cdb_size[GETGROUP(cdbp)];
break;
}
if (cdblen <= 0 || cdblen > sdinfo->satadrv_atapi_cdb_len) {
sata_log(NULL, CE_WARN,
"sata: invalid ATAPI cdb length %d",
cdblen);
mutex_exit(cport_mutex);
return (TRAN_BADPKT);
}
SATAATAPITRACE(spx, cdblen);
switch ((uint_t)scsipkt->pkt_cdbp[0]) {
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:
if (bp != NULL) {
if (bp->b_flags & (B_PHYS | B_PAGEIO))
bp_mapin(bp);
}
break;
}
if (scmd->satacmd_bp) {
if (scmd->satacmd_bp->b_flags & B_READ) {
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
} else {
scmd->satacmd_flags.sata_data_direction =
SATA_DIR_WRITE;
}
}
sata_atapi_packet_cmd_setup(scmd, sdinfo);
scmd->satacmd_acdb_len = sdinfo->satadrv_atapi_cdb_len;
bzero(scmd->satacmd_acdb, SATA_ATAPI_MAX_CDB_LEN);
bcopy(cdbp, scmd->satacmd_acdb, cdblen);
if (scmd->satacmd_acdb[0] == SCMD_INQUIRY) {
if (scmd->satacmd_acdb[3] != 0)
scmd->satacmd_acdb[4] = 255;
}
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
uint8_t *p = scmd->satacmd_acdb;
char buf[3 * SATA_ATAPI_MAX_CDB_LEN];
(void) snprintf(buf, SATA_ATAPI_MAX_CDB_LEN,
"%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]);
buf[(3 * SATA_ATAPI_MAX_CDB_LEN) - 1] = '\0';
cmn_err(CE_NOTE, "ATAPI cdb: %s\n", buf);
}
#endif
bzero(scmd->satacmd_rqsense, SATA_ATAPI_RQSENSE_LEN);
sata_fixed_sense_data_preset(
(struct scsi_extended_sense *)scmd->satacmd_rqsense);
if (!(spx->txlt_sata_pkt->satapkt_op_mode & SATA_OPMODE_SYNCH)) {
spx->txlt_sata_pkt->satapkt_comp = sata_txlt_atapi_completion;
synch = FALSE;
} else
synch = TRUE;
if (sata_hba_start(spx, &rval) != 0) {
mutex_exit(cport_mutex);
return (rval);
}
mutex_exit(cport_mutex);
if (synch) {
SATADBG1(SATA_DBG_SCSI_IF, spx->txlt_sata_hba_inst,
"synchronous execution status %x\n",
spx->txlt_sata_pkt->satapkt_reason);
sata_txlt_atapi_completion(spx->txlt_sata_pkt);
}
return (TRAN_ACCEPT);
}
static void
sata_txlt_atapi_completion(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
struct buf *bp;
int rval;
#ifdef SATA_DEBUG
uint8_t *rqsp = sata_pkt->satapkt_cmd.satacmd_rqsense;
#endif
scsipkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
STATE_SENT_CMD | STATE_GOT_STATUS;
if (sata_pkt->satapkt_reason == SATA_PKT_COMPLETED) {
if (sata_pkt->satapkt_cmd.satacmd_bp != NULL)
scsipkt->pkt_state |= STATE_XFERRED_DATA;
scsipkt->pkt_reason = CMD_CMPLT;
*scsipkt->pkt_scbp = STATUS_GOOD;
if (spx->txlt_tmp_buf != NULL) {
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
if (bp->b_flags & B_READ) {
rval = ddi_dma_sync(
spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORCPU);
ASSERT(rval == DDI_SUCCESS);
bcopy(spx->txlt_tmp_buf, bp->b_un.b_addr,
bp->b_bcount);
}
}
} else {
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
if (sata_pkt->satapkt_reason == SATA_PKT_DEV_ERROR) {
scsipkt->pkt_reason = CMD_CMPLT;
bcopy(sata_pkt->satapkt_cmd.satacmd_rqsense, sense,
SATA_ATAPI_MIN_RQSENSE_LEN);
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_SCSI_IF) {
sata_log(spx->txlt_sata_hba_inst, CE_WARN,
"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",
scsipkt->pkt_reason,
rqsp[0], rqsp[1], rqsp[2], rqsp[3],
rqsp[4], rqsp[5], rqsp[6], rqsp[7],
rqsp[8], rqsp[9], rqsp[10], rqsp[11],
rqsp[12], rqsp[13], rqsp[14], rqsp[15],
rqsp[16], rqsp[17]);
}
#endif
} else {
switch (sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
scsipkt->pkt_reason = CMD_INCOMPLETE;
scsipkt->pkt_state &= ~(STATE_GOT_BUS |
STATE_GOT_TARGET | STATE_SENT_CMD |
STATE_GOT_STATUS);
sense->es_key = KEY_HARDWARE_ERROR;
break;
case SATA_PKT_TIMEOUT:
scsipkt->pkt_reason = CMD_TIMEOUT;
scsipkt->pkt_statistics |=
STAT_TIMEOUT | STAT_DEV_RESET;
break;
case SATA_PKT_ABORTED:
scsipkt->pkt_reason = CMD_ABORTED;
scsipkt->pkt_statistics |= STAT_ABORTED;
break;
case SATA_PKT_RESET:
scsipkt->pkt_reason = CMD_RESET;
scsipkt->pkt_statistics |= STAT_DEV_RESET;
sense->es_key = KEY_UNIT_ATTENTION;
sense->es_add_code = SD_SCSI_ASC_RESET;
break;
default:
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_txlt_atapi_completion: "
"invalid packet completion reason"));
scsipkt->pkt_reason = CMD_TRAN_ERR;
scsipkt->pkt_state &= ~(STATE_GOT_BUS |
STATE_GOT_TARGET | STATE_SENT_CMD |
STATE_GOT_STATUS);
break;
}
}
}
SATAATAPITRACE(spx, 0);
if ((scsipkt->pkt_flags & FLAG_NOINTR) == 0 &&
scsipkt->pkt_comp != NULL) {
(*scsipkt->pkt_comp)(scsipkt);
}
}
static int
sata_atapi_err_ret_cmd_setup(sata_pkt_txlate_t *spx, sata_drive_info_t *sdinfo)
{
sata_pkt_t *spkt = spx->txlt_sata_pkt;
sata_cmd_t *scmd;
struct buf *bp;
bp = sata_alloc_local_buffer(spx, SATA_ATAPI_MIN_RQSENSE_LEN);
if (bp == NULL) {
SATADBG1(SATA_DBG_ATAPI, spx->txlt_sata_hba_inst,
"sata_get_err_retrieval_pkt: "
"cannot allocate buffer for error data", NULL);
return (SATA_FAILURE);
}
bp_mapin(bp);
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
sata_atapi_packet_cmd_setup(scmd, sdinfo);
scmd->satacmd_acdb_len = sdinfo->satadrv_atapi_cdb_len;
bcopy(sata_rqsense_cdb, scmd->satacmd_acdb, SATA_ATAPI_RQSENSE_CDB_LEN);
bzero(&scmd->satacmd_acdb[SATA_ATAPI_RQSENSE_CDB_LEN],
sdinfo->satadrv_atapi_cdb_len - SATA_ATAPI_RQSENSE_CDB_LEN);
scmd->satacmd_err_ret_buf_handle = &spx->txlt_buf_dma_handle;
bzero(scmd->satacmd_rqsense, SATA_ATAPI_RQSENSE_LEN);
sata_fixed_sense_data_preset(
(struct scsi_extended_sense *)scmd->satacmd_rqsense);
ASSERT(scmd->satacmd_num_dma_cookies != 0);
ASSERT(scmd->satacmd_dma_cookie_list != NULL);
return (SATA_SUCCESS);
}
static void
sata_atapi_packet_cmd_setup(sata_cmd_t *scmd, sata_drive_info_t *sdinfo)
{
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = (uint8_t)SATA_ATAPI_MAX_BYTES_PER_DRQ;
scmd->satacmd_lba_high_lsb =
(uint8_t)(SATA_ATAPI_MAX_BYTES_PER_DRQ >> 8);
scmd->satacmd_cmd_reg = SATAC_PACKET;
if (sdinfo->satadrv_settings & SATA_DEV_DMA)
scmd->satacmd_features_reg = SATA_ATAPI_F_DMA;
if (sdinfo->satadrv_id.ai_majorversion != 0 &&
sdinfo->satadrv_id.ai_majorversion != 0xffff &&
(sdinfo->satadrv_id.ai_majorversion & SATA_MAJVER_7) != 0) {
if ((sdinfo->satadrv_id.ai_dirdma &
SATA_ATAPI_ID_DMADIR_REQ) != 0) {
if (scmd->satacmd_flags.sata_data_direction ==
SATA_DIR_READ) {
scmd->satacmd_features_reg |=
SATA_ATAPI_F_DATA_DIR_READ;
}
}
}
}
#ifdef SATA_DEBUG
static void
sata_show_inqry_data(uint8_t *buf)
{
struct scsi_inquiry *inq = (struct scsi_inquiry *)buf;
uint8_t *p;
cmn_err(CE_NOTE, "Inquiry data:");
cmn_err(CE_NOTE, "device type %x", inq->inq_dtype);
cmn_err(CE_NOTE, "removable media %x", inq->inq_rmb);
cmn_err(CE_NOTE, "version %x", inq->inq_ansi);
cmn_err(CE_NOTE, "ATAPI transport version %d",
SATA_ATAPI_TRANS_VERSION(inq));
cmn_err(CE_NOTE, "response data format %d, aenc %d",
inq->inq_rdf, inq->inq_aenc);
cmn_err(CE_NOTE, " additional length %d", inq->inq_len);
cmn_err(CE_NOTE, "tpgs %d", inq->inq_tpgs);
p = (uint8_t *)inq->inq_vid;
cmn_err(CE_NOTE, "vendor id (binary): %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 = (uint8_t *)inq->inq_vid;
cmn_err(CE_NOTE, "vendor id: %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 = (uint8_t *)inq->inq_pid;
cmn_err(CE_NOTE, "product id (binary): %02x %02x %02x %02x "
"%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]);
p = (uint8_t *)inq->inq_pid;
cmn_err(CE_NOTE, "product id: %c %c %c %c %c %c %c %c "
"%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 = (uint8_t *)inq->inq_revision;
cmn_err(CE_NOTE, "revision (binary): %02x %02x %02x %02x",
p[0], p[1], p[2], p[3]);
p = (uint8_t *)inq->inq_revision;
cmn_err(CE_NOTE, "revision: %c %c %c %c",
p[0], p[1], p[2], p[3]);
}
static void
sata_save_atapi_trace(sata_pkt_txlate_t *spx, int count)
{
struct scsi_pkt *scsi_pkt = spx->txlt_scsi_pkt;
if (scsi_pkt == NULL)
return;
if (count != 0) {
bzero(sata_atapi_trace[sata_atapi_trace_index].acdb,
SATA_ATAPI_MAX_CDB_LEN);
bcopy(scsi_pkt->pkt_cdbp,
sata_atapi_trace[sata_atapi_trace_index].acdb, count);
} else {
bcopy(&((struct scsi_arq_status *)scsi_pkt->pkt_scbp)->
sts_sensedata,
sata_atapi_trace[sata_atapi_trace_index].arqs,
SATA_ATAPI_MIN_RQSENSE_LEN);
sata_atapi_trace[sata_atapi_trace_index].scsi_pkt_reason =
scsi_pkt->pkt_reason;
sata_atapi_trace[sata_atapi_trace_index].sata_pkt_reason =
spx->txlt_sata_pkt->satapkt_reason;
if (++sata_atapi_trace_index >= 64)
sata_atapi_trace_index = 0;
}
}
#endif
static int
sata_get_atapi_inquiry_data(sata_hba_inst_t *sata_hba,
sata_address_t *saddr, struct scsi_inquiry *inq)
{
sata_pkt_txlate_t *spx;
sata_pkt_t *spkt;
struct buf *bp;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd;
int rval;
uint8_t *rqsp;
dev_info_t *dip = SATA_DIP(sata_hba);
#ifdef SATA_DEBUG
char msg_buf[MAXPATHLEN];
#endif
kmutex_t *cport_mutex;
ASSERT(sata_hba != NULL);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, NULL);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (SATA_FAILURE);
}
spkt->satapkt_device.satadev_addr = *saddr;
bp = sata_alloc_local_buffer(spx, sizeof (struct scsi_inquiry));
if (bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
SATA_LOG_D((sata_hba, CE_WARN,
"sata_get_atapi_inquiry_data: "
"cannot allocate data buffer"));
return (SATA_FAILURE);
}
bp_mapin(bp);
scmd = &spkt->satapkt_cmd;
ASSERT(scmd->satacmd_num_dma_cookies != 0);
ASSERT(scmd->satacmd_dma_cookie_list != NULL);
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
cport_mutex = &(SATA_CPORT_MUTEX(sata_hba, saddr->cport));
mutex_enter(cport_mutex);
sdinfo = sata_get_device_info(sata_hba,
&spx->txlt_sata_pkt->satapkt_device);
if (sdinfo == NULL) {
mutex_exit(cport_mutex);
rval = SATA_FAILURE;
goto cleanup;
}
sata_atapi_packet_cmd_setup(scmd, sdinfo);
scmd->satacmd_acdb_len = sdinfo->satadrv_atapi_cdb_len;
bzero(scmd->satacmd_acdb, SATA_ATAPI_MAX_CDB_LEN);
scmd->satacmd_acdb[0] = 0x12;
scmd->satacmd_acdb[1] = 0x00;
scmd->satacmd_acdb[2] = 0x00;
scmd->satacmd_acdb[3] = 0x00;
scmd->satacmd_acdb[4] = sizeof (struct scsi_inquiry);
scmd->satacmd_acdb[5] = 0x00;
sata_fixed_sense_data_preset(
(struct scsi_extended_sense *)scmd->satacmd_rqsense);
if (sata_hba_start(spx, &rval) != 0) {
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"Packet not accepted for execution - ret: %02x", rval);
mutex_exit(cport_mutex);
rval = SATA_FAILURE;
goto cleanup;
}
mutex_exit(cport_mutex);
if (spkt->satapkt_reason == SATA_PKT_COMPLETED) {
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"Packet completed successfully - ret: %02x", rval);
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORCPU);
ASSERT(rval == DDI_SUCCESS);
}
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip, DDI_SERVICE_UNAFFECTED);
rval = SATA_FAILURE;
} else {
bcopy(bp->b_un.b_addr, (uint8_t *)inq,
sizeof (struct scsi_inquiry));
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
sata_show_inqry_data((uint8_t *)inq);
}
#endif
rval = SATA_SUCCESS;
}
} else {
rval = SATA_FAILURE;
if (spkt->satapkt_reason == SATA_PKT_DEV_ERROR) {
rqsp = scmd->satacmd_rqsense;
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_ATAPI) {
msg_buf[0] = '\0';
(void) snprintf(msg_buf, MAXPATHLEN,
"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",
spkt->satapkt_reason,
rqsp[0], rqsp[1], rqsp[2], rqsp[3],
rqsp[4], rqsp[5], rqsp[6], rqsp[7],
rqsp[8], rqsp[9], rqsp[10], rqsp[11],
rqsp[12], rqsp[13], rqsp[14], rqsp[15],
rqsp[16], rqsp[17]);
sata_log(spx->txlt_sata_hba_inst, CE_WARN,
"%s", msg_buf);
}
#endif
} else {
switch (spkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"packet reason: port error", NULL);
break;
case SATA_PKT_TIMEOUT:
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"packet reason: timeout", NULL);
break;
case SATA_PKT_ABORTED:
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"packet reason: aborted", NULL);
break;
case SATA_PKT_RESET:
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"packet reason: reset\n", NULL);
break;
default:
SATADBG1(SATA_DBG_ATAPI, sata_hba,
"sata_get_atapi_inquiry_data: "
"invalid packet reason: %02x\n",
spkt->satapkt_reason);
break;
}
}
}
cleanup:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
#if 0
#ifdef SATA_DEBUG
static void
sata_test_atapi_packet_command(sata_hba_inst_t *sata_hba_inst, int cport)
{
sata_pkt_txlate_t *spx;
sata_pkt_t *spkt;
struct buf *bp;
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
sata_cmd_t *scmd;
int rval;
uint8_t *rqsp;
ASSERT(sata_hba_inst != NULL);
sata_device.satadev_addr.cport = cport;
sata_device.satadev_addr.pmport = 0;
sata_device.satadev_addr.qual = SATA_ADDR_DCPORT;
sata_device.satadev_rev = SATA_DEVICE_REV;
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
sdinfo = sata_get_device_info(sata_hba_inst, &sata_device);
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (sdinfo == NULL) {
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"no device info for cport %d",
sata_device.satadev_addr.cport);
return;
}
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, NULL);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return;
}
spkt->satapkt_device.satadev_addr = sata_device.satadev_addr;
bp = sata_alloc_local_buffer(spx, 1024);
if (bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"cannot allocate data buffer");
return;
}
bp_mapin(bp);
scmd = &spkt->satapkt_cmd;
ASSERT(scmd->satacmd_num_dma_cookies != 0);
ASSERT(scmd->satacmd_dma_cookie_list != NULL);
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
sata_atapi_packet_cmd_setup(scmd, sdinfo);
scmd->satacmd_acdb_len = sdinfo->satadrv_atapi_cdb_len;
bzero(scmd->satacmd_acdb, SATA_ATAPI_MAX_CDB_LEN);
scmd->satacmd_acdb[0] = 0x12;
scmd->satacmd_acdb[1] = 0x00;
scmd->satacmd_acdb[2] = 0x00;
scmd->satacmd_acdb[3] = 0x00;
scmd->satacmd_acdb[4] = sizeof (struct scsi_inquiry);
scmd->satacmd_acdb[5] = 0x00;
sata_fixed_sense_data_preset(
(struct scsi_extended_sense *)scmd->satacmd_rqsense);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (sata_hba_start(spx, &rval) != 0) {
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"Packet not accepted for execution - ret: %02x", rval);
mutex_exit(
&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
goto cleanup;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORCPU);
ASSERT(rval == DDI_SUCCESS);
}
if (spkt->satapkt_reason == SATA_PKT_COMPLETED) {
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"Packet completed successfully");
sata_show_inqry_data((uint8_t *)bp->b_un.b_addr);
} else {
if (spkt->satapkt_reason == SATA_PKT_DEV_ERROR) {
rqsp = scmd->satacmd_rqsense;
sata_log(spx->txlt_sata_hba_inst, CE_WARN,
"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",
spkt->satapkt_reason,
rqsp[0], rqsp[1], rqsp[2], rqsp[3],
rqsp[4], rqsp[5], rqsp[6], rqsp[7],
rqsp[8], rqsp[9], rqsp[10], rqsp[11],
rqsp[12], rqsp[13], rqsp[14], rqsp[15],
rqsp[16], rqsp[17]);
} else {
switch (spkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"packet reason: port error\n");
break;
case SATA_PKT_TIMEOUT:
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"packet reason: timeout\n");
break;
case SATA_PKT_ABORTED:
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"packet reason: aborted\n");
break;
case SATA_PKT_RESET:
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"packet reason: reset\n");
break;
default:
sata_log(sata_hba_inst, CE_WARN,
"sata_test_atapi_packet_command: "
"invalid packet reason: %02x\n",
spkt->satapkt_reason);
break;
}
}
}
cleanup:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
}
#endif
#endif
static int
sata_validate_sata_hba_tran(dev_info_t *dip, sata_hba_tran_t *sata_tran)
{
if (sata_tran->sata_tran_hba_rev > SATA_TRAN_HBA_REV) {
sata_log(NULL, CE_WARN,
"sata: invalid sata_hba_tran version %d for driver %s",
sata_tran->sata_tran_hba_rev, ddi_driver_name(dip));
return (SATA_FAILURE);
}
if (dip != sata_tran->sata_tran_hba_dip) {
SATA_LOG_D((NULL, CE_WARN,
"sata: inconsistent sata_tran_hba_dip "
"%p / %p", sata_tran->sata_tran_hba_dip, dip));
return (SATA_FAILURE);
}
if (sata_tran->sata_tran_probe_port == NULL ||
sata_tran->sata_tran_start == NULL ||
sata_tran->sata_tran_abort == NULL ||
sata_tran->sata_tran_reset_dport == NULL ||
sata_tran->sata_tran_hotplug_ops == NULL ||
sata_tran->sata_tran_hotplug_ops->sata_tran_port_activate == NULL ||
sata_tran->sata_tran_hotplug_ops->sata_tran_port_deactivate ==
NULL) {
SATA_LOG_D((NULL, CE_WARN, "sata: sata_hba_tran missing "
"required functions"));
}
return (SATA_SUCCESS);
}
static void
sata_remove_hba_instance(dev_info_t *dip)
{
sata_hba_inst_t *sata_hba_inst;
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list;
sata_hba_inst != (struct sata_hba_inst *)NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
if (sata_hba_inst->satahba_dip == dip)
break;
}
if (sata_hba_inst == (struct sata_hba_inst *)NULL) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata_remove_hba_instance: "
"unknown HBA instance\n");
#endif
ASSERT(FALSE);
}
if (sata_hba_inst == sata_hba_list) {
sata_hba_list = sata_hba_inst->satahba_next;
if (sata_hba_list) {
sata_hba_list->satahba_prev =
(struct sata_hba_inst *)NULL;
}
if (sata_hba_inst == sata_hba_list_tail) {
sata_hba_list_tail = NULL;
}
} else if (sata_hba_inst == sata_hba_list_tail) {
sata_hba_list_tail = sata_hba_inst->satahba_prev;
if (sata_hba_list_tail) {
sata_hba_list_tail->satahba_next =
(struct sata_hba_inst *)NULL;
}
} else {
sata_hba_inst->satahba_prev->satahba_next =
sata_hba_inst->satahba_next;
sata_hba_inst->satahba_next->satahba_prev =
sata_hba_inst->satahba_prev;
}
mutex_exit(&sata_mutex);
}
static void
sata_probe_ports(sata_hba_inst_t *sata_hba_inst)
{
dev_info_t *dip = SATA_DIP(sata_hba_inst);
int ncport;
sata_cport_info_t *cportinfo;
sata_drive_info_t *drive;
sata_device_t sata_device;
int rval;
dev_t minor_number;
char name[16];
clock_t start_time, cur_time;
for (ncport = 0; ncport < SATA_NUM_CPORTS(sata_hba_inst); ncport++) {
cportinfo = kmem_zalloc(sizeof (sata_cport_info_t), KM_SLEEP);
ASSERT(cportinfo != NULL);
mutex_init(&cportinfo->cport_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_addr.cport = ncport;
cportinfo->cport_addr.pmport = 0;
cportinfo->cport_addr.qual = SATA_ADDR_CPORT;
cportinfo->cport_state &= ~SATA_PORT_STATE_CLEAR_MASK;
cportinfo->cport_state |= SATA_STATE_PROBING;
SATA_CPORT_INFO(sata_hba_inst, ncport) = cportinfo;
mutex_exit(&cportinfo->cport_mutex);
minor_number = SATA_MAKE_AP_MINOR(ddi_get_instance(dip),
ncport, 0, SATA_ADDR_CPORT);
(void) sprintf(name, "%d", ncport);
if (ddi_create_minor_node(dip, name, S_IFCHR,
minor_number, DDI_NT_SATA_ATTACHMENT_POINT, 0) !=
DDI_SUCCESS) {
sata_log(sata_hba_inst, CE_WARN, "sata_hba_attach: "
"cannot create SATA attachment point for port %d",
ncport);
}
start_time = ddi_get_lbolt();
reprobe_cport:
sata_device.satadev_addr.cport = ncport;
sata_device.satadev_addr.pmport = 0;
sata_device.satadev_addr.qual = SATA_ADDR_CPORT;
sata_device.satadev_rev = SATA_DEVICE_REV;
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(dip, &sata_device);
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_scr = sata_device.satadev_scr;
if (rval != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&cportinfo->cport_mutex);
continue;
}
cportinfo->cport_state &= ~SATA_STATE_PROBING;
cportinfo->cport_state |= SATA_STATE_PROBED;
cportinfo->cport_dev_type = sata_device.satadev_type;
cportinfo->cport_state |= SATA_STATE_READY;
if (cportinfo->cport_dev_type == SATA_DTYPE_NONE) {
mutex_exit(&cportinfo->cport_mutex);
continue;
}
if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) {
if (SATA_CPORTINFO_DRV_INFO(cportinfo) == NULL) {
mutex_exit(&cportinfo->cport_mutex);
SATA_CPORTINFO_DRV_INFO(cportinfo) =
kmem_zalloc(sizeof (sata_drive_info_t),
KM_SLEEP);
mutex_enter(&cportinfo->cport_mutex);
}
drive = SATA_CPORTINFO_DRV_INFO(cportinfo);
drive->satadrv_addr = cportinfo->cport_addr;
drive->satadrv_addr.qual = SATA_ADDR_DCPORT;
drive->satadrv_type = cportinfo->cport_dev_type;
drive->satadrv_state = SATA_STATE_UNKNOWN;
mutex_exit(&cportinfo->cport_mutex);
if (sata_add_device(dip, sata_hba_inst, &sata_device) !=
SATA_SUCCESS) {
cur_time = ddi_get_lbolt();
if ((cur_time - start_time) <
drv_usectohz(SATA_DEV_IDENTIFY_TIMEOUT)) {
delay(drv_usectohz(
SATA_DEV_RETRY_DLY));
goto reprobe_cport;
}
}
} else {
mutex_exit(&cportinfo->cport_mutex);
if (sata_alloc_pmult(sata_hba_inst, &sata_device) !=
SATA_SUCCESS)
continue;
sata_show_pmult_info(sata_hba_inst, &sata_device);
sata_probe_pmports(sata_hba_inst, ncport);
}
}
}
static void
sata_probe_pmports(sata_hba_inst_t *sata_hba_inst, uint8_t ncport)
{
dev_info_t *dip = SATA_DIP(sata_hba_inst);
sata_pmult_info_t *pmultinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_drive_info_t *drive = NULL;
sata_device_t sata_device;
clock_t start_time, cur_time;
int npmport;
int rval;
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, ncport);
for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports; npmport++) {
pmportinfo = pmultinfo->pmult_dev_port[npmport];
start_time = ddi_get_lbolt();
reprobe_pmport:
sata_device.satadev_addr.cport = ncport;
sata_device.satadev_addr.pmport = npmport;
sata_device.satadev_addr.qual = SATA_ADDR_PMPORT;
sata_device.satadev_rev = SATA_DEVICE_REV;
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(dip, &sata_device);
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_scr = sata_device.satadev_scr;
if (rval != SATA_SUCCESS) {
pmportinfo->pmport_state =
SATA_PSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
continue;
}
pmportinfo->pmport_state &= ~SATA_STATE_PROBING;
pmportinfo->pmport_state |= SATA_STATE_PROBED;
pmportinfo->pmport_dev_type = sata_device.satadev_type;
pmportinfo->pmport_state |= SATA_STATE_READY;
if (pmportinfo->pmport_dev_type ==
SATA_DTYPE_NONE) {
SATADBG2(SATA_DBG_PMULT, sata_hba_inst,
"no device found at port %d:%d", ncport, npmport);
mutex_exit(&pmportinfo->pmport_mutex);
continue;
}
ASSERT(pmportinfo->pmport_dev_type != SATA_DTYPE_PMULT);
if (pmportinfo->pmport_sata_drive == NULL) {
mutex_exit(&pmportinfo->pmport_mutex);
pmportinfo->pmport_sata_drive =
kmem_zalloc(sizeof (sata_drive_info_t), KM_SLEEP);
mutex_enter(&pmportinfo->pmport_mutex);
}
drive = pmportinfo->pmport_sata_drive;
drive->satadrv_addr.cport = pmportinfo->pmport_addr.cport;
drive->satadrv_addr.pmport = npmport;
drive->satadrv_addr.qual = SATA_ADDR_DPMPORT;
drive->satadrv_type = pmportinfo-> pmport_dev_type;
drive->satadrv_state = SATA_STATE_UNKNOWN;
mutex_exit(&pmportinfo->pmport_mutex);
rval = sata_add_device(dip, sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
cur_time = ddi_get_lbolt();
if ((cur_time - start_time) < drv_usectohz(
SATA_DEV_IDENTIFY_TIMEOUT)) {
delay(drv_usectohz(SATA_DEV_RETRY_DLY));
goto reprobe_pmport;
}
}
}
}
static int
sata_add_device(dev_info_t *pdip, sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pminfo;
sata_pmport_info_t *pmportinfo;
dev_info_t *cdip;
sata_address_t *saddr = &sata_device->satadev_addr;
uint8_t cport, pmport;
int rval;
cport = saddr->cport;
pmport = saddr->pmport;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
ASSERT(cportinfo->cport_dev_type != SATA_DTYPE_NONE);
sata_device->satadev_rev = SATA_DEVICE_REV;
switch (saddr->qual) {
case SATA_ADDR_CPORT:
saddr->qual = SATA_ADDR_DCPORT;
rval = sata_probe_device(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
sata_device->satadev_type == SATA_DTYPE_UNKNOWN)
return (SATA_FAILURE);
mutex_enter(&cportinfo->cport_mutex);
sata_show_drive_info(sata_hba_inst,
SATA_CPORTINFO_DRV_INFO(cportinfo));
if ((sata_device->satadev_type & SATA_VALID_DEV_TYPE) == 0) {
cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN;
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
cportinfo->cport_dev_type = sata_device->satadev_type;
cportinfo->cport_tgtnode_clean = B_TRUE;
mutex_exit(&cportinfo->cport_mutex);
if (sata_initialize_device(sata_hba_inst,
SATA_CPORTINFO_DRV_INFO(cportinfo)) != SATA_SUCCESS) {
rval = sata_initialize_device(sata_hba_inst,
SATA_CPORTINFO_DRV_INFO(cportinfo));
if (rval == SATA_RETRY)
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d - "
"default device features could not be set."
" Device may not operate as expected.",
cport);
}
cdip = sata_create_target_node(pdip, sata_hba_inst, saddr);
if (cdip == NULL) {
return (SATA_SUCCESS);
}
mutex_enter(&cportinfo->cport_mutex);
(SATA_CPORTINFO_DRV_INFO(cportinfo))->
satadrv_state = SATA_STATE_READY;
mutex_exit(&cportinfo->cport_mutex);
break;
case SATA_ADDR_PMPORT:
saddr->qual = SATA_ADDR_DPMPORT;
mutex_enter(&cportinfo->cport_mutex);
ASSERT(cportinfo->cport_dev_type == SATA_DTYPE_PMULT);
pminfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
pmportinfo = pminfo->pmult_dev_port[saddr->pmport];
mutex_exit(&cportinfo->cport_mutex);
rval = sata_probe_device(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
sata_device->satadev_type == SATA_DTYPE_UNKNOWN) {
return (SATA_FAILURE);
}
mutex_enter(&pmportinfo->pmport_mutex);
sata_show_drive_info(sata_hba_inst,
SATA_PMPORTINFO_DRV_INFO(pmportinfo));
if ((sata_device->satadev_type & SATA_VALID_DEV_TYPE) == 0) {
pmportinfo->pmport_dev_type = SATA_DTYPE_UNKNOWN;
mutex_exit(&pmportinfo->pmport_mutex);
return (SATA_SUCCESS);
}
pmportinfo->pmport_dev_type = sata_device->satadev_type;
pmportinfo->pmport_tgtnode_clean = B_TRUE;
mutex_exit(&pmportinfo->pmport_mutex);
if (sata_initialize_device(sata_hba_inst,
pmportinfo->pmport_sata_drive) != SATA_SUCCESS) {
rval = sata_initialize_device(sata_hba_inst,
pmportinfo->pmport_sata_drive);
if (rval == SATA_RETRY)
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d:%d - "
"default device features could not be set."
" Device may not operate as expected.",
cport, pmport);
}
cdip = sata_create_target_node(pdip, sata_hba_inst, saddr);
if (cdip == NULL) {
return (SATA_SUCCESS);
}
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_sata_drive->satadrv_state |=
SATA_STATE_READY;
mutex_exit(&pmportinfo->pmport_mutex);
break;
default:
return (SATA_FAILURE);
}
return (SATA_SUCCESS);
}
static int
sata_offline_device(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_drive_info_t *sdinfo)
{
uint8_t cport, pmport, qual;
dev_info_t *tdip;
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
if (qual == SATA_ADDR_DCPORT) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: disconnect device at port %d", cport));
} else {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: disconnect device at port %d:%d",
cport, pmport));
}
sata_device->satadev_addr.qual =
sdinfo->satadrv_addr.qual;
tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&sata_device->satadev_addr);
if (tdip != NULL && ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) != NDI_SUCCESS) {
if (qual == SATA_ADDR_DCPORT) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: disconnect: could "
"not unconfigure device before "
"disconnecting the SATA port %d",
cport));
} else {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: disconnect: could "
"not unconfigure device before "
"disconnecting the SATA port %d:%d",
cport, pmport));
}
sata_set_device_removed(tdip);
sata_set_target_node_cleanup(
sata_hba_inst, &sata_device->satadev_addr);
}
return (SATA_SUCCESS);
}
static dev_info_t *
sata_create_target_node(dev_info_t *dip, sata_hba_inst_t *sata_hba_inst,
sata_address_t *sata_addr)
{
dev_info_t *cdip = NULL;
int rval;
char *nname = NULL;
char **compatible = NULL;
int ncompatible;
struct scsi_inquiry inq;
sata_device_t sata_device;
sata_drive_info_t *sdinfo;
int target;
int i;
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *sata_addr;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, sata_addr->cport)));
sdinfo = sata_get_device_info(sata_hba_inst, &sata_device);
target = SATA_TO_SCSI_TARGET(sata_addr->cport,
sata_addr->pmport, sata_addr->qual);
if (sdinfo == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_addr->cport)));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_create_target_node: no sdinfo for target %x",
target));
return (NULL);
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
sata_identdev_to_inquiry(sata_hba_inst, sdinfo,
(uint8_t *)&inq);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_addr->cport)));
} else {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_addr->cport)));
if (sata_get_atapi_inquiry_data(sata_hba_inst, sata_addr,
&inq) == SATA_FAILURE)
return (NULL);
sdinfo->satadrv_atapi_trans_ver =
SATA_ATAPI_TRANS_VERSION(&inq);
}
scsi_hba_nodename_compatible_get(&inq, NULL,
inq.inq_dtype, NULL, &nname, &compatible, &ncompatible);
#ifdef SATA_DEBUG
if (sata_debug_flags & SATA_DBG_NODES) {
if (nname == NULL) {
cmn_err(CE_NOTE, "sata_create_target_node: "
"cannot determine nodename for target %d\n",
target);
} else {
cmn_err(CE_WARN, "sata_create_target_node: "
"target %d nodename: %s\n", target, nname);
}
if (compatible == NULL) {
cmn_err(CE_WARN,
"sata_create_target_node: no compatible name\n");
} else {
for (i = 0; i < ncompatible; i++) {
cmn_err(CE_WARN, "sata_create_target_node: "
"compatible name: %s\n", compatible[i]);
}
}
}
#endif
if (nname == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_create_target_node: cannot determine nodename "
"for target %d\n", target));
scsi_hba_nodename_compatible_free(nname, compatible);
return (NULL);
}
ndi_devi_alloc_sleep(dip, nname, (pnode_t)DEVI_SID_NODEID, &cdip);
rval = ndi_prop_update_string(DDI_DEV_T_NONE, cdip,
"device-type", "scsi");
if (rval != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_create_target_node: "
"updating device_type prop failed %d", rval));
goto fail;
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "target", target);
if (rval != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_create_target_node: "
"updating target prop failed %d", rval));
goto fail;
}
rval = ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "lun", 0);
if (rval != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_create_target_node: "
"updating target prop failed %d", rval));
goto fail;
}
if (sdinfo->satadrv_type & SATA_DTYPE_ATAPI) {
rval = ndi_prop_update_string(DDI_DEV_T_NONE, cdip,
"variant", "atapi");
if (rval != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_create_target_node: variant atapi "
"property could not be created: %d", rval));
goto fail;
}
}
if (ndi_prop_update_string_array(DDI_DEV_T_NONE, cdip, "compatible",
compatible, ncompatible) != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_create_target_node: FAIL compatible props cdip 0x%p",
(void *)cdip));
goto fail;
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
if (ndi_prop_update_int(DDI_DEV_T_NONE, cdip, "sata-phy",
(int)sata_addr->cport) != DDI_PROP_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_create_target_node: failed to create "
"\"sata-phy\" property: port %d",
sata_addr->cport));
}
}
rval = ndi_devi_online(cdip, NDI_ONLINE_ATTACH);
scsi_hba_nodename_compatible_free(nname, compatible);
if (rval == NDI_SUCCESS)
return (cdip);
return (NULL);
fail:
scsi_hba_nodename_compatible_free(nname, compatible);
ddi_prop_remove_all(cdip);
rval = ndi_devi_free(cdip);
if (rval != NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_create_target_node: "
"node removal failed %d", rval));
}
sata_log(sata_hba_inst, CE_WARN, "sata_create_target_node: "
"cannot create target node for SATA device at port %d",
sata_addr->cport);
return (NULL);
}
static void
sata_remove_target_node(sata_hba_inst_t *sata_hba_inst,
sata_address_t *sata_addr)
{
dev_info_t *tdip;
uint8_t cport = sata_addr->cport;
uint8_t pmport = sata_addr->pmport;
uint8_t qual = sata_addr->qual;
ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT);
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), cport, pmport);
if (tdip != NULL) {
if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) != NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_remove_target_node: "
"Failed to remove target node for "
"detached SATA device."));
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst),
cport, pmport);
if (tdip != NULL) {
sata_set_device_removed(tdip);
sata_set_target_node_cleanup(sata_hba_inst,
sata_addr);
}
}
if (qual == SATA_ADDR_CPORT)
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached at port %d", cport);
else
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached at port %d:%d",
cport, pmport);
}
#ifdef SATA_DEBUG
else {
if (qual == SATA_ADDR_CPORT)
sata_log(sata_hba_inst, CE_WARN,
"target node not found at port %d", cport);
else
sata_log(sata_hba_inst, CE_WARN,
"target node not found at port %d:%d",
cport, pmport);
}
#endif
}
static int
sata_reprobe_port(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device,
int flag)
{
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pmultinfo;
sata_drive_info_t *sdinfo, *osdinfo;
boolean_t init_device = B_FALSE;
int prev_device_type = SATA_DTYPE_NONE;
int prev_device_settings = 0;
int prev_device_state = 0;
clock_t start_time = 0;
int retry = B_FALSE;
uint8_t cport = sata_device->satadev_addr.cport;
int rval_probe, rval_init;
if (sata_device->satadev_addr.qual == SATA_ADDR_PMPORT ||
sata_device->satadev_addr.qual == SATA_ADDR_DPMPORT)
return (sata_reprobe_pmport(sata_hba_inst, sata_device, flag));
cportinfo = SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT)
return (sata_reprobe_pmult(sata_hba_inst, sata_device, flag));
osdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
if (osdinfo != NULL) {
prev_device_type = cportinfo->cport_dev_type;
prev_device_settings = osdinfo->satadrv_settings;
prev_device_state = osdinfo->satadrv_state;
}
if (flag == SATA_DEV_IDENTIFY_RETRY) {
start_time = ddi_get_lbolt();
retry = B_TRUE;
}
retry_probe:
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_state &= ~SATA_PORT_STATE_CLEAR_MASK;
cportinfo->cport_state |= SATA_STATE_PROBING;
mutex_exit(&cportinfo->cport_mutex);
rval_probe = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
mutex_enter(&cportinfo->cport_mutex);
if (rval_probe != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&cportinfo->cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_port: "
"SATA port %d probing failed",
cportinfo->cport_addr.cport));
return (SATA_FAILURE);
}
sata_update_port_info(sata_hba_inst, sata_device);
cportinfo->cport_state &= ~SATA_STATE_PROBING;
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
(cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP) {
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&cportinfo->cport_mutex);
if (sdinfo != NULL)
kmem_free(sdinfo, sizeof (sata_drive_info_t));
return (SATA_SUCCESS);
}
cportinfo->cport_state |= SATA_STATE_READY;
cportinfo->cport_state |= SATA_STATE_PROBED;
cportinfo->cport_dev_type = sata_device->satadev_type;
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
if (sata_device->satadev_type == SATA_DTYPE_NONE) {
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
if (sdinfo != NULL) {
kmem_free(sdinfo, sizeof (sata_drive_info_t));
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached "
"from port %d", cportinfo->cport_addr.cport);
}
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
if (sata_device->satadev_type != SATA_DTYPE_PMULT) {
if (sdinfo == NULL) {
mutex_exit(&cportinfo->cport_mutex);
sdinfo = kmem_zalloc(
sizeof (sata_drive_info_t), KM_SLEEP);
mutex_enter(&cportinfo->cport_mutex);
if (cportinfo->cport_state & SATA_STATE_READY) {
SATA_CPORTINFO_DRV_INFO(cportinfo) = sdinfo;
sdinfo->satadrv_addr = cportinfo->cport_addr;
sdinfo->satadrv_addr.qual = SATA_ADDR_DCPORT;
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_state = SATA_STATE_UNKNOWN;
} else {
mutex_exit(&cportinfo->cport_mutex);
kmem_free(sdinfo, sizeof (sata_drive_info_t));
return (SATA_SUCCESS);
}
init_device = B_TRUE;
}
cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN;
sata_device->satadev_addr.qual = sdinfo->satadrv_addr.qual;
} else {
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
if (sdinfo != NULL) {
kmem_free(sdinfo, sizeof (sata_drive_info_t));
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached "
"from port %d", cportinfo->cport_addr.cport);
}
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detected at port %d",
cportinfo->cport_addr.cport);
mutex_exit(&cportinfo->cport_mutex);
if (sata_alloc_pmult(sata_hba_inst, sata_device) !=
SATA_SUCCESS)
return (SATA_FAILURE);
sata_show_pmult_info(sata_hba_inst, sata_device);
mutex_enter(&cportinfo->cport_mutex);
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
pmultinfo->pmult_event_flags |= SATA_EVNT_DEVICE_RESET;
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
mutex_exit(&cportinfo->cport_mutex);
rval_probe = sata_probe_device(sata_hba_inst, sata_device);
rval_init = SATA_FAILURE;
mutex_enter(&cportinfo->cport_mutex);
if (rval_probe == SATA_SUCCESS) {
if (osdinfo != NULL &&
sata_device->satadev_type == prev_device_type)
sdinfo->satadrv_settings = prev_device_settings;
mutex_exit(&cportinfo->cport_mutex);
rval_init = SATA_SUCCESS;
if (init_device == B_TRUE) {
rval_init = sata_initialize_device(sata_hba_inst,
sdinfo);
}
if (rval_init == SATA_SUCCESS)
return (rval_init);
} else {
if (sata_device->satadev_type != SATA_DTYPE_NONE) {
if (osdinfo != NULL) {
cportinfo->cport_dev_type = prev_device_type;
sdinfo->satadrv_type = prev_device_type;
sdinfo->satadrv_state = prev_device_state;
}
} else {
kmem_free(sdinfo, sizeof (sata_drive_info_t));
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
mutex_exit(&cportinfo->cport_mutex);
}
if (retry) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time - start_time) <
drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) {
delay(drv_usectohz(SATA_DEV_RETRY_DLY));
goto retry_probe;
}
mutex_enter(&cportinfo->cport_mutex);
if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
if (rval_init == SATA_RETRY) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d - desired "
"drive features could not be set. "
"Device may not operate as expected.",
cportinfo->cport_addr.cport);
} else {
SATA_CPORTINFO_DRV_INFO(cportinfo)->
satadrv_state = SATA_DSTATE_FAILED;
}
}
mutex_exit(&cportinfo->cport_mutex);
}
return (SATA_SUCCESS);
}
static int
sata_reprobe_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device,
int flag)
{
_NOTE(ARGUNUSED(flag))
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pmultinfo;
uint8_t cport = sata_device->satadev_addr.cport;
int rval_probe;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_state &= ~SATA_PORT_STATE_CLEAR_MASK;
cportinfo->cport_state |= SATA_STATE_PROBING;
mutex_exit(&cportinfo->cport_mutex);
rval_probe = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
mutex_enter(&cportinfo->cport_mutex);
if (rval_probe != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmult: "
"SATA port %d probing failed", cport));
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detached at port %d", cport);
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, sata_device);
return (SATA_FAILURE);
}
sata_update_port_info(sata_hba_inst, sata_device);
cportinfo->cport_state &= ~SATA_STATE_PROBING;
cportinfo->cport_state |= SATA_STATE_PROBED;
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
(cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP ||
(sata_device->satadev_type == SATA_DTYPE_NONE)) {
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, sata_device);
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detached at port %d", cport);
return (SATA_SUCCESS);
}
if (sata_device->satadev_type != SATA_DTYPE_PMULT) {
cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN;
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, sata_device);
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detached at port %d", cport);
return (SATA_FAILURE);
}
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, sata_device);
if (sata_alloc_pmult(sata_hba_inst, sata_device) != SATA_SUCCESS)
return (SATA_FAILURE);
mutex_enter(&cportinfo->cport_mutex);
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"SATA port multiplier [changed] at port %d", cport);
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detected at port %d", cport);
pmultinfo->pmult_event_flags |= SATA_EVNT_DEVICE_RESET;
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
static int
sata_reprobe_pmport(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device,
int flag)
{
sata_cport_info_t *cportinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_drive_info_t *sdinfo, *osdinfo;
sata_device_t sdevice;
boolean_t init_device = B_FALSE;
int prev_device_type = SATA_DTYPE_NONE;
int prev_device_settings = 0;
int prev_device_state = 0;
clock_t start_time;
uint8_t cport = sata_device->satadev_addr.cport;
uint8_t pmport = sata_device->satadev_addr.pmport;
int rval;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
osdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
if (osdinfo != NULL) {
prev_device_type = pmportinfo->pmport_dev_type;
prev_device_settings = osdinfo->satadrv_settings;
prev_device_state = osdinfo->satadrv_state;
}
start_time = ddi_get_lbolt();
mutex_enter(&cportinfo->cport_mutex);
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
(cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP) {
mutex_exit(&cportinfo->cport_mutex);
return (SATA_FAILURE);
}
mutex_exit(&cportinfo->cport_mutex);
retry_probe_pmport:
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state &= ~SATA_PORT_STATE_CLEAR_MASK;
pmportinfo->pmport_state |= SATA_STATE_PROBING;
mutex_exit(&pmportinfo->pmport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
if (rval == SATA_FAILURE) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmport: "
"SATA port %d:%d probing failed",
cport, pmport));
return (SATA_FAILURE);
} else if (rval == SATA_RETRY) {
SATA_LOG_D((sata_hba_inst, CE_WARN, "sata_reprobe_pmport: "
"SATA port %d:%d probing failed, retrying...",
cport, pmport));
clock_t cur_time = ddi_get_lbolt();
if ((cur_time - start_time) <
drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) {
delay(drv_usectohz(SATA_DEV_RETRY_DLY));
goto retry_probe_pmport;
} else {
mutex_enter(&pmportinfo->pmport_mutex);
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL)
SATA_PMPORTINFO_DRV_INFO(pmportinfo)->
satadrv_state = SATA_DSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
return (SATA_SUCCESS);
}
}
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
(cportinfo->cport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP ||
(cportinfo->cport_dev_type != SATA_DTYPE_PMULT)) {
cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN;
sdevice.satadev_addr.cport = cport;
sdevice.satadev_addr.pmport = pmport;
sdevice.satadev_addr.qual = SATA_ADDR_PMULT;
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, &sdevice);
return (SATA_FAILURE);
}
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
pmportinfo->pmport_state &= ~SATA_STATE_PROBING;
if ((pmportinfo->pmport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
(pmportinfo->pmport_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP) {
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&pmportinfo->pmport_mutex);
if (sdinfo != NULL)
kmem_free(sdinfo, sizeof (sata_drive_info_t));
return (SATA_SUCCESS);
}
pmportinfo->pmport_state |= SATA_STATE_READY;
pmportinfo->pmport_dev_type = sata_device->satadev_type;
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
if (sata_device->satadev_type == SATA_DTYPE_NONE) {
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
if (sdinfo != NULL) {
kmem_free(sdinfo, sizeof (sata_drive_info_t));
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached from port %d:%d",
cport, pmport);
}
mutex_exit(&pmportinfo->pmport_mutex);
return (SATA_SUCCESS);
}
ASSERT(sata_device->satadev_type != SATA_DTYPE_PMULT);
if (sdinfo == NULL) {
mutex_exit(&pmportinfo->pmport_mutex);
sdinfo = kmem_zalloc(sizeof (sata_drive_info_t),
KM_SLEEP);
mutex_enter(&pmportinfo->pmport_mutex);
if (pmportinfo->pmport_state & SATA_STATE_READY) {
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = sdinfo;
sdinfo->satadrv_addr = pmportinfo->pmport_addr;
sdinfo->satadrv_addr.qual = SATA_ADDR_DPMPORT;
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_state = SATA_STATE_UNKNOWN;
} else {
mutex_exit(&pmportinfo->pmport_mutex);
kmem_free(sdinfo, sizeof (sata_drive_info_t));
return (SATA_SUCCESS);
}
init_device = B_TRUE;
}
pmportinfo->pmport_dev_type = SATA_DTYPE_UNKNOWN;
sata_device->satadev_addr.qual = sdinfo->satadrv_addr.qual;
mutex_exit(&pmportinfo->pmport_mutex);
rval = sata_probe_device(sata_hba_inst, sata_device);
mutex_enter(&pmportinfo->pmport_mutex);
if (rval == SATA_SUCCESS) {
if (osdinfo != NULL &&
sata_device->satadev_type == prev_device_type)
sdinfo->satadrv_settings = prev_device_settings;
mutex_exit(&pmportinfo->pmport_mutex);
if (init_device == B_TRUE) {
rval = sata_initialize_device(sata_hba_inst, sdinfo);
}
if (rval == SATA_SUCCESS)
return (rval);
} else {
if (sata_device->satadev_type != SATA_DTYPE_NONE) {
if (osdinfo != NULL) {
pmportinfo->pmport_dev_type = prev_device_type;
sdinfo->satadrv_type = prev_device_type;
sdinfo->satadrv_state = prev_device_state;
}
} else {
kmem_free(sdinfo, sizeof (sata_drive_info_t));
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
mutex_exit(&pmportinfo->pmport_mutex);
return (SATA_SUCCESS);
}
mutex_exit(&pmportinfo->pmport_mutex);
}
if (flag == SATA_DEV_IDENTIFY_RETRY) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time - start_time) <
drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) {
delay(drv_usectohz(SATA_DEV_RETRY_DLY));
goto retry_probe_pmport;
} else {
mutex_enter(&pmportinfo->pmport_mutex);
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL)
SATA_PMPORTINFO_DRV_INFO(pmportinfo)->
satadrv_state = SATA_DSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
}
}
return (SATA_SUCCESS);
}
static int
sata_alloc_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device)
{
dev_info_t *dip = SATA_DIP(sata_hba_inst);
sata_cport_info_t *cportinfo = NULL;
sata_pmult_info_t *pmultinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_device_t sd;
dev_t minor_number;
char name[16];
uint8_t cport = sata_device->satadev_addr.cport;
int rval;
int npmport;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
mutex_enter(&cportinfo->cport_mutex);
if (SATA_CPORTINFO_PMULT_INFO(cportinfo) == NULL) {
SATA_CPORTINFO_PMULT_INFO(cportinfo) =
kmem_zalloc(sizeof (sata_pmult_info_t), KM_SLEEP);
}
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
pmultinfo->pmult_addr = sata_device->satadev_addr;
pmultinfo->pmult_addr.qual = SATA_ADDR_PMULT;
pmultinfo->pmult_state = SATA_STATE_PROBING;
sd.satadev_addr = sata_device->satadev_addr;
sd.satadev_addr.qual = SATA_ADDR_PMULT_SPEC;
mutex_exit(&cportinfo->cport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sd);
mutex_enter(&cportinfo->cport_mutex);
if (rval != SATA_SUCCESS ||
(sd.satadev_type != SATA_DTYPE_PMULT) ||
!(sd.satadev_state & SATA_DSTATE_PMULT_INIT)) {
SATA_CPORTINFO_PMULT_INFO(cportinfo) = NULL;
kmem_free(pmultinfo, sizeof (sata_pmult_info_t));
cportinfo->cport_state = SATA_PSTATE_FAILED;
cportinfo->cport_dev_type = SATA_DTYPE_UNKNOWN;
mutex_exit(&cportinfo->cport_mutex);
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"sata_alloc_pmult: failed to initialize pmult "
"at port %d.", cport)
return (SATA_FAILURE);
}
for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports;
npmport++) {
if (SATA_PMPORT_INFO(sata_hba_inst, cport, npmport) != NULL)
continue;
pmportinfo = kmem_zalloc(sizeof (sata_pmport_info_t), KM_SLEEP);
mutex_init(&pmportinfo->pmport_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_exit(&cportinfo->cport_mutex);
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_addr.cport = cport;
pmportinfo->pmport_addr.pmport = (uint8_t)npmport;
pmportinfo->pmport_addr.qual = SATA_ADDR_PMPORT;
pmportinfo->pmport_state &= ~SATA_PORT_STATE_CLEAR_MASK;
mutex_exit(&pmportinfo->pmport_mutex);
mutex_enter(&cportinfo->cport_mutex);
SATA_PMPORT_INFO(sata_hba_inst, cport, npmport) = pmportinfo;
minor_number = SATA_MAKE_AP_MINOR(ddi_get_instance(dip),
cport, (uint8_t)npmport, SATA_ADDR_PMPORT);
(void) sprintf(name, "%d.%d", cport, npmport);
if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number,
DDI_NT_SATA_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
sata_log(sata_hba_inst, CE_WARN, "sata_hba_attach: "
"cannot create SATA attachment point for "
"port %d:%d", cport, npmport);
}
}
pmultinfo->pmult_state &= ~SATA_STATE_PROBING;
pmultinfo->pmult_state |= (SATA_STATE_PROBED|SATA_STATE_READY);
cportinfo->cport_dev_type = SATA_DTYPE_PMULT;
mutex_exit(&cportinfo->cport_mutex);
return (SATA_SUCCESS);
}
static void
sata_free_pmult(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device)
{
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pmultinfo;
sata_pmport_info_t *pmportinfo;
sata_device_t pmport_device;
sata_drive_info_t *sdinfo;
dev_info_t *tdip;
char name[16];
uint8_t cport = sata_device->satadev_addr.cport;
int npmport;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
ASSERT(pmultinfo != NULL);
for (npmport = 0; npmport < pmultinfo->pmult_num_dev_ports;
npmport++) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, npmport);
if (pmportinfo == NULL)
continue;
mutex_exit(&cportinfo->cport_mutex);
mutex_enter(&pmportinfo->pmport_mutex);
sdinfo = pmportinfo->pmport_sata_drive;
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
mutex_exit(&pmportinfo->pmport_mutex);
name[0] = '\0';
(void) sprintf(name, "%d.%d", cport, npmport);
ddi_remove_minor_node(SATA_DIP(sata_hba_inst), name);
sata_log(sata_hba_inst, CE_NOTE,
"Remove attachment point of port %d:%d",
cport, npmport);
bzero(&pmport_device, sizeof (sata_device_t));
pmport_device.satadev_rev = SATA_DEVICE_REV;
pmport_device.satadev_addr.cport = cport;
pmport_device.satadev_addr.pmport = (uint8_t)npmport;
pmport_device.satadev_addr.qual = SATA_ADDR_DPMPORT;
tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&(pmport_device.satadev_addr));
if (tdip != NULL && ndi_devi_offline(tdip,
NDI_DEVI_REMOVE) != NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_free_pmult: could not unconfigure device "
"before disconnecting the SATA port %d:%d",
cport, npmport));
sata_set_device_removed(tdip);
sata_set_target_node_cleanup(
sata_hba_inst, &(pmport_device.satadev_addr));
}
mutex_enter(&cportinfo->cport_mutex);
if (sdinfo != NULL) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached from port %d:%d",
cport, npmport);
kmem_free(sdinfo, sizeof (sata_drive_info_t));
}
mutex_destroy(&pmportinfo->pmport_mutex);
kmem_free(pmportinfo, sizeof (sata_pmport_info_t));
}
kmem_free(pmultinfo, sizeof (sata_pmult_info_t));
cportinfo->cport_devp.cport_sata_pmult = NULL;
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detached at port %d", cport);
mutex_exit(&cportinfo->cport_mutex);
}
static int
sata_initialize_device(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo)
{
int rval;
sata_save_drive_settings(sdinfo);
sdinfo->satadrv_settings |= SATA_DEV_READ_AHEAD;
sata_init_write_cache_mode(sdinfo);
rval = sata_set_drive_features(sata_hba_inst, sdinfo, 0);
if ((sdinfo->satadrv_id.ai_cap & SATA_DMA_SUPPORT) == 0) {
sdinfo->satadrv_settings &= ~SATA_DEV_DMA;
} else if ((sdinfo->satadrv_id.ai_validinfo &
SATA_VALIDINFO_88) != 0 &&
(sdinfo->satadrv_id.ai_ultradma & SATA_UDMA_SEL_MASK) != 0) {
sdinfo->satadrv_settings |= SATA_DEV_DMA;
} else if ((sdinfo->satadrv_id.ai_dworddma &
SATA_MDMA_SEL_MASK) != 0) {
sdinfo->satadrv_settings |= SATA_DEV_DMA;
} else
sdinfo->satadrv_settings &= ~SATA_DEV_DMA;
if ((sdinfo->satadrv_id.ai_cmdset83 & 0x20) &&
(sdinfo->satadrv_id.ai_features86 & 0x20))
sdinfo->satadrv_power_level = SATA_POWER_STANDBY;
else
sdinfo->satadrv_power_level = SATA_POWER_ACTIVE;
return (rval);
}
static void
sata_init_write_cache_mode(sata_drive_info_t *sdinfo)
{
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
if (sata_write_cache == 1)
sdinfo->satadrv_settings |= SATA_DEV_WRITE_CACHE;
else if (sata_write_cache == 0)
sdinfo->satadrv_settings &= ~SATA_DEV_WRITE_CACHE;
break;
case SATA_DTYPE_ATAPICD:
if (sata_atapicdvd_write_cache == 1)
sdinfo->satadrv_settings |= SATA_DEV_WRITE_CACHE;
else if (sata_atapicdvd_write_cache == 0)
sdinfo->satadrv_settings &= ~SATA_DEV_WRITE_CACHE;
break;
case SATA_DTYPE_ATAPITAPE:
if (sata_atapitape_write_cache == 1)
sdinfo->satadrv_settings |= SATA_DEV_WRITE_CACHE;
else if (sata_atapitape_write_cache == 0)
sdinfo->satadrv_settings &= ~SATA_DEV_WRITE_CACHE;
break;
case SATA_DTYPE_ATAPIDISK:
if (sata_atapidisk_write_cache == 1)
sdinfo->satadrv_settings |= SATA_DEV_WRITE_CACHE;
else if (sata_atapidisk_write_cache == 0)
sdinfo->satadrv_settings &= ~SATA_DEV_WRITE_CACHE;
break;
}
}
static int
sata_validate_sata_address(sata_hba_inst_t *sata_hba_inst, int cport,
int pmport, int qual)
{
if (qual == SATA_ADDR_DCPORT && pmport != 0)
goto invalid_address;
if (cport >= SATA_NUM_CPORTS(sata_hba_inst))
goto invalid_address;
if ((qual == SATA_ADDR_DPMPORT || qual == SATA_ADDR_PMPORT) &&
((SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) != SATA_DTYPE_PMULT) ||
(SATA_PMULT_INFO(sata_hba_inst, cport) == NULL) ||
(pmport >= SATA_NUM_PMPORTS(sata_hba_inst, cport))))
goto invalid_address;
return (0);
invalid_address:
return (-1);
}
static int
sata_validate_scsi_address(sata_hba_inst_t *sata_hba_inst,
struct scsi_address *ap, sata_device_t *sata_device)
{
int cport, pmport, qual, rval;
rval = -1;
if (ap->a_lun != 0)
goto out;
qual = SCSI_TO_SATA_ADDR_QUAL(ap->a_target);
cport = SCSI_TO_SATA_CPORT(ap->a_target);
pmport = SCSI_TO_SATA_PMPORT(ap->a_target);
if (qual != SATA_ADDR_DCPORT && qual != SATA_ADDR_DPMPORT)
goto out;
if (sata_validate_sata_address(sata_hba_inst, cport, pmport, qual) ==
0) {
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pmultinfo;
sata_drive_info_t *sdinfo = NULL;
sata_device->satadev_addr.qual = qual;
sata_device->satadev_addr.cport = cport;
sata_device->satadev_addr.pmport = pmport;
sata_device->satadev_rev = SATA_DEVICE_REV_1;
rval = 1;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
if (qual == SATA_ADDR_DCPORT) {
if (cportinfo == NULL ||
cportinfo->cport_dev_type == SATA_DTYPE_NONE)
goto out;
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
if (cportinfo->cport_dev_type == SATA_DTYPE_UNKNOWN &&
sdinfo != NULL) {
rval = 2;
goto out;
}
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) == 0) {
rval = -1;
goto out;
}
} else if (qual == SATA_ADDR_DPMPORT) {
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
if (pmultinfo == NULL) {
rval = -1;
goto out;
}
if (SATA_PMPORT_INFO(sata_hba_inst, cport, pmport) ==
NULL ||
SATA_PMPORT_DEV_TYPE(sata_hba_inst, cport,
pmport) == SATA_DTYPE_NONE)
goto out;
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, cport,
pmport);
if (SATA_PMPORT_DEV_TYPE(sata_hba_inst, cport,
pmport) == SATA_DTYPE_UNKNOWN && sdinfo != NULL) {
rval = 2;
goto out;
}
if ((SATA_PMPORT_DEV_TYPE(sata_hba_inst, cport,
pmport) & SATA_VALID_DEV_TYPE) == 0) {
rval = -1;
goto out;
}
} else {
rval = -1;
goto out;
}
if ((sdinfo == NULL) ||
(sdinfo->satadrv_type & SATA_VALID_DEV_TYPE) == 0)
goto out;
sata_device->satadev_type = sdinfo->satadrv_type;
return (0);
}
out:
if (rval > 0) {
SATADBG2(SATA_DBG_SCSI_IF, sata_hba_inst,
"sata_validate_scsi_address: no valid target %x lun %x",
ap->a_target, ap->a_lun);
}
return (rval);
}
static dev_info_t *
sata_devt_to_devinfo(dev_t dev)
{
dev_info_t *dip;
#ifndef __lock_lint
struct devnames *dnp;
major_t major = getmajor(dev);
int instance = SATA_MINOR2INSTANCE(getminor(dev));
if (major >= devcnt)
return (NULL);
dnp = &devnamesp[major];
LOCK_DEV_OPS(&(dnp->dn_lock));
dip = dnp->dn_head;
while (dip && (ddi_get_instance(dip) != instance)) {
dip = ddi_get_next(dip);
}
UNLOCK_DEV_OPS(&(dnp->dn_lock));
#endif
return (dip);
}
static int
sata_probe_device(sata_hba_inst_t *sata_hba_inst, sata_device_t *sata_device)
{
sata_pmport_info_t *pmportinfo = NULL;
sata_drive_info_t *sdinfo;
sata_drive_info_t new_sdinfo;
int rval;
ASSERT((SATA_CPORT_STATE(sata_hba_inst,
sata_device->satadev_addr.cport) &
(SATA_STATE_PROBED | SATA_STATE_READY)) != 0);
sata_device->satadev_type = SATA_DTYPE_NONE;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
if (sata_device->satadev_addr.qual == SATA_ADDR_DPMPORT) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
ASSERT(pmportinfo != NULL);
}
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo != NULL) {
sdinfo->satadrv_state &=
~(SATA_STATE_PROBED | SATA_STATE_READY);
sdinfo->satadrv_state |= SATA_STATE_PROBING;
} else {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
sata_device->satadev_type = SATA_DTYPE_NONE;
sata_device->satadev_state = SATA_STATE_UNKNOWN;
return (SATA_FAILURE);
}
bzero(&new_sdinfo, sizeof (sata_drive_info_t));
new_sdinfo.satadrv_addr = sata_device->satadev_addr;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
new_sdinfo.satadrv_type = SATA_DTYPE_ATADISK;
rval = sata_identify_device(sata_hba_inst, &new_sdinfo);
if (rval == SATA_RETRY) {
if (SATA_FEATURES(sata_hba_inst) & SATA_CTLF_ATAPI) {
new_sdinfo.satadrv_type = SATA_DTYPE_ATAPI;
rval = sata_identify_device(sata_hba_inst, &new_sdinfo);
}
}
if (rval == SATA_SUCCESS) {
sata_device->satadev_type = new_sdinfo.satadrv_type;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo == NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
return (SATA_FAILURE);
}
*sdinfo = new_sdinfo;
sdinfo->satadrv_state &= ~SATA_STATE_PROBING;
sdinfo->satadrv_state |= SATA_STATE_PROBED;
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
SATA_CPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport) =
sdinfo->satadrv_type;
else {
mutex_enter(&pmportinfo->pmport_mutex);
SATA_PMPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport) =
sdinfo->satadrv_type;
mutex_exit(&pmportinfo->pmport_mutex);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
return (SATA_SUCCESS);
}
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo != NULL) {
sata_device->satadev_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_state &= ~SATA_STATE_PROBING;
sdinfo->satadrv_state |= SATA_STATE_PROBED;
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
SATA_CPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport) =
SATA_DTYPE_UNKNOWN;
else {
mutex_enter(&pmportinfo->pmport_mutex);
if ((SATA_PMULT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport) != NULL) &&
(SATA_PMPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport) != NULL))
SATA_PMPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport) =
SATA_DTYPE_UNKNOWN;
mutex_exit(&pmportinfo->pmport_mutex);
}
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sata_device->satadev_addr.cport)));
return (rval);
}
static sata_drive_info_t *
sata_get_device_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
uint8_t cport = sata_device->satadev_addr.cport;
uint8_t pmport = sata_device->satadev_addr.pmport;
uint8_t qual = sata_device->satadev_addr.qual;
if (cport >= SATA_NUM_CPORTS(sata_hba_inst))
return (NULL);
if (!(SATA_CPORT_STATE(sata_hba_inst, cport) &
(SATA_STATE_PROBED | SATA_STATE_READY)))
return (NULL);
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_NONE)
return (NULL);
if (qual == SATA_ADDR_DCPORT) {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) ==
SATA_DTYPE_PMULT)
return (NULL);
return (SATA_CPORT_DRV_INFO(sata_hba_inst, cport));
}
if (qual == SATA_ADDR_DPMPORT) {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) !=
SATA_DTYPE_PMULT)
return (NULL);
if (pmport > SATA_NUM_PMPORTS(sata_hba_inst, cport))
return (NULL);
if (!(SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) &
(SATA_STATE_PROBED | SATA_STATE_READY)))
return (NULL);
return (SATA_PMPORT_DRV_INFO(sata_hba_inst, cport, pmport));
}
return (NULL);
}
static int
sata_identify_device(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo)
{
uint16_t cfg_word;
int rval;
if ((rval = sata_fetch_device_identify_data(sata_hba_inst,
sdinfo)) != SATA_SUCCESS)
goto fail_unknown;
cfg_word = sdinfo->satadrv_id.ai_config;
if ((cfg_word & SATA_ATA_TYPE_MASK) == SATA_ATA_TYPE) {
sdinfo->satadrv_type = SATA_DTYPE_ATADISK;
} else if (cfg_word == SATA_CFA_TYPE) {
sdinfo->satadrv_type = SATA_DTYPE_ATADISK;
} else if ((cfg_word & SATA_ATAPI_TYPE_MASK) == SATA_ATAPI_TYPE) {
switch (cfg_word & SATA_ATAPI_ID_DEV_TYPE) {
case SATA_ATAPI_CDROM_DEV:
sdinfo->satadrv_type = SATA_DTYPE_ATAPICD;
break;
case SATA_ATAPI_SQACC_DEV:
sdinfo->satadrv_type = SATA_DTYPE_ATAPITAPE;
break;
case SATA_ATAPI_DIRACC_DEV:
sdinfo->satadrv_type = SATA_DTYPE_ATAPIDISK;
break;
case SATA_ATAPI_PROC_DEV:
sdinfo->satadrv_type = SATA_DTYPE_ATAPIPROC;
break;
default:
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
}
} else {
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
if (sdinfo->satadrv_capacity == 0) {
sata_log(sata_hba_inst, CE_WARN,
"SATA disk device at port %d does not support LBA",
sdinfo->satadrv_addr.cport);
rval = SATA_FAILURE;
goto fail_unknown;
}
}
#if 0
for (i = 6; i >= 0; --i) {
if (sdinfo->satadrv_id.ai_ultradma & (1 << i))
break;
}
if (i < 4) {
sata_log(sata_hba_inst, CE_WARN,
"SATA disk device at port %d does not support UDMA "
"mode 4 or higher", sdinfo->satadrv_addr.cport);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"mode 4 or higher required, %d supported", i));
rval = SATA_FAILURE;
goto fail_unknown;
}
#endif
if ((sdinfo->satadrv_type == SATA_DTYPE_ATADISK) &&
!((sdinfo->satadrv_id.ai_validinfo & SATA_VALIDINFO_88) != 0 &&
(sdinfo->satadrv_id.ai_ultradma & SATA_UDMA_SUP_MASK) != 0)) {
sata_log(sata_hba_inst, CE_WARN,
"SATA disk device at port %d does not support UDMA",
sdinfo->satadrv_addr.cport);
rval = SATA_FAILURE;
goto fail_unknown;
}
return (SATA_SUCCESS);
fail_unknown:
sdinfo->satadrv_type = SATA_DTYPE_UNKNOWN;
sdinfo->satadrv_state = SATA_STATE_UNKNOWN;
return (rval);
}
static void
sata_show_drive_info(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo)
{
int valid_version = 0;
char msg_buf[MAXPATHLEN];
int i;
(void) ddi_pathname(SATA_DIP(sata_hba_inst), msg_buf);
cmn_err(CE_CONT, "?%s :\n", msg_buf);
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
(void) sprintf(msg_buf, "SATA disk device at");
break;
case SATA_DTYPE_ATAPICD:
(void) sprintf(msg_buf, "SATA CD/DVD (ATAPI) device at");
break;
case SATA_DTYPE_ATAPITAPE:
(void) sprintf(msg_buf, "SATA tape (ATAPI) device at");
break;
case SATA_DTYPE_ATAPIDISK:
(void) sprintf(msg_buf, "SATA disk (ATAPI) device at");
break;
case SATA_DTYPE_ATAPIPROC:
(void) sprintf(msg_buf, "SATA processor (ATAPI) device at");
break;
case SATA_DTYPE_UNKNOWN:
(void) sprintf(msg_buf,
"Unsupported SATA device type (cfg 0x%x) at ",
sdinfo->satadrv_id.ai_config);
break;
}
if (sdinfo->satadrv_addr.qual == SATA_ADDR_DCPORT)
cmn_err(CE_CONT, "?\t%s port %d\n",
msg_buf, sdinfo->satadrv_addr.cport);
else
cmn_err(CE_CONT, "?\t%s port %d:%d\n",
msg_buf, sdinfo->satadrv_addr.cport,
sdinfo->satadrv_addr.pmport);
bcopy(&sdinfo->satadrv_id.ai_model, msg_buf,
sizeof (sdinfo->satadrv_id.ai_model));
swab(msg_buf, msg_buf, sizeof (sdinfo->satadrv_id.ai_model));
msg_buf[sizeof (sdinfo->satadrv_id.ai_model)] = '\0';
cmn_err(CE_CONT, "?\tmodel %s\n", msg_buf);
bcopy(&sdinfo->satadrv_id.ai_fw, msg_buf,
sizeof (sdinfo->satadrv_id.ai_fw));
swab(msg_buf, msg_buf, sizeof (sdinfo->satadrv_id.ai_fw));
msg_buf[sizeof (sdinfo->satadrv_id.ai_fw)] = '\0';
cmn_err(CE_CONT, "?\tfirmware %s\n", msg_buf);
bcopy(&sdinfo->satadrv_id.ai_drvser, msg_buf,
sizeof (sdinfo->satadrv_id.ai_drvser));
swab(msg_buf, msg_buf, sizeof (sdinfo->satadrv_id.ai_drvser));
msg_buf[sizeof (sdinfo->satadrv_id.ai_drvser)] = '\0';
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
cmn_err(CE_CONT, "?\tserial number %s\n", msg_buf);
} else {
for (i = 0; i < sizeof (sdinfo->satadrv_id.ai_drvser); i++) {
if (msg_buf[i] != '\0' && msg_buf[i] != ' ')
break;
}
if (i == sizeof (sdinfo->satadrv_id.ai_drvser)) {
cmn_err(CE_CONT, "?\tserial number - none\n");
} else {
cmn_err(CE_CONT, "?\tserial number %s\n", msg_buf);
}
}
#ifdef SATA_DEBUG
if (sdinfo->satadrv_id.ai_majorversion != 0 &&
sdinfo->satadrv_id.ai_majorversion != 0xffff) {
int i;
for (i = 14; i >= 2; i--) {
if (sdinfo->satadrv_id.ai_majorversion & (1 << i)) {
valid_version = i;
break;
}
}
cmn_err(CE_CONT,
"?\tATA/ATAPI-%d supported, majver 0x%x minver 0x%x\n",
valid_version,
sdinfo->satadrv_id.ai_majorversion,
sdinfo->satadrv_id.ai_minorversion);
}
#endif
cmn_err(CE_CONT, "?\tsupported features:\n");
msg_buf[0] = '\0';
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA48)
(void) strlcat(msg_buf, "48-bit LBA, ", MAXPATHLEN);
else if (sdinfo->satadrv_features_support & SATA_DEV_F_LBA28)
(void) strlcat(msg_buf, "28-bit LBA, ", MAXPATHLEN);
}
if (sdinfo->satadrv_features_support & SATA_DEV_F_DMA)
(void) strlcat(msg_buf, "DMA", MAXPATHLEN);
if (sdinfo->satadrv_features_support & SATA_DEV_F_NCQ)
(void) strlcat(msg_buf, ", Native Command Queueing",
MAXPATHLEN);
if (sdinfo->satadrv_features_support & SATA_DEV_F_TCQ)
(void) strlcat(msg_buf, ", Legacy Tagged Queuing", MAXPATHLEN);
if ((sdinfo->satadrv_id.ai_cmdset82 & SATA_SMART_SUPPORTED) &&
(sdinfo->satadrv_id.ai_features85 & SATA_SMART_ENABLED))
(void) strlcat(msg_buf, ", SMART", MAXPATHLEN);
if ((sdinfo->satadrv_id.ai_cmdset84 & SATA_SMART_SELF_TEST_SUPPORTED) &&
(sdinfo->satadrv_id.ai_features87 & SATA_SMART_SELF_TEST_SUPPORTED))
(void) strlcat(msg_buf, ", SMART self-test", MAXPATHLEN);
cmn_err(CE_CONT, "?\t %s\n", msg_buf);
if (sdinfo->satadrv_features_support & SATA_DEV_F_SATA3)
cmn_err(CE_CONT, "?\tSATA Gen3 signaling speed (6.0Gbps)\n");
else if (sdinfo->satadrv_features_support & SATA_DEV_F_SATA2)
cmn_err(CE_CONT, "?\tSATA Gen2 signaling speed (3.0Gbps)\n");
else if (sdinfo->satadrv_features_support & SATA_DEV_F_SATA1)
cmn_err(CE_CONT, "?\tSATA Gen1 signaling speed (1.5Gbps)\n");
if (sdinfo->satadrv_features_support &
(SATA_DEV_F_TCQ | SATA_DEV_F_NCQ)) {
msg_buf[0] = '\0';
(void) snprintf(msg_buf, MAXPATHLEN,
"Supported queue depth %d",
sdinfo->satadrv_queue_depth);
if (!(sata_func_enable &
(SATA_ENABLE_QUEUING | SATA_ENABLE_NCQ)))
(void) strlcat(msg_buf,
" - queueing disabled globally", MAXPATHLEN);
else if (sdinfo->satadrv_queue_depth >
sdinfo->satadrv_max_queue_depth) {
(void) snprintf(&msg_buf[strlen(msg_buf)],
MAXPATHLEN - strlen(msg_buf), ", limited to %d",
(int)sdinfo->satadrv_max_queue_depth);
}
cmn_err(CE_CONT, "?\t%s\n", msg_buf);
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
(void) sprintf(msg_buf, "\tcapacity = %lu sectors\n",
sdinfo->satadrv_capacity);
cmn_err(CE_CONT, "?%s", msg_buf);
}
}
static void
sata_show_pmult_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
_NOTE(ARGUNUSED(sata_hba_inst))
int cport = sata_device->satadev_addr.cport;
sata_pmult_info_t *pmultinfo;
char msg_buf[MAXPATHLEN];
uint32_t gscr0, gscr1, gscr2, gscr64;
mutex_enter(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
if (pmultinfo == NULL) {
mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
return;
}
gscr0 = pmultinfo->pmult_gscr.gscr0;
gscr1 = pmultinfo->pmult_gscr.gscr1;
gscr2 = pmultinfo->pmult_gscr.gscr2;
gscr64 = pmultinfo->pmult_gscr.gscr64;
mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
cmn_err(CE_CONT, "?Port Multiplier %d device-ports found at port %d",
sata_device->satadev_add_info, sata_device->satadev_addr.cport);
(void) sprintf(msg_buf, "\tVendor_ID 0x%04x, Module_ID 0x%04x",
gscr0 & 0xffff, (gscr0 >> 16) & 0xffff);
cmn_err(CE_CONT, "?%s", msg_buf);
(void) strcpy(msg_buf, "\tSupport SATA PMP Spec ");
if (gscr1 & (1 << 3))
(void) strlcat(msg_buf, "1.2", MAXPATHLEN);
else if (gscr1 & (1 << 2))
(void) strlcat(msg_buf, "1.1", MAXPATHLEN);
else if (gscr1 & (1 << 1))
(void) strlcat(msg_buf, "1.0", MAXPATHLEN);
else
(void) strlcat(msg_buf, "unknown", MAXPATHLEN);
cmn_err(CE_CONT, "?%s", msg_buf);
(void) strcpy(msg_buf, "\tSupport ");
if (gscr64 & (1 << 3))
(void) strlcat(msg_buf, "Asy-Notif, ",
MAXPATHLEN);
if (gscr64 & (1 << 2))
(void) strlcat(msg_buf, "Dyn-SSC, ", MAXPATHLEN);
if (gscr64 & (1 << 1))
(void) strlcat(msg_buf, "Iss-PMREQ, ", MAXPATHLEN);
if (gscr64 & (1 << 0))
(void) strlcat(msg_buf, "BIST", MAXPATHLEN);
if ((gscr64 & 0xf) == 0)
(void) strlcat(msg_buf, "nothing", MAXPATHLEN);
cmn_err(CE_CONT, "?%s", msg_buf);
(void) sprintf(msg_buf, "\tNumber of exposed device fan-out ports: %d",
gscr2 & SATA_PMULT_PORTNUM_MASK);
cmn_err(CE_CONT, "?%s", msg_buf);
}
static void
sata_save_drive_settings(sata_drive_info_t *sdinfo)
{
if (SATA_READ_AHEAD_SUPPORTED(sdinfo->satadrv_id) ||
SATA_WRITE_CACHE_SUPPORTED(sdinfo->satadrv_id)) {
if (SATA_READ_AHEAD_ENABLED(sdinfo->satadrv_id))
sdinfo->satadrv_settings |= SATA_DEV_READ_AHEAD;
else
sdinfo->satadrv_settings &= ~SATA_DEV_READ_AHEAD;
if (SATA_WRITE_CACHE_ENABLED(sdinfo->satadrv_id))
sdinfo->satadrv_settings |= SATA_DEV_WRITE_CACHE;
else
sdinfo->satadrv_settings &= ~SATA_DEV_WRITE_CACHE;
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATAPICD) {
if (SATA_RM_NOTIFIC_SUPPORTED(sdinfo->satadrv_id))
sdinfo->satadrv_settings |= SATA_DEV_RMSN;
else
sdinfo->satadrv_settings &= ~SATA_DEV_RMSN;
}
}
static uint64_t
sata_check_capacity(sata_drive_info_t *sdinfo)
{
uint64_t capacity = 0;
int i;
if (sdinfo->satadrv_type != SATA_DTYPE_ATADISK ||
(sdinfo->satadrv_id.ai_cap & SATA_LBA_SUPPORT) == 0)
return (0);
if ((sdinfo->satadrv_id.ai_validinfo & SATA_VALIDINFO_88) &&
(sdinfo->satadrv_id.ai_cmdset83 & SATA_EXT48) &&
(sdinfo->satadrv_id.ai_features86 & SATA_EXT48)) {
sdinfo->satadrv_features_support |= SATA_DEV_F_LBA48 |
SATA_DEV_F_LBA28;
for (i = 3; i >= 0; --i) {
capacity <<= 16;
capacity += sdinfo->satadrv_id.ai_addrsecxt[i];
}
} else {
capacity = sdinfo->satadrv_id.ai_addrsec[1];
capacity <<= 16;
capacity += sdinfo->satadrv_id.ai_addrsec[0];
if (capacity >= 0x1000000)
sdinfo->satadrv_features_support |= SATA_DEV_F_LBA28;
}
return (capacity);
}
static struct buf *
sata_alloc_local_buffer(sata_pkt_txlate_t *spx, size_t len)
{
struct scsi_address ap;
struct buf *bp;
ddi_dma_attr_t cur_dma_attr;
ASSERT(spx->txlt_sata_pkt != NULL);
ap.a_hba_tran = spx->txlt_sata_hba_inst->satahba_scsi_tran;
ap.a_target = SATA_TO_SCSI_TARGET(
spx->txlt_sata_pkt->satapkt_device.satadev_addr.cport,
spx->txlt_sata_pkt->satapkt_device.satadev_addr.pmport,
spx->txlt_sata_pkt->satapkt_device.satadev_addr.qual);
ap.a_lun = 0;
bp = scsi_alloc_consistent_buf(&ap, NULL, len,
B_READ, SLEEP_FUNC, NULL);
if (bp != NULL) {
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = bp;
sata_adjust_dma_attr(NULL,
SATA_DMA_ATTR(spx->txlt_sata_hba_inst), &cur_dma_attr);
if (sata_dma_buf_setup(spx, PKT_CONSISTENT,
SLEEP_FUNC, NULL, &cur_dma_attr) != DDI_SUCCESS) {
scsi_free_consistent_buf(bp);
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
bp = NULL;
}
}
return (bp);
}
static void
sata_free_local_buffer(sata_pkt_txlate_t *spx)
{
ASSERT(spx->txlt_sata_pkt != NULL);
ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp != NULL);
spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 0;
spx->txlt_sata_pkt->satapkt_cmd.satacmd_dma_cookie_list = NULL;
sata_common_free_dma_rsrcs(spx);
scsi_free_consistent_buf(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp);
spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp = NULL;
}
static sata_pkt_t *
sata_pkt_alloc(sata_pkt_txlate_t *spx, int (*callback)(caddr_t))
{
sata_pkt_t *spkt;
int kmsflag;
kmsflag = (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP;
spkt = kmem_zalloc(sizeof (sata_pkt_t), kmsflag);
if (spkt == NULL) {
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_pkt_alloc: failed"));
return (NULL);
}
spkt->satapkt_rev = SATA_PKT_REV;
spkt->satapkt_cmd.satacmd_rev = SATA_CMD_REV;
spkt->satapkt_device.satadev_rev = SATA_DEVICE_REV;
spkt->satapkt_framework_private = spx;
spx->txlt_sata_pkt = spkt;
return (spkt);
}
static void
sata_pkt_free(sata_pkt_txlate_t *spx)
{
ASSERT(spx->txlt_sata_pkt != NULL);
ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp == NULL);
kmem_free(spx->txlt_sata_pkt, sizeof (sata_pkt_t));
spx->txlt_sata_pkt = NULL;
}
void
sata_adjust_dma_attr(sata_drive_info_t *sdinfo, ddi_dma_attr_t *dma_attr,
ddi_dma_attr_t *adj_dma_attr)
{
uint32_t count_max;
*adj_dma_attr = *dma_attr;
if (sdinfo == NULL) {
count_max = dma_attr->dma_attr_granular * 0x100;
if (dma_attr->dma_attr_count_max > count_max)
adj_dma_attr->dma_attr_count_max = count_max;
if (dma_attr->dma_attr_maxxfer > count_max)
adj_dma_attr->dma_attr_maxxfer = count_max;
return;
}
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
if (sdinfo->satadrv_features_support & (SATA_DEV_F_LBA48)) {
count_max = adj_dma_attr->dma_attr_granular * 0x10000;
} else {
count_max = adj_dma_attr->dma_attr_granular * 0x100;
}
if (dma_attr->dma_attr_count_max > count_max)
adj_dma_attr->dma_attr_count_max = count_max;
if (dma_attr->dma_attr_maxxfer > count_max)
adj_dma_attr->dma_attr_maxxfer = count_max;
}
}
static int
sata_dma_buf_setup(sata_pkt_txlate_t *spx, int flags,
int (*callback)(caddr_t), caddr_t arg,
ddi_dma_attr_t *cur_dma_attr)
{
int rval;
off_t offset;
size_t size;
int max_sg_len, req_len, i;
uint_t dma_flags;
struct buf *bp;
uint64_t cur_txfer_len;
ASSERT(spx->txlt_sata_pkt != NULL);
bp = spx->txlt_sata_pkt->satapkt_cmd.satacmd_bp;
ASSERT(bp != NULL);
if (spx->txlt_buf_dma_handle == NULL) {
rval = ddi_dma_alloc_handle(SATA_DIP(spx->txlt_sata_hba_inst),
cur_dma_attr, callback, arg, &spx->txlt_buf_dma_handle);
if (rval != DDI_SUCCESS) {
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_dma_buf_setup: no buf DMA resources %x",
rval));
return (rval);
}
if (bp->b_flags & B_READ)
dma_flags = DDI_DMA_READ;
else
dma_flags = DDI_DMA_WRITE;
if (flags & PKT_CONSISTENT)
dma_flags |= DDI_DMA_CONSISTENT;
if (flags & PKT_DMA_PARTIAL)
dma_flags |= DDI_DMA_PARTIAL;
if (IS_P2ALIGNED(bp->b_un.b_addr,
cur_dma_attr->dma_attr_align)) {
rval = ddi_dma_buf_bind_handle(
spx->txlt_buf_dma_handle,
bp, dma_flags, callback, arg,
&spx->txlt_dma_cookie,
&spx->txlt_curwin_num_dma_cookies);
} else {
int (*ddicallback)(caddr_t);
size_t bufsz;
ddicallback = (callback == NULL_FUNC) ?
DDI_DMA_DONTWAIT : DDI_DMA_SLEEP;
SATADBG2(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst,
"mis-aligned buffer: addr=0x%p, cnt=%lu",
(void *)bp->b_un.b_addr, bp->b_bcount);
if (bp->b_flags & (B_PAGEIO|B_PHYS))
bp_mapin(bp);
ASSERT(spx->txlt_tmp_buf == NULL);
rval = ddi_dma_mem_alloc(
spx->txlt_buf_dma_handle,
bp->b_bcount,
&sata_acc_attr,
DDI_DMA_STREAMING,
ddicallback, NULL,
&spx->txlt_tmp_buf,
&bufsz,
&spx->txlt_tmp_buf_handle);
if (rval != DDI_SUCCESS) {
(void) ddi_dma_free_handle(
&spx->txlt_buf_dma_handle);
spx->txlt_buf_dma_handle = NULL;
#ifdef SATA_DEBUG
mbuffail_count++;
#endif
SATADBG1(SATA_DBG_DMA_SETUP,
spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: "
"buf dma mem alloc failed %x\n", rval);
return (rval);
}
ASSERT(IS_P2ALIGNED(spx->txlt_tmp_buf,
cur_dma_attr->dma_attr_align));
#ifdef SATA_DEBUG
mbuf_count++;
if (bp->b_bcount != bufsz)
SATADBG2(SATA_DBG_DMA_SETUP,
spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: bp size %x != "
"bufsz %x\n", bp->b_bcount, bufsz);
#endif
if (dma_flags & DDI_DMA_WRITE) {
bcopy(bp->b_un.b_addr, spx->txlt_tmp_buf,
bp->b_bcount);
}
rval = ddi_dma_addr_bind_handle(
spx->txlt_buf_dma_handle,
NULL,
spx->txlt_tmp_buf,
bufsz, dma_flags, ddicallback, 0,
&spx->txlt_dma_cookie,
&spx->txlt_curwin_num_dma_cookies);
}
switch (rval) {
case DDI_DMA_PARTIAL_MAP:
SATADBG1(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: DMA Partial Map\n", NULL);
if (ddi_dma_numwin(spx->txlt_buf_dma_handle,
&spx->txlt_num_dma_win) != DDI_SUCCESS) {
if (spx->txlt_tmp_buf != NULL) {
ddi_dma_mem_free(
&spx->txlt_tmp_buf_handle);
spx->txlt_tmp_buf = NULL;
}
(void) ddi_dma_unbind_handle(
spx->txlt_buf_dma_handle);
(void) ddi_dma_free_handle(
&spx->txlt_buf_dma_handle);
spx->txlt_buf_dma_handle = NULL;
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_dma_buf_setup: numwin failed\n"));
return (DDI_FAILURE);
}
SATADBG2(SATA_DBG_DMA_SETUP,
spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: windows: %d, cookies: %d\n",
spx->txlt_num_dma_win,
spx->txlt_curwin_num_dma_cookies);
spx->txlt_cur_dma_win = 0;
break;
case DDI_DMA_MAPPED:
spx->txlt_num_dma_win = 1;
spx->txlt_cur_dma_win = 0;
SATADBG1(SATA_DBG_DMA_SETUP,
spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: windows: 1 "
"cookies: %d\n", spx->txlt_curwin_num_dma_cookies);
break;
default:
if (spx->txlt_tmp_buf != NULL) {
ddi_dma_mem_free(
&spx->txlt_tmp_buf_handle);
spx->txlt_tmp_buf = NULL;
}
(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
spx->txlt_buf_dma_handle = NULL;
SATA_LOG_D((spx->txlt_sata_hba_inst, CE_WARN,
"sata_dma_buf_setup: buf dma handle binding "
"failed %x\n", rval));
return (rval);
}
spx->txlt_curwin_processed_dma_cookies = 0;
spx->txlt_dma_cookie_list = NULL;
} else {
ASSERT(spx->txlt_curwin_processed_dma_cookies <=
spx->txlt_curwin_num_dma_cookies);
if (spx->txlt_curwin_processed_dma_cookies ==
spx->txlt_curwin_num_dma_cookies) {
spx->txlt_cur_dma_win++;
if (spx->txlt_cur_dma_win < spx->txlt_num_dma_win) {
(void) ddi_dma_getwin(spx->txlt_buf_dma_handle,
spx->txlt_cur_dma_win, &offset, &size,
&spx->txlt_dma_cookie,
&spx->txlt_curwin_num_dma_cookies);
spx->txlt_curwin_processed_dma_cookies = 0;
} else {
ASSERT(spx->txlt_cur_dma_win >=
spx->txlt_num_dma_win);
spx->txlt_curwin_num_dma_cookies = 0;
spx->txlt_curwin_processed_dma_cookies = 0;
spx->txlt_sata_pkt->
satapkt_cmd.satacmd_num_dma_cookies = 0;
return (DDI_SUCCESS);
}
}
}
ASSERT((spx->txlt_curwin_num_dma_cookies -
spx->txlt_curwin_processed_dma_cookies) > 0);
if (spx->txlt_dma_cookie_list == &spx->txlt_dma_cookie) {
ASSERT(spx->txlt_curwin_processed_dma_cookies == 0);
spx->txlt_dma_cookie_list = NULL;
spx->txlt_dma_cookie_list_len = 0;
}
if (spx->txlt_curwin_processed_dma_cookies == 0) {
if (spx->txlt_dma_cookie_list != NULL &&
spx->txlt_dma_cookie_list_len <
spx->txlt_curwin_num_dma_cookies) {
(void) kmem_free(spx->txlt_dma_cookie_list,
spx->txlt_dma_cookie_list_len *
sizeof (ddi_dma_cookie_t));
spx->txlt_dma_cookie_list = NULL;
spx->txlt_dma_cookie_list_len = 0;
}
if (spx->txlt_dma_cookie_list == NULL) {
max_sg_len = cur_dma_attr->dma_attr_sgllen;
req_len = MIN(max_sg_len,
spx->txlt_curwin_num_dma_cookies);
if (req_len == 1) {
spx->txlt_dma_cookie_list =
&spx->txlt_dma_cookie;
spx->txlt_dma_cookie_list_len = 1;
} else {
spx->txlt_dma_cookie_list = kmem_zalloc(
sizeof (ddi_dma_cookie_t) * req_len,
callback == NULL_FUNC ? KM_NOSLEEP :
KM_SLEEP);
if (spx->txlt_dma_cookie_list == NULL) {
SATADBG1(SATA_DBG_DMA_SETUP,
spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: cookie list "
"allocation failed\n", NULL);
return (DDI_DMA_NORESOURCES);
} else {
spx->txlt_dma_cookie_list_len = req_len;
}
}
}
*(&spx->txlt_dma_cookie_list[0]) = spx->txlt_dma_cookie;
cur_txfer_len =
(uint64_t)spx->txlt_dma_cookie_list[0].dmac_size;
spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies = 1;
spx->txlt_curwin_processed_dma_cookies++;
for (i = 1; (i < spx->txlt_dma_cookie_list_len) &&
(i < spx->txlt_curwin_num_dma_cookies); i++) {
ddi_dma_nextcookie(spx->txlt_buf_dma_handle,
&spx->txlt_dma_cookie_list[i]);
cur_txfer_len +=
(uint64_t)spx->txlt_dma_cookie_list[i].dmac_size;
spx->txlt_curwin_processed_dma_cookies++;
spx->txlt_sata_pkt->
satapkt_cmd.satacmd_num_dma_cookies += 1;
}
} else {
SATADBG2(SATA_DBG_DMA_SETUP, spx->txlt_sata_hba_inst,
"sata_dma_buf_setup: sliding within DMA window, "
"cur cookie %d, total cookies %d\n",
spx->txlt_curwin_processed_dma_cookies,
spx->txlt_curwin_num_dma_cookies);
if (spx->txlt_dma_cookie_list == NULL) {
spx->txlt_dma_cookie_list = &spx->txlt_dma_cookie;
spx->txlt_dma_cookie_list_len = 1;
}
req_len = MIN(spx->txlt_dma_cookie_list_len,
(spx->txlt_curwin_num_dma_cookies -
spx->txlt_curwin_processed_dma_cookies));
for (i = 0, cur_txfer_len = 0; i < req_len; i++) {
ddi_dma_nextcookie(spx->txlt_buf_dma_handle,
&spx->txlt_dma_cookie_list[i]);
cur_txfer_len +=
(uint64_t)spx->txlt_dma_cookie_list[i].dmac_size;
spx->txlt_sata_pkt->
satapkt_cmd.satacmd_num_dma_cookies++;
spx->txlt_curwin_processed_dma_cookies++;
}
}
ASSERT(spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies > 0);
spx->txlt_sata_pkt->satapkt_cmd.satacmd_dma_cookie_list =
&spx->txlt_dma_cookie_list[0];
spx->txlt_num_dma_cookies =
spx->txlt_sata_pkt->satapkt_cmd.satacmd_num_dma_cookies;
ASSERT(cur_txfer_len != 0);
if (cur_txfer_len <= bp->b_bcount)
spx->txlt_total_residue -= cur_txfer_len;
else {
spx->txlt_total_residue -= bp->b_bcount;
}
return (DDI_SUCCESS);
}
static void
sata_common_free_dma_rsrcs(sata_pkt_txlate_t *spx)
{
if (spx->txlt_buf_dma_handle != NULL) {
if (spx->txlt_tmp_buf != NULL) {
ddi_dma_mem_free(&spx->txlt_tmp_buf_handle);
spx->txlt_tmp_buf = NULL;
}
if (spx->txlt_dma_cookie_list != NULL) {
if (spx->txlt_dma_cookie_list !=
&spx->txlt_dma_cookie) {
(void) kmem_free(spx->txlt_dma_cookie_list,
spx->txlt_dma_cookie_list_len *
sizeof (ddi_dma_cookie_t));
spx->txlt_dma_cookie_list = NULL;
}
}
(void) ddi_dma_unbind_handle(spx->txlt_buf_dma_handle);
(void) ddi_dma_free_handle(&spx->txlt_buf_dma_handle);
spx->txlt_buf_dma_handle = NULL;
}
}
void
sata_free_dma_resources(sata_pkt_t *sata_pkt)
{
sata_pkt_txlate_t *spx;
if (sata_pkt == NULL)
return;
spx = (sata_pkt_txlate_t *)sata_pkt->satapkt_framework_private;
sata_common_free_dma_rsrcs(spx);
}
static int
sata_fetch_device_identify_data(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo)
{
struct buf *bp;
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval;
dev_info_t *dip = SATA_DIP(sata_hba_inst);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (SATA_RETRY);
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
bp = sata_alloc_local_buffer(spx, sizeof (sata_id_t));
if (bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_fetch_device_identify_data: "
"cannot allocate buffer for ID"));
return (SATA_RETRY);
}
sdinfo->satadrv_state = SATA_STATE_PROBING;
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_bp = bp;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = 0;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_features_reg = 0;
scmd->satacmd_device_reg = 0;
if (sdinfo->satadrv_type & SATA_DTYPE_ATAPI) {
scmd->satacmd_cmd_reg = SATAC_ID_PACKET_DEVICE;
} else {
scmd->satacmd_cmd_reg = SATAC_ID_DEVICE;
}
rval = (*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt);
#ifdef SATA_INJECT_FAULTS
sata_inject_pkt_fault(spkt, &rval, sata_fault_type);
#endif
if (rval == SATA_TRAN_ACCEPTED &&
spkt->satapkt_reason == SATA_PKT_COMPLETED) {
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ASSERT(rval == DDI_SUCCESS);
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
rval = SATA_RETRY;
goto fail;
}
}
if ((((sata_id_t *)(bp->b_un.b_addr))->ai_config &
SATA_INCOMPLETE_DATA) == SATA_INCOMPLETE_DATA) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA disk device at port %d - "
"partial Identify Data",
sdinfo->satadrv_addr.cport));
rval = SATA_RETRY;
goto fail;
}
bcopy(bp->b_un.b_addr, &sdinfo->satadrv_id,
sizeof (sata_id_t));
sdinfo->satadrv_features_support = 0;
if (sdinfo->satadrv_type == SATA_DTYPE_ATADISK) {
sdinfo->satadrv_capacity = sata_check_capacity(sdinfo);
} else {
sdinfo->satadrv_capacity = 0;
if ((sdinfo->satadrv_id.ai_config &
SATA_ATAPI_ID_PKT_SZ) == SATA_ATAPI_ID_PKT_16B)
sdinfo->satadrv_atapi_cdb_len = 16;
else
sdinfo->satadrv_atapi_cdb_len = 12;
}
if (sdinfo->satadrv_id.ai_cap & SATA_DMA_SUPPORT)
sdinfo->satadrv_features_support |= SATA_DEV_F_DMA;
if (sdinfo->satadrv_id.ai_satacap != 0 &&
sdinfo->satadrv_id.ai_satacap != 0xffff) {
if (sdinfo->satadrv_id.ai_satacap & SATA_NCQ)
sdinfo->satadrv_features_support |=
SATA_DEV_F_NCQ;
if (sdinfo->satadrv_id.ai_satacap &
(SATA_1_SPEED | SATA_2_SPEED | SATA_3_SPEED)) {
if (sdinfo->satadrv_id.ai_satacap &
SATA_3_SPEED)
sdinfo->satadrv_features_support |=
SATA_DEV_F_SATA3;
if (sdinfo->satadrv_id.ai_satacap &
SATA_2_SPEED)
sdinfo->satadrv_features_support |=
SATA_DEV_F_SATA2;
if (sdinfo->satadrv_id.ai_satacap &
SATA_1_SPEED)
sdinfo->satadrv_features_support |=
SATA_DEV_F_SATA1;
} else {
sdinfo->satadrv_features_support |=
SATA_DEV_F_SATA1;
}
}
if ((sdinfo->satadrv_id.ai_cmdset83 & SATA_RW_DMA_QUEUED_CMD) &&
(sdinfo->satadrv_id.ai_features86 & SATA_RW_DMA_QUEUED_CMD))
sdinfo->satadrv_features_support |= SATA_DEV_F_TCQ;
sdinfo->satadrv_queue_depth = sdinfo->satadrv_id.ai_qdepth;
if ((sdinfo->satadrv_features_support & SATA_DEV_F_NCQ) ||
(sdinfo->satadrv_features_support & SATA_DEV_F_TCQ)) {
++sdinfo->satadrv_queue_depth;
sdinfo->satadrv_max_queue_depth = MIN(
sdinfo->satadrv_queue_depth,
SATA_QDEPTH(sata_hba_inst));
sdinfo->satadrv_max_queue_depth = MIN(
sdinfo->satadrv_max_queue_depth,
sata_current_max_qdepth);
if (sdinfo->satadrv_max_queue_depth == 0)
sdinfo->satadrv_max_queue_depth = 1;
} else
sdinfo->satadrv_max_queue_depth = 1;
rval = SATA_SUCCESS;
} else {
if (rval == SATA_TRAN_BUSY || rval == SATA_TRAN_QUEUE_FULL) {
rval = SATA_RETRY;
} else if (rval == SATA_TRAN_ACCEPTED) {
if (spkt->satapkt_reason == SATA_PKT_DEV_ERROR ||
spkt->satapkt_reason == SATA_PKT_ABORTED ||
spkt->satapkt_reason == SATA_PKT_TIMEOUT ||
spkt->satapkt_reason == SATA_PKT_RESET)
rval = SATA_RETRY;
else
rval = SATA_FAILURE;
} else {
rval = SATA_FAILURE;
}
}
fail:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_set_dma_mode(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int i, mode;
uint8_t subcmd;
int rval = SATA_SUCCESS;
ASSERT(sdinfo != NULL);
ASSERT(sata_hba_inst != NULL);
if ((sdinfo->satadrv_id.ai_validinfo & SATA_VALIDINFO_88) != 0 &&
(sdinfo->satadrv_id.ai_ultradma & SATA_UDMA_SUP_MASK) != 0) {
for (mode = 6; mode >= 0; --mode) {
if (sdinfo->satadrv_id.ai_ultradma & (1 << mode))
break;
}
#if 0
if (mode < 4)
return (SATA_FAILURE);
#endif
if (sdinfo->satadrv_type != SATA_DTYPE_ATADISK) {
for (i = 6; i >= 0; --i) {
if (sdinfo->satadrv_id.ai_ultradma &
(1 << (i + 8)))
break;
}
if (i >= mode)
return (SATA_SUCCESS);
}
subcmd = SATAC_TRANSFER_MODE_ULTRA_DMA;
} else if ((sdinfo->satadrv_id.ai_dworddma & SATA_MDMA_SUP_MASK) != 0) {
for (mode = 2; mode >= 0; --mode) {
if (sdinfo->satadrv_id.ai_dworddma & (1 << mode))
break;
}
if (sdinfo->satadrv_type != SATA_DTYPE_ATADISK) {
for (i = 2; i >= 0; --i) {
if (sdinfo->satadrv_id.ai_dworddma &
(1 << (i + 8)))
break;
}
if (i >= mode)
return (SATA_SUCCESS);
}
subcmd = SATAC_TRANSFER_MODE_MULTI_WORD_DMA;
} else
return (SATA_SUCCESS);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_set_dma_mode: could not set DMA mode %d", mode));
rval = SATA_FAILURE;
goto done;
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_time = sata_default_pkt_time;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SET_FEATURES;
scmd->satacmd_features_reg = SATAC_SF_TRANSFER_MODE;
scmd->satacmd_sec_count_lsb = subcmd | mode;
if ((*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst),
spkt) != SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
rval = SATA_FAILURE;
}
done:
if (spkt != NULL)
sata_pkt_free(spx);
(void) kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_set_cache_mode(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo,
int cache_op)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval = SATA_SUCCESS;
int hba_rval;
char *infop = NULL;
ASSERT(sdinfo != NULL);
ASSERT(sata_hba_inst != NULL);
ASSERT(cache_op == SATAC_SF_ENABLE_READ_AHEAD ||
cache_op == SATAC_SF_DISABLE_READ_AHEAD ||
cache_op == SATAC_SF_ENABLE_WRITE_CACHE ||
cache_op == SATAC_SF_DISABLE_WRITE_CACHE);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
rval = SATA_FAILURE;
goto failure;
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_time = sata_default_pkt_time;
spkt->satapkt_op_mode =
SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SET_FEATURES;
scmd->satacmd_features_reg = cache_op;
hba_rval = (*SATA_START_FUNC(sata_hba_inst))(
SATA_DIP(sata_hba_inst), spkt);
#ifdef SATA_INJECT_FAULTS
sata_inject_pkt_fault(spkt, &rval, sata_fault_type);
#endif
if ((hba_rval != SATA_TRAN_ACCEPTED) ||
(spkt->satapkt_reason != SATA_PKT_COMPLETED)) {
switch (cache_op) {
case SATAC_SF_ENABLE_READ_AHEAD:
infop = "enabling read ahead failed";
break;
case SATAC_SF_DISABLE_READ_AHEAD:
infop = "disabling read ahead failed";
break;
case SATAC_SF_ENABLE_WRITE_CACHE:
infop = "enabling write cache failed";
break;
case SATAC_SF_DISABLE_WRITE_CACHE:
infop = "disabling write cache failed";
break;
}
SATA_LOG_D((sata_hba_inst, CE_WARN, "%s", infop));
rval = SATA_RETRY;
}
failure:
if (spkt != NULL)
sata_pkt_free(spx);
(void) kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_set_rmsn(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo,
int state)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval = SATA_SUCCESS;
char *infop;
ASSERT(sdinfo != NULL);
ASSERT(sata_hba_inst != NULL);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
rval = SATA_FAILURE;
goto failure;
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_time = sata_default_pkt_time;
spkt->satapkt_op_mode =
SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
scmd->satacmd_addr_type = 0;
scmd->satacmd_device_reg = 0;
scmd->satacmd_status_reg = 0;
scmd->satacmd_error_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SET_FEATURES;
if (state == 0)
scmd->satacmd_features_reg = SATAC_SF_DISABLE_RMSN;
else
scmd->satacmd_features_reg = SATAC_SF_ENABLE_RMSN;
if (((*SATA_START_FUNC(sata_hba_inst))(
SATA_DIP(sata_hba_inst), spkt) != SATA_TRAN_ACCEPTED) ||
(spkt->satapkt_reason != SATA_PKT_COMPLETED)) {
if (state == 0)
infop = "disabling Removable Media Status "
"Notification failed";
else
infop = "enabling Removable Media Status "
"Notification failed";
SATA_LOG_D((sata_hba_inst, CE_WARN, "%s", infop));
rval = SATA_FAILURE;
}
failure:
if (spkt != NULL)
sata_pkt_free(spx);
(void) kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static void
sata_update_port_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_cport_info_t *cportinfo;
if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT ||
sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) {
if (SATA_NUM_CPORTS(sata_hba_inst) <=
sata_device->satadev_addr.cport)
return;
cportinfo = SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
ASSERT(mutex_owned(&cportinfo->cport_mutex));
cportinfo->cport_scr = sata_device->satadev_scr;
cportinfo->cport_state &= ~(SATA_PSTATE_PWRON |
SATA_PSTATE_PWROFF | SATA_PSTATE_FAILED);
cportinfo->cport_state |=
sata_device->satadev_state & SATA_PSTATE_VALID;
}
}
void
sata_update_pmport_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_pmport_info_t *pmportinfo;
if ((sata_device->satadev_addr.qual != SATA_ADDR_PMPORT &&
sata_device->satadev_addr.qual != SATA_ADDR_DPMPORT) ||
SATA_NUM_PMPORTS(sata_hba_inst,
sata_device->satadev_addr.cport) <
sata_device->satadev_addr.pmport) {
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"sata_update_port_info: error address %p.",
&sata_device->satadev_addr);
return;
}
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
ASSERT(mutex_owned(&pmportinfo->pmport_mutex));
pmportinfo->pmport_scr = sata_device->satadev_scr;
pmportinfo->pmport_state &=
~(SATA_PSTATE_PWRON | SATA_PSTATE_PWROFF | SATA_PSTATE_FAILED);
pmportinfo->pmport_state |=
sata_device->satadev_state & SATA_PSTATE_VALID;
}
static int32_t
sata_get_port_num(sata_hba_inst_t *sata_hba_inst, struct devctl_iocdata *dcp)
{
int32_t port;
if (nvlist_lookup_int32(ndi_dc_get_ap_data(dcp), "port", &port) != 0) {
SATA_LOG_D((sata_hba_inst, CE_NOTE,
"sata_get_port_num: invalid port spec 0x%x in ioctl",
port));
port = -1;
}
return (port);
}
static dev_info_t *
sata_get_target_dip(dev_info_t *dip, uint8_t cport, uint8_t pmport)
{
dev_info_t *cdip = NULL;
int target, tgt;
uint8_t qual;
sata_hba_inst_t *sata_hba_inst;
scsi_hba_tran_t *scsi_hba_tran;
scsi_hba_tran = ddi_get_driver_private(dip);
if (scsi_hba_tran == NULL)
return (NULL);
sata_hba_inst = scsi_hba_tran->tran_hba_private;
if (sata_hba_inst == NULL)
return (NULL);
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT)
qual = SATA_ADDR_DPMPORT;
else
qual = SATA_ADDR_DCPORT;
target = SATA_TO_SCSI_TARGET(cport, pmport, qual);
ndi_devi_enter(dip);
for (cdip = ddi_get_child(dip); cdip != NULL; ) {
dev_info_t *next = ddi_get_next_sibling(cdip);
tgt = ddi_prop_get_int(DDI_DEV_T_ANY, cdip,
DDI_PROP_DONTPASS, "target", -1);
if (tgt == -1) {
cdip = next;
continue;
}
if (tgt == target)
break;
cdip = next;
}
ndi_devi_exit(dip);
return (cdip);
}
static dev_info_t *
sata_get_scsi_target_dip(dev_info_t *dip, sata_address_t *saddr)
{
dev_info_t *cdip = NULL;
int target, tgt;
target = SATA_TO_SCSI_TARGET(saddr->cport, saddr->pmport, saddr->qual);
ndi_devi_enter(dip);
for (cdip = ddi_get_child(dip); cdip != NULL; ) {
dev_info_t *next = ddi_get_next_sibling(cdip);
tgt = ddi_prop_get_int(DDI_DEV_T_ANY, cdip,
DDI_PROP_DONTPASS, "target", -1);
if (tgt == -1) {
cdip = next;
continue;
}
if (tgt == target)
break;
cdip = next;
}
ndi_devi_exit(dip);
return (cdip);
}
static int
sata_ioctl_disconnect(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_drive_info_t *sdinfo = NULL, *subsdinfo = NULL;
sata_cport_info_t *cportinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_pmult_info_t *pmultinfo = NULL;
sata_device_t subsdevice;
int cport, pmport, qual;
int rval = SATA_SUCCESS;
int npmport = 0;
int rv = 0;
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT);
if (qual == SATA_ADDR_DCPORT)
qual = SATA_ADDR_CPORT;
else
qual = SATA_ADDR_PMPORT;
if (SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst) == NULL) {
return (EINVAL);
}
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
if (qual == SATA_ADDR_CPORT &&
SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT) {
mutex_enter(&cportinfo->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
(sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) {
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: connect: failed to deactivate "
"SATA port %d", cport);
mutex_exit(&cportinfo->cport_mutex);
return (EIO);
}
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
if (pmultinfo != NULL) {
for (npmport = 0; npmport < SATA_NUM_PMPORTS(
sata_hba_inst, cport); npmport ++) {
subsdinfo = SATA_PMPORT_DRV_INFO(
sata_hba_inst, cport, npmport);
if (subsdinfo == NULL)
continue;
subsdevice.satadev_addr = subsdinfo->
satadrv_addr;
mutex_exit(&cportinfo->cport_mutex);
if (sata_ioctl_disconnect(sata_hba_inst,
&subsdevice) == SATA_SUCCESS) {
SATADBG2(SATA_DBG_PMULT, sata_hba_inst,
"[Remove] device at port %d:%d "
"successfully.", cport, npmport);
}
mutex_enter(&cportinfo->cport_mutex);
}
}
cportinfo->cport_state &= ~SATA_STATE_READY;
mutex_exit(&cportinfo->cport_mutex);
sata_device->satadev_addr.qual = qual;
rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
mutex_enter(&cportinfo->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS &&
sata_device->satadev_state & SATA_PSTATE_FAILED) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
rv = EIO;
} else {
cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN;
}
mutex_exit(&cportinfo->cport_mutex);
return (rv);
}
if (qual == SATA_ADDR_PMPORT) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
(sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) {
SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) =
SATA_PSTATE_FAILED;
SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: connect: failed to deactivate "
"SATA port %d:%d", cport, pmport);
mutex_exit(&pmportinfo->pmport_mutex);
return (EIO);
}
if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE) {
sdinfo = pmportinfo->pmport_sata_drive;
ASSERT(sdinfo != NULL);
}
pmportinfo->pmport_state &= ~SATA_STATE_READY;
if (sdinfo != NULL) {
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
mutex_exit(&pmportinfo->pmport_mutex);
(void) sata_offline_device(sata_hba_inst,
sata_device, sdinfo);
mutex_enter(&pmportinfo->pmport_mutex);
}
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
(void) kmem_free((void *)sdinfo,
sizeof (sata_drive_info_t));
}
mutex_exit(&pmportinfo->pmport_mutex);
} else if (qual == SATA_ADDR_CPORT) {
mutex_enter(&cportinfo->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
(sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) {
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: connect: failed to deactivate "
"SATA port %d", cport);
mutex_exit(&cportinfo->cport_mutex);
return (EIO);
}
if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) {
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
ASSERT(pmultinfo != NULL);
} else if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) {
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
ASSERT(sdinfo != NULL);
}
cportinfo->cport_state &= ~SATA_STATE_READY;
if (sdinfo != NULL) {
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
mutex_exit(&cportinfo->cport_mutex);
(void) sata_offline_device(sata_hba_inst,
sata_device, sdinfo);
mutex_enter(&cportinfo->cport_mutex);
}
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
(void) kmem_free((void *)sdinfo,
sizeof (sata_drive_info_t));
}
mutex_exit(&cportinfo->cport_mutex);
}
sata_device->satadev_addr.qual = qual;
rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
if (qual == SATA_ADDR_PMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS &&
sata_device->satadev_state & SATA_PSTATE_FAILED) {
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
rv = EIO;
} else {
pmportinfo->pmport_state |= SATA_PSTATE_SHUTDOWN;
}
mutex_exit(&pmportinfo->pmport_mutex);
} else if (qual == SATA_ADDR_CPORT) {
mutex_enter(&cportinfo->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS &&
sata_device->satadev_state & SATA_PSTATE_FAILED) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
rv = EIO;
} else {
cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN;
}
mutex_exit(&cportinfo->cport_mutex);
}
return (rv);
}
static int
sata_ioctl_connect(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_pmport_info_t *pmportinfo = NULL;
uint8_t cport, pmport, qual;
int rv = 0;
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT);
if (qual == SATA_ADDR_DCPORT)
qual = SATA_ADDR_CPORT;
else
qual = SATA_ADDR_PMPORT;
if (qual == SATA_ADDR_PMPORT)
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
if (SATA_PORT_ACTIVATE_FUNC(sata_hba_inst) == NULL) {
return (EINVAL);
}
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device) != SATA_SUCCESS) {
if (qual == SATA_ADDR_CPORT) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: connect: failed to "
"activate SATA port %d", cport);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
} else {
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
SATA_PMPORT_STATE(sata_hba_inst, cport,
pmport) = SATA_PSTATE_FAILED;
SATADBG2(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: connect: failed to "
"activate SATA port %d:%d", cport, pmport);
}
mutex_exit(&pmportinfo->pmport_mutex);
}
return (EIO);
}
if (qual == SATA_ADDR_CPORT) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
SATA_CPORT_STATE(sata_hba_inst, cport) = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
} else {
mutex_enter(&pmportinfo->pmport_mutex);
SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) = 0;
mutex_exit(&pmportinfo->pmport_mutex);
}
if (sata_reprobe_port(sata_hba_inst, sata_device,
SATA_DEV_IDENTIFY_RETRY) == SATA_FAILURE)
rv = EIO;
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
if (sata_device->satadev_type != SATA_DTYPE_NONE) {
if (qual == SATA_ADDR_CPORT) {
if (sata_device->satadev_type == SATA_DTYPE_PMULT) {
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detected "
"at port %d", cport);
} else {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d", cport);
if (sata_device->satadev_type ==
SATA_DTYPE_UNKNOWN) {
sata_log(sata_hba_inst, CE_WARN,
"Could not identify SATA "
"device at port %d", cport);
}
}
} else {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d:%d",
cport, pmport);
if (sata_device->satadev_type == SATA_DTYPE_UNKNOWN) {
sata_log(sata_hba_inst, CE_WARN,
"Could not identify SATA "
"device at port %d:%d", cport, pmport);
}
}
}
return (rv);
}
static int
sata_ioctl_unconfigure(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int rv = 0;
dev_info_t *tdip;
if (sata_device->satadev_addr.qual == SATA_ADDR_CPORT)
sata_device->satadev_addr.qual = SATA_ADDR_DCPORT;
else if (sata_device->satadev_addr.qual == SATA_ADDR_PMPORT)
sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT;
if ((tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&sata_device->satadev_addr)) != NULL) {
if (ndi_devi_offline(tdip, NDI_UNCONFIG) != NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: unconfigure: "
"failed to unconfigure device at SATA port %d:%d",
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport));
rv = EIO;
}
} else {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: unconfigure: "
"attempt to unconfigure non-existing device "
"at SATA port %d:%d",
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport));
rv = ENXIO;
}
return (rv);
}
static int
sata_ioctl_configure(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int cport, pmport, qual;
int rval;
boolean_t target = B_TRUE;
sata_cport_info_t *cportinfo;
sata_pmport_info_t *pmportinfo = NULL;
dev_info_t *tdip;
sata_drive_info_t *sdinfo;
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
if (qual == SATA_ADDR_DPMPORT) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
(sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) {
mutex_exit(&pmportinfo->pmport_mutex);
return (ENXIO);
}
mutex_exit(&pmportinfo->pmport_mutex);
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS ||
(sata_device->satadev_state & SATA_PSTATE_FAILED) != 0) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
return (ENXIO);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
}
if ((sata_device->satadev_state & SATA_PSTATE_SHUTDOWN) != 0) {
target = B_FALSE;
if (SATA_PORT_ACTIVATE_FUNC(sata_hba_inst) == NULL)
return (ENXIO);
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device) != SATA_SUCCESS) {
if (qual == SATA_ADDR_DPMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst,
sata_device);
if (sata_device->satadev_state &
SATA_PSTATE_FAILED)
pmportinfo->pmport_state =
SATA_PSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
sata_update_port_info(sata_hba_inst,
sata_device);
if (sata_device->satadev_state &
SATA_PSTATE_FAILED)
cportinfo->cport_state =
SATA_PSTATE_FAILED;
mutex_exit(&SATA_CPORT_INFO(
sata_hba_inst, cport)->cport_mutex);
}
}
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: configure: "
"failed to activate SATA port %d:%d",
cport, pmport));
return (EIO);
}
sata_gen_sysevent(sata_hba_inst,
&sata_device->satadev_addr, SE_NO_HINT);
if (qual == SATA_ADDR_DPMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state = 0;
mutex_exit(&pmportinfo->pmport_mutex);
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)-> cport_mutex);
cportinfo->cport_state = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
}
if (sata_reprobe_port(sata_hba_inst, sata_device,
SATA_DEV_IDENTIFY_RETRY) != SATA_SUCCESS)
return (EIO);
if (sata_device->satadev_type != SATA_DTYPE_NONE && target == B_FALSE) {
if (qual == SATA_ADDR_DPMPORT) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d:%d",
cport, pmport);
} else {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d", cport);
}
}
if (!(sata_device->satadev_type & SATA_VALID_DEV_TYPE)) {
if (qual == SATA_ADDR_DCPORT) {
if (sata_device->satadev_type == SATA_DTYPE_UNKNOWN) {
sata_log(sata_hba_inst, CE_WARN,
"Could not identify SATA "
"device at port %d", cport);
}
} else {
if (sata_device->satadev_type == SATA_DTYPE_UNKNOWN) {
sata_log(sata_hba_inst, CE_WARN,
"Could not identify SATA "
"device at port %d:%d", cport, pmport);
}
}
return (ENXIO);
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (qual == SATA_ADDR_DPMPORT)
sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT;
else
sata_device->satadev_addr.qual = SATA_ADDR_DCPORT;
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo == NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
return (ENXIO);
}
if (sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) {
sdinfo->satadrv_event_flags = 0;
}
sdinfo->satadrv_event_flags |= SATA_EVNT_CLEAR_DEVICE_RESET;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if ((tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&sata_device->satadev_addr)) != NULL) {
if (sata_check_device_removed(tdip) == B_TRUE) {
if (qual == SATA_ADDR_DPMPORT)
sata_log(sata_hba_inst, CE_WARN,
"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_log(sata_hba_inst, CE_WARN,
"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.",
cport, pmport);
return (EIO);
}
if (ndi_devi_online(tdip, 0) != NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: configure: "
"onlining device at SATA port "
"%d:%d failed", cport, pmport));
return (EIO);
}
if (qual == SATA_ADDR_DPMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_tgtnode_clean = B_TRUE;
mutex_exit(&pmportinfo->pmport_mutex);
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
cportinfo-> cport_tgtnode_clean = B_TRUE;
mutex_exit(&SATA_CPORT_INFO(
sata_hba_inst, cport)->cport_mutex);
}
} else {
if (qual == SATA_ADDR_DPMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_tgtnode_clean = B_TRUE;
mutex_exit(&pmportinfo->pmport_mutex);
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
cportinfo-> cport_tgtnode_clean = B_TRUE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
}
tdip = sata_create_target_node(SATA_DIP(sata_hba_inst),
sata_hba_inst, &sata_device->satadev_addr);
if (tdip == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: configure: "
"configuring SATA device at port %d:%d "
"failed", cport, pmport));
return (EIO);
}
}
return (0);
}
static int
sata_ioctl_deactivate(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int cport, pmport, qual;
int rval, rv = 0;
int npmport;
sata_cport_info_t *cportinfo;
sata_pmport_info_t *pmportinfo;
sata_pmult_info_t *pmultinfo;
dev_info_t *tdip;
sata_drive_info_t *sdinfo = NULL;
sata_device_t subsdevice;
if (SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst) == NULL)
return (ENOTSUP);
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT);
if (qual == SATA_ADDR_DCPORT)
qual = SATA_ADDR_CPORT;
else
qual = SATA_ADDR_PMPORT;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
if (qual == SATA_ADDR_PMPORT)
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
if (qual == SATA_ADDR_CPORT &&
SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) == SATA_DTYPE_PMULT) {
mutex_enter(&cportinfo->cport_mutex);
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
if (pmultinfo != NULL) {
for (npmport = 0; npmport < SATA_NUM_PMPORTS(
sata_hba_inst, cport); npmport++) {
subsdevice.satadev_addr.cport = cport;
subsdevice.satadev_addr.pmport =
(uint8_t)npmport;
subsdevice.satadev_addr.qual =
SATA_ADDR_DPMPORT;
SATADBG2(SATA_DBG_PMULT, sata_hba_inst,
"sata_hba_ioctl: deactivate: trying to "
"deactivate SATA port %d:%d",
cport, npmport);
mutex_exit(&cportinfo->cport_mutex);
if (sata_ioctl_deactivate(sata_hba_inst,
&subsdevice) == SATA_SUCCESS) {
SATADBG2(SATA_DBG_PMULT, sata_hba_inst,
"[Deactivate] device at port %d:%d "
"successfully.", cport, npmport);
}
mutex_enter(&cportinfo->cport_mutex);
}
}
cportinfo->cport_state &= ~SATA_STATE_READY;
mutex_exit(&cportinfo->cport_mutex);
sata_device->satadev_addr.qual = qual;
rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
mutex_enter(&cportinfo->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (rval != SATA_SUCCESS) {
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
}
rv = EIO;
} else {
cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN;
}
mutex_exit(&cportinfo->cport_mutex);
return (rv);
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (qual == SATA_ADDR_CPORT) {
sata_device->satadev_addr.qual = SATA_ADDR_DCPORT;
if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) {
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) != 0)
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
}
cportinfo->cport_state &= ~SATA_STATE_READY;
} else {
mutex_enter(&pmportinfo->pmport_mutex);
sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT;
if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE &&
(pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) != 0)
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
pmportinfo->pmport_state &= ~SATA_STATE_READY;
mutex_exit(&pmportinfo->pmport_mutex);
}
if (sdinfo != NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&sata_device->satadev_addr);
if (tdip != NULL) {
SATADBG1(SATA_DBG_IOCTL_IF, sata_hba_inst,
"sata_hba_ioctl: port deactivate: "
"target node exists.", NULL);
if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) !=
NDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: port deactivate: "
"failed to unconfigure device at port "
"%d:%d before deactivating the port",
cport, pmport));
sata_set_device_removed(tdip);
sata_set_target_node_cleanup(sata_hba_inst,
&sata_device->satadev_addr);
}
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
if (qual == SATA_ADDR_CPORT) {
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
} else {
mutex_enter(&pmportinfo->pmport_mutex);
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&pmportinfo->pmport_mutex);
}
(void) kmem_free((void *)sdinfo, sizeof (sata_drive_info_t));
}
if (qual == SATA_ADDR_CPORT) {
cportinfo->cport_state &= ~(SATA_STATE_PROBED |
SATA_STATE_PROBING);
} else if (qual == SATA_ADDR_PMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state &= ~(SATA_STATE_PROBED |
SATA_STATE_PROBING);
mutex_exit(&pmportinfo->pmport_mutex);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
sata_device->satadev_addr.qual = qual;
rval = (*SATA_PORT_DEACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device);
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (qual == SATA_ADDR_CPORT) {
if (rval != SATA_SUCCESS) {
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
}
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: port deactivate: "
"cannot deactivate SATA port %d", cport));
rv = EIO;
} else {
cportinfo->cport_state |= SATA_PSTATE_SHUTDOWN;
}
} else {
mutex_enter(&pmportinfo->pmport_mutex);
if (rval != SATA_SUCCESS) {
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
SATA_PMPORT_STATE(sata_hba_inst, cport,
pmport) = SATA_PSTATE_FAILED;
}
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: port deactivate: "
"cannot deactivate SATA port %d:%d",
cport, pmport));
rv = EIO;
} else {
pmportinfo->pmport_state |= SATA_PSTATE_SHUTDOWN;
}
mutex_exit(&pmportinfo->pmport_mutex);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
return (rv);
}
static int
sata_ioctl_activate(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int cport, pmport, qual;
sata_cport_info_t *cportinfo;
sata_pmport_info_t *pmportinfo = NULL;
boolean_t dev_existed = B_TRUE;
if (SATA_PORT_ACTIVATE_FUNC(sata_hba_inst) == NULL)
return (ENOTSUP);
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
ASSERT(qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_DPMPORT);
if (qual == SATA_ADDR_DCPORT)
sata_device->satadev_addr.qual = qual = SATA_ADDR_CPORT;
else
sata_device->satadev_addr.qual = qual = SATA_ADDR_PMPORT;
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (qual == SATA_ADDR_PMPORT) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
if (pmportinfo->pmport_state & SATA_PSTATE_SHUTDOWN ||
pmportinfo->pmport_dev_type == SATA_DTYPE_NONE)
dev_existed = B_FALSE;
} else {
if (cportinfo->cport_state & SATA_PSTATE_SHUTDOWN ||
cportinfo->cport_dev_type == SATA_DTYPE_NONE)
dev_existed = B_FALSE;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if ((*SATA_PORT_ACTIVATE_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device) != SATA_SUCCESS) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (sata_device->satadev_state & SATA_PSTATE_FAILED) {
if (qual == SATA_ADDR_PMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
mutex_exit(&pmportinfo->pmport_mutex);
} else
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
cport)->cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: port activate: cannot activate "
"SATA port %d:%d", cport, pmport));
return (EIO);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (qual == SATA_ADDR_PMPORT) {
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state &= ~SATA_PSTATE_SHUTDOWN;
mutex_exit(&pmportinfo->pmport_mutex);
} else
cportinfo->cport_state &= ~SATA_PSTATE_SHUTDOWN;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
(void) sata_reprobe_port(sata_hba_inst, sata_device,
SATA_DEV_IDENTIFY_RETRY);
sata_gen_sysevent(sata_hba_inst, &sata_device->satadev_addr,
SE_NO_HINT);
if (dev_existed == B_FALSE) {
if (qual == SATA_ADDR_PMPORT &&
pmportinfo->pmport_dev_type != SATA_DTYPE_NONE) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d:%d",
cport, pmport);
} else if (qual == SATA_ADDR_CPORT &&
cportinfo->cport_dev_type != SATA_DTYPE_NONE) {
if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d", cport);
} else {
sata_log(sata_hba_inst, CE_WARN,
"SATA port multiplier detected at port %d",
cport);
}
}
}
return (0);
}
static int
sata_ioctl_reset_port(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int cport, pmport, qual;
int rv = 0;
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
if (qual == SATA_ADDR_DCPORT)
sata_device->satadev_addr.qual = qual = SATA_ADDR_CPORT;
else
sata_device->satadev_addr.qual = qual = SATA_ADDR_PMPORT;
ASSERT(qual == SATA_ADDR_CPORT || qual == SATA_ADDR_PMPORT);
if (SATA_RESET_DPORT_FUNC(sata_hba_inst) == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: sata_hba_tran missing required "
"function sata_tran_reset_dport"));
return (ENOTSUP);
}
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst),
sata_device) != SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: reset port: failed %d:%d",
cport, pmport));
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (qual == SATA_ADDR_CPORT)
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
else {
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport,
pmport));
SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) =
SATA_PSTATE_FAILED;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport,
pmport));
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
rv = EIO;
}
return (rv);
}
static int
sata_ioctl_reset_device(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
sata_drive_info_t *sdinfo = NULL;
sata_pmult_info_t *pmultinfo = NULL;
int cport, pmport;
int rv = 0;
if (SATA_RESET_DPORT_FUNC(sata_hba_inst) == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: sata_hba_tran missing required "
"function sata_tran_reset_dport"));
return (ENOTSUP);
}
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT) {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) ==
SATA_DTYPE_PMULT)
pmultinfo = SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_devp.cport_sata_pmult;
else
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
} else {
sata_device->satadev_addr.qual = SATA_ADDR_DPMPORT;
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
}
if (sdinfo == NULL && pmultinfo == NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
return (EINVAL);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device) != SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: reset device: failed at port %d:%d",
cport, pmport));
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (sdinfo != NULL) {
sdinfo->satadrv_state &= ~SATA_STATE_READY;
sdinfo->satadrv_state |= SATA_DSTATE_FAILED;
} else if (pmultinfo != NULL) {
pmultinfo->pmult_state &= ~SATA_STATE_READY;
pmultinfo->pmult_state |= SATA_DSTATE_FAILED;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->cport_mutex);
rv = EIO;
}
return (rv);
}
static int
sata_ioctl_reset_all(sata_hba_inst_t *sata_hba_inst)
{
sata_device_t sata_device;
int rv = 0;
int tcport;
sata_device.satadev_rev = SATA_DEVICE_REV;
if (SATA_RESET_DPORT_FUNC(sata_hba_inst) == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: sata_hba_tran missing required "
"function sata_tran_reset_dport"));
return (ENOTSUP);
}
for (tcport = 0; tcport < SATA_NUM_CPORTS(sata_hba_inst); tcport++) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_mutex);
if (((SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_event_flags) & SATA_EVNT_LOCK_PORT_BUSY) != 0) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_mutex);
rv = EBUSY;
break;
} else {
SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_event_flags |= SATA_APCTL_LOCK_PORT_BUSY;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_mutex);
}
if (rv == 0) {
sata_device.satadev_addr.qual = SATA_ADDR_CNTRL;
sata_device.satadev_addr.cport = 0;
sata_device.satadev_addr.pmport = 0;
if ((*SATA_RESET_DPORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device) != SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: reset controller failed"));
return (EIO);
}
}
for (tcport = 0; tcport < SATA_NUM_CPORTS(sata_hba_inst); tcport++) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_mutex);
SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_event_flags &= ~SATA_APCTL_LOCK_PORT_BUSY;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, tcport)->
cport_mutex);
}
return (rv);
}
static int
sata_ioctl_port_self_test(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device)
{
int cport, pmport, qual;
int rv = 0;
if (SATA_SELFTEST_FUNC(sata_hba_inst) == NULL)
return (ENOTSUP);
cport = sata_device->satadev_addr.cport;
pmport = sata_device->satadev_addr.pmport;
qual = sata_device->satadev_addr.qual;
if ((*SATA_SELFTEST_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), sata_device) != SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_ioctl: port selftest: "
"failed port %d:%d", cport, pmport));
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
sata_update_port_info(sata_hba_inst, sata_device);
if (qual == SATA_ADDR_CPORT)
SATA_CPORT_STATE(sata_hba_inst, cport) =
SATA_PSTATE_FAILED;
else {
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst,
cport, pmport));
SATA_PMPORT_STATE(sata_hba_inst, cport, pmport) =
SATA_PSTATE_FAILED;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst,
cport, pmport));
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, cport)->
cport_mutex);
return (EIO);
}
if (sata_reprobe_port(sata_hba_inst, sata_device,
SATA_DEV_IDENTIFY_RETRY) != SATA_SUCCESS)
rv = EIO;
return (rv);
}
static void
sata_cfgadm_state(sata_hba_inst_t *sata_hba_inst, int32_t port,
devctl_ap_state_t *ap_state)
{
uint8_t cport, pmport, qual;
uint32_t port_state, pmult_state;
uint32_t dev_type;
sata_drive_info_t *sdinfo;
cport = SCSI_TO_SATA_CPORT(port);
pmport = SCSI_TO_SATA_PMPORT(port);
qual = SCSI_TO_SATA_ADDR_QUAL(port);
port_state = SATA_CPORT_STATE(sata_hba_inst, cport);
if (port_state & SATA_PSTATE_SHUTDOWN ||
port_state & SATA_PSTATE_FAILED) {
ap_state->ap_rstate = AP_RSTATE_DISCONNECTED;
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
if (port_state & SATA_PSTATE_FAILED)
ap_state->ap_condition = AP_COND_FAILED;
else
ap_state->ap_condition = AP_COND_UNKNOWN;
return;
}
if (qual == SATA_ADDR_DPMPORT || qual == SATA_ADDR_PMPORT) {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, cport) !=
SATA_DTYPE_PMULT || SATA_PMPORT_INFO(sata_hba_inst,
cport, pmport) == NULL)
return;
port_state = SATA_PMPORT_STATE(sata_hba_inst, cport, pmport);
if (port_state & SATA_PSTATE_SHUTDOWN ||
port_state & SATA_PSTATE_FAILED) {
ap_state->ap_rstate = AP_RSTATE_DISCONNECTED;
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
if (port_state & SATA_PSTATE_FAILED)
ap_state->ap_condition = AP_COND_FAILED;
else
ap_state->ap_condition = AP_COND_UNKNOWN;
return;
}
}
if (qual == SATA_ADDR_DCPORT || qual == SATA_ADDR_CPORT)
dev_type = SATA_CPORT_DEV_TYPE(sata_hba_inst, cport);
else
dev_type = SATA_PMPORT_DEV_TYPE(sata_hba_inst, cport, pmport);
switch (dev_type) {
case SATA_DTYPE_NONE:
{
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
ap_state->ap_condition = AP_COND_OK;
ap_state->ap_rstate = AP_RSTATE_EMPTY;
break;
}
case SATA_DTYPE_PMULT:
{
ASSERT(qual == SATA_ADDR_DCPORT);
pmult_state = SATA_PMULT_INFO(sata_hba_inst, cport)->
pmult_state;
if (pmult_state & (SATA_PSTATE_SHUTDOWN|SATA_PSTATE_FAILED)) {
ap_state->ap_rstate = AP_RSTATE_DISCONNECTED;
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
if (pmult_state & SATA_PSTATE_FAILED)
ap_state->ap_condition = AP_COND_FAILED;
else
ap_state->ap_condition = AP_COND_UNKNOWN;
return;
}
ap_state->ap_ostate = AP_OSTATE_CONFIGURED;
ap_state->ap_rstate = AP_RSTATE_CONNECTED;
ap_state->ap_condition = AP_COND_OK;
break;
}
case SATA_DTYPE_ATADISK:
case SATA_DTYPE_ATAPICD:
case SATA_DTYPE_ATAPITAPE:
case SATA_DTYPE_ATAPIDISK:
{
dev_info_t *tdip = NULL;
dev_info_t *dip = NULL;
dip = SATA_DIP(sata_hba_inst);
tdip = sata_get_target_dip(dip, cport, pmport);
ap_state->ap_rstate = AP_RSTATE_CONNECTED;
if (tdip != NULL) {
ndi_devi_enter(dip);
mutex_enter(&(DEVI(tdip)->devi_lock));
if (DEVI_IS_DEVICE_REMOVED(tdip)) {
ap_state->ap_condition = AP_COND_UNUSABLE;
} else {
mutex_enter(&SATA_CPORT_MUTEX(sata_hba_inst,
cport));
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst,
cport);
if (sdinfo != NULL) {
if ((sdinfo->satadrv_state &
SATA_DSTATE_FAILED) != 0)
ap_state->ap_condition =
AP_COND_FAILED;
else
ap_state->ap_condition =
AP_COND_OK;
} else {
ap_state->ap_condition =
AP_COND_UNKNOWN;
}
mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst,
cport));
}
if ((DEVI_IS_DEVICE_OFFLINE(tdip)) ||
(DEVI_IS_DEVICE_DOWN(tdip))) {
ap_state->ap_ostate =
AP_OSTATE_UNCONFIGURED;
} else {
ap_state->ap_ostate =
AP_OSTATE_CONFIGURED;
}
mutex_exit(&(DEVI(tdip)->devi_lock));
ndi_devi_exit(dip);
} else {
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
ap_state->ap_condition = AP_COND_UNKNOWN;
}
break;
}
case SATA_DTYPE_ATAPIPROC:
ap_state->ap_rstate = AP_RSTATE_CONNECTED;
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
ap_state->ap_condition = AP_COND_OK;
break;
default:
ap_state->ap_rstate = AP_RSTATE_CONNECTED;
ap_state->ap_ostate = AP_OSTATE_UNCONFIGURED;
ap_state->ap_condition = AP_COND_UNKNOWN;
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_cfgadm_state: Internal error: "
"unknown device type"));
break;
}
}
static int
sata_ioctl_get_device_path(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_ioctl_data_t *ioc, int mode)
{
char path[MAXPATHLEN];
uint32_t size;
dev_info_t *tdip;
(void) strcpy(path, "/devices");
if ((tdip = sata_get_scsi_target_dip(SATA_DIP(sata_hba_inst),
&sata_device->satadev_addr)) == NULL) {
if (ioc->get_size == 0)
return (EINVAL);
} else {
(void) ddi_pathname(tdip, path + strlen(path));
}
size = strlen(path) + 1;
if (ioc->get_size != 0) {
if (ddi_copyout((void *)&size, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
} else {
if (ioc->bufsiz != size)
return (EINVAL);
else if (ddi_copyout((void *)&path, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
}
return (0);
}
static int
sata_ioctl_get_ap_type(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_ioctl_data_t *ioc, int mode)
{
uint32_t type_len;
const char *ap_type;
int dev_type;
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
dev_type = SATA_CPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport);
else
dev_type = SATA_PMPORT_DEV_TYPE(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.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:
ap_type = "cd/dvd";
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;
}
type_len = strlen(ap_type) + 1;
if (ioc->get_size) {
if (ddi_copyout((void *)&type_len, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
} else {
if (ioc->bufsiz != type_len)
return (EINVAL);
if (ddi_copyout((void *)ap_type, ioc->buf,
ioc->bufsiz, mode) != 0)
return (EFAULT);
}
return (0);
}
static int
sata_ioctl_get_model_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_ioctl_data_t *ioc, int mode)
{
sata_drive_info_t *sdinfo;
uint32_t info_len;
char ap_info[SATA_ID_MODEL_LEN + 1];
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
else
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
if (sdinfo == NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
swab(sdinfo->satadrv_id.ai_model, ap_info, SATA_ID_MODEL_LEN);
#else
bcopy(sdinfo->satadrv_id.ai_model, ap_info, SATA_ID_MODEL_LEN);
#endif
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
ap_info[SATA_ID_MODEL_LEN] = '\0';
info_len = strlen(ap_info) + 1;
if (ioc->get_size) {
if (ddi_copyout((void *)&info_len, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
} else {
if (ioc->bufsiz < info_len)
return (EINVAL);
if (ddi_copyout((void *)ap_info, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
}
return (0);
}
static int
sata_ioctl_get_revfirmware_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_ioctl_data_t *ioc, int mode)
{
sata_drive_info_t *sdinfo;
uint32_t info_len;
char ap_info[SATA_ID_FW_LEN + 1];
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
else
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
if (sdinfo == NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
swab(sdinfo->satadrv_id.ai_fw, ap_info, SATA_ID_FW_LEN);
#else
bcopy(sdinfo->satadrv_id.ai_fw, ap_info, SATA_ID_FW_LEN);
#endif
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
ap_info[SATA_ID_FW_LEN] = '\0';
info_len = strlen(ap_info) + 1;
if (ioc->get_size) {
if (ddi_copyout((void *)&info_len, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
} else {
if (ioc->bufsiz < info_len)
return (EINVAL);
if (ddi_copyout((void *)ap_info, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
}
return (0);
}
static int
sata_ioctl_get_serialnumber_info(sata_hba_inst_t *sata_hba_inst,
sata_device_t *sata_device, sata_ioctl_data_t *ioc, int mode)
{
sata_drive_info_t *sdinfo;
uint32_t info_len;
char ap_info[SATA_ID_SERIAL_LEN + 1];
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
if (sata_device->satadev_addr.qual == SATA_ADDR_DCPORT)
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport);
else
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst,
sata_device->satadev_addr.cport,
sata_device->satadev_addr.pmport);
if (sdinfo == NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
return (EINVAL);
}
#ifdef _LITTLE_ENDIAN
swab(sdinfo->satadrv_id.ai_drvser, ap_info, SATA_ID_SERIAL_LEN);
#else
bcopy(sdinfo->satadrv_id.ai_drvser, ap_info, SATA_ID_SERIAL_LEN);
#endif
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
sata_device->satadev_addr.cport)->cport_mutex);
ap_info[SATA_ID_SERIAL_LEN] = '\0';
info_len = strlen(ap_info) + 1;
if (ioc->get_size) {
if (ddi_copyout((void *)&info_len, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
} else {
if (ioc->bufsiz < info_len)
return (EINVAL);
if (ddi_copyout((void *)ap_info, ioc->buf, ioc->bufsiz,
mode) != 0)
return (EFAULT);
}
return (0);
}
static void
sata_fixed_sense_data_preset(struct scsi_extended_sense *sense)
{
sense->es_valid = 1;
sense->es_class = CLASS_EXTENDED_SENSE;
sense->es_key = KEY_NO_SENSE;
sense->es_info_1 = 0;
sense->es_info_2 = 0;
sense->es_info_3 = 0;
sense->es_info_4 = 0;
sense->es_add_len = 10;
sense->es_cmd_info[0] = 0;
sense->es_cmd_info[1] = 0;
sense->es_cmd_info[2] = 0;
sense->es_cmd_info[3] = 0;
sense->es_add_code = 0;
sense->es_qual_code = 0;
}
static void
sata_target_devid_register(dev_info_t *dip, sata_drive_info_t *sdinfo)
{
char *hwid;
int modlen;
int serlen;
int rval;
ddi_devid_t devid;
hwid = kmem_zalloc(LEGACY_HWID_LEN, KM_SLEEP);
bcopy(&sdinfo->satadrv_id.ai_model, hwid,
sizeof (sdinfo->satadrv_id.ai_model));
swab(hwid, hwid, sizeof (sdinfo->satadrv_id.ai_model));
modlen = sata_check_modser(hwid, sizeof (sdinfo->satadrv_id.ai_model));
if (modlen == 0)
goto err;
hwid[modlen++] = '=';
bcopy(&sdinfo->satadrv_id.ai_drvser, &hwid[modlen],
sizeof (sdinfo->satadrv_id.ai_drvser));
swab(&hwid[modlen], &hwid[modlen],
sizeof (sdinfo->satadrv_id.ai_drvser));
serlen = sata_check_modser(&hwid[modlen],
sizeof (sdinfo->satadrv_id.ai_drvser));
if (serlen == 0)
goto err;
hwid[modlen + serlen] = 0;
if ((rval = ddi_devid_init(dip, DEVID_ATA_SERIAL,
(ushort_t)(modlen + serlen), hwid, &devid)) == DDI_SUCCESS) {
rval = ddi_devid_register(dip, devid);
ddi_devid_free(devid);
}
if (rval != DDI_SUCCESS)
cmn_err(CE_WARN, "sata: failed to create devid for the disk"
" on port %d", sdinfo->satadrv_addr.cport);
err:
kmem_free(hwid, LEGACY_HWID_LEN);
}
static int
sata_check_modser(char *buf, int buf_len)
{
boolean_t ret;
char *s;
int i;
int tb = 0;
char ch;
ret = B_FALSE;
s = buf;
for (i = 0; i < buf_len; i++) {
ch = *s++;
if (ch != ' ' && ch != '\0')
tb = i + 1;
if (ch != ' ' && ch != '\0' && ch != '0')
ret = B_TRUE;
}
if (ret == B_FALSE)
return (0);
return (tb);
}
static int
sata_set_drive_features(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo, int restore)
{
int rval = SATA_SUCCESS;
int rval_set;
sata_drive_info_t new_sdinfo;
char *finfo = "sata_set_drive_features: cannot";
char *finfox;
int cache_op;
bzero(&new_sdinfo, sizeof (sata_drive_info_t));
new_sdinfo.satadrv_addr = sdinfo->satadrv_addr;
new_sdinfo.satadrv_type = sdinfo->satadrv_type;
if (sata_fetch_device_identify_data(sata_hba_inst, &new_sdinfo) != 0) {
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"%s fetch device identify data\n", finfo);
return (SATA_FAILURE);
}
finfox = (restore != 0) ? " restore device features" :
" initialize device features\n";
switch (sdinfo->satadrv_type) {
case SATA_DTYPE_ATADISK:
if (sata_set_dma_mode(sata_hba_inst, &new_sdinfo) !=
SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"%s set UDMA mode\n", finfo));
return (SATA_FAILURE);
}
break;
case SATA_DTYPE_ATAPICD:
case SATA_DTYPE_ATAPITAPE:
case SATA_DTYPE_ATAPIDISK:
if (SATA_RM_NOTIFIC_SUPPORTED(new_sdinfo.satadrv_id) &&
restore != 0) {
if (((sdinfo->satadrv_settings & SATA_DEV_RMSN) &&
(!SATA_RM_NOTIFIC_ENABLED(new_sdinfo.satadrv_id)))||
((!(sdinfo->satadrv_settings & SATA_DEV_RMSN)) &&
SATA_RM_NOTIFIC_ENABLED(new_sdinfo.satadrv_id))) {
if (sata_set_rmsn(sata_hba_inst, sdinfo,
sdinfo->satadrv_settings &
SATA_DEV_RMSN) != SATA_SUCCESS)
rval = SATA_FAILURE;
}
}
if (new_sdinfo.satadrv_id.ai_cap & SATA_DMA_SUPPORT) {
if (sata_set_dma_mode(sata_hba_inst, &new_sdinfo) !=
SATA_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"%s set UDMA mode\n", finfo));
rval = SATA_FAILURE;
}
}
break;
}
if (!SATA_READ_AHEAD_SUPPORTED(new_sdinfo.satadrv_id) &&
!SATA_WRITE_CACHE_SUPPORTED(new_sdinfo.satadrv_id)) {
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"settable features not supported\n", NULL);
goto update_sdinfo;
}
if ((SATA_READ_AHEAD_ENABLED(new_sdinfo.satadrv_id) &&
(sdinfo->satadrv_settings & SATA_DEV_READ_AHEAD)) &&
(SATA_WRITE_CACHE_ENABLED(new_sdinfo.satadrv_id) &&
(sdinfo->satadrv_settings & SATA_DEV_WRITE_CACHE))) {
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"no device features to set\n", NULL);
goto update_sdinfo;
}
cache_op = 0;
if (SATA_READ_AHEAD_SUPPORTED(new_sdinfo.satadrv_id)) {
if ((sdinfo->satadrv_settings & SATA_DEV_READ_AHEAD) &&
!SATA_READ_AHEAD_ENABLED(new_sdinfo.satadrv_id)) {
cache_op = SATAC_SF_ENABLE_READ_AHEAD;
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"enabling read cache\n", NULL);
} else if (!(sdinfo->satadrv_settings & SATA_DEV_READ_AHEAD) &&
SATA_READ_AHEAD_ENABLED(new_sdinfo.satadrv_id)) {
cache_op = SATAC_SF_DISABLE_READ_AHEAD;
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"disabling read cache\n", NULL);
}
if (cache_op != 0) {
rval_set = sata_set_cache_mode(sata_hba_inst,
&new_sdinfo, cache_op);
if (rval != SATA_FAILURE && rval_set != SATA_SUCCESS)
rval = rval_set;
}
}
cache_op = 0;
if (SATA_WRITE_CACHE_SUPPORTED(new_sdinfo.satadrv_id)) {
if ((sdinfo->satadrv_settings & SATA_DEV_WRITE_CACHE) &&
!SATA_WRITE_CACHE_ENABLED(new_sdinfo.satadrv_id)) {
cache_op = SATAC_SF_ENABLE_WRITE_CACHE;
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"enabling write cache\n", NULL);
} else if (!(sdinfo->satadrv_settings & SATA_DEV_WRITE_CACHE) &&
SATA_WRITE_CACHE_ENABLED(new_sdinfo.satadrv_id)) {
cache_op = SATAC_SF_DISABLE_WRITE_CACHE;
SATADBG1(SATA_DBG_DEV_SETTINGS, sata_hba_inst,
"disabling write cache\n", NULL);
}
if (cache_op != 0) {
rval_set = sata_set_cache_mode(sata_hba_inst,
&new_sdinfo, cache_op);
if (rval != SATA_FAILURE && rval_set != SATA_SUCCESS)
rval = rval_set;
}
}
if (rval != SATA_SUCCESS)
SATA_LOG_D((sata_hba_inst, CE_WARN,
"%s %s", finfo, finfox));
update_sdinfo:
if (sata_fetch_device_identify_data(sata_hba_inst, &new_sdinfo) != 0) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"%s re-fetch device identify data\n", finfo));
rval = SATA_FAILURE;
}
sdinfo->satadrv_id = new_sdinfo.satadrv_id;
return (rval);
}
static int
sata_fetch_smart_return_status(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval;
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (-1);
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_special_regs = B_TRUE;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_NODATA_XFER;
scmd->satacmd_flags.sata_copy_out_lba_mid_lsb = B_TRUE;
scmd->satacmd_flags.sata_copy_out_lba_high_lsb = B_TRUE;
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = SMART_MAGIC_VAL_1;
scmd->satacmd_lba_high_lsb = SMART_MAGIC_VAL_2;
scmd->satacmd_features_reg = SATA_SMART_RETURN_STATUS;
scmd->satacmd_device_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SMART;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if ((*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt) !=
SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
rval = -1;
} else {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if (scmd->satacmd_error_reg & SATA_ERROR_ABORT) {
rval = -1;
goto fail;
}
if (scmd->satacmd_status_reg & SATA_STATUS_ERR) {
rval = -1;
goto fail;
}
if ((scmd->satacmd_lba_mid_lsb == SMART_MAGIC_VAL_1) &&
(scmd->satacmd_lba_high_lsb == SMART_MAGIC_VAL_2))
rval = 0;
else if ((scmd->satacmd_lba_mid_lsb == SMART_MAGIC_VAL_3) &&
(scmd->satacmd_lba_high_lsb == SMART_MAGIC_VAL_4))
rval = 1;
else {
rval = -1;
goto fail;
}
}
fail:
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_fetch_smart_data(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo,
struct smart_data *smart_data)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval = 0;
dev_info_t *dip = SATA_DIP(sata_hba_inst);
#if ! defined(lint)
ASSERT(sizeof (struct smart_data) == 512);
#endif
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (-1);
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_bp = sata_alloc_local_buffer(spx,
sizeof (struct smart_data));
if (scmd->satacmd_bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_fetch_smart_data: "
"cannot allocate buffer"));
return (-1);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 0;
scmd->satacmd_lba_low_lsb = 0;
scmd->satacmd_lba_mid_lsb = SMART_MAGIC_VAL_1;
scmd->satacmd_lba_high_lsb = SMART_MAGIC_VAL_2;
scmd->satacmd_features_reg = SATA_SMART_READ_DATA;
scmd->satacmd_device_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SMART;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if ((*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt) !=
SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
rval = -1;
goto fail;
} else {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ASSERT(rval == DDI_SUCCESS);
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
rval = -1;
goto fail;
}
}
bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)smart_data,
sizeof (struct smart_data));
}
fail:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_read_log_ext(sata_hba_inst_t *sata_hba_inst, sata_drive_info_t *sdinfo,
uint8_t log_addr, uint16_t page_num, void *buf, uint16_t nsect)
{
dev_info_t *dip;
sata_pkt_txlate_t *spx;
sata_pkt_t *spkt;
sata_cmd_t *scmd;
kmutex_t *cmutex;
int rval;
dip = SATA_DIP(sata_hba_inst);
cmutex = &SATA_CPORT_MUTEX(sata_hba_inst, sdinfo->satadrv_addr.cport);
ASSERT(MUTEX_HELD(cmutex));
spx = kmem_zalloc(sizeof (*spx), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_bp = sata_alloc_local_buffer(spx, (size_t)nsect * 512);
if (scmd->satacmd_bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (*spx));
SATA_LOG_D((sata_hba_inst, CE_WARN, "%s: cannot allocate bp",
__func__));
return (-1);
}
scmd->satacmd_cmd_reg = SATAC_READ_LOG_EXT;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_addr_type = ATA_ADDR_LBA48;
scmd->satacmd_sec_count_lsb = nsect & 0xff;
scmd->satacmd_sec_count_msb = nsect >> 8;
scmd->satacmd_lba_low_lsb = log_addr;
scmd->satacmd_lba_mid_lsb = page_num & 0xff;
scmd->satacmd_lba_high_lsb = 0;
scmd->satacmd_lba_low_msb = 0;
scmd->satacmd_lba_mid_msb = page_num >> 8;
scmd->satacmd_lba_high_msb = 0;
scmd->satacmd_device_reg = 0;
mutex_exit(cmutex);
rval = (*SATA_START_FUNC(sata_hba_inst))(dip, spkt);
mutex_enter(cmutex);
if (rval != SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
rval = -1;
goto fail;
}
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ASSERT3S(rval, ==, DDI_SUCCESS);
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip, DDI_SERVICE_UNAFFECTED);
rval = -1;
goto fail;
}
bcopy(scmd->satacmd_bp->b_un.b_addr, buf, (size_t)nsect * 512);
rval = 0;
}
fail:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (*spx));
return (rval);
}
CTASSERT(sizeof (struct smart_ext_selftest_log) == 512);
static int
sata_ext_smart_selftest_read_log(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo, struct smart_ext_selftest_log *ext_selftest_log,
uint16_t block_num)
{
return (sata_read_log_ext(sata_hba_inst, sdinfo,
EXT_SMART_SELFTEST_LOG_PAGE, block_num, ext_selftest_log, 1));
}
static int
sata_smart_selftest_log(
sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo,
struct smart_selftest_log *selftest_log)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval;
dev_info_t *dip = SATA_DIP(sata_hba_inst);
#if ! defined(lint)
ASSERT(sizeof (struct smart_selftest_log) == 512);
#endif
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (-1);
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_bp = sata_alloc_local_buffer(spx,
sizeof (struct smart_selftest_log));
if (scmd->satacmd_bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_smart_selftest_log: "
"cannot allocate buffer"));
return (-1);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = 1;
scmd->satacmd_lba_low_lsb = SMART_SELFTEST_LOG_PAGE;
scmd->satacmd_lba_mid_lsb = SMART_MAGIC_VAL_1;
scmd->satacmd_lba_high_lsb = SMART_MAGIC_VAL_2;
scmd->satacmd_features_reg = SATA_SMART_READ_LOG;
scmd->satacmd_device_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SMART;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if ((*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt) !=
SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
rval = -1;
goto fail;
} else {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ASSERT(rval == DDI_SUCCESS);
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
rval = -1;
goto fail;
}
}
bcopy(scmd->satacmd_bp->b_un.b_addr, (uint8_t *)selftest_log,
sizeof (struct smart_selftest_log));
rval = 0;
}
fail:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
static int
sata_smart_read_log(
sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo,
uint8_t *smart_log,
uint8_t which_log,
uint8_t log_size)
{
sata_pkt_t *spkt;
sata_cmd_t *scmd;
sata_pkt_txlate_t *spx;
int rval;
dev_info_t *dip = SATA_DIP(sata_hba_inst);
spx = kmem_zalloc(sizeof (sata_pkt_txlate_t), KM_SLEEP);
spx->txlt_sata_hba_inst = sata_hba_inst;
spx->txlt_scsi_pkt = NULL;
spkt = sata_pkt_alloc(spx, SLEEP_FUNC);
if (spkt == NULL) {
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (-1);
}
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_device.satadev_addr = sdinfo->satadrv_addr;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
scmd->satacmd_flags.sata_data_direction = SATA_DIR_READ;
scmd->satacmd_bp = sata_alloc_local_buffer(spx, (size_t)log_size * 512);
if (scmd->satacmd_bp == NULL) {
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_smart_read_log: " "cannot allocate buffer"));
return (-1);
}
scmd->satacmd_addr_type = 0;
scmd->satacmd_sec_count_lsb = log_size;
scmd->satacmd_lba_low_lsb = which_log;
scmd->satacmd_lba_mid_lsb = SMART_MAGIC_VAL_1;
scmd->satacmd_lba_high_lsb = SMART_MAGIC_VAL_2;
scmd->satacmd_features_reg = SATA_SMART_READ_LOG;
scmd->satacmd_device_reg = 0;
scmd->satacmd_cmd_reg = SATAC_SMART;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if ((*SATA_START_FUNC(sata_hba_inst))(SATA_DIP(sata_hba_inst), spkt) !=
SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
rval = -1;
goto fail;
} else {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst,
sdinfo->satadrv_addr.cport)));
if (spx->txlt_buf_dma_handle != NULL) {
rval = ddi_dma_sync(spx->txlt_buf_dma_handle, 0, 0,
DDI_DMA_SYNC_FORKERNEL);
ASSERT(rval == DDI_SUCCESS);
if (sata_check_for_dma_error(dip, spx)) {
ddi_fm_service_impact(dip,
DDI_SERVICE_UNAFFECTED);
rval = -1;
goto fail;
}
}
bcopy(scmd->satacmd_bp->b_un.b_addr, smart_log, log_size * 512);
rval = 0;
}
fail:
sata_free_local_buffer(spx);
sata_pkt_free(spx);
kmem_free(spx, sizeof (sata_pkt_txlate_t));
return (rval);
}
CTASSERT(sizeof (struct read_log_ext_directory) == 512);
static int
sata_read_log_ext_directory(sata_hba_inst_t *sata_hba_inst,
sata_drive_info_t *sdinfo, struct read_log_ext_directory *logdir)
{
return (sata_read_log_ext(sata_hba_inst, sdinfo,
READ_LOG_EXT_LOG_DIRECTORY, 0, logdir, 1));
}
static int
sata_ncq_err_ret_cmd_setup(sata_pkt_txlate_t *spx, sata_drive_info_t *sdinfo)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(sdinfo))
#endif
sata_pkt_t *spkt = spx->txlt_sata_pkt;
sata_cmd_t *scmd;
struct buf *bp;
spkt->satapkt_op_mode = SATA_OPMODE_SYNCH | SATA_OPMODE_INTERRUPTS;
spkt->satapkt_comp = NULL;
spkt->satapkt_time = sata_default_pkt_time;
scmd = &spkt->satapkt_cmd;
bcopy(&sata_rle_cmd, scmd, sizeof (sata_cmd_t));
scmd->satacmd_flags.sata_ignore_dev_reset = B_TRUE;
bp = sata_alloc_local_buffer(spx,
sizeof (struct sata_ncq_error_recovery_page));
if (bp == NULL)
return (SATA_FAILURE);
bp_mapin(bp);
scmd->satacmd_bp = bp;
scmd->satacmd_err_ret_buf_handle = &spx->txlt_buf_dma_handle;
ASSERT(scmd->satacmd_num_dma_cookies != 0);
ASSERT(scmd->satacmd_dma_cookie_list != NULL);
return (SATA_SUCCESS);
}
static void
sata_xlate_errors(sata_pkt_txlate_t *spx)
{
struct scsi_pkt *scsipkt = spx->txlt_scsi_pkt;
struct scsi_extended_sense *sense;
scsipkt->pkt_reason = CMD_INCOMPLETE;
*scsipkt->pkt_scbp = STATUS_CHECK;
sense = sata_arq_sense(spx);
switch (spx->txlt_sata_pkt->satapkt_reason) {
case SATA_PKT_PORT_ERROR:
sense->es_key = KEY_HARDWARE_ERROR;
break;
case SATA_PKT_DEV_ERROR:
if (spx->txlt_sata_pkt->satapkt_cmd.satacmd_status_reg &
SATA_STATUS_ERR) {
sata_decode_device_error(spx, sense);
break;
}
break;
case SATA_PKT_TIMEOUT:
scsipkt->pkt_reason = CMD_TIMEOUT;
scsipkt->pkt_statistics |= STAT_TIMEOUT | STAT_DEV_RESET;
break;
case SATA_PKT_ABORTED:
scsipkt->pkt_reason = CMD_ABORTED;
scsipkt->pkt_statistics |= STAT_ABORTED;
break;
case SATA_PKT_RESET:
scsipkt->pkt_reason = CMD_RESET;
scsipkt->pkt_statistics |= STAT_DEV_RESET;
break;
default:
scsipkt->pkt_reason = CMD_TRAN_ERR;
break;
}
}
static void
sata_log(sata_hba_inst_t *sata_hba_inst, uint_t level, char *fmt, ...)
{
char pathname[128];
dev_info_t *dip = NULL;
va_list ap;
mutex_enter(&sata_log_mutex);
va_start(ap, fmt);
(void) vsprintf(sata_log_buf, fmt, ap);
va_end(ap);
if (sata_hba_inst != NULL) {
dip = SATA_DIP(sata_hba_inst);
(void) ddi_pathname(dip, pathname);
} else {
pathname[0] = 0;
}
if (level == CE_CONT) {
if (sata_debug_flags == 0)
cmn_err(level, "?%s:\n %s\n", pathname, sata_log_buf);
else
cmn_err(level, "%s:\n %s\n", pathname, sata_log_buf);
} else {
if (level != CE_NOTE) {
cmn_err(level, "%s:\n %s", pathname, sata_log_buf);
} else if (sata_msg) {
cmn_err(level, "%s:\n %s", pathname,
sata_log_buf);
}
}
sata_trace_debug(dip, sata_log_buf);
mutex_exit(&sata_log_mutex);
}
static void
sata_event_thread_control(int startstop)
{
static int sata_event_thread_terminating = 0;
static int sata_event_thread_starting = 0;
int i;
mutex_enter(&sata_event_mutex);
if (startstop == 0 && (sata_event_thread_starting == 1 ||
sata_event_thread_terminating == 1)) {
mutex_exit(&sata_event_mutex);
return;
}
if (startstop == 1 && sata_event_thread_starting == 1) {
mutex_exit(&sata_event_mutex);
return;
}
if (startstop == 1 && sata_event_thread_terminating == 1) {
sata_event_thread_starting = 1;
i = SATA_EVNT_DAEMON_TERM_WAIT/SATA_EVNT_DAEMON_TERM_TIMEOUT;
while (sata_event_thread_terminating == 1) {
if (i-- <= 0) {
sata_event_thread_starting = 0;
mutex_exit(&sata_event_mutex);
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata_event_thread_control: "
"timeout waiting for thread to terminate");
#endif
return;
}
mutex_exit(&sata_event_mutex);
delay(drv_usectohz(SATA_EVNT_DAEMON_TERM_TIMEOUT));
mutex_enter(&sata_event_mutex);
}
}
if (startstop == 1) {
if (sata_event_thread == NULL) {
sata_event_thread = thread_create(NULL, 0,
(void (*)())sata_event_daemon,
&sata_hba_list, 0, &p0, TS_RUN, minclsyspri);
}
sata_event_thread_starting = 0;
mutex_exit(&sata_event_mutex);
return;
}
if (sata_event_thread != NULL) {
int i;
sata_event_thread_terminating = 1;
sata_event_thread_terminate = 1;
cv_signal(&sata_event_cv);
i = SATA_EVNT_DAEMON_TERM_WAIT/SATA_EVNT_DAEMON_TERM_TIMEOUT;
while (sata_event_thread_terminate == 1) {
mutex_exit(&sata_event_mutex);
if (i-- <= 0) {
#ifdef SATA_DEBUG
cmn_err(CE_WARN, "sata_event_thread_control: "
"cannot terminate event daemon thread");
#endif
mutex_enter(&sata_event_mutex);
break;
}
delay(drv_usectohz(SATA_EVNT_DAEMON_TERM_TIMEOUT));
mutex_enter(&sata_event_mutex);
}
sata_event_thread_terminating = 0;
}
ASSERT(sata_event_thread_terminating == 0);
ASSERT(sata_event_thread_starting == 0);
mutex_exit(&sata_event_mutex);
}
#define SATA_EVENT_MAX_MSG_LENGTH 79
void
sata_hba_event_notify(dev_info_t *dip, sata_device_t *sata_device, int event)
{
sata_hba_inst_t *sata_hba_inst = NULL;
sata_address_t *saddr;
sata_pmult_info_t *pmultinfo;
sata_drive_info_t *sdinfo;
sata_port_stats_t *pstats;
sata_cport_info_t *cportinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
int cport, pmport;
char buf1[SATA_EVENT_MAX_MSG_LENGTH + 1];
char buf2[SATA_EVENT_MAX_MSG_LENGTH + 1];
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;
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
if (SATA_DIP(sata_hba_inst) == dip)
if (sata_hba_inst->satahba_attached == 1)
break;
}
mutex_exit(&sata_mutex);
if (sata_hba_inst == NULL)
return;
ASSERT(sata_device != NULL);
saddr = &sata_device->satadev_addr;
if (saddr->cport >= SATA_NUM_CPORTS(sata_hba_inst))
return;
cport = saddr->cport;
pmport = saddr->pmport;
buf1[0] = buf2[0] = '\0';
if ((saddr->qual & (SATA_ADDR_CPORT | SATA_ADDR_PMPORT |
SATA_ADDR_DCPORT | SATA_ADDR_DPMPORT | SATA_ADDR_PMULT)) != 0) {
mutex_enter(&sata_hba_inst->satahba_mutex);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
mutex_exit(&sata_hba_inst->satahba_mutex);
if (cportinfo == NULL || cportinfo->cport_state == 0)
return;
}
if ((saddr->qual & (SATA_ADDR_PMULT | SATA_ADDR_PMPORT |
SATA_ADDR_DPMPORT)) != 0) {
if (cportinfo->cport_dev_type != SATA_DTYPE_PMULT) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: Non-pmult device (0x%x)"
"is attached to port %d, ignore pmult/pmport "
"event 0x%x", cportinfo->cport_dev_type,
cport, event));
return;
}
mutex_enter(&cportinfo->cport_mutex);
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
mutex_exit(&cportinfo->cport_mutex);
if (pmultinfo == NULL ||
pmultinfo->pmult_state == SATA_STATE_UNKNOWN) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: pmult is not"
"available at port %d:%d, ignore event 0x%x",
cport, pmport, event));
return;
}
}
if ((saddr->qual &
(SATA_ADDR_PMPORT | SATA_ADDR_DPMPORT)) != 0) {
mutex_enter(&cportinfo->cport_mutex);
if (pmport > SATA_NUM_PMPORTS(sata_hba_inst, cport)) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: invalid/"
"un-implemented port %d:%d (%d ports), "
"ignore event 0x%x", cport, pmport,
SATA_NUM_PMPORTS(sata_hba_inst, cport), event));
mutex_exit(&cportinfo->cport_mutex);
return;
}
mutex_exit(&cportinfo->cport_mutex);
mutex_enter(&sata_hba_inst->satahba_mutex);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
cport, pmport);
mutex_exit(&sata_hba_inst->satahba_mutex);
if (pmportinfo == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: invalid/"
"un-implemented port %d:%d, ignore "
"event 0x%x", cport, pmport, event));
return;
}
}
if (saddr->qual & (SATA_ADDR_CPORT | SATA_ADDR_PMPORT)) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if ((event & SATA_EVNT_PORT_EVENTS) == 0) {
(void) sprintf(buf2, err_msg_evnt_1,
event & SATA_EVNT_PORT_EVENTS);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
goto event_info;
}
if (saddr->qual == SATA_ADDR_CPORT) {
(SATA_CPORT_INFO(sata_hba_inst, cport))->
cport_event_flags |=
event & SATA_EVNT_PORT_EVENTS;
pstats =
&(SATA_CPORT_INFO(sata_hba_inst, cport))->
cport_stats;
} else {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
mutex_enter(&pmportinfo->pmport_mutex);
(SATA_PMPORT_INFO(sata_hba_inst, cport, pmport))->
pmport_event_flags |=
event & SATA_EVNT_PORT_EVENTS;
pstats =
&(SATA_PMPORT_INFO(sata_hba_inst, cport, pmport))->
pmport_stats;
mutex_exit(&pmportinfo->pmport_mutex);
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
}
linkevent = event &
(SATA_EVNT_LINK_LOST | SATA_EVNT_LINK_ESTABLISHED);
if (linkevent) {
if (linkevent == (SATA_EVNT_LINK_LOST |
SATA_EVNT_LINK_ESTABLISHED)) {
(void) strlcat(buf1, "link lost/established, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (pstats->link_lost < 0xffffffffffffffffULL)
pstats->link_lost++;
if (pstats->link_established <
0xffffffffffffffffULL)
pstats->link_established++;
linkevent = 0;
} else if (linkevent & SATA_EVNT_LINK_LOST) {
(void) strlcat(buf1, "link lost, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (pstats->link_lost < 0xffffffffffffffffULL)
pstats->link_lost++;
} else {
(void) strlcat(buf1, "link established, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (pstats->link_established <
0xffffffffffffffffULL)
pstats->link_established++;
}
}
if (event & SATA_EVNT_DEVICE_ATTACHED) {
(void) strlcat(buf1, "device attached, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (pstats->device_attached < 0xffffffffffffffffULL)
pstats->device_attached++;
}
if (event & SATA_EVNT_DEVICE_DETACHED) {
(void) strlcat(buf1, "device detached, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (pstats->device_detached < 0xffffffffffffffffULL)
pstats->device_detached++;
}
if (event & SATA_EVNT_PWR_LEVEL_CHANGED) {
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"port %d power level changed", cport);
if (pstats->port_pwr_changed < 0xffffffffffffffffULL)
pstats->port_pwr_changed++;
}
if ((event & ~SATA_EVNT_PORT_EVENTS) != 0) {
(void) sprintf(buf2, err_msg_evnt_1,
event & ~SATA_EVNT_PORT_EVENTS);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
} else if (saddr->qual & (SATA_ADDR_DCPORT | SATA_ADDR_DPMPORT)) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if ((event & SATA_EVNT_DEVICE_RESET) == 0) {
(void) sprintf(buf2, err_msg_evnt_2,
event & SATA_EVNT_DEVICE_RESET);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
goto event_info;
}
sdinfo = sata_get_device_info(sata_hba_inst, sata_device);
if (sdinfo != NULL) {
if (event & SATA_EVNT_DEVICE_RESET) {
(void) strlcat(buf1, "device reset, ",
SATA_EVENT_MAX_MSG_LENGTH);
if (sdinfo->satadrv_stats.drive_reset <
0xffffffffffffffffULL)
sdinfo->satadrv_stats.drive_reset++;
sdinfo->satadrv_event_flags |=
SATA_EVNT_DEVICE_RESET;
}
}
if ((event & ~SATA_EVNT_DEVICE_RESET) != 0) {
(void) sprintf(buf2, err_msg_evnt_2,
event & ~SATA_EVNT_DRIVE_EVENTS);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
} else if (saddr->qual == SATA_ADDR_PMULT) {
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
if ((event & (SATA_EVNT_DEVICE_RESET |
SATA_EVNT_PMULT_LINK_CHANGED)) == 0) {
(void) sprintf(buf2, err_msg_evnt_2,
event & SATA_EVNT_DEVICE_RESET);
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
goto event_info;
}
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
if (event & SATA_EVNT_DEVICE_RESET) {
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"[Reset] port-mult on cport %d", cport);
pmultinfo->pmult_event_flags |=
SATA_EVNT_DEVICE_RESET;
(void) strlcat(buf1, "pmult reset, ",
SATA_EVENT_MAX_MSG_LENGTH);
}
if (event & SATA_EVNT_PMULT_LINK_CHANGED) {
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"pmult link changed on cport %d", cport);
pmultinfo->pmult_event_flags |=
SATA_EVNT_PMULT_LINK_CHANGED;
(void) strlcat(buf1, "pmult link changed, ",
SATA_EVENT_MAX_MSG_LENGTH);
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, cport)));
} else {
if (saddr->qual != SATA_ADDR_NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: invalid address 0x%x",
*(uint32_t *)saddr));
return;
}
if ((event & SATA_EVNT_CONTROLLER_EVENTS) == 0 ||
(event & ~SATA_EVNT_CONTROLLER_EVENTS) != 0) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: invalid event 0x%x for "
"controller",
event & SATA_EVNT_CONTROLLER_EVENTS));
return;
}
buf1[0] = '\0';
SATADBG1(SATA_DBG_EVENTS, sata_hba_inst,
"controller power level changed\n", NULL);
mutex_enter(&sata_hba_inst->satahba_mutex);
if (sata_hba_inst->satahba_stats.ctrl_pwr_change <
0xffffffffffffffffULL)
sata_hba_inst->satahba_stats.ctrl_pwr_change++;
sata_hba_inst->satahba_event_flags |=
SATA_EVNT_PWR_LEVEL_CHANGED;
mutex_exit(&sata_hba_inst->satahba_mutex);
}
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
mutex_enter(&sata_event_mutex);
if (sata_event_thread_active == 0)
cv_signal(&sata_event_cv);
mutex_exit(&sata_event_mutex);
event_info:
if (buf1[0] != '\0') {
lcp = strrchr(buf1, ',');
if (lcp != NULL)
*lcp = '\0';
}
if (saddr->qual == SATA_ADDR_CPORT ||
saddr->qual == SATA_ADDR_DCPORT) {
if (buf1[0] != '\0') {
sata_log(sata_hba_inst, CE_NOTE, "port %d: %s\n",
cport, buf1);
}
if (buf2[0] != '\0') {
sata_log(sata_hba_inst, CE_NOTE, "port %d: %s\n",
cport, buf2);
}
} else if (saddr->qual == SATA_ADDR_PMPORT ||
saddr->qual == SATA_ADDR_DPMPORT) {
if (buf1[0] != '\0') {
sata_log(sata_hba_inst, CE_NOTE,
"port %d pmport %d: %s\n", cport, pmport, buf1);
}
if (buf2[0] != '\0') {
sata_log(sata_hba_inst, CE_NOTE,
"port %d pmport %d: %s\n", cport, pmport, buf2);
}
}
}
static void
sata_event_daemon(void *arg)
{
#ifndef __lock_lint
_NOTE(ARGUNUSED(arg))
#endif
sata_hba_inst_t *sata_hba_inst;
clock_t delta;
SATADBG1(SATA_DBG_EVENTS_DAEMON, NULL,
"SATA event daemon started\n", NULL);
loop:
mutex_enter(&sata_mutex);
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
ASSERT(sata_hba_inst != NULL);
mutex_enter(&sata_hba_inst->satahba_mutex);
if (sata_hba_inst->satahba_attached == 0 ||
(sata_hba_inst->satahba_event_flags &
SATA_EVNT_SKIP) != 0) {
mutex_exit(&sata_hba_inst->satahba_mutex);
continue;
}
if (sata_hba_inst->satahba_event_flags & SATA_EVNT_MAIN) {
sata_hba_inst->satahba_event_flags |= SATA_EVNT_SKIP;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_exit(&sata_mutex);
sata_process_controller_events(sata_hba_inst);
goto loop;
}
mutex_exit(&sata_hba_inst->satahba_mutex);
}
for (sata_hba_inst = sata_hba_list; sata_hba_inst != NULL;
sata_hba_inst = sata_hba_inst->satahba_next) {
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags &= ~SATA_EVNT_SKIP;
mutex_exit(&sata_hba_inst->satahba_mutex);
}
mutex_exit(&sata_mutex);
SATADBG1(SATA_DBG_EVENTS_DAEMON, NULL,
"SATA EVENT DAEMON suspending itself", NULL);
#ifdef SATA_DEBUG
if ((sata_func_enable & SATA_ENABLE_PROCESS_EVENTS) == 0) {
sata_log(sata_hba_inst, CE_WARN,
"SATA EVENTS PROCESSING DISABLED\n");
thread_exit();
}
#endif
mutex_enter(&sata_event_mutex);
sata_event_thread_active = 0;
mutex_exit(&sata_event_mutex);
delta = drv_usectohz(SATA_EVNT_DAEMON_SLEEP_TIME);
do {
mutex_enter(&sata_event_mutex);
(void) cv_reltimedwait(&sata_event_cv, &sata_event_mutex,
delta, TR_CLOCK_TICK);
if (sata_event_thread_active != 0) {
mutex_exit(&sata_event_mutex);
continue;
}
if (sata_event_thread_terminate == 1) {
sata_event_thread_terminate = 0;
sata_event_thread = NULL;
mutex_exit(&sata_event_mutex);
SATADBG1(SATA_DBG_EVENTS_DAEMON, NULL,
"SATA_EVENT_DAEMON_TERMINATING", NULL);
thread_exit(); { _NOTE(NOT_REACHED) }
}
mutex_exit(&sata_event_mutex);
} while (!(sata_event_pending & SATA_EVNT_MAIN));
mutex_enter(&sata_event_mutex);
sata_event_thread_active = 1;
mutex_exit(&sata_event_mutex);
mutex_enter(&sata_mutex);
sata_event_pending &= ~SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
SATADBG1(SATA_DBG_EVENTS_DAEMON, NULL,
"SATA EVENT DAEMON READY TO PROCESS EVENT", NULL);
goto loop;
}
static void
sata_process_controller_events(sata_hba_inst_t *sata_hba_inst)
{
int ncport;
uint32_t event_flags;
sata_address_t *saddr;
sata_cport_info_t *cportinfo;
sata_pmult_info_t *pmultinfo;
SATADBG1(SATA_DBG_EVENTS_CNTRL, sata_hba_inst,
"Processing controller %d event(s)",
ddi_get_instance(SATA_DIP(sata_hba_inst)));
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags &= ~SATA_EVNT_MAIN;
event_flags = sata_hba_inst->satahba_event_flags;
mutex_exit(&sata_hba_inst->satahba_mutex);
if (event_flags & SATA_EVNT_PWR_LEVEL_CHANGED)
sata_process_cntrl_pwr_level_change(sata_hba_inst);
for (ncport = 0; ncport < SATA_NUM_CPORTS(sata_hba_inst); ncport++) {
mutex_enter(&sata_hba_inst->satahba_mutex);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, ncport);
mutex_exit(&sata_hba_inst->satahba_mutex);
if (cportinfo == NULL || cportinfo->cport_state == 0)
continue;
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport)));
event_flags = (SATA_CPORT_INFO(sata_hba_inst, ncport))->
cport_event_flags;
if (event_flags & SATA_APCTL_LOCK_PORT_BUSY) {
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport)));
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
SATADBG1(SATA_DBG_EVENTS_PROCPST, sata_hba_inst,
"Event processing postponed until "
"AP control processing completes",
NULL);
continue;
} else {
(SATA_CPORT_INFO(sata_hba_inst, ncport))->
cport_event_flags |= SATA_EVNT_LOCK_PORT_BUSY;
}
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport)));
saddr = &(SATA_CPORT_INFO(sata_hba_inst, ncport))->cport_addr;
if ((event_flags &
(SATA_EVNT_PORT_EVENTS | SATA_EVNT_DRIVE_EVENTS)) != 0) {
if (event_flags & SATA_EVNT_PORT_FAILED) {
sata_process_port_failed_event(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_DETACHED) {
sata_process_device_detached(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_ATTACHED) {
sata_process_device_attached(sata_hba_inst,
saddr);
}
if (event_flags &
(SATA_EVNT_LINK_ESTABLISHED |
SATA_EVNT_LINK_LOST)) {
sata_process_port_link_events(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_PWR_LEVEL_CHANGED) {
sata_process_port_pwr_change(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) {
sata_process_target_node_cleanup(
sata_hba_inst, saddr);
}
if (event_flags & SATA_EVNT_AUTOONLINE_DEVICE) {
sata_process_device_autoonline(
sata_hba_inst, saddr);
}
}
mutex_enter(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport)));
if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) {
pmultinfo = SATA_PMULT_INFO(sata_hba_inst,
ncport);
if (pmultinfo != NULL) {
mutex_exit(&(SATA_CPORT_MUTEX(
sata_hba_inst, ncport)));
sata_process_pmult_events(
sata_hba_inst, ncport);
mutex_enter(&(SATA_CPORT_MUTEX(
sata_hba_inst, ncport)));
} else {
SATADBG1(SATA_DBG_PMULT, sata_hba_inst,
"Port-multiplier is gone. "
"Ignore all sub-device events "
"at port %d.", ncport);
}
}
if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, ncport) !=
SATA_DTYPE_NONE) &&
(SATA_CPORT_DRV_INFO(sata_hba_inst, ncport) != NULL)) {
if (SATA_CPORT_DRV_INFO(sata_hba_inst, ncport)->
satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET |
SATA_EVNT_INPROC_DEVICE_RESET)) {
sata_process_device_reset(sata_hba_inst,
saddr);
}
}
(SATA_CPORT_INFO(sata_hba_inst, ncport))->
cport_event_flags &= ~SATA_EVNT_LOCK_PORT_BUSY;
mutex_exit(&(SATA_CPORT_MUTEX(sata_hba_inst, ncport)));
}
}
static void
sata_process_pmult_events(sata_hba_inst_t *sata_hba_inst, uint8_t cport)
{
sata_cport_info_t *cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
sata_pmult_info_t *pmultinfo;
sata_pmport_info_t *pmportinfo;
sata_address_t *saddr;
sata_device_t sata_device;
uint32_t event_flags;
int npmport;
int rval;
SATADBG2(SATA_DBG_EVENTS_CNTRL|SATA_DBG_PMULT, sata_hba_inst,
"Processing pmult event(s) on cport %d of controller %d",
cport, ddi_get_instance(SATA_DIP(sata_hba_inst)));
mutex_enter(&cportinfo->cport_mutex);
pmultinfo = SATA_PMULT_INFO(sata_hba_inst, cport);
event_flags = pmultinfo->pmult_event_flags;
if (event_flags & SATA_EVNT_DEVICE_RESET) {
for (npmport = 0; npmport < SATA_NUM_PMPORTS(
sata_hba_inst, cport); npmport ++) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
cport, npmport);
if (pmportinfo == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_hba_event_notify: "
"invalid/un-implemented "
"port %d:%d (%d ports), ",
cport, npmport, SATA_NUM_PMPORTS(
sata_hba_inst, cport)));
continue;
}
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_state = SATA_STATE_UNKNOWN;
pmportinfo->pmport_event_flags =
(SATA_EVNT_LINK_ESTABLISHED|SATA_EVNT_LINK_LOST);
mutex_exit(&pmportinfo->pmport_mutex);
}
} else if (event_flags & SATA_EVNT_PMULT_LINK_CHANGED) {
bzero(&sata_device, sizeof (sata_device_t));
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr.cport = cport;
sata_device.satadev_addr.pmport = SATA_PMULT_HOSTPORT;
sata_device.satadev_addr.qual = SATA_ADDR_PMULT;
mutex_exit(&cportinfo->cport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&cportinfo->cport_mutex);
if (rval != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&cportinfo->cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d probing failed", cport));
sata_free_pmult(sata_hba_inst, &sata_device);
return;
}
sata_update_port_info(sata_hba_inst, &sata_device);
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) ||
((cportinfo->cport_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) != SATA_PORT_DEVLINK_UP) ||
(cportinfo->cport_dev_type != SATA_DTYPE_PMULT)) {
mutex_exit(&cportinfo->cport_mutex);
sata_free_pmult(sata_hba_inst, &sata_device);
return;
}
cportinfo->cport_state |=
SATA_STATE_PROBED | SATA_STATE_READY;
}
pmultinfo->pmult_event_flags &=
~(SATA_EVNT_DEVICE_RESET|SATA_EVNT_PMULT_LINK_CHANGED);
mutex_exit(&cportinfo->cport_mutex);
for (npmport = 0; npmport < SATA_NUM_PMPORTS(sata_hba_inst, cport);
npmport ++) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, npmport);
mutex_enter(&pmportinfo->pmport_mutex);
event_flags = pmportinfo->pmport_event_flags;
mutex_exit(&pmportinfo->pmport_mutex);
saddr = &pmportinfo->pmport_addr;
if ((event_flags &
(SATA_EVNT_PORT_EVENTS | SATA_EVNT_DRIVE_EVENTS)) != 0) {
if (event_flags & SATA_EVNT_DEVICE_DETACHED) {
sata_process_pmdevice_detached(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_DEVICE_ATTACHED) {
sata_process_pmdevice_attached(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_LINK_ESTABLISHED ||
event_flags & SATA_EVNT_LINK_LOST) {
sata_process_pmport_link_events(sata_hba_inst,
saddr);
}
if (event_flags & SATA_EVNT_TARGET_NODE_CLEANUP) {
sata_process_target_node_cleanup(
sata_hba_inst, saddr);
}
}
mutex_enter(&pmportinfo->pmport_mutex);
if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE &&
pmportinfo->pmport_sata_drive != NULL) {
event_flags = pmportinfo->pmport_sata_drive->
satadrv_event_flags;
if (event_flags & (SATA_EVNT_DEVICE_RESET |
SATA_EVNT_INPROC_DEVICE_RESET)) {
sata_process_pmdevice_reset(sata_hba_inst,
saddr);
}
}
mutex_exit(&pmportinfo->pmport_mutex);
mutex_enter(&cportinfo->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_LOCK_PORT_BUSY;
mutex_exit(&cportinfo->cport_mutex);
}
SATADBG2(SATA_DBG_EVENTS_CNTRL|SATA_DBG_PMULT, sata_hba_inst,
"[DONE] pmult event(s) on cport %d of controller %d",
cport, ddi_get_instance(SATA_DIP(sata_hba_inst)));
}
static void
sata_process_cntrl_pwr_level_change(sata_hba_inst_t *sata_hba_inst)
{
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing controller power level change", NULL);
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags &= ~SATA_EVNT_PWR_LEVEL_CHANGED;
mutex_exit(&sata_hba_inst->satahba_mutex);
}
static void
sata_process_port_pwr_change(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port power level change", NULL);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_PWR_LEVEL_CHANGED;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
}
static void
sata_process_port_failed_event(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_PORT_FAILED;
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
return;
}
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_log(sata_hba_inst, CE_WARN, "SATA port %d failed", saddr->cport);
}
static void
sata_process_device_reset(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_drive_info_t old_sdinfo;
sata_drive_info_t *sdinfo;
sata_cport_info_t *cportinfo;
sata_device_t sata_device;
int rval_probe, rval_set;
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, saddr->cport);
if (((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) ||
(sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) {
sdinfo->satadrv_event_flags &=
~(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET);
return;
}
if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) ==
SATA_DTYPE_PMULT)) {
mutex_exit(&cportinfo->cport_mutex);
goto done;
}
if ((SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) &
SATA_VALID_DEV_TYPE) == 0) {
#ifdef SATA_DEBUG
sata_log(sata_hba_inst, CE_WARN,
"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) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"No device reset event!!!!", NULL);
return;
}
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) ==
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Overlapping device reset events!", NULL);
}
#endif
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d device reset", saddr->cport);
sdinfo->satadrv_event_flags &= ~SATA_EVNT_DEVICE_RESET;
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
rval_probe = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, &sata_device);
if (rval_probe != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, saddr->cport);
if (sdinfo != NULL)
sdinfo->satadrv_event_flags = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d probing failed",
saddr->cport));
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
return;
}
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP ||
sata_device.satadev_type == SATA_DTYPE_NONE) {
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, saddr->cport);
if (sdinfo != NULL) {
sdinfo->satadrv_event_flags &=
~SATA_EVNT_INPROC_DEVICE_RESET;
sdinfo->satadrv_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
return;
}
sdinfo = SATA_CPORT_DRV_INFO(sata_hba_inst, saddr->cport);
if (sdinfo == NULL) {
return;
}
if ((sdinfo->satadrv_event_flags &
SATA_EVNT_INPROC_DEVICE_RESET) == 0) {
sdinfo->satadrv_reset_time = ddi_get_lbolt();
}
sdinfo->satadrv_event_flags |= SATA_EVNT_INPROC_DEVICE_RESET;
old_sdinfo = *sdinfo;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
rval_set = sata_set_drive_features(sata_hba_inst, &old_sdinfo, 1);
if (rval_set != SATA_SUCCESS) {
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
sata_device.satadev_addr.qual = SATA_ADDR_CPORT;
rval_probe = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
if (rval_probe == SATA_SUCCESS &&
(sata_device.satadev_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0 &&
(sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP &&
sata_device.satadev_type != SATA_DTYPE_NONE) {
if ((cportinfo->cport_dev_type &
SATA_VALID_DEV_TYPE) != 0 &&
SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL &&
sdinfo->satadrv_reset_time != 0) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time - sdinfo->satadrv_reset_time) <
drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) {
mutex_enter(
&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |=
SATA_EVNT_MAIN;
mutex_exit(
&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
return;
}
if (rval_set == SATA_RETRY) {
mutex_exit(&SATA_CPORT_INFO(
sata_hba_inst,
saddr->cport)->cport_mutex);
goto done;
}
}
sdinfo->satadrv_state = SATA_DSTATE_FAILED;
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d - device failed",
saddr->cport);
DTRACE_PROBE(port_failed_f);
}
sdinfo->satadrv_event_flags |= SATA_EVNT_CLEAR_DEVICE_RESET;
sdinfo->satadrv_event_flags &= ~SATA_EVNT_INPROC_DEVICE_RESET;
sdinfo->satadrv_reset_time = 0;
return;
}
done:
if (rval_set == SATA_RETRY) {
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d - desired setting could not be "
"restored after reset. Device may not operate as expected.",
saddr->cport);
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
sdinfo->satadrv_reset_time = 0;
if ((cportinfo->cport_dev_type & SATA_VALID_DEV_TYPE) != 0) {
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
sdinfo->satadrv_event_flags &=
~SATA_EVNT_INPROC_DEVICE_RESET;
sdinfo->satadrv_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
}
}
static void
sata_process_pmdevice_reset(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_drive_info_t old_sdinfo;
sata_drive_info_t *sdinfo = NULL;
sata_cport_info_t *cportinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_pmult_info_t *pminfo = NULL;
sata_device_t sata_device;
uint8_t cport = saddr->cport;
uint8_t pmport = saddr->pmport;
int rval;
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing drive reset at port %d:%d", cport, pmport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, cport);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, cport, pmport);
if (((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) ||
(sdinfo->satadrv_state & SATA_DSTATE_FAILED) != 0) {
sdinfo->satadrv_event_flags &=
~(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET);
return;
}
if ((pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) == 0) {
#ifdef SATA_DEBUG
sata_log(sata_hba_inst, CE_WARN,
"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) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"No device reset event!!!!", NULL);
return;
}
if ((sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) ==
(SATA_EVNT_DEVICE_RESET | SATA_EVNT_INPROC_DEVICE_RESET)) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Overlapping device reset events!", NULL);
}
#endif
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d device reset", cport, pmport);
sdinfo->satadrv_event_flags &= ~SATA_EVNT_DEVICE_RESET;
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&pmportinfo->pmport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport,
saddr->pmport);
if (sdinfo != NULL)
sdinfo->satadrv_event_flags = 0;
mutex_exit(&pmportinfo->pmport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d:%d probing failed",
saddr->cport, saddr->pmport));
mutex_enter(&pmportinfo->pmport_mutex);
return;
}
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP ||
sata_device.satadev_type == SATA_DTYPE_NONE) {
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport,
saddr->pmport);
if (sdinfo != NULL) {
sdinfo->satadrv_event_flags &=
~SATA_EVNT_INPROC_DEVICE_RESET;
pminfo = SATA_PMULT_INFO(sata_hba_inst,
saddr->cport);
pminfo->pmult_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
return;
}
sdinfo = SATA_PMPORT_DRV_INFO(sata_hba_inst, saddr->cport,
saddr->pmport);
if (sdinfo == NULL) {
return;
}
if ((sdinfo->satadrv_event_flags &
SATA_EVNT_INPROC_DEVICE_RESET) == 0) {
sdinfo->satadrv_reset_time = ddi_get_lbolt();
}
sdinfo->satadrv_event_flags |= SATA_EVNT_INPROC_DEVICE_RESET;
old_sdinfo = *sdinfo;
mutex_exit(&pmportinfo->pmport_mutex);
if (sata_set_drive_features(sata_hba_inst, &old_sdinfo, 1) ==
SATA_FAILURE) {
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
sata_device.satadev_addr.qual = SATA_ADDR_PMPORT;
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&pmportinfo->pmport_mutex);
if (rval == SATA_SUCCESS &&
(sata_device.satadev_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) == 0 &&
(sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP &&
sata_device.satadev_type != SATA_DTYPE_NONE) {
if ((pmportinfo->pmport_dev_type &
SATA_VALID_DEV_TYPE) != 0 &&
SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL &&
sdinfo->satadrv_reset_time != 0) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time - sdinfo->satadrv_reset_time) <
drv_usectohz(SATA_DEV_REPROBE_TIMEOUT)) {
mutex_enter(
&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |=
SATA_EVNT_MAIN;
mutex_exit(
&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
return;
}
}
sdinfo->satadrv_state = SATA_DSTATE_FAILED;
sata_log(sata_hba_inst, CE_WARN,
"SATA device at port %d:%d - device failed",
saddr->cport, saddr->pmport);
} else {
sdinfo->satadrv_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
sdinfo->satadrv_event_flags &= ~SATA_EVNT_INPROC_DEVICE_RESET;
sdinfo->satadrv_reset_time = 0;
return;
}
mutex_enter(&pmportinfo->pmport_mutex);
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) {
sdinfo->satadrv_reset_time = 0;
if (pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) {
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
sdinfo->satadrv_event_flags &=
~SATA_EVNT_INPROC_DEVICE_RESET;
pminfo = SATA_PMULT_INFO(sata_hba_inst,
saddr->cport);
pminfo->pmult_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
}
}
static void
sata_process_port_link_events(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_device_t sata_device;
sata_cport_info_t *cportinfo;
sata_drive_info_t *sdinfo;
uint32_t event_flags;
int rval;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d link event(s)", saddr->cport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
event_flags = cportinfo->cport_event_flags;
cportinfo->cport_event_flags &=
~(SATA_EVNT_LINK_ESTABLISHED | SATA_EVNT_LINK_LOST);
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
return;
}
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d probing failed",
saddr->cport));
return;
} else {
cportinfo->cport_state |= SATA_STATE_PROBED | SATA_STATE_READY;
}
if (event_flags & SATA_EVNT_LINK_ESTABLISHED) {
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) != SATA_PORT_DEVLINK_UP) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring port %d link established event - "
"link down",
saddr->cport);
goto linklost;
}
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d link established event",
saddr->cport);
if (sata_device.satadev_type != SATA_DTYPE_NONE) {
if (cportinfo->cport_dev_type != SATA_DTYPE_NONE) {
ASSERT(SATA_CPORTINFO_DRV_INFO(cportinfo) !=
NULL);
sdinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
(SATA_CPORTINFO_DRV_INFO(cportinfo))->
satadrv_event_flags |=
SATA_EVNT_DEVICE_RESET;
}
} else if (cportinfo->cport_dev_type ==
SATA_DTYPE_NONE) {
if (!(SATA_FEATURES(sata_hba_inst) &
SATA_CTLF_HOTPLUG)) {
cportinfo->cport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
}
}
cportinfo->cport_link_lost_time = 0;
}
}
linklost:
if (event_flags & SATA_EVNT_LINK_LOST) {
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring port %d link lost event - link is up",
saddr->cport);
goto done;
}
#ifdef SATA_DEBUG
if (cportinfo->cport_link_lost_time == 0) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d link lost event",
saddr->cport);
}
#endif
if (!(SATA_FEATURES(sata_hba_inst) & SATA_CTLF_HOTPLUG)) {
if (cportinfo->cport_link_lost_time == 0) {
cportinfo->cport_link_lost_time =
ddi_get_lbolt();
cportinfo->cport_event_flags |=
SATA_EVNT_LINK_LOST;
} else {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time -
cportinfo->cport_link_lost_time) >=
drv_usectohz(
SATA_EVNT_LINK_LOST_TIMEOUT)) {
cportinfo->cport_event_flags |=
SATA_EVNT_DEVICE_DETACHED;
cportinfo->cport_link_lost_time = 0;
SATADBG1(SATA_DBG_EVENTS,
sata_hba_inst,
"Triggering port %d "
"device detached event",
saddr->cport);
} else {
cportinfo->cport_event_flags |=
SATA_EVNT_LINK_LOST;
}
}
}
}
done:
event_flags = cportinfo->cport_event_flags;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
if (event_flags != 0) {
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
}
static void
sata_process_pmport_link_events(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_device_t sata_device;
sata_pmport_info_t *pmportinfo = NULL;
sata_drive_info_t *sdinfo = NULL;
uint32_t event_flags;
uint8_t cport = saddr->cport;
uint8_t pmport = saddr->pmport;
int rval;
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d link event(s)",
cport, pmport);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
mutex_enter(&pmportinfo->pmport_mutex);
event_flags = pmportinfo->pmport_event_flags;
pmportinfo->pmport_event_flags &=
~(SATA_EVNT_LINK_ESTABLISHED | SATA_EVNT_LINK_LOST);
if ((pmportinfo->pmport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
mutex_exit(&pmportinfo->pmport_mutex);
return;
}
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport,
saddr->pmport));
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport,
saddr->pmport));
sata_update_pmport_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport,
saddr->pmport));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d:%d probing failed",
saddr->cport, saddr->pmport));
return;
} else {
pmportinfo->pmport_state |=
SATA_STATE_PROBED | SATA_STATE_READY;
}
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst,
saddr->cport, saddr->pmport));
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst,
saddr->cport, saddr->pmport));
if (event_flags & SATA_EVNT_LINK_ESTABLISHED) {
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) != SATA_PORT_DEVLINK_UP) {
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring port %d:%d link established event - "
"link down",
saddr->cport, saddr->pmport);
goto linklost;
}
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d link established event",
cport, pmport);
if (sata_device.satadev_type != SATA_DTYPE_NONE &&
sata_device.satadev_type != SATA_DTYPE_PMULT) {
if (pmportinfo->pmport_dev_type != SATA_DTYPE_NONE) {
ASSERT(SATA_PMPORTINFO_DRV_INFO(pmportinfo) !=
NULL);
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
if ((sdinfo->satadrv_type &
SATA_VALID_DEV_TYPE) != 0) {
(SATA_PMPORTINFO_DRV_INFO(pmportinfo))->
satadrv_event_flags |=
SATA_EVNT_DEVICE_RESET;
}
} else if (pmportinfo->pmport_dev_type ==
SATA_DTYPE_NONE) {
if (!(SATA_FEATURES(sata_hba_inst) &
SATA_CTLF_HOTPLUG)) {
pmportinfo->pmport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
}
}
pmportinfo->pmport_link_lost_time = 0;
}
}
linklost:
if (event_flags & SATA_EVNT_LINK_LOST) {
#ifdef SATA_DEBUG
if (pmportinfo->pmport_link_lost_time == 0) {
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d link lost event",
saddr->cport, saddr->pmport);
}
#endif
if ((sata_device.satadev_scr.sstatus &
SATA_PORT_DEVLINK_UP_MASK) == SATA_PORT_DEVLINK_UP) {
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring port %d:%d link lost event - link is up",
saddr->cport, saddr->pmport);
goto done;
}
if (!(SATA_FEATURES(sata_hba_inst) & SATA_CTLF_HOTPLUG)) {
if (pmportinfo->pmport_link_lost_time == 0) {
pmportinfo->pmport_link_lost_time =
ddi_get_lbolt();
pmportinfo->pmport_event_flags |=
SATA_EVNT_LINK_LOST;
} else {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time -
pmportinfo->pmport_link_lost_time) >=
drv_usectohz(
SATA_EVNT_LINK_LOST_TIMEOUT)) {
pmportinfo->pmport_event_flags |=
SATA_EVNT_DEVICE_DETACHED;
pmportinfo->pmport_link_lost_time = 0;
SATADBG2(SATA_DBG_EVENTS,
sata_hba_inst,
"Triggering port %d:%d "
"device detached event",
saddr->cport, saddr->pmport);
} else {
pmportinfo->pmport_event_flags |=
SATA_EVNT_LINK_LOST;
}
}
}
}
done:
event_flags = pmportinfo->pmport_event_flags;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, saddr->cport,
saddr->pmport));
if (event_flags != 0) {
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
}
static void
sata_process_device_detached(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo;
sata_pmport_info_t *pmportinfo;
sata_drive_info_t *sdevinfo;
sata_device_t sata_device;
sata_address_t pmport_addr;
char name[16];
uint8_t cport = saddr->cport;
int npmport;
int rval;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d device detached", saddr->cport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_DEVICE_DETACHED;
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
return;
}
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d probing failed",
saddr->cport));
return;
} else {
cportinfo->cport_state |= SATA_STATE_PROBED | SATA_STATE_READY;
}
if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) ==
SATA_PORT_DEVLINK_UP && sata_device.satadev_type !=
SATA_DTYPE_NONE) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring detach - device still attached to port %d",
sata_device.satadev_addr.cport);
return;
}
if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) {
for (npmport = 0; npmport < SATA_NUM_PMPORTS(sata_hba_inst,
cport); npmport ++) {
SATADBG2(SATA_DBG_PMULT|SATA_DBG_EVENTS_PROC,
sata_hba_inst,
"Detaching target node at port %d:%d",
cport, npmport);
mutex_exit(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
name[0] = '\0';
(void) sprintf(name, "%d.%d", cport, npmport);
ddi_remove_minor_node(SATA_DIP(sata_hba_inst), name);
sata_log(sata_hba_inst, CE_NOTE,
"Remove attachment point of port %d:%d",
cport, npmport);
pmport_addr.cport = cport;
pmport_addr.pmport = (uint8_t)npmport;
pmport_addr.qual = SATA_ADDR_PMPORT;
sata_remove_target_node(sata_hba_inst, &pmport_addr);
mutex_enter(&SATA_CPORT_MUTEX(sata_hba_inst, cport));
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
cport, npmport);
ASSERT(pmportinfo != NULL);
sdevinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
if (sdevinfo != NULL) {
(void) kmem_free((void *) sdevinfo,
sizeof (sata_drive_info_t));
}
(void) kmem_free((void *) pmportinfo,
sizeof (sata_pmport_info_t));
}
(void) kmem_free((void *)
SATA_CPORTINFO_PMULT_INFO(cportinfo),
sizeof (sata_pmult_info_t));
SATA_CPORTINFO_PMULT_INFO(cportinfo) = NULL;
sata_log(sata_hba_inst, CE_WARN,
"SATA port-multiplier detached at port %d", cport);
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
} else {
if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
(void) kmem_free((void *)sdevinfo,
sizeof (sata_drive_info_t));
}
sata_log(sata_hba_inst, CE_WARN,
"SATA device detached at port %d", cport);
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
sata_remove_target_node(sata_hba_inst, saddr);
}
sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_REMOVE);
}
static void
sata_process_pmdevice_detached(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_pmport_info_t *pmportinfo;
sata_drive_info_t *sdevinfo;
sata_device_t sata_device;
int rval;
uint8_t cport, pmport;
cport = saddr->cport;
pmport = saddr->pmport;
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d device detached",
cport, pmport);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
pmportinfo->pmport_event_flags &= ~SATA_EVNT_DEVICE_DETACHED;
if ((pmportinfo->pmport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
return;
}
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
sata_update_pmport_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d:%d probing failed",
saddr->pmport));
return;
} else {
pmportinfo->pmport_state |=
SATA_STATE_PROBED | SATA_STATE_READY;
}
if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) ==
SATA_PORT_DEVLINK_UP && sata_device.satadev_type !=
SATA_DTYPE_NONE) {
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring detach - device still attached to port %d",
sata_device.satadev_addr.pmport);
return;
}
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) {
sdevinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
(void) kmem_free((void *)sdevinfo,
sizeof (sata_drive_info_t));
}
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
mutex_exit(&SATA_PMPORT_MUTEX(sata_hba_inst, cport, pmport));
sata_remove_target_node(sata_hba_inst, saddr);
sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_REMOVE);
}
static void
sata_process_device_attached(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo = NULL;
sata_drive_info_t *sdevinfo = NULL;
sata_pmult_info_t *pmultinfo = NULL;
sata_pmport_info_t *pmportinfo = NULL;
sata_device_t sata_device;
dev_info_t *tdip;
uint32_t event_flags = 0, pmult_event_flags = 0;
int rval;
int npmport;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d device attached", saddr->cport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_DEVICE_ATTACHED;
if ((cportinfo->cport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
cportinfo->cport_dev_attach_time = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
return;
}
if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
sdevinfo = SATA_CPORTINFO_DRV_INFO(cportinfo);
SATA_CPORTINFO_DRV_INFO(cportinfo) = NULL;
(void) kmem_free((void *)sdevinfo,
sizeof (sata_drive_info_t));
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Arbitrarily detaching old device info.", NULL);
}
cportinfo->cport_dev_type = SATA_DTYPE_NONE;
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_update_port_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
cportinfo->cport_state = SATA_PSTATE_FAILED;
cportinfo->cport_dev_attach_time = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d probing failed",
saddr->cport));
return;
} else {
cportinfo->cport_state |= SATA_STATE_PROBED | SATA_STATE_READY;
}
if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP || sata_device.satadev_type ==
SATA_DTYPE_NONE) {
cportinfo->cport_dev_attach_time = 0;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring attach - no device connected to port %d",
sata_device.satadev_addr.cport);
return;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_INSERT);
sata_device.satadev_addr = *saddr;
(void) sata_reprobe_port(sata_hba_inst, &sata_device,
SATA_DEV_IDENTIFY_NORETRY);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_mutex);
if ((cportinfo->cport_state & SATA_STATE_READY) &&
(cportinfo->cport_dev_type != SATA_DTYPE_NONE)) {
if (cportinfo->cport_dev_type == SATA_DTYPE_UNKNOWN) {
if (cportinfo->cport_dev_attach_time != 0) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time -
cportinfo->cport_dev_attach_time) <
drv_usectohz(
SATA_DEV_IDENTIFY_TIMEOUT)) {
cportinfo->cport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
} else {
cportinfo->cport_dev_attach_time = 0;
sata_log(sata_hba_inst,
CE_WARN,
"Could not identify SATA device "
"at port %d",
saddr->cport);
}
} else {
cportinfo->cport_dev_attach_time =
ddi_get_lbolt();
cportinfo->cport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
}
} else if (cportinfo->cport_dev_type == SATA_DTYPE_PMULT) {
cportinfo->cport_dev_attach_time = 0;
sata_log(sata_hba_inst, CE_NOTE,
"SATA port-multiplier detected at port %d",
saddr->cport);
if (SATA_CPORTINFO_PMULT_INFO(cportinfo) != NULL) {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
sata_show_pmult_info(sata_hba_inst,
&sata_device);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
}
ASSERT(SATA_CPORTINFO_PMULT_INFO(cportinfo) != NULL);
pmultinfo = SATA_CPORTINFO_PMULT_INFO(cportinfo);
for (npmport = 0; npmport <
pmultinfo->pmult_num_dev_ports; npmport++) {
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst,
saddr->cport, npmport);
ASSERT(pmportinfo != NULL);
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_event_flags =
SATA_EVNT_LINK_ESTABLISHED;
pmult_event_flags |=
pmportinfo->pmport_event_flags;
mutex_exit(&pmportinfo->pmport_mutex);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
}
} else {
cportinfo->cport_dev_attach_time = 0;
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d", saddr->cport);
if (SATA_CPORTINFO_DRV_INFO(cportinfo) != NULL) {
sata_drive_info_t new_sdinfo;
new_sdinfo = *(SATA_CPORTINFO_DRV_INFO(
cportinfo));
sata_show_drive_info(sata_hba_inst,
&new_sdinfo);
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst),
saddr->cport, saddr->pmport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
if (tdip != NULL) {
#ifdef SATA_DEBUG
if ((cportinfo->cport_event_flags &
SATA_EVNT_TARGET_NODE_CLEANUP) == 0)
sata_log(sata_hba_inst, CE_WARN,
"sata_process_device_attached: "
"old device target node exists!");
#endif
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
rval = ndi_devi_offline(tdip,
NDI_DEVI_REMOVE);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
if (rval == NDI_SUCCESS) {
cportinfo->cport_event_flags &=
~SATA_EVNT_TARGET_NODE_CLEANUP;
cportinfo->cport_tgtnode_clean = B_TRUE;
} else {
sata_log(sata_hba_inst,
CE_WARN,
"Application(s) accessing "
"previously attached SATA "
"device have to release "
"it before newly inserted "
"device can be made accessible.",
saddr->cport);
cportinfo->cport_event_flags |=
SATA_EVNT_TARGET_NODE_CLEANUP;
cportinfo->cport_tgtnode_clean =
B_FALSE;
}
}
if (sata_auto_online != 0) {
cportinfo->cport_event_flags |=
SATA_EVNT_AUTOONLINE_DEVICE;
}
}
} else {
cportinfo->cport_dev_attach_time = 0;
}
event_flags = cportinfo->cport_event_flags;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
if (event_flags != 0 || pmult_event_flags != 0) {
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
}
static void
sata_process_pmdevice_attached(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_pmport_info_t *pmportinfo;
sata_drive_info_t *sdinfo;
sata_device_t sata_device;
dev_info_t *tdip;
uint32_t event_flags;
uint8_t cport = saddr->cport;
uint8_t pmport = saddr->pmport;
int rval;
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d:%d device attached", cport, pmport);
pmportinfo = SATA_PMPORT_INFO(sata_hba_inst, cport, pmport);
mutex_enter(&pmportinfo->pmport_mutex);
pmportinfo->pmport_event_flags &= ~SATA_EVNT_DEVICE_ATTACHED;
if ((pmportinfo->pmport_state &
(SATA_PSTATE_SHUTDOWN | SATA_PSTATE_FAILED)) != 0) {
pmportinfo->pmport_dev_attach_time = 0;
mutex_exit(&pmportinfo->pmport_mutex);
return;
}
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) {
sdinfo = SATA_PMPORTINFO_DRV_INFO(pmportinfo);
SATA_PMPORTINFO_DRV_INFO(pmportinfo) = NULL;
(void) kmem_free((void *)sdinfo,
sizeof (sata_drive_info_t));
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Arbitrarily detaching old device info.", NULL);
}
pmportinfo->pmport_dev_type = SATA_DTYPE_NONE;
sata_device.satadev_rev = SATA_DEVICE_REV;
sata_device.satadev_addr = *saddr;
mutex_exit(&pmportinfo->pmport_mutex);
rval = (*SATA_PROBE_PORT_FUNC(sata_hba_inst))
(SATA_DIP(sata_hba_inst), &sata_device);
mutex_enter(&pmportinfo->pmport_mutex);
sata_update_pmport_info(sata_hba_inst, &sata_device);
if (rval != SATA_SUCCESS) {
pmportinfo->pmport_state = SATA_PSTATE_FAILED;
pmportinfo->pmport_dev_attach_time = 0;
mutex_exit(&pmportinfo->pmport_mutex);
SATA_LOG_D((sata_hba_inst, CE_WARN,
"SATA port %d:%d probing failed", cport, pmport));
return;
} else {
pmportinfo->pmport_state |=
SATA_STATE_PROBED | SATA_STATE_READY;
}
if ((sata_device.satadev_scr.sstatus & SATA_PORT_DEVLINK_UP_MASK) !=
SATA_PORT_DEVLINK_UP || sata_device.satadev_type ==
SATA_DTYPE_NONE) {
pmportinfo->pmport_dev_attach_time = 0;
mutex_exit(&pmportinfo->pmport_mutex);
SATADBG2(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Ignoring attach - no device connected to port %d:%d",
cport, pmport);
return;
}
mutex_exit(&pmportinfo->pmport_mutex);
sata_gen_sysevent(sata_hba_inst, saddr, SE_HINT_INSERT);
sata_device.satadev_addr = *saddr;
(void) sata_reprobe_port(sata_hba_inst, &sata_device,
SATA_DEV_IDENTIFY_NORETRY);
mutex_enter(&pmportinfo->pmport_mutex);
if ((pmportinfo->pmport_state & SATA_STATE_READY) &&
(pmportinfo->pmport_dev_type != SATA_DTYPE_NONE)) {
if (pmportinfo->pmport_dev_type == SATA_DTYPE_UNKNOWN) {
if (pmportinfo->pmport_dev_attach_time != 0) {
clock_t cur_time = ddi_get_lbolt();
if ((cur_time -
pmportinfo->pmport_dev_attach_time) <
drv_usectohz(
SATA_DEV_IDENTIFY_TIMEOUT)) {
pmportinfo->pmport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
} else {
pmportinfo->pmport_dev_attach_time = 0;
sata_log(sata_hba_inst, CE_WARN,
"Could not identify SATA device "
"at port %d:%d",
cport, pmport);
}
} else {
pmportinfo->pmport_dev_attach_time =
ddi_get_lbolt();
pmportinfo->pmport_event_flags |=
SATA_EVNT_DEVICE_ATTACHED;
}
} else {
pmportinfo->pmport_dev_attach_time = 0;
sata_log(sata_hba_inst, CE_WARN,
"SATA device detected at port %d:%d",
cport, pmport);
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) {
sata_drive_info_t new_sdinfo;
new_sdinfo = *(SATA_PMPORTINFO_DRV_INFO(
pmportinfo));
sata_show_drive_info(sata_hba_inst,
&new_sdinfo);
}
mutex_exit(&pmportinfo->pmport_mutex);
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst),
saddr->cport, saddr->pmport);
mutex_enter(&pmportinfo->pmport_mutex);
if (tdip != NULL) {
#ifdef SATA_DEBUG
if ((pmportinfo->pmport_event_flags &
SATA_EVNT_TARGET_NODE_CLEANUP) == 0)
sata_log(sata_hba_inst, CE_WARN,
"sata_process_device_attached: "
"old device target node exists!");
#endif
mutex_exit(&pmportinfo->pmport_mutex);
rval = ndi_devi_offline(tdip,
NDI_DEVI_REMOVE);
mutex_enter(&pmportinfo->pmport_mutex);
if (rval == NDI_SUCCESS) {
pmportinfo->pmport_event_flags &=
~SATA_EVNT_TARGET_NODE_CLEANUP;
pmportinfo->pmport_tgtnode_clean =
B_TRUE;
} else {
sata_log(sata_hba_inst,
CE_WARN,
"Application(s) accessing "
"previously attached SATA "
"device have to release "
"it before newly inserted "
"device can be made accessible."
"at port %d:%d",
cport, pmport);
pmportinfo->pmport_event_flags |=
SATA_EVNT_TARGET_NODE_CLEANUP;
pmportinfo->pmport_tgtnode_clean =
B_FALSE;
}
}
if (sata_auto_online != 0) {
pmportinfo->pmport_event_flags |=
SATA_EVNT_AUTOONLINE_DEVICE;
}
}
} else {
pmportinfo->pmport_dev_attach_time = 0;
}
event_flags = pmportinfo->pmport_event_flags;
mutex_exit(&pmportinfo->pmport_mutex);
if (event_flags != 0) {
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
if (SATA_PMPORTINFO_DRV_INFO(pmportinfo) != NULL) {
if (pmportinfo->pmport_dev_type & SATA_VALID_DEV_TYPE) {
sata_pmult_info_t *pminfo =
SATA_PMULT_INFO(sata_hba_inst,
saddr->cport);
pminfo->pmult_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
}
}
}
static void
sata_process_target_node_cleanup(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo;
dev_info_t *tdip;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d device target node cleanup", saddr->cport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport,
saddr->pmport);
if (tdip != NULL) {
if (sata_check_device_removed(tdip) == B_TRUE) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"sata_process_target_node_cleanup: "
"old device target node exists!", NULL);
if (ndi_devi_offline(tdip, NDI_DEVI_REMOVE) ==
NDI_SUCCESS) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &=
~SATA_EVNT_TARGET_NODE_CLEANUP;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
return;
}
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
} else {
if (saddr->qual == SATA_ADDR_CPORT ||
saddr->qual == SATA_ADDR_DCPORT) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &=
~SATA_EVNT_TARGET_NODE_CLEANUP;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
} else {
if (SATA_CPORT_DEV_TYPE(sata_hba_inst, saddr->cport) !=
SATA_DTYPE_PMULT || SATA_PMULT_INFO(sata_hba_inst,
saddr->cport) == NULL)
return;
if (SATA_PMPORT_INFO(sata_hba_inst, saddr->cport,
saddr->pmport) == NULL)
return;
mutex_enter(&SATA_PMPORT_INFO(sata_hba_inst,
saddr->cport, saddr->pmport)->pmport_mutex);
SATA_PMPORT_INFO(sata_hba_inst, saddr->cport,
saddr->pmport)->pmport_event_flags &=
~SATA_EVNT_TARGET_NODE_CLEANUP;
mutex_exit(&SATA_PMPORT_INFO(sata_hba_inst,
saddr->cport, saddr->pmport)->pmport_mutex);
}
}
}
static void
sata_process_device_autoonline(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
sata_cport_info_t *cportinfo;
sata_drive_info_t *sdinfo;
sata_device_t sata_device;
dev_info_t *tdip;
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"Processing port %d attached device auto-onlining", saddr->cport);
cportinfo = SATA_CPORT_INFO(sata_hba_inst, saddr->cport);
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
if ((cportinfo->cport_dev_type & SATA_VALID_DEV_TYPE) == 0) {
cportinfo->cport_event_flags &= ~SATA_EVNT_AUTOONLINE_DEVICE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
return;
}
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
tdip = sata_get_target_dip(SATA_DIP(sata_hba_inst), saddr->cport,
saddr->pmport);
if (tdip != NULL) {
if (sata_check_device_removed(tdip) == B_TRUE) {
SATADBG1(SATA_DBG_EVENTS_PROC, sata_hba_inst,
"sata_process_device_autoonline: "
"old device target node exists!", NULL);
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
return;
}
} else {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
sata_device.satadev_addr = *saddr;
if (saddr->qual == SATA_ADDR_CPORT)
sata_device.satadev_addr.qual = SATA_ADDR_DCPORT;
else
sata_device.satadev_addr.qual = SATA_ADDR_DPMPORT;
sdinfo = sata_get_device_info(sata_hba_inst, &sata_device);
if (sdinfo != NULL) {
if (sdinfo->satadrv_event_flags &
(SATA_EVNT_DEVICE_RESET |
SATA_EVNT_INPROC_DEVICE_RESET))
sdinfo->satadrv_event_flags = 0;
sdinfo->satadrv_event_flags |=
SATA_EVNT_CLEAR_DEVICE_RESET;
cportinfo->cport_tgtnode_clean = B_TRUE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
tdip = sata_create_target_node(SATA_DIP(sata_hba_inst),
sata_hba_inst, &sata_device.satadev_addr);
if (tdip == NULL) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_process_device_autoonline: "
"configuring SATA device at port %d failed",
saddr->cport));
}
} else {
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
}
}
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->cport_mutex);
cportinfo->cport_event_flags &= ~SATA_EVNT_AUTOONLINE_DEVICE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
}
static void
sata_gen_sysevent(sata_hba_inst_t *sata_hba_inst, sata_address_t *saddr,
int hint)
{
char ap[MAXPATHLEN];
nvlist_t *ev_attr_list = NULL;
int err;
err = nvlist_alloc(&ev_attr_list, NV_UNIQUE_NAME_TYPE, DDI_NOSLEEP);
if (err != 0) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_gen_sysevent: "
"cannot allocate memory for sysevent attributes\n"));
return;
}
err = nvlist_add_string(ev_attr_list, DR_HINT, SE_HINT2STR(hint));
if (err != 0) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_gen_sysevent: "
"failed to add DR_HINT attr for sysevent"));
nvlist_free(ev_attr_list);
return;
}
(void) snprintf(ap, MAXPATHLEN, "/devices");
(void) ddi_pathname(SATA_DIP(sata_hba_inst), ap + strlen(ap));
(void) snprintf(ap + strlen(ap), MAXPATHLEN - strlen(ap), ":%d",
SATA_MAKE_AP_NUMBER(saddr->cport, saddr->pmport, saddr->qual));
err = nvlist_add_string(ev_attr_list, DR_AP_ID, ap);
if (err != 0) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_gen_sysevent: "
"failed to add DR_AP_ID attr for sysevent"));
nvlist_free(ev_attr_list);
return;
}
err = ddi_log_sysevent(SATA_DIP(sata_hba_inst), DDI_VENDOR_SUNW, EC_DR,
ESC_DR_AP_STATE_CHANGE, ev_attr_list, NULL, DDI_NOSLEEP);
if (err != DDI_SUCCESS) {
SATA_LOG_D((sata_hba_inst, CE_WARN,
"sata_gen_sysevent: "
"cannot log sysevent, err code %x\n", err));
}
nvlist_free(ev_attr_list);
}
static void
sata_set_device_removed(dev_info_t *tdip)
{
ASSERT(tdip != NULL);
ndi_devi_enter(tdip);
mutex_enter(&DEVI(tdip)->devi_lock);
DEVI_SET_DEVICE_REMOVED(tdip);
mutex_exit(&DEVI(tdip)->devi_lock);
ndi_devi_exit(tdip);
}
static void
sata_set_target_node_cleanup(sata_hba_inst_t *sata_hba_inst,
sata_address_t *saddr)
{
if (saddr->qual == SATA_ADDR_CPORT ||
saddr->qual == SATA_ADDR_DCPORT) {
mutex_enter(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
SATA_CPORT_EVENT_FLAGS(sata_hba_inst, saddr->cport) |=
SATA_EVNT_TARGET_NODE_CLEANUP;
SATA_CPORT_INFO(sata_hba_inst, saddr->cport)->
cport_tgtnode_clean = B_FALSE;
mutex_exit(&SATA_CPORT_INFO(sata_hba_inst,
saddr->cport)->cport_mutex);
} else {
mutex_enter(&SATA_PMPORT_INFO(sata_hba_inst,
saddr->cport, saddr->pmport)->pmport_mutex);
SATA_PMPORT_EVENT_FLAGS(sata_hba_inst, saddr->cport,
saddr->pmport) |= SATA_EVNT_TARGET_NODE_CLEANUP;
SATA_PMPORT_INFO(sata_hba_inst, saddr->cport, saddr->pmport)->
pmport_tgtnode_clean = B_FALSE;
mutex_exit(&SATA_PMPORT_INFO(sata_hba_inst,
saddr->cport, saddr->pmport)->pmport_mutex);
}
mutex_enter(&sata_hba_inst->satahba_mutex);
sata_hba_inst->satahba_event_flags |= SATA_EVNT_MAIN;
mutex_exit(&sata_hba_inst->satahba_mutex);
mutex_enter(&sata_mutex);
sata_event_pending |= SATA_EVNT_MAIN;
mutex_exit(&sata_mutex);
}
static boolean_t
sata_check_device_removed(dev_info_t *tdip)
{
ASSERT(tdip != NULL);
if (DEVI_IS_DEVICE_REMOVED(tdip))
return (B_TRUE);
else
return (B_FALSE);
}
static boolean_t
sata_check_for_dma_error(dev_info_t *dip, sata_pkt_txlate_t *spx)
{
int fm_capability = ddi_fm_capable(dip);
ddi_fm_error_t de;
if (fm_capability & DDI_FM_DMACHK_CAPABLE) {
if (spx->txlt_buf_dma_handle != NULL) {
ddi_fm_dma_err_get(spx->txlt_buf_dma_handle, &de,
DDI_FME_VERSION);
if (de.fme_status != DDI_SUCCESS)
return (B_TRUE);
}
}
return (B_FALSE);
}
#ifdef SATA_INJECT_FAULTS
static uint32_t sata_fault_count = 0;
static uint32_t sata_fault_suspend_count = 0;
static void
sata_inject_pkt_fault(sata_pkt_t *spkt, int *rval, int fault)
{
if (sata_inject_fault != SATA_INJECT_PKT_FAULT)
return;
if (sata_inject_fault_count == 0)
return;
if (fault == 0)
return;
if (sata_fault_cmd != spkt->satapkt_cmd.satacmd_cmd_reg)
return;
if (sata_fault_ctrl != NULL) {
sata_pkt_txlate_t *spx =
(sata_pkt_txlate_t *)spkt->satapkt_framework_private;
if (sata_fault_ctrl != NULL && sata_fault_ctrl !=
spx->txlt_sata_hba_inst->satahba_dip)
return;
if (sata_fault_device.satadev_addr.cport !=
spkt->satapkt_device.satadev_addr.cport ||
sata_fault_device.satadev_addr.pmport !=
spkt->satapkt_device.satadev_addr.pmport ||
sata_fault_device.satadev_addr.qual !=
spkt->satapkt_device.satadev_addr.qual)
return;
}
if (*rval != SATA_TRAN_ACCEPTED ||
spkt->satapkt_reason != SATA_PKT_COMPLETED) {
sata_fault_count = 0;
sata_fault_suspend_count = 0;
return;
}
if (sata_fault_count == 0 && sata_fault_suspend_count != 0) {
sata_fault_suspend_count -= 1;
return;
}
if (sata_fault_count == 0 && sata_fault_suspend_count == 0) {
if (sata_inject_fault_count != -1) {
sata_fault_count = sata_inject_fault_count;
sata_fault_suspend_count =
sata_inject_fault_pause_count;
if (sata_fault_suspend_count == 0)
sata_inject_fault_count = 0;
}
}
if (sata_fault_count != 0)
sata_fault_count -= 1;
switch (fault) {
case SATA_PKT_BUSY:
*rval = SATA_TRAN_BUSY;
spkt->satapkt_reason = SATA_PKT_BUSY;
break;
case SATA_PKT_QUEUE_FULL:
*rval = SATA_TRAN_QUEUE_FULL;
spkt->satapkt_reason = SATA_PKT_QUEUE_FULL;
break;
case SATA_PKT_CMD_UNSUPPORTED:
*rval = SATA_TRAN_CMD_UNSUPPORTED;
spkt->satapkt_reason = SATA_PKT_CMD_UNSUPPORTED;
break;
case SATA_PKT_PORT_ERROR:
*rval = SATA_TRAN_PORT_ERROR;
spkt->satapkt_reason = SATA_PKT_PORT_ERROR;
break;
case SATA_PKT_DEV_ERROR:
spkt->satapkt_reason = SATA_PKT_DEV_ERROR;
break;
case SATA_PKT_ABORTED:
spkt->satapkt_reason = SATA_PKT_ABORTED;
break;
case SATA_PKT_TIMEOUT:
spkt->satapkt_reason = SATA_PKT_TIMEOUT;
break;
case SATA_PKT_RESET:
spkt->satapkt_reason = SATA_PKT_RESET;
break;
default:
break;
}
}
#endif
void
sata_vtrace_debug(dev_info_t *dip, const char *fmt, va_list ap)
{
sata_trace_dmsg_t *dmsg;
if (sata_debug_rbuf == NULL) {
return;
}
if (sata_debug_rbuf->maxsize < (sizeof (sata_trace_dmsg_t))) {
return;
}
mutex_enter(&sata_debug_rbuf->lock);
dmsg = sata_trace_dmsg_alloc();
if (dmsg == NULL) {
mutex_exit(&sata_debug_rbuf->lock);
return;
}
dmsg->dip = dip;
gethrestime(&dmsg->timestamp);
(void) vsnprintf(dmsg->buf, sizeof (dmsg->buf), fmt, ap);
mutex_exit(&sata_debug_rbuf->lock);
}
void
sata_trace_debug(dev_info_t *dip, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
sata_vtrace_debug(dip, fmt, ap);
va_end(ap);
}
static sata_trace_dmsg_t *
sata_trace_dmsg_alloc(void)
{
sata_trace_dmsg_t *dmsg_alloc, *dmsg = sata_debug_rbuf->dmsgp;
if (sata_debug_rbuf->looped == TRUE) {
sata_debug_rbuf->dmsgp = dmsg->next;
return (sata_debug_rbuf->dmsgp);
}
if (((sata_debug_rbuf->size + (sizeof (sata_trace_dmsg_t))) >
sata_debug_rbuf->maxsize) && (sata_debug_rbuf->dmsgh != NULL)) {
dmsg->next = sata_debug_rbuf->dmsgh;
sata_debug_rbuf->dmsgp = sata_debug_rbuf->dmsgh;
sata_debug_rbuf->looped = TRUE;
return (sata_debug_rbuf->dmsgp);
}
dmsg_alloc = kmem_zalloc(sizeof (sata_trace_dmsg_t), KM_NOSLEEP);
if (dmsg_alloc == NULL) {
sata_debug_rbuf->allocfailed++;
return (dmsg_alloc);
} else {
sata_debug_rbuf->size += sizeof (sata_trace_dmsg_t);
}
if (sata_debug_rbuf->dmsgp != NULL) {
dmsg->next = dmsg_alloc;
sata_debug_rbuf->dmsgp = dmsg->next;
return (sata_debug_rbuf->dmsgp);
} else {
if (sata_debug_rbuf->dmsgh == NULL) {
sata_debug_rbuf->dmsgh = dmsg_alloc;
} else {
kmem_free(dmsg_alloc, sizeof (sata_trace_dmsg_t));
return (NULL);
}
sata_debug_rbuf->dmsgp = dmsg_alloc;
return (sata_debug_rbuf->dmsgp);
}
}
static void
sata_trace_dmsg_free(void)
{
sata_trace_dmsg_t *dmsg_next, *dmsg = sata_debug_rbuf->dmsgh;
while (dmsg != NULL) {
dmsg_next = dmsg->next;
kmem_free(dmsg, sizeof (sata_trace_dmsg_t));
if (dmsg_next == sata_debug_rbuf->dmsgh) {
break;
} else {
dmsg = dmsg_next;
}
}
}
static void
sata_trace_rbuf_alloc(void)
{
sata_debug_rbuf = kmem_zalloc(sizeof (sata_trace_rbuf_t), KM_SLEEP);
mutex_init(&sata_debug_rbuf->lock, NULL, MUTEX_DRIVER, NULL);
if (dmsg_ring_size > 0) {
sata_debug_rbuf->maxsize = (size_t)dmsg_ring_size;
}
}
static void
sata_trace_rbuf_free(void)
{
sata_trace_dmsg_free();
mutex_destroy(&sata_debug_rbuf->lock);
kmem_free(sata_debug_rbuf, sizeof (sata_trace_rbuf_t));
}
#ifndef SATA_DEBUG
static void
sata_trace_log(sata_hba_inst_t *sata_hba_inst, uint_t level __unused,
const char *fmt, ...)
{
dev_info_t *dip = NULL;
va_list ap;
if (sata_hba_inst != NULL) {
dip = SATA_DIP(sata_hba_inst);
}
va_start(ap, fmt);
sata_vtrace_debug(dip, fmt, ap);
va_end(ap);
}
#endif