fwdev.c revision 122227
1106813Ssimokawa/* 2113584Ssimokawa * Copyright (c) 2003 Hidetoshi Shimokawa 3106813Ssimokawa * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa 4106813Ssimokawa * All rights reserved. 5106813Ssimokawa * 6106813Ssimokawa * Redistribution and use in source and binary forms, with or without 7106813Ssimokawa * modification, are permitted provided that the following conditions 8106813Ssimokawa * are met: 9106813Ssimokawa * 1. Redistributions of source code must retain the above copyright 10106813Ssimokawa * notice, this list of conditions and the following disclaimer. 11106813Ssimokawa * 2. Redistributions in binary form must reproduce the above copyright 12106813Ssimokawa * notice, this list of conditions and the following disclaimer in the 13106813Ssimokawa * documentation and/or other materials provided with the distribution. 14106813Ssimokawa * 3. All advertising materials mentioning features or use of this software 15106813Ssimokawa * must display the acknowledgement as bellow: 16106813Ssimokawa * 17106813Ssimokawa * This product includes software developed by K. Kobayashi and H. Shimokawa 18106813Ssimokawa * 19106813Ssimokawa * 4. The name of the author may not be used to endorse or promote products 20106813Ssimokawa * derived from this software without specific prior written permission. 21106813Ssimokawa * 22106813Ssimokawa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23106813Ssimokawa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 24106813Ssimokawa * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 25106813Ssimokawa * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 26106813Ssimokawa * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27106813Ssimokawa * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28106813Ssimokawa * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29106813Ssimokawa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30106813Ssimokawa * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31106813Ssimokawa * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32106813Ssimokawa * POSSIBILITY OF SUCH DAMAGE. 33106813Ssimokawa * 34106813Ssimokawa * $FreeBSD: head/sys/dev/firewire/fwdev.c 122227 2003-11-07 12:30:57Z simokawa $ 35106813Ssimokawa * 36106813Ssimokawa */ 37106813Ssimokawa 38106813Ssimokawa#include <sys/param.h> 39106813Ssimokawa#include <sys/systm.h> 40106813Ssimokawa#include <sys/types.h> 41106813Ssimokawa#include <sys/mbuf.h> 42120660Ssimokawa#if __FreeBSD_version < 500000 43120660Ssimokawa#include <sys/buf.h> 44120660Ssimokawa#else 45120660Ssimokawa#include <sys/bio.h> 46120660Ssimokawa#endif 47106813Ssimokawa 48106813Ssimokawa#include <sys/kernel.h> 49106813Ssimokawa#include <sys/malloc.h> 50106813Ssimokawa#include <sys/conf.h> 51106813Ssimokawa#include <sys/poll.h> 52106813Ssimokawa 53106813Ssimokawa#include <sys/bus.h> 54118455Ssimokawa#include <sys/ctype.h> 55113584Ssimokawa#include <machine/bus.h> 56106813Ssimokawa 57106813Ssimokawa#include <sys/ioccom.h> 58106813Ssimokawa 59106813Ssimokawa#include <dev/firewire/firewire.h> 60106813Ssimokawa#include <dev/firewire/firewirereg.h> 61113584Ssimokawa#include <dev/firewire/fwdma.h> 62106813Ssimokawa#include <dev/firewire/fwmem.h> 63109282Ssimokawa#include <dev/firewire/iec68113.h> 64106813Ssimokawa 65106813Ssimokawa#define CDEV_MAJOR 127 66106813Ssimokawa#define FWNODE_INVAL 0xffff 67106813Ssimokawa 68106813Ssimokawastatic d_open_t fw_open; 69106813Ssimokawastatic d_close_t fw_close; 70106813Ssimokawastatic d_ioctl_t fw_ioctl; 71106813Ssimokawastatic d_poll_t fw_poll; 72106813Ssimokawastatic d_read_t fw_read; /* for Isochronous packet */ 73106813Ssimokawastatic d_write_t fw_write; 74106813Ssimokawastatic d_mmap_t fw_mmap; 75120660Ssimokawastatic d_strategy_t fw_strategy; 76106813Ssimokawa 77106813Ssimokawastruct cdevsw firewire_cdevsw = 78106813Ssimokawa{ 79111942Ssimokawa#if __FreeBSD_version >= 500104 80111815Sphk .d_open = fw_open, 81111815Sphk .d_close = fw_close, 82111815Sphk .d_read = fw_read, 83111815Sphk .d_write = fw_write, 84111815Sphk .d_ioctl = fw_ioctl, 85111815Sphk .d_poll = fw_poll, 86111815Sphk .d_mmap = fw_mmap, 87120660Ssimokawa .d_strategy = fw_strategy, 88111815Sphk .d_name = "fw", 89111815Sphk .d_maj = CDEV_MAJOR, 90111815Sphk .d_flags = D_MEM 91111942Ssimokawa#else 92111942Ssimokawa fw_open, fw_close, fw_read, fw_write, fw_ioctl, 93120660Ssimokawa fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR, 94118455Ssimokawa nodump, nopsize, D_MEM, -1 95111942Ssimokawa#endif 96106813Ssimokawa}; 97106813Ssimokawa 98118293Ssimokawastruct fw_drv1 { 99118293Ssimokawa struct fw_xferq *ir; 100118293Ssimokawa struct fw_xferq *it; 101118293Ssimokawa struct fw_isobufreq bufreq; 102118293Ssimokawa}; 103118293Ssimokawa 104106813Ssimokawastatic int 105118293Ssimokawafwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 106118293Ssimokawa struct fw_bufspec *b) 107118293Ssimokawa{ 108118293Ssimokawa int i; 109118293Ssimokawa 110118293Ssimokawa if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 111118293Ssimokawa return(EBUSY); 112118293Ssimokawa 113118293Ssimokawa q->bulkxfer = (struct fw_bulkxfer *) malloc( 114118293Ssimokawa sizeof(struct fw_bulkxfer) * b->nchunk, 115118293Ssimokawa M_FW, M_WAITOK); 116118293Ssimokawa if (q->bulkxfer == NULL) 117118293Ssimokawa return(ENOMEM); 118118293Ssimokawa 119118293Ssimokawa b->psize = roundup2(b->psize, sizeof(u_int32_t)); 120118293Ssimokawa q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t), 121118293Ssimokawa b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 122118293Ssimokawa 123118293Ssimokawa if (q->buf == NULL) { 124118293Ssimokawa free(q->bulkxfer, M_FW); 125118293Ssimokawa q->bulkxfer = NULL; 126118293Ssimokawa return(ENOMEM); 127118293Ssimokawa } 128118293Ssimokawa q->bnchunk = b->nchunk; 129118293Ssimokawa q->bnpacket = b->npacket; 130118293Ssimokawa q->psize = (b->psize + 3) & ~3; 131118293Ssimokawa q->queued = 0; 132118293Ssimokawa 133118293Ssimokawa STAILQ_INIT(&q->stvalid); 134118293Ssimokawa STAILQ_INIT(&q->stfree); 135118293Ssimokawa STAILQ_INIT(&q->stdma); 136118293Ssimokawa q->stproc = NULL; 137118293Ssimokawa 138118293Ssimokawa for(i = 0 ; i < q->bnchunk; i++){ 139118293Ssimokawa q->bulkxfer[i].poffset = i * q->bnpacket; 140118293Ssimokawa q->bulkxfer[i].mbuf = NULL; 141118293Ssimokawa STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 142118293Ssimokawa } 143118293Ssimokawa 144118293Ssimokawa q->flag &= ~FWXFERQ_MODEMASK; 145118293Ssimokawa q->flag |= FWXFERQ_STREAM; 146118293Ssimokawa q->flag |= FWXFERQ_EXTBUF; 147118293Ssimokawa 148118293Ssimokawa return (0); 149118293Ssimokawa} 150118293Ssimokawa 151118293Ssimokawastatic int 152118293Ssimokawafwdev_freebuf(struct fw_xferq *q) 153118293Ssimokawa{ 154118293Ssimokawa if (q->flag & FWXFERQ_EXTBUF) { 155118293Ssimokawa if (q->buf != NULL) 156118293Ssimokawa fwdma_free_multiseg(q->buf); 157118293Ssimokawa q->buf = NULL; 158118293Ssimokawa free(q->bulkxfer, M_FW); 159118293Ssimokawa q->bulkxfer = NULL; 160118293Ssimokawa q->flag &= ~FWXFERQ_EXTBUF; 161118293Ssimokawa q->psize = 0; 162118293Ssimokawa q->maxq = FWMAXQUEUE; 163118293Ssimokawa } 164118293Ssimokawa return (0); 165118293Ssimokawa} 166118293Ssimokawa 167118293Ssimokawa 168118293Ssimokawastatic int 169106813Ssimokawafw_open (dev_t dev, int flags, int fmt, fw_proc *td) 170106813Ssimokawa{ 171106813Ssimokawa int err = 0; 172106813Ssimokawa 173122227Ssimokawa if (DEV_FWMEM(dev)) 174122227Ssimokawa return fwmem_open(dev, flags, fmt, td); 175122227Ssimokawa 176118293Ssimokawa if (dev->si_drv1 != NULL) 177118293Ssimokawa return (EBUSY); 178118293Ssimokawa 179118293Ssimokawa#if __FreeBSD_version >= 500000 180118455Ssimokawa if ((dev->si_flags & SI_NAMED) == 0) { 181118455Ssimokawa int unit = DEV2UNIT(dev); 182118455Ssimokawa int sub = DEV2SUB(dev); 183118455Ssimokawa 184118293Ssimokawa make_dev(&firewire_cdevsw, minor(dev), 185118293Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 186118293Ssimokawa "fw%d.%d", unit, sub); 187118455Ssimokawa } 188118455Ssimokawa#endif 189118293Ssimokawa 190118293Ssimokawa dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 191118293Ssimokawa 192106813Ssimokawa return err; 193106813Ssimokawa} 194106813Ssimokawa 195106813Ssimokawastatic int 196106813Ssimokawafw_close (dev_t dev, int flags, int fmt, fw_proc *td) 197106813Ssimokawa{ 198106813Ssimokawa struct firewire_softc *sc; 199118293Ssimokawa struct firewire_comm *fc; 200118293Ssimokawa struct fw_drv1 *d; 201106813Ssimokawa int unit = DEV2UNIT(dev); 202106813Ssimokawa struct fw_xfer *xfer; 203106813Ssimokawa struct fw_bind *fwb; 204106813Ssimokawa int err = 0; 205106813Ssimokawa 206106813Ssimokawa if (DEV_FWMEM(dev)) 207106813Ssimokawa return fwmem_close(dev, flags, fmt, td); 208106813Ssimokawa 209106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 210118293Ssimokawa fc = sc->fc; 211118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 212118293Ssimokawa 213118293Ssimokawa if (d->ir != NULL) { 214118293Ssimokawa struct fw_xferq *ir = d->ir; 215118293Ssimokawa 216118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 217118293Ssimokawa return (EINVAL); 218118293Ssimokawa if (ir->flag & FWXFERQ_RUNNING) { 219118293Ssimokawa ir->flag &= ~FWXFERQ_RUNNING; 220118293Ssimokawa fc->irx_disable(fc, ir->dmach); 221118293Ssimokawa } 222118293Ssimokawa /* free extbuf */ 223118293Ssimokawa fwdev_freebuf(ir); 224118293Ssimokawa /* drain receiving buffer */ 225118293Ssimokawa for (xfer = STAILQ_FIRST(&ir->q); 226118293Ssimokawa xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 227118293Ssimokawa ir->queued --; 228118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 229118293Ssimokawa 230118293Ssimokawa xfer->resp = 0; 231118293Ssimokawa fw_xfer_done(xfer); 232118293Ssimokawa } 233118293Ssimokawa /* remove binding */ 234118293Ssimokawa for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL; 235118293Ssimokawa fwb = STAILQ_FIRST(&ir->binds)) { 236118293Ssimokawa STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist); 237118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->binds, chlist); 238118293Ssimokawa free(fwb, M_FW); 239118293Ssimokawa } 240118293Ssimokawa ir->flag &= ~(FWXFERQ_OPEN | 241118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 242118293Ssimokawa d->ir = NULL; 243118293Ssimokawa 244106813Ssimokawa } 245118293Ssimokawa if (d->it != NULL) { 246118293Ssimokawa struct fw_xferq *it = d->it; 247106813Ssimokawa 248118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 249118293Ssimokawa return (EINVAL); 250118293Ssimokawa if (it->flag & FWXFERQ_RUNNING) { 251118293Ssimokawa it->flag &= ~FWXFERQ_RUNNING; 252118293Ssimokawa fc->itx_disable(fc, it->dmach); 253118293Ssimokawa } 254118293Ssimokawa /* free extbuf */ 255118293Ssimokawa fwdev_freebuf(it); 256118293Ssimokawa it->flag &= ~(FWXFERQ_OPEN | 257118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 258118293Ssimokawa d->it = NULL; 259106813Ssimokawa } 260118293Ssimokawa free(dev->si_drv1, M_FW); 261118293Ssimokawa dev->si_drv1 = NULL; 262106813Ssimokawa 263106813Ssimokawa return err; 264106813Ssimokawa} 265106813Ssimokawa 266106813Ssimokawa/* 267106813Ssimokawa * read request. 268106813Ssimokawa */ 269106813Ssimokawastatic int 270106813Ssimokawafw_read (dev_t dev, struct uio *uio, int ioflag) 271106813Ssimokawa{ 272106813Ssimokawa struct firewire_softc *sc; 273106813Ssimokawa struct fw_xferq *ir; 274106813Ssimokawa struct fw_xfer *xfer; 275106813Ssimokawa int err = 0, s, slept = 0; 276106813Ssimokawa int unit = DEV2UNIT(dev); 277106813Ssimokawa struct fw_pkt *fp; 278106813Ssimokawa 279106813Ssimokawa if (DEV_FWMEM(dev)) 280120660Ssimokawa return physio(dev, uio, ioflag); 281106813Ssimokawa 282106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 283106813Ssimokawa 284118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 285118293Ssimokawa if (ir == NULL || ir->buf == NULL) 286118293Ssimokawa return (EIO); 287106813Ssimokawa 288106813Ssimokawareadloop: 289106813Ssimokawa xfer = STAILQ_FIRST(&ir->q); 290113584Ssimokawa if (ir->stproc == NULL) { 291109988Ssimokawa /* iso bulkxfer */ 292106813Ssimokawa ir->stproc = STAILQ_FIRST(&ir->stvalid); 293109988Ssimokawa if (ir->stproc != NULL) { 294106813Ssimokawa s = splfw(); 295106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->stvalid, link); 296106813Ssimokawa splx(s); 297106813Ssimokawa ir->queued = 0; 298106813Ssimokawa } 299106813Ssimokawa } 300109988Ssimokawa if (xfer == NULL && ir->stproc == NULL) { 301109988Ssimokawa /* no data avaliable */ 302109988Ssimokawa if (slept == 0) { 303106813Ssimokawa slept = 1; 304106813Ssimokawa ir->flag |= FWXFERQ_WAKEUP; 305111748Sdes err = tsleep(ir, FWPRI, "fw_read", hz); 306109988Ssimokawa ir->flag &= ~FWXFERQ_WAKEUP; 307109988Ssimokawa if (err == 0) 308109988Ssimokawa goto readloop; 309109988Ssimokawa } else if (slept == 1) 310106813Ssimokawa err = EIO; 311109988Ssimokawa return err; 312109988Ssimokawa } else if(xfer != NULL) { 313120660Ssimokawa#if 0 /* XXX broken */ 314113584Ssimokawa /* per packet mode or FWACT_CH bind?*/ 315106813Ssimokawa s = splfw(); 316106813Ssimokawa ir->queued --; 317106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 318106813Ssimokawa splx(s); 319120660Ssimokawa fp = &xfer->recv.hdr; 320120660Ssimokawa if (sc->fc->irx_post != NULL) 321106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 322120660Ssimokawa err = uiomove((void *)fp, 1 /* XXX header size */, uio); 323120660Ssimokawa /* XXX copy payload too */ 324113584Ssimokawa /* XXX we should recycle this xfer */ 325120660Ssimokawa#endif 326106813Ssimokawa fw_xfer_free( xfer); 327109988Ssimokawa } else if(ir->stproc != NULL) { 328109988Ssimokawa /* iso bulkxfer */ 329113584Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 330113584Ssimokawa ir->stproc->poffset + ir->queued); 331106813Ssimokawa if(sc->fc->irx_post != NULL) 332106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 333113584Ssimokawa if(fp->mode.stream.len == 0){ 334106813Ssimokawa err = EIO; 335106813Ssimokawa return err; 336106813Ssimokawa } 337109988Ssimokawa err = uiomove((caddr_t)fp, 338113584Ssimokawa fp->mode.stream.len + sizeof(u_int32_t), uio); 339106813Ssimokawa ir->queued ++; 340106813Ssimokawa if(ir->queued >= ir->bnpacket){ 341106813Ssimokawa s = splfw(); 342106813Ssimokawa STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 343106813Ssimokawa splx(s); 344118293Ssimokawa sc->fc->irx_enable(sc->fc, ir->dmach); 345106813Ssimokawa ir->stproc = NULL; 346106813Ssimokawa } 347109988Ssimokawa if (uio->uio_resid >= ir->psize) { 348109988Ssimokawa slept = -1; 349109988Ssimokawa goto readloop; 350109988Ssimokawa } 351106813Ssimokawa } 352106813Ssimokawa return err; 353106813Ssimokawa} 354106813Ssimokawa 355106813Ssimokawastatic int 356106813Ssimokawafw_write (dev_t dev, struct uio *uio, int ioflag) 357106813Ssimokawa{ 358106813Ssimokawa int err = 0; 359106813Ssimokawa struct firewire_softc *sc; 360106813Ssimokawa int unit = DEV2UNIT(dev); 361106813Ssimokawa int s, slept = 0; 362106813Ssimokawa struct fw_pkt *fp; 363106813Ssimokawa struct firewire_comm *fc; 364106813Ssimokawa struct fw_xferq *it; 365106813Ssimokawa 366106813Ssimokawa if (DEV_FWMEM(dev)) 367120660Ssimokawa return physio(dev, uio, ioflag); 368106813Ssimokawa 369106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 370106813Ssimokawa fc = sc->fc; 371118293Ssimokawa it = ((struct fw_drv1 *)dev->si_drv1)->it; 372118293Ssimokawa if (it == NULL || it->buf == NULL) 373118293Ssimokawa return (EIO); 374106813Ssimokawaisoloop: 375113802Ssimokawa if (it->stproc == NULL) { 376113802Ssimokawa it->stproc = STAILQ_FIRST(&it->stfree); 377113802Ssimokawa if (it->stproc != NULL) { 378106813Ssimokawa s = splfw(); 379113802Ssimokawa STAILQ_REMOVE_HEAD(&it->stfree, link); 380106813Ssimokawa splx(s); 381113802Ssimokawa it->queued = 0; 382113802Ssimokawa } else if (slept == 0) { 383113802Ssimokawa slept = 1; 384118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 385113802Ssimokawa if (err) 386113802Ssimokawa return err; 387113802Ssimokawa err = tsleep(it, FWPRI, "fw_write", hz); 388113802Ssimokawa if (err) 389113802Ssimokawa return err; 390109988Ssimokawa goto isoloop; 391113802Ssimokawa } else { 392113802Ssimokawa err = EIO; 393106813Ssimokawa return err; 394106813Ssimokawa } 395106813Ssimokawa } 396113802Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 397113802Ssimokawa it->stproc->poffset + it->queued); 398113802Ssimokawa err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 399113802Ssimokawa err = uiomove((caddr_t)fp->mode.stream.payload, 400113802Ssimokawa fp->mode.stream.len, uio); 401113802Ssimokawa it->queued ++; 402113802Ssimokawa if (it->queued >= it->bnpacket) { 403113802Ssimokawa s = splfw(); 404113802Ssimokawa STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 405113802Ssimokawa splx(s); 406113802Ssimokawa it->stproc = NULL; 407118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 408113802Ssimokawa } 409113802Ssimokawa if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 410113802Ssimokawa slept = 0; 411113802Ssimokawa goto isoloop; 412113802Ssimokawa } 413113802Ssimokawa return err; 414106813Ssimokawa} 415106813Ssimokawa/* 416106813Ssimokawa * ioctl support. 417106813Ssimokawa */ 418106813Ssimokawaint 419106813Ssimokawafw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 420106813Ssimokawa{ 421106813Ssimokawa struct firewire_softc *sc; 422118293Ssimokawa struct firewire_comm *fc; 423118293Ssimokawa struct fw_drv1 *d; 424106813Ssimokawa int unit = DEV2UNIT(dev); 425113584Ssimokawa int s, i, len, err = 0; 426106813Ssimokawa struct fw_device *fwdev; 427106813Ssimokawa struct fw_bind *fwb; 428106813Ssimokawa struct fw_xferq *ir, *it; 429106813Ssimokawa struct fw_xfer *xfer; 430106813Ssimokawa struct fw_pkt *fp; 431109814Ssimokawa struct fw_devinfo *devinfo; 432117473Ssimokawa void *ptr; 433106813Ssimokawa 434106813Ssimokawa struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 435106813Ssimokawa struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 436106813Ssimokawa struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 437106813Ssimokawa struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 438106813Ssimokawa struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 439106813Ssimokawa struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 440106813Ssimokawa 441106813Ssimokawa if (DEV_FWMEM(dev)) 442106813Ssimokawa return fwmem_ioctl(dev, cmd, data, flag, td); 443106813Ssimokawa 444106813Ssimokawa if (!data) 445106813Ssimokawa return(EINVAL); 446106813Ssimokawa 447118293Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 448118293Ssimokawa fc = sc->fc; 449118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 450118293Ssimokawa ir = d->ir; 451118293Ssimokawa it = d->it; 452118293Ssimokawa 453106813Ssimokawa switch (cmd) { 454106813Ssimokawa case FW_STSTREAM: 455118293Ssimokawa if (it == NULL) { 456118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 457118293Ssimokawa it = fc->it[i]; 458118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 459118293Ssimokawa break; 460118293Ssimokawa } 461118293Ssimokawa if (i >= fc->nisodma) { 462118293Ssimokawa err = EBUSY; 463118293Ssimokawa break; 464118293Ssimokawa } 465118293Ssimokawa err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 466118293Ssimokawa if (err) 467118293Ssimokawa break; 468118293Ssimokawa it->flag |= FWXFERQ_OPEN; 469118293Ssimokawa } 470118293Ssimokawa it->flag &= ~0xff; 471118293Ssimokawa it->flag |= (0x3f & ichreq->ch); 472118293Ssimokawa it->flag |= ((0x3 & ichreq->tag) << 6); 473118293Ssimokawa d->it = it; 474106813Ssimokawa break; 475106813Ssimokawa case FW_GTSTREAM: 476118293Ssimokawa if (it != NULL) { 477118293Ssimokawa ichreq->ch = it->flag & 0x3f; 478118293Ssimokawa ichreq->tag = it->flag >> 2 & 0x3; 479118293Ssimokawa } else 480118293Ssimokawa err = EINVAL; 481106813Ssimokawa break; 482106813Ssimokawa case FW_SRSTREAM: 483118293Ssimokawa if (ir == NULL) { 484118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 485118293Ssimokawa ir = fc->ir[i]; 486118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 487118293Ssimokawa break; 488118293Ssimokawa } 489118293Ssimokawa if (i >= fc->nisodma) { 490118293Ssimokawa err = EBUSY; 491118293Ssimokawa break; 492118293Ssimokawa } 493118293Ssimokawa err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 494118293Ssimokawa if (err) 495118293Ssimokawa break; 496118293Ssimokawa ir->flag |= FWXFERQ_OPEN; 497118293Ssimokawa } 498118293Ssimokawa ir->flag &= ~0xff; 499118293Ssimokawa ir->flag |= (0x3f & ichreq->ch); 500118293Ssimokawa ir->flag |= ((0x3 & ichreq->tag) << 6); 501118293Ssimokawa d->ir = ir; 502118293Ssimokawa err = fc->irx_enable(fc, ir->dmach); 503106813Ssimokawa break; 504106813Ssimokawa case FW_GRSTREAM: 505118293Ssimokawa if (d->ir != NULL) { 506118293Ssimokawa ichreq->ch = ir->flag & 0x3f; 507118293Ssimokawa ichreq->tag = ir->flag >> 2 & 0x3; 508118293Ssimokawa } else 509118293Ssimokawa err = EINVAL; 510106813Ssimokawa break; 511106813Ssimokawa case FW_SSTBUF: 512118293Ssimokawa bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 513106813Ssimokawa break; 514106813Ssimokawa case FW_GSTBUF: 515118293Ssimokawa bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 516118293Ssimokawa if (ir != NULL) { 517118293Ssimokawa ibufreq->rx.nchunk = ir->bnchunk; 518118293Ssimokawa ibufreq->rx.npacket = ir->bnpacket; 519118293Ssimokawa ibufreq->rx.psize = ir->psize; 520118293Ssimokawa } 521118293Ssimokawa bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 522118293Ssimokawa if (it != NULL) { 523118293Ssimokawa ibufreq->tx.nchunk = it->bnchunk; 524118293Ssimokawa ibufreq->tx.npacket = it->bnpacket; 525118293Ssimokawa ibufreq->tx.psize = it->psize; 526118293Ssimokawa } 527106813Ssimokawa break; 528106813Ssimokawa case FW_ASYREQ: 529120660Ssimokawa { 530120660Ssimokawa struct tcode_info *tinfo; 531121466Ssimokawa int pay_len = 0; 532120660Ssimokawa 533106813Ssimokawa fp = &asyreq->pkt; 534121466Ssimokawa tinfo = &sc->fc->tcode[fp->mode.hdr.tcode]; 535121466Ssimokawa 536121466Ssimokawa if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) 537121466Ssimokawa pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); 538121466Ssimokawa 539121466Ssimokawa xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); 540121466Ssimokawa if (xfer == NULL) 541121466Ssimokawa return (ENOMEM); 542121466Ssimokawa 543106813Ssimokawa switch (asyreq->req.type) { 544106813Ssimokawa case FWASREQNODE: 545106813Ssimokawa break; 546106813Ssimokawa case FWASREQEUI: 547110072Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, 548110582Ssimokawa &asyreq->req.dst.eui); 549106813Ssimokawa if (fwdev == NULL) { 550108782Ssimokawa device_printf(sc->fc->bdev, 551108782Ssimokawa "cannot find node\n"); 552106813Ssimokawa err = EINVAL; 553121466Ssimokawa goto out; 554106813Ssimokawa } 555120660Ssimokawa fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; 556106813Ssimokawa break; 557106813Ssimokawa case FWASRESTL: 558106813Ssimokawa /* XXX what's this? */ 559106813Ssimokawa break; 560106813Ssimokawa case FWASREQSTREAM: 561106813Ssimokawa /* nothing to do */ 562106813Ssimokawa break; 563106813Ssimokawa } 564121466Ssimokawa 565120660Ssimokawa bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); 566121466Ssimokawa if (pay_len > 0) 567120660Ssimokawa bcopy((char *)fp + tinfo->hdr_len, 568121466Ssimokawa (void *)&xfer->send.payload, pay_len); 569121466Ssimokawa xfer->send.spd = asyreq->req.sped; 570106813Ssimokawa xfer->act.hand = fw_asy_callback; 571121466Ssimokawa 572121466Ssimokawa if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0) 573121466Ssimokawa goto out; 574121466Ssimokawa if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0) 575121466Ssimokawa goto out; 576121466Ssimokawa if (xfer->resp != 0) { 577121466Ssimokawa err = EIO; 578121466Ssimokawa goto out; 579106813Ssimokawa } 580121466Ssimokawa if ((tinfo->flag & FWTI_TLABEL) == 0) 581121466Ssimokawa goto out; 582121466Ssimokawa 583121466Ssimokawa /* copy response */ 584121466Ssimokawa tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; 585121466Ssimokawa if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) 586121466Ssimokawa asyreq->req.len = xfer->recv.pay_len; 587121466Ssimokawa else 588121466Ssimokawa err = EINVAL; 589121466Ssimokawa bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); 590121466Ssimokawa bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, 591121466Ssimokawa MAX(0, asyreq->req.len - tinfo->hdr_len)); 592121466Ssimokawaout: 593120660Ssimokawa fw_xfer_free_buf(xfer); 594106813Ssimokawa break; 595120660Ssimokawa } 596106813Ssimokawa case FW_IBUSRST: 597106813Ssimokawa sc->fc->ibr(sc->fc); 598106813Ssimokawa break; 599106813Ssimokawa case FW_CBINDADDR: 600106813Ssimokawa fwb = fw_bindlookup(sc->fc, 601106813Ssimokawa bindreq->start.hi, bindreq->start.lo); 602106813Ssimokawa if(fwb == NULL){ 603106813Ssimokawa err = EINVAL; 604106813Ssimokawa break; 605106813Ssimokawa } 606106813Ssimokawa STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); 607118293Ssimokawa STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist); 608110195Ssimokawa free(fwb, M_FW); 609106813Ssimokawa break; 610106813Ssimokawa case FW_SBINDADDR: 611106813Ssimokawa if(bindreq->len <= 0 ){ 612106813Ssimokawa err = EINVAL; 613106813Ssimokawa break; 614106813Ssimokawa } 615106813Ssimokawa if(bindreq->start.hi > 0xffff ){ 616106813Ssimokawa err = EINVAL; 617106813Ssimokawa break; 618106813Ssimokawa } 619110195Ssimokawa fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); 620106813Ssimokawa if(fwb == NULL){ 621106813Ssimokawa err = ENOMEM; 622106813Ssimokawa break; 623106813Ssimokawa } 624120660Ssimokawa fwb->start = ((u_int64_t)bindreq->start.hi << 32) | 625120660Ssimokawa bindreq->start.lo; 626120660Ssimokawa fwb->end = fwb->start + bindreq->len; 627118293Ssimokawa /* XXX */ 628118293Ssimokawa fwb->sub = ir->dmach; 629113584Ssimokawa fwb->act_type = FWACT_CH; 630106813Ssimokawa 631120660Ssimokawa /* XXX alloc buf */ 632110269Ssimokawa xfer = fw_xfer_alloc(M_FWXFER); 633106813Ssimokawa if(xfer == NULL){ 634106813Ssimokawa err = ENOMEM; 635106813Ssimokawa return err; 636106813Ssimokawa } 637106813Ssimokawa xfer->fc = sc->fc; 638106813Ssimokawa 639113584Ssimokawa s = splfw(); 640113584Ssimokawa /* XXX broken. need multiple xfer */ 641113584Ssimokawa STAILQ_INIT(&fwb->xferlist); 642113584Ssimokawa STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 643113584Ssimokawa splx(s); 644106813Ssimokawa err = fw_bindadd(sc->fc, fwb); 645106813Ssimokawa break; 646106813Ssimokawa case FW_GDEVLST: 647109814Ssimokawa i = len = 1; 648109814Ssimokawa /* myself */ 649109814Ssimokawa devinfo = &fwdevlst->dev[0]; 650109814Ssimokawa devinfo->dst = sc->fc->nodeid; 651109814Ssimokawa devinfo->status = 0; /* XXX */ 652109814Ssimokawa devinfo->eui.hi = sc->fc->eui.hi; 653109814Ssimokawa devinfo->eui.lo = sc->fc->eui.lo; 654110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) { 655109814Ssimokawa if(len < FW_MAX_DEVLST){ 656109814Ssimokawa devinfo = &fwdevlst->dev[len++]; 657109814Ssimokawa devinfo->dst = fwdev->dst; 658109814Ssimokawa devinfo->status = 659109814Ssimokawa (fwdev->status == FWDEVINVAL)?0:1; 660109814Ssimokawa devinfo->eui.hi = fwdev->eui.hi; 661109814Ssimokawa devinfo->eui.lo = fwdev->eui.lo; 662106813Ssimokawa } 663106813Ssimokawa i++; 664106813Ssimokawa } 665106813Ssimokawa fwdevlst->n = i; 666109814Ssimokawa fwdevlst->info_len = len; 667106813Ssimokawa break; 668106813Ssimokawa case FW_GTPMAP: 669106813Ssimokawa bcopy(sc->fc->topology_map, data, 670106813Ssimokawa (sc->fc->topology_map->crc_len + 1) * 4); 671106813Ssimokawa break; 672106813Ssimokawa case FW_GCROM: 673110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) 674110193Ssimokawa if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 675106813Ssimokawa break; 676106813Ssimokawa if (fwdev == NULL) { 677117473Ssimokawa if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) { 678117473Ssimokawa err = FWNODE_INVAL; 679117473Ssimokawa break; 680117473Ssimokawa } 681117473Ssimokawa /* myself */ 682117473Ssimokawa ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 683117473Ssimokawa len = CROMSIZE; 684117473Ssimokawa for (i = 0; i < CROMSIZE/4; i++) 685117473Ssimokawa ((u_int32_t *)ptr)[i] 686117473Ssimokawa = ntohl(sc->fc->config_rom[i]); 687117473Ssimokawa } else { 688117473Ssimokawa /* found */ 689117473Ssimokawa ptr = (void *)&fwdev->csrrom[0]; 690117473Ssimokawa if (fwdev->rommax < CSRROMOFF) 691117473Ssimokawa len = 0; 692117473Ssimokawa else 693117473Ssimokawa len = fwdev->rommax - CSRROMOFF + 4; 694106813Ssimokawa } 695106813Ssimokawa if (crom_buf->len < len) 696106813Ssimokawa len = crom_buf->len; 697106813Ssimokawa else 698106813Ssimokawa crom_buf->len = len; 699117473Ssimokawa err = copyout(ptr, crom_buf->ptr, len); 700117473Ssimokawa if (fwdev == NULL) 701117473Ssimokawa /* myself */ 702117473Ssimokawa free(ptr, M_FW); 703106813Ssimokawa break; 704106813Ssimokawa default: 705106813Ssimokawa sc->fc->ioctl (dev, cmd, data, flag, td); 706106813Ssimokawa break; 707106813Ssimokawa } 708106813Ssimokawa return err; 709106813Ssimokawa} 710106813Ssimokawaint 711106813Ssimokawafw_poll(dev_t dev, int events, fw_proc *td) 712106813Ssimokawa{ 713118293Ssimokawa struct firewire_softc *sc; 714118293Ssimokawa struct fw_xferq *ir; 715106813Ssimokawa int revents; 716106813Ssimokawa int tmp; 717106813Ssimokawa int unit = DEV2UNIT(dev); 718106813Ssimokawa 719106813Ssimokawa if (DEV_FWMEM(dev)) 720106813Ssimokawa return fwmem_poll(dev, events, td); 721106813Ssimokawa 722106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 723118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 724106813Ssimokawa revents = 0; 725106813Ssimokawa tmp = POLLIN | POLLRDNORM; 726106813Ssimokawa if (events & tmp) { 727118293Ssimokawa if (STAILQ_FIRST(&ir->q) != NULL) 728106813Ssimokawa revents |= tmp; 729106813Ssimokawa else 730118293Ssimokawa selrecord(td, &ir->rsel); 731106813Ssimokawa } 732106813Ssimokawa tmp = POLLOUT | POLLWRNORM; 733106813Ssimokawa if (events & tmp) { 734106813Ssimokawa /* XXX should be fixed */ 735106813Ssimokawa revents |= tmp; 736106813Ssimokawa } 737106813Ssimokawa 738106813Ssimokawa return revents; 739106813Ssimokawa} 740106813Ssimokawa 741106813Ssimokawastatic int 742113584Ssimokawa#if __FreeBSD_version < 500102 743111615Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, int nproto) 744111615Ssimokawa#else 745113584Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) 746111615Ssimokawa#endif 747106813Ssimokawa{ 748118455Ssimokawa struct firewire_softc *sc; 749106813Ssimokawa int unit = DEV2UNIT(dev); 750106813Ssimokawa 751106813Ssimokawa if (DEV_FWMEM(dev)) 752113584Ssimokawa#if __FreeBSD_version < 500102 753111615Ssimokawa return fwmem_mmap(dev, offset, nproto); 754111615Ssimokawa#else 755111462Smux return fwmem_mmap(dev, offset, paddr, nproto); 756111615Ssimokawa#endif 757106813Ssimokawa 758118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 759106813Ssimokawa 760106813Ssimokawa return EINVAL; 761106813Ssimokawa} 762118455Ssimokawa 763120660Ssimokawastatic void 764120660Ssimokawafw_strategy(struct bio *bp) 765120660Ssimokawa{ 766120660Ssimokawa dev_t dev; 767120660Ssimokawa 768120660Ssimokawa dev = bp->bio_dev; 769120660Ssimokawa if (DEV_FWMEM(dev)) { 770120660Ssimokawa fwmem_strategy(bp); 771120660Ssimokawa return; 772120660Ssimokawa } 773120660Ssimokawa 774120660Ssimokawa bp->bio_error = EOPNOTSUPP; 775120660Ssimokawa bp->bio_flags |= BIO_ERROR; 776120660Ssimokawa bp->bio_resid = bp->bio_bcount; 777120660Ssimokawa biodone(bp); 778120660Ssimokawa} 779120660Ssimokawa 780118455Ssimokawaint 781118455Ssimokawafwdev_makedev(struct firewire_softc *sc) 782118455Ssimokawa{ 783118455Ssimokawa int err = 0; 784118455Ssimokawa 785118455Ssimokawa#if __FreeBSD_version >= 500000 786118455Ssimokawa dev_t d; 787118455Ssimokawa int unit; 788118455Ssimokawa 789118455Ssimokawa unit = device_get_unit(sc->fc->bdev); 790118455Ssimokawa sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 791118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 792118455Ssimokawa "fw%d.%d", unit, 0); 793118455Ssimokawa d = make_dev(&firewire_cdevsw, 794118455Ssimokawa MAKEMINOR(FWMEM_FLAG, unit, 0), 795118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 796118455Ssimokawa "fwmem%d.%d", unit, 0); 797118455Ssimokawa dev_depends(sc->dev, d); 798118455Ssimokawa make_dev_alias(sc->dev, "fw%d", unit); 799118455Ssimokawa make_dev_alias(d, "fwmem%d", unit); 800118455Ssimokawa#else 801118455Ssimokawa cdevsw_add(&firewire_cdevsw); 802118455Ssimokawa#endif 803118455Ssimokawa 804118455Ssimokawa return (err); 805118455Ssimokawa} 806118455Ssimokawa 807118455Ssimokawaint 808118455Ssimokawafwdev_destroydev(struct firewire_softc *sc) 809118455Ssimokawa{ 810118455Ssimokawa int err = 0; 811118455Ssimokawa 812118455Ssimokawa#if __FreeBSD_version >= 500000 813118455Ssimokawa destroy_dev(sc->dev); 814118455Ssimokawa#else 815118455Ssimokawa cdevsw_remove(&firewire_cdevsw); 816118455Ssimokawa#endif 817118455Ssimokawa return (err); 818118455Ssimokawa} 819118455Ssimokawa 820118455Ssimokawa#if __FreeBSD_version >= 500000 821118455Ssimokawa#define NDEVTYPE 2 822118455Ssimokawavoid 823118455Ssimokawafwdev_clone(void *arg, char *name, int namelen, dev_t *dev) 824118455Ssimokawa{ 825118455Ssimokawa struct firewire_softc *sc; 826118455Ssimokawa char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 827118455Ssimokawa char *subp = NULL; 828118455Ssimokawa int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 829118455Ssimokawa int i, unit = 0, sub = 0; 830118455Ssimokawa 831118455Ssimokawa if (*dev != NODEV) 832118455Ssimokawa return; 833118455Ssimokawa 834118455Ssimokawa for (i = 0; i < NDEVTYPE; i++) 835120624Ssimokawa if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 836118455Ssimokawa goto found; 837118455Ssimokawa /* not match */ 838118455Ssimokawa return; 839118455Ssimokawafound: 840118455Ssimokawa 841118455Ssimokawa if (subp == NULL || *subp++ != '.') 842118455Ssimokawa return; 843118455Ssimokawa 844118455Ssimokawa /* /dev/fwU.S */ 845118455Ssimokawa while (isdigit(*subp)) { 846118455Ssimokawa sub *= 10; 847118455Ssimokawa sub += *subp++ - '0'; 848118455Ssimokawa } 849118455Ssimokawa if (*subp != '\0') 850118455Ssimokawa return; 851118455Ssimokawa 852118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 853118455Ssimokawa if (sc == NULL) 854118455Ssimokawa return; 855118455Ssimokawa *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), 856118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 857118455Ssimokawa "%s%d.%d", devnames[i], unit, sub); 858118455Ssimokawa (*dev)->si_flags |= SI_CHEAPCLONE; 859118455Ssimokawa dev_depends(sc->dev, *dev); 860118455Ssimokawa return; 861118455Ssimokawa} 862118455Ssimokawa#endif 863