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