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 $
35 * $DragonFly: src/sys/bus/firewire/fwdev.c,v 1.20 2008/01/06 16:55:49 swildner Exp $
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
43 #if defined(__DragonFly__) || __FreeBSD_version < 500000
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
55 #include <sys/ctype.h>
57 #include <sys/ioccom.h>
58 #include <sys/thread2.h>
62 #include "firewirereg.h"
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>
74 #define CDEV_MAJOR 127
75 #define FWNODE_INVAL 0xffff
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
;
86 struct dev_ops firewire_ops
=
88 { "fw", CDEV_MAJOR
, D_MEM
},
96 .d_strategy
= fw_strategy
,
102 struct fw_isobufreq bufreq
;
106 fwdev_allocbuf(struct firewire_comm
*fc
, struct fw_xferq
*q
,
107 struct fw_bufspec
*b
)
111 if (q
->flag
& (FWXFERQ_RUNNING
| FWXFERQ_EXTBUF
))
114 q
->bulkxfer
= (struct fw_bulkxfer
*) kmalloc(
115 sizeof(struct fw_bulkxfer
) * b
->nchunk
,
118 b
->psize
= roundup2(b
->psize
, sizeof(u_int32_t
));
119 q
->buf
= fwdma_malloc_multiseg(fc
, sizeof(u_int32_t
),
120 b
->psize
, b
->nchunk
* b
->npacket
, BUS_DMA_WAITOK
);
122 if (q
->buf
== NULL
) {
123 kfree(q
->bulkxfer
, M_FW
);
127 q
->bnchunk
= b
->nchunk
;
128 q
->bnpacket
= b
->npacket
;
129 q
->psize
= (b
->psize
+ 3) & ~3;
132 STAILQ_INIT(&q
->stvalid
);
133 STAILQ_INIT(&q
->stfree
);
134 STAILQ_INIT(&q
->stdma
);
137 for(i
= 0 ; i
< q
->bnchunk
; i
++){
138 q
->bulkxfer
[i
].poffset
= i
* q
->bnpacket
;
139 q
->bulkxfer
[i
].mbuf
= NULL
;
140 STAILQ_INSERT_TAIL(&q
->stfree
, &q
->bulkxfer
[i
], link
);
143 q
->flag
&= ~FWXFERQ_MODEMASK
;
144 q
->flag
|= FWXFERQ_STREAM
;
145 q
->flag
|= FWXFERQ_EXTBUF
;
151 fwdev_freebuf(struct fw_xferq
*q
)
153 if (q
->flag
& FWXFERQ_EXTBUF
) {
155 fwdma_free_multiseg(q
->buf
);
157 kfree(q
->bulkxfer
, M_FW
);
159 q
->flag
&= ~FWXFERQ_EXTBUF
;
161 q
->maxq
= FWMAXQUEUE
;
168 fw_open (struct dev_open_args
*ap
)
170 cdev_t dev
= ap
->a_head
.a_dev
;
174 return fwmem_open(ap
);
176 if (dev
->si_drv1
!= NULL
)
179 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
180 if ((dev
->si_flags
& SI_NAMED
) == 0) {
181 int unit
= DEV2UNIT(dev
);
182 int sub
= DEV2SUB(dev
);
184 make_dev(&firewire_ops
, minor(dev
),
185 UID_ROOT
, GID_OPERATOR
, 0660,
186 "fw%d.%d", unit
, sub
);
190 dev
->si_drv1
= kmalloc(sizeof(struct fw_drv1
), M_FW
, M_WAITOK
| M_ZERO
);
196 fw_close (struct dev_close_args
*ap
)
198 cdev_t dev
= ap
->a_head
.a_dev
;
199 struct firewire_softc
*sc
;
200 struct firewire_comm
*fc
;
202 int unit
= DEV2UNIT(dev
);
203 struct fw_xfer
*xfer
;
208 return fwmem_close(ap
);
210 sc
= devclass_get_softc(firewire_devclass
, unit
);
212 d
= (struct fw_drv1
*)dev
->si_drv1
;
215 struct fw_xferq
*ir
= d
->ir
;
217 if ((ir
->flag
& FWXFERQ_OPEN
) == 0)
219 if (ir
->flag
& FWXFERQ_RUNNING
) {
220 ir
->flag
&= ~FWXFERQ_RUNNING
;
221 fc
->irx_disable(fc
, ir
->dmach
);
225 /* drain receiving buffer */
226 for (xfer
= STAILQ_FIRST(&ir
->q
);
227 xfer
!= NULL
; xfer
= STAILQ_FIRST(&ir
->q
)) {
229 STAILQ_REMOVE_HEAD(&ir
->q
, link
);
235 for (fwb
= STAILQ_FIRST(&ir
->binds
); fwb
!= NULL
;
236 fwb
= STAILQ_FIRST(&ir
->binds
)) {
237 STAILQ_REMOVE(&fc
->binds
, fwb
, fw_bind
, fclist
);
238 STAILQ_REMOVE_HEAD(&ir
->binds
, chlist
);
241 ir
->flag
&= ~(FWXFERQ_OPEN
|
242 FWXFERQ_MODEMASK
| FWXFERQ_CHTAGMASK
);
247 struct fw_xferq
*it
= d
->it
;
249 if ((it
->flag
& FWXFERQ_OPEN
) == 0)
251 if (it
->flag
& FWXFERQ_RUNNING
) {
252 it
->flag
&= ~FWXFERQ_RUNNING
;
253 fc
->itx_disable(fc
, it
->dmach
);
257 it
->flag
&= ~(FWXFERQ_OPEN
|
258 FWXFERQ_MODEMASK
| FWXFERQ_CHTAGMASK
);
261 kfree(dev
->si_drv1
, M_FW
);
271 fw_read (struct dev_read_args
*ap
)
273 cdev_t dev
= ap
->a_head
.a_dev
;
274 struct uio
*uio
= ap
->a_uio
;
275 struct firewire_softc
*sc
;
277 struct fw_xfer
*xfer
;
278 int err
= 0, slept
= 0;
279 int unit
= DEV2UNIT(dev
);
285 sc
= devclass_get_softc(firewire_devclass
, unit
);
287 ir
= ((struct fw_drv1
*)dev
->si_drv1
)->ir
;
288 if (ir
== NULL
|| ir
->buf
== NULL
)
292 xfer
= STAILQ_FIRST(&ir
->q
);
293 if (ir
->stproc
== NULL
) {
295 ir
->stproc
= STAILQ_FIRST(&ir
->stvalid
);
296 if (ir
->stproc
!= NULL
) {
298 STAILQ_REMOVE_HEAD(&ir
->stvalid
, link
);
303 if (xfer
== NULL
&& ir
->stproc
== NULL
) {
304 /* no data avaliable */
307 ir
->flag
|= FWXFERQ_WAKEUP
;
308 err
= tsleep(ir
, FWPRI
, "fw_read", hz
);
309 ir
->flag
&= ~FWXFERQ_WAKEUP
;
312 } else if (slept
== 1)
315 } else if(xfer
!= NULL
) {
316 #if 0 /* XXX broken */
317 /* per packet mode or FWACT_CH bind?*/
320 STAILQ_REMOVE_HEAD(&ir
->q
, link
);
322 fp
= &xfer
->recv
.hdr
;
323 if (sc
->fc
->irx_post
!= NULL
)
324 sc
->fc
->irx_post(sc
->fc
, fp
->mode
.ld
);
325 err
= uiomove((void *)fp
, 1 /* XXX header size */, uio
);
326 /* XXX copy payload too */
327 /* XXX we should recycle this xfer */
330 } else if(ir
->stproc
!= NULL
) {
332 fp
= (struct fw_pkt
*)fwdma_v_addr(ir
->buf
,
333 ir
->stproc
->poffset
+ ir
->queued
);
334 if(sc
->fc
->irx_post
!= NULL
)
335 sc
->fc
->irx_post(sc
->fc
, fp
->mode
.ld
);
336 if(fp
->mode
.stream
.len
== 0){
340 err
= uiomove((caddr_t
)fp
,
341 fp
->mode
.stream
.len
+ sizeof(u_int32_t
), uio
);
343 if(ir
->queued
>= ir
->bnpacket
){
345 STAILQ_INSERT_TAIL(&ir
->stfree
, ir
->stproc
, link
);
347 sc
->fc
->irx_enable(sc
->fc
, ir
->dmach
);
350 if (uio
->uio_resid
>= ir
->psize
) {
359 fw_write (struct dev_write_args
*ap
)
361 cdev_t dev
= ap
->a_head
.a_dev
;
362 struct uio
*uio
= ap
->a_uio
;
364 struct firewire_softc
*sc
;
365 int unit
= DEV2UNIT(dev
);
368 struct firewire_comm
*fc
;
372 return physwrite(ap
);
374 sc
= devclass_get_softc(firewire_devclass
, unit
);
376 it
= ((struct fw_drv1
*)dev
->si_drv1
)->it
;
377 if (it
== NULL
|| it
->buf
== NULL
)
380 if (it
->stproc
== NULL
) {
381 it
->stproc
= STAILQ_FIRST(&it
->stfree
);
382 if (it
->stproc
!= NULL
) {
384 STAILQ_REMOVE_HEAD(&it
->stfree
, link
);
387 } else if (slept
== 0) {
389 err
= sc
->fc
->itx_enable(sc
->fc
, it
->dmach
);
392 err
= tsleep(it
, FWPRI
, "fw_write", hz
);
401 fp
= (struct fw_pkt
*)fwdma_v_addr(it
->buf
,
402 it
->stproc
->poffset
+ it
->queued
);
403 err
= uiomove((caddr_t
)fp
, sizeof(struct fw_isohdr
), uio
);
404 err
= uiomove((caddr_t
)fp
->mode
.stream
.payload
,
405 fp
->mode
.stream
.len
, uio
);
407 if (it
->queued
>= it
->bnpacket
) {
409 STAILQ_INSERT_TAIL(&it
->stvalid
, it
->stproc
, link
);
412 err
= sc
->fc
->itx_enable(sc
->fc
, it
->dmach
);
414 if (uio
->uio_resid
>= sizeof(struct fw_isohdr
)) {
424 fw_ioctl (struct dev_ioctl_args
*ap
)
426 cdev_t dev
= ap
->a_head
.a_dev
;
427 struct firewire_softc
*sc
;
428 struct firewire_comm
*fc
;
430 int unit
= DEV2UNIT(dev
);
432 struct fw_device
*fwdev
;
434 struct fw_xferq
*ir
, *it
;
435 struct fw_xfer
*xfer
;
437 struct fw_devinfo
*devinfo
;
440 struct fw_devlstreq
*fwdevlst
= (struct fw_devlstreq
*)ap
->a_data
;
441 struct fw_asyreq
*asyreq
= (struct fw_asyreq
*)ap
->a_data
;
442 struct fw_isochreq
*ichreq
= (struct fw_isochreq
*)ap
->a_data
;
443 struct fw_isobufreq
*ibufreq
= (struct fw_isobufreq
*)ap
->a_data
;
444 struct fw_asybindreq
*bindreq
= (struct fw_asybindreq
*)ap
->a_data
;
445 struct fw_crom_buf
*crom_buf
= (struct fw_crom_buf
*)ap
->a_data
;
448 return fwmem_ioctl(ap
);
453 sc
= devclass_get_softc(firewire_devclass
, unit
);
455 d
= (struct fw_drv1
*)dev
->si_drv1
;
462 for (i
= 0; i
< fc
->nisodma
; i
++) {
464 if ((it
->flag
& FWXFERQ_OPEN
) == 0)
467 if (i
>= fc
->nisodma
) {
471 err
= fwdev_allocbuf(fc
, it
, &d
->bufreq
.tx
);
474 it
->flag
|= FWXFERQ_OPEN
;
477 it
->flag
|= (0x3f & ichreq
->ch
);
478 it
->flag
|= ((0x3 & ichreq
->tag
) << 6);
483 ichreq
->ch
= it
->flag
& 0x3f;
484 ichreq
->tag
= it
->flag
>> 2 & 0x3;
490 for (i
= 0; i
< fc
->nisodma
; i
++) {
492 if ((ir
->flag
& FWXFERQ_OPEN
) == 0)
495 if (i
>= fc
->nisodma
) {
499 err
= fwdev_allocbuf(fc
, ir
, &d
->bufreq
.rx
);
502 ir
->flag
|= FWXFERQ_OPEN
;
505 ir
->flag
|= (0x3f & ichreq
->ch
);
506 ir
->flag
|= ((0x3 & ichreq
->tag
) << 6);
508 err
= fc
->irx_enable(fc
, ir
->dmach
);
512 ichreq
->ch
= ir
->flag
& 0x3f;
513 ichreq
->tag
= ir
->flag
>> 2 & 0x3;
518 bcopy(ibufreq
, &d
->bufreq
, sizeof(d
->bufreq
));
521 bzero(&ibufreq
->rx
, sizeof(ibufreq
->rx
));
523 ibufreq
->rx
.nchunk
= ir
->bnchunk
;
524 ibufreq
->rx
.npacket
= ir
->bnpacket
;
525 ibufreq
->rx
.psize
= ir
->psize
;
527 bzero(&ibufreq
->tx
, sizeof(ibufreq
->tx
));
529 ibufreq
->tx
.nchunk
= it
->bnchunk
;
530 ibufreq
->tx
.npacket
= it
->bnpacket
;
531 ibufreq
->tx
.psize
= it
->psize
;
536 struct tcode_info
*tinfo
;
540 tinfo
= &sc
->fc
->tcode
[fp
->mode
.hdr
.tcode
];
542 if ((tinfo
->flag
& FWTI_BLOCK_ASY
) != 0)
543 pay_len
= MAX(0, asyreq
->req
.len
- tinfo
->hdr_len
);
545 xfer
= fw_xfer_alloc_buf(M_FWXFER
, pay_len
, PAGE_SIZE
/*XXX*/);
549 switch (asyreq
->req
.type
) {
553 fwdev
= fw_noderesolve_eui64(sc
->fc
,
554 &asyreq
->req
.dst
.eui
);
556 device_printf(sc
->fc
->bdev
,
557 "cannot find node\n");
561 fp
->mode
.hdr
.dst
= FWLOCALBUS
| fwdev
->dst
;
564 /* XXX what's this? */
571 bcopy(fp
, (void *)&xfer
->send
.hdr
, tinfo
->hdr_len
);
573 bcopy((char *)fp
+ tinfo
->hdr_len
,
574 (void *)&xfer
->send
.payload
, pay_len
);
575 xfer
->send
.spd
= asyreq
->req
.sped
;
576 xfer
->act
.hand
= fw_asy_callback
;
578 if ((err
= fw_asyreq(sc
->fc
, -1, xfer
)) != 0)
580 if ((err
= tsleep(xfer
, FWPRI
, "asyreq", hz
)) != 0)
582 if (xfer
->resp
!= 0) {
586 if ((tinfo
->flag
& FWTI_TLABEL
) == 0)
590 tinfo
= &sc
->fc
->tcode
[xfer
->recv
.hdr
.mode
.hdr
.tcode
];
591 if (asyreq
->req
.len
>= xfer
->recv
.pay_len
+ tinfo
->hdr_len
)
592 asyreq
->req
.len
= xfer
->recv
.pay_len
;
595 bcopy(&xfer
->recv
.hdr
, fp
, tinfo
->hdr_len
);
596 bcopy(xfer
->recv
.payload
, (char *)fp
+ tinfo
->hdr_len
,
597 MAX(0, asyreq
->req
.len
- tinfo
->hdr_len
));
599 fw_xfer_free_buf(xfer
);
606 fwb
= fw_bindlookup(sc
->fc
,
607 bindreq
->start
.hi
, bindreq
->start
.lo
);
612 STAILQ_REMOVE(&sc
->fc
->binds
, fwb
, fw_bind
, fclist
);
613 STAILQ_REMOVE(&ir
->binds
, fwb
, fw_bind
, chlist
);
617 if(bindreq
->len
<= 0 ){
621 if(bindreq
->start
.hi
> 0xffff ){
625 fwb
= kmalloc(sizeof (struct fw_bind
), M_FW
, M_WAITOK
);
626 fwb
->start
= ((u_int64_t
)bindreq
->start
.hi
<< 32) |
628 fwb
->end
= fwb
->start
+ bindreq
->len
;
630 fwb
->sub
= ir
->dmach
;
631 fwb
->act_type
= FWACT_CH
;
634 xfer
= fw_xfer_alloc(M_FWXFER
);
642 /* XXX broken. need multiple xfer */
643 STAILQ_INIT(&fwb
->xferlist
);
644 STAILQ_INSERT_TAIL(&fwb
->xferlist
, xfer
, link
);
646 err
= fw_bindadd(sc
->fc
, fwb
);
651 devinfo
= &fwdevlst
->dev
[0];
652 devinfo
->dst
= sc
->fc
->nodeid
;
653 devinfo
->status
= 0; /* XXX */
654 devinfo
->eui
.hi
= sc
->fc
->eui
.hi
;
655 devinfo
->eui
.lo
= sc
->fc
->eui
.lo
;
656 STAILQ_FOREACH(fwdev
, &sc
->fc
->devices
, link
) {
657 if(len
< FW_MAX_DEVLST
){
658 devinfo
= &fwdevlst
->dev
[len
++];
659 devinfo
->dst
= fwdev
->dst
;
661 (fwdev
->status
== FWDEVINVAL
)?0:1;
662 devinfo
->eui
.hi
= fwdev
->eui
.hi
;
663 devinfo
->eui
.lo
= fwdev
->eui
.lo
;
668 fwdevlst
->info_len
= len
;
671 bcopy(sc
->fc
->topology_map
, ap
->a_data
,
672 (sc
->fc
->topology_map
->crc_len
+ 1) * 4);
675 STAILQ_FOREACH(fwdev
, &sc
->fc
->devices
, link
)
676 if (FW_EUI64_EQUAL(fwdev
->eui
, crom_buf
->eui
))
679 if (!FW_EUI64_EQUAL(sc
->fc
->eui
, crom_buf
->eui
)) {
684 ptr
= kmalloc(CROMSIZE
, M_FW
, M_WAITOK
);
686 for (i
= 0; i
< CROMSIZE
/4; i
++)
687 ((u_int32_t
*)ptr
)[i
]
688 = ntohl(sc
->fc
->config_rom
[i
]);
691 ptr
= (void *)&fwdev
->csrrom
[0];
692 if (fwdev
->rommax
< CSRROMOFF
)
695 len
= fwdev
->rommax
- CSRROMOFF
+ 4;
697 if (crom_buf
->len
< len
&& crom_buf
->len
>= 0)
701 err
= copyout(ptr
, crom_buf
->ptr
, len
);
713 fw_poll(struct dev_poll_args
*ap
)
715 cdev_t dev
= ap
->a_head
.a_dev
;
716 struct firewire_softc
*sc
;
720 int unit
= DEV2UNIT(dev
);
723 return fwmem_poll(ap
);
725 sc
= devclass_get_softc(firewire_devclass
, unit
);
726 ir
= ((struct fw_drv1
*)dev
->si_drv1
)->ir
;
728 tmp
= POLLIN
| POLLRDNORM
;
729 if (ap
->a_events
& tmp
) {
730 if (STAILQ_FIRST(&ir
->q
) != NULL
)
733 selrecord(curthread
, &ir
->rsel
);
735 tmp
= POLLOUT
| POLLWRNORM
;
736 if (ap
->a_events
& tmp
) {
737 /* XXX should be fixed */
740 ap
->a_events
= revents
;
745 fw_mmap (struct dev_mmap_args
*ap
)
747 cdev_t dev
= ap
->a_head
.a_dev
;
748 struct firewire_softc
*sc
;
749 int unit
= DEV2UNIT(dev
);
752 return fwmem_mmap(ap
);
753 sc
= devclass_get_softc(firewire_devclass
, unit
);
759 fw_strategy(struct dev_strategy_args
*ap
)
761 cdev_t dev
= ap
->a_head
.a_dev
;
762 struct bio
*bio
= ap
->a_bio
;
763 struct buf
*bp
= bio
->bio_buf
;
765 if (DEV_FWMEM(dev
)) {
769 bp
->b_error
= EOPNOTSUPP
;
770 bp
->b_flags
|= B_ERROR
;
771 bp
->b_resid
= bp
->b_bcount
;
777 fwdev_makedev(struct firewire_softc
*sc
)
781 unit
= device_get_unit(sc
->fc
->bdev
);
782 dev_ops_add(&firewire_ops
, FW_UNITMASK
, FW_UNIT(unit
));
787 fwdev_destroydev(struct firewire_softc
*sc
)
791 unit
= device_get_unit(sc
->fc
->bdev
);
792 dev_ops_remove(&firewire_ops
, FW_UNITMASK
, FW_UNIT(unit
));
796 #if defined(__FreeBSD__) && __FreeBSD_version >= 500000
799 fwdev_clone(void *arg
, char *name
, int namelen
, cdev_t
*dev
)
801 struct firewire_softc
*sc
;
802 char *devnames
[NDEVTYPE
] = {"fw", "fwmem"};
804 int devflag
[NDEVTYPE
] = {0, FWMEM_FLAG
};
805 int i
, unit
= 0, sub
= 0;
810 for (i
= 0; i
< NDEVTYPE
; i
++)
811 if (dev_stdclone(name
, &subp
, devnames
[i
], &unit
) == 2)
817 if (subp
== NULL
|| *subp
++ != '.')
821 while (isdigit(*subp
)) {
823 sub
+= *subp
++ - '0';
828 sc
= devclass_get_softc(firewire_devclass
, unit
);
831 *dev
= make_dev(&firewire_ops
, MAKEMINOR(devflag
[i
], unit
, sub
),
832 UID_ROOT
, GID_OPERATOR
, 0660,
833 "%s%d.%d", devnames
[i
], unit
, sub
);
834 (*dev
)->si_flags
|= SI_CHEAPCLONE
;
835 dev_depends(sc
->dev
, *dev
);