fwdev.c revision 129585
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 129585 2004-05-22 16:14:17Z dfr $ 35106813Ssimokawa * 36106813Ssimokawa */ 37106813Ssimokawa 38106813Ssimokawa#include <sys/param.h> 39106813Ssimokawa#include <sys/systm.h> 40106813Ssimokawa#include <sys/types.h> 41106813Ssimokawa#include <sys/mbuf.h> 42127468Ssimokawa#if defined(__DragonFly__) || __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 59127468Ssimokawa#ifdef __DragonFly__ 60127468Ssimokawa#include "firewire.h" 61127468Ssimokawa#include "firewirereg.h" 62127468Ssimokawa#include "fwdma.h" 63127468Ssimokawa#include "fwmem.h" 64127468Ssimokawa#include "iec68113.h" 65127468Ssimokawa#else 66106813Ssimokawa#include <dev/firewire/firewire.h> 67106813Ssimokawa#include <dev/firewire/firewirereg.h> 68113584Ssimokawa#include <dev/firewire/fwdma.h> 69106813Ssimokawa#include <dev/firewire/fwmem.h> 70109282Ssimokawa#include <dev/firewire/iec68113.h> 71127468Ssimokawa#endif 72106813Ssimokawa 73106813Ssimokawa#define FWNODE_INVAL 0xffff 74106813Ssimokawa 75106813Ssimokawastatic d_open_t fw_open; 76106813Ssimokawastatic d_close_t fw_close; 77106813Ssimokawastatic d_ioctl_t fw_ioctl; 78106813Ssimokawastatic d_poll_t fw_poll; 79106813Ssimokawastatic d_read_t fw_read; /* for Isochronous packet */ 80106813Ssimokawastatic d_write_t fw_write; 81106813Ssimokawastatic d_mmap_t fw_mmap; 82120660Ssimokawastatic d_strategy_t fw_strategy; 83106813Ssimokawa 84126080Sphkstruct cdevsw firewire_cdevsw = { 85127468Ssimokawa#ifdef __DragonFly__ 86127468Ssimokawa#define CDEV_MAJOR 127 87127468Ssimokawa "fw", CDEV_MAJOR, D_MEM, NULL, 0, 88127468Ssimokawa fw_open, fw_close, fw_read, fw_write, fw_ioctl, 89127468Ssimokawa fw_poll, fw_mmap, fw_strategy, nodump, nopsize, 90127468Ssimokawa#elif __FreeBSD_version >= 500104 91126080Sphk .d_version = D_VERSION, 92111815Sphk .d_open = fw_open, 93111815Sphk .d_close = fw_close, 94111815Sphk .d_read = fw_read, 95111815Sphk .d_write = fw_write, 96111815Sphk .d_ioctl = fw_ioctl, 97111815Sphk .d_poll = fw_poll, 98111815Sphk .d_mmap = fw_mmap, 99120660Ssimokawa .d_strategy = fw_strategy, 100111815Sphk .d_name = "fw", 101126080Sphk .d_flags = D_MEM | D_NEEDGIANT 102111942Ssimokawa#else 103127468Ssimokawa#define CDEV_MAJOR 127 104111942Ssimokawa fw_open, fw_close, fw_read, fw_write, fw_ioctl, 105120660Ssimokawa fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR, 106118455Ssimokawa nodump, nopsize, D_MEM, -1 107111942Ssimokawa#endif 108106813Ssimokawa}; 109106813Ssimokawa 110118293Ssimokawastruct fw_drv1 { 111118293Ssimokawa struct fw_xferq *ir; 112118293Ssimokawa struct fw_xferq *it; 113118293Ssimokawa struct fw_isobufreq bufreq; 114118293Ssimokawa}; 115118293Ssimokawa 116106813Ssimokawastatic int 117118293Ssimokawafwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q, 118118293Ssimokawa struct fw_bufspec *b) 119118293Ssimokawa{ 120118293Ssimokawa int i; 121118293Ssimokawa 122118293Ssimokawa if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) 123118293Ssimokawa return(EBUSY); 124118293Ssimokawa 125118293Ssimokawa q->bulkxfer = (struct fw_bulkxfer *) malloc( 126118293Ssimokawa sizeof(struct fw_bulkxfer) * b->nchunk, 127118293Ssimokawa M_FW, M_WAITOK); 128118293Ssimokawa if (q->bulkxfer == NULL) 129118293Ssimokawa return(ENOMEM); 130118293Ssimokawa 131129585Sdfr b->psize = roundup2(b->psize, sizeof(uint32_t)); 132129585Sdfr q->buf = fwdma_malloc_multiseg(fc, sizeof(uint32_t), 133118293Ssimokawa b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK); 134118293Ssimokawa 135118293Ssimokawa if (q->buf == NULL) { 136118293Ssimokawa free(q->bulkxfer, M_FW); 137118293Ssimokawa q->bulkxfer = NULL; 138118293Ssimokawa return(ENOMEM); 139118293Ssimokawa } 140118293Ssimokawa q->bnchunk = b->nchunk; 141118293Ssimokawa q->bnpacket = b->npacket; 142118293Ssimokawa q->psize = (b->psize + 3) & ~3; 143118293Ssimokawa q->queued = 0; 144118293Ssimokawa 145118293Ssimokawa STAILQ_INIT(&q->stvalid); 146118293Ssimokawa STAILQ_INIT(&q->stfree); 147118293Ssimokawa STAILQ_INIT(&q->stdma); 148118293Ssimokawa q->stproc = NULL; 149118293Ssimokawa 150118293Ssimokawa for(i = 0 ; i < q->bnchunk; i++){ 151118293Ssimokawa q->bulkxfer[i].poffset = i * q->bnpacket; 152118293Ssimokawa q->bulkxfer[i].mbuf = NULL; 153118293Ssimokawa STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); 154118293Ssimokawa } 155118293Ssimokawa 156118293Ssimokawa q->flag &= ~FWXFERQ_MODEMASK; 157118293Ssimokawa q->flag |= FWXFERQ_STREAM; 158118293Ssimokawa q->flag |= FWXFERQ_EXTBUF; 159118293Ssimokawa 160118293Ssimokawa return (0); 161118293Ssimokawa} 162118293Ssimokawa 163118293Ssimokawastatic int 164118293Ssimokawafwdev_freebuf(struct fw_xferq *q) 165118293Ssimokawa{ 166118293Ssimokawa if (q->flag & FWXFERQ_EXTBUF) { 167118293Ssimokawa if (q->buf != NULL) 168118293Ssimokawa fwdma_free_multiseg(q->buf); 169118293Ssimokawa q->buf = NULL; 170118293Ssimokawa free(q->bulkxfer, M_FW); 171118293Ssimokawa q->bulkxfer = NULL; 172118293Ssimokawa q->flag &= ~FWXFERQ_EXTBUF; 173118293Ssimokawa q->psize = 0; 174118293Ssimokawa q->maxq = FWMAXQUEUE; 175118293Ssimokawa } 176118293Ssimokawa return (0); 177118293Ssimokawa} 178118293Ssimokawa 179118293Ssimokawa 180118293Ssimokawastatic int 181106813Ssimokawafw_open (dev_t dev, int flags, int fmt, fw_proc *td) 182106813Ssimokawa{ 183106813Ssimokawa int err = 0; 184106813Ssimokawa 185122227Ssimokawa if (DEV_FWMEM(dev)) 186122227Ssimokawa return fwmem_open(dev, flags, fmt, td); 187122227Ssimokawa 188118293Ssimokawa if (dev->si_drv1 != NULL) 189118293Ssimokawa return (EBUSY); 190118293Ssimokawa 191127468Ssimokawa#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 192118455Ssimokawa if ((dev->si_flags & SI_NAMED) == 0) { 193118455Ssimokawa int unit = DEV2UNIT(dev); 194118455Ssimokawa int sub = DEV2SUB(dev); 195118455Ssimokawa 196118293Ssimokawa make_dev(&firewire_cdevsw, minor(dev), 197118293Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 198118293Ssimokawa "fw%d.%d", unit, sub); 199118455Ssimokawa } 200118455Ssimokawa#endif 201118293Ssimokawa 202118293Ssimokawa dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO); 203118293Ssimokawa 204106813Ssimokawa return err; 205106813Ssimokawa} 206106813Ssimokawa 207106813Ssimokawastatic int 208106813Ssimokawafw_close (dev_t dev, int flags, int fmt, fw_proc *td) 209106813Ssimokawa{ 210106813Ssimokawa struct firewire_softc *sc; 211118293Ssimokawa struct firewire_comm *fc; 212118293Ssimokawa struct fw_drv1 *d; 213106813Ssimokawa int unit = DEV2UNIT(dev); 214106813Ssimokawa struct fw_xfer *xfer; 215106813Ssimokawa struct fw_bind *fwb; 216106813Ssimokawa int err = 0; 217106813Ssimokawa 218106813Ssimokawa if (DEV_FWMEM(dev)) 219106813Ssimokawa return fwmem_close(dev, flags, fmt, td); 220106813Ssimokawa 221106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 222118293Ssimokawa fc = sc->fc; 223118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 224118293Ssimokawa 225118293Ssimokawa if (d->ir != NULL) { 226118293Ssimokawa struct fw_xferq *ir = d->ir; 227118293Ssimokawa 228118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 229118293Ssimokawa return (EINVAL); 230118293Ssimokawa if (ir->flag & FWXFERQ_RUNNING) { 231118293Ssimokawa ir->flag &= ~FWXFERQ_RUNNING; 232118293Ssimokawa fc->irx_disable(fc, ir->dmach); 233118293Ssimokawa } 234118293Ssimokawa /* free extbuf */ 235118293Ssimokawa fwdev_freebuf(ir); 236118293Ssimokawa /* drain receiving buffer */ 237118293Ssimokawa for (xfer = STAILQ_FIRST(&ir->q); 238118293Ssimokawa xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { 239118293Ssimokawa ir->queued --; 240118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 241118293Ssimokawa 242118293Ssimokawa xfer->resp = 0; 243118293Ssimokawa fw_xfer_done(xfer); 244118293Ssimokawa } 245118293Ssimokawa /* remove binding */ 246118293Ssimokawa for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL; 247118293Ssimokawa fwb = STAILQ_FIRST(&ir->binds)) { 248118293Ssimokawa STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist); 249118293Ssimokawa STAILQ_REMOVE_HEAD(&ir->binds, chlist); 250118293Ssimokawa free(fwb, M_FW); 251118293Ssimokawa } 252118293Ssimokawa ir->flag &= ~(FWXFERQ_OPEN | 253118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 254118293Ssimokawa d->ir = NULL; 255118293Ssimokawa 256106813Ssimokawa } 257118293Ssimokawa if (d->it != NULL) { 258118293Ssimokawa struct fw_xferq *it = d->it; 259106813Ssimokawa 260118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 261118293Ssimokawa return (EINVAL); 262118293Ssimokawa if (it->flag & FWXFERQ_RUNNING) { 263118293Ssimokawa it->flag &= ~FWXFERQ_RUNNING; 264118293Ssimokawa fc->itx_disable(fc, it->dmach); 265118293Ssimokawa } 266118293Ssimokawa /* free extbuf */ 267118293Ssimokawa fwdev_freebuf(it); 268118293Ssimokawa it->flag &= ~(FWXFERQ_OPEN | 269118293Ssimokawa FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); 270118293Ssimokawa d->it = NULL; 271106813Ssimokawa } 272118293Ssimokawa free(dev->si_drv1, M_FW); 273118293Ssimokawa dev->si_drv1 = NULL; 274106813Ssimokawa 275106813Ssimokawa return err; 276106813Ssimokawa} 277106813Ssimokawa 278106813Ssimokawa/* 279106813Ssimokawa * read request. 280106813Ssimokawa */ 281106813Ssimokawastatic int 282106813Ssimokawafw_read (dev_t dev, struct uio *uio, int ioflag) 283106813Ssimokawa{ 284106813Ssimokawa struct firewire_softc *sc; 285106813Ssimokawa struct fw_xferq *ir; 286106813Ssimokawa struct fw_xfer *xfer; 287106813Ssimokawa int err = 0, s, slept = 0; 288106813Ssimokawa int unit = DEV2UNIT(dev); 289106813Ssimokawa struct fw_pkt *fp; 290106813Ssimokawa 291106813Ssimokawa if (DEV_FWMEM(dev)) 292120660Ssimokawa return physio(dev, uio, ioflag); 293106813Ssimokawa 294106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 295106813Ssimokawa 296118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 297118293Ssimokawa if (ir == NULL || ir->buf == NULL) 298118293Ssimokawa return (EIO); 299106813Ssimokawa 300106813Ssimokawareadloop: 301106813Ssimokawa xfer = STAILQ_FIRST(&ir->q); 302113584Ssimokawa if (ir->stproc == NULL) { 303109988Ssimokawa /* iso bulkxfer */ 304106813Ssimokawa ir->stproc = STAILQ_FIRST(&ir->stvalid); 305109988Ssimokawa if (ir->stproc != NULL) { 306106813Ssimokawa s = splfw(); 307106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->stvalid, link); 308106813Ssimokawa splx(s); 309106813Ssimokawa ir->queued = 0; 310106813Ssimokawa } 311106813Ssimokawa } 312109988Ssimokawa if (xfer == NULL && ir->stproc == NULL) { 313109988Ssimokawa /* no data avaliable */ 314109988Ssimokawa if (slept == 0) { 315106813Ssimokawa slept = 1; 316106813Ssimokawa ir->flag |= FWXFERQ_WAKEUP; 317111748Sdes err = tsleep(ir, FWPRI, "fw_read", hz); 318109988Ssimokawa ir->flag &= ~FWXFERQ_WAKEUP; 319109988Ssimokawa if (err == 0) 320109988Ssimokawa goto readloop; 321109988Ssimokawa } else if (slept == 1) 322106813Ssimokawa err = EIO; 323109988Ssimokawa return err; 324109988Ssimokawa } else if(xfer != NULL) { 325120660Ssimokawa#if 0 /* XXX broken */ 326113584Ssimokawa /* per packet mode or FWACT_CH bind?*/ 327106813Ssimokawa s = splfw(); 328106813Ssimokawa ir->queued --; 329106813Ssimokawa STAILQ_REMOVE_HEAD(&ir->q, link); 330106813Ssimokawa splx(s); 331120660Ssimokawa fp = &xfer->recv.hdr; 332120660Ssimokawa if (sc->fc->irx_post != NULL) 333106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 334120660Ssimokawa err = uiomove((void *)fp, 1 /* XXX header size */, uio); 335120660Ssimokawa /* XXX copy payload too */ 336113584Ssimokawa /* XXX we should recycle this xfer */ 337120660Ssimokawa#endif 338106813Ssimokawa fw_xfer_free( xfer); 339109988Ssimokawa } else if(ir->stproc != NULL) { 340109988Ssimokawa /* iso bulkxfer */ 341113584Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 342113584Ssimokawa ir->stproc->poffset + ir->queued); 343106813Ssimokawa if(sc->fc->irx_post != NULL) 344106813Ssimokawa sc->fc->irx_post(sc->fc, fp->mode.ld); 345113584Ssimokawa if(fp->mode.stream.len == 0){ 346106813Ssimokawa err = EIO; 347106813Ssimokawa return err; 348106813Ssimokawa } 349109988Ssimokawa err = uiomove((caddr_t)fp, 350129585Sdfr fp->mode.stream.len + sizeof(uint32_t), uio); 351106813Ssimokawa ir->queued ++; 352106813Ssimokawa if(ir->queued >= ir->bnpacket){ 353106813Ssimokawa s = splfw(); 354106813Ssimokawa STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 355106813Ssimokawa splx(s); 356118293Ssimokawa sc->fc->irx_enable(sc->fc, ir->dmach); 357106813Ssimokawa ir->stproc = NULL; 358106813Ssimokawa } 359109988Ssimokawa if (uio->uio_resid >= ir->psize) { 360109988Ssimokawa slept = -1; 361109988Ssimokawa goto readloop; 362109988Ssimokawa } 363106813Ssimokawa } 364106813Ssimokawa return err; 365106813Ssimokawa} 366106813Ssimokawa 367106813Ssimokawastatic int 368106813Ssimokawafw_write (dev_t dev, struct uio *uio, int ioflag) 369106813Ssimokawa{ 370106813Ssimokawa int err = 0; 371106813Ssimokawa struct firewire_softc *sc; 372106813Ssimokawa int unit = DEV2UNIT(dev); 373106813Ssimokawa int s, slept = 0; 374106813Ssimokawa struct fw_pkt *fp; 375106813Ssimokawa struct firewire_comm *fc; 376106813Ssimokawa struct fw_xferq *it; 377106813Ssimokawa 378106813Ssimokawa if (DEV_FWMEM(dev)) 379120660Ssimokawa return physio(dev, uio, ioflag); 380106813Ssimokawa 381106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 382106813Ssimokawa fc = sc->fc; 383118293Ssimokawa it = ((struct fw_drv1 *)dev->si_drv1)->it; 384118293Ssimokawa if (it == NULL || it->buf == NULL) 385118293Ssimokawa return (EIO); 386106813Ssimokawaisoloop: 387113802Ssimokawa if (it->stproc == NULL) { 388113802Ssimokawa it->stproc = STAILQ_FIRST(&it->stfree); 389113802Ssimokawa if (it->stproc != NULL) { 390106813Ssimokawa s = splfw(); 391113802Ssimokawa STAILQ_REMOVE_HEAD(&it->stfree, link); 392106813Ssimokawa splx(s); 393113802Ssimokawa it->queued = 0; 394113802Ssimokawa } else if (slept == 0) { 395113802Ssimokawa slept = 1; 396118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 397113802Ssimokawa if (err) 398113802Ssimokawa return err; 399113802Ssimokawa err = tsleep(it, FWPRI, "fw_write", hz); 400113802Ssimokawa if (err) 401113802Ssimokawa return err; 402109988Ssimokawa goto isoloop; 403113802Ssimokawa } else { 404113802Ssimokawa err = EIO; 405106813Ssimokawa return err; 406106813Ssimokawa } 407106813Ssimokawa } 408113802Ssimokawa fp = (struct fw_pkt *)fwdma_v_addr(it->buf, 409113802Ssimokawa it->stproc->poffset + it->queued); 410113802Ssimokawa err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); 411113802Ssimokawa err = uiomove((caddr_t)fp->mode.stream.payload, 412113802Ssimokawa fp->mode.stream.len, uio); 413113802Ssimokawa it->queued ++; 414113802Ssimokawa if (it->queued >= it->bnpacket) { 415113802Ssimokawa s = splfw(); 416113802Ssimokawa STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 417113802Ssimokawa splx(s); 418113802Ssimokawa it->stproc = NULL; 419118293Ssimokawa err = sc->fc->itx_enable(sc->fc, it->dmach); 420113802Ssimokawa } 421113802Ssimokawa if (uio->uio_resid >= sizeof(struct fw_isohdr)) { 422113802Ssimokawa slept = 0; 423113802Ssimokawa goto isoloop; 424113802Ssimokawa } 425113802Ssimokawa return err; 426106813Ssimokawa} 427106813Ssimokawa/* 428106813Ssimokawa * ioctl support. 429106813Ssimokawa */ 430106813Ssimokawaint 431106813Ssimokawafw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 432106813Ssimokawa{ 433106813Ssimokawa struct firewire_softc *sc; 434118293Ssimokawa struct firewire_comm *fc; 435118293Ssimokawa struct fw_drv1 *d; 436106813Ssimokawa int unit = DEV2UNIT(dev); 437113584Ssimokawa int s, i, len, err = 0; 438106813Ssimokawa struct fw_device *fwdev; 439106813Ssimokawa struct fw_bind *fwb; 440106813Ssimokawa struct fw_xferq *ir, *it; 441106813Ssimokawa struct fw_xfer *xfer; 442106813Ssimokawa struct fw_pkt *fp; 443109814Ssimokawa struct fw_devinfo *devinfo; 444117473Ssimokawa void *ptr; 445106813Ssimokawa 446106813Ssimokawa struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 447106813Ssimokawa struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 448106813Ssimokawa struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 449106813Ssimokawa struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 450106813Ssimokawa struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 451106813Ssimokawa struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 452106813Ssimokawa 453106813Ssimokawa if (DEV_FWMEM(dev)) 454106813Ssimokawa return fwmem_ioctl(dev, cmd, data, flag, td); 455106813Ssimokawa 456106813Ssimokawa if (!data) 457106813Ssimokawa return(EINVAL); 458106813Ssimokawa 459118293Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 460118293Ssimokawa fc = sc->fc; 461118293Ssimokawa d = (struct fw_drv1 *)dev->si_drv1; 462118293Ssimokawa ir = d->ir; 463118293Ssimokawa it = d->it; 464118293Ssimokawa 465106813Ssimokawa switch (cmd) { 466106813Ssimokawa case FW_STSTREAM: 467118293Ssimokawa if (it == NULL) { 468118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 469118293Ssimokawa it = fc->it[i]; 470118293Ssimokawa if ((it->flag & FWXFERQ_OPEN) == 0) 471118293Ssimokawa break; 472118293Ssimokawa } 473118293Ssimokawa if (i >= fc->nisodma) { 474118293Ssimokawa err = EBUSY; 475118293Ssimokawa break; 476118293Ssimokawa } 477118293Ssimokawa err = fwdev_allocbuf(fc, it, &d->bufreq.tx); 478118293Ssimokawa if (err) 479118293Ssimokawa break; 480118293Ssimokawa it->flag |= FWXFERQ_OPEN; 481118293Ssimokawa } 482118293Ssimokawa it->flag &= ~0xff; 483118293Ssimokawa it->flag |= (0x3f & ichreq->ch); 484118293Ssimokawa it->flag |= ((0x3 & ichreq->tag) << 6); 485118293Ssimokawa d->it = it; 486106813Ssimokawa break; 487106813Ssimokawa case FW_GTSTREAM: 488118293Ssimokawa if (it != NULL) { 489118293Ssimokawa ichreq->ch = it->flag & 0x3f; 490118293Ssimokawa ichreq->tag = it->flag >> 2 & 0x3; 491118293Ssimokawa } else 492118293Ssimokawa err = EINVAL; 493106813Ssimokawa break; 494106813Ssimokawa case FW_SRSTREAM: 495118293Ssimokawa if (ir == NULL) { 496118293Ssimokawa for (i = 0; i < fc->nisodma; i ++) { 497118293Ssimokawa ir = fc->ir[i]; 498118293Ssimokawa if ((ir->flag & FWXFERQ_OPEN) == 0) 499118293Ssimokawa break; 500118293Ssimokawa } 501118293Ssimokawa if (i >= fc->nisodma) { 502118293Ssimokawa err = EBUSY; 503118293Ssimokawa break; 504118293Ssimokawa } 505118293Ssimokawa err = fwdev_allocbuf(fc, ir, &d->bufreq.rx); 506118293Ssimokawa if (err) 507118293Ssimokawa break; 508118293Ssimokawa ir->flag |= FWXFERQ_OPEN; 509118293Ssimokawa } 510118293Ssimokawa ir->flag &= ~0xff; 511118293Ssimokawa ir->flag |= (0x3f & ichreq->ch); 512118293Ssimokawa ir->flag |= ((0x3 & ichreq->tag) << 6); 513118293Ssimokawa d->ir = ir; 514118293Ssimokawa err = fc->irx_enable(fc, ir->dmach); 515106813Ssimokawa break; 516106813Ssimokawa case FW_GRSTREAM: 517118293Ssimokawa if (d->ir != NULL) { 518118293Ssimokawa ichreq->ch = ir->flag & 0x3f; 519118293Ssimokawa ichreq->tag = ir->flag >> 2 & 0x3; 520118293Ssimokawa } else 521118293Ssimokawa err = EINVAL; 522106813Ssimokawa break; 523106813Ssimokawa case FW_SSTBUF: 524118293Ssimokawa bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); 525106813Ssimokawa break; 526106813Ssimokawa case FW_GSTBUF: 527118293Ssimokawa bzero(&ibufreq->rx, sizeof(ibufreq->rx)); 528118293Ssimokawa if (ir != NULL) { 529118293Ssimokawa ibufreq->rx.nchunk = ir->bnchunk; 530118293Ssimokawa ibufreq->rx.npacket = ir->bnpacket; 531118293Ssimokawa ibufreq->rx.psize = ir->psize; 532118293Ssimokawa } 533118293Ssimokawa bzero(&ibufreq->tx, sizeof(ibufreq->tx)); 534118293Ssimokawa if (it != NULL) { 535118293Ssimokawa ibufreq->tx.nchunk = it->bnchunk; 536118293Ssimokawa ibufreq->tx.npacket = it->bnpacket; 537118293Ssimokawa ibufreq->tx.psize = it->psize; 538118293Ssimokawa } 539106813Ssimokawa break; 540106813Ssimokawa case FW_ASYREQ: 541120660Ssimokawa { 542120660Ssimokawa struct tcode_info *tinfo; 543121466Ssimokawa int pay_len = 0; 544120660Ssimokawa 545106813Ssimokawa fp = &asyreq->pkt; 546121466Ssimokawa tinfo = &sc->fc->tcode[fp->mode.hdr.tcode]; 547121466Ssimokawa 548121466Ssimokawa if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) 549121466Ssimokawa pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); 550121466Ssimokawa 551121466Ssimokawa xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/); 552121466Ssimokawa if (xfer == NULL) 553121466Ssimokawa return (ENOMEM); 554121466Ssimokawa 555106813Ssimokawa switch (asyreq->req.type) { 556106813Ssimokawa case FWASREQNODE: 557106813Ssimokawa break; 558106813Ssimokawa case FWASREQEUI: 559110072Ssimokawa fwdev = fw_noderesolve_eui64(sc->fc, 560110582Ssimokawa &asyreq->req.dst.eui); 561106813Ssimokawa if (fwdev == NULL) { 562108782Ssimokawa device_printf(sc->fc->bdev, 563108782Ssimokawa "cannot find node\n"); 564106813Ssimokawa err = EINVAL; 565121466Ssimokawa goto out; 566106813Ssimokawa } 567120660Ssimokawa fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; 568106813Ssimokawa break; 569106813Ssimokawa case FWASRESTL: 570106813Ssimokawa /* XXX what's this? */ 571106813Ssimokawa break; 572106813Ssimokawa case FWASREQSTREAM: 573106813Ssimokawa /* nothing to do */ 574106813Ssimokawa break; 575106813Ssimokawa } 576121466Ssimokawa 577120660Ssimokawa bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); 578121466Ssimokawa if (pay_len > 0) 579120660Ssimokawa bcopy((char *)fp + tinfo->hdr_len, 580127347Ssimokawa (void *)xfer->send.payload, pay_len); 581121466Ssimokawa xfer->send.spd = asyreq->req.sped; 582106813Ssimokawa xfer->act.hand = fw_asy_callback; 583121466Ssimokawa 584121466Ssimokawa if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0) 585121466Ssimokawa goto out; 586121466Ssimokawa if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0) 587121466Ssimokawa goto out; 588121466Ssimokawa if (xfer->resp != 0) { 589121466Ssimokawa err = EIO; 590121466Ssimokawa goto out; 591106813Ssimokawa } 592121466Ssimokawa if ((tinfo->flag & FWTI_TLABEL) == 0) 593121466Ssimokawa goto out; 594121466Ssimokawa 595121466Ssimokawa /* copy response */ 596121466Ssimokawa tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; 597121466Ssimokawa if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) 598121466Ssimokawa asyreq->req.len = xfer->recv.pay_len; 599121466Ssimokawa else 600121466Ssimokawa err = EINVAL; 601121466Ssimokawa bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); 602121466Ssimokawa bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, 603121466Ssimokawa MAX(0, asyreq->req.len - tinfo->hdr_len)); 604121466Ssimokawaout: 605120660Ssimokawa fw_xfer_free_buf(xfer); 606106813Ssimokawa break; 607120660Ssimokawa } 608106813Ssimokawa case FW_IBUSRST: 609106813Ssimokawa sc->fc->ibr(sc->fc); 610106813Ssimokawa break; 611106813Ssimokawa case FW_CBINDADDR: 612106813Ssimokawa fwb = fw_bindlookup(sc->fc, 613106813Ssimokawa bindreq->start.hi, bindreq->start.lo); 614106813Ssimokawa if(fwb == NULL){ 615106813Ssimokawa err = EINVAL; 616106813Ssimokawa break; 617106813Ssimokawa } 618106813Ssimokawa STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); 619118293Ssimokawa STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist); 620110195Ssimokawa free(fwb, M_FW); 621106813Ssimokawa break; 622106813Ssimokawa case FW_SBINDADDR: 623106813Ssimokawa if(bindreq->len <= 0 ){ 624106813Ssimokawa err = EINVAL; 625106813Ssimokawa break; 626106813Ssimokawa } 627106813Ssimokawa if(bindreq->start.hi > 0xffff ){ 628106813Ssimokawa err = EINVAL; 629106813Ssimokawa break; 630106813Ssimokawa } 631110195Ssimokawa fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); 632106813Ssimokawa if(fwb == NULL){ 633106813Ssimokawa err = ENOMEM; 634106813Ssimokawa break; 635106813Ssimokawa } 636120660Ssimokawa fwb->start = ((u_int64_t)bindreq->start.hi << 32) | 637120660Ssimokawa bindreq->start.lo; 638120660Ssimokawa fwb->end = fwb->start + bindreq->len; 639118293Ssimokawa /* XXX */ 640118293Ssimokawa fwb->sub = ir->dmach; 641113584Ssimokawa fwb->act_type = FWACT_CH; 642106813Ssimokawa 643120660Ssimokawa /* XXX alloc buf */ 644110269Ssimokawa xfer = fw_xfer_alloc(M_FWXFER); 645106813Ssimokawa if(xfer == NULL){ 646124836Ssimokawa free(fwb, M_FW); 647124836Ssimokawa return (ENOMEM); 648106813Ssimokawa } 649106813Ssimokawa xfer->fc = sc->fc; 650106813Ssimokawa 651113584Ssimokawa s = splfw(); 652113584Ssimokawa /* XXX broken. need multiple xfer */ 653113584Ssimokawa STAILQ_INIT(&fwb->xferlist); 654113584Ssimokawa STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); 655113584Ssimokawa splx(s); 656106813Ssimokawa err = fw_bindadd(sc->fc, fwb); 657106813Ssimokawa break; 658106813Ssimokawa case FW_GDEVLST: 659109814Ssimokawa i = len = 1; 660109814Ssimokawa /* myself */ 661109814Ssimokawa devinfo = &fwdevlst->dev[0]; 662109814Ssimokawa devinfo->dst = sc->fc->nodeid; 663109814Ssimokawa devinfo->status = 0; /* XXX */ 664109814Ssimokawa devinfo->eui.hi = sc->fc->eui.hi; 665109814Ssimokawa devinfo->eui.lo = sc->fc->eui.lo; 666110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) { 667109814Ssimokawa if(len < FW_MAX_DEVLST){ 668109814Ssimokawa devinfo = &fwdevlst->dev[len++]; 669109814Ssimokawa devinfo->dst = fwdev->dst; 670109814Ssimokawa devinfo->status = 671109814Ssimokawa (fwdev->status == FWDEVINVAL)?0:1; 672109814Ssimokawa devinfo->eui.hi = fwdev->eui.hi; 673109814Ssimokawa devinfo->eui.lo = fwdev->eui.lo; 674106813Ssimokawa } 675106813Ssimokawa i++; 676106813Ssimokawa } 677106813Ssimokawa fwdevlst->n = i; 678109814Ssimokawa fwdevlst->info_len = len; 679106813Ssimokawa break; 680106813Ssimokawa case FW_GTPMAP: 681106813Ssimokawa bcopy(sc->fc->topology_map, data, 682106813Ssimokawa (sc->fc->topology_map->crc_len + 1) * 4); 683106813Ssimokawa break; 684106813Ssimokawa case FW_GCROM: 685110193Ssimokawa STAILQ_FOREACH(fwdev, &sc->fc->devices, link) 686110193Ssimokawa if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) 687106813Ssimokawa break; 688106813Ssimokawa if (fwdev == NULL) { 689117473Ssimokawa if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) { 690117473Ssimokawa err = FWNODE_INVAL; 691117473Ssimokawa break; 692117473Ssimokawa } 693117473Ssimokawa /* myself */ 694117473Ssimokawa ptr = malloc(CROMSIZE, M_FW, M_WAITOK); 695117473Ssimokawa len = CROMSIZE; 696117473Ssimokawa for (i = 0; i < CROMSIZE/4; i++) 697129585Sdfr ((uint32_t *)ptr)[i] 698117473Ssimokawa = ntohl(sc->fc->config_rom[i]); 699117473Ssimokawa } else { 700117473Ssimokawa /* found */ 701117473Ssimokawa ptr = (void *)&fwdev->csrrom[0]; 702117473Ssimokawa if (fwdev->rommax < CSRROMOFF) 703117473Ssimokawa len = 0; 704117473Ssimokawa else 705117473Ssimokawa len = fwdev->rommax - CSRROMOFF + 4; 706106813Ssimokawa } 707106813Ssimokawa if (crom_buf->len < len) 708106813Ssimokawa len = crom_buf->len; 709106813Ssimokawa else 710106813Ssimokawa crom_buf->len = len; 711117473Ssimokawa err = copyout(ptr, crom_buf->ptr, len); 712117473Ssimokawa if (fwdev == NULL) 713117473Ssimokawa /* myself */ 714117473Ssimokawa free(ptr, M_FW); 715106813Ssimokawa break; 716106813Ssimokawa default: 717106813Ssimokawa sc->fc->ioctl (dev, cmd, data, flag, td); 718106813Ssimokawa break; 719106813Ssimokawa } 720106813Ssimokawa return err; 721106813Ssimokawa} 722106813Ssimokawaint 723106813Ssimokawafw_poll(dev_t dev, int events, fw_proc *td) 724106813Ssimokawa{ 725118293Ssimokawa struct firewire_softc *sc; 726118293Ssimokawa struct fw_xferq *ir; 727106813Ssimokawa int revents; 728106813Ssimokawa int tmp; 729106813Ssimokawa int unit = DEV2UNIT(dev); 730106813Ssimokawa 731106813Ssimokawa if (DEV_FWMEM(dev)) 732106813Ssimokawa return fwmem_poll(dev, events, td); 733106813Ssimokawa 734106813Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 735118293Ssimokawa ir = ((struct fw_drv1 *)dev->si_drv1)->ir; 736106813Ssimokawa revents = 0; 737106813Ssimokawa tmp = POLLIN | POLLRDNORM; 738106813Ssimokawa if (events & tmp) { 739118293Ssimokawa if (STAILQ_FIRST(&ir->q) != NULL) 740106813Ssimokawa revents |= tmp; 741106813Ssimokawa else 742118293Ssimokawa selrecord(td, &ir->rsel); 743106813Ssimokawa } 744106813Ssimokawa tmp = POLLOUT | POLLWRNORM; 745106813Ssimokawa if (events & tmp) { 746106813Ssimokawa /* XXX should be fixed */ 747106813Ssimokawa revents |= tmp; 748106813Ssimokawa } 749106813Ssimokawa 750106813Ssimokawa return revents; 751106813Ssimokawa} 752106813Ssimokawa 753106813Ssimokawastatic int 754127468Ssimokawa#if defined(__DragonFly__) || __FreeBSD_version < 500102 755111615Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, int nproto) 756111615Ssimokawa#else 757113584Ssimokawafw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto) 758111615Ssimokawa#endif 759106813Ssimokawa{ 760118455Ssimokawa struct firewire_softc *sc; 761106813Ssimokawa int unit = DEV2UNIT(dev); 762106813Ssimokawa 763106813Ssimokawa if (DEV_FWMEM(dev)) 764127468Ssimokawa#if defined(__DragonFly__) || __FreeBSD_version < 500102 765111615Ssimokawa return fwmem_mmap(dev, offset, nproto); 766111615Ssimokawa#else 767111462Smux return fwmem_mmap(dev, offset, paddr, nproto); 768111615Ssimokawa#endif 769106813Ssimokawa 770118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 771106813Ssimokawa 772106813Ssimokawa return EINVAL; 773106813Ssimokawa} 774118455Ssimokawa 775120660Ssimokawastatic void 776120660Ssimokawafw_strategy(struct bio *bp) 777120660Ssimokawa{ 778120660Ssimokawa dev_t dev; 779120660Ssimokawa 780120660Ssimokawa dev = bp->bio_dev; 781120660Ssimokawa if (DEV_FWMEM(dev)) { 782120660Ssimokawa fwmem_strategy(bp); 783120660Ssimokawa return; 784120660Ssimokawa } 785120660Ssimokawa 786120660Ssimokawa bp->bio_error = EOPNOTSUPP; 787120660Ssimokawa bp->bio_flags |= BIO_ERROR; 788120660Ssimokawa bp->bio_resid = bp->bio_bcount; 789120660Ssimokawa biodone(bp); 790120660Ssimokawa} 791120660Ssimokawa 792118455Ssimokawaint 793118455Ssimokawafwdev_makedev(struct firewire_softc *sc) 794118455Ssimokawa{ 795118455Ssimokawa int err = 0; 796118455Ssimokawa 797127468Ssimokawa#if defined(__DragonFly__) || __FreeBSD_version < 500000 798127468Ssimokawa cdevsw_add(&firewire_cdevsw); 799127468Ssimokawa#else 800118455Ssimokawa dev_t d; 801118455Ssimokawa int unit; 802118455Ssimokawa 803118455Ssimokawa unit = device_get_unit(sc->fc->bdev); 804118455Ssimokawa sc->dev = make_dev(&firewire_cdevsw, MAKEMINOR(0, unit, 0), 805118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 806118455Ssimokawa "fw%d.%d", unit, 0); 807118455Ssimokawa d = make_dev(&firewire_cdevsw, 808118455Ssimokawa MAKEMINOR(FWMEM_FLAG, unit, 0), 809118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 810118455Ssimokawa "fwmem%d.%d", unit, 0); 811118455Ssimokawa dev_depends(sc->dev, d); 812118455Ssimokawa make_dev_alias(sc->dev, "fw%d", unit); 813118455Ssimokawa make_dev_alias(d, "fwmem%d", unit); 814118455Ssimokawa#endif 815118455Ssimokawa 816118455Ssimokawa return (err); 817118455Ssimokawa} 818118455Ssimokawa 819118455Ssimokawaint 820118455Ssimokawafwdev_destroydev(struct firewire_softc *sc) 821118455Ssimokawa{ 822118455Ssimokawa int err = 0; 823118455Ssimokawa 824127468Ssimokawa#if defined(__DragonFly__) || __FreeBSD_version < 500000 825127468Ssimokawa cdevsw_remove(&firewire_cdevsw); 826127468Ssimokawa#else 827118455Ssimokawa destroy_dev(sc->dev); 828118455Ssimokawa#endif 829118455Ssimokawa return (err); 830118455Ssimokawa} 831118455Ssimokawa 832127468Ssimokawa#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 833118455Ssimokawa#define NDEVTYPE 2 834118455Ssimokawavoid 835118455Ssimokawafwdev_clone(void *arg, char *name, int namelen, dev_t *dev) 836118455Ssimokawa{ 837118455Ssimokawa struct firewire_softc *sc; 838118455Ssimokawa char *devnames[NDEVTYPE] = {"fw", "fwmem"}; 839118455Ssimokawa char *subp = NULL; 840118455Ssimokawa int devflag[NDEVTYPE] = {0, FWMEM_FLAG}; 841118455Ssimokawa int i, unit = 0, sub = 0; 842118455Ssimokawa 843118455Ssimokawa if (*dev != NODEV) 844118455Ssimokawa return; 845118455Ssimokawa 846118455Ssimokawa for (i = 0; i < NDEVTYPE; i++) 847120624Ssimokawa if (dev_stdclone(name, &subp, devnames[i], &unit) == 2) 848118455Ssimokawa goto found; 849118455Ssimokawa /* not match */ 850118455Ssimokawa return; 851118455Ssimokawafound: 852118455Ssimokawa 853118455Ssimokawa if (subp == NULL || *subp++ != '.') 854118455Ssimokawa return; 855118455Ssimokawa 856118455Ssimokawa /* /dev/fwU.S */ 857118455Ssimokawa while (isdigit(*subp)) { 858118455Ssimokawa sub *= 10; 859118455Ssimokawa sub += *subp++ - '0'; 860118455Ssimokawa } 861118455Ssimokawa if (*subp != '\0') 862118455Ssimokawa return; 863118455Ssimokawa 864118455Ssimokawa sc = devclass_get_softc(firewire_devclass, unit); 865118455Ssimokawa if (sc == NULL) 866118455Ssimokawa return; 867118455Ssimokawa *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub), 868118455Ssimokawa UID_ROOT, GID_OPERATOR, 0660, 869118455Ssimokawa "%s%d.%d", devnames[i], unit, sub); 870118455Ssimokawa (*dev)->si_flags |= SI_CHEAPCLONE; 871118455Ssimokawa dev_depends(sc->dev, *dev); 872118455Ssimokawa return; 873118455Ssimokawa} 874118455Ssimokawa#endif 875