diff --git a/sys/cam/cam.c b/sys/cam/cam.c new file mode 100644 index 000000000000..8be456820567 --- /dev/null +++ b/sys/cam/cam.c @@ -0,0 +1,108 @@ +/* + * Generic utility routines for the Common Access Method layer. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include + +#include + +void +cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen) +{ + + /* Trim leading/trailing spaces. */ + while (srclen > 0 && src[0] == ' ') + src++, srclen--; + while (srclen > 0 && src[srclen-1] == ' ') + srclen--; + + while (srclen > 0 && dstlen > 1) { + u_int8_t *cur_pos = dst; + + if (*src < 0x20 || *src >= 0x80) { + /* SCSI-II Specifies that these should never occur. */ + /* non-printable character */ + if (dstlen > 4) { + *cur_pos++ = '\\'; + *cur_pos++ = ((*src & 0300) >> 6) + '0'; + *cur_pos++ = ((*src & 0070) >> 3) + '0'; + *cur_pos++ = ((*src & 0007) >> 0) + '0'; + } else { + *cur_pos++ = '?'; + } + } else { + /* normal character */ + *cur_pos++ = *src; + } + src++; + srclen--; + dstlen -= cur_pos - dst; + dst = cur_pos; + } + *dst = '\0'; +} + +/* + * Compare string with pattern, returning 0 on match. + * Short pattern matches trailing blanks in name, + * wildcard '*' in pattern matches rest of name, + * wildcard '?' matches a single non-space character. + */ +int +cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, int str_len) +{ + + while (*pattern != '\0'&& str_len > 0) { + + if (*pattern == '*') { + return (0); + } + if ((*pattern != *str) + && (*pattern != '?' || *str == ' ')) { + return (1); + } + pattern++; + str++; + str_len--; + } + while (str_len > 0 && *str++ == ' ') + str_len--; + + return (str_len); +} + +caddr_t +cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, + int entry_size, cam_quirkmatch_t *comp_func) +{ + for (; num_entries > 0; num_entries--, quirk_table += entry_size) { + if ((*comp_func)(target, quirk_table) == 0) + return (quirk_table); + } + return (NULL); +} diff --git a/sys/cam/cam.h b/sys/cam/cam.h new file mode 100644 index 000000000000..21aea0257726 --- /dev/null +++ b/sys/cam/cam.h @@ -0,0 +1,165 @@ +/* + * Data structures and definitions for the CAM system. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_H +#define _CAM_CAM_H 1 + +#ifdef KERNEL +#include +#endif /* KERNEL */ + +#include + +typedef u_int path_id_t; +typedef u_int target_id_t; +typedef u_int lun_id_t; + +#define CAM_XPT_PATH_ID ((path_id_t)~0) +#define CAM_BUS_WILDCARD ((path_id_t)~0) +#define CAM_TARGET_WILDCARD ((target_id_t)~0) +#define CAM_LUN_WILDCARD ((lun_id_t)~0) + +/* + * Maximum length for a CAM CDB. + */ +#define CAM_MAX_CDBLEN 16 + +/* + * Definition of a CAM peripheral driver entry. Peripheral drivers instantiate + * one of these for each device they wish to communicate with and pass it into + * the xpt layer when they wish to schedule work on that device via the + * xpt_schecule API. + */ +struct cam_periph; + +/* + * Priority information for a CAM structure. The generation number is + * incremented everytime a new entry is entered into the queue giving round + * robin per priority level scheduling. + */ +typedef struct { + u_int32_t priority; +#define CAM_PRIORITY_NONE (u_int32_t)-1 + u_int32_t generation; + int index; +#define CAM_UNQUEUED_INDEX -1 +#define CAM_ACTIVE_INDEX -2 +#define CAM_DONEQ_INDEX -3 +} cam_pinfo; + +/* CAM flags */ +typedef enum { + CAM_FLAG_NONE = 0x00, + CAM_EXPECT_INQ_CHANGE = 0x01 +} cam_flags; + +/* CAM Status field values */ +typedef enum { + CAM_REQ_INPROG, /* CCB request is in progress */ + CAM_REQ_CMP, /* CCB request completed without error */ + CAM_REQ_ABORTED, /* CCB request aborted by the host */ + CAM_UA_ABORT, /* Unable to abort CCB request */ + CAM_REQ_CMP_ERR, /* CCB request completed with an error */ + CAM_BUSY, /* CAM subsytem is busy */ + CAM_REQ_INVALID, /* CCB request was invalid */ + CAM_PATH_INVALID, /* Supplied Path ID is invalid */ + CAM_DEV_NOT_THERE, /* SCSI Device Not Installed/there */ + CAM_UA_TERMIO, /* Unable to terminate I/O CCB request */ + CAM_SEL_TIMEOUT, /* Target Selection Timeout */ + CAM_CMD_TIMEOUT, /* Command timeout */ + CAM_SCSI_STATUS_ERROR, /* SCSI error, look at error code in CCB */ + CAM_MSG_REJECT_REC, /* Message Reject Reveived */ + CAM_SCSI_BUS_RESET, /* SCSI Bus Reset Sent/Received */ + CAM_UNCOR_PARITY, /* Uncorrectable parity error occurred */ + CAM_AUTOSENSE_FAIL = 0x10,/* Autosense: request sense cmd fail */ + CAM_NO_HBA, /* No HBA Detected error */ + CAM_DATA_RUN_ERR, /* Data Overrun error */ + CAM_UNEXP_BUSFREE, /* Unexpected Bus Free */ + CAM_SEQUENCE_FAIL, /* Target Bus Phase Sequence Failure */ + CAM_CCB_LEN_ERR, /* CCB length supplied is inadequate */ + CAM_PROVIDE_FAIL, /* Unable to provide requested capability */ + CAM_BDR_SENT, /* A SCSI BDR msg was sent to target */ + CAM_REQ_TERMIO, /* CCB request terminated by the host */ + CAM_UNREC_HBA_ERROR, /* Unrecoverable Host Bus Adapter Error */ + CAM_REQ_TOO_BIG, /* The request was too large for this host */ + CAM_REQUEUE_REQ, /* + * This request should be requeued to preserve + * transaction ordering. This typically occurs + * when the SIM recognizes an error that should + * freeze the queue and must place additional + * requests for the target at the sim level + * back into the XPT queue. + */ + CAM_IDE = 0x33, /* Initiator Detected Error */ + CAM_RESRC_UNAVAIL, /* Resource Unavailable */ + CAM_UNACKED_EVENT, /* Unacknowledged Event by Host */ + CAM_MESSAGE_RECV, /* Message Received in Host Target Mode */ + CAM_INVALID_CDB, /* Invalid CDB received in Host Target Mode */ + CAM_LUN_INVALID, /* Lun supplied is invalid */ + CAM_TID_INVALID, /* Target ID supplied is invalid */ + CAM_FUNC_NOTAVAIL, /* The requested function is not available */ + CAM_NO_NEXUS, /* Nexus is not established */ + CAM_IID_INVALID, /* The initiator ID is invalid */ + CAM_CDB_RECVD, /* The SCSI CDB has been received */ + CAM_LUN_ALRDY_ENA, /* The LUN is already eanbeld for target mode */ + CAM_SCSI_BUSY, /* SCSI Bus Busy */ + + CAM_DEV_QFRZN = 0x40, /* The DEV queue is frozen w/this err */ + + /* Autosense data valid for target */ + CAM_AUTOSNS_VALID = 0x80, + CAM_RELEASE_SIMQ = 0x100,/* SIM ready to take more commands */ + CAM_SIM_QUEUED = 0x200,/* SIM has this command in it's queue */ + + CAM_STATUS_MASK = 0x3F, /* Mask bits for just the status # */ +} cam_status; + +__BEGIN_DECLS +typedef int (cam_quirkmatch_t)(caddr_t, caddr_t); + +caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, + int entry_size, cam_quirkmatch_t *comp_func); + +void cam_strvis(u_int8_t *dst, const u_int8_t *src, int srclen, int dstlen); + +int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, int str_len); +__END_DECLS + +#ifdef KERNEL +static __inline void cam_init_pinfo(cam_pinfo *pinfo); + +static __inline void cam_init_pinfo(cam_pinfo *pinfo) +{ + pinfo->priority = CAM_PRIORITY_NONE; + pinfo->index = CAM_UNQUEUED_INDEX; +} +#endif /* KERNEL */ + +#endif /* _CAM_CAM_H */ diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h new file mode 100644 index 000000000000..1e80e37885ff --- /dev/null +++ b/sys/cam/cam_ccb.h @@ -0,0 +1,779 @@ +/* + * Data structures and definitions for CAM Control Blocks (CCBs). + * + * Copyright (c) 1997, 1998 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_CCB_H +#define _CAM_CAM_CCB_H 1 + +#include +#include +#ifndef KERNEL +#include +#endif +#include +#include + + +/* General allocation length definitions for CCB structures */ +#define IOCDBLEN CAM_MAX_CDBLEN /* Space for CDB bytes/pointer */ +#define VUHBALEN 14 /* Vendor Unique HBA length */ +#define SIM_IDLEN 16 /* ASCII string len for SIM ID */ +#define HBA_IDLEN 16 /* ASCII string len for HBA ID */ +#define DEV_IDLEN 16 /* ASCII string len for device names */ +#define CCB_PERIPH_PRIV_SIZE 2 /* size of peripheral private area */ +#define CCB_SIM_PRIV_SIZE 2 /* size of sim private area */ + +/* Struct definitions for CAM control blocks */ + +/* Common CCB header */ +/* CAM CCB flags */ +typedef enum { + CAM_CDB_POINTER = 0x00000001,/* The CDB field is a pointer */ + CAM_QUEUE_ENABLE = 0x00000002,/* SIM queue actions are enabled */ + CAM_CDB_LINKED = 0x00000004,/* CCB contains a linked CDB */ + CAM_SCATTER_VALID = 0x00000010,/* Scatter/gather list is valid */ + CAM_DIS_AUTOSENSE = 0x00000020,/* Disable autosense feature */ + CAM_DIR_RESV = 0x00000000,/* Data direction (00:reserved) */ + CAM_DIR_IN = 0x00000040,/* Data direction (01:DATA IN) */ + CAM_DIR_OUT = 0x00000080,/* Data direction (10:DATA OUT) */ + CAM_DIR_NONE = 0x000000C0,/* Data direction (11:no data) */ + CAM_DIR_MASK = 0x000000C0,/* Data direction Mask */ + CAM_SOFT_RST_OP = 0x00000100,/* Use Soft reset alternative */ + CAM_ENG_SYNC = 0x00000200,/* Flush resid bytes on complete */ + CAM_DEV_QFRZDIS = 0x00000400,/* Disable DEV Q freezing */ + CAM_DEV_QFREEZE = 0x00000800,/* Freeze DEV Q on execution */ + CAM_HIGH_POWER = 0x00001000,/* Command takes a lot of power */ + CAM_SENSE_PTR = 0x00002000,/* Sense data is a pointer */ + CAM_SENSE_PHYS = 0x00004000,/* Sense pointer is physical addr*/ + CAM_TAG_ACTION_VALID = 0x00008000,/* Use the tag action in this ccb*/ + CAM_PASS_ERR_RECOVER = 0x00010000,/* Pass driver does err. recovery*/ + CAM_DIS_DISCONNECT = 0x00020000,/* Disable disconnect */ + CAM_SG_LIST_PHYS = 0x00040000,/* SG list has physical addrs. */ + CAM_MSG_BUF_PHYS = 0x00080000,/* Message buffer ptr is physical*/ + CAM_SNS_BUF_PHYS = 0x00100000,/* Autosense data ptr is physical*/ + CAM_DATA_PHYS = 0x00200000,/* SG/Buffer data ptrs are phys. */ + CAM_CDB_PHYS = 0x00400000,/* CDB poiner is physical */ + CAM_ENG_SGLIST = 0x00800000,/* SG list is for the HBA engine */ + +/* Phase cognizant mode flags */ + CAM_DIS_AUTOSRP = 0x01000000,/* Diable autosave/restore ptrs */ + CAM_DIS_AUTODISC = 0x02000000,/* Disable auto disconnect */ + CAM_TGT_CCB_AVAIL = 0x04000000,/* Target CCB available */ + CAM_TGT_PHASE_MODE = 0x08000000,/* The SIM runs in phase mode */ + CAM_MSGB_VALID = 0x20000000,/* Message buffer valid */ + CAM_STATUS_VALID = 0x40000000,/* Status buffer valid */ + CAM_DATAB_VALID = 0x80000000,/* Data buffer valid */ + +/* Host target Mode flags */ + CAM_TERM_IO = 0x20000000,/* Terminate I/O Message sup. */ + CAM_DISCONNECT = 0x40000000,/* Disconnects are mandatory */ + CAM_SEND_STATUS = 0x80000000,/* Send status after data phase */ +} ccb_flags; + +/* XPT Opcodes for xpt_action */ +typedef enum { +/* Common function commands: 0x00->0x0F */ + XPT_NOOP, /* Execute Nothing */ + XPT_SCSI_IO, /* Execute the requested I/O operation */ + XPT_GDEV_TYPE, /* Get type information for specified device */ + XPT_GDEVLIST, /* Get a list of peripheral devices */ + XPT_PATH_INQ, /* Path routing inquiry */ + XPT_REL_SIMQ, /* Release a frozen SIM queue */ + XPT_SASYNC_CB, /* Set Asynchronous Callback Parameters */ + XPT_SDEV_TYPE, /* Set device type information */ + XPT_SCAN_BUS, /* (Re)Scan the SCSI Bus */ + XPT_DEV_MATCH, /* Get EDT entries matching the given pattern */ + XPT_DEBUG, /* Turn on debugging for a bus, target or lun */ +/* SCSI Control Functions: 0x10->0x1F */ + XPT_ABORT = 0x10, /* Abort the specified CCB */ + XPT_RESET_BUS, /* Reset the specified SCSI bus */ + XPT_RESET_DEV, /* Bus Device Reset the specified SCSI device */ + XPT_TERM_IO, /* Terminate the I/O process */ + XPT_SCAN_LUN, /* Scan Logical Unit */ + XPT_GET_TRAN_SETTINGS, /* + * Get default/user transfer settings + * for the target + */ + XPT_SET_TRAN_SETTINGS, /* + * Set transfer rate/width + * negotiation settings + */ + XPT_CALC_GEOMETRY, /* + * Calculate the geometry parameters for + * a device give the sector size and + * volume size. + */ + +/* HBA engine commands 0x20->0x2F */ + XPT_ENG_INQ = 0x20, /* HBA engine feature inquiry */ + XPT_ENG_EXEC, /* HBA execute engine request */ + +/* Target mode commands: 0x30->0x3F */ + XPT_EN_LUN = 0x30, /* Enable LUN as a target */ + XPT_TARGET_IO, /* Execute target I/O request */ + XPT_ACCEPT_TARGET_IO, /* Accept Host Target Mode CDB */ + XPT_CONT_TARGET_IO, /* Continue Host Target I/O Connection */ + XPT_IMMED_NOTIFY, /* Notify Host Target driver of event */ + XPT_NOTIFY_ACK, /* Acknowledgement of event */ + +/* Vendor Unique codes: 0x80->0x8F */ + XPT_VUNIQUE = 0x80 +} xpt_opcode; + +typedef union { + LIST_ENTRY(ccb_hdr) le; + SLIST_ENTRY(ccb_hdr) sle; + TAILQ_ENTRY(ccb_hdr) tqe; + STAILQ_ENTRY(ccb_hdr) stqe; +} camq_entry; + +typedef union { + void *ptr; + u_long field; + u_int8_t bytes[sizeof(void *) > sizeof(u_long) + ? sizeof(void *) : sizeof(u_long)]; +} ccb_priv_entry; + +typedef union { + ccb_priv_entry entries[CCB_PERIPH_PRIV_SIZE]; + u_int8_t bytes[CCB_PERIPH_PRIV_SIZE * sizeof(ccb_priv_entry)]; +} ccb_ppriv_area; + +typedef union { + ccb_priv_entry entries[CCB_SIM_PRIV_SIZE]; + u_int8_t bytes[CCB_SIM_PRIV_SIZE * sizeof(ccb_priv_entry)]; +} ccb_spriv_area; + +struct ccb_hdr { + cam_pinfo pinfo; /* Info for priority scheduling */ + camq_entry xpt_links; /* For chaining in the XPT layer */ + camq_entry sim_links; /* For chaining in the SIM layer */ + camq_entry periph_links;/* For chaining in the type driver */ + u_int32_t retry_count; + /* Callback on completion function */ + void (*cbfcnp)(struct cam_periph *, union ccb *); + xpt_opcode func_code; /* XPT function code */ + u_int32_t status; /* Status returned by CAM subsystem */ + /* Compiled path for this ccb */ + struct cam_path *path; + path_id_t path_id; /* Path ID for the request */ + target_id_t target_id; /* Target device ID */ + lun_id_t target_lun; /* Target LUN number */ + u_int32_t flags; + ccb_ppriv_area periph_priv; + ccb_spriv_area sim_priv; + u_int32_t timeout; /* Timeout value */ + /* Callout handle used for timeouts */ + struct callout_handle timeout_ch; +}; + +/* Get Device Information CCB */ +struct ccb_getdev { + struct ccb_hdr ccb_h; + struct scsi_inquiry_data inq_data; + u_int8_t serial_num[252]; + u_int8_t serial_num_len; + u_int8_t pd_type; /* returned peripheral device type */ + int dev_openings; /* Space left for more work on device*/ + int dev_active; /* Transactions running on the device */ + int devq_openings;/* Space left for more queued work */ + int devq_queued; /* Transactions queued to be sent */ + int held; /* + * CCBs held by peripheral drivers + * for this device + */ + u_int8_t maxtags; /* + * Boundary conditions for number of + * tagged operations + */ + u_int8_t mintags; +}; + + +typedef enum { + CAM_GDEVLIST_LAST_DEVICE, + CAM_GDEVLIST_LIST_CHANGED, + CAM_GDEVLIST_MORE_DEVS, + CAM_GDEVLIST_ERROR +} ccb_getdevlist_status_e; + +struct ccb_getdevlist { + struct ccb_hdr ccb_h; + char periph_name[DEV_IDLEN]; + u_int32_t unit_number; + unsigned int generation; + u_int32_t index; + ccb_getdevlist_status_e status; +}; + +typedef enum { + PERIPH_MATCH_NONE = 0x000, + PERIPH_MATCH_PATH = 0x001, + PERIPH_MATCH_TARGET = 0x002, + PERIPH_MATCH_LUN = 0x004, + PERIPH_MATCH_NAME = 0x008, + PERIPH_MATCH_UNIT = 0x010, + PERIPH_MATCH_ANY = 0x01f +} periph_pattern_flags; + +struct periph_match_pattern { + char periph_name[DEV_IDLEN]; + u_int32_t unit_number; + path_id_t path_id; + target_id_t target_id; + lun_id_t target_lun; + periph_pattern_flags flags; +}; + +typedef enum { + DEV_MATCH_NONE = 0x000, + DEV_MATCH_PATH = 0x001, + DEV_MATCH_TARGET = 0x002, + DEV_MATCH_LUN = 0x004, + DEV_MATCH_INQUIRY = 0x008, + DEV_MATCH_ANY = 0x00f +} dev_pattern_flags; + +struct device_match_pattern { + path_id_t path_id; + target_id_t target_id; + lun_id_t target_lun; + struct scsi_static_inquiry_pattern inq_pat; + dev_pattern_flags flags; +}; + +typedef enum { + BUS_MATCH_NONE = 0x000, + BUS_MATCH_PATH = 0x001, + BUS_MATCH_NAME = 0x002, + BUS_MATCH_UNIT = 0x004, + BUS_MATCH_BUS_ID = 0x008, + BUS_MATCH_ANY = 0x00f +} bus_pattern_flags; + +struct bus_match_pattern { + path_id_t path_id; + char dev_name[DEV_IDLEN]; + u_int32_t unit_number; + u_int32_t bus_id; + bus_pattern_flags flags; +}; + +union match_pattern { + struct periph_match_pattern periph_pattern; + struct device_match_pattern device_pattern; + struct bus_match_pattern bus_pattern; +}; + +typedef enum { + DEV_MATCH_PERIPH, + DEV_MATCH_DEVICE, + DEV_MATCH_BUS +} dev_match_type; + +struct dev_match_pattern { + dev_match_type type; + union match_pattern pattern; +}; + +struct periph_match_result { + char periph_name[DEV_IDLEN]; + u_int32_t unit_number; + path_id_t path_id; + target_id_t target_id; + lun_id_t target_lun; +}; + +struct device_match_result { + path_id_t path_id; + target_id_t target_id; + lun_id_t target_lun; + struct scsi_inquiry_data inq_data; +}; + +struct bus_match_result { + path_id_t path_id; + char dev_name[DEV_IDLEN]; + u_int32_t unit_number; + u_int32_t bus_id; +}; + +union match_result { + struct periph_match_result periph_result; + struct device_match_result device_result; + struct bus_match_result bus_result; +}; + +struct dev_match_result { + dev_match_type type; + union match_result result; +}; + +typedef enum { + CAM_DEV_MATCH_LAST, + CAM_DEV_MATCH_MORE, + CAM_DEV_MATCH_LIST_CHANGED, + CAM_DEV_MATCH_SIZE_ERROR, + CAM_DEV_MATCH_ERROR +} ccb_dev_match_status; + +typedef enum { + CAM_DEV_POS_NONE = 0x000, + CAM_DEV_POS_BUS = 0x001, + CAM_DEV_POS_TARGET = 0x002, + CAM_DEV_POS_DEVICE = 0x004, + CAM_DEV_POS_PERIPH = 0x008, + CAM_DEV_POS_PDPTR = 0x010, + CAM_DEV_POS_TYPEMASK = 0xf00, + CAM_DEV_POS_EDT = 0x100, + CAM_DEV_POS_PDRV = 0x200 +} dev_pos_type; + +struct ccb_dm_cookie { + void *bus; + void *target; + void *device; + void *periph; + void *pdrv; +}; + +struct ccb_dev_position { + u_int generations[4]; +#define CAM_BUS_GENERATION 0x00 +#define CAM_TARGET_GENERATION 0x01 +#define CAM_DEV_GENERATION 0x02 +#define CAM_PERIPH_GENERATION 0x03 + dev_pos_type position_type; + struct ccb_dm_cookie cookie; +}; + +struct ccb_dev_match { + struct ccb_hdr ccb_h; + ccb_dev_match_status status; + u_int32_t num_patterns; + u_int32_t pattern_buf_len; + struct dev_match_pattern *patterns; + u_int32_t num_matches; + u_int32_t match_buf_len; + struct dev_match_result *matches; + struct ccb_dev_position pos; +}; + +/* + * Definitions for the path inquiry CCB fields. + */ +#define CAM_VERSION 0x10 /* Hex value for current version */ + +typedef enum { + PI_MDP_ABLE = 0x80, /* Supports MDP message */ + PI_WIDE_32 = 0x40, /* Supports 32 bit wide SCSI */ + PI_WIDE_16 = 0x20, /* Supports 16 bit wide SCSI */ + PI_SDTR_ABLE = 0x10, /* Supports SDTR message */ + PI_LINKED_CDB = 0x08, /* Supports linked CDBs */ + PI_TAG_ABLE = 0x02, /* Supports tag queue messages */ + PI_SOFT_RST = 0x01, /* Supports soft reset alternative */ +} pi_inqflag; + +typedef enum { + PIT_PROCESSOR = 0x80, /* Target mode processor mode */ + PIT_PHASE = 0x40, /* Target mode phase cog. mode */ + PIT_DISCONNECT = 0x20, /* Disconnects supported in target mode */ + PIT_TERM_IO = 0x10, /* Terminate I/O message supported in TM */ + PIT_GRP_6 = 0x08, /* Group 6 commands supported */ + PIT_GRP_7 = 0x04, /* Group 7 commands supported */ +} pi_tmflag; + +typedef enum { + PIM_SCANHILO = 0x80, /* Bus scans from high ID to low ID */ + PIM_NOREMOVE = 0x40, /* Removeable devices not included in scan */ + PIM_NOINQUIRY = 0x20, /* Inquiry data not kept by XPT */ +} pi_miscflag; + +/* Path Inquiry CCB */ +struct ccb_pathinq { + struct ccb_hdr ccb_h; + u_int8_t version_num; /* Version number for the SIM/HBA */ + u_int8_t hba_inquiry; /* Mimic of INQ byte 7 for the HBA */ + u_int8_t target_sprt; /* Flags for target mode support */ + u_int8_t hba_misc; /* Misc HBA features */ + u_int16_t hba_eng_cnt; /* HBA engine count */ + /* Vendor Unique capabilities */ + u_int8_t vuhba_flags[VUHBALEN]; + u_int32_t max_target; /* Maximum supported Target */ + u_int32_t max_lun; /* Maximum supported Lun */ + u_int32_t async_flags; /* Installed Async handlers */ + path_id_t hpath_id; /* Highest Path ID in the subsystem */ + target_id_t initiator_id; /* ID of the HBA on the SCSI bus */ + char sim_vid[SIM_IDLEN]; /* Vendor ID of the SIM */ + char hba_vid[HBA_IDLEN]; /* Vendor ID of the HBA */ + char dev_name[DEV_IDLEN];/* Device name for SIM */ + u_int32_t unit_number; /* Unit number for SIM */ + u_int32_t bus_id; /* Bus ID for SIM */ +}; + +typedef union { + u_int8_t *sense_ptr; /* + * Pointer to storage + * for sense information + */ + /* Storage Area for sense information */ + struct scsi_sense_data sense_buf; +} sense_t; + +typedef union { + u_int8_t *cdb_ptr; /* Pointer to the CDB bytes to send */ + /* Area for the CDB send */ + u_int8_t cdb_bytes[IOCDBLEN]; +} cdb_t; + +/* + * SCSI I/O Request CCB used for the XPT_SCSI_IO and XPT_CONT_TARGET_IO + * function codes. + */ +struct ccb_scsiio { + struct ccb_hdr ccb_h; + union ccb *next_ccb; /* Ptr for next CCB for action */ + u_int8_t *req_map; /* Ptr to mapping info */ + u_int8_t *data_ptr; /* Ptr to the data buf/SG list */ + u_int32_t dxfer_len; /* Data transfer length */ + /* Autosense storage */ + struct scsi_sense_data sense_data; + u_int8_t sense_len; /* Number of bytes to autosense */ + u_int8_t cdb_len; /* Number of bytes for the CDB */ + u_int16_t sglist_cnt; /* Number of SG list entries */ + u_int8_t scsi_status; /* Returned SCSI status */ + u_int8_t sense_resid; /* Autosense resid length: 2's comp */ + u_int32_t resid; /* Transfer residual length: 2's comp */ + cdb_t cdb_io; /* Union for CDB bytes/pointer */ + u_int8_t *msg_ptr; /* Pointer to the message buffer */ + u_int16_t msg_len; /* Number of bytes for the Message */ + u_int8_t tag_action; /* What to do for tag queueing */ + u_int8_t tag_id; /* tag id from initator (target mode) */ + u_int8_t init_id; /* initiator id of who selected */ +}; + +struct ccb_accept_tio { + struct ccb_hdr ccb_h; + cdb_t cdb_io; /* Union for CDB bytes/pointer */ + u_int8_t cdb_len; /* Number of bytes for the CDB */ + u_int8_t tag_action; /* What to do for tag queueing */ + u_int8_t tag_id; /* tag id from initator (target mode) */ + u_int8_t init_id; /* initiator id of who selected */ +}; + +/* Release SIM Queue */ +struct ccb_relsim { + struct ccb_hdr ccb_h; + u_int32_t release_flags; +#define RELSIM_ADJUST_OPENINGS 0x01 +#define RELSIM_RELEASE_AFTER_TIMEOUT 0x02 +#define RELSIM_RELEASE_AFTER_CMDCMPLT 0x04 +#define RELSIM_RELEASE_AFTER_QEMPTY 0x08 + u_int32_t openings; + u_int32_t release_timeout; + u_int32_t qfrozen_cnt; +}; + +/* + * Definitions for the asynchronous callback CCB fields. + */ +typedef enum { + AC_GETDEV_CHANGED = 0x800,/* Getdev info might have changed */ + AC_INQ_CHANGED = 0x400,/* Inquiry info might have changed */ + AC_TRANSFER_NEG = 0x200,/* New transfer settings in effect */ + AC_LOST_DEVICE = 0x100,/* A device went away */ + AC_FOUND_DEVICE = 0x080,/* A new device was found */ + AC_PATH_DEREGISTERED = 0x040,/* A path has de-registered */ + AC_PATH_REGISTERED = 0x020,/* A new path has been registered */ + AC_SENT_BDR = 0x010,/* A BDR message was sent to target */ + AC_SCSI_AEN = 0x008,/* A SCSI AEN has been received */ + AC_UNSOL_RESEL = 0x002,/* Unsolicited reselection occurred */ + AC_BUS_RESET = 0x001 /* A SCSI bus reset occurred */ +} ac_code; + +typedef void ac_callback_t (void *softc, u_int32_t code, + struct cam_path *path, void *args); + +/* Set Asynchronous Callback CCB */ +struct ccb_setasync { + struct ccb_hdr ccb_h; + u_int32_t event_enable; /* Async Event enables */ + ac_callback_t *callback; + void *callback_arg; +}; + +/* Set Device Type CCB */ +struct ccb_setdev { + struct ccb_hdr ccb_h; + u_int8_t dev_type; /* Value for dev type field in EDT */ +}; + +/* SCSI Control Functions */ + +/* Abort XPT request CCB */ +struct ccb_abort { + struct ccb_hdr ccb_h; + union ccb *abort_ccb; /* Pointer to CCB to abort */ +}; + +/* Reset SCSI Bus CCB */ +struct ccb_resetbus { + struct ccb_hdr ccb_h; +}; + +/* Reset SCSI Device CCB */ +struct ccb_resetdev { + struct ccb_hdr ccb_h; +}; + +/* Terminate I/O Process Request CCB */ +struct ccb_termio { + struct ccb_hdr ccb_h; + union ccb *termio_ccb; /* Pointer to CCB to terminate */ +}; + +/* Get/Set transfer rate/width/disconnection/tag queueing settings */ +struct ccb_trans_settings { + struct ccb_hdr ccb_h; + u_int valid; /* Which fields to honor */ +#define CCB_TRANS_SYNC_RATE_VALID 0x01 +#define CCB_TRANS_SYNC_OFFSET_VALID 0x02 +#define CCB_TRANS_BUS_WIDTH_VALID 0x04 +#define CCB_TRANS_DISC_VALID 0x08 +#define CCB_TRANS_TQ_VALID 0x10 + u_int flags; +#define CCB_TRANS_CURRENT_SETTINGS 0x01 +#define CCB_TRANS_USER_SETTINGS 0x02 +#define CCB_TRANS_DISC_ENB 0x04 +#define CCB_TRANS_TAG_ENB 0x08 + u_int sync_period; + u_int sync_offset; + u_int bus_width; +}; + +/* + * Calculate the geometry parameters for a device + * give the block size and volume size in blocks. + */ +struct ccb_calc_geometry { + struct ccb_hdr ccb_h; + u_int32_t block_size; + u_int32_t volume_size; + u_int16_t cylinders; + u_int8_t heads; + u_int8_t secs_per_track; +}; + +/* + * Rescan the given bus, or bus/target/lun + */ +struct ccb_rescan { + struct ccb_hdr ccb_h; + cam_flags flags; +}; + +/* + * Turn on debugging for the given bus, bus/target, or bus/target/lun. + */ +struct ccb_debug { + struct ccb_hdr ccb_h; + cam_debug_flags flags; +}; + +/* Target mode structures. */ + +struct ccb_en_lun { + struct ccb_hdr ccb_h; + u_int16_t grp6_len; /* Group 6 VU CDB length */ + u_int16_t grp7_len; /* Group 7 VU CDB length */ + u_int8_t enable; +}; + +struct ccb_immed_notify { + struct ccb_hdr ccb_h; + struct scsi_sense_data sense_data; + u_int8_t sense_len; /* Number of bytes in sese buffer */ + u_int8_t initiator_id; /* Id of initiator that selected */ + u_int16_t seq_id; /* Sequence Identifier */ + u_int8_t message_code; /* Message Code */ + u_int8_t message_args[7]; /* Message Arguments */ +}; + +struct ccb_notify_ack { + struct ccb_hdr ccb_h; + u_int16_t seq_id; /* Sequence identifier */ + u_int8_t event; /* Event flags */ +}; + +/* HBA engine structures. */ + +typedef enum { + EIT_BUFFER, /* Engine type: buffer memory */ + EIT_LOSSLESS, /* Engine type: lossless compression */ + EIT_LOSSY, /* Engine type: lossy compression */ + EIT_ENCRYPT /* Engine type: encryption */ +} ei_type; + +typedef enum { + EAD_VUNIQUE, /* Engine algorithm ID: vendor unique */ + EAD_LZ1V1, /* Engine algorithm ID: LZ1 var.1 */ + EAD_LZ2V1, /* Engine algorithm ID: LZ2 var.1 */ + EAD_LZ2V2, /* Engine algorithm ID: LZ2 var.2 */ +} ei_algo; + +struct ccb_eng_inq { + struct ccb_hdr ccb_h; + u_int16_t eng_num; /* The engine number for this inquiry */ + ei_type eng_type; /* Returned engine type */ + ei_algo eng_algo; /* Returned engine algorithm type */ + u_int32_t eng_memeory; /* Returned engine memory size */ +}; + +struct ccb_eng_exec { /* This structure must match SCSIIO size */ + struct ccb_hdr ccb_h; + u_int8_t *pdrv_ptr; /* Ptr used by the peripheral driver */ + u_int8_t *req_map; /* Ptr for mapping info on the req. */ + u_int8_t *data_ptr; /* Pointer to the data buf/SG list */ + u_int32_t dxfer_len; /* Data transfer length */ + u_int8_t *engdata_ptr; /* Pointer to the engine buffer data */ + u_int16_t sglist_cnt; /* Num of scatter gather list entries */ + u_int32_t dmax_len; /* Destination data maximum length */ + u_int32_t dest_len; /* Destination data length */ + int32_t src_resid; /* Source residual length: 2's comp */ + u_int32_t timeout; /* Timeout value */ + u_int16_t eng_num; /* Engine number for this request */ + u_int16_t vu_flags; /* Vendor Unique flags */ +}; + +/* + * Definitions for the timeout field in the SCSI I/O CCB. + */ +#define CAM_TIME_DEFAULT 0x00000000 /* Use SIM default value */ +#define CAM_TIME_INFINITY 0xFFFFFFFF /* Infinite timeout */ + +#define CAM_SUCCESS 0 /* For signaling general success */ +#define CAM_FAILURE 1 /* For signaling general failure */ + +#define CAM_FALSE 0 +#define CAM_TRUE 1 + +#define XPT_CCB_INVALID -1 /* for signaling a bad CCB to free */ + +/* + * Union of all CCB types for kernel space allocation. This union should + * never be used for manipulating CCBs - its only use is for the allocation + * and deallocation of raw CCB space and is the return type of xpt_ccb_alloc + * and the argument to xpt_ccb_free. + */ +union ccb { + struct ccb_hdr ccb_h; /* For convenience */ + struct ccb_scsiio csio; + struct ccb_getdev cgd; + struct ccb_getdevlist cgdl; + struct ccb_pathinq cpi; + struct ccb_relsim crs; + struct ccb_setasync csa; + struct ccb_setdev csd; + struct ccb_dev_match cdm; + struct ccb_trans_settings cts; + struct ccb_calc_geometry ccg; + struct ccb_abort cab; + struct ccb_resetbus crb; + struct ccb_resetdev crd; + struct ccb_termio tio; + struct ccb_accept_tio atio; + struct ccb_scsiio ctio; + struct ccb_en_lun cel; + struct ccb_immed_notify cin; + struct ccb_notify_ack cna; + struct ccb_eng_inq cei; + struct ccb_eng_exec cee; + struct ccb_rescan crcn; + struct ccb_debug cdbg; +}; + +__BEGIN_DECLS +static __inline void +cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int8_t tag_action, + u_int8_t *data_ptr, u_int32_t dxfer_len, + u_int8_t sense_len, u_int8_t cdb_len, + u_int32_t timeout); + +static __inline void +cam_fill_ctio(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int tag_action, u_int tag_id, + u_int init_id, u_int scsi_status, u_int8_t *data_ptr, + u_int32_t dxfer_len, u_int32_t timeout); + +static __inline void +cam_fill_csio(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int8_t tag_action, + u_int8_t *data_ptr, u_int32_t dxfer_len, + u_int8_t sense_len, u_int8_t cdb_len, + u_int32_t timeout) +{ + csio->ccb_h.func_code = XPT_SCSI_IO; + csio->ccb_h.flags = flags; + csio->ccb_h.retry_count = retries; + csio->ccb_h.cbfcnp = cbfcnp; + csio->ccb_h.timeout = timeout; + csio->data_ptr = data_ptr; + csio->dxfer_len = dxfer_len; + csio->sense_len = sense_len; + csio->cdb_len = cdb_len; + csio->tag_action = tag_action; +} + +static __inline void +cam_fill_ctio(struct ccb_scsiio *csio, u_int32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + u_int32_t flags, u_int tag_action, u_int tag_id, + u_int init_id, u_int scsi_status, u_int8_t *data_ptr, + u_int32_t dxfer_len, u_int32_t timeout) +{ + csio->ccb_h.func_code = XPT_CONT_TARGET_IO; + csio->ccb_h.flags = flags; + csio->ccb_h.retry_count = retries; + csio->ccb_h.cbfcnp = cbfcnp; + csio->ccb_h.timeout = timeout; + csio->data_ptr = data_ptr; + csio->dxfer_len = dxfer_len; + csio->scsi_status = scsi_status; + csio->tag_action = tag_action; + csio->tag_id = tag_id; + csio->init_id = init_id; +} + +__END_DECLS + +#endif /* _CAM_CAM_CCB_H */ diff --git a/sys/cam/cam_conf.h b/sys/cam/cam_conf.h new file mode 100644 index 000000000000..080575c66c60 --- /dev/null +++ b/sys/cam/cam_conf.h @@ -0,0 +1,67 @@ +/* + * Data structures and definitions for linking CAM into the autoconf system. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_CONF_H +#define _CAM_CAM_CONF_H 1 + +#ifdef KERNEL + +#define CAMCONF_UNSPEC 255 +#define CAMCONF_ANY 254 + +/* + * Macro that lets us know something is specified. + */ +#define IS_SPECIFIED(ARG) (ARG != CAMCONF_UNSPEC && ARG != CAMCONF_ANY) + +struct cam_sim_config +{ + int pathid; + char *sim_name; + int sim_unit; + int sim_bus; +}; + +struct cam_periph_config +{ + char *periph_name; + int periph_unit; /* desired device unit */ + int pathid; /* Controller unit */ + int target; + int lun; + int flags; /* Flags from config */ +}; + +extern struct cam_sim_config cam_sinit[]; +extern struct cam_periph_config cam_pinit[]; + +#endif /* KERNEL */ + +#endif /* _CAM_CAM_CONF_H */ diff --git a/sys/cam/cam_debug.h b/sys/cam/cam_debug.h new file mode 100644 index 000000000000..f1b95f208d82 --- /dev/null +++ b/sys/cam/cam_debug.h @@ -0,0 +1,77 @@ +/* + * Macros for tracing/loging information in the CAM layer + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#ifndef _CAM_CAM_DEBUG_H +#define _CAM_CAM_DEBUG_H 1 + +#if defined(CAMDEBUG) && defined(KERNEL) +#include +#endif /* CAMDEBUG && KERNEL */ + +/* + * Debugging flags. + */ +typedef enum { + CAM_DEBUG_NONE = 0x00, /* no debugging */ + CAM_DEBUG_INFO = 0x01, /* scsi commands, errors, data */ + CAM_DEBUG_TRACE = 0x02, /* routine flow tracking */ + CAM_DEBUG_SUBTRACE = 0x04, /* internal to routine flows */ +} cam_debug_flags; + +#if defined(CAMDEBUG) && defined(KERNEL) + +/* Path we want to debug */ +extern struct cam_path *cam_dpath; +/* Current debug levels set */ +extern u_int32_t cam_dflags; + +/* Debugging macros. */ +#define CAM_DEBUG(path, flag, printfargs) \ + if ((cam_dflags & (flag)) \ + && (cam_dpath != NULL) \ + && (xpt_path_comp(path, cam_dpath) >= 0)) { \ + xpt_print_path(path); \ + printf printfargs; \ + DELAY(100000); \ + } +#define CAM_DEBUG_PRINT(flag, printfargs) \ + if (cam_dflags & (flag)) { \ + printf("cam_debug: "); \ + printf printfargs; \ + DELAY(100000); \ + } + +#else /* !CAMDEBUG || !KERNEL */ + +#define CAM_DEBUG(A, B, C) +#define CAM_DEBUG_PRINT(A, B) + +#endif /* CAMDEBUG && KERNEL */ + +#endif /* _CAM_CAM_DEBUG_H */ diff --git a/sys/cam/cam_extend.c b/sys/cam/cam_extend.c new file mode 100644 index 000000000000..c7acbdfdefbe --- /dev/null +++ b/sys/cam/cam_extend.c @@ -0,0 +1,115 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * $Id$ + */ +/* + * XXX XXX XXX XXX We should get DEVFS working so that we + * don't have to do this, possibly sparse, array based junk. + */ +/* + * Extensible arrays: Use a realloc like implementation to permit + * the arrays to be extend. + */ +#include +#include +#include +#include + +#include + +struct extend_array +{ + int nelem; + void **ps; +}; + +static void * +cam_extend_alloc(size_t s) +{ + void *p = malloc(s, M_DEVBUF, M_NOWAIT); + if (!p) + panic("extend_alloc: malloc failed."); + return p; +} + +static void +cam_extend_free(void *p) +{ + free(p, M_DEVBUF); +} + +/* EXTEND_CHUNK: Number of extend slots to allocate whenever we need a new + * one. + */ +#ifndef EXTEND_CHUNK + #define EXTEND_CHUNK 8 +#endif + +struct extend_array * +cam_extend_new(void) +{ + struct extend_array *p = cam_extend_alloc(sizeof(*p)); + if (p) { + p->nelem = 0; + p->ps = 0; + } + + return p; +} + +void * +cam_extend_set(struct extend_array *ea, int index, void *value) +{ + if (index >= ea->nelem) { + void **space; + space = cam_extend_alloc(sizeof(void *) * (index + EXTEND_CHUNK)); + bzero(space, sizeof(void *) * (index + EXTEND_CHUNK)); + + /* Make sure we have something to copy before we copy it */ + if (ea->nelem) { + bcopy(ea->ps, space, sizeof(void *) * ea->nelem); + cam_extend_free(ea->ps); + } + + ea->ps = space; + ea->nelem = index + EXTEND_CHUNK; + } + if (ea->ps[index]) { + printf("extend_set: entry %d already has storage.\n", index); + return 0; + } + else + ea->ps[index] = value; + + return value; +} + +void * +cam_extend_get(struct extend_array *ea, + int index) +{ + if (ea == NULL || index >= ea->nelem || index < 0) + return NULL; + return ea->ps[index]; +} + +void +cam_extend_release(struct extend_array *ea, int index) +{ + void *p = cam_extend_get(ea, index); + if (p) { + ea->ps[index] = 0; + } +} diff --git a/sys/cam/cam_extend.h b/sys/cam/cam_extend.h new file mode 100644 index 000000000000..8aec51d391fe --- /dev/null +++ b/sys/cam/cam_extend.h @@ -0,0 +1,30 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * $Id$ + */ + +#ifndef _CAM_CAM_EXTEND_H +#define _CAM_CAM_EXTEND_H 1 + +#ifdef KERNEL +struct extend_array; + +void *cam_extend_get(struct extend_array *ea, int index); +struct extend_array *cam_extend_new(void); +void *cam_extend_set(struct extend_array *ea, int index, void *value); +void cam_extend_release(struct extend_array *ea, int index); + +#endif /* KERNEL */ +#endif /* _CAM_CAM_EXTEND_H */ diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c new file mode 100644 index 000000000000..3465b03a2d23 --- /dev/null +++ b/sys/cam/cam_periph.c @@ -0,0 +1,1493 @@ +/* + * Common functions for CAM "type" (peripheral) drivers. + * + * Copyright (c) 1997, 1998 Justin T. Gibbs. + * Copyright (c) 1997, 1998 Kenneth D. Merry. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static u_int camperiphnextunit(struct periph_driver *p_drv, + u_int newunit, int wired); +static u_int camperiphunit(struct periph_driver *p_drv, + path_id_t path_id_t, + target_id_t target, lun_id_t lun); +static void camperiphdone(struct cam_periph *periph, + union ccb *done_ccb); +static void camperiphfree(struct cam_periph *periph); + +cam_status +cam_periph_alloc(periph_ctor_t *periph_ctor, periph_dtor_t *periph_dtor, + periph_start_t *periph_start, char *name, cam_periph_type type, + struct cam_path *path, ac_callback_t *ac_callback, + ac_code code, void *arg) +{ + struct periph_driver **p_drv; + struct cam_periph *periph; + struct cam_periph *cur_periph; + path_id_t path_id; + target_id_t target_id; + lun_id_t lun_id; + cam_status status; + u_int init_level; + int s; + + init_level = 0; + /* + * Handle Hot-Plug scenarios. If there is already a peripheral + * of our type assigned to this path, we are likely waiting for + * final close on an old, invalidated, peripheral. If this is + * the case, queue up a deferred call to the peripheral's async + * handler. If it looks like a mistaken re-alloation, complain. + */ + if ((periph = cam_periph_find(path, name)) != NULL) { + + if ((periph->flags & CAM_PERIPH_INVALID) != 0 + && (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) == 0) { + periph->flags |= CAM_PERIPH_NEW_DEV_FOUND; + periph->deferred_callback = ac_callback; + periph->deferred_ac = code; + return (CAM_REQ_INPROG); + } else { + printf("cam_periph_alloc: attempt to re-allocate " + "valid device %s%d rejected\n", + periph->periph_name, periph->unit_number); + } + return (CAM_REQ_INVALID); + } + + periph = (struct cam_periph *)malloc(sizeof(*periph), M_DEVBUF, + M_NOWAIT); + + if (periph == NULL) + return (CAM_RESRC_UNAVAIL); + + init_level++; + + for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; + *p_drv != NULL; p_drv++) { + if (strcmp((*p_drv)->driver_name, name) == 0) + break; + } + + path_id = xpt_path_path_id(path); + target_id = xpt_path_target_id(path); + lun_id = xpt_path_lun_id(path); + bzero(periph, sizeof(*periph)); + cam_init_pinfo(&periph->pinfo); + periph->periph_start = periph_start; + periph->periph_dtor = periph_dtor; + periph->type = type; + periph->periph_name = name; + periph->unit_number = camperiphunit(*p_drv, path_id, target_id, lun_id); + periph->immediate_priority = CAM_PRIORITY_NONE; + periph->refcount = 0; + SLIST_INIT(&periph->ccb_list); + status = xpt_create_path(&path, periph, path_id, target_id, lun_id); + if (status != CAM_REQ_CMP) + goto failure; + + periph->path = path; + init_level++; + + status = xpt_add_periph(periph); + + if (status != CAM_REQ_CMP) + goto failure; + + s = splsoftcam(); + cur_periph = TAILQ_FIRST(&(*p_drv)->units); + while (cur_periph != NULL + && cur_periph->unit_number < periph->unit_number) + cur_periph = TAILQ_NEXT(cur_periph, unit_links); + + if (cur_periph != NULL) + TAILQ_INSERT_BEFORE(cur_periph, periph, unit_links); + else { + TAILQ_INSERT_TAIL(&(*p_drv)->units, periph, unit_links); + (*p_drv)->generation++; + } + + splx(s); + + init_level++; + + status = periph_ctor(periph, arg); + + if (status == CAM_REQ_CMP) + init_level++; + +failure: + switch (init_level) { + case 4: + /* Initialized successfully */ + break; + case 3: + s = splsoftcam(); + TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); + splx(s); + xpt_remove_periph(periph); + case 2: + xpt_free_path(periph->path); + case 1: + free(periph, M_DEVBUF); + case 0: + /* No cleanup to perform. */ + break; + default: + panic("cam_periph_alloc: Unkown init level"); + } + return(status); +} + +/* + * Find a peripheral structure with the specified path, target, lun, + * and (optionally) type. If the name is NULL, this function will return + * the first peripheral driver that matches the specified path. + */ +struct cam_periph * +cam_periph_find(struct cam_path *path, char *name) +{ + struct periph_driver **p_drv; + struct cam_periph *periph; + int s; + + for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; + *p_drv != NULL; p_drv++) { + + if (name != NULL && (strcmp((*p_drv)->driver_name, name) != 0)) + continue; + + s = splsoftcam(); + for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; + periph = TAILQ_NEXT(periph, unit_links)) { + if (xpt_path_comp(periph->path, path) == 0) { + splx(s); + return(periph); + } + } + splx(s); + if (name != NULL) + return(NULL); + } + return(NULL); +} + +cam_status +cam_periph_acquire(struct cam_periph *periph) +{ + int s; + + if (periph == NULL) + return(CAM_REQ_CMP_ERR); + + s = splsoftcam(); + periph->refcount++; + splx(s); + + return(CAM_REQ_CMP); +} + +void +cam_periph_release(struct cam_periph *periph) +{ + int s; + + if (periph == NULL) + return; + + s = splsoftcam(); + if ((--periph->refcount == 0) + && (periph->flags & CAM_PERIPH_INVALID)) { + camperiphfree(periph); + } + splx(s); + +} + +/* + * Look for the next unit number that is not currently in use for this + * peripheral type starting at "newunit". Also exclude unit numbers that + * are reserved by for future "hardwiring" unless we already know that this + * is a potential wired device. Only assume that the device is "wired" the + * first time through the loop since after that we'll be looking at unit + * numbers that did not match a wiring entry. + */ +static u_int +camperiphnextunit(struct periph_driver *p_drv, u_int newunit, int wired) +{ + struct cam_periph *periph; + struct cam_periph_config *periph_conf; + char *periph_name; + u_int i; + int s; + + s = splsoftcam(); + periph_name = p_drv->driver_name; + for (;;newunit++) { + + for (periph = TAILQ_FIRST(&p_drv->units); + periph != NULL && periph->unit_number != newunit; + periph = TAILQ_NEXT(periph, unit_links)) + ; + + if (periph != NULL && periph->unit_number == newunit) { + if (wired != 0) { + xpt_print_path(periph->path); + printf("Duplicate Wired Device entry!\n"); + xpt_print_path(periph->path); + printf("Second device will not be wired\n"); + wired = 0; + } + continue; + } + + for (periph_conf = cam_pinit; + wired == 0 && periph_conf->periph_name != NULL; + periph_conf++) { + + /* + * Don't match entries like "da 4" as a wired down + * device, but do match entries like "da 4 target 5" + * or even "da 4 scbus 1". + */ + if (IS_SPECIFIED(periph_conf->periph_unit) + && (!strcmp(periph_name, periph_conf->periph_name)) + && (IS_SPECIFIED(periph_conf->target) + || IS_SPECIFIED(periph_conf->pathid)) + && (newunit == periph_conf->periph_unit)) + break; + } + + if (wired != 0 || periph_conf->periph_name == NULL) + break; + } + splx(s); + return (newunit); +} + +static u_int +camperiphunit(struct periph_driver *p_drv, path_id_t pathid, + target_id_t target, lun_id_t lun) +{ + struct cam_periph_config *periph_conf; + u_int unit; + int hit; + + unit = 0; + hit = 0; + + for (periph_conf = cam_pinit; + periph_conf->periph_name != NULL; + periph_conf++, hit = 0) { + + if (!strcmp(p_drv->driver_name, periph_conf->periph_name) + && IS_SPECIFIED(periph_conf->periph_unit)) { + + if (IS_SPECIFIED(periph_conf->pathid)) { + + if (pathid != periph_conf->pathid) + continue; + hit++; + } + + if (IS_SPECIFIED(periph_conf->target)) { + + if (target != periph_conf->target) + continue; + hit++; + } + + if (IS_SPECIFIED(periph_conf->lun)) { + + if (lun != periph_conf->lun) + continue; + hit++; + } + + if (hit != 0) { + unit = periph_conf->periph_unit; + break; + } + } + } + + /* + * Either start from 0 looking for the next unit or from + * the unit number given in the periph_conf. This way, + * if we have wildcard matches, we don't return the same + * unit number twice. + */ + unit = camperiphnextunit(p_drv, unit, /*wired*/hit); + + return (unit); +} + +void +cam_periph_invalidate(struct cam_periph *periph) +{ + int s; + + periph->flags |= CAM_PERIPH_INVALID; + periph->flags &= ~CAM_PERIPH_NEW_DEV_FOUND; + + s = splsoftcam(); + if (periph->refcount == 0) + camperiphfree(periph); + else if (periph->refcount < 0) + printf("cam_invalidate_periph: refcount < 0!!\n"); + splx(s); +} + +static void +camperiphfree(struct cam_periph *periph) +{ + int s; + struct periph_driver **p_drv; + + for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; + *p_drv != NULL; p_drv++) { + if (strcmp((*p_drv)->driver_name, periph->periph_name) == 0) + break; + } + + if (periph->periph_dtor != NULL) + periph->periph_dtor(periph); + + s = splsoftcam(); + TAILQ_REMOVE(&(*p_drv)->units, periph, unit_links); + (*p_drv)->generation++; + splx(s); + + xpt_remove_periph(periph); + + if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { + union ccb ccb; + void *arg; + + switch (periph->deferred_ac) { + case AC_FOUND_DEVICE: + ccb.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); + xpt_action(&ccb); + arg = &ccb; + break; + case AC_PATH_REGISTERED: + ccb.ccb_h.func_code = XPT_PATH_INQ; + xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/ 1); + xpt_action(&ccb); + arg = &ccb; + break; + default: + arg = NULL; + break; + } + periph->deferred_callback(NULL, periph->deferred_ac, + periph->path, arg); + } + xpt_free_path(periph->path); + free(periph, M_DEVBUF); +} + +/* + * Wait interruptibly for an exclusive lock. + */ +int +cam_periph_lock(struct cam_periph *periph, int priority) +{ + int error; + int s; + + while ((periph->flags & CAM_PERIPH_LOCKED) != 0) { + periph->flags |= CAM_PERIPH_LOCK_WANTED; + if ((error = tsleep(periph, priority, "caplck", 0)) != 0) + return error; + } + + if (cam_periph_acquire(periph) != CAM_REQ_CMP) + return(ENXIO); + + periph->flags |= CAM_PERIPH_LOCKED; + return 0; +} + +/* + * Unlock and wake up any waiters. + */ +void +cam_periph_unlock(struct cam_periph *periph) +{ + periph->flags &= ~CAM_PERIPH_LOCKED; + if ((periph->flags & CAM_PERIPH_LOCK_WANTED) != 0) { + periph->flags &= ~CAM_PERIPH_LOCK_WANTED; + wakeup(periph); + } + + cam_periph_release(periph); +} + +/* + * Map user virtual pointers into kernel virtual address space, so we can + * access the memory. This won't work on physical pointers, for now it's + * up to the caller to check for that. (XXX KDM -- should we do that here + * instead?) This also only works for up to MAXPHYS memory. Since we use + * buffers to map stuff in and out, we're limited to the buffer size. + */ +int +cam_periph_mapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) +{ + int flags, numbufs, i; + u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; + u_int32_t lengths[CAM_PERIPH_MAXMAPS]; + u_int32_t dirs[CAM_PERIPH_MAXMAPS]; + + switch(ccb->ccb_h.func_code) { + case XPT_DEV_MATCH: + if (ccb->cdm.pattern_buf_len > MAXPHYS) { + printf("cam_periph_mapmem: attempt to map %u bytes, " + "which is greater than MAXPHYS(%d)\n", + ccb->cdm.pattern_buf_len, MAXPHYS); + return(E2BIG); + } else if (ccb->cdm.match_buf_len > MAXPHYS) { + printf("cam_periph_mapmem: attempt to map %u bytes, " + "which is greater than MAXPHYS(%d)\n", + ccb->cdm.match_buf_len, MAXPHYS); + return(E2BIG); + } + if (ccb->cdm.match_buf_len == 0) { + printf("cam_periph_mapmem: invalid match buffer " + "length 0\n"); + return(EINVAL); + } + if (ccb->cdm.pattern_buf_len > 0) { + data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; + lengths[0] = ccb->cdm.pattern_buf_len; + dirs[0] = CAM_DIR_OUT; + data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; + lengths[1] = ccb->cdm.match_buf_len; + dirs[1] = CAM_DIR_IN; + numbufs = 2; + } else { + data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; + lengths[0] = ccb->cdm.match_buf_len; + dirs[0] = CAM_DIR_IN; + numbufs = 1; + } + break; + case XPT_SCSI_IO: + if (ccb->csio.dxfer_len > MAXPHYS) { + printf("cam_periph_mapmem: attempt to map %u bytes, " + "which is greater than MAXPHYS(%d)\n", + ccb->csio.dxfer_len, MAXPHYS); + return(E2BIG); + } + + if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_NONE) + return(0); + + data_ptrs[0] = &ccb->csio.data_ptr; + lengths[0] = ccb->csio.dxfer_len;; + dirs[0] = ccb->ccb_h.flags & CAM_DIR_MASK; + numbufs = 1; + break; + default: + return(EINVAL); + break; /* NOTREACHED */ + } + + /* this keeps the current process from getting swapped */ + /* + * XXX KDM should I use P_NOSWAP instead? + */ + curproc->p_flag |= P_PHYSIO; + + for (i = 0; i < numbufs; i++) { + flags = 0; + + if (dirs[i] & CAM_DIR_IN) { + flags = B_READ; + if (useracc(*data_ptrs[i], lengths[i], B_READ) == 0){ + printf("cam_periph_mapmem: error, " + "address %#lx, length %d isn't " + "user accessible for READ\n", + (u_long)(*data_ptrs[i]), lengths[i]); + /* + * If we've already mapped one or more + * buffers for this CCB, unmap it (them). + */ + if (i > 0) + cam_periph_unmapmem(ccb, mapinfo); + else + curproc->p_flag &= ~P_PHYSIO; + + return(EACCES); + } + } + + /* + * XXX this check is really bogus, since B_WRITE currently + * is all 0's, and so it is "set" all the time. + */ + if (dirs[i] & CAM_DIR_OUT) { + flags |= B_WRITE; + if (useracc(*data_ptrs[i], lengths[i], B_WRITE) == 0){ + printf("cam_periph_mapmem: error, " + "address %#lx, length %d isn't " + "user accessible for WRITE\n", + (u_long)(*data_ptrs[i]), lengths[i]); + /* + * If we've already mapped one or more + * buffers for this CCB, unmap it (them). + */ + if (i > 0) + cam_periph_unmapmem(ccb, mapinfo); + else + curproc->p_flag &= ~P_PHYSIO; + + return(EACCES); + } + } + + /* + * Get the buffer. + */ + mapinfo->bp[i] = getpbuf(); + + /* save the buffer's data address */ + mapinfo->bp[i]->b_saveaddr = mapinfo->bp[i]->b_data; + + /* put our pointer in the data slot */ + mapinfo->bp[i]->b_data = *data_ptrs[i]; + + /* set the transfer length, we know it's < 64K */ + mapinfo->bp[i]->b_bufsize = lengths[i]; + + /* set the flags */ + mapinfo->bp[i]->b_flags = flags | B_PHYS | B_BUSY; + + /* map the buffer into kernel memory */ + vmapbuf(mapinfo->bp[i]); + + /* set our pointer to the new mapped area */ + *data_ptrs[i] = mapinfo->bp[i]->b_data; + + mapinfo->num_bufs_used++; + } + + return(0); +} + +/* + * Unmap memory segments mapped into kernel virtual address space by + * cam_periph_mapmem(). + */ +void +cam_periph_unmapmem(union ccb *ccb, struct cam_periph_map_info *mapinfo) +{ + int numbufs, i; + u_int8_t **data_ptrs[CAM_PERIPH_MAXMAPS]; + + if (mapinfo->num_bufs_used <= 0) { + /* allow ourselves to be swapped once again */ + curproc->p_flag &= ~P_PHYSIO; + return; + } + + switch (ccb->ccb_h.func_code) { + case XPT_DEV_MATCH: + numbufs = min(mapinfo->num_bufs_used, 2); + + if (numbufs == 1) { + data_ptrs[0] = (u_int8_t **)&ccb->cdm.matches; + } else { + data_ptrs[0] = (u_int8_t **)&ccb->cdm.patterns; + data_ptrs[1] = (u_int8_t **)&ccb->cdm.matches; + } + break; + case XPT_SCSI_IO: + data_ptrs[0] = &ccb->csio.data_ptr; + numbufs = min(mapinfo->num_bufs_used, 1); + break; + default: + /* allow ourselves to be swapped once again */ + curproc->p_flag &= ~P_PHYSIO; + return; + break; /* NOTREACHED */ + } + + for (i = 0; i < numbufs; i++) { + /* Set the user's pointer back to the original value */ + *data_ptrs[i] = mapinfo->bp[i]->b_saveaddr; + + /* unmap the buffer */ + vunmapbuf(mapinfo->bp[i]); + + /* clear the flags we set above */ + mapinfo->bp[i]->b_flags &= ~(B_PHYS|B_BUSY); + + /* release the buffer */ + relpbuf(mapinfo->bp[i]); + } + + /* allow ourselves to be swapped once again */ + curproc->p_flag &= ~P_PHYSIO; +} + +union ccb * +cam_periph_getccb(struct cam_periph *periph, u_int32_t priority) +{ + struct ccb_hdr *ccb_h; + int s; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering cdgetccb\n")); + + s = splsoftcam(); + + while (periph->ccb_list.slh_first == NULL) { + if (periph->immediate_priority > priority) + periph->immediate_priority = priority; + xpt_schedule(periph, priority); + if ((periph->ccb_list.slh_first != NULL) + && (periph->ccb_list.slh_first->pinfo.priority == priority)) + break; + tsleep(&periph->ccb_list, PRIBIO, "cgticb", 0); + } + + ccb_h = periph->ccb_list.slh_first; + SLIST_REMOVE_HEAD(&periph->ccb_list, periph_links.sle); + splx(s); + return ((union ccb *)ccb_h); +} + +void +cam_periph_ccbwait(union ccb *ccb) +{ + int s; + + s = splsoftcam(); + if ((ccb->ccb_h.pinfo.index != CAM_UNQUEUED_INDEX) + || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)) + tsleep(&ccb->ccb_h.cbfcnp, PRIBIO, "cbwait", 0); + + splx(s); +} + +int +cam_periph_ioctl(struct cam_periph *periph, int cmd, caddr_t addr, + int (*error_routine)(union ccb *ccb, + cam_flags camflags, + u_int32_t sense_flags)) +{ + union ccb *ccb; + int error; + int found; + + error = found = 0; + + switch(cmd){ + case CAMGETPASSTHRU: + ccb = cam_periph_getccb(periph, /* priority */ 1); + xpt_setup_ccb(&ccb->ccb_h, + ccb->ccb_h.path, + /*priority*/1); + ccb->ccb_h.func_code = XPT_GDEVLIST; + + /* + * Basically, the point of this is that we go through + * getting the list of devices, until we find a passthrough + * device. In the current version of the CAM code, the + * only way to determine what type of device we're dealing + * with is by its name. + */ + while (found == 0) { + ccb->cgdl.index = 0; + ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; + while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { + + /* we want the next device in the list */ + xpt_action(ccb); + if (strncmp(ccb->cgdl.periph_name, + "pass", 4) == 0){ + found = 1; + break; + } + } + if ((ccb->cgdl.status == CAM_GDEVLIST_LAST_DEVICE) && + (found == 0)) { + ccb->cgdl.periph_name[0] = '\0'; + ccb->cgdl.unit_number = 0; + break; + } + } + + /* copy the result back out */ + bcopy(ccb, addr, sizeof(union ccb)); + + /* and release the ccb */ + xpt_release_ccb(ccb); + + break; + default: + error = ENOTTY; + break; + } + return(error); +} + +int +cam_periph_runccb(union ccb *ccb, + int (*error_routine)(union ccb *ccb, + cam_flags camflags, + u_int32_t sense_flags), + cam_flags camflags, u_int32_t sense_flags, + struct devstat *ds) +{ + int error; + + error = 0; + + /* + * If the user has supplied a stats structure, and if we understand + * this particular type of ccb, record the transaction start. + */ + if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) + devstat_start_transaction(ds); + + xpt_action(ccb); + + do { + cam_periph_ccbwait(ccb); + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + error = 0; + else if (error_routine != NULL) + error = (*error_routine)(ccb, camflags, sense_flags); + else + error = 0; + + } while (error == ERESTART); + + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + cam_release_devq(ccb->ccb_h.path, + /* relsim_flags */0, + /* openings */0, + /* timeout */0, + /* getcount_only */ FALSE); + + if ((ds != NULL) && (ccb->ccb_h.func_code == XPT_SCSI_IO)) + devstat_end_transaction(ds, + ccb->csio.dxfer_len, + ccb->csio.tag_action & 0xf, + ((ccb->ccb_h.flags & CAM_DIR_MASK) == + CAM_DIR_NONE) ? DEVSTAT_NO_DATA : + (ccb->ccb_h.flags & CAM_DIR_OUT) ? + DEVSTAT_WRITE : + DEVSTAT_READ); + + return(error); +} + +u_int32_t +cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, + u_int32_t openings, u_int32_t timeout, + int getcount_only) +{ + struct ccb_relsim crs; + + xpt_setup_ccb(&crs.ccb_h, path, + /*priority*/1); + crs.ccb_h.func_code = XPT_REL_SIMQ; + crs.ccb_h.flags = getcount_only ? CAM_DEV_QFREEZE : 0; + crs.release_flags = relsim_flags; + crs.openings = openings; + crs.release_timeout = timeout; + xpt_action((union ccb *)&crs); + return (crs.qfrozen_cnt); +} + +#define saved_ccb_ptr ppriv_ptr0 +static void +camperiphdone(struct cam_periph *periph, union ccb *done_ccb) +{ + cam_status status; + int frozen; + int sense; + struct scsi_start_stop_unit *scsi_cmd; + u_int32_t relsim_flags, timeout; + u_int32_t qfrozen_cnt; + + status = done_ccb->ccb_h.status; + frozen = (status & CAM_DEV_QFRZN) != 0; + sense = (status & CAM_AUTOSNS_VALID) != 0; + status &= CAM_STATUS_MASK; + + timeout = 0; + relsim_flags = 0; + + /* + * Unfreeze the queue once if it is already frozen.. + */ + if (frozen != 0) { + qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/0, + /*openings*/0, + /*timeout*/0, + /*getcount_only*/0); + } + + switch (status) { + + case CAM_REQ_CMP: + + /* + * If we have successfully taken a device from the not + * ready to ready state, re-scan the device and re-get the + * inquiry information. Many devices (mostly disks) don't + * properly report their inquiry information unless they + * are spun up. + */ + if (done_ccb->ccb_h.func_code == XPT_SCSI_IO) { + scsi_cmd = (struct scsi_start_stop_unit *) + &done_ccb->csio.cdb_io.cdb_bytes; + + if (scsi_cmd->opcode == START_STOP_UNIT) + xpt_async(AC_INQ_CHANGED, + done_ccb->ccb_h.path, NULL); + } + bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, + sizeof(union ccb)); + + xpt_action(done_ccb); + + break; + case CAM_SCSI_STATUS_ERROR: + scsi_cmd = (struct scsi_start_stop_unit *) + &done_ccb->csio.cdb_io.cdb_bytes; + if (sense != 0) { + struct scsi_sense_data *sense; + int error_code, sense_key, asc, ascq; + + sense = &done_ccb->csio.sense_data; + scsi_extract_sense(sense, &error_code, + &sense_key, &asc, &ascq); + + /* + * If the error is "invalid field in CDB", + * and the load/eject flag is set, turn the + * flag off and try again. This is just in + * case the drive in question barfs on the + * load eject flag. The CAM code should set + * the load/eject flag by default for + * removable media. + */ + + /* XXX KDM + * Should we check to see what the specific + * scsi status is?? Or does it not matter + * since we already know that there was an + * error, and we know what the specific + * error code was, and we know what the + * opcode is.. + */ + if ((scsi_cmd->opcode == START_STOP_UNIT) && + ((scsi_cmd->how & SSS_LOEJ) != 0) && + (asc == 0x24) && (ascq == 0x00) && + (done_ccb->ccb_h.retry_count > 0)) { + + scsi_cmd->how &= ~SSS_LOEJ; + + xpt_action(done_ccb); + + } else if (done_ccb->ccb_h.retry_count > 0) { + /* + * In this case, the error recovery + * command failed, but we've got + * some retries left on it. Give + * it another try. + */ + + /* set the timeout to .5 sec */ + relsim_flags = + RELSIM_RELEASE_AFTER_TIMEOUT; + timeout = 500; + + xpt_action(done_ccb); + + break; + + } else { + /* + * Copy the original CCB back and + * send it back to the caller. + */ + bcopy(done_ccb->ccb_h.saved_ccb_ptr, + done_ccb, sizeof(union ccb)); + + xpt_action(done_ccb); + } + } else { + /* + * Eh?? The command failed, but we don't + * have any sense. What's up with that? + * Fire the CCB again to return it to the + * caller. + */ + bcopy(done_ccb->ccb_h.saved_ccb_ptr, + done_ccb, sizeof(union ccb)); + + xpt_action(done_ccb); + + } + break; + default: + bcopy(done_ccb->ccb_h.saved_ccb_ptr, done_ccb, + sizeof(union ccb)); + + xpt_action(done_ccb); + + break; + } + + /* decrement the retry count */ + if (done_ccb->ccb_h.retry_count > 0) + done_ccb->ccb_h.retry_count--; + + qfrozen_cnt = cam_release_devq(done_ccb->ccb_h.path, + /*relsim_flags*/relsim_flags, + /*openings*/0, + /*timeout*/timeout, + /*getcount_only*/0); +} + +/* + * Generic error handler. Peripheral drivers usually filter + * out the errors that they handle in a unique mannor, then + * call this function. + */ +int +cam_periph_error(union ccb *ccb, cam_flags camflags, + u_int32_t sense_flags, union ccb *save_ccb) +{ + cam_status status; + int frozen; + int sense; + int error; + int openings; + int retry; + u_int32_t relsim_flags; + u_int32_t timeout; + + status = ccb->ccb_h.status; + frozen = (status & CAM_DEV_QFRZN) != 0; + sense = (status & CAM_AUTOSNS_VALID) != 0; + status &= CAM_STATUS_MASK; + relsim_flags = 0; + + + switch (status) { + case CAM_REQ_CMP: + /* decrement the number of retries */ + retry = ccb->ccb_h.retry_count > 0; + if (retry) + ccb->ccb_h.retry_count--; + error = 0; + break; + case CAM_SCSI_STATUS_ERROR: + + switch (ccb->csio.scsi_status) { + case SCSI_STATUS_OK: + case SCSI_STATUS_COND_MET: + case SCSI_STATUS_INTERMED: + case SCSI_STATUS_INTERMED_COND_MET: + error = 0; + break; + case SCSI_STATUS_CMD_TERMINATED: + case SCSI_STATUS_CHECK_COND: + if (sense != 0) { + struct scsi_sense_data *sense; + int error_code, sense_key, asc, ascq; + struct cam_periph *periph; + scsi_sense_action err_action; + struct ccb_getdev cgd; + + sense = &ccb->csio.sense_data; + scsi_extract_sense(sense, &error_code, + &sense_key, &asc, &ascq); + periph = xpt_path_periph(ccb->ccb_h.path); + + /* + * Grab the inquiry data for this device. + */ + xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, + /*priority*/ 1); + cgd.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)&cgd); + + err_action = scsi_error_action(asc, ascq, + &cgd.inq_data); + + /* + * Send a Test Unit Ready to the device. + * If the 'many' flag is set, we send 120 + * test unit ready commands, one every half + * second. Otherwise, we just send one TUR. + * We only want to do this if the retry + * count has not been exhausted. + */ + if (((err_action & SS_MASK) == SS_TUR) + && save_ccb != NULL + && ccb->ccb_h.retry_count > 0) { + + /* decrement the number of retries */ + if ((err_action & + SSQ_DECREMENT_COUNT) != 0) { + retry = 1; + ccb->ccb_h.retry_count--; + } + + bcopy(ccb, save_ccb, sizeof(*save_ccb)); + + /* + * We retry this one every half + * second for a minute. If the + * device hasn't become ready in a + * minute's time, it's unlikely to + * ever become ready. If the table + * doesn't specify SSQ_MANY, we can + * only try this once. Oh well. + */ + if ((err_action & SSQ_MANY) != 0) + scsi_test_unit_ready(&ccb->csio, + /*retries*/120, + camperiphdone, + MSG_SIMPLE_Q_TAG, + SSD_FULL_SIZE, + /*timeout*/5000); + else + scsi_test_unit_ready(&ccb->csio, + /*retries*/1, + camperiphdone, + MSG_SIMPLE_Q_TAG, + SSD_FULL_SIZE, + /*timeout*/5000); + + /* release the queue after .5 sec. */ + relsim_flags = + RELSIM_RELEASE_AFTER_TIMEOUT; + timeout = 500; + /* + * Drop the priority to 0 so that + * we are the first to execute. Also + * freeze the queue after this command + * is sent so that we can restore the + * old csio and have it queued in the + * proper order before we let normal + * transactions go to the drive. + */ + ccb->ccb_h.pinfo.priority = 0; + ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + + /* + * Save a pointer to the original + * CCB in the new CCB. + */ + ccb->ccb_h.saved_ccb_ptr = save_ccb; + + error = ERESTART; + } + /* + * Send a start unit command to the device, + * and then retry the command. We only + * want to do this if the retry count has + * not been exhausted. If the user + * specified 0 retries, then we follow + * their request and do not retry. + */ + else if (((err_action & SS_MASK) == SS_START) + && save_ccb != NULL + && ccb->ccb_h.retry_count > 0) { + int le; + + /* decrement the number of retries */ + retry = 1; + ccb->ccb_h.retry_count--; + + /* + * Check for removable media and + * set load/eject flag + * appropriately. + */ + if (SID_IS_REMOVABLE(&cgd.inq_data)) + le = TRUE; + else + le = FALSE; + + /* + * Attempt to start the drive up. + * + * Save the current ccb so it can + * be restored and retried once the + * drive is started up. + */ + bcopy(ccb, save_ccb, sizeof(*save_ccb)); + + scsi_start_stop(&ccb->csio, + /*retries*/1, + camperiphdone, + MSG_SIMPLE_Q_TAG, + /*start*/TRUE, + /*load/eject*/le, + /*immediate*/FALSE, + SSD_FULL_SIZE, + /*timeout*/50000); + /* + * Drop the priority to 0 so that + * we are the first to execute. Also + * freeze the queue after this command + * is sent so that we can restore the + * old csio and have it queued in the + * proper order before we let normal + * transactions go to the drive. + */ + ccb->ccb_h.pinfo.priority = 0; + ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + + /* + * Save a pointer to the original + * CCB in the new CCB. + */ + ccb->ccb_h.saved_ccb_ptr = save_ccb; + + error = ERESTART; + } else if ((sense_flags & SF_RETRY_UA) != 0) { + /* + * XXX KDM this is a *horrible* + * hack. + */ + error = scsi_interpret_sense(ccb, + sense_flags, + &relsim_flags, + &openings, + &timeout, + err_action); + } + + /* + * Theoretically, this code should send a + * test unit ready to the given device, and + * if it returns and error, send a start + * unit command. Since we don't yet have + * the capability to do two-command error + * recovery, just send a start unit. + * XXX KDM fix this! + */ + else if (((err_action & SS_MASK) == SS_TURSTART) + && save_ccb != NULL + && ccb->ccb_h.retry_count > 0) { + int le; + + /* decrement the number of retries */ + retry = 1; + ccb->ccb_h.retry_count--; + + /* + * Check for removable media and + * set load/eject flag + * appropriately. + */ + if (SID_IS_REMOVABLE(&cgd.inq_data)) + le = TRUE; + else + le = FALSE; + + /* + * Attempt to start the drive up. + * + * Save the current ccb so it can + * be restored and retried once the + * drive is started up. + */ + bcopy(ccb, save_ccb, sizeof(*save_ccb)); + + scsi_start_stop(&ccb->csio, + /*retries*/1, + camperiphdone, + MSG_SIMPLE_Q_TAG, + /*start*/TRUE, + /*load/eject*/le, + /*immediate*/FALSE, + SSD_FULL_SIZE, + /*timeout*/50000); + + /* release the queue after .5 sec. */ + relsim_flags = + RELSIM_RELEASE_AFTER_TIMEOUT; + timeout = 500; + /* + * Drop the priority to 0 so that + * we are the first to execute. Also + * freeze the queue after this command + * is sent so that we can restore the + * old csio and have it queued in the + * proper order before we let normal + * transactions go to the drive. + */ + ccb->ccb_h.pinfo.priority = 0; + ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + + /* + * Save a pointer to the original + * CCB in the new CCB. + */ + ccb->ccb_h.saved_ccb_ptr = save_ccb; + + error = ERESTART; + } else { + error = scsi_interpret_sense(ccb, + sense_flags, + &relsim_flags, + &openings, + &timeout, + err_action); + } + } else if (ccb->csio.scsi_status == + SCSI_STATUS_CHECK_COND) { + /* no point in decrementing the retry count */ + panic("cam_periph_error: scsi status of " + "CHECK COND returned but no sense " + "information is availible. " + "Controller should have returned " + "CAM_AUTOSENSE_FAILED"); + /* NOTREACHED */ + error = EIO; + } else if (ccb->ccb_h.retry_count > 0) { + /* + * XXX KDM shouldn't there be a better + * argument to return?? + */ + error = EIO; + } else { + /* decrement the number of retries */ + retry = ccb->ccb_h.retry_count > 0; + if (retry) + ccb->ccb_h.retry_count--; + /* + * If it was aborted with no + * clue as to the reason, just + * retry it again. + */ + error = ERESTART; + } + break; + case SCSI_STATUS_QUEUE_FULL: + { + /* no decrement */ + struct ccb_getdev cgd; + + /* + * First off, find out what the current + * transaction counts are. + */ + xpt_setup_ccb(&cgd.ccb_h, + ccb->ccb_h.path, + /*priority*/1); + cgd.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)&cgd); + + /* + * If we were the only transaction active, treat + * the QUEUE FULL as if it were a BUSY condition. + */ + if (cgd.dev_active != 0) { + /* + * Reduce the number of openings to + * be 1 less than the amount it took + * to get a queue full bounded by the + * minimum allowed tag count for this + * device. + */ + openings = cgd.dev_active; + if (openings < cgd.mintags) + openings = cgd.mintags; + if (openings < cgd.dev_active+cgd.dev_openings) + relsim_flags = RELSIM_ADJUST_OPENINGS; + else { + /* + * Some devices report queue full for + * temporary resource shortages. For + * this reason, we allow a minimum + * tag count to be entered via a + * quirk entry to prevent the queue + * count on these devices from falling + * to a pessimisticly low value. We + * still wait for the next successful + * completion, however, before queueing + * more transactions to the device. + */ + relsim_flags = + RELSIM_RELEASE_AFTER_CMDCMPLT; + } + timeout = 0; + error = ERESTART; + break; + } + /* FALLTHROUGH */ + } + case SCSI_STATUS_BUSY: + /* + * Restart the queue after either another + * command completes or a 1 second timeout. + */ + /* + * XXX KDM ask JTG about this again, do we need to + * be looking at the retry count here? + */ + error = ERESTART; + relsim_flags = RELSIM_RELEASE_AFTER_TIMEOUT + | RELSIM_RELEASE_AFTER_CMDCMPLT; + timeout = 1000; + break; + case SCSI_STATUS_RESERV_CONFLICT: + error = EIO; + break; + default: + error = EIO; + break; + } + break; + case CAM_REQ_CMP_ERR: + case CAM_AUTOSENSE_FAIL: + case CAM_CMD_TIMEOUT: + case CAM_UNEXP_BUSFREE: + case CAM_UNCOR_PARITY: + case CAM_DATA_RUN_ERR: + /* decrement the number of retries */ + retry = ccb->ccb_h.retry_count > 0; + if (retry) { + ccb->ccb_h.retry_count--; + error = ERESTART; + } else { + error = EIO; + } + break; + case CAM_UA_ABORT: + case CAM_UA_TERMIO: + case CAM_MSG_REJECT_REC: + /* XXX Don't know that these are correct */ + error = EIO; + break; + case CAM_SEL_TIMEOUT: + { + struct cam_path *newpath; + + error = ENXIO; + + /* Should we do more if we can't create the path?? */ + if (xpt_create_path(&newpath, xpt_path_periph(ccb->ccb_h.path), + xpt_path_path_id(ccb->ccb_h.path), + xpt_path_target_id(ccb->ccb_h.path), + CAM_LUN_WILDCARD) != CAM_REQ_CMP) + break; + /* + * Let peripheral drivers know that this device has gone + * away. + */ + xpt_async(AC_LOST_DEVICE, newpath, NULL); + xpt_free_path(newpath); + + break; + } + case CAM_REQ_INVALID: + case CAM_PATH_INVALID: + case CAM_DEV_NOT_THERE: + case CAM_NO_HBA: + case CAM_PROVIDE_FAIL: + case CAM_REQ_TOO_BIG: + error = EINVAL; + break; + case CAM_SCSI_BUS_RESET: + case CAM_BDR_SENT: + case CAM_REQUEUE_REQ: + /* Unconditional requeue, dammit */ + error = ERESTART; + break; + case CAM_RESRC_UNAVAIL: + case CAM_BUSY: + /* timeout??? */ + default: + /* decrement the number of retries */ + retry = ccb->ccb_h.retry_count > 0; + if (retry) { + ccb->ccb_h.retry_count--; + error = ERESTART; + } else { + /* Check the sense codes */ + error = EIO; + } + break; + } + + /* Attempt a retry */ + if (error == ERESTART || error == 0) { + if (frozen != 0) + ccb->ccb_h.status &= ~CAM_DEV_QFRZN; + + if (error == ERESTART) + xpt_action(ccb); + + if (frozen != 0) { + cam_release_devq(ccb->ccb_h.path, + relsim_flags, + openings, + timeout, + /*getcount_only*/0); + } + } + + + return (error); +} diff --git a/sys/cam/cam_periph.h b/sys/cam/cam_periph.h new file mode 100644 index 000000000000..04437d3ced8a --- /dev/null +++ b/sys/cam/cam_periph.h @@ -0,0 +1,135 @@ +/* + * Data structures and definitions for CAM peripheral ("type") drivers. + * + * Copyright (c) 1997, 1998 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_PERIPH_H +#define _CAM_CAM_PERIPH_H 1 + +#include + +#ifdef KERNEL + +extern struct linker_set periphdriver_set; + +typedef void (periph_init_t)(void); /* + * Callback informing the peripheral driver + * it can perform it's initialization since + * the XPT is now fully initialized. + */ +typedef periph_init_t *periph_init_func_t; + +struct periph_driver { + periph_init_func_t init; + char *driver_name; + TAILQ_HEAD(,cam_periph) units; + u_int generation; +}; + +typedef enum { + CAM_PERIPH_BIO, + CAM_PERIPH_NET +} cam_periph_type; + +/* Generically usefull offsets into the peripheral private area */ +#define ppriv_ptr0 periph_priv.entries[0].ptr +#define ppriv_ptr1 periph_priv.entries[1].ptr +#define ppriv_field0 periph_priv.entries[0].field +#define ppriv_field1 periph_priv.entries[1].field + +typedef void periph_start_t (struct cam_periph *periph, + union ccb *start_ccb); +typedef cam_status periph_ctor_t (struct cam_periph *periph, + void *arg); +typedef void periph_dtor_t (struct cam_periph *periph); + +struct cam_periph { + cam_pinfo pinfo; + periph_start_t *periph_start; + periph_dtor_t *periph_dtor; + char *periph_name; + struct cam_path *path; /* Compiled path to device */ + void *softc; + u_int32_t unit_number; + cam_periph_type type; + u_int32_t flags; +#define CAM_PERIPH_RUNNING 0x01 +#define CAM_PERIPH_LOCKED 0x02 +#define CAM_PERIPH_LOCK_WANTED 0x04 +#define CAM_PERIPH_INVALID 0x08 +#define CAM_PERIPH_NEW_DEV_FOUND 0x10 + u_int32_t immediate_priority; + u_int32_t refcount; + SLIST_HEAD(, ccb_hdr) ccb_list; /* For "immediate" requests */ + SLIST_ENTRY(cam_periph) periph_links; + TAILQ_ENTRY(cam_periph) unit_links; + ac_callback_t *deferred_callback; + ac_code deferred_ac; +}; + +#define CAM_PERIPH_MAXMAPS 2 + +struct cam_periph_map_info { + int num_bufs_used; + struct buf *bp[CAM_PERIPH_MAXMAPS]; +}; + +cam_status cam_periph_alloc(periph_ctor_t*, periph_dtor_t*, periph_start_t*, + char *name, cam_periph_type type, struct cam_path *, ac_callback_t *, ac_code, void *arg); +struct cam_periph *cam_periph_find(struct cam_path *path, char *name); +int cam_periph_lock(struct cam_periph *periph, int priority); +void cam_periph_unlock(struct cam_periph *periph); +cam_status cam_periph_acquire(struct cam_periph *periph); +void cam_periph_release(struct cam_periph *periph); +void cam_periph_invalidate(struct cam_periph *periph); +int cam_periph_mapmem(union ccb *ccb, + struct cam_periph_map_info *mapinfo); +void cam_periph_unmapmem(union ccb *ccb, + struct cam_periph_map_info *mapinfo); +union ccb *cam_periph_getccb(struct cam_periph *periph, + u_int32_t priority); +void cam_periph_ccbwait(union ccb *ccb); +int cam_periph_runccb(union ccb *ccb, + int (*error_routine)(union ccb *ccb, + cam_flags camflags, + u_int32_t sense_flags), + cam_flags camflags, u_int32_t sense_flags, + struct devstat *ds); +int cam_periph_ioctl(struct cam_periph *periph, int cmd, + caddr_t addr, + int (*error_routine)(union ccb *ccb, + cam_flags camflags, + u_int32_t sense_flags)); +u_int32_t cam_release_devq(struct cam_path *path, u_int32_t relsim_flags, + u_int32_t opening_reduction, u_int32_t timeout, + int getcount_only); +int cam_periph_error(union ccb *ccb, cam_flags camflags, + u_int32_t sense_flags, union ccb *save_ccb); + +#endif /* KERNEL */ +#endif /* _CAM_CAM_PERIPH_H */ diff --git a/sys/cam/cam_queue.c b/sys/cam/cam_queue.c new file mode 100644 index 000000000000..809955e28dde --- /dev/null +++ b/sys/cam/cam_queue.c @@ -0,0 +1,441 @@ +/* + * CAM request queue management functions. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +static __inline int + queue_cmp(cam_pinfo **queue_array, int i, int j); +static __inline void + swap(cam_pinfo **queue_array, int i, int j); +static void heap_up(cam_pinfo **queue_array, int new_index); +static void heap_down(cam_pinfo **queue_array, int index, + int last_index); + +struct camq * +camq_alloc(int size) +{ + struct camq *camq; + + camq = (struct camq *)malloc(sizeof(*camq), M_DEVBUF, M_NOWAIT); + if (camq != NULL) { + if (camq_init(camq, size) != 0) { + free(camq, M_DEVBUF); + camq = NULL; + } + } + return (camq); +} + +int +camq_init(struct camq *camq, int size) +{ + bzero(camq, sizeof(*camq)); + camq->array_size = size; + if (camq->array_size != 0) { + camq->queue_array = (cam_pinfo**)malloc(size*sizeof(cam_pinfo*), + M_DEVBUF, M_NOWAIT); + if (camq->queue_array == NULL) { + printf("camq_init: - cannot malloc array!\n"); + return (1); + } + } + return (0); +} + +/* + * Free a camq structure. This should only be called if a controller + * driver failes somehow during its attach routine or is unloaded and has + * obtained a camq structure. The XPT should ensure that the queue + * is empty before calling this routine. + */ +void +camq_free(struct camq *queue) +{ + if (queue != NULL) { + camq_fini(queue); + free(queue, M_DEVBUF); + } +} + +void +camq_fini(struct camq *queue) +{ + if (queue->queue_array != NULL) { + free(queue->queue_array, M_DEVBUF); + } +} + +u_int32_t +camq_resize(struct camq *queue, int new_size) +{ + cam_pinfo **new_array; + +#ifdef DIAGNOSTIC + if (new_size < queue->entries) + panic("camq_resize: New queue size can't accomodate " + "queued entries."); +#endif + new_array = (cam_pinfo **)malloc(new_size * sizeof(cam_pinfo *), + M_DEVBUF, M_NOWAIT); + if (new_array == NULL) { + /* Couldn't satisfy request */ + return (CAM_RESRC_UNAVAIL); + } + if (queue->queue_array != NULL) { + bcopy(queue->queue_array, new_array, + queue->entries * sizeof(cam_pinfo *)); + free(queue->queue_array, M_DEVBUF); + } + queue->queue_array = new_array; + queue->array_size = new_size; + return (CAM_REQ_CMP); +} + +/* + * camq_regen: Given an array of cam_pinfo* elements with the + * Heap(0, num_elements) property, perform the second half of + * a heap sort, and assign new generation numbers to all entries. + * It is assumed that the starting generation number plus the + * number of entries in the queue is smaller than the wrap point + * of the generation number. + */ +void +camq_regen(struct camq *queue) +{ + int index; + + for (index = 0; index < queue->entries; index++) { + + heap_down(queue->queue_array, index, queue->entries); + queue->queue_array[index]->generation = queue->generation++; + } + /* A sorted array is still a heap, so we are done */ +} + +/* + * camq_insert: Given an array of cam_pinfo* elememnts with + * the Heap(0, num_elements) property and array_size - num_elements >= 1, + * output Heap(0, num_elements+1) including new_entry in the array. + */ +void +camq_insert(struct camq *queue, cam_pinfo *new_entry) +{ +#ifdef DIAGNOSTIC + if (queue->entries >= queue->array_size) + panic("camq_insert: Attempt to insert into a full queue"); +#endif + queue->queue_array[queue->entries] = new_entry; + new_entry->index = queue->entries; + if (queue->entries != 0) + heap_up(queue->queue_array, queue->entries); + queue->entries++; +} + +/* + * camq_remove: Given an array of cam_pinfo* elevements with the + * Heap(0, num_elements) property and an index such that 0 <= index <= + * num_elements, remove that entry and restore the Heap(0, num_elements-1) + * property. + */ +cam_pinfo * +camq_remove(struct camq *queue, int index) +{ + cam_pinfo *removed_entry; + + if ((queue->entries - index) <= 0) + return (NULL); + removed_entry = queue->queue_array[index]; + queue->entries--; + if (queue->entries != index) { + queue->queue_array[index] = queue->queue_array[queue->entries]; + queue->queue_array[index]->index = index; + heap_down(queue->queue_array, index, queue->entries); + } + removed_entry->index = CAM_UNQUEUED_INDEX; + return (removed_entry); +} + +/* + * camq_change_priority: Given an array of cam_pinfo* elements with the + * Heap(0, num_entries) property, an index such that 0 <= index <= num_elements, + * and an new priority for the element at index, change the priority of + * element index and restore the Heap(0, num_elements) property. + */ +void +camq_change_priority(struct camq *queue, int index, u_int32_t new_priority) +{ + if (new_priority > queue->queue_array[index]->priority) { + queue->queue_array[index]->priority = new_priority; + heap_down(queue->queue_array, index, queue->entries); + } else { + /* new_priority <= old_priority */ + queue->queue_array[index]->priority = new_priority; + heap_up(queue->queue_array, index); + } +} + +struct cam_devq * +cam_devq_alloc(int devices, int openings) +{ + struct cam_devq *devq; + + devq = (struct cam_devq *)malloc(sizeof(*devq), M_DEVBUF, M_NOWAIT); + if (devq == NULL) { + printf("cam_devq_alloc: - cannot malloc!\n"); + return (NULL); + } + if (cam_devq_init(devq, devices, openings) != 0) { + free(devq, M_DEVBUF); + return (NULL); + } + + return (devq); +} + +int +cam_devq_init(struct cam_devq *devq, int devices, int openings) +{ + bzero(devq, sizeof(*devq)); + if (camq_init(&devq->alloc_queue, devices) != 0) { + return (1); + } + if (camq_init(&devq->send_queue, devices) != 0) { + camq_fini(&devq->alloc_queue); + return (1); + } + devq->alloc_openings = openings; + devq->alloc_active = 0; + devq->send_openings = openings; + devq->send_active = 0; + return (0); +} + +void +cam_devq_free(struct cam_devq *devq) +{ + camq_free(&devq->alloc_queue); + camq_free(&devq->send_queue); + free(devq, M_DEVBUF); +} + +u_int32_t +cam_devq_resize(struct cam_devq *camq, int devices) +{ + u_int32_t retval; + + retval = camq_resize(&camq->alloc_queue, devices); + + if (retval == CAM_REQ_CMP) + retval = camq_resize(&camq->send_queue, devices); + + return (retval); +} + +struct cam_ccbq * +cam_ccbq_alloc(int openings) +{ + struct cam_ccbq *ccbq; + + ccbq = (struct cam_ccbq *)malloc(sizeof(*ccbq), M_DEVBUF, M_NOWAIT); + if (ccbq == NULL) { + printf("cam_ccbq_alloc: - cannot malloc!\n"); + return (NULL); + } + if (cam_ccbq_init(ccbq, openings) != 0) { + free(ccbq, M_DEVBUF); + return (NULL); + } + + return (ccbq); +} + +void +cam_ccbq_free(struct cam_ccbq *ccbq) +{ + if (ccbq) { + camq_fini(&ccbq->queue); + free(ccbq, M_DEVBUF); + } +} + +u_int32_t +cam_ccbq_resize(struct cam_ccbq *ccbq, int new_size) +{ + int delta; + int space_left; + + delta = new_size - (ccbq->dev_active + ccbq->dev_openings); + space_left = new_size + - ccbq->queue.entries + - ccbq->held + - ccbq->dev_active; + + /* + * Only attempt to change the underlying queue size if we are + * shrinking it and there is space for all outstanding entries + * in the new array or we have been requested to grow the array. + * We don't fail in the case where we can't reduce the array size, + * but clients that care that the queue be "garbage collected" + * should detect this condition and call us again with the + * same size once the outstanding entries have been processed. + */ + if (space_left < 0 + || camq_resize(&ccbq->queue, new_size) == CAM_REQ_CMP) { + ccbq->devq_openings += delta; + ccbq->dev_openings += delta; + return (CAM_REQ_CMP); + } else { + return (CAM_RESRC_UNAVAIL); + } +} + +int +cam_ccbq_init(struct cam_ccbq *ccbq, int openings) +{ + bzero(ccbq, sizeof(*ccbq)); + if (camq_init(&ccbq->queue, openings) != 0) { + return (1); + } + ccbq->devq_openings = openings; + ccbq->dev_openings = openings; + TAILQ_INIT(&ccbq->active_ccbs); + return (0); +} + +void +cam_ccbq_regen(struct cam_ccbq *ccbq) +{ + struct ccb_hdr *ccbh; + + /* First get all of the guys down at a device */ + ccbh = ccbq->active_ccbs.tqh_first; + + while (ccbh != NULL) { + ccbh->pinfo.generation = ccbq->queue.generation++; + ccbh = ccbh->xpt_links.tqe.tqe_next; + } + + /* Now get everyone in our CAM queue */ + camq_regen(&ccbq->queue); +} + +/* + * Heap routines for manipulating CAM queues. + */ +/* + * queue_cmp: Given an array of cam_pinfo* elements and indexes i + * and j, return less than 0, 0, or greater than 0 if i is less than, + * equal too, or greater than j respectively. + */ +static __inline int +queue_cmp(cam_pinfo **queue_array, int i, int j) +{ + if (queue_array[i]->priority == queue_array[j]->priority) + return ( queue_array[i]->generation + - queue_array[j]->generation ); + else + return ( queue_array[i]->priority + - queue_array[j]->priority ); +} + +/* + * swap: Given an array of cam_pinfo* elements and indexes i and j, + * exchange elements i and j. + */ +static __inline void +swap(cam_pinfo **queue_array, int i, int j) +{ + cam_pinfo *temp_qentry; + + temp_qentry = queue_array[j]; + queue_array[j] = queue_array[i]; + queue_array[i] = temp_qentry; + queue_array[j]->index = j; + queue_array[i]->index = i; +} + +/* + * heap_up: Given an array of cam_pinfo* elements with the + * Heap(0, new_index-1) property and a new element in location + * new_index, output Heap(0, new_index). + */ +static void +heap_up(cam_pinfo **queue_array, int new_index) +{ + int child; + int parent; + + child = new_index; + + while (child != 0) { + + parent = child >> 1; + if (queue_cmp(queue_array, parent, child) <= 0) + break; + swap(queue_array, parent, child); + child = parent; + } +} + +/* + * heap_down: Given an array of cam_pinfo* elements with the + * Heap(1, num_entries - 1) property with index 0 containing an unsorted + * entry, output Heap(0, num_entries - 1). + */ +static void +heap_down(cam_pinfo **queue_array, int index, int num_entries) +{ + int child; + int parent; + + parent = index; + child = parent == 0 ? 1 : parent << 1; + for (; child < num_entries; child = parent << 1) { + + if (child + 1 < num_entries) { + /* child+1 is the right child of parent */ + if (queue_cmp(queue_array, child + 1, child) < 0) + child++; + } + /* child is now the least child of parent */ + if (queue_cmp(queue_array, parent, child) <= 0) + break; + swap(queue_array, child, parent); + parent = child; + } +} + diff --git a/sys/cam/cam_queue.h b/sys/cam/cam_queue.h new file mode 100644 index 000000000000..8123222f577c --- /dev/null +++ b/sys/cam/cam_queue.h @@ -0,0 +1,236 @@ +/* + * CAM request queue management definitions. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_QUEUE_H +#define _CAM_CAM_QUEUE_H 1 + +#ifdef KERNEL + +#include + +/* + * This structure implements a heap based priority queue. The queue + * assumes that the objects stored in it begin with a cam_qentry + * structure holding the priority information used to sort the objects. + * This structure is opaque to clients (outside of the XPT layer) to allow + * the implementation to change without affecting them. + */ +struct camq { + cam_pinfo **queue_array; + int array_size; + int entries; + u_int32_t generation; + u_int32_t qfrozen_cnt; +}; + +TAILQ_HEAD(ccb_hdr_list, ccb_hdr); + +struct cam_ccbq { + struct camq queue; + int devq_openings; + int dev_openings; + int dev_active; + int held; + struct ccb_hdr_list active_ccbs; +}; + +struct cam_ed; + +struct cam_devq { + struct camq alloc_queue; + struct camq send_queue; + struct cam_ed *active_dev; + int alloc_openings; + int alloc_active; + int send_openings; + int send_active; +}; + + +struct cam_devq *cam_devq_alloc(int devices, int openings); + +int cam_devq_init(struct cam_devq *devq, int devices, + int openings); + +void cam_devq_free(struct cam_devq *devq); + +u_int32_t cam_devq_resize(struct cam_devq *camq, int openings); + +/* + * Allocate a cam_ccb_queue structure and initialize it. + */ +struct cam_ccbq *cam_ccbq_alloc(int openings); + +u_int32_t cam_ccbq_resize(struct cam_ccbq *ccbq, int devices); + +int cam_ccbq_init(struct cam_ccbq *ccbq, int openings); + +void cam_ccbq_free(struct cam_ccbq *ccbq); + +void cam_ccbq_fini(struct cam_ccbq *ccbq); + +void cam_ccbq_regen(struct cam_ccbq *ccbq); + +/* + * Allocate and initialize a cam_queue structure. + */ +struct camq *camq_alloc(int size); + +/* + * Resize a cam queue + */ +u_int32_t camq_resize(struct camq *queue, int new_size); + +/* + * Initialize a camq structure. Return 0 on success, 1 on failure. + */ +int camq_init(struct camq *camq, int size); + +/* + * Free a cam_queue structure. This should only be called if a controller + * driver failes somehow during its attach routine or is unloaded and has + * obtained a cam_queue structure. + */ +void camq_free(struct camq *queue); + +/* + * Finialize any internal storage or state of a cam_queue. + */ +void camq_fini(struct camq *queue); + +/* + * cam_queue_insert: Given a CAM queue with at least one open spot, + * insert the new entry maintaining order. + */ +void camq_insert(struct camq *queue, cam_pinfo *new_entry); + +/* + * camq_remove: Remove and arbitrary entry from the queue maintaining + * queue order. + */ +cam_pinfo *camq_remove(struct camq *queue, int index); + +/* + * camq_change_priority: Raise or lower the priority of an entry + * maintaining queue order. + */ +void camq_change_priority(struct camq *queue, int index, + u_int32_t new_priority); + +void camq_regen(struct camq *queue); + +static __inline int +cam_ccbq_pending_ccb_count(struct cam_ccbq *ccbq); + +static __inline void +cam_ccbq_take_opening(struct cam_ccbq *ccbq); + +static __inline void +cam_ccbq_insert_ccb(struct cam_ccbq *ccbq, union ccb *new_ccb); + +static __inline void +cam_ccbq_remove_ccb(struct cam_ccbq *ccbq, union ccb *ccb); + +static __inline union ccb * +cam_ccbq_peek_ccb(struct cam_ccbq *ccbq, int index); + +static __inline void +cam_ccbq_send_ccb(struct cam_ccbq *queue, union ccb *send_ccb); + +static __inline void +cam_ccbq_ccb_done(struct cam_ccbq *ccbq, union ccb *done_ccb); + +static __inline void +cam_ccbq_release_opening(struct cam_ccbq *ccbq); + + +static __inline int +cam_ccbq_pending_ccb_count(struct cam_ccbq *ccbq) +{ + return (ccbq->queue.entries); +} + +static __inline void +cam_ccbq_take_opening(struct cam_ccbq *ccbq) +{ + ccbq->devq_openings--; + ccbq->held++; +} + +static __inline void +cam_ccbq_insert_ccb(struct cam_ccbq *ccbq, union ccb *new_ccb) +{ + ccbq->held--; + camq_insert(&ccbq->queue, &new_ccb->ccb_h.pinfo); +} + +static __inline void +cam_ccbq_remove_ccb(struct cam_ccbq *ccbq, union ccb *ccb) +{ + camq_remove(&ccbq->queue, ccb->ccb_h.pinfo.index); +} + +static __inline union ccb * +cam_ccbq_peek_ccb(struct cam_ccbq *ccbq, int index) +{ + return((union ccb *)ccbq->queue.queue_array[index]); +} + +static __inline void +cam_ccbq_send_ccb(struct cam_ccbq *ccbq, union ccb *send_ccb) +{ + + TAILQ_INSERT_TAIL(&ccbq->active_ccbs, + &(send_ccb->ccb_h), + xpt_links.tqe); + send_ccb->ccb_h.pinfo.index = CAM_ACTIVE_INDEX; + ccbq->dev_active++; + ccbq->dev_openings--; +} + +static __inline void +cam_ccbq_ccb_done(struct cam_ccbq *ccbq, union ccb *done_ccb) +{ + TAILQ_REMOVE(&ccbq->active_ccbs, &done_ccb->ccb_h, + xpt_links.tqe); + ccbq->dev_active--; + ccbq->dev_openings++; + ccbq->held++; +} + +static __inline void +cam_ccbq_release_opening(struct cam_ccbq *ccbq) +{ + ccbq->held--; + ccbq->devq_openings++; +} + +#endif /* KERNEL */ +#endif /* _CAM_CAM_QUEUE_H */ diff --git a/sys/cam/cam_sim.c b/sys/cam/cam_sim.c new file mode 100644 index 000000000000..70204411f062 --- /dev/null +++ b/sys/cam/cam_sim.c @@ -0,0 +1,112 @@ +/* + * Common functions for SCSI Interface Modules (SIMs). + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#include +#include +#include + +#include +#include +#include +#include + +#define CAM_PATH_ANY (u_int32_t)-1 + +struct cam_devq * +cam_simq_alloc(u_int32_t max_sim_transactions) +{ + return (cam_devq_alloc(/*size*/0, max_sim_transactions)); +} + +void +cam_simq_free(struct cam_devq *devq) +{ + cam_devq_free(devq); +} + +struct cam_sim * +cam_sim_alloc(sim_action_func sim_action, sim_poll_func sim_poll, + char *sim_name, void *softc, u_int32_t unit, + u_int32_t max_dev_transactions, + u_int32_t max_tagged_dev_transactions, struct cam_devq *queue) +{ + struct cam_sim *sim; + + /* + * If this is the xpt layer creating a sim, then it's OK + * to wait for an allocation. + * + * XXX Should we pass in a flag to indicate that wait is OK? + */ + if (strcmp(sim_name, "xpt") == 0) + sim = (struct cam_sim *)malloc(sizeof(struct cam_sim), + M_DEVBUF, M_WAITOK); + else + sim = (struct cam_sim *)malloc(sizeof(struct cam_sim), + M_DEVBUF, M_NOWAIT); + + if (sim != NULL) { + sim->sim_action = sim_action; + sim->sim_poll = sim_poll; + sim->sim_name = sim_name; + sim->softc = softc; + sim->path_id = CAM_PATH_ANY; + sim->unit_number = unit; + sim->bus_id = 0; /* set in xpt_bus_register */ + sim->base_transfer_speed = 3300; /* asynchronous 3300 kB/sec */ + sim->max_tagged_dev_openings = max_tagged_dev_transactions; + sim->max_dev_openings = max_dev_transactions; + sim->flags = 0; + callout_handle_init(&sim->c_handle); + sim->devq = queue; + } + + return (sim); +} + +void +cam_sim_free(struct cam_sim *sim, int free_devq) +{ + if (free_devq) + cam_simq_free(sim->devq); + free(sim, M_DEVBUF); +} + +void +cam_sim_set_basexfer_speed(struct cam_sim *sim, u_int32_t base_xfer_speed) +{ + sim->base_transfer_speed = base_xfer_speed; +} + +void +cam_sim_set_path(struct cam_sim *sim, u_int32_t path_id) +{ + sim->path_id = path_id; +} diff --git a/sys/cam/cam_sim.h b/sys/cam/cam_sim.h new file mode 100644 index 000000000000..183bcedd8213 --- /dev/null +++ b/sys/cam/cam_sim.h @@ -0,0 +1,138 @@ +/* + * Data structures and definitions for SCSI Interface Modules (SIMs). + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_SIM_H +#define _CAM_CAM_SIM_H 1 + +#ifdef KERNEL + +/* + * The sim driver creates a sim for each controller. The sim device + * queue is separately created in order to allow resource sharing between + * sims. For instance, a driver may create one sim for each channel of + * a multi-channel controller and use the same queue for each channel. + * In this way, the queue resources are shared across all the channels + * of the multi-channel controller. + */ + +struct cam_sim; +struct cam_devq; + +typedef void (*sim_action_func)(struct cam_sim *sim, union ccb *ccb); +typedef void (*sim_poll_func)(struct cam_sim *sim); + +struct cam_devq * cam_simq_alloc(u_int32_t max_sim_transactions); +void cam_simq_free(struct cam_devq *devq); + +struct cam_sim * cam_sim_alloc(sim_action_func sim_action, + sim_poll_func sim_poll, + char *sim_name, + void *softc, + u_int32_t unit, + u_int32_t max_dev_transactions, + u_int32_t max_tagged_dev_transactions, + struct cam_devq *queue); +void cam_sim_free(struct cam_sim *sim, int free_devq); + +/* Optional sim attributes may be set with these. */ +void cam_sim_set_basexfer_speed(struct cam_sim *sim, + u_int32_t base_xfer_speed); +void cam_sim_set_path(struct cam_sim *sim, u_int32_t path_id); + + + +/* Convenience routines for accessing sim attributes. */ +static __inline u_int32_t cam_sim_path(struct cam_sim *sim); +static __inline char * cam_sim_name(struct cam_sim *sim); +static __inline void * cam_sim_softc(struct cam_sim *sim); +static __inline u_int32_t cam_sim_unit(struct cam_sim *sim); +static __inline u_int32_t cam_sim_bus(struct cam_sim *sim); + + + +/* Generically useful offsets into the sim private area */ +#define spriv_ptr0 sim_priv.entries[0].ptr +#define spriv_ptr1 sim_priv.entries[1].ptr +#define spriv_field0 sim_priv.entries[0].field +#define spriv_field1 sim_priv.entries[1].field + +/* + * The sim driver should not access anything directly from this + * structure. + */ +struct cam_sim { + sim_action_func sim_action; + sim_poll_func sim_poll; + char *sim_name; + void *softc; + u_int32_t path_id;/* The Boot device may set this to 0? */ + u_int32_t unit_number; + u_int32_t bus_id; + u_int32_t base_transfer_speed; /* in kB/s */ + u_int32_t max_tagged_dev_openings; + u_int32_t max_dev_openings; + u_int32_t flags; +#define CAM_SIM_REL_TIMEOUT_PENDING 0x01 + struct callout_handle c_handle; + struct cam_devq *devq; /* Device Queue to use for this SIM */ +}; + +static __inline u_int32_t +cam_sim_path(struct cam_sim *sim) +{ + return (sim->path_id); +} + +static __inline char * +cam_sim_name(struct cam_sim *sim) +{ + return (sim->sim_name); +} + +static __inline void * +cam_sim_softc(struct cam_sim *sim) +{ + return (sim->softc); +} + +static __inline u_int32_t +cam_sim_unit(struct cam_sim *sim) +{ + return (sim->unit_number); +} + +static __inline u_int32_t +cam_sim_bus(struct cam_sim *sim) +{ + return (sim->bus_id); +} + +#endif /* KERNEL */ +#endif /* _CAM_CAM_SIM_H */ diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c new file mode 100644 index 000000000000..11a2afe1d1d2 --- /dev/null +++ b/sys/cam/cam_xpt.c @@ -0,0 +1,5610 @@ +/* + * Implementation of the Common Access Method Transport (XPT) layer. + * + * Copyright (c) 1997, 1998 Justin T. Gibbs. + * Copyright (c) 1997, 1998 Kenneth D. Merry. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef PC98 +#include /* geometry translation */ +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "opt_cam.h" +#include "opt_scsi.h" + +extern void (*ihandlers[32]) __P((void)); + +/* Datastructures internal to the xpt layer */ + +/* + * Definition of an async handler callback block. These are used to add + * SIMs and peripherals to the async callback lists. + */ +struct async_node { + SLIST_ENTRY(async_node) links; + u_int32_t event_enable; /* Async Event enables */ + void (*callback)(void *arg, u_int32_t code, + struct cam_path *path, void *args); + void *callback_arg; +}; + +SLIST_HEAD(async_list, async_node); +SLIST_HEAD(periph_list, cam_periph); +STAILQ_HEAD(highpowerlist, ccb_hdr) highpowerq; + +/* + * This is the maximum number of high powered commands (e.g. start unit) + * that can be outstanding at a particular time. + */ +#ifndef CAM_MAX_HIGHPOWER +#define CAM_MAX_HIGHPOWER 4 +#endif + +/* + * This is the number of seconds we wait for devices to settle after a SCSI + * bus reset. + */ +#ifndef SCSI_DELAY +#define SCSI_DELAY 2000 +#endif +#if (SCSI_DELAY < 100) +#error "SCSI_DELAY is in milliseconds, not seconds! Please use a larger value" +#endif + +/* number of high powered commands that can go through right now */ +static int num_highpower = CAM_MAX_HIGHPOWER; + +/* + * Structure for queueing a device in a run queue. + * There is one run queue for allocating new ccbs, + * and another for sending ccbs to the controller. + */ +struct cam_ed_qinfo { + cam_pinfo pinfo; + struct cam_ed *device; +}; + +/* + * The CAM EDT (Existing Device Table) contains the device information for + * all devices for all busses in the system. The table contains a + * cam_ed structure for each device on the bus. + */ +struct cam_ed { + TAILQ_ENTRY(cam_ed) links; + struct cam_ed_qinfo alloc_ccb_entry; + struct cam_ed_qinfo send_ccb_entry; + struct cam_et *target; + lun_id_t lun_id; + struct camq drvq; /* + * Queue of type drivers wanting to do + * work on this device. + */ + struct cam_ccbq ccbq; /* Queue of pending ccbs */ + struct async_list asyncs; /* Async callback info for this B/T/L */ + struct periph_list periphs; /* All attached devices */ + u_int generation; /* Generation number */ + struct cam_periph *owner; /* Peripheral driver's ownership tag */ + struct xpt_quirk_entry *quirk; /* Oddities about this device */ + /* Storage for the inquiry data */ + struct scsi_inquiry_data inq_data; + u_int8_t inq_flags; /* + * Current settings for inquiry flags. + * This allows us to override settings + * like disconnection and tagged + * queuing for a device. + */ + u_int8_t queue_flags; /* Queue flags from the control page */ + u_int8_t *serial_num; + u_int8_t serial_num_len; + u_int32_t qfrozen_cnt; + u_int32_t flags; +#define CAM_DEV_UNCONFIGURED 0x01 +#define CAM_DEV_REL_TIMEOUT_PENDING 0x02 +#define CAM_DEV_REL_ON_COMPLETE 0x04 +#define CAM_DEV_REL_ON_QUEUE_EMPTY 0x08 +#define CAM_DEV_RESIZE_QUEUE_NEEDED 0x10 + u_int32_t refcount; + struct callout_handle c_handle; +}; + +/* + * Each target is represented by an ET (Existing Target). These + * entries are created when a target is successfully probed with an + * identify, and removed when a device fails to respond after a number + * of retries, or a bus rescan finds the device missing. + */ +struct cam_et { + TAILQ_HEAD(, cam_ed) ed_entries; + TAILQ_ENTRY(cam_et) links; + struct cam_eb *bus; + target_id_t target_id; + u_int32_t refcount; + u_int generation; +}; + +/* + * Each bus is represented by an EB (Existing Bus). These entries + * are created by calls to xpt_bus_register and deleted by calls to + * xpt_bus_deregister. + */ +struct cam_eb { + TAILQ_HEAD(, cam_et) et_entries; + TAILQ_ENTRY(cam_eb) links; + struct async_list asyncs; /* Async callback info for this B/T/L */ + path_id_t path_id; + struct cam_sim *sim; + u_int32_t flags; +#define CAM_EB_RUNQ_SCHEDULED 0x01 + u_int generation; +}; + +struct cam_path { + struct cam_periph *periph; + struct cam_eb *bus; + struct cam_et *target; + struct cam_ed *device; +}; + +struct xpt_quirk_entry { + struct scsi_inquiry_pattern inq_pat; + u_int8_t quirks; +#define CAM_QUIRK_NOLUNS 0x01 +#define CAM_QUIRK_NOSERIAL 0x02 + u_int8_t mintags; + u_int8_t maxtags; +}; + +typedef enum { + XPT_FLAG_OPEN = 0x01 +} xpt_flags; + +struct xpt_softc { + xpt_flags flags; + u_int32_t generation; +#ifdef DEVFS + void *xpt_devfs_token; + void *ctl_devfs_token; +#endif +}; + +static const char quantum[] = "QUANTUM"; + +static struct xpt_quirk_entry xpt_quirk_table[] = +{ + { + /* Reports QUEUE FULL for temporary resource shortages */ + { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP39100?", "*" }, + /*quirks*/0, /*mintags*/24, /*maxtags*/32 + }, + { + /* Reports QUEUE FULL for temporary resource shortages */ + { T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP34550?", "*" }, + /*quirks*/0, /*mintags*/24, /*maxtags*/32 + }, + { + /* Broken tagged queuing drive */ + { T_DIRECT, SIP_MEDIA_FIXED, "HP", "C372*", "*" }, + /*quirks*/0, /*mintags*/0, /*maxtags*/0 + }, + { + /* Broken tagged queuing drive */ + { T_DIRECT, SIP_MEDIA_FIXED, "MICROP", "3391*", "x43h" }, + /*quirks*/0, /*mintags*/0, /*maxtags*/0 + }, + { + /* Broken tagged queuing drive */ + { T_DIRECT, SIP_MEDIA_REMOVABLE, "iomega", "jaz*", "*" }, + /*quirks*/0, /*mintags*/0, /*maxtags*/0 + }, + { + /* Doesn't understand EVP Serial Requests */ + { + T_CDROM, SIP_MEDIA_REMOVABLE, + "TOSHIBA", "CD-ROM XM-3401TA", "1094" + }, + CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0 + }, + { + /* + * Hack until multiple-luns are supported by + * the target mode code. + */ + { + T_PROCESSOR, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, + "FreeBSD", "TM-PT", "*" + }, + CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 + }, + { + /* Really only one LUN */ + { + T_ENCLOSURE, SIP_MEDIA_FIXED, "SUN", "SENA*", "*" }, + CAM_QUIRK_NOLUNS, /*mintags*/0, /*maxtags*/0 + }, + { + /* Default tagged queuing parameters for all devices */ + { + T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED, + /*vendor*/"*", /*product*/"*", /*revision*/"*" + }, + /*quirks*/0, /*mintags*/2, /*maxtags*/64 + }, +}; +typedef enum { + DM_RET_COPY = 0x01, + DM_RET_FLAG_MASK = 0x0f, + DM_RET_NONE = 0x00, + DM_RET_STOP = 0x10, + DM_RET_DESCEND = 0x20, + DM_RET_ERROR = 0x30, + DM_RET_ACTION_MASK = 0xf0 +} dev_match_ret; + +typedef enum { + XPT_DEPTH_BUS, + XPT_DEPTH_TARGET, + XPT_DEPTH_DEVICE, + XPT_DEPTH_PERIPH +} xpt_traverse_depth; + +struct xpt_traverse_config { + xpt_traverse_depth depth; + void *tr_func; + void *tr_arg; +}; + +typedef int xpt_busfunc_t (struct cam_eb *bus, void *arg); +typedef int xpt_targetfunc_t (struct cam_et *target, void *arg); +typedef int xpt_devicefunc_t (struct cam_ed *device, void *arg); +typedef int xpt_periphfunc_t (struct cam_periph *periph, void *arg); +typedef int xpt_pdrvfunc_t (struct periph_driver **pdrv, void *arg); + +/* Transport layer configuration information */ +static struct xpt_softc xsoftc; + +/* Queues for our software interrupt handler */ +typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t; +static cam_isrq_t cam_bioq; +static cam_isrq_t cam_netq; + +/* "Pool" of inactive ccbs managed by xpt_alloc_ccb and xpt_free_ccb */ +SLIST_HEAD(,ccb_hdr) ccb_freeq; +static u_int xpt_max_ccbs; /* + * Maximum size of ccb pool. Modified as + * devices are added/removed or have their + * opening counts changed. + */ +static u_int xpt_ccb_count; /* Current count of allocated ccbs */ + +static struct cam_periph *xpt_periph; + +static periph_init_t xpt_periph_init; + +static periph_init_t probe_periph_init; + +static struct periph_driver xpt_driver = +{ + xpt_periph_init, "xpt", + TAILQ_HEAD_INITIALIZER(xpt_driver.units) +}; + +static struct periph_driver probe_driver = +{ + probe_periph_init, "probe", + TAILQ_HEAD_INITIALIZER(probe_driver.units) +}; + +DATA_SET(periphdriver_set, xpt_driver); +DATA_SET(periphdriver_set, probe_driver); + +#define XPT_CDEV_MAJOR 104 + +static d_open_t xptopen; +static d_close_t xptclose; +static d_ioctl_t xptioctl; + +static struct cdevsw xpt_cdevsw = +{ + /*d_open*/ xptopen, + /*d_close*/ xptclose, + /*d_read*/ noread, + /*d_write*/ nowrite, + /*d_ioctl*/ xptioctl, + /*d_stop*/ nostop, + /*d_reset*/ noreset, + /*d_devtotty*/ nodevtotty, + /*d_poll*/ NULL, + /*d_mmap*/ nommap, + /*d_strategy*/ nostrategy, + /*d_name*/ "xpt", + /*d_spare*/ NULL, + /*d_maj*/ -1, + /*d_dump*/ nodump, + /*d_psize*/ nopsize, + /*d_flags*/ 0, + /*d_maxio*/ 0, + /*b_maj*/ -1 +}; + +static struct intr_config_hook *xpt_config_hook; + +/* Registered busses */ +TAILQ_HEAD(,cam_eb) xpt_busses; +static u_int bus_generation; + +/* Storage for debugging datastructures */ +#ifdef CAMDEBUG +struct cam_path *cam_dpath; +u_int32_t cam_dflags; +#endif + +#if defined(CAM_DEBUG_FLAGS) && !defined(CAMDEBUG) +#error "You must have options CAMDEBUG to use options CAM_DEBUG_FLAGS" +#endif + +/* + * In order to enable the CAM_DEBUG_* options, the user must have CAMDEBUG + * enabled. Also, the user must have either none, or all of CAM_DEBUG_BUS, + * CAM_DEBUG_TARGET, and CAM_DEBUG_LUN specified. + */ +#if defined(CAM_DEBUG_BUS) || defined(CAM_DEBUG_TARGET) \ + || defined(CAM_DEBUG_LUN) +#ifdef CAMDEBUG +#if !defined(CAM_DEBUG_BUS) || !defined(CAM_DEBUG_TARGET) \ + || !defined(CAM_DEBUG_LUN) +#error "You must define all or none of CAM_DEBUG_BUS, CAM_DEBUG_TARGET \ + and CAM_DEBUG_LUN" +#endif /* !CAM_DEBUG_BUS || !CAM_DEBUG_TARGET || !CAM_DEBUG_LUN */ +#else /* !CAMDEBUG */ +#error "You must use options CAMDEBUG if you use the CAM_DEBUG_* options" +#endif /* CAMDEBUG */ +#endif /* CAM_DEBUG_BUS || CAM_DEBUG_TARGET || CAM_DEBUG_LUN */ + +/* Forward declarations for private functions */ +void xpt_init(void); + +static cam_status xpt_compile_path(struct cam_path *new_path, + struct cam_periph *perph, + path_id_t path_id, + target_id_t target_id, + lun_id_t lun_id); + +static void xpt_release_path(struct cam_path *path); + +static void xpt_async_bcast(struct async_list *async_head, + u_int32_t async_code, + struct cam_path *path, + void *async_arg); +static int xptnextfreebus(path_id_t startbus); +static int xptpathid(const char *sim_name, int sim_unit, int sim_bus, + path_id_t *nextpath); +static union ccb *xpt_get_ccb(struct cam_ed *device); +static int xpt_schedule_dev(struct camq *queue, cam_pinfo *dev_pinfo, + u_int32_t new_priority); +static void xpt_run_dev_allocq(struct cam_eb *bus); +static void xpt_run_dev_sendq(struct cam_eb *bus); +static timeout_t xpt_release_devq_timeout; +static timeout_t xpt_release_simq_timeout; +static struct cam_et* + xpt_alloc_target(struct cam_eb *bus, target_id_t target_id); +static void xpt_release_target(struct cam_eb *bus, struct cam_et *target); +static struct cam_ed* + xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, + lun_id_t lun_id); +static void xpt_release_device(struct cam_eb *bus, struct cam_et *target, + struct cam_ed *device); +static u_int32_t xpt_dev_ccbq_resize(struct cam_path *path, int newopenings); +static struct cam_eb* + xpt_find_bus(path_id_t path_id); +static struct cam_et* + xpt_find_target(struct cam_eb *bus, target_id_t target_id); +static struct cam_ed* + xpt_find_device(struct cam_et *target, lun_id_t lun_id); +static void xpt_scan_bus(struct cam_periph *periph, union ccb *ccb); +static void xpt_scan_lun(struct cam_periph *periph, + struct cam_path *path, cam_flags flags, + union ccb *ccb); +static void xptscandone(struct cam_periph *periph, union ccb *done_ccb); +static xpt_busfunc_t xptconfigbuscountfunc; +static xpt_busfunc_t xptconfigfunc; +static void xpt_config(void *arg); +static xpt_devicefunc_t xptfinishconfigfunc; +static xpt_devicefunc_t xptpassannouncefunc; +static void xpt_finishconfig(struct cam_periph *periph, union ccb *ccb); +static void xptaction(struct cam_sim *sim, union ccb *work_ccb); + void swi_camnet(void); + void swi_cambio(void); +static void camisr(cam_isrq_t *queue); +#if 0 +static void xptstart(struct cam_periph *periph, union ccb *work_ccb); +static void xptasync(struct cam_periph *periph, + u_int32_t code, cam_path *path); +#endif +static dev_match_ret xptbusmatch(struct dev_match_pattern *patterns, + int num_patterns, struct cam_eb *bus); +static dev_match_ret xptdevicematch(struct dev_match_pattern *patterns, + int num_patterns, struct cam_ed *device); +static dev_match_ret xptperiphmatch(struct dev_match_pattern *patterns, + int num_patterns, + struct cam_periph *periph); +static xpt_busfunc_t xptedtbusfunc; +static xpt_targetfunc_t xptedttargetfunc; +static xpt_devicefunc_t xptedtdevicefunc; +static xpt_periphfunc_t xptedtperiphfunc; +static xpt_pdrvfunc_t xptplistpdrvfunc; +static xpt_periphfunc_t xptplistperiphfunc; +static int xptedtmatch(struct ccb_dev_match *cdm); +static int xptperiphlistmatch(struct ccb_dev_match *cdm); +static int xptbustraverse(struct cam_eb *start_bus, + xpt_busfunc_t *tr_func, void *arg); +static int xpttargettraverse(struct cam_eb *bus, + struct cam_et *start_target, + xpt_targetfunc_t *tr_func, void *arg); +static int xptdevicetraverse(struct cam_et *target, + struct cam_ed *start_device, + xpt_devicefunc_t *tr_func, void *arg); +static int xptperiphtraverse(struct cam_ed *device, + struct cam_periph *start_periph, + xpt_periphfunc_t *tr_func, void *arg); +static int xptpdrvtraverse(struct periph_driver **start_pdrv, + xpt_pdrvfunc_t *tr_func, void *arg); +static int xptpdperiphtraverse(struct periph_driver **pdrv, + struct cam_periph *start_periph, + xpt_periphfunc_t *tr_func, + void *arg); +static xpt_busfunc_t xptdefbusfunc; +static xpt_targetfunc_t xptdeftargetfunc; +static xpt_devicefunc_t xptdefdevicefunc; +static xpt_periphfunc_t xptdefperiphfunc; +static int xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg); +static int xpt_for_all_targets(xpt_targetfunc_t *tr_func, + void *arg); +static int xpt_for_all_devices(xpt_devicefunc_t *tr_func, + void *arg); +static int xpt_for_all_periphs(xpt_periphfunc_t *tr_func, + void *arg); +static xpt_devicefunc_t xptsetasyncfunc; +static xpt_busfunc_t xptsetasyncbusfunc; +static cam_status xptregister(struct cam_periph *periph, + void *arg); +static cam_status proberegister(struct cam_periph *periph, + void *arg); +static void probeschedule(struct cam_periph *probe_periph); +static void probestart(struct cam_periph *periph, union ccb *start_ccb); +static void probedone(struct cam_periph *periph, union ccb *done_ccb); +static void probecleanup(struct cam_periph *periph); +static void xpt_find_quirk(struct cam_ed *device); +static void xpt_set_transfer_settings(struct ccb_trans_settings *cts, + int async_update); +static __inline int xpt_schedule_dev_allocq(struct cam_eb *bus, + struct cam_ed *dev); +static __inline int xpt_schedule_dev_sendq(struct cam_eb *bus, + struct cam_ed *dev); +static __inline int periph_is_queued(struct cam_periph *periph); +static __inline int device_is_alloc_queued(struct cam_ed *device); +static __inline int device_is_send_queued(struct cam_ed *device); +static __inline int dev_allocq_is_runnable(struct cam_devq *devq); + +static __inline int +xpt_schedule_dev_allocq(struct cam_eb *bus, struct cam_ed *dev) +{ + int retval; + + if (dev->ccbq.devq_openings > 0) { + if ((dev->flags & CAM_DEV_RESIZE_QUEUE_NEEDED) != 0) { + cam_ccbq_resize(&dev->ccbq, + dev->ccbq.dev_openings + + dev->ccbq.dev_active); + dev->flags &= ~CAM_DEV_RESIZE_QUEUE_NEEDED; + } + retval = xpt_schedule_dev(&bus->sim->devq->alloc_queue, + &dev->alloc_ccb_entry.pinfo, + dev->drvq.queue_array[0]->priority); + } else { + retval = 0; + } + + return (retval); +} + +static __inline int +xpt_schedule_dev_sendq(struct cam_eb *bus, struct cam_ed *dev) +{ + int retval; + + if (dev->ccbq.dev_openings > 0) { + retval = xpt_schedule_dev(&bus->sim->devq->send_queue, + &dev->send_ccb_entry.pinfo, + dev->ccbq.queue.queue_array[0]->priority); + } else { + retval = 0; + } + return (retval); +} + +static __inline int +periph_is_queued(struct cam_periph *periph) +{ + return (periph->pinfo.index != CAM_UNQUEUED_INDEX); +} + +static __inline int +device_is_alloc_queued(struct cam_ed *device) +{ + return (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); +} + +static __inline int +device_is_send_queued(struct cam_ed *device) +{ + return (device->send_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX); +} + +static __inline int +dev_allocq_is_runnable(struct cam_devq *devq) +{ + /* + * Have work to do. + * Have space to do more work. + * Allowed to do work. + */ + return ((devq->alloc_queue.qfrozen_cnt == 0) + && (devq->alloc_queue.entries > 0) + && (devq->alloc_openings > 0)); +} + +static void +xpt_periph_init() +{ + dev_t dev; + + dev = makedev(XPT_CDEV_MAJOR, 0); + cdevsw_add(&dev, &xpt_cdevsw, NULL); +} + +static void +probe_periph_init() +{ +} + + +static void +xptdone(struct cam_periph *periph, union ccb *done_ccb) +{ + /* Caller will release the CCB */ + wakeup(&done_ccb->ccb_h.cbfcnp); +} + +static int +xptopen(dev_t dev, int flags, int fmt, struct proc *p) +{ + int unit; + + unit = minor(dev) & 0xff; + + /* + * We don't allow nonblocking access. + */ + if ((flags & O_NONBLOCK) != 0) { + printf("xpt%d: can't do nonblocking accesss\n", unit); + return(ENODEV); + } + + /* + * We only have one transport layer right now. If someone accesses + * us via something other than minor number 1, point out their + * mistake. + */ + if (unit != 0) { + printf("xptopen: got invalid xpt unit %d\n", unit); + return(ENXIO); + } + + /* Mark ourselves open */ + xsoftc.flags |= XPT_FLAG_OPEN; + + return(0); +} + +static int +xptclose(dev_t dev, int flag, int fmt, struct proc *p) +{ + int unit; + + unit = minor(dev) & 0xff; + + /* + * We only have one transport layer right now. If someone accesses + * us via something other than minor number 1, point out their + * mistake. + */ + if (unit != 0) { + printf("xptclose: got invalid xpt unit %d\n", unit); + return(ENXIO); + } + + /* Mark ourselves closed */ + xsoftc.flags &= ~XPT_FLAG_OPEN; + + return(0); +} + +static int +xptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +{ + int unit, error; + + error = 0; + unit = minor(dev) & 0xff; + + /* + * We only have one transport layer right now. If someone accesses + * us via something other than minor number 1, point out their + * mistake. + */ + if (unit != 0) { + printf("xptioctl: got invalid xpt unit %d\n", unit); + return(ENXIO); + } + + switch(cmd) { + /* + * For the transport layer CAMIOCOMMAND ioctl, we really only want + * to accept CCB types that don't quite make sense to send through a + * passthrough driver. + */ + case CAMIOCOMMAND: { + union ccb *ccb; + union ccb *inccb; + + inccb = (union ccb *)addr; + + switch(inccb->ccb_h.func_code) { + case XPT_SCAN_BUS: + case XPT_RESET_BUS: + if ((inccb->ccb_h.target_id != CAM_TARGET_WILDCARD) + || (inccb->ccb_h.target_lun != CAM_LUN_WILDCARD)) { + error = EINVAL; + break; + } + /* FALLTHROUGH */ + case XPT_SCAN_LUN: + case XPT_ENG_INQ: /* XXX not implemented yet */ + case XPT_ENG_EXEC: + + ccb = xpt_alloc_ccb(); + + /* + * Create a path using the bus, target, and lun the + * user passed in. + */ + if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, + inccb->ccb_h.path_id, + inccb->ccb_h.target_id, + inccb->ccb_h.target_lun) != + CAM_REQ_CMP){ + error = EINVAL; + xpt_free_ccb(ccb); + break; + } + /* Ensure all of our fields are correct */ + xpt_setup_ccb(&ccb->ccb_h, ccb->ccb_h.path, + inccb->ccb_h.pinfo.priority); + xpt_merge_ccb(ccb, inccb); + ccb->ccb_h.cbfcnp = xptdone; + cam_periph_runccb(ccb, NULL, 0, 0, NULL); + bcopy(ccb, inccb, sizeof(union ccb)); + xpt_free_path(ccb->ccb_h.path); + xpt_free_ccb(ccb); + break; + + case XPT_DEBUG: { + union ccb ccb; + + /* + * This is an immedaite CCB, so it's okay to + * allocate it on the stack. + */ + + /* + * Create a path using the bus, target, and lun the + * user passed in. + */ + if (xpt_create_path(&ccb.ccb_h.path, xpt_periph, + inccb->ccb_h.path_id, + inccb->ccb_h.target_id, + inccb->ccb_h.target_lun) != + CAM_REQ_CMP){ + error = EINVAL; + break; + } + /* Ensure all of our fields are correct */ + xpt_setup_ccb(&ccb.ccb_h, ccb.ccb_h.path, + inccb->ccb_h.pinfo.priority); + xpt_merge_ccb(&ccb, inccb); + ccb.ccb_h.cbfcnp = xptdone; + xpt_action(&ccb); + bcopy(&ccb, inccb, sizeof(union ccb)); + xpt_free_path(ccb.ccb_h.path); + break; + + } + case XPT_DEV_MATCH: { + struct cam_periph_map_info mapinfo; + + /* + * We can't deal with physical addresses for this + * type of transaction. + */ + if (inccb->ccb_h.flags & CAM_DATA_PHYS) { + error = EINVAL; + break; + } + bzero(&mapinfo, sizeof(mapinfo)); + + /* + * Map the pattern and match buffers into kernel + * virtual address space. + */ + error = cam_periph_mapmem(inccb, &mapinfo); + + if (error) + break; + + /* + * This is an immediate CCB, we can send it on directly. + */ + xpt_action(inccb); + + /* + * Map the buffers back into user space. + */ + cam_periph_unmapmem(inccb, &mapinfo); + + error = 0; + break; + } + default: + error = EINVAL; + break; + } + break; + } + /* + * This is the getpassthru ioctl. It takes a XPT_GDEVLIST ccb as input, + * with the periphal driver name and unit name filled in. The other + * fields don't really matter as input. The passthrough driver name + * ("pass"), and unit number are passed back in the ccb. The current + * device generation number, and the index into the device peripheral + * driver list, and the status are also passed back. Note that + * since we do everything in one pass, unlike the XPT_GDEVLIST ccb, + * we never return a status of CAM_GDEVLIST_LIST_CHANGED. It is + * (or rather should be) impossible for the device peripheral driver + * list to change since we look at the whole thing in one pass, and + * we do it with splsoftcam protection. + * + */ + case CAMGETPASSTHRU: { + union ccb *ccb; + struct cam_periph *periph; + struct periph_driver **p_drv; + char *name; + int unit; + int cur_generation; + int splbreaknum; + int s; + int i; + + ccb = (union ccb *)addr; + unit = ccb->cgdl.unit_number; + name = ccb->cgdl.periph_name; + /* + * Every 100 devices, we want to drop our spl protection to + * give the software interrupt handler a chance to run. + * Most systems won't run into this check, but this should + * avoid starvation in the software interrupt handler in + * large systems. + */ + splbreaknum = 100; + + ccb = (union ccb *)addr; + + /* + * Sanity check -- make sure we don't get a null peripheral + * driver name. + */ + if (*ccb->cgdl.periph_name == '\0') { + error = EINVAL; + break; + } + + /* Keep the list from changing while we traverse it */ + s = splsoftcam(); +ptstartover: + cur_generation = xsoftc.generation; + + /* first find our driver in the list of drivers */ + for (p_drv = (struct periph_driver **)periphdriver_set.ls_items; + *p_drv != NULL; p_drv++) + if (strcmp((*p_drv)->driver_name, name) == 0) + break; + + if (*p_drv == NULL) { + splx(s); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + ccb->cgdl.status = CAM_GDEVLIST_ERROR; + *ccb->cgdl.periph_name = '\0'; + ccb->cgdl.unit_number = 0; + error = ENOENT; + break; + } + + /* + * Run through every peripheral instance of this driver + * and check to see whether it matches the unit passed + * in by the user. If it does, get out of the loops and + * find the passthrough driver associated with that + * peripheral driver. + */ + for (periph = TAILQ_FIRST(&(*p_drv)->units); periph != NULL; + periph = TAILQ_NEXT(periph, unit_links)) { + + if (periph->unit_number == unit) { + break; + } else if (--splbreaknum == 0) { + splx(s); + s = splsoftcam(); + splbreaknum = 100; + if (cur_generation != xsoftc.generation) + goto ptstartover; + } + } + /* + * If we found the peripheral driver that the user passed + * in, go through all of the peripheral drivers for that + * particular device and look for a passthrough driver. + */ + if (periph != NULL) { + struct cam_ed *device; + int i; + + device = periph->path->device; + for (i = 0, periph = device->periphs.slh_first; + periph != NULL; + periph = periph->periph_links.sle_next, i++) { + /* + * Check to see whether we have a + * passthrough device or not. + */ + if (strcmp(periph->periph_name, "pass") == 0) { + /* + * Fill in the getdevlist fields. + */ + strcpy(ccb->cgdl.periph_name, + periph->periph_name); + ccb->cgdl.unit_number = + periph->unit_number; + if (periph->periph_links.sle_next) + ccb->cgdl.status = + CAM_GDEVLIST_MORE_DEVS; + else + ccb->cgdl.status = + CAM_GDEVLIST_LAST_DEVICE; + ccb->cgdl.generation = + device->generation; + ccb->cgdl.index = i; + /* + * Fill in some CCB header fields + * that the user may want. + */ + ccb->ccb_h.path_id = + periph->path->bus->path_id; + ccb->ccb_h.target_id = + periph->path->target->target_id; + ccb->ccb_h.target_lun = + periph->path->device->lun_id; + ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + } + } + + /* + * If the periph is null here, one of two things has + * happened. The first possibility is that we couldn't + * find the unit number of the particular peripheral driver + * that the user is asking about. e.g. the user asks for + * the passthrough driver for "da11". We find the list of + * "da" peripherals all right, but there is no unit 11. + * The other possibility is that we went through the list + * of peripheral drivers attached to the device structure, + * but didn't find one with the name "pass". Either way, + * we return ENOENT, since we couldn't find something. + */ + if (periph == NULL) { + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + ccb->cgdl.status = CAM_GDEVLIST_ERROR; + *ccb->cgdl.periph_name = '\0'; + ccb->cgdl.unit_number = 0; + error = ENOENT; + } + splx(s); + break; + } + default: + error = ENOTTY; + break; + } + + return(error); +} + +/* Functions accessed by the peripheral drivers */ +void +xpt_init() +{ + struct cam_sim *xpt_sim; + struct cam_path *path; + struct cam_devq; + cam_status status; + + TAILQ_INIT(&xpt_busses); + TAILQ_INIT(&cam_bioq); + TAILQ_INIT(&cam_netq); + SLIST_INIT(&ccb_freeq); + STAILQ_INIT(&highpowerq); + + /* + * The xpt layer is, itself, the equivelent of a SIM. + * Allow 16 ccbs in the ccb pool for it. This should + * give decent parallelism when we probe busses and + * perform other XPT functions. + */ + xpt_sim = (struct cam_sim *)malloc(sizeof(*xpt_sim), + M_DEVBUF, M_WAITOK); + xpt_sim->sim_action = xptaction; + xpt_sim->sim_name = "xpt"; + xpt_sim->path_id = CAM_XPT_PATH_ID; + xpt_sim->bus_id = 0; + xpt_sim->max_tagged_dev_openings = 0; + xpt_sim->max_dev_openings = 0; + xpt_sim->devq = cam_simq_alloc(16); + xpt_max_ccbs = 16; + + xpt_bus_register(xpt_sim, 0); + + /* + * Looking at the XPT from the SIM layer, the XPT is + * the equivelent of a peripheral driver. Allocate + * a peripheral driver entry for us. + */ + if ((status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID, + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD)) != CAM_REQ_CMP) { + printf("xpt_init: xpt_create_path failed with status %#x," + " failing attach\n", status); + return; + } + + cam_periph_alloc(xptregister, NULL, NULL, "xpt", CAM_PERIPH_BIO, + path, NULL, 0, NULL); + xpt_free_path(path); + + xpt_sim->softc = xpt_periph; + + /* + * Register a callback for when interrupts are enabled. + */ + xpt_config_hook = + (struct intr_config_hook *)malloc(sizeof(struct intr_config_hook), + M_TEMP, M_NOWAIT); + if (xpt_config_hook == NULL) { + printf("xpt_init: Cannot malloc config hook " + "- failing attach\n"); + return; + } + bzero(xpt_config_hook, sizeof(*xpt_config_hook)); + + xpt_config_hook->ich_func = xpt_config; + if (config_intrhook_establish(xpt_config_hook) != 0) { + free (xpt_config_hook, M_TEMP); + printf("xpt_init: config_intrhook_establish failed " + "- failing attach\n"); + } + + /* Install our software interrupt handlers */ + /* XXX Should call some MI function to do this */ + ihandlers[SWI_CAMNET] = swi_camnet; + ihandlers[SWI_CAMBIO] = swi_cambio; +} + +static cam_status +xptregister(struct cam_periph *periph, void *arg) +{ + if (periph == NULL) { + printf("xptregister: periph was NULL!!\n"); + return(CAM_REQ_CMP_ERR); + } + + periph->softc = NULL; + + xpt_periph = periph; + + return(CAM_REQ_CMP); +} + +int32_t +xpt_add_periph(struct cam_periph *periph) +{ + struct cam_ed *device; + int32_t status; + struct periph_list *periph_head; + + device = periph->path->device; + + periph_head = &device->periphs; + + status = CAM_REQ_CMP; + + if (device != NULL) { + int s; + + /* + * Make room for this peripheral + * so it will fit in the queue + * when it's scheduled to run + */ + s = splsoftcam(); + status = camq_resize(&device->drvq, + device->drvq.array_size + 1); + + device->generation++; + + SLIST_INSERT_HEAD(periph_head, periph, periph_links); + + splx(s); + } + + xsoftc.generation++; + + return (status); +} + +void +xpt_remove_periph(struct cam_periph *periph) +{ + struct cam_ed *device; + + device = periph->path->device; + + if (device != NULL) { + int s; + struct periph_list *periph_head; + + periph_head = &device->periphs; + + /* Release the slot for this peripheral */ + s = splsoftcam(); + camq_resize(&device->drvq, device->drvq.array_size - 1); + + device->generation++; + + SLIST_REMOVE(periph_head, periph, cam_periph, periph_links); + + splx(s); + } + + xsoftc.generation++; + +} + +void +xpt_announce_periph(struct cam_periph *periph, char *announce_string) +{ + int s; + u_int mb; + struct cam_path *path; + struct ccb_trans_settings cts; + + path = periph->path; + /* + * To ensure that this is printed in one piece, + * mask out CAM interrupts. + */ + s = splsoftcam(); + printf("%s%d at %s%d bus %d target %d lun %d\n", + periph->periph_name, periph->unit_number, + path->bus->sim->sim_name, + path->bus->sim->unit_number, + path->bus->sim->bus_id, + path->target->target_id, + path->device->lun_id); + printf("%s%d: ", periph->periph_name, periph->unit_number); + scsi_print_inquiry(&path->device->inq_data); + if ((bootverbose) + && (path->device->serial_num_len > 0)) { + /* Don't wrap the screen - print only the first 60 chars */ + printf("%s%d: Serial Number %.60s\n", periph->periph_name, + periph->unit_number, path->device->serial_num); + } + xpt_setup_ccb(&cts.ccb_h, path, /*priority*/1); + cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS; + cts.flags = CCB_TRANS_CURRENT_SETTINGS; + xpt_action((union ccb*)&cts); + if (cts.ccb_h.status == CAM_REQ_CMP) { + u_int speed; + u_int freq; + + if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 + && cts.sync_offset != 0) { + freq = scsi_calc_syncsrate(cts.sync_period); + speed = freq; + } else { + freq = 0; + speed = path->bus->sim->base_transfer_speed; + } + if ((cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) + speed *= (0x01 << cts.bus_width); + mb = speed / 1000; + if (mb > 0) + printf("%s%d: %d.%dMB/s transfers", periph->periph_name, + periph->unit_number, mb, speed % 1000); + else + printf("%s%d: %dKB/s transfers", periph->periph_name, + periph->unit_number, (speed % 1000) * 1000); + if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 + && cts.sync_offset != 0) { + printf(" (%d.%dMHz, offset %d", freq / 1000, + freq % 1000, cts.sync_offset); + } + if ((cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0 + && cts.bus_width > 0) { + if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 + && cts.sync_offset != 0) { + printf(", "); + } else { + printf(" ("); + } + printf("%dbit)", 8 * (0x01 << cts.bus_width)); + } else if ((cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0 + && cts.sync_offset != 0) { + printf(")"); + } + if (path->device->inq_flags & SID_CmdQue) { + printf(", Tagged Queueing Enabled"); + } + + printf("\n"); + } else if (path->device->inq_flags & SID_CmdQue) { + printf("%s%d: Tagged Queueing Enabled\n", + periph->periph_name, periph->unit_number); + } + + /* + * We only want to print the caller's announce string if they've + * passed one in.. + */ + if (announce_string != NULL) + printf("%s%d: %s\n", periph->periph_name, + periph->unit_number, announce_string); + splx(s); +} + + +static dev_match_ret +xptbusmatch(struct dev_match_pattern *patterns, int num_patterns, + struct cam_eb *bus) +{ + dev_match_ret retval; + int i; + + retval = DM_RET_NONE; + + /* + * If we aren't given something to match against, that's an error. + */ + if (bus == NULL) + return(DM_RET_ERROR); + + /* + * If there are no match entries, then this bus matches no + * matter what. + */ + if ((patterns == NULL) || (num_patterns == 0)) + return(DM_RET_DESCEND | DM_RET_COPY); + + for (i = 0; i < num_patterns; i++) { + struct bus_match_pattern *cur_pattern; + + /* + * If the pattern in question isn't for a bus node, we + * aren't interested. However, we do indicate to the + * calling routine that we should continue descending the + * tree, since the user wants to match against lower-level + * EDT elements. + */ + if (patterns[i].type != DEV_MATCH_BUS) { + if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) + retval |= DM_RET_DESCEND; + continue; + } + + cur_pattern = &patterns[i].pattern.bus_pattern; + + /* + * If they want to match any bus node, we give them any + * device node. + */ + if (cur_pattern->flags == BUS_MATCH_ANY) { + /* set the copy flag */ + retval |= DM_RET_COPY; + + /* + * If we've already decided on an action, go ahead + * and return. + */ + if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) + return(retval); + } + + /* + * Not sure why someone would do this... + */ + if (cur_pattern->flags == BUS_MATCH_NONE) + continue; + + if (((cur_pattern->flags & BUS_MATCH_PATH) != 0) + && (cur_pattern->path_id != bus->path_id)) + continue; + + if (((cur_pattern->flags & BUS_MATCH_BUS_ID) != 0) + && (cur_pattern->bus_id != bus->sim->bus_id)) + continue; + + if (((cur_pattern->flags & BUS_MATCH_UNIT) != 0) + && (cur_pattern->unit_number != bus->sim->unit_number)) + continue; + + if (((cur_pattern->flags & BUS_MATCH_NAME) != 0) + && (strncmp(cur_pattern->dev_name, bus->sim->sim_name, + DEV_IDLEN) != 0)) + continue; + + /* + * If we get to this point, the user definitely wants + * information on this bus. So tell the caller to copy the + * data out. + */ + retval |= DM_RET_COPY; + + /* + * If the return action has been set to descend, then we + * know that we've already seen a non-bus matching + * expression, therefore we need to further descend the tree. + * This won't change by continuing around the loop, so we + * go ahead and return. If we haven't seen a non-bus + * matching expression, we keep going around the loop until + * we exhaust the matching expressions. We'll set the stop + * flag once we fall out of the loop. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) + return(retval); + } + + /* + * If the return action hasn't been set to descend yet, that means + * we haven't seen anything other than bus matching patterns. So + * tell the caller to stop descending the tree -- the user doesn't + * want to match against lower level tree elements. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) + retval |= DM_RET_STOP; + + return(retval); +} + +static dev_match_ret +xptdevicematch(struct dev_match_pattern *patterns, int num_patterns, + struct cam_ed *device) +{ + dev_match_ret retval; + int i; + + retval = DM_RET_NONE; + + /* + * If we aren't given something to match against, that's an error. + */ + if (device == NULL) + return(DM_RET_ERROR); + + /* + * If there are no match entries, then this device matches no + * matter what. + */ + if ((patterns == NULL) || (patterns == 0)) + return(DM_RET_DESCEND | DM_RET_COPY); + + for (i = 0; i < num_patterns; i++) { + struct device_match_pattern *cur_pattern; + + /* + * If the pattern in question isn't for a device node, we + * aren't interested. + */ + if (patterns[i].type != DEV_MATCH_DEVICE) { + if ((patterns[i].type == DEV_MATCH_PERIPH) + && ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE)) + retval |= DM_RET_DESCEND; + continue; + } + + cur_pattern = &patterns[i].pattern.device_pattern; + + /* + * If they want to match any device node, we give them any + * device node. + */ + if (cur_pattern->flags == DEV_MATCH_ANY) { + /* set the copy flag */ + retval |= DM_RET_COPY; + + + /* + * If we've already decided on an action, go ahead + * and return. + */ + if ((retval & DM_RET_ACTION_MASK) != DM_RET_NONE) + return(retval); + } + + /* + * Not sure why someone would do this... + */ + if (cur_pattern->flags == DEV_MATCH_NONE) + continue; + + if (((cur_pattern->flags & DEV_MATCH_PATH) != 0) + && (cur_pattern->path_id != device->target->bus->path_id)) + continue; + + if (((cur_pattern->flags & DEV_MATCH_TARGET) != 0) + && (cur_pattern->target_id != device->target->target_id)) + continue; + + if (((cur_pattern->flags & DEV_MATCH_LUN) != 0) + && (cur_pattern->target_lun != device->lun_id)) + continue; + + if (((cur_pattern->flags & DEV_MATCH_INQUIRY) != 0) + && (cam_quirkmatch((caddr_t)&device->inq_data, + (caddr_t)&cur_pattern->inq_pat, + 1, sizeof(cur_pattern->inq_pat), + scsi_static_inquiry_match) == NULL)) + continue; + + /* + * If we get to this point, the user definitely wants + * information on this device. So tell the caller to copy + * the data out. + */ + retval |= DM_RET_COPY; + + /* + * If the return action has been set to descend, then we + * know that we've already seen a peripheral matching + * expression, therefore we need to further descend the tree. + * This won't change by continuing around the loop, so we + * go ahead and return. If we haven't seen a peripheral + * matching expression, we keep going around the loop until + * we exhaust the matching expressions. We'll set the stop + * flag once we fall out of the loop. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_DESCEND) + return(retval); + } + + /* + * If the return action hasn't been set to descend yet, that means + * we haven't seen any peripheral matching patterns. So tell the + * caller to stop descending the tree -- the user doesn't want to + * match against lower level tree elements. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_NONE) + retval |= DM_RET_STOP; + + return(retval); +} + +/* + * Match a single peripheral against any number of match patterns. + */ +static dev_match_ret +xptperiphmatch(struct dev_match_pattern *patterns, int num_patterns, + struct cam_periph *periph) +{ + dev_match_ret retval; + int i; + + /* + * If we aren't given something to match against, that's an error. + */ + if (periph == NULL) + return(DM_RET_ERROR); + + /* + * If there are no match entries, then this peripheral matches no + * matter what. + */ + if ((patterns == NULL) || (num_patterns == 0)) + return(DM_RET_STOP | DM_RET_COPY); + + /* + * There aren't any nodes below a peripheral node, so there's no + * reason to descend the tree any further. + */ + retval = DM_RET_STOP; + + for (i = 0; i < num_patterns; i++) { + struct periph_match_pattern *cur_pattern; + + /* + * If the pattern in question isn't for a peripheral, we + * aren't interested. + */ + if (patterns[i].type != DEV_MATCH_PERIPH) + continue; + + cur_pattern = &patterns[i].pattern.periph_pattern; + + /* + * If they want to match on anything, then we will do so. + */ + if (cur_pattern->flags == PERIPH_MATCH_ANY) { + /* set the copy flag */ + retval |= DM_RET_COPY; + + /* + * We've already set the return action to stop, + * since there are no nodes below peripherals in + * the tree. + */ + return(retval); + } + + /* + * Not sure why someone would do this... + */ + if (cur_pattern->flags == PERIPH_MATCH_NONE) + continue; + + if (((cur_pattern->flags & PERIPH_MATCH_PATH) != 0) + && (cur_pattern->path_id != periph->path->bus->path_id)) + continue; + + /* + * For the target and lun id's, we have to make sure the + * target and lun pointers aren't NULL. The xpt peripheral + * has a wildcard target and device. + */ + if (((cur_pattern->flags & PERIPH_MATCH_TARGET) != 0) + && ((periph->path->target == NULL) + ||(cur_pattern->target_id != periph->path->target->target_id))) + continue; + + if (((cur_pattern->flags & PERIPH_MATCH_LUN) != 0) + && ((periph->path->device == NULL) + || (cur_pattern->target_lun != periph->path->device->lun_id))) + continue; + + if (((cur_pattern->flags & PERIPH_MATCH_UNIT) != 0) + && (cur_pattern->unit_number != periph->unit_number)) + continue; + + if (((cur_pattern->flags & PERIPH_MATCH_NAME) != 0) + && (strncmp(cur_pattern->periph_name, periph->periph_name, + DEV_IDLEN) != 0)) + continue; + + /* + * If we get to this point, the user definitely wants + * information on this peripheral. So tell the caller to + * copy the data out. + */ + retval |= DM_RET_COPY; + + /* + * The return action has already been set to stop, since + * peripherals don't have any nodes below them in the EDT. + */ + return(retval); + } + + /* + * If we get to this point, the peripheral that was passed in + * doesn't match any of the patterns. + */ + return(retval); +} + +static int +xptedtbusfunc(struct cam_eb *bus, void *arg) +{ + struct ccb_dev_match *cdm; + dev_match_ret retval; + + cdm = (struct ccb_dev_match *)arg; + + /* + * If our position is for something deeper in the tree, that means + * that we've already seen this node. So, we keep going down. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus == bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.cookie.target != NULL)) + retval = DM_RET_DESCEND; + else + retval = xptbusmatch(cdm->patterns, cdm->num_patterns, bus); + + /* + * If we got an error, bail out of the search. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { + cdm->status = CAM_DEV_MATCH_ERROR; + return(0); + } + + /* + * If the copy flag is set, copy this bus out. + */ + if (retval & DM_RET_COPY) { + int spaceleft, j; + + spaceleft = cdm->match_buf_len - (cdm->num_matches * + sizeof(struct dev_match_result)); + + /* + * If we don't have enough space to put in another + * match result, save our position and tell the + * user there are more devices to check. + */ + if (spaceleft < sizeof(struct dev_match_result)) { + bzero(&cdm->pos, sizeof(cdm->pos)); + cdm->pos.position_type = + CAM_DEV_POS_EDT | CAM_DEV_POS_BUS; + + cdm->pos.cookie.bus = bus; + cdm->pos.generations[CAM_BUS_GENERATION]= + bus_generation; + cdm->status = CAM_DEV_MATCH_MORE; + return(0); + } + j = cdm->num_matches; + cdm->num_matches++; + cdm->matches[j].type = DEV_MATCH_BUS; + cdm->matches[j].result.bus_result.path_id = bus->path_id; + cdm->matches[j].result.bus_result.bus_id = bus->sim->bus_id; + cdm->matches[j].result.bus_result.unit_number = + bus->sim->unit_number; + strncpy(cdm->matches[j].result.bus_result.dev_name, + bus->sim->sim_name, DEV_IDLEN); + } + + /* + * If the user is only interested in busses, there's no + * reason to descend to the next level in the tree. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) + return(1); + + /* + * If there is a target generation recorded, check it to + * make sure the target list hasn't changed. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (bus == cdm->pos.cookie.bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.generations[CAM_TARGET_GENERATION] != 0) + && (cdm->pos.generations[CAM_TARGET_GENERATION] != + bus->generation)) { + cdm->status = CAM_DEV_MATCH_LIST_CHANGED; + return(0); + } + + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus == bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.cookie.target != NULL)) + return(xpttargettraverse(bus, + (struct cam_et *)cdm->pos.cookie.target, + xptedttargetfunc, arg)); + else + return(xpttargettraverse(bus, NULL, xptedttargetfunc, arg)); +} + +static int +xptedttargetfunc(struct cam_et *target, void *arg) +{ + struct ccb_dev_match *cdm; + + cdm = (struct ccb_dev_match *)arg; + + /* + * If there is a device list generation recorded, check it to + * make sure the device list hasn't changed. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus == target->bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.cookie.target == target) + && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) + && (cdm->pos.generations[CAM_DEV_GENERATION] != 0) + && (cdm->pos.generations[CAM_DEV_GENERATION] != + target->generation)) { + cdm->status = CAM_DEV_MATCH_LIST_CHANGED; + return(0); + } + + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus == target->bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.cookie.target == target) + && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) + && (cdm->pos.cookie.device != NULL)) + return(xptdevicetraverse(target, + (struct cam_ed *)cdm->pos.cookie.device, + xptedtdevicefunc, arg)); + else + return(xptdevicetraverse(target, NULL, xptedtdevicefunc, arg)); +} + +static int +xptedtdevicefunc(struct cam_ed *device, void *arg) +{ + + struct ccb_dev_match *cdm; + dev_match_ret retval; + u_int dev_gen; + + cdm = (struct ccb_dev_match *)arg; + + /* + * If our position is for something deeper in the tree, that means + * that we've already seen this node. So, we keep going down. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_DEVICE) + && (cdm->pos.cookie.device == device) + && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) + && (cdm->pos.cookie.periph != NULL)) + retval = DM_RET_DESCEND; + else + retval = xptdevicematch(cdm->patterns, cdm->num_patterns, + device); + + if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { + cdm->status = CAM_DEV_MATCH_ERROR; + return(0); + } + + /* + * If the copy flag is set, copy this device out. + */ + if (retval & DM_RET_COPY) { + int spaceleft, j; + + spaceleft = cdm->match_buf_len - (cdm->num_matches * + sizeof(struct dev_match_result)); + + /* + * If we don't have enough space to put in another + * match result, save our position and tell the + * user there are more devices to check. + */ + if (spaceleft < sizeof(struct dev_match_result)) { + bzero(&cdm->pos, sizeof(cdm->pos)); + cdm->pos.position_type = + CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | + CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE; + + cdm->pos.cookie.bus = device->target->bus; + cdm->pos.generations[CAM_BUS_GENERATION]= + bus_generation; + cdm->pos.cookie.target = device->target; + cdm->pos.generations[CAM_TARGET_GENERATION] = + device->target->bus->generation; + cdm->pos.cookie.device = device; + cdm->pos.generations[CAM_DEV_GENERATION] = + device->target->generation; + cdm->status = CAM_DEV_MATCH_MORE; + return(0); + } + j = cdm->num_matches; + cdm->num_matches++; + cdm->matches[j].type = DEV_MATCH_DEVICE; + cdm->matches[j].result.device_result.path_id = + device->target->bus->path_id; + cdm->matches[j].result.device_result.target_id = + device->target->target_id; + cdm->matches[j].result.device_result.target_lun = + device->lun_id; + bcopy(&device->inq_data, + &cdm->matches[j].result.device_result.inq_data, + sizeof(struct scsi_inquiry_data)); + } + + /* + * If the user isn't interested in peripherals, don't descend + * the tree any further. + */ + if ((retval & DM_RET_ACTION_MASK) == DM_RET_STOP) + return(1); + + /* + * If there is a peripheral list generation recorded, make sure + * it hasn't changed. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (device->target->bus == cdm->pos.cookie.bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (device->target == cdm->pos.cookie.target) + && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) + && (device == cdm->pos.cookie.device) + && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) + && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) + && (cdm->pos.generations[CAM_PERIPH_GENERATION] != + device->generation)){ + cdm->status = CAM_DEV_MATCH_LIST_CHANGED; + return(0); + } + + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus == device->target->bus) + && (cdm->pos.position_type & CAM_DEV_POS_TARGET) + && (cdm->pos.cookie.target == device->target) + && (cdm->pos.position_type & CAM_DEV_POS_DEVICE) + && (cdm->pos.cookie.device == device) + && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) + && (cdm->pos.cookie.periph != NULL)) + return(xptperiphtraverse(device, + (struct cam_periph *)cdm->pos.cookie.periph, + xptedtperiphfunc, arg)); + else + return(xptperiphtraverse(device, NULL, xptedtperiphfunc, arg)); +} + +static int +xptedtperiphfunc(struct cam_periph *periph, void *arg) +{ + struct ccb_dev_match *cdm; + dev_match_ret retval; + + cdm = (struct ccb_dev_match *)arg; + + retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); + + if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { + cdm->status = CAM_DEV_MATCH_ERROR; + return(0); + } + + /* + * If the copy flag is set, copy this peripheral out. + */ + if (retval & DM_RET_COPY) { + int spaceleft, j; + + spaceleft = cdm->match_buf_len - (cdm->num_matches * + sizeof(struct dev_match_result)); + + /* + * If we don't have enough space to put in another + * match result, save our position and tell the + * user there are more devices to check. + */ + if (spaceleft < sizeof(struct dev_match_result)) { + bzero(&cdm->pos, sizeof(cdm->pos)); + cdm->pos.position_type = + CAM_DEV_POS_EDT | CAM_DEV_POS_BUS | + CAM_DEV_POS_TARGET | CAM_DEV_POS_DEVICE | + CAM_DEV_POS_PERIPH; + + cdm->pos.cookie.bus = periph->path->bus; + cdm->pos.generations[CAM_BUS_GENERATION]= + bus_generation; + cdm->pos.cookie.target = periph->path->target; + cdm->pos.generations[CAM_TARGET_GENERATION] = + periph->path->bus->generation; + cdm->pos.cookie.device = periph->path->device; + cdm->pos.generations[CAM_DEV_GENERATION] = + periph->path->target->generation; + cdm->pos.cookie.periph = periph; + cdm->pos.generations[CAM_PERIPH_GENERATION] = + periph->path->device->generation; + cdm->status = CAM_DEV_MATCH_MORE; + return(0); + } + + j = cdm->num_matches; + cdm->num_matches++; + cdm->matches[j].type = DEV_MATCH_PERIPH; + cdm->matches[j].result.periph_result.path_id = + periph->path->bus->path_id; + cdm->matches[j].result.periph_result.target_id = + periph->path->target->target_id; + cdm->matches[j].result.periph_result.target_lun = + periph->path->device->lun_id; + cdm->matches[j].result.periph_result.unit_number = + periph->unit_number; + strncpy(cdm->matches[j].result.periph_result.periph_name, + periph->periph_name, DEV_IDLEN); + } + + return(1); +} + +static int +xptedtmatch(struct ccb_dev_match *cdm) +{ + int ret; + + cdm->num_matches = 0; + + /* + * Check the bus list generation. If it has changed, the user + * needs to reset everything and start over. + */ + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.generations[CAM_BUS_GENERATION] != 0) + && (cdm->pos.generations[CAM_BUS_GENERATION] != bus_generation)) { + cdm->status = CAM_DEV_MATCH_LIST_CHANGED; + return(0); + } + + if ((cdm->pos.position_type & CAM_DEV_POS_BUS) + && (cdm->pos.cookie.bus != NULL)) + ret = xptbustraverse((struct cam_eb *)cdm->pos.cookie.bus, + xptedtbusfunc, cdm); + else + ret = xptbustraverse(NULL, xptedtbusfunc, cdm); + + /* + * If we get back 0, that means that we had to stop before fully + * traversing the EDT. It also means that one of the subroutines + * has set the status field to the proper value. If we get back 1, + * we've fully traversed the EDT and copied out any matching entries. + */ + if (ret == 1) + cdm->status = CAM_DEV_MATCH_LAST; + + return(ret); +} + +static int +xptplistpdrvfunc(struct periph_driver **pdrv, void *arg) +{ + struct ccb_dev_match *cdm; + + cdm = (struct ccb_dev_match *)arg; + + if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) + && (cdm->pos.cookie.pdrv == pdrv) + && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) + && (cdm->pos.generations[CAM_PERIPH_GENERATION] != 0) + && (cdm->pos.generations[CAM_PERIPH_GENERATION] != + (*pdrv)->generation)) { + cdm->status = CAM_DEV_MATCH_LIST_CHANGED; + return(0); + } + + if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) + && (cdm->pos.cookie.pdrv == pdrv) + && (cdm->pos.position_type & CAM_DEV_POS_PERIPH) + && (cdm->pos.cookie.periph != NULL)) + return(xptpdperiphtraverse(pdrv, + (struct cam_periph *)cdm->pos.cookie.periph, + xptplistperiphfunc, arg)); + else + return(xptpdperiphtraverse(pdrv, NULL,xptplistperiphfunc, arg)); +} + +static int +xptplistperiphfunc(struct cam_periph *periph, void *arg) +{ + struct ccb_dev_match *cdm; + dev_match_ret retval; + + cdm = (struct ccb_dev_match *)arg; + + retval = xptperiphmatch(cdm->patterns, cdm->num_patterns, periph); + + if ((retval & DM_RET_ACTION_MASK) == DM_RET_ERROR) { + cdm->status = CAM_DEV_MATCH_ERROR; + return(0); + } + + /* + * If the copy flag is set, copy this peripheral out. + */ + if (retval & DM_RET_COPY) { + int spaceleft, j; + + spaceleft = cdm->match_buf_len - (cdm->num_matches * + sizeof(struct dev_match_result)); + + /* + * If we don't have enough space to put in another + * match result, save our position and tell the + * user there are more devices to check. + */ + if (spaceleft < sizeof(struct dev_match_result)) { + struct periph_driver **pdrv; + + pdrv = NULL; + bzero(&cdm->pos, sizeof(cdm->pos)); + cdm->pos.position_type = + CAM_DEV_POS_PDRV | CAM_DEV_POS_PDPTR | + CAM_DEV_POS_PERIPH; + + /* + * This may look a bit non-sensical, but it is + * actually quite logical. There are very few + * peripheral drivers, and bloating every peripheral + * structure with a pointer back to its parent + * peripheral driver linker set entry would cost + * more in the long run than doing this quick lookup. + */ + for (pdrv = + (struct periph_driver **)periphdriver_set.ls_items; + *pdrv != NULL; pdrv++) { + if (strcmp((*pdrv)->driver_name, + periph->periph_name) == 0) + break; + } + + if (pdrv == NULL) { + cdm->status = CAM_DEV_MATCH_ERROR; + return(0); + } + + cdm->pos.cookie.pdrv = pdrv; + /* + * The periph generation slot does double duty, as + * does the periph pointer slot. They are used for + * both edt and pdrv lookups and positioning. + */ + cdm->pos.cookie.periph = periph; + cdm->pos.generations[CAM_PERIPH_GENERATION] = + (*pdrv)->generation; + cdm->status = CAM_DEV_MATCH_MORE; + return(0); + } + + j = cdm->num_matches; + cdm->num_matches++; + cdm->matches[j].type = DEV_MATCH_PERIPH; + cdm->matches[j].result.periph_result.path_id = + periph->path->bus->path_id; + + /* + * The transport layer peripheral doesn't have a target or + * lun. + */ + if (periph->path->target) + cdm->matches[j].result.periph_result.target_id = + periph->path->target->target_id; + else + cdm->matches[j].result.periph_result.target_id = -1; + + if (periph->path->device) + cdm->matches[j].result.periph_result.target_lun = + periph->path->device->lun_id; + else + cdm->matches[j].result.periph_result.target_lun = -1; + + cdm->matches[j].result.periph_result.unit_number = + periph->unit_number; + strncpy(cdm->matches[j].result.periph_result.periph_name, + periph->periph_name, DEV_IDLEN); + } + + return(1); +} + +static int +xptperiphlistmatch(struct ccb_dev_match *cdm) +{ + int ret; + + cdm->num_matches = 0; + + /* + * At this point in the edt traversal function, we check the bus + * list generation to make sure that no busses have been added or + * removed since the user last sent a XPT_DEV_MATCH ccb through. + * For the peripheral driver list traversal function, however, we + * don't have to worry about new peripheral driver types coming or + * going; they're in a linker set, and therefore can't change + * without a recompile. + */ + + if ((cdm->pos.position_type & CAM_DEV_POS_PDPTR) + && (cdm->pos.cookie.pdrv != NULL)) + ret = xptpdrvtraverse( + (struct periph_driver **)cdm->pos.cookie.pdrv, + xptplistpdrvfunc, cdm); + else + ret = xptpdrvtraverse(NULL, xptplistpdrvfunc, cdm); + + /* + * If we get back 0, that means that we had to stop before fully + * traversing the peripheral driver tree. It also means that one of + * the subroutines has set the status field to the proper value. If + * we get back 1, we've fully traversed the EDT and copied out any + * matching entries. + */ + if (ret == 1) + cdm->status = CAM_DEV_MATCH_LAST; + + return(ret); +} + +static int +xptbustraverse(struct cam_eb *start_bus, xpt_busfunc_t *tr_func, void *arg) +{ + struct cam_eb *bus, *next_bus; + int retval; + + retval = 1; + + for (bus = (start_bus ? start_bus : TAILQ_FIRST(&xpt_busses)); + bus != NULL; + bus = next_bus) { + next_bus = TAILQ_NEXT(bus, links); + + retval = tr_func(bus, arg); + if (retval == 0) + return(retval); + } + + return(retval); +} + +static int +xpttargettraverse(struct cam_eb *bus, struct cam_et *start_target, + xpt_targetfunc_t *tr_func, void *arg) +{ + struct cam_et *target, *next_target; + int retval; + + retval = 1; + for (target = (start_target ? start_target : + TAILQ_FIRST(&bus->et_entries)); + target != NULL; target = next_target) { + + next_target = TAILQ_NEXT(target, links); + + retval = tr_func(target, arg); + + if (retval == 0) + return(retval); + } + + return(retval); +} + +static int +xptdevicetraverse(struct cam_et *target, struct cam_ed *start_device, + xpt_devicefunc_t *tr_func, void *arg) +{ + struct cam_ed *device, *next_device; + int retval; + + retval = 1; + for (device = (start_device ? start_device : + TAILQ_FIRST(&target->ed_entries)); + device != NULL; + device = next_device) { + + next_device = TAILQ_NEXT(device, links); + + retval = tr_func(device, arg); + + if (retval == 0) + return(retval); + } + + return(retval); +} + +static int +xptperiphtraverse(struct cam_ed *device, struct cam_periph *start_periph, + xpt_periphfunc_t *tr_func, void *arg) +{ + struct cam_periph *periph, *next_periph; + int retval; + + retval = 1; + + for (periph = (start_periph ? start_periph : + SLIST_FIRST(&device->periphs)); + periph != NULL; + periph = next_periph) { + + next_periph = SLIST_NEXT(periph, periph_links); + + retval = tr_func(periph, arg); + if (retval == 0) + return(retval); + } + + return(retval); +} + +static int +xptpdrvtraverse(struct periph_driver **start_pdrv, + xpt_pdrvfunc_t *tr_func, void *arg) +{ + struct periph_driver **pdrv; + int retval; + + retval = 1; + + /* + * We don't traverse the peripheral driver list like we do the + * other lists, because it is a linker set, and therefore cannot be + * changed during runtime. If the peripheral driver list is ever + * re-done to be something other than a linker set (i.e. it can + * change while the system is running), the list traversal should + * be modified to work like the other traversal functions. + */ + for (pdrv = (start_pdrv ? start_pdrv : + (struct periph_driver **)periphdriver_set.ls_items); + *pdrv != NULL; pdrv++) { + retval = tr_func(pdrv, arg); + + if (retval == 0) + return(retval); + } + + return(retval); +} + +static int +xptpdperiphtraverse(struct periph_driver **pdrv, + struct cam_periph *start_periph, + xpt_periphfunc_t *tr_func, void *arg) +{ + struct cam_periph *periph, *next_periph; + int retval; + + retval = 1; + + for (periph = (start_periph ? start_periph : + TAILQ_FIRST(&(*pdrv)->units)); periph != NULL; + periph = next_periph) { + + next_periph = TAILQ_NEXT(periph, unit_links); + + retval = tr_func(periph, arg); + if (retval == 0) + return(retval); + } + return(retval); +} + +static int +xptdefbusfunc(struct cam_eb *bus, void *arg) +{ + struct xpt_traverse_config *tr_config; + + tr_config = (struct xpt_traverse_config *)arg; + + if (tr_config->depth == XPT_DEPTH_BUS) { + xpt_busfunc_t *tr_func; + + tr_func = (xpt_busfunc_t *)tr_config->tr_func; + + return(tr_func(bus, tr_config->tr_arg)); + } else + return(xpttargettraverse(bus, NULL, xptdeftargetfunc, arg)); +} + +static int +xptdeftargetfunc(struct cam_et *target, void *arg) +{ + struct xpt_traverse_config *tr_config; + + tr_config = (struct xpt_traverse_config *)arg; + + if (tr_config->depth == XPT_DEPTH_TARGET) { + xpt_targetfunc_t *tr_func; + + tr_func = (xpt_targetfunc_t *)tr_config->tr_func; + + return(tr_func(target, tr_config->tr_arg)); + } else + return(xptdevicetraverse(target, NULL, xptdefdevicefunc, arg)); +} + +static int +xptdefdevicefunc(struct cam_ed *device, void *arg) +{ + struct xpt_traverse_config *tr_config; + + tr_config = (struct xpt_traverse_config *)arg; + + if (tr_config->depth == XPT_DEPTH_DEVICE) { + xpt_devicefunc_t *tr_func; + + tr_func = (xpt_devicefunc_t *)tr_config->tr_func; + + return(tr_func(device, tr_config->tr_arg)); + } else + return(xptperiphtraverse(device, NULL, xptdefperiphfunc, arg)); +} + +static int +xptdefperiphfunc(struct cam_periph *periph, void *arg) +{ + struct xpt_traverse_config *tr_config; + xpt_periphfunc_t *tr_func; + + tr_config = (struct xpt_traverse_config *)arg; + + tr_func = (xpt_periphfunc_t *)tr_config->tr_func; + + /* + * Unlike the other default functions, we don't check for depth + * here. The peripheral driver level is the last level in the EDT, + * so if we're here, we should execute the function in question. + */ + return(tr_func(periph, tr_config->tr_arg)); +} + +/* + * Execute the given function for every bus in the EDT. + */ +static int +xpt_for_all_busses(xpt_busfunc_t *tr_func, void *arg) +{ + struct xpt_traverse_config tr_config; + + tr_config.depth = XPT_DEPTH_BUS; + tr_config.tr_func = tr_func; + tr_config.tr_arg = arg; + + return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); +} + +/* + * Execute the given function for every target in the EDT. + */ +static int +xpt_for_all_targets(xpt_targetfunc_t *tr_func, void *arg) +{ + struct xpt_traverse_config tr_config; + + tr_config.depth = XPT_DEPTH_TARGET; + tr_config.tr_func = tr_func; + tr_config.tr_arg = arg; + + return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); +} + +/* + * Execute the given function for every device in the EDT. + */ +static int +xpt_for_all_devices(xpt_devicefunc_t *tr_func, void *arg) +{ + struct xpt_traverse_config tr_config; + + tr_config.depth = XPT_DEPTH_DEVICE; + tr_config.tr_func = tr_func; + tr_config.tr_arg = arg; + + return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); +} + +/* + * Execute the given function for every peripheral in the EDT. + */ +static int +xpt_for_all_periphs(xpt_periphfunc_t *tr_func, void *arg) +{ + struct xpt_traverse_config tr_config; + + tr_config.depth = XPT_DEPTH_PERIPH; + tr_config.tr_func = tr_func; + tr_config.tr_arg = arg; + + return(xptbustraverse(NULL, xptdefbusfunc, &tr_config)); +} + +static int +xptsetasyncfunc(struct cam_ed *device, void *arg) +{ + struct cam_path path; + struct ccb_getdev cgd; + struct async_node *cur_entry; + + cur_entry = (struct async_node *)arg; + + xpt_compile_path(&path, + NULL, + device->target->bus->path_id, + device->target->target_id, + device->lun_id); + xpt_setup_ccb(&cgd.ccb_h, &path, /*priority*/1); + cgd.ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)&cgd); + cur_entry->callback(cur_entry->callback_arg, + AC_FOUND_DEVICE, + &path, &cgd); + xpt_release_path(&path); + + return(1); +} +static int +xptsetasyncbusfunc(struct cam_eb *bus, void *arg) +{ + struct cam_path path; + struct ccb_pathinq cpi; + struct async_node *cur_entry; + + cur_entry = (struct async_node *)arg; + + xpt_compile_path(&path, /*periph*/NULL, + bus->sim->path_id, + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD); + xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + cur_entry->callback(cur_entry->callback_arg, + AC_PATH_REGISTERED, + &path, &cpi); + xpt_release_path(&path); + + return(1); +} + +void +xpt_action(union ccb *start_ccb) +{ + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_action\n")); + + start_ccb->ccb_h.status = CAM_REQ_INPROG; + + switch (start_ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + /* + * For the sake of compatibility with SCSI-1 + * devices that may not understand the identify + * message, we include lun information in the + * second byte of all commands. SCSI-1 specifies + * that luns are a 3 bit value and reserves only 3 + * bits for lun information in the CDB. Later + * revisions of the SCSI spec allow for more than 8 + * luns, but have deprecated lun information in the + * CDB. So, if the lun won't fit, we must omit. + * + * Also be aware that during initial probing for devices, + * the inquiry information is unknown but initialized to 0. + * This means that this code will be exercised while probing + * devices with an ANSI revision greater than 2. + */ + if (SID_ANSI_REV(&start_ccb->ccb_h.path->device->inq_data) <= 2 + && start_ccb->ccb_h.target_lun < 8 + && (start_ccb->ccb_h.flags & CAM_CDB_POINTER) == 0) { + + start_ccb->csio.cdb_io.cdb_bytes[1] |= + start_ccb->ccb_h.target_lun << 5; + } + start_ccb->csio.scsi_status = SCSI_STATUS_OK; + start_ccb->csio.sense_resid = 0; + start_ccb->csio.resid = 0; + /* FALLTRHOUGH */ + case XPT_TARGET_IO: + case XPT_CONT_TARGET_IO: + case XPT_ENG_EXEC: + { + struct cam_path *path; + int s; + int runq; + + path = start_ccb->ccb_h.path; + s = splsoftcam(); + + cam_ccbq_insert_ccb(&path->device->ccbq, start_ccb); + if (path->device->qfrozen_cnt == 0) + runq = xpt_schedule_dev_sendq(path->bus, path->device); + else + runq = 0; + splx(s); + if (runq != 0) + xpt_run_dev_sendq(path->bus); + break; + } + case XPT_SET_TRAN_SETTINGS: + { + xpt_set_transfer_settings(&start_ccb->cts, + /*async_update*/FALSE); + break; + } + case XPT_CALC_GEOMETRY: + /* Filter out garbage */ + if (start_ccb->ccg.block_size == 0 + || start_ccb->ccg.volume_size == 0) { + start_ccb->ccg.cylinders = 0; + start_ccb->ccg.heads = 0; + start_ccb->ccg.secs_per_track = 0; + start_ccb->ccb_h.status = CAM_REQ_CMP; + break; + } +#ifdef PC98 + /* + * In a PC-98 system, geometry translation depens on + * the "real" device geometry obtained from mode page 4. + * SCSI geometry translation is performed in the + * initialization routine of the SCSI BIOS and the result + * stored in host memory. If the translation is available + * in host memory, use it. If not, rely on the default + * translation the device driver performs. + */ + if (scsi_da_bios_params(&start_ccb->ccg) != 0) { + start_ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + /* FALLTHROUGH */ +#endif + case XPT_ACCEPT_TARGET_IO: + case XPT_EN_LUN: + case XPT_IMMED_NOTIFY: + case XPT_NOTIFY_ACK: + case XPT_GET_TRAN_SETTINGS: + case XPT_PATH_INQ: + case XPT_RESET_BUS: + { + struct cam_sim *sim; + + sim = start_ccb->ccb_h.path->bus->sim; + (*(sim->sim_action))(sim, start_ccb); + break; + } + case XPT_GDEV_TYPE: + if ((start_ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) != 0) { + start_ccb->ccb_h.status = CAM_DEV_NOT_THERE; + } else { + struct ccb_getdev *cgd; + struct cam_et *tar; + struct cam_ed *dev; + int s; + + s = splsoftcam(); + cgd = &start_ccb->cgd; + tar = cgd->ccb_h.path->target; + dev = cgd->ccb_h.path->device; + cgd->inq_data = dev->inq_data; + cgd->pd_type = SID_TYPE(&dev->inq_data); + cgd->dev_openings = dev->ccbq.dev_openings; + cgd->dev_active = dev->ccbq.dev_active; + cgd->devq_openings = dev->ccbq.devq_openings; + cgd->devq_queued = dev->ccbq.queue.entries; + cgd->held = dev->ccbq.held; + cgd->maxtags = dev->quirk->maxtags; + cgd->mintags = dev->quirk->mintags; + cgd->ccb_h.status = CAM_REQ_CMP; + cgd->serial_num_len = dev->serial_num_len; + if ((dev->serial_num_len > 0) + && (dev->serial_num != NULL)) + bcopy(dev->serial_num, cgd->serial_num, + dev->serial_num_len); + splx(s); + } + break; + case XPT_GDEVLIST: + { + struct cam_periph *nperiph; + struct periph_list *periph_head; + struct ccb_getdevlist *cgdl; + int i; + int s; + struct cam_ed *device; + int found; + + + found = 0; + + /* + * Don't want anyone mucking with our data. + */ + s = splsoftcam(); + device = start_ccb->ccb_h.path->device; + periph_head = &device->periphs; + cgdl = &start_ccb->cgdl; + + /* + * Check and see if the list has changed since the user + * last requested a list member. If so, tell them that the + * list has changed, and therefore they need to start over + * from the beginning. + */ + if ((cgdl->index != 0) && + (cgdl->generation != device->generation)) { + cgdl->status = CAM_GDEVLIST_LIST_CHANGED; + splx(s); + break; + } + + /* + * Traverse the list of peripherals and attempt to find + * the requested peripheral. + */ + for (nperiph = periph_head->slh_first, i = 0; + (nperiph != NULL) && (i <= cgdl->index); + nperiph = nperiph->periph_links.sle_next, i++) { + if (i == cgdl->index) { + strncpy(cgdl->periph_name, + nperiph->periph_name, + DEV_IDLEN); + cgdl->unit_number = nperiph->unit_number; + found = 1; + } + } + if (found == 0) { + cgdl->status = CAM_GDEVLIST_ERROR; + splx(s); + break; + } + + if (nperiph == NULL) + cgdl->status = CAM_GDEVLIST_LAST_DEVICE; + else + cgdl->status = CAM_GDEVLIST_MORE_DEVS; + + cgdl->index++; + cgdl->generation = device->generation; + + splx(s); + cgdl->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_DEV_MATCH: + { + int s; + dev_pos_type position_type; + struct ccb_dev_match *cdm; + int ret; + + cdm = &start_ccb->cdm; + + /* + * Prevent EDT changes while we traverse it. + */ + s = splsoftcam(); + /* + * There are two ways of getting at information in the EDT. + * The first way is via the primary EDT tree. It starts + * with a list of busses, then a list of targets on a bus, + * then devices/luns on a target, and then peripherals on a + * device/lun. The "other" way is by the peripheral driver + * lists. The peripheral driver lists are organized by + * peripheral driver. (obviously) So it makes sense to + * use the peripheral driver list if the user is looking + * for something like "da1", or all "da" devices. If the + * user is looking for something on a particular bus/target + * or lun, it's generally better to go through the EDT tree. + */ + + if (cdm->pos.position_type != CAM_DEV_POS_NONE) + position_type = cdm->pos.position_type; + else { + int i; + + position_type = CAM_DEV_POS_NONE; + + for (i = 0; i < cdm->num_patterns; i++) { + if ((cdm->patterns[i].type == DEV_MATCH_BUS) + ||(cdm->patterns[i].type == DEV_MATCH_DEVICE)){ + position_type = CAM_DEV_POS_EDT; + break; + } + } + + if (cdm->num_patterns == 0) + position_type = CAM_DEV_POS_EDT; + else if (position_type == CAM_DEV_POS_NONE) + position_type = CAM_DEV_POS_PDRV; + } + + switch(position_type & CAM_DEV_POS_TYPEMASK) { + case CAM_DEV_POS_EDT: + ret = xptedtmatch(cdm); + break; + case CAM_DEV_POS_PDRV: + ret = xptperiphlistmatch(cdm); + break; + default: + cdm->status = CAM_DEV_MATCH_ERROR; + break; + } + + splx(s); + + if (cdm->status == CAM_DEV_MATCH_ERROR) + start_ccb->ccb_h.status = CAM_REQ_CMP_ERR; + else + start_ccb->ccb_h.status = CAM_REQ_CMP; + + break; + } + case XPT_SASYNC_CB: + { + /* + * First off, determine the list we want to + * be insterted into. + */ + struct ccb_setasync *csa; + struct async_node *cur_entry; + struct async_list *async_head; + u_int32_t added; + int s; + + csa = &start_ccb->csa; + added = csa->event_enable; + if (csa->ccb_h.path->device != NULL) { + async_head = &csa->ccb_h.path->device->asyncs; + } else { + async_head = &csa->ccb_h.path->bus->asyncs; + } + + /* + * If there is already an entry for us, simply + * update it. + */ + s = splsoftcam(); + cur_entry = SLIST_FIRST(async_head); + while (cur_entry != NULL) { + if ((cur_entry->callback_arg == csa->callback_arg) + && (cur_entry->callback == csa->callback)) + break; + cur_entry = SLIST_NEXT(cur_entry, links); + } + + if (cur_entry != NULL) { + /* + * If the request has no flags set, + * remove the entry. + */ + added &= ~cur_entry->event_enable; + if (csa->event_enable == 0) { + SLIST_REMOVE(async_head, cur_entry, + async_node, links); + free(cur_entry, M_DEVBUF); + } else { + cur_entry->event_enable = csa->event_enable; + } + } else { + cur_entry = malloc(sizeof(*cur_entry), M_DEVBUF, + M_NOWAIT); + if (cur_entry == NULL) { + splx(s); + csa->ccb_h.status = CAM_RESRC_UNAVAIL; + break; + } + cur_entry->callback_arg = csa->callback_arg; + cur_entry->callback = csa->callback; + cur_entry->event_enable = csa->event_enable; + SLIST_INSERT_HEAD(async_head, cur_entry, links); + } + + if ((added & AC_FOUND_DEVICE) != 0) { + /* + * Get this peripheral up to date with all + * the currently existing devices. + */ + xpt_for_all_devices(xptsetasyncfunc, cur_entry); + } + if ((added & AC_PATH_REGISTERED) != 0) { + /* + * Get this peripheral up to date with all + * the currently existing busses. + */ + xpt_for_all_busses(xptsetasyncbusfunc, cur_entry); + } + splx(s); + start_ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_REL_SIMQ: + { + struct ccb_relsim *crs; + struct cam_ed *dev; + int s; + + crs = &start_ccb->crs; + dev = crs->ccb_h.path->device; + if (dev == NULL) { + + crs->ccb_h.status = CAM_DEV_NOT_THERE; + break; + } + + s = splcam(); + + if ((crs->release_flags & RELSIM_ADJUST_OPENINGS) != 0) { + + if ((dev->inq_data.flags & SID_CmdQue) != 0) { + int reduction; + + /* Don't ever go below one opening */ + if (crs->openings > 0) { + xpt_dev_ccbq_resize(crs->ccb_h.path, + crs->openings); + + if (bootverbose || 1) { + xpt_print_path(crs->ccb_h.path); + printf("tagged openings " + "now %d\n", + crs->openings); + } + } + } + } + + if ((crs->release_flags & RELSIM_RELEASE_AFTER_TIMEOUT) != 0) { + + if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { + + /* + * Just extend the old timeout and decrement + * the freeze count so that a single timeout + * is sufficient for releasing the queue. + */ + start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; + untimeout(xpt_release_devq_timeout, + dev, dev->c_handle); + } else { + + start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + } + + dev->c_handle = + timeout(xpt_release_devq_timeout, + dev, + (crs->release_timeout * hz) / 1000); + + dev->flags |= CAM_DEV_REL_TIMEOUT_PENDING; + + } + + if ((crs->release_flags & RELSIM_RELEASE_AFTER_CMDCMPLT) != 0) { + + if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0) { + /* + * Decrement the freeze count so that a single + * completion is still sufficient to unfreeze + * the queue. + */ + start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; + } else { + + dev->flags |= CAM_DEV_REL_ON_COMPLETE; + start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + } + } + + if ((crs->release_flags & RELSIM_RELEASE_AFTER_QEMPTY) != 0) { + + if ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 + || (dev->ccbq.dev_active == 0)) { + + start_ccb->ccb_h.flags &= ~CAM_DEV_QFREEZE; + } else { + + dev->flags |= CAM_DEV_REL_ON_QUEUE_EMPTY; + start_ccb->ccb_h.flags |= CAM_DEV_QFREEZE; + } + } + splx(s); + + if ((start_ccb->ccb_h.flags & CAM_DEV_QFREEZE) == 0) { + + xpt_release_devq(crs->ccb_h.path->device, + /*run_queue*/TRUE); + } + start_ccb->crs.qfrozen_cnt = dev->qfrozen_cnt; + start_ccb->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_SCAN_BUS: + xpt_scan_bus(start_ccb->ccb_h.path->periph, start_ccb); + break; + case XPT_SCAN_LUN: + xpt_scan_lun(start_ccb->ccb_h.path->periph, + start_ccb->ccb_h.path, start_ccb->crcn.flags, + start_ccb); + break; + case XPT_DEBUG: { +#ifdef CAMDEBUG + int s; + + s = splcam(); + cam_dflags = start_ccb->cdbg.flags; + if (cam_dpath != NULL) { + xpt_free_path(cam_dpath); + cam_dpath = NULL; + } + + if (cam_dflags != CAM_DEBUG_NONE) { + if (xpt_create_path(&cam_dpath, xpt_periph, + start_ccb->ccb_h.path_id, + start_ccb->ccb_h.target_id, + start_ccb->ccb_h.target_lun) != + CAM_REQ_CMP) { + start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + cam_dflags = CAM_DEBUG_NONE; + } else + start_ccb->ccb_h.status = CAM_REQ_CMP; + } else { + cam_dpath = NULL; + start_ccb->ccb_h.status = CAM_REQ_CMP; + } + splx(s); +#else /* !CAMDEBUG */ + start_ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; +#endif /* CAMDEBUG */ + break; + } + case XPT_NOOP: + start_ccb->ccb_h.status = CAM_REQ_CMP; + break; + default: + case XPT_SDEV_TYPE: + case XPT_ABORT: + case XPT_RESET_DEV: + case XPT_TERM_IO: + case XPT_ENG_INQ: + /* XXX Implement */ + start_ccb->ccb_h.status = CAM_PROVIDE_FAIL; + break; + } +} + +void +xpt_polled_action(union ccb *start_ccb) +{ + int s; + u_int32_t timeout; + struct cam_sim *sim; + struct cam_devq *devq; + struct cam_ed *dev; + + timeout = start_ccb->ccb_h.timeout; + sim = start_ccb->ccb_h.path->bus->sim; + devq = sim->devq; + dev = start_ccb->ccb_h.path->device; + + s = splcam(); + + /* + * Steal an opening so that no other queued requests + * can get it before us while we simulate interrupts. + */ + dev->ccbq.devq_openings--; + dev->ccbq.dev_openings--; + + while((devq->send_openings <= 0 || dev->ccbq.dev_openings < 0) + && (--timeout > 0)) { + DELAY(1000); + (*(sim->sim_poll))(sim); + swi_camnet(); + swi_cambio(); + } + + dev->ccbq.devq_openings++; + dev->ccbq.dev_openings++; + + if (timeout != 0) { + xpt_action(start_ccb); + while(--timeout > 0) { + (*(sim->sim_poll))(sim); + swi_camnet(); + swi_cambio(); + if ((start_ccb->ccb_h.status & CAM_STATUS_MASK) + != CAM_REQ_INPROG) + break; + DELAY(1000); + } + if (timeout == 0) { + /* + * XXX Is it worth adding a sim_timeout entry + * point so we can attempt recovery? If + * this is only used for dumps, I don't think + * it is. + */ + start_ccb->ccb_h.status = CAM_CMD_TIMEOUT; + } + } else { + start_ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + } + splx(s); +} + +/* + * Schedule a peripheral driver to receive a ccb when it's + * target device has space for more transactions. + */ +void +xpt_schedule(struct cam_periph *perph, u_int32_t new_priority) +{ + struct cam_ed *device; + int s; + int runq; + + CAM_DEBUG(perph->path, CAM_DEBUG_TRACE, ("xpt_schedule\n")); + device = perph->path->device; + s = splsoftcam(); + if (periph_is_queued(perph)) { + /* Simply reorder based on new priority */ + CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, + (" change priority to %d\n", new_priority)); + if (new_priority < perph->pinfo.priority) { + camq_change_priority(&device->drvq, + perph->pinfo.index, + new_priority); + } + runq = 0; + } else { + /* New entry on the queue */ + CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, + (" added periph to queue\n")); + if (device->drvq.generation++ == 0) { + /* Generation wrap, regen all entries */ + camq_regen(&device->drvq); + } + perph->pinfo.priority = new_priority; + perph->pinfo.generation = device->drvq.generation; + camq_insert(&device->drvq, &perph->pinfo); + runq = xpt_schedule_dev_allocq(perph->path->bus, device); + } + splx(s); + if (runq != 0) { + CAM_DEBUG(perph->path, CAM_DEBUG_SUBTRACE, + (" calling xpt_run_devq\n")); + xpt_run_dev_allocq(perph->path->bus); + } +} + + +/* + * Schedule a device to run on a given queue. + * If the device was inserted as a new entry on the queue, + * return 1 meaning the device queue should be run. If we + * were already queued, implying someone else has already + * started the queue, return 0 so the caller doesn't attempt + * to run the queue. Must be run at either splsoftcam + * (or splcam since that encompases splsoftcam). + */ +static int +xpt_schedule_dev(struct camq *queue, cam_pinfo *pinfo, + u_int32_t new_priority) +{ + int retval; + u_int32_t old_priority; + + CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("xpt_schedule_dev\n")); + + old_priority = pinfo->priority; + + /* + * Are we already queued? + */ + if (pinfo->index != CAM_UNQUEUED_INDEX) { + /* Simply reorder based on new priority */ + if (new_priority < old_priority) { + camq_change_priority(queue, pinfo->index, + new_priority); + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + ("changed priority to %d\n", + new_priority)); + } + retval = 0; + } else { + /* New entry on the queue */ + if (new_priority < old_priority) + pinfo->priority = new_priority; + + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + ("Inserting onto queue\n")); + if (queue->generation++ == 0) { + /* Generation wrap, regen all entries */ + camq_regen(queue); + } + pinfo->generation = queue->generation; + camq_insert(queue, pinfo); + retval = 1; + } + return (retval); +} + +static void +xpt_run_dev_allocq(struct cam_eb *bus) +{ + struct cam_devq *devq; + int s; + + CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("xpt_run_dev_allocq\n")); + devq = bus->sim->devq; + + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + (" qfrozen_cnt == 0x%x, entries == %d, " + "openings == %d, active == %d\n", + devq->alloc_queue.qfrozen_cnt, + devq->alloc_queue.entries, + devq->alloc_openings, + devq->alloc_active)); + + s = splsoftcam(); + devq->alloc_queue.qfrozen_cnt++; + while ((devq->alloc_queue.entries > 0) + && (devq->alloc_openings > 0) + && (devq->alloc_queue.qfrozen_cnt <= 1)) { + struct cam_ed_qinfo *qinfo; + struct cam_ed *device; + union ccb *work_ccb; + struct cam_periph *drv; + struct camq *drvq; + + qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->alloc_queue, + /*position*/0); + device = qinfo->device; + + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + ("running device 0x%x\n", device)); + + drvq = &device->drvq; + +#ifdef CAMDEBUG + if (drvq->entries <= 0) { + panic("xpt_run_dev_allocq: " + "Device on queue without any work to do"); + } +#endif + if ((work_ccb = xpt_get_ccb(device)) != NULL) { + devq->alloc_openings--; + devq->alloc_active++; + drv = (struct cam_periph*)camq_remove(drvq, + /*pos*/0); + /* Update priority */ + if (drvq->entries > 0) { + qinfo->pinfo.priority = drvq->queue_array[0]->priority; + } else { + qinfo->pinfo.priority = CAM_PRIORITY_NONE; + } + splx(s); + xpt_setup_ccb(&work_ccb->ccb_h, drv->path, + drv->pinfo.priority); + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + ("calling periph start\n")); + drv->periph_start(drv, work_ccb); + } else { + /* + * Malloc failure in alloc_ccb + */ + /* + * XXX add us to a list to be run from free_ccb + * if we don't have any ccbs active on this + * device queue otherwise we may never get run + * again. + */ + break; + } + + /* Raise IPL for possible insertion and test at top of loop */ + s = splsoftcam(); + + if (drvq->entries > 0) { + /* We have more work. Attempt to reschedule */ + xpt_schedule_dev_allocq(bus, device); + } + } + devq->alloc_queue.qfrozen_cnt--; + splx(s); +} + +static void +xpt_run_dev_sendq(struct cam_eb *bus) +{ + struct cam_devq *devq; + int s; + + CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("xpt_run_dev_sendq\n")); + + devq = bus->sim->devq; + + s = splcam(); + devq->send_queue.qfrozen_cnt++; + splx(s); + s = splsoftcam(); + while ((devq->send_queue.entries > 0) + && (devq->send_openings > 0)) { + struct cam_ed_qinfo *qinfo; + struct cam_ed *device; + union ccb *work_ccb; + struct cam_sim *sim; + int ospl; + + ospl = splcam(); + if (devq->send_queue.qfrozen_cnt > 1) { + splx(ospl); + break; + } + + qinfo = (struct cam_ed_qinfo *)camq_remove(&devq->send_queue, + /*position*/0); + device = qinfo->device; + + /* + * If the device has been "frozen", don't attempt + * to run it. + */ + if (device->qfrozen_cnt > 0) { + splx(ospl); + continue; + } + + CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, + ("running device 0x%x\n", device)); + + work_ccb = cam_ccbq_peek_ccb(&device->ccbq, 0); + if (work_ccb == NULL) { + printf("device on run queue with no ccbs???"); + splx(ospl); + continue; + } + + if ((work_ccb->ccb_h.flags & CAM_HIGH_POWER) != 0) { + + if (num_highpower <= 0) { + /* + * We got a high power command, but we + * don't have any available slots. Freeze + * the device queue until we have a slot + * available. + */ + device->qfrozen_cnt++; + STAILQ_INSERT_TAIL(&highpowerq, + &work_ccb->ccb_h, + xpt_links.stqe); + + splx(ospl); + continue; + } else { + /* + * Consume a high power slot while + * this ccb runs. + */ + num_highpower--; + } + } + devq->active_dev = device; + cam_ccbq_remove_ccb(&device->ccbq, work_ccb); + + cam_ccbq_send_ccb(&device->ccbq, work_ccb); + splx(ospl); + + devq->send_openings--; + devq->send_active++; + + if (device->ccbq.queue.entries > 0) { + qinfo->pinfo.priority = + device->ccbq.queue.queue_array[0]->priority; + xpt_schedule_dev_sendq(bus, device); + } else { + qinfo->pinfo.priority = CAM_PRIORITY_NONE; + } + + if (work_ccb && (work_ccb->ccb_h.flags & CAM_DEV_QFREEZE) != 0){ + /* + * The client wants to freeze the queue + * after this CCB is sent. + */ + ospl = splcam(); + device->qfrozen_cnt++; + splx(ospl); + } + + splx(s); + + if ((device->inq_flags & SID_CmdQue) != 0) + work_ccb->ccb_h.flags |= CAM_TAG_ACTION_VALID; + else + /* + * Clear this in case of a retried CCB that failed + * due to a rejected tag. + */ + work_ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID; + + /* + * Device queues can be shared among multiple sim instances + * that reside on different busses. Use the SIM in the queue + * CCB's path, rather than the one in the bus that was passed + * into this function. + */ + sim = work_ccb->ccb_h.path->bus->sim; + (*(sim->sim_action))(sim, work_ccb); + + ospl = splcam(); + devq->active_dev = NULL; + splx(ospl); + /* Raise IPL for possible insertion and test at top of loop */ + s = splsoftcam(); + } + splx(s); + s = splcam(); + devq->send_queue.qfrozen_cnt--; + splx(s); +} + +/* + * This function merges stuff from the slave ccb into the master ccb, while + * keeping important fields in the master ccb constant. + */ +void +xpt_merge_ccb(union ccb *master_ccb, union ccb *slave_ccb) +{ + /* + * Pull fields that are valid for peripheral drivers to set + * into the master CCB along with the CCB "payload". + */ + master_ccb->ccb_h.retry_count = slave_ccb->ccb_h.retry_count; + master_ccb->ccb_h.func_code = slave_ccb->ccb_h.func_code; + master_ccb->ccb_h.timeout = slave_ccb->ccb_h.timeout; + master_ccb->ccb_h.flags = slave_ccb->ccb_h.flags; + bcopy(&(&slave_ccb->ccb_h)[1], &(&master_ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); +} + +void +xpt_setup_ccb(struct ccb_hdr *ccb_h, struct cam_path *path, u_int32_t priority) +{ + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_setup_ccb\n")); + ccb_h->pinfo.priority = priority; + ccb_h->path = path; + ccb_h->path_id = path->bus->path_id; + if (path->target) + ccb_h->target_id = path->target->target_id; + else + ccb_h->target_id = CAM_TARGET_WILDCARD; + if (path->device) { + if (path->device->ccbq.queue.generation++ == 0) { + /* Generation wrap, regen all entries */ + cam_ccbq_regen(&path->device->ccbq); + } + ccb_h->target_lun = path->device->lun_id; + ccb_h->pinfo.generation = path->device->ccbq.queue.generation; + } else { + ccb_h->target_lun = CAM_TARGET_WILDCARD; + } + ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; + ccb_h->flags = 0; +} + +/* Path manipulation functions */ +cam_status +xpt_create_path(struct cam_path **new_path_ptr, struct cam_periph *perph, + path_id_t path_id, target_id_t target_id, lun_id_t lun_id) +{ + struct cam_path *path; + cam_status status; + + path = (struct cam_path *)malloc(sizeof(*path), M_DEVBUF, M_NOWAIT); + + if (path == NULL) { + status = CAM_RESRC_UNAVAIL; + return(status); + } + status = xpt_compile_path(path, perph, path_id, target_id, lun_id); + if (status != CAM_REQ_CMP) { + free(path, M_DEVBUF); + path = NULL; + } + *new_path_ptr = path; + return (status); +} + +static cam_status +xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, + path_id_t path_id, target_id_t target_id, lun_id_t lun_id) +{ + struct cam_eb *bus; + struct cam_et *target; + struct cam_ed *device; + cam_status status; + int s; + + status = CAM_REQ_CMP; /* Completed without error */ + target = NULL; /* Wildcarded */ + device = NULL; /* Wildcarded */ + s = splsoftcam(); + bus = xpt_find_bus(path_id); + if (bus == NULL) { + status = CAM_PATH_INVALID; + } else if (target_id != CAM_TARGET_WILDCARD) { + target = xpt_find_target(bus, target_id); + if (target == NULL) { + if (path_id == CAM_XPT_PATH_ID) { + status = CAM_TID_INVALID; + } else { + /* Create one */ + struct cam_et *new_target; + + new_target = xpt_alloc_target(bus, target_id); + if (new_target == NULL) { + status = CAM_RESRC_UNAVAIL; + } else { + target = new_target; + } + } + } + if (target != NULL && lun_id != CAM_LUN_WILDCARD) { + device = xpt_find_device(target, lun_id); + if (device == NULL) { + if (path_id == CAM_XPT_PATH_ID) { + status = CAM_LUN_INVALID; + } else { + /* Create one */ + struct cam_ed *new_device; + + new_device = xpt_alloc_device(bus, + target, + lun_id); + if (new_device == NULL) { + status = CAM_RESRC_UNAVAIL; + } else { + device = new_device; + } + } + } + } + } else if (lun_id != CAM_LUN_WILDCARD) { + /* + * Specific luns are not allowed if the + * target is wildcarded + */ + status = CAM_LUN_INVALID; + } + + /* + * Only touch the user's data if we are successful. + */ + if (status == CAM_REQ_CMP) { + new_path->periph = perph; + new_path->bus = bus; + new_path->target = target; + new_path->device = device; + CAM_DEBUG(new_path, CAM_DEBUG_TRACE, ("xpt_compile_path\n")); + } else { + if (device != NULL) + xpt_release_device(bus, target, device); + if (target != NULL) + xpt_release_target(bus, target); + } + splx(s); + return (status); +} + +static void +xpt_release_path(struct cam_path *path) +{ + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_path\n")); + if (path->device != NULL) + xpt_release_device(path->bus, path->target, path->device); + if (path->target != NULL) + xpt_release_target(path->bus, path->target); +} + +void +xpt_free_path(struct cam_path *path) +{ + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_free_path\n")); + xpt_release_path(path); + free(path, M_DEVBUF); +} + + +/* + * Return -1 for failure, 0 for exact match, 1 for match with wildcards. + */ +int +xpt_path_comp(struct cam_path *path1, struct cam_path *path2) +{ + int retval = 0; + + if (path1->bus != path2->bus) { + if ((path1->bus == NULL) + || (path2->bus == NULL)) + retval = 1; + else + return (-1); + } + if (path1->target != path2->target) { + if ((path1->target == NULL) + || (path2->target == NULL)) + retval = 1; + else + return (-1); + } + if (path1->device != path2->device) { + if ((path1->device == NULL) + || (path2->device == NULL)) + retval = 1; + else + return (-1); + } + return (retval); +} + +void +xpt_print_path(struct cam_path *path) +{ + if (path == NULL) + printf("(nopath): "); + else { + if (path->periph != NULL) + printf("(%s%d:", path->periph->periph_name, + path->periph->unit_number); + else + printf("(noperiph:"); + + if (path->bus != NULL) + printf("%s%d:%d:", path->bus->sim->sim_name, + path->bus->sim->unit_number, + path->bus->sim->bus_id); + else + printf("nobus:"); + + if (path->target != NULL) + printf("%d:", path->target->target_id); + else + printf("X:"); + + if (path->device != NULL) + printf("%d): ", path->device->lun_id); + else + printf("X): "); + } +} + +path_id_t +xpt_path_path_id(struct cam_path *path) +{ + return(path->bus->path_id); +} + +target_id_t +xpt_path_target_id(struct cam_path *path) +{ + if (path->target != NULL) + return (path->target->target_id); + else + return (CAM_TARGET_WILDCARD); +} + +lun_id_t +xpt_path_lun_id(struct cam_path *path) +{ + if (path->device != NULL) + return (path->device->lun_id); + else + return (CAM_LUN_WILDCARD); +} + +struct cam_sim * +xpt_path_sim(struct cam_path *path) +{ + return (path->bus->sim); +} + +struct cam_periph* +xpt_path_periph(struct cam_path *path) +{ + return (path->periph); +} + +/* + * Release a CAM control block for the caller. Remit the cost of the structure + * to the device referenced by the path. If the this device had no 'credits' + * and peripheral drivers have registered async callbacks for this notification + * call them now. + */ +void +xpt_release_ccb(union ccb *free_ccb) +{ + int s; + struct cam_path *path; + struct cam_ed *device; + struct cam_eb *bus; + + CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("xpt_release_ccb\n")); + path = free_ccb->ccb_h.path; + device = path->device; + bus = path->bus; + s = splsoftcam(); + cam_ccbq_release_opening(&device->ccbq); + if (xpt_ccb_count > xpt_max_ccbs) { + xpt_free_ccb(free_ccb); + xpt_ccb_count--; + } else { + SLIST_INSERT_HEAD(&ccb_freeq, &free_ccb->ccb_h, xpt_links.sle); + } + bus->sim->devq->alloc_openings++; + bus->sim->devq->alloc_active--; + /* XXX Turn this into an inline function - xpt_run_device?? */ + if ((device_is_alloc_queued(device) == 0) + && (device->drvq.entries > 0)) { + xpt_schedule_dev_allocq(bus, device); + } + splx(s); + if (dev_allocq_is_runnable(bus->sim->devq)) + xpt_run_dev_allocq(bus); +} + +/* Functions accessed by SIM drivers */ + +/* + * A sim structure, listing the SIM entry points and instance + * identification info is passed to xpt_bus_register to hook the SIM + * into the CAM framework. xpt_bus_register creates a cam_eb entry + * for this new bus and places it in the array of busses and assigns + * it a path_id. The path_id may be influenced by "hard wiring" + * information specified by the user. Once interrupt services are + * availible, the bus will be probed. + */ +int32_t +xpt_bus_register(struct cam_sim *sim, u_int32_t bus) +{ + static path_id_t buscount; + struct cam_eb *new_bus; + struct ccb_pathinq cpi; + int s; + + sim->bus_id = bus; + new_bus = (struct cam_eb *)malloc(sizeof(*new_bus), + M_DEVBUF, M_NOWAIT); + if (new_bus == NULL) { + /* Couldn't satisfy request */ + return (CAM_RESRC_UNAVAIL); + } + + bzero(new_bus, sizeof(*new_bus)); + + if (strcmp(sim->sim_name, "xpt") != 0) { + + sim->path_id = xptpathid(sim->sim_name, sim->unit_number, + sim->bus_id, &buscount); + } + + new_bus->path_id = sim->path_id; + new_bus->sim = sim; + SLIST_INIT(&new_bus->asyncs); + TAILQ_INIT(&new_bus->et_entries); + s = splsoftcam(); + TAILQ_INSERT_TAIL(&xpt_busses, new_bus, links); + bus_generation++; + + /* Notify interested parties */ + if (sim->path_id != CAM_XPT_PATH_ID) { + struct cam_path path; + + xpt_compile_path(&path, /*periph*/NULL, sim->path_id, + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); + xpt_setup_ccb(&cpi.ccb_h, &path, /*priority*/1); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + xpt_async(AC_PATH_REGISTERED, xpt_periph->path, &cpi); + xpt_release_path(&path); + } + splx(s); + return (CAM_SUCCESS); +} + +static int +xptnextfreebus(path_id_t startbus) +{ + struct cam_sim_config *sim_conf; + + sim_conf = cam_sinit; + while (sim_conf->sim_name != NULL) { + + if (IS_SPECIFIED(sim_conf->pathid) + && (startbus == sim_conf->pathid)) { + ++startbus; + /* Start the search over */ + sim_conf = cam_sinit; + } else { + sim_conf++; + } + } + return (startbus); +} + +static int +xptpathid(const char *sim_name, int sim_unit, + int sim_bus, path_id_t *nextpath) +{ + struct cam_sim_config *sim_conf; + path_id_t pathid; + + pathid = CAM_XPT_PATH_ID; + for (sim_conf = cam_sinit; sim_conf->sim_name != NULL; sim_conf++) { + + if (!IS_SPECIFIED(sim_conf->pathid)) + continue; + + if (!strcmp(sim_name, sim_conf->sim_name) + && (sim_unit == sim_conf->sim_unit)) { + + if (IS_SPECIFIED(sim_conf->sim_bus)) { + if (sim_bus == sim_conf->sim_bus) { + pathid = sim_conf->pathid; + break; + } + } else if (sim_bus == 0) { + /* Unspecified matches bus 0 */ + pathid = sim_conf->pathid; + break; + } else { + printf("Ambiguous scbus configuration for %s%d " + "bus %d, cannot wire down. The kernel " + "config entry for scbus%d should " + "specify a controller bus.\n" + "Scbus will be assigned dynamically.\n", + sim_name, sim_unit, sim_bus, + sim_conf->pathid); + break; + } + } + } + + if (pathid == CAM_XPT_PATH_ID) { + pathid = xptnextfreebus(*nextpath); + *nextpath = pathid + 1; + } + return (pathid); +} + +int32_t +xpt_bus_deregister(path_id) + u_int8_t path_id; +{ + /* XXX */ + return (CAM_SUCCESS); +} + +void +xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) +{ + struct cam_eb *bus; + struct cam_et *target, *next_target; + struct cam_ed *device, *next_device; + int s; + + CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_async\n")); + + s = splsoftcam(); + + bus = path->bus; + + /* + * Freeze the SIM queue for SCSI_DELAY ms to + * allow the bus to settle. + */ + if (async_code == AC_BUS_RESET) { + struct cam_sim *sim; + + sim = bus->sim; + + /* + * If there isn't already another timeout pending, go ahead + * and freeze the simq and set the timeout flag. If there + * is another timeout pending, replace it with this + * timeout. There could be two bus reset async broadcasts + * sent for some dual-channel controllers. + */ + if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) == 0) { + xpt_freeze_simq(sim, 1); + sim->flags |= CAM_SIM_REL_TIMEOUT_PENDING; + } else + untimeout(xpt_release_simq_timeout, sim, sim->c_handle); + + sim->c_handle = timeout(xpt_release_simq_timeout, + sim, (SCSI_DELAY * hz) / 1000); + } + + for (target = TAILQ_FIRST(&bus->et_entries); + target != NULL; + target = next_target) { + + next_target = TAILQ_NEXT(target, links); + + if (path->target != target + && path->target != NULL) + continue; + + if (async_code == AC_TRANSFER_NEG) { + struct ccb_trans_settings *settings; + + settings = (struct ccb_trans_settings *)async_arg; + xpt_set_transfer_settings(settings, + /*async_update*/TRUE); + } + + for (device = TAILQ_FIRST(&target->ed_entries); + device != NULL; + device = next_device) { + cam_status status; + struct cam_path newpath; + + next_device = TAILQ_NEXT(device, links); + + if (path->device != device + && path->device != NULL) + continue; + + /* + * We'll need this path for either one of these two + * async callback codes. Basically, we need to + * compile our own path instead of just using the path + * the user passes in since the user may well have + * passed in a wildcardded path. I'm not really + * sure why anyone would want to wildcard a path for + * either one of these async callbacks, but we need + * to be able to handle it if they do. + */ + if ((async_code == AC_SENT_BDR) + || (async_code == AC_INQ_CHANGED)) + status = xpt_compile_path(&newpath, NULL, + bus->path_id, + target->target_id, + device->lun_id); + else + status = CAM_REQ_CMP; /* silence the compiler */ + + /* + * If we send a BDR, freeze the device queue for + * SCSI_DELAY seconds to allow it to settle down. + */ + if (async_code == AC_SENT_BDR) { + if (status == CAM_REQ_CMP) { + xpt_freeze_devq(&newpath, 1); + /* + * Although this looks bad, it + * isn't as bad as it seems. We're + * passing in a stack-allocated path + * that we then immediately release + * after scheduling a timeout to + * release the device queue. So + * the path won't be around when + * the timeout fires, right? Right. + * But it doesn't matter, since + * xpt_release_devq and its timeout + * function both take the device as + * an argument. Theoretically, the + * device will still be there when + * the timeout fires, even though + * the path will be gone. + */ + cam_release_devq( + &newpath, + /*relsim_flags*/ + RELSIM_RELEASE_AFTER_TIMEOUT, + /*reduction*/0, + /*timeout*/SCSI_DELAY, + /*getcount_only*/0); + xpt_release_path(&newpath); + } + } else if (async_code == AC_INQ_CHANGED) { + /* + * We've sent a start unit command, or + * something similar to a device that may + * have caused its inquiry data to change. + * So we re-scan the device to refresh the + * inquiry data for it. + */ + if (status == CAM_REQ_CMP) { + xpt_scan_lun(path->periph, &newpath, + CAM_EXPECT_INQ_CHANGE, + NULL); + xpt_release_path(&newpath); + } + } else if (async_code == AC_LOST_DEVICE) + device->flags |= CAM_DEV_UNCONFIGURED; + + xpt_async_bcast(&device->asyncs, + async_code, + path, + async_arg); + } + } + xpt_async_bcast(&bus->asyncs, async_code, + path, async_arg); + splx(s); +} + +static void +xpt_async_bcast(struct async_list *async_head, + u_int32_t async_code, + struct cam_path *path, void *async_arg) +{ + struct async_node *cur_entry; + + cur_entry = SLIST_FIRST(async_head); + while (cur_entry != NULL) { + struct async_node *next_entry; + /* + * Grab the next list entry before we call the current + * entry's callback. This is because the callback function + * can delete its async callback entry. + */ + next_entry = SLIST_NEXT(cur_entry, links); + if ((cur_entry->event_enable & async_code) != 0) + cur_entry->callback(cur_entry->callback_arg, + async_code, path, + async_arg); + cur_entry = next_entry; + } +} + +u_int32_t +xpt_freeze_devq(struct cam_path *path, u_int count) +{ + int s; + struct ccb_hdr *ccbh; + + s = splcam(); + path->device->qfrozen_cnt += count; + + /* + * Mark the last CCB in the queue as needing + * to be requeued if the driver hasn't + * changed it's state yet. This fixes a race + * where a ccb is just about to be queued to + * a controller driver when it's interrupt routine + * freezes the queue. To completly close the + * hole, controller drives must check to see + * if a ccb's status is still CAM_REQ_INPROG + * under spl protection just before they queue + * the CCB. See ahc_action/ahc_freeze_devq for + * an example. + */ + ccbh = TAILQ_LAST(&path->device->ccbq.active_ccbs, ccb_hdr_list); + if (ccbh && ccbh->status == CAM_REQ_INPROG) + ccbh->status = CAM_REQUEUE_REQ; + splx(s); + return (path->device->qfrozen_cnt); +} + +u_int32_t +xpt_freeze_simq(struct cam_sim *sim, u_int count) +{ + sim->devq->send_queue.qfrozen_cnt += count; + if (sim->devq->active_dev != NULL) { + struct ccb_hdr *ccbh; + + ccbh = TAILQ_LAST(&sim->devq->active_dev->ccbq.active_ccbs, + ccb_hdr_list); + if (ccbh && ccbh->status == CAM_REQ_INPROG) + ccbh->status = CAM_REQUEUE_REQ; + } + return (sim->devq->send_queue.qfrozen_cnt); +} + +static void +xpt_release_devq_timeout(void *arg) +{ + struct cam_ed *device; + + device = (struct cam_ed *)arg; + + xpt_release_devq(device, /*run_queue*/TRUE); +} + +void +xpt_release_devq(struct cam_ed *dev, int run_queue) +{ + int rundevq; + int s; + + rundevq = 0; + s = splcam(); + if (dev->qfrozen_cnt > 0) { + + dev->qfrozen_cnt--; + if (dev->qfrozen_cnt == 0) { + + /* + * No longer need to wait for a successful + * command completion. + */ + dev->flags &= ~CAM_DEV_REL_ON_COMPLETE; + + /* + * Remove any timeouts that might be scheduled + * to release this queue. + */ + if ((dev->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) { + untimeout(xpt_release_devq_timeout, dev, + dev->c_handle); + dev->flags &= ~CAM_DEV_REL_TIMEOUT_PENDING; + } + + /* + * Now that we are unfrozen schedule the + * device so any pending transactions are + * run. + */ + if ((dev->ccbq.queue.entries > 0) + && (xpt_schedule_dev_sendq(dev->target->bus, dev)) + && (run_queue != 0)) { + rundevq = 1; + } + } + } + splx(s); + if (rundevq != 0) + xpt_run_dev_sendq(dev->target->bus); +} + +void +xpt_release_simq(struct cam_sim *sim, int run_queue) +{ + int s; + struct camq *sendq; + + sendq = &(sim->devq->send_queue); + s = splcam(); + if (sendq->qfrozen_cnt > 0) { + + sendq->qfrozen_cnt--; + if (sendq->qfrozen_cnt == 0) { + + /* + * If there is a timeout scheduled to release this + * sim queue, remove it. The queue frozen count is + * already at 0. + */ + if ((sim->flags & CAM_SIM_REL_TIMEOUT_PENDING) != 0){ + untimeout(xpt_release_simq_timeout, sim, + sim->c_handle); + sim->flags &= ~CAM_SIM_REL_TIMEOUT_PENDING; + } + + splx(s); + + if (run_queue) { + /* + * Now that we are unfrozen run the send queue. + */ + xpt_run_dev_sendq(xpt_find_bus(sim->path_id)); + } + } else + splx(s); + } else + splx(s); +} + +static void +xpt_release_simq_timeout(void *arg) +{ + struct cam_sim *sim; + + sim = (struct cam_sim *)arg; + xpt_release_simq(sim, /* run_queue */ TRUE); +} + +void +xpt_done(union ccb *done_ccb) +{ + int s; + + s = splcam(); + + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xpt_done\n")); + switch (done_ccb->ccb_h.func_code) { + case XPT_SCSI_IO: + case XPT_ENG_EXEC: + case XPT_TARGET_IO: + case XPT_ACCEPT_TARGET_IO: + case XPT_CONT_TARGET_IO: + case XPT_SCAN_BUS: + case XPT_SCAN_LUN: + { + /* + * Queue up the request for handling by our SWI handler + * any of the "non-immediate" type of ccbs. + */ + switch (done_ccb->ccb_h.path->periph->type) { + case CAM_PERIPH_BIO: + TAILQ_INSERT_TAIL(&cam_bioq, &done_ccb->ccb_h, + sim_links.tqe); + done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; + setsoftcambio(); + break; + case CAM_PERIPH_NET: + TAILQ_INSERT_TAIL(&cam_netq, &done_ccb->ccb_h, + sim_links.tqe); + done_ccb->ccb_h.pinfo.index = CAM_DONEQ_INDEX; + setsoftcamnet(); + break; + } + break; + } + default: + break; + } + splx(s); +} + +union ccb * +xpt_alloc_ccb() +{ + union ccb *new_ccb; + + new_ccb = malloc(sizeof(*new_ccb), M_DEVBUF, M_WAITOK); + return (new_ccb); +} + +void +xpt_free_ccb(union ccb *free_ccb) +{ + free(free_ccb, M_DEVBUF); +} + + + +/* Private XPT functions */ + +/* + * Get a CAM control block for the caller. Charge the structure to the device + * referenced by the path. If the this device has no 'credits' then the + * device already has the maximum number of outstanding operations under way + * and we return NULL. If we don't have sufficient resources to allocate more + * ccbs, we also return NULL. + */ +static union ccb * +xpt_get_ccb(struct cam_ed *device) +{ + union ccb *new_ccb; + int s; + + s = splsoftcam(); + if ((new_ccb = (union ccb *)ccb_freeq.slh_first) == NULL) { + new_ccb = malloc(sizeof(*new_ccb), M_DEVBUF, M_NOWAIT); + if (new_ccb == NULL) { + splx(s); + return (NULL); + } + callout_handle_init(&new_ccb->ccb_h.timeout_ch); + SLIST_INSERT_HEAD(&ccb_freeq, &new_ccb->ccb_h, + xpt_links.sle); + xpt_ccb_count++; + } + cam_ccbq_take_opening(&device->ccbq); + SLIST_REMOVE_HEAD(&ccb_freeq, xpt_links.sle); + splx(s); + return (new_ccb); +} + + +static struct cam_et * +xpt_alloc_target(struct cam_eb *bus, target_id_t target_id) +{ + struct cam_et *target; + + target = (struct cam_et *)malloc(sizeof(*target), M_DEVBUF, M_NOWAIT); + if (target != NULL) { + struct cam_et *cur_target; + + target->bus = bus; + target->target_id = target_id; + target->refcount = 1; + TAILQ_INIT(&target->ed_entries); + + /* Insertion sort into our bus's target list */ + cur_target = TAILQ_FIRST(&bus->et_entries); + while (cur_target != NULL && cur_target->target_id < target_id) + cur_target = TAILQ_NEXT(cur_target, links); + + if (cur_target != NULL) { + TAILQ_INSERT_BEFORE(cur_target, target, links); + } else { + TAILQ_INSERT_TAIL(&bus->et_entries, target, links); + bus->generation++; + } + } + return (target); +} + +void +xpt_release_target(struct cam_eb *bus, struct cam_et *target) +{ + if ((--target->refcount == 0) + && (TAILQ_FIRST(&target->ed_entries) == NULL)) { + TAILQ_REMOVE(&bus->et_entries, target, links); + bus->generation++; + free(target, M_DEVBUF); + } +} + +static struct cam_ed * +xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) +{ + struct cam_ed *device; + struct cam_devq *devq; + int32_t status; + int s; + + s = splsoftcam(); + /* Make space for us in the device queue on our bus */ + devq = bus->sim->devq; + status = cam_devq_resize(devq, devq->alloc_queue.array_size + 1); + splx(s); + + if (status != CAM_REQ_CMP) { + device = NULL; + } else { + device = (struct cam_ed *)malloc(sizeof(*device), + M_DEVBUF, M_NOWAIT); + } + + if (device != NULL) { + struct cam_ed *cur_device; + + bzero(device, sizeof(*device)); + + SLIST_INIT(&device->asyncs); + SLIST_INIT(&device->periphs); + callout_handle_init(&device->c_handle); + device->refcount = 1; + device->flags |= CAM_DEV_UNCONFIGURED; + + cam_init_pinfo(&device->alloc_ccb_entry.pinfo); + device->alloc_ccb_entry.device = device; + cam_init_pinfo(&device->send_ccb_entry.pinfo); + device->send_ccb_entry.device = device; + + device->target = target; + + device->lun_id = lun_id; + + /* Initialize our queues */ + if (camq_init(&device->drvq, 0) != 0) { + free(device, M_DEVBUF); + return (NULL); + } + + if (cam_ccbq_init(&device->ccbq, + bus->sim->max_dev_openings) != 0) { + camq_fini(&device->drvq); + free(device, M_DEVBUF); + return (NULL); + } + s = splsoftcam(); + /* + * XXX should be limited by number of CCBs this bus can + * do. + */ + xpt_max_ccbs += device->ccbq.devq_openings; + /* Insertion sort into our target's device list */ + cur_device = TAILQ_FIRST(&target->ed_entries); + while (cur_device != NULL && cur_device->lun_id < lun_id) + cur_device = TAILQ_NEXT(cur_device, links); + if (cur_device != NULL) { + TAILQ_INSERT_BEFORE(cur_device, device, links); + } else { + TAILQ_INSERT_TAIL(&target->ed_entries, device, links); + target->generation++; + } + splx(s); + } + return (device); +} + +static void +xpt_release_device(struct cam_eb *bus, struct cam_et *target, + struct cam_ed *device) +{ + int s; + + if ((--device->refcount == 0) + && ((device->flags & CAM_DEV_UNCONFIGURED) != 0)) { + struct cam_devq *devq; + + s = splsoftcam(); + TAILQ_REMOVE(&target->ed_entries, device,links); + target->generation++; + xpt_max_ccbs -= device->ccbq.devq_openings; + free(device, M_DEVBUF); + /* Release our slot in the devq */ + devq = bus->sim->devq; + cam_devq_resize(devq, devq->alloc_queue.array_size - 1); + splx(s); + } +} + +static u_int32_t +xpt_dev_ccbq_resize(struct cam_path *path, int newopenings) +{ + int s; + int diff; + int result; + struct cam_ed *dev; + + dev = path->device; + s = splsoftcam(); + + diff = newopenings - (dev->ccbq.dev_active + dev->ccbq.dev_openings); + result = cam_ccbq_resize(&dev->ccbq, newopenings); + if (result == CAM_REQ_CMP && (diff < 0)) { + dev->flags |= CAM_DEV_RESIZE_QUEUE_NEEDED; + } + /* Adjust the global limit */ + xpt_max_ccbs += diff; + splx(s); + return (result); +} + +static struct cam_eb * +xpt_find_bus(path_id_t path_id) +{ + struct cam_eb *bus; + + for (bus = TAILQ_FIRST(&xpt_busses); + bus != NULL; + bus = TAILQ_NEXT(bus, links)) { + if (bus->path_id == path_id) + break; + } + return (bus); +} + +static struct cam_et * +xpt_find_target(struct cam_eb *bus, target_id_t target_id) +{ + struct cam_et *target; + + for (target = TAILQ_FIRST(&bus->et_entries); + target != NULL; + target = TAILQ_NEXT(target, links)) { + if (target->target_id == target_id) { + target->refcount++; + break; + } + } + return (target); +} + +static struct cam_ed * +xpt_find_device(struct cam_et *target, lun_id_t lun_id) +{ + struct cam_ed *device; + + for (device = TAILQ_FIRST(&target->ed_entries); + device != NULL; + device = TAILQ_NEXT(device, links)) { + if (device->lun_id == lun_id) { + device->refcount++; + break; + } + } + return (device); +} + +typedef struct { + union ccb *request_ccb; + struct ccb_pathinq *cpi; + int pending_count; +} xpt_scan_bus_info; + +/* + * To start a scan, request_ccb is an XPT_SCAN_BUS ccb. + * As the scan progresses, xpt_scan_bus is used as the + * callback on completion function. + */ +static void +xpt_scan_bus(struct cam_periph *periph, union ccb *request_ccb) +{ + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("xpt_scan_bus\n")); + switch (request_ccb->ccb_h.func_code) { + case XPT_SCAN_BUS: + { + xpt_scan_bus_info *scan_info; + union ccb *work_ccb; + struct cam_path *path; + u_int i; + u_int max_target; + u_int initiator_id; + + /* Find out the characteristics of the bus */ + work_ccb = xpt_alloc_ccb(); + xpt_setup_ccb(&work_ccb->ccb_h, request_ccb->ccb_h.path, + request_ccb->ccb_h.pinfo.priority); + work_ccb->ccb_h.func_code = XPT_PATH_INQ; + xpt_action(work_ccb); + if (work_ccb->ccb_h.status != CAM_REQ_CMP) { + request_ccb->ccb_h.status = work_ccb->ccb_h.status; + xpt_free_ccb(work_ccb); + xpt_done(request_ccb); + return; + } + + /* Save some state for use while we probe for devices */ + scan_info = (xpt_scan_bus_info *) + malloc(sizeof(xpt_scan_bus_info), M_TEMP, M_WAITOK); + scan_info->request_ccb = request_ccb; + scan_info->cpi = &work_ccb->cpi; + + /* Cache on our stack so we can work asynchronously */ + max_target = scan_info->cpi->max_target; + initiator_id = scan_info->cpi->initiator_id; + + /* + * Don't count the initiator if the + * initiator is addressable. + */ + scan_info->pending_count = max_target + 1; + if (initiator_id <= max_target) + scan_info->pending_count--; + + for (i = 0; i <= max_target; i++) { + cam_status status; + if (i == initiator_id) + continue; + + status = xpt_create_path(&path, xpt_periph, + request_ccb->ccb_h.path_id, + i, 0); + if (status != CAM_REQ_CMP) { + printf("xpt_scan_bus: xpt_create_path failed" + " with status %#x, bus scan halted\n", + status); + break; + } + work_ccb = xpt_alloc_ccb(); + xpt_setup_ccb(&work_ccb->ccb_h, path, + request_ccb->ccb_h.pinfo.priority); + work_ccb->ccb_h.func_code = XPT_SCAN_LUN; + work_ccb->ccb_h.cbfcnp = xpt_scan_bus; + work_ccb->ccb_h.ppriv_ptr0 = scan_info; + work_ccb->crcn.flags = request_ccb->crcn.flags; +#if 0 + printf("xpt_scan_bus: probing %d:%d:%d\n", + request_ccb->ccb_h.path_id, i, 0); +#endif + xpt_action(work_ccb); + } + break; + } + case XPT_SCAN_LUN: + { + xpt_scan_bus_info *scan_info; + path_id_t path_id; + target_id_t target_id; + lun_id_t lun_id; + + /* Reuse the same CCB to query if a device was really found */ + scan_info = (xpt_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0; + xpt_setup_ccb(&request_ccb->ccb_h, request_ccb->ccb_h.path, + request_ccb->ccb_h.pinfo.priority); + request_ccb->ccb_h.func_code = XPT_GDEV_TYPE; + + path_id = request_ccb->ccb_h.path_id; + target_id = request_ccb->ccb_h.target_id; + lun_id = request_ccb->ccb_h.target_lun; + xpt_action(request_ccb); + +#if 0 + printf("xpt_scan_bus: got back probe from %d:%d:%d\n", + path_id, target_id, lun_id); +#endif + + if (request_ccb->ccb_h.status != CAM_REQ_CMP) { + struct cam_ed *device; + struct cam_et *target; + + /* + * If we already probed lun 0 successfully, or + * we have additional configured luns on this + * target that might have "gone away", go onto + * the next lun. + */ + target = request_ccb->ccb_h.path->target; + device = TAILQ_FIRST(&target->ed_entries); + if (device != NULL) + device = TAILQ_NEXT(device, links); + + if ((lun_id != 0) || (device != NULL)) { + /* Try the next lun */ + lun_id++; + } + } else { + struct cam_ed *device; + + device = request_ccb->ccb_h.path->device; + + if ((device->quirk->quirks & CAM_QUIRK_NOLUNS) == 0) { + /* Try the next lun */ + lun_id++; + } + } + + xpt_free_path(request_ccb->ccb_h.path); + + /* Check Bounds */ + if ((lun_id == request_ccb->ccb_h.target_lun) + || lun_id > scan_info->cpi->max_lun) { + /* We're done */ + + xpt_free_ccb(request_ccb); + scan_info->pending_count--; + if (scan_info->pending_count == 0) { + xpt_free_ccb((union ccb *)scan_info->cpi); + request_ccb = scan_info->request_ccb; + free(scan_info, M_TEMP); + request_ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(request_ccb); + } + } else { + /* Try the next device */ + struct cam_path *path; + cam_status status; + + path = request_ccb->ccb_h.path; + status = xpt_create_path(&path, xpt_periph, + path_id, target_id, lun_id); + if (status != CAM_REQ_CMP) { + printf("xpt_scan_bus: xpt_create_path failed " + "with status %#x, halting LUN scan\n", + status); + xpt_free_ccb(request_ccb); + scan_info->pending_count--; + if (scan_info->pending_count == 0) { + xpt_free_ccb( + (union ccb *)scan_info->cpi); + request_ccb = scan_info->request_ccb; + free(scan_info, M_TEMP); + request_ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(request_ccb); + break; + } + } + xpt_setup_ccb(&request_ccb->ccb_h, path, + request_ccb->ccb_h.pinfo.priority); + request_ccb->ccb_h.func_code = XPT_SCAN_LUN; + request_ccb->ccb_h.cbfcnp = xpt_scan_bus; + request_ccb->ccb_h.ppriv_ptr0 = scan_info; + request_ccb->crcn.flags = + scan_info->request_ccb->crcn.flags; +#if 0 + xpt_print_path(path); + printf("xpt_scan bus probing\n"); +#endif + xpt_action(request_ccb); + } + break; + } + default: + break; + } +} + +typedef enum { + PROBE_TUR, + PROBE_INQUIRY, + PROBE_MODE_SENSE, + PROBE_SERIAL_NUM, + PROBE_TUR_FOR_NEGOTIATION +} probe_action; + +typedef enum { + PROBE_INQUIRY_CKSUM = 0x01, + PROBE_SERIAL_CKSUM = 0x02, + PROBE_NO_ANNOUNCE = 0x04 +} probe_flags; + +typedef struct { + TAILQ_HEAD(, ccb_hdr) request_ccbs; + probe_action action; + union ccb saved_ccb; + probe_flags flags; + MD5_CTX context; + u_int8_t digest[16]; +} probe_softc; + +static void +xpt_scan_lun(struct cam_periph *periph, struct cam_path *path, + cam_flags flags, union ccb *request_ccb) +{ + u_int32_t unit; + cam_status status; + struct cam_path *new_path; + struct cam_periph *old_periph; + int s; + + CAM_DEBUG(request_ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("xpt_scan_lun\n")); + + if (request_ccb == NULL) { + request_ccb = malloc(sizeof(union ccb), M_TEMP, M_NOWAIT); + if (request_ccb == NULL) { + xpt_print_path(path); + printf("xpt_scan_lun: can't allocate CCB, can't " + "continue\n"); + return; + } + new_path = malloc(sizeof(*new_path), M_TEMP, M_NOWAIT); + if (new_path == NULL) { + xpt_print_path(path); + printf("xpt_scan_lun: can't allocate path, can't " + "continue\n"); + free(request_ccb, M_TEMP); + return; + } + status = xpt_compile_path(new_path, periph, path->bus->path_id, + path->target->target_id, + path->device->lun_id); + + if (status != CAM_REQ_CMP) { + xpt_print_path(path); + printf("xpt_scan_lun: can't compile path, can't " + "continue\n"); + free(request_ccb, M_TEMP); + free(new_path, M_TEMP); + return; + } + xpt_setup_ccb(&request_ccb->ccb_h, new_path, /*priority*/ 1); + request_ccb->ccb_h.cbfcnp = xptscandone; + request_ccb->ccb_h.func_code = XPT_SCAN_LUN; + request_ccb->crcn.flags = flags; + } + + s = splsoftcam(); + if ((old_periph = cam_periph_find(path, "probe")) != NULL) { + probe_softc *softc; + + softc = (probe_softc *)old_periph->softc; + TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h, + periph_links.tqe); + } else { + status = cam_periph_alloc(proberegister, probecleanup, + probestart, "probe", + CAM_PERIPH_BIO, + request_ccb->ccb_h.path, NULL, 0, + request_ccb); + + if (status != CAM_REQ_CMP) { + xpt_print_path(path); + printf("xpt_scan_lun: cam_alloc_periph returned an " + "error, can't continue probe\n"); + request_ccb->ccb_h.status = status; + xpt_done(request_ccb); + } + } + splx(s); +} + +static void +xptscandone(struct cam_periph *periph, union ccb *done_ccb) +{ + xpt_release_path(done_ccb->ccb_h.path); + free(done_ccb->ccb_h.path, M_TEMP); + free(done_ccb, M_TEMP); +} + +static cam_status +proberegister(struct cam_periph *periph, void *arg) +{ + struct ccb_getdev *cgd; + probe_softc *softc; + union ccb *ccb; + + cgd = (struct ccb_getdev *)arg; + if (periph == NULL) { + printf("proberegister: periph was NULL!!\n"); + return(CAM_REQ_CMP_ERR); + } + + if (cgd == NULL) { + printf("proberegister: no getdev CCB, can't register device\n"); + return(CAM_REQ_CMP_ERR); + } + + softc = (probe_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); + + if (softc == NULL) { + printf("proberegister: Unable to probe new device. " + "Unable to allocate softc\n"); + return(CAM_REQ_CMP_ERR); + } + ccb = (union ccb *)cgd; + TAILQ_INIT(&softc->request_ccbs); + TAILQ_INSERT_TAIL(&softc->request_ccbs, &ccb->ccb_h, periph_links.tqe); + softc->flags = 0; + periph->softc = softc; + cam_periph_acquire(periph); + probeschedule(periph); + return(CAM_REQ_CMP); +} + +static void +probeschedule(struct cam_periph *periph) +{ + union ccb *ccb; + probe_softc *softc; + + softc = (probe_softc *)periph->softc; + ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); + + /* + * If a device has gone away and another device, or the same one, + * is back in the same place, it should have a unit attention + * condition pending. It will not report the unit attention in + * response to an inquiry, which may leave invalid transfer + * negotiations in effect. The TUR will reveal the unit attention + * condition. Only send the TUR for lun 0, since some devices + * will get confused by commands other than inquiry to non-existent + * luns. If you think a device has gone away start your scan from + * lun 0. This will insure that any bogus transfer settings are + * invalidated. + */ + if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED)==0) + && (ccb->ccb_h.target_lun == 0)) + softc->action = PROBE_TUR; + else + softc->action = PROBE_INQUIRY; + + if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE) + softc->flags |= PROBE_NO_ANNOUNCE; + else + softc->flags &= ~PROBE_NO_ANNOUNCE; + + xpt_schedule(periph, ccb->ccb_h.pinfo.priority); +} + +static void +probestart(struct cam_periph *periph, union ccb *start_ccb) +{ + /* Probe the device that our peripheral driver points to */ + struct ccb_scsiio *csio; + probe_softc *softc; + + CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n")); + + softc = (probe_softc *)periph->softc; + csio = &start_ccb->csio; + + switch (softc->action) { + case PROBE_TUR: + case PROBE_TUR_FOR_NEGOTIATION: + { + scsi_test_unit_ready(csio, + /*retries*/4, + probedone, + MSG_SIMPLE_Q_TAG, + SSD_FULL_SIZE, + /*timeout*/10000); + break; + } + case PROBE_INQUIRY: + { + struct scsi_inquiry_data *inq_buf; + + inq_buf = &periph->path->device->inq_data; + /* + * If the device is currently configured, we calculate an + * MD5 checksum of the inquiry data, and if the serial number + * length is greater than 0, add the serial number data + * into the checksum as well. Once the inquiry and the + * serial number check finish, we attempt to figure out + * whether we still have the same device. + */ + if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) { + + MD5Init(&softc->context); + MD5Update(&softc->context, (unsigned char *)inq_buf, + sizeof(struct scsi_inquiry_data)); + softc->flags |= PROBE_INQUIRY_CKSUM; + if (periph->path->device->serial_num_len > 0) { + MD5Update(&softc->context, + periph->path->device->serial_num, + periph->path->device->serial_num_len); + softc->flags |= PROBE_SERIAL_CKSUM; + } + MD5Final(softc->digest, &softc->context); + } + + scsi_inquiry(csio, + /*retries*/4, + probedone, + MSG_SIMPLE_Q_TAG, + (u_int8_t *)inq_buf, + sizeof(*inq_buf), + /*evpd*/FALSE, + /*page_code*/0, + SSD_MIN_SIZE, + /*timeout*/5 * 1000); + break; + } + case PROBE_MODE_SENSE: + { + void *mode_buf; + int mode_buf_len; + + mode_buf_len = sizeof(struct scsi_mode_header_6) + + sizeof(struct scsi_mode_blk_desc) + + sizeof(struct scsi_control_page); + mode_buf = malloc(mode_buf_len, M_TEMP, M_NOWAIT); + if (mode_buf != NULL) { + scsi_mode_sense(csio, + /*retries*/4, + probedone, + MSG_SIMPLE_Q_TAG, + /*dbd*/FALSE, + SMS_PAGE_CTRL_CURRENT, + SMS_CONTROL_MODE_PAGE, + mode_buf, + mode_buf_len, + SSD_FULL_SIZE, + /*timeout*/5000); + break; + } + xpt_print_path(periph->path); + printf("Unable to mode sense control page - malloc failure\n"); + softc->action = PROBE_SERIAL_NUM; + /* FALLTHROUGH */ + } + case PROBE_SERIAL_NUM: + { + struct scsi_vpd_unit_serial_number *serial_buf; + struct cam_ed* device; + + serial_buf = NULL; + device = periph->path->device; + device->serial_num = NULL; + device->serial_num_len = 0; + + if ((device->quirk->quirks & CAM_QUIRK_NOSERIAL) == 0) + serial_buf = (struct scsi_vpd_unit_serial_number *) + malloc(sizeof(*serial_buf), M_TEMP, M_NOWAIT); + + if (serial_buf != NULL) { + bzero(serial_buf, sizeof(*serial_buf)); + scsi_inquiry(csio, + /*retries*/4, + probedone, + MSG_SIMPLE_Q_TAG, + (u_int8_t *)serial_buf, + sizeof(*serial_buf), + /*evpd*/TRUE, + SVPD_UNIT_SERIAL_NUMBER, + SSD_MIN_SIZE, + /*timeout*/5 * 1000); + break; + } + /* + * We'll have to do without, let our probedone + * routine finish up for us. + */ + start_ccb->csio.data_ptr = NULL; + probedone(periph, start_ccb); + return; + } + } + xpt_action(start_ccb); +} + +static void +probedone(struct cam_periph *periph, union ccb *done_ccb) +{ + probe_softc *softc; + struct cam_path *path; + u_int32_t priority; + + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n")); + + softc = (probe_softc *)periph->softc; + path = done_ccb->ccb_h.path; + priority = done_ccb->ccb_h.pinfo.priority; + + switch (softc->action) { + case PROBE_TUR: + { + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + + if (cam_periph_error(done_ccb, 0, + SF_NO_PRINT, NULL) == ERESTART) + return; + else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path->device, + /*run_queue*/TRUE); + } + softc->action = PROBE_INQUIRY; + xpt_release_ccb(done_ccb); + xpt_schedule(periph, priority); + return; + } + case PROBE_INQUIRY: + { + if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { + struct scsi_inquiry_data *inq_buf; + u_int8_t periph_qual; + u_int8_t periph_dtype; + + inq_buf = &path->device->inq_data; + + periph_qual = SID_QUAL(inq_buf); + periph_dtype = SID_TYPE(inq_buf); + if (periph_dtype != T_NODEVICE) { + switch(periph_qual) { + case SID_QUAL_LU_CONNECTED: + { + xpt_find_quirk(path->device); + + if ((inq_buf->flags & SID_CmdQue) != 0) + softc->action = + PROBE_MODE_SENSE; + else + softc->action = + PROBE_SERIAL_NUM; + + path->device->flags &= + ~CAM_DEV_UNCONFIGURED; + + xpt_release_ccb(done_ccb); + xpt_schedule(periph, priority); + return; + } + default: + break; + } + } + } else if (cam_periph_error(done_ccb, 0, + done_ccb->ccb_h.target_lun > 0 + ? SF_RETRY_UA|SF_QUIET_IR + : SF_RETRY_UA, + &softc->saved_ccb) == ERESTART) { + return; + } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path->device, + /*run_queue*/TRUE); + } + /* + * If we get to this point, we got an error status back + * from the inquiry and the error status doesn't require + * automatically retrying the command. Therefore, the + * inquiry failed. If we had inquiry information before + * for this device, but this latest inquiry command failed, + * the device has probably gone away. If this device isn't + * already marked unconfigured, notify the peripheral + * drivers that this device is no more. + */ + if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0) + /* Send the async notification. */ + xpt_async(AC_LOST_DEVICE, path, NULL); + + xpt_release_ccb(done_ccb); + break; + } + case PROBE_MODE_SENSE: + { + struct ccb_scsiio *csio; + struct scsi_mode_header_6 *mode_hdr; + + csio = &done_ccb->csio; + mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr; + if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { + struct scsi_control_page *page; + u_int8_t *offset; + + offset = ((u_int8_t *)&mode_hdr[1]) + + mode_hdr->blk_desc_len; + page = (struct scsi_control_page *)offset; + path->device->queue_flags = page->queue_flags; + } else if (cam_periph_error(done_ccb, 0, + SF_RETRY_UA|SF_NO_PRINT, + &softc->saved_ccb) == ERESTART) { + return; + } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path->device, + /*run_queue*/TRUE); + } + xpt_release_ccb(done_ccb); + free(mode_hdr, M_TEMP); + softc->action = PROBE_SERIAL_NUM; + xpt_schedule(periph, priority); + return; + } + case PROBE_SERIAL_NUM: + { + struct ccb_scsiio *csio; + struct scsi_vpd_unit_serial_number *serial_buf; + u_int32_t priority; + int changed; + int have_serialnum; + + changed = 1; + have_serialnum = 0; + csio = &done_ccb->csio; + priority = done_ccb->ccb_h.pinfo.priority; + serial_buf = + (struct scsi_vpd_unit_serial_number *)csio->data_ptr; + + /* Clean up from previous instance of this device */ + if (path->device->serial_num != NULL) { + free(path->device->serial_num, M_DEVBUF); + path->device->serial_num = NULL; + path->device->serial_num_len = 0; + } + + if (serial_buf == NULL) { + /* + * Don't process the command as it was never sent + */ + } else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP + && (serial_buf->length > 0)) { + + have_serialnum = 1; + path->device->serial_num = + (u_int8_t *)malloc((serial_buf->length + 1), + M_DEVBUF, M_NOWAIT); + if (path->device->serial_num != NULL) { + bcopy(serial_buf->serial_num, + path->device->serial_num, + serial_buf->length); + path->device->serial_num_len = + serial_buf->length; + path->device->serial_num[serial_buf->length] + = '\0'; + } + } else if (cam_periph_error(done_ccb, 0, + SF_RETRY_UA|SF_NO_PRINT, + &softc->saved_ccb) == ERESTART) { + return; + } else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path->device, + /*run_queue*/TRUE); + } + + /* + * Let's see if we have seen this device before. + */ + if ((softc->flags & PROBE_INQUIRY_CKSUM) != 0) { + MD5_CTX context; + u_int8_t digest[16]; + + MD5Init(&context); + + MD5Update(&context, + (unsigned char *)&path->device->inq_data, + sizeof(struct scsi_inquiry_data)); + + if (have_serialnum) + MD5Update(&context, serial_buf->serial_num, + serial_buf->length); + + MD5Final(digest, &context); + if (bcmp(softc->digest, digest, 16) == 0) + changed = 0; + + /* + * XXX Do we need to do a TUR in order to ensure + * that the device really hasn't changed??? + */ + if ((changed != 0) + && ((softc->flags & PROBE_NO_ANNOUNCE) == 0)) + xpt_async(AC_LOST_DEVICE, path, NULL); + } + if (serial_buf != NULL) + free(serial_buf, M_TEMP); + + if (changed != 0) { + /* + * Now that we have all the necessary + * information to safely perform transfer + * negotiations... Controllers don't perform + * any negotiation or tagged queuing until + * after the first XPT_SET_TRAN_SETTINGS ccb is + * received. So, on a new device, just retreive + * the user settings, and set them as the current + * settings to set the device up. + */ + done_ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS; + done_ccb->cts.flags = CCB_TRANS_USER_SETTINGS; + xpt_action(done_ccb); + done_ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS; + done_ccb->cts.flags &= ~CCB_TRANS_USER_SETTINGS; + done_ccb->cts.flags |= CCB_TRANS_CURRENT_SETTINGS; + xpt_action(done_ccb); + xpt_release_ccb(done_ccb); + + /* + * Perform a TUR to allow the controller to + * perform any necessary transfer negotiation. + */ + softc->action = PROBE_TUR_FOR_NEGOTIATION; + xpt_schedule(periph, priority); + return; + } + xpt_release_ccb(done_ccb); + break; + } + case PROBE_TUR_FOR_NEGOTIATION: + if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) { + /* Don't wedge the queue */ + xpt_release_devq(done_ccb->ccb_h.path->device, + /*run_queue*/TRUE); + } + + path->device->flags &= ~CAM_DEV_UNCONFIGURED; + + if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { + /* Inform the XPT that a new device has been found */ + done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action(done_ccb); + + xpt_async(AC_FOUND_DEVICE, xpt_periph->path, done_ccb); + } + xpt_release_ccb(done_ccb); + break; + } + done_ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs); + TAILQ_REMOVE(&softc->request_ccbs, &done_ccb->ccb_h, periph_links.tqe); + done_ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(done_ccb); + if (TAILQ_FIRST(&softc->request_ccbs) == NULL) { + cam_periph_invalidate(periph); + cam_periph_release(periph); + } else { + probeschedule(periph); + } +} + +static void +probecleanup(struct cam_periph *periph) +{ + free(periph->softc, M_TEMP); +} + +static void +xpt_find_quirk(struct cam_ed *device) +{ + caddr_t match; + + match = cam_quirkmatch((caddr_t)&device->inq_data, + (caddr_t)xpt_quirk_table, + sizeof(xpt_quirk_table)/sizeof(*xpt_quirk_table), + sizeof(*xpt_quirk_table), scsi_inquiry_match); + + if (match == NULL) + panic("xpt_find_quirk: device didn't match wildcard entry!!"); + + device->quirk = (struct xpt_quirk_entry *)match; +} + +static void +xpt_set_transfer_settings(struct ccb_trans_settings *cts, int async_update) +{ + struct cam_ed *device; + struct cam_sim *sim; + int qfrozen; + + device = cts->ccb_h.path->device; + sim = cts->ccb_h.path->bus->sim; + if (async_update == FALSE) { + struct scsi_inquiry_data *inq_data; + struct ccb_pathinq cpi; + + /* + * Perform sanity checking against what the + * controller and device can do. + */ + xpt_setup_ccb(&cpi.ccb_h, cts->ccb_h.path, /*priority*/1); + cpi.ccb_h.func_code = XPT_PATH_INQ; + xpt_action((union ccb *)&cpi); + + inq_data = &device->inq_data; + if ((inq_data->flags & SID_Sync) == 0 + || (cpi.hba_inquiry & PI_SDTR_ABLE) == 0) { + /* Force async */ + cts->sync_period = 0; + cts->sync_offset = 0; + } + + switch (cts->bus_width) { + case MSG_EXT_WDTR_BUS_32_BIT: + if ((inq_data->flags & SID_WBus32) != 0 + && (cpi.hba_inquiry & PI_WIDE_32) != 0) + break; + /* Fall Through to 16-bit */ + case MSG_EXT_WDTR_BUS_16_BIT: + if ((inq_data->flags & SID_WBus16) != 0 + && (cpi.hba_inquiry & PI_WIDE_16) != 0) { + cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; + break; + } + /* Fall Through to 8-bit */ + default: /* New bus width?? */ + case MSG_EXT_WDTR_BUS_8_BIT: + /* All targets can do this */ + cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; + break; + } + + if ((cts->flags & CCB_TRANS_DISC_ENB) == 0) { + /* + * Can't tag queue without disconnection. + */ + cts->flags &= ~CCB_TRANS_TAG_ENB; + } + + if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0 + || (inq_data->flags & SID_CmdQue) == 0 + || (device->queue_flags & SCP_QUEUE_DQUE) != 0 + || (device->quirk->mintags == 0)) { + /* + * Can't tag on hardware that doesn't support, + * doesn't have it enabled, or has broken tag support. + */ + cts->flags &= ~CCB_TRANS_TAG_ENB; + } + } + + /* + * If we are transitioning from tags to no-tags or + * vice-versa, we need to carefully freeze and restart + * the queue so that we don't overlap tagged and non-tagged + * commands. + */ + if ((cts->valid & CCB_TRANS_TQ_VALID) != 0 + && (((cts->flags & CCB_TRANS_TAG_ENB) != 0 + && (device->inq_flags & SID_CmdQue) == 0) + || ((cts->flags & CCB_TRANS_TAG_ENB) == 0 + && (device->inq_flags & SID_CmdQue) != 0))) { + int newopenings; + + xpt_freeze_devq(cts->ccb_h.path, /*count*/1); + qfrozen = TRUE; + + if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { + newopenings = min(device->quirk->maxtags, + sim->max_tagged_dev_openings); + device->inq_flags |= SID_CmdQue; + } else { + newopenings = sim->max_dev_openings; + device->inq_flags &= ~SID_CmdQue; + } + xpt_dev_ccbq_resize(cts->ccb_h.path, newopenings); + } else { + qfrozen = FALSE; + } + + if (async_update == FALSE) + (*(sim->sim_action))(sim, (union ccb *)cts); + + if (qfrozen) { + struct ccb_relsim crs; + + xpt_setup_ccb(&crs.ccb_h, cts->ccb_h.path, + /*priority*/1); + crs.ccb_h.func_code = XPT_REL_SIMQ; + crs.release_flags = RELSIM_RELEASE_AFTER_QEMPTY; + crs.openings + = crs.release_timeout + = crs.qfrozen_cnt + = 0; + xpt_action((union ccb *)&crs); + } +} + +static int busses_to_config; + +static int +xptconfigbuscountfunc(struct cam_eb *bus, void *arg) +{ + if (bus->path_id != CAM_XPT_PATH_ID) + busses_to_config++; + + return(1); +} + +static int +xptconfigfunc(struct cam_eb *bus, void *arg) +{ + struct cam_path *path; + union ccb *work_ccb; + + if (bus->path_id != CAM_XPT_PATH_ID) { + cam_status status; + + work_ccb = xpt_alloc_ccb(); + if ((status = xpt_create_path(&path, xpt_periph, bus->path_id, + CAM_TARGET_WILDCARD, + CAM_LUN_WILDCARD)) !=CAM_REQ_CMP){ + printf("xptconfigfunc: xpt_create_path failed with " + "status %#x for bus %d\n", status, bus->path_id); + printf("xptconfigfunc: halting bus configuration\n"); + xpt_free_ccb(work_ccb); + return(0); + } + xpt_setup_ccb(&work_ccb->ccb_h, path, /*priority*/1); + work_ccb->ccb_h.func_code = XPT_RESET_BUS; + work_ccb->ccb_h.cbfcnp = NULL; + CAM_DEBUG(path, CAM_DEBUG_SUBTRACE, + ("Resetting Bus\n")); + xpt_action(work_ccb); + xpt_finishconfig(xpt_periph, work_ccb); + } + + return(1); + +} + +static void +xpt_config(void *arg) +{ + /* Now that interrupts are enabled, go find our devices */ + struct cam_eb *bus; + +#ifdef CAMDEBUG + /* Setup debugging flags and path */ +#ifdef CAM_DEBUG_FLAGS + cam_dflags = CAM_DEBUG_FLAGS; +#else /* !CAM_DEBUG_FLAGS */ + cam_dflags = CAM_DEBUG_NONE; +#endif /* CAM_DEBUG_FLAGS */ +#ifdef CAM_DEBUG_BUS + if (cam_dflags != CAM_DEBUG_NONE) { + if (xpt_create_path(&cam_dpath, xpt_periph, + CAM_DEBUG_BUS, CAM_DEBUG_TARGET, + CAM_DEBUG_LUN) != CAM_REQ_CMP) { + printf("xpt_config: xpt_create_path() failed for debug" + " target %d:%d:%d, debugging disabled\n", + CAM_DEBUG_BUS, CAM_DEBUG_TARGET, CAM_DEBUG_LUN); + cam_dflags = CAM_DEBUG_NONE; + } + } else + cam_dpath = NULL; +#else /* !CAM_DEBUG_BUS */ + cam_dpath = NULL; +#endif /* CAM_DEBUG_BUS */ +#endif /* CAMDEBUG */ + + /* Scan all installed busses */ + xpt_for_all_busses(xptconfigbuscountfunc, NULL); + + xpt_for_all_busses(xptconfigfunc, NULL); + + /* Call xpt_finishconfig once in case we dodn't have any busses */ + xpt_finishconfig(xpt_periph, NULL); +} + +static int +xptfinishconfigfunc(struct cam_ed *device, void *arg) +{ + union ccb *done_ccb; + cam_status status; + + done_ccb = (union ccb *)arg; + + if ((status = xpt_create_path(&done_ccb->ccb_h.path, + xpt_periph, device->target->bus->path_id, + device->target->target_id, + device->lun_id)) != CAM_REQ_CMP) { + printf("xptfinishconfig: xpt_create_path failed with status" + " %#x, halting bus configuration\n"); + return(0); + } + + xpt_setup_ccb(&done_ccb->ccb_h, + done_ccb->ccb_h.path, + /*priority*/1); + + done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action(done_ccb); + xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); + + return(1); +} + +/* + * If the given device only has one peripheral attached to it, and if that + * peripheral is the passthrough driver, announce it. This insures that the + * user sees some sort of announcement for every peripheral in their system. + */ +static int +xptpassannouncefunc(struct cam_ed *device, void *arg) +{ + struct cam_periph *periph; + int i; + + for (periph = SLIST_FIRST(&device->periphs), i = 0; periph != NULL; + periph = SLIST_NEXT(periph, periph_links), i++); + + periph = SLIST_FIRST(&device->periphs); + if ((i == 1) + && (strncmp(periph->periph_name, "pass", 4) == 0)) + xpt_announce_periph(periph, NULL); + + return(1); +} + +static void +xpt_finishconfig(struct cam_periph *periph, union ccb *done_ccb) +{ + struct periph_driver **p_drv; + struct cam_eb *bus; + struct cam_et *target; + struct cam_ed *dev; + struct cam_periph *nperiph; + struct periph_list *periph_head; + int i; + + if (done_ccb != NULL) { + CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("xpt_finishconfig\n")); + switch(done_ccb->ccb_h.func_code) { + case XPT_RESET_BUS: + if (done_ccb->ccb_h.status == CAM_REQ_CMP) { + done_ccb->ccb_h.func_code = XPT_SCAN_BUS; + done_ccb->ccb_h.cbfcnp = xpt_finishconfig; + xpt_action(done_ccb); + return; + } + /* FALLTHROUGH */ + case XPT_SCAN_BUS: + xpt_free_path(done_ccb->ccb_h.path); + busses_to_config--; + break; + default: + break; + } + } + + if (busses_to_config == 0) { + /* Register all the peripheral drivers */ + /* XXX This will have to change when we have LKMs */ + p_drv = (struct periph_driver **)periphdriver_set.ls_items; + for (i = 0; p_drv[i] != NULL; i++) { + (*p_drv[i]->init)(); + } + + /* + * Itterate through our devices announcing + * them in probed bus order. + */ + xpt_for_all_devices(xptfinishconfigfunc, done_ccb); + + /* + * Check for devices with no "standard" peripheral driver + * attached. For any devices like that, announce the + * passthrough driver so the user will see something. + */ + xpt_for_all_devices(xptpassannouncefunc, NULL); + + /* Release our hook so that the boot can continue. */ + config_intrhook_disestablish(xpt_config_hook); + } + if (done_ccb != NULL) + xpt_free_ccb(done_ccb); +} + +static void +xptaction(struct cam_sim *sim, union ccb *work_ccb) +{ + CAM_DEBUG(work_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("xptaction\n")); + + switch (work_ccb->ccb_h.func_code) { + /* Common cases first */ + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi; + + cpi = &work_ccb->cpi; + cpi->version_num = 1; /* XXX??? */ + cpi->hba_inquiry = 0; + cpi->target_sprt = 0; + cpi->hba_misc = 0; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; + cpi->max_lun = 0; + cpi->initiator_id = 0; + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "", HBA_IDLEN); + strncpy(cpi->dev_name, sim->sim_name, DEV_IDLEN); + cpi->unit_number = sim->unit_number; + cpi->bus_id = sim->bus_id; + cpi->ccb_h.status = CAM_REQ_CMP; + xpt_done(work_ccb); + break; + } + default: + work_ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(work_ccb); + break; + } +} + +/* + * Should only be called by the machine interrupt dispatch routines, + * so put these prototypes here instead of in the header. + * + * XXX we should really have a way to dynamically register SWI handlers. + */ + +void +swi_camnet() +{ + camisr(&cam_netq); +} + +void +swi_cambio() +{ + camisr(&cam_bioq); +} + +static void +camisr(cam_isrq_t *queue) +{ + int s; + struct ccb_hdr *ccb_h; + + s = splcam(); + while ((ccb_h = TAILQ_FIRST(queue)) != NULL) { + int runq; + + TAILQ_REMOVE(queue, ccb_h, sim_links.tqe); + ccb_h->pinfo.index = CAM_UNQUEUED_INDEX; + splx(s); + + CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE, + ("camisr")); + + runq = FALSE; + + if (ccb_h->flags & CAM_HIGH_POWER) { + struct highpowerlist *hphead; + struct cam_ed *device; + union ccb *send_ccb; + + hphead = &highpowerq; + + send_ccb = (union ccb *)STAILQ_FIRST(hphead); + + /* + * Increment the count since this command is done. + */ + num_highpower++; + + /* + * Any high powered commands queued up? + */ + if (send_ccb != NULL) { + device = send_ccb->ccb_h.path->device; + + STAILQ_REMOVE_HEAD(hphead, xpt_links.stqe); + + xpt_release_devq(send_ccb->ccb_h.path->device, + TRUE); + } + } + if ((ccb_h->func_code != XPT_ACCEPT_TARGET_IO) + && (ccb_h->func_code != XPT_SCAN_LUN) + && (ccb_h->func_code != XPT_SCAN_BUS)) { + struct cam_ed *dev; + + dev = ccb_h->path->device; + + s = splcam(); + cam_ccbq_ccb_done(&dev->ccbq, (union ccb *)ccb_h); + + ccb_h->path->bus->sim->devq->send_active--; + ccb_h->path->bus->sim->devq->send_openings++; + splx(s); + + if ((dev->flags & CAM_DEV_REL_ON_COMPLETE) != 0 + || ((dev->flags & CAM_DEV_REL_ON_QUEUE_EMPTY) != 0 + && (dev->ccbq.dev_active == 0))) { + + xpt_release_devq(ccb_h->path->device, + /*run_queue*/TRUE); + } + + if ((dev->ccbq.queue.entries > 0) + && (dev->qfrozen_cnt == 0) + && (device_is_send_queued(dev) == 0)) { + runq = xpt_schedule_dev_sendq(ccb_h->path->bus, + dev); + } + } + + if (ccb_h->status & CAM_RELEASE_SIMQ) { + xpt_release_simq(ccb_h->path->bus->sim, + /*run_queue*/TRUE); + } else if ((ccb_h->flags & CAM_DEV_QFRZDIS) + && (ccb_h->status & CAM_DEV_QFRZN)) { + xpt_release_devq(ccb_h->path->device, + /*run_queue*/TRUE); + ccb_h->status &= ~CAM_DEV_QFRZN; + } else if (runq) { + xpt_run_dev_sendq(ccb_h->path->bus); + } + + /* Call the peripheral driver's callback */ + (*ccb_h->cbfcnp)(ccb_h->path->periph, + (union ccb *)ccb_h); + + /* Raise IPL for while test */ + s = splcam(); + } + splx(s); +} diff --git a/sys/cam/cam_xpt.h b/sys/cam/cam_xpt.h new file mode 100644 index 000000000000..0ed413d9dfa6 --- /dev/null +++ b/sys/cam/cam_xpt.h @@ -0,0 +1,75 @@ + /* + * Data structures and definitions for dealing with the + * Common Access Method Transport (xpt) layer. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_XPT_H +#define _CAM_CAM_XPT_H 1 + +/* Forward Declarations */ +union ccb; +struct cam_periph; +struct cam_sim; + +/* + * Definition of a CAM path. Paths are created from bus, target, and lun ids + * via xpt_create_path and allow for reference to devices without recurring + * lookups in the edt. + */ +struct cam_path; + +/* Path functions */ + +#ifdef KERNEL + +void xpt_action(union ccb *new_ccb); +void xpt_setup_ccb(struct ccb_hdr *ccb_h, + struct cam_path *path, + u_int32_t priority); +void xpt_merge_ccb(union ccb *master_ccb, + union ccb *slave_ccb); +cam_status xpt_create_path(struct cam_path **new_path_ptr, + struct cam_periph *perph, + path_id_t path_id, + target_id_t target_id, lun_id_t lun_id); +void xpt_free_path(struct cam_path *path); +int xpt_path_comp(struct cam_path *path1, + struct cam_path *path2); +void xpt_print_path(struct cam_path *path); +path_id_t xpt_path_path_id(struct cam_path *path); +target_id_t xpt_path_target_id(struct cam_path *path); +lun_id_t xpt_path_lun_id(struct cam_path *path); +struct cam_sim *xpt_path_sim(struct cam_path *path); +struct cam_periph *xpt_path_periph(struct cam_path *path); +void xpt_async(u_int32_t async_code, struct cam_path *path, + void *async_arg); +#endif /* KERNEL */ + +#endif /* _CAM_CAM_XPT_H */ + diff --git a/sys/cam/cam_xpt_periph.h b/sys/cam/cam_xpt_periph.h new file mode 100644 index 000000000000..ee64e213ae94 --- /dev/null +++ b/sys/cam/cam_xpt_periph.h @@ -0,0 +1,51 @@ + /* + * Data structures and definitions for dealing with the + * Common Access Method Transport (xpt) layer from peripheral + * drivers. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_XPT_PERIPH_H +#define _CAM_CAM_XPT_PERIPH_H 1 + +#include + +/* Functions accessed by the peripheral drivers */ +#ifdef KERNEL +void xpt_polled_action(union ccb *ccb); +union ccb *xpt_alloc_ccb(void); +void xpt_free_ccb(union ccb *free_ccb); +void xpt_release_ccb(union ccb *released_ccb); +void xpt_schedule(struct cam_periph *perph, u_int32_t new_priority); +int32_t xpt_add_periph(struct cam_periph *periph); +void xpt_remove_periph(struct cam_periph *periph); +void xpt_announce_periph(struct cam_periph *periph, + char *announce_string); +#endif /* KERNEL */ + +#endif /* _CAM_CAM_XPT_PERIPH_H */ diff --git a/sys/cam/cam_xpt_sim.h b/sys/cam/cam_xpt_sim.h new file mode 100644 index 000000000000..e19a8696bb61 --- /dev/null +++ b/sys/cam/cam_xpt_sim.h @@ -0,0 +1,50 @@ + /* + * Data structures and definitions for dealing with the + * Common Access Method Transport (xpt) layer. + * + * Copyright (c) 1997 Justin T. Gibbs. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id$ + */ + +#ifndef _CAM_CAM_XPT_SIM_H +#define _CAM_CAM_XPT_SIM_H 1 + +#include +#include + +/* Functions accessed by SIM drivers */ +#ifdef KERNEL +int32_t xpt_bus_register(struct cam_sim *sim, u_int32_t bus); +int32_t xpt_bus_deregister(u_int8_t path_id); +u_int32_t xpt_freeze_simq(struct cam_sim *sim, u_int count); +void xpt_release_simq(struct cam_sim *sim, int run_queue); +u_int32_t xpt_freeze_devq(struct cam_path *path, u_int count); +void xpt_release_devq(struct cam_ed *dev, int run_queue); +void xpt_done(union ccb *done_ccb); +#endif /* KERNEL */ + +#endif /* _CAM_CAM_XPT_SIM_H */ +