fwdev.c revision 255359
1114902Sscottl/*- 2119418Sobrien * Copyright (c) 2003 Hidetoshi Shimokawa 3114902Sscottl * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa 4114902Sscottl * All rights reserved. 5114902Sscottl * 6114902Sscottl * Redistribution and use in source and binary forms, with or without 7114902Sscottl * modification, are permitted provided that the following conditions 8114902Sscottl * are met: 9114902Sscottl * 1. Redistributions of source code must retain the above copyright 10114902Sscottl * notice, this list of conditions and the following disclaimer. 11114902Sscottl * 2. Redistributions in binary form must reproduce the above copyright 12114902Sscottl * notice, this list of conditions and the following disclaimer in the 13114902Sscottl * documentation and/or other materials provided with the distribution. 14114902Sscottl * 3. All advertising materials mentioning features or use of this software 15114902Sscottl * must display the acknowledgement as bellow: 16114902Sscottl * 17114902Sscottl * This product includes software developed by K. Kobayashi and H. Shimokawa 18114902Sscottl * 19114902Sscottl * 4. The name of the author may not be used to endorse or promote products 20114902Sscottl * derived from this software without specific prior written permission. 21114902Sscottl * 22114902Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23114902Sscottl * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24114902Sscottl * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25114902Sscottl * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 26114902Sscottl * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27114902Sscottl * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28119418Sobrien * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29119418Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30114902Sscottl * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31152919Sscottl * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32114902Sscottl * POSSIBILITY OF SUCH DAMAGE. 33114902Sscottl * 34114902Sscottl * $FreeBSD: head/sys/dev/firewire/fwdev.c 255359 2013-09-07 13:45:44Z davide $ 35114902Sscottl * 36114902Sscottl */ 37114902Sscottl 38114902Sscottl#include <sys/param.h> 39114902Sscottl#include <sys/systm.h> 40114902Sscottl#include <sys/types.h> 41114902Sscottl#include <sys/mbuf.h> 42114902Sscottl#if defined(__DragonFly__) || __FreeBSD_version < 500000 43114902Sscottl#include <sys/buf.h> 44114902Sscottl#else 45140923Sscottl#include <sys/bio.h> 46114902Sscottl#endif 47114902Sscottl 48114902Sscottl#include <sys/kernel.h> 49114902Sscottl#include <sys/malloc.h> 50114902Sscottl#include <sys/conf.h> 51114902Sscottl#include <sys/poll.h> 52114902Sscottl 53114902Sscottl#include <sys/bus.h> 54114902Sscottl#include <sys/ctype.h> 55114902Sscottl#include <machine/bus.h> 56114902Sscottl 57114902Sscottl#include <sys/ioccom.h> 58114902Sscottl 59114902Sscottl#ifdef __DragonFly__ 60114902Sscottl#include "firewire.h" 61114902Sscottl#include "firewirereg.h" 62114902Sscottl#include "fwdma.h" 63150535Sscottl#include "fwmem.h" 64114902Sscottl#include "iec68113.h" 65114902Sscottl#else 66150611Sglebius#include <dev/firewire/firewire.h> 67114902Sscottl#include <dev/firewire/firewirereg.h> 68114902Sscottl#include <dev/firewire/fwdma.h> 69114902Sscottl#include <dev/firewire/fwmem.h> 70114902Sscottl#include <dev/firewire/iec68113.h> 71114902Sscottl#endif 72114902Sscottl 73114902Sscottl#define FWNODE_INVAL 0xffff 74114902Sscottl 75114902Sscottlstatic d_open_t fw_open; 76114902Sscottlstatic d_close_t fw_close; 77114902Sscottlstatic d_ioctl_t fw_ioctl; 78114902Sscottlstatic d_poll_t fw_poll; 79114902Sscottlstatic d_read_t fw_read; /* for Isochronous packet */ 80114902Sscottlstatic d_write_t fw_write; 81114902Sscottlstatic d_mmap_t fw_mmap; 82114902Sscottlstatic d_strategy_t fw_strategy; 83114902Sscottl 84114902Sscottlstruct cdevsw firewire_cdevsw = { 85114902Sscottl#ifdef __DragonFly__ 86114902Sscottl#define CDEV_MAJOR 127 87114902Sscottl "fw", CDEV_MAJOR, D_MEM, NULL, 0, 88114902Sscottl fw_open, fw_close, fw_read, fw_write, fw_ioctl, 89114902Sscottl fw_poll, fw_mmap, fw_strategy, nodump, nopsize, 90114902Sscottl#elif __FreeBSD_version >= 500104 91114902Sscottl .d_version = D_VERSION, 92114902Sscottl .d_open = fw_open, 93114902Sscottl .d_close = fw_close, 94116931Speter .d_read = fw_read, 95114902Sscottl .d_write = fw_write, 96114902Sscottl .d_ioctl = fw_ioctl, 97114902Sscottl .d_poll = fw_poll, 98114902Sscottl .d_mmap = fw_mmap, 99114902Sscottl .d_strategy = fw_strategy, 100114902Sscottl .d_name = "fw", 101114902Sscottl .d_flags = D_MEM 102114902Sscottl#else 103114902Sscottl#define CDEV_MAJOR 127 104114902Sscottl fw_open, fw_close, fw_read, fw_write, fw_ioctl, 105114902Sscottl fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR, 106114902Sscottl nodump, nopsize, D_MEM, -1 107114902Sscottl#endif 108114902Sscottl}; 109114902Sscottl 110114902Sscottlstruct fw_drv1 { 111114902Sscottl struct firewire_comm *fc; 112114902Sscottl struct fw_xferq *ir; 113114902Sscottl struct fw_xferq *it; 114114902Sscottl struct fw_isobufreq bufreq; 115114902Sscottl STAILQ_HEAD(, fw_bind) binds; 116114902Sscottl STAILQ_HEAD(, fw_xfer) rq; 117114902Sscottl}; 118114902Sscottl 119114902Sscottlstatic int 120114902Sscottlfwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 121114902Sscottl struct fw_bufspec *b) 122114902Sscottl{ 123114902Sscottl int i; 124114902Sscottl 125114902Sscottl if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 126114902Sscottl return(EBUSY); 127114902Sscottl 128114902Sscottl q->bulkxfer = (struct fw_bulkxfer *) malloc( 129114902Sscottl sizeof(struct fw_bulkxfer) * b->nchunk, 130114902Sscottl M_FW, M_WAITOK); 131121211Sphk if (q->bulkxfer == NULL) 132114902Sscottl return(ENOMEM); 133121211Sphk 134114902Sscottl b->psize = roundup2(b->psize, sizeof(uint32_t)); 135114902Sscottl q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), 136114902Sscottl b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 137114902Sscottl 138114902Sscottl if (q->buf == NULL) { 139114902Sscottl free(q->bulkxfer, M_FW); 140140923Sscottl q->bulkxfer = NULL; 141114902Sscottl return(ENOMEM); 142114902Sscottl } 143140923Sscottl q->bnchunk = b->nchunk; 144114902Sscottl q->bnpacket = b->npacket; 145114902Sscottl q->psize = (b->psize + 3) & ~3; 146114902Sscottl q->queued = 0; 147114902Sscottl 148114902Sscottl STAILQ_INIT(&q->stvalid); 149114902Sscottl STAILQ_INIT(&q->stfree); 150114902Sscottl STAILQ_INIT(&q->stdma); 151126364Sscottl q->stproc = NULL; 152114902Sscottl 153126364Sscottl for(i = 0 ; i < q->bnchunk; i++){ 154140923Sscottl q->bulkxfer[i].poffset = i * q->bnpacket; 155126364Sscottl q->bulkxfer[i].mbuf = NULL; 156126364Sscottl STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 157140923Sscottl } 158114902Sscottl 159126364Sscottl q->flag &= ~FWXFERQ_MODEMASK; 160140923Sscottl q->flag |= FWXFERQ_STREAM; 161126364Sscottl q->flag |= FWXFERQ_EXTBUF; 162126364Sscottl 163126364Sscottl return (0); 164140923Sscottl} 165114902Sscottl 166114902Sscottlstatic int 167114902Sscottlfwdev_freebuf(struct fw_xferq *q) 168114902Sscottl{ 169114902Sscottl if (q->flag & FWXFERQ_EXTBUF) { 170114902Sscottl if (q->buf != NULL) 171114902Sscottl fwdma_free_multiseg(q->buf); 172114902Sscottl q->buf = NULL; 173114902Sscottl free(q->bulkxfer, M_FW); 174114902Sscottl q->bulkxfer = NULL; 175114902Sscottl q->flag &= ~FWXFERQ_EXTBUF; 176114902Sscottl q->psize = 0; 177114902Sscottl q->maxq = FWMAXQUEUE; 178114902Sscottl } 179150535Sscottl return (0); 180114902Sscottl} 181114902Sscottl 182114902Sscottl 183114902Sscottlstatic int 184114902Sscottlfw_open (struct cdev *dev, int flags, int fmt, fw_proc *td) 185114902Sscottl{ 186114902Sscottl int err = 0; 187114902Sscottl int unit = DEV2UNIT(dev); 188114902Sscottl struct fw_drv1 *d; 189114902Sscottl struct firewire_softc *sc; 190114902Sscottl 191114902Sscottl if (DEV_FWMEM(dev)) 192114902Sscottl return fwmem_open(dev, flags, fmt, td); 193150535Sscottl 194150535Sscottl sc = devclass_get_softc(firewire_devclass, unit); 195150535Sscottl if (sc == NULL) 196150535Sscottl return (ENXIO); 197150535Sscottl 198150535Sscottl FW_GLOCK(sc->fc); 199150535Sscottl if (dev->si_drv1 != NULL) { 200150535Sscottl FW_GUNLOCK(sc->fc); 201114902Sscottl return (EBUSY); 202114902Sscottl } 203114902Sscottl /* set dummy value for allocation */ 204114902Sscottl dev->si_drv1 = (void *)-1; 205114902Sscottl FW_GUNLOCK(sc->fc); 206114902Sscottl 207114902Sscottl dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 208114902Sscottl if (dev->si_drv1 == NULL) 209114902Sscottl return (ENOMEM); 210114902Sscottl 211114902Sscottl#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 212114902Sscottl if ((dev->si_flags & SI_NAMED) == 0) { 213114902Sscottl int unit = DEV2UNIT(dev); 214114902Sscottl int sub = DEV2SUB(dev); 215114902Sscottl 216114902Sscottl make_dev(&firewire_cdevsw, dev2unit(dev), 217114902Sscottl UID_ROOT, GID_OPERATOR, 0660, 218114902Sscottl "fw%d.%d", unit, sub); 219114902Sscottl } 220114902Sscottl#endif 221140923Sscottl d = (struct fw_drv1 *)dev->si_drv1; 222140923Sscottl d->fc = sc->fc; 223114902Sscottl STAILQ_INIT(&d->binds); 224114902Sscottl STAILQ_INIT(&d->rq); 225114902Sscottl 226114902Sscottl return err; 227114902Sscottl} 228114902Sscottl 229114902Sscottlstatic int 230114902Sscottlfw_close (struct cdev *dev, int flags, int fmt, fw_proc *td) 231114902Sscottl{ 232114902Sscottl struct firewire_comm *fc; 233114902Sscottl struct fw_drv1 *d; 234150535Sscottl struct fw_xfer *xfer; 235150535Sscottl struct fw_bind *fwb; 236150535Sscottl int err = 0; 237150535Sscottl 238114902Sscottl if (DEV_FWMEM(dev)) 239150535Sscottl return fwmem_close(dev, flags, fmt, td); 240150535Sscottl 241114902Sscottl d = (struct fw_drv1 *)dev->si_drv1; 242114902Sscottl fc = d->fc; 243114902Sscottl 244114902Sscottl /* remove binding */ 245114902Sscottl for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL; 246114902Sscottl fwb = STAILQ_FIRST(&d->binds)) { 247114902Sscottl fw_bindremove(fc, fwb); 248114902Sscottl STAILQ_REMOVE_HEAD(&d->binds, chlist); 249114902Sscottl fw_xferlist_remove(&fwb->xferlist); 250114902Sscottl free(fwb, M_FW); 251114902Sscottl } 252114902Sscottl if (d->ir != NULL) { 253140923Sscottl struct fw_xferq *ir = d->ir; 254114902Sscottl 255140923Sscottl if ((ir->flag & FWXFERQ_OPEN) == 0) 256140923Sscottl return (EINVAL); 257114902Sscottl if (ir->flag & FWXFERQ_RUNNING) { 258114902Sscottl ir->flag &= ~FWXFERQ_RUNNING; 259114902Sscottl fc->irx_disable(fc, ir->dmach); 260140923Sscottl } 261150535Sscottl /* free extbuf */ 262114902Sscottl fwdev_freebuf(ir); 263114902Sscottl /* drain receiving buffer */ 264114902Sscottl for (xfer = STAILQ_FIRST(&ir->q); 265114902Sscottl xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 266114902Sscottl ir->queued --; 267114902Sscottl STAILQ_REMOVE_HEAD(&ir->q, link); 268114902Sscottl 269114902Sscottl xfer->resp = 0; 270114902Sscottl fw_xfer_done(xfer); 271114902Sscottl } 272114902Sscottl ir->flag &= ~(FWXFERQ_OPEN | 273114902Sscottl FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 274114902Sscottl d->ir = NULL; 275114902Sscottl 276150535Sscottl } 277150535Sscottl if (d->it != NULL) { 278114902Sscottl struct fw_xferq *it = d->it; 279114902Sscottl 280150535Sscottl if ((it->flag & FWXFERQ_OPEN) == 0) 281114902Sscottl return (EINVAL); 282114902Sscottl if (it->flag & FWXFERQ_RUNNING) { 283114902Sscottl it->flag &= ~FWXFERQ_RUNNING; 284114902Sscottl fc->itx_disable(fc, it->dmach); 285114902Sscottl } 286114902Sscottl /* free extbuf */ 287114902Sscottl fwdev_freebuf(it); 288114902Sscottl it->flag &= ~(FWXFERQ_OPEN | 289114902Sscottl FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 290114902Sscottl d->it = NULL; 291114902Sscottl } 292114902Sscottl free(dev->si_drv1, M_FW); 293114902Sscottl dev->si_drv1 = NULL; 294150535Sscottl 295150535Sscottl return err; 296150535Sscottl} 297150535Sscottl 298150535Sscottlstatic int 299150535Sscottlfw_read_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 300150535Sscottl{ 301150535Sscottl int err = 0, s; 302150535Sscottl struct fw_xfer *xfer; 303150535Sscottl struct fw_bind *fwb; 304150535Sscottl struct fw_pkt *fp; 305114902Sscottl struct tcode_info *tinfo; 306114902Sscottl 307114902Sscottl FW_GLOCK(d->fc); 308114902Sscottl while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == 0) 309114902Sscottl err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0); 310114902Sscottl 311114902Sscottl if (err != 0) { 312114902Sscottl FW_GUNLOCK(d->fc); 313114902Sscottl return (err); 314114902Sscottl } 315114902Sscottl 316114902Sscottl s = splfw(); 317114902Sscottl STAILQ_REMOVE_HEAD(&d->rq, link); 318114902Sscottl FW_GUNLOCK(xfer->fc); 319114902Sscottl splx(s); 320114902Sscottl fp = &xfer->recv.hdr; 321114902Sscottl#if 0 /* for GASP ?? */ 322114902Sscottl if (fc->irx_post != NULL) 323140923Sscottl fc->irx_post(fc, fp->mode.ld); 324140923Sscottl#endif 325114902Sscottl tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode]; 326114902Sscottl err = uiomove((void *)fp, tinfo->hdr_len, uio); 327114902Sscottl if (err) 328114902Sscottl goto out; 329114902Sscottl err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio); 330114902Sscottl 331114902Sscottlout: 332114902Sscottl /* recycle this xfer */ 333114902Sscottl fwb = (struct fw_bind *)xfer->sc; 334114902Sscottl fw_xfer_unload(xfer); 335114902Sscottl xfer->recv.pay_len = PAGE_SIZE; 336150535Sscottl FW_GLOCK(xfer->fc); 337150535Sscottl STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 338150535Sscottl FW_GUNLOCK(xfer->fc); 339150535Sscottl return (err); 340150535Sscottl} 341150535Sscottl 342114902Sscottl/* 343114902Sscottl * read request. 344114902Sscottl */ 345114902Sscottlstatic int 346114902Sscottlfw_read (struct cdev *dev, struct uio *uio, int ioflag) 347114902Sscottl{ 348114902Sscottl struct fw_drv1 *d; 349114902Sscottl struct fw_xferq *ir; 350114902Sscottl struct firewire_comm *fc; 351114902Sscottl int err = 0, s, slept = 0; 352114902Sscottl struct fw_pkt *fp; 353114902Sscottl 354114902Sscottl if (DEV_FWMEM(dev)) 355140923Sscottl return (physio(dev, uio, ioflag)); 356140923Sscottl 357140923Sscottl d = (struct fw_drv1 *)dev->si_drv1; 358114902Sscottl fc = d->fc; 359114902Sscottl ir = d->ir; 360114902Sscottl 361140923Sscottl if (ir == NULL) 362150535Sscottl return (fw_read_async(d, uio, ioflag)); 363114902Sscottl 364114902Sscottl if (ir->buf == NULL) 365114902Sscottl return (EIO); 366114902Sscottl 367114902Sscottl FW_GLOCK(fc); 368114902Sscottlreadloop: 369114902Sscottl if (ir->stproc == NULL) { 370114902Sscottl /* iso bulkxfer */ 371114902Sscottl ir->stproc = STAILQ_FIRST(&ir->stvalid); 372114902Sscottl if (ir->stproc != NULL) { 373114902Sscottl s = splfw(); 374114902Sscottl STAILQ_REMOVE_HEAD(&ir->stvalid, link); 375114902Sscottl splx(s); 376114902Sscottl ir->queued = 0; 377114902Sscottl } 378114902Sscottl } 379114902Sscottl if (ir->stproc == NULL) { 380114902Sscottl /* no data avaliable */ 381114902Sscottl if (slept == 0) { 382114902Sscottl slept = 1; 383150535Sscottl ir->flag |= FWXFERQ_WAKEUP; 384140923Sscottl err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz); 385114902Sscottl ir->flag &= ~FWXFERQ_WAKEUP; 386114902Sscottl if (err == 0) 387114902Sscottl goto readloop; 388114902Sscottl } else if (slept == 1) 389114902Sscottl err = EIO; 390114902Sscottl FW_GUNLOCK(fc); 391140923Sscottl return err; 392140923Sscottl } else if(ir->stproc != NULL) { 393114902Sscottl /* iso bulkxfer */ 394140923Sscottl FW_GUNLOCK(fc); 395114902Sscottl fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 396114902Sscottl ir->stproc->poffset + ir->queued); 397140923Sscottl if(fc->irx_post != NULL) 398150535Sscottl fc->irx_post(fc, fp->mode.ld); 399114902Sscottl if(fp->mode.stream.len == 0){ 400114902Sscottl err = EIO; 401114902Sscottl return err; 402114902Sscottl } 403114902Sscottl err = uiomove((caddr_t)fp, 404122999Smbr fp->mode.stream.len + sizeof(uint32_t), uio); 405122999Smbr ir->queued ++; 406122999Smbr if(ir->queued >= ir->bnpacket){ 407122999Smbr s = splfw(); 408122999Smbr STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 409122999Smbr splx(s); 410122999Smbr fc->irx_enable(fc, ir->dmach); 411122999Smbr ir->stproc = NULL; 412122999Smbr } 413122999Smbr if (uio->uio_resid >= ir->psize) { 414122999Smbr slept = -1; 415122999Smbr FW_GLOCK(fc); 416122999Smbr goto readloop; 417122999Smbr } 418122999Smbr } 419122999Smbr return err; 420122999Smbr} 421122999Smbr 422122999Smbrstatic int 423122999Smbrfw_write_async(struct fw_drv1 *d, struct uio *uio, int ioflag) 424122999Smbr{ 425122999Smbr struct fw_xfer *xfer; 426122999Smbr struct fw_pkt pkt; 427122999Smbr struct tcode_info *tinfo; 428122999Smbr int err; 429122999Smbr 430122999Smbr bzero(&pkt, sizeof(struct fw_pkt)); 431122999Smbr if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio))) 432122999Smbr return (err); 433122999Smbr tinfo = &d->fc->tcode[pkt.mode.hdr.tcode]; 434122999Smbr if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t), 435122999Smbr tinfo->hdr_len - sizeof(uint32_t), uio))) 436122999Smbr return (err); 437122999Smbr 438122999Smbr if ((xfer = fw_xfer_alloc_buf(M_FWXFER, uio->uio_resid, 439122999Smbr PAGE_SIZE/*XXX*/)) == NULL) 440122999Smbr return (ENOMEM); 441122999Smbr 442122999Smbr bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt)); 443122999Smbr xfer->send.pay_len = uio->uio_resid; 444122999Smbr if (uio->uio_resid > 0) { 445122999Smbr if ((err = uiomove((caddr_t)&xfer->send.payload[0], 446122999Smbr uio->uio_resid, uio))) 447122999Smbr goto out; 448122999Smbr } 449122999Smbr 450122999Smbr xfer->fc = d->fc; 451122999Smbr xfer->sc = NULL; 452122999Smbr xfer->hand = fw_xferwake; 453122999Smbr xfer->send.spd = 2 /* XXX */; 454122999Smbr 455122999Smbr if ((err = fw_asyreq(xfer->fc, -1, xfer))) 456122999Smbr goto out; 457122999Smbr 458124040Smbr if ((err = fw_xferwait(xfer))) 459122999Smbr goto out; 460122999Smbr 461122999Smbr if (xfer->resp != 0) { 462122999Smbr err = xfer->resp; 463122999Smbr goto out; 464150535Sscottl } 465140923Sscottl 466122999Smbr if (xfer->flag & FWXF_RCVD) { 467122999Smbr FW_GLOCK(xfer->fc); 468122999Smbr STAILQ_INSERT_TAIL(&d->rq, xfer, link); 469122999Smbr FW_GUNLOCK(xfer->fc); 470122999Smbr return (0); 471122999Smbr } 472140923Sscottl 473140923Sscottlout: 474140923Sscottl fw_xfer_free(xfer); 475122999Smbr return (err); 476122999Smbr} 477140923Sscottl 478150535Sscottlstatic int 479122999Smbrfw_write (struct cdev *dev, struct uio *uio, int ioflag) 480122999Smbr{ 481122999Smbr int err = 0; 482122999Smbr int s, slept = 0; 483122999Smbr struct fw_drv1 *d; 484114902Sscottl struct fw_pkt *fp; 485114902Sscottl struct firewire_comm *fc; 486114902Sscottl struct fw_xferq *it; 487114902Sscottl 488114902Sscottl if (DEV_FWMEM(dev)) 489114902Sscottl return (physio(dev, uio, ioflag)); 490114902Sscottl 491114902Sscottl d = (struct fw_drv1 *)dev->si_drv1; 492114902Sscottl fc = d->fc; 493114902Sscottl it = d->it; 494114902Sscottl 495114902Sscottl if (it == NULL) 496114902Sscottl return (fw_write_async(d, uio, ioflag)); 497114902Sscottl 498114902Sscottl if (it->buf == NULL) 499122999Smbr return (EIO); 500122999Smbr 501122999Smbr FW_GLOCK(fc); 502114902Sscottlisoloop: 503114902Sscottl if (it->stproc == NULL) { 504114902Sscottl it->stproc = STAILQ_FIRST(&it->stfree); 505114902Sscottl if (it->stproc != NULL) { 506114902Sscottl s = splfw(); 507114902Sscottl STAILQ_REMOVE_HEAD(&it->stfree, link); 508114902Sscottl splx(s); 509114902Sscottl it->queued = 0; 510114902Sscottl } else if (slept == 0) { 511114902Sscottl slept = 1; 512114902Sscottl#if 0 /* XXX to avoid lock recursion */ 513114902Sscottl err = fc->itx_enable(fc, it->dmach); 514114902Sscottl if (err) 515114902Sscottl goto out; 516114902Sscottl#endif 517150535Sscottl err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz); 518114902Sscottl if (err) 519114902Sscottl goto out; 520114902Sscottl goto isoloop; 521114902Sscottl } else { 522114902Sscottl err = EIO; 523114902Sscottl goto out; 524114902Sscottl } 525114902Sscottl } 526114902Sscottl FW_GUNLOCK(fc); 527114902Sscottl fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 528114902Sscottl it->stproc->poffset + it->queued); 529114902Sscottl err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 530114902Sscottl err = uiomove((caddr_t)fp->mode.stream.payload, 531114902Sscottl fp->mode.stream.len, uio); 532114902Sscottl it->queued ++; 533150535Sscottl if (it->queued >= it->bnpacket) { 534150535Sscottl s = splfw(); 535150535Sscottl STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 536150535Sscottl splx(s); 537150535Sscottl it->stproc = NULL; 538150535Sscottl err = fc->itx_enable(fc, it->dmach); 539114902Sscottl } 540114902Sscottl if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 541140923Sscottl slept = 0; 542140923Sscottl FW_GLOCK(fc); 543114902Sscottl goto isoloop; 544114902Sscottl } 545114902Sscottl return err; 546114902Sscottl 547114902Sscottlout: 548114902Sscottl FW_GUNLOCK(fc); 549114902Sscottl return err; 550114902Sscottl} 551114902Sscottl 552114902Sscottlstatic void 553114902Sscottlfw_hand(struct fw_xfer *xfer) 554114902Sscottl{ 555114902Sscottl struct fw_bind *fwb; 556114902Sscottl struct fw_drv1 *d; 557140923Sscottl 558140923Sscottl fwb = (struct fw_bind *)xfer->sc; 559114902Sscottl d = (struct fw_drv1 *)fwb->sc; 560114902Sscottl FW_GLOCK(xfer->fc); 561114902Sscottl STAILQ_INSERT_TAIL(&d->rq, xfer, link); 562114902Sscottl FW_GUNLOCK(xfer->fc); 563114902Sscottl wakeup(&d->rq); 564114902Sscottl} 565114902Sscottl 566114902Sscottl/* 567114902Sscottl * ioctl support. 568114902Sscottl */ 569114902Sscottlint 570150535Sscottlfw_ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 571150535Sscottl{ 572150535Sscottl struct firewire_comm *fc; 573150535Sscottl struct fw_drv1 *d; 574150535Sscottl int i, len, err = 0; 575150535Sscottl struct fw_device *fwdev; 576114902Sscottl struct fw_bind *fwb; 577114902Sscottl struct fw_xferq *ir, *it; 578114902Sscottl struct fw_xfer *xfer; 579114902Sscottl struct fw_pkt *fp; 580114902Sscottl struct fw_devinfo *devinfo; 581114902Sscottl void *ptr; 582114902Sscottl 583114902Sscottl struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 584114902Sscottl struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 585114902Sscottl struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 586114902Sscottl struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 587140923Sscottl struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 588140923Sscottl struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 589140923Sscottl 590114902Sscottl if (DEV_FWMEM(dev)) 591114902Sscottl return fwmem_ioctl(dev, cmd, data, flag, td); 592114902Sscottl 593140923Sscottl if (!data) 594150535Sscottl return(EINVAL); 595114902Sscottl 596114902Sscottl d = (struct fw_drv1 *)dev->si_drv1; 597114902Sscottl fc = d->fc; 598114902Sscottl ir = d->ir; 599114902Sscottl it = d->it; 600114902Sscottl 601114902Sscottl switch (cmd) { 602114902Sscottl case FW_STSTREAM: 603114902Sscottl if (it == NULL) { 604114902Sscottl i = fw_open_isodma(fc, /* tx */1); 605114902Sscottl if (i < 0) { 606114902Sscottl err = EBUSY; 607114902Sscottl break; 608114902Sscottl } 609114902Sscottl it = fc->it[i]; 610114902Sscottl err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 611114902Sscottl if (err) { 612114902Sscottl it->flag &= ~FWXFERQ_OPEN; 613114902Sscottl break; 614114902Sscottl } 615114902Sscottl } 616114902Sscottl it->flag &= ~0xff; 617150535Sscottl it->flag |= (0x3f & ichreq->ch); 618140923Sscottl it->flag |= ((0x3 & ichreq->tag) << 6); 619114902Sscottl d->it = it; 620114902Sscottl break; 621114902Sscottl case FW_GTSTREAM: 622114902Sscottl if (it != NULL) { 623114902Sscottl ichreq->ch = it->flag & 0x3f; 624114902Sscottl ichreq->tag = it->flag >> 2 & 0x3; 625114902Sscottl } else 626114902Sscottl err = EINVAL; 627114902Sscottl break; 628114902Sscottl case FW_SRSTREAM: 629114902Sscottl if (ir == NULL) { 630114902Sscottl i = fw_open_isodma(fc, /* tx */0); 631114902Sscottl if (i < 0) { 632114902Sscottl err = EBUSY; 633114902Sscottl break; 634114902Sscottl } 635114902Sscottl ir = fc->ir[i]; 636114902Sscottl err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 637150535Sscottl if (err) { 638140923Sscottl ir->flag &= ~FWXFERQ_OPEN; 639114902Sscottl break; 640114902Sscottl } 641114902Sscottl } 642114902Sscottl ir->flag &= ~0xff; 643114902Sscottl ir->flag |= (0x3f & ichreq->ch); 644114902Sscottl ir->flag |= ((0x3 & ichreq->tag) << 6); 645114902Sscottl d->ir = ir; 646140923Sscottl err = fc->irx_enable(fc, ir->dmach); 647140923Sscottl break; 648114902Sscottl case FW_GRSTREAM: 649140923Sscottl if (d->ir != NULL) { 650114902Sscottl ichreq->ch = ir->flag & 0x3f; 651114902Sscottl ichreq->tag = ir->flag >> 2 & 0x3; 652114902Sscottl } else 653140923Sscottl err = EINVAL; 654150535Sscottl break; 655114902Sscottl case FW_SSTBUF: 656114902Sscottl bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 657114902Sscottl break; 658114902Sscottl case FW_GSTBUF: 659114902Sscottl bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 660140923Sscottl if (ir != NULL) { 661114902Sscottl ibufreq->rx.nchunk = ir->bnchunk; 662114902Sscottl ibufreq->rx.npacket = ir->bnpacket; 663114902Sscottl ibufreq->rx.psize = ir->psize; 664140923Sscottl } 665150535Sscottl bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 666114902Sscottl if (it != NULL) { 667114902Sscottl ibufreq->tx.nchunk = it->bnchunk; 668114902Sscottl ibufreq->tx.npacket = it->bnpacket; 669114902Sscottl ibufreq->tx.psize = it->psize; 670114902Sscottl } 671114902Sscottl break; 672 case FW_ASYREQ: 673 { 674 struct tcode_info *tinfo; 675 int pay_len = 0; 676 677 fp = &asyreq->pkt; 678 tinfo = &fc->tcode[fp->mode.hdr.tcode]; 679 680 if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) 681 pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); 682 683 xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); 684 if (xfer == NULL) 685 return (ENOMEM); 686 687 switch (asyreq->req.type) { 688 case FWASREQNODE: 689 break; 690 case FWASREQEUI: 691 fwdev = fw_noderesolve_eui64(fc, 692 &asyreq->req.dst.eui); 693 if (fwdev == NULL) { 694 device_printf(fc->bdev, 695 "cannot find node\n"); 696 err = EINVAL; 697 goto out; 698 } 699 fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; 700 break; 701 case FWASRESTL: 702 /* XXX what's this? */ 703 break; 704 case FWASREQSTREAM: 705 /* nothing to do */ 706 break; 707 } 708 709 bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); 710 if (pay_len > 0) 711 bcopy((char *)fp + tinfo->hdr_len, 712 (void *)xfer->send.payload, pay_len); 713 xfer->send.spd = asyreq->req.sped; 714 xfer->hand = fw_xferwake; 715 716 if ((err = fw_asyreq(fc, -1, xfer)) != 0) 717 goto out; 718 if ((err = fw_xferwait(xfer)) != 0) 719 goto out; 720 if (xfer->resp != 0) { 721 err = EIO; 722 goto out; 723 } 724 if ((tinfo->flag & FWTI_TLABEL) == 0) 725 goto out; 726 727 /* copy response */ 728 tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; 729 if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB || 730 xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) { 731 pay_len = xfer->recv.pay_len; 732 if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) { 733 asyreq->req.len = xfer->recv.pay_len + 734 tinfo->hdr_len; 735 } else { 736 err = EINVAL; 737 pay_len = 0; 738 } 739 } else { 740 pay_len = 0; 741 } 742 bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); 743 bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len); 744out: 745 fw_xfer_free_buf(xfer); 746 break; 747 } 748 case FW_IBUSRST: 749 fc->ibr(fc); 750 break; 751 case FW_CBINDADDR: 752 fwb = fw_bindlookup(fc, 753 bindreq->start.hi, bindreq->start.lo); 754 if(fwb == NULL){ 755 err = EINVAL; 756 break; 757 } 758 fw_bindremove(fc, fwb); 759 STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist); 760 fw_xferlist_remove(&fwb->xferlist); 761 free(fwb, M_FW); 762 break; 763 case FW_SBINDADDR: 764 if(bindreq->len <= 0 ){ 765 err = EINVAL; 766 break; 767 } 768 if(bindreq->start.hi > 0xffff ){ 769 err = EINVAL; 770 break; 771 } 772 fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_WAITOK); 773 if(fwb == NULL){ 774 err = ENOMEM; 775 break; 776 } 777 fwb->start = ((u_int64_t)bindreq->start.hi << 32) | 778 bindreq->start.lo; 779 fwb->end = fwb->start + bindreq->len; 780 fwb->sc = (void *)d; 781 STAILQ_INIT(&fwb->xferlist); 782 err = fw_bindadd(fc, fwb); 783 if (err == 0) { 784 fw_xferlist_add(&fwb->xferlist, M_FWXFER, 785 /* XXX */ 786 PAGE_SIZE, PAGE_SIZE, 5, 787 fc, (void *)fwb, fw_hand); 788 STAILQ_INSERT_TAIL(&d->binds, fwb, chlist); 789 } 790 break; 791 case FW_GDEVLST: 792 i = len = 1; 793 /* myself */ 794 devinfo = &fwdevlst->dev[0]; 795 devinfo->dst = fc->nodeid; 796 devinfo->status = 0; /* XXX */ 797 devinfo->eui.hi = fc->eui.hi; 798 devinfo->eui.lo = fc->eui.lo; 799 STAILQ_FOREACH(fwdev, &fc->devices, link) { 800 if(len < FW_MAX_DEVLST){ 801 devinfo = &fwdevlst->dev[len++]; 802 devinfo->dst = fwdev->dst; 803 devinfo->status = 804 (fwdev->status == FWDEVINVAL)?0:1; 805 devinfo->eui.hi = fwdev->eui.hi; 806 devinfo->eui.lo = fwdev->eui.lo; 807 } 808 i++; 809 } 810 fwdevlst->n = i; 811 fwdevlst->info_len = len; 812 break; 813 case FW_GTPMAP: 814 bcopy(fc->topology_map, data, 815 (fc->topology_map->crc_len + 1) * 4); 816 break; 817 case FW_GCROM: 818 STAILQ_FOREACH(fwdev, &fc->devices, link) 819 if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 820 break; 821 if (fwdev == NULL) { 822 if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) { 823 err = FWNODE_INVAL; 824 break; 825 } 826 /* myself */ 827 ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 828 len = CROMSIZE; 829 for (i = 0; i < CROMSIZE/4; i++) 830 ((uint32_t *)ptr)[i] 831 = ntohl(fc->config_rom[i]); 832 } else { 833 /* found */ 834 ptr = (void *)&fwdev->csrrom[0]; 835 if (fwdev->rommax < CSRROMOFF) 836 len = 0; 837 else 838 len = fwdev->rommax - CSRROMOFF + 4; 839 } 840 if (crom_buf->len < len) 841 len = crom_buf->len; 842 else 843 crom_buf->len = len; 844 err = copyout(ptr, crom_buf->ptr, len); 845 if (fwdev == NULL) 846 /* myself */ 847 free(ptr, M_FW); 848 break; 849 default: 850 fc->ioctl (dev, cmd, data, flag, td); 851 break; 852 } 853 return err; 854} 855int 856fw_poll(struct cdev *dev, int events, fw_proc *td) 857{ 858 struct fw_xferq *ir; 859 int revents; 860 int tmp; 861 862 if (DEV_FWMEM(dev)) 863 return fwmem_poll(dev, events, td); 864 865 ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 866 revents = 0; 867 tmp = POLLIN | POLLRDNORM; 868 if (events & tmp) { 869 if (STAILQ_FIRST(&ir->q) != NULL) 870 revents |= tmp; 871 else 872 selrecord(td, &ir->rsel); 873 } 874 tmp = POLLOUT | POLLWRNORM; 875 if (events & tmp) { 876 /* XXX should be fixed */ 877 revents |= tmp; 878 } 879 880 return revents; 881} 882 883static int 884#if defined(__DragonFly__) || __FreeBSD_version < 500102 885fw_mmap (struct cdev *dev, vm_offset_t offset, int nproto) 886#else 887fw_mmap (struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 888 int nproto, vm_memattr_t *memattr) 889#endif 890{ 891 892 if (DEV_FWMEM(dev)) 893#if defined(__DragonFly__) || __FreeBSD_version < 500102 894 return fwmem_mmap(dev, offset, nproto); 895#else 896 return fwmem_mmap(dev, offset, paddr, nproto, memattr); 897#endif 898 899 return EINVAL; 900} 901 902static void 903fw_strategy(struct bio *bp) 904{ 905 struct cdev *dev; 906 907 dev = bp->bio_dev; 908 if (DEV_FWMEM(dev)) { 909 fwmem_strategy(bp); 910 return; 911 } 912 913 bp->bio_error = EOPNOTSUPP; 914 bp->bio_flags |= BIO_ERROR; 915 bp->bio_resid = bp->bio_bcount; 916 biodone(bp); 917} 918 919int 920fwdev_makedev(struct firewire_softc *sc) 921{ 922 int err = 0; 923 924#if defined(__DragonFly__) || __FreeBSD_version < 500000 925 cdevsw_add(&firewire_cdevsw); 926#else 927 struct cdev *d; 928 int unit; 929 930 unit = device_get_unit(sc->fc->bdev); 931 sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 932 UID_ROOT, GID_OPERATOR, 0660, 933 "fw%d.%d", unit, 0); 934 d = make_dev(&firewire_cdevsw, 935 MAKEMINOR(FWMEM_FLAG, unit, 0), 936 UID_ROOT, GID_OPERATOR, 0660, 937 "fwmem%d.%d", unit, 0); 938 dev_depends(sc->dev, d); 939 make_dev_alias(sc->dev, "fw%d", unit); 940 make_dev_alias(d, "fwmem%d", unit); 941#endif 942 943 return (err); 944} 945 946int 947fwdev_destroydev(struct firewire_softc *sc) 948{ 949 int err = 0; 950 951#if defined(__DragonFly__) || __FreeBSD_version < 500000 952 cdevsw_remove(&firewire_cdevsw); 953#else 954 destroy_dev(sc->dev); 955#endif 956 return (err); 957} 958 959#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 960#define NDEVTYPE 2 961void 962fwdev_clone(void *arg, struct ucred *cred, char *name, int namelen, 963 struct cdev **dev) 964{ 965 struct firewire_softc *sc; 966 char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 967 char *subp = NULL; 968 int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 969 int i, unit = 0, sub = 0; 970 971 if (*dev != NULL) 972 return; 973 974 for (i = 0; i < NDEVTYPE; i++) 975 if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 976 goto found; 977 /* not match */ 978 return; 979found: 980 981 if (subp == NULL || *subp++ != '.') 982 return; 983 984 /* /dev/fwU.S */ 985 while (isdigit(*subp)) { 986 sub *= 10; 987 sub += *subp++ - '0'; 988 } 989 if (*subp != '\0') 990 return; 991 992 sc = devclass_get_softc(firewire_devclass, unit); 993 if (sc == NULL) 994 return; 995 *dev = make_dev_credf(MAKEDEV_REF, &firewire_cdevsw, 996 MAKEMINOR(devflag[i], unit, sub), cred, UID_ROOT, GID_OPERATOR, 997 0660, "%s%d.%d", devnames[i], unit, sub); 998 dev_depends(sc->dev, *dev); 999 return; 1000} 1001#endif 1002