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$"); 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 59255119Smav#include <opt_ses.h> 60255119Smav 61235911SmavMALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); 62235911Smav 63235911Smav/* Enclosure type independent driver */ 64235911Smav 65235911Smavstatic d_open_t enc_open; 66235911Smavstatic d_close_t enc_close; 67235911Smavstatic d_ioctl_t enc_ioctl; 68235911Smavstatic periph_init_t enc_init; 69235911Smavstatic periph_ctor_t enc_ctor; 70235911Smavstatic periph_oninv_t enc_oninvalidate; 71235911Smavstatic periph_dtor_t enc_dtor; 72235911Smav 73235911Smavstatic void enc_async(void *, uint32_t, struct cam_path *, void *); 74235911Smavstatic enctyp enc_type(struct ccb_getdev *); 75235911Smav 76235911SmavSYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0, 77235911Smav "CAM Enclosure Services driver"); 78235911Smav 79235911Smavstatic struct periph_driver encdriver = { 80235911Smav enc_init, "ses", 81235911Smav TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 82235911Smav}; 83235911Smav 84235911SmavPERIPHDRIVER_DECLARE(enc, encdriver); 85235911Smav 86235911Smavstatic struct cdevsw enc_cdevsw = { 87235911Smav .d_version = D_VERSION, 88235911Smav .d_open = enc_open, 89235911Smav .d_close = enc_close, 90235911Smav .d_ioctl = enc_ioctl, 91235911Smav .d_name = "ses", 92236138Sken .d_flags = D_TRACKCLOSE, 93235911Smav}; 94235911Smav 95235911Smavstatic void 96235911Smavenc_init(void) 97235911Smav{ 98235911Smav cam_status status; 99235911Smav 100235911Smav /* 101235911Smav * Install a global async callback. This callback will 102235911Smav * receive async callbacks like "new device found". 103235911Smav */ 104235911Smav status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL); 105235911Smav 106235911Smav if (status != CAM_REQ_CMP) { 107235911Smav printf("enc: Failed to attach master async callback " 108235911Smav "due to status 0x%x!\n", status); 109235911Smav } 110235911Smav} 111235911Smav 112235911Smavstatic void 113237328Skenenc_devgonecb(void *arg) 114237328Sken{ 115237328Sken struct cam_periph *periph; 116244014Sken struct enc_softc *enc; 117260387Sscottl struct mtx *mtx; 118244014Sken int i; 119237328Sken 120237328Sken periph = (struct cam_periph *)arg; 121260387Sscottl mtx = cam_periph_mtx(periph); 122260387Sscottl mtx_lock(mtx); 123244014Sken enc = (struct enc_softc *)periph->softc; 124237328Sken 125244014Sken /* 126244014Sken * When we get this callback, we will get no more close calls from 127244014Sken * devfs. So if we have any dangling opens, we need to release the 128244014Sken * reference held for that particular context. 129244014Sken */ 130244014Sken for (i = 0; i < enc->open_count; i++) 131244014Sken cam_periph_release_locked(periph); 132244014Sken 133244014Sken enc->open_count = 0; 134244014Sken 135244014Sken /* 136244014Sken * Release the reference held for the device node, it is gone now. 137244014Sken */ 138244014Sken cam_periph_release_locked(periph); 139244014Sken 140244014Sken /* 141260387Sscottl * We reference the lock directly here, instead of using 142244014Sken * cam_periph_unlock(). The reason is that the final call to 143244014Sken * cam_periph_release_locked() above could result in the periph 144244014Sken * getting freed. If that is the case, dereferencing the periph 145244014Sken * with a cam_periph_unlock() call would cause a page fault. 146244014Sken */ 147260387Sscottl mtx_unlock(mtx); 148237328Sken} 149237328Sken 150237328Skenstatic void 151235911Smavenc_oninvalidate(struct cam_periph *periph) 152235911Smav{ 153235911Smav struct enc_softc *enc; 154235911Smav 155235911Smav enc = periph->softc; 156235911Smav 157235911Smav enc->enc_flags |= ENC_FLAG_INVALID; 158235911Smav 159235911Smav /* If the sub-driver has an invalidate routine, call it */ 160235911Smav if (enc->enc_vec.softc_invalidate != NULL) 161235911Smav enc->enc_vec.softc_invalidate(enc); 162235911Smav 163235911Smav /* 164235911Smav * Unregister any async callbacks. 165235911Smav */ 166235911Smav xpt_register_async(0, enc_async, periph, periph->path); 167235911Smav 168235911Smav /* 169235911Smav * Shutdown our daemon. 170235911Smav */ 171235911Smav enc->enc_flags |= ENC_FLAG_SHUTDOWN; 172235911Smav if (enc->enc_daemon != NULL) { 173235980Smav /* Signal the ses daemon to terminate. */ 174235911Smav wakeup(enc->enc_daemon); 175235911Smav } 176235911Smav callout_drain(&enc->status_updater); 177235911Smav 178237328Sken destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph); 179235911Smav} 180235911Smav 181235911Smavstatic void 182235911Smavenc_dtor(struct cam_periph *periph) 183235911Smav{ 184235911Smav struct enc_softc *enc; 185235911Smav 186235911Smav enc = periph->softc; 187235911Smav 188235911Smav /* If the sub-driver has a cleanup routine, call it */ 189235911Smav if (enc->enc_vec.softc_cleanup != NULL) 190235911Smav enc->enc_vec.softc_cleanup(enc); 191235911Smav 192235911Smav if (enc->enc_boot_hold_ch.ich_func != NULL) { 193235911Smav config_intrhook_disestablish(&enc->enc_boot_hold_ch); 194235911Smav enc->enc_boot_hold_ch.ich_func = NULL; 195235911Smav } 196235911Smav 197235911Smav ENC_FREE(enc); 198235911Smav} 199235911Smav 200235911Smavstatic void 201235911Smavenc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 202235911Smav{ 203235911Smav struct cam_periph *periph; 204235911Smav 205235911Smav periph = (struct cam_periph *)callback_arg; 206235911Smav 207235911Smav switch(code) { 208235911Smav case AC_FOUND_DEVICE: 209235911Smav { 210235911Smav struct ccb_getdev *cgd; 211235911Smav cam_status status; 212235911Smav path_id_t path_id; 213235911Smav 214235911Smav cgd = (struct ccb_getdev *)arg; 215235911Smav if (arg == NULL) { 216235911Smav break; 217235911Smav } 218235911Smav 219235911Smav if (enc_type(cgd) == ENC_NONE) { 220235911Smav /* 221235911Smav * Schedule announcement of the ENC bindings for 222235911Smav * this device if it is managed by a SEP. 223235911Smav */ 224235911Smav path_id = xpt_path_path_id(path); 225235911Smav xpt_lock_buses(); 226235911Smav TAILQ_FOREACH(periph, &encdriver.units, unit_links) { 227235911Smav struct enc_softc *softc; 228235911Smav 229235911Smav softc = (struct enc_softc *)periph->softc; 230235911Smav if (xpt_path_path_id(periph->path) != path_id 231235911Smav || softc == NULL 232235911Smav || (softc->enc_flags & ENC_FLAG_INITIALIZED) 233235911Smav == 0 234235911Smav || softc->enc_vec.device_found == NULL) 235235911Smav continue; 236235911Smav 237235911Smav softc->enc_vec.device_found(softc); 238235911Smav } 239235911Smav xpt_unlock_buses(); 240235911Smav return; 241235911Smav } 242235911Smav 243235911Smav status = cam_periph_alloc(enc_ctor, enc_oninvalidate, 244260387Sscottl enc_dtor, NULL, "ses", CAM_PERIPH_BIO, 245260387Sscottl path, enc_async, AC_FOUND_DEVICE, cgd); 246235911Smav 247235911Smav if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { 248235911Smav printf("enc_async: Unable to probe new device due to " 249235911Smav "status 0x%x\n", status); 250235911Smav } 251235911Smav break; 252235911Smav } 253235911Smav default: 254235911Smav cam_periph_async(periph, code, path, arg); 255235911Smav break; 256235911Smav } 257235911Smav} 258235911Smav 259235911Smavstatic int 260235911Smavenc_open(struct cdev *dev, int flags, int fmt, struct thread *td) 261235911Smav{ 262235911Smav struct cam_periph *periph; 263235911Smav struct enc_softc *softc; 264235911Smav int error = 0; 265235911Smav 266235911Smav periph = (struct cam_periph *)dev->si_drv1; 267235911Smav if (periph == NULL) { 268235911Smav return (ENXIO); 269235911Smav } 270235911Smav 271235911Smav if (cam_periph_acquire(periph) != CAM_REQ_CMP) 272235911Smav return (ENXIO); 273235911Smav 274235911Smav cam_periph_lock(periph); 275235911Smav 276235911Smav softc = (struct enc_softc *)periph->softc; 277235911Smav 278235911Smav if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 279235911Smav error = ENXIO; 280235911Smav goto out; 281235911Smav } 282235911Smav if (softc->enc_flags & ENC_FLAG_INVALID) { 283235911Smav error = ENXIO; 284235911Smav goto out; 285235911Smav } 286236138Skenout: 287236138Sken if (error != 0) 288236138Sken cam_periph_release_locked(periph); 289244014Sken else 290244014Sken softc->open_count++; 291235911Smav 292235911Smav cam_periph_unlock(periph); 293236138Sken 294235911Smav return (error); 295235911Smav} 296235911Smav 297235911Smavstatic int 298235911Smavenc_close(struct cdev *dev, int flag, int fmt, struct thread *td) 299235911Smav{ 300235911Smav struct cam_periph *periph; 301244014Sken struct enc_softc *enc; 302260387Sscottl struct mtx *mtx; 303235911Smav 304235911Smav periph = (struct cam_periph *)dev->si_drv1; 305235911Smav if (periph == NULL) 306235911Smav return (ENXIO); 307260387Sscottl mtx = cam_periph_mtx(periph); 308260387Sscottl mtx_lock(mtx); 309235911Smav 310244014Sken enc = periph->softc; 311244014Sken enc->open_count--; 312244014Sken 313244014Sken cam_periph_release_locked(periph); 314244014Sken 315244014Sken /* 316260387Sscottl * We reference the lock directly here, instead of using 317244014Sken * cam_periph_unlock(). The reason is that the call to 318244014Sken * cam_periph_release_locked() above could result in the periph 319244014Sken * getting freed. If that is the case, dereferencing the periph 320244014Sken * with a cam_periph_unlock() call would cause a page fault. 321244014Sken * 322244014Sken * cam_periph_release() avoids this problem using the same method, 323244014Sken * but we're manually acquiring and dropping the lock here to 324244014Sken * protect the open count and avoid another lock acquisition and 325244014Sken * release. 326244014Sken */ 327260387Sscottl mtx_unlock(mtx); 328244014Sken 329235911Smav return (0); 330235911Smav} 331235911Smav 332235911Smavint 333235911Smavenc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) 334235911Smav{ 335235911Smav struct enc_softc *softc; 336235911Smav struct cam_periph *periph; 337235911Smav 338235911Smav periph = xpt_path_periph(ccb->ccb_h.path); 339235911Smav softc = (struct enc_softc *)periph->softc; 340235911Smav 341235911Smav return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb)); 342235911Smav} 343235911Smav 344235911Smavstatic int 345235911Smavenc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, 346235911Smav struct thread *td) 347235911Smav{ 348235911Smav struct cam_periph *periph; 349235911Smav encioc_enc_status_t tmp; 350235911Smav encioc_string_t sstr; 351235911Smav encioc_elm_status_t elms; 352235911Smav encioc_elm_desc_t elmd; 353235911Smav encioc_elm_devnames_t elmdn; 354235911Smav encioc_element_t *uelm; 355235911Smav enc_softc_t *enc; 356235911Smav enc_cache_t *cache; 357235911Smav void *addr; 358235911Smav int error, i; 359235911Smav 360235911Smav 361235911Smav if (arg_addr) 362235911Smav addr = *((caddr_t *) arg_addr); 363235911Smav else 364235911Smav addr = NULL; 365235911Smav 366235911Smav periph = (struct cam_periph *)dev->si_drv1; 367235911Smav if (periph == NULL) 368235911Smav return (ENXIO); 369235911Smav 370235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); 371235911Smav 372235911Smav cam_periph_lock(periph); 373235911Smav enc = (struct enc_softc *)periph->softc; 374235911Smav cache = &enc->enc_cache; 375235911Smav 376235911Smav /* 377235911Smav * Now check to see whether we're initialized or not. 378235911Smav * This actually should never fail as we're not supposed 379235911Smav * to get past enc_open w/o successfully initializing 380235911Smav * things. 381235911Smav */ 382235911Smav if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 383235911Smav cam_periph_unlock(periph); 384235911Smav return (ENXIO); 385235911Smav } 386235911Smav cam_periph_unlock(periph); 387235911Smav 388235911Smav error = 0; 389235911Smav 390235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 391235911Smav ("trying to do ioctl %#lx\n", cmd)); 392235911Smav 393235911Smav /* 394235911Smav * If this command can change the device's state, 395235911Smav * we must have the device open for writing. 396235911Smav * 397235911Smav * For commands that get information about the 398235911Smav * device- we don't need to lock the peripheral 399235911Smav * if we aren't running a command. The periph 400235911Smav * also can't go away while a user process has 401235911Smav * it open. 402235911Smav */ 403235911Smav switch (cmd) { 404235911Smav case ENCIOC_GETNELM: 405235911Smav case ENCIOC_GETELMMAP: 406235911Smav case ENCIOC_GETENCSTAT: 407235911Smav case ENCIOC_GETELMSTAT: 408235911Smav case ENCIOC_GETELMDESC: 409235911Smav case ENCIOC_GETELMDEVNAMES: 410235911Smav break; 411235911Smav default: 412235911Smav if ((flag & FWRITE) == 0) { 413235911Smav return (EBADF); 414235911Smav } 415235911Smav } 416235911Smav 417235911Smav /* 418235911Smav * XXX The values read here are only valid for the current 419235911Smav * configuration generation. We need these ioctls 420235911Smav * to also pass in/out a generation number. 421235911Smav */ 422235911Smav sx_slock(&enc->enc_cache_lock); 423235911Smav switch (cmd) { 424235911Smav case ENCIOC_GETNELM: 425235911Smav error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); 426235911Smav break; 427235911Smav 428235911Smav case ENCIOC_GETELMMAP: 429235911Smav for (uelm = addr, i = 0; i != cache->nelms; i++) { 430235911Smav encioc_element_t kelm; 431235911Smav kelm.elm_idx = i; 432235911Smav kelm.elm_subenc_id = cache->elm_map[i].subenclosure; 433235911Smav kelm.elm_type = cache->elm_map[i].enctype; 434235911Smav error = copyout(&kelm, &uelm[i], sizeof(kelm)); 435235911Smav if (error) 436235911Smav break; 437235911Smav } 438235911Smav break; 439235911Smav 440235911Smav case ENCIOC_GETENCSTAT: 441235911Smav cam_periph_lock(periph); 442235911Smav error = enc->enc_vec.get_enc_status(enc, 1); 443235911Smav if (error) { 444235911Smav cam_periph_unlock(periph); 445235911Smav break; 446235911Smav } 447235911Smav tmp = cache->enc_status; 448235911Smav cam_periph_unlock(periph); 449235911Smav error = copyout(&tmp, addr, sizeof(tmp)); 450235911Smav cache->enc_status = tmp; 451235911Smav break; 452235911Smav 453235911Smav case ENCIOC_SETENCSTAT: 454235911Smav error = copyin(addr, &tmp, sizeof(tmp)); 455235911Smav if (error) 456235911Smav break; 457235911Smav cam_periph_lock(periph); 458235911Smav error = enc->enc_vec.set_enc_status(enc, tmp, 1); 459235911Smav cam_periph_unlock(periph); 460235911Smav break; 461235911Smav 462235911Smav case ENCIOC_GETSTRING: 463235911Smav case ENCIOC_SETSTRING: 464235911Smav if (enc->enc_vec.handle_string == NULL) { 465235911Smav error = EINVAL; 466235911Smav break; 467235911Smav } 468235911Smav error = copyin(addr, &sstr, sizeof(sstr)); 469235911Smav if (error) 470235911Smav break; 471235911Smav cam_periph_lock(periph); 472235911Smav error = enc->enc_vec.handle_string(enc, &sstr, cmd); 473235911Smav cam_periph_unlock(periph); 474235911Smav break; 475235911Smav 476235911Smav case ENCIOC_GETELMSTAT: 477235911Smav error = copyin(addr, &elms, sizeof(elms)); 478235911Smav if (error) 479235911Smav break; 480235911Smav if (elms.elm_idx >= cache->nelms) { 481235911Smav error = EINVAL; 482235911Smav break; 483235911Smav } 484235911Smav cam_periph_lock(periph); 485235911Smav error = enc->enc_vec.get_elm_status(enc, &elms, 1); 486235911Smav cam_periph_unlock(periph); 487235911Smav if (error) 488235911Smav break; 489235911Smav error = copyout(&elms, addr, sizeof(elms)); 490235911Smav break; 491235911Smav 492235911Smav case ENCIOC_GETELMDESC: 493235911Smav error = copyin(addr, &elmd, sizeof(elmd)); 494235911Smav if (error) 495235911Smav break; 496235911Smav if (elmd.elm_idx >= cache->nelms) { 497235911Smav error = EINVAL; 498235911Smav break; 499235911Smav } 500235911Smav if (enc->enc_vec.get_elm_desc != NULL) { 501235911Smav error = enc->enc_vec.get_elm_desc(enc, &elmd); 502235911Smav if (error) 503235911Smav break; 504235911Smav } else 505235911Smav elmd.elm_desc_len = 0; 506235911Smav error = copyout(&elmd, addr, sizeof(elmd)); 507235911Smav break; 508235911Smav 509235911Smav case ENCIOC_GETELMDEVNAMES: 510235911Smav if (enc->enc_vec.get_elm_devnames == NULL) { 511235911Smav error = EINVAL; 512235911Smav break; 513235911Smav } 514235911Smav error = copyin(addr, &elmdn, sizeof(elmdn)); 515235911Smav if (error) 516235911Smav break; 517235911Smav if (elmdn.elm_idx >= cache->nelms) { 518235911Smav error = EINVAL; 519235911Smav break; 520235911Smav } 521235911Smav cam_periph_lock(periph); 522235911Smav error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); 523235911Smav cam_periph_unlock(periph); 524235911Smav if (error) 525235911Smav break; 526235911Smav error = copyout(&elmdn, addr, sizeof(elmdn)); 527235911Smav break; 528235911Smav 529235911Smav case ENCIOC_SETELMSTAT: 530235911Smav error = copyin(addr, &elms, sizeof(elms)); 531235911Smav if (error) 532235911Smav break; 533235911Smav 534235911Smav if (elms.elm_idx >= cache->nelms) { 535235911Smav error = EINVAL; 536235911Smav break; 537235911Smav } 538235911Smav cam_periph_lock(periph); 539235911Smav error = enc->enc_vec.set_elm_status(enc, &elms, 1); 540235911Smav cam_periph_unlock(periph); 541235911Smav 542235911Smav break; 543235911Smav 544235911Smav case ENCIOC_INIT: 545235911Smav 546235911Smav cam_periph_lock(periph); 547235911Smav error = enc->enc_vec.init_enc(enc); 548235911Smav cam_periph_unlock(periph); 549235911Smav break; 550235911Smav 551235911Smav default: 552235911Smav cam_periph_lock(periph); 553235911Smav error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); 554235911Smav cam_periph_unlock(periph); 555235911Smav break; 556235911Smav } 557235911Smav sx_sunlock(&enc->enc_cache_lock); 558235911Smav return (error); 559235911Smav} 560235911Smav 561235911Smavint 562235911Smavenc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) 563235911Smav{ 564235911Smav int error, dlen, tdlen; 565235911Smav ccb_flags ddf; 566235911Smav union ccb *ccb; 567235911Smav 568235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, 569235911Smav ("entering enc_runcmd\n")); 570235911Smav if (dptr) { 571235911Smav if ((dlen = *dlenp) < 0) { 572235911Smav dlen = -dlen; 573235911Smav ddf = CAM_DIR_OUT; 574235911Smav } else { 575235911Smav ddf = CAM_DIR_IN; 576235911Smav } 577235911Smav } else { 578235911Smav dlen = 0; 579235911Smav ddf = CAM_DIR_NONE; 580235911Smav } 581235911Smav 582235911Smav if (cdbl > IOCDBLEN) { 583235911Smav cdbl = IOCDBLEN; 584235911Smav } 585235911Smav 586242173Smav ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 587235911Smav if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { 588235911Smav tdlen = min(dlen, 1020); 589235911Smav tdlen = (tdlen + 3) & ~3; 590260387Sscottl cam_fill_ataio(&ccb->ataio, 0, NULL, ddf, 0, dptr, tdlen, 591235911Smav 30 * 1000); 592235911Smav if (cdb[0] == RECEIVE_DIAGNOSTIC) 593235911Smav ata_28bit_cmd(&ccb->ataio, 594235911Smav ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); 595235911Smav else if (cdb[0] == SEND_DIAGNOSTIC) 596235911Smav ata_28bit_cmd(&ccb->ataio, 597235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 598235911Smav 0x82, tdlen / 4); 599235911Smav else if (cdb[0] == READ_BUFFER) 600235911Smav ata_28bit_cmd(&ccb->ataio, 601235911Smav ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); 602235911Smav else 603235911Smav ata_28bit_cmd(&ccb->ataio, 604235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 605235911Smav 0x80, tdlen / 4); 606235911Smav } else { 607235911Smav tdlen = dlen; 608260387Sscottl cam_fill_csio(&ccb->csio, 0, NULL, ddf, MSG_SIMPLE_Q_TAG, 609235911Smav dptr, dlen, sizeof (struct scsi_sense_data), cdbl, 610235911Smav 60 * 1000); 611235911Smav bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); 612235911Smav } 613235911Smav 614235911Smav error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 615235911Smav if (error) { 616235911Smav if (dptr) { 617235911Smav *dlenp = dlen; 618235911Smav } 619235911Smav } else { 620235911Smav if (dptr) { 621235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 622235911Smav *dlenp = ccb->ataio.resid; 623235911Smav else 624235911Smav *dlenp = ccb->csio.resid; 625235911Smav *dlenp += tdlen - dlen; 626235911Smav } 627235911Smav } 628235911Smav xpt_release_ccb(ccb); 629235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 630235911Smav ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); 631235911Smav return (error); 632235911Smav} 633235911Smav 634235911Smavvoid 635235911Smavenc_log(struct enc_softc *enc, const char *fmt, ...) 636235911Smav{ 637235911Smav va_list ap; 638235911Smav 639235911Smav printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); 640235911Smav va_start(ap, fmt); 641235911Smav vprintf(fmt, ap); 642235911Smav va_end(ap); 643235911Smav} 644235911Smav 645235911Smav/* 646235911Smav * The code after this point runs on many platforms, 647235911Smav * so forgive the slightly awkward and nonconforming 648235911Smav * appearance. 649235911Smav */ 650235911Smav 651235911Smav/* 652235911Smav * Is this a device that supports enclosure services? 653235911Smav * 654240521Seadler * It's a pretty simple ruleset- if it is device type 655239213Smjacob * 0x0D (13), it's an ENCLOSURE device. 656235911Smav */ 657235911Smav 658235911Smav#define SAFTE_START 44 659235911Smav#define SAFTE_END 50 660235911Smav#define SAFTE_LEN SAFTE_END-SAFTE_START 661235911Smav 662235911Smavstatic enctyp 663235911Smavenc_type(struct ccb_getdev *cgd) 664235911Smav{ 665235911Smav int buflen; 666235911Smav unsigned char *iqd; 667235911Smav 668235911Smav if (cgd->protocol == PROTO_SEMB) { 669235911Smav iqd = (unsigned char *)&cgd->ident_data; 670235911Smav if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) 671235911Smav return (ENC_SEMB_SES); 672235911Smav else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) 673235911Smav return (ENC_SEMB_SAFT); 674235911Smav return (ENC_NONE); 675235911Smav 676235911Smav } else if (cgd->protocol != PROTO_SCSI) 677235911Smav return (ENC_NONE); 678235911Smav 679235911Smav iqd = (unsigned char *)&cgd->inq_data; 680235911Smav buflen = min(sizeof(cgd->inq_data), 681235911Smav SID_ADDITIONAL_LENGTH(&cgd->inq_data)); 682235911Smav 683235911Smav if ((iqd[0] & 0x1f) == T_ENCLOSURE) { 684239213Smjacob if ((iqd[2] & 0x7) > 2) { 685235911Smav return (ENC_SES); 686235911Smav } else { 687235911Smav return (ENC_SES_SCSI2); 688235911Smav } 689235911Smav return (ENC_NONE); 690235911Smav } 691235911Smav 692255119Smav#ifdef SES_ENABLE_PASSTHROUGH 693235911Smav if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { 694235911Smav /* 695235911Smav * PassThrough Device. 696235911Smav */ 697255119Smav return (ENC_SES_PASSTHROUGH); 698235911Smav } 699235911Smav#endif 700235911Smav 701235911Smav /* 702235911Smav * The comparison is short for a reason- 703235911Smav * some vendors were chopping it short. 704235911Smav */ 705235911Smav 706235911Smav if (buflen < SAFTE_END - 2) { 707235911Smav return (ENC_NONE); 708235911Smav } 709235911Smav 710235911Smav if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { 711235911Smav return (ENC_SAFT); 712235911Smav } 713235911Smav return (ENC_NONE); 714235911Smav} 715235911Smav 716235911Smav/*================== Enclosure Monitoring/Processing Daemon ==================*/ 717235911Smav/** 718235911Smav * \brief Queue an update request for a given action, if needed. 719235911Smav * 720235911Smav * \param enc SES softc to queue the request for. 721235911Smav * \param action Action requested. 722235911Smav */ 723235911Smavvoid 724235911Smavenc_update_request(enc_softc_t *enc, uint32_t action) 725235911Smav{ 726235911Smav if ((enc->pending_actions & (0x1 << action)) == 0) { 727235911Smav enc->pending_actions |= (0x1 << action); 728235911Smav ENC_DLOG(enc, "%s: queing requested action %d\n", 729235911Smav __func__, action); 730235911Smav if (enc->current_action == ENC_UPDATE_NONE) 731235911Smav wakeup(enc->enc_daemon); 732235911Smav } else { 733235911Smav ENC_DLOG(enc, "%s: ignoring requested action %d - " 734235911Smav "Already queued\n", __func__, action); 735235911Smav } 736235911Smav} 737235911Smav 738235911Smav/** 739235911Smav * \brief Invoke the handler of the highest priority pending 740235911Smav * state in the SES state machine. 741235911Smav * 742235911Smav * \param enc The SES instance invoking the state machine. 743235911Smav */ 744235911Smavstatic void 745235911Smavenc_fsm_step(enc_softc_t *enc) 746235911Smav{ 747235911Smav union ccb *ccb; 748235911Smav uint8_t *buf; 749235911Smav struct enc_fsm_state *cur_state; 750235911Smav int error; 751235911Smav uint32_t xfer_len; 752235911Smav 753235911Smav ENC_DLOG(enc, "%s enter %p\n", __func__, enc); 754235911Smav 755235911Smav enc->current_action = ffs(enc->pending_actions) - 1; 756235911Smav enc->pending_actions &= ~(0x1 << enc->current_action); 757235911Smav 758235911Smav cur_state = &enc->enc_fsm_states[enc->current_action]; 759235911Smav 760235911Smav buf = NULL; 761235911Smav if (cur_state->buf_size != 0) { 762235911Smav cam_periph_unlock(enc->periph); 763235911Smav buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); 764235911Smav cam_periph_lock(enc->periph); 765235911Smav } 766235911Smav 767235911Smav error = 0; 768235911Smav ccb = NULL; 769235911Smav if (cur_state->fill != NULL) { 770235911Smav ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 771235911Smav 772235911Smav error = cur_state->fill(enc, cur_state, ccb, buf); 773235911Smav if (error != 0) 774235911Smav goto done; 775235911Smav 776235911Smav error = cam_periph_runccb(ccb, cur_state->error, 777235911Smav ENC_CFLAGS, 778235911Smav ENC_FLAGS|SF_QUIET_IR, NULL); 779235911Smav } 780235911Smav 781235911Smav if (ccb != NULL) { 782235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 783235911Smav xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 784235911Smav else 785235911Smav xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; 786235911Smav } else 787235911Smav xfer_len = 0; 788235911Smav 789235911Smav cam_periph_unlock(enc->periph); 790235911Smav cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); 791235911Smav cam_periph_lock(enc->periph); 792235911Smav 793235911Smavdone: 794235911Smav ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); 795235911Smav ENC_FREE_AND_NULL(buf); 796235911Smav if (ccb != NULL) 797235911Smav xpt_release_ccb(ccb); 798235911Smav} 799235911Smav 800235911Smav/** 801235911Smav * \invariant Called with cam_periph mutex held. 802235911Smav */ 803235911Smavstatic void 804235911Smavenc_status_updater(void *arg) 805235911Smav{ 806235911Smav enc_softc_t *enc; 807235911Smav 808235911Smav enc = arg; 809235911Smav if (enc->enc_vec.poll_status != NULL) 810235911Smav enc->enc_vec.poll_status(enc); 811235911Smav} 812235911Smav 813235911Smavstatic void 814235911Smavenc_daemon(void *arg) 815235911Smav{ 816235911Smav enc_softc_t *enc; 817235911Smav 818235911Smav enc = arg; 819235911Smav 820235911Smav cam_periph_lock(enc->periph); 821235911Smav while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { 822235911Smav if (enc->pending_actions == 0) { 823235911Smav struct intr_config_hook *hook; 824235911Smav 825235911Smav /* 826235911Smav * Reset callout and msleep, or 827235911Smav * issue timed task completion 828235911Smav * status command. 829235911Smav */ 830235911Smav enc->current_action = ENC_UPDATE_NONE; 831235911Smav 832235911Smav /* 833235911Smav * We've been through our state machine at least 834235911Smav * once. Allow the transition to userland. 835235911Smav */ 836235911Smav hook = &enc->enc_boot_hold_ch; 837235911Smav if (hook->ich_func != NULL) { 838235911Smav config_intrhook_disestablish(hook); 839235911Smav hook->ich_func = NULL; 840235911Smav } 841235911Smav 842235911Smav callout_reset(&enc->status_updater, 60*hz, 843235911Smav enc_status_updater, enc); 844235911Smav 845235911Smav cam_periph_sleep(enc->periph, enc->enc_daemon, 846235911Smav PUSER, "idle", 0); 847235911Smav } else { 848235911Smav enc_fsm_step(enc); 849235911Smav } 850235911Smav } 851235911Smav enc->enc_daemon = NULL; 852235911Smav cam_periph_unlock(enc->periph); 853235980Smav cam_periph_release(enc->periph); 854235911Smav kproc_exit(0); 855235911Smav} 856235911Smav 857235911Smavstatic int 858235911Smavenc_kproc_init(enc_softc_t *enc) 859235911Smav{ 860235911Smav int result; 861235911Smav 862260387Sscottl callout_init_mtx(&enc->status_updater, cam_periph_mtx(enc->periph), 0); 863235911Smav 864235980Smav if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP) 865235980Smav return (ENXIO); 866235980Smav 867235911Smav result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, 868235911Smav /*stackpgs*/0, "enc_daemon%d", 869235911Smav enc->periph->unit_number); 870235911Smav if (result == 0) { 871235911Smav /* Do an initial load of all page data. */ 872235911Smav cam_periph_lock(enc->periph); 873235911Smav enc->enc_vec.poll_status(enc); 874235911Smav cam_periph_unlock(enc->periph); 875235980Smav } else 876235980Smav cam_periph_release(enc->periph); 877235911Smav return (result); 878235911Smav} 879235911Smav 880235911Smav/** 881235911Smav * \brief Interrupt configuration hook callback associated with 882235911Smav * enc_boot_hold_ch. 883235911Smav * 884235911Smav * Since interrupts are always functional at the time of enclosure 885235911Smav * configuration, there is nothing to be done when the callback occurs. 886235911Smav * This hook is only registered to hold up boot processing while initial 887235911Smav * eclosure processing occurs. 888235911Smav * 889235911Smav * \param arg The enclosure softc, but currently unused in this callback. 890235911Smav */ 891235911Smavstatic void 892235911Smavenc_nop_confighook_cb(void *arg __unused) 893235911Smav{ 894235911Smav} 895235911Smav 896235911Smavstatic cam_status 897235911Smavenc_ctor(struct cam_periph *periph, void *arg) 898235911Smav{ 899235911Smav cam_status status = CAM_REQ_CMP_ERR; 900235911Smav int err; 901235911Smav enc_softc_t *enc; 902235911Smav struct ccb_getdev *cgd; 903235911Smav char *tname; 904235911Smav 905235911Smav cgd = (struct ccb_getdev *)arg; 906235911Smav if (cgd == NULL) { 907235911Smav printf("enc_ctor: no getdev CCB, can't register device\n"); 908235911Smav goto out; 909235911Smav } 910235911Smav 911235911Smav enc = ENC_MALLOCZ(sizeof(*enc)); 912235911Smav if (enc == NULL) { 913235911Smav printf("enc_ctor: Unable to probe new device. " 914235911Smav "Unable to allocate enc\n"); 915235911Smav goto out; 916235911Smav } 917235911Smav enc->periph = periph; 918235911Smav enc->current_action = ENC_UPDATE_INVALID; 919235911Smav 920235911Smav enc->enc_type = enc_type(cgd); 921235911Smav sx_init(&enc->enc_cache_lock, "enccache"); 922235911Smav 923235911Smav switch (enc->enc_type) { 924235911Smav case ENC_SES: 925235911Smav case ENC_SES_SCSI2: 926235911Smav case ENC_SES_PASSTHROUGH: 927235911Smav case ENC_SEMB_SES: 928235911Smav err = ses_softc_init(enc); 929235911Smav break; 930235911Smav case ENC_SAFT: 931235911Smav case ENC_SEMB_SAFT: 932235911Smav err = safte_softc_init(enc); 933235911Smav break; 934235911Smav case ENC_NONE: 935235911Smav default: 936235911Smav ENC_FREE(enc); 937235911Smav return (CAM_REQ_CMP_ERR); 938235911Smav } 939235911Smav 940235911Smav if (err) { 941235911Smav xpt_print(periph->path, "error %d initializing\n", err); 942235911Smav goto out; 943235911Smav } 944235911Smav 945235911Smav /* 946235911Smav * Hold off userland until we have made at least one pass 947235911Smav * through our state machine so that physical path data is 948235911Smav * present. 949235911Smav */ 950235911Smav if (enc->enc_vec.poll_status != NULL) { 951235911Smav enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; 952235911Smav enc->enc_boot_hold_ch.ich_arg = enc; 953235911Smav config_intrhook_establish(&enc->enc_boot_hold_ch); 954235911Smav } 955235911Smav 956235911Smav /* 957235911Smav * The softc field is set only once the enc is fully initialized 958235911Smav * so that we can rely on this field to detect partially 959235911Smav * initialized periph objects in the AC_FOUND_DEVICE handler. 960235911Smav */ 961235911Smav periph->softc = enc; 962235911Smav 963235911Smav cam_periph_unlock(periph); 964235911Smav if (enc->enc_vec.poll_status != NULL) { 965235911Smav err = enc_kproc_init(enc); 966235911Smav if (err) { 967235911Smav xpt_print(periph->path, 968235980Smav "error %d starting enc_daemon\n", err); 969235911Smav goto out; 970235911Smav } 971235911Smav } 972237328Sken 973244014Sken /* 974244014Sken * Acquire a reference to the periph before we create the devfs 975244014Sken * instance for it. We'll release this reference once the devfs 976244014Sken * instance has been freed. 977244014Sken */ 978237328Sken if (cam_periph_acquire(periph) != CAM_REQ_CMP) { 979237328Sken xpt_print(periph->path, "%s: lost periph during " 980237328Sken "registration!\n", __func__); 981237328Sken cam_periph_lock(periph); 982237328Sken 983237328Sken return (CAM_REQ_CMP_ERR); 984237328Sken } 985237328Sken 986235911Smav enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number, 987235911Smav UID_ROOT, GID_OPERATOR, 0600, "%s%d", 988235911Smav periph->periph_name, periph->unit_number); 989237328Sken 990235911Smav cam_periph_lock(periph); 991235911Smav enc->enc_dev->si_drv1 = periph; 992235911Smav 993235911Smav enc->enc_flags |= ENC_FLAG_INITIALIZED; 994235911Smav 995235911Smav /* 996235911Smav * Add an async callback so that we get notified if this 997235911Smav * device goes away. 998235911Smav */ 999235911Smav xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); 1000235911Smav 1001235911Smav switch (enc->enc_type) { 1002235911Smav default: 1003235911Smav case ENC_NONE: 1004235911Smav tname = "No ENC device"; 1005235911Smav break; 1006235911Smav case ENC_SES_SCSI2: 1007235911Smav tname = "SCSI-2 ENC Device"; 1008235911Smav break; 1009235911Smav case ENC_SES: 1010235911Smav tname = "SCSI-3 ENC Device"; 1011235911Smav break; 1012235911Smav case ENC_SES_PASSTHROUGH: 1013235911Smav tname = "ENC Passthrough Device"; 1014235911Smav break; 1015235911Smav case ENC_SAFT: 1016235911Smav tname = "SAF-TE Compliant Device"; 1017235911Smav break; 1018235911Smav case ENC_SEMB_SES: 1019235911Smav tname = "SEMB SES Device"; 1020235911Smav break; 1021235911Smav case ENC_SEMB_SAFT: 1022235911Smav tname = "SEMB SAF-TE Device"; 1023235911Smav break; 1024235911Smav } 1025235911Smav xpt_announce_periph(periph, tname); 1026235911Smav status = CAM_REQ_CMP; 1027235911Smav 1028235911Smavout: 1029235911Smav if (status != CAM_REQ_CMP) 1030235911Smav enc_dtor(periph); 1031235911Smav return (status); 1032235911Smav} 1033235911Smav 1034