2 * Copyright (c) 2003 Hidetoshi Shimokawa
3 * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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:
17 * This product includes software developed by K. Kobayashi and H. Shimokawa
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.
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.
34 * $FreeBSD: src/sys/dev/firewire/fwdev.c,v 1.2.4.11 2003/04/28 03:29:18 simokawa Exp $
35 * $DragonFly: src/sys/bus/firewire/fwdev.c,v 1.2 2003/06/17 04:28:25 dillon Exp $
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
50 #include <machine/bus.h>
52 #include <sys/ioccom.h>
54 #include <dev/firewire/firewire.h>
55 #include <dev/firewire/firewirereg.h>
56 #include <dev/firewire/fwdma.h>
57 #include <dev/firewire/fwmem.h>
58 #include <dev/firewire/iec68113.h>
60 #define CDEV_MAJOR 127
61 #define FWNODE_INVAL 0xffff
63 static d_open_t fw_open;
64 static d_close_t fw_close;
65 static d_ioctl_t fw_ioctl;
66 static d_poll_t fw_poll;
67 static d_read_t fw_read; /* for Isochronous packet */
68 static d_write_t fw_write;
69 static d_mmap_t fw_mmap;
71 struct cdevsw firewire_cdevsw =
73 #if __FreeBSD_version >= 500104
85 fw_open, fw_close, fw_read, fw_write, fw_ioctl,
86 fw_poll, fw_mmap, nostrategy, "fw", CDEV_MAJOR, nodump, nopsize, D_MEM
91 fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
93 struct firewire_softc *sc;
94 int unit = DEV2UNIT(dev);
95 int sub = DEV2DMACH(dev);
100 return fwmem_open(dev, flags, fmt, td);
102 sc = devclass_get_softc(firewire_devclass, unit);
103 if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
107 if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
111 if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
115 /* Default is per packet mode */
116 sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
117 sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
122 fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
124 struct firewire_softc *sc;
125 int unit = DEV2UNIT(dev);
126 int sub = DEV2DMACH(dev);
127 struct fw_xfer *xfer;
132 return fwmem_close(dev, flags, fmt, td);
134 sc = devclass_get_softc(firewire_devclass, unit);
135 if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){
139 sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN;
140 if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){
144 sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN;
146 if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){
147 sc->fc->irx_disable(sc->fc, sub);
149 if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){
150 sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING;
151 sc->fc->itx_disable(sc->fc, sub);
153 if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
154 if (sc->fc->ir[sub]->buf != NULL)
155 fwdma_free_multiseg(sc->fc->ir[sub]->buf);
156 sc->fc->ir[sub]->buf = NULL;
157 free(sc->fc->ir[sub]->bulkxfer, M_FW);
158 sc->fc->ir[sub]->bulkxfer = NULL;
159 sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
160 sc->fc->ir[sub]->psize = PAGE_SIZE;
161 sc->fc->ir[sub]->maxq = FWMAXQUEUE;
163 if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
164 if (sc->fc->it[sub]->buf != NULL)
165 fwdma_free_multiseg(sc->fc->it[sub]->buf);
166 sc->fc->it[sub]->buf = NULL;
167 free(sc->fc->it[sub]->bulkxfer, M_FW);
168 sc->fc->it[sub]->bulkxfer = NULL;
169 sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
170 sc->fc->it[sub]->psize = 0;
171 sc->fc->it[sub]->maxq = FWMAXQUEUE;
173 for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
174 xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
175 sc->fc->ir[sub]->queued--;
176 STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
181 for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
182 fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
183 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
184 STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
187 sc->fc->ir[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
188 sc->fc->it[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
196 fw_read (dev_t dev, struct uio *uio, int ioflag)
198 struct firewire_softc *sc;
200 struct fw_xfer *xfer;
201 int err = 0, s, slept = 0;
202 int unit = DEV2UNIT(dev);
203 int sub = DEV2DMACH(dev);
207 return fwmem_read(dev, uio, ioflag);
209 sc = devclass_get_softc(firewire_devclass, unit);
211 ir = sc->fc->ir[sub];
214 xfer = STAILQ_FIRST(&ir->q);
215 if (ir->stproc == NULL) {
217 ir->stproc = STAILQ_FIRST(&ir->stvalid);
218 if (ir->stproc != NULL) {
220 STAILQ_REMOVE_HEAD(&ir->stvalid, link);
225 if (xfer == NULL && ir->stproc == NULL) {
226 /* no data avaliable */
229 ir->flag |= FWXFERQ_WAKEUP;
230 err = tsleep(ir, FWPRI, "fw_read", hz);
231 ir->flag &= ~FWXFERQ_WAKEUP;
234 } else if (slept == 1)
237 } else if(xfer != NULL) {
238 /* per packet mode or FWACT_CH bind?*/
241 STAILQ_REMOVE_HEAD(&ir->q, link);
243 fp = (struct fw_pkt *)xfer->recv.buf;
244 if(sc->fc->irx_post != NULL)
245 sc->fc->irx_post(sc->fc, fp->mode.ld);
246 err = uiomove(xfer->recv.buf, xfer->recv.len, uio);
247 /* XXX we should recycle this xfer */
249 } else if(ir->stproc != NULL) {
251 fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
252 ir->stproc->poffset + ir->queued);
253 if(sc->fc->irx_post != NULL)
254 sc->fc->irx_post(sc->fc, fp->mode.ld);
255 if(fp->mode.stream.len == 0){
259 err = uiomove((caddr_t)fp,
260 fp->mode.stream.len + sizeof(u_int32_t), uio);
262 if(ir->queued >= ir->bnpacket){
264 STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
266 sc->fc->irx_enable(sc->fc, sub);
269 if (uio->uio_resid >= ir->psize) {
278 fw_write (dev_t dev, struct uio *uio, int ioflag)
281 struct firewire_softc *sc;
282 int unit = DEV2UNIT(dev);
283 int sub = DEV2DMACH(dev);
286 struct firewire_comm *fc;
290 return fwmem_write(dev, uio, ioflag);
292 sc = devclass_get_softc(firewire_devclass, unit);
294 it = sc->fc->it[sub];
296 if (it->stproc == NULL) {
297 it->stproc = STAILQ_FIRST(&it->stfree);
298 if (it->stproc != NULL) {
300 STAILQ_REMOVE_HEAD(&it->stfree, link);
303 } else if (slept == 0) {
305 err = sc->fc->itx_enable(sc->fc, sub);
308 err = tsleep(it, FWPRI, "fw_write", hz);
317 fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
318 it->stproc->poffset + it->queued);
319 err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
320 err = uiomove((caddr_t)fp->mode.stream.payload,
321 fp->mode.stream.len, uio);
323 if (it->queued >= it->bnpacket) {
325 STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
328 err = sc->fc->itx_enable(sc->fc, sub);
330 if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
341 fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
343 struct firewire_softc *sc;
344 int unit = DEV2UNIT(dev);
345 int sub = DEV2DMACH(dev);
346 int s, i, len, err = 0;
347 struct fw_device *fwdev;
349 struct fw_xferq *ir, *it;
350 struct fw_xfer *xfer;
352 struct fw_devinfo *devinfo;
354 struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
355 struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
356 struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
357 struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
358 struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
359 struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
362 return fwmem_ioctl(dev, cmd, data, flag, td);
364 sc = devclass_get_softc(firewire_devclass, unit);
370 sc->fc->it[sub]->flag &= ~0xff;
371 sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
372 sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
376 ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
377 ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
381 sc->fc->ir[sub]->flag &= ~0xff;
382 sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
383 sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
384 err = sc->fc->irx_enable(sc->fc, sub);
387 ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
388 ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
392 ir = sc->fc->ir[sub];
393 it = sc->fc->it[sub];
395 if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
398 if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
401 if((ibufreq->rx.nchunk *
402 ibufreq->rx.psize * ibufreq->rx.npacket) +
403 (ibufreq->tx.nchunk *
404 ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
408 = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK);
409 if(ir->bulkxfer == NULL){
413 = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK);
414 if(it->bulkxfer == NULL){
417 if (ibufreq->rx.psize > 0) {
418 ibufreq->rx.psize = roundup2(ibufreq->rx.psize,
420 ir->buf = fwdma_malloc_multiseg(
421 sc->fc, sizeof(u_int32_t),
423 ibufreq->rx.nchunk * ibufreq->rx.npacket,
427 free(ir->bulkxfer, M_FW);
428 free(it->bulkxfer, M_FW);
435 if (ibufreq->tx.psize > 0) {
436 ibufreq->tx.psize = roundup2(ibufreq->tx.psize,
438 it->buf = fwdma_malloc_multiseg(
439 sc->fc, sizeof(u_int32_t),
441 ibufreq->tx.nchunk * ibufreq->tx.npacket,
445 free(ir->bulkxfer, M_FW);
446 free(it->bulkxfer, M_FW);
447 fwdma_free_multiseg(ir->buf);
455 ir->bnchunk = ibufreq->rx.nchunk;
456 ir->bnpacket = ibufreq->rx.npacket;
457 ir->psize = (ibufreq->rx.psize + 3) & ~3;
460 it->bnchunk = ibufreq->tx.nchunk;
461 it->bnpacket = ibufreq->tx.npacket;
462 it->psize = (ibufreq->tx.psize + 3) & ~3;
465 STAILQ_INIT(&ir->stvalid);
466 STAILQ_INIT(&ir->stfree);
467 STAILQ_INIT(&ir->stdma);
470 STAILQ_INIT(&it->stvalid);
471 STAILQ_INIT(&it->stfree);
472 STAILQ_INIT(&it->stdma);
475 for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
476 ir->bulkxfer[i].poffset = i * ir->bnpacket;
477 ir->bulkxfer[i].mbuf = NULL;
478 STAILQ_INSERT_TAIL(&ir->stfree,
479 &ir->bulkxfer[i], link);
481 for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
482 it->bulkxfer[i].poffset = i * it->bnpacket;
483 it->bulkxfer[i].mbuf = NULL;
484 STAILQ_INSERT_TAIL(&it->stfree,
485 &it->bulkxfer[i], link);
487 ir->flag &= ~FWXFERQ_MODEMASK;
488 ir->flag |= FWXFERQ_STREAM;
489 ir->flag |= FWXFERQ_EXTBUF;
491 it->flag &= ~FWXFERQ_MODEMASK;
492 it->flag |= FWXFERQ_STREAM;
493 it->flag |= FWXFERQ_EXTBUF;
497 ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
498 ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
499 ibufreq->rx.psize = sc->fc->ir[sub]->psize;
501 ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
502 ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
503 ibufreq->tx.psize = sc->fc->it[sub]->psize;
506 xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len,
507 PAGE_SIZE /* XXX */);
513 switch (asyreq->req.type) {
515 xfer->dst = fp->mode.hdr.dst;
518 fwdev = fw_noderesolve_eui64(sc->fc,
519 &asyreq->req.dst.eui);
521 device_printf(sc->fc->bdev,
522 "cannot find node\n");
526 xfer->dst = FWLOCALBUS | fwdev->dst;
527 fp->mode.hdr.dst = xfer->dst;
530 /* XXX what's this? */
536 xfer->spd = asyreq->req.sped;
537 bcopy(fp, xfer->send.buf, xfer->send.len);
538 xfer->act.hand = fw_asy_callback;
539 err = fw_asyreq(sc->fc, sub, xfer);
544 err = tsleep(xfer, FWPRI, "asyreq", hz);
546 if(asyreq->req.len >= xfer->recv.len){
547 asyreq->req.len = xfer->recv.len;
551 bcopy(xfer->recv.buf, fp, asyreq->req.len);
560 fwb = fw_bindlookup(sc->fc,
561 bindreq->start.hi, bindreq->start.lo);
566 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
567 STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
571 if(bindreq->len <= 0 ){
575 if(bindreq->start.hi > 0xffff ){
579 fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
584 fwb->start_hi = bindreq->start.hi;
585 fwb->start_lo = bindreq->start.lo;
586 fwb->addrlen = bindreq->len;
588 fwb->act_type = FWACT_CH;
590 xfer = fw_xfer_alloc(M_FWXFER);
598 /* XXX broken. need multiple xfer */
599 STAILQ_INIT(&fwb->xferlist);
600 STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
602 err = fw_bindadd(sc->fc, fwb);
607 devinfo = &fwdevlst->dev[0];
608 devinfo->dst = sc->fc->nodeid;
609 devinfo->status = 0; /* XXX */
610 devinfo->eui.hi = sc->fc->eui.hi;
611 devinfo->eui.lo = sc->fc->eui.lo;
612 STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
613 if(len < FW_MAX_DEVLST){
614 devinfo = &fwdevlst->dev[len++];
615 devinfo->dst = fwdev->dst;
617 (fwdev->status == FWDEVINVAL)?0:1;
618 devinfo->eui.hi = fwdev->eui.hi;
619 devinfo->eui.lo = fwdev->eui.lo;
624 fwdevlst->info_len = len;
627 bcopy(sc->fc->topology_map, data,
628 (sc->fc->topology_map->crc_len + 1) * 4);
631 STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
632 if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
638 if (fwdev->rommax < CSRROMOFF)
641 len = fwdev->rommax - CSRROMOFF + 4;
642 if (crom_buf->len < len)
646 err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
649 sc->fc->ioctl (dev, cmd, data, flag, td);
655 fw_poll(dev_t dev, int events, fw_proc *td)
659 int unit = DEV2UNIT(dev);
660 int sub = DEV2DMACH(dev);
661 struct firewire_softc *sc;
664 return fwmem_poll(dev, events, td);
666 sc = devclass_get_softc(firewire_devclass, unit);
668 tmp = POLLIN | POLLRDNORM;
670 if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
673 selrecord(td, &sc->fc->ir[sub]->rsel);
675 tmp = POLLOUT | POLLWRNORM;
677 /* XXX should be fixed */
685 #if __FreeBSD_version < 500102
686 fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
688 fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
691 struct firewire_softc *fc;
692 int unit = DEV2UNIT(dev);
695 #if __FreeBSD_version < 500102
696 return fwmem_mmap(dev, offset, nproto);
698 return fwmem_mmap(dev, offset, paddr, nproto);
701 fc = devclass_get_softc(firewire_devclass, unit);