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.36 2004/01/22 14:41:17 simokawa Exp $
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/types.h>
44 #include <sys/kernel.h>
45 #include <sys/malloc.h>
47 #include <sys/event.h>
50 #include <sys/ctype.h>
52 #include <sys/thread2.h>
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>
60 #define FWNODE_INVAL 0xffff
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
;
71 static void fwfilt_detach(struct knote
*);
72 static int fwfilt_read(struct knote
*, long);
73 static int fwfilt_write(struct knote
*, long);
75 struct dev_ops firewire_ops
=
83 .d_kqfilter
= fw_kqfilter
,
85 .d_strategy
= fw_strategy
,
91 struct fw_isobufreq bufreq
;
95 fwdev_allocbuf(struct firewire_comm
*fc
, struct fw_xferq
*q
,
100 if (q
->flag
& (FWXFERQ_RUNNING
| FWXFERQ_EXTBUF
))
103 q
->bulkxfer
= (struct fw_bulkxfer
*) kmalloc(
104 sizeof(struct fw_bulkxfer
) * b
->nchunk
,
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
);
111 if (q
->buf
== NULL
) {
112 kfree(q
->bulkxfer
, M_FW
);
116 q
->bnchunk
= b
->nchunk
;
117 q
->bnpacket
= b
->npacket
;
118 q
->psize
= (b
->psize
+ 3) & ~3;
121 STAILQ_INIT(&q
->stvalid
);
122 STAILQ_INIT(&q
->stfree
);
123 STAILQ_INIT(&q
->stdma
);
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
);
132 q
->flag
&= ~FWXFERQ_MODEMASK
;
133 q
->flag
|= FWXFERQ_STREAM
;
134 q
->flag
|= FWXFERQ_EXTBUF
;
140 fwdev_freebuf(struct fw_xferq
*q
)
142 if (q
->flag
& FWXFERQ_EXTBUF
) {
144 fwdma_free_multiseg(q
->buf
);
146 kfree(q
->bulkxfer
, M_FW
);
148 q
->flag
&= ~FWXFERQ_EXTBUF
;
150 q
->maxq
= FWMAXQUEUE
;
157 fw_open (struct dev_open_args
*ap
)
159 cdev_t dev
= ap
->a_head
.a_dev
;
163 return fwmem_open(ap
);
165 if (dev
->si_drv1
!= NULL
)
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
);
173 make_dev(&firewire_ops
, minor(dev
),
174 UID_ROOT
, GID_OPERATOR
, 0660,
175 "fw%d.%d", unit
, sub
);
179 dev
->si_drv1
= kmalloc(sizeof(struct fw_drv1
), M_FW
, M_WAITOK
| M_ZERO
);
185 fw_close (struct dev_close_args
*ap
)
187 cdev_t dev
= ap
->a_head
.a_dev
;
188 struct firewire_softc
*sc
;
189 struct firewire_comm
*fc
;
191 int unit
= DEV2UNIT(dev
);
192 struct fw_xfer
*xfer
;
197 return fwmem_close(ap
);
199 sc
= devclass_get_softc(firewire_devclass
, unit
);
201 d
= (struct fw_drv1
*)dev
->si_drv1
;
204 struct fw_xferq
*ir
= d
->ir
;
206 if ((ir
->flag
& FWXFERQ_OPEN
) == 0)
208 if (ir
->flag
& FWXFERQ_RUNNING
) {
209 ir
->flag
&= ~FWXFERQ_RUNNING
;
210 fc
->irx_disable(fc
, ir
->dmach
);
214 /* drain receiving buffer */
215 for (xfer
= STAILQ_FIRST(&ir
->q
);
216 xfer
!= NULL
; xfer
= STAILQ_FIRST(&ir
->q
)) {
218 STAILQ_REMOVE_HEAD(&ir
->q
, link
);
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
);
230 ir
->flag
&= ~(FWXFERQ_OPEN
|
231 FWXFERQ_MODEMASK
| FWXFERQ_CHTAGMASK
);
236 struct fw_xferq
*it
= d
->it
;
238 if ((it
->flag
& FWXFERQ_OPEN
) == 0)
240 if (it
->flag
& FWXFERQ_RUNNING
) {
241 it
->flag
&= ~FWXFERQ_RUNNING
;
242 fc
->itx_disable(fc
, it
->dmach
);
246 it
->flag
&= ~(FWXFERQ_OPEN
|
247 FWXFERQ_MODEMASK
| FWXFERQ_CHTAGMASK
);
250 kfree(dev
->si_drv1
, M_FW
);
260 fw_read (struct dev_read_args
*ap
)
262 cdev_t dev
= ap
->a_head
.a_dev
;
263 struct uio
*uio
= ap
->a_uio
;
264 struct firewire_softc
*sc
;
266 struct fw_xfer
*xfer
;
267 int err
= 0, slept
= 0;
268 int unit
= DEV2UNIT(dev
);
274 sc
= devclass_get_softc(firewire_devclass
, unit
);
276 ir
= ((struct fw_drv1
*)dev
->si_drv1
)->ir
;
277 if (ir
== NULL
|| ir
->buf
== NULL
)
281 xfer
= STAILQ_FIRST(&ir
->q
);
282 if (ir
->stproc
== NULL
) {
284 ir
->stproc
= STAILQ_FIRST(&ir
->stvalid
);
285 if (ir
->stproc
!= NULL
) {
287 STAILQ_REMOVE_HEAD(&ir
->stvalid
, link
);
292 if (xfer
== NULL
&& ir
->stproc
== NULL
) {
293 /* no data avaliable */
296 ir
->flag
|= FWXFERQ_WAKEUP
;
297 err
= tsleep(ir
, FWPRI
, "fw_read", hz
);
298 ir
->flag
&= ~FWXFERQ_WAKEUP
;
301 } else if (slept
== 1)
304 } else if(xfer
!= NULL
) {
305 #if 0 /* XXX broken */
306 /* per packet mode or FWACT_CH bind?*/
309 STAILQ_REMOVE_HEAD(&ir
->q
, link
);
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 */
319 } else if(ir
->stproc
!= NULL
) {
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){
329 err
= uiomove((caddr_t
)fp
,
330 fp
->mode
.stream
.len
+ sizeof(u_int32_t
), uio
);
332 if(ir
->queued
>= ir
->bnpacket
){
334 STAILQ_INSERT_TAIL(&ir
->stfree
, ir
->stproc
, link
);
336 sc
->fc
->irx_enable(sc
->fc
, ir
->dmach
);
339 if (uio
->uio_resid
>= ir
->psize
) {
348 fw_write (struct dev_write_args
*ap
)
350 cdev_t dev
= ap
->a_head
.a_dev
;
351 struct uio
*uio
= ap
->a_uio
;
353 struct firewire_softc
*sc
;
354 int unit
= DEV2UNIT(dev
);
360 return physwrite(ap
);
362 sc
= devclass_get_softc(firewire_devclass
, unit
);
363 it
= ((struct fw_drv1
*)dev
->si_drv1
)->it
;
364 if (it
== NULL
|| it
->buf
== NULL
)
367 if (it
->stproc
== NULL
) {
368 it
->stproc
= STAILQ_FIRST(&it
->stfree
);
369 if (it
->stproc
!= NULL
) {
371 STAILQ_REMOVE_HEAD(&it
->stfree
, link
);
374 } else if (slept
== 0) {
376 err
= sc
->fc
->itx_enable(sc
->fc
, it
->dmach
);
379 err
= tsleep(it
, FWPRI
, "fw_write", hz
);
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
);
394 if (it
->queued
>= it
->bnpacket
) {
396 STAILQ_INSERT_TAIL(&it
->stvalid
, it
->stproc
, link
);
399 err
= sc
->fc
->itx_enable(sc
->fc
, it
->dmach
);
401 if (uio
->uio_resid
>= sizeof(struct fw_isohdr
)) {
411 fw_ioctl (struct dev_ioctl_args
*ap
)
413 cdev_t dev
= ap
->a_head
.a_dev
;
414 struct firewire_softc
*sc
;
415 struct firewire_comm
*fc
;
417 int unit
= DEV2UNIT(dev
);
419 struct fw_device
*fwdev
;
421 struct fw_xferq
*ir
, *it
;
422 struct fw_xfer
*xfer
;
424 struct fw_devinfo
*devinfo
;
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
;
435 return fwmem_ioctl(ap
);
440 sc
= devclass_get_softc(firewire_devclass
, unit
);
442 d
= (struct fw_drv1
*)dev
->si_drv1
;
449 for (i
= 0; i
< fc
->nisodma
; i
++) {
451 if ((it
->flag
& FWXFERQ_OPEN
) == 0)
454 if (i
>= fc
->nisodma
) {
458 err
= fwdev_allocbuf(fc
, it
, &d
->bufreq
.tx
);
461 it
->flag
|= FWXFERQ_OPEN
;
464 it
->flag
|= (0x3f & ichreq
->ch
);
465 it
->flag
|= ((0x3 & ichreq
->tag
) << 6);
470 ichreq
->ch
= it
->flag
& 0x3f;
471 ichreq
->tag
= it
->flag
>> 2 & 0x3;
477 for (i
= 0; i
< fc
->nisodma
; i
++) {
479 if ((ir
->flag
& FWXFERQ_OPEN
) == 0)
482 if (i
>= fc
->nisodma
) {
486 err
= fwdev_allocbuf(fc
, ir
, &d
->bufreq
.rx
);
489 ir
->flag
|= FWXFERQ_OPEN
;
492 ir
->flag
|= (0x3f & ichreq
->ch
);
493 ir
->flag
|= ((0x3 & ichreq
->tag
) << 6);
495 err
= fc
->irx_enable(fc
, ir
->dmach
);
499 ichreq
->ch
= ir
->flag
& 0x3f;
500 ichreq
->tag
= ir
->flag
>> 2 & 0x3;
505 bcopy(ibufreq
, &d
->bufreq
, sizeof(d
->bufreq
));
508 bzero(&ibufreq
->rx
, sizeof(ibufreq
->rx
));
510 ibufreq
->rx
.nchunk
= ir
->bnchunk
;
511 ibufreq
->rx
.npacket
= ir
->bnpacket
;
512 ibufreq
->rx
.psize
= ir
->psize
;
514 bzero(&ibufreq
->tx
, sizeof(ibufreq
->tx
));
516 ibufreq
->tx
.nchunk
= it
->bnchunk
;
517 ibufreq
->tx
.npacket
= it
->bnpacket
;
518 ibufreq
->tx
.psize
= it
->psize
;
523 struct tcode_info
*tinfo
;
527 tinfo
= &sc
->fc
->tcode
[fp
->mode
.hdr
.tcode
];
529 if ((tinfo
->flag
& FWTI_BLOCK_ASY
) != 0)
530 pay_len
= MAX(0, asyreq
->req
.len
- tinfo
->hdr_len
);
532 xfer
= fw_xfer_alloc_buf(M_FWXFER
, pay_len
, PAGE_SIZE
/*XXX*/);
536 switch (asyreq
->req
.type
) {
540 fwdev
= fw_noderesolve_eui64(sc
->fc
,
541 &asyreq
->req
.dst
.eui
);
543 device_printf(sc
->fc
->bdev
,
544 "cannot find node\n");
548 fp
->mode
.hdr
.dst
= FWLOCALBUS
| fwdev
->dst
;
551 /* XXX what's this? */
558 bcopy(fp
, (void *)&xfer
->send
.hdr
, tinfo
->hdr_len
);
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
;
565 if ((err
= fw_asyreq(sc
->fc
, -1, xfer
)) != 0)
567 if ((err
= tsleep(xfer
, FWPRI
, "asyreq", hz
)) != 0)
569 if (xfer
->resp
!= 0) {
573 if ((tinfo
->flag
& FWTI_TLABEL
) == 0)
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
;
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
));
586 fw_xfer_free_buf(xfer
);
593 fwb
= fw_bindlookup(sc
->fc
,
594 bindreq
->start
.hi
, bindreq
->start
.lo
);
599 STAILQ_REMOVE(&sc
->fc
->binds
, fwb
, fw_bind
, fclist
);
600 STAILQ_REMOVE(&ir
->binds
, fwb
, fw_bind
, chlist
);
604 if(bindreq
->len
<= 0 ){
608 if(bindreq
->start
.hi
> 0xffff ){
612 fwb
= kmalloc(sizeof (struct fw_bind
), M_FW
, M_WAITOK
);
613 fwb
->start
= ((u_int64_t
)bindreq
->start
.hi
<< 32) |
615 fwb
->end
= fwb
->start
+ bindreq
->len
;
617 fwb
->sub
= ir
->dmach
;
618 fwb
->act_type
= FWACT_CH
;
621 xfer
= fw_xfer_alloc(M_FWXFER
);
629 /* XXX broken. need multiple xfer */
630 STAILQ_INIT(&fwb
->xferlist
);
631 STAILQ_INSERT_TAIL(&fwb
->xferlist
, xfer
, link
);
633 err
= fw_bindadd(sc
->fc
, fwb
);
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
;
648 (fwdev
->status
== FWDEVINVAL
)?0:1;
649 devinfo
->eui
.hi
= fwdev
->eui
.hi
;
650 devinfo
->eui
.lo
= fwdev
->eui
.lo
;
655 fwdevlst
->info_len
= len
;
658 bcopy(sc
->fc
->topology_map
, ap
->a_data
,
659 (sc
->fc
->topology_map
->crc_len
+ 1) * 4);
662 STAILQ_FOREACH(fwdev
, &sc
->fc
->devices
, link
)
663 if (FW_EUI64_EQUAL(fwdev
->eui
, crom_buf
->eui
))
666 if (!FW_EUI64_EQUAL(sc
->fc
->eui
, crom_buf
->eui
)) {
671 ptr
= kmalloc(CROMSIZE
, M_FW
, M_WAITOK
);
673 for (i
= 0; i
< CROMSIZE
/4; i
++)
674 ((u_int32_t
*)ptr
)[i
]
675 = ntohl(sc
->fc
->config_rom
[i
]);
678 ptr
= (void *)&fwdev
->csrrom
[0];
679 if (fwdev
->rommax
< CSRROMOFF
)
682 len
= fwdev
->rommax
- CSRROMOFF
+ 4;
684 if (crom_buf
->len
< len
&& crom_buf
->len
>= 0)
688 err
= copyout(ptr
, crom_buf
->ptr
, len
);
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
};
706 fw_kqfilter(struct dev_kqfilter_args
*ap
)
708 cdev_t dev
= ap
->a_head
.a_dev
;
710 struct knote
*kn
= ap
->a_kn
;
714 * XXX Implement filters for mem?
716 if (DEV_FWMEM(dev
)) {
717 ap
->a_result
= EOPNOTSUPP
;
721 ir
= ((struct fw_drv1
*)dev
->si_drv1
)->ir
;
725 switch (kn
->kn_filter
) {
727 kn
->kn_fop
= &fw_read_filterops
;
728 kn
->kn_hook
= (caddr_t
)ir
;
731 kn
->kn_fop
= &fw_write_filterops
;
732 kn
->kn_hook
= (caddr_t
)ir
;
735 ap
->a_result
= EOPNOTSUPP
;
739 klist
= &ir
->rkq
.ki_note
;
740 knote_insert(klist
, kn
);
746 fwfilt_detach(struct knote
*kn
)
748 struct fw_xferq
*ir
= (struct fw_xferq
*)kn
->kn_hook
;
749 struct klist
*klist
= &ir
->rkq
.ki_note
;
751 knote_remove(klist
, kn
);
755 fwfilt_read(struct knote
*kn
, long hint
)
757 struct fw_xferq
*ir
= (struct fw_xferq
*)kn
->kn_hook
;
760 if (STAILQ_FIRST(&ir
->q
) != NULL
)
767 fwfilt_write(struct knote
*kn
, long hint
)
769 /* XXX should be fixed */
774 fw_mmap (struct dev_mmap_args
*ap
)
776 cdev_t dev
= ap
->a_head
.a_dev
;
779 return fwmem_mmap(ap
);
785 fw_strategy(struct dev_strategy_args
*ap
)
787 cdev_t dev
= ap
->a_head
.a_dev
;
788 struct bio
*bio
= ap
->a_bio
;
789 struct buf
*bp
= bio
->bio_buf
;
791 if (DEV_FWMEM(dev
)) {
795 bp
->b_error
= EOPNOTSUPP
;
796 bp
->b_flags
|= B_ERROR
;
797 bp
->b_resid
= bp
->b_bcount
;
803 fwdev_makedev(struct firewire_softc
*sc
)
805 /*HELPME dev_ops_add(&firewire_ops, FW_UNITMASK, FW_UNIT(unit));*/
810 fwdev_destroydev(struct firewire_softc
*sc
)
814 unit
= device_get_unit(sc
->fc
->bdev
);
815 dev_ops_remove_minor(&firewire_ops
, /*FW_UNITMASK, */FW_UNIT(unit
));
819 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
822 fwdev_clone(void *arg
, char *name
, int namelen
, cdev_t
*dev
)
824 struct firewire_softc
*sc
;
825 char *devnames
[NDEVTYPE
] = {"fw", "fwmem"};
827 int devflag
[NDEVTYPE
] = {0, FWMEM_FLAG
};
828 int i
, unit
= 0, sub
= 0;
833 for (i
= 0; i
< NDEVTYPE
; i
++)
834 if (dev_stdclone(name
, &subp
, devnames
[i
], &unit
) == 2)
840 if (subp
== NULL
|| *subp
++ != '.')
844 while (isdigit(*subp
)) {
846 sub
+= *subp
++ - '0';
851 sc
= devclass_get_softc(firewire_devclass
, unit
);
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
);