fwdev.c revision 196969
1178172Simp/*- 2195162Simp * Copyright (c) 2003 Hidetoshi Shimokawa 3178172Simp * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa 4178172Simp * All rights reserved. 5178172Simp * 6178172Simp * Redistribution and use in source and binary forms, with or without 7178172Simp * modification, are permitted provided that the following conditions 8178172Simp * are met: 9178172Simp * 1. Redistributions of source code must retain the above copyright 10178172Simp * notice, this list of conditions and the following disclaimer. 11178172Simp * 2. Redistributions in binary form must reproduce the above copyright 12178172Simp * notice, this list of conditions and the following disclaimer in the 13178172Simp * documentation and/or other materials provided with the distribution. 14178172Simp * 3. All advertising materials mentioning features or use of this software 15178172Simp * must display the acknowledgement as bellow: 16178172Simp * 17178172Simp * This product includes software developed by K. Kobayashi and H. Shimokawa 18178172Simp * 19178172Simp * 4. The name of the author may not be used to endorse or promote products 20178172Simp * derived from this software without specific prior written permission. 21178172Simp * 22178172Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23178172Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24178172Simp * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25178172Simp * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 26202046Simp * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27178172Simp * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28178172Simp * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29178172Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30178172Simp * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31178172Simp * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32202046Simp * POSSIBILITY OF SUCH DAMAGE. 33202046Simp * 34202046Simp * $FreeBSD: head/sys/dev/firewire/fwdev.c 196969 2009-09-08 13:16:55Z phk $ 35202046Simp * 36178172Simp */ 37178172Simp 38178172Simp#include <sys/param.h> 39178172Simp#include <sys/systm.h> 40178172Simp#include <sys/types.h> 41178172Simp#include <sys/mbuf.h> 42178172Simp#if defined(__DragonFly__) || __FreeBSD_version < 500000 43178172Simp#include <sys/buf.h> 44178172Simp#else 45178172Simp#include <sys/bio.h> 46178172Simp#endif 47178172Simp 48202046Simp#include <sys/kernel.h> 49178172Simp#include <sys/malloc.h> 50178172Simp#include <sys/conf.h> 51178172Simp#include <sys/poll.h> 52178172Simp 53178172Simp#include <sys/bus.h> 54178172Simp#include <sys/ctype.h> 55178172Simp#include <machine/bus.h> 56178172Simp 57178172Simp#include <sys/ioccom.h> 58204689Sneel 59202046Simp#ifdef __DragonFly__ 60178172Simp#include "firewire.h" 61202046Simp#include "firewirereg.h" 62202046Simp#include "fwdma.h" 63202046Simp#include "fwmem.h" 64202046Simp#include "iec68113.h" 65202046Simp#else 66202046Simp#include <dev/firewire/firewire.h> 67178172Simp#include <dev/firewire/firewirereg.h> 68178172Simp#include <dev/firewire/fwdma.h> 69178172Simp#include <dev/firewire/fwmem.h> 70178172Simp#include <dev/firewire/iec68113.h> 71178172Simp#endif 72178172Simp 73178172Simp#define FWNODE_INVAL 0xffff 74178172Simp 75178172Simpstatic d_open_t fw_open; 76178172Simpstatic d_close_t fw_close; 77178172Simpstatic d_ioctl_t fw_ioctl; 78178172Simpstatic d_poll_t fw_poll; 79178172Simpstatic d_read_t fw_read; /* for Isochronous packet */ 80178172Simpstatic d_write_t fw_write; 81178172Simpstatic d_mmap_t fw_mmap; 82178172Simpstatic d_strategy_t fw_strategy; 83202046Simp 84178172Simpstruct cdevsw firewire_cdevsw = { 85178172Simp#ifdef __DragonFly__ 86202046Simp#define CDEV_MAJOR 127 87202046Simp "fw", CDEV_MAJOR, D_MEM, NULL, 0, 88202046Simp fw_open, fw_close, fw_read, fw_write, fw_ioctl, 89202046Simp fw_poll, fw_mmap, fw_strategy, nodump, nopsize, 90202046Simp#elif __FreeBSD_version >= 500104 91202046Simp .d_version = D_VERSION, 92202046Simp .d_open = fw_open, 93202046Simp .d_close = fw_close, 94202046Simp .d_read = fw_read, 95202046Simp .d_write = fw_write, 96202046Simp .d_ioctl = fw_ioctl, 97202046Simp .d_poll = fw_poll, 98202046Simp .d_mmap = fw_mmap, 99202046Simp .d_strategy = fw_strategy, 100202046Simp .d_name = "fw", 101202046Simp .d_flags = D_MEM 102202046Simp#else 103202046Simp#define CDEV_MAJOR 127 104202046Simp fw_open, fw_close, fw_read, fw_write, fw_ioctl, 105202046Simp fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR, 106202046Simp nodump, nopsize, D_MEM, -1 107202046Simp#endif 108202046Simp}; 109202046Simp 110202046Simpstruct fw_drv1 { 111202046Simp struct firewire_comm *fc; 112202046Simp struct fw_xferq *ir; 113202046Simp struct fw_xferq *it; 114202046Simp struct fw_isobufreq bufreq; 115202046Simp STAILQ_HEAD(, fw_bind) binds; 116202046Simp STAILQ_HEAD(, fw_xfer) rq; 117202046Simp}; 118202046Simp 119202046Simpstatic int 120227309Sedfwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 121202046Simp struct fw_bufspec *b) 122202046Simp{ 123202046Simp int i; 124178172Simp 125178172Simp if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 126178172Simp return(EBUSY); 127178172Simp 128204689Sneel q->bulkxfer = (struct fw_bulkxfer *) malloc( 129202046Simp sizeof(struct fw_bulkxfer) * b->nchunk, 130202046Simp M_FW, M_WAITOK); 131202046Simp if (q->bulkxfer == NULL) 132178172Simp return(ENOMEM); 133202046Simp 134202046Simp b->psize = roundup2(b->psize, sizeof(uint32_t)); 135202046Simp q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), 136212284Sjchandra b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 137178172Simp 138178172Simp if (q->buf == NULL) { 139178172Simp free(q->bulkxfer, M_FW); 140178172Simp q->bulkxfer = NULL; 141178172Simp return(ENOMEM); 142178172Simp } 143202046Simp q->bnchunk = b->nchunk; 144202046Simp q->bnpacket = b->npacket; 145212284Sjchandra q->psize = (b->psize + 3) & ~3; 146202046Simp q->queued = 0; 147178172Simp 148178172Simp STAILQ_INIT(&q->stvalid); 149202046Simp STAILQ_INIT(&q->stfree); 150202046Simp STAILQ_INIT(&q->stdma); 151202046Simp q->stproc = NULL; 152178172Simp 153178172Simp for(i = 0 ; i < q->bnchunk; i++){ 154178172Simp q->bulkxfer[i].poffset = i * q->bnpacket; 155178172Simp q->bulkxfer[i].mbuf = NULL; 156178172Simp STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 157178172Simp } 158178172Simp 159178172Simp q->flag &= ~FWXFERQ_MODEMASK; 160178172Simp q->flag |= FWXFERQ_STREAM; 161178172Simp q->flag |= FWXFERQ_EXTBUF; 162202046Simp 163202046Simp return (0); 164202046Simp} 165202046Simp 166202046Simpstatic int 167202046Simpfwdev_freebuf(struct fw_xferq *q) 168202046Simp{ 169202046Simp if (q->flag & FWXFERQ_EXTBUF) { 170202046Simp if (q->buf != NULL) 171202046Simp fwdma_free_multiseg(q->buf); 172202046Simp q->buf = NULL; 173202046Simp free(q->bulkxfer, M_FW); 174202046Simp q->bulkxfer = NULL; 175202046Simp q->flag &= ~FWXFERQ_EXTBUF; 176202046Simp q->psize = 0; 177202046Simp q->maxq = FWMAXQUEUE; 178202046Simp } 179202046Simp return (0); 180202046Simp} 181202046Simp 182202046Simp 183202046Simpstatic int 184202046Simpfw_open (struct cdev *dev, int flags, int fmt, fw_proc *td) 185202046Simp{ 186202046Simp int err = 0; 187202046Simp int unit = DEV2UNIT(dev); 188202046Simp struct fw_drv1 *d; 189202046Simp struct firewire_softc *sc; 190202046Simp 191202046Simp if (DEV_FWMEM(dev)) 192202046Simp return fwmem_open(dev, flags, fmt, td); 193202046Simp 194202046Simp sc = devclass_get_softc(firewire_devclass, unit); 195202046Simp if (sc == NULL) 196202046Simp return (ENXIO); 197202046Simp 198202046Simp FW_GLOCK(sc->fc); 199202046Simp if (dev->si_drv1 != NULL) { 200202046Simp FW_GUNLOCK(sc->fc); 201178172Simp return (EBUSY); 202178172Simp } 203178172Simp /* set dummy value for allocation */ 204178172Simp dev->si_drv1 = (void *)-1; 205178172Simp FW_GUNLOCK(sc->fc); 206178172Simp 207178172Simp dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 208178172Simp if (dev->si_drv1 == NULL) 209178172Simp return (ENOMEM); 210178172Simp 211178172Simp#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 212178172Simp if ((dev->si_flags & SI_NAMED) == 0) { 213178172Simp int unit = DEV2UNIT(dev); 214178172Simp int sub = DEV2SUB(dev); 215178172Simp 216178172Simp make_dev(&firewire_cdevsw, dev2unit(dev), 217178172Simp UID_ROOT, GID_OPERATOR, 0660, 218178172Simp "fw%d.%d", unit, sub); 219178172Simp } 220178172Simp#endif 221202046Simp d = (struct fw_drv1 *)dev->si_drv1; 222202046Simp d->fc = sc->fc; 223202046Simp STAILQ_INIT(&d->binds); 224202046Simp STAILQ_INIT(&d->rq); 225202046Simp 226202046Simp return err; 227202046Simp} 228202046Simp 229202046Simpstatic int 230202046Simpfw_close (struct cdev *dev, int flags, int fmt, fw_proc *td) 231202046Simp{ 232202046Simp struct firewire_comm *fc; 233202046Simp struct fw_drv1 *d; 234178172Simp struct fw_xfer *xfer; 235178172Simp struct fw_bind *fwb; 236178172Simp int err = 0; 237178172Simp 238178172Simp if (DEV_FWMEM(dev)) 239178172Simp return fwmem_close(dev, flags, fmt, td); 240178172Simp 241178172Simp d = (struct fw_drv1 *)dev->si_drv1; 242178172Simp fc = d->fc; 243178172Simp 244178172Simp /* remove binding */ 245178172Simp for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL; 246178172Simp fwb = STAILQ_FIRST(&d->binds)) { 247178172Simp fw_bindremove(fc, fwb); 248178172Simp STAILQ_REMOVE_HEAD(&d->binds, chlist); 249178172Simp fw_xferlist_remove(&fwb->xferlist); 250178172Simp free(fwb, M_FW); 251178172Simp } 252178172Simp if (d->ir != NULL) { 253178172Simp struct fw_xferq *ir = d->ir; 254178172Simp 255178172Simp if ((ir->flag & FWXFERQ_OPEN) == 0) 256178172Simp return (EINVAL); 257178172Simp if (ir->flag & FWXFERQ_RUNNING) { 258178172Simp ir->flag &= ~FWXFERQ_RUNNING; 259178172Simp fc->irx_disable(fc, ir->dmach); 260178172Simp } 261178172Simp /* free extbuf */ 262178172Simp fwdev_freebuf(ir); 263178172Simp /* drain receiving buffer */ 264178172Simp for (xfer = STAILQ_FIRST(&ir->q); 265178172Simp xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 266178172Simp ir->queued --; 267178172Simp STAILQ_REMOVE_HEAD(&ir->q, link); 268178172Simp 269178172Simp xfer->resp = 0; 270178172Simp fw_xfer_done(xfer); 271178172Simp } 272178172Simp ir->flag &= ~(FWXFERQ_OPEN | 273178172Simp FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 274178172Simp d->ir = NULL; 275178172Simp 276178172Simp } 277178172Simp if (d->it != NULL) { 278178172Simp struct fw_xferq *it = d->it; 279178172Simp 280178172Simp if ((it->flag & FWXFERQ_OPEN) == 0) 281178172Simp return (EINVAL); 282178172Simp if (it->flag & FWXFERQ_RUNNING) { 283178172Simp it->flag &= ~FWXFERQ_RUNNING; 284178172Simp fc->itx_disable(fc, it->dmach); 285178172Simp } 286178172Simp /* free extbuf */ 287178172Simp fwdev_freebuf(it); 288178172Simp it->flag &= ~(FWXFERQ_OPEN | 289178172Simp FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 290202046Simp d->it = NULL; 291178172Simp } 292178172Simp free(dev->si_drv1, M_FW); 293178172Simp dev->si_drv1 = NULL; 294178172Simp 295178172Simp return err; 296178172Simp} 297178172Simp 298178172Simpstatic int 299178172Simpfw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 300178172Simp{ 301178172Simp int err = 0, s; 302178172Simp struct fw_xfer *xfer; 303178172Simp struct fw_bind *fwb; 304178172Simp struct fw_pkt *fp; 305178172Simp struct tcode_info *tinfo; 306202046Simp 307202046Simp FW_GLOCK(d->fc); 308202046Simp while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0) 309202046Simp err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0); 310202046Simp 311178172Simp if (err != 0) { 312178172Simp FW_GUNLOCK(d->fc); 313212284Sjchandra return (err); 314212284Sjchandra } 315212284Sjchandra 316212284Sjchandra s = splfw(); 317212284Sjchandra STAILQ_REMOVE_HEAD(&d->rq, link); 318178172Simp FW_GUNLOCK(xfer->fc); 319178172Simp splx(s); 320178172Simp fp = &xfer->recv.hdr; 321178172Simp#if 0 /* for GASP ?? */ 322178172Simp if (fc->irx_post != NULL) 323202046Simp fc->irx_post(fc, fp->mode.ld); 324202046Simp#endif 325178172Simp tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode]; 326202046Simp err = uiomove((void *)fp, tinfo->hdr_len, uio); 327178172Simp if (err) 328178172Simp goto out; 329178172Simp err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio); 330178172Simp 331178172Simpout: 332178172Simp /* recycle this xfer */ 333178172Simp fwb = (struct fw_bind *)xfer->sc; 334178172Simp fw_xfer_unload(xfer); 335178172Simp xfer->recv.pay_len = PAGE_SIZE; 336202046Simp FW_GLOCK(xfer->fc); 337202046Simp STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 338178172Simp FW_GUNLOCK(xfer->fc); 339178172Simp return (err); 340212284Sjchandra} 341212284Sjchandra 342178172Simp/* 343178172Simp * read request. 344204689Sneel */ 345204689Sneelstatic int 346178172Simpfw_read (struct cdev *dev, struct uio *uio, int ioflag) 347178172Simp{ 348178172Simp struct fw_drv1 *d; 349178172Simp struct fw_xferq *ir; 350178172Simp struct firewire_comm *fc; 351178172Simp int err = 0, s, slept = 0; 352178172Simp struct fw_pkt *fp; 353178172Simp 354178172Simp if (DEV_FWMEM(dev)) 355212284Sjchandra return (physio(dev, uio, ioflag)); 356202046Simp 357202046Simp d = (struct fw_drv1 *)dev->si_drv1; 358212284Sjchandra fc = d->fc; 359212284Sjchandra ir = d->ir; 360212284Sjchandra 361178172Simp if (ir == NULL) 362178172Simp return (fw_read_async(d, uio, ioflag)); 363178172Simp 364212284Sjchandra if (ir->buf == NULL) 365212284Sjchandra return (EIO); 366202046Simp 367202046Simp FW_GLOCK(fc); 368202046Simpreadloop: 369212284Sjchandra if (ir->stproc == NULL) { 370212284Sjchandra /* iso bulkxfer */ 371212284Sjchandra ir->stproc = STAILQ_FIRST(&ir->stvalid); 372212284Sjchandra if (ir->stproc != NULL) { 373212284Sjchandra s = splfw(); 374212284Sjchandra STAILQ_REMOVE_HEAD(&ir->stvalid, link); 375212284Sjchandra splx(s); 376212284Sjchandra ir->queued = 0; 377178172Simp } 378178172Simp } 379178172Simp if (ir->stproc == NULL) { 380178172Simp /* no data avaliable */ 381202046Simp if (slept == 0) { 382202046Simp slept = 1; 383202046Simp ir->flag |= FWXFERQ_WAKEUP; 384178172Simp err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz); 385202046Simp ir->flag &= ~FWXFERQ_WAKEUP; 386202046Simp if (err == 0) 387202046Simp goto readloop; 388202046Simp } else if (slept == 1) 389202046Simp err = EIO; 390202046Simp FW_GUNLOCK(fc); 391202046Simp return err; 392202046Simp } else if(ir->stproc != NULL) { 393202046Simp /* iso bulkxfer */ 394202046Simp FW_GUNLOCK(fc); 395202046Simp fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 396202046Simp ir->stproc->poffset + ir->queued); 397202046Simp if(fc->irx_post != NULL) 398202046Simp fc->irx_post(fc, fp->mode.ld); 399202046Simp if(fp->mode.stream.len == 0){ 400202046Simp err = EIO; 401202046Simp return err; 402202046Simp } 403202046Simp err = uiomove((caddr_t)fp, 404202046Simp fp->mode.stream.len + sizeof(uint32_t), uio); 405202046Simp ir->queued ++; 406202046Simp if(ir->queued >= ir->bnpacket){ 407202046Simp s = splfw(); 408202046Simp STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 409202046Simp splx(s); 410202046Simp fc->irx_enable(fc, ir->dmach); 411178172Simp ir->stproc = NULL; 412202046Simp } 413178172Simp if (uio->uio_resid >= ir->psize) { 414178172Simp slept = -1; 415178172Simp FW_GLOCK(fc); 416202046Simp goto readloop; 417178172Simp } 418178172Simp } 419178172Simp return err; 420178172Simp} 421178172Simp 422178172Simpstatic int 423178172Simpfw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 424178172Simp{ 425178172Simp struct fw_xfer *xfer; 426178172Simp struct fw_pkt pkt; 427178172Simp struct tcode_info *tinfo; 428212284Sjchandra int err; 429212284Sjchandra 430178172Simp bzero(&pkt, sizeof(struct fw_pkt)); 431212284Sjchandra if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio))) 432212284Sjchandra return (err); 433178172Simp tinfo = &d->fc->tcode[pkt.mode.hdr.tcode]; 434212284Sjchandra if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t), 435212284Sjchandra tinfo->hdr_len - sizeof(uint32_t), uio))) 436212284Sjchandra return (err); 437212284Sjchandra 438212284Sjchandra if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid, 439212284Sjchandra PAGE_SIZE/*XXX*/)) == NULL) 440212284Sjchandra return (ENOMEM); 441212284Sjchandra 442212284Sjchandra bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt)); 443212284Sjchandra xfer->send.pay_len = uio->uio_resid; 444212284Sjchandra if (uio->uio_resid > 0) { 445212284Sjchandra if ((err = uiomove((caddr_t)&xfer->send.payload[0], 446212284Sjchandra uio->uio_resid, uio))) 447212284Sjchandra goto out; 448178172Simp } 449178172Simp 450212284Sjchandra xfer->fc = d->fc; 451178172Simp xfer->sc = NULL; 452178172Simp xfer->hand = fw_xferwake; 453202046Simp xfer->send.spd = 2 /* XXX */; 454178172Simp 455178172Simp if ((err = fw_asyreq(xfer->fc, -1, xfer))) 456178172Simp goto out; 457178172Simp 458178172Simp if ((err = fw_xferwait(xfer))) 459178172Simp goto out; 460178172Simp 461178172Simp if (xfer->resp != 0) { 462178172Simp err = xfer->resp; 463178172Simp goto out; 464178172Simp } 465178172Simp 466178172Simp if (xfer->flag & FWXF_RCVD) { 467178172Simp FW_GLOCK(xfer->fc); 468178172Simp STAILQ_INSERT_TAIL(&d->rq, xfer, link); 469178172Simp FW_GUNLOCK(xfer->fc); 470178172Simp return (0); 471202046Simp } 472178172Simp 473178172Simpout: 474202046Simp fw_xfer_free(xfer); 475202046Simp return (err); 476202046Simp} 477202046Simp 478202046Simpstatic int 479202046Simpfw_write (struct cdev *dev, struct uio *uio, int ioflag) 480202046Simp{ 481202046Simp int err = 0; 482202046Simp int s, slept = 0; 483202046Simp struct fw_drv1 *d; 484202046Simp struct fw_pkt *fp; 485202046Simp struct firewire_comm *fc; 486202046Simp struct fw_xferq *it; 487202046Simp 488202046Simp if (DEV_FWMEM(dev)) 489202046Simp return (physio(dev, uio, ioflag)); 490202046Simp 491202046Simp d = (struct fw_drv1 *)dev->si_drv1; 492202046Simp fc = d->fc; 493202046Simp it = d->it; 494202046Simp 495202046Simp if (it == NULL) 496202046Simp return (fw_write_async(d, uio, ioflag)); 497202046Simp 498202046Simp if (it->buf == NULL) 499202046Simp return (EIO); 500202046Simp 501202046Simp FW_GLOCK(fc); 502202046Simpisoloop: 503202046Simp if (it->stproc == NULL) { 504202046Simp it->stproc = STAILQ_FIRST(&it->stfree); 505202046Simp if (it->stproc != NULL) { 506202046Simp s = splfw(); 507202046Simp STAILQ_REMOVE_HEAD(&it->stfree, link); 508202046Simp splx(s); 509202046Simp it->queued = 0; 510202046Simp } else if (slept == 0) { 511202046Simp slept = 1; 512202046Simp#if 0 /* XXX to avoid lock recursion */ 513202046Simp err = fc->itx_enable(fc, it->dmach); 514202046Simp if (err) 515202046Simp goto out; 516202046Simp#endif 517202046Simp err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz); 518202046Simp if (err) 519202046Simp goto out; 520202046Simp goto isoloop; 521202046Simp } else { 522178172Simp err = EIO; 523178172Simp goto out; 524178172Simp } 525178172Simp } 526178172Simp FW_GUNLOCK(fc); 527178172Simp fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 528178172Simp it->stproc->poffset + it->queued); 529178172Simp err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 530178172Simp err = uiomove((caddr_t)fp->mode.stream.payload, 531178172Simp fp->mode.stream.len, uio); 532178172Simp it->queued ++; 533178172Simp if (it->queued >= it->bnpacket) { 534178172Simp s = splfw(); 535202046Simp STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 536178172Simp splx(s); 537202046Simp it->stproc = NULL; 538202046Simp err = fc->itx_enable(fc, it->dmach); 539202046Simp } 540202046Simp if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 541202046Simp slept = 0; 542202046Simp FW_GLOCK(fc); 543202046Simp goto isoloop; 544178172Simp } 545178172Simp return err; 546178172Simp 547178172Simpout: 548178172Simp FW_GUNLOCK(fc); 549178172Simp return err; 550178172Simp} 551178172Simp 552178172Simpstatic void 553178172Simpfw_hand(struct fw_xfer *xfer) 554178172Simp{ 555178172Simp struct fw_bind *fwb; 556212284Sjchandra struct fw_drv1 *d; 557178172Simp 558178172Simp fwb = (struct fw_bind *)xfer->sc; 559178172Simp d = (struct fw_drv1 *)fwb->sc; 560178172Simp FW_GLOCK(xfer->fc); 561178172Simp STAILQ_INSERT_TAIL(&d->rq, xfer, link); 562178172Simp FW_GUNLOCK(xfer->fc); 563178172Simp wakeup(&d->rq); 564178172Simp} 565178172Simp 566178172Simp/* 567178172Simp * ioctl support. 568178172Simp */ 569178172Simpint 570178172Simpfw_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 571178172Simp{ 572178172Simp struct firewire_comm *fc; 573178172Simp struct fw_drv1 *d; 574178172Simp int i, len, err = 0; 575178172Simp struct fw_device *fwdev; 576178172Simp struct fw_bind *fwb; 577178172Simp struct fw_xferq *ir, *it; 578202046Simp struct fw_xfer *xfer; 579204689Sneel struct fw_pkt *fp; 580204689Sneel struct fw_devinfo *devinfo; 581204689Sneel void *ptr; 582204689Sneel 583204689Sneel struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 584204689Sneel struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 585204689Sneel struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 586204689Sneel struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 587204689Sneel struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 588204689Sneel struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 589202046Simp 590204689Sneel if (DEV_FWMEM(dev)) 591204689Sneel return fwmem_ioctl(dev, cmd, data, flag, td); 592212284Sjchandra 593202046Simp if (!data) 594202046Simp return(EINVAL); 595204689Sneel 596178172Simp d = (struct fw_drv1 *)dev->si_drv1; 597202046Simp fc = d->fc; 598212284Sjchandra ir = d->ir; 599212284Sjchandra it = d->it; 600212284Sjchandra 601212284Sjchandra switch (cmd) { 602212284Sjchandra case FW_STSTREAM: 603212284Sjchandra if (it == NULL) { 604212284Sjchandra i = fw_open_isodma(fc, /* tx */1); 605212284Sjchandra if (i < 0) { 606212284Sjchandra err = EBUSY; 607212284Sjchandra break; 608212284Sjchandra } 609178172Simp it = fc->it[i]; 610178172Simp err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 611178172Simp if (err) { 612178172Simp it->flag &= ~FWXFERQ_OPEN; 613178172Simp break; 614212284Sjchandra } 615178172Simp } 616202046Simp it->flag &= ~0xff; 617204689Sneel it->flag |= (0x3f & ichreq->ch); 618178172Simp it->flag |= ((0x3 & ichreq->tag) << 6); 619178172Simp d->it = it; 620178172Simp break; 621212283Sjchandra case FW_GTSTREAM: 622212283Sjchandra if (it != NULL) { 623178172Simp ichreq->ch = it->flag & 0x3f; 624178172Simp ichreq->tag = it->flag >> 2 & 0x3; 625178172Simp } else 626178172Simp err = EINVAL; 627178172Simp break; 628178172Simp case FW_SRSTREAM: 629178172Simp if (ir == NULL) { 630202046Simp i = fw_open_isodma(fc, /* tx */0); 631178172Simp if (i < 0) { 632202046Simp err = EBUSY; 633212284Sjchandra break; 634178172Simp } 635178172Simp ir = fc->ir[i]; 636178172Simp err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 637178172Simp if (err) { 638178172Simp ir->flag &= ~FWXFERQ_OPEN; 639178172Simp break; 640178172Simp } 641178172Simp } 642178172Simp ir->flag &= ~0xff; 643178172Simp ir->flag |= (0x3f & ichreq->ch); 644178172Simp ir->flag |= ((0x3 & ichreq->tag) << 6); 645178172Simp d->ir = ir; 646178172Simp err = fc->irx_enable(fc, ir->dmach); 647178172Simp break; 648202046Simp case FW_GRSTREAM: 649212283Sjchandra if (d->ir != NULL) { 650212283Sjchandra ichreq->ch = ir->flag & 0x3f; 651212284Sjchandra ichreq->tag = ir->flag >> 2 & 0x3; 652178172Simp } else 653212284Sjchandra err = EINVAL; 654178172Simp break; 655202046Simp case FW_SSTBUF: 656178172Simp bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 657178172Simp break; 658178172Simp case FW_GSTBUF: 659202046Simp bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 660178172Simp if (ir != NULL) { 661202046Simp ibufreq->rx.nchunk = ir->bnchunk; 662202046Simp ibufreq->rx.npacket = ir->bnpacket; 663202046Simp ibufreq->rx.psize = ir->psize; 664202046Simp } 665202046Simp bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 666202046Simp if (it != NULL) { 667202046Simp ibufreq->tx.nchunk = it->bnchunk; 668202046Simp ibufreq->tx.npacket = it->bnpacket; 669202046Simp ibufreq->tx.psize = it->psize; 670202046Simp } 671202046Simp break; 672202046Simp case FW_ASYREQ: 673202046Simp { 674202046Simp struct tcode_info *tinfo; 675202046Simp int pay_len = 0; 676202046Simp 677202046Simp fp = &asyreq->pkt; 678206405Snwhitehorn tinfo = &fc->tcode[fp->mode.hdr.tcode]; 679202046Simp 680202046Simp if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) 681202046Simp pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); 682206405Snwhitehorn 683206405Snwhitehorn xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); 684202046Simp if (xfer == NULL) 685206405Snwhitehorn return (ENOMEM); 686202046Simp 687202046Simp switch (asyreq->req.type) { 688206405Snwhitehorn case FWASREQNODE: 689206405Snwhitehorn break; 690202046Simp case FWASREQEUI: 691206405Snwhitehorn fwdev = fw_noderesolve_eui64(fc, 692206405Snwhitehorn &asyreq->req.dst.eui); 693202046Simp if (fwdev == NULL) { 694202046Simp device_printf(fc->bdev, 695202046Simp "cannot find node\n"); 696202046Simp err = EINVAL; 697202046Simp goto out; 698202046Simp } 699202046Simp fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; 700202046Simp break; 701202046Simp case FWASRESTL: 702202046Simp /* XXX what's this? */ 703202046Simp break; 704202046Simp case FWASREQSTREAM: 705202046Simp /* nothing to do */ 706202046Simp break; 707202046Simp } 708202046Simp 709202046Simp bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); 710202046Simp if (pay_len > 0) 711202046Simp bcopy((char *)fp + tinfo->hdr_len, 712202046Simp (void *)xfer->send.payload, pay_len); 713202046Simp xfer->send.spd = asyreq->req.sped; 714202046Simp xfer->hand = fw_xferwake; 715202046Simp 716202046Simp if ((err = fw_asyreq(fc, -1, xfer)) != 0) 717202046Simp goto out; 718178172Simp if ((err = fw_xferwait(xfer)) != 0) 719178172Simp goto out; 720178172Simp if (xfer->resp != 0) { 721178172Simp err = EIO; 722178172Simp goto out; 723178172Simp } 724178172Simp if ((tinfo->flag & FWTI_TLABEL) == 0) 725178172Simp goto out; 726178172Simp 727178172Simp /* copy response */ 728178172Simp tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; 729178172Simp if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB || 730178172Simp xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) { 731178172Simp pay_len = xfer->recv.pay_len; 732202046Simp if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) { 733178172Simp asyreq->req.len = xfer->recv.pay_len + 734178172Simp tinfo->hdr_len; 735178172Simp } else { 736178172Simp err = EINVAL; 737178172Simp pay_len = 0; 738178172Simp } 739178172Simp } else { 740202046Simp pay_len = 0; 741202046Simp } 742202046Simp bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); 743202046Simp bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len); 744202046Simpout: 745202046Simp fw_xfer_free_buf(xfer); 746202046Simp break; 747202046Simp } 748202046Simp case FW_IBUSRST: 749178172Simp fc->ibr(fc); 750178172Simp break; 751178172Simp case FW_CBINDADDR: 752202046Simp fwb = fw_bindlookup(fc, 753202046Simp bindreq->start.hi, bindreq->start.lo); 754202046Simp if(fwb == NULL){ 755178172Simp err = EINVAL; 756178172Simp break; 757178172Simp } 758178172Simp fw_bindremove(fc, fwb); 759178172Simp STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist); 760178172Simp fw_xferlist_remove(&fwb->xferlist); 761178172Simp free(fwb, M_FW); 762178172Simp break; 763202046Simp case FW_SBINDADDR: 764202046Simp if(bindreq->len <= 0 ){ 765178172Simp err = EINVAL; 766178172Simp break; 767178172Simp } 768178172Simp if(bindreq->start.hi > 0xffff ){ 769202046Simp err = EINVAL; 770202046Simp break; 771202046Simp } 772202046Simp fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_WAITOK); 773202046Simp if(fwb == NULL){ 774202046Simp err = ENOMEM; 775202046Simp break; 776202046Simp } 777202046Simp fwb->start = ((u_int64_t)bindreq->start.hi << 32) | 778202046Simp bindreq->start.lo; 779202046Simp fwb->end = fwb->start + bindreq->len; 780202046Simp fwb->sc = (void *)d; 781202046Simp STAILQ_INIT(&fwb->xferlist); 782178172Simp err = fw_bindadd(fc, fwb); 783178172Simp if (err == 0) { 784178172Simp fw_xferlist_add(&fwb->xferlist, M_FWXFER, 785178172Simp /* XXX */ 786178172Simp PAGE_SIZE, PAGE_SIZE, 5, 787178172Simp fc, (void *)fwb, fw_hand); 788178172Simp STAILQ_INSERT_TAIL(&d->binds, fwb, chlist); 789178172Simp } 790178172Simp break; 791178172Simp case FW_GDEVLST: 792178172Simp i = len = 1; 793178172Simp /* myself */ 794178172Simp devinfo = &fwdevlst->dev[0]; 795178172Simp devinfo->dst = fc->nodeid; 796178172Simp devinfo->status = 0; /* XXX */ 797178172Simp devinfo->eui.hi = fc->eui.hi; 798178172Simp devinfo->eui.lo = fc->eui.lo; 799178172Simp STAILQ_FOREACH(fwdev, &fc->devices, link) { 800178172Simp if(len < FW_MAX_DEVLST){ 801178172Simp devinfo = &fwdevlst->dev[len++]; 802178172Simp devinfo->dst = fwdev->dst; 803178172Simp devinfo->status = 804178172Simp (fwdev->status == FWDEVINVAL)?0:1; 805178172Simp devinfo->eui.hi = fwdev->eui.hi; 806178172Simp devinfo->eui.lo = fwdev->eui.lo; 807178172Simp } 808178172Simp i++; 809178172Simp } 810178172Simp fwdevlst->n = i; 811178172Simp fwdevlst->info_len = len; 812178172Simp break; 813202046Simp case FW_GTPMAP: 814202046Simp bcopy(fc->topology_map, data, 815178172Simp (fc->topology_map->crc_len + 1) * 4); 816178172Simp break; 817178172Simp case FW_GCROM: 818178172Simp STAILQ_FOREACH(fwdev, &fc->devices, link) 819178172Simp if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 820178172Simp break; 821178172Simp if (fwdev == NULL) { 822212284Sjchandra if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) { 823212284Sjchandra err = FWNODE_INVAL; 824178172Simp break; 825212284Sjchandra } 826178172Simp /* myself */ 827178172Simp ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 828178172Simp len = CROMSIZE; 829178172Simp for (i = 0; i < CROMSIZE/4; i++) 830178172Simp ((uint32_t *)ptr)[i] 831178172Simp = ntohl(fc->config_rom[i]); 832178172Simp } else { 833178172Simp /* found */ 834178172Simp ptr = (void *)&fwdev->csrrom[0]; 835202046Simp if (fwdev->rommax < CSRROMOFF) 836202046Simp len = 0; 837178172Simp else 838202046Simp len = fwdev->rommax - CSRROMOFF + 4; 839178172Simp } 840178172Simp if (crom_buf->len < len) 841178172Simp len = crom_buf->len; 842178172Simp else 843178172Simp crom_buf->len = len; 844202046Simp err = copyout(ptr, crom_buf->ptr, len); 845202046Simp if (fwdev == NULL) 846178172Simp /* myself */ 847178172Simp /* XXX: Flexelint no sure about modified pointer */ 848178172Simp free(ptr, M_FW); 849178172Simp break; 850178172Simp default: 851178172Simp fc->ioctl (dev, cmd, data, flag, td); 852178172Simp break; 853178172Simp } 854202046Simp return err; 855178172Simp} 856178172Simpint 857178172Simpfw_poll(struct cdev *dev, int events, fw_proc *td) 858178172Simp{ 859178172Simp struct fw_xferq *ir; 860178172Simp int revents; 861178172Simp int tmp; 862212284Sjchandra 863212284Sjchandra if (DEV_FWMEM(dev)) 864178172Simp return fwmem_poll(dev, events, td); 865178172Simp 866178172Simp ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 867178172Simp revents = 0; 868178172Simp tmp = POLLIN | POLLRDNORM; 869178172Simp if (events & tmp) { 870178172Simp if (STAILQ_FIRST(&ir->q) != NULL) 871178172Simp revents |= tmp; 872178172Simp else 873178172Simp selrecord(td, &ir->rsel); 874178172Simp } 875202046Simp tmp = POLLOUT | POLLWRNORM; 876178172Simp if (events & tmp) { 877178172Simp /* XXX should be fixed */ 878178172Simp revents |= tmp; 879178172Simp } 880178172Simp 881178172Simp return revents; 882178172Simp} 883178172Simp 884178172Simpstatic int 885178172Simp#if defined(__DragonFly__) || __FreeBSD_version < 500102 886188506Simpfw_mmap (struct cdev *dev, vm_offset_t offset, int nproto) 887178172Simp#else 888178172Simpfw_mmap (struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) 889178172Simp#endif 890178172Simp{ 891178172Simp 892178172Simp if (DEV_FWMEM(dev)) 893178172Simp#if defined(__DragonFly__) || __FreeBSD_version < 500102 894178172Simp return fwmem_mmap(dev, offset, nproto); 895178172Simp#else 896178172Simp return fwmem_mmap(dev, offset, paddr, nproto); 897178172Simp#endif 898178172Simp 899178172Simp return EINVAL; 900178172Simp} 901178172Simp 902178172Simpstatic void 903178172Simpfw_strategy(struct bio *bp) 904178172Simp{ 905178172Simp struct cdev *dev; 906178172Simp 907178172Simp dev = bp->bio_dev; 908178172Simp if (DEV_FWMEM(dev)) { 909178172Simp fwmem_strategy(bp); 910178172Simp return; 911178172Simp } 912178172Simp 913178172Simp bp->bio_error = EOPNOTSUPP; 914178172Simp bp->bio_flags |= BIO_ERROR; 915178172Simp bp->bio_resid = bp->bio_bcount; 916178172Simp biodone(bp); 917178172Simp} 918178172Simp 919178172Simpint 920202046Simpfwdev_makedev(struct firewire_softc *sc) 921202046Simp{ 922178172Simp int err = 0; 923178172Simp 924178172Simp#if defined(__DragonFly__) || __FreeBSD_version < 500000 925178172Simp cdevsw_add(&firewire_cdevsw); 926178172Simp#else 927178172Simp struct cdev *d; 928178172Simp int unit; 929178172Simp 930202046Simp unit = device_get_unit(sc->fc->bdev); 931202046Simp sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 932202046Simp UID_ROOT, GID_OPERATOR, 0660, 933178172Simp "fw%d.%d", unit, 0); 934178172Simp d = make_dev(&firewire_cdevsw, 935178172Simp MAKEMINOR(FWMEM_FLAG, unit, 0), 936178172Simp UID_ROOT, GID_OPERATOR, 0660, 937178172Simp "fwmem%d.%d", unit, 0); 938178172Simp dev_depends(sc->dev, d); 939178172Simp make_dev_alias(sc->dev, "fw%d", unit); 940202046Simp make_dev_alias(d, "fwmem%d", unit); 941178172Simp#endif 942178172Simp 943178172Simp return (err); 944178172Simp} 945178172Simp 946178172Simpint 947178172Simpfwdev_destroydev(struct firewire_softc *sc) 948178172Simp{ 949178172Simp int err = 0; 950178172Simp 951178172Simp#if defined(__DragonFly__) || __FreeBSD_version < 500000 952178172Simp cdevsw_remove(&firewire_cdevsw); 953178172Simp#else 954178172Simp destroy_dev(sc->dev); 955202046Simp#endif 956202046Simp return (err); 957202046Simp} 958202046Simp 959202046Simp#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 960202046Simp#define NDEVTYPE 2 961202046Simpvoid 962202046Simpfwdev_clone(void *arg, struct ucred *cred, char *name, int namelen, 963202046Simp struct cdev **dev) 964202046Simp{ 965178172Simp struct firewire_softc *sc; 966202046Simp char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 967202046Simp char *subp = NULL; 968202046Simp int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 969202046Simp int i, unit = 0, sub = 0; 970202046Simp 971202046Simp if (*dev != NULL) 972202046Simp return; 973202046Simp 974202046Simp for (i = 0; i < NDEVTYPE; i++) 975202046Simp if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 976202046Simp goto found; 977202046Simp /* not match */ 978202046Simp return; 979202046Simpfound: 980202046Simp 981202046Simp if (subp == NULL || *subp++ != '.') 982202046Simp return; 983202046Simp 984202046Simp /* /dev/fwU.S */ 985202046Simp while (isdigit(*subp)) { 986202046Simp sub *= 10; 987202046Simp sub += *subp++ - '0'; 988202046Simp } 989202046Simp if (*subp != '\0') 990202046Simp return; 991202046Simp 992202046Simp sc = devclass_get_softc(firewire_devclass, unit); 993202046Simp if (sc == NULL) 994202046Simp return; 995202046Simp *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), 996202046Simp UID_ROOT, GID_OPERATOR, 0660, 997202046Simp "%s%d.%d", devnames[i], unit, sub); 998202046Simp dev_ref(*dev); 999202046Simp (*dev)->si_flags |= SI_CHEAPCLONE; 1000202046Simp dev_depends(sc->dev, *dev); 1001202046Simp return; 1002202046Simp} 1003202046Simp#endif 1004202046Simp