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