fwdev.c revision 109802
1/*
2 * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the acknowledgement as bellow:
15 *
16 *    This product includes software developed by K. Kobayashi and H. Shimokawa
17 *
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $FreeBSD: head/sys/dev/firewire/fwdev.c 109802 2003-01-24 13:03:19Z simokawa $
34 *
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/types.h>
40#include <sys/mbuf.h>
41
42#include <sys/kernel.h>
43#include <sys/malloc.h>
44#include <sys/conf.h>
45#include <sys/uio.h>
46#include <sys/poll.h>
47
48#include <sys/bus.h>
49
50#include <sys/ioccom.h>
51
52#include <dev/firewire/firewire.h>
53#include <dev/firewire/firewirereg.h>
54#include <dev/firewire/fwmem.h>
55#include <dev/firewire/iec68113.h>
56
57#define CDEV_MAJOR 127
58#define	FWNODE_INVAL 0xffff
59
60static	d_open_t	fw_open;
61static	d_close_t	fw_close;
62static	d_ioctl_t	fw_ioctl;
63static	d_poll_t	fw_poll;
64static	d_read_t	fw_read;	/* for Isochronous packet */
65static	d_write_t	fw_write;
66static	d_mmap_t	fw_mmap;
67
68struct cdevsw firewire_cdevsw =
69{
70	fw_open, fw_close, fw_read, fw_write, fw_ioctl,
71	fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM
72};
73
74static int
75fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
76{
77	struct firewire_softc *sc;
78	int unit = DEV2UNIT(dev);
79	int sub = DEV2DMACH(dev);
80
81	int err = 0;
82
83	if (DEV_FWMEM(dev))
84		return fwmem_open(dev, flags, fmt, td);
85
86	sc = devclass_get_softc(firewire_devclass, unit);
87	if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
88		err = EBUSY;
89		return err;
90	}
91	if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
92		err = EBUSY;
93		return err;
94	}
95	if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
96		err = EBUSY;
97		return err;
98	}
99/* Default is per packet mode */
100	sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
101	sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
102	sc->fc->ir[sub]->flag |= FWXFERQ_PACKET;
103	return err;
104}
105
106static int
107fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
108{
109	struct firewire_softc *sc;
110	int unit = DEV2UNIT(dev);
111	int sub = DEV2DMACH(dev);
112	struct fw_xfer *xfer;
113	struct fw_bind *fwb;
114	int err = 0;
115
116	if (DEV_FWMEM(dev))
117		return fwmem_close(dev, flags, fmt, td);
118
119	sc = devclass_get_softc(firewire_devclass, unit);
120	if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){
121		err = EINVAL;
122		return err;
123	}
124	sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN;
125	if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){
126		err = EINVAL;
127		return err;
128	}
129	sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN;
130
131	if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){
132		sc->fc->irx_disable(sc->fc, sub);
133	}
134	if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){
135		sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING;
136		sc->fc->itx_disable(sc->fc, sub);
137	}
138#ifdef FWXFERQ_DV
139	if(sc->fc->it[sub]->flag & FWXFERQ_DV){
140		struct fw_dvbuf *dvbuf;
141
142		if((dvbuf = sc->fc->it[sub]->dvproc) != NULL){
143			free(dvbuf->buf, M_DEVBUF);
144			sc->fc->it[sub]->dvproc = NULL;
145		}
146		if((dvbuf = sc->fc->it[sub]->dvdma) != NULL){
147			free(dvbuf->buf, M_DEVBUF);
148			sc->fc->it[sub]->dvdma = NULL;
149		}
150		while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvvalid)) != NULL){
151			STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvvalid, link);
152			free(dvbuf->buf, M_DEVBUF);
153		}
154		while((dvbuf = STAILQ_FIRST(&sc->fc->it[sub]->dvfree)) != NULL){
155			STAILQ_REMOVE_HEAD(&sc->fc->it[sub]->dvfree, link);
156			free(dvbuf->buf, M_DEVBUF);
157		}
158		free(sc->fc->it[sub]->dvbuf, M_DEVBUF);
159		sc->fc->it[sub]->dvbuf = NULL;
160	}
161#endif
162	if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
163		free(sc->fc->ir[sub]->buf, M_DEVBUF);
164		sc->fc->ir[sub]->buf = NULL;
165		free(sc->fc->ir[sub]->bulkxfer, M_DEVBUF);
166		sc->fc->ir[sub]->bulkxfer = NULL;
167		sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
168		sc->fc->ir[sub]->psize = PAGE_SIZE;
169		sc->fc->ir[sub]->maxq = FWMAXQUEUE;
170	}
171	if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
172		free(sc->fc->it[sub]->buf, M_DEVBUF);
173		sc->fc->it[sub]->buf = NULL;
174		free(sc->fc->it[sub]->bulkxfer, M_DEVBUF);
175		sc->fc->it[sub]->bulkxfer = NULL;
176		sc->fc->it[sub]->dvbuf = NULL;
177		sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
178		sc->fc->it[sub]->psize = 0;
179		sc->fc->it[sub]->maxq = FWMAXQUEUE;
180	}
181	for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
182		xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
183		sc->fc->ir[sub]->queued--;
184		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
185
186		xfer->resp = 0;
187		switch(xfer->act_type){
188		case FWACT_XFER:
189			fw_xfer_done(xfer);
190			break;
191		default:
192			break;
193		}
194		fw_xfer_free(xfer);
195	}
196	for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
197		fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
198		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
199		STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
200		free(fwb, M_DEVBUF);
201	}
202	sc->fc->ir[sub]->flag &= ~FWXFERQ_MODEMASK;
203	sc->fc->it[sub]->flag &= ~FWXFERQ_MODEMASK;
204	return err;
205}
206
207/*
208 * read request.
209 */
210static int
211fw_read (dev_t dev, struct uio *uio, int ioflag)
212{
213	struct firewire_softc *sc;
214	struct fw_xferq *ir;
215	struct fw_xfer *xfer;
216	int err = 0, s, slept = 0;
217	int unit = DEV2UNIT(dev);
218	int sub = DEV2DMACH(dev);
219	struct fw_pkt *fp;
220
221	if (DEV_FWMEM(dev))
222		return fwmem_read(dev, uio, ioflag);
223
224	sc = devclass_get_softc(firewire_devclass, unit);
225
226	ir = sc->fc->ir[sub];
227
228	if(ir->flag & FWXFERQ_PACKET){
229		ir->stproc = NULL;
230	}
231readloop:
232	xfer = STAILQ_FIRST(&ir->q);
233	if(!(ir->flag & FWXFERQ_PACKET) && ir->stproc == NULL){
234		ir->stproc = STAILQ_FIRST(&ir->stvalid);
235		if(ir->stproc != NULL){
236			s = splfw();
237			STAILQ_REMOVE_HEAD(&ir->stvalid, link);
238			splx(s);
239			ir->queued = 0;
240		}
241	}
242
243	if(xfer == NULL && ir->stproc == NULL){
244		if(slept == 0){
245			slept = 1;
246			if(!(ir->flag & FWXFERQ_RUNNING)
247				&& (ir->flag & FWXFERQ_PACKET)){
248				err = sc->fc->irx_enable(sc->fc, sub);
249			}
250			if(err){
251				return err;
252			}
253			ir->flag |= FWXFERQ_WAKEUP;
254			err = tsleep((caddr_t)ir, FWPRI, "fw_read", hz);
255			if(err){
256				ir->flag &= ~FWXFERQ_WAKEUP;
257				return err;
258			}
259			goto readloop;
260		}else{
261			err = EIO;
262			return err;
263		}
264	}else if(xfer != NULL){
265		s = splfw();
266		ir->queued --;
267		STAILQ_REMOVE_HEAD(&ir->q, link);
268		splx(s);
269		fp = (struct fw_pkt *)(xfer->recv.buf + xfer->recv.off);
270		if(sc->fc->irx_post != NULL)
271			sc->fc->irx_post(sc->fc, fp->mode.ld);
272		err = uiomove(xfer->recv.buf + xfer->recv.off, xfer->recv.len, uio);
273		fw_xfer_free( xfer);
274	}else if(ir->stproc != NULL){
275		fp = (struct fw_pkt *)(ir->stproc->buf + ir->queued * ir->psize);
276		if(sc->fc->irx_post != NULL)
277			sc->fc->irx_post(sc->fc, fp->mode.ld);
278		if(ntohs(fp->mode.stream.len) == 0){
279			err = EIO;
280			return err;
281		}
282		err = uiomove((caddr_t)fp, ntohs(fp->mode.stream.len) + sizeof(u_int32_t), uio);
283		fp->mode.stream.len = 0;
284		ir->queued ++;
285		if(ir->queued >= ir->bnpacket){
286			s = splfw();
287			ir->stproc->flag = 0;
288			STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
289			splx(s);
290			ir->stproc = NULL;
291		}
292	}
293#if 0
294	if(STAILQ_FIRST(&ir->q) == NULL &&
295		(ir->flag & FWXFERQ_RUNNING) && (ir->flag & FWXFERQ_PACKET)){
296		err = sc->fc->irx_enable(sc->fc, sub);
297	}
298#endif
299#if 0
300	if(STAILQ_FIRST(&ir->stvalid) == NULL &&
301		(ir->flag & FWXFERQ_RUNNING) && !(ir->flag & FWXFERQ_PACKET)){
302		err = sc->fc->irx_enable(sc->fc, sub);
303	}
304#endif
305	return err;
306}
307
308static int
309fw_write (dev_t dev, struct uio *uio, int ioflag)
310{
311	int err = 0;
312	struct firewire_softc *sc;
313	int unit = DEV2UNIT(dev);
314	int sub = DEV2DMACH(dev);
315	int s, slept = 0;
316	struct fw_pkt *fp;
317	struct fw_xfer *xfer;
318	struct fw_xferq *xferq;
319	struct firewire_comm *fc;
320	struct fw_xferq *it;
321
322	if (DEV_FWMEM(dev))
323		return fwmem_write(dev, uio, ioflag);
324
325	sc = devclass_get_softc(firewire_devclass, unit);
326	fc = sc->fc;
327	it = sc->fc->it[sub];
328
329	fp = (struct fw_pkt *)uio->uio_iov->iov_base;
330	switch(fp->mode.common.tcode){
331	case FWTCODE_RREQQ:
332	case FWTCODE_RREQB:
333	case FWTCODE_LREQ:
334		err = EINVAL;
335		return err;
336	case FWTCODE_WREQQ:
337	case FWTCODE_WREQB:
338		xferq = fc->atq;
339		break;
340	case FWTCODE_STREAM:
341		if(it->flag & FWXFERQ_PACKET){
342			xferq = fc->atq;
343		}else{
344			xferq = NULL;
345		}
346		break;
347	case FWTCODE_WRES:
348	case FWTCODE_RRESQ:
349	case FWTCODE_RRESB:
350	case FWTCODE_LRES:
351		xferq = fc->ats;
352		break;
353	default:
354		err = EINVAL;
355		return err;
356	}
357	/* Discard unsent buffered stream packet, when sending Asyrequrst */
358	if(xferq != NULL && it->stproc != NULL){
359		s = splfw();
360		it->stproc->flag = 0;
361		STAILQ_INSERT_TAIL(&it->stfree, it->stproc, link);
362		splx(s);
363		it->stproc = NULL;
364	}
365#ifdef FWXFERQ_DV
366	if(xferq == NULL && !(it->flag & FWXFERQ_DV)){
367#else
368	if (xferq == NULL) {
369#endif
370isoloop:
371		if(it->stproc == NULL){
372			it->stproc = STAILQ_FIRST(&it->stfree);
373			if(it->stproc != NULL){
374				s = splfw();
375				STAILQ_REMOVE_HEAD(&it->stfree, link);
376				splx(s);
377				it->queued = 0;
378			}else if(slept == 0){
379				slept = 1;
380				err = sc->fc->itx_enable(sc->fc, sub);
381				if(err){
382					return err;
383				}
384				err = tsleep((caddr_t)it, FWPRI, "fw_write", hz);
385				if(err){
386					return err;
387				}
388				goto isoloop;
389			}else{
390				err = EIO;
391				return err;
392			}
393		}
394#if 0 /* What's this for? (overwritten by the following uiomove)*/
395		fp = (struct fw_pkt *)(it->stproc->buf + it->queued * it->psize);
396		fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
397#endif
398		err = uiomove(it->stproc->buf + it->queued * it->psize,
399							uio->uio_resid, uio);
400		it->queued ++;
401		if(it->queued >= it->btpacket){
402			s = splfw();
403			STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
404			splx(s);
405			it->stproc = NULL;
406			fw_tbuf_update(sc->fc, sub, 0);
407			err = sc->fc->itx_enable(sc->fc, sub);
408		}
409		return err;
410	}
411#ifdef FWXFERQ_DV
412	if(xferq == NULL && it->flag & FWXFERQ_DV){
413dvloop:
414		if(it->dvproc == NULL){
415			it->dvproc = STAILQ_FIRST(&it->dvfree);
416			if(it->dvproc != NULL){
417				s = splfw();
418				STAILQ_REMOVE_HEAD(&it->dvfree, link);
419				splx(s);
420				it->dvptr = 0;
421			}else if(slept == 0){
422				slept = 1;
423				err = sc->fc->itx_enable(sc->fc, sub);
424				if(err){
425					return err;
426				}
427				err = tsleep((caddr_t)it, FWPRI, "fw_write", hz);
428				if(err){
429					return err;
430				}
431				goto dvloop;
432			}else{
433				err = EIO;
434				return err;
435			}
436		}
437#if 0 /* What's this for? (it->dvptr? overwritten by the following uiomove)*/
438		fp = (struct fw_pkt *)(it->dvproc->buf + it->queued * it->psize);
439		fp->mode.stream.len = htons(uio->uio_resid - sizeof(u_int32_t));
440#endif
441		err = uiomove(it->dvproc->buf + it->dvptr,
442							uio->uio_resid, uio);
443		it->dvptr += it->psize;
444		if(err){
445			return err;
446		}
447		if(it->dvptr >= it->psize * it->dvpacket){
448			s = splfw();
449			STAILQ_INSERT_TAIL(&it->dvvalid, it->dvproc, link);
450			splx(s);
451			it->dvproc = NULL;
452			err = fw_tbuf_update(sc->fc, sub, 0);
453			if(err){
454				return err;
455			}
456			err = sc->fc->itx_enable(sc->fc, sub);
457		}
458		return err;
459	}
460#endif
461	if(xferq != NULL){
462		xfer = fw_xfer_alloc();
463		if(xfer == NULL){
464			err = ENOMEM;
465			return err;
466		}
467		xfer->send.buf = malloc(uio->uio_resid, M_DEVBUF, M_NOWAIT);
468		if(xfer->send.buf == NULL){
469			fw_xfer_free( xfer);
470			err = ENOBUFS;
471			return err;
472		}
473		xfer->dst = ntohs(fp->mode.hdr.dst);
474#if 0
475		switch(fp->mode.common.tcode){
476		case FWTCODE_WREQQ:
477		case FWTCODE_WREQB:
478			if((tl = fw_get_tlabel(fc, xfer)) == -1 ){
479				fw_xfer_free( xfer);
480				err = EAGAIN;
481				return err;
482			}
483			fp->mode.hdr.tlrt = tl << 2;
484		default:
485			break;
486		}
487
488		xfer->tl = fp->mode.hdr.tlrt >> 2;
489		xfer->tcode = fp->mode.common.tcode;
490		xfer->fc = fc;
491		xfer->q = xferq;
492		xfer->act_type = FWACT_XFER;
493		xfer->retry_req = fw_asybusy;
494#endif
495		xfer->send.len = uio->uio_resid;
496		xfer->send.off = 0;
497		xfer->spd = 0;/* XXX: how to setup it */
498		xfer->act.hand = fw_asy_callback;
499
500		err = uiomove(xfer->send.buf, uio->uio_resid, uio);
501		if(err){
502			fw_xfer_free( xfer);
503			return err;
504		}
505#if 0
506		fw_asystart(xfer);
507#else
508		fw_asyreq(fc, -1, xfer);
509#endif
510		err = tsleep((caddr_t)xfer, FWPRI, "fw_write", hz);
511		if(xfer->resp == EBUSY)
512			return EBUSY;
513		fw_xfer_free( xfer);
514		return err;
515	}
516	return EINVAL;
517}
518
519/*
520 * ioctl support.
521 */
522int
523fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
524{
525	struct firewire_softc *sc;
526	int unit = DEV2UNIT(dev);
527	int sub = DEV2DMACH(dev);
528	int i, len, err = 0;
529	struct fw_device *fwdev;
530	struct fw_bind *fwb;
531	struct fw_xferq *ir, *it;
532	struct fw_xfer *xfer;
533	struct fw_pkt *fp;
534
535	struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
536	struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
537	struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
538	struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
539	struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
540#if 0
541	struct fw_map_buf *map_buf = (struct fw_map_buf *)data;
542#endif
543	struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
544
545	if (DEV_FWMEM(dev))
546		return fwmem_ioctl(dev, cmd, data, flag, td);
547
548	sc = devclass_get_softc(firewire_devclass, unit);
549	if (!data)
550		return(EINVAL);
551
552	switch (cmd) {
553	case FW_STSTREAM:
554		sc->fc->it[sub]->flag &= ~0xff;
555		sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
556		sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
557		err = 0;
558		break;
559	case FW_GTSTREAM:
560		ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
561		ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
562		err = 0;
563		break;
564	case FW_SRSTREAM:
565		sc->fc->ir[sub]->flag &= ~0xff;
566		sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
567		sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
568		err = sc->fc->irx_enable(sc->fc, sub);
569		break;
570	case FW_GRSTREAM:
571		ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
572		ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
573		err = 0;
574		break;
575#ifdef FWXFERQ_DV
576	case FW_SSTDV:
577		ibufreq = (struct fw_isobufreq *)
578			malloc(sizeof(struct fw_isobufreq), M_DEVBUF, M_NOWAIT);
579		if(ibufreq == NULL){
580			err = ENOMEM;
581			break;
582		}
583#if DV_PAL
584#define FWDVPACKET 300
585#else
586#define FWDVPACKET 250
587#endif
588#define FWDVPMAX 512
589		ibufreq->rx.nchunk = 8;
590		ibufreq->rx.npacket = 50;
591		ibufreq->rx.psize = FWDVPMAX;
592
593		ibufreq->tx.nchunk = 5;
594		ibufreq->tx.npacket = FWDVPACKET + 30;	/* > 320 or 267 */
595		ibufreq->tx.psize = FWDVPMAX;
596
597		err = fw_ioctl(dev, FW_SSTBUF, (caddr_t)ibufreq, flag, td);
598		sc->fc->it[sub]->dvpacket = FWDVPACKET;
599		free(ibufreq, M_DEVBUF);
600/* reserve a buffer space */
601#define NDVCHUNK 8
602		sc->fc->it[sub]->dvproc = NULL;
603		sc->fc->it[sub]->dvdma = NULL;
604		sc->fc->it[sub]->flag |= FWXFERQ_DV;
605		/* XXX check malloc failure */
606		sc->fc->it[sub]->dvbuf
607			= (struct fw_dvbuf *)malloc(sizeof(struct fw_dvbuf) * NDVCHUNK, M_DEVBUF, M_NOWAIT);
608		STAILQ_INIT(&sc->fc->it[sub]->dvvalid);
609		STAILQ_INIT(&sc->fc->it[sub]->dvfree);
610		for( i = 0 ; i < NDVCHUNK ; i++){
611			/* XXX check malloc failure */
612			sc->fc->it[sub]->dvbuf[i].buf
613				= malloc(FWDVPMAX * sc->fc->it[sub]->dvpacket, M_DEVBUF, M_NOWAIT);
614			STAILQ_INSERT_TAIL(&sc->fc->it[sub]->dvfree,
615					&sc->fc->it[sub]->dvbuf[i], link);
616		}
617		break;
618#endif
619	case FW_SSTBUF:
620		ir = sc->fc->ir[sub];
621		it = sc->fc->it[sub];
622
623		if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
624			return(EBUSY);
625		}
626		if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
627			return(EBUSY);
628		}
629		if((ibufreq->rx.nchunk *
630			ibufreq->rx.psize * ibufreq->rx.npacket) +
631		   (ibufreq->tx.nchunk *
632			ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
633				return(EINVAL);
634		}
635		if(ibufreq->rx.nchunk > FWSTMAXCHUNK ||
636				ibufreq->tx.nchunk > FWSTMAXCHUNK){
637			return(EINVAL);
638		}
639		ir->bulkxfer
640			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_DEVBUF, M_NOWAIT);
641		if(ir->bulkxfer == NULL){
642			return(ENOMEM);
643		}
644		it->bulkxfer
645			= (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_DEVBUF, M_NOWAIT);
646		if(it->bulkxfer == NULL){
647			return(ENOMEM);
648		}
649		ir->buf = malloc(
650			ibufreq->rx.nchunk * ibufreq->rx.npacket
651			/* XXX psize must be 2^n and less or
652						equal to PAGE_SIZE */
653			* ((ibufreq->rx.psize + 3) &~3),
654			M_DEVBUF, M_NOWAIT);
655		if(ir->buf == NULL){
656			free(ir->bulkxfer, M_DEVBUF);
657			free(it->bulkxfer, M_DEVBUF);
658			ir->bulkxfer = NULL;
659			it->bulkxfer = NULL;
660			it->buf = NULL;
661			return(ENOMEM);
662		}
663		it->buf = malloc(
664			ibufreq->tx.nchunk * ibufreq->tx.npacket
665			/* XXX psize must be 2^n and less or
666						equal to PAGE_SIZE */
667			* ((ibufreq->tx.psize + 3) &~3),
668			M_DEVBUF, M_NOWAIT);
669		if(it->buf == NULL){
670			free(ir->bulkxfer, M_DEVBUF);
671			free(it->bulkxfer, M_DEVBUF);
672			free(ir->buf, M_DEVBUF);
673			ir->bulkxfer = NULL;
674			it->bulkxfer = NULL;
675			it->buf = NULL;
676			return(ENOMEM);
677		}
678
679		ir->bnchunk = ibufreq->rx.nchunk;
680		ir->bnpacket = ibufreq->rx.npacket;
681		ir->btpacket = ibufreq->rx.npacket;
682		ir->psize = (ibufreq->rx.psize + 3) & ~3;
683		ir->queued = 0;
684
685		it->bnchunk = ibufreq->tx.nchunk;
686		it->bnpacket = ibufreq->tx.npacket;
687		it->btpacket = ibufreq->tx.npacket;
688		it->psize = (ibufreq->tx.psize + 3) & ~3;
689		ir->queued = 0;
690		it->dvdbc = 0;
691		it->dvdiff = 0;
692		it->dvsync = 0;
693		it->dvoffset = 0;
694
695		STAILQ_INIT(&ir->stvalid);
696		STAILQ_INIT(&ir->stfree);
697		ir->stdma = NULL;
698		ir->stdma2 = NULL;
699		ir->stproc = NULL;
700
701		STAILQ_INIT(&it->stvalid);
702		STAILQ_INIT(&it->stfree);
703		it->stdma = NULL;
704		it->stdma2 = NULL;
705		it->stproc = NULL;
706
707		for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
708			ir->bulkxfer[i].buf =
709				ir->buf +
710				i * sc->fc->ir[sub]->bnpacket *
711			  	sc->fc->ir[sub]->psize;
712			ir->bulkxfer[i].flag = 0;
713			STAILQ_INSERT_TAIL(&ir->stfree,
714					&ir->bulkxfer[i], link);
715			ir->bulkxfer[i].npacket = ir->bnpacket;
716		}
717		for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
718			it->bulkxfer[i].buf =
719				it->buf +
720				i * sc->fc->it[sub]->bnpacket *
721			  	sc->fc->it[sub]->psize;
722			it->bulkxfer[i].flag = 0;
723			STAILQ_INSERT_TAIL(&it->stfree,
724					&it->bulkxfer[i], link);
725			it->bulkxfer[i].npacket = it->bnpacket;
726		}
727		ir->flag &= ~FWXFERQ_MODEMASK;
728		ir->flag |= FWXFERQ_STREAM;
729		ir->flag |= FWXFERQ_EXTBUF;
730
731		it->flag &= ~FWXFERQ_MODEMASK;
732		it->flag |= FWXFERQ_STREAM;
733		it->flag |= FWXFERQ_EXTBUF;
734		err = 0;
735		break;
736	case FW_GSTBUF:
737		ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
738		ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
739		ibufreq->rx.psize = sc->fc->ir[sub]->psize;
740
741		ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
742		ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
743		ibufreq->tx.psize = sc->fc->it[sub]->psize;
744		break;
745	case FW_ASYREQ:
746		xfer = fw_xfer_alloc();
747		if(xfer == NULL){
748			err = ENOMEM;
749			return err;
750		}
751		fp = &asyreq->pkt;
752		switch (asyreq->req.type) {
753		case FWASREQNODE:
754			xfer->dst = ntohs(fp->mode.hdr.dst);
755			break;
756		case FWASREQEUI:
757			fwdev = fw_noderesolve(sc->fc, asyreq->req.dst.eui);
758			if (fwdev == NULL) {
759				device_printf(sc->fc->bdev,
760					"cannot find node\n");
761				err = EINVAL;
762				goto error;
763			}
764			xfer->dst = fwdev->dst;
765			fp->mode.hdr.dst = htons(FWLOCALBUS | xfer->dst);
766			break;
767		case FWASRESTL:
768			/* XXX what's this? */
769			break;
770		case FWASREQSTREAM:
771			/* nothing to do */
772			break;
773		}
774		xfer->spd = asyreq->req.sped;
775		xfer->send.len = asyreq->req.len;
776		xfer->send.buf = malloc(xfer->send.len, M_DEVBUF, M_NOWAIT);
777		if(xfer->send.buf == NULL){
778			return ENOMEM;
779		}
780		xfer->send.off = 0;
781		bcopy(fp, xfer->send.buf, xfer->send.len);
782		xfer->act.hand = fw_asy_callback;
783		err = fw_asyreq(sc->fc, sub, xfer);
784		if(err){
785			fw_xfer_free( xfer);
786			return err;
787		}
788		err = tsleep((caddr_t)xfer, FWPRI, "asyreq", hz);
789		if(err == 0){
790			if(asyreq->req.len >= xfer->recv.len){
791				asyreq->req.len = xfer->recv.len;
792			}else{
793				err = EINVAL;
794			}
795			bcopy(xfer->recv.buf + xfer->recv.off, fp, asyreq->req.len);
796		}
797error:
798		fw_xfer_free( xfer);
799		break;
800	case FW_IBUSRST:
801		sc->fc->ibr(sc->fc);
802		break;
803	case FW_CBINDADDR:
804		fwb = fw_bindlookup(sc->fc,
805				bindreq->start.hi, bindreq->start.lo);
806		if(fwb == NULL){
807			err = EINVAL;
808			break;
809		}
810		STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
811		STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
812		free(fwb, M_DEVBUF);
813		break;
814	case FW_SBINDADDR:
815		if(bindreq->len <= 0 ){
816			err = EINVAL;
817			break;
818		}
819		if(bindreq->start.hi > 0xffff ){
820			err = EINVAL;
821			break;
822		}
823		fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_DEVBUF, M_NOWAIT);
824		if(fwb == NULL){
825			err = ENOMEM;
826			break;
827		}
828		fwb->start_hi = bindreq->start.hi;
829		fwb->start_lo = bindreq->start.lo;
830		fwb->addrlen = bindreq->len;
831
832		xfer = fw_xfer_alloc();
833		if(xfer == NULL){
834			err = ENOMEM;
835			return err;
836		}
837		xfer->act_type = FWACT_CH;
838		xfer->sub = sub;
839		xfer->fc = sc->fc;
840
841		fwb->xfer = xfer;
842		err = fw_bindadd(sc->fc, fwb);
843		break;
844	case FW_GDEVLST:
845		i = 0;
846		for(fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL;
847			fwdev = TAILQ_NEXT(fwdev, link)){
848			if(i < fwdevlst->n){
849				fwdevlst->dst[i] = fwdev->dst;
850				fwdevlst->status[i] =
851					(fwdev->status == FWDEVATTACHED)?1:0;
852				fwdevlst->eui[i].hi = fwdev->eui.hi;
853				fwdevlst->eui[i].lo = fwdev->eui.lo;
854			}
855			i++;
856		}
857		fwdevlst->n = i;
858		break;
859	case FW_GTPMAP:
860		bcopy(sc->fc->topology_map, data,
861				(sc->fc->topology_map->crc_len + 1) * 4);
862		break;
863	case FW_GCROM:
864		for (fwdev = TAILQ_FIRST(&sc->fc->devices); fwdev != NULL;
865			fwdev = TAILQ_NEXT(fwdev, link)) {
866			if (fwdev->eui.hi == crom_buf->eui.hi &&
867					fwdev->eui.lo == crom_buf->eui.lo)
868				break;
869		}
870		if (fwdev == NULL) {
871			err = FWNODE_INVAL;
872			break;
873		}
874#if 0
875		if (fwdev->csrrom[0] >> 24 == 1)
876			len = 4;
877		else
878			len = (1 + ((fwdev->csrrom[0] >> 16) & 0xff)) * 4;
879#else
880		if (fwdev->rommax < CSRROMOFF)
881			len = 0;
882		else
883			len = fwdev->rommax - CSRROMOFF + 4;
884#endif
885		if (crom_buf->len < len)
886			len = crom_buf->len;
887		else
888			crom_buf->len = len;
889		err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
890		break;
891	default:
892		sc->fc->ioctl (dev, cmd, data, flag, td);
893		break;
894	}
895	return err;
896}
897int
898fw_poll(dev_t dev, int events, fw_proc *td)
899{
900	int revents;
901	int tmp;
902	int unit = DEV2UNIT(dev);
903	int sub = DEV2DMACH(dev);
904	struct firewire_softc *sc;
905
906	if (DEV_FWMEM(dev))
907		return fwmem_poll(dev, events, td);
908
909	sc = devclass_get_softc(firewire_devclass, unit);
910	revents = 0;
911	tmp = POLLIN | POLLRDNORM;
912	if (events & tmp) {
913		if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
914			revents |= tmp;
915		else
916			selrecord(td, &sc->fc->ir[sub]->rsel);
917	}
918	tmp = POLLOUT | POLLWRNORM;
919	if (events & tmp) {
920		/* XXX should be fixed */
921		revents |= tmp;
922	}
923
924	return revents;
925}
926
927static int
928fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
929{
930	struct firewire_softc *fc;
931	int unit = DEV2UNIT(dev);
932
933	if (DEV_FWMEM(dev))
934		return fwmem_mmap(dev, offset, nproto);
935
936	fc = devclass_get_softc(firewire_devclass, unit);
937
938	return EINVAL;
939}
940