fwdev.c revision 120624
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 120624 2003-10-01 06:04:30Z simokawa $ 35106813Ssimokawa * 36106813Ssimokawa */ 37106813Ssimokawa 38106813Ssimokawa#include <sys/param.h> 39106813Ssimokawa#include <sys/systm.h> 40106813Ssimokawa#include <sys/types.h> 41106813Ssimokawa#include <sys/mbuf.h> 42106813Ssimokawa 43106813Ssimokawa#include <sys/kernel.h> 44106813Ssimokawa#include <sys/malloc.h> 45106813Ssimokawa#include <sys/conf.h> 46106813Ssimokawa#include <sys/poll.h> 47106813Ssimokawa 48106813Ssimokawa#include <sys/bus.h> 49118455Ssimokawa#include <sys/ctype.h> 50113584Ssimokawa#include <machine/bus.h> 51106813Ssimokawa 52106813Ssimokawa#include <sys/ioccom.h> 53106813Ssimokawa 54106813Ssimokawa#include <dev/firewire/firewire.h> 55106813Ssimokawa#include <dev/firewire/firewirereg.h> 56113584Ssimokawa#include <dev/firewire/fwdma.h> 57106813Ssimokawa#include <dev/firewire/fwmem.h> 58109282Ssimokawa#include <dev/firewire/iec68113.h> 59106813Ssimokawa 60106813Ssimokawa#define CDEV_MAJOR 127 61106813Ssimokawa#define FWNODE_INVAL 0xffff 62106813Ssimokawa 63106813Ssimokawastatic d_open_t fw_open; 64106813Ssimokawastatic d_close_t fw_close; 65106813Ssimokawastatic d_ioctl_t fw_ioctl; 66106813Ssimokawastatic d_poll_t fw_poll; 67106813Ssimokawastatic d_read_t fw_read; /* for Isochronous packet */ 68106813Ssimokawastatic d_write_t fw_write; 69106813Ssimokawastatic d_mmap_t fw_mmap; 70106813Ssimokawa 71106813Ssimokawastruct cdevsw firewire_cdevsw = 72106813Ssimokawa{ 73111942Ssimokawa#if __FreeBSD_version >= 500104 74111815Sphk .d_open = fw_open, 75111815Sphk .d_close = fw_close, 76111815Sphk .d_read = fw_read, 77111815Sphk .d_write = fw_write, 78111815Sphk .d_ioctl = fw_ioctl, 79111815Sphk .d_poll = fw_poll, 80111815Sphk .d_mmap = fw_mmap, 81111815Sphk .d_name = "fw", 82111815Sphk .d_maj = CDEV_MAJOR, 83111815Sphk .d_flags = D_MEM 84111942Ssimokawa#else 85111942Ssimokawa fw_open, fw_close, fw_read, fw_write, fw_ioctl, 86118455Ssimokawa fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, 87118455Ssimokawa nodump, nopsize, D_MEM, -1 88111942Ssimokawa#endif 89106813Ssimokawa}; 90106813Ssimokawa 91118293Ssimokawastruct fw_drv1 { 92118293Ssimokawa struct fw_xferq *ir; 93118293Ssimokawa struct fw_xferq *it; 94118293Ssimokawa struct fw_isobufreq bufreq; 95118293Ssimokawa}; 96118293Ssimokawa 97106813Ssimokawastatic int 98118293Ssimokawafwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 99118293Ssimokawa struct fw_bufspec *b) 100118293Ssimokawa{ 101118293Ssimokawa int i; 102118293Ssimokawa 103118293Ssimokawa if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 104118293Ssimokawa return(EBUSY); 105118293Ssimokawa 106118293Ssimokawa q->bulkxfer = (struct fw_bulkxfer *) malloc( 107118293Ssimokawa sizeof(struct fw_bulkxfer) * b->nchunk, 108118293Ssimokawa M_FW, M_WAITOK); 109118293Ssimokawa if (q->bulkxfer == NULL) 110118293Ssimokawa return(ENOMEM); 111118293Ssimokawa 112118293Ssimokawa b->psize = roundup2(b->psize, sizeof(u_int32_t)); 113118293Ssimokawa q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t), 114118293Ssimokawa b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 115118293Ssimokawa 116118293Ssimokawa if (q->buf == NULL) { 117118293Ssimokawa free(q->bulkxfer, M_FW); 118118293Ssimokawa q->bulkxfer = NULL; 119118293Ssimokawa return(ENOMEM); 120118293Ssimokawa } 121118293Ssimokawa q->bnchunk = b->nchunk; 122118293Ssimokawa q->bnpacket = b->npacket; 123118293Ssimokawa q->psize = (b->psize + 3) & ~3; 124118293Ssimokawa q->queued = 0; 125118293Ssimokawa 126118293Ssimokawa STAILQ_INIT(&q->stvalid); 127118293Ssimokawa STAILQ_INIT(&q->stfree); 128118293Ssimokawa STAILQ_INIT(&q->stdma); 129118293Ssimokawa q->stproc = NULL; 130118293Ssimokawa 131118293Ssimokawa for(i = 0 ; i < q->bnchunk; i++){ 132118293Ssimokawa q->bulkxfer[i].poffset = i * q->bnpacket; 133118293Ssimokawa q->bulkxfer[i].mbuf = NULL; 134118293Ssimokawa STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 135118293Ssimokawa } 136118293Ssimokawa 137118293Ssimokawa q->flag &= ~FWXFERQ_MODEMASK; 138118293Ssimokawa q->flag |= FWXFERQ_STREAM; 139118293Ssimokawa q->flag |= FWXFERQ_EXTBUF; 140118293Ssimokawa 141118293Ssimokawa return (0); 142118293Ssimokawa} 143118293Ssimokawa 144118293Ssimokawastatic int 145118293Ssimokawafwdev_freebuf(struct fw_xferq *q) 146118293Ssimokawa{ 147118293Ssimokawa if (q->flag & FWXFERQ_EXTBUF) { 148118293Ssimokawa if (q->buf != NULL) 149118293Ssimokawa fwdma_free_multiseg(q->buf); 150118293Ssimokawa q->buf = NULL; 151118293Ssimokawa free(q->bulkxfer, M_FW); 152118293Ssimokawa q->bulkxfer = NULL; 153118293Ssimokawa q->flag &= ~FWXFERQ_EXTBUF; 154118293Ssimokawa q->psize = 0; 155118293Ssimokawa q->maxq = FWMAXQUEUE; 156118293Ssimokawa } 157118293Ssimokawa return (0); 158118293Ssimokawa} 159118293Ssimokawa 160118293Ssimokawa 161118293Ssimokawastatic int 162106813Ssimokawafw_open (dev_t dev, int flags, int fmt, fw_proc *td) 163106813Ssimokawa{ 164106813Ssimokawa int err = 0; 165106813Ssimokawa 166118293Ssimokawa if (dev->si_drv1 != NULL) 167118293Ssimokawa return (EBUSY); 168118293Ssimokawa 169106813Ssimokawa if (DEV_FWMEM(dev)) 170106813Ssimokawa return fwmem_open(dev, flags, fmt, td); 171106813Ssimokawa 172118293Ssimokawa#if __FreeBSD_version >= 500000 173118455Ssimokawa if ((dev->si_flags & SI_NAMED) == 0) { 174118455Ssimokawa int unit = DEV2UNIT(dev); 175118455Ssimokawa int sub = DEV2SUB(dev); 176118455Ssimokawa 177118293Ssimokawa make_dev(&firewire_cdevsw, minor(dev), 178118293Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 179118293Ssimokawa "fw%d.%d", unit, sub); 180118455Ssimokawa } 181118455Ssimokawa#endif 182118293Ssimokawa 183118293Ssimokawa dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 184118293Ssimokawa 185106813Ssimokawa return err; 186106813Ssimokawa} 187106813Ssimokawa 188106813Ssimokawastatic int 189106813Ssimokawafw_close (dev_t dev, int flags, int fmt, fw_proc *td) 190106813Ssimokawa{ 191106813Ssimokawa struct firewire_softc *sc; 192118293Ssimokawa struct firewire_comm *fc; 193118293Ssimokawa struct fw_drv1 *d; 194106813Ssimokawa int unit = DEV2UNIT(dev); 195106813Ssimokawa struct fw_xfer *xfer; 196106813Ssimokawa struct fw_bind *fwb; 197106813Ssimokawa int err = 0; 198106813Ssimokawa 199106813Ssimokawa if (DEV_FWMEM(dev)) 200106813Ssimokawa return fwmem_close(dev, flags, fmt, td); 201106813Ssimokawa 202106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 203118293Ssimokawa fc = sc->fc; 204118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 205118293Ssimokawa 206118293Ssimokawa if (d->ir != NULL) { 207118293Ssimokawa struct fw_xferq *ir = d->ir; 208118293Ssimokawa 209118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 210118293Ssimokawa return (EINVAL); 211118293Ssimokawa if (ir->flag & FWXFERQ_RUNNING) { 212118293Ssimokawa ir->flag &= ~FWXFERQ_RUNNING; 213118293Ssimokawa fc->irx_disable(fc, ir->dmach); 214118293Ssimokawa } 215118293Ssimokawa /* free extbuf */ 216118293Ssimokawa fwdev_freebuf(ir); 217118293Ssimokawa /* drain receiving buffer */ 218118293Ssimokawa for (xfer = STAILQ_FIRST(&ir->q); 219118293Ssimokawa xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 220118293Ssimokawa ir->queued --; 221118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 222118293Ssimokawa 223118293Ssimokawa xfer->resp = 0; 224118293Ssimokawa fw_xfer_done(xfer); 225118293Ssimokawa } 226118293Ssimokawa /* remove binding */ 227118293Ssimokawa for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL; 228118293Ssimokawa fwb = STAILQ_FIRST(&ir->binds)) { 229118293Ssimokawa STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist); 230118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->binds, chlist); 231118293Ssimokawa free(fwb, M_FW); 232118293Ssimokawa } 233118293Ssimokawa ir->flag &= ~(FWXFERQ_OPEN | 234118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 235118293Ssimokawa d->ir = NULL; 236118293Ssimokawa 237106813Ssimokawa } 238118293Ssimokawa if (d->it != NULL) { 239118293Ssimokawa struct fw_xferq *it = d->it; 240106813Ssimokawa 241118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 242118293Ssimokawa return (EINVAL); 243118293Ssimokawa if (it->flag & FWXFERQ_RUNNING) { 244118293Ssimokawa it->flag &= ~FWXFERQ_RUNNING; 245118293Ssimokawa fc->itx_disable(fc, it->dmach); 246118293Ssimokawa } 247118293Ssimokawa /* free extbuf */ 248118293Ssimokawa fwdev_freebuf(it); 249118293Ssimokawa it->flag &= ~(FWXFERQ_OPEN | 250118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 251118293Ssimokawa d->it = NULL; 252106813Ssimokawa } 253118293Ssimokawa free(dev->si_drv1, M_FW); 254118293Ssimokawa dev->si_drv1 = NULL; 255106813Ssimokawa 256106813Ssimokawa return err; 257106813Ssimokawa} 258106813Ssimokawa 259106813Ssimokawa/* 260106813Ssimokawa * read request. 261106813Ssimokawa */ 262106813Ssimokawastatic int 263106813Ssimokawafw_read (dev_t dev, struct uio *uio, int ioflag) 264106813Ssimokawa{ 265106813Ssimokawa struct firewire_softc *sc; 266106813Ssimokawa struct fw_xferq *ir; 267106813Ssimokawa struct fw_xfer *xfer; 268106813Ssimokawa int err = 0, s, slept = 0; 269106813Ssimokawa int unit = DEV2UNIT(dev); 270106813Ssimokawa struct fw_pkt *fp; 271106813Ssimokawa 272106813Ssimokawa if (DEV_FWMEM(dev)) 273106813Ssimokawa return fwmem_read(dev, uio, ioflag); 274106813Ssimokawa 275106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 276106813Ssimokawa 277118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 278118293Ssimokawa if (ir == NULL || ir->buf == NULL) 279118293Ssimokawa return (EIO); 280106813Ssimokawa 281106813Ssimokawareadloop: 282106813Ssimokawa xfer = STAILQ_FIRST(&ir->q); 283113584Ssimokawa if (ir->stproc == NULL) { 284109988Ssimokawa /* iso bulkxfer */ 285106813Ssimokawa ir->stproc = STAILQ_FIRST(&ir->stvalid); 286109988Ssimokawa if (ir->stproc != NULL) { 287106813Ssimokawa s = splfw(); 288106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->stvalid, link); 289106813Ssimokawa splx(s); 290106813Ssimokawa ir->queued = 0; 291106813Ssimokawa } 292106813Ssimokawa } 293109988Ssimokawa if (xfer == NULL && ir->stproc == NULL) { 294109988Ssimokawa /* no data avaliable */ 295109988Ssimokawa if (slept == 0) { 296106813Ssimokawa slept = 1; 297106813Ssimokawa ir->flag |= FWXFERQ_WAKEUP; 298111748Sdes err = tsleep(ir, FWPRI, "fw_read", hz); 299109988Ssimokawa ir->flag &= ~FWXFERQ_WAKEUP; 300109988Ssimokawa if (err == 0) 301109988Ssimokawa goto readloop; 302109988Ssimokawa } else if (slept == 1) 303106813Ssimokawa err = EIO; 304109988Ssimokawa return err; 305109988Ssimokawa } else if(xfer != NULL) { 306113584Ssimokawa /* per packet mode or FWACT_CH bind?*/ 307106813Ssimokawa s = splfw(); 308106813Ssimokawa ir->queued --; 309106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 310106813Ssimokawa splx(s); 311113584Ssimokawa fp = (struct fw_pkt *)xfer->recv.buf; 312106813Ssimokawa if(sc->fc->irx_post != NULL) 313106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 314113584Ssimokawa err = uiomove(xfer->recv.buf, xfer->recv.len, uio); 315113584Ssimokawa /* XXX we should recycle this xfer */ 316106813Ssimokawa fw_xfer_free( xfer); 317109988Ssimokawa } else if(ir->stproc != NULL) { 318109988Ssimokawa /* iso bulkxfer */ 319113584Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 320113584Ssimokawa ir->stproc->poffset + ir->queued); 321106813Ssimokawa if(sc->fc->irx_post != NULL) 322106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 323113584Ssimokawa if(fp->mode.stream.len == 0){ 324106813Ssimokawa err = EIO; 325106813Ssimokawa return err; 326106813Ssimokawa } 327109988Ssimokawa err = uiomove((caddr_t)fp, 328113584Ssimokawa fp->mode.stream.len + sizeof(u_int32_t), uio); 329106813Ssimokawa ir->queued ++; 330106813Ssimokawa if(ir->queued >= ir->bnpacket){ 331106813Ssimokawa s = splfw(); 332106813Ssimokawa STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 333106813Ssimokawa splx(s); 334118293Ssimokawa sc->fc->irx_enable(sc->fc, ir->dmach); 335106813Ssimokawa ir->stproc = NULL; 336106813Ssimokawa } 337109988Ssimokawa if (uio->uio_resid >= ir->psize) { 338109988Ssimokawa slept = -1; 339109988Ssimokawa goto readloop; 340109988Ssimokawa } 341106813Ssimokawa } 342106813Ssimokawa return err; 343106813Ssimokawa} 344106813Ssimokawa 345106813Ssimokawastatic int 346106813Ssimokawafw_write (dev_t dev, struct uio *uio, int ioflag) 347106813Ssimokawa{ 348106813Ssimokawa int err = 0; 349106813Ssimokawa struct firewire_softc *sc; 350106813Ssimokawa int unit = DEV2UNIT(dev); 351106813Ssimokawa int s, slept = 0; 352106813Ssimokawa struct fw_pkt *fp; 353106813Ssimokawa struct firewire_comm *fc; 354106813Ssimokawa struct fw_xferq *it; 355106813Ssimokawa 356106813Ssimokawa if (DEV_FWMEM(dev)) 357106813Ssimokawa return fwmem_write(dev, uio, ioflag); 358106813Ssimokawa 359106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 360106813Ssimokawa fc = sc->fc; 361118293Ssimokawa it = ((struct fw_drv1 *)dev->si_drv1)->it; 362118293Ssimokawa if (it == NULL || it->buf == NULL) 363118293Ssimokawa return (EIO); 364106813Ssimokawaisoloop: 365113802Ssimokawa if (it->stproc == NULL) { 366113802Ssimokawa it->stproc = STAILQ_FIRST(&it->stfree); 367113802Ssimokawa if (it->stproc != NULL) { 368106813Ssimokawa s = splfw(); 369113802Ssimokawa STAILQ_REMOVE_HEAD(&it->stfree, link); 370106813Ssimokawa splx(s); 371113802Ssimokawa it->queued = 0; 372113802Ssimokawa } else if (slept == 0) { 373113802Ssimokawa slept = 1; 374118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 375113802Ssimokawa if (err) 376113802Ssimokawa return err; 377113802Ssimokawa err = tsleep(it, FWPRI, "fw_write", hz); 378113802Ssimokawa if (err) 379113802Ssimokawa return err; 380109988Ssimokawa goto isoloop; 381113802Ssimokawa } else { 382113802Ssimokawa err = EIO; 383106813Ssimokawa return err; 384106813Ssimokawa } 385106813Ssimokawa } 386113802Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 387113802Ssimokawa it->stproc->poffset + it->queued); 388113802Ssimokawa err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 389113802Ssimokawa err = uiomove((caddr_t)fp->mode.stream.payload, 390113802Ssimokawa fp->mode.stream.len, uio); 391113802Ssimokawa it->queued ++; 392113802Ssimokawa if (it->queued >= it->bnpacket) { 393113802Ssimokawa s = splfw(); 394113802Ssimokawa STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 395113802Ssimokawa splx(s); 396113802Ssimokawa it->stproc = NULL; 397118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 398113802Ssimokawa } 399113802Ssimokawa if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 400113802Ssimokawa slept = 0; 401113802Ssimokawa goto isoloop; 402113802Ssimokawa } 403113802Ssimokawa return err; 404106813Ssimokawa} 405106813Ssimokawa/* 406106813Ssimokawa * ioctl support. 407106813Ssimokawa */ 408106813Ssimokawaint 409106813Ssimokawafw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 410106813Ssimokawa{ 411106813Ssimokawa struct firewire_softc *sc; 412118293Ssimokawa struct firewire_comm *fc; 413118293Ssimokawa struct fw_drv1 *d; 414106813Ssimokawa int unit = DEV2UNIT(dev); 415113584Ssimokawa int s, i, len, err = 0; 416106813Ssimokawa struct fw_device *fwdev; 417106813Ssimokawa struct fw_bind *fwb; 418106813Ssimokawa struct fw_xferq *ir, *it; 419106813Ssimokawa struct fw_xfer *xfer; 420106813Ssimokawa struct fw_pkt *fp; 421109814Ssimokawa struct fw_devinfo *devinfo; 422117473Ssimokawa void *ptr; 423106813Ssimokawa 424106813Ssimokawa struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 425106813Ssimokawa struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 426106813Ssimokawa struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 427106813Ssimokawa struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 428106813Ssimokawa struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 429106813Ssimokawa struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 430106813Ssimokawa 431106813Ssimokawa if (DEV_FWMEM(dev)) 432106813Ssimokawa return fwmem_ioctl(dev, cmd, data, flag, td); 433106813Ssimokawa 434106813Ssimokawa if (!data) 435106813Ssimokawa return(EINVAL); 436106813Ssimokawa 437118293Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 438118293Ssimokawa fc = sc->fc; 439118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 440118293Ssimokawa ir = d->ir; 441118293Ssimokawa it = d->it; 442118293Ssimokawa 443106813Ssimokawa switch (cmd) { 444106813Ssimokawa case FW_STSTREAM: 445118293Ssimokawa if (it == NULL) { 446118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 447118293Ssimokawa it = fc->it[i]; 448118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 449118293Ssimokawa break; 450118293Ssimokawa } 451118293Ssimokawa if (i >= fc->nisodma) { 452118293Ssimokawa err = EBUSY; 453118293Ssimokawa break; 454118293Ssimokawa } 455118293Ssimokawa err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 456118293Ssimokawa if (err) 457118293Ssimokawa break; 458118293Ssimokawa it->flag |= FWXFERQ_OPEN; 459118293Ssimokawa } 460118293Ssimokawa it->flag &= ~0xff; 461118293Ssimokawa it->flag |= (0x3f & ichreq->ch); 462118293Ssimokawa it->flag |= ((0x3 & ichreq->tag) << 6); 463118293Ssimokawa d->it = it; 464106813Ssimokawa err = 0; 465106813Ssimokawa break; 466106813Ssimokawa case FW_GTSTREAM: 467118293Ssimokawa if (it != NULL) { 468118293Ssimokawa ichreq->ch = it->flag & 0x3f; 469118293Ssimokawa ichreq->tag = it->flag >> 2 & 0x3; 470118293Ssimokawa err = 0; 471118293Ssimokawa } else 472118293Ssimokawa err = EINVAL; 473106813Ssimokawa break; 474106813Ssimokawa case FW_SRSTREAM: 475118293Ssimokawa if (ir == NULL) { 476118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 477118293Ssimokawa ir = fc->ir[i]; 478118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 479118293Ssimokawa break; 480118293Ssimokawa } 481118293Ssimokawa if (i >= fc->nisodma) { 482118293Ssimokawa err = EBUSY; 483118293Ssimokawa break; 484118293Ssimokawa } 485118293Ssimokawa err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 486118293Ssimokawa if (err) 487118293Ssimokawa break; 488118293Ssimokawa ir->flag |= FWXFERQ_OPEN; 489118293Ssimokawa } 490118293Ssimokawa ir->flag &= ~0xff; 491118293Ssimokawa ir->flag |= (0x3f & ichreq->ch); 492118293Ssimokawa ir->flag |= ((0x3 & ichreq->tag) << 6); 493118293Ssimokawa d->ir = ir; 494118293Ssimokawa err = fc->irx_enable(fc, ir->dmach); 495106813Ssimokawa break; 496106813Ssimokawa case FW_GRSTREAM: 497118293Ssimokawa if (d->ir != NULL) { 498118293Ssimokawa ichreq->ch = ir->flag & 0x3f; 499118293Ssimokawa ichreq->tag = ir->flag >> 2 & 0x3; 500118293Ssimokawa err = 0; 501118293Ssimokawa } else 502118293Ssimokawa err = EINVAL; 503106813Ssimokawa break; 504106813Ssimokawa case FW_SSTBUF: 505118293Ssimokawa bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 506106813Ssimokawa err = 0; 507106813Ssimokawa break; 508106813Ssimokawa case FW_GSTBUF: 509118293Ssimokawa bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 510118293Ssimokawa if (ir != NULL) { 511118293Ssimokawa ibufreq->rx.nchunk = ir->bnchunk; 512118293Ssimokawa ibufreq->rx.npacket = ir->bnpacket; 513118293Ssimokawa ibufreq->rx.psize = ir->psize; 514118293Ssimokawa } 515118293Ssimokawa bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 516118293Ssimokawa if (it != NULL) { 517118293Ssimokawa ibufreq->tx.nchunk = it->bnchunk; 518118293Ssimokawa ibufreq->tx.npacket = it->bnpacket; 519118293Ssimokawa ibufreq->tx.psize = it->psize; 520118293Ssimokawa } 521106813Ssimokawa break; 522106813Ssimokawa case FW_ASYREQ: 523113584Ssimokawa xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len, 524113584Ssimokawa PAGE_SIZE /* XXX */); 525106813Ssimokawa if(xfer == NULL){ 526106813Ssimokawa err = ENOMEM; 527106813Ssimokawa return err; 528106813Ssimokawa } 529106813Ssimokawa fp = &asyreq->pkt; 530106813Ssimokawa switch (asyreq->req.type) { 531106813Ssimokawa case FWASREQNODE: 532113584Ssimokawa xfer->dst = fp->mode.hdr.dst; 533106813Ssimokawa break; 534106813Ssimokawa case FWASREQEUI: 535110072Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, 536110582Ssimokawa &asyreq->req.dst.eui); 537106813Ssimokawa if (fwdev == NULL) { 538108782Ssimokawa device_printf(sc->fc->bdev, 539108782Ssimokawa "cannot find node\n"); 540106813Ssimokawa err = EINVAL; 541106813Ssimokawa goto error; 542106813Ssimokawa } 543113832Ssimokawa xfer->dst = FWLOCALBUS | fwdev->dst; 544113832Ssimokawa fp->mode.hdr.dst = xfer->dst; 545106813Ssimokawa break; 546106813Ssimokawa case FWASRESTL: 547106813Ssimokawa /* XXX what's this? */ 548106813Ssimokawa break; 549106813Ssimokawa case FWASREQSTREAM: 550106813Ssimokawa /* nothing to do */ 551106813Ssimokawa break; 552106813Ssimokawa } 553106813Ssimokawa xfer->spd = asyreq->req.sped; 554106813Ssimokawa bcopy(fp, xfer->send.buf, xfer->send.len); 555106813Ssimokawa xfer->act.hand = fw_asy_callback; 556118293Ssimokawa err = fw_asyreq(sc->fc, -1, xfer); 557106813Ssimokawa if(err){ 558106813Ssimokawa fw_xfer_free( xfer); 559106813Ssimokawa return err; 560106813Ssimokawa } 561111748Sdes err = tsleep(xfer, FWPRI, "asyreq", hz); 562106813Ssimokawa if(err == 0){ 563106813Ssimokawa if(asyreq->req.len >= xfer->recv.len){ 564106813Ssimokawa asyreq->req.len = xfer->recv.len; 565106813Ssimokawa }else{ 566106813Ssimokawa err = EINVAL; 567106813Ssimokawa } 568113584Ssimokawa bcopy(xfer->recv.buf, fp, asyreq->req.len); 569106813Ssimokawa } 570106813Ssimokawaerror: 571106813Ssimokawa fw_xfer_free( xfer); 572106813Ssimokawa break; 573106813Ssimokawa case FW_IBUSRST: 574106813Ssimokawa sc->fc->ibr(sc->fc); 575106813Ssimokawa break; 576106813Ssimokawa case FW_CBINDADDR: 577106813Ssimokawa fwb = fw_bindlookup(sc->fc, 578106813Ssimokawa bindreq->start.hi, bindreq->start.lo); 579106813Ssimokawa if(fwb == NULL){ 580106813Ssimokawa err = EINVAL; 581106813Ssimokawa break; 582106813Ssimokawa } 583106813Ssimokawa STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); 584118293Ssimokawa STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist); 585110195Ssimokawa free(fwb, M_FW); 586106813Ssimokawa break; 587106813Ssimokawa case FW_SBINDADDR: 588106813Ssimokawa if(bindreq->len <= 0 ){ 589106813Ssimokawa err = EINVAL; 590106813Ssimokawa break; 591106813Ssimokawa } 592106813Ssimokawa if(bindreq->start.hi > 0xffff ){ 593106813Ssimokawa err = EINVAL; 594106813Ssimokawa break; 595106813Ssimokawa } 596110195Ssimokawa fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); 597106813Ssimokawa if(fwb == NULL){ 598106813Ssimokawa err = ENOMEM; 599106813Ssimokawa break; 600106813Ssimokawa } 601106813Ssimokawa fwb->start_hi = bindreq->start.hi; 602106813Ssimokawa fwb->start_lo = bindreq->start.lo; 603106813Ssimokawa fwb->addrlen = bindreq->len; 604118293Ssimokawa /* XXX */ 605118293Ssimokawa fwb->sub = ir->dmach; 606113584Ssimokawa fwb->act_type = FWACT_CH; 607106813Ssimokawa 608110269Ssimokawa xfer = fw_xfer_alloc(M_FWXFER); 609106813Ssimokawa if(xfer == NULL){ 610106813Ssimokawa err = ENOMEM; 611106813Ssimokawa return err; 612106813Ssimokawa } 613106813Ssimokawa xfer->fc = sc->fc; 614106813Ssimokawa 615113584Ssimokawa s = splfw(); 616113584Ssimokawa /* XXX broken. need multiple xfer */ 617113584Ssimokawa STAILQ_INIT(&fwb->xferlist); 618113584Ssimokawa STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 619113584Ssimokawa splx(s); 620106813Ssimokawa err = fw_bindadd(sc->fc, fwb); 621106813Ssimokawa break; 622106813Ssimokawa case FW_GDEVLST: 623109814Ssimokawa i = len = 1; 624109814Ssimokawa /* myself */ 625109814Ssimokawa devinfo = &fwdevlst->dev[0]; 626109814Ssimokawa devinfo->dst = sc->fc->nodeid; 627109814Ssimokawa devinfo->status = 0; /* XXX */ 628109814Ssimokawa devinfo->eui.hi = sc->fc->eui.hi; 629109814Ssimokawa devinfo->eui.lo = sc->fc->eui.lo; 630110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) { 631109814Ssimokawa if(len < FW_MAX_DEVLST){ 632109814Ssimokawa devinfo = &fwdevlst->dev[len++]; 633109814Ssimokawa devinfo->dst = fwdev->dst; 634109814Ssimokawa devinfo->status = 635109814Ssimokawa (fwdev->status == FWDEVINVAL)?0:1; 636109814Ssimokawa devinfo->eui.hi = fwdev->eui.hi; 637109814Ssimokawa devinfo->eui.lo = fwdev->eui.lo; 638106813Ssimokawa } 639106813Ssimokawa i++; 640106813Ssimokawa } 641106813Ssimokawa fwdevlst->n = i; 642109814Ssimokawa fwdevlst->info_len = len; 643106813Ssimokawa break; 644106813Ssimokawa case FW_GTPMAP: 645106813Ssimokawa bcopy(sc->fc->topology_map, data, 646106813Ssimokawa (sc->fc->topology_map->crc_len + 1) * 4); 647106813Ssimokawa break; 648106813Ssimokawa case FW_GCROM: 649110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) 650110193Ssimokawa if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 651106813Ssimokawa break; 652106813Ssimokawa if (fwdev == NULL) { 653117473Ssimokawa if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) { 654117473Ssimokawa err = FWNODE_INVAL; 655117473Ssimokawa break; 656117473Ssimokawa } 657117473Ssimokawa /* myself */ 658117473Ssimokawa ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 659117473Ssimokawa len = CROMSIZE; 660117473Ssimokawa for (i = 0; i < CROMSIZE/4; i++) 661117473Ssimokawa ((u_int32_t *)ptr)[i] 662117473Ssimokawa = ntohl(sc->fc->config_rom[i]); 663117473Ssimokawa } else { 664117473Ssimokawa /* found */ 665117473Ssimokawa ptr = (void *)&fwdev->csrrom[0]; 666117473Ssimokawa if (fwdev->rommax < CSRROMOFF) 667117473Ssimokawa len = 0; 668117473Ssimokawa else 669117473Ssimokawa len = fwdev->rommax - CSRROMOFF + 4; 670106813Ssimokawa } 671106813Ssimokawa if (crom_buf->len < len) 672106813Ssimokawa len = crom_buf->len; 673106813Ssimokawa else 674106813Ssimokawa crom_buf->len = len; 675117473Ssimokawa err = copyout(ptr, crom_buf->ptr, len); 676117473Ssimokawa if (fwdev == NULL) 677117473Ssimokawa /* myself */ 678117473Ssimokawa free(ptr, M_FW); 679106813Ssimokawa break; 680106813Ssimokawa default: 681106813Ssimokawa sc->fc->ioctl (dev, cmd, data, flag, td); 682106813Ssimokawa break; 683106813Ssimokawa } 684106813Ssimokawa return err; 685106813Ssimokawa} 686106813Ssimokawaint 687106813Ssimokawafw_poll(dev_t dev, int events, fw_proc *td) 688106813Ssimokawa{ 689118293Ssimokawa struct firewire_softc *sc; 690118293Ssimokawa struct fw_xferq *ir; 691106813Ssimokawa int revents; 692106813Ssimokawa int tmp; 693106813Ssimokawa int unit = DEV2UNIT(dev); 694106813Ssimokawa 695106813Ssimokawa if (DEV_FWMEM(dev)) 696106813Ssimokawa return fwmem_poll(dev, events, td); 697106813Ssimokawa 698106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 699118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 700106813Ssimokawa revents = 0; 701106813Ssimokawa tmp = POLLIN | POLLRDNORM; 702106813Ssimokawa if (events & tmp) { 703118293Ssimokawa if (STAILQ_FIRST(&ir->q) != NULL) 704106813Ssimokawa revents |= tmp; 705106813Ssimokawa else 706118293Ssimokawa selrecord(td, &ir->rsel); 707106813Ssimokawa } 708106813Ssimokawa tmp = POLLOUT | POLLWRNORM; 709106813Ssimokawa if (events & tmp) { 710106813Ssimokawa /* XXX should be fixed */ 711106813Ssimokawa revents |= tmp; 712106813Ssimokawa } 713106813Ssimokawa 714106813Ssimokawa return revents; 715106813Ssimokawa} 716106813Ssimokawa 717106813Ssimokawastatic int 718113584Ssimokawa#if __FreeBSD_version < 500102 719111615Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, int nproto) 720111615Ssimokawa#else 721113584Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) 722111615Ssimokawa#endif 723106813Ssimokawa{ 724118455Ssimokawa struct firewire_softc *sc; 725106813Ssimokawa int unit = DEV2UNIT(dev); 726106813Ssimokawa 727106813Ssimokawa if (DEV_FWMEM(dev)) 728113584Ssimokawa#if __FreeBSD_version < 500102 729111615Ssimokawa return fwmem_mmap(dev, offset, nproto); 730111615Ssimokawa#else 731111462Smux return fwmem_mmap(dev, offset, paddr, nproto); 732111615Ssimokawa#endif 733106813Ssimokawa 734118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 735106813Ssimokawa 736106813Ssimokawa return EINVAL; 737106813Ssimokawa} 738118455Ssimokawa 739118455Ssimokawaint 740118455Ssimokawafwdev_makedev(struct firewire_softc *sc) 741118455Ssimokawa{ 742118455Ssimokawa int err = 0; 743118455Ssimokawa 744118455Ssimokawa#if __FreeBSD_version >= 500000 745118455Ssimokawa dev_t d; 746118455Ssimokawa int unit; 747118455Ssimokawa 748118455Ssimokawa unit = device_get_unit(sc->fc->bdev); 749118455Ssimokawa sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 750118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 751118455Ssimokawa "fw%d.%d", unit, 0); 752118455Ssimokawa d = make_dev(&firewire_cdevsw, 753118455Ssimokawa MAKEMINOR(FWMEM_FLAG, unit, 0), 754118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 755118455Ssimokawa "fwmem%d.%d", unit, 0); 756118455Ssimokawa dev_depends(sc->dev, d); 757118455Ssimokawa make_dev_alias(sc->dev, "fw%d", unit); 758118455Ssimokawa make_dev_alias(d, "fwmem%d", unit); 759118455Ssimokawa#else 760118455Ssimokawa cdevsw_add(&firewire_cdevsw); 761118455Ssimokawa#endif 762118455Ssimokawa 763118455Ssimokawa return (err); 764118455Ssimokawa} 765118455Ssimokawa 766118455Ssimokawaint 767118455Ssimokawafwdev_destroydev(struct firewire_softc *sc) 768118455Ssimokawa{ 769118455Ssimokawa int err = 0; 770118455Ssimokawa 771118455Ssimokawa#if __FreeBSD_version >= 500000 772118455Ssimokawa destroy_dev(sc->dev); 773118455Ssimokawa#else 774118455Ssimokawa cdevsw_remove(&firewire_cdevsw); 775118455Ssimokawa#endif 776118455Ssimokawa return (err); 777118455Ssimokawa} 778118455Ssimokawa 779118455Ssimokawa#if __FreeBSD_version >= 500000 780118455Ssimokawa#define NDEVTYPE 2 781118455Ssimokawavoid 782118455Ssimokawafwdev_clone(void *arg, char *name, int namelen, dev_t *dev) 783118455Ssimokawa{ 784118455Ssimokawa struct firewire_softc *sc; 785118455Ssimokawa char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 786118455Ssimokawa char *subp = NULL; 787118455Ssimokawa int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 788118455Ssimokawa int i, unit = 0, sub = 0; 789118455Ssimokawa 790118455Ssimokawa if (*dev != NODEV) 791118455Ssimokawa return; 792118455Ssimokawa 793118455Ssimokawa for (i = 0; i < NDEVTYPE; i++) 794120624Ssimokawa if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 795118455Ssimokawa goto found; 796118455Ssimokawa /* not match */ 797118455Ssimokawa return; 798118455Ssimokawafound: 799118455Ssimokawa 800118455Ssimokawa if (subp == NULL || *subp++ != '.') 801118455Ssimokawa return; 802118455Ssimokawa 803118455Ssimokawa /* /dev/fwU.S */ 804118455Ssimokawa while (isdigit(*subp)) { 805118455Ssimokawa sub *= 10; 806118455Ssimokawa sub += *subp++ - '0'; 807118455Ssimokawa } 808118455Ssimokawa if (*subp != '\0') 809118455Ssimokawa return; 810118455Ssimokawa 811118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 812118455Ssimokawa if (sc == NULL) 813118455Ssimokawa return; 814118455Ssimokawa *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), 815118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 816118455Ssimokawa "%s%d.%d", devnames[i], unit, sub); 817118455Ssimokawa (*dev)->si_flags |= SI_CHEAPCLONE; 818118455Ssimokawa dev_depends(sc->dev, *dev); 819118455Ssimokawa return; 820118455Ssimokawa} 821118455Ssimokawa#endif 822