Merge from vendor branch GROFF:
[dragonfly.git] / sys / bus / firewire / fwdev.c
1 /*
2  * Copyright (c) 2003 Hidetoshi Shimokawa
3  * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the acknowledgement as bellow:
16  *
17  *    This product includes software developed by K. Kobayashi and H. Shimokawa
18  *
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
26  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
30  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  * 
34  * $FreeBSD: src/sys/dev/firewire/fwdev.c,v 1.36 2004/01/22 14:41:17 simokawa Exp $
35  * $DragonFly: src/sys/bus/firewire/fwdev.c,v 1.10 2005/06/02 20:40:33 dillon Exp $
36  *
37  */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
42 #include <sys/mbuf.h>
43 #if defined(__DragonFly__) || __FreeBSD_version < 500000
44 #include <sys/buf.h>
45 #else
46 #include <sys/bio.h>
47 #endif
48
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/conf.h>
52 #include <sys/poll.h>
53
54 #include <sys/bus.h>
55 #include <sys/ctype.h>
56 #include <machine/bus.h>
57
58 #include <sys/ioccom.h>
59 #include <sys/thread2.h>
60
61 #ifdef __DragonFly__
62 #include "firewire.h"
63 #include "firewirereg.h"
64 #include "fwdma.h"
65 #include "fwmem.h"
66 #include "iec68113.h"
67 #else
68 #include <dev/firewire/firewire.h>
69 #include <dev/firewire/firewirereg.h>
70 #include <dev/firewire/fwdma.h>
71 #include <dev/firewire/fwmem.h>
72 #include <dev/firewire/iec68113.h>
73 #endif
74
75 #define CDEV_MAJOR 127
76 #define FWNODE_INVAL 0xffff
77
78 static  d_open_t        fw_open;
79 static  d_close_t       fw_close;
80 static  d_ioctl_t       fw_ioctl;
81 static  d_poll_t        fw_poll;
82 static  d_read_t        fw_read;        /* for Isochronous packet */
83 static  d_write_t       fw_write;
84 static  d_mmap_t        fw_mmap;
85 static  d_strategy_t    fw_strategy;
86
87 struct cdevsw firewire_cdevsw = 
88 {
89 #ifdef __DragonFly__
90         "fw", CDEV_MAJOR, D_MEM, NULL, 0,
91         fw_open, fw_close, fw_read, fw_write, fw_ioctl,
92         fw_poll, fw_mmap, fw_strategy, nodump, nopsize,
93 #elif __FreeBSD_version >= 500104
94         .d_open =       fw_open,
95         .d_close =      fw_close,
96         .d_read =       fw_read,
97         .d_write =      fw_write,
98         .d_ioctl =      fw_ioctl,
99         .d_poll =       fw_poll,
100         .d_mmap =       fw_mmap,
101         .d_strategy =   fw_strategy,
102         .d_name =       "fw",
103         .d_maj =        CDEV_MAJOR,
104         .d_flags =      D_MEM
105 #else
106         fw_open, fw_close, fw_read, fw_write, fw_ioctl,
107         fw_poll, fw_mmap, fw_strategy, "fw", CDEV_MAJOR,
108         nodump, nopsize, D_MEM, -1
109 #endif
110 };
111
112 struct fw_drv1 {
113         struct fw_xferq *ir;
114         struct fw_xferq *it;
115         struct fw_isobufreq bufreq;
116 };
117
118 static int
119 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
120         struct fw_bufspec *b)
121 {
122         int i;
123
124         if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
125                 return(EBUSY);
126
127         q->bulkxfer = (struct fw_bulkxfer *) malloc(
128                 sizeof(struct fw_bulkxfer) * b->nchunk,
129                 M_FW, M_WAITOK);
130         if (q->bulkxfer == NULL)
131                 return(ENOMEM);
132
133         b->psize = roundup2(b->psize, sizeof(u_int32_t));
134         q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t),
135                         b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
136
137         if (q->buf == NULL) {
138                 free(q->bulkxfer, M_FW);
139                 q->bulkxfer = NULL;
140                 return(ENOMEM);
141         }
142         q->bnchunk = b->nchunk;
143         q->bnpacket = b->npacket;
144         q->psize = (b->psize + 3) & ~3;
145         q->queued = 0;
146
147         STAILQ_INIT(&q->stvalid);
148         STAILQ_INIT(&q->stfree);
149         STAILQ_INIT(&q->stdma);
150         q->stproc = NULL;
151
152         for(i = 0 ; i < q->bnchunk; i++){
153                 q->bulkxfer[i].poffset = i * q->bnpacket;
154                 q->bulkxfer[i].mbuf = NULL;
155                 STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
156         }
157
158         q->flag &= ~FWXFERQ_MODEMASK;
159         q->flag |= FWXFERQ_STREAM;
160         q->flag |= FWXFERQ_EXTBUF;
161
162         return (0);
163 }
164
165 static int
166 fwdev_freebuf(struct fw_xferq *q)
167 {
168         if (q->flag & FWXFERQ_EXTBUF) {
169                 if (q->buf != NULL)
170                         fwdma_free_multiseg(q->buf);
171                 q->buf = NULL;
172                 free(q->bulkxfer, M_FW);
173                 q->bulkxfer = NULL;
174                 q->flag &= ~FWXFERQ_EXTBUF;
175                 q->psize = 0;
176                 q->maxq = FWMAXQUEUE;
177         }
178         return (0);
179 }
180
181
182 static int
183 fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
184 {
185         int err = 0;
186
187         if (DEV_FWMEM(dev))
188                 return fwmem_open(dev, flags, fmt, td);
189
190         if (dev->si_drv1 != NULL)
191                 return (EBUSY);
192
193 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
194         if ((dev->si_flags & SI_NAMED) == 0) {
195                 int unit = DEV2UNIT(dev);
196                 int sub = DEV2SUB(dev);
197
198                 make_dev(&firewire_cdevsw, minor(dev),
199                         UID_ROOT, GID_OPERATOR, 0660,
200                         "fw%d.%d", unit, sub);
201         }
202 #endif
203
204         dev->si_drv1 = malloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
205
206         return err;
207 }
208
209 static int
210 fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
211 {
212         struct firewire_softc *sc;
213         struct firewire_comm *fc;
214         struct fw_drv1 *d;
215         int unit = DEV2UNIT(dev);
216         struct fw_xfer *xfer;
217         struct fw_bind *fwb;
218         int err = 0;
219
220         if (DEV_FWMEM(dev))
221                 return fwmem_close(dev, flags, fmt, td);
222
223         sc = devclass_get_softc(firewire_devclass, unit);
224         fc = sc->fc;
225         d = (struct fw_drv1 *)dev->si_drv1;
226
227         if (d->ir != NULL) {
228                 struct fw_xferq *ir = d->ir;
229
230                 if ((ir->flag & FWXFERQ_OPEN) == 0)
231                         return (EINVAL);
232                 if (ir->flag & FWXFERQ_RUNNING) {
233                         ir->flag &= ~FWXFERQ_RUNNING;
234                         fc->irx_disable(fc, ir->dmach);
235                 }
236                 /* free extbuf */
237                 fwdev_freebuf(ir);
238                 /* drain receiving buffer */
239                 for (xfer = STAILQ_FIRST(&ir->q);
240                         xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
241                         ir->queued --;
242                         STAILQ_REMOVE_HEAD(&ir->q, link);
243
244                         xfer->resp = 0;
245                         fw_xfer_done(xfer);
246                 }
247                 /* remove binding */
248                 for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL;
249                                 fwb = STAILQ_FIRST(&ir->binds)) {
250                         STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
251                         STAILQ_REMOVE_HEAD(&ir->binds, chlist);
252                         free(fwb, M_FW);
253                 }
254                 ir->flag &= ~(FWXFERQ_OPEN |
255                         FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
256                 d->ir = NULL;
257
258         }
259         if (d->it != NULL) {
260                 struct fw_xferq *it = d->it;
261
262                 if ((it->flag & FWXFERQ_OPEN) == 0)
263                         return (EINVAL);
264                 if (it->flag & FWXFERQ_RUNNING) {
265                         it->flag &= ~FWXFERQ_RUNNING;
266                         fc->itx_disable(fc, it->dmach);
267                 }
268                 /* free extbuf */
269                 fwdev_freebuf(it);
270                 it->flag &= ~(FWXFERQ_OPEN |
271                         FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
272                 d->it = NULL;
273         }
274         free(dev->si_drv1, M_FW);
275         dev->si_drv1 = NULL;
276
277         return err;
278 }
279
280 /*
281  * read request.
282  */
283 static int
284 fw_read (dev_t dev, struct uio *uio, int ioflag)
285 {
286         struct firewire_softc *sc;
287         struct fw_xferq *ir;
288         struct fw_xfer *xfer;
289         int err = 0, slept = 0;
290         int unit = DEV2UNIT(dev);
291         struct fw_pkt *fp;
292
293         if (DEV_FWMEM(dev))
294                 return physio(dev, uio, ioflag);
295
296         sc = devclass_get_softc(firewire_devclass, unit);
297
298         ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
299         if (ir == NULL || ir->buf == NULL)
300                 return (EIO);
301
302 readloop:
303         xfer = STAILQ_FIRST(&ir->q);
304         if (ir->stproc == NULL) {
305                 /* iso bulkxfer */
306                 ir->stproc = STAILQ_FIRST(&ir->stvalid);
307                 if (ir->stproc != NULL) {
308                         crit_enter();
309                         STAILQ_REMOVE_HEAD(&ir->stvalid, link);
310                         crit_exit();
311                         ir->queued = 0;
312                 }
313         }
314         if (xfer == NULL && ir->stproc == NULL) {
315                 /* no data avaliable */
316                 if (slept == 0) {
317                         slept = 1;
318                         ir->flag |= FWXFERQ_WAKEUP;
319                         err = tsleep(ir, FWPRI, "fw_read", hz);
320                         ir->flag &= ~FWXFERQ_WAKEUP;
321                         if (err == 0)
322                                 goto readloop;
323                 } else if (slept == 1)
324                         err = EIO;
325                 return err;
326         } else if(xfer != NULL) {
327 #if 0 /* XXX broken */
328                 /* per packet mode or FWACT_CH bind?*/
329                 crit_enter();
330                 ir->queued --;
331                 STAILQ_REMOVE_HEAD(&ir->q, link);
332                 crit_exit();
333                 fp = &xfer->recv.hdr;
334                 if (sc->fc->irx_post != NULL)
335                         sc->fc->irx_post(sc->fc, fp->mode.ld);
336                 err = uiomove((void *)fp, 1 /* XXX header size */, uio);
337                 /* XXX copy payload too */
338                 /* XXX we should recycle this xfer */
339 #endif
340                 fw_xfer_free( xfer);
341         } else if(ir->stproc != NULL) {
342                 /* iso bulkxfer */
343                 fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 
344                                 ir->stproc->poffset + ir->queued);
345                 if(sc->fc->irx_post != NULL)
346                         sc->fc->irx_post(sc->fc, fp->mode.ld);
347                 if(fp->mode.stream.len == 0){
348                         err = EIO;
349                         return err;
350                 }
351                 err = uiomove((caddr_t)fp,
352                         fp->mode.stream.len + sizeof(u_int32_t), uio);
353                 ir->queued ++;
354                 if(ir->queued >= ir->bnpacket){
355                         crit_enter();
356                         STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
357                         crit_exit();
358                         sc->fc->irx_enable(sc->fc, ir->dmach);
359                         ir->stproc = NULL;
360                 }
361                 if (uio->uio_resid >= ir->psize) {
362                         slept = -1;
363                         goto readloop;
364                 }
365         }
366         return err;
367 }
368
369 static int
370 fw_write (dev_t dev, struct uio *uio, int ioflag)
371 {
372         int err = 0;
373         struct firewire_softc *sc;
374         int unit = DEV2UNIT(dev);
375         int slept = 0;
376         struct fw_pkt *fp;
377         struct firewire_comm *fc;
378         struct fw_xferq *it;
379
380         if (DEV_FWMEM(dev))
381                 return physio(dev, uio, ioflag);
382
383         sc = devclass_get_softc(firewire_devclass, unit);
384         fc = sc->fc;
385         it = ((struct fw_drv1 *)dev->si_drv1)->it;
386         if (it == NULL || it->buf == NULL)
387                 return (EIO);
388 isoloop:
389         if (it->stproc == NULL) {
390                 it->stproc = STAILQ_FIRST(&it->stfree);
391                 if (it->stproc != NULL) {
392                         crit_enter();
393                         STAILQ_REMOVE_HEAD(&it->stfree, link);
394                         crit_exit();
395                         it->queued = 0;
396                 } else if (slept == 0) {
397                         slept = 1;
398                         err = sc->fc->itx_enable(sc->fc, it->dmach);
399                         if (err)
400                                 return err;
401                         err = tsleep(it, FWPRI, "fw_write", hz);
402                         if (err)
403                                 return err;
404                         goto isoloop;
405                 } else {
406                         err = EIO;
407                         return err;
408                 }
409         }
410         fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
411                         it->stproc->poffset + it->queued);
412         err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
413         err = uiomove((caddr_t)fp->mode.stream.payload,
414                                 fp->mode.stream.len, uio);
415         it->queued ++;
416         if (it->queued >= it->bnpacket) {
417                 crit_enter();
418                 STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
419                 crit_exit();
420                 it->stproc = NULL;
421                 err = sc->fc->itx_enable(sc->fc, it->dmach);
422         }
423         if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
424                 slept = 0;
425                 goto isoloop;
426         }
427         return err;
428 }
429 /*
430  * ioctl support.
431  */
432 int
433 fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
434 {
435         struct firewire_softc *sc;
436         struct firewire_comm *fc;
437         struct fw_drv1 *d;
438         int unit = DEV2UNIT(dev);
439         int i, len, err = 0;
440         struct fw_device *fwdev;
441         struct fw_bind *fwb;
442         struct fw_xferq *ir, *it;
443         struct fw_xfer *xfer;
444         struct fw_pkt *fp;
445         struct fw_devinfo *devinfo;
446         void *ptr;
447
448         struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
449         struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
450         struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
451         struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
452         struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
453         struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
454
455         if (DEV_FWMEM(dev))
456                 return fwmem_ioctl(dev, cmd, data, flag, td);
457
458         if (!data)
459                 return(EINVAL);
460
461         sc = devclass_get_softc(firewire_devclass, unit);
462         fc = sc->fc;
463         d = (struct fw_drv1 *)dev->si_drv1;
464         ir = d->ir;
465         it = d->it;
466
467         switch (cmd) {
468         case FW_STSTREAM:
469                 if (it == NULL) {
470                         for (i = 0; i < fc->nisodma; i ++) {
471                                 it = fc->it[i];
472                                 if ((it->flag & FWXFERQ_OPEN) == 0)
473                                          break;
474                         }       
475                         if (i >= fc->nisodma) {
476                                 err = EBUSY;
477                                 break;
478                         }
479                         err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
480                         if (err)
481                                 break;
482                         it->flag |=  FWXFERQ_OPEN;
483                 }
484                 it->flag &= ~0xff;
485                 it->flag |= (0x3f & ichreq->ch);
486                 it->flag |= ((0x3 & ichreq->tag) << 6);
487                 d->it = it;
488                 break;
489         case FW_GTSTREAM:
490                 if (it != NULL) {
491                         ichreq->ch = it->flag & 0x3f;
492                         ichreq->tag = it->flag >> 2 & 0x3;
493                 } else
494                         err = EINVAL;
495                 break;
496         case FW_SRSTREAM:
497                 if (ir == NULL) {
498                         for (i = 0; i < fc->nisodma; i ++) {
499                                 ir = fc->ir[i];
500                                 if ((ir->flag & FWXFERQ_OPEN) == 0)
501                                         break;
502                         }       
503                         if (i >= fc->nisodma) {
504                                 err = EBUSY;
505                                 break;
506                         }
507                         err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
508                         if (err)
509                                 break;
510                         ir->flag |=  FWXFERQ_OPEN;
511                 }
512                 ir->flag &= ~0xff;
513                 ir->flag |= (0x3f & ichreq->ch);
514                 ir->flag |= ((0x3 & ichreq->tag) << 6);
515                 d->ir = ir;
516                 err = fc->irx_enable(fc, ir->dmach);
517                 break;
518         case FW_GRSTREAM:
519                 if (d->ir != NULL) {
520                         ichreq->ch = ir->flag & 0x3f;
521                         ichreq->tag = ir->flag >> 2 & 0x3;
522                 } else
523                         err = EINVAL;
524                 break;
525         case FW_SSTBUF:
526                 bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
527                 break;
528         case FW_GSTBUF:
529                 bzero(&ibufreq->rx, sizeof(ibufreq->rx));
530                 if (ir != NULL) {
531                         ibufreq->rx.nchunk = ir->bnchunk;
532                         ibufreq->rx.npacket = ir->bnpacket;
533                         ibufreq->rx.psize = ir->psize;
534                 }
535                 bzero(&ibufreq->tx, sizeof(ibufreq->tx));
536                 if (it != NULL) {
537                         ibufreq->tx.nchunk = it->bnchunk;
538                         ibufreq->tx.npacket = it->bnpacket;
539                         ibufreq->tx.psize = it->psize;
540                 }
541                 break;
542         case FW_ASYREQ:
543         {
544                 struct tcode_info *tinfo;
545                 int pay_len = 0;
546
547                 fp = &asyreq->pkt;
548                 tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
549
550                 if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
551                         pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
552
553                 xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
554                 if (xfer == NULL)
555                         return (ENOMEM);
556
557                 switch (asyreq->req.type) {
558                 case FWASREQNODE:
559                         break;
560                 case FWASREQEUI:
561                         fwdev = fw_noderesolve_eui64(sc->fc,
562                                                 &asyreq->req.dst.eui);
563                         if (fwdev == NULL) {
564                                 device_printf(sc->fc->bdev,
565                                         "cannot find node\n");
566                                 err = EINVAL;
567                                 goto out;
568                         }
569                         fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
570                         break;
571                 case FWASRESTL:
572                         /* XXX what's this? */
573                         break;
574                 case FWASREQSTREAM:
575                         /* nothing to do */
576                         break;
577                 }
578
579                 bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
580                 if (pay_len > 0)
581                         bcopy((char *)fp + tinfo->hdr_len,
582                             (void *)&xfer->send.payload, pay_len);
583                 xfer->send.spd = asyreq->req.sped;
584                 xfer->act.hand = fw_asy_callback;
585
586                 if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
587                         goto out;
588                 if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
589                         goto out;
590                 if (xfer->resp != 0) {
591                         err = EIO;
592                         goto out;
593                 }
594                 if ((tinfo->flag & FWTI_TLABEL) == 0)
595                         goto out;
596
597                 /* copy response */
598                 tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
599                 if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len)
600                         asyreq->req.len = xfer->recv.pay_len;
601                 else
602                         err = EINVAL;
603                 bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
604                 bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len,
605                     MAX(0, asyreq->req.len - tinfo->hdr_len));
606 out:
607                 fw_xfer_free_buf(xfer);
608                 break;
609         }
610         case FW_IBUSRST:
611                 sc->fc->ibr(sc->fc);
612                 break;
613         case FW_CBINDADDR:
614                 fwb = fw_bindlookup(sc->fc,
615                                 bindreq->start.hi, bindreq->start.lo);
616                 if(fwb == NULL){
617                         err = EINVAL;
618                         break;
619                 }
620                 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
621                 STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
622                 free(fwb, M_FW);
623                 break;
624         case FW_SBINDADDR:
625                 if(bindreq->len <= 0 ){
626                         err = EINVAL;
627                         break;
628                 }
629                 if(bindreq->start.hi > 0xffff ){
630                         err = EINVAL;
631                         break;
632                 }
633                 fwb = malloc(sizeof (struct fw_bind), M_FW, M_WAITOK);
634                 fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
635                     bindreq->start.lo;
636                 fwb->end = fwb->start +  bindreq->len;
637                 /* XXX */
638                 fwb->sub = ir->dmach;
639                 fwb->act_type = FWACT_CH;
640
641                 /* XXX alloc buf */
642                 xfer = fw_xfer_alloc(M_FWXFER);
643                 if(xfer == NULL){
644                         free(fwb, M_FW);
645                         return (ENOMEM);
646                 }
647                 xfer->fc = sc->fc;
648
649                 crit_enter();
650                 /* XXX broken. need multiple xfer */
651                 STAILQ_INIT(&fwb->xferlist);
652                 STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
653                 crit_exit();
654                 err = fw_bindadd(sc->fc, fwb);
655                 break;
656         case FW_GDEVLST:
657                 i = len = 1;
658                 /* myself */
659                 devinfo = &fwdevlst->dev[0];
660                 devinfo->dst = sc->fc->nodeid;
661                 devinfo->status = 0;    /* XXX */
662                 devinfo->eui.hi = sc->fc->eui.hi;
663                 devinfo->eui.lo = sc->fc->eui.lo;
664                 STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
665                         if(len < FW_MAX_DEVLST){
666                                 devinfo = &fwdevlst->dev[len++];
667                                 devinfo->dst = fwdev->dst;
668                                 devinfo->status = 
669                                         (fwdev->status == FWDEVINVAL)?0:1;
670                                 devinfo->eui.hi = fwdev->eui.hi;
671                                 devinfo->eui.lo = fwdev->eui.lo;
672                         }
673                         i++;
674                 }
675                 fwdevlst->n = i;
676                 fwdevlst->info_len = len;
677                 break;
678         case FW_GTPMAP:
679                 bcopy(sc->fc->topology_map, data,
680                                 (sc->fc->topology_map->crc_len + 1) * 4);
681                 break;
682         case FW_GCROM:
683                 STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
684                         if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
685                                 break;
686                 if (fwdev == NULL) {
687                         if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
688                                 err = FWNODE_INVAL;
689                                 break;
690                         }
691                         /* myself */
692                         ptr = malloc(CROMSIZE, M_FW, M_WAITOK);
693                         len = CROMSIZE;
694                         for (i = 0; i < CROMSIZE/4; i++)
695                                 ((u_int32_t *)ptr)[i]
696                                         = ntohl(sc->fc->config_rom[i]);
697                 } else {
698                         /* found */
699                         ptr = (void *)&fwdev->csrrom[0];
700                         if (fwdev->rommax < CSRROMOFF)
701                                 len = 0;
702                         else
703                                 len = fwdev->rommax - CSRROMOFF + 4;
704                 }
705                 if (crom_buf->len < len)
706                         len = crom_buf->len;
707                 else
708                         crom_buf->len = len;
709                 err = copyout(ptr, crom_buf->ptr, len);
710                 if (fwdev == NULL)
711                         /* myself */
712                         free(ptr, M_FW);
713                 break;
714         default:
715                 sc->fc->ioctl (dev, cmd, data, flag, td);
716                 break;
717         }
718         return err;
719 }
720 int
721 fw_poll(dev_t dev, int events, fw_proc *td)
722 {
723         struct firewire_softc *sc;
724         struct fw_xferq *ir;
725         int revents;
726         int tmp;
727         int unit = DEV2UNIT(dev);
728
729         if (DEV_FWMEM(dev))
730                 return fwmem_poll(dev, events, td);
731
732         sc = devclass_get_softc(firewire_devclass, unit);
733         ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
734         revents = 0;
735         tmp = POLLIN | POLLRDNORM;
736         if (events & tmp) {
737                 if (STAILQ_FIRST(&ir->q) != NULL)
738                         revents |= tmp;
739                 else
740                         selrecord(td, &ir->rsel);
741         }
742         tmp = POLLOUT | POLLWRNORM;
743         if (events & tmp) {
744                 /* XXX should be fixed */       
745                 revents |= tmp;
746         }
747
748         return revents;
749 }
750
751 static int
752 #if defined(__DragonFly__) || __FreeBSD_version < 500102
753 fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
754 #else
755 fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
756 #endif
757 {  
758         struct firewire_softc *sc;
759         int unit = DEV2UNIT(dev);
760
761         if (DEV_FWMEM(dev))
762 #if defined(__DragonFly__) || __FreeBSD_version < 500102
763                 return fwmem_mmap(dev, offset, nproto);
764 #else
765                 return fwmem_mmap(dev, offset, paddr, nproto);
766 #endif
767
768         sc = devclass_get_softc(firewire_devclass, unit);
769
770         return EINVAL;
771 }
772
773 static void
774 fw_strategy(struct bio *bp)
775 {
776         dev_t dev;
777
778         dev = bp->bio_dev;
779         if (DEV_FWMEM(dev)) {
780                 fwmem_strategy(bp);
781                 return;
782         }
783
784         bp->bio_error = EOPNOTSUPP;
785         bp->bio_flags |= BIO_ERROR;
786         bp->bio_resid = bp->bio_bcount;
787         biodone(bp);
788 }
789
790 int
791 fwdev_makedev(struct firewire_softc *sc)
792 {
793         int unit;
794
795         unit = device_get_unit(sc->fc->bdev);
796         cdevsw_add(&firewire_cdevsw, FW_UNITMASK, FW_UNIT(unit));
797         return(0);
798 }
799
800 int
801 fwdev_destroydev(struct firewire_softc *sc)
802 {
803         int unit;
804
805         unit = device_get_unit(sc->fc->bdev);
806         cdevsw_remove(&firewire_cdevsw, FW_UNITMASK, FW_UNIT(unit));
807         return(0);
808 }
809
810 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
811 #define NDEVTYPE 2
812 void
813 fwdev_clone(void *arg, char *name, int namelen, dev_t *dev)
814 {
815         struct firewire_softc *sc;
816         char *devnames[NDEVTYPE] = {"fw", "fwmem"};
817         char *subp = NULL;
818         int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
819         int i, unit = 0, sub = 0;
820
821         if (*dev != NODEV)
822                 return;
823
824         for (i = 0; i < NDEVTYPE; i++)
825                 if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
826                         goto found;
827         /* not match */
828         return;
829 found:
830
831         if (subp == NULL || *subp++ != '.')
832                 return;
833
834         /* /dev/fwU.S */
835         while (isdigit(*subp)) {
836                 sub *= 10;
837                 sub += *subp++ - '0';
838         }
839         if (*subp != '\0')
840                 return;
841
842         sc = devclass_get_softc(firewire_devclass, unit);
843         if (sc == NULL)
844                 return;
845         *dev = make_dev(&firewire_cdevsw, MAKEMINOR(devflag[i], unit, sub),
846                        UID_ROOT, GID_OPERATOR, 0660,
847                        "%s%d.%d", devnames[i], unit, sub);
848         (*dev)->si_flags |= SI_CHEAPCLONE;
849         dev_depends(sc->dev, *dev);
850         return;
851 }
852 #endif