1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2003 Hidetoshi Shimokawa 5 * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the acknowledgement as bellow: 18 * 19 * This product includes software developed by K. Kobayashi and H. Shimokawa 20 * 21 * 4. The name of the author may not be used to endorse or promote products 22 * derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 * 36 */ 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/types.h> 41 #include <sys/mbuf.h> 42 #include <sys/bio.h> 43 44 #include <sys/kernel.h> 45 #include <sys/malloc.h> 46 #include <sys/conf.h> 47 #include <sys/poll.h> 48 49 #include <sys/bus.h> 50 #include <sys/ctype.h> 51 #include <machine/bus.h> 52 53 #include <sys/ioccom.h> 54 55 #include <dev/firewire/firewire.h> 56 #include <dev/firewire/firewirereg.h> 57 #include <dev/firewire/fwdma.h> 58 #include <dev/firewire/fwmem.h> 59 #include <dev/firewire/iec68113.h> 60 61 #define FWNODE_INVAL 0xffff 62 63 static d_open_t fw_open; 64 static d_close_t fw_close; 65 static d_ioctl_t fw_ioctl; 66 static d_poll_t fw_poll; 67 static d_read_t fw_read; /* for Isochronous packet */ 68 static d_write_t fw_write; 69 static d_mmap_t fw_mmap; 70 static d_strategy_t fw_strategy; 71 72 struct cdevsw firewire_cdevsw = { 73 .d_version = D_VERSION, 74 .d_open = fw_open, 75 .d_close = fw_close, 76 .d_read = fw_read, 77 .d_write = fw_write, 78 .d_ioctl = fw_ioctl, 79 .d_poll = fw_poll, 80 .d_mmap = fw_mmap, 81 .d_strategy = fw_strategy, 82 .d_name = "fw", 83 }; 84 85 struct fw_drv1 { 86 struct firewire_comm *fc; 87 struct fw_xferq *ir; 88 struct fw_xferq *it; 89 struct fw_isobufreq bufreq; 90 STAILQ_HEAD(, fw_bind) binds; 91 STAILQ_HEAD(, fw_xfer) rq; 92 }; 93 94 static int 95 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 96 struct fw_bufspec *b) 97 { 98 int i; 99 100 if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 101 return (EBUSY); 102 103 q->bulkxfer = malloc(sizeof(struct fw_bulkxfer) * b->nchunk, 104 M_FW, M_WAITOK); 105 106 b->psize = roundup2(b->psize, sizeof(uint32_t)); 107 q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), 108 b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 109 110 if (q->buf == NULL) { 111 free(q->bulkxfer, M_FW); 112 q->bulkxfer = NULL; 113 return (ENOMEM); 114 } 115 q->bnchunk = b->nchunk; 116 q->bnpacket = b->npacket; 117 q->psize = (b->psize + 3) & ~3; 118 q->queued = 0; 119 120 STAILQ_INIT(&q->stvalid); 121 STAILQ_INIT(&q->stfree); 122 STAILQ_INIT(&q->stdma); 123 q->stproc = NULL; 124 125 for (i = 0; i < q->bnchunk; i++) { 126 q->bulkxfer[i].poffset = i * q->bnpacket; 127 q->bulkxfer[i].mbuf = NULL; 128 STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 129 } 130 131 q->flag &= ~FWXFERQ_MODEMASK; 132 q->flag |= FWXFERQ_STREAM; 133 q->flag |= FWXFERQ_EXTBUF; 134 135 return (0); 136 } 137 138 static int 139 fwdev_freebuf(struct fw_xferq *q) 140 { 141 if (q->flag & FWXFERQ_EXTBUF) { 142 if (q->buf != NULL) 143 fwdma_free_multiseg(q->buf); 144 q->buf = NULL; 145 free(q->bulkxfer, M_FW); 146 q->bulkxfer = NULL; 147 q->flag &= ~FWXFERQ_EXTBUF; 148 q->psize = 0; 149 q->maxq = FWMAXQUEUE; 150 } 151 return (0); 152 } 153 154 155 static int 156 fw_open(struct cdev *dev, int flags, int fmt, fw_proc *td) 157 { 158 int err = 0; 159 int unit = DEV2UNIT(dev); 160 struct fw_drv1 *d; 161 struct firewire_softc *sc; 162 163 if (DEV_FWMEM(dev)) 164 return fwmem_open(dev, flags, fmt, td); 165 166 sc = devclass_get_softc(firewire_devclass, unit); 167 if (sc == NULL) 168 return (ENXIO); 169 170 FW_GLOCK(sc->fc); 171 if (dev->si_drv1 != NULL) { 172 FW_GUNLOCK(sc->fc); 173 return (EBUSY); 174 } 175 /* set dummy value for allocation */ 176 dev->si_drv1 = (void *)-1; 177 FW_GUNLOCK(sc->fc); 178 179 dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 180 181 if ((dev->si_flags & SI_NAMED) == 0) { 182 int unit = DEV2UNIT(dev); 183 int sub = DEV2SUB(dev); 184 185 make_dev(&firewire_cdevsw, dev2unit(dev), 186 UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, sub); 187 } 188 189 d = dev->si_drv1; 190 d->fc = sc->fc; 191 STAILQ_INIT(&d->binds); 192 STAILQ_INIT(&d->rq); 193 194 return err; 195 } 196 197 static int 198 fw_close(struct cdev *dev, int flags, int fmt, fw_proc *td) 199 { 200 struct firewire_comm *fc; 201 struct fw_drv1 *d; 202 struct fw_xfer *xfer; 203 struct fw_bind *fwb; 204 int err = 0; 205 206 if (DEV_FWMEM(dev)) 207 return fwmem_close(dev, flags, fmt, td); 208 209 d = dev->si_drv1; 210 fc = d->fc; 211 212 /* remove binding */ 213 for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL; 214 fwb = STAILQ_FIRST(&d->binds)) { 215 fw_bindremove(fc, fwb); 216 STAILQ_REMOVE_HEAD(&d->binds, chlist); 217 fw_xferlist_remove(&fwb->xferlist); 218 free(fwb, M_FW); 219 } 220 if (d->ir != NULL) { 221 struct fw_xferq *ir = d->ir; 222 223 if ((ir->flag & FWXFERQ_OPEN) == 0) 224 return (EINVAL); 225 if (ir->flag & FWXFERQ_RUNNING) { 226 ir->flag &= ~FWXFERQ_RUNNING; 227 fc->irx_disable(fc, ir->dmach); 228 } 229 /* free extbuf */ 230 fwdev_freebuf(ir); 231 /* drain receiving buffer */ 232 for (xfer = STAILQ_FIRST(&ir->q); 233 xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 234 ir->queued--; 235 STAILQ_REMOVE_HEAD(&ir->q, link); 236 237 xfer->resp = 0; 238 fw_xfer_done(xfer); 239 } 240 ir->flag &= ~(FWXFERQ_OPEN | FWXFERQ_MODEMASK | 241 FWXFERQ_CHTAGMASK); 242 d->ir = NULL; 243 244 } 245 if (d->it != NULL) { 246 struct fw_xferq *it = d->it; 247 248 if ((it->flag & FWXFERQ_OPEN) == 0) 249 return (EINVAL); 250 if (it->flag & FWXFERQ_RUNNING) { 251 it->flag &= ~FWXFERQ_RUNNING; 252 fc->itx_disable(fc, it->dmach); 253 } 254 /* free extbuf */ 255 fwdev_freebuf(it); 256 it->flag &= ~(FWXFERQ_OPEN | 257 FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 258 d->it = NULL; 259 } 260 free(dev->si_drv1, M_FW); 261 dev->si_drv1 = NULL; 262 263 return err; 264 } 265 266 static int 267 fw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 268 { 269 int err = 0; 270 struct fw_xfer *xfer; 271 struct fw_bind *fwb; 272 struct fw_pkt *fp; 273 struct tcode_info *tinfo; 274 275 FW_GLOCK(d->fc); 276 while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0) 277 err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0); 278 279 if (err != 0) { 280 FW_GUNLOCK(d->fc); 281 return (err); 282 } 283 284 STAILQ_REMOVE_HEAD(&d->rq, link); 285 FW_GUNLOCK(xfer->fc); 286 fp = &xfer->recv.hdr; 287 tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode]; 288 err = uiomove(fp, tinfo->hdr_len, uio); 289 if (err) 290 goto out; 291 err = uiomove(xfer->recv.payload, xfer->recv.pay_len, uio); 292 293 out: 294 /* recycle this xfer */ 295 fwb = (struct fw_bind *)xfer->sc; 296 fw_xfer_unload(xfer); 297 xfer->recv.pay_len = PAGE_SIZE; 298 FW_GLOCK(xfer->fc); 299 STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 300 FW_GUNLOCK(xfer->fc); 301 return (err); 302 } 303 304 /* 305 * read request. 306 */ 307 static int 308 fw_read(struct cdev *dev, struct uio *uio, int ioflag) 309 { 310 struct fw_drv1 *d; 311 struct fw_xferq *ir; 312 struct firewire_comm *fc; 313 int err = 0, slept = 0; 314 struct fw_pkt *fp; 315 316 if (DEV_FWMEM(dev)) 317 return (physio(dev, uio, ioflag)); 318 319 d = dev->si_drv1; 320 fc = d->fc; 321 ir = d->ir; 322 323 if (ir == NULL) 324 return (fw_read_async(d, uio, ioflag)); 325 326 if (ir->buf == NULL) 327 return (EIO); 328 329 FW_GLOCK(fc); 330 readloop: 331 if (ir->stproc == NULL) { 332 /* iso bulkxfer */ 333 ir->stproc = STAILQ_FIRST(&ir->stvalid); 334 if (ir->stproc != NULL) { 335 STAILQ_REMOVE_HEAD(&ir->stvalid, link); 336 ir->queued = 0; 337 } 338 } 339 if (ir->stproc == NULL) { 340 /* no data available */ 341 if (slept == 0) { 342 slept = 1; 343 ir->flag |= FWXFERQ_WAKEUP; 344 err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz); 345 ir->flag &= ~FWXFERQ_WAKEUP; 346 if (err == 0) 347 goto readloop; 348 } else if (slept == 1) 349 err = EIO; 350 FW_GUNLOCK(fc); 351 return err; 352 } else if (ir->stproc != NULL) { 353 /* iso bulkxfer */ 354 FW_GUNLOCK(fc); 355 fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 356 ir->stproc->poffset + ir->queued); 357 if (fc->irx_post != NULL) 358 fc->irx_post(fc, fp->mode.ld); 359 if (fp->mode.stream.len == 0) { 360 err = EIO; 361 return err; 362 } 363 err = uiomove((caddr_t)fp, 364 fp->mode.stream.len + sizeof(uint32_t), uio); 365 ir->queued++; 366 if (ir->queued >= ir->bnpacket) { 367 STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 368 fc->irx_enable(fc, ir->dmach); 369 ir->stproc = NULL; 370 } 371 if (uio->uio_resid >= ir->psize) { 372 slept = -1; 373 FW_GLOCK(fc); 374 goto readloop; 375 } 376 } 377 return err; 378 } 379 380 static int 381 fw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 382 { 383 struct fw_xfer *xfer; 384 struct fw_pkt pkt; 385 struct tcode_info *tinfo; 386 int err; 387 388 bzero(&pkt, sizeof(struct fw_pkt)); 389 if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio))) 390 return (err); 391 tinfo = &d->fc->tcode[pkt.mode.hdr.tcode]; 392 if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t), 393 tinfo->hdr_len - sizeof(uint32_t), uio))) 394 return (err); 395 396 if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid, 397 PAGE_SIZE/*XXX*/)) == NULL) 398 return (ENOMEM); 399 400 bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt)); 401 xfer->send.pay_len = uio->uio_resid; 402 if (uio->uio_resid > 0) { 403 if ((err = uiomove((caddr_t)&xfer->send.payload[0], 404 uio->uio_resid, uio))) 405 goto out; 406 } 407 408 xfer->fc = d->fc; 409 xfer->sc = NULL; 410 xfer->hand = fw_xferwake; 411 xfer->send.spd = FWSPD_S400; 412 413 if ((err = fw_asyreq(xfer->fc, -1, xfer))) 414 goto out; 415 416 if ((err = fw_xferwait(xfer))) 417 goto out; 418 419 if (xfer->resp != 0) { 420 err = xfer->resp; 421 goto out; 422 } 423 424 if (xfer->flag & FWXF_RCVD) { 425 FW_GLOCK(xfer->fc); 426 STAILQ_INSERT_TAIL(&d->rq, xfer, link); 427 FW_GUNLOCK(xfer->fc); 428 return (0); 429 } 430 431 out: 432 fw_xfer_free(xfer); 433 return (err); 434 } 435 436 static int 437 fw_write(struct cdev *dev, struct uio *uio, int ioflag) 438 { 439 int err = 0; 440 int slept = 0; 441 struct fw_drv1 *d; 442 struct fw_pkt *fp; 443 struct firewire_comm *fc; 444 struct fw_xferq *it; 445 446 if (DEV_FWMEM(dev)) 447 return (physio(dev, uio, ioflag)); 448 449 d = dev->si_drv1; 450 fc = d->fc; 451 it = d->it; 452 453 if (it == NULL) 454 return (fw_write_async(d, uio, ioflag)); 455 456 if (it->buf == NULL) 457 return (EIO); 458 459 FW_GLOCK(fc); 460 isoloop: 461 if (it->stproc == NULL) { 462 it->stproc = STAILQ_FIRST(&it->stfree); 463 if (it->stproc != NULL) { 464 STAILQ_REMOVE_HEAD(&it->stfree, link); 465 it->queued = 0; 466 } else if (slept == 0) { 467 slept = 1; 468 err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz); 469 if (err) 470 goto out; 471 goto isoloop; 472 } else { 473 err = EIO; 474 goto out; 475 } 476 } 477 FW_GUNLOCK(fc); 478 fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 479 it->stproc->poffset + it->queued); 480 err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 481 err = uiomove((caddr_t)fp->mode.stream.payload, 482 fp->mode.stream.len, uio); 483 it->queued++; 484 if (it->queued >= it->bnpacket) { 485 STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 486 it->stproc = NULL; 487 err = fc->itx_enable(fc, it->dmach); 488 } 489 if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 490 slept = 0; 491 FW_GLOCK(fc); 492 goto isoloop; 493 } 494 return err; 495 496 out: 497 FW_GUNLOCK(fc); 498 return err; 499 } 500 501 static void 502 fw_hand(struct fw_xfer *xfer) 503 { 504 struct fw_bind *fwb; 505 struct fw_drv1 *d; 506 507 fwb = (struct fw_bind *)xfer->sc; 508 d = fwb->sc; 509 FW_GLOCK(xfer->fc); 510 STAILQ_INSERT_TAIL(&d->rq, xfer, link); 511 FW_GUNLOCK(xfer->fc); 512 wakeup(&d->rq); 513 } 514 515 /* 516 * ioctl support. 517 */ 518 int 519 fw_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 520 { 521 struct firewire_comm *fc; 522 struct fw_drv1 *d; 523 int i, len, err = 0; 524 struct fw_device *fwdev; 525 struct fw_bind *fwb; 526 struct fw_xferq *ir, *it; 527 struct fw_xfer *xfer; 528 struct fw_pkt *fp; 529 struct fw_devinfo *devinfo; 530 void *ptr; 531 532 struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 533 struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 534 struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 535 struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 536 struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 537 struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 538 539 if (DEV_FWMEM(dev)) 540 return fwmem_ioctl(dev, cmd, data, flag, td); 541 542 if (!data) 543 return (EINVAL); 544 545 d = dev->si_drv1; 546 fc = d->fc; 547 ir = d->ir; 548 it = d->it; 549 550 switch (cmd) { 551 case FW_STSTREAM: 552 if (it == NULL) { 553 i = fw_open_isodma(fc, /* tx */1); 554 if (i < 0) { 555 err = EBUSY; 556 break; 557 } 558 it = fc->it[i]; 559 err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 560 if (err) { 561 it->flag &= ~FWXFERQ_OPEN; 562 break; 563 } 564 } 565 it->flag &= ~0xff; 566 it->flag |= (0x3f & ichreq->ch); 567 it->flag |= ((0x3 & ichreq->tag) << 6); 568 d->it = it; 569 break; 570 case FW_GTSTREAM: 571 if (it != NULL) { 572 ichreq->ch = it->flag & 0x3f; 573 ichreq->tag = it->flag >> 2 & 0x3; 574 } else 575 err = EINVAL; 576 break; 577 case FW_SRSTREAM: 578 if (ir == NULL) { 579 i = fw_open_isodma(fc, /* tx */0); 580 if (i < 0) { 581 err = EBUSY; 582 break; 583 } 584 ir = fc->ir[i]; 585 err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 586 if (err) { 587 ir->flag &= ~FWXFERQ_OPEN; 588 break; 589 } 590 } 591 ir->flag &= ~0xff; 592 ir->flag |= (0x3f & ichreq->ch); 593 ir->flag |= ((0x3 & ichreq->tag) << 6); 594 d->ir = ir; 595 err = fc->irx_enable(fc, ir->dmach); 596 break; 597 case FW_GRSTREAM: 598 if (d->ir != NULL) { 599 ichreq->ch = ir->flag & 0x3f; 600 ichreq->tag = ir->flag >> 2 & 0x3; 601 } else 602 err = EINVAL; 603 break; 604 case FW_SSTBUF: 605 bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 606 break; 607 case FW_GSTBUF: 608 bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 609 if (ir != NULL) { 610 ibufreq->rx.nchunk = ir->bnchunk; 611 ibufreq->rx.npacket = ir->bnpacket; 612 ibufreq->rx.psize = ir->psize; 613 } 614 bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 615 if (it != NULL) { 616 ibufreq->tx.nchunk = it->bnchunk; 617 ibufreq->tx.npacket = it->bnpacket; 618 ibufreq->tx.psize = it->psize; 619 } 620 break; 621 case FW_ASYREQ: 622 { 623 struct tcode_info *tinfo; 624 int pay_len = 0; 625 626 fp = &asyreq->pkt; 627 tinfo = &fc->tcode[fp->mode.hdr.tcode]; 628 629 if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) 630 pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); 631 632 xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); 633 if (xfer == NULL) 634 return (ENOMEM); 635 636 switch (asyreq->req.type) { 637 case FWASREQNODE: 638 break; 639 case FWASREQEUI: 640 fwdev = fw_noderesolve_eui64(fc, 641 &asyreq->req.dst.eui); 642 if (fwdev == NULL) { 643 device_printf(fc->bdev, 644 "cannot find node\n"); 645 err = EINVAL; 646 goto out; 647 } 648 fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; 649 break; 650 case FWASRESTL: 651 /* XXX what's this? */ 652 break; 653 case FWASREQSTREAM: 654 /* nothing to do */ 655 break; 656 } 657 658 bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); 659 if (pay_len > 0) 660 bcopy((char *)fp + tinfo->hdr_len, 661 xfer->send.payload, pay_len); 662 xfer->send.spd = asyreq->req.sped; 663 xfer->hand = fw_xferwake; 664 665 if ((err = fw_asyreq(fc, -1, xfer)) != 0) 666 goto out; 667 if ((err = fw_xferwait(xfer)) != 0) 668 goto out; 669 if (xfer->resp != 0) { 670 err = EIO; 671 goto out; 672 } 673 if ((tinfo->flag & FWTI_TLABEL) == 0) 674 goto out; 675 676 /* copy response */ 677 tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; 678 if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB || 679 xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) { 680 pay_len = xfer->recv.pay_len; 681 if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) { 682 asyreq->req.len = xfer->recv.pay_len + 683 tinfo->hdr_len; 684 } else { 685 err = EINVAL; 686 pay_len = 0; 687 } 688 } else { 689 pay_len = 0; 690 } 691 bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); 692 bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len); 693 out: 694 fw_xfer_free_buf(xfer); 695 break; 696 } 697 case FW_IBUSRST: 698 fc->ibr(fc); 699 break; 700 case FW_CBINDADDR: 701 fwb = fw_bindlookup(fc, 702 bindreq->start.hi, bindreq->start.lo); 703 if (fwb == NULL) { 704 err = EINVAL; 705 break; 706 } 707 fw_bindremove(fc, fwb); 708 STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist); 709 fw_xferlist_remove(&fwb->xferlist); 710 free(fwb, M_FW); 711 break; 712 case FW_SBINDADDR: 713 if (bindreq->len <= 0) { 714 err = EINVAL; 715 break; 716 } 717 if (bindreq->start.hi > 0xffff) { 718 err = EINVAL; 719 break; 720 } 721 fwb = malloc(sizeof(struct fw_bind), M_FW, M_WAITOK); 722 fwb->start = ((u_int64_t)bindreq->start.hi << 32) | 723 bindreq->start.lo; 724 fwb->end = fwb->start + bindreq->len; 725 fwb->sc = d; 726 STAILQ_INIT(&fwb->xferlist); 727 err = fw_bindadd(fc, fwb); 728 if (err == 0) { 729 fw_xferlist_add(&fwb->xferlist, M_FWXFER, 730 /* XXX */ 731 PAGE_SIZE, PAGE_SIZE, 5, 732 fc, fwb, fw_hand); 733 STAILQ_INSERT_TAIL(&d->binds, fwb, chlist); 734 } 735 break; 736 case FW_GDEVLST: 737 i = len = 1; 738 /* myself */ 739 devinfo = &fwdevlst->dev[0]; 740 devinfo->dst = fc->nodeid; 741 devinfo->status = 0; /* XXX */ 742 devinfo->eui.hi = fc->eui.hi; 743 devinfo->eui.lo = fc->eui.lo; 744 STAILQ_FOREACH(fwdev, &fc->devices, link) { 745 if (len < FW_MAX_DEVLST) { 746 devinfo = &fwdevlst->dev[len++]; 747 devinfo->dst = fwdev->dst; 748 devinfo->status = 749 (fwdev->status == FWDEVINVAL) ? 0 : 1; 750 devinfo->eui.hi = fwdev->eui.hi; 751 devinfo->eui.lo = fwdev->eui.lo; 752 } 753 i++; 754 } 755 fwdevlst->n = i; 756 fwdevlst->info_len = len; 757 break; 758 case FW_GTPMAP: 759 bcopy(fc->topology_map, data, 760 (fc->topology_map->crc_len + 1) * 4); 761 break; 762 case FW_GCROM: 763 STAILQ_FOREACH(fwdev, &fc->devices, link) 764 if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 765 break; 766 if (fwdev == NULL) { 767 if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) { 768 err = FWNODE_INVAL; 769 break; 770 } 771 /* myself */ 772 ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 773 len = CROMSIZE; 774 for (i = 0; i < CROMSIZE/4; i++) 775 ((uint32_t *)ptr)[i] 776 = ntohl(fc->config_rom[i]); 777 } else { 778 /* found */ 779 ptr = (void *)&fwdev->csrrom[0]; 780 if (fwdev->rommax < CSRROMOFF) 781 len = 0; 782 else 783 len = fwdev->rommax - CSRROMOFF + 4; 784 } 785 if (crom_buf->len < len) 786 len = crom_buf->len; 787 else 788 crom_buf->len = len; 789 err = copyout(ptr, crom_buf->ptr, len); 790 if (fwdev == NULL) 791 /* myself */ 792 free(ptr, M_FW); 793 break; 794 default: 795 fc->ioctl(dev, cmd, data, flag, td); 796 break; 797 } 798 return err; 799 } 800 int 801 fw_poll(struct cdev *dev, int events, fw_proc *td) 802 { 803 struct fw_xferq *ir; 804 int revents; 805 int tmp; 806 807 if (DEV_FWMEM(dev)) 808 return fwmem_poll(dev, events, td); 809 810 ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 811 revents = 0; 812 tmp = POLLIN | POLLRDNORM; 813 if (events & tmp) { 814 if (STAILQ_FIRST(&ir->q) != NULL) 815 revents |= tmp; 816 else 817 selrecord(td, &ir->rsel); 818 } 819 tmp = POLLOUT | POLLWRNORM; 820 if (events & tmp) { 821 /* XXX should be fixed */ 822 revents |= tmp; 823 } 824 825 return revents; 826 } 827 828 static int 829 fw_mmap (struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 830 int nproto, vm_memattr_t *memattr) 831 { 832 833 if (DEV_FWMEM(dev)) 834 return fwmem_mmap(dev, offset, paddr, nproto, memattr); 835 836 return EINVAL; 837 } 838 839 static void 840 fw_strategy(struct bio *bp) 841 { 842 struct cdev *dev; 843 844 dev = bp->bio_dev; 845 if (DEV_FWMEM(dev)) { 846 fwmem_strategy(bp); 847 return; 848 } 849 850 bp->bio_error = EOPNOTSUPP; 851 bp->bio_flags |= BIO_ERROR; 852 bp->bio_resid = bp->bio_bcount; 853 biodone(bp); 854 } 855 856 int 857 fwdev_makedev(struct firewire_softc *sc) 858 { 859 int err = 0; 860 861 struct cdev *d; 862 int unit; 863 864 unit = device_get_unit(sc->fc->bdev); 865 sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 866 UID_ROOT, GID_OPERATOR, 0660, "fw%d.%d", unit, 0); 867 d = make_dev(&firewire_cdevsw, MAKEMINOR(FWMEM_FLAG, unit, 0), 868 UID_ROOT, GID_OPERATOR, 0660, "fwmem%d.%d", unit, 0); 869 dev_depends(sc->dev, d); 870 make_dev_alias(sc->dev, "fw%d", unit); 871 make_dev_alias(d, "fwmem%d", unit); 872 873 return (err); 874 } 875 876 int 877 fwdev_destroydev(struct firewire_softc *sc) 878 { 879 int err = 0; 880 881 destroy_dev(sc->dev); 882 return (err); 883 } 884 885 #define NDEVTYPE 2 886 void 887 fwdev_clone(void *arg, struct ucred *cred, char *name, int namelen, 888 struct cdev **dev) 889 { 890 struct firewire_softc *sc; 891 char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 892 char *subp = NULL; 893 int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 894 int i, unit = 0, sub = 0; 895 896 if (*dev != NULL) 897 return; 898 899 for (i = 0; i < NDEVTYPE; i++) 900 if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 901 goto found; 902 /* not match */ 903 return; 904 found: 905 906 if (subp == NULL || *subp++ != '.') 907 return; 908 909 /* /dev/fwU.S */ 910 while (isdigit(*subp)) { 911 sub *= 10; 912 sub += *subp++ - '0'; 913 } 914 if (*subp != '\0') 915 return; 916 917 sc = devclass_get_softc(firewire_devclass, unit); 918 if (sc == NULL) 919 return; 920 *dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw, 921 MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR, 922 0660, "%s%d.%d", devnames[i], unit, sub); 923 dev_depends(sc->dev, *dev); 924 return; 925 } 926