diff --git a/sys/cam/scsi/scsi_ses.c b/sys/cam/scsi/scsi_ses.c new file mode 100644 index 00000000000..c95eb6cad3c --- /dev/null +++ b/sys/cam/scsi/scsi_ses.c @@ -0,0 +1,557 @@ +/* $FreeBSD$ */ +/* + * Copyright (c) 2000 Matthew Jacob + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define QFRLS(ccb) \ + if (((ccb)->ccb_h.status & CAM_DEV_QFRZN) != 0) \ + cam_release_devq((ccb)->ccb_h.path, 0, 0, 0, FALSE) + +#define ccb_state ppriv_field0 +#define ccb_bp ppriv_ptr1 + +struct ses_softc { + enctyp ses_type; /* type of enclosure */ + encvec ses_vec; /* vector to handlers */ + u_int32_t ses_nobjects; /* number of objects */ + void * ses_private; /* private data */ + encobj * ses_objmap; /* objects */ + u_int8_t ses_encstat; /* overall status */ + u_int8_t ses_flags; + union ccb ses_saved_ccb; + dev_t ses_dev; +}; +#define SES_FLAG_INVALID 0x01 +#define SES_FLAG_OPEN 0x02 + +#define SESUNIT(x) (minor((x))) +#define SES_CDEV_MAJOR 110 + +typedef enum { + SES_CCB_BUFFER_IO, + SES_CCB_WAITING +} ses_ccb_types; +#define ccb_type ppriv_field0 + +static d_open_t sesopen; +static d_close_t sesclose; +static d_ioctl_t sesioctl; +static periph_init_t sesinit; +static periph_ctor_t sesregister; +static periph_oninv_t sesoninvalidate; +static periph_dtor_t sescleanup; +static periph_start_t sesstart; + +static void sesasync(void *, u_int32_t, struct cam_path *, void *); +static void sesdone(struct cam_periph *, union ccb *); +static int seserror(union ccb *, u_int32_t, u_int32_t); + +static struct periph_driver sesdriver = { + sesinit, "ses", + TAILQ_HEAD_INITIALIZER(sesdriver.units), /* generation */ 0 +}; + +DATA_SET(periphdriver_set, sesdriver); + +static struct cdevsw ses_cdevsw = +{ + /* open */ sesopen, + /* close */ sesclose, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ sesioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "ses", + /* maj */ SES_CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ 0, + /* bmaj */ -1 +}; +static struct extend_array *sesperiphs; + +void +sesinit(void) +{ + cam_status status; + struct cam_path *path; + + /* + * Create our extend array for storing the devices we attach to. + */ + sesperiphs = cam_extend_new(); + if (sesperiphs == NULL) { + printf("ses: Failed to alloc extend array!\n"); + return; + } + + /* + * Install a global async callback. This callback will + * receive async callbacks like "new device found". + */ + status = xpt_create_path(&path, NULL, CAM_XPT_PATH_ID, + CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); + + if (status == CAM_REQ_CMP) { + struct ccb_setasync csa; + + xpt_setup_ccb(&csa.ccb_h, path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_FOUND_DEVICE; + csa.callback = sesasync; + csa.callback_arg = NULL; + xpt_action((union ccb *)&csa); + status = csa.ccb_h.status; + xpt_free_path(path); + } + + if (status != CAM_REQ_CMP) { + printf("ses: Failed to attach master async callback " + "due to status 0x%x!\n", status); + } +} + +static void +sesoninvalidate(struct cam_periph *periph) +{ + struct ses_softc *softc; + struct ccb_setasync csa; + + softc = (struct ses_softc *)periph->softc; + + /* + * Unregister any async callbacks. + */ + xpt_setup_ccb(&csa.ccb_h, periph->path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = 0; + csa.callback = sesasync; + csa.callback_arg = periph; + xpt_action((union ccb *)&csa); + + softc->ses_flags |= SES_FLAG_INVALID; + + xpt_print_path(periph->path); + printf("lost device\n"); +} + +static void +sescleanup(struct cam_periph *periph) +{ + struct ses_softc *softc; + + softc = (struct ses_softc *)periph->softc; + + destroy_dev(softc->ses_dev); + + cam_extend_release(sesperiphs, periph->unit_number); + xpt_print_path(periph->path); + printf("removing device entry\n"); + free(softc, M_DEVBUF); +} + +static void +sesasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) +{ + struct cam_periph *periph; + + periph = (struct cam_periph *)callback_arg; + + switch(code) { + case AC_FOUND_DEVICE: + { + struct ccb_getdev *cgd; + cam_status status; + + cgd = (struct ccb_getdev *)arg; + /* + * PROBLEM: WE NEED TO LOOK AT BYTES 48-53 TO SEE IF THIS IS + * PROBLEM: IS A SAF-TE DEVICE. + */ + + switch (ses_type(&cgd->inq_data, sizeof (cgd->inq_data))) { + case SES_SES: + case SES_SEN: + case SES_SAFT: + break; + default: + return; + } + + status = cam_periph_alloc(sesregister, sesoninvalidate, + sescleanup, sesstart, "ses", CAM_PERIPH_BIO, + cgd->ccb_h.path, sesasync, AC_FOUND_DEVICE, cgd); + + if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { + printf("sesasync: Unable to probe new device due to " + "status 0x%x\n", status); + } + break; + } + default: + cam_periph_async(periph, code, path, arg); + break; + } +} + +static cam_status +sesregister(struct cam_periph *periph, void *arg) +{ + struct ses_softc *softc; + struct ccb_setasync csa; + struct ccb_getdev *cgd; + char *tname; + + cgd = (struct ccb_getdev *)arg; + if (periph == NULL) { + printf("chregister: periph was NULL!!\n"); + return (CAM_REQ_CMP_ERR); + } + + if (cgd == NULL) { + printf("chregister: no getdev CCB, can't register device\n"); + return (CAM_REQ_CMP_ERR); + } + + softc = malloc(sizeof (struct ses_softc), M_DEVBUF, M_NOWAIT); + if (softc == NULL) { + printf("sesregister: Unable to probe new device. " + "Unable to allocate softc\n"); + return (CAM_REQ_CMP_ERR); + } + bzero(softc, sizeof (struct ses_softc)); + + + softc->ses_type = ses_type(&cgd->inq_data, sizeof (cgd->inq_data)); + + switch (softc->ses_type) { + case SES_SES_SCSI2: + case SES_SES: + case SES_SES_PASSTHROUGH: + case SES_SEN: + case SES_SAFT: + break; + case SES_NONE: + default: + free(softc, M_DEVBUF); + return (CAM_REQ_CMP_ERR); + } + + periph->softc = softc; + cam_extend_set(sesperiphs, periph->unit_number, periph); + + softc->ses_dev = make_dev(&ses_cdevsw, periph->unit_number, + UID_ROOT, GID_OPERATOR, 0600, "%s%d", + periph->periph_name, periph->unit_number); + + /* + * Add an async callback so that we get + * notified if this device goes away. + */ + xpt_setup_ccb(&csa.ccb_h, periph->path, 5); + csa.ccb_h.func_code = XPT_SASYNC_CB; + csa.event_enable = AC_LOST_DEVICE; + csa.callback = sesasync; + csa.callback_arg = periph; + xpt_action((union ccb *)&csa); + + /* + * Lock this peripheral until we are setup. + * This first call can't block + */ + (void)cam_periph_lock(periph, PRIBIO); + xpt_schedule(periph, 5); + + switch (softc->ses_type) { + default: + case SES_NONE: + tname = "No SES device"; + break; + case SES_SES_SCSI2: + tname = "SCSI-2 SES Device"; + break; + case SES_SES: + tname = "SCSI-3 SES Device"; + break; + case SES_SES_PASSTHROUGH: + tname = "SES Passthrough Device"; + break; + case SES_SEN: + tname = "Unisys SEN Device"; + break; + case SES_SAFT: + tname = "SAF-TE Compliant Device"; + break; + } + xpt_announce_periph(periph, tname); + return (CAM_REQ_CMP); +} + +static int +sesopen(dev_t dev, int flags, int fmt, struct proc *p) +{ + struct cam_periph *periph; + struct ses_softc *softc; + int error, s; + + periph = cam_extend_get(sesperiphs, SESUNIT(dev)); + if (periph == NULL) + return (ENXIO); + + softc = (struct ses_softc *)periph->softc; + + s = splsoftcam(); + if (softc->ses_flags & SES_FLAG_INVALID) { + splx(s); + return (ENXIO); + } + if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { + splx(s); + return (error); + } + splx(s); + if ((softc->ses_flags & SES_FLAG_OPEN) == 0) { + if (cam_periph_acquire(periph) != CAM_REQ_CMP) + return (ENXIO); + softc->ses_flags |= SES_FLAG_OPEN; + } + cam_periph_unlock(periph); + return (error); +} + +static int +sesclose(dev_t dev, int flag, int fmt, struct proc *p) +{ + struct cam_periph *periph; + struct ses_softc *softc; + int unit, error; + + error = 0; + + unit = SESUNIT(dev); + periph = cam_extend_get(sesperiphs, unit); + if (periph == NULL) + return (ENXIO); + + softc = (struct ses_softc *)periph->softc; + + if ((error = cam_periph_lock(periph, PRIBIO)) != 0) + return (error); + + softc->ses_flags &= ~SES_FLAG_OPEN; + + cam_periph_unlock(periph); + cam_periph_release(periph); + + return (0); +} + +static void +sesstart(struct cam_periph *periph, union ccb *start_ccb) +{ + int s; + + s = splbio(); + if (periph->immediate_priority <= periph->pinfo.priority) { + start_ccb->ccb_h.ccb_type = SES_CCB_WAITING; + + SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, + periph_links.sle); + periph->immediate_priority = CAM_PRIORITY_NONE; + splx(s); + wakeup(&periph->ccb_list); + } else { + splx(s); + } +} + +static void +sesdone(struct cam_periph *periph, union ccb *done_ccb) +{ + struct ses_softc *softc; + struct ccb_scsiio *csio; + + softc = (struct ses_softc *)periph->softc; + csio = &done_ccb->csio; + + switch (done_ccb->ccb_h.ccb_type) { + case SES_CCB_WAITING: + { + /* Caller will release the CCB */ + wakeup(&done_ccb->ccb_h.cbfcnp); + return; + } + default: + break; + } + xpt_release_ccb(done_ccb); +} + +static int +seserror(union ccb *ccb, u_int32_t cflags, u_int32_t sflags) +{ + struct ses_softc *softc; + struct cam_periph *periph; + + periph = xpt_path_periph(ccb->ccb_h.path); + softc = (struct ses_softc *)periph->softc; + + return (cam_periph_error(ccb, cflags, sflags, &softc->ses_saved_ccb)); +} + +static int +sesioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) +{ + struct cam_periph *periph; + struct ses_softc *softc; + u_int8_t unit; + int error; + + unit = SESUNIT(dev); + + periph = cam_extend_get(sesperiphs, unit); + if (periph == NULL) + return (ENXIO); + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering sesioctl\n")); + + softc = (struct ses_softc *)periph->softc; + + error = 0; + + CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, + ("trying to do ioctl %#lx\n", cmd)); + + /* + * If this command can change the device's state, we must + * have the device open for writing. + */ + switch (cmd) { + case SESIOC_GETNOBJ: + case SESIOC_GETOBJMAP: + case SESIOC_GETENCSTAT: + case SESIOC_GETOBJSTAT: + break; + default: + if ((flag & FWRITE) == 0) + return (EBADF); + } + + switch (cmd) { + case SESIOC_GETNOBJ: + case SESIOC_GETOBJMAP: + case SESIOC_GETENCSTAT: + case SESIOC_SETENCSTAT: + case SESIOC_GETOBJSTAT: + case SESIOC_SETOBJSTAT: + case SESIOC_INIT: + error = EINVAL; + break; + default: + error = cam_periph_ioctl(periph, cmd, addr, seserror); + break; + } + return (error); +} + +/* + * Is this a device that supports enclosure services? + * + * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's + * an SES device. If it happens to be an old UNISYS SEN device, we can + * handle that too. + */ + +enctyp +ses_type(void *buf, int buflen) +{ + unsigned char *iqd = buf; + + if (buflen < 32) + return (SES_NONE); + + if ((iqd[0] & 0x1f) == SES_DEVICE_TYPE) { + if (strncmp(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { + return (SES_SEN); + } else if ((iqd[2] & 0x7) > 2) { + return (SES_SES); + } else { + return (SES_SES_SCSI2); + } + return (SES_NONE); + } + +#ifdef SES_ENABLE_PASSTHROUGH + if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { + /* + * PassThrough Device. + */ + return (SES_SES_PASSTHRU); + } +#endif + + if (buflen < 47) { + return (SES_NONE); + } + /* + * The comparison is short for a reason- some vendors were chopping + * it short. + */ + if (strncmp((char *)&iqd[44], "SAF-TE", 4) == 0) { + return (SES_SAFT); + } + return (SES_NONE); +} diff --git a/sys/cam/scsi/scsi_ses.h b/sys/cam/scsi/scsi_ses.h new file mode 100644 index 00000000000..d794dca4f2c --- /dev/null +++ b/sys/cam/scsi/scsi_ses.h @@ -0,0 +1,206 @@ +/* $FreeBSD$ */ +/* + * Copyright (c) 2000 by Matthew Jacob + * 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. + * + * Alternatively, this software may be distributed under the terms of the + * the GNU Public License ("GPL"). + * + * 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. + * + */ + +#define SESIOC (('s' - 040) << 8) +#define SESIOC_GETNOBJ _IOR(SESIOC, 1, unsigned int) +#define SESIOC_GETOBJMAP _IOW(SESIOC, 2, void *) +#define SESIOC_GETENCSTAT _IOR(SESIOC, 3, ses_encstat) +#define SESIOC_SETENCSTAT _IOW(SESIOC, 4, ses_encstat) +#define SESIOC_GETOBJSTAT _IOR(SESIOC, 5, ses_objstat) +#define SESIOC_SETOBJSTAT _IOW(SESIOC, 6, ses_objstat) +#define SESIOC_INIT _IO(SESIOC, 7) + +/* + * Platform Independent Definitions for SES devices. + */ +/* + * SCSI Based Environmental Services Application Defines + * + * Based almost entirely on SCSI-3 SES Revision 8A specification, + * but slightly abstracted as the underlying device may in fact + * be a SAF-TE or vendor unique device. + */ +/* + * SES Driver Operations: + * (The defines themselves are platform and access method specific) + * + * SESIOC_GETNOBJ + * SESIOC_GETOBJMAP + * SESIOC_GETENCSTAT + * SESIOC_SETENCSTAT + * SESIOC_GETOBJSTAT + * SESIOC_SETOBJSTAT + * SESIOC_INIT + * + * + * An application finds out how many objects an SES instance + * is managing by performing a SESIOC_GETNOBJ operation. It then + * performs a SESIOC_GETOBJMAP to get the map that contains the + * object identifiers for all objects (see ses_object below). + * This information is static. + * + * The application may perform SESIOC_GETOBJSTAT operations to retrieve + * status on an object (see the ses_objstat structure below), SESIOC_SETOBJSTAT + * operations to set status for an object. + * + * Similarly overall enclosure status me be fetched or set via + * SESIOC_GETENCSTAT or SESIOC_SETENCSTAT operations (see ses_encstat below). + * + * Readers should note that there is nothing that requires either a set + * or a clear operation to actually latch and do anything in the target. + * + * A SESIOC_INIT operation causes the enclosure to be initialized. + */ + +typedef struct { + unsigned int obj_id; /* Object Identifier */ + unsigned char subencid; /* SubEnclosure ID */ + unsigned char object_type; /* Object Type */ +} ses_object; + +/* Object Types */ +#define SESTYP_UNSPECIFIED 0x00 +#define SESTYP_DEVICE 0x01 +#define SESTYP_POWER 0x02 +#define SESTYP_FAN 0x03 +#define SESTYP_THERM 0x04 +#define SESTYP_DOORLOCK 0x05 +#define SESTYP_ALARM 0x06 +#define SESTYP_ESCC 0x07 /* Enclosure SCC */ +#define SESTYP_SCC 0x08 /* SCC */ +#define SESTYP_NVRAM 0x09 +#define SESTYP_UPS 0x0b +#define SESTYP_DISPLAY 0x0c +#define SESTYP_KEYPAD 0x0d +#define SESTYP_SCSIXVR 0x0f +#define SESTYP_LANGUAGE 0x10 +#define SESTYP_COMPORT 0x11 +#define SESTYP_VOM 0x12 +#define SESTYP_AMMETER 0x13 +#define SESTYP_SCSI_TGT 0x14 +#define SESTYP_SCSI_INI 0x15 +#define SESTYP_SUBENC 0x16 + +/* + * Overall Enclosure Status + */ +typedef unsigned char ses_encstat; +#define SES_ENCSTAT_UNRECOV 0x1 +#define SES_ENCSTAT_CRITICAL 0x2 +#define SES_ENCSTAT_NONCRITICAL 0x4 +#define SES_ENCSTAT_INFO 0x8 + +/* + * Object Status + */ +typedef struct { + unsigned int obj_id; + unsigned char cstat[4]; +} ses_objstat; + +/* Summary SES Status Defines, Common Status Codes */ +#define SES_OBJSTAT_UNSUPPORTED 0 +#define SES_OBJSTAT_OK 1 +#define SES_OBJSTAT_CRIT 2 +#define SES_OBJSTAT_NONCRIT 3 +#define SES_OBJSTAT_UNRECOV 4 +#define SES_OBJSTAT_NOTINSTALLED 5 +#define SES_OBJSTAT_UNKNOWN 6 +#define SES_OBJSTAT_NOTAVAIL 7 + +/* + * For control pages, cstat[0] is the same for the + * enclosure and is common across all device types. + * + * If SESCTL_CSEL is set, then PRDFAIL, DISABLE and RSTSWAP + * are checked, otherwise bits that are specific to the device + * type in the other 3 bytes of cstat or checked. + */ +#define SESCTL_CSEL 0x80 +#define SESCTL_PRDFAIL 0x40 +#define SESCTL_DISABLE 0x20 +#define SESCTL_RSTSWAP 0x10 + + +/* Control bits, Device Elements, byte 2 */ +#define SESCTL_DRVLCK 0x40 /* "DO NOT REMOVE" */ +#define SESCTL_RQSINS 0x08 /* RQST INSERT */ +#define SESCTL_RQSRMV 0x04 /* RQST REMOVE */ +#define SESCTL_RQSID 0x02 /* RQST IDENT */ +/* Control bits, Device Elements, byte 3 */ +#define SESCTL_RQSFLT 0x20 /* RQST FAULT */ +#define SESCTL_DEVOFF 0x10 /* DEVICE OFF */ + +/* Control bits, Generic, byte 3 */ +#define SESCTL_RQSTFAIL 0x40 +#define SESCTL_RQSTON 0x20 +/* + * Platform Independent Driver Internal Definitions for SES devices. + */ +typedef enum { + SES_NONE, + SES_SES_SCSI2, + SES_SES, + SES_SES_PASSTHROUGH, + SES_SEN, + SES_SAFT +} enctyp; + +#define SES_DEVICE_TYPE 0x0D + +#define SEN_ID "UNISYS SUN_SEN" +#define SEN_ID_LEN 24 + +struct ses_softc; +typedef struct ses_softc ses_softc_t; +typedef struct { + int (*softc_init)(ses_softc_t *, int); + int (*init_enc)(ses_softc_t *); + int (*get_encstat)(ses_softc_t *, ses_encstat *); + int (*set_encstat)(ses_softc_t *, ses_encstat *, int); + int (*get_objstat)(ses_softc_t *, ses_objstat *, int); + int (*set_objstat)(ses_softc_t *, ses_objstat *, int); +} encvec; + +#define ENCI_SVALID 0x80 + +typedef struct { + u_int32_t + enctype : 8, /* enclosure type */ + subenclosure : 8, /* subenclosure id */ + svalid : 1, /* enclosure information valid */ + priv : 15; /* private data, per object */ + u_int8_t encstat[4]; /* state && stats */ +} encobj; + +#if defined(_KERNEL) || defined(KERNEL) +enctyp ses_type __P((void *, int)); +#endif