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.4 2003/07/21 05:50:30 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>
51 #include <machine/bus.h>
53 #include <sys/ioccom.h>
55 #include <dev/firewire/firewire.h>
56 #include <dev/firewire/firewirereg.h>
57 #include <dev/firewire/fwdma.h>
58 #include <dev/firewire/fwmem.h>
59 #include <dev/firewire/iec68113.h>
61 #define CDEV_MAJOR 127
62 #define FWNODE_INVAL 0xffff
64 static d_open_t fw_open;
65 static d_close_t fw_close;
66 static d_ioctl_t fw_ioctl;
67 static d_poll_t fw_poll;
68 static d_read_t fw_read; /* for Isochronous packet */
69 static d_write_t fw_write;
70 static d_mmap_t fw_mmap;
72 struct cdevsw firewire_cdevsw =
74 #if __FreeBSD_version >= 500104
86 "fw", CDEV_MAJOR, D_MEM, NULL, 0,
87 fw_open, fw_close, fw_read, fw_write, fw_ioctl,
88 fw_poll, fw_mmap, nostrategy, nodump, nopsize,
93 fw_open (dev_t dev, int flags, int fmt, fw_proc *td)
95 struct firewire_softc *sc;
96 int unit = DEV2UNIT(dev);
97 int sub = DEV2DMACH(dev);
102 return fwmem_open(dev, flags, fmt, td);
104 sc = devclass_get_softc(firewire_devclass, unit);
105 if(sc->fc->ir[sub]->flag & FWXFERQ_OPEN){
109 if(sc->fc->it[sub]->flag & FWXFERQ_OPEN){
113 if(sc->fc->ir[sub]->flag & FWXFERQ_MODEMASK){
117 /* Default is per packet mode */
118 sc->fc->ir[sub]->flag |= FWXFERQ_OPEN;
119 sc->fc->it[sub]->flag |= FWXFERQ_OPEN;
124 fw_close (dev_t dev, int flags, int fmt, fw_proc *td)
126 struct firewire_softc *sc;
127 int unit = DEV2UNIT(dev);
128 int sub = DEV2DMACH(dev);
129 struct fw_xfer *xfer;
134 return fwmem_close(dev, flags, fmt, td);
136 sc = devclass_get_softc(firewire_devclass, unit);
137 if(!(sc->fc->ir[sub]->flag & FWXFERQ_OPEN)){
141 sc->fc->ir[sub]->flag &= ~FWXFERQ_OPEN;
142 if(!(sc->fc->it[sub]->flag & FWXFERQ_OPEN)){
146 sc->fc->it[sub]->flag &= ~FWXFERQ_OPEN;
148 if(sc->fc->ir[sub]->flag & FWXFERQ_RUNNING){
149 sc->fc->irx_disable(sc->fc, sub);
151 if(sc->fc->it[sub]->flag & FWXFERQ_RUNNING){
152 sc->fc->it[sub]->flag &= ~FWXFERQ_RUNNING;
153 sc->fc->itx_disable(sc->fc, sub);
155 if(sc->fc->ir[sub]->flag & FWXFERQ_EXTBUF){
156 if (sc->fc->ir[sub]->buf != NULL)
157 fwdma_free_multiseg(sc->fc->ir[sub]->buf);
158 sc->fc->ir[sub]->buf = NULL;
159 free(sc->fc->ir[sub]->bulkxfer, M_FW);
160 sc->fc->ir[sub]->bulkxfer = NULL;
161 sc->fc->ir[sub]->flag &= ~FWXFERQ_EXTBUF;
162 sc->fc->ir[sub]->psize = PAGE_SIZE;
163 sc->fc->ir[sub]->maxq = FWMAXQUEUE;
165 if(sc->fc->it[sub]->flag & FWXFERQ_EXTBUF){
166 if (sc->fc->it[sub]->buf != NULL)
167 fwdma_free_multiseg(sc->fc->it[sub]->buf);
168 sc->fc->it[sub]->buf = NULL;
169 free(sc->fc->it[sub]->bulkxfer, M_FW);
170 sc->fc->it[sub]->bulkxfer = NULL;
171 sc->fc->it[sub]->flag &= ~FWXFERQ_EXTBUF;
172 sc->fc->it[sub]->psize = 0;
173 sc->fc->it[sub]->maxq = FWMAXQUEUE;
175 for(xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q);
176 xfer != NULL; xfer = STAILQ_FIRST(&sc->fc->ir[sub]->q)){
177 sc->fc->ir[sub]->queued--;
178 STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->q, link);
183 for(fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds); fwb != NULL;
184 fwb = STAILQ_FIRST(&sc->fc->ir[sub]->binds)){
185 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
186 STAILQ_REMOVE_HEAD(&sc->fc->ir[sub]->binds, chlist);
189 sc->fc->ir[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
190 sc->fc->it[sub]->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK);
198 fw_read (dev_t dev, struct uio *uio, int ioflag)
200 struct firewire_softc *sc;
202 struct fw_xfer *xfer;
203 int err = 0, s, slept = 0;
204 int unit = DEV2UNIT(dev);
205 int sub = DEV2DMACH(dev);
209 return fwmem_read(dev, uio, ioflag);
211 sc = devclass_get_softc(firewire_devclass, unit);
213 ir = sc->fc->ir[sub];
216 xfer = STAILQ_FIRST(&ir->q);
217 if (ir->stproc == NULL) {
219 ir->stproc = STAILQ_FIRST(&ir->stvalid);
220 if (ir->stproc != NULL) {
222 STAILQ_REMOVE_HEAD(&ir->stvalid, link);
227 if (xfer == NULL && ir->stproc == NULL) {
228 /* no data avaliable */
231 ir->flag |= FWXFERQ_WAKEUP;
232 err = tsleep(ir, FWPRI, "fw_read", hz);
233 ir->flag &= ~FWXFERQ_WAKEUP;
236 } else if (slept == 1)
239 } else if(xfer != NULL) {
240 /* per packet mode or FWACT_CH bind?*/
243 STAILQ_REMOVE_HEAD(&ir->q, link);
245 fp = (struct fw_pkt *)xfer->recv.buf;
246 if(sc->fc->irx_post != NULL)
247 sc->fc->irx_post(sc->fc, fp->mode.ld);
248 err = uiomove(xfer->recv.buf, xfer->recv.len, uio);
249 /* XXX we should recycle this xfer */
251 } else if(ir->stproc != NULL) {
253 fp = (struct fw_pkt *)fwdma_v_addr(ir->buf,
254 ir->stproc->poffset + ir->queued);
255 if(sc->fc->irx_post != NULL)
256 sc->fc->irx_post(sc->fc, fp->mode.ld);
257 if(fp->mode.stream.len == 0){
261 err = uiomove((caddr_t)fp,
262 fp->mode.stream.len + sizeof(u_int32_t), uio);
264 if(ir->queued >= ir->bnpacket){
266 STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link);
268 sc->fc->irx_enable(sc->fc, sub);
271 if (uio->uio_resid >= ir->psize) {
280 fw_write (dev_t dev, struct uio *uio, int ioflag)
283 struct firewire_softc *sc;
284 int unit = DEV2UNIT(dev);
285 int sub = DEV2DMACH(dev);
288 struct firewire_comm *fc;
292 return fwmem_write(dev, uio, ioflag);
294 sc = devclass_get_softc(firewire_devclass, unit);
296 it = sc->fc->it[sub];
298 if (it->stproc == NULL) {
299 it->stproc = STAILQ_FIRST(&it->stfree);
300 if (it->stproc != NULL) {
302 STAILQ_REMOVE_HEAD(&it->stfree, link);
305 } else if (slept == 0) {
307 err = sc->fc->itx_enable(sc->fc, sub);
310 err = tsleep(it, FWPRI, "fw_write", hz);
319 fp = (struct fw_pkt *)fwdma_v_addr(it->buf,
320 it->stproc->poffset + it->queued);
321 err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio);
322 err = uiomove((caddr_t)fp->mode.stream.payload,
323 fp->mode.stream.len, uio);
325 if (it->queued >= it->bnpacket) {
327 STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link);
330 err = sc->fc->itx_enable(sc->fc, sub);
332 if (uio->uio_resid >= sizeof(struct fw_isohdr)) {
343 fw_ioctl (dev_t dev, u_long cmd, caddr_t data, int flag, fw_proc *td)
345 struct firewire_softc *sc;
346 int unit = DEV2UNIT(dev);
347 int sub = DEV2DMACH(dev);
348 int s, i, len, err = 0;
349 struct fw_device *fwdev;
351 struct fw_xferq *ir, *it;
352 struct fw_xfer *xfer;
354 struct fw_devinfo *devinfo;
356 struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data;
357 struct fw_asyreq *asyreq = (struct fw_asyreq *)data;
358 struct fw_isochreq *ichreq = (struct fw_isochreq *)data;
359 struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data;
360 struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data;
361 struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data;
364 return fwmem_ioctl(dev, cmd, data, flag, td);
366 sc = devclass_get_softc(firewire_devclass, unit);
372 sc->fc->it[sub]->flag &= ~0xff;
373 sc->fc->it[sub]->flag |= (0x3f & ichreq->ch);
374 sc->fc->it[sub]->flag |= ((0x3 & ichreq->tag) << 6);
378 ichreq->ch = sc->fc->it[sub]->flag & 0x3f;
379 ichreq->tag =(sc->fc->it[sub]->flag) >> 2 & 0x3;
383 sc->fc->ir[sub]->flag &= ~0xff;
384 sc->fc->ir[sub]->flag |= (0x3f & ichreq->ch);
385 sc->fc->ir[sub]->flag |= ((0x3 & ichreq->tag) << 6);
386 err = sc->fc->irx_enable(sc->fc, sub);
389 ichreq->ch = sc->fc->ir[sub]->flag & 0x3f;
390 ichreq->tag =(sc->fc->ir[sub]->flag) >> 2 & 0x3;
394 ir = sc->fc->ir[sub];
395 it = sc->fc->it[sub];
397 if(ir->flag & FWXFERQ_RUNNING || it->flag & FWXFERQ_RUNNING){
400 if((ir->flag & FWXFERQ_EXTBUF) || (it->flag & FWXFERQ_EXTBUF)){
403 if((ibufreq->rx.nchunk *
404 ibufreq->rx.psize * ibufreq->rx.npacket) +
405 (ibufreq->tx.nchunk *
406 ibufreq->tx.psize * ibufreq->tx.npacket) <= 0){
410 = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->rx.nchunk, M_FW, M_WAITOK);
411 if(ir->bulkxfer == NULL){
415 = (struct fw_bulkxfer *)malloc(sizeof(struct fw_bulkxfer) * ibufreq->tx.nchunk, M_FW, M_WAITOK);
416 if(it->bulkxfer == NULL){
419 if (ibufreq->rx.psize > 0) {
420 ibufreq->rx.psize = roundup2(ibufreq->rx.psize,
422 ir->buf = fwdma_malloc_multiseg(
423 sc->fc, sizeof(u_int32_t),
425 ibufreq->rx.nchunk * ibufreq->rx.npacket,
429 free(ir->bulkxfer, M_FW);
430 free(it->bulkxfer, M_FW);
437 if (ibufreq->tx.psize > 0) {
438 ibufreq->tx.psize = roundup2(ibufreq->tx.psize,
440 it->buf = fwdma_malloc_multiseg(
441 sc->fc, sizeof(u_int32_t),
443 ibufreq->tx.nchunk * ibufreq->tx.npacket,
447 free(ir->bulkxfer, M_FW);
448 free(it->bulkxfer, M_FW);
449 fwdma_free_multiseg(ir->buf);
457 ir->bnchunk = ibufreq->rx.nchunk;
458 ir->bnpacket = ibufreq->rx.npacket;
459 ir->psize = (ibufreq->rx.psize + 3) & ~3;
462 it->bnchunk = ibufreq->tx.nchunk;
463 it->bnpacket = ibufreq->tx.npacket;
464 it->psize = (ibufreq->tx.psize + 3) & ~3;
467 STAILQ_INIT(&ir->stvalid);
468 STAILQ_INIT(&ir->stfree);
469 STAILQ_INIT(&ir->stdma);
472 STAILQ_INIT(&it->stvalid);
473 STAILQ_INIT(&it->stfree);
474 STAILQ_INIT(&it->stdma);
477 for(i = 0 ; i < sc->fc->ir[sub]->bnchunk; i++){
478 ir->bulkxfer[i].poffset = i * ir->bnpacket;
479 ir->bulkxfer[i].mbuf = NULL;
480 STAILQ_INSERT_TAIL(&ir->stfree,
481 &ir->bulkxfer[i], link);
483 for(i = 0 ; i < sc->fc->it[sub]->bnchunk; i++){
484 it->bulkxfer[i].poffset = i * it->bnpacket;
485 it->bulkxfer[i].mbuf = NULL;
486 STAILQ_INSERT_TAIL(&it->stfree,
487 &it->bulkxfer[i], link);
489 ir->flag &= ~FWXFERQ_MODEMASK;
490 ir->flag |= FWXFERQ_STREAM;
491 ir->flag |= FWXFERQ_EXTBUF;
493 it->flag &= ~FWXFERQ_MODEMASK;
494 it->flag |= FWXFERQ_STREAM;
495 it->flag |= FWXFERQ_EXTBUF;
499 ibufreq->rx.nchunk = sc->fc->ir[sub]->bnchunk;
500 ibufreq->rx.npacket = sc->fc->ir[sub]->bnpacket;
501 ibufreq->rx.psize = sc->fc->ir[sub]->psize;
503 ibufreq->tx.nchunk = sc->fc->it[sub]->bnchunk;
504 ibufreq->tx.npacket = sc->fc->it[sub]->bnpacket;
505 ibufreq->tx.psize = sc->fc->it[sub]->psize;
508 xfer = fw_xfer_alloc_buf(M_FWXFER, asyreq->req.len,
509 PAGE_SIZE /* XXX */);
515 switch (asyreq->req.type) {
517 xfer->dst = fp->mode.hdr.dst;
520 fwdev = fw_noderesolve_eui64(sc->fc,
521 &asyreq->req.dst.eui);
523 device_printf(sc->fc->bdev,
524 "cannot find node\n");
528 xfer->dst = FWLOCALBUS | fwdev->dst;
529 fp->mode.hdr.dst = xfer->dst;
532 /* XXX what's this? */
538 xfer->spd = asyreq->req.sped;
539 bcopy(fp, xfer->send.buf, xfer->send.len);
540 xfer->act.hand = fw_asy_callback;
541 err = fw_asyreq(sc->fc, sub, xfer);
546 err = tsleep(xfer, FWPRI, "asyreq", hz);
548 if(asyreq->req.len >= xfer->recv.len){
549 asyreq->req.len = xfer->recv.len;
553 bcopy(xfer->recv.buf, fp, asyreq->req.len);
562 fwb = fw_bindlookup(sc->fc,
563 bindreq->start.hi, bindreq->start.lo);
568 STAILQ_REMOVE(&sc->fc->binds, fwb, fw_bind, fclist);
569 STAILQ_REMOVE(&sc->fc->ir[sub]->binds, fwb, fw_bind, chlist);
573 if(bindreq->len <= 0 ){
577 if(bindreq->start.hi > 0xffff ){
581 fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT);
586 fwb->start_hi = bindreq->start.hi;
587 fwb->start_lo = bindreq->start.lo;
588 fwb->addrlen = bindreq->len;
590 fwb->act_type = FWACT_CH;
592 xfer = fw_xfer_alloc(M_FWXFER);
600 /* XXX broken. need multiple xfer */
601 STAILQ_INIT(&fwb->xferlist);
602 STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link);
604 err = fw_bindadd(sc->fc, fwb);
609 devinfo = &fwdevlst->dev[0];
610 devinfo->dst = sc->fc->nodeid;
611 devinfo->status = 0; /* XXX */
612 devinfo->eui.hi = sc->fc->eui.hi;
613 devinfo->eui.lo = sc->fc->eui.lo;
614 STAILQ_FOREACH(fwdev, &sc->fc->devices, link) {
615 if(len < FW_MAX_DEVLST){
616 devinfo = &fwdevlst->dev[len++];
617 devinfo->dst = fwdev->dst;
619 (fwdev->status == FWDEVINVAL)?0:1;
620 devinfo->eui.hi = fwdev->eui.hi;
621 devinfo->eui.lo = fwdev->eui.lo;
626 fwdevlst->info_len = len;
629 bcopy(sc->fc->topology_map, data,
630 (sc->fc->topology_map->crc_len + 1) * 4);
633 STAILQ_FOREACH(fwdev, &sc->fc->devices, link)
634 if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui))
640 if (fwdev->rommax < CSRROMOFF)
643 len = fwdev->rommax - CSRROMOFF + 4;
644 if (crom_buf->len < len)
648 err = copyout(&fwdev->csrrom[0], crom_buf->ptr, len);
651 sc->fc->ioctl (dev, cmd, data, flag, td);
657 fw_poll(dev_t dev, int events, fw_proc *td)
661 int unit = DEV2UNIT(dev);
662 int sub = DEV2DMACH(dev);
663 struct firewire_softc *sc;
666 return fwmem_poll(dev, events, td);
668 sc = devclass_get_softc(firewire_devclass, unit);
670 tmp = POLLIN | POLLRDNORM;
672 if (STAILQ_FIRST(&sc->fc->ir[sub]->q) != NULL)
675 selrecord(td, &sc->fc->ir[sub]->rsel); /* YYY */
677 tmp = POLLOUT | POLLWRNORM;
679 /* XXX should be fixed */
687 #if __FreeBSD_version < 500102
688 fw_mmap (dev_t dev, vm_offset_t offset, int nproto)
690 fw_mmap (dev_t dev, vm_offset_t offset, vm_paddr_t *paddr, int nproto)
693 struct firewire_softc *fc;
694 int unit = DEV2UNIT(dev);
697 #if __FreeBSD_version < 500102
698 return fwmem_mmap(dev, offset, nproto);
700 return fwmem_mmap(dev, offset, paddr, nproto);
703 fc = devclass_get_softc(firewire_devclass, unit);