Merge from vendor branch GDB:
[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.20 2008/01/06 16:55:49 swildner 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
57 #include <sys/ioccom.h>
58 #include <sys/thread2.h>
59
60 #ifdef __DragonFly__
61 #include "firewire.h"
62 #include "firewirereg.h"
63 #include "fwdma.h"
64 #include "fwmem.h"
65 #include "iec68113.h"
66 #else
67 #include <dev/firewire/firewire.h>
68 #include <dev/firewire/firewirereg.h>
69 #include <dev/firewire/fwdma.h>
70 #include <dev/firewire/fwmem.h>
71 #include <dev/firewire/iec68113.h>
72 #endif
73
74 #define CDEV_MAJOR 127
75 #define FWNODE_INVAL 0xffff
76
77 static  d_open_t        fw_open;
78 static  d_close_t       fw_close;
79 static  d_ioctl_t       fw_ioctl;
80 static  d_poll_t        fw_poll;
81 static  d_read_t        fw_read;        /* for Isochronous packet */
82 static  d_write_t       fw_write;
83 static  d_mmap_t        fw_mmap;
84 static  d_strategy_t    fw_strategy;
85
86 struct dev_ops firewire_ops = 
87 {
88         { "fw", CDEV_MAJOR, D_MEM },
89         .d_open =       fw_open,
90         .d_close =      fw_close,
91         .d_read =       fw_read,
92         .d_write =      fw_write,
93         .d_ioctl =      fw_ioctl,
94         .d_poll =       fw_poll,
95         .d_mmap =       fw_mmap,
96         .d_strategy =   fw_strategy,
97 };
98
99 struct fw_drv1 {
100         struct fw_xferq *ir;
101         struct fw_xferq *it;
102         struct fw_isobufreq bufreq;
103 };
104
105 static int
106 fwdev_allocbuf(struct firewire_comm *fc, struct fw_xferq *q,
107         struct fw_bufspec *b)
108 {
109         int i;
110
111         if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF))
112                 return(EBUSY);
113
114         q->bulkxfer = (struct fw_bulkxfer *) kmalloc(
115                 sizeof(struct fw_bulkxfer) * b->nchunk,
116                 M_FW, M_WAITOK);
117
118         b->psize = roundup2(b->psize, sizeof(u_int32_t));
119         q->buf = fwdma_malloc_multiseg(fc, sizeof(u_int32_t),
120                         b->psize, b->nchunk * b->npacket, BUS_DMA_WAITOK);
121
122         if (q->buf == NULL) {
123                 kfree(q->bulkxfer, M_FW);
124                 q->bulkxfer = NULL;
125                 return(ENOMEM);
126         }
127         q->bnchunk = b->nchunk;
128         q->bnpacket = b->npacket;
129         q->psize = (b->psize + 3) & ~3;
130         q->queued = 0;
131
132         STAILQ_INIT(&q->stvalid);
133         STAILQ_INIT(&q->stfree);
134         STAILQ_INIT(&q->stdma);
135         q->stproc = NULL;
136
137         for(i = 0 ; i < q->bnchunk; i++){
138                 q->bulkxfer[i].poffset = i * q->bnpacket;
139                 q->bulkxfer[i].mbuf = NULL;
140                 STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link);
141         }
142
143         q->flag &= ~FWXFERQ_MODEMASK;
144         q->flag |= FWXFERQ_STREAM;
145         q->flag |= FWXFERQ_EXTBUF;
146
147         return (0);
148 }
149
150 static int
151 fwdev_freebuf(struct fw_xferq *q)
152 {
153         if (q->flag & FWXFERQ_EXTBUF) {
154                 if (q->buf != NULL)
155                         fwdma_free_multiseg(q->buf);
156                 q->buf = NULL;
157                 kfree(q->bulkxfer, M_FW);
158                 q->bulkxfer = NULL;
159                 q->flag &= ~FWXFERQ_EXTBUF;
160                 q->psize = 0;
161                 q->maxq = FWMAXQUEUE;
162         }
163         return (0);
164 }
165
166
167 static int
168 fw_open (struct dev_open_args *ap)
169 {
170         cdev_t dev = ap->a_head.a_dev;
171         int err = 0;
172
173         if (DEV_FWMEM(dev))
174                 return fwmem_open(ap);
175
176         if (dev->si_drv1 != NULL)
177                 return (EBUSY);
178
179 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
180         if ((dev->si_flags & SI_NAMED) == 0) {
181                 int unit = DEV2UNIT(dev);
182                 int sub = DEV2SUB(dev);
183
184                 make_dev(&firewire_ops, minor(dev),
185                         UID_ROOT, GID_OPERATOR, 0660,
186                         "fw%d.%d", unit, sub);
187         }
188 #endif
189
190         dev->si_drv1 = kmalloc(sizeof(struct fw_drv1), M_FW, M_WAITOK | M_ZERO);
191
192         return err;
193 }
194
195 static int
196 fw_close (struct dev_close_args *ap)
197 {
198         cdev_t dev = ap->a_head.a_dev;
199         struct firewire_softc *sc;
200         struct firewire_comm *fc;
201         struct fw_drv1 *d;
202         int unit = DEV2UNIT(dev);
203         struct fw_xfer *xfer;
204         struct fw_bind *fwb;
205         int err = 0;
206
207         if (DEV_FWMEM(dev))
208                 return fwmem_close(ap);
209
210         sc = devclass_get_softc(firewire_devclass, unit);
211         fc = sc->fc;
212         d = (struct fw_drv1 *)dev->si_drv1;
213
214         if (d->ir != NULL) {
215                 struct fw_xferq *ir = d->ir;
216
217                 if ((ir->flag & FWXFERQ_OPEN) == 0)
218                         return (EINVAL);
219                 if (ir->flag & FWXFERQ_RUNNING) {
220                         ir->flag &= ~FWXFERQ_RUNNING;
221                         fc->irx_disable(fc, ir->dmach);
222                 }
223                 /* free extbuf */
224                 fwdev_freebuf(ir);
225                 /* drain receiving buffer */
226                 for (xfer = STAILQ_FIRST(&ir->q);
227                         xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) {
228                         ir->queued --;
229                         STAILQ_REMOVE_HEAD(&ir->q, link);
230
231                         xfer->resp = 0;
232                         fw_xfer_done(xfer);
233                 }
234                 /* remove binding */
235                 for (fwb = STAILQ_FIRST(&ir->binds); fwb != NULL;
236                                 fwb = STAILQ_FIRST(&ir->binds)) {
237                         STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist);
238                         STAILQ_REMOVE_HEAD(&ir->binds, chlist);
239                         kfree(fwb, M_FW);
240                 }
241                 ir->flag &= ~(FWXFERQ_OPEN |
242                         FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
243                 d->ir = NULL;
244
245         }
246         if (d->it != NULL) {
247                 struct fw_xferq *it = d->it;
248
249                 if ((it->flag & FWXFERQ_OPEN) == 0)
250                         return (EINVAL);
251                 if (it->flag & FWXFERQ_RUNNING) {
252                         it->flag &= ~FWXFERQ_RUNNING;
253                         fc->itx_disable(fc, it->dmach);
254                 }
255                 /* free extbuf */
256                 fwdev_freebuf(it);
257                 it->flag &= ~(FWXFERQ_OPEN |
258                         FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
259                 d->it = NULL;
260         }
261         kfree(dev->si_drv1, M_FW);
262         dev->si_drv1 = NULL;
263
264         return err;
265 }
266
267 /*
268  * read request.
269  */
270 static int
271 fw_read (struct dev_read_args *ap)
272 {
273         cdev_t dev = ap->a_head.a_dev;
274         struct uio *uio = ap->a_uio;
275         struct firewire_softc *sc;
276         struct fw_xferq *ir;
277         struct fw_xfer *xfer;
278         int err = 0, slept = 0;
279         int unit = DEV2UNIT(dev);
280         struct fw_pkt *fp;
281
282         if (DEV_FWMEM(dev))
283                 return physread(ap);
284
285         sc = devclass_get_softc(firewire_devclass, unit);
286
287         ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
288         if (ir == NULL || ir->buf == NULL)
289                 return (EIO);
290
291 readloop:
292         xfer = STAILQ_FIRST(&ir->q);
293         if (ir->stproc == NULL) {
294                 /* iso bulkxfer */
295                 ir->stproc = STAILQ_FIRST(&ir->stvalid);
296                 if (ir->stproc != NULL) {
297                         crit_enter();
298                         STAILQ_REMOVE_HEAD(&ir->stvalid, link);
299                         crit_exit();
300                         ir->queued = 0;
301                 }
302         }
303         if (xfer == NULL && ir->stproc == NULL) {
304                 /* no data avaliable */
305                 if (slept == 0) {
306                         slept = 1;
307                         ir->flag |= FWXFERQ_WAKEUP;
308                         err = tsleep(ir, FWPRI, "fw_read", hz);
309                         ir->flag &= ~FWXFERQ_WAKEUP;
310                         if (err == 0)
311                                 goto readloop;
312                 } else if (slept == 1)
313                         err = EIO;
314                 return err;
315         } else if(xfer != NULL) {
316 #if 0 /* XXX broken */
317                 /* per packet mode or FWACT_CH bind?*/
318                 crit_enter();
319                 ir->queued --;
320                 STAILQ_REMOVE_HEAD(&ir->q, link);
321                 crit_exit();
322                 fp = &xfer->recv.hdr;
323                 if (sc->fc->irx_post != NULL)
324                         sc->fc->irx_post(sc->fc, fp->mode.ld);
325                 err = uiomove((void *)fp, 1 /* XXX header size */, uio);
326                 /* XXX copy payload too */
327                 /* XXX we should recycle this xfer */
328 #endif
329                 fw_xfer_free( xfer);
330         } else if(ir->stproc != NULL) {
331                 /* iso bulkxfer */
332                 fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, 
333                                 ir->stproc->poffset + ir->queued);
334                 if(sc->fc->irx_post != NULL)
335                         sc->fc->irx_post(sc->fc, fp->mode.ld);
336                 if(fp->mode.stream.len == 0){
337                         err = EIO;
338                         return err;
339                 }
340                 err = uiomove((caddr_t)fp,
341                         fp->mode.stream.len + sizeof(u_int32_t), uio);
342                 ir->queued ++;
343                 if(ir->queued >= ir->bnpacket){
344                         crit_enter();
345                         STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
346                         crit_exit();
347                         sc->fc->irx_enable(sc->fc, ir->dmach);
348                         ir->stproc = NULL;
349                 }
350                 if (uio->uio_resid >= ir->psize) {
351                         slept = -1;
352                         goto readloop;
353                 }
354         }
355         return err;
356 }
357
358 static int
359 fw_write (struct dev_write_args *ap)
360 {
361         cdev_t dev = ap->a_head.a_dev;
362         struct uio *uio = ap->a_uio;
363         int err = 0;
364         struct firewire_softc *sc;
365         int unit = DEV2UNIT(dev);
366         int slept = 0;
367         struct fw_pkt *fp;
368         struct firewire_comm *fc;
369         struct fw_xferq *it;
370
371         if (DEV_FWMEM(dev))
372                 return physwrite(ap);
373
374         sc = devclass_get_softc(firewire_devclass, unit);
375         fc = sc->fc;
376         it = ((struct fw_drv1 *)dev->si_drv1)->it;
377         if (it == NULL || it->buf == NULL)
378                 return (EIO);
379 isoloop:
380         if (it->stproc == NULL) {
381                 it->stproc = STAILQ_FIRST(&it->stfree);
382                 if (it->stproc != NULL) {
383                         crit_enter();
384                         STAILQ_REMOVE_HEAD(&it->stfree, link);
385                         crit_exit();
386                         it->queued = 0;
387                 } else if (slept == 0) {
388                         slept = 1;
389                         err = sc->fc->itx_enable(sc->fc, it->dmach);
390                         if (err)
391                                 return err;
392                         err = tsleep(it, FWPRI, "fw_write", hz);
393                         if (err)
394                                 return err;
395                         goto isoloop;
396                 } else {
397                         err = EIO;
398                         return err;
399                 }
400         }
401         fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
402                         it->stproc->poffset + it->queued);
403         err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
404         err = uiomove((caddr_t)fp->mode.stream.payload,
405                                 fp->mode.stream.len, uio);
406         it->queued ++;
407         if (it->queued >= it->bnpacket) {
408                 crit_enter();
409                 STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
410                 crit_exit();
411                 it->stproc = NULL;
412                 err = sc->fc->itx_enable(sc->fc, it->dmach);
413         }
414         if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
415                 slept = 0;
416                 goto isoloop;
417         }
418         return err;
419 }
420 /*
421  * ioctl support.
422  */
423 int
424 fw_ioctl (struct dev_ioctl_args *ap)
425 {
426         cdev_t dev = ap->a_head.a_dev;
427         struct firewire_softc *sc;
428         struct firewire_comm *fc;
429         struct fw_drv1 *d;
430         int unit = DEV2UNIT(dev);
431         int i, len, err = 0;
432         struct fw_device *fwdev;
433         struct fw_bind *fwb;
434         struct fw_xferq *ir, *it;
435         struct fw_xfer *xfer;
436         struct fw_pkt *fp;
437         struct fw_devinfo *devinfo;
438         void *ptr;
439
440         struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)ap->a_data;
441         struct fw_asyreq *asyreq = (struct fw_asyreq *)ap->a_data;
442         struct fw_isochreq *ichreq = (struct fw_isochreq *)ap->a_data;
443         struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)ap->a_data;
444         struct fw_asybindreq *bindreq = (struct fw_asybindreq *)ap->a_data;
445         struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)ap->a_data;
446
447         if (DEV_FWMEM(dev))
448                 return fwmem_ioctl(ap);
449
450         if (!ap->a_data)
451                 return(EINVAL);
452
453         sc = devclass_get_softc(firewire_devclass, unit);
454         fc = sc->fc;
455         d = (struct fw_drv1 *)dev->si_drv1;
456         ir = d->ir;
457         it = d->it;
458
459         switch (ap->a_cmd) {
460         case FW_STSTREAM:
461                 if (it == NULL) {
462                         for (i = 0; i < fc->nisodma; i ++) {
463                                 it = fc->it[i];
464                                 if ((it->flag & FWXFERQ_OPEN) == 0)
465                                          break;
466                         }       
467                         if (i >= fc->nisodma) {
468                                 err = EBUSY;
469                                 break;
470                         }
471                         err = fwdev_allocbuf(fc, it, &d->bufreq.tx);
472                         if (err)
473                                 break;
474                         it->flag |=  FWXFERQ_OPEN;
475                 }
476                 it->flag &= ~0xff;
477                 it->flag |= (0x3f & ichreq->ch);
478                 it->flag |= ((0x3 & ichreq->tag) << 6);
479                 d->it = it;
480                 break;
481         case FW_GTSTREAM:
482                 if (it != NULL) {
483                         ichreq->ch = it->flag & 0x3f;
484                         ichreq->tag = it->flag >> 2 & 0x3;
485                 } else
486                         err = EINVAL;
487                 break;
488         case FW_SRSTREAM:
489                 if (ir == NULL) {
490                         for (i = 0; i < fc->nisodma; i ++) {
491                                 ir = fc->ir[i];
492                                 if ((ir->flag & FWXFERQ_OPEN) == 0)
493                                         break;
494                         }       
495                         if (i >= fc->nisodma) {
496                                 err = EBUSY;
497                                 break;
498                         }
499                         err = fwdev_allocbuf(fc, ir, &d->bufreq.rx);
500                         if (err)
501                                 break;
502                         ir->flag |=  FWXFERQ_OPEN;
503                 }
504                 ir->flag &= ~0xff;
505                 ir->flag |= (0x3f & ichreq->ch);
506                 ir->flag |= ((0x3 & ichreq->tag) << 6);
507                 d->ir = ir;
508                 err = fc->irx_enable(fc, ir->dmach);
509                 break;
510         case FW_GRSTREAM:
511                 if (d->ir != NULL) {
512                         ichreq->ch = ir->flag & 0x3f;
513                         ichreq->tag = ir->flag >> 2 & 0x3;
514                 } else
515                         err = EINVAL;
516                 break;
517         case FW_SSTBUF:
518                 bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq));
519                 break;
520         case FW_GSTBUF:
521                 bzero(&ibufreq->rx, sizeof(ibufreq->rx));
522                 if (ir != NULL) {
523                         ibufreq->rx.nchunk = ir->bnchunk;
524                         ibufreq->rx.npacket = ir->bnpacket;
525                         ibufreq->rx.psize = ir->psize;
526                 }
527                 bzero(&ibufreq->tx, sizeof(ibufreq->tx));
528                 if (it != NULL) {
529                         ibufreq->tx.nchunk = it->bnchunk;
530                         ibufreq->tx.npacket = it->bnpacket;
531                         ibufreq->tx.psize = it->psize;
532                 }
533                 break;
534         case FW_ASYREQ:
535         {
536                 struct tcode_info *tinfo;
537                 int pay_len = 0;
538
539                 fp = &asyreq->pkt;
540                 tinfo = &sc->fc->tcode[fp->mode.hdr.tcode];
541
542                 if ((tinfo->flag & FWTI_BLOCK_ASY) != 0)
543                         pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len);
544
545                 xfer = fw_xfer_alloc_buf(M_FWXFER, pay_len, PAGE_SIZE/*XXX*/);
546                 if (xfer == NULL)
547                         return (ENOMEM);
548
549                 switch (asyreq->req.type) {
550                 case FWASREQNODE:
551                         break;
552                 case FWASREQEUI:
553                         fwdev = fw_noderesolve_eui64(sc->fc,
554                                                 &asyreq->req.dst.eui);
555                         if (fwdev == NULL) {
556                                 device_printf(sc->fc->bdev,
557                                         "cannot find node\n");
558                                 err = EINVAL;
559                                 goto out;
560                         }
561                         fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst;
562                         break;
563                 case FWASRESTL:
564                         /* XXX what's this? */
565                         break;
566                 case FWASREQSTREAM:
567                         /* nothing to do */
568                         break;
569                 }
570
571                 bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len);
572                 if (pay_len > 0)
573                         bcopy((char *)fp + tinfo->hdr_len,
574                             (void *)&xfer->send.payload, pay_len);
575                 xfer->send.spd = asyreq->req.sped;
576                 xfer->act.hand = fw_asy_callback;
577
578                 if ((err = fw_asyreq(sc->fc, -1, xfer)) != 0)
579                         goto out;
580                 if ((err = tsleep(xfer, FWPRI, "asyreq", hz)) != 0)
581                         goto out;
582                 if (xfer->resp != 0) {
583                         err = EIO;
584                         goto out;
585                 }
586                 if ((tinfo->flag & FWTI_TLABEL) == 0)
587                         goto out;
588
589                 /* copy response */
590                 tinfo = &sc->fc->tcode[xfer->recv.hdr.mode.hdr.tcode];
591                 if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len)
592                         asyreq->req.len = xfer->recv.pay_len;
593                 else
594                         err = EINVAL;
595                 bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len);
596                 bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len,
597                     MAX(0, asyreq->req.len - tinfo->hdr_len));
598 out:
599                 fw_xfer_free_buf(xfer);
600                 break;
601         }
602         case FW_IBUSRST:
603                 sc->fc->ibr(sc->fc);
604                 break;
605         case FW_CBINDADDR:
606                 fwb = fw_bindlookup(sc->fc,
607                                 bindreq->start.hi, bindreq->start.lo);
608                 if(fwb == NULL){
609                         err = EINVAL;
610                         break;
611                 }
612                 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
613                 STAILQ_REMOVE(&ir->binds, fwb, fw_bind, chlist);
614                 kfree(fwb, M_FW);
615                 break;
616         case FW_SBINDADDR:
617                 if(bindreq->len <= 0 ){
618                         err = EINVAL;
619                         break;
620                 }
621                 if(bindreq->start.hi > 0xffff ){
622                         err = EINVAL;
623                         break;
624                 }
625                 fwb = kmalloc(sizeof (struct fw_bind), M_FW, M_WAITOK);
626                 fwb->start = ((u_int64_t)bindreq->start.hi << 32) |
627                     bindreq->start.lo;
628                 fwb->end = fwb->start +  bindreq->len;
629                 /* XXX */
630                 fwb->sub = ir->dmach;
631                 fwb->act_type = FWACT_CH;
632
633                 /* XXX alloc buf */
634                 xfer = fw_xfer_alloc(M_FWXFER);
635                 if(xfer == NULL){
636                         kfree(fwb, M_FW);
637                         return (ENOMEM);
638                 }
639                 xfer->fc = sc->fc;
640
641                 crit_enter();
642                 /* XXX broken. need multiple xfer */
643                 STAILQ_INIT(&fwb->xferlist);
644                 STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
645                 crit_exit();
646                 err = fw_bindadd(sc->fc, fwb);
647                 break;
648         case FW_GDEVLST:
649                 i = len = 1;
650                 /* myself */
651                 devinfo = &fwdevlst->dev[0];
652                 devinfo->dst = sc->fc->nodeid;
653                 devinfo->status = 0;    /* XXX */
654                 devinfo->eui.hi = sc->fc->eui.hi;
655                 devinfo->eui.lo = sc->fc->eui.lo;
656                 STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
657                         if(len < FW_MAX_DEVLST){
658                                 devinfo = &fwdevlst->dev[len++];
659                                 devinfo->dst = fwdev->dst;
660                                 devinfo->status = 
661                                         (fwdev->status == FWDEVINVAL)?0:1;
662                                 devinfo->eui.hi = fwdev->eui.hi;
663                                 devinfo->eui.lo = fwdev->eui.lo;
664                         }
665                         i++;
666                 }
667                 fwdevlst->n = i;
668                 fwdevlst->info_len = len;
669                 break;
670         case FW_GTPMAP:
671                 bcopy(sc->fc->topology_map, ap->a_data,
672                                 (sc->fc->topology_map->crc_len + 1) * 4);
673                 break;
674         case FW_GCROM:
675                 STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
676                         if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
677                                 break;
678                 if (fwdev == NULL) {
679                         if (!FW_EUI64_EQUAL(sc->fc->eui, crom_buf->eui)) {
680                                 err = FWNODE_INVAL;
681                                 break;
682                         }
683                         /* myself */
684                         ptr = kmalloc(CROMSIZE, M_FW, M_WAITOK);
685                         len = CROMSIZE;
686                         for (i = 0; i < CROMSIZE/4; i++)
687                                 ((u_int32_t *)ptr)[i]
688                                         = ntohl(sc->fc->config_rom[i]);
689                 } else {
690                         /* found */
691                         ptr = (void *)&fwdev->csrrom[0];
692                         if (fwdev->rommax < CSRROMOFF)
693                                 len = 0;
694                         else
695                                 len = fwdev->rommax - CSRROMOFF + 4;
696                 }
697                 if (crom_buf->len < len && crom_buf->len >= 0)
698                         len = crom_buf->len;
699                 else
700                         crom_buf->len = len;
701                 err = copyout(ptr, crom_buf->ptr, len);
702                 if (fwdev == NULL)
703                         /* myself */
704                         kfree(ptr, M_FW);
705                 break;
706         default:
707                 sc->fc->ioctl(ap);
708                 break;
709         }
710         return err;
711 }
712 int
713 fw_poll(struct dev_poll_args *ap)
714 {
715         cdev_t dev = ap->a_head.a_dev;
716         struct firewire_softc *sc;
717         struct fw_xferq *ir;
718         int revents;
719         int tmp;
720         int unit = DEV2UNIT(dev);
721
722         if (DEV_FWMEM(dev))
723                 return fwmem_poll(ap);
724
725         sc = devclass_get_softc(firewire_devclass, unit);
726         ir = ((struct fw_drv1 *)dev->si_drv1)->ir;
727         revents = 0;
728         tmp = POLLIN | POLLRDNORM;
729         if (ap->a_events & tmp) {
730                 if (STAILQ_FIRST(&ir->q) != NULL)
731                         revents |= tmp;
732                 else
733                         selrecord(curthread, &ir->rsel);
734         }
735         tmp = POLLOUT | POLLWRNORM;
736         if (ap->a_events & tmp) {
737                 /* XXX should be fixed */       
738                 revents |= tmp;
739         }
740         ap->a_events = revents;
741         return(0);
742 }
743
744 static int
745 fw_mmap (struct dev_mmap_args *ap)
746 {  
747         cdev_t dev = ap->a_head.a_dev;
748         struct firewire_softc *sc;
749         int unit = DEV2UNIT(dev);
750
751         if (DEV_FWMEM(dev))
752                 return fwmem_mmap(ap);
753         sc = devclass_get_softc(firewire_devclass, unit);
754
755         return EINVAL;
756 }
757
758 static int
759 fw_strategy(struct dev_strategy_args *ap)
760 {
761         cdev_t dev = ap->a_head.a_dev;
762         struct bio *bio = ap->a_bio;
763         struct buf *bp = bio->bio_buf;
764
765         if (DEV_FWMEM(dev)) {
766                 fwmem_strategy(ap);
767                 return(0);
768         }
769         bp->b_error = EOPNOTSUPP;
770         bp->b_flags |= B_ERROR;
771         bp->b_resid = bp->b_bcount;
772         biodone(bio);
773         return(0);
774 }
775
776 int
777 fwdev_makedev(struct firewire_softc *sc)
778 {
779         int unit;
780
781         unit = device_get_unit(sc->fc->bdev);
782         dev_ops_add(&firewire_ops, FW_UNITMASK, FW_UNIT(unit));
783         return(0);
784 }
785
786 int
787 fwdev_destroydev(struct firewire_softc *sc)
788 {
789         int unit;
790
791         unit = device_get_unit(sc->fc->bdev);
792         dev_ops_remove(&firewire_ops, FW_UNITMASK, FW_UNIT(unit));
793         return(0);
794 }
795
796 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
797 #define NDEVTYPE 2
798 void
799 fwdev_clone(void *arg, char *name, int namelen, cdev_t *dev)
800 {
801         struct firewire_softc *sc;
802         char *devnames[NDEVTYPE] = {"fw", "fwmem"};
803         char *subp = NULL;
804         int devflag[NDEVTYPE] = {0, FWMEM_FLAG};
805         int i, unit = 0, sub = 0;
806
807         if (*dev != NULL)
808                 return;
809
810         for (i = 0; i < NDEVTYPE; i++)
811                 if (dev_stdclone(name, &subp, devnames[i], &unit) == 2)
812                         goto found;
813         /* not match */
814         return;
815 found:
816
817         if (subp == NULL || *subp++ != '.')
818                 return;
819
820         /* /dev/fwU.S */
821         while (isdigit(*subp)) {
822                 sub *= 10;
823                 sub += *subp++ - '0';
824         }
825         if (*subp != '\0')
826                 return;
827
828         sc = devclass_get_softc(firewire_devclass, unit);
829         if (sc == NULL)
830                 return;
831         *dev = make_dev(&firewire_ops, MAKEMINOR(devflag[i], unit, sub),
832                        UID_ROOT, GID_OPERATOR, 0660,
833                        "%s%d.%d", devnames[i], unit, sub);
834         (*dev)->si_flags |= SI_CHEAPCLONE;
835         dev_depends(sc->dev, *dev);
836         return;
837 }
838 #endif