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: stable/10/sys/cam/scsi/scsi_enc.c 331633 2018-03-27 17:43:03Z brooks $"); 29235911Smav 30331633Sbrooks#include "opt_compat.h" 31331633Sbrooks 32235911Smav#include <sys/param.h> 33235911Smav 34235911Smav#include <sys/conf.h> 35235911Smav#include <sys/errno.h> 36235911Smav#include <sys/fcntl.h> 37235911Smav#include <sys/kernel.h> 38235911Smav#include <sys/kthread.h> 39235911Smav#include <sys/lock.h> 40235911Smav#include <sys/malloc.h> 41235911Smav#include <sys/mutex.h> 42331633Sbrooks#include <sys/proc.h> 43235911Smav#include <sys/queue.h> 44235911Smav#include <sys/sx.h> 45331633Sbrooks#include <sys/sysent.h> 46235911Smav#include <sys/systm.h> 47235911Smav#include <sys/sysctl.h> 48235911Smav#include <sys/types.h> 49235911Smav 50235911Smav#include <machine/stdarg.h> 51235911Smav 52235911Smav#include <cam/cam.h> 53235911Smav#include <cam/cam_ccb.h> 54235911Smav#include <cam/cam_debug.h> 55235911Smav#include <cam/cam_periph.h> 56235911Smav#include <cam/cam_xpt_periph.h> 57235911Smav 58235911Smav#include <cam/scsi/scsi_all.h> 59235911Smav#include <cam/scsi/scsi_message.h> 60235911Smav#include <cam/scsi/scsi_enc.h> 61235911Smav#include <cam/scsi/scsi_enc_internal.h> 62235911Smav 63255119Smav#include <opt_ses.h> 64255119Smav 65235911SmavMALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); 66235911Smav 67235911Smav/* Enclosure type independent driver */ 68235911Smav 69235911Smavstatic d_open_t enc_open; 70235911Smavstatic d_close_t enc_close; 71235911Smavstatic d_ioctl_t enc_ioctl; 72235911Smavstatic periph_init_t enc_init; 73235911Smavstatic periph_ctor_t enc_ctor; 74235911Smavstatic periph_oninv_t enc_oninvalidate; 75235911Smavstatic periph_dtor_t enc_dtor; 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", 96236138Sken .d_flags = D_TRACKCLOSE, 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 117237328Skenenc_devgonecb(void *arg) 118237328Sken{ 119237328Sken struct cam_periph *periph; 120244014Sken struct enc_softc *enc; 121260387Sscottl struct mtx *mtx; 122244014Sken int i; 123237328Sken 124237328Sken periph = (struct cam_periph *)arg; 125260387Sscottl mtx = cam_periph_mtx(periph); 126260387Sscottl mtx_lock(mtx); 127244014Sken enc = (struct enc_softc *)periph->softc; 128237328Sken 129244014Sken /* 130244014Sken * When we get this callback, we will get no more close calls from 131244014Sken * devfs. So if we have any dangling opens, we need to release the 132244014Sken * reference held for that particular context. 133244014Sken */ 134244014Sken for (i = 0; i < enc->open_count; i++) 135244014Sken cam_periph_release_locked(periph); 136244014Sken 137244014Sken enc->open_count = 0; 138244014Sken 139244014Sken /* 140244014Sken * Release the reference held for the device node, it is gone now. 141244014Sken */ 142244014Sken cam_periph_release_locked(periph); 143244014Sken 144244014Sken /* 145260387Sscottl * We reference the lock directly here, instead of using 146244014Sken * cam_periph_unlock(). The reason is that the final call to 147244014Sken * cam_periph_release_locked() above could result in the periph 148244014Sken * getting freed. If that is the case, dereferencing the periph 149244014Sken * with a cam_periph_unlock() call would cause a page fault. 150244014Sken */ 151260387Sscottl mtx_unlock(mtx); 152237328Sken} 153237328Sken 154237328Skenstatic void 155235911Smavenc_oninvalidate(struct cam_periph *periph) 156235911Smav{ 157235911Smav struct enc_softc *enc; 158235911Smav 159235911Smav enc = periph->softc; 160235911Smav 161235911Smav enc->enc_flags |= ENC_FLAG_INVALID; 162235911Smav 163235911Smav /* If the sub-driver has an invalidate routine, call it */ 164235911Smav if (enc->enc_vec.softc_invalidate != NULL) 165235911Smav enc->enc_vec.softc_invalidate(enc); 166235911Smav 167235911Smav /* 168235911Smav * Unregister any async callbacks. 169235911Smav */ 170235911Smav xpt_register_async(0, enc_async, periph, periph->path); 171235911Smav 172235911Smav /* 173235911Smav * Shutdown our daemon. 174235911Smav */ 175235911Smav enc->enc_flags |= ENC_FLAG_SHUTDOWN; 176235911Smav if (enc->enc_daemon != NULL) { 177235980Smav /* Signal the ses daemon to terminate. */ 178235911Smav wakeup(enc->enc_daemon); 179235911Smav } 180235911Smav callout_drain(&enc->status_updater); 181235911Smav 182237328Sken destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph); 183235911Smav} 184235911Smav 185235911Smavstatic void 186235911Smavenc_dtor(struct cam_periph *periph) 187235911Smav{ 188235911Smav struct enc_softc *enc; 189235911Smav 190235911Smav enc = periph->softc; 191235911Smav 192235911Smav /* If the sub-driver has a cleanup routine, call it */ 193235911Smav if (enc->enc_vec.softc_cleanup != NULL) 194235911Smav enc->enc_vec.softc_cleanup(enc); 195235911Smav 196235911Smav if (enc->enc_boot_hold_ch.ich_func != NULL) { 197235911Smav config_intrhook_disestablish(&enc->enc_boot_hold_ch); 198235911Smav enc->enc_boot_hold_ch.ich_func = NULL; 199235911Smav } 200235911Smav 201235911Smav ENC_FREE(enc); 202235911Smav} 203235911Smav 204235911Smavstatic void 205235911Smavenc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 206235911Smav{ 207235911Smav struct cam_periph *periph; 208235911Smav 209235911Smav periph = (struct cam_periph *)callback_arg; 210235911Smav 211235911Smav switch(code) { 212235911Smav case AC_FOUND_DEVICE: 213235911Smav { 214235911Smav struct ccb_getdev *cgd; 215235911Smav cam_status status; 216235911Smav path_id_t path_id; 217235911Smav 218235911Smav cgd = (struct ccb_getdev *)arg; 219235911Smav if (arg == NULL) { 220235911Smav break; 221235911Smav } 222235911Smav 223235911Smav if (enc_type(cgd) == ENC_NONE) { 224235911Smav /* 225235911Smav * Schedule announcement of the ENC bindings for 226235911Smav * this device if it is managed by a SEP. 227235911Smav */ 228235911Smav path_id = xpt_path_path_id(path); 229235911Smav xpt_lock_buses(); 230235911Smav TAILQ_FOREACH(periph, &encdriver.units, unit_links) { 231235911Smav struct enc_softc *softc; 232235911Smav 233235911Smav softc = (struct enc_softc *)periph->softc; 234235911Smav if (xpt_path_path_id(periph->path) != path_id 235235911Smav || softc == NULL 236235911Smav || (softc->enc_flags & ENC_FLAG_INITIALIZED) 237235911Smav == 0 238235911Smav || softc->enc_vec.device_found == NULL) 239235911Smav continue; 240235911Smav 241235911Smav softc->enc_vec.device_found(softc); 242235911Smav } 243235911Smav xpt_unlock_buses(); 244235911Smav return; 245235911Smav } 246235911Smav 247235911Smav status = cam_periph_alloc(enc_ctor, enc_oninvalidate, 248260387Sscottl enc_dtor, NULL, "ses", CAM_PERIPH_BIO, 249260387Sscottl path, enc_async, AC_FOUND_DEVICE, cgd); 250235911Smav 251235911Smav if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { 252235911Smav printf("enc_async: Unable to probe new device due to " 253235911Smav "status 0x%x\n", status); 254235911Smav } 255235911Smav break; 256235911Smav } 257235911Smav default: 258235911Smav cam_periph_async(periph, code, path, arg); 259235911Smav break; 260235911Smav } 261235911Smav} 262235911Smav 263235911Smavstatic int 264235911Smavenc_open(struct cdev *dev, int flags, int fmt, struct thread *td) 265235911Smav{ 266235911Smav struct cam_periph *periph; 267235911Smav struct enc_softc *softc; 268235911Smav int error = 0; 269235911Smav 270235911Smav periph = (struct cam_periph *)dev->si_drv1; 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; 305260387Sscottl mtx = cam_periph_mtx(periph); 306260387Sscottl mtx_lock(mtx); 307235911Smav 308244014Sken enc = periph->softc; 309244014Sken enc->open_count--; 310244014Sken 311244014Sken cam_periph_release_locked(periph); 312244014Sken 313244014Sken /* 314260387Sscottl * We reference the lock directly here, instead of using 315244014Sken * cam_periph_unlock(). The reason is that the call to 316244014Sken * cam_periph_release_locked() above could result in the periph 317244014Sken * getting freed. If that is the case, dereferencing the periph 318244014Sken * with a cam_periph_unlock() call would cause a page fault. 319244014Sken * 320244014Sken * cam_periph_release() avoids this problem using the same method, 321244014Sken * but we're manually acquiring and dropping the lock here to 322244014Sken * protect the open count and avoid another lock acquisition and 323244014Sken * release. 324244014Sken */ 325260387Sscottl mtx_unlock(mtx); 326244014Sken 327235911Smav return (0); 328235911Smav} 329235911Smav 330235911Smavint 331235911Smavenc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) 332235911Smav{ 333235911Smav struct enc_softc *softc; 334235911Smav struct cam_periph *periph; 335235911Smav 336235911Smav periph = xpt_path_periph(ccb->ccb_h.path); 337235911Smav softc = (struct enc_softc *)periph->softc; 338235911Smav 339235911Smav return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb)); 340235911Smav} 341235911Smav 342235911Smavstatic int 343235911Smavenc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, 344235911Smav struct thread *td) 345235911Smav{ 346235911Smav struct cam_periph *periph; 347235911Smav encioc_enc_status_t tmp; 348235911Smav encioc_string_t sstr; 349235911Smav encioc_elm_status_t elms; 350235911Smav encioc_elm_desc_t elmd; 351235911Smav encioc_elm_devnames_t elmdn; 352235911Smav encioc_element_t *uelm; 353235911Smav enc_softc_t *enc; 354235911Smav enc_cache_t *cache; 355235911Smav void *addr; 356235911Smav int error, i; 357235911Smav 358331633Sbrooks#ifdef COMPAT_FREEBSD32 359331633Sbrooks if (SV_PROC_FLAG(td->td_proc, SV_ILP32)) 360331633Sbrooks return (ENOTTY); 361331633Sbrooks#endif 362235911Smav 363235911Smav if (arg_addr) 364235911Smav addr = *((caddr_t *) arg_addr); 365235911Smav else 366235911Smav addr = NULL; 367235911Smav 368235911Smav periph = (struct cam_periph *)dev->si_drv1; 369235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); 370235911Smav 371235911Smav cam_periph_lock(periph); 372235911Smav enc = (struct enc_softc *)periph->softc; 373235911Smav cache = &enc->enc_cache; 374235911Smav 375235911Smav /* 376235911Smav * Now check to see whether we're initialized or not. 377235911Smav * This actually should never fail as we're not supposed 378235911Smav * to get past enc_open w/o successfully initializing 379235911Smav * things. 380235911Smav */ 381235911Smav if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 382235911Smav cam_periph_unlock(periph); 383235911Smav return (ENXIO); 384235911Smav } 385235911Smav cam_periph_unlock(periph); 386235911Smav 387235911Smav error = 0; 388235911Smav 389235911Smav CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 390235911Smav ("trying to do ioctl %#lx\n", cmd)); 391235911Smav 392235911Smav /* 393235911Smav * If this command can change the device's state, 394235911Smav * we must have the device open for writing. 395235911Smav * 396235911Smav * For commands that get information about the 397235911Smav * device- we don't need to lock the peripheral 398235911Smav * if we aren't running a command. The periph 399235911Smav * also can't go away while a user process has 400235911Smav * it open. 401235911Smav */ 402235911Smav switch (cmd) { 403235911Smav case ENCIOC_GETNELM: 404235911Smav case ENCIOC_GETELMMAP: 405235911Smav case ENCIOC_GETENCSTAT: 406235911Smav case ENCIOC_GETELMSTAT: 407235911Smav case ENCIOC_GETELMDESC: 408235911Smav case ENCIOC_GETELMDEVNAMES: 409291429Smav case ENCIOC_GETENCNAME: 410291429Smav case ENCIOC_GETENCID: 411235911Smav break; 412235911Smav default: 413235911Smav if ((flag & FWRITE) == 0) { 414235911Smav return (EBADF); 415235911Smav } 416235911Smav } 417235911Smav 418235911Smav /* 419235911Smav * XXX The values read here are only valid for the current 420235911Smav * configuration generation. We need these ioctls 421235911Smav * to also pass in/out a generation number. 422235911Smav */ 423235911Smav sx_slock(&enc->enc_cache_lock); 424235911Smav switch (cmd) { 425235911Smav case ENCIOC_GETNELM: 426235911Smav error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); 427235911Smav break; 428235911Smav 429235911Smav case ENCIOC_GETELMMAP: 430235911Smav for (uelm = addr, i = 0; i != cache->nelms; i++) { 431235911Smav encioc_element_t kelm; 432235911Smav kelm.elm_idx = i; 433235911Smav kelm.elm_subenc_id = cache->elm_map[i].subenclosure; 434235911Smav kelm.elm_type = cache->elm_map[i].enctype; 435235911Smav error = copyout(&kelm, &uelm[i], sizeof(kelm)); 436235911Smav if (error) 437235911Smav break; 438235911Smav } 439235911Smav break; 440235911Smav 441235911Smav case ENCIOC_GETENCSTAT: 442235911Smav cam_periph_lock(periph); 443235911Smav error = enc->enc_vec.get_enc_status(enc, 1); 444235911Smav if (error) { 445235911Smav cam_periph_unlock(periph); 446235911Smav break; 447235911Smav } 448235911Smav tmp = cache->enc_status; 449235911Smav cam_periph_unlock(periph); 450235911Smav error = copyout(&tmp, addr, sizeof(tmp)); 451235911Smav cache->enc_status = tmp; 452235911Smav break; 453235911Smav 454235911Smav case ENCIOC_SETENCSTAT: 455235911Smav error = copyin(addr, &tmp, sizeof(tmp)); 456235911Smav if (error) 457235911Smav break; 458235911Smav cam_periph_lock(periph); 459235911Smav error = enc->enc_vec.set_enc_status(enc, tmp, 1); 460235911Smav cam_periph_unlock(periph); 461235911Smav break; 462235911Smav 463235911Smav case ENCIOC_GETSTRING: 464235911Smav case ENCIOC_SETSTRING: 465291429Smav case ENCIOC_GETENCNAME: 466291429Smav case ENCIOC_GETENCID: 467235911Smav if (enc->enc_vec.handle_string == NULL) { 468235911Smav error = EINVAL; 469235911Smav break; 470235911Smav } 471235911Smav error = copyin(addr, &sstr, sizeof(sstr)); 472235911Smav if (error) 473235911Smav break; 474235911Smav cam_periph_lock(periph); 475235911Smav error = enc->enc_vec.handle_string(enc, &sstr, cmd); 476235911Smav cam_periph_unlock(periph); 477235911Smav break; 478235911Smav 479235911Smav case ENCIOC_GETELMSTAT: 480235911Smav error = copyin(addr, &elms, sizeof(elms)); 481235911Smav if (error) 482235911Smav break; 483235911Smav if (elms.elm_idx >= cache->nelms) { 484235911Smav error = EINVAL; 485235911Smav break; 486235911Smav } 487235911Smav cam_periph_lock(periph); 488235911Smav error = enc->enc_vec.get_elm_status(enc, &elms, 1); 489235911Smav cam_periph_unlock(periph); 490235911Smav if (error) 491235911Smav break; 492235911Smav error = copyout(&elms, addr, sizeof(elms)); 493235911Smav break; 494235911Smav 495235911Smav case ENCIOC_GETELMDESC: 496235911Smav error = copyin(addr, &elmd, sizeof(elmd)); 497235911Smav if (error) 498235911Smav break; 499235911Smav if (elmd.elm_idx >= cache->nelms) { 500235911Smav error = EINVAL; 501235911Smav break; 502235911Smav } 503235911Smav if (enc->enc_vec.get_elm_desc != NULL) { 504235911Smav error = enc->enc_vec.get_elm_desc(enc, &elmd); 505235911Smav if (error) 506235911Smav break; 507235911Smav } else 508235911Smav elmd.elm_desc_len = 0; 509235911Smav error = copyout(&elmd, addr, sizeof(elmd)); 510235911Smav break; 511235911Smav 512235911Smav case ENCIOC_GETELMDEVNAMES: 513235911Smav if (enc->enc_vec.get_elm_devnames == NULL) { 514235911Smav error = EINVAL; 515235911Smav break; 516235911Smav } 517235911Smav error = copyin(addr, &elmdn, sizeof(elmdn)); 518235911Smav if (error) 519235911Smav break; 520235911Smav if (elmdn.elm_idx >= cache->nelms) { 521235911Smav error = EINVAL; 522235911Smav break; 523235911Smav } 524235911Smav cam_periph_lock(periph); 525235911Smav error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); 526235911Smav cam_periph_unlock(periph); 527235911Smav if (error) 528235911Smav break; 529235911Smav error = copyout(&elmdn, addr, sizeof(elmdn)); 530235911Smav break; 531235911Smav 532235911Smav case ENCIOC_SETELMSTAT: 533235911Smav error = copyin(addr, &elms, sizeof(elms)); 534235911Smav if (error) 535235911Smav break; 536235911Smav 537235911Smav if (elms.elm_idx >= cache->nelms) { 538235911Smav error = EINVAL; 539235911Smav break; 540235911Smav } 541235911Smav cam_periph_lock(periph); 542235911Smav error = enc->enc_vec.set_elm_status(enc, &elms, 1); 543235911Smav cam_periph_unlock(periph); 544235911Smav 545235911Smav break; 546235911Smav 547235911Smav case ENCIOC_INIT: 548235911Smav 549235911Smav cam_periph_lock(periph); 550235911Smav error = enc->enc_vec.init_enc(enc); 551235911Smav cam_periph_unlock(periph); 552235911Smav break; 553235911Smav 554235911Smav default: 555235911Smav cam_periph_lock(periph); 556235911Smav error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); 557235911Smav cam_periph_unlock(periph); 558235911Smav break; 559235911Smav } 560235911Smav sx_sunlock(&enc->enc_cache_lock); 561235911Smav return (error); 562235911Smav} 563235911Smav 564235911Smavint 565235911Smavenc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) 566235911Smav{ 567235911Smav int error, dlen, tdlen; 568235911Smav ccb_flags ddf; 569235911Smav union ccb *ccb; 570235911Smav 571235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, 572235911Smav ("entering enc_runcmd\n")); 573235911Smav if (dptr) { 574235911Smav if ((dlen = *dlenp) < 0) { 575235911Smav dlen = -dlen; 576235911Smav ddf = CAM_DIR_OUT; 577235911Smav } else { 578235911Smav ddf = CAM_DIR_IN; 579235911Smav } 580235911Smav } else { 581235911Smav dlen = 0; 582235911Smav ddf = CAM_DIR_NONE; 583235911Smav } 584235911Smav 585235911Smav if (cdbl > IOCDBLEN) { 586235911Smav cdbl = IOCDBLEN; 587235911Smav } 588235911Smav 589242173Smav ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 590235911Smav if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { 591235911Smav tdlen = min(dlen, 1020); 592235911Smav tdlen = (tdlen + 3) & ~3; 593260387Sscottl cam_fill_ataio(&ccb->ataio, 0, NULL, ddf, 0, dptr, tdlen, 594235911Smav 30 * 1000); 595235911Smav if (cdb[0] == RECEIVE_DIAGNOSTIC) 596235911Smav ata_28bit_cmd(&ccb->ataio, 597235911Smav ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); 598235911Smav else if (cdb[0] == SEND_DIAGNOSTIC) 599235911Smav ata_28bit_cmd(&ccb->ataio, 600235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 601235911Smav 0x82, tdlen / 4); 602235911Smav else if (cdb[0] == READ_BUFFER) 603235911Smav ata_28bit_cmd(&ccb->ataio, 604235911Smav ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); 605235911Smav else 606235911Smav ata_28bit_cmd(&ccb->ataio, 607235911Smav ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 608235911Smav 0x80, tdlen / 4); 609235911Smav } else { 610235911Smav tdlen = dlen; 611260387Sscottl cam_fill_csio(&ccb->csio, 0, NULL, ddf, MSG_SIMPLE_Q_TAG, 612235911Smav dptr, dlen, sizeof (struct scsi_sense_data), cdbl, 613235911Smav 60 * 1000); 614235911Smav bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); 615235911Smav } 616235911Smav 617235911Smav error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 618235911Smav if (error) { 619235911Smav if (dptr) { 620235911Smav *dlenp = dlen; 621235911Smav } 622235911Smav } else { 623235911Smav if (dptr) { 624235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 625235911Smav *dlenp = ccb->ataio.resid; 626235911Smav else 627235911Smav *dlenp = ccb->csio.resid; 628235911Smav *dlenp += tdlen - dlen; 629235911Smav } 630235911Smav } 631235911Smav xpt_release_ccb(ccb); 632235911Smav CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 633235911Smav ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); 634235911Smav return (error); 635235911Smav} 636235911Smav 637235911Smavvoid 638235911Smavenc_log(struct enc_softc *enc, const char *fmt, ...) 639235911Smav{ 640235911Smav va_list ap; 641235911Smav 642235911Smav printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); 643235911Smav va_start(ap, fmt); 644235911Smav vprintf(fmt, ap); 645235911Smav va_end(ap); 646235911Smav} 647235911Smav 648235911Smav/* 649235911Smav * The code after this point runs on many platforms, 650235911Smav * so forgive the slightly awkward and nonconforming 651235911Smav * appearance. 652235911Smav */ 653235911Smav 654235911Smav/* 655235911Smav * Is this a device that supports enclosure services? 656235911Smav * 657240521Seadler * It's a pretty simple ruleset- if it is device type 658239213Smjacob * 0x0D (13), it's an ENCLOSURE device. 659235911Smav */ 660235911Smav 661235911Smav#define SAFTE_START 44 662235911Smav#define SAFTE_END 50 663235911Smav#define SAFTE_LEN SAFTE_END-SAFTE_START 664235911Smav 665235911Smavstatic enctyp 666235911Smavenc_type(struct ccb_getdev *cgd) 667235911Smav{ 668235911Smav int buflen; 669235911Smav unsigned char *iqd; 670235911Smav 671235911Smav if (cgd->protocol == PROTO_SEMB) { 672235911Smav iqd = (unsigned char *)&cgd->ident_data; 673235911Smav if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) 674235911Smav return (ENC_SEMB_SES); 675235911Smav else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) 676235911Smav return (ENC_SEMB_SAFT); 677235911Smav return (ENC_NONE); 678235911Smav 679235911Smav } else if (cgd->protocol != PROTO_SCSI) 680235911Smav return (ENC_NONE); 681235911Smav 682235911Smav iqd = (unsigned char *)&cgd->inq_data; 683235911Smav buflen = min(sizeof(cgd->inq_data), 684235911Smav SID_ADDITIONAL_LENGTH(&cgd->inq_data)); 685235911Smav 686235911Smav if ((iqd[0] & 0x1f) == T_ENCLOSURE) { 687239213Smjacob if ((iqd[2] & 0x7) > 2) { 688235911Smav return (ENC_SES); 689235911Smav } else { 690235911Smav return (ENC_SES_SCSI2); 691235911Smav } 692235911Smav return (ENC_NONE); 693235911Smav } 694235911Smav 695255119Smav#ifdef SES_ENABLE_PASSTHROUGH 696235911Smav if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { 697235911Smav /* 698235911Smav * PassThrough Device. 699235911Smav */ 700255119Smav return (ENC_SES_PASSTHROUGH); 701235911Smav } 702235911Smav#endif 703235911Smav 704235911Smav /* 705235911Smav * The comparison is short for a reason- 706235911Smav * some vendors were chopping it short. 707235911Smav */ 708235911Smav 709235911Smav if (buflen < SAFTE_END - 2) { 710235911Smav return (ENC_NONE); 711235911Smav } 712235911Smav 713235911Smav if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { 714235911Smav return (ENC_SAFT); 715235911Smav } 716235911Smav return (ENC_NONE); 717235911Smav} 718235911Smav 719235911Smav/*================== Enclosure Monitoring/Processing Daemon ==================*/ 720235911Smav/** 721235911Smav * \brief Queue an update request for a given action, if needed. 722235911Smav * 723235911Smav * \param enc SES softc to queue the request for. 724235911Smav * \param action Action requested. 725235911Smav */ 726235911Smavvoid 727235911Smavenc_update_request(enc_softc_t *enc, uint32_t action) 728235911Smav{ 729235911Smav if ((enc->pending_actions & (0x1 << action)) == 0) { 730235911Smav enc->pending_actions |= (0x1 << action); 731235911Smav ENC_DLOG(enc, "%s: queing requested action %d\n", 732235911Smav __func__, action); 733235911Smav if (enc->current_action == ENC_UPDATE_NONE) 734235911Smav wakeup(enc->enc_daemon); 735235911Smav } else { 736235911Smav ENC_DLOG(enc, "%s: ignoring requested action %d - " 737235911Smav "Already queued\n", __func__, action); 738235911Smav } 739235911Smav} 740235911Smav 741235911Smav/** 742235911Smav * \brief Invoke the handler of the highest priority pending 743235911Smav * state in the SES state machine. 744235911Smav * 745235911Smav * \param enc The SES instance invoking the state machine. 746235911Smav */ 747235911Smavstatic void 748235911Smavenc_fsm_step(enc_softc_t *enc) 749235911Smav{ 750235911Smav union ccb *ccb; 751235911Smav uint8_t *buf; 752235911Smav struct enc_fsm_state *cur_state; 753235911Smav int error; 754235911Smav uint32_t xfer_len; 755235911Smav 756235911Smav ENC_DLOG(enc, "%s enter %p\n", __func__, enc); 757235911Smav 758235911Smav enc->current_action = ffs(enc->pending_actions) - 1; 759235911Smav enc->pending_actions &= ~(0x1 << enc->current_action); 760235911Smav 761235911Smav cur_state = &enc->enc_fsm_states[enc->current_action]; 762235911Smav 763235911Smav buf = NULL; 764235911Smav if (cur_state->buf_size != 0) { 765235911Smav cam_periph_unlock(enc->periph); 766235911Smav buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); 767235911Smav cam_periph_lock(enc->periph); 768235911Smav } 769235911Smav 770235911Smav error = 0; 771235911Smav ccb = NULL; 772235911Smav if (cur_state->fill != NULL) { 773235911Smav ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 774235911Smav 775235911Smav error = cur_state->fill(enc, cur_state, ccb, buf); 776235911Smav if (error != 0) 777235911Smav goto done; 778235911Smav 779235911Smav error = cam_periph_runccb(ccb, cur_state->error, 780235911Smav ENC_CFLAGS, 781235911Smav ENC_FLAGS|SF_QUIET_IR, NULL); 782235911Smav } 783235911Smav 784235911Smav if (ccb != NULL) { 785235911Smav if (ccb->ccb_h.func_code == XPT_ATA_IO) 786235911Smav xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 787235911Smav else 788235911Smav xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; 789235911Smav } else 790235911Smav xfer_len = 0; 791235911Smav 792235911Smav cam_periph_unlock(enc->periph); 793235911Smav cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); 794235911Smav cam_periph_lock(enc->periph); 795235911Smav 796235911Smavdone: 797235911Smav ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); 798235911Smav ENC_FREE_AND_NULL(buf); 799235911Smav if (ccb != NULL) 800235911Smav xpt_release_ccb(ccb); 801235911Smav} 802235911Smav 803235911Smav/** 804235911Smav * \invariant Called with cam_periph mutex held. 805235911Smav */ 806235911Smavstatic void 807235911Smavenc_status_updater(void *arg) 808235911Smav{ 809235911Smav enc_softc_t *enc; 810235911Smav 811235911Smav enc = arg; 812235911Smav if (enc->enc_vec.poll_status != NULL) 813235911Smav enc->enc_vec.poll_status(enc); 814235911Smav} 815235911Smav 816235911Smavstatic void 817235911Smavenc_daemon(void *arg) 818235911Smav{ 819235911Smav enc_softc_t *enc; 820235911Smav 821235911Smav enc = arg; 822235911Smav 823235911Smav cam_periph_lock(enc->periph); 824235911Smav while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { 825235911Smav if (enc->pending_actions == 0) { 826235911Smav struct intr_config_hook *hook; 827235911Smav 828235911Smav /* 829235911Smav * Reset callout and msleep, or 830235911Smav * issue timed task completion 831235911Smav * status command. 832235911Smav */ 833235911Smav enc->current_action = ENC_UPDATE_NONE; 834235911Smav 835235911Smav /* 836235911Smav * We've been through our state machine at least 837235911Smav * once. Allow the transition to userland. 838235911Smav */ 839235911Smav hook = &enc->enc_boot_hold_ch; 840235911Smav if (hook->ich_func != NULL) { 841235911Smav config_intrhook_disestablish(hook); 842235911Smav hook->ich_func = NULL; 843235911Smav } 844235911Smav 845235911Smav callout_reset(&enc->status_updater, 60*hz, 846235911Smav enc_status_updater, enc); 847235911Smav 848235911Smav cam_periph_sleep(enc->periph, enc->enc_daemon, 849235911Smav PUSER, "idle", 0); 850235911Smav } else { 851235911Smav enc_fsm_step(enc); 852235911Smav } 853235911Smav } 854235911Smav enc->enc_daemon = NULL; 855235911Smav cam_periph_unlock(enc->periph); 856235980Smav cam_periph_release(enc->periph); 857235911Smav kproc_exit(0); 858235911Smav} 859235911Smav 860235911Smavstatic int 861235911Smavenc_kproc_init(enc_softc_t *enc) 862235911Smav{ 863235911Smav int result; 864235911Smav 865260387Sscottl callout_init_mtx(&enc->status_updater, cam_periph_mtx(enc->periph), 0); 866235911Smav 867235980Smav if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP) 868235980Smav return (ENXIO); 869235980Smav 870235911Smav result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, 871235911Smav /*stackpgs*/0, "enc_daemon%d", 872235911Smav enc->periph->unit_number); 873235911Smav if (result == 0) { 874235911Smav /* Do an initial load of all page data. */ 875235911Smav cam_periph_lock(enc->periph); 876235911Smav enc->enc_vec.poll_status(enc); 877235911Smav cam_periph_unlock(enc->periph); 878235980Smav } else 879235980Smav cam_periph_release(enc->periph); 880235911Smav return (result); 881235911Smav} 882235911Smav 883235911Smav/** 884235911Smav * \brief Interrupt configuration hook callback associated with 885235911Smav * enc_boot_hold_ch. 886235911Smav * 887235911Smav * Since interrupts are always functional at the time of enclosure 888235911Smav * configuration, there is nothing to be done when the callback occurs. 889235911Smav * This hook is only registered to hold up boot processing while initial 890235911Smav * eclosure processing occurs. 891235911Smav * 892235911Smav * \param arg The enclosure softc, but currently unused in this callback. 893235911Smav */ 894235911Smavstatic void 895235911Smavenc_nop_confighook_cb(void *arg __unused) 896235911Smav{ 897235911Smav} 898235911Smav 899235911Smavstatic cam_status 900235911Smavenc_ctor(struct cam_periph *periph, void *arg) 901235911Smav{ 902235911Smav cam_status status = CAM_REQ_CMP_ERR; 903235911Smav int err; 904235911Smav enc_softc_t *enc; 905235911Smav struct ccb_getdev *cgd; 906235911Smav char *tname; 907294978Skib struct make_dev_args args; 908235911Smav 909235911Smav cgd = (struct ccb_getdev *)arg; 910235911Smav if (cgd == NULL) { 911235911Smav printf("enc_ctor: no getdev CCB, can't register device\n"); 912235911Smav goto out; 913235911Smav } 914235911Smav 915235911Smav enc = ENC_MALLOCZ(sizeof(*enc)); 916235911Smav if (enc == NULL) { 917235911Smav printf("enc_ctor: Unable to probe new device. " 918235911Smav "Unable to allocate enc\n"); 919235911Smav goto out; 920235911Smav } 921235911Smav enc->periph = periph; 922235911Smav enc->current_action = ENC_UPDATE_INVALID; 923235911Smav 924235911Smav enc->enc_type = enc_type(cgd); 925235911Smav sx_init(&enc->enc_cache_lock, "enccache"); 926235911Smav 927235911Smav switch (enc->enc_type) { 928235911Smav case ENC_SES: 929235911Smav case ENC_SES_SCSI2: 930235911Smav case ENC_SES_PASSTHROUGH: 931235911Smav case ENC_SEMB_SES: 932235911Smav err = ses_softc_init(enc); 933235911Smav break; 934235911Smav case ENC_SAFT: 935235911Smav case ENC_SEMB_SAFT: 936235911Smav err = safte_softc_init(enc); 937235911Smav break; 938235911Smav case ENC_NONE: 939235911Smav default: 940235911Smav ENC_FREE(enc); 941235911Smav return (CAM_REQ_CMP_ERR); 942235911Smav } 943235911Smav 944235911Smav if (err) { 945235911Smav xpt_print(periph->path, "error %d initializing\n", err); 946235911Smav goto out; 947235911Smav } 948235911Smav 949235911Smav /* 950235911Smav * Hold off userland until we have made at least one pass 951235911Smav * through our state machine so that physical path data is 952235911Smav * present. 953235911Smav */ 954235911Smav if (enc->enc_vec.poll_status != NULL) { 955235911Smav enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; 956235911Smav enc->enc_boot_hold_ch.ich_arg = enc; 957235911Smav config_intrhook_establish(&enc->enc_boot_hold_ch); 958235911Smav } 959235911Smav 960235911Smav /* 961235911Smav * The softc field is set only once the enc is fully initialized 962235911Smav * so that we can rely on this field to detect partially 963235911Smav * initialized periph objects in the AC_FOUND_DEVICE handler. 964235911Smav */ 965235911Smav periph->softc = enc; 966235911Smav 967235911Smav cam_periph_unlock(periph); 968235911Smav if (enc->enc_vec.poll_status != NULL) { 969235911Smav err = enc_kproc_init(enc); 970235911Smav if (err) { 971235911Smav xpt_print(periph->path, 972235980Smav "error %d starting enc_daemon\n", err); 973235911Smav goto out; 974235911Smav } 975235911Smav } 976237328Sken 977244014Sken /* 978244014Sken * Acquire a reference to the periph before we create the devfs 979244014Sken * instance for it. We'll release this reference once the devfs 980244014Sken * instance has been freed. 981244014Sken */ 982237328Sken if (cam_periph_acquire(periph) != CAM_REQ_CMP) { 983237328Sken xpt_print(periph->path, "%s: lost periph during " 984237328Sken "registration!\n", __func__); 985237328Sken cam_periph_lock(periph); 986237328Sken 987237328Sken return (CAM_REQ_CMP_ERR); 988237328Sken } 989237328Sken 990294978Skib make_dev_args_init(&args); 991294978Skib args.mda_devsw = &enc_cdevsw; 992294978Skib args.mda_unit = periph->unit_number; 993294978Skib args.mda_uid = UID_ROOT; 994294978Skib args.mda_gid = GID_OPERATOR; 995294978Skib args.mda_mode = 0600; 996294978Skib args.mda_si_drv1 = periph; 997294978Skib err = make_dev_s(&args, &enc->enc_dev, "%s%d", periph->periph_name, 998294978Skib periph->unit_number); 999235911Smav cam_periph_lock(periph); 1000294978Skib if (err != 0) { 1001294978Skib cam_periph_release_locked(periph); 1002294978Skib return (CAM_REQ_CMP_ERR); 1003294978Skib } 1004235911Smav 1005235911Smav enc->enc_flags |= ENC_FLAG_INITIALIZED; 1006235911Smav 1007235911Smav /* 1008235911Smav * Add an async callback so that we get notified if this 1009235911Smav * device goes away. 1010235911Smav */ 1011235911Smav xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); 1012235911Smav 1013235911Smav switch (enc->enc_type) { 1014235911Smav default: 1015235911Smav case ENC_NONE: 1016235911Smav tname = "No ENC device"; 1017235911Smav break; 1018235911Smav case ENC_SES_SCSI2: 1019235911Smav tname = "SCSI-2 ENC Device"; 1020235911Smav break; 1021235911Smav case ENC_SES: 1022235911Smav tname = "SCSI-3 ENC Device"; 1023235911Smav break; 1024235911Smav case ENC_SES_PASSTHROUGH: 1025235911Smav tname = "ENC Passthrough Device"; 1026235911Smav break; 1027235911Smav case ENC_SAFT: 1028235911Smav tname = "SAF-TE Compliant Device"; 1029235911Smav break; 1030235911Smav case ENC_SEMB_SES: 1031235911Smav tname = "SEMB SES Device"; 1032235911Smav break; 1033235911Smav case ENC_SEMB_SAFT: 1034235911Smav tname = "SEMB SAF-TE Device"; 1035235911Smav break; 1036235911Smav } 1037235911Smav xpt_announce_periph(periph, tname); 1038235911Smav status = CAM_REQ_CMP; 1039235911Smav 1040235911Smavout: 1041235911Smav if (status != CAM_REQ_CMP) 1042235911Smav enc_dtor(periph); 1043235911Smav return (status); 1044235911Smav} 1045235911Smav 1046