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