diff --git a/headers/private/firewire/firewire_module.h b/headers/private/firewire/firewire_module.h new file mode 100644 index 0000000000..ccc813e86f --- /dev/null +++ b/headers/private/firewire/firewire_module.h @@ -0,0 +1,67 @@ +/* Kernel driver for firewire + * + * Copyright (C) 2007 JiSheng Zhang . All rights reserved + * Distributed under the terms of the MIT license. + */ +#ifndef _FW_MODULE_H +#define _FW_MODULE_H + +#include +#include +#include + +#include "firewire.h" +#include "firewirereg.h" + + +#define FIREWIRE_MODULE_NAME "bus_managers/firewire/v1" +#define MAX_CARDS 4 +struct fw_module_info{ + + bus_manager_info binfo; + + struct fw_device * (*fw_noderesolve_nodeid)(struct firewire_comm *fc, int dst); + + struct fw_device * (*fw_noderesolve_eui64)(struct firewire_comm *fc, struct fw_eui64 *eui); + + int (*fw_asyreq)(struct firewire_comm *fc, int sub, struct fw_xfer *xfer); + + void (*fw_xferwake)(struct fw_xfer *xfer); + int (*fw_xferwait)(struct fw_xfer *xfer); + + struct fw_bind * (*fw_bindlookup)(struct firewire_comm *fc, uint16_t dest_hi, uint32_t dest_lo); + + int (*fw_bindadd)(struct firewire_comm *fc, struct fw_bind *fwb); + + int (*fw_bindremove)(struct firewire_comm *fc, struct fw_bind *fwb); + + int (*fw_xferlist_add)(struct fw_xferlist *q, + int slen, int rlen, int n, + struct firewire_comm *fc, void *sc, void (*hand)(struct fw_xfer *)); + + void (*fw_xferlist_remove)(struct fw_xferlist *q); + + struct fw_xfer * (*fw_xfer_alloc)(); + + struct fw_xfer * (*fw_xfer_alloc_buf)(int send_len, int recv_len); + + void (*fw_xfer_done)(struct fw_xfer *xfer); + + void (*fw_xfer_unload)(struct fw_xfer* xfer); + + void (*fw_xfer_free_buf)( struct fw_xfer* xfer); + + void (*fw_xfer_free)( struct fw_xfer* xfer); + + void (*fw_asy_callback_free)(struct fw_xfer *xfer); + + int (*fw_open_isodma)(struct firewire_comm *fc, int tx); + + int (*get_handle)(int socket, struct firewire_softc *handle); + + struct fwdma_alloc_multi * (*fwdma_malloc_multiseg)(int alignment, + int esize, int n); + + void (*fwdma_free_multiseg)(struct fwdma_alloc_multi *); +}; +#endif diff --git a/headers/private/firewire/firewirereg.h b/headers/private/firewire/firewirereg.h new file mode 100644 index 0000000000..5120f334b7 --- /dev/null +++ b/headers/private/firewire/firewirereg.h @@ -0,0 +1,391 @@ +/*- + * Copyright (c) 2003 Hidetoshi Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/firewire/firewirereg.h,v 1.49 2007/07/15 13:20:48 simokawa Exp $ + * + */ +#ifndef _FIREWIREREG_H +#define _FIREWIREREG_H +#ifndef __HAIKU__ +#ifdef __DragonFly__ +typedef d_thread_t fw_proc; +#include +#elif __FreeBSD_version >= 500000 +typedef struct thread fw_proc; +#include +#else +typedef struct proc fw_proc; +#include +#endif +#endif/*__HAIKU__*/ + +#include + +#ifndef __HAIKU__ +#include +#include +#define splfw splimp +#else +#define splfw disable_interrupts +#define splx restore_interrupts +#endif + +STAILQ_HEAD(fw_xferlist, fw_xfer); + +struct fw_device{ + uint16_t dst; + struct fw_eui64 eui; + uint8_t speed; + uint8_t maxrec; + uint8_t nport; + uint8_t power; +#define CSRROMOFF 0x400 +#define CSRROMSIZE 0x400 + int rommax; /* offset from 0xffff f000 0000 */ + uint32_t csrrom[CSRROMSIZE/4]; + int rcnt; + struct firewire_comm *fc; + uint32_t status; +#define FWDEVINIT 1 +#define FWDEVATTACHED 2 +#define FWDEVINVAL 3 + STAILQ_ENTRY(fw_device) link; +}; + +struct firewire_softc { +#if defined(__FreeBSD__) && __FreeBSD_version >= 500000 + struct cdev *dev; +#endif + struct firewire_comm *fc; +}; + +#define FW_MAX_DMACH 0x20 +#define FW_MAX_DEVCH FW_MAX_DMACH +#define FW_XFERTIMEOUT 1 + +struct firewire_dev_comm { +#ifndef __HAIKU__ + device_t dev; +#endif + struct firewire_comm *fc; + void (*post_busreset) (void *); + void (*post_explore) (void *); +}; + +struct tcode_info { + u_char hdr_len; /* IEEE1394 header length */ + u_char flag; +#define FWTI_REQ (1 << 0) +#define FWTI_RES (1 << 1) +#define FWTI_TLABEL (1 << 2) +#define FWTI_BLOCK_STR (1 << 3) +#define FWTI_BLOCK_ASY (1 << 4) + u_char valid_res; +}; + +#ifdef __HAIKU__ +struct crom_src_buf { + struct crom_src src; + struct crom_chunk root; + struct crom_chunk vendor; + struct crom_chunk hw; +}; +#endif //move from firewire.c to firewirereg.h + +struct firewire_comm{ +#ifndef __HAIKU__ + device_t dev; + device_t bdev; +#endif + uint16_t busid:10, + nodeid:6; + u_int mode; + u_int nport; + u_int speed; + u_int maxrec; + u_int irm; + u_int max_node; + u_int max_hop; +#define FWPHYASYST (1 << 0) + uint32_t status; +#define FWBUSDETACH (-2) +#define FWBUSNOTREADY (-1) +#define FWBUSRESET 0 +#define FWBUSINIT 1 +#define FWBUSCYMELECT 2 +#define FWBUSMGRELECT 3 +#define FWBUSMGRDONE 4 +#define FWBUSEXPLORE 5 +#define FWBUSPHYCONF 6 +#define FWBUSEXPDONE 7 +#define FWBUSCOMPLETION 10 + int nisodma; + struct fw_eui64 eui; + struct fw_xferq + *arq, *atq, *ars, *ats, *it[FW_MAX_DMACH],*ir[FW_MAX_DMACH]; + struct fw_xferlist tlabels[0x40]; + u_char last_tlabel[0x40]; + STAILQ_HEAD(, fw_bind) binds; + STAILQ_HEAD(, fw_device) devices; + u_int sid_cnt; +#define CSRSIZE 0x4000 + uint32_t csr_arc[CSRSIZE/4]; +#define CROMSIZE 0x400 + uint32_t *config_rom; + struct crom_src_buf *crom_src_buf; + struct crom_src *crom_src; + struct crom_chunk *crom_root; + struct fw_topology_map *topology_map; + struct fw_speed_map *speed_map; +#ifndef __HAIKU__ + struct callout busprobe_callout; + struct callout bmr_callout; + struct callout timeout_callout; +#else + //timer_id busprobe_callout; + //timer_id bmr_callout; + //timer_id timeout_callout; +#endif + //struct task task_timeout; + uint32_t (*cyctimer) (struct firewire_comm *); + void (*ibr) (struct firewire_comm *); + uint32_t (*set_bmr) (struct firewire_comm *, uint32_t); +#ifndef __HAIKU__ + int (*ioctl) (struct cdev *, u_long, caddr_t, int, fw_proc *); +#else + int (*ioctl) (void *cookie, uint32 cmd, void *arg, size_t len); +#endif + int (*irx_enable) (struct firewire_comm *, int); + int (*irx_disable) (struct firewire_comm *, int); + int (*itx_enable) (struct firewire_comm *, int); + int (*itx_disable) (struct firewire_comm *, int); + void (*timeout) (void *); + void (*poll) (struct firewire_comm *, int, int); + void (*set_intr) (struct firewire_comm *, int); + void (*irx_post) (struct firewire_comm *, uint32_t *); + void (*itx_post) (struct firewire_comm *, uint32_t *); + struct tcode_info *tcode; +#ifndef __HAIKU__ + bus_dma_tag_t dmat; + struct mtx mtx; + struct mtx wait_lock; +#else + sem_id mtx; + sem_id wait_lock; +#endif +// struct taskqueue *taskqueue;//to be completed +#ifndef __HAIKU__ + struct proc *probe_thread; +#else + thread_id probe_thread; +#endif +}; +#define CSRARC(sc, offset) ((sc)->csr_arc[(offset)/4]) + +#ifndef __HAIKU__ +#define FW_GMTX(fc) (&(fc)->mtx) +#define FW_GLOCK(fc) mtx_lock(FW_GMTX(fc)) +#define FW_GUNLOCK(fc) mtx_unlock(FW_GMTX(fc)) +#define FW_GLOCK_ASSERT(fc) mtx_assert(FW_GMTX(fc), MA_OWNED) +#endif +#define FW_GMTX(fc) ((fc)->mtx) +#define FW_GLOCK(fc) acquire_sem_etc(FW_GMTX(fc), 1, B_CAN_INTERRUPT | B_TIMEOUT, 0) +#define FW_GUNLOCK(fc) release_sem(FW_GMTX(fc)) +#define FW_GLOCK_ASSERT(fc) + +struct fw_xferq { + int flag; +#define FWXFERQ_CHTAGMASK 0xff +#define FWXFERQ_RUNNING (1 << 8) +#define FWXFERQ_STREAM (1 << 9) + +#define FWXFERQ_BULK (1 << 11) +#define FWXFERQ_MODEMASK (7 << 10) + +#define FWXFERQ_EXTBUF (1 << 13) +#define FWXFERQ_OPEN (1 << 14) + +#define FWXFERQ_HANDLER (1 << 16) +#define FWXFERQ_WAKEUP (1 << 17) + void (*start) (struct firewire_comm*); + int dmach; + struct fw_xferlist q; + u_int queued; + u_int maxq; + u_int psize; + struct fwdma_alloc_multi *buf; + u_int bnchunk; + u_int bnpacket; + struct fw_bulkxfer *bulkxfer; + STAILQ_HEAD(, fw_bulkxfer) stvalid; + STAILQ_HEAD(, fw_bulkxfer) stfree; + STAILQ_HEAD(, fw_bulkxfer) stdma; + struct fw_bulkxfer *stproc; +// struct selinfo rsel; + spinlock Spinlock; + sem_id Sem; + caddr_t sc; + void (*hand) (struct fw_xferq *); +}; + +struct fw_bulkxfer{ + int poffset; +// struct mbuf *mbuf;//to be completed + STAILQ_ENTRY(fw_bulkxfer) link; + caddr_t start; + caddr_t end; + int resp; +}; + +struct fw_bind{ + u_int64_t start; + u_int64_t end; + struct fw_xferlist xferlist; + STAILQ_ENTRY(fw_bind) fclist; + STAILQ_ENTRY(fw_bind) chlist; + void *sc; +}; + +struct fw_xfer{ + caddr_t sc; + struct firewire_comm *fc; + struct fw_xferq *q; +// struct timeval tv;//to be completed + int8_t resp; +#define FWXF_INIT 0x00 +#define FWXF_INQ 0x01 +#define FWXF_START 0x02 +#define FWXF_SENT 0x04 +#define FWXF_SENTERR 0x08 +#define FWXF_BUSY 0x10 +#define FWXF_RCVD 0x20 + +#define FWXF_WAKE 0x80 + uint8_t flag; + int8_t tl; + void (*hand) (struct fw_xfer *); + struct { + struct fw_pkt hdr; + uint32_t *payload; + uint16_t pay_len; + uint8_t spd; + } send, recv; +// struct mbuf *mbuf;//to be completed + STAILQ_ENTRY(fw_xfer) link; + STAILQ_ENTRY(fw_xfer) tlabel; + struct malloc_type *malloc; +}; + +struct fw_rcv_buf { + struct firewire_comm *fc; + struct fw_xfer *xfer; + struct iovec *vec; + u_int nvec; + uint8_t spd; +}; +#ifndef __HAIKU__ +void fw_sidrcv (struct firewire_comm *, uint32_t *, u_int); +void fw_rcv (struct fw_rcv_buf *); +void fw_xfer_unload ( struct fw_xfer*); +void fw_xfer_free_buf ( struct fw_xfer*); +void fw_xfer_free ( struct fw_xfer*); +struct fw_xfer *fw_xfer_alloc (struct malloc_type *); +struct fw_xfer *fw_xfer_alloc_buf (struct malloc_type *, int, int); +void fw_init (struct firewire_comm *); +int fw_tbuf_update (struct firewire_comm *, int, int); +int fw_rbuf_update (struct firewire_comm *, int, int); +int fw_bindadd (struct firewire_comm *, struct fw_bind *); +int fw_bindremove (struct firewire_comm *, struct fw_bind *); +int fw_xferlist_add (struct fw_xferlist *, struct malloc_type *, int, int, int, + struct firewire_comm *, void *, void (*)(struct fw_xfer *)); +void fw_xferlist_remove (struct fw_xferlist *); +int fw_asyreq (struct firewire_comm *, int, struct fw_xfer*); +void fw_busreset (struct firewire_comm *, uint32_t); +uint16_t fw_crc16 (uint32_t *, uint32_t); +void fw_xfer_timeout (void *); +void fw_xfer_done (struct fw_xfer *); +void fw_xferwake (struct fw_xfer *); +int fw_xferwait (struct fw_xfer *); +void fw_asy_callback_free (struct fw_xfer *); +struct fw_device *fw_noderesolve_nodeid (struct firewire_comm *, int); +struct fw_device *fw_noderesolve_eui64 (struct firewire_comm *, struct fw_eui64 *); +struct fw_bind *fw_bindlookup (struct firewire_comm *, uint16_t, uint32_t); +void fw_drain_txq (struct firewire_comm *); +int fwdev_makedev (struct firewire_softc *); +int fwdev_destroydev (struct firewire_softc *); +void fwdev_clone (void *, struct ucred *, char *, int, struct cdev **); +int fw_open_isodma(struct firewire_comm *, int); +#endif +extern int firewire_debug; +#ifndef __HAIKU__ +extern devclass_t firewire_devclass; +#endif +extern int firewire_phydma_enable; + +#ifndef __HAIKU__ +#ifdef __DragonFly__ +#define FWPRI PCATCH +#else +#define FWPRI ((PZERO+8)|PCATCH) +#endif + +#if defined(__DragonFly__) || __FreeBSD_version < 500000 +#define CALLOUT_INIT(x) callout_init(x) +#else +#define CALLOUT_INIT(x) callout_init(x, 1 /* mpsafe */) +#endif + +#if defined(__DragonFly__) || __FreeBSD_version < 500000 +/* compatibility shim for 4.X */ +#define bio buf +#define bio_bcount b_bcount +#define bio_cmd b_flags +#define bio_count b_count +#define bio_data b_data +#define bio_dev b_dev +#define bio_error b_error +#define bio_flags b_flags +#define bio_offset b_offset +#define bio_resid b_resid +#define BIO_ERROR B_ERROR +#define BIO_READ B_READ +#define BIO_WRITE B_WRITE +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +MALLOC_DECLARE(M_FW); +MALLOC_DECLARE(M_FWXFER); +#endif /*__HAIKU__*/ + +#endif diff --git a/headers/private/firewire/fwdma.h b/headers/private/firewire/fwdma.h new file mode 100644 index 0000000000..5edf7b3c87 --- /dev/null +++ b/headers/private/firewire/fwdma.h @@ -0,0 +1,130 @@ +/*- + * Copyright (C) 2003 + * Hidetoshi Shimokawa. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * + * This product includes software developed by Hidetoshi Shimokawa. + * + * 4. Neither the name of the author nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/firewire/fwdma.h,v 1.3 2005/01/06 01:42:41 imp Exp $ + */ +#ifndef _FWDMA_H +#define _FWDMA_H + +#ifndef howmany +#define howmany(x, y) (((x)+((y)-1))/(y)) // x/y的上界 +#endif +#define rounddown(x, y) (((x)/(y))*(y)) // 比x小,y的最大的倍数 +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ // 比x大,y的最小倍数 +#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ +#define powerof2(x) ((((x)-1)&(x))==0) // 是否是2的次方 + +typedef uint32_t bus_addr_t; +typedef uint32_t bus_size_t; + +struct fwdma_alloc { +// bus_dma_tag_t dma_tag; +// bus_dmamap_t dma_map; + area_id BufArea; + void * v_addr; + bus_addr_t bus_addr; +}; + +struct fwdma_seg { +// bus_dmamap_t dma_map; + + void * v_addr; + bus_addr_t bus_addr; +}; + +struct fwdma_alloc_multi { + bus_size_t ssize; + bus_size_t esize; + int nseg; +// bus_dma_tag_t dma_tag; + area_id BufArea; + + struct fwdma_seg seg[0]; +}; + +static __inline void * +fwdma_v_addr(struct fwdma_alloc_multi *am, int index) +{ + bus_size_t ssize = am->ssize; + int offset = am->esize * index; + + return ((caddr_t)am->seg[offset / ssize].v_addr + (offset % ssize)); +} + +static __inline bus_addr_t +fwdma_bus_addr(struct fwdma_alloc_multi *am, int index) +{ + bus_size_t ssize = am->ssize; + int offset = am->esize * index; + + return (am->seg[offset / ssize].bus_addr + (offset % ssize)); +} +#ifndef __HAIKU__ +static __inline void +fwdma_sync(struct fwdma_alloc *dma, bus_dmasync_op_t op) +{ + bus_dmamap_sync(dma->dma_tag, dma->dma_map, op); +} + +static __inline void +fwdma_sync_multiseg(struct fwdma_alloc_multi *am, + int start, int end, bus_dmasync_op_t op) +{ + struct fwdma_seg *seg, *eseg; + + seg = &am->seg[am->esize * start / am->ssize]; + eseg = &am->seg[am->esize * end / am->ssize]; + for (; seg <= eseg; seg ++) + bus_dmamap_sync(am->dma_tag, seg->dma_map, op); +} + +static __inline void +fwdma_sync_multiseg_all(struct fwdma_alloc_multi *am, bus_dmasync_op_t op) +{ + struct fwdma_seg *seg; + int i; + + seg = &am->seg[0]; + for (i = 0; i < am->nseg; i++, seg++) + bus_dmamap_sync(am->dma_tag, seg->dma_map, op); +} + +void *fwdma_malloc(int, bus_size_t, struct fwdma_alloc *, int); +void fwdma_free(struct fwdma_alloc *); +void *fwdma_malloc_size(bus_dma_tag_t, bus_dmamap_t *, bus_size_t, bus_addr_t *, int); +void fwdma_free_size(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t); +struct fwdma_alloc_multi *fwdma_malloc_multiseg(struct firewire_comm *, + int, int, int, int); +#endif /*__HAIKU__*/ + +#endif /* _FWDMA_H*/ diff --git a/src/add-ons/kernel/drivers/bus/Jamfile b/src/add-ons/kernel/drivers/bus/Jamfile index 8a9c4e591a..98253e3204 100644 --- a/src/add-ons/kernel/drivers/bus/Jamfile +++ b/src/add-ons/kernel/drivers/bus/Jamfile @@ -1,5 +1,6 @@ SubDir HAIKU_TOP src add-ons kernel drivers bus ; +SubInclude HAIKU_TOP src add-ons kernel drivers bus firewire ; SubInclude HAIKU_TOP src add-ons kernel drivers bus pcmcia ; SubInclude HAIKU_TOP src add-ons kernel drivers bus scsi ; SubInclude HAIKU_TOP src add-ons kernel drivers bus usb ; diff --git a/src/add-ons/kernel/drivers/bus/firewire/Jamfile b/src/add-ons/kernel/drivers/bus/firewire/Jamfile new file mode 100644 index 0000000000..a016bb97cf --- /dev/null +++ b/src/add-ons/kernel/drivers/bus/firewire/Jamfile @@ -0,0 +1,10 @@ +SubDir HAIKU_TOP src add-ons kernel drivers bus firewire ; + +UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ; +UsePrivateHeaders firewire ; + +SubDirCcFlags [ FDefines _KERNEL=1 ] ; + +KernelAddon fw_raw : + fw_raw.c + ; diff --git a/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c b/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c new file mode 100644 index 0000000000..18b56de286 --- /dev/null +++ b/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c @@ -0,0 +1,1013 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * JiSheng Zhang + * Jérôme DUVAL + */ +/*- + * Copyright (c) 2003 Hidetoshi Shimokawa + * Copyright (c) 1998-2002 Katsushi Kobayashi and Hidetoshi Shimokawa + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the acknowledgement as bellow: + * + * This product includes software developed by K. Kobayashi and H. Shimokawa + * + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/sys/dev/firewire/fwdev.c,v 1.52 2007/06/06 14:31:36 simokawa Exp $ + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "queue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwdma.h" +//#include "fwmem.h" +#include "iec68113.h" + +#include "firewire_module.h" + +#define FWNODE_INVAL 0xffff + +int32 api_version = B_CUR_DRIVER_API_VERSION; +struct fw_module_info *gFirewire; +const char fwname[] = "bus/fw/%ld"; +#define FWNAMEMAX 9 +static char ** devices; +uint32 devices_count = 0; + + +struct fw_drv1 { + struct firewire_comm *fc; + struct fw_xferq *ir; + struct fw_xferq *it; + struct fw_isobufreq bufreq; + STAILQ_HEAD(, fw_bind) binds; + STAILQ_HEAD(, fw_xfer) rq; + sem_id rqSem; +}; + +static int +fwdev_allocbuf(struct fw_xferq *q, + struct fw_bufspec *b) +{ + int i; + + if (q->flag & (FWXFERQ_RUNNING | FWXFERQ_EXTBUF)) + return(EBUSY); + + q->bulkxfer = (struct fw_bulkxfer *) malloc( + sizeof(struct fw_bulkxfer) * b->nchunk); + if (q->bulkxfer == NULL) + return(ENOMEM); + + b->psize = roundup2(b->psize, sizeof(uint32_t)); + q->buf = gFirewire->fwdma_malloc_multiseg(sizeof(uint32_t), + b->psize, b->nchunk * b->npacket); + + if (q->buf == NULL) { + free(q->bulkxfer); + q->bulkxfer = NULL; + return(ENOMEM); + } + q->bnchunk = b->nchunk; + q->bnpacket = b->npacket; + q->psize = (b->psize + 3) & ~3; + q->queued = 0; + + STAILQ_INIT(&q->stvalid); + STAILQ_INIT(&q->stfree); + STAILQ_INIT(&q->stdma); + q->stproc = NULL; + + for(i = 0 ; i < q->bnchunk; i++){ + q->bulkxfer[i].poffset = i * q->bnpacket; + //q->bulkxfer[i].mbuf = NULL; //to be completed when find mbuf's counterpart + STAILQ_INSERT_TAIL(&q->stfree, &q->bulkxfer[i], link); + } + + q->flag &= ~FWXFERQ_MODEMASK; + q->flag |= FWXFERQ_STREAM; + q->flag |= FWXFERQ_EXTBUF; + + return (0); +} + +static int +fwdev_freebuf(struct fw_xferq *q) +{ + if (q->flag & FWXFERQ_EXTBUF) { + if (q->buf != NULL) + gFirewire->fwdma_free_multiseg(q->buf); + q->buf = NULL; + free(q->bulkxfer); + q->bulkxfer = NULL; + q->flag &= ~FWXFERQ_EXTBUF; + q->psize = 0; + q->maxq = FWMAXQUEUE; + } + return (0); +} + + +static status_t +fw_open (const char *name, uint32 flags, void **cookie) +{ + int32 count = -1; + int32 i; + struct fw_drv1 *d; + struct firewire_softc *sc = NULL; + + *cookie = NULL; + for (i=0; iget_handle(count, sc) != B_OK) { + return ENODEV; + } + + if (sc == NULL) + return (ENXIO); + + FW_GLOCK(sc->fc); + if (*cookie != NULL) { + FW_GUNLOCK(sc->fc); + return (EBUSY); + } + /* set dummy value for allocation */ + *cookie = (void *)-1; + FW_GUNLOCK(sc->fc); + + *cookie = malloc(sizeof(struct fw_drv1)); + if (*cookie == NULL) + return (ENOMEM); + memset(*cookie, 0, sizeof(struct fw_drv1)); + + d = (struct fw_drv1 *)(*cookie); + d->fc = sc->fc; + STAILQ_INIT(&d->binds); + STAILQ_INIT(&d->rq); + d->rqSem = create_sem(0, "fw_raw rq"); + if(d->rqSem < B_OK){ + dprintf("fwraw: failed creating rq semaphore\n"); + free(*cookie); + return B_ERROR; + } + + return B_OK; +} + + +static status_t +fw_close (void *cookie) +{ + return B_OK; +} + + +static status_t +fw_free(void *cookie) +{ + struct firewire_comm *fc; + struct fw_drv1 *d; + struct fw_xfer *xfer; + struct fw_bind *fwb; + + + d = (struct fw_drv1 *)cookie; + delete_sem(d->rqSem); + fc = d->fc; + + /* remove binding */ + for (fwb = STAILQ_FIRST(&d->binds); fwb != NULL; + fwb = STAILQ_FIRST(&d->binds)) { + gFirewire->fw_bindremove(fc, fwb); + STAILQ_REMOVE_HEAD(&d->binds, chlist); + gFirewire->fw_xferlist_remove(&fwb->xferlist); + free(fwb); + } + if (d->ir != NULL) { + struct fw_xferq *ir = d->ir; + + if ((ir->flag & FWXFERQ_OPEN) == 0) + return (EINVAL); + if (ir->flag & FWXFERQ_RUNNING) { + ir->flag &= ~FWXFERQ_RUNNING; + fc->irx_disable(fc, ir->dmach); + } + /* free extbuf */ + fwdev_freebuf(ir); + /* drain receiving buffer */ + for (xfer = STAILQ_FIRST(&ir->q); + xfer != NULL; xfer = STAILQ_FIRST(&ir->q)) { + ir->queued --; + STAILQ_REMOVE_HEAD(&ir->q, link); + + xfer->resp = 0; + gFirewire->fw_xfer_done(xfer); + } + ir->flag &= ~(FWXFERQ_OPEN | + FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); + d->ir = NULL; + + } + if (d->it != NULL) { + struct fw_xferq *it = d->it; + + if ((it->flag & FWXFERQ_OPEN) == 0) + return (EINVAL); + if (it->flag & FWXFERQ_RUNNING) { + it->flag &= ~FWXFERQ_RUNNING; + fc->itx_disable(fc, it->dmach); + } + /* free extbuf */ + fwdev_freebuf(it); + it->flag &= ~(FWXFERQ_OPEN | + FWXFERQ_MODEMASK | FWXFERQ_CHTAGMASK); + d->it = NULL; + } + free(d); + d = NULL; + + return B_OK; +} + + +static int +fw_read_async(struct fw_drv1 *d, off_t position, void *buf, size_t *num_bytes) +{ + int err = 0; + struct fw_xfer *xfer; + struct fw_bind *fwb; + struct fw_pkt *fp; + struct tcode_info *tinfo; + cpu_status s; + + int len, pbytes = (int)*num_bytes;//pbytes:to be processed bytes + *num_bytes = 0; + + FW_GLOCK(d->fc); + while ((xfer = STAILQ_FIRST(&d->rq)) == NULL && err == B_OK){ + //err = msleep(&d->rq, FW_GMTX(d->fc), FWPRI, "fwra", 0); + FW_GUNLOCK(d->fc); + err = acquire_sem_etc(d->rqSem, 1, B_CAN_INTERRUPT | B_TIMEOUT, B_INFINITE_TIMEOUT);//B_INFINITE_TIMEOUT means no timeout? + FW_GLOCK(d->fc); + } + +// if (err != 0) { + if (err != B_OK) { + + FW_GUNLOCK(d->fc); + return (err); + } + + s = splfw(); + STAILQ_REMOVE_HEAD(&d->rq, link); + FW_GUNLOCK(xfer->fc); + splx(s); + fp = &xfer->recv.hdr; +#if 0 /* for GASP ?? */ + if (fc->irx_post != NULL) + fc->irx_post(fc, fp->mode.ld); +#endif + tinfo = &xfer->fc->tcode[fp->mode.hdr.tcode]; +// err = uiomove((void *)fp, tinfo->hdr_len, uio); + len = tinfo->hdr_len; + len = MIN(len, pbytes); + err = user_memcpy(buf, fp, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + +// if (err) + if (err < B_OK){ + goto out; + } + +// err = uiomove((void *)xfer->recv.payload, xfer->recv.pay_len, uio); + len = xfer->recv.pay_len; + len = MIN(len, pbytes); + err = user_memcpy(buf, (void *)xfer->recv.payload, len); + pbytes -= len; + *num_bytes += len;//get have been processed bytes num + buf = (void *)((caddr_t)buf + len); + +out: + /* recycle this xfer */ + fwb = (struct fw_bind *)xfer->sc; + gFirewire->fw_xfer_unload(xfer); + xfer->recv.pay_len = B_PAGE_SIZE; + FW_GLOCK(xfer->fc); + STAILQ_INSERT_TAIL(&fwb->xferlist, xfer, link); + FW_GUNLOCK(xfer->fc); + return (err); +} + + +/* + * read request. + */ +static status_t +fw_read (void *cookie, off_t position, void *buf, size_t *num_bytes) +{ + struct fw_drv1 *d; + struct fw_xferq *ir; + struct firewire_comm *fc; + int err = 0, slept = 0; + struct fw_pkt *fp; + cpu_status s; + + int len, pbytes = (int)*num_bytes; + *num_bytes = 0; + + d = (struct fw_drv1 *)cookie; + fc = d->fc; + ir = d->ir; + + if (ir == NULL) + return (fw_read_async(d, position, buf, num_bytes)); + + if (ir->buf == NULL) + return (EIO); + + FW_GLOCK(fc); +readloop: + if (ir->stproc == NULL) { + /* iso bulkxfer */ + ir->stproc = STAILQ_FIRST(&ir->stvalid); + if (ir->stproc != NULL) { + s = splfw(); + STAILQ_REMOVE_HEAD(&ir->stvalid, link); + splx(s); + ir->queued = 0; + } + } + if (ir->stproc == NULL) { + /* no data avaliable */ + if (slept == 0) { + slept = 1; + ir->flag |= FWXFERQ_WAKEUP; + //err = msleep(ir, FW_GMTX(fc), FWPRI, "fw_read", hz); + FW_GUNLOCK(fc); + err = acquire_sem_etc(ir->Sem, 1, B_CAN_INTERRUPT | B_TIMEOUT, 1000000); + FW_GLOCK(fc); + + ir->flag &= ~FWXFERQ_WAKEUP; + if (err == B_OK) + goto readloop; + } else if (slept == 1) + err = EIO; + FW_GUNLOCK(fc); + return err; + } else if(ir->stproc != NULL) { + /* iso bulkxfer */ + FW_GUNLOCK(fc); + fp = (struct fw_pkt *)fwdma_v_addr(ir->buf, + ir->stproc->poffset + ir->queued); + if(fc->irx_post != NULL) + fc->irx_post(fc, fp->mode.ld); + if(fp->mode.stream.len == 0){ + err = EIO; + return err; + } + // err = uiomove((caddr_t)fp, + // fp->mode.stream.len + sizeof(uint32_t), uio); + len = fp->mode.stream.len + sizeof(uint32_t); + len = MIN(len, pbytes); + err = user_memcpy(buf, (void *)fp, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + + ir->queued ++; + if(ir->queued >= ir->bnpacket){ + s = splfw(); + STAILQ_INSERT_TAIL(&ir->stfree, ir->stproc, link); + splx(s); + fc->irx_enable(fc, ir->dmach); + ir->stproc = NULL; + } + if (pbytes >= ir->psize) { + slept = -1; + FW_GLOCK(fc); + goto readloop; + } + } + return err; +} + + +static int +fw_write_async(struct fw_drv1 *d, off_t position, const void *buf, size_t *num_bytes) +{ + struct fw_xfer *xfer; + struct fw_pkt pkt; + struct tcode_info *tinfo; + int err; + + int len, pbytes = (int)*num_bytes; + *num_bytes = 0; + + bzero(&pkt, sizeof(struct fw_pkt)); +// if ((err = uiomove((caddr_t)&pkt, sizeof(uint32_t), uio))) + len = sizeof(uint32_t); + len = MIN(len, pbytes); + err = user_memcpy(&pkt, buf, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + if (err < B_OK) + return (err); + + tinfo = &d->fc->tcode[pkt.mode.hdr.tcode]; +// if ((err = uiomove((caddr_t)&pkt + sizeof(uint32_t), +// tinfo->hdr_len - sizeof(uint32_t), uio))) + len = tinfo->hdr_len - sizeof(uint32_t); + len = MIN(len, pbytes); + err = user_memcpy((void*)((caddr_t)&pkt + sizeof(uint32_t)), + buf, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + + if (err < B_OK) + return (err); + + if ((xfer = gFirewire->fw_xfer_alloc_buf(pbytes, + B_PAGE_SIZE/*XXX*/)) == NULL) + return (ENOMEM); + + bcopy(&pkt, &xfer->send.hdr, sizeof(struct fw_pkt)); +// xfer->send.pay_len = uio->uio_resid; + xfer->send.pay_len = pbytes; + +/* if (uio->uio_resid > 0) { + if ((err = uiomove((caddr_t)&xfer->send.payload[0], + uio->uio_resid, uio)));// here the FreeBSD's stack author may be careless? + goto out; + }*/ + if (pbytes > 0) { + len = pbytes; + err = user_memcpy((void*)&xfer->send.payload[0], + buf, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + if (err < B_OK) + goto out; + } + + xfer->fc = d->fc; + xfer->sc = NULL; + xfer->hand = gFirewire->fw_xferwake; + xfer->send.spd = 2 /* XXX */; + + if ((err = gFirewire->fw_asyreq(xfer->fc, -1, xfer))) + goto out; + + if ((err = gFirewire->fw_xferwait(xfer))) + goto out; + + if (xfer->resp != 0) { + err = xfer->resp; + goto out; + } + + if (xfer->flag & FWXF_RCVD) { + FW_GLOCK(xfer->fc); + STAILQ_INSERT_TAIL(&d->rq, xfer, link); + FW_GUNLOCK(xfer->fc); + return B_OK; + } + +out: + gFirewire->fw_xfer_free(xfer); + return (err); +} + + +static status_t +fw_write (void *cookie, off_t position, const void *buf, size_t *num_bytes) +{ + int err = 0; + int slept = 0; + struct fw_drv1 *d; + struct fw_pkt *fp; + struct firewire_comm *fc; + struct fw_xferq *it; + cpu_status s; + + int len, pbytes = (int)*num_bytes; + *num_bytes = 0; + + d = (struct fw_drv1 *)cookie; + fc = d->fc; + it = d->it; + + if (it == NULL) + return (fw_write_async(d, position, buf, num_bytes)); + + if (it->buf == NULL) + return (EIO); + + FW_GLOCK(fc); +isoloop: + if (it->stproc == NULL) { + it->stproc = STAILQ_FIRST(&it->stfree); + if (it->stproc != NULL) { + s = splfw(); + STAILQ_REMOVE_HEAD(&it->stfree, link); + splx(s); + it->queued = 0; + } else if (slept == 0) { + slept = 1; +#if 0 /* XXX to avoid lock recursion */ + err = fc->itx_enable(fc, it->dmach); + if (err) + goto out; +#endif +// err = msleep(it, FW_GMTX(fc), FWPRI, "fw_write", hz); + FW_GUNLOCK(fc); + acquire_sem_etc(it->Sem, 1, B_CAN_INTERRUPT | B_TIMEOUT, 1000000); + FW_GLOCK(fc); + +// if (err) + if (err < B_OK) + goto out; + goto isoloop; + } else { + err = EIO; + goto out; + } + } + FW_GUNLOCK(fc); + fp = (struct fw_pkt *)fwdma_v_addr(it->buf, + it->stproc->poffset + it->queued); +// err = uiomove((caddr_t)fp, sizeof(struct fw_isohdr), uio); + len = sizeof(struct fw_isohdr); + len = MIN(len, pbytes); + err = user_memcpy(fp, buf, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + +// err = uiomove((caddr_t)fp->mode.stream.payload, +// fp->mode.stream.len, uio); + len = fp->mode.stream.len; + len = MIN(len, pbytes); + err = user_memcpy(fp->mode.stream.payload, buf, len); + pbytes -= len; + *num_bytes += len; + buf = (void *)((caddr_t)buf + len); + + it->queued ++; + if (it->queued >= it->bnpacket) { + s = splfw(); + STAILQ_INSERT_TAIL(&it->stvalid, it->stproc, link); + splx(s); + it->stproc = NULL; + err = fc->itx_enable(fc, it->dmach); + } +// if (uio->uio_resid >= sizeof(struct fw_isohdr)) { + if (pbytes >= sizeof(struct fw_isohdr)) { + slept = 0; + FW_GLOCK(fc); + goto isoloop; + } + return err; + +out: + FW_GUNLOCK(fc); + return err; +} + + +static void +fw_hand(struct fw_xfer *xfer) +{ + struct fw_bind *fwb; + struct fw_drv1 *d; + + fwb = (struct fw_bind *)xfer->sc; + d = (struct fw_drv1 *)fwb->sc; + FW_GLOCK(xfer->fc); + STAILQ_INSERT_TAIL(&d->rq, xfer, link); + FW_GUNLOCK(xfer->fc); +// wakeup(&d->rq); + release_sem_etc(d->rqSem, 1, B_DO_NOT_RESCHEDULE); +} + + +/* + * ioctl support. + */ +static status_t +fw_ioctl (void *cookie, uint32 cmd, void *data, size_t length) +{ + struct firewire_comm *fc; + struct fw_drv1 *d; + int i, len, err = 0; + struct fw_device *fwdev; + struct fw_bind *fwb; + struct fw_xferq *ir, *it; + struct fw_xfer *xfer; + struct fw_pkt *fp; + struct fw_devinfo *devinfo; + void *ptr; + + struct fw_devlstreq *fwdevlst = (struct fw_devlstreq *)data; + struct fw_asyreq *asyreq = (struct fw_asyreq *)data; + struct fw_isochreq *ichreq = (struct fw_isochreq *)data; + struct fw_isobufreq *ibufreq = (struct fw_isobufreq *)data; + struct fw_asybindreq *bindreq = (struct fw_asybindreq *)data; + struct fw_crom_buf *crom_buf = (struct fw_crom_buf *)data; + + if (!data) + return(EINVAL); + + d = (struct fw_drv1 *)cookie; + fc = d->fc; + ir = d->ir; + it = d->it; + + switch (cmd) { + case FW_STSTREAM: + if (it == NULL) { + i = gFirewire->fw_open_isodma(fc, /* tx */1); + if (i < 0) { + err = EBUSY; + break; + } + it = fc->it[i]; + err = fwdev_allocbuf(it, &d->bufreq.tx); + if (err) { + it->flag &= ~FWXFERQ_OPEN; + break; + } + } + it->flag &= ~0xff; + it->flag |= (0x3f & ichreq->ch); + it->flag |= ((0x3 & ichreq->tag) << 6); + d->it = it; + break; + case FW_GTSTREAM: + if (it != NULL) { + ichreq->ch = it->flag & 0x3f; + ichreq->tag = it->flag >> 2 & 0x3; + } else + err = EINVAL; + break; + case FW_SRSTREAM: + if (ir == NULL) { + i = gFirewire->fw_open_isodma(fc, /* tx */0); + if (i < 0) { + err = EBUSY; + break; + } + ir = fc->ir[i]; + err = fwdev_allocbuf(ir, &d->bufreq.rx); + if (err) { + ir->flag &= ~FWXFERQ_OPEN; + break; + } + } + ir->flag &= ~0xff; + ir->flag |= (0x3f & ichreq->ch); + ir->flag |= ((0x3 & ichreq->tag) << 6); + d->ir = ir; + err = fc->irx_enable(fc, ir->dmach); + break; + case FW_GRSTREAM: + if (d->ir != NULL) { + ichreq->ch = ir->flag & 0x3f; + ichreq->tag = ir->flag >> 2 & 0x3; + } else + err = EINVAL; + break; + case FW_SSTBUF: + bcopy(ibufreq, &d->bufreq, sizeof(d->bufreq)); + break; + case FW_GSTBUF: + bzero(&ibufreq->rx, sizeof(ibufreq->rx)); + if (ir != NULL) { + ibufreq->rx.nchunk = ir->bnchunk; + ibufreq->rx.npacket = ir->bnpacket; + ibufreq->rx.psize = ir->psize; + } + bzero(&ibufreq->tx, sizeof(ibufreq->tx)); + if (it != NULL) { + ibufreq->tx.nchunk = it->bnchunk; + ibufreq->tx.npacket = it->bnpacket; + ibufreq->tx.psize = it->psize; + } + break; + case FW_ASYREQ: + { + struct tcode_info *tinfo; + int pay_len = 0; + + fp = &asyreq->pkt; + tinfo = &fc->tcode[fp->mode.hdr.tcode]; + + if ((tinfo->flag & FWTI_BLOCK_ASY) != 0) + pay_len = MAX(0, asyreq->req.len - tinfo->hdr_len); + + xfer = gFirewire->fw_xfer_alloc_buf(pay_len, B_PAGE_SIZE/*XXX*/); + if (xfer == NULL) + return (ENOMEM); + + switch (asyreq->req.type) { + case FWASREQNODE: + break; + case FWASREQEUI: + fwdev = gFirewire->fw_noderesolve_eui64(fc, + &asyreq->req.dst.eui); + if (fwdev == NULL) { + dprintf("FWASREQEUI cannot find node\n"); + err = EINVAL; + goto out; + } + fp->mode.hdr.dst = FWLOCALBUS | fwdev->dst; + break; + case FWASRESTL: + /* XXX what's this? */ + break; + case FWASREQSTREAM: + /* nothing to do */ + break; + } + + bcopy(fp, (void *)&xfer->send.hdr, tinfo->hdr_len); + if (pay_len > 0) + bcopy((char *)fp + tinfo->hdr_len, + (void *)xfer->send.payload, pay_len); + xfer->send.spd = asyreq->req.sped; + xfer->hand = gFirewire->fw_xferwake; + + if ((err = gFirewire->fw_asyreq(fc, -1, xfer)) != 0) + goto out; + if ((err = gFirewire->fw_xferwait(xfer)) != 0) + goto out; + if (xfer->resp != 0) { + err = EIO; + goto out; + } + if ((tinfo->flag & FWTI_TLABEL) == 0) + goto out; + + /* copy response */ + tinfo = &fc->tcode[xfer->recv.hdr.mode.hdr.tcode]; + if (xfer->recv.hdr.mode.hdr.tcode == FWTCODE_RRESB || + xfer->recv.hdr.mode.hdr.tcode == FWTCODE_LRES) { + pay_len = xfer->recv.pay_len; + if (asyreq->req.len >= xfer->recv.pay_len + tinfo->hdr_len) { + asyreq->req.len = xfer->recv.pay_len + + tinfo->hdr_len; + } else { + err = EINVAL; + pay_len = 0; + } + } else { + pay_len = 0; + } + bcopy(&xfer->recv.hdr, fp, tinfo->hdr_len); + bcopy(xfer->recv.payload, (char *)fp + tinfo->hdr_len, pay_len); +out: + gFirewire->fw_xfer_free_buf(xfer); + break; + } + case FW_IBUSRST: + fc->ibr(fc); + break; + case FW_CBINDADDR: + fwb = gFirewire->fw_bindlookup(fc, + bindreq->start.hi, bindreq->start.lo); + if(fwb == NULL){ + err = EINVAL; + break; + } + gFirewire->fw_bindremove(fc, fwb); + STAILQ_REMOVE(&d->binds, fwb, fw_bind, chlist); + gFirewire->fw_xferlist_remove(&fwb->xferlist); + free(fwb); + break; + case FW_SBINDADDR: + if(bindreq->len <= 0 ){ + err = EINVAL; + break; + } + if(bindreq->start.hi > 0xffff ){ + err = EINVAL; + break; + } + fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind)); + if(fwb == NULL){ + err = ENOMEM; + break; + } + fwb->start = ((u_int64_t)bindreq->start.hi << 32) | + bindreq->start.lo; + fwb->end = fwb->start + bindreq->len; + fwb->sc = (void *)d; + STAILQ_INIT(&fwb->xferlist); + err = gFirewire->fw_bindadd(fc, fwb); + if (err == 0) { + gFirewire->fw_xferlist_add(&fwb->xferlist, + /* XXX */ + B_PAGE_SIZE, B_PAGE_SIZE, 5, + fc, (void *)fwb, fw_hand); + STAILQ_INSERT_TAIL(&d->binds, fwb, chlist); + } + break; + case FW_GDEVLST: + i = len = 1; + /* myself */ + devinfo = &fwdevlst->dev[0]; + devinfo->dst = fc->nodeid; + devinfo->status = 0; /* XXX */ + devinfo->eui.hi = fc->eui.hi; + devinfo->eui.lo = fc->eui.lo; + STAILQ_FOREACH(fwdev, &fc->devices, link) { + if(len < FW_MAX_DEVLST){ + devinfo = &fwdevlst->dev[len++]; + devinfo->dst = fwdev->dst; + devinfo->status = + (fwdev->status == FWDEVINVAL)?0:1; + devinfo->eui.hi = fwdev->eui.hi; + devinfo->eui.lo = fwdev->eui.lo; + } + i++; + } + fwdevlst->n = i; + fwdevlst->info_len = len; + break; + case FW_GTPMAP: + bcopy(fc->topology_map, data, + (fc->topology_map->crc_len + 1) * 4); + break; + case FW_GCROM: + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, crom_buf->eui)) + break; + if (fwdev == NULL) { + if (!FW_EUI64_EQUAL(fc->eui, crom_buf->eui)) { + err = FWNODE_INVAL; + break; + } + /* myself */ + ptr = malloc(CROMSIZE); + len = CROMSIZE; + for (i = 0; i < CROMSIZE/4; i++) + ((uint32_t *)ptr)[i] + = B_BENDIAN_TO_HOST_INT32(fc->config_rom[i]); + } else { + /* found */ + ptr = (void *)&fwdev->csrrom[0]; + if (fwdev->rommax < CSRROMOFF) + len = 0; + else + len = fwdev->rommax - CSRROMOFF + 4; + } + if (crom_buf->len < len) + len = crom_buf->len; + else + crom_buf->len = len; +// err = copyout(ptr, crom_buf->ptr, len);//to be completed + err = user_memcpy(crom_buf->ptr, ptr, len); + if (fwdev == NULL) + /* myself */ + free(ptr); + break; + default: + fc->ioctl (cookie, cmd, data, length); + break; + } + return err; +} + + +status_t +init_hardware() +{ + return B_OK; +} + + +const char ** +publish_devices(void) +{ + return (const char **)devices; +} + + +static device_hooks hooks = { + &fw_open, + &fw_close, + &fw_free, + &fw_ioctl, + &fw_read, + &fw_write, + NULL, + NULL, + NULL, + NULL +}; + +device_hooks * +find_device(const char *name) +{ + return &hooks; +} + + +status_t +init_driver() +{ + status_t err; + struct firewire_softc *sc = NULL; + uint32 i; + +#if DEBUG + load_driver_symbols("fw_raw"); +#endif + + if ((err = get_module(FIREWIRE_MODULE_NAME, (module_info **)&gFirewire)) != B_OK) { + return err; + } + + devices_count = 0; + while (gFirewire->get_handle(devices_count, sc)==B_OK) { + devices_count++; + } + + if (devices_count <= 0) + return ENODEV; + + devices = malloc(sizeof(char *) * (devices_count+1)); + for (i=0; i