fwdev.c revision 109424
140939Sdes/* 2135546Sdes * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa 340939Sdes * All rights reserved. 440939Sdes * 540939Sdes * Redistribution and use in source and binary forms, with or without 640939Sdes * modification, are permitted provided that the following conditions 740939Sdes * are met: 840939Sdes * 1. Redistributions of source code must retain the above copyright 940939Sdes * notice, this list of conditions and the following disclaimer. 1040939Sdes * 2. Redistributions in binary form must reproduce the above copyright 1140939Sdes * notice, this list of conditions and the following disclaimer in the 1240939Sdes * documentation and/or other materials provided with the distribution. 1340939Sdes * 3. All advertising materials mentioning features or use of this software 1440939Sdes * must display the acknowledgement as bellow: 1540939Sdes * 1640939Sdes * This product includes software developed by K. Kobayashi and H. Shimokawa 1740939Sdes * 1840939Sdes * 4. The name of the author may not be used to endorse or promote products 1940939Sdes * derived from this software without specific prior written permission. 2040939Sdes * 2140939Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2240939Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 2340939Sdes * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 2440939Sdes * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 2540939Sdes * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 2640939Sdes * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2740939Sdes * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2840939Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2984203Sdillon * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 3084203Sdillon * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 3184203Sdillon * POSSIBILITY OF SUCH DAMAGE. 3241862Sdes * 3340939Sdes * $FreeBSD: head/sys/dev/firewire/fwdev.c 109424 2003-01-17 15:15:21Z simokawa $ 3455557Sdes * 3562981Sdes */ 36174752Sdes 3740939Sdes#include <sys/param.h> 3840939Sdes#include <sys/systm.h> 39174752Sdes#include <sys/types.h> 4040939Sdes#include <sys/mbuf.h> 4140939Sdes 42109695Sdes#include <sys/kernel.h> 4360924Sdes#include <sys/malloc.h> 4441862Sdes#include <sys/conf.h> 4541862Sdes#include <sys/uio.h> 4640939Sdes#include <sys/poll.h> 4740939Sdes 4840939Sdes#include <sys/bus.h> 4940939Sdes 5040939Sdes#include <sys/ioccom.h> 5140939Sdes 5240975Sdes#include <dev/firewire/firewire.h> 5340939Sdes#include <dev/firewire/firewirereg.h> 5440939Sdes#include <dev/firewire/fwmem.h> 5540939Sdes#include <dev/firewire/iec68113.h> 5640939Sdes 5740939Sdes#define CDEV_MAJOR 127 58174588Sdes#define FWNODE_INVAL 0xffff 59121423Sume 6090267Sdesstatic d_open_t fw_open; 61121423Sumestatic d_close_t fw_close; 6290267Sdesstatic d_ioctl_t fw_ioctl; 6390267Sdesstatic d_poll_t fw_poll; 6490267Sdesstatic d_read_t fw_read; /* for Isochronous packet */ 6590267Sdesstatic d_write_t fw_write; 6640939Sdesstatic d_mmap_t fw_mmap; 6740939Sdes 6862981Sdesstruct cdevsw firewire_cdevsw = 6975891Sarchie{ 7040939Sdes fw_open, fw_close, fw_read, fw_write, fw_ioctl, 7162981Sdes fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM 7240939Sdes}; 7340939Sdes 7440939Sdesstatic int 7540939Sdesfw_open (dev_t dev, int flags, int fmt, fw_proc *td) 7640939Sdes{ 7760924Sdes struct firewire_softc *sc; 78174588Sdes int unit = DEV2UNIT(dev); 7940939Sdes int sub = DEV2DMACH(dev); 8090267Sdes 8190267Sdes int err = 0; 8290267Sdes 8340939Sdes if (DEV_FWMEM(dev)) 8440939Sdes return fwmem_open(dev, flags, fmt, td); 8540939Sdes 8640939Sdes sc = devclass_get_softc(firewire_devclass, unit); 8740939Sdes if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){ 8840939Sdes err = EBUSY; 89174588Sdes return err; 9040939Sdes } 91174588Sdes if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){ 9290267Sdes err = EBUSY; 9390267Sdes return err; 9440939Sdes } 9540939Sdes if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){ 9640939Sdes err = EBUSY; 9740939Sdes return err; 9840939Sdes } 9940939Sdes/* Default is per packet mode */ 100174588Sdes sc->fc->ir[sub]->flag |= FWXFERQ_OPEN; 10140939Sdes sc->fc->it[sub]->flag |= FWXFERQ_OPEN; 10290267Sdes sc->fc->ir[sub]->flag |= FWXFERQ_PACKET; 10390267Sdes return err; 10490267Sdes} 10590267Sdes 10690267Sdesstatic int 10790267Sdesfw_close (dev_t dev, int flags, int fmt, fw_proc *td) 10890267Sdes{ 10990267Sdes struct firewire_softc *sc; 11090267Sdes int unit = DEV2UNIT(dev); 11190267Sdes int sub = DEV2DMACH(dev); 11290267Sdes struct fw_xfer *xfer; 11390267Sdes struct fw_dvbuf *dvbuf; 11490267Sdes struct fw_bind *fwb; 11590267Sdes int err = 0; 11690267Sdes 11790267Sdes if (DEV_FWMEM(dev)) 11890267Sdes return fwmem_close(dev, flags, fmt, td); 11990267Sdes 12090267Sdes sc = devclass_get_softc(firewire_devclass, unit); 12190267Sdes if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){ 12290267Sdes err = EINVAL; 12390267Sdes return err; 12490267Sdes } 12590267Sdes sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN; 12690267Sdes if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){ 12790267Sdes err = EINVAL; 12890267Sdes return err; 12990267Sdes } 13090267Sdes sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN; 13190267Sdes 13290267Sdes if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){ 13390267Sdes sc->fc->irx_disable(sc->fc, sub); 13490267Sdes } 13590267Sdes if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){ 13690267Sdes sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING; 13790267Sdes sc->fc->itx_disable(sc->fc, sub); 13890267Sdes } 13990267Sdes if(sc->fc->it[sub]->flag & FWXFERQ_DV){ 14090267Sdes if((dvbuf = sc->fc->it[sub]->dvproc) != NULL){ 14190267Sdes free(dvbuf->buf, M_DEVBUF); 14290267Sdes sc->fc->it[sub]->dvproc = NULL; 14390267Sdes } 14490267Sdes if((dvbuf = sc->fc->it[sub]->dvdma) != NULL){ 14590267Sdes free(dvbuf->buf, M_DEVBUF); 14690267Sdes sc->fc->it[sub]->dvdma = NULL; 14790267Sdes } 14890267Sdes while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvvalid)) != NULL){ 14990267Sdes STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvvalid, link); 15090267Sdes free(dvbuf->buf, M_DEVBUF); 15190267Sdes } 15290267Sdes while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvfree)) != NULL){ 15340939Sdes STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvfree, link); 15440939Sdes free(dvbuf->buf, M_DEVBUF); 15540939Sdes } 15641862Sdes free(sc->fc->it[sub]->dvbuf, M_DEVBUF); 15741862Sdes sc->fc->it[sub]->dvbuf = NULL; 15841862Sdes } 15960924Sdes if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){ 160174588Sdes free(sc->fc->ir[sub]->buf, M_DEVBUF); 16141862Sdes sc->fc->ir[sub]->buf = NULL; 16290267Sdes free(sc->fc->ir[sub]->bulkxfer, M_DEVBUF); 16390267Sdes sc->fc->ir[sub]->bulkxfer = NULL; 16490267Sdes sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF; 16590267Sdes sc->fc->ir[sub]->psize = PAGE_SIZE; 16690267Sdes sc->fc->ir[sub]->maxq = FWMAXQUEUE; 16790267Sdes } 16841862Sdes if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){ 16941862Sdes free(sc->fc->it[sub]->buf, M_DEVBUF); 17041862Sdes sc->fc->it[sub]->buf = NULL; 17140939Sdes free(sc->fc->it[sub]->bulkxfer, M_DEVBUF); 17240939Sdes sc->fc->it[sub]->bulkxfer = NULL; 17340939Sdes sc->fc->it[sub]->dvbuf = NULL; 17468551Sdes sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF; 17568551Sdes sc->fc->it[sub]->psize = 0; 17668551Sdes sc->fc->it[sub]->maxq = FWMAXQUEUE; 177174588Sdes } 17868551Sdes for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q); 17990267Sdes xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){ 18068551Sdes sc->fc->ir[sub]->queued--; 18190267Sdes STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link); 18290267Sdes 18390267Sdes xfer->resp = 0; 18490267Sdes switch(xfer->act_type){ 18590267Sdes case FWACT_XFER: 18690267Sdes fw_xfer_done(xfer); 18790267Sdes break; 18868551Sdes default: 18968551Sdes break; 19068551Sdes } 19168551Sdes fw_xfer_free(xfer); 19268551Sdes } 19368551Sdes for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL; 194174588Sdes fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){ 19568551Sdes STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); 19690267Sdes STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist); 19790267Sdes free(fwb, M_DEVBUF); 19890267Sdes } 19990267Sdes sc->fc->ir[sub]->flag &= ~FWXFERQ_MODEMASK; 20090267Sdes sc->fc->it[sub]->flag &= ~FWXFERQ_MODEMASK; 20168551Sdes return err; 20268551Sdes} 20398117Sdes 20468551Sdes/* 20597866Sdes * read request. 20697866Sdes */ 20797866Sdesstatic int 208174588Sdesfw_read (dev_t dev, struct uio *uio, int ioflag) 20997866Sdes{ 21097866Sdes struct firewire_softc *sc; 21197866Sdes struct fw_xferq *ir; 21297866Sdes struct fw_xfer *xfer; 213109967Sdes int err = 0, s, slept = 0; 21497866Sdes int unit = DEV2UNIT(dev); 21597866Sdes int sub = DEV2DMACH(dev); 21698117Sdes struct fw_pkt *fp; 21797866Sdes 21897866Sdes if (DEV_FWMEM(dev)) 21997866Sdes return fwmem_read(dev, uio, ioflag); 22097866Sdes 22197866Sdes sc = devclass_get_softc(firewire_devclass, unit); 22298117Sdes 22398117Sdes ir = sc->fc->ir[sub]; 22498117Sdes 225174588Sdes if(ir->flag & FWXFERQ_PACKET){ 22698117Sdes ir->stproc = NULL; 22798117Sdes } 22898117Sdesreadloop: 22998117Sdes xfer = STAILQ_FIRST(&ir->q); 23098117Sdes if(!(ir->flag & FWXFERQ_PACKET) && ir->stproc == NULL){ 23198117Sdes ir->stproc = STAILQ_FIRST(&ir->stvalid); 23298117Sdes if(ir->stproc != NULL){ 23398117Sdes s = splfw(); 234111816Sdes STAILQ_REMOVE_HEAD(&ir->stvalid, link); 235111816Sdes splx(s); 236111816Sdes ir->queued = 0; 237174588Sdes } 238111816Sdes } 239111816Sdes 240111816Sdes if(xfer == NULL && ir->stproc == NULL){ 241111816Sdes if(slept == 0){ 242111816Sdes slept = 1; 243111816Sdes if(!(ir->flag & FWXFERQ_RUNNING) 244111816Sdes && (ir->flag & FWXFERQ_PACKET)){ 245111816Sdes err = sc->fc->irx_enable(sc->fc, sub); 246111816Sdes } 247111816Sdes if(err){ 248111816Sdes return err; 249111816Sdes } 250111816Sdes ir->flag |= FWXFERQ_WAKEUP; 251111816Sdes err = tsleep((caddr_t)ir, FWPRI, "fw_read", hz); 252111816Sdes if(err){ 253111816Sdes ir->flag &= ~FWXFERQ_WAKEUP; 254111816Sdes return err; 255111816Sdes } 25640939Sdes goto readloop; 25740939Sdes }else{ 25897856Sdes err = EIO; 259174588Sdes return err; 26040939Sdes } 26197856Sdes }else if(xfer != NULL){ 26290267Sdes s = splfw(); 263111816Sdes ir->queued --; 26490267Sdes STAILQ_REMOVE_HEAD(&ir->q, link); 26590267Sdes splx(s); 26640939Sdes fp = (struct fw_pkt *)(xfer->recv.buf + xfer->recv.off); 26790267Sdes if(sc->fc->irx_post != NULL) 26841862Sdes sc->fc->irx_post(sc->fc, fp->mode.ld); 26990267Sdes err = uiomove(xfer->recv.buf + xfer->recv.off, xfer->recv.len, uio); 270174588Sdes fw_xfer_free( xfer); 27140939Sdes }else if(ir->stproc != NULL){ 27290267Sdes fp = (struct fw_pkt *)(ir->stproc->buf + ir->queued * ir->psize); 27390267Sdes if(sc->fc->irx_post != NULL) 27490267Sdes sc->fc->irx_post(sc->fc, fp->mode.ld); 27590267Sdes if(ntohs(fp->mode.stream.len) == 0){ 27690267Sdes err = EIO; 27790267Sdes return err; 27890267Sdes } 279174588Sdes err = uiomove((caddr_t)fp, ntohs(fp->mode.stream.len) + sizeof(u_int32_t), uio); 28097856Sdes fp->mode.stream.len = 0; 28190267Sdes ir->queued ++; 282111816Sdes if(ir->queued >= ir->bnpacket){ 28390267Sdes s = splfw(); 28490267Sdes ir->stproc->flag = 0; 285174588Sdes STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); 28690267Sdes splx(s); 28790267Sdes ir->stproc = NULL; 288111816Sdes } 28990267Sdes } 29062981Sdes#if 0 29190267Sdes if(STAILQ_FIRST(&ir->q) == NULL && 292111816Sdes (ir->flag & FWXFERQ_RUNNING) && (ir->flag & FWXFERQ_PACKET)){ 293174588Sdes err = sc->fc->irx_enable(sc->fc, sub); 294174588Sdes } 295111816Sdes#endif 296111816Sdes#if 0 297111816Sdes if(STAILQ_FIRST(&ir->stvalid) == NULL && 298111816Sdes (ir->flag & FWXFERQ_RUNNING) && !(ir->flag & FWXFERQ_PACKET)){ 29990267Sdes err = sc->fc->irx_enable(sc->fc, sub); 30090267Sdes } 30190267Sdes#endif 30290267Sdes return err; 30390267Sdes} 304174588Sdes 30597856Sdesstatic int 30690267Sdesfw_write (dev_t dev, struct uio *uio, int ioflag) 30740939Sdes{ 308174588Sdes int err = 0; 309174588Sdes struct firewire_softc *sc; 31097856Sdes int unit = DEV2UNIT(dev); 311103459Sfenner int sub = DEV2DMACH(dev); 31297856Sdes int s, slept = 0; 31340939Sdes struct fw_pkt *fp; 31441989Sdes struct fw_xfer *xfer; 31541989Sdes struct fw_xferq *xferq; 31655557Sdes struct firewire_comm *fc; 31797868Sdes struct fw_xferq *it; 31897868Sdes 31997868Sdes if (DEV_FWMEM(dev)) 320174588Sdes return fwmem_write(dev, uio, ioflag); 32197868Sdes 32297868Sdes sc = devclass_get_softc(firewire_devclass, unit); 32397891Sdes fc = sc->fc; 32497868Sdes it = sc->fc->it[sub]; 32597868Sdes 32697868Sdes fp = (struct fw_pkt *)uio->uio_iov->iov_base; 32797868Sdes switch(fp->mode.common.tcode){ 32897868Sdes case FWTCODE_RREQQ: 32997868Sdes case FWTCODE_RREQB: 33097868Sdes case FWTCODE_LREQ: 33197868Sdes err = EINVAL; 33297868Sdes return err; 33397868Sdes case FWTCODE_WREQQ: 334108579Sdes case FWTCODE_WREQB: 33597868Sdes xferq = fc->atq; 33697868Sdes break; 33797868Sdes case FWTCODE_STREAM: 33897868Sdes if(it->flag & FWXFERQ_PACKET){ 33997868Sdes xferq = fc->atq; 34097868Sdes }else{ 34197868Sdes xferq = NULL; 34297868Sdes } 34397868Sdes break; 34497868Sdes case FWTCODE_WRES: 34597868Sdes case FWTCODE_RRESQ: 34697868Sdes case FWTCODE_RRESB: 34797868Sdes case FWTCODE_LRES: 34897868Sdes xferq = fc->ats; 34997868Sdes break; 35097868Sdes default: 35197868Sdes err = EINVAL; 35297868Sdes return err; 35397868Sdes } 35497868Sdes /* Discard unsent buffered stream packet, when sending Asyrequrst */ 35597868Sdes if(xferq != NULL && it->stproc != NULL){ 35697868Sdes s = splfw(); 35797868Sdes it->stproc->flag = 0; 35897868Sdes STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link); 35997868Sdes splx(s); 36097868Sdes it->stproc = NULL; 36197868Sdes } 36297868Sdes if(xferq == NULL && !(it->flag & FWXFERQ_DV)){ 36397868Sdesisoloop: 36497868Sdes if(it->stproc == NULL){ 36597891Sdes it->stproc = STAILQ_FIRST(&it->stfree); 36697891Sdes if(it->stproc != NULL){ 36797891Sdes s = splfw(); 36897891Sdes STAILQ_REMOVE_HEAD(&it->stfree, link); 36997891Sdes splx(s); 37097891Sdes it->queued = 0; 37197868Sdes }else if(slept == 0){ 37297868Sdes slept = 1; 37398117Sdes err = sc->fc->itx_enable(sc->fc, sub); 37497868Sdes if(err){ 37597866Sdes return err; 37655557Sdes } 37797866Sdes err = tsleep((caddr_t)it, FWPRI, "fw_write", hz); 378174588Sdes if(err){ 37955557Sdes return err; 380177447Sdes } 38190267Sdes goto isoloop; 38297866Sdes }else{ 38390267Sdes err = EIO; 38490267Sdes return err; 38555557Sdes } 38697866Sdes } 38790267Sdes#if 0 /* What's this for? (overwritten by the following uiomove)*/ 38890267Sdes fp = (struct fw_pkt *)(it->stproc->buf + it->queued * it->psize); 38955557Sdes fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t)); 39090267Sdes#endif 39197866Sdes err = uiomove(it->stproc->buf + it->queued * it->psize, 39297866Sdes uio->uio_resid, uio); 39397866Sdes it->queued ++; 39497856Sdes if(it->queued >= it->btpacket){ 39590267Sdes s = splfw(); 396177447Sdes STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); 397177447Sdes splx(s); 398177447Sdes it->stproc = NULL; 399177447Sdes fw_tbuf_update(sc->fc, sub, 0); 400177447Sdes err = sc->fc->itx_enable(sc->fc, sub); 40190267Sdes } 402177447Sdes return err; 403106186Sdes } if(xferq == NULL && it->flag & FWXFERQ_DV){ 404174588Sdesdvloop: 405106186Sdes if(it->dvproc == NULL){ 406106186Sdes it->dvproc = STAILQ_FIRST(&it->dvfree); 40797866Sdes if(it->dvproc != NULL){ 408177447Sdes s = splfw(); 40990267Sdes STAILQ_REMOVE_HEAD(&it->dvfree, link); 41090267Sdes splx(s); 41190267Sdes it->dvptr = 0; 412174588Sdes }else if(slept == 0){ 41390267Sdes slept = 1; 41490267Sdes err = sc->fc->itx_enable(sc->fc, sub); 41590267Sdes if(err){ 41697891Sdes return err; 41797866Sdes } 41897866Sdes err = tsleep((caddr_t)it, FWPRI, "fw_write", hz); 41997866Sdes if(err){ 42097891Sdes return err; 42197866Sdes } 422106049Sdes goto dvloop; 423106049Sdes }else{ 42497866Sdes err = EIO; 42590267Sdes return err; 42690267Sdes } 42790267Sdes } 42890267Sdes#if 0 /* What's this for? (it->dvptr? overwritten by the following uiomove)*/ 42997866Sdes fp = (struct fw_pkt *)(it->dvproc->buf + it->queued * it->psize); 43097866Sdes fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t)); 43197866Sdes#endif 43297866Sdes err = uiomove(it->dvproc->buf + it->dvptr, 43397866Sdes uio->uio_resid, uio); 43497866Sdes it->dvptr += it->psize; 43597866Sdes if(err){ 43698117Sdes return err; 43797866Sdes } 43897866Sdes if(it->dvptr >= it->psize * it->dvpacket){ 43997866Sdes s = splfw(); 44097866Sdes STAILQ_INSERT_TAIL(&it->dvvalid, it->dvproc, link); 44197866Sdes splx(s); 44297866Sdes it->dvproc = NULL; 443174588Sdes err = fw_tbuf_update(sc->fc, sub, 0); 44497866Sdes if(err){ 44597866Sdes return err; 44697866Sdes } 447106186Sdes err = sc->fc->itx_enable(sc->fc, sub); 44897866Sdes } 44997866Sdes return err; 45097866Sdes } 45197866Sdes if(xferq != NULL){ 45297866Sdes xfer = fw_xfer_alloc(); 45397866Sdes if(xfer == NULL){ 45497866Sdes err = ENOMEM; 45597866Sdes return err; 45697866Sdes } 45797866Sdes xfer->send.buf = malloc(uio->uio_resid, M_DEVBUF, M_NOWAIT); 45897866Sdes if(xfer->send.buf == NULL){ 45997866Sdes fw_xfer_free( xfer); 46097866Sdes err = ENOBUFS; 46197866Sdes return err; 462174588Sdes } 463106186Sdes xfer->dst = ntohs(fp->mode.hdr.dst); 46497866Sdes#if 0 465106186Sdes switch(fp->mode.common.tcode){ 466106137Sobrien case FWTCODE_WREQQ: 46797856Sdes case FWTCODE_WREQB: 46897856Sdes if((tl = fw_get_tlabel(fc, xfer)) == -1 ){ 46997856Sdes fw_xfer_free( xfer); 47097856Sdes err = EAGAIN; 47197856Sdes return err; 47290267Sdes } 47390267Sdes fp->mode.hdr.tlrt = tl << 2; 47490267Sdes default: 47597856Sdes break; 47697856Sdes } 47790267Sdes 47890267Sdes xfer->tl = fp->mode.hdr.tlrt >> 2; 47990267Sdes xfer->tcode = fp->mode.common.tcode; 48097856Sdes xfer->fc = fc; 48197856Sdes xfer->q = xferq; 48290267Sdes xfer->act_type = FWACT_XFER; 48355557Sdes xfer->retry_req = fw_asybusy; 48455557Sdes#endif 48555557Sdes xfer->send.len = uio->uio_resid; 48662981Sdes xfer->send.off = 0; 48797866Sdes xfer->spd = 0;/* XXX: how to setup it */ 48862981Sdes xfer->act.hand = fw_asy_callback; 48997866Sdes 490174588Sdes err = uiomove(xfer->send.buf, uio->uio_resid, uio); 49197866Sdes if(err){ 492106175Simp fw_xfer_free( xfer); 493106175Simp return err; 494106175Simp } 495106175Simp#if 0 496174588Sdes fw_asystart(xfer); 497106175Simp#else 498106175Simp fw_asyreq(fc, -1, xfer); 499106175Simp#endif 500106175Simp err = tsleep((caddr_t)xfer, FWPRI, "fw_write", hz); 501106175Simp if(xfer->resp == EBUSY) 502106175Simp return EBUSY; 503106175Simp fw_xfer_free( xfer); 504174588Sdes return err; 505106175Simp } 506177447Sdes return EINVAL; 50797866Sdes} 50897866Sdes 50997866Sdes/* 51097866Sdes * ioctl support. 51197866Sdes */ 51297866Sdesint 51397866Sdesfw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td) 51497866Sdes{ 51597866Sdes struct firewire_softc *sc; 51697866Sdes int unit = DEV2UNIT(dev); 517106175Simp int sub = DEV2DMACH(dev); 518106175Simp int i, len, err = 0; 51997866Sdes struct fw_device *fwdev; 52097866Sdes struct fw_bind *fwb; 52197866Sdes struct fw_xferq *ir, *it; 522177447Sdes struct fw_xfer *xfer; 523177447Sdes struct fw_pkt *fp; 524177447Sdes 525177447Sdes struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; 526177447Sdes struct fw_asyreq *asyreq = (struct fw_asyreq *)data; 52797866Sdes struct fw_isochreq *ichreq = (struct fw_isochreq *)data; 528177447Sdes struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; 52997866Sdes struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; 530174588Sdes#if 0 53197866Sdes struct fw_map_buf *map_buf = (struct fw_map_buf *)data; 53297866Sdes#endif 53397866Sdes struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; 534177447Sdes 53597866Sdes if (DEV_FWMEM(dev)) 53697866Sdes return fwmem_ioctl(dev, cmd, data, flag, td); 53797866Sdes 53897866Sdes sc = devclass_get_softc(firewire_devclass, unit); 53997866Sdes if (!data) 54097866Sdes return(EINVAL); 54197866Sdes 54297891Sdes switch (cmd) { 54397866Sdes case FW_STSTREAM: 544106175Simp sc->fc->it[sub]->flag &= ~0xff; 545106175Simp sc->fc->it[sub]->flag |= (0x3f & ichreq->ch); 54697866Sdes sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6); 54797891Sdes err = 0; 548106175Simp break; 549106175Simp case FW_GTSTREAM: 55097866Sdes ichreq->ch = sc->fc->it[sub]->flag & 0x3f; 551106175Simp ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3; 552174588Sdes err = 0; 55397866Sdes break; 554106175Simp case FW_SRSTREAM: 55597866Sdes sc->fc->ir[sub]->flag &= ~0xff; 55697866Sdes sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch); 55797866Sdes sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6); 55897866Sdes err = sc->fc->irx_enable(sc->fc, sub); 55997866Sdes break; 56097866Sdes case FW_GRSTREAM: 561106175Simp ichreq->ch = sc->fc->ir[sub]->flag & 0x3f; 562106175Simp ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3; 563106175Simp err = 0; 564106175Simp break; 565106175Simp case FW_SSTDV: 566106175Simp ibufreq = (struct fw_isobufreq *) 567106175Simp malloc(sizeof(struct fw_isobufreq), M_DEVBUF, M_NOWAIT); 568106175Simp if(ibufreq == NULL){ 569106175Simp err = ENOMEM; 57097866Sdes break; 57197866Sdes } 57297866Sdes#if DV_PAL 57397866Sdes#define FWDVPACKET 300 57498117Sdes#else 57597866Sdes#define FWDVPACKET 250 57697866Sdes#endif 57797866Sdes#define FWDVPMAX 512 57862981Sdes ibufreq->rx.nchunk = 8; 579174588Sdes ibufreq->rx.npacket = 50; 58062981Sdes ibufreq->rx.psize = FWDVPMAX; 581106175Simp 582106205Sdes ibufreq->tx.nchunk = 5; 58398748Sdes ibufreq->tx.npacket = FWDVPACKET + 30; /* > 320 or 267 */ 58498748Sdes ibufreq->tx.psize = FWDVPMAX; 585106175Simp 586106175Simp err = fw_ioctl(dev, FW_SSTBUF, (caddr_t)ibufreq, flag, td); 587106175Simp sc->fc->it[sub]->dvpacket = FWDVPACKET; 588109967Sdes free(ibufreq, M_DEVBUF); 589106205Sdes/* reserve a buffer space */ 590174588Sdes#define NDVCHUNK 8 591106205Sdes sc->fc->it[sub]->dvproc = NULL; 592174588Sdes sc->fc->it[sub]->dvdma = NULL; 593106205Sdes sc->fc->it[sub]->flag |= FWXFERQ_DV; 59490267Sdes /* XXX check malloc failure */ 59590267Sdes sc->fc->it[sub]->dvbuf 59662981Sdes = (struct fw_dvbuf *)malloc(sizeof(struct fw_dvbuf) * NDVCHUNK, M_DEVBUF, M_NOWAIT); 59762981Sdes STAILQ_INIT(&sc->fc->it[sub]->dvvalid); 59862981Sdes STAILQ_INIT(&sc->fc->it[sub]->dvfree); 59997856Sdes for( i = 0 ; i < NDVCHUNK ; i++){ 60097856Sdes /* XXX check malloc failure */ 60197856Sdes sc->fc->it[sub]->dvbuf[i].buf 60297856Sdes = malloc(FWDVPMAX * sc->fc->it[sub]->dvpacket, M_DEVBUF, M_NOWAIT); 603174588Sdes STAILQ_INSERT_TAIL(&sc->fc->it[sub]->dvfree, 60497856Sdes &sc->fc->it[sub]->dvbuf[i], link); 60597856Sdes } 60697856Sdes break; 60798117Sdes case FW_SSTBUF: 60898117Sdes ir = sc->fc->ir[sub]; 60997856Sdes it = sc->fc->it[sub]; 610141970Sdes 61197856Sdes if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){ 61297856Sdes return(EBUSY); 61397856Sdes } 61497856Sdes if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){ 61597856Sdes return(EBUSY); 61641989Sdes } 61741989Sdes if((ibufreq->rx.nchunk * 61841989Sdes ibufreq->rx.psize * ibufreq->rx.npacket) + 619174588Sdes (ibufreq->tx.nchunk * 62090267Sdes ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){ 62141989Sdes return(EINVAL); 62290267Sdes } 62341989Sdes if(ibufreq->rx.nchunk > FWSTMAXCHUNK || 62490267Sdes ibufreq->tx.nchunk > FWSTMAXCHUNK){ 62590268Sdes return(EINVAL); 62690267Sdes } 62741989Sdes ir->bulkxfer 62841989Sdes = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_DEVBUF, M_NOWAIT); 62990267Sdes if(ir->bulkxfer == NULL){ 630109967Sdes return(ENOMEM); 63190267Sdes } 63290267Sdes it->bulkxfer 633174588Sdes = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_DEVBUF, M_NOWAIT); 63490267Sdes if(it->bulkxfer == NULL){ 63590267Sdes return(ENOMEM); 63690268Sdes } 63790267Sdes ir->buf = malloc( 63890267Sdes ibufreq->rx.nchunk * ibufreq->rx.npacket 63941989Sdes /* XXX psize must be 2^n and less or 64090267Sdes equal to PAGE_SIZE */ 64190267Sdes * ((ibufreq->rx.psize + 3) &~3), 642176105Sdes M_DEVBUF, M_NOWAIT); 64341989Sdes if(ir->buf == NULL){ 64490267Sdes free(ir->bulkxfer, M_DEVBUF); 64590267Sdes free(it->bulkxfer, M_DEVBUF); 64690267Sdes ir->bulkxfer = NULL; 64790267Sdes it->bulkxfer = NULL; 64841989Sdes it->buf = NULL; 649109695Sdes return(ENOMEM); 650109695Sdes } 651109695Sdes it->buf = malloc( 652109695Sdes ibufreq->tx.nchunk * ibufreq->tx.npacket 653109695Sdes /* XXX psize must be 2^n and less or 654174588Sdes equal to PAGE_SIZE */ 655109695Sdes * ((ibufreq->tx.psize + 3) &~3), 656109695Sdes M_DEVBUF, M_NOWAIT); 657109695Sdes if(it->buf == NULL){ 658109695Sdes free(ir->bulkxfer, M_DEVBUF); 659109695Sdes free(it->bulkxfer, M_DEVBUF); 660109695Sdes free(ir->buf, M_DEVBUF); 661109695Sdes ir->bulkxfer = NULL; 662109695Sdes it->bulkxfer = NULL; 663109695Sdes it->buf = NULL; 664109695Sdes return(ENOMEM); 665109695Sdes } 666109695Sdes 667174588Sdes ir->bnchunk = ibufreq->rx.nchunk; 668109695Sdes ir->bnpacket = ibufreq->rx.npacket; 669109695Sdes ir->btpacket = ibufreq->rx.npacket; 670109695Sdes ir->psize = (ibufreq->rx.psize + 3) & ~3; 671109695Sdes ir->queued = 0; 672109695Sdes 673109695Sdes it->bnchunk = ibufreq->tx.nchunk; 674109695Sdes it->bnpacket = ibufreq->tx.npacket; 675109967Sdes it->btpacket = ibufreq->tx.npacket; 676174588Sdes it->psize = (ibufreq->tx.psize + 3) & ~3; 677109695Sdes ir->queued = 0; 678109695Sdes it->dvdbc = 0; 679109695Sdes it->dvdiff = 0; 680109695Sdes it->dvsync = 0; 681109695Sdes it->dvoffset = 0; 682109695Sdes 683109695Sdes STAILQ_INIT(&ir->stvalid); 684109695Sdes STAILQ_INIT(&ir->stfree); 685109695Sdes ir->stdma = NULL; 686109695Sdes ir->stdma2 = NULL; 687109695Sdes ir->stproc = NULL; 688109967Sdes 689109695Sdes STAILQ_INIT(&it->stvalid); 690109695Sdes STAILQ_INIT(&it->stfree); 691109695Sdes it->stdma = NULL; 692109695Sdes it->stdma2 = NULL; 693109695Sdes it->stproc = NULL; 694174588Sdes 695109695Sdes for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){ 696174588Sdes ir->bulkxfer[i].buf = 697109695Sdes ir->buf + 698109695Sdes i * sc->fc->ir[sub]->bnpacket * 699109695Sdes sc->fc->ir[sub]->psize; 700174588Sdes ir->bulkxfer[i].flag = 0; 701109695Sdes STAILQ_INSERT_TAIL(&ir->stfree, 702174588Sdes &ir->bulkxfer[i], link); 703109695Sdes ir->bulkxfer[i].npacket = ir->bnpacket; 704109695Sdes } 705109695Sdes for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){ 706109695Sdes it->bulkxfer[i].buf = 707109695Sdes it->buf + 708174588Sdes i * sc->fc->it[sub]->bnpacket * 709109695Sdes sc->fc->it[sub]->psize; 710174588Sdes it->bulkxfer[i].flag = 0; 711109695Sdes STAILQ_INSERT_TAIL(&it->stfree, 712109967Sdes &it->bulkxfer[i], link); 713109960Sjwd it->bulkxfer[i].npacket = it->bnpacket; 714174588Sdes } 715109695Sdes ir->flag &= ~FWXFERQ_MODEMASK; 716109695Sdes ir->flag |= FWXFERQ_STREAM; 717109695Sdes ir->flag |= FWXFERQ_EXTBUF; 718174588Sdes 719109695Sdes it->flag &= ~FWXFERQ_MODEMASK; 720109967Sdes it->flag |= FWXFERQ_STREAM; 721109960Sjwd it->flag |= FWXFERQ_EXTBUF; 722174588Sdes err = 0; 723109695Sdes break; 724109695Sdes case FW_GSTBUF: 725109695Sdes ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk; 726174588Sdes ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket; 727109695Sdes ibufreq->rx.psize = sc->fc->ir[sub]->psize; 728109695Sdes 729109695Sdes ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk; 730109695Sdes ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket; 731109695Sdes ibufreq->tx.psize = sc->fc->it[sub]->psize; 732109695Sdes break; 733109695Sdes case FW_ASYREQ: 734109695Sdes xfer = fw_xfer_alloc(); 735109695Sdes if(xfer == NULL){ 736109695Sdes err = ENOMEM; 737109695Sdes return err; 738109695Sdes } 739174752Sdes fp = &asyreq->pkt; 740174752Sdes switch (asyreq->req.type) { 741174752Sdes case FWASREQNODE: 742174752Sdes xfer->dst = ntohs(fp->mode.hdr.dst); 743174752Sdes break; 744174752Sdes case FWASREQEUI: 745174752Sdes fwdev = fw_noderesolve(sc->fc, asyreq->req.dst.eui); 746174752Sdes if (fwdev == NULL) { 747174752Sdes device_printf(sc->fc->bdev, 748174752Sdes "cannot find node\n"); 749174752Sdes err = EINVAL; 750174752Sdes goto error; 751174752Sdes } 752174752Sdes xfer->dst = fwdev->dst; 753174752Sdes fp->mode.hdr.dst = htons(FWLOCALBUS | xfer->dst); 754174752Sdes break; 755174752Sdes case FWASRESTL: 756174752Sdes /* XXX what's this? */ 757174752Sdes break; 758174752Sdes case FWASREQSTREAM: 759174752Sdes /* nothing to do */ 760174752Sdes break; 761174752Sdes } 762174752Sdes xfer->spd = asyreq->req.sped; 763174752Sdes xfer->send.len = asyreq->req.len; 764174752Sdes xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT); 765174752Sdes if(xfer->send.buf == NULL){ 766174761Sdes return ENOMEM; 767174752Sdes } 768174752Sdes xfer->send.off = 0; 769174752Sdes bcopy(fp, xfer->send.buf, xfer->send.len); 770174752Sdes xfer->act.hand = fw_asy_callback; 771174761Sdes err = fw_asyreq(sc->fc, sub, xfer); 772174752Sdes if(err){ 773174752Sdes fw_xfer_free( xfer); 774174752Sdes return err; 775174752Sdes } 776174752Sdes err = tsleep((caddr_t)xfer, FWPRI, "asyreq", hz); 777174752Sdes if(err == 0){ 778174752Sdes if(asyreq->req.len >= xfer->recv.len){ 779174752Sdes asyreq->req.len = xfer->recv.len; 780174752Sdes }else{ 781174752Sdes err = EINVAL; 782174752Sdes } 783174752Sdes bcopy(xfer->recv.buf + xfer->recv.off, fp, asyreq->req.len); 784174752Sdes } 785174752Sdeserror: 786174752Sdes fw_xfer_free( xfer); 787 break; 788 case FW_IBUSRST: 789 sc->fc->ibr(sc->fc); 790 break; 791 case FW_CBINDADDR: 792 fwb = fw_bindlookup(sc->fc, 793 bindreq->start.hi, bindreq->start.lo); 794 if(fwb == NULL){ 795 err = EINVAL; 796 break; 797 } 798 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist); 799 STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist); 800 free(fwb, M_DEVBUF); 801 break; 802 case FW_SBINDADDR: 803 if(bindreq->len <= 0 ){ 804 err = EINVAL; 805 break; 806 } 807 if(bindreq->start.hi > 0xffff ){ 808 err = EINVAL; 809 break; 810 } 811 fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_DEVBUF, M_NOWAIT); 812 if(fwb == NULL){ 813 err = ENOMEM; 814 break; 815 } 816 fwb->start_hi = bindreq->start.hi; 817 fwb->start_lo = bindreq->start.lo; 818 fwb->addrlen = bindreq->len; 819 820 xfer = fw_xfer_alloc(); 821 if(xfer == NULL){ 822 err = ENOMEM; 823 return err; 824 } 825 xfer->act_type = FWACT_CH; 826 xfer->sub = sub; 827 xfer->fc = sc->fc; 828 829 fwb->xfer = xfer; 830 err = fw_bindadd(sc->fc, fwb); 831 break; 832 case FW_GDEVLST: 833 i = 0; 834 for(fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL; 835 fwdev = TAILQ_NEXT(fwdev, link)){ 836 if(i < fwdevlst->n){ 837 fwdevlst->dst[i] = fwdev->dst; 838 fwdevlst->status[i] = 839 (fwdev->status == FWDEVATTACHED)?1:0; 840 fwdevlst->eui[i].hi = fwdev->eui.hi; 841 fwdevlst->eui[i].lo = fwdev->eui.lo; 842 } 843 i++; 844 } 845 fwdevlst->n = i; 846 break; 847 case FW_GTPMAP: 848 bcopy(sc->fc->topology_map, data, 849 (sc->fc->topology_map->crc_len + 1) * 4); 850 break; 851 case FW_GCROM: 852 for (fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL; 853 fwdev = TAILQ_NEXT(fwdev, link)) { 854 if (fwdev->eui.hi == crom_buf->eui.hi && 855 fwdev->eui.lo == crom_buf->eui.lo) 856 break; 857 } 858 if (fwdev == NULL) { 859 err = FWNODE_INVAL; 860 break; 861 } 862#if 0 863 if (fwdev->csrrom[0] >> 24 == 1) 864 len = 4; 865 else 866 len = (1 + ((fwdev->csrrom[0] >> 16) & 0xff)) * 4; 867#else 868 if (fwdev->rommax < CSRROMOFF) 869 len = 0; 870 else 871 len = fwdev->rommax - CSRROMOFF + 4; 872#endif 873 if (crom_buf->len < len) 874 len = crom_buf->len; 875 else 876 crom_buf->len = len; 877 err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len); 878 break; 879 default: 880 sc->fc->ioctl (dev, cmd, data, flag, td); 881 break; 882 } 883 return err; 884} 885int 886fw_poll(dev_t dev, int events, fw_proc *td) 887{ 888 int revents; 889 int tmp; 890 int unit = DEV2UNIT(dev); 891 int sub = DEV2DMACH(dev); 892 struct firewire_softc *sc; 893 894 if (DEV_FWMEM(dev)) 895 return fwmem_poll(dev, events, td); 896 897 sc = devclass_get_softc(firewire_devclass, unit); 898 revents = 0; 899 tmp = POLLIN | POLLRDNORM; 900 if (events & tmp) { 901 if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL) 902 revents |= tmp; 903 else 904 selrecord(td, &sc->fc->ir[sub]->rsel); 905 } 906 tmp = POLLOUT | POLLWRNORM; 907 if (events & tmp) { 908 /* XXX should be fixed */ 909 revents |= tmp; 910 } 911 912 return revents; 913} 914 915static int 916fw_mmap (dev_t dev, vm_offset_t offset, int nproto) 917{ 918 struct firewire_softc *fc; 919 int unit = DEV2UNIT(dev); 920 921 if (DEV_FWMEM(dev)) 922 return fwmem_mmap(dev, offset, nproto); 923 924 fc = devclass_get_softc(firewire_devclass, unit); 925 926 return EINVAL; 927} 928