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