1 1.96 andvar /* $NetBSD: ch.c,v 1.96 2025/08/18 20:59:56 andvar Exp $ */ 2 1.8 cgd 3 1.39 thorpej /*- 4 1.63 mycroft * Copyright (c) 1996, 1997, 1998, 1999, 2004 The NetBSD Foundation, Inc. 5 1.20 thorpej * All rights reserved. 6 1.20 thorpej * 7 1.39 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.39 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.39 thorpej * NASA Ames Research Center. 10 1.6 mycroft * 11 1.6 mycroft * Redistribution and use in source and binary forms, with or without 12 1.6 mycroft * modification, are permitted provided that the following conditions 13 1.6 mycroft * are met: 14 1.6 mycroft * 1. Redistributions of source code must retain the above copyright 15 1.6 mycroft * notice, this list of conditions and the following disclaimer. 16 1.6 mycroft * 2. Redistributions in binary form must reproduce the above copyright 17 1.6 mycroft * notice, this list of conditions and the following disclaimer in the 18 1.6 mycroft * documentation and/or other materials provided with the distribution. 19 1.6 mycroft * 20 1.39 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.39 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.39 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.39 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.39 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.39 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.39 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.39 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.39 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.39 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.39 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.6 mycroft */ 32 1.48 lukem 33 1.48 lukem #include <sys/cdefs.h> 34 1.96 andvar __KERNEL_RCSID(0, "$NetBSD: ch.c,v 1.96 2025/08/18 20:59:56 andvar Exp $"); 35 1.1 cgd 36 1.1 cgd #include <sys/param.h> 37 1.1 cgd #include <sys/systm.h> 38 1.39 thorpej #include <sys/kernel.h> 39 1.1 cgd #include <sys/errno.h> 40 1.1 cgd #include <sys/ioctl.h> 41 1.1 cgd #include <sys/buf.h> 42 1.1 cgd #include <sys/proc.h> 43 1.69 perry #include <sys/chio.h> 44 1.6 mycroft #include <sys/device.h> 45 1.20 thorpej #include <sys/malloc.h> 46 1.21 christos #include <sys/conf.h> 47 1.22 thorpej #include <sys/fcntl.h> 48 1.39 thorpej #include <sys/vnode.h> 49 1.39 thorpej #include <sys/time.h> 50 1.39 thorpej #include <sys/select.h> 51 1.39 thorpej #include <sys/poll.h> 52 1.1 cgd 53 1.27 bouyer #include <dev/scsipi/scsipi_all.h> 54 1.27 bouyer #include <dev/scsipi/scsi_all.h> 55 1.27 bouyer #include <dev/scsipi/scsi_changer.h> 56 1.27 bouyer #include <dev/scsipi/scsiconf.h> 57 1.1 cgd 58 1.20 thorpej #define CHRETRIES 2 59 1.88 kardel #define CHTIMEOUT (5 * 60 * 1000) 60 1.88 kardel 61 1.20 thorpej #define CHUNIT(x) (minor((x))) 62 1.20 thorpej 63 1.20 thorpej struct ch_softc { 64 1.87 chs device_t sc_dev; /* generic device info */ 65 1.44 bouyer struct scsipi_periph *sc_periph;/* our periph data */ 66 1.20 thorpej 67 1.67 reinoud u_int sc_events; /* event bitmask */ 68 1.39 thorpej struct selinfo sc_selq; /* select/poll queue for events */ 69 1.39 thorpej 70 1.39 thorpej int sc_flags; /* misc. info */ 71 1.39 thorpej 72 1.20 thorpej int sc_picker; /* current picker */ 73 1.1 cgd 74 1.20 thorpej /* 75 1.20 thorpej * The following information is obtained from the 76 1.20 thorpej * element address assignment page. 77 1.20 thorpej */ 78 1.20 thorpej int sc_firsts[4]; /* firsts, indexed by CHET_* */ 79 1.20 thorpej int sc_counts[4]; /* counts, indexed by CHET_* */ 80 1.1 cgd 81 1.20 thorpej /* 82 1.20 thorpej * The following mask defines the legal combinations 83 1.20 thorpej * of elements for the MOVE MEDIUM command. 84 1.20 thorpej */ 85 1.67 reinoud u_int8_t sc_movemask[4]; 86 1.20 thorpej 87 1.20 thorpej /* 88 1.20 thorpej * As above, but for EXCHANGE MEDIUM. 89 1.20 thorpej */ 90 1.67 reinoud u_int8_t sc_exchangemask[4]; 91 1.1 cgd 92 1.26 thorpej /* 93 1.26 thorpej * Quirks; see below. 94 1.26 thorpej */ 95 1.26 thorpej int sc_settledelay; /* delay for settle */ 96 1.26 thorpej 97 1.6 mycroft }; 98 1.6 mycroft 99 1.20 thorpej /* sc_flags */ 100 1.39 thorpej #define CHF_ROTATE 0x01 /* picker can rotate */ 101 1.20 thorpej 102 1.20 thorpej /* Autoconfiguration glue */ 103 1.84 cegger static int chmatch(device_t, cfdata_t, void *); 104 1.84 cegger static void chattach(device_t, device_t, void *); 105 1.6 mycroft 106 1.87 chs CFATTACH_DECL_NEW(ch, sizeof(struct ch_softc), 107 1.53 thorpej chmatch, chattach, NULL, NULL); 108 1.17 thorpej 109 1.32 thorpej extern struct cfdriver ch_cd; 110 1.1 cgd 111 1.61 thorpej static struct scsipi_inquiry_pattern ch_patterns[] = { 112 1.20 thorpej {T_CHANGER, T_REMOV, 113 1.20 thorpej "", "", ""}, 114 1.50 gehenna }; 115 1.50 gehenna 116 1.61 thorpej static dev_type_open(chopen); 117 1.61 thorpej static dev_type_close(chclose); 118 1.61 thorpej static dev_type_read(chread); 119 1.61 thorpej static dev_type_ioctl(chioctl); 120 1.61 thorpej static dev_type_poll(chpoll); 121 1.61 thorpej static dev_type_kqfilter(chkqfilter); 122 1.50 gehenna 123 1.50 gehenna const struct cdevsw ch_cdevsw = { 124 1.89 dholland .d_open = chopen, 125 1.89 dholland .d_close = chclose, 126 1.89 dholland .d_read = chread, 127 1.89 dholland .d_write = nowrite, 128 1.89 dholland .d_ioctl = chioctl, 129 1.89 dholland .d_stop = nostop, 130 1.89 dholland .d_tty = notty, 131 1.89 dholland .d_poll = chpoll, 132 1.89 dholland .d_mmap = nommap, 133 1.89 dholland .d_kqfilter = chkqfilter, 134 1.90 dholland .d_discard = nodiscard, 135 1.91 mlelstv .d_flag = D_OTHER | D_MPSAFE 136 1.20 thorpej }; 137 1.20 thorpej 138 1.20 thorpej /* SCSI glue */ 139 1.61 thorpej static int ch_interpret_sense(struct scsipi_xfer *); 140 1.39 thorpej 141 1.61 thorpej static const struct scsipi_periphsw ch_switch = { 142 1.39 thorpej ch_interpret_sense, /* check our error handler first */ 143 1.39 thorpej NULL, /* no queue; our commands are synchronous */ 144 1.39 thorpej NULL, /* have no async handler */ 145 1.39 thorpej NULL, /* nothing to be done when xfer is done */ 146 1.6 mycroft }; 147 1.6 mycroft 148 1.61 thorpej static int ch_move(struct ch_softc *, struct changer_move_request *); 149 1.61 thorpej static int ch_exchange(struct ch_softc *, 150 1.61 thorpej struct changer_exchange_request *); 151 1.61 thorpej static int ch_position(struct ch_softc *, 152 1.61 thorpej struct changer_position_request *); 153 1.61 thorpej static int ch_ielem(struct ch_softc *); 154 1.67 reinoud static int ch_ousergetelemstatus(struct ch_softc *, int, u_int8_t *); 155 1.61 thorpej static int ch_usergetelemstatus(struct ch_softc *, 156 1.61 thorpej struct changer_element_status_request *); 157 1.61 thorpej static int ch_getelemstatus(struct ch_softc *, int, int, void *, 158 1.61 thorpej size_t, int, int); 159 1.61 thorpej static int ch_setvoltag(struct ch_softc *, 160 1.61 thorpej struct changer_set_voltag_request *); 161 1.61 thorpej static int ch_get_params(struct ch_softc *, int); 162 1.61 thorpej static void ch_get_quirks(struct ch_softc *, 163 1.61 thorpej struct scsipi_inquiry_pattern *); 164 1.67 reinoud static void ch_event(struct ch_softc *, u_int); 165 1.67 reinoud static int ch_map_element(struct ch_softc *, u_int16_t, int *, int *); 166 1.61 thorpej 167 1.61 thorpej static void ch_voltag_convert_in(const struct changer_volume_tag *, 168 1.61 thorpej struct changer_voltag *); 169 1.61 thorpej static int ch_voltag_convert_out(const struct changer_voltag *, 170 1.61 thorpej struct changer_volume_tag *); 171 1.26 thorpej 172 1.26 thorpej /* 173 1.26 thorpej * SCSI changer quirks. 174 1.26 thorpej */ 175 1.26 thorpej struct chquirk { 176 1.27 bouyer struct scsipi_inquiry_pattern cq_match; /* device id pattern */ 177 1.26 thorpej int cq_settledelay; /* settle delay, in seconds */ 178 1.26 thorpej }; 179 1.26 thorpej 180 1.61 thorpej static const struct chquirk chquirks[] = { 181 1.26 thorpej {{T_CHANGER, T_REMOV, 182 1.26 thorpej "SPECTRA", "9000", "0200"}, 183 1.26 thorpej 75}, 184 1.26 thorpej }; 185 1.13 mycroft 186 1.61 thorpej static int 187 1.84 cegger chmatch(device_t parent, cfdata_t match, 188 1.75 christos void *aux) 189 1.13 mycroft { 190 1.27 bouyer struct scsipibus_attach_args *sa = aux; 191 1.13 mycroft int priority; 192 1.13 mycroft 193 1.27 bouyer (void)scsipi_inqmatch(&sa->sa_inqbuf, 194 1.77 christos (void *)ch_patterns, sizeof(ch_patterns) / sizeof(ch_patterns[0]), 195 1.13 mycroft sizeof(ch_patterns[0]), &priority); 196 1.20 thorpej 197 1.13 mycroft return (priority); 198 1.13 mycroft } 199 1.13 mycroft 200 1.61 thorpej static void 201 1.84 cegger chattach(device_t parent, device_t self, void *aux) 202 1.1 cgd { 203 1.73 thorpej struct ch_softc *sc = device_private(self); 204 1.27 bouyer struct scsipibus_attach_args *sa = aux; 205 1.44 bouyer struct scsipi_periph *periph = sa->sa_periph; 206 1.20 thorpej 207 1.87 chs sc->sc_dev = self; 208 1.79 rmind selinit(&sc->sc_selq); 209 1.79 rmind 210 1.20 thorpej /* Glue into the SCSI bus */ 211 1.44 bouyer sc->sc_periph = periph; 212 1.87 chs periph->periph_dev = sc->sc_dev; 213 1.44 bouyer periph->periph_switch = &ch_switch; 214 1.1 cgd 215 1.24 christos printf("\n"); 216 1.1 cgd 217 1.6 mycroft /* 218 1.26 thorpej * Find out our device's quirks. 219 1.26 thorpej */ 220 1.27 bouyer ch_get_quirks(sc, &sa->sa_inqbuf); 221 1.26 thorpej 222 1.26 thorpej /* 223 1.26 thorpej * Some changers require a long time to settle out, to do 224 1.26 thorpej * tape inventory, for instance. 225 1.26 thorpej */ 226 1.26 thorpej if (sc->sc_settledelay) { 227 1.26 thorpej printf("%s: waiting %d seconds for changer to settle...\n", 228 1.87 chs device_xname(sc->sc_dev), sc->sc_settledelay); 229 1.26 thorpej delay(1000000 * sc->sc_settledelay); 230 1.26 thorpej } 231 1.26 thorpej 232 1.26 thorpej /* 233 1.20 thorpej * Get information about the device. Note we can't use 234 1.20 thorpej * interrupts yet. 235 1.6 mycroft */ 236 1.40 thorpej if (ch_get_params(sc, XS_CTL_DISCOVERY|XS_CTL_IGNORE_MEDIA_CHANGE)) 237 1.87 chs printf("%s: offline\n", device_xname(sc->sc_dev)); 238 1.20 thorpej else { 239 1.26 thorpej #define PLURAL(c) (c) == 1 ? "" : "s" 240 1.26 thorpej printf("%s: %d slot%s, %d drive%s, %d picker%s, %d portal%s\n", 241 1.87 chs device_xname(sc->sc_dev), 242 1.26 thorpej sc->sc_counts[CHET_ST], PLURAL(sc->sc_counts[CHET_ST]), 243 1.26 thorpej sc->sc_counts[CHET_DT], PLURAL(sc->sc_counts[CHET_DT]), 244 1.26 thorpej sc->sc_counts[CHET_MT], PLURAL(sc->sc_counts[CHET_MT]), 245 1.26 thorpej sc->sc_counts[CHET_IE], PLURAL(sc->sc_counts[CHET_IE])); 246 1.26 thorpej #undef PLURAL 247 1.20 thorpej #ifdef CHANGER_DEBUG 248 1.24 christos printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", 249 1.87 chs device_xname(sc->sc_dev), 250 1.20 thorpej sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], 251 1.20 thorpej sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); 252 1.24 christos printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", 253 1.87 chs device_xname(sc->sc_dev), 254 1.20 thorpej sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], 255 1.20 thorpej sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); 256 1.20 thorpej #endif /* CHANGER_DEBUG */ 257 1.20 thorpej } 258 1.6 mycroft 259 1.20 thorpej /* Default the current picker. */ 260 1.20 thorpej sc->sc_picker = sc->sc_firsts[CHET_MT]; 261 1.1 cgd } 262 1.1 cgd 263 1.61 thorpej static int 264 1.76 christos chopen(dev_t dev, int flags, int fmt, struct lwp *l) 265 1.1 cgd { 266 1.20 thorpej struct ch_softc *sc; 267 1.44 bouyer struct scsipi_periph *periph; 268 1.44 bouyer struct scsipi_adapter *adapt; 269 1.35 thorpej int unit, error; 270 1.6 mycroft 271 1.6 mycroft unit = CHUNIT(dev); 272 1.82 tsutsui sc = device_lookup_private(&ch_cd, unit); 273 1.82 tsutsui if (sc == NULL) 274 1.20 thorpej return (ENXIO); 275 1.12 mycroft 276 1.44 bouyer periph = sc->sc_periph; 277 1.44 bouyer adapt = periph->periph_channel->chan_adapter; 278 1.44 bouyer 279 1.12 mycroft /* 280 1.20 thorpej * Only allow one open at a time. 281 1.12 mycroft */ 282 1.44 bouyer if (periph->periph_flags & PERIPH_OPEN) 283 1.20 thorpej return (EBUSY); 284 1.20 thorpej 285 1.44 bouyer if ((error = scsipi_adapter_addref(adapt)) != 0) 286 1.35 thorpej return (error); 287 1.35 thorpej 288 1.6 mycroft /* 289 1.39 thorpej * Make sure the unit is on-line. If a UNIT ATTENTION 290 1.39 thorpej * occurs, we will mark that an Init-Element-Status is 291 1.39 thorpej * needed in ch_get_params(). 292 1.39 thorpej * 293 1.39 thorpej * We ignore NOT READY in case e.g a magazine isn't actually 294 1.39 thorpej * loaded into the changer or a tape isn't in the drive. 295 1.6 mycroft */ 296 1.44 bouyer error = scsipi_test_unit_ready(periph, XS_CTL_IGNORE_NOT_READY); 297 1.21 christos if (error) 298 1.13 mycroft goto bad; 299 1.6 mycroft 300 1.49 bouyer periph->periph_flags |= PERIPH_OPEN; 301 1.49 bouyer 302 1.6 mycroft /* 303 1.20 thorpej * Make sure our parameters are up to date. 304 1.6 mycroft */ 305 1.21 christos if ((error = ch_get_params(sc, 0)) != 0) 306 1.12 mycroft goto bad; 307 1.6 mycroft 308 1.20 thorpej return (0); 309 1.12 mycroft 310 1.20 thorpej bad: 311 1.44 bouyer scsipi_adapter_delref(adapt); 312 1.44 bouyer periph->periph_flags &= ~PERIPH_OPEN; 313 1.20 thorpej return (error); 314 1.6 mycroft } 315 1.6 mycroft 316 1.61 thorpej static int 317 1.76 christos chclose(dev_t dev, int flags, int fmt, struct lwp *l) 318 1.1 cgd { 319 1.82 tsutsui struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); 320 1.44 bouyer struct scsipi_periph *periph = sc->sc_periph; 321 1.44 bouyer struct scsipi_adapter *adapt = periph->periph_channel->chan_adapter; 322 1.36 thorpej 323 1.44 bouyer scsipi_wait_drain(periph); 324 1.6 mycroft 325 1.44 bouyer scsipi_adapter_delref(adapt); 326 1.39 thorpej 327 1.39 thorpej sc->sc_events = 0; 328 1.39 thorpej 329 1.44 bouyer periph->periph_flags &= ~PERIPH_OPEN; 330 1.20 thorpej return (0); 331 1.6 mycroft } 332 1.6 mycroft 333 1.61 thorpej static int 334 1.76 christos chread(dev_t dev, struct uio *uio, int flags) 335 1.39 thorpej { 336 1.82 tsutsui struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); 337 1.39 thorpej int error; 338 1.39 thorpej 339 1.39 thorpej if (uio->uio_resid != CHANGER_EVENT_SIZE) 340 1.39 thorpej return (EINVAL); 341 1.39 thorpej 342 1.39 thorpej /* 343 1.39 thorpej * Read never blocks; if there are no events pending, we just 344 1.39 thorpej * return an all-clear bitmask. 345 1.39 thorpej */ 346 1.39 thorpej error = uiomove(&sc->sc_events, CHANGER_EVENT_SIZE, uio); 347 1.39 thorpej if (error == 0) 348 1.39 thorpej sc->sc_events = 0; 349 1.39 thorpej return (error); 350 1.39 thorpej } 351 1.39 thorpej 352 1.61 thorpej static int 353 1.77 christos chioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 354 1.6 mycroft { 355 1.82 tsutsui struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); 356 1.20 thorpej int error = 0; 357 1.22 thorpej 358 1.22 thorpej /* 359 1.22 thorpej * If this command can change the device's state, we must 360 1.22 thorpej * have the device open for writing. 361 1.22 thorpej */ 362 1.22 thorpej switch (cmd) { 363 1.22 thorpej case CHIOGPICKER: 364 1.22 thorpej case CHIOGPARAMS: 365 1.39 thorpej case OCHIOGSTATUS: 366 1.22 thorpej break; 367 1.22 thorpej 368 1.22 thorpej default: 369 1.22 thorpej if ((flags & FWRITE) == 0) 370 1.22 thorpej return (EBADF); 371 1.22 thorpej } 372 1.20 thorpej 373 1.20 thorpej switch (cmd) { 374 1.20 thorpej case CHIOMOVE: 375 1.39 thorpej error = ch_move(sc, (struct changer_move_request *)data); 376 1.20 thorpej break; 377 1.20 thorpej 378 1.20 thorpej case CHIOEXCHANGE: 379 1.39 thorpej error = ch_exchange(sc, 380 1.39 thorpej (struct changer_exchange_request *)data); 381 1.20 thorpej break; 382 1.20 thorpej 383 1.20 thorpej case CHIOPOSITION: 384 1.39 thorpej error = ch_position(sc, 385 1.39 thorpej (struct changer_position_request *)data); 386 1.20 thorpej break; 387 1.20 thorpej 388 1.20 thorpej case CHIOGPICKER: 389 1.20 thorpej *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; 390 1.20 thorpej break; 391 1.20 thorpej 392 1.39 thorpej case CHIOSPICKER: 393 1.39 thorpej { 394 1.20 thorpej int new_picker = *(int *)data; 395 1.20 thorpej 396 1.20 thorpej if (new_picker > (sc->sc_counts[CHET_MT] - 1)) 397 1.20 thorpej return (EINVAL); 398 1.20 thorpej sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; 399 1.39 thorpej break; 400 1.39 thorpej } 401 1.20 thorpej 402 1.39 thorpej case CHIOGPARAMS: 403 1.39 thorpej { 404 1.20 thorpej struct changer_params *cp = (struct changer_params *)data; 405 1.20 thorpej 406 1.20 thorpej cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; 407 1.20 thorpej cp->cp_npickers = sc->sc_counts[CHET_MT]; 408 1.20 thorpej cp->cp_nslots = sc->sc_counts[CHET_ST]; 409 1.20 thorpej cp->cp_nportals = sc->sc_counts[CHET_IE]; 410 1.20 thorpej cp->cp_ndrives = sc->sc_counts[CHET_DT]; 411 1.39 thorpej break; 412 1.39 thorpej } 413 1.20 thorpej 414 1.28 mjacob case CHIOIELEM: 415 1.28 mjacob error = ch_ielem(sc); 416 1.41 mjacob if (error == 0) { 417 1.44 bouyer sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; 418 1.41 mjacob } 419 1.28 mjacob break; 420 1.28 mjacob 421 1.39 thorpej case OCHIOGSTATUS: 422 1.39 thorpej { 423 1.39 thorpej struct ochanger_element_status_request *cesr = 424 1.39 thorpej (struct ochanger_element_status_request *)data; 425 1.39 thorpej 426 1.39 thorpej error = ch_ousergetelemstatus(sc, cesr->cesr_type, 427 1.39 thorpej cesr->cesr_data); 428 1.39 thorpej break; 429 1.39 thorpej } 430 1.39 thorpej 431 1.39 thorpej case CHIOGSTATUS: 432 1.39 thorpej error = ch_usergetelemstatus(sc, 433 1.39 thorpej (struct changer_element_status_request *)data); 434 1.39 thorpej break; 435 1.20 thorpej 436 1.39 thorpej case CHIOSVOLTAG: 437 1.39 thorpej error = ch_setvoltag(sc, 438 1.39 thorpej (struct changer_set_voltag_request *)data); 439 1.39 thorpej break; 440 1.6 mycroft 441 1.20 thorpej /* Implement prevent/allow? */ 442 1.6 mycroft 443 1.1 cgd default: 444 1.44 bouyer error = scsipi_do_ioctl(sc->sc_periph, dev, cmd, data, 445 1.72 christos flags, l); 446 1.20 thorpej break; 447 1.1 cgd } 448 1.20 thorpej 449 1.20 thorpej return (error); 450 1.1 cgd } 451 1.1 cgd 452 1.61 thorpej static int 453 1.72 christos chpoll(dev_t dev, int events, struct lwp *l) 454 1.39 thorpej { 455 1.82 tsutsui struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); 456 1.39 thorpej int revents; 457 1.39 thorpej 458 1.39 thorpej revents = events & (POLLOUT | POLLWRNORM); 459 1.39 thorpej 460 1.39 thorpej if ((events & (POLLIN | POLLRDNORM)) == 0) 461 1.39 thorpej return (revents); 462 1.39 thorpej 463 1.39 thorpej if (sc->sc_events == 0) 464 1.39 thorpej revents |= events & (POLLIN | POLLRDNORM); 465 1.39 thorpej else 466 1.72 christos selrecord(l, &sc->sc_selq); 467 1.39 thorpej 468 1.39 thorpej return (revents); 469 1.39 thorpej } 470 1.39 thorpej 471 1.54 jdolecek static void 472 1.54 jdolecek filt_chdetach(struct knote *kn) 473 1.54 jdolecek { 474 1.54 jdolecek struct ch_softc *sc = kn->kn_hook; 475 1.54 jdolecek 476 1.93 thorpej selremove_knote(&sc->sc_selq, kn); 477 1.54 jdolecek } 478 1.54 jdolecek 479 1.54 jdolecek static int 480 1.76 christos filt_chread(struct knote *kn, long hint) 481 1.54 jdolecek { 482 1.54 jdolecek struct ch_softc *sc = kn->kn_hook; 483 1.54 jdolecek 484 1.54 jdolecek if (sc->sc_events == 0) 485 1.54 jdolecek return (0); 486 1.54 jdolecek kn->kn_data = CHANGER_EVENT_SIZE; 487 1.54 jdolecek return (1); 488 1.54 jdolecek } 489 1.54 jdolecek 490 1.92 maya static const struct filterops chread_filtops = { 491 1.94 thorpej .f_flags = FILTEROP_ISFD, 492 1.92 maya .f_attach = NULL, 493 1.92 maya .f_detach = filt_chdetach, 494 1.92 maya .f_event = filt_chread, 495 1.92 maya }; 496 1.54 jdolecek 497 1.61 thorpej static int 498 1.54 jdolecek chkqfilter(dev_t dev, struct knote *kn) 499 1.54 jdolecek { 500 1.82 tsutsui struct ch_softc *sc = device_lookup_private(&ch_cd, CHUNIT(dev)); 501 1.54 jdolecek 502 1.54 jdolecek switch (kn->kn_filter) { 503 1.54 jdolecek case EVFILT_READ: 504 1.54 jdolecek kn->kn_fop = &chread_filtops; 505 1.95 thorpej kn->kn_hook = sc; 506 1.95 thorpej selrecord_knote(&sc->sc_selq, kn); 507 1.54 jdolecek break; 508 1.54 jdolecek 509 1.54 jdolecek case EVFILT_WRITE: 510 1.95 thorpej kn->kn_fop = &seltrue_filtops; 511 1.54 jdolecek break; 512 1.54 jdolecek 513 1.54 jdolecek default: 514 1.78 pooka return (EINVAL); 515 1.54 jdolecek } 516 1.54 jdolecek 517 1.54 jdolecek return (0); 518 1.54 jdolecek } 519 1.54 jdolecek 520 1.61 thorpej static int 521 1.61 thorpej ch_interpret_sense(struct scsipi_xfer *xs) 522 1.39 thorpej { 523 1.44 bouyer struct scsipi_periph *periph = xs->xs_periph; 524 1.68 thorpej struct scsi_sense_data *sense = &xs->sense.scsi_sense; 525 1.87 chs struct ch_softc *sc = device_private(periph->periph_dev); 526 1.67 reinoud u_int16_t asc_ascq; 527 1.39 thorpej 528 1.39 thorpej /* 529 1.44 bouyer * If the periph is already recovering, just do the 530 1.44 bouyer * normal error recovering. 531 1.44 bouyer */ 532 1.44 bouyer if (periph->periph_flags & PERIPH_RECOVERING) 533 1.44 bouyer return (EJUSTRETURN); 534 1.44 bouyer 535 1.44 bouyer /* 536 1.41 mjacob * If it isn't an extended or extended/deferred error, let 537 1.39 thorpej * the generic code handle it. 538 1.39 thorpej */ 539 1.70 drochner if (SSD_RCODE(sense->response_code) != SSD_RCODE_CURRENT && 540 1.70 drochner SSD_RCODE(sense->response_code) != SSD_RCODE_DEFERRED) 541 1.44 bouyer return (EJUSTRETURN); 542 1.41 mjacob 543 1.41 mjacob /* 544 1.96 andvar * We're only interested in conditions that 545 1.41 mjacob * indicate potential inventory violation. 546 1.41 mjacob * 547 1.41 mjacob * We use ASC/ASCQ codes for this. 548 1.41 mjacob */ 549 1.39 thorpej 550 1.68 thorpej asc_ascq = (((u_int16_t) sense->asc) << 8) | 551 1.68 thorpej sense->ascq; 552 1.41 mjacob 553 1.41 mjacob switch (asc_ascq) { 554 1.41 mjacob case 0x2800: 555 1.41 mjacob /* "Not Ready To Ready Transition (Medium May Have Changed)" */ 556 1.41 mjacob case 0x2900: 557 1.41 mjacob /* "Power On, Reset, or Bus Device Reset Occurred" */ 558 1.44 bouyer sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; 559 1.39 thorpej /* 560 1.44 bouyer * Enqueue an Element-Status-Changed event, and wake up 561 1.44 bouyer * any processes waiting for them. 562 1.39 thorpej */ 563 1.44 bouyer if ((xs->xs_control & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) 564 1.44 bouyer ch_event(sc, CHEV_ELEMENT_STATUS_CHANGED); 565 1.41 mjacob break; 566 1.41 mjacob default: 567 1.41 mjacob break; 568 1.39 thorpej } 569 1.44 bouyer 570 1.44 bouyer return (EJUSTRETURN); 571 1.39 thorpej } 572 1.39 thorpej 573 1.61 thorpej static void 574 1.67 reinoud ch_event(struct ch_softc *sc, u_int event) 575 1.39 thorpej { 576 1.39 thorpej 577 1.39 thorpej sc->sc_events |= event; 578 1.79 rmind selnotify(&sc->sc_selq, 0, 0); 579 1.39 thorpej } 580 1.39 thorpej 581 1.61 thorpej static int 582 1.61 thorpej ch_move(struct ch_softc *sc, struct changer_move_request *cm) 583 1.1 cgd { 584 1.20 thorpej struct scsi_move_medium cmd; 585 1.67 reinoud u_int16_t fromelem, toelem; 586 1.20 thorpej 587 1.20 thorpej /* 588 1.20 thorpej * Check arguments. 589 1.20 thorpej */ 590 1.20 thorpej if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) 591 1.20 thorpej return (EINVAL); 592 1.20 thorpej if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || 593 1.20 thorpej (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) 594 1.20 thorpej return (ENODEV); 595 1.20 thorpej 596 1.20 thorpej /* 597 1.20 thorpej * Check the request against the changer's capabilities. 598 1.20 thorpej */ 599 1.20 thorpej if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) 600 1.37 gibbs return (ENODEV); 601 1.20 thorpej 602 1.20 thorpej /* 603 1.20 thorpej * Calculate the source and destination elements. 604 1.20 thorpej */ 605 1.20 thorpej fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; 606 1.20 thorpej toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; 607 1.20 thorpej 608 1.20 thorpej /* 609 1.20 thorpej * Build the SCSI command. 610 1.20 thorpej */ 611 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 612 1.20 thorpej cmd.opcode = MOVE_MEDIUM; 613 1.20 thorpej _lto2b(sc->sc_picker, cmd.tea); 614 1.20 thorpej _lto2b(fromelem, cmd.src); 615 1.20 thorpej _lto2b(toelem, cmd.dst); 616 1.20 thorpej if (cm->cm_flags & CM_INVERT) 617 1.20 thorpej cmd.flags |= MOVE_MEDIUM_INVERT; 618 1.20 thorpej 619 1.20 thorpej /* 620 1.20 thorpej * Send command to changer. 621 1.20 thorpej */ 622 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, 623 1.88 kardel CHRETRIES, CHTIMEOUT, NULL, 0)); 624 1.1 cgd } 625 1.1 cgd 626 1.61 thorpej static int 627 1.61 thorpej ch_exchange(struct ch_softc *sc, struct changer_exchange_request *ce) 628 1.20 thorpej { 629 1.20 thorpej struct scsi_exchange_medium cmd; 630 1.67 reinoud u_int16_t src, dst1, dst2; 631 1.20 thorpej 632 1.20 thorpej /* 633 1.20 thorpej * Check arguments. 634 1.20 thorpej */ 635 1.20 thorpej if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || 636 1.20 thorpej (ce->ce_sdsttype > CHET_DT)) 637 1.20 thorpej return (EINVAL); 638 1.20 thorpej if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || 639 1.20 thorpej (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || 640 1.20 thorpej (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) 641 1.20 thorpej return (ENODEV); 642 1.20 thorpej 643 1.20 thorpej /* 644 1.20 thorpej * Check the request against the changer's capabilities. 645 1.20 thorpej */ 646 1.20 thorpej if (((sc->sc_exchangemask[ce->ce_srctype] & 647 1.20 thorpej (1 << ce->ce_fdsttype)) == 0) || 648 1.20 thorpej ((sc->sc_exchangemask[ce->ce_fdsttype] & 649 1.20 thorpej (1 << ce->ce_sdsttype)) == 0)) 650 1.37 gibbs return (ENODEV); 651 1.20 thorpej 652 1.20 thorpej /* 653 1.20 thorpej * Calculate the source and destination elements. 654 1.20 thorpej */ 655 1.20 thorpej src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; 656 1.20 thorpej dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; 657 1.20 thorpej dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; 658 1.20 thorpej 659 1.20 thorpej /* 660 1.20 thorpej * Build the SCSI command. 661 1.20 thorpej */ 662 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 663 1.20 thorpej cmd.opcode = EXCHANGE_MEDIUM; 664 1.20 thorpej _lto2b(sc->sc_picker, cmd.tea); 665 1.20 thorpej _lto2b(src, cmd.src); 666 1.20 thorpej _lto2b(dst1, cmd.fdst); 667 1.20 thorpej _lto2b(dst2, cmd.sdst); 668 1.20 thorpej if (ce->ce_flags & CE_INVERT1) 669 1.20 thorpej cmd.flags |= EXCHANGE_MEDIUM_INV1; 670 1.20 thorpej if (ce->ce_flags & CE_INVERT2) 671 1.20 thorpej cmd.flags |= EXCHANGE_MEDIUM_INV2; 672 1.20 thorpej 673 1.20 thorpej /* 674 1.20 thorpej * Send command to changer. 675 1.20 thorpej */ 676 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, 677 1.88 kardel CHRETRIES, CHTIMEOUT, NULL, 0)); 678 1.1 cgd } 679 1.1 cgd 680 1.61 thorpej static int 681 1.61 thorpej ch_position(struct ch_softc *sc, struct changer_position_request *cp) 682 1.20 thorpej { 683 1.20 thorpej struct scsi_position_to_element cmd; 684 1.67 reinoud u_int16_t dst; 685 1.20 thorpej 686 1.20 thorpej /* 687 1.20 thorpej * Check arguments. 688 1.20 thorpej */ 689 1.20 thorpej if (cp->cp_type > CHET_DT) 690 1.20 thorpej return (EINVAL); 691 1.20 thorpej if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) 692 1.20 thorpej return (ENODEV); 693 1.20 thorpej 694 1.20 thorpej /* 695 1.20 thorpej * Calculate the destination element. 696 1.20 thorpej */ 697 1.20 thorpej dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; 698 1.20 thorpej 699 1.20 thorpej /* 700 1.20 thorpej * Build the SCSI command. 701 1.20 thorpej */ 702 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 703 1.20 thorpej cmd.opcode = POSITION_TO_ELEMENT; 704 1.20 thorpej _lto2b(sc->sc_picker, cmd.tea); 705 1.20 thorpej _lto2b(dst, cmd.dst); 706 1.20 thorpej if (cp->cp_flags & CP_INVERT) 707 1.20 thorpej cmd.flags |= POSITION_TO_ELEMENT_INVERT; 708 1.20 thorpej 709 1.20 thorpej /* 710 1.20 thorpej * Send command to changer. 711 1.20 thorpej */ 712 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, 713 1.88 kardel CHRETRIES, CHTIMEOUT, NULL, 0)); 714 1.1 cgd } 715 1.1 cgd 716 1.6 mycroft /* 717 1.20 thorpej * Perform a READ ELEMENT STATUS on behalf of the user, and return to 718 1.39 thorpej * the user only the data the user is interested in. This returns the 719 1.39 thorpej * old data format. 720 1.6 mycroft */ 721 1.61 thorpej static int 722 1.67 reinoud ch_ousergetelemstatus(struct ch_softc *sc, int chet, u_int8_t *uptr) 723 1.20 thorpej { 724 1.39 thorpej struct read_element_status_header *st_hdrp, st_hdr; 725 1.39 thorpej struct read_element_status_page_header *pg_hdrp; 726 1.20 thorpej struct read_element_status_descriptor *desc; 727 1.20 thorpej size_t size, desclen; 728 1.77 christos void *data; 729 1.20 thorpej int avail, i, error = 0; 730 1.67 reinoud u_int8_t user_data; 731 1.20 thorpej 732 1.20 thorpej /* 733 1.20 thorpej * If there are no elements of the requested type in the changer, 734 1.20 thorpej * the request is invalid. 735 1.20 thorpej */ 736 1.20 thorpej if (sc->sc_counts[chet] == 0) 737 1.20 thorpej return (EINVAL); 738 1.20 thorpej 739 1.20 thorpej /* 740 1.39 thorpej * Do the request the user wants, but only read the status header. 741 1.39 thorpej * This will tell us the amount of storage we must allocate in 742 1.39 thorpej * order to read all data. 743 1.20 thorpej */ 744 1.39 thorpej error = ch_getelemstatus(sc, sc->sc_firsts[chet], 745 1.85 rmind sc->sc_counts[chet], &st_hdr, sizeof(st_hdr), 0, 0); 746 1.21 christos if (error) 747 1.39 thorpej return (error); 748 1.20 thorpej 749 1.39 thorpej size = sizeof(struct read_element_status_header) + 750 1.39 thorpej _3btol(st_hdr.nbytes); 751 1.20 thorpej 752 1.39 thorpej /* 753 1.39 thorpej * We must have at least room for the status header and 754 1.39 thorpej * one page header (since we only ask for one element type 755 1.39 thorpej * at a time). 756 1.39 thorpej */ 757 1.39 thorpej if (size < (sizeof(struct read_element_status_header) + 758 1.39 thorpej sizeof(struct read_element_status_page_header))) 759 1.39 thorpej return (EIO); 760 1.20 thorpej 761 1.20 thorpej /* 762 1.39 thorpej * Allocate the storage and do the request again. 763 1.20 thorpej */ 764 1.39 thorpej data = malloc(size, M_DEVBUF, M_WAITOK); 765 1.21 christos error = ch_getelemstatus(sc, sc->sc_firsts[chet], 766 1.43 enami sc->sc_counts[chet], data, size, 0, 0); 767 1.21 christos if (error) 768 1.20 thorpej goto done; 769 1.20 thorpej 770 1.39 thorpej st_hdrp = (struct read_element_status_header *)data; 771 1.67 reinoud pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + 772 1.39 thorpej sizeof(struct read_element_status_header)); 773 1.39 thorpej desclen = _2btol(pg_hdrp->edl); 774 1.39 thorpej 775 1.20 thorpej /* 776 1.20 thorpej * Fill in the user status array. 777 1.20 thorpej */ 778 1.39 thorpej avail = _2btol(st_hdrp->count); 779 1.28 mjacob 780 1.20 thorpej if (avail != sc->sc_counts[chet]) 781 1.24 christos printf("%s: warning, READ ELEMENT STATUS avail != count\n", 782 1.87 chs device_xname(sc->sc_dev)); 783 1.20 thorpej 784 1.67 reinoud desc = (struct read_element_status_descriptor *)((u_long)data + 785 1.20 thorpej sizeof(struct read_element_status_header) + 786 1.20 thorpej sizeof(struct read_element_status_page_header)); 787 1.20 thorpej for (i = 0; i < avail; ++i) { 788 1.39 thorpej user_data = desc->flags1; 789 1.39 thorpej error = copyout(&user_data, &uptr[i], avail); 790 1.39 thorpej if (error) 791 1.39 thorpej break; 792 1.67 reinoud desc = (struct read_element_status_descriptor *)((u_long)desc 793 1.46 lukem + desclen); 794 1.20 thorpej } 795 1.20 thorpej 796 1.39 thorpej done: 797 1.39 thorpej if (data != NULL) 798 1.39 thorpej free(data, M_DEVBUF); 799 1.39 thorpej return (error); 800 1.39 thorpej } 801 1.39 thorpej 802 1.39 thorpej /* 803 1.39 thorpej * Perform a READ ELEMENT STATUS on behalf of the user. This returns 804 1.39 thorpej * the new (more complete) data format. 805 1.39 thorpej */ 806 1.61 thorpej static int 807 1.61 thorpej ch_usergetelemstatus(struct ch_softc *sc, 808 1.61 thorpej struct changer_element_status_request *cesr) 809 1.39 thorpej { 810 1.44 bouyer struct scsipi_channel *chan = sc->sc_periph->periph_channel; 811 1.44 bouyer struct scsipi_periph *dtperiph; 812 1.39 thorpej struct read_element_status_header *st_hdrp, st_hdr; 813 1.39 thorpej struct read_element_status_page_header *pg_hdrp; 814 1.39 thorpej struct read_element_status_descriptor *desc; 815 1.39 thorpej struct changer_volume_tag *avol, *pvol; 816 1.39 thorpej size_t size, desclen, stddesclen, offset; 817 1.39 thorpej int first, avail, i, error = 0; 818 1.77 christos void *data; 819 1.39 thorpej void *uvendptr; 820 1.39 thorpej struct changer_element_status ces; 821 1.39 thorpej 822 1.39 thorpej /* 823 1.39 thorpej * Check arguments. 824 1.39 thorpej */ 825 1.39 thorpej if (cesr->cesr_type > CHET_DT) 826 1.39 thorpej return (EINVAL); 827 1.39 thorpej if (sc->sc_counts[cesr->cesr_type] == 0) 828 1.39 thorpej return (ENODEV); 829 1.39 thorpej if (cesr->cesr_unit > (sc->sc_counts[cesr->cesr_type] - 1)) 830 1.39 thorpej return (ENODEV); 831 1.39 thorpej if (cesr->cesr_count > 832 1.39 thorpej (sc->sc_counts[cesr->cesr_type] + cesr->cesr_unit)) 833 1.39 thorpej return (EINVAL); 834 1.39 thorpej 835 1.39 thorpej /* 836 1.39 thorpej * Do the request the user wants, but only read the status header. 837 1.39 thorpej * This will tell us the amount of storage we must allocate 838 1.39 thorpej * in order to read all the data. 839 1.39 thorpej */ 840 1.39 thorpej error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 841 1.43 enami cesr->cesr_unit, cesr->cesr_count, &st_hdr, sizeof(st_hdr), 0, 842 1.39 thorpej cesr->cesr_flags); 843 1.39 thorpej if (error) 844 1.39 thorpej return (error); 845 1.39 thorpej 846 1.39 thorpej size = sizeof(struct read_element_status_header) + 847 1.39 thorpej _3btol(st_hdr.nbytes); 848 1.39 thorpej 849 1.39 thorpej /* 850 1.39 thorpej * We must have at least room for the status header and 851 1.39 thorpej * one page header (since we only ask for oen element type 852 1.39 thorpej * at a time). 853 1.39 thorpej */ 854 1.39 thorpej if (size < (sizeof(struct read_element_status_header) + 855 1.39 thorpej sizeof(struct read_element_status_page_header))) 856 1.39 thorpej return (EIO); 857 1.39 thorpej 858 1.39 thorpej /* 859 1.39 thorpej * Allocate the storage and do the request again. 860 1.39 thorpej */ 861 1.39 thorpej data = malloc(size, M_DEVBUF, M_WAITOK); 862 1.39 thorpej error = ch_getelemstatus(sc, sc->sc_firsts[cesr->cesr_type] + 863 1.43 enami cesr->cesr_unit, cesr->cesr_count, data, size, 0, 864 1.43 enami cesr->cesr_flags); 865 1.39 thorpej if (error) 866 1.39 thorpej goto done; 867 1.39 thorpej 868 1.39 thorpej st_hdrp = (struct read_element_status_header *)data; 869 1.67 reinoud pg_hdrp = (struct read_element_status_page_header *)((u_long)st_hdrp + 870 1.39 thorpej sizeof(struct read_element_status_header)); 871 1.39 thorpej desclen = _2btol(pg_hdrp->edl); 872 1.39 thorpej 873 1.39 thorpej /* 874 1.39 thorpej * Fill in the user status array. 875 1.39 thorpej */ 876 1.39 thorpej first = _2btol(st_hdrp->fear); 877 1.39 thorpej if (first < (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit) || 878 1.39 thorpej first >= (sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit + 879 1.39 thorpej cesr->cesr_count)) { 880 1.39 thorpej error = EIO; 881 1.39 thorpej goto done; 882 1.39 thorpej } 883 1.39 thorpej first -= sc->sc_firsts[cesr->cesr_type] + cesr->cesr_unit; 884 1.39 thorpej 885 1.39 thorpej avail = _2btol(st_hdrp->count); 886 1.39 thorpej if (avail <= 0 || avail > cesr->cesr_count) { 887 1.39 thorpej error = EIO; 888 1.39 thorpej goto done; 889 1.39 thorpej } 890 1.39 thorpej 891 1.39 thorpej offset = sizeof(struct read_element_status_header) + 892 1.39 thorpej sizeof(struct read_element_status_page_header); 893 1.39 thorpej 894 1.39 thorpej for (i = 0; i < cesr->cesr_count; i++) { 895 1.39 thorpej memset(&ces, 0, sizeof(ces)); 896 1.39 thorpej if (i < first || i >= (first + avail)) { 897 1.39 thorpej error = copyout(&ces, &cesr->cesr_data[i], 898 1.39 thorpej sizeof(ces)); 899 1.39 thorpej if (error) 900 1.39 thorpej goto done; 901 1.39 thorpej } 902 1.39 thorpej 903 1.39 thorpej desc = (struct read_element_status_descriptor *) 904 1.77 christos ((char *)data + offset); 905 1.39 thorpej stddesclen = sizeof(struct read_element_status_descriptor); 906 1.39 thorpej offset += desclen; 907 1.39 thorpej 908 1.39 thorpej ces.ces_flags = CESTATUS_STATUS_VALID; 909 1.39 thorpej 910 1.39 thorpej /* 911 1.39 thorpej * The SCSI flags conveniently map directly to the 912 1.39 thorpej * chio API flags. 913 1.39 thorpej */ 914 1.39 thorpej ces.ces_flags |= (desc->flags1 & 0x3f); 915 1.39 thorpej 916 1.39 thorpej ces.ces_asc = desc->sense_code; 917 1.39 thorpej ces.ces_ascq = desc->sense_qual; 918 1.39 thorpej 919 1.39 thorpej /* 920 1.39 thorpej * For Data Transport elemenets, get the SCSI ID and LUN, 921 1.39 thorpej * and attempt to map them to a device name if they're 922 1.39 thorpej * on the same SCSI bus. 923 1.39 thorpej */ 924 1.39 thorpej if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_IDVALID) { 925 1.39 thorpej ces.ces_target = desc->dt_scsi_addr; 926 1.39 thorpej ces.ces_flags |= CESTATUS_TARGET_VALID; 927 1.39 thorpej } 928 1.39 thorpej if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_LUVALID) { 929 1.39 thorpej ces.ces_lun = desc->dt_scsi_flags & 930 1.39 thorpej READ_ELEMENT_STATUS_DT_LUNMASK; 931 1.39 thorpej ces.ces_flags |= CESTATUS_LUN_VALID; 932 1.39 thorpej } 933 1.39 thorpej if (desc->dt_scsi_flags & READ_ELEMENT_STATUS_DT_NOTBUS) 934 1.39 thorpej ces.ces_flags |= CESTATUS_NOTBUS; 935 1.39 thorpej else if ((ces.ces_flags & 936 1.39 thorpej (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) == 937 1.39 thorpej (CESTATUS_TARGET_VALID|CESTATUS_LUN_VALID)) { 938 1.44 bouyer if (ces.ces_target < chan->chan_ntargets && 939 1.44 bouyer ces.ces_lun < chan->chan_nluns && 940 1.44 bouyer (dtperiph = scsipi_lookup_periph(chan, 941 1.44 bouyer ces.ces_target, ces.ces_lun)) != NULL && 942 1.44 bouyer dtperiph->periph_dev != NULL) { 943 1.59 itojun strlcpy(ces.ces_xname, 944 1.80 cegger device_xname(dtperiph->periph_dev), 945 1.59 itojun sizeof(ces.ces_xname)); 946 1.39 thorpej ces.ces_flags |= CESTATUS_XNAME_VALID; 947 1.39 thorpej } 948 1.39 thorpej } 949 1.39 thorpej 950 1.39 thorpej if (desc->flags2 & READ_ELEMENT_STATUS_INVERT) 951 1.39 thorpej ces.ces_flags |= CESTATUS_INVERTED; 952 1.39 thorpej 953 1.39 thorpej if (desc->flags2 & READ_ELEMENT_STATUS_SVALID) { 954 1.39 thorpej if (ch_map_element(sc, _2btol(desc->ssea), 955 1.39 thorpej &ces.ces_from_type, &ces.ces_from_unit)) 956 1.39 thorpej ces.ces_flags |= CESTATUS_FROM_VALID; 957 1.39 thorpej } 958 1.39 thorpej 959 1.39 thorpej /* 960 1.39 thorpej * Extract volume tag information. 961 1.39 thorpej */ 962 1.39 thorpej switch (pg_hdrp->flags & 963 1.39 thorpej (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG)) { 964 1.39 thorpej case (READ_ELEMENT_STATUS_PVOLTAG|READ_ELEMENT_STATUS_AVOLTAG): 965 1.39 thorpej pvol = (struct changer_volume_tag *)(desc + 1); 966 1.39 thorpej avol = pvol + 1; 967 1.39 thorpej break; 968 1.39 thorpej 969 1.39 thorpej case READ_ELEMENT_STATUS_PVOLTAG: 970 1.39 thorpej pvol = (struct changer_volume_tag *)(desc + 1); 971 1.39 thorpej avol = NULL; 972 1.39 thorpej break; 973 1.39 thorpej 974 1.39 thorpej case READ_ELEMENT_STATUS_AVOLTAG: 975 1.39 thorpej pvol = NULL; 976 1.39 thorpej avol = (struct changer_volume_tag *)(desc + 1); 977 1.39 thorpej break; 978 1.39 thorpej 979 1.39 thorpej default: 980 1.39 thorpej avol = pvol = NULL; 981 1.39 thorpej break; 982 1.39 thorpej } 983 1.39 thorpej 984 1.39 thorpej if (pvol != NULL) { 985 1.39 thorpej ch_voltag_convert_in(pvol, &ces.ces_pvoltag); 986 1.39 thorpej ces.ces_flags |= CESTATUS_PVOL_VALID; 987 1.39 thorpej stddesclen += sizeof(struct changer_volume_tag); 988 1.39 thorpej } 989 1.39 thorpej if (avol != NULL) { 990 1.39 thorpej ch_voltag_convert_in(avol, &ces.ces_avoltag); 991 1.39 thorpej ces.ces_flags |= CESTATUS_AVOL_VALID; 992 1.39 thorpej stddesclen += sizeof(struct changer_volume_tag); 993 1.39 thorpej } 994 1.39 thorpej 995 1.39 thorpej /* 996 1.39 thorpej * Compute vendor-specific length. Note the 4 reserved 997 1.39 thorpej * bytes between the volume tags and the vendor-specific 998 1.39 thorpej * data. Copy it out of the user wants it. 999 1.39 thorpej */ 1000 1.39 thorpej stddesclen += 4; 1001 1.39 thorpej if (desclen > stddesclen) 1002 1.39 thorpej ces.ces_vendor_len = desclen - stddesclen; 1003 1.39 thorpej 1004 1.39 thorpej if (ces.ces_vendor_len != 0 && cesr->cesr_vendor_data != NULL) { 1005 1.39 thorpej error = copyin(&cesr->cesr_vendor_data[i], &uvendptr, 1006 1.39 thorpej sizeof(uvendptr)); 1007 1.39 thorpej if (error) 1008 1.39 thorpej goto done; 1009 1.67 reinoud error = copyout((void *)((u_long)desc + stddesclen), 1010 1.39 thorpej uvendptr, ces.ces_vendor_len); 1011 1.39 thorpej if (error) 1012 1.39 thorpej goto done; 1013 1.39 thorpej } 1014 1.39 thorpej 1015 1.39 thorpej /* 1016 1.39 thorpej * Now copy out the status descriptor we've constructed. 1017 1.39 thorpej */ 1018 1.39 thorpej error = copyout(&ces, &cesr->cesr_data[i], sizeof(ces)); 1019 1.39 thorpej if (error) 1020 1.39 thorpej goto done; 1021 1.39 thorpej } 1022 1.20 thorpej 1023 1.20 thorpej done: 1024 1.20 thorpej if (data != NULL) 1025 1.20 thorpej free(data, M_DEVBUF); 1026 1.20 thorpej return (error); 1027 1.20 thorpej } 1028 1.20 thorpej 1029 1.61 thorpej static int 1030 1.61 thorpej ch_getelemstatus(struct ch_softc *sc, int first, int count, void *data, 1031 1.61 thorpej size_t datalen, int scsiflags, int flags) 1032 1.1 cgd { 1033 1.20 thorpej struct scsi_read_element_status cmd; 1034 1.6 mycroft 1035 1.6 mycroft /* 1036 1.20 thorpej * Build SCSI command. 1037 1.6 mycroft */ 1038 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 1039 1.20 thorpej cmd.opcode = READ_ELEMENT_STATUS; 1040 1.39 thorpej cmd.byte2 = ELEMENT_TYPE_ALL; 1041 1.39 thorpej if (flags & CESR_VOLTAGS) 1042 1.39 thorpej cmd.byte2 |= READ_ELEMENT_STATUS_VOLTAG; 1043 1.20 thorpej _lto2b(first, cmd.sea); 1044 1.20 thorpej _lto2b(count, cmd.count); 1045 1.20 thorpej _lto3b(datalen, cmd.len); 1046 1.6 mycroft 1047 1.6 mycroft /* 1048 1.20 thorpej * Send command to changer. 1049 1.6 mycroft */ 1050 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 1051 1.64 mycroft (void *)data, datalen, 1052 1.88 kardel CHRETRIES, CHTIMEOUT, NULL, scsiflags | XS_CTL_DATA_IN)); 1053 1.20 thorpej } 1054 1.20 thorpej 1055 1.61 thorpej static int 1056 1.61 thorpej ch_setvoltag(struct ch_softc *sc, struct changer_set_voltag_request *csvr) 1057 1.39 thorpej { 1058 1.39 thorpej struct scsi_send_volume_tag cmd; 1059 1.39 thorpej struct changer_volume_tag voltag; 1060 1.39 thorpej void *data = NULL; 1061 1.39 thorpej size_t datalen = 0; 1062 1.39 thorpej int error; 1063 1.67 reinoud u_int16_t dst; 1064 1.39 thorpej 1065 1.39 thorpej /* 1066 1.39 thorpej * Check arguments. 1067 1.39 thorpej */ 1068 1.39 thorpej if (csvr->csvr_type > CHET_DT) 1069 1.39 thorpej return (EINVAL); 1070 1.39 thorpej if (csvr->csvr_unit > (sc->sc_counts[csvr->csvr_type] - 1)) 1071 1.39 thorpej return (ENODEV); 1072 1.39 thorpej 1073 1.39 thorpej dst = sc->sc_firsts[csvr->csvr_type] + csvr->csvr_unit; 1074 1.39 thorpej 1075 1.39 thorpej /* 1076 1.39 thorpej * Build the SCSI command. 1077 1.39 thorpej */ 1078 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 1079 1.39 thorpej cmd.opcode = SEND_VOLUME_TAG; 1080 1.39 thorpej _lto2b(dst, cmd.eaddr); 1081 1.39 thorpej 1082 1.39 thorpej #define ALTERNATE (csvr->csvr_flags & CSVR_ALTERNATE) 1083 1.39 thorpej 1084 1.39 thorpej switch (csvr->csvr_flags & CSVR_MODE_MASK) { 1085 1.39 thorpej case CSVR_MODE_SET: 1086 1.39 thorpej cmd.sac = ALTERNATE ? SAC_ASSERT_ALT : SAC_ASSERT_PRIMARY; 1087 1.39 thorpej break; 1088 1.39 thorpej 1089 1.39 thorpej case CSVR_MODE_REPLACE: 1090 1.39 thorpej cmd.sac = ALTERNATE ? SAC_REPLACE_ALT : SAC_REPLACE_PRIMARY; 1091 1.39 thorpej break; 1092 1.39 thorpej 1093 1.39 thorpej case CSVR_MODE_CLEAR: 1094 1.39 thorpej cmd.sac = ALTERNATE ? SAC_UNDEFINED_ALT : SAC_UNDEFINED_PRIMARY; 1095 1.39 thorpej break; 1096 1.39 thorpej 1097 1.39 thorpej default: 1098 1.39 thorpej return (EINVAL); 1099 1.39 thorpej } 1100 1.39 thorpej 1101 1.39 thorpej #undef ALTERNATE 1102 1.39 thorpej 1103 1.39 thorpej if (cmd.sac < SAC_UNDEFINED_PRIMARY) { 1104 1.39 thorpej error = ch_voltag_convert_out(&csvr->csvr_voltag, &voltag); 1105 1.39 thorpej if (error) 1106 1.39 thorpej return (error); 1107 1.39 thorpej data = &voltag; 1108 1.39 thorpej datalen = sizeof(voltag); 1109 1.39 thorpej _lto2b(datalen, cmd.length); 1110 1.39 thorpej } 1111 1.39 thorpej 1112 1.39 thorpej /* 1113 1.39 thorpej * Send command to changer. 1114 1.39 thorpej */ 1115 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 1116 1.88 kardel (void *)data, datalen, CHRETRIES, CHTIMEOUT, NULL, 1117 1.85 rmind datalen ? XS_CTL_DATA_OUT : 0)); 1118 1.39 thorpej } 1119 1.20 thorpej 1120 1.61 thorpej static int 1121 1.61 thorpej ch_ielem(struct ch_softc *sc) 1122 1.28 mjacob { 1123 1.33 mjacob int tmo; 1124 1.28 mjacob struct scsi_initialize_element_status cmd; 1125 1.28 mjacob 1126 1.28 mjacob /* 1127 1.28 mjacob * Build SCSI command. 1128 1.28 mjacob */ 1129 1.47 thorpej memset(&cmd, 0, sizeof(cmd)); 1130 1.28 mjacob cmd.opcode = INITIALIZE_ELEMENT_STATUS; 1131 1.28 mjacob 1132 1.28 mjacob /* 1133 1.28 mjacob * Send command to changer. 1134 1.33 mjacob * 1135 1.33 mjacob * The problem is, how long to allow for the command? 1136 1.33 mjacob * It can take a *really* long time, and also depends 1137 1.33 mjacob * on unknowable factors such as whether there are 1138 1.33 mjacob * *almost* readable labels on tapes that a barcode 1139 1.33 mjacob * reader is trying to decipher. 1140 1.33 mjacob * 1141 1.33 mjacob * I'm going to make this long enough to allow 5 minutes 1142 1.33 mjacob * per element plus an initial 10 minute wait. 1143 1.33 mjacob */ 1144 1.33 mjacob tmo = sc->sc_counts[CHET_MT] + 1145 1.33 mjacob sc->sc_counts[CHET_ST] + 1146 1.33 mjacob sc->sc_counts[CHET_IE] + 1147 1.33 mjacob sc->sc_counts[CHET_DT]; 1148 1.38 bouyer tmo *= 5 * 60 * 1000; 1149 1.33 mjacob tmo += (10 * 60 * 1000); 1150 1.33 mjacob 1151 1.64 mycroft return (scsipi_command(sc->sc_periph, (void *)&cmd, sizeof(cmd), 0, 0, 1152 1.64 mycroft CHRETRIES, tmo, NULL, XS_CTL_IGNORE_ILLEGAL_REQUEST)); 1153 1.28 mjacob } 1154 1.28 mjacob 1155 1.20 thorpej /* 1156 1.20 thorpej * Ask the device about itself and fill in the parameters in our 1157 1.20 thorpej * softc. 1158 1.20 thorpej */ 1159 1.61 thorpej static int 1160 1.61 thorpej ch_get_params(struct ch_softc *sc, int scsiflags) 1161 1.20 thorpej { 1162 1.20 thorpej struct scsi_mode_sense_data { 1163 1.68 thorpej struct scsi_mode_parameter_header_6 header; 1164 1.20 thorpej union { 1165 1.20 thorpej struct page_element_address_assignment ea; 1166 1.20 thorpej struct page_transport_geometry_parameters tg; 1167 1.20 thorpej struct page_device_capabilities cap; 1168 1.20 thorpej } pages; 1169 1.20 thorpej } sense_data; 1170 1.20 thorpej int error, from; 1171 1.67 reinoud u_int8_t *moves, *exchanges; 1172 1.6 mycroft 1173 1.6 mycroft /* 1174 1.20 thorpej * Grab info from the element address assignment page. 1175 1.6 mycroft */ 1176 1.47 thorpej memset(&sense_data, 0, sizeof(sense_data)); 1177 1.45 bouyer error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1d, 1178 1.45 bouyer &sense_data.header, sizeof(sense_data), 1179 1.85 rmind scsiflags, CHRETRIES, 6000); 1180 1.15 christos if (error) { 1181 1.87 chs aprint_error_dev(sc->sc_dev, "could not sense element address page\n"); 1182 1.20 thorpej return (error); 1183 1.6 mycroft } 1184 1.6 mycroft 1185 1.20 thorpej sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); 1186 1.20 thorpej sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); 1187 1.20 thorpej sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); 1188 1.20 thorpej sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); 1189 1.20 thorpej sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); 1190 1.20 thorpej sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); 1191 1.20 thorpej sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); 1192 1.20 thorpej sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); 1193 1.20 thorpej 1194 1.41 mjacob /* XXX ask for transport geometry page XXX */ 1195 1.6 mycroft 1196 1.6 mycroft /* 1197 1.20 thorpej * Grab info from the capabilities page. 1198 1.20 thorpej */ 1199 1.47 thorpej memset(&sense_data, 0, sizeof(sense_data)); 1200 1.28 mjacob /* 1201 1.28 mjacob * XXX: Note: not all changers can deal with disabled block descriptors 1202 1.28 mjacob */ 1203 1.45 bouyer error = scsipi_mode_sense(sc->sc_periph, SMS_DBD, 0x1f, 1204 1.45 bouyer &sense_data.header, sizeof(sense_data), 1205 1.85 rmind scsiflags, CHRETRIES, 6000); 1206 1.20 thorpej if (error) { 1207 1.87 chs aprint_error_dev(sc->sc_dev, "could not sense capabilities page\n"); 1208 1.20 thorpej return (error); 1209 1.20 thorpej } 1210 1.20 thorpej 1211 1.47 thorpej memset(sc->sc_movemask, 0, sizeof(sc->sc_movemask)); 1212 1.47 thorpej memset(sc->sc_exchangemask, 0, sizeof(sc->sc_exchangemask)); 1213 1.20 thorpej moves = &sense_data.pages.cap.move_from_mt; 1214 1.20 thorpej exchanges = &sense_data.pages.cap.exchange_with_mt; 1215 1.20 thorpej for (from = CHET_MT; from <= CHET_DT; ++from) { 1216 1.20 thorpej sc->sc_movemask[from] = moves[from]; 1217 1.20 thorpej sc->sc_exchangemask[from] = exchanges[from]; 1218 1.1 cgd } 1219 1.20 thorpej 1220 1.44 bouyer #ifdef CH_AUTOMATIC_IELEM_POLICY 1221 1.39 thorpej /* 1222 1.41 mjacob * If we need to do an Init-Element-Status, 1223 1.41 mjacob * do that now that we know what's in the changer. 1224 1.39 thorpej */ 1225 1.40 thorpej if ((scsiflags & XS_CTL_IGNORE_MEDIA_CHANGE) == 0) { 1226 1.44 bouyer if ((sc->sc_periph->periph_flags & PERIPH_MEDIA_LOADED) == 0) 1227 1.39 thorpej error = ch_ielem(sc); 1228 1.39 thorpej if (error == 0) 1229 1.44 bouyer sc->sc_periph->periph_flags |= PERIPH_MEDIA_LOADED; 1230 1.39 thorpej else 1231 1.44 bouyer sc->sc_periph->periph_flags &= ~PERIPH_MEDIA_LOADED; 1232 1.39 thorpej } 1233 1.41 mjacob #endif 1234 1.39 thorpej return (error); 1235 1.26 thorpej } 1236 1.26 thorpej 1237 1.61 thorpej static void 1238 1.61 thorpej ch_get_quirks(struct ch_softc *sc, struct scsipi_inquiry_pattern *inqbuf) 1239 1.26 thorpej { 1240 1.71 christos const struct chquirk *match; 1241 1.26 thorpej int priority; 1242 1.26 thorpej 1243 1.26 thorpej sc->sc_settledelay = 0; 1244 1.26 thorpej 1245 1.71 christos match = scsipi_inqmatch(inqbuf, chquirks, 1246 1.26 thorpej sizeof(chquirks) / sizeof(chquirks[0]), 1247 1.26 thorpej sizeof(chquirks[0]), &priority); 1248 1.29 enami if (priority != 0) 1249 1.26 thorpej sc->sc_settledelay = match->cq_settledelay; 1250 1.39 thorpej } 1251 1.39 thorpej 1252 1.61 thorpej static int 1253 1.67 reinoud ch_map_element(struct ch_softc *sc, u_int16_t elem, int *typep, int *unitp) 1254 1.39 thorpej { 1255 1.39 thorpej int chet; 1256 1.39 thorpej 1257 1.39 thorpej for (chet = CHET_MT; chet <= CHET_DT; chet++) { 1258 1.39 thorpej if (elem >= sc->sc_firsts[chet] && 1259 1.39 thorpej elem < (sc->sc_firsts[chet] + sc->sc_counts[chet])) { 1260 1.39 thorpej *typep = chet; 1261 1.39 thorpej *unitp = elem - sc->sc_firsts[chet]; 1262 1.39 thorpej return (1); 1263 1.39 thorpej } 1264 1.39 thorpej } 1265 1.39 thorpej return (0); 1266 1.39 thorpej } 1267 1.39 thorpej 1268 1.61 thorpej static void 1269 1.61 thorpej ch_voltag_convert_in(const struct changer_volume_tag *sv, 1270 1.61 thorpej struct changer_voltag *cv) 1271 1.39 thorpej { 1272 1.39 thorpej int i; 1273 1.39 thorpej 1274 1.39 thorpej memset(cv, 0, sizeof(struct changer_voltag)); 1275 1.39 thorpej 1276 1.39 thorpej /* 1277 1.39 thorpej * Copy the volume tag string from the SCSI representation. 1278 1.39 thorpej * Per the SCSI-2 spec, we stop at the first blank character. 1279 1.39 thorpej */ 1280 1.39 thorpej for (i = 0; i < sizeof(sv->volid); i++) { 1281 1.39 thorpej if (sv->volid[i] == ' ') 1282 1.39 thorpej break; 1283 1.39 thorpej cv->cv_tag[i] = sv->volid[i]; 1284 1.39 thorpej } 1285 1.39 thorpej cv->cv_tag[i] = '\0'; 1286 1.39 thorpej 1287 1.39 thorpej cv->cv_serial = _2btol(sv->volseq); 1288 1.39 thorpej } 1289 1.39 thorpej 1290 1.61 thorpej static int 1291 1.61 thorpej ch_voltag_convert_out(const struct changer_voltag *cv, 1292 1.61 thorpej struct changer_volume_tag *sv) 1293 1.39 thorpej { 1294 1.39 thorpej int i; 1295 1.39 thorpej 1296 1.39 thorpej memset(sv, ' ', sizeof(struct changer_volume_tag)); 1297 1.39 thorpej 1298 1.39 thorpej for (i = 0; i < sizeof(sv->volid); i++) { 1299 1.39 thorpej if (cv->cv_tag[i] == '\0') 1300 1.39 thorpej break; 1301 1.39 thorpej /* 1302 1.39 thorpej * Limit the character set to what is suggested in 1303 1.39 thorpej * the SCSI-2 spec. 1304 1.39 thorpej */ 1305 1.39 thorpej if ((cv->cv_tag[i] < '0' || cv->cv_tag[i] > '9') && 1306 1.39 thorpej (cv->cv_tag[i] < 'A' || cv->cv_tag[i] > 'Z') && 1307 1.39 thorpej (cv->cv_tag[i] != '_')) 1308 1.39 thorpej return (EINVAL); 1309 1.39 thorpej sv->volid[i] = cv->cv_tag[i]; 1310 1.39 thorpej } 1311 1.39 thorpej 1312 1.39 thorpej _lto2b(cv->cv_serial, sv->volseq); 1313 1.39 thorpej 1314 1.39 thorpej return (0); 1315 1.1 cgd } 1316