scsi_enc.c revision 235911
1235911Smav/*- 2235911Smav * Copyright (c) 2000 Matthew Jacob 3235911Smav * All rights reserved. 4235911Smav * 5235911Smav * Redistribution and use in source and binary forms, with or without 6235911Smav * modification, are permitted provided that the following conditions 7235911Smav * are met: 8235911Smav * 1. Redistributions of source code must retain the above copyright 9235911Smav * notice, this list of conditions, and the following disclaimer, 10235911Smav * without modification, immediately at the beginning of the file. 11235911Smav * 2. The name of the author may not be used to endorse or promote products 12235911Smav * derived from this software without specific prior written permission. 13235911Smav * 14235911Smav * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235911Smav * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235911Smav * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235911Smav * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18235911Smav * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235911Smav * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235911Smav * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235911Smav * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235911Smav * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235911Smav * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235911Smav * SUCH DAMAGE. 25235911Smav */ 26235911Smav 27235911Smav#include <sys/cdefs.h> 28235911Smav__FBSDID("$FreeBSD: head/sys/cam/scsi/scsi_enc.c 235911 2012-05-24 14:07:44Z mav $"); 29235911Smav 30235911Smav#include <sys/param.h> 31235911Smav 32235911Smav#include <sys/conf.h> 33235911Smav#include <sys/errno.h> 34235911Smav#include <sys/fcntl.h> 35235911Smav#include <sys/kernel.h> 36235911Smav#include <sys/kthread.h> 37235911Smav#include <sys/lock.h> 38235911Smav#include <sys/malloc.h> 39235911Smav#include <sys/mutex.h> 40235911Smav#include <sys/queue.h> 41235911Smav#include <sys/sx.h> 42235911Smav#include <sys/systm.h> 43235911Smav#include <sys/sysctl.h> 44235911Smav#include <sys/types.h> 45235911Smav 46235911Smav#include <machine/stdarg.h> 47235911Smav 48235911Smav#include <cam/cam.h> 49235911Smav#include <cam/cam_ccb.h> 50235911Smav#include <cam/cam_debug.h> 51235911Smav#include <cam/cam_periph.h> 52235911Smav#include <cam/cam_xpt_periph.h> 53235911Smav 54235911Smav#include <cam/scsi/scsi_all.h> 55235911Smav#include <cam/scsi/scsi_message.h> 56235911Smav#include <cam/scsi/scsi_enc.h> 57235911Smav#include <cam/scsi/scsi_enc_internal.h> 58235911Smav 59235911Smav#include <opt_enc.h> 60235911Smav 61235911SmavMALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); 62235911Smav 63235911Smav/* Enclosure type independent driver */ 64235911Smav 65235911Smav#define SEN_ID "UNISYS SUN_SEN" 66235911Smav#define SEN_ID_LEN 24 67235911Smav 68235911Smavstatic d_open_t enc_open; 69235911Smavstatic d_close_t enc_close; 70235911Smavstatic d_ioctl_t enc_ioctl; 71235911Smavstatic periph_init_t enc_init; 72235911Smavstatic periph_ctor_t enc_ctor; 73235911Smavstatic periph_oninv_t enc_oninvalidate; 74235911Smavstatic periph_dtor_t enc_dtor; 75235911Smavstatic periph_start_t enc_start; 76235911Smav 77235911Smavstatic void enc_async(void *, uint32_t, struct cam_path *, void *); 78235911Smavstatic enctyp enc_type(struct ccb_getdev *); 79235911Smav 80235911SmavSYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0, 81235911Smav "CAM Enclosure Services driver"); 82235911Smav 83235911Smavstatic struct periph_driver encdriver = { 84235911Smav enc_init, "ses", 85235911Smav TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 86235911Smav}; 87235911Smav 88235911SmavPERIPHDRIVER_DECLARE(enc, encdriver); 89235911Smav 90235911Smavstatic struct cdevsw enc_cdevsw = { 91235911Smav .d_version = D_VERSION, 92235911Smav .d_open = enc_open, 93235911Smav .d_close = enc_close, 94235911Smav .d_ioctl = enc_ioctl, 95235911Smav .d_name = "ses", 96235911Smav .d_flags = 0, 97235911Smav}; 98235911Smav 99235911Smavstatic void 100235911Smavenc_init(void) 101235911Smav{ 102235911Smav cam_status status; 103235911Smav 104235911Smav /* 105235911Smav * Install a global async callback. This callback will 106235911Smav * receive async callbacks like "new device found". 107235911Smav */ 108235911Smav status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL); 109235911Smav 110235911Smav if (status != CAM_REQ_CMP) { 111235911Smav printf("enc: Failed to attach master async callback " 112235911Smav "due to status 0x%x!\n", status); 113235911Smav } 114235911Smav} 115235911Smav 116235911Smavstatic void 117235911Smavenc_oninvalidate(struct cam_periph *periph) 118235911Smav{ 119235911Smav struct enc_softc *enc; 120235911Smav 121235911Smav enc = periph->softc; 122235911Smav 123235911Smav enc->enc_flags |= ENC_FLAG_INVALID; 124235911Smav 125235911Smav /* If the sub-driver has an invalidate routine, call it */ 126235911Smav if (enc->enc_vec.softc_invalidate != NULL) 127235911Smav enc->enc_vec.softc_invalidate(enc); 128235911Smav 129235911Smav /* 130235911Smav * Unregister any async callbacks. 131235911Smav */ 132235911Smav xpt_register_async(0, enc_async, periph, periph->path); 133235911Smav 134235911Smav /* 135235911Smav * Shutdown our daemon. 136235911Smav */ 137235911Smav enc->enc_flags |= ENC_FLAG_SHUTDOWN; 138235911Smav if (enc->enc_daemon != NULL) { 139235911Smav /* Signal and wait for the ses daemon to terminate. */ 140235911Smav wakeup(enc->enc_daemon); 141235911Smav /* 142235911Smav * We're called with the SIM mutex held, but we're dropping 143235911Smav * the update mutex here on sleep. So we have to manually 144235911Smav * drop the SIM mutex. 145235911Smav */ 146235911Smav cam_periph_sleep(enc->periph, enc->enc_daemon, 147235911Smav PUSER, "thtrm", 0); 148235911Smav } 149235911Smav callout_drain(&enc->status_updater); 150235911Smav 151235911Smav xpt_print(periph->path, "lost device\n"); 152235911Smav} 153235911Smav 154235911Smavstatic void 155235911Smavenc_dtor(struct cam_periph *periph) 156235911Smav{ 157235911Smav struct enc_softc *enc; 158235911Smav 159235911Smav enc = periph->softc; 160235911Smav 161235911Smav xpt_print(periph->path, "removing device entry\n"); 162235911Smav cam_periph_unlock(periph); 163235911Smav destroy_dev(enc->enc_dev); 164235911Smav cam_periph_lock(periph); 165235911Smav 166235911Smav /* If the sub-driver has a cleanup routine, call it */ 167235911Smav if (enc->enc_vec.softc_cleanup != NULL) 168235911Smav enc->enc_vec.softc_cleanup(enc); 169235911Smav 170235911Smav if (enc->enc_boot_hold_ch.ich_func != NULL) { 171235911Smav config_intrhook_disestablish(&enc->enc_boot_hold_ch); 172235911Smav enc->enc_boot_hold_ch.ich_func = NULL; 173235911Smav } 174235911Smav 175235911Smav ENC_FREE(enc); 176235911Smav} 177235911Smav 178235911Smavstatic void 179235911Smavenc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 180235911Smav{ 181235911Smav struct cam_periph *periph; 182235911Smav 183235911Smav periph = (struct cam_periph *)callback_arg; 184235911Smav 185235911Smav switch(code) { 186235911Smav case AC_FOUND_DEVICE: 187235911Smav { 188235911Smav struct ccb_getdev *cgd; 189235911Smav cam_status status; 190235911Smav path_id_t path_id; 191235911Smav 192235911Smav cgd = (struct ccb_getdev *)arg; 193235911Smav if (arg == NULL) { 194235911Smav break; 195235911Smav } 196235911Smav 197235911Smav if (enc_type(cgd) == ENC_NONE) { 198235911Smav /* 199235911Smav * Schedule announcement of the ENC bindings for 200235911Smav * this device if it is managed by a SEP. 201235911Smav */ 202235911Smav path_id = xpt_path_path_id(path); 203235911Smav xpt_lock_buses(); 204235911Smav TAILQ_FOREACH(periph, &encdriver.units, unit_links) { 205235911Smav struct enc_softc *softc; 206235911Smav 207235911Smav softc = (struct enc_softc *)periph->softc; 208235911Smav if (xpt_path_path_id(periph->path) != path_id 209235911Smav || softc == NULL 210235911Smav || (softc->enc_flags & ENC_FLAG_INITIALIZED) 211235911Smav == 0 212235911Smav || softc->enc_vec.device_found == NULL) 213235911Smav continue; 214235911Smav 215235911Smav softc->enc_vec.device_found(softc); 216235911Smav } 217235911Smav xpt_unlock_buses(); 218235911Smav return; 219235911Smav } 220235911Smav 221235911Smav status = cam_periph_alloc(enc_ctor, enc_oninvalidate, 222235911Smav enc_dtor, enc_start, "ses", CAM_PERIPH_BIO, 223235911Smav cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd); 224235911Smav 225235911Smav if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { 226235911Smav printf("enc_async: Unable to probe new device due to " 227235911Smav "status 0x%x\n", status); 228235911Smav } 229235911Smav break; 230235911Smav } 231235911Smav default: 232235911Smav cam_periph_async(periph, code, path, arg); 233235911Smav break; 234235911Smav } 235235911Smav} 236235911Smav 237235911Smavstatic int 238235911Smavenc_open(struct cdev *dev, int flags, int fmt, struct thread *td) 239235911Smav{ 240235911Smav struct cam_periph *periph; 241235911Smav struct enc_softc *softc; 242235911Smav int error = 0; 243235911Smav 244235911Smav periph = (struct cam_periph *)dev->si_drv1; 245235911Smav if (periph == NULL) { 246235911Smav return (ENXIO); 247235911Smav } 248235911Smav 249235911Smav if (cam_periph_acquire(periph) != CAM_REQ_CMP) 250235911Smav return (ENXIO); 251235911Smav 252235911Smav cam_periph_lock(periph); 253235911Smav 254235911Smav softc = (struct enc_softc *)periph->softc; 255235911Smav 256235911Smav if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 257235911Smav error = ENXIO; 258235911Smav goto out; 259235911Smav } 260235911Smav if (softc->enc_flags & ENC_FLAG_INVALID) { 261235911Smav error = ENXIO; 262235911Smav goto out; 263235911Smav } 264235911Smav 265235911Smavout: 266235911Smav cam_periph_unlock(periph); 267235911Smav if (error) { 268235911Smav cam_periph_release(periph); 269235911Smav } 270235911Smav return (error); 271235911Smav} 272235911Smav 273235911Smavstatic int 274235911Smavenc_close(struct cdev *dev, int flag, int fmt, struct thread *td) 275235911Smav{ 276235911Smav struct cam_periph *periph; 277235911Smav struct enc_softc *softc; 278235911Smav 279235911Smav periph = (struct cam_periph *)dev->si_drv1; 280235911Smav if (periph == NULL) 281235911Smav return (ENXIO); 282235911Smav 283235911Smav cam_periph_lock(periph); 284235911Smav 285235911Smav softc = (struct enc_softc *)periph->softc; 286235911Smav 287235911Smav cam_periph_unlock(periph); 288235911Smav cam_periph_release(periph); 289235911Smav 290235911Smav return (0); 291235911Smav} 292235911Smav 293235911Smavstatic void 294235911Smavenc_start(struct cam_periph *p, union ccb *sccb) 295235911Smav{ 296235911Smav struct enc_softc *enc; 297235911Smav 298235911Smav enc = p->softc; 299235911Smav ENC_DLOG(enc, "%s enter imm=%d prio=%d\n", 300235911Smav __func__, p->immediate_priority, p->pinfo.priority); 301235911Smav if (p->immediate_priority <= p->pinfo.priority) { 302235911Smav SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); 303235911Smav p->immediate_priority = CAM_PRIORITY_NONE; 304235911Smav wakeup(&p->ccb_list); 305235911Smav } else 306235911Smav xpt_release_ccb(sccb); 307235911Smav ENC_DLOG(enc, "%s exit\n", __func__); 308235911Smav} 309235911Smav 310235911Smavvoid 311235911Smavenc_done(struct cam_periph *periph, union ccb *dccb) 312235911Smav{ 313235911Smav wakeup(&dccb->ccb_h.cbfcnp); 314235911Smav} 315235911Smav 316235911Smavint 317235911Smavenc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) 318235911Smav{ 319235911Smav struct enc_softc *softc; 320235911Smav struct cam_periph *periph; 321235911Smav 322235911Smav periph = xpt_path_periph(ccb->ccb_h.path); 323235911Smav softc = (struct enc_softc *)periph->softc; 324235911Smav 325235911Smav return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb)); 326235911Smav} 327235911Smav 328235911Smavstatic int 329235911Smavenc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, 330235911Smav struct thread *td) 331235911Smav{ 332235911Smav struct cam_periph *periph; 333235911Smav encioc_enc_status_t tmp; 334235911Smav encioc_string_t sstr; 335235911Smav encioc_elm_status_t elms; 336235911Smav encioc_elm_desc_t elmd; 337235911Smav encioc_elm_devnames_t elmdn; 338235911Smav encioc_element_t *uelm; 339235911Smav enc_softc_t *enc; 340235911Smav enc_cache_t *cache; 341235911Smav void *addr; 342235911Smav int error, i; 343235911Smav 344235911Smav 345235911Smav if (arg_addr) 346235911Smav addr = *((caddr_t *) arg_addr); 347235911Smav else 348235911Smav addr = NULL; 349235911Smav 350235911Smav periph = (struct cam_periph *)dev->si_drv1; 351235911Smav if (periph == NULL) 352235911Smav return (ENXIO); 353235911Smav 354235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); 355235911Smav 356235911Smav cam_periph_lock(periph); 357235911Smav enc = (struct enc_softc *)periph->softc; 358235911Smav cache = &enc->enc_cache; 359235911Smav 360235911Smav /* 361235911Smav * Now check to see whether we're initialized or not. 362235911Smav * This actually should never fail as we're not supposed 363235911Smav * to get past enc_open w/o successfully initializing 364235911Smav * things. 365235911Smav */ 366235911Smav if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 367235911Smav cam_periph_unlock(periph); 368235911Smav return (ENXIO); 369235911Smav } 370235911Smav cam_periph_unlock(periph); 371235911Smav 372235911Smav error = 0; 373235911Smav 374235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 375235911Smav ("trying to do ioctl %#lx\n", cmd)); 376235911Smav 377235911Smav /* 378235911Smav * If this command can change the device's state, 379235911Smav * we must have the device open for writing. 380235911Smav * 381235911Smav * For commands that get information about the 382235911Smav * device- we don't need to lock the peripheral 383235911Smav * if we aren't running a command. The periph 384235911Smav * also can't go away while a user process has 385235911Smav * it open. 386235911Smav */ 387235911Smav switch (cmd) { 388235911Smav case ENCIOC_GETNELM: 389235911Smav case ENCIOC_GETELMMAP: 390235911Smav case ENCIOC_GETENCSTAT: 391235911Smav case ENCIOC_GETELMSTAT: 392235911Smav case ENCIOC_GETELMDESC: 393235911Smav case ENCIOC_GETELMDEVNAMES: 394235911Smav break; 395235911Smav default: 396235911Smav if ((flag & FWRITE) == 0) { 397235911Smav return (EBADF); 398235911Smav } 399235911Smav } 400235911Smav 401235911Smav /* 402235911Smav * XXX The values read here are only valid for the current 403235911Smav * configuration generation. We need these ioctls 404235911Smav * to also pass in/out a generation number. 405235911Smav */ 406235911Smav sx_slock(&enc->enc_cache_lock); 407235911Smav switch (cmd) { 408235911Smav case ENCIOC_GETNELM: 409235911Smav error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); 410235911Smav break; 411235911Smav 412235911Smav case ENCIOC_GETELMMAP: 413235911Smav for (uelm = addr, i = 0; i != cache->nelms; i++) { 414235911Smav encioc_element_t kelm; 415235911Smav kelm.elm_idx = i; 416235911Smav kelm.elm_subenc_id = cache->elm_map[i].subenclosure; 417235911Smav kelm.elm_type = cache->elm_map[i].enctype; 418235911Smav error = copyout(&kelm, &uelm[i], sizeof(kelm)); 419235911Smav if (error) 420235911Smav break; 421235911Smav } 422235911Smav break; 423235911Smav 424235911Smav case ENCIOC_GETENCSTAT: 425235911Smav cam_periph_lock(periph); 426235911Smav error = enc->enc_vec.get_enc_status(enc, 1); 427235911Smav if (error) { 428235911Smav cam_periph_unlock(periph); 429235911Smav break; 430235911Smav } 431235911Smav tmp = cache->enc_status; 432235911Smav cam_periph_unlock(periph); 433235911Smav error = copyout(&tmp, addr, sizeof(tmp)); 434235911Smav cache->enc_status = tmp; 435235911Smav break; 436235911Smav 437235911Smav case ENCIOC_SETENCSTAT: 438235911Smav error = copyin(addr, &tmp, sizeof(tmp)); 439235911Smav if (error) 440235911Smav break; 441235911Smav cam_periph_lock(periph); 442235911Smav error = enc->enc_vec.set_enc_status(enc, tmp, 1); 443235911Smav cam_periph_unlock(periph); 444235911Smav break; 445235911Smav 446235911Smav case ENCIOC_GETSTRING: 447235911Smav case ENCIOC_SETSTRING: 448235911Smav if (enc->enc_vec.handle_string == NULL) { 449235911Smav error = EINVAL; 450235911Smav break; 451235911Smav } 452235911Smav error = copyin(addr, &sstr, sizeof(sstr)); 453235911Smav if (error) 454235911Smav break; 455235911Smav cam_periph_lock(periph); 456235911Smav error = enc->enc_vec.handle_string(enc, &sstr, cmd); 457235911Smav cam_periph_unlock(periph); 458235911Smav break; 459235911Smav 460235911Smav case ENCIOC_GETELMSTAT: 461235911Smav error = copyin(addr, &elms, sizeof(elms)); 462235911Smav if (error) 463235911Smav break; 464235911Smav if (elms.elm_idx >= cache->nelms) { 465235911Smav error = EINVAL; 466235911Smav break; 467235911Smav } 468235911Smav cam_periph_lock(periph); 469235911Smav error = enc->enc_vec.get_elm_status(enc, &elms, 1); 470235911Smav cam_periph_unlock(periph); 471235911Smav if (error) 472235911Smav break; 473235911Smav error = copyout(&elms, addr, sizeof(elms)); 474235911Smav break; 475235911Smav 476235911Smav case ENCIOC_GETELMDESC: 477235911Smav error = copyin(addr, &elmd, sizeof(elmd)); 478235911Smav if (error) 479235911Smav break; 480235911Smav if (elmd.elm_idx >= cache->nelms) { 481235911Smav error = EINVAL; 482235911Smav break; 483235911Smav } 484235911Smav if (enc->enc_vec.get_elm_desc != NULL) { 485235911Smav error = enc->enc_vec.get_elm_desc(enc, &elmd); 486235911Smav if (error) 487235911Smav break; 488235911Smav } else 489235911Smav elmd.elm_desc_len = 0; 490235911Smav error = copyout(&elmd, addr, sizeof(elmd)); 491235911Smav break; 492235911Smav 493235911Smav case ENCIOC_GETELMDEVNAMES: 494235911Smav if (enc->enc_vec.get_elm_devnames == NULL) { 495235911Smav error = EINVAL; 496235911Smav break; 497235911Smav } 498235911Smav error = copyin(addr, &elmdn, sizeof(elmdn)); 499235911Smav if (error) 500235911Smav break; 501235911Smav if (elmdn.elm_idx >= cache->nelms) { 502235911Smav error = EINVAL; 503235911Smav break; 504235911Smav } 505235911Smav cam_periph_lock(periph); 506235911Smav error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); 507235911Smav cam_periph_unlock(periph); 508235911Smav if (error) 509235911Smav break; 510235911Smav error = copyout(&elmdn, addr, sizeof(elmdn)); 511235911Smav break; 512235911Smav 513235911Smav case ENCIOC_SETELMSTAT: 514235911Smav error = copyin(addr, &elms, sizeof(elms)); 515235911Smav if (error) 516235911Smav break; 517235911Smav 518235911Smav if (elms.elm_idx >= cache->nelms) { 519235911Smav error = EINVAL; 520235911Smav break; 521235911Smav } 522235911Smav cam_periph_lock(periph); 523235911Smav error = enc->enc_vec.set_elm_status(enc, &elms, 1); 524235911Smav cam_periph_unlock(periph); 525235911Smav 526235911Smav break; 527235911Smav 528235911Smav case ENCIOC_INIT: 529235911Smav 530235911Smav cam_periph_lock(periph); 531235911Smav error = enc->enc_vec.init_enc(enc); 532235911Smav cam_periph_unlock(periph); 533235911Smav break; 534235911Smav 535235911Smav default: 536235911Smav cam_periph_lock(periph); 537235911Smav error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); 538235911Smav cam_periph_unlock(periph); 539235911Smav break; 540235911Smav } 541235911Smav sx_sunlock(&enc->enc_cache_lock); 542235911Smav return (error); 543235911Smav} 544235911Smav 545235911Smavint 546235911Smavenc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) 547235911Smav{ 548235911Smav int error, dlen, tdlen; 549235911Smav ccb_flags ddf; 550235911Smav union ccb *ccb; 551235911Smav 552235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, 553235911Smav ("entering enc_runcmd\n")); 554235911Smav if (dptr) { 555235911Smav if ((dlen = *dlenp) < 0) { 556235911Smav dlen = -dlen; 557235911Smav ddf = CAM_DIR_OUT; 558235911Smav } else { 559235911Smav ddf = CAM_DIR_IN; 560235911Smav } 561235911Smav } else { 562235911Smav dlen = 0; 563235911Smav ddf = CAM_DIR_NONE; 564235911Smav } 565235911Smav 566235911Smav if (cdbl > IOCDBLEN) { 567235911Smav cdbl = IOCDBLEN; 568235911Smav } 569235911Smav 570235911Smav ccb = cam_periph_getccb(enc->periph, 1); 571235911Smav if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { 572235911Smav tdlen = min(dlen, 1020); 573235911Smav tdlen = (tdlen + 3) & ~3; 574235911Smav cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen, 575235911Smav 30 * 1000); 576235911Smav if (cdb[0] == RECEIVE_DIAGNOSTIC) 577235911Smav ata_28bit_cmd(&ccb->ataio, 578235911Smav ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); 579235911Smav else if (cdb[0] == SEND_DIAGNOSTIC) 580235911Smav ata_28bit_cmd(&ccb->ataio, 581235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 582235911Smav 0x82, tdlen / 4); 583235911Smav else if (cdb[0] == READ_BUFFER) 584235911Smav ata_28bit_cmd(&ccb->ataio, 585235911Smav ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); 586235911Smav else 587235911Smav ata_28bit_cmd(&ccb->ataio, 588235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 589235911Smav 0x80, tdlen / 4); 590235911Smav } else { 591235911Smav tdlen = dlen; 592235911Smav cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG, 593235911Smav dptr, dlen, sizeof (struct scsi_sense_data), cdbl, 594235911Smav 60 * 1000); 595235911Smav bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); 596235911Smav } 597235911Smav 598235911Smav error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 599235911Smav if (error) { 600235911Smav if (dptr) { 601235911Smav *dlenp = dlen; 602235911Smav } 603235911Smav } else { 604235911Smav if (dptr) { 605235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 606235911Smav *dlenp = ccb->ataio.resid; 607235911Smav else 608235911Smav *dlenp = ccb->csio.resid; 609235911Smav *dlenp += tdlen - dlen; 610235911Smav } 611235911Smav } 612235911Smav xpt_release_ccb(ccb); 613235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 614235911Smav ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); 615235911Smav return (error); 616235911Smav} 617235911Smav 618235911Smavvoid 619235911Smavenc_log(struct enc_softc *enc, const char *fmt, ...) 620235911Smav{ 621235911Smav va_list ap; 622235911Smav 623235911Smav printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); 624235911Smav va_start(ap, fmt); 625235911Smav vprintf(fmt, ap); 626235911Smav va_end(ap); 627235911Smav} 628235911Smav 629235911Smav/* 630235911Smav * The code after this point runs on many platforms, 631235911Smav * so forgive the slightly awkward and nonconforming 632235911Smav * appearance. 633235911Smav */ 634235911Smav 635235911Smav/* 636235911Smav * Is this a device that supports enclosure services? 637235911Smav * 638235911Smav * It's a a pretty simple ruleset- if it is device type 0x0D (13), it's 639235911Smav * an ENC device. If it happens to be an old UNISYS SEN device, we can 640235911Smav * handle that too. 641235911Smav */ 642235911Smav 643235911Smav#define SAFTE_START 44 644235911Smav#define SAFTE_END 50 645235911Smav#define SAFTE_LEN SAFTE_END-SAFTE_START 646235911Smav 647235911Smavstatic enctyp 648235911Smavenc_type(struct ccb_getdev *cgd) 649235911Smav{ 650235911Smav int buflen; 651235911Smav unsigned char *iqd; 652235911Smav 653235911Smav if (cgd->protocol == PROTO_SEMB) { 654235911Smav iqd = (unsigned char *)&cgd->ident_data; 655235911Smav if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) 656235911Smav return (ENC_SEMB_SES); 657235911Smav else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) 658235911Smav return (ENC_SEMB_SAFT); 659235911Smav return (ENC_NONE); 660235911Smav 661235911Smav } else if (cgd->protocol != PROTO_SCSI) 662235911Smav return (ENC_NONE); 663235911Smav 664235911Smav iqd = (unsigned char *)&cgd->inq_data; 665235911Smav buflen = min(sizeof(cgd->inq_data), 666235911Smav SID_ADDITIONAL_LENGTH(&cgd->inq_data)); 667235911Smav if (buflen < 8+SEN_ID_LEN) 668235911Smav return (ENC_NONE); 669235911Smav 670235911Smav if ((iqd[0] & 0x1f) == T_ENCLOSURE) { 671235911Smav if (STRNCMP(&iqd[8], SEN_ID, SEN_ID_LEN) == 0) { 672235911Smav return (ENC_SEN); 673235911Smav } else if ((iqd[2] & 0x7) > 2) { 674235911Smav return (ENC_SES); 675235911Smav } else { 676235911Smav return (ENC_SES_SCSI2); 677235911Smav } 678235911Smav return (ENC_NONE); 679235911Smav } 680235911Smav 681235911Smav#ifdef ENC_ENABLE_PASSTHROUGH 682235911Smav if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { 683235911Smav /* 684235911Smav * PassThrough Device. 685235911Smav */ 686235911Smav return (ENC_ENC_PASSTHROUGH); 687235911Smav } 688235911Smav#endif 689235911Smav 690235911Smav /* 691235911Smav * The comparison is short for a reason- 692235911Smav * some vendors were chopping it short. 693235911Smav */ 694235911Smav 695235911Smav if (buflen < SAFTE_END - 2) { 696235911Smav return (ENC_NONE); 697235911Smav } 698235911Smav 699235911Smav if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { 700235911Smav return (ENC_SAFT); 701235911Smav } 702235911Smav return (ENC_NONE); 703235911Smav} 704235911Smav 705235911Smav/*================== Enclosure Monitoring/Processing Daemon ==================*/ 706235911Smav/** 707235911Smav * \brief Queue an update request for a given action, if needed. 708235911Smav * 709235911Smav * \param enc SES softc to queue the request for. 710235911Smav * \param action Action requested. 711235911Smav */ 712235911Smavvoid 713235911Smavenc_update_request(enc_softc_t *enc, uint32_t action) 714235911Smav{ 715235911Smav if ((enc->pending_actions & (0x1 << action)) == 0) { 716235911Smav enc->pending_actions |= (0x1 << action); 717235911Smav ENC_DLOG(enc, "%s: queing requested action %d\n", 718235911Smav __func__, action); 719235911Smav if (enc->current_action == ENC_UPDATE_NONE) 720235911Smav wakeup(enc->enc_daemon); 721235911Smav } else { 722235911Smav ENC_DLOG(enc, "%s: ignoring requested action %d - " 723235911Smav "Already queued\n", __func__, action); 724235911Smav } 725235911Smav} 726235911Smav 727235911Smav/** 728235911Smav * \brief Invoke the handler of the highest priority pending 729235911Smav * state in the SES state machine. 730235911Smav * 731235911Smav * \param enc The SES instance invoking the state machine. 732235911Smav */ 733235911Smavstatic void 734235911Smavenc_fsm_step(enc_softc_t *enc) 735235911Smav{ 736235911Smav union ccb *ccb; 737235911Smav uint8_t *buf; 738235911Smav struct enc_fsm_state *cur_state; 739235911Smav int error; 740235911Smav uint32_t xfer_len; 741235911Smav 742235911Smav ENC_DLOG(enc, "%s enter %p\n", __func__, enc); 743235911Smav 744235911Smav enc->current_action = ffs(enc->pending_actions) - 1; 745235911Smav enc->pending_actions &= ~(0x1 << enc->current_action); 746235911Smav 747235911Smav cur_state = &enc->enc_fsm_states[enc->current_action]; 748235911Smav 749235911Smav buf = NULL; 750235911Smav if (cur_state->buf_size != 0) { 751235911Smav cam_periph_unlock(enc->periph); 752235911Smav buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); 753235911Smav cam_periph_lock(enc->periph); 754235911Smav } 755235911Smav 756235911Smav error = 0; 757235911Smav ccb = NULL; 758235911Smav if (cur_state->fill != NULL) { 759235911Smav ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 760235911Smav 761235911Smav error = cur_state->fill(enc, cur_state, ccb, buf); 762235911Smav if (error != 0) 763235911Smav goto done; 764235911Smav 765235911Smav error = cam_periph_runccb(ccb, cur_state->error, 766235911Smav ENC_CFLAGS, 767235911Smav ENC_FLAGS|SF_QUIET_IR, NULL); 768235911Smav } 769235911Smav 770235911Smav if (ccb != NULL) { 771235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 772235911Smav xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 773235911Smav else 774235911Smav xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; 775235911Smav } else 776235911Smav xfer_len = 0; 777235911Smav 778235911Smav cam_periph_unlock(enc->periph); 779235911Smav cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); 780235911Smav cam_periph_lock(enc->periph); 781235911Smav 782235911Smavdone: 783235911Smav ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); 784235911Smav ENC_FREE_AND_NULL(buf); 785235911Smav if (ccb != NULL) 786235911Smav xpt_release_ccb(ccb); 787235911Smav} 788235911Smav 789235911Smav/** 790235911Smav * \invariant Called with cam_periph mutex held. 791235911Smav */ 792235911Smavstatic void 793235911Smavenc_status_updater(void *arg) 794235911Smav{ 795235911Smav enc_softc_t *enc; 796235911Smav 797235911Smav enc = arg; 798235911Smav if (enc->enc_vec.poll_status != NULL) 799235911Smav enc->enc_vec.poll_status(enc); 800235911Smav} 801235911Smav 802235911Smavstatic void 803235911Smavenc_daemon(void *arg) 804235911Smav{ 805235911Smav enc_softc_t *enc; 806235911Smav 807235911Smav enc = arg; 808235911Smav 809235911Smav cam_periph_lock(enc->periph); 810235911Smav while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { 811235911Smav if (enc->pending_actions == 0) { 812235911Smav struct intr_config_hook *hook; 813235911Smav 814235911Smav /* 815235911Smav * Reset callout and msleep, or 816235911Smav * issue timed task completion 817235911Smav * status command. 818235911Smav */ 819235911Smav enc->current_action = ENC_UPDATE_NONE; 820235911Smav 821235911Smav /* 822235911Smav * We've been through our state machine at least 823235911Smav * once. Allow the transition to userland. 824235911Smav */ 825235911Smav hook = &enc->enc_boot_hold_ch; 826235911Smav if (hook->ich_func != NULL) { 827235911Smav config_intrhook_disestablish(hook); 828235911Smav hook->ich_func = NULL; 829235911Smav } 830235911Smav 831235911Smav callout_reset(&enc->status_updater, 60*hz, 832235911Smav enc_status_updater, enc); 833235911Smav 834235911Smav cam_periph_sleep(enc->periph, enc->enc_daemon, 835235911Smav PUSER, "idle", 0); 836235911Smav } else { 837235911Smav enc_fsm_step(enc); 838235911Smav } 839235911Smav } 840235911Smav enc->enc_daemon = NULL; 841235911Smav cam_periph_unlock(enc->periph); 842235911Smav kproc_exit(0); 843235911Smav} 844235911Smav 845235911Smavstatic int 846235911Smavenc_kproc_init(enc_softc_t *enc) 847235911Smav{ 848235911Smav int result; 849235911Smav 850235911Smav callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0); 851235911Smav 852235911Smav result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, 853235911Smav /*stackpgs*/0, "enc_daemon%d", 854235911Smav enc->periph->unit_number); 855235911Smav if (result == 0) { 856235911Smav /* Do an initial load of all page data. */ 857235911Smav cam_periph_lock(enc->periph); 858235911Smav enc->enc_vec.poll_status(enc); 859235911Smav cam_periph_unlock(enc->periph); 860235911Smav } 861235911Smav return (result); 862235911Smav} 863235911Smav 864235911Smav/** 865235911Smav * \brief Interrupt configuration hook callback associated with 866235911Smav * enc_boot_hold_ch. 867235911Smav * 868235911Smav * Since interrupts are always functional at the time of enclosure 869235911Smav * configuration, there is nothing to be done when the callback occurs. 870235911Smav * This hook is only registered to hold up boot processing while initial 871235911Smav * eclosure processing occurs. 872235911Smav * 873235911Smav * \param arg The enclosure softc, but currently unused in this callback. 874235911Smav */ 875235911Smavstatic void 876235911Smavenc_nop_confighook_cb(void *arg __unused) 877235911Smav{ 878235911Smav} 879235911Smav 880235911Smavstatic cam_status 881235911Smavenc_ctor(struct cam_periph *periph, void *arg) 882235911Smav{ 883235911Smav cam_status status = CAM_REQ_CMP_ERR; 884235911Smav int err; 885235911Smav enc_softc_t *enc; 886235911Smav struct ccb_getdev *cgd; 887235911Smav char *tname; 888235911Smav 889235911Smav cgd = (struct ccb_getdev *)arg; 890235911Smav if (periph == NULL) { 891235911Smav printf("enc_ctor: periph was NULL!!\n"); 892235911Smav goto out; 893235911Smav } 894235911Smav 895235911Smav if (cgd == NULL) { 896235911Smav printf("enc_ctor: no getdev CCB, can't register device\n"); 897235911Smav goto out; 898235911Smav } 899235911Smav 900235911Smav enc = ENC_MALLOCZ(sizeof(*enc)); 901235911Smav if (enc == NULL) { 902235911Smav printf("enc_ctor: Unable to probe new device. " 903235911Smav "Unable to allocate enc\n"); 904235911Smav goto out; 905235911Smav } 906235911Smav enc->periph = periph; 907235911Smav enc->current_action = ENC_UPDATE_INVALID; 908235911Smav 909235911Smav enc->enc_type = enc_type(cgd); 910235911Smav sx_init(&enc->enc_cache_lock, "enccache"); 911235911Smav 912235911Smav switch (enc->enc_type) { 913235911Smav case ENC_SES: 914235911Smav case ENC_SES_SCSI2: 915235911Smav case ENC_SES_PASSTHROUGH: 916235911Smav case ENC_SEMB_SES: 917235911Smav err = ses_softc_init(enc); 918235911Smav break; 919235911Smav case ENC_SAFT: 920235911Smav case ENC_SEMB_SAFT: 921235911Smav err = safte_softc_init(enc); 922235911Smav break; 923235911Smav case ENC_SEN: 924235911Smav case ENC_NONE: 925235911Smav default: 926235911Smav ENC_FREE(enc); 927235911Smav return (CAM_REQ_CMP_ERR); 928235911Smav } 929235911Smav 930235911Smav if (err) { 931235911Smav xpt_print(periph->path, "error %d initializing\n", err); 932235911Smav goto out; 933235911Smav } 934235911Smav 935235911Smav /* 936235911Smav * Hold off userland until we have made at least one pass 937235911Smav * through our state machine so that physical path data is 938235911Smav * present. 939235911Smav */ 940235911Smav if (enc->enc_vec.poll_status != NULL) { 941235911Smav enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; 942235911Smav enc->enc_boot_hold_ch.ich_arg = enc; 943235911Smav config_intrhook_establish(&enc->enc_boot_hold_ch); 944235911Smav } 945235911Smav 946235911Smav /* 947235911Smav * The softc field is set only once the enc is fully initialized 948235911Smav * so that we can rely on this field to detect partially 949235911Smav * initialized periph objects in the AC_FOUND_DEVICE handler. 950235911Smav */ 951235911Smav periph->softc = enc; 952235911Smav 953235911Smav cam_periph_unlock(periph); 954235911Smav if (enc->enc_vec.poll_status != NULL) { 955235911Smav err = enc_kproc_init(enc); 956235911Smav if (err) { 957235911Smav xpt_print(periph->path, 958235911Smav "error %d string enc_daemon\n", err); 959235911Smav goto out; 960235911Smav } 961235911Smav } 962235911Smav enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number, 963235911Smav UID_ROOT, GID_OPERATOR, 0600, "%s%d", 964235911Smav periph->periph_name, periph->unit_number); 965235911Smav cam_periph_lock(periph); 966235911Smav enc->enc_dev->si_drv1 = periph; 967235911Smav 968235911Smav enc->enc_flags |= ENC_FLAG_INITIALIZED; 969235911Smav 970235911Smav /* 971235911Smav * Add an async callback so that we get notified if this 972235911Smav * device goes away. 973235911Smav */ 974235911Smav xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); 975235911Smav 976235911Smav switch (enc->enc_type) { 977235911Smav default: 978235911Smav case ENC_NONE: 979235911Smav tname = "No ENC device"; 980235911Smav break; 981235911Smav case ENC_SES_SCSI2: 982235911Smav tname = "SCSI-2 ENC Device"; 983235911Smav break; 984235911Smav case ENC_SES: 985235911Smav tname = "SCSI-3 ENC Device"; 986235911Smav break; 987235911Smav case ENC_SES_PASSTHROUGH: 988235911Smav tname = "ENC Passthrough Device"; 989235911Smav break; 990235911Smav case ENC_SEN: 991235911Smav tname = "UNISYS SEN Device (NOT HANDLED YET)"; 992235911Smav break; 993235911Smav case ENC_SAFT: 994235911Smav tname = "SAF-TE Compliant Device"; 995235911Smav break; 996235911Smav case ENC_SEMB_SES: 997235911Smav tname = "SEMB SES Device"; 998235911Smav break; 999235911Smav case ENC_SEMB_SAFT: 1000235911Smav tname = "SEMB SAF-TE Device"; 1001235911Smav break; 1002235911Smav } 1003235911Smav xpt_announce_periph(periph, tname); 1004235911Smav status = CAM_REQ_CMP; 1005235911Smav 1006235911Smavout: 1007235911Smav if (status != CAM_REQ_CMP) 1008235911Smav enc_dtor(periph); 1009235911Smav return (status); 1010235911Smav} 1011235911Smav 1012