diff --git a/headers/private/firewire/firewire_module.h b/headers/private/firewire/firewire_module.h index ccc813e86f..9f9daa2178 100644 --- a/headers/private/firewire/firewire_module.h +++ b/headers/private/firewire/firewire_module.h @@ -5,63 +5,61 @@ */ #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_nodeid)(struct firewire_comm *fc, int dst); - struct fw_device * (*fw_noderesolve_eui64)(struct firewire_comm *fc, struct fw_eui64 *eui); +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); +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); +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); +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 *); +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 index 5120f334b7..49162325b7 100644 --- a/headers/private/firewire/firewirereg.h +++ b/headers/private/firewire/firewirereg.h @@ -36,7 +36,9 @@ */ #ifndef _FIREWIREREG_H #define _FIREWIREREG_H -#ifndef __HAIKU__ +#ifdef __HAIKU__ +#include +#else #ifdef __DragonFly__ typedef d_thread_t fw_proc; #include @@ -55,11 +57,15 @@ typedef struct proc fw_proc; #include #include #define splfw splimp -#else -#define splfw disable_interrupts -#define splx restore_interrupts +#else +#include +#include +#include "fwglue.h" +#include "timer.h" #endif +#define MAX_REQCOUNT 0xffff + STAILQ_HEAD(fw_xferlist, fw_xfer); struct fw_device{ @@ -119,8 +125,20 @@ struct crom_src_buf { struct crom_chunk root; struct crom_chunk vendor; struct crom_chunk hw; +};//move from firewire.c to here + +struct firewire_notify_hooks { + status_t (*device_attach)(struct firewire_softc *sc, void **cookie); + status_t (*device_detach)(struct firewire_softc *sc, void *cookie); }; -#endif //move from firewire.c to firewirereg.h + +struct firewire_child_info { + const char *child_name; + void *cookie; + struct firewire_notify_hooks notify_hooks; + struct firewire_child_info *link; +}; +#endif struct firewire_comm{ #ifndef __HAIKU__ @@ -171,12 +189,12 @@ struct firewire_comm{ struct callout busprobe_callout; struct callout bmr_callout; struct callout timeout_callout; + struct task task_timeout; #else - //timer_id busprobe_callout; - //timer_id bmr_callout; - //timer_id timeout_callout; + 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); @@ -199,29 +217,28 @@ struct firewire_comm{ 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 taskqueue *taskqueue; struct proc *probe_thread; #else + benaphore mtx; + benaphore wait_lock; + void *taskqueue; thread_id probe_thread; + sem_id Sem; + area_id crom_sid_Area; + struct firewire_child_info *childList; #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)) +#ifndef __HAIKU__ #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)) +#else #define FW_GLOCK_ASSERT(fc) +#endif struct fw_xferq { int flag; @@ -252,15 +269,15 @@ struct fw_xferq { 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 *); + spinlock Spinlock; + sem_id Sem; }; struct fw_bulkxfer{ int poffset; -// struct mbuf *mbuf;//to be completed +// struct mbuf *mbuf; STAILQ_ENTRY(fw_bulkxfer) link; caddr_t start; caddr_t end; @@ -280,8 +297,10 @@ struct fw_xfer{ caddr_t sc; struct firewire_comm *fc; struct fw_xferq *q; -// struct timeval tv;//to be completed - int8_t resp; +// struct timeval tv; + bigtime_t tv; + + int32_t resp; #define FWXF_INIT 0x00 #define FWXF_INQ 0x01 #define FWXF_START 0x02 @@ -294,16 +313,19 @@ struct fw_xfer{ uint8_t flag; int8_t tl; void (*hand) (struct fw_xfer *); - struct { + struct send_recv{ struct fw_pkt hdr; uint32_t *payload; + area_id payArea; + bus_addr_t bus_addr; uint16_t pay_len; uint8_t spd; } send, recv; -// struct mbuf *mbuf;//to be completed +// struct mbuf *mbuf; STAILQ_ENTRY(fw_xfer) link; STAILQ_ENTRY(fw_xfer) tlabel; - struct malloc_type *malloc; +// struct malloc_type *malloc; + sem_id Sem; }; struct fw_rcv_buf { @@ -313,20 +335,20 @@ struct fw_rcv_buf { u_int nvec; uint8_t spd; }; -#ifndef __HAIKU__ +//#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); +struct fw_xfer *fw_xfer_alloc (); +struct fw_xfer *fw_xfer_alloc_buf (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, +int fw_xferlist_add (struct fw_xferlist *, 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*); @@ -341,11 +363,11 @@ 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 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 +//#endif extern int firewire_debug; #ifndef __HAIKU__ extern devclass_t firewire_devclass; diff --git a/headers/private/firewire/fwdma.h b/headers/private/firewire/fwdma.h index 5edf7b3c87..e8187d86bc 100644 --- a/headers/private/firewire/fwdma.h +++ b/headers/private/firewire/fwdma.h @@ -36,21 +36,13 @@ #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的次方 +#include "fwglue.h" -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; + area_id Area; void * v_addr; bus_addr_t bus_addr; }; @@ -67,7 +59,7 @@ struct fwdma_alloc_multi { bus_size_t esize; int nseg; // bus_dma_tag_t dma_tag; - area_id BufArea; + area_id Area; struct fwdma_seg seg[0]; }; @@ -119,12 +111,16 @@ fwdma_sync_multiseg_all(struct fwdma_alloc_multi *am, bus_dmasync_op_t op) 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(struct firewire_comm *, int, bus_size_t, struct fwdma_alloc *, int); +void fwdma_free(struct firewire_comm *, 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); +#else +struct fwdma_alloc_multi *fwdma_malloc_multiseg(int, int, int); +void fwdma_free_multiseg(struct fwdma_alloc_multi *); #endif /*__HAIKU__*/ #endif /* _FWDMA_H*/ diff --git a/headers/private/firewire/fwglue.h b/headers/private/firewire/fwglue.h new file mode 100644 index 0000000000..266aa7bfcd --- /dev/null +++ b/headers/private/firewire/fwglue.h @@ -0,0 +1,56 @@ +/* + * Copyright 2007, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * JiSheng Zhang + */ + +#ifndef _FW_GLUE_H +#define _FW_GLUE_H + +#include +#include + +#define device_printf(dev, a...) dprintf("firewire:" a) +#define printf(a...) dprintf(a) +#define KASSERT(cond,msg) do { \ + if (!cond) \ + panic msg; \ +} while(0) + +#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; + +#define atomic_readandclear_int(a) atomic_set(a, 0) +#define atomic_set_int(addr, newvalue) atomic_set(addr, newvalue) + +#define mtx_lock benaphore_lock +#define mtx_unlock benaphore_unlock +#define mtx_destroy benaphore_destroy +#define mtx_init(lockaddr, name, type, opts) benaphore_init(lockaddr, name) + +#define splfw() 0 +#define splx(s) + +#define hz 1000000LL + +#define DELAY(n) snooze(n) + +#define OWRITE(sc, offset, value) (*(volatile uint32 *)((char *)(sc->regAddr) + (offset)) = value) +#define OREAD(sc, offset) (*(volatile uint32 *)((char *)(sc->regAddr) + (offset))) + +#define MAX_CARDS 4 +extern dpc_module_info *gDpc; + +#define __offsetof(type, field) ((size_t)(&((type *)0)->field)) + +#endif /*_FW_GLUE_H*/ diff --git a/headers/private/firewire/timer.h b/headers/private/firewire/timer.h new file mode 100644 index 0000000000..0c3cc97177 --- /dev/null +++ b/headers/private/firewire/timer.h @@ -0,0 +1,44 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __TIMER_H +#define __TIMER_H + +#include + +// Since the BeOS kernel timers are executed in interrupt context, +// a new timer has been created. The timers are executed in thread +// context, and are passed a cookie. + +typedef int32 timer_id; + +typedef void (*timer_function)(void *cookie); + +// create_timer() can be called from interrupt context. +// Only up to 8 concurrent timers are supported. +// Flags can be one of B_ONE_SHOT_ABSOLUTE_TIMER, +// B_ONE_SHOT_RELATIVE_TIMER or B_PERIODIC_TIMER. + +timer_id create_timer(timer_function func, void *cookie, bigtime_t interval, uint32 flags); +status_t delete_timer(timer_id id); + + +status_t initialize_timer(void); +status_t terminate_timer(void); + +#endif diff --git a/src/add-ons/kernel/bus_managers/firewire/00README b/src/add-ons/kernel/bus_managers/firewire/00README new file mode 100644 index 0000000000..e18fc9db62 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/00README @@ -0,0 +1,152 @@ +$FreeBSD: src/sys/dev/firewire/00README,v 1.2 2005/10/01 18:56:16 glebius Exp $ + +IEEE 1394 support for FreeBSD-5.X and 4.X. + +1. Introduction + + This tarball contains IEEE1394(FireWire) driver which is first + written by Katsushi Kobayashi[1] and + modified by Hidetoshi Shimokawa . + Please note this driver is still under development. + You can find latest snapshots under: + http://people.freebsd.org/~simokawa/ + named firewire-2002XXXX.tar.gz + + The driver consists of 6 parts: + + - fwohci.c/fwohci_pci.c + OHCI[2] driver + - IEEE1394 link/phy chip control + - firewire.c + Chip independent driver + - CSR + - Transaction + - Character devices for userland + - fwmem.c + /dev/fwmem0: physical memory of a remote node. + - sbp.c + SBP-II[3] (a.k.a. SCSI over FireWire) driver + + - if_fwe.c + NON-Standard implementation of Ethernet over FireWire. + + - bus_mgm.c (userland) + Bus management function for user. + show topology map, change gap count, bus reset, etc. + +2. Installation + + Suppose you have kernel source at /sys. + + - Extract tarball at root directory. + - cd /sys/dev/firewire + - make + - make install + - make load + + For FreeBSD-4 user: + + - ./MAKEDEV + +3. SBP-II support (sbp) + + - You need CAM(SCSI) support in your kernel. + If you are using FreeBSD-5 before 2002/03/23 or FreeBSD-4 before + 2002/4/8, you need to apply CAM-patch in this archive + to handle HDD's(T_RBC or T_DIRECT which doesn't support READ_6). + + - If you connect a few firewire devices only, try the following to + reduce gap overhead. + + - ./bus_mgm -g 8 + +4. Ethernet over FireWire (if_fwe) + + This is a sample driver for ethernet emulation. Please note this + does NOT conform to any standards like IP over FireWire(RFC2734[4]). + It just sends ethernet frames encapsulated in asynchronous stream + packets. It doesn't scale because it does something like unicast over multicast, but it's easy to be implemented and you can use any + facilities what ethernet can do. (ipv6, bridging, vlan etc.) + + It also has DEVICE_POLLING[5] support. To enable it, edit your + kernel config file and Makefile.fwe then rebuild kernel and if_fwe.ko. + +5. FireWire for Kernel Hackers + + As you know, IEEE1394 is a bus and OHCI supports physical access + to the host memory. This means that you can access the remote + host over firewire without software support at the remote host. + In other words, you can investigate remote host's physical memory + whether its OS is alive or crashed or hangs up. + + You need to apply KVMLIB-patch and rebuild libkvm then rebuild ps, + dmesg and gdb those are statically linked. + You may want to apply GDB-patch in this archive to get same behavior + as gdb with /dev/mem or want to insert savectx(&dumppcb) into panic(), + breakpoint() and so on to emulation crash dump. + + You have to determine target node_id manually at this point. + (guess using bus_mgm -t or dmesg) + (Targets should be specified by EUI64 in the future) + + # sysctl kern.firewire.fwmem_node=[node_id] + + # ps -agx -M /dev/fwmem0 -N /sys/i386/compile/GENERIC/kernel + # dmesg -M /dev/fwmem0 -N /sys/i386/compile/GENERIC/kernel + # gdb -k -c /dev/fwmem0 /sys/i386/compile/GENERIC/kernel.debug + # dd if=/dev/fwmem0 of=vmcore bs=1m count=[phys. memory in MB] + + remote gdb at 400,000,000 bps :-) + + +6. DV + I have not tested yet. + +7. Tested HW + + OS + - FreeBSD-4/i386 + - FreeBSD-4/alpha + - FreeBSD-5/i386 + + * Not tested on SMP. + * Not tested on big-endian machine... + + OHCI + - Texas Instruments TSB12LV26 (PCI) + - Texas Instruments TSB43AA22 (PCI/Cardbus) + + * There might be phy probing problem but most of the OHCI + chips should work. + * Tested with multiple firewire buses. + + SBP-II + - HDD: Logitec USB/FireWire LHD-P30FU + - HDD: Yano A-dish 120GB + - HDD: Yano B-Max 320GB + The repository of cvsup2.jp.freebsd.org is on this device. + - HDD: Personal Storage 3000XT 160GB + The last sector of this drive cannot be accessed.. + - DVD-RAM: Panasonic LF-D340JD + - SCSI-FireWire converter: Yano FWSCSI-01 + We can recognize only 1 device/lun at this point + - HDD: iPod, PowerBook G4 (target mode) + Reported by ikob + - Scanner: Epson GT-9700F + Now works!! + Sane-backend needs a patch(SANE-patch in this archive). + + if_fwe + - IPv4, IPv6, bridging, vlan. + - You need at least two FreeBSD machines with this driver to use. + +References: +[1] ftp://ftp.uec.ac.jp/pub/firewire/beta/ +[2] http://developer.intel.com/technology/1394/download/ohci_11.htm +[3] http://www.t10.org/scsi-3.htm +[4] http://www.faqs.org/rfcs/rfc2734.html +[5] http://info.iet.unipi.it/~luigi/polling/ + + +Hidetoshi Shimokawa +simokawa@freebsd.org diff --git a/src/add-ons/kernel/bus_managers/firewire/Jamfile b/src/add-ons/kernel/bus_managers/firewire/Jamfile new file mode 100644 index 0000000000..d81261f489 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/Jamfile @@ -0,0 +1,22 @@ +SubDir HAIKU_TOP src add-ons kernel bus_managers firewire ; + +UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ; +UsePrivateHeaders firewire kernel ; + +SubDirCcFlags [ FDefines _KERNEL=1 ] ; + +KernelAddon firewire : + util.c + timer.c + firewire_module.c + firewire.c + fwdma.c + fwmem.c + fwohci.c + fwohci_pci.c + fwcrom.c + ; + +SEARCH on [ FGristFiles + fwcrom.c + ] = [ FDirName $(HAIKU_TOP) src bin fwcontrol ] ; diff --git a/src/add-ons/kernel/bus_managers/firewire/firewire.c b/src/add-ons/kernel/bus_managers/firewire/firewire.c new file mode 100644 index 0000000000..fbef485f22 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/firewire.c @@ -0,0 +1,2317 @@ +/*- + * 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/firewire.c,v 1.99 2007/07/15 13:00:29 simokawa Exp $ + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fwdebug.h" +#include "fwglue.h" +#include "queue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwmem.h" +#include "iec68113.h" +#include "timer.h" +#include "util.h" + +#define PROBE_THREAD_PRIORITY 80 + +int firewire_debug=1, try_bmr=1, hold_count=3; + +#define FW_MAXASYRTY 4 + +static void firewire_xfer_timeout(void *); +static void fw_try_bmr (void *); +static void fw_try_bmr_callback (struct fw_xfer *); +static void fw_asystart (struct fw_xfer *); +static int fw_get_tlabel (struct firewire_comm *, struct fw_xfer *); +static void fw_bus_probe (void *); +static void fw_attach_dev (struct firewire_comm *); +static int32 fw_bus_probe_thread(void *); +#ifdef FW_VMACCESS +static void fw_vmaccess (struct fw_xfer *); +#endif +static int fw_bmr (struct firewire_comm *); +static void fw_dump_hdr(struct fw_pkt *, char *); + + +char *linkspeed[] = { + "S100", "S200", "S400", "S800", + "S1600", "S3200", "undef", "undef" +}; + +static char *tcode_str[] = { + "WREQQ", "WREQB", "WRES", "undef", + "RREQQ", "RREQB", "RRESQ", "RRESB", + "CYCS", "LREQ", "STREAM", "LRES", + "undef", "undef", "PHY", "undef" +}; + +/* IEEE-1394a Table C-2 Gap count as a function of hops*/ +#define MAX_GAPHOP 15 +u_int gap_cnt[] = { 5, 5, 7, 8, 10, 13, 16, 18, + 21, 24, 26, 29, 32, 35, 37, 40}; + + +/* + * Lookup fwdev by node id. + */ +struct fw_device * +fw_noderesolve_nodeid(struct firewire_comm *fc, int dst) +{ + struct fw_device *fwdev; + int s; + + s = splfw(); + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (fwdev->dst == dst && fwdev->status != FWDEVINVAL) + break; + splx(s); + + return fwdev; +} + +/* + * Lookup fwdev by EUI64. + */ +struct fw_device * +fw_noderesolve_eui64(struct firewire_comm *fc, struct fw_eui64 *eui) +{ + struct fw_device *fwdev; + int s; + + s = splfw(); + FW_GLOCK(fc); + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, *eui)) + break; + FW_GUNLOCK(fc); + splx(s); + + if(fwdev == NULL) return NULL; + if(fwdev->status == FWDEVINVAL) return NULL; + return fwdev; +} + +/* + * Async. request procedure for userland application. + */ +int +fw_asyreq(struct firewire_comm *fc, int sub, struct fw_xfer *xfer) +{ + int err = 0; + struct fw_xferq *xferq; + int len; + struct fw_pkt *fp; + int tcode; + struct tcode_info *info; + + if(xfer == NULL) return EINVAL; + if(xfer->hand == NULL){ + printf("hand == NULL\n"); + return EINVAL; + } + fp = &xfer->send.hdr; + + tcode = fp->mode.common.tcode & 0xf; + info = &fc->tcode[tcode]; + if (info->flag == 0) { + printf("invalid tcode=%x\n", tcode); + return EINVAL; + } + + /* XXX allow bus explore packets only after bus rest */ + if ((fc->status < FWBUSEXPLORE) && + ((tcode != FWTCODE_RREQQ) || (fp->mode.rreqq.dest_hi != 0xffff) || + (fp->mode.rreqq.dest_lo < 0xf0000000) || + (fp->mode.rreqq.dest_lo >= 0xf0001000))) { + xfer->resp = EAGAIN; + xfer->flag = FWXF_BUSY; + return (EAGAIN); + } + + if (info->flag & FWTI_REQ) + xferq = fc->atq; + else + xferq = fc->ats; + len = info->hdr_len; + if (xfer->send.pay_len > MAXREC(fc->maxrec)) { + printf("send.pay_len > maxrec\n"); + return EINVAL; + } + if (info->flag & FWTI_BLOCK_STR) + len = fp->mode.stream.len; + else if (info->flag & FWTI_BLOCK_ASY) + len = fp->mode.rresb.len; + else + len = 0; + if (len != xfer->send.pay_len){ + printf("len(%d) != send.pay_len(%d) %s(%x)\n", + len, xfer->send.pay_len, tcode_str[tcode], tcode); + return EINVAL; + } + + if(xferq->start == NULL){ + printf("xferq->start == NULL\n"); + return EINVAL; + } + if(!(xferq->queued < xferq->maxq)){ + device_printf(fc->bdev, "Discard a packet (queued=%d)\n", + xferq->queued); + return EAGAIN; + } + + xfer->tl = -1; + if (info->flag & FWTI_TLABEL) { + if (fw_get_tlabel(fc, xfer) < 0) + return EAGAIN; + } + + xfer->resp = 0; + xfer->fc = fc; + xfer->q = xferq; + + fw_asystart(xfer); + return err; +} +/* + * Wakeup blocked process. + */ +void +fw_xferwake(struct fw_xfer *xfer) +{ +// struct mtx *lock = &xfer->fc->wait_lock; + benaphore *lock = &xfer->fc->wait_lock; + + mtx_lock(lock); + xfer->flag |= FWXF_WAKE; + mtx_unlock(lock); + +// wakeup(xfer); + release_sem(xfer->Sem); + return; +} + +int +fw_xferwait(struct fw_xfer *xfer) +{ +// struct mtx *lock = &xfer->fc->wait_lock; + benaphore *lock = &xfer->fc->wait_lock; + int err = 0; + + mtx_lock(lock); + if ((xfer->flag & FWXF_WAKE) == 0){ +// err = msleep((void *)xfer, lock, PWAIT|PCATCH, "fw_xferwait", 0); + mtx_unlock(lock); + err = acquire_sem(xfer->Sem); + return err; + } + mtx_unlock(lock); + + return (err); +} + +/* + * Async. request with given xfer structure. + */ +static void +fw_asystart(struct fw_xfer *xfer) +{ + struct firewire_comm *fc = xfer->fc; + int s; + s = splfw(); + /* Protect from interrupt/timeout */ + FW_GLOCK(fc); + xfer->flag = FWXF_INQ; + STAILQ_INSERT_TAIL(&xfer->q->q, xfer, link); +#if 0 + xfer->q->queued ++; +#endif + FW_GUNLOCK(fc); + splx(s); + /* XXX just queue for mbuf */ +// if (xfer->mbuf == NULL) + xfer->q->start(fc); + return; +} + + +static void +firewire_xfer_timeout(void *arg) +{ + struct firewire_comm *fc = (struct firewire_comm *)arg; + struct fw_xfer *xfer, *txfer; +// struct timeval tv; +// struct timeval split_timeout; + bigtime_t tv; + STAILQ_HEAD(, fw_xfer) xfer_timeout; + int i, s; + +// split_timeout.tv_sec = 0; +// split_timeout.tv_usec = 200 * 1000; /* 200 msec */ + +// microtime(&tv); +// timevalsub(&tv, &split_timeout); + tv = system_time(); + tv -= 200*1000; + STAILQ_INIT(&xfer_timeout); + + s = splfw(); + FW_GLOCK(fc); + for (i = 0; i < 0x40; i ++) { + while ((xfer = STAILQ_FIRST(&fc->tlabels[i])) != NULL) { + if ((xfer->flag & FWXF_SENT) == 0) + /* not sent yet */ + break; +// if (timevalcmp(&xfer->tv, &tv, >)) + if (xfer->tv > tv) + /* the rests are newer than this */ + break; + device_printf(fc->bdev, + "split transaction timeout: " + "tl=0x%x flag=0x%02x\n", i, xfer->flag); + fw_dump_hdr(&xfer->send.hdr, "send"); + xfer->resp = ETIMEDOUT; + STAILQ_REMOVE_HEAD(&fc->tlabels[i], tlabel); + STAILQ_INSERT_TAIL(&xfer_timeout, xfer, tlabel); + } + } + FW_GUNLOCK(fc); + splx(s); + fc->timeout(fc); + + STAILQ_FOREACH_SAFE(xfer, &xfer_timeout, tlabel, txfer) + xfer->hand(xfer); +} + +#define WATCHDOG_HZ 10 +static void +firewire_watchdog(void *arg) +{ + struct firewire_comm *fc; + static int watchdog_clock = 0; + + fc = (struct firewire_comm *)arg; + + /* + * At boot stage, the device interrupt is disabled and + * We encounter a timeout easily. To avoid this, + * ignore clock interrupt for a while. + */ + if (watchdog_clock > WATCHDOG_HZ * 15) +// taskqueue_enqueue(fc->taskqueue, &fc->task_timeout); + gDpc->queue_dpc(fc->taskqueue, firewire_xfer_timeout, fc); + else + watchdog_clock ++; + +// callout_reset(&fc->timeout_callout, hz / WATCHDOG_HZ, +// (void *)firewire_watchdog, (void *)fc); + fc->timeout_callout = create_timer(firewire_watchdog, fc, + hz/WATCHDOG_HZ, B_ONE_SHOT_RELATIVE_TIMER); +} + +status_t +firewire_add_child(struct firewire_softc *sc, const char *childname, + struct firewire_notify_hooks *hooks) +{ + struct firewire_child_info *element, *info; + if (!childname) + return B_BAD_VALUE; + + element = sc->fc->childList; + while (element) { + if (strcmp(element->child_name, childname) == 0) { + // we already have an entry for this child + return B_OK; + } + + element = element->link; + } + + info = malloc(sizeof(struct firewire_child_info)); + if (!info) { + return B_NO_MEMORY; + } + + info->child_name = strdup(childname); + info->notify_hooks.device_attach = hooks->device_attach; + info->notify_hooks.device_detach = hooks->device_detach; + info->cookie = NULL; + info->link = NULL; + + element = sc->fc->childList; + if (element) { + while (element->link) + element = element->link; + element->link = info; + } else + sc->fc->childList = info; + + hooks->device_attach(sc, &info->cookie); + return B_OK; +} + +status_t +firewire_remove_child(struct firewire_softc *sc, const char *childname) +{ + + struct firewire_child_info *element = sc->fc->childList; + struct firewire_child_info *temp = element; + while (element) { + if (strcmp(element->child_name, childname) == 0) { + // trigger the device removed hook + element->notify_hooks.device_detach(sc, element->cookie); + if(temp->link == NULL) + sc->fc->childList = NULL;//childList has only a node + else + temp->link = element->link; + + free(element); + return B_OK; + } + + temp = element; + element = element->link; + } + + return B_NAME_NOT_FOUND; +} + +/* + * The attach routine. + */ +int +firewire_attach(struct firewire_comm *fc, struct firewire_softc *sc) +{ + sc->fc = fc; + fc->status = FWBUSNOTREADY; + +// unit = device_get_unit(dev); + if( fc->nisodma > FWMAXNDMA) fc->nisodma = FWMAXNDMA; + +// fwdev_makedev(sc); + + mtx_init(&fc->wait_lock, "fwwait", NULL, MTX_DEF); +// CALLOUT_INIT(&fc->timeout_callout); +// CALLOUT_INIT(&fc->bmr_callout); +// CALLOUT_INIT(&fc->busprobe_callout); +// TASK_INIT(&fc->task_timeout, 0, firewire_xfer_timeout, (void *)fc); + +// callout_reset(&sc->fc->timeout_callout, hz, +// (void *)firewire_watchdog, (void *)sc->fc); + fc->timeout_callout = create_timer(firewire_watchdog, fc, + hz/WATCHDOG_HZ, B_ONE_SHOT_RELATIVE_TIMER); + + /* create thread */ +// kthread_create(fw_bus_probe_thread, (void *)fc, &fc->probe_thread, +// 0, 0, "fw%d_probe", unit); + fc->probe_thread = spawn_kernel_thread(fw_bus_probe_thread, + "fw_probe", PROBE_THREAD_PRIORITY, fc); + if(fc->probe_thread < B_OK) + dprintf("can not Create bus probe thread\n"); + resume_thread(fc->probe_thread); + + /* Locate our children */ +// bus_generic_probe(dev); + + /* launch attachement of the added children */ +// bus_generic_attach(dev); +/* firewire_driver_info *element = sc->childList; + while(element){ + element->notify_hooks.device_attach(sc, element->cookie); + element = element->link; + }*/ + + /* bus_reset */ + fw_busreset(fc, FWBUSNOTREADY); + fc->ibr(fc); + + return 0; +} + +/* + * Dettach it. + */ +void +firewire_detach(struct firewire_softc *sc) +{ + struct firewire_comm *fc; + struct fw_device *fwdev, *fwdev_next; + struct firewire_child_info *element, *temp; +// int err; + + fc = sc->fc; + mtx_lock(&fc->wait_lock); + fc->status = FWBUSDETACH; +// wakeup(fc); + release_sem(fc->Sem); +/* if (msleep(fc->probe_thread, &fc->wait_lock, PWAIT, "fwthr", hz * 60)) + printf("firewire probe thread didn't die\n");*/ + mtx_unlock(&fc->wait_lock); + DELAY(hz*60); + +/* if ((err = fwdev_destroydev(sc)) != 0) + return err; + + if ((err = bus_generic_detach(dev)) != 0) + return err;*/ + + element = sc->fc->childList; + while(element){ + element->notify_hooks.device_detach(sc, element->cookie); + temp = element; + element = element->link; + free(temp); + } + sc->fc->childList = NULL; + +/* callout_stop(&fc->timeout_callout); + callout_stop(&fc->bmr_callout); + callout_stop(&fc->busprobe_callout);*/ + delete_timer(fc->timeout_callout); + delete_timer(fc->bmr_callout); + delete_timer(fc->busprobe_callout); + + + /* XXX xfree_free and untimeout on all xfers */ + for (fwdev = STAILQ_FIRST(&fc->devices); fwdev != NULL; + fwdev = fwdev_next) { + fwdev_next = STAILQ_NEXT(fwdev, link); + free(fwdev); + } + free(fc->topology_map); + free(fc->speed_map); + free(fc->crom_src_buf); + + mtx_destroy(&fc->wait_lock); +// return(0); +} +#if 0 +static int +firewire_shutdown( device_t dev ) +{ + return 0; +} +#endif + + +static void +fw_xferq_drain(struct fw_xferq *xferq) +{ + struct fw_xfer *xfer; + + while ((xfer = STAILQ_FIRST(&xferq->q)) != NULL) { + STAILQ_REMOVE_HEAD(&xferq->q, link); +#if 0 + xferq->queued --; +#endif + xfer->resp = EAGAIN; + xfer->flag = FWXF_SENTERR; + fw_xfer_done(xfer); + } +} + +void +fw_drain_txq(struct firewire_comm *fc) +{ + struct fw_xfer *xfer, *txfer; + STAILQ_HEAD(, fw_xfer) xfer_drain; + int i; + + STAILQ_INIT(&xfer_drain); + + FW_GLOCK(fc); + fw_xferq_drain(fc->atq); + fw_xferq_drain(fc->ats); + for(i = 0; i < fc->nisodma; i++) + fw_xferq_drain(fc->it[i]); + + for (i = 0; i < 0x40; i ++) + while ((xfer = STAILQ_FIRST(&fc->tlabels[i])) != NULL) { + if (firewire_debug) + printf("tl=%d flag=%d\n", i, xfer->flag); + xfer->resp = EAGAIN; + STAILQ_REMOVE_HEAD(&fc->tlabels[i], tlabel); + STAILQ_INSERT_TAIL(&xfer_drain, xfer, tlabel); + } + FW_GUNLOCK(fc); + + STAILQ_FOREACH_SAFE(xfer, &xfer_drain, tlabel, txfer) + xfer->hand(xfer); +} + +static void +fw_reset_csr(struct firewire_comm *fc) +{ + int i; + + CSRARC(fc, STATE_CLEAR) + = 1 << 23 | 0 << 17 | 1 << 16 | 1 << 15 | 1 << 14 ; + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); + CSRARC(fc, NODE_IDS) = 0x3f; + + CSRARC(fc, TOPO_MAP + 8) = 0; + fc->irm = -1; + + fc->max_node = -1; + + for(i = 2; i < 0x100/4 - 2 ; i++){ + CSRARC(fc, SPED_MAP + i * 4) = 0; + } + CSRARC(fc, STATE_CLEAR) = 1 << 23 | 0 << 17 | 1 << 16 | 1 << 15 | 1 << 14 ; + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); + CSRARC(fc, RESET_START) = 0; + CSRARC(fc, SPLIT_TIMEOUT_HI) = 0; + CSRARC(fc, SPLIT_TIMEOUT_LO) = 800 << 19; + CSRARC(fc, CYCLE_TIME) = 0x0; + CSRARC(fc, BUS_TIME) = 0x0; + CSRARC(fc, BUS_MGR_ID) = 0x3f; + CSRARC(fc, BANDWIDTH_AV) = 4915; + CSRARC(fc, CHANNELS_AV_HI) = 0xffffffff; + CSRARC(fc, CHANNELS_AV_LO) = 0xffffffff; + CSRARC(fc, IP_CHANNELS) = (1 << 31); + + CSRARC(fc, CONF_ROM) = 0x04 << 24; + CSRARC(fc, CONF_ROM + 4) = 0x31333934; /* means strings 1394 */ + CSRARC(fc, CONF_ROM + 8) = 1 << 31 | 1 << 30 | 1 << 29 | + 1 << 28 | 0xff << 16 | 0x09 << 8; + CSRARC(fc, CONF_ROM + 0xc) = 0; + +/* DV depend CSRs see blue book */ + CSRARC(fc, oPCR) &= ~DV_BROADCAST_ON; + CSRARC(fc, iPCR) &= ~DV_BROADCAST_ON; + + CSRARC(fc, STATE_CLEAR) &= ~(1 << 23 | 1 << 15 | 1 << 14 ); + CSRARC(fc, STATE_SET) = CSRARC(fc, STATE_CLEAR); +} + +static void +fw_init_crom(struct firewire_comm *fc) +{ + struct crom_src *src; + + fc->crom_src_buf = (struct crom_src_buf *) + malloc(sizeof(struct crom_src_buf)); + if (fc->crom_src_buf == NULL) + return; + memset(fc->crom_src_buf, 0, sizeof(struct crom_src_buf));; + + src = &fc->crom_src_buf->src; + bzero(src, sizeof(struct crom_src)); + + /* BUS info sample */ + src->hdr.info_len = 4; + + src->businfo.bus_name = CSR_BUS_NAME_IEEE1394; + + src->businfo.irmc = 1; + src->businfo.cmc = 1; + src->businfo.isc = 1; + src->businfo.bmc = 1; + src->businfo.pmc = 0; + src->businfo.cyc_clk_acc = 100; + src->businfo.max_rec = fc->maxrec; + src->businfo.max_rom = MAXROM_4; + src->businfo.generation = 1; + src->businfo.link_spd = fc->speed; + + src->businfo.eui64.hi = fc->eui.hi; + src->businfo.eui64.lo = fc->eui.lo; + + STAILQ_INIT(&src->chunk_list); + + fc->crom_src = src; + fc->crom_root = &fc->crom_src_buf->root; +} + +static void +fw_reset_crom(struct firewire_comm *fc) +{ + struct crom_src_buf *buf; + struct crom_src *src; + struct crom_chunk *root; + + if (fc->crom_src_buf == NULL) + fw_init_crom(fc); + + buf = fc->crom_src_buf; + src = fc->crom_src; + root = fc->crom_root; + + STAILQ_INIT(&src->chunk_list); + + bzero(root, sizeof(struct crom_chunk)); + crom_add_chunk(src, NULL, root, 0); + crom_add_entry(root, CSRKEY_NCAP, 0x0083c0); /* XXX */ + /* private company_id */ + crom_add_entry(root, CSRKEY_VENDOR, CSRVAL_VENDOR_PRIVATE); +#if 0 +#ifdef __DragonFly__ + crom_add_simple_text(src, root, &buf->vendor, "DragonFly Project"); + crom_add_entry(root, CSRKEY_HW, __DragonFly_cc_version); +#else + crom_add_simple_text(src, root, &buf->vendor, "FreeBSD Project"); + crom_add_entry(root, CSRKEY_HW, __FreeBSD_version); +#endif +#endif + crom_add_simple_text(src, root, &buf->vendor, "HAIKU Project"); + crom_add_entry(root, CSRKEY_HW, B_CUR_DRIVER_API_VERSION); + crom_add_simple_text(src, root, &buf->hw, ""); +} + +/* + * Called after bus reset. + */ +void +fw_busreset(struct firewire_comm *fc, uint32_t new_status) +{ + struct firewire_dev_comm *fdc; + struct crom_src *src; +// device_t *devlistp; + void *newrom; +// int i, devcnt; + struct firewire_child_info *element = fc->childList; + + switch(fc->status){ + case FWBUSMGRELECT: +// callout_stop(&fc->bmr_callout); + delete_timer(fc->bmr_callout); + break; + default: + break; + } + fc->status = new_status; + fw_reset_csr(fc); + fw_reset_crom(fc); + + +/* if (device_get_children(fc->bdev, &devlistp, &devcnt) == 0) { + for( i = 0 ; i < devcnt ; i++) + if (device_get_state(devlistp[i]) >= DS_ATTACHED) { + fdc = device_get_softc(devlistp[i]); + if (fdc->post_busreset != NULL) + fdc->post_busreset(fdc); + } + free(devlistp); + }*/ + + while(element){ +// fdc = &element->cookie.fd; + fdc = (struct firewire_dev_comm *)element->cookie; + if (fdc->post_busreset != NULL) + fdc->post_busreset(fdc); + element = element->link; + } + + newrom = malloc(CROMSIZE); + memset(newrom, 0, CROMSIZE); + src = &fc->crom_src_buf->src; + crom_load(src, (uint32_t *)newrom, CROMSIZE); + if (bcmp(newrom, fc->config_rom, CROMSIZE) != 0) { + /* bump generation and reload */ + src->businfo.generation ++; + /* generation must be between 0x2 and 0xF */ + if (src->businfo.generation < 2) + src->businfo.generation ++; + crom_load(src, (uint32_t *)newrom, CROMSIZE); + bcopy(newrom, (void *)fc->config_rom, CROMSIZE); + } + free(newrom); +} + +/* Call once after reboot */ +void fw_init(struct firewire_comm *fc) +{ + int i; + char tempname[16]; +#ifdef FW_VMACCESS + struct fw_xfer *xfer; + struct fw_bind *fwb; +#endif + + fc->arq->queued = 0; + fc->ars->queued = 0; + fc->atq->queued = 0; + fc->ats->queued = 0; + + fc->arq->buf = NULL; + fc->ars->buf = NULL; + fc->atq->buf = NULL; + fc->ats->buf = NULL; + + fc->arq->flag = 0; + fc->ars->flag = 0; + fc->atq->flag = 0; + fc->ats->flag = 0; + + STAILQ_INIT(&fc->atq->q); + STAILQ_INIT(&fc->ats->q); + + fc->Sem = create_sem(0, "fc Sem"); + + for( i = 0 ; i < fc->nisodma ; i ++ ){ + fc->it[i]->queued = 0; + fc->ir[i]->queued = 0; + + fc->it[i]->start = NULL; + fc->ir[i]->start = NULL; + + fc->it[i]->buf = NULL; + fc->ir[i]->buf = NULL; + + fc->it[i]->flag = FWXFERQ_STREAM; + fc->ir[i]->flag = FWXFERQ_STREAM; + + STAILQ_INIT(&fc->it[i]->q); + STAILQ_INIT(&fc->ir[i]->q); + + snprintf(tempname, sizeof(tempname), "it%d ready sem", i); + fc->it[i]->Sem = create_sem(0, tempname); + snprintf(tempname, sizeof(tempname), "ir%d ready sem", i); + fc->ir[i]->Sem = create_sem(0, tempname); + } + + fc->arq->maxq = FWMAXQUEUE; + fc->ars->maxq = FWMAXQUEUE; + fc->atq->maxq = FWMAXQUEUE; + fc->ats->maxq = FWMAXQUEUE; + + for( i = 0 ; i < fc->nisodma ; i++){ + fc->ir[i]->maxq = FWMAXQUEUE; + fc->it[i]->maxq = FWMAXQUEUE; + } +/* Initialize csr registers */ + fc->topology_map = (struct fw_topology_map *)malloc( + sizeof(struct fw_topology_map)); + memset(fc->topology_map, 0, sizeof(struct fw_topology_map)); + fc->speed_map = (struct fw_speed_map *)malloc( + sizeof(struct fw_speed_map)); + memset(fc->speed_map, 0, sizeof(struct fw_speed_map)); + CSRARC(fc, TOPO_MAP) = 0x3f1 << 16; + CSRARC(fc, TOPO_MAP + 4) = 1; + CSRARC(fc, SPED_MAP) = 0x3f1 << 16; + CSRARC(fc, SPED_MAP + 4) = 1; + + STAILQ_INIT(&fc->devices); + +/* Initialize Async handlers */ + STAILQ_INIT(&fc->binds); + for( i = 0 ; i < 0x40 ; i++){ + STAILQ_INIT(&fc->tlabels[i]); + } + +/* DV depend CSRs see blue book */ +#if 0 + CSRARC(fc, oMPR) = 0x3fff0001; /* # output channel = 1 */ + CSRARC(fc, oPCR) = 0x8000007a; + for(i = 4 ; i < 0x7c/4 ; i+=4){ + CSRARC(fc, i + oPCR) = 0x8000007a; + } + + CSRARC(fc, iMPR) = 0x00ff0001; /* # input channel = 1 */ + CSRARC(fc, iPCR) = 0x803f0000; + for(i = 4 ; i < 0x7c/4 ; i+=4){ + CSRARC(fc, i + iPCR) = 0x0; + } +#endif + + fc->crom_src_buf = NULL; + +#ifdef FW_VMACCESS + xfer = fw_xfer_alloc(); + if(xfer == NULL) return; + + fwb = (struct fw_bind *)malloc(sizeof (struct fw_bind), M_FW, M_NOWAIT); + if(fwb == NULL){ + fw_xfer_free(xfer); + return; + } + xfer->hand = fw_vmaccess; + xfer->fc = fc; + xfer->sc = NULL; + + fwb->start_hi = 0x2; + fwb->start_lo = 0; + fwb->addrlen = 0xffffffff; + fwb->xfer = xfer; + fw_bindadd(fc, fwb); +#endif +} + +#define BIND_CMP(addr, fwb) (((addr) < (fwb)->start)?-1:\ + ((fwb)->end < (addr))?1:0) + +/* + * To lookup bound process from IEEE1394 address. + */ +struct fw_bind * +fw_bindlookup(struct firewire_comm *fc, uint16_t dest_hi, uint32_t dest_lo) +{ + u_int64_t addr; + struct fw_bind *tfw, *r = NULL; + + addr = ((u_int64_t)dest_hi << 32) | dest_lo; + FW_GLOCK(fc); + STAILQ_FOREACH(tfw, &fc->binds, fclist) + if (BIND_CMP(addr, tfw) == 0) { + r = tfw; + break; + } + FW_GUNLOCK(fc); + return(r); +} + +/* + * To bind IEEE1394 address block to process. + */ +int +fw_bindadd(struct firewire_comm *fc, struct fw_bind *fwb) +{ + struct fw_bind *tfw, *prev = NULL; + int r = 0; + + if (fwb->start > fwb->end) { + printf("%s: invalid range\n", __func__); + return EINVAL; + } + + FW_GLOCK(fc); + STAILQ_FOREACH(tfw, &fc->binds, fclist) { + if (fwb->end < tfw->start) + break; + prev = tfw; + } + if (prev == NULL) + STAILQ_INSERT_HEAD(&fc->binds, fwb, fclist); + else if (prev->end < fwb->start) + STAILQ_INSERT_AFTER(&fc->binds, prev, fwb, fclist); + else { + printf("%s: bind failed\n", __func__); + r = EBUSY; + } + FW_GUNLOCK(fc); + return (r); +} + +/* + * To free IEEE1394 address block. + */ +int +fw_bindremove(struct firewire_comm *fc, struct fw_bind *fwb) +{ +#if 0 + struct fw_xfer *xfer, *next; +#endif + struct fw_bind *tfw; + int s; + + s = splfw(); + FW_GLOCK(fc); + STAILQ_FOREACH(tfw, &fc->binds, fclist) + if (tfw == fwb) { + STAILQ_REMOVE(&fc->binds, fwb, fw_bind, fclist); + goto found; + } + + printf("%s: no such binding\n", __func__); + FW_GUNLOCK(fc); + splx(s); + return (1); +found: +#if 0 + /* shall we do this? */ + for (xfer = STAILQ_FIRST(&fwb->xferlist); xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free(xfer); + } + STAILQ_INIT(&fwb->xferlist); +#endif + FW_GUNLOCK(fc); + + splx(s); + return 0; +} + +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 *)) +{ + int i, s; + struct fw_xfer *xfer; + + for (i = 0; i < n; i++) { + xfer = fw_xfer_alloc_buf(slen, rlen); + if (xfer == NULL) + return (n); + xfer->fc = fc; + xfer->sc = sc; + xfer->hand = hand; + s = splfw(); + STAILQ_INSERT_TAIL(q, xfer, link); + splx(s); + } + return (n); +} + +void +fw_xferlist_remove(struct fw_xferlist *q) +{ + struct fw_xfer *xfer, *next; + + for (xfer = STAILQ_FIRST(q); xfer != NULL; xfer = next) { + next = STAILQ_NEXT(xfer, link); + fw_xfer_free_buf(xfer); + } + STAILQ_INIT(q); +} +/* + * dump packet header + */ +static void +fw_dump_hdr(struct fw_pkt *fp, char *prefix) +{ + printf("%s: dst=0x%02x tl=0x%02x rt=%d tcode=0x%x pri=0x%x " + "src=0x%03x\n", prefix, + fp->mode.hdr.dst & 0x3f, + fp->mode.hdr.tlrt >> 2, fp->mode.hdr.tlrt & 3, + fp->mode.hdr.tcode, fp->mode.hdr.pri, + fp->mode.hdr.src); +} + +/* + * To free transaction label. + */ +static void +fw_tl_free(struct firewire_comm *fc, struct fw_xfer *xfer) +{ + struct fw_xfer *txfer; + int s; + + if (xfer->tl < 0) + return; + + s = splfw(); + FW_GLOCK(fc); +#if 1 /* make sure the label is allocated */ + STAILQ_FOREACH(txfer, &fc->tlabels[xfer->tl], tlabel) + if(txfer == xfer) + break; + if (txfer == NULL) { + printf("%s: the xfer is not in the queue " + "(tlabel=%d, flag=0x%x)\n", + __FUNCTION__, xfer->tl, xfer->flag); + fw_dump_hdr(&xfer->send.hdr, "send"); + fw_dump_hdr(&xfer->recv.hdr, "recv"); +// kdb_backtrace(); + FW_GUNLOCK(fc); + splx(s); + return; + } +#endif + + STAILQ_REMOVE(&fc->tlabels[xfer->tl], xfer, fw_xfer, tlabel); + FW_GUNLOCK(fc); + splx(s); + return; +} + +/* + * To obtain XFER structure by transaction label. + */ +static struct fw_xfer * +fw_tl2xfer(struct firewire_comm *fc, int node, int tlabel, int tcode) +{ + struct fw_xfer *xfer; + int s = splfw(); + int req; + + FW_GLOCK(fc); + STAILQ_FOREACH(xfer, &fc->tlabels[tlabel], tlabel) + if(xfer->send.hdr.mode.hdr.dst == node) { + FW_GUNLOCK(fc); + splx(s); + KASSERT(xfer->tl == tlabel, + ("xfer->tl 0x%x != 0x%x", xfer->tl, tlabel)); + /* extra sanity check */ + req = xfer->send.hdr.mode.hdr.tcode; + if (xfer->fc->tcode[req].valid_res != tcode) { + printf("%s: invalid response tcode " + "(0x%x for 0x%x)\n", __FUNCTION__, + tcode, req); + return(NULL); + } + + if (firewire_debug > 2) + printf("fw_tl2xfer: found tl=%d\n", tlabel); + return(xfer); + } + FW_GUNLOCK(fc); + if (firewire_debug > 1) + printf("fw_tl2xfer: not found tl=%d\n", tlabel); + splx(s); + return(NULL); +} + +/* + * To allocate IEEE1394 XFER structure. + */ +struct fw_xfer * +fw_xfer_alloc() +{ + struct fw_xfer *xfer; + + xfer = malloc(sizeof(struct fw_xfer)); + if (xfer == NULL) + return xfer; + memset(xfer, 0, sizeof(struct fw_xfer)); + + xfer->Sem = create_sem(0, "xfer ready sem"); + +// xfer->malloc = type; + + return xfer; +} + +struct fw_xfer * +fw_xfer_alloc_buf(int send_len, int recv_len) +{ + struct fw_xfer *xfer; + void *send_virt, *send_phy, *recv_virt, *recv_phy; + + xfer = fw_xfer_alloc(); + if (xfer == NULL) + return(NULL); + xfer->send.pay_len = send_len; + xfer->recv.pay_len = recv_len; + if (send_len > 0) { +/* xfer->send.payload = malloc(send_len); + if (xfer->send.payload == NULL) { + fw_xfer_free(xfer); + return(NULL); + } + memset(xfer->send.payload 0, send_len);*/ + xfer->send.payArea = alloc_mem(&send_virt, &send_phy, + send_len, 0, "firewire tx buf"); + if (xfer->send.payArea < B_OK){ + fw_xfer_free(xfer); + return(NULL); + } + xfer->send.payload = (uint32_t *)send_virt; + xfer->send.bus_addr = (bus_addr_t)send_phy; + } + if (recv_len > 0) { +/* xfer->recv.payload = malloc(recv_len); + if (xfer->recv.payload == NULL) { + if (xfer->send.payload != NULL) + free(xfer->send.payload); + fw_xfer_free(xfer); + return(NULL); + }*/ + xfer->recv.payArea = alloc_mem(&recv_virt, &recv_phy, + recv_len, 0, "firewire rx buf"); + if (xfer->recv.payArea < B_OK){ + if (xfer->send.payArea > B_OK){ + delete_area(xfer->send.payArea); + xfer->send.payArea = -1; + } + + fw_xfer_free(xfer); + return(NULL); + } + xfer->recv.payload = (uint32_t *)send_virt; + xfer->recv.bus_addr = (bus_addr_t)send_phy; + } + return(xfer); +} + +/* + * IEEE1394 XFER post process. + */ +void +fw_xfer_done(struct fw_xfer *xfer) +{ + if (xfer->hand == NULL) { + printf("hand == NULL\n"); + return; + } + + if (xfer->fc == NULL) + panic("fw_xfer_done: why xfer->fc is NULL?"); + + fw_tl_free(xfer->fc, xfer); + xfer->hand(xfer); +} + +void +fw_xfer_unload(struct fw_xfer* xfer) +{ + int s; + + if(xfer == NULL ) return; + if(xfer->flag & FWXF_INQ){ + printf("fw_xfer_free FWXF_INQ\n"); + s = splfw(); + FW_GLOCK(xfer->fc); + STAILQ_REMOVE(&xfer->q->q, xfer, fw_xfer, link); +#if 0 + xfer->q->queued --; +#endif + FW_GUNLOCK(xfer->fc); + splx(s); + } + if (xfer->fc != NULL) { +#if 1 + if(xfer->flag & FWXF_START) + /* + * This could happen if: + * 1. We call fwohci_arcv() before fwohci_txd(). + * 2. firewire_watch() is called. + */ + printf("fw_xfer_free FWXF_START\n"); +#endif + } + xfer->flag = FWXF_INIT; + xfer->resp = 0; +} +/* + * To free IEEE1394 XFER structure. + */ +void +fw_xfer_free_buf( struct fw_xfer* xfer) +{ + if (xfer == NULL) { + printf("%s: xfer == NULL\n", __func__); + return; + } + fw_xfer_unload(xfer); +/* if(xfer->send.payload != NULL){ + free(xfer->send.payload); + } + if(xfer->recv.payload != NULL){ + free(xfer->recv.payload); + }*/ + if (xfer->send.payArea > B_OK){ + delete_area(xfer->send.payArea); + xfer->send.payArea = -1; + } + if (xfer->recv.payArea > B_OK){ + delete_area(xfer->recv.payArea); + xfer->recv.payArea = -1; + } + delete_sem(xfer->Sem); + free(xfer); +} + +void +fw_xfer_free( struct fw_xfer* xfer) +{ + if (xfer == NULL) { + printf("%s: xfer == NULL\n", __func__); + return; + } + delete_sem(xfer->Sem); + fw_xfer_unload(xfer); + free(xfer); +} + +void +fw_asy_callback_free(struct fw_xfer *xfer) +{ +#if 0 + printf("asyreq done flag=0x%02x resp=%d\n", + xfer->flag, xfer->resp); +#endif + fw_xfer_free(xfer); +} + +/* + * To configure PHY. + */ +static void +fw_phy_config(struct firewire_comm *fc, int root_node, int gap_count) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + fc->status = FWBUSPHYCONF; + + xfer = fw_xfer_alloc(); + if (xfer == NULL) + return; + xfer->fc = fc; + xfer->hand = fw_asy_callback_free; + + fp = &xfer->send.hdr; + fp->mode.ld[1] = 0; + if (root_node >= 0) + fp->mode.ld[1] |= (root_node & 0x3f) << 24 | 1 << 23; + if (gap_count >= 0) + fp->mode.ld[1] |= 1 << 22 | (gap_count & 0x3f) << 16; + fp->mode.ld[2] = ~fp->mode.ld[1]; +/* XXX Dangerous, how to pass PHY packet to device driver */ + fp->mode.common.tcode |= FWTCODE_PHY; + + if (firewire_debug) + printf("send phy_config root_node=%d gap_count=%d\n", + root_node, gap_count); + fw_asyreq(fc, -1, xfer); +} + +#if 0 +/* + * Dump self ID. + */ +static void +fw_print_sid(uint32_t sid) +{ + union fw_self_id *s; + s = (union fw_self_id *) &sid; + printf("node:%d link:%d gap:%d spd:%d del:%d con:%d pwr:%d" + " p0:%d p1:%d p2:%d i:%d m:%d\n", + s->p0.phy_id, s->p0.link_active, s->p0.gap_count, + s->p0.phy_speed, s->p0.phy_delay, s->p0.contender, + s->p0.power_class, s->p0.port0, s->p0.port1, + s->p0.port2, s->p0.initiated_reset, s->p0.more_packets); +} +#endif + +/* + * To receive self ID. + */ +void fw_sidrcv(struct firewire_comm* fc, uint32_t *sid, u_int len) +{ + uint32_t *p; + union fw_self_id *self_id; + u_int i, j, node, c_port = 0, i_branch = 0; + + fc->sid_cnt = len /(sizeof(uint32_t) * 2); + fc->max_node = fc->nodeid & 0x3f; + CSRARC(fc, NODE_IDS) = ((uint32_t)fc->nodeid) << 16; + fc->status = FWBUSCYMELECT; + fc->topology_map->crc_len = 2; + fc->topology_map->generation ++; + fc->topology_map->self_id_count = 0; + fc->topology_map->node_count = 0; + fc->speed_map->generation ++; + fc->speed_map->crc_len = 1 + (64*64 + 3) / 4; + self_id = &fc->topology_map->self_id[0]; + for(i = 0; i < fc->sid_cnt; i ++){ + if (sid[1] != ~sid[0]) { + printf("fw_sidrcv: invalid self-id packet\n"); + sid += 2; + continue; + } + *self_id = *((union fw_self_id *)sid); + fc->topology_map->crc_len++; + if(self_id->p0.sequel == 0){ + fc->topology_map->node_count ++; + c_port = 0; +#if 0 + fw_print_sid(sid[0]); +#endif + node = self_id->p0.phy_id; + if(fc->max_node < node){ + fc->max_node = self_id->p0.phy_id; + } + /* XXX I'm not sure this is the right speed_map */ + fc->speed_map->speed[node][node] + = self_id->p0.phy_speed; + for (j = 0; j < node; j ++) { + fc->speed_map->speed[j][node] + = fc->speed_map->speed[node][j] + = min(fc->speed_map->speed[j][j], + self_id->p0.phy_speed); + } + if ((fc->irm == -1 || self_id->p0.phy_id > fc->irm) && + (self_id->p0.link_active && self_id->p0.contender)) { + fc->irm = self_id->p0.phy_id; + } + if(self_id->p0.port0 >= 0x2){ + c_port++; + } + if(self_id->p0.port1 >= 0x2){ + c_port++; + } + if(self_id->p0.port2 >= 0x2){ + c_port++; + } + } + if(c_port > 2){ + i_branch += (c_port - 2); + } + sid += 2; + self_id++; + fc->topology_map->self_id_count ++; + } + device_printf(fc->bdev, "%d nodes", fc->max_node + 1); + /* CRC */ + fc->topology_map->crc = fw_crc16( + (uint32_t *)&fc->topology_map->generation, + fc->topology_map->crc_len * 4); + fc->speed_map->crc = fw_crc16( + (uint32_t *)&fc->speed_map->generation, + fc->speed_map->crc_len * 4); + /* byteswap and copy to CSR */ + p = (uint32_t *)fc->topology_map; + for (i = 0; i <= fc->topology_map->crc_len; i++) + CSRARC(fc, TOPO_MAP + i * 4) = htonl(*p++); + p = (uint32_t *)fc->speed_map; + CSRARC(fc, SPED_MAP) = htonl(*p++); + CSRARC(fc, SPED_MAP + 4) = htonl(*p++); + /* don't byte-swap uint8_t array */ + bcopy(p, &CSRARC(fc, SPED_MAP + 8), (fc->speed_map->crc_len - 1)*4); + + fc->max_hop = fc->max_node - i_branch; + printf(", maxhop <= %d", fc->max_hop); + + if(fc->irm == -1 ){ + printf(", Not found IRM capable node"); + }else{ + printf(", cable IRM = %d", fc->irm); + if (fc->irm == fc->nodeid) + printf(" (me)"); + } + printf("\n"); + + if (try_bmr && (fc->irm != -1) && (CSRARC(fc, BUS_MGR_ID) == 0x3f)) { + if (fc->irm == fc->nodeid) { + fc->status = FWBUSMGRDONE; + CSRARC(fc, BUS_MGR_ID) = fc->set_bmr(fc, fc->irm); + fw_bmr(fc); + } else { + fc->status = FWBUSMGRELECT; +// callout_reset(&fc->bmr_callout, hz/8, +// (void *)fw_try_bmr, (void *)fc); + fc->bmr_callout = create_timer(fw_try_bmr, fc, + hz/8, B_ONE_SHOT_RELATIVE_TIMER); + } + } else + fc->status = FWBUSMGRDONE; + +// callout_reset(&fc->busprobe_callout, hz/4, +// (void *)fw_bus_probe, (void *)fc); + fc->busprobe_callout = create_timer(fw_bus_probe, fc, + hz/4, B_ONE_SHOT_RELATIVE_TIMER); +} + +/* + * To probe devices on the IEEE1394 bus. + */ +static void +fw_bus_probe(void *arg) +{ + int s; + struct firewire_comm *fc = (struct firewire_comm *)arg; + struct fw_device *fwdev; + + s = splfw(); + fc->status = FWBUSEXPLORE; + + /* Invalidate all devices, just after bus reset. */ + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (fwdev->status != FWDEVINVAL) { + fwdev->status = FWDEVINVAL; + fwdev->rcnt = 0; + } + splx(s); + +// wakeup((void *)fc); + release_sem(fc->Sem); +} + +static int +fw_explore_read_quads(struct fw_device *fwdev, int offset, + uint32_t *quad, int n) +{ + struct fw_xfer *xfer; + uint32_t tmp; + int i, error; + + for (i = 0; i < n; i ++, offset += sizeof(uint32_t)) { + xfer = fwmem_read_quad(fwdev, NULL, -1, + 0xffff, 0xf0000000 | offset, (void *)&tmp, + fw_xferwake); + if (xfer == NULL) + return (-1); + fw_xferwait(xfer); + + if (xfer->resp == 0) + quad[i] = ntohl(tmp); + + error = xfer->resp; + fw_xfer_free(xfer); + if (error) + return (error); + } + return (0); +} + + +static int +fw_explore_csrblock(struct fw_device *fwdev, int offset, int recur) +{ + int err, i, off; + struct csrdirectory *dir; + struct csrreg *reg; + + dir = (struct csrdirectory *)&fwdev->csrrom[offset/sizeof(uint32_t)]; + err = fw_explore_read_quads(fwdev, CSRROMOFF + offset, + (uint32_t *)dir, 1); + if (err) + return (-1); + + offset += sizeof(uint32_t); + reg = (struct csrreg *)&fwdev->csrrom[offset/sizeof(uint32_t)]; + err = fw_explore_read_quads(fwdev, CSRROMOFF + offset, + (uint32_t *)reg, dir->crc_len); + if (err) + return (-1); + + /* XXX check CRC */ + + off = CSRROMOFF + offset + sizeof(uint32_t) * (dir->crc_len - 1); + if (fwdev->rommax < off) + fwdev->rommax = off; + + if (recur == 0) + return (0); + + for (i = 0; i < dir->crc_len; i ++, offset += sizeof(uint32_t)) { + if ((reg[i].key & CSRTYPE_MASK) == CSRTYPE_D) + recur = 1; + else if ((reg[i].key & CSRTYPE_MASK) == CSRTYPE_L) + recur = 0; + else + continue; + + off = offset + reg[i].val * sizeof(uint32_t); + if (off > CROMSIZE) { + printf("%s: invalid offset %d\n", __FUNCTION__, off); + return(-1); + } + err = fw_explore_csrblock(fwdev, off, recur); + if (err) + return (-1); + } + return (0); +} + +static int +fw_explore_node(struct fw_device *dfwdev) +{ + struct firewire_comm *fc; + struct fw_device *fwdev, *pfwdev, *tfwdev; + uint32_t *csr; + struct csrhdr *hdr; + struct bus_info *binfo; + int err, node, spd; + + fc = dfwdev->fc; + csr = dfwdev->csrrom; + node = dfwdev->dst; + + /* First quad */ + err = fw_explore_read_quads(dfwdev, CSRROMOFF, &csr[0], 1); + if (err) + return (-1); + hdr = (struct csrhdr *)&csr[0]; + if (hdr->info_len != 4) { + if (firewire_debug) + printf("node%d: wrong bus info len(%d)\n", + node, hdr->info_len); + return (-1); + } + + /* bus info */ + err = fw_explore_read_quads(dfwdev, CSRROMOFF + 0x04, &csr[1], 4); + if (err) + return (-1); + binfo = (struct bus_info *)&csr[1]; + if (binfo->bus_name != CSR_BUS_NAME_IEEE1394) { + if (firewire_debug) + printf("node%d: invalid bus name 0x%08x\n", + node, binfo->bus_name); + return (-1); + } + spd = fc->speed_map->speed[fc->nodeid][node]; + STAILQ_FOREACH(fwdev, &fc->devices, link) + if (FW_EUI64_EQUAL(fwdev->eui, binfo->eui64)) + break; + if (fwdev == NULL) { + /* new device */ + fwdev = malloc(sizeof(struct fw_device)); + if (fwdev == NULL) { + if (firewire_debug) + printf("node%d: no memory\n", node); + return (-1); + } + memset(fwdev, 0, sizeof(struct fw_device)); + fwdev->fc = fc; + fwdev->eui = binfo->eui64; + /* inesrt into sorted fwdev list */ + pfwdev = NULL; + STAILQ_FOREACH(tfwdev, &fc->devices, link) { + if (tfwdev->eui.hi > fwdev->eui.hi || + (tfwdev->eui.hi == fwdev->eui.hi && + tfwdev->eui.lo > fwdev->eui.lo)) + break; + pfwdev = tfwdev; + } + if (pfwdev == NULL) + STAILQ_INSERT_HEAD(&fc->devices, fwdev, link); + else + STAILQ_INSERT_AFTER(&fc->devices, pfwdev, fwdev, link); + + device_printf(fc->bdev, "New %s device ID:%08x%08x\n", + linkspeed[spd], + fwdev->eui.hi, fwdev->eui.lo); + } + fwdev->dst = node; + fwdev->status = FWDEVINIT; + fwdev->speed = spd; + + /* unchanged ? */ + if (bcmp(&csr[0], &fwdev->csrrom[0], sizeof(uint32_t) * 5) == 0) { + if (firewire_debug) + printf("node%d: crom unchanged\n", node); + return (0); + } + + bzero(&fwdev->csrrom[0], CROMSIZE); + + /* copy first quad and bus info block */ + bcopy(&csr[0], &fwdev->csrrom[0], sizeof(uint32_t) * 5); + fwdev->rommax = CSRROMOFF + sizeof(uint32_t) * 4; + + err = fw_explore_csrblock(fwdev, 0x14, 1); /* root directory */ + + if (err) { + fwdev->status = FWDEVINVAL; + fwdev->csrrom[0] = 0; + } + return (err); + +} + +/* + * Find the self_id packet for a node, ignoring sequels. + */ +static union fw_self_id * +fw_find_self_id(struct firewire_comm *fc, int node) +{ + uint32_t i; + union fw_self_id *s; + + for (i = 0; i < fc->topology_map->self_id_count; i++) { + s = &fc->topology_map->self_id[i]; + if (s->p0.sequel) + continue; + if (s->p0.phy_id == node) + return s; + } + return 0; +} + +static void +fw_explore(struct firewire_comm *fc) +{ + int node, err, s, i, todo, todo2, trys; + char nodes[63]; + struct fw_device dfwdev; + union fw_self_id *fwsid; + + todo = 0; + /* setup dummy fwdev */ + dfwdev.fc = fc; + dfwdev.speed = 0; + dfwdev.maxrec = 8; /* 512 */ + dfwdev.status = FWDEVINIT; + + for (node = 0; node <= fc->max_node; node ++) { + /* We don't probe myself and linkdown nodes */ + if (node == fc->nodeid) + continue; + fwsid = fw_find_self_id(fc, node); + if (!fwsid || !fwsid->p0.link_active) { + if (firewire_debug) + printf("node%d: link down\n", node); + continue; + } + nodes[todo++] = node; + } + + s = splfw(); + for (trys = 0; todo > 0 && trys < 3; trys ++) { + todo2 = 0; + for (i = 0; i < todo; i ++) { + dfwdev.dst = nodes[i]; + err = fw_explore_node(&dfwdev); + if (err) + nodes[todo2++] = nodes[i]; + if (firewire_debug) + printf("%s: node %d, err = %d\n", + __FUNCTION__, node, err); + } + todo = todo2; + } + splx(s); +} + + +static int32 +fw_bus_probe_thread(void *arg) +{ + struct firewire_comm *fc; + + fc = (struct firewire_comm *)arg; + mtx_lock(&fc->wait_lock); + while (fc->status != FWBUSDETACH) { + if (fc->status == FWBUSEXPLORE) { + mtx_unlock(&fc->wait_lock); + fw_explore(fc); + fc->status = FWBUSEXPDONE; + if (firewire_debug) + printf("bus_explore done\n"); + fw_attach_dev(fc); + mtx_lock(&fc->wait_lock); + } +// msleep((void *)fc, &fc->wait_lock, PWAIT|PCATCH, "-", 0); + mtx_unlock(&fc->wait_lock); + acquire_sem(fc->Sem); + mtx_lock(&fc->wait_lock); + + } + mtx_unlock(&fc->wait_lock); +// kthread_exit(0); + return 0; +} + +/* + * To attach sub-devices layer onto IEEE1394 bus. + */ +static void +fw_attach_dev(struct firewire_comm *fc) +{ + struct fw_device *fwdev, *next; +// int i, err; +// device_t *devlistp; +// int devcnt; + struct firewire_dev_comm *fdc; + struct firewire_child_info *element = fc->childList; + + for (fwdev = STAILQ_FIRST(&fc->devices); fwdev != NULL; fwdev = next) { + next = STAILQ_NEXT(fwdev, link); + if (fwdev->status == FWDEVINIT) { + fwdev->status = FWDEVATTACHED; + } else if (fwdev->status == FWDEVINVAL) { + fwdev->rcnt ++; + if (fwdev->rcnt > hold_count) { + /* + * Remove devices which have not been seen + * for a while. + */ + STAILQ_REMOVE(&fc->devices, fwdev, fw_device, + link); + free(fwdev); + } + } + } + +/* err = device_get_children(fc->bdev, &devlistp, &devcnt); + if( err != 0 ) + return; + for( i = 0 ; i < devcnt ; i++){ + if (device_get_state(devlistp[i]) >= DS_ATTACHED) { + fdc = device_get_softc(devlistp[i]); + if (fdc->post_explore != NULL) + fdc->post_explore(fdc); + } + } + free(devlistp, M_TEMP);*/ + + while(element){ +// fdc = &element->cookie.fd; + fdc = (struct firewire_dev_comm *)element->cookie; + if (fdc->post_explore != NULL) + fdc->post_explore(fdc); + element = element->link; + } + + + return; +} + +/* + * To allocate unique transaction label. + */ +static int +fw_get_tlabel(struct firewire_comm *fc, struct fw_xfer *xfer) +{ + u_int dst, new_tlabel; + struct fw_xfer *txfer; + int s; + + dst = xfer->send.hdr.mode.hdr.dst & 0x3f; + s = splfw(); + FW_GLOCK(fc); + new_tlabel = (fc->last_tlabel[dst] + 1) & 0x3f; + STAILQ_FOREACH(txfer, &fc->tlabels[new_tlabel], tlabel) + if ((txfer->send.hdr.mode.hdr.dst & 0x3f) == dst) + break; + if(txfer == NULL) { + fc->last_tlabel[dst] = new_tlabel; + STAILQ_INSERT_TAIL(&fc->tlabels[new_tlabel], xfer, tlabel); + FW_GUNLOCK(fc); + splx(s); + xfer->tl = new_tlabel; + xfer->send.hdr.mode.hdr.tlrt = new_tlabel << 2; + if (firewire_debug > 1) + printf("fw_get_tlabel: dst=%d tl=%d\n", dst, new_tlabel); + return (new_tlabel); + } + FW_GUNLOCK(fc); + splx(s); + + if (firewire_debug > 1) + printf("fw_get_tlabel: no free tlabel\n"); + return (-1); +} + +static void +fw_rcv_copy(struct fw_rcv_buf *rb) +{ + struct fw_pkt *pkt; + u_char *p; + struct tcode_info *tinfo; + u_int res, i, len, plen; + + rb->xfer->recv.spd = rb->spd; + + pkt = (struct fw_pkt *)rb->vec->iov_base; + tinfo = &rb->fc->tcode[pkt->mode.hdr.tcode]; + + /* Copy header */ + p = (u_char *)&rb->xfer->recv.hdr; + bcopy(rb->vec->iov_base, p, tinfo->hdr_len); + rb->vec->iov_base = (u_char *)rb->vec->iov_base + tinfo->hdr_len; + rb->vec->iov_len -= tinfo->hdr_len; + + /* Copy payload */ + p = (u_char *)rb->xfer->recv.payload; + res = rb->xfer->recv.pay_len; + + /* special handling for RRESQ */ + if (pkt->mode.hdr.tcode == FWTCODE_RRESQ && + p != NULL && res >= sizeof(uint32_t)) { + *(uint32_t *)p = pkt->mode.rresq.data; + rb->xfer->recv.pay_len = sizeof(uint32_t); + return; + } + + if ((tinfo->flag & FWTI_BLOCK_ASY) == 0) + return; + + plen = pkt->mode.rresb.len; + + for (i = 0; i < rb->nvec; i++, rb->vec++) { + len = MIN(rb->vec->iov_len, plen); + if (res < len) { + printf("rcv buffer(%d) is %d bytes short.\n", + rb->xfer->recv.pay_len, len - res); + len = res; + } + bcopy(rb->vec->iov_base, p, len); + p += len; + res -= len; + plen -= len; + if (res == 0 || plen == 0) + break; + } + rb->xfer->recv.pay_len -= res; + +} + +/* + * Generic packet receiving process. + */ +void +fw_rcv(struct fw_rcv_buf *rb) +{ + struct fw_pkt *fp, *resfp; + struct fw_bind *bind; + int tcode; + int i, len, oldstate; +#if 0 + { + uint32_t *qld; + int i; + qld = (uint32_t *)buf; + printf("spd %d len:%d\n", spd, len); + for( i = 0 ; i <= len && i < 32; i+= 4){ + printf("0x%08x ", ntohl(qld[i/4])); + if((i % 16) == 15) printf("\n"); + } + if((i % 16) != 15) printf("\n"); + } +#endif + fp = (struct fw_pkt *)rb->vec[0].iov_base; + tcode = fp->mode.common.tcode; + switch (tcode) { + case FWTCODE_WRES: + case FWTCODE_RRESQ: + case FWTCODE_RRESB: + case FWTCODE_LRES: + rb->xfer = fw_tl2xfer(rb->fc, fp->mode.hdr.src, + fp->mode.hdr.tlrt >> 2, fp->mode.hdr.tcode); + if(rb->xfer == NULL) { + printf("fw_rcv: unknown response " + "%s(%x) src=0x%x tl=0x%x rt=%d data=0x%x\n", + tcode_str[tcode], tcode, + fp->mode.hdr.src, + fp->mode.hdr.tlrt >> 2, + fp->mode.hdr.tlrt & 3, + fp->mode.rresq.data); +#if 0 + printf("try ad-hoc work around!!\n"); + rb->xfer = fw_tl2xfer(rb->fc, fp->mode.hdr.src, + (fp->mode.hdr.tlrt >> 2)^3); + if (rb->xfer == NULL) { + printf("no use...\n"); + return; + } +#else + return; +#endif + } + fw_rcv_copy(rb); + if (rb->xfer->recv.hdr.mode.wres.rtcode != RESP_CMP) + rb->xfer->resp = EIO; + else + rb->xfer->resp = 0; + /* make sure the packet is drained in AT queue */ + oldstate = rb->xfer->flag; + rb->xfer->flag = FWXF_RCVD; + switch (oldstate) { + case FWXF_SENT: + fw_xfer_done(rb->xfer); + break; + case FWXF_START: +#if 0 + if (firewire_debug) + printf("not sent yet tl=%x\n", rb->xfer->tl); +#endif + break; + default: + printf("unexpected flag 0x%02x\n", rb->xfer->flag); + } + return; + case FWTCODE_WREQQ: + case FWTCODE_WREQB: + case FWTCODE_RREQQ: + case FWTCODE_RREQB: + case FWTCODE_LREQ: + bind = fw_bindlookup(rb->fc, fp->mode.rreqq.dest_hi, + fp->mode.rreqq.dest_lo); + if(bind == NULL){ + printf("Unknown service addr 0x%04x:0x%08x %s(%x)" + " src=0x%x data=%x\n", + fp->mode.wreqq.dest_hi, fp->mode.wreqq.dest_lo, + tcode_str[tcode], tcode, + fp->mode.hdr.src, ntohl(fp->mode.wreqq.data)); + if (rb->fc->status == FWBUSINIT) { + printf("fw_rcv: cannot respond(bus reset)!\n"); + return; + } + rb->xfer = fw_xfer_alloc(); + if(rb->xfer == NULL){ + return; + } + rb->xfer->send.spd = rb->spd; + rb->xfer->send.pay_len = 0; + resfp = &rb->xfer->send.hdr; + switch (tcode) { + case FWTCODE_WREQQ: + case FWTCODE_WREQB: + resfp->mode.hdr.tcode = FWTCODE_WRES; + break; + case FWTCODE_RREQQ: + resfp->mode.hdr.tcode = FWTCODE_RRESQ; + break; + case FWTCODE_RREQB: + resfp->mode.hdr.tcode = FWTCODE_RRESB; + break; + case FWTCODE_LREQ: + resfp->mode.hdr.tcode = FWTCODE_LRES; + break; + } + resfp->mode.hdr.dst = fp->mode.hdr.src; + resfp->mode.hdr.tlrt = fp->mode.hdr.tlrt; + resfp->mode.hdr.pri = fp->mode.hdr.pri; + resfp->mode.rresb.rtcode = RESP_ADDRESS_ERROR; + resfp->mode.rresb.extcode = 0; + resfp->mode.rresb.len = 0; +/* + rb->xfer->hand = fw_xferwake; +*/ + rb->xfer->hand = fw_xfer_free; + if(fw_asyreq(rb->fc, -1, rb->xfer)){ + fw_xfer_free(rb->xfer); + return; + } + return; + } + len = 0; + for (i = 0; i < rb->nvec; i ++) + len += rb->vec[i].iov_len; + rb->xfer = STAILQ_FIRST(&bind->xferlist); + if (rb->xfer == NULL) { +#if 1 + printf("Discard a packet for this bind.\n"); +#endif + return; + } + STAILQ_REMOVE_HEAD(&bind->xferlist, link); + fw_rcv_copy(rb); + rb->xfer->hand(rb->xfer); + return; +#if 0 /* shouldn't happen ?? or for GASP */ + case FWTCODE_STREAM: + { + struct fw_xferq *xferq; + + xferq = rb->fc->ir[sub]; +#if 0 + printf("stream rcv dma %d len %d off %d spd %d\n", + sub, len, off, spd); +#endif + if(xferq->queued >= xferq->maxq) { + printf("receive queue is full\n"); + return; + } + /* XXX get xfer from xfer queue, we don't need copy for + per packet mode */ + rb->xfer = fw_xfer_alloc_buf(M_FWXFER, 0, /* XXX */ + vec[0].iov_len); + if (rb->xfer == NULL) + return; + fw_rcv_copy(rb) + s = splfw(); + xferq->queued++; + STAILQ_INSERT_TAIL(&xferq->q, rb->xfer, link); + splx(s); + sc = device_get_softc(rb->fc->bdev); +#if defined(__DragonFly__) || __FreeBSD_version < 500000 + if (&xferq->rsel.si_pid != 0) +#else + if (SEL_WAITING(&xferq->rsel)) +#endif + selwakeuppri(&xferq->rsel, FWPRI); + if (xferq->flag & FWXFERQ_WAKEUP) { + xferq->flag &= ~FWXFERQ_WAKEUP; + wakeup((caddr_t)xferq); + } + if (xferq->flag & FWXFERQ_HANDLER) { + xferq->hand(xferq); + } + return; + break; + } +#endif + default: + printf("fw_rcv: unknow tcode %d\n", tcode); + break; + } +} + +/* + * Post process for Bus Manager election process. + */ +static void +fw_try_bmr_callback(struct fw_xfer *xfer) +{ + struct firewire_comm *fc; + int bmr; + + if (xfer == NULL) + return; + fc = xfer->fc; + if (xfer->resp != 0) + goto error; + if (xfer->recv.payload == NULL) + goto error; + if (xfer->recv.hdr.mode.lres.rtcode != FWRCODE_COMPLETE) + goto error; + + bmr = ntohl(xfer->recv.payload[0]); + if (bmr == 0x3f) + bmr = fc->nodeid; + + CSRARC(fc, BUS_MGR_ID) = fc->set_bmr(fc, bmr & 0x3f); + fw_xfer_free_buf(xfer); + fw_bmr(fc); + return; + +error: + device_printf(fc->bdev, "bus manager election failed\n"); + fw_xfer_free_buf(xfer); +} + + +/* + * To candidate Bus Manager election process. + */ +static void +fw_try_bmr(void *arg) +{ + struct fw_xfer *xfer; + struct firewire_comm *fc = (struct firewire_comm *)arg; + struct fw_pkt *fp; + int err = 0; + + xfer = fw_xfer_alloc_buf(8, 4); + if(xfer == NULL){ + return; + } + xfer->send.spd = 0; + fc->status = FWBUSMGRELECT; + + fp = &xfer->send.hdr; + fp->mode.lreq.dest_hi = 0xffff; + fp->mode.lreq.tlrt = 0; + fp->mode.lreq.tcode = FWTCODE_LREQ; + fp->mode.lreq.pri = 0; + fp->mode.lreq.src = 0; + fp->mode.lreq.len = 8; + fp->mode.lreq.extcode = EXTCODE_CMP_SWAP; + fp->mode.lreq.dst = FWLOCALBUS | fc->irm; + fp->mode.lreq.dest_lo = 0xf0000000 | BUS_MGR_ID; + xfer->send.payload[0] = htonl(0x3f); + xfer->send.payload[1] = htonl(fc->nodeid); + xfer->hand = fw_try_bmr_callback; + + err = fw_asyreq(fc, -1, xfer); + if(err){ + fw_xfer_free_buf(xfer); + return; + } + return; +} + +#ifdef FW_VMACCESS +/* + * Software implementation for physical memory block access. + * XXX:Too slow, usef for debug purpose only. + */ +static void +fw_vmaccess(struct fw_xfer *xfer){ + struct fw_pkt *rfp, *sfp = NULL; + uint32_t *ld = (uint32_t *)xfer->recv.buf; + + printf("vmaccess spd:%2x len:%03x data:%08x %08x %08x %08x\n", + xfer->spd, xfer->recv.len, ntohl(ld[0]), ntohl(ld[1]), ntohl(ld[2]), ntohl(ld[3])); + printf("vmaccess data:%08x %08x %08x %08x\n", ntohl(ld[4]), ntohl(ld[5]), ntohl(ld[6]), ntohl(ld[7])); + if(xfer->resp != 0){ + fw_xfer_free( xfer); + return; + } + if(xfer->recv.buf == NULL){ + fw_xfer_free( xfer); + return; + } + rfp = (struct fw_pkt *)xfer->recv.buf; + switch(rfp->mode.hdr.tcode){ + /* XXX need fix for 64bit arch */ + case FWTCODE_WREQB: + xfer->send.buf = malloc(12, M_FW, M_NOWAIT); + xfer->send.len = 12; + sfp = (struct fw_pkt *)xfer->send.buf; + bcopy(rfp->mode.wreqb.payload, + (caddr_t)ntohl(rfp->mode.wreqb.dest_lo), ntohs(rfp->mode.wreqb.len)); + sfp->mode.wres.tcode = FWTCODE_WRES; + sfp->mode.wres.rtcode = 0; + break; + case FWTCODE_WREQQ: + xfer->send.buf = malloc(12, M_FW, M_NOWAIT); + xfer->send.len = 12; + sfp->mode.wres.tcode = FWTCODE_WRES; + *((uint32_t *)(ntohl(rfp->mode.wreqb.dest_lo))) = rfp->mode.wreqq.data; + sfp->mode.wres.rtcode = 0; + break; + case FWTCODE_RREQB: + xfer->send.buf = malloc(16 + rfp->mode.rreqb.len, M_FW, M_NOWAIT); + xfer->send.len = 16 + ntohs(rfp->mode.rreqb.len); + sfp = (struct fw_pkt *)xfer->send.buf; + bcopy((caddr_t)ntohl(rfp->mode.rreqb.dest_lo), + sfp->mode.rresb.payload, (uint16_t)ntohs(rfp->mode.rreqb.len)); + sfp->mode.rresb.tcode = FWTCODE_RRESB; + sfp->mode.rresb.len = rfp->mode.rreqb.len; + sfp->mode.rresb.rtcode = 0; + sfp->mode.rresb.extcode = 0; + break; + case FWTCODE_RREQQ: + xfer->send.buf = malloc(16, M_FW, M_NOWAIT); + xfer->send.len = 16; + sfp = (struct fw_pkt *)xfer->send.buf; + sfp->mode.rresq.data = *(uint32_t *)(ntohl(rfp->mode.rreqq.dest_lo)); + sfp->mode.wres.tcode = FWTCODE_RRESQ; + sfp->mode.rresb.rtcode = 0; + break; + default: + fw_xfer_free( xfer); + return; + } + sfp->mode.hdr.dst = rfp->mode.hdr.src; + xfer->dst = ntohs(rfp->mode.hdr.src); + xfer->hand = fw_xfer_free; + + sfp->mode.hdr.tlrt = rfp->mode.hdr.tlrt; + sfp->mode.hdr.pri = 0; + + fw_asyreq(xfer->fc, -1, xfer); +/**/ + return; +} +#endif + +/* + * CRC16 check-sum for IEEE1394 register blocks. + */ +uint16_t +fw_crc16(uint32_t *ptr, uint32_t len){ + uint32_t i, sum, crc = 0; + int shift; + len = (len + 3) & ~3; + for(i = 0 ; i < len ; i+= 4){ + for( shift = 28 ; shift >= 0 ; shift -= 4){ + sum = ((crc >> 12) ^ (ptr[i/4] >> shift)) & 0xf; + crc = (crc << 4) ^ ( sum << 12 ) ^ ( sum << 5) ^ sum; + } + crc &= 0xffff; + } + return((uint16_t) crc); +} + +static int +fw_bmr(struct firewire_comm *fc) +{ + struct fw_device fwdev; + union fw_self_id *self_id; + int cmstr; + uint32_t quad; + + /* Check to see if the current root node is cycle master capable */ + self_id = fw_find_self_id(fc, fc->max_node); + if (fc->max_node > 0) { + /* XXX check cmc bit of businfo block rather than contender */ + if (self_id->p0.link_active && self_id->p0.contender) + cmstr = fc->max_node; + else { + device_printf(fc->bdev, + "root node is not cycle master capable\n"); + /* XXX shall we be the cycle master? */ + cmstr = fc->nodeid; + /* XXX need bus reset */ + } + } else + cmstr = -1; + + device_printf(fc->bdev, "bus manager %d ", CSRARC(fc, BUS_MGR_ID)); + if(CSRARC(fc, BUS_MGR_ID) != fc->nodeid) { + /* We are not the bus manager */ + printf("\n"); + return(0); + } + printf("(me)\n"); + + /* Optimize gapcount */ + if(fc->max_hop <= MAX_GAPHOP ) + fw_phy_config(fc, cmstr, gap_cnt[fc->max_hop]); + /* If we are the cycle master, nothing to do */ + if (cmstr == fc->nodeid || cmstr == -1) + return 0; + /* Bus probe has not finished, make dummy fwdev for cmstr */ + bzero(&fwdev, sizeof(fwdev)); + fwdev.fc = fc; + fwdev.dst = cmstr; + fwdev.speed = 0; + fwdev.maxrec = 8; /* 512 */ + fwdev.status = FWDEVINIT; + /* Set cmstr bit on the cycle master */ + quad = htonl(1 << 8); + fwmem_write_quad(&fwdev, NULL, 0/*spd*/, + 0xffff, 0xf0000000 | STATE_SET, &quad, fw_asy_callback_free); + + return 0; +} + +int +fw_open_isodma(struct firewire_comm *fc, int tx) +{ + struct fw_xferq **xferqa; + struct fw_xferq *xferq; + int i; + + if (tx) + xferqa = &fc->it[0]; + else + xferqa = &fc->ir[0]; + + FW_GLOCK(fc); + for (i = 0; i < fc->nisodma; i ++) { + xferq = xferqa[i]; + if ((xferq->flag & FWXFERQ_OPEN) == 0) { + xferq->flag |= FWXFERQ_OPEN; + break; + } + } + if (i == fc->nisodma) { + printf("no free dma channel (tx=%d)\n", tx); + i = -1; + } + FW_GUNLOCK(fc); + return (i); +} diff --git a/src/add-ons/kernel/bus_managers/firewire/firewire_module.c b/src/add-ons/kernel/bus_managers/firewire/firewire_module.c new file mode 100644 index 0000000000..2e2a3c4bb3 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/firewire_module.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2007 JiSheng Zhang . All rights reserved + * Distributed under the terms of the MIT license. + * + * Kernel driver for firewire + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fwdebug.h" +#include "queue.h" +#include "fwglue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwdma.h" +#include "fwohcireg.h" +#include "fwohcivar.h" +#include "firewire_module.h" + +status_t fwohci_pci_attach(int index); +status_t fwohci_pci_detach(int index); +pci_info *pciInfo[MAX_CARDS]; +fwohci_softc_t *gFwohci_softc[MAX_CARDS]; +struct firewire_softc *gFirewire_softc[MAX_CARDS]; +pci_module_info *pci; +dpc_module_info *gDpc; + +struct supported_device{ + uint16 vendor_id; + uint32 device_id; + char *name; +}; + +struct supported_device supported_devices[] = { + {FW_VENDORID_NATSEMI, FW_DEVICE_CS4210, "National Semiconductor CS4210"}, + {FW_VENDORID_NEC, FW_DEVICE_UPD861, "NEC uPD72861"}, + {FW_VENDORID_NEC, FW_DEVICE_UPD871, "NEC uPD72871/2"}, + {FW_VENDORID_NEC, FW_DEVICE_UPD72870, "NEC uPD72870"}, + {FW_VENDORID_NEC, FW_DEVICE_UPD72873, "NEC uPD72873"}, + {FW_VENDORID_NEC, FW_DEVICE_UPD72874, "NEC uPD72874"}, + {FW_VENDORID_SIS, FW_DEVICE_7007, "SiS 7007"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB22, "Texas Instruments TSB12LV22"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB23, "Texas Instruments TSB12LV23"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB26, "Texas Instruments TSB12LV26"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB43, "Texas Instruments TSB43AA22"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB43A, "Texas Instruments TSB43AB22/A"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB43AB21, "Texas Instruments TSB43AB21/A/AI/A-EP"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB43AB23, "Texas Instruments TSB43AB23"}, + {FW_VENDORID_TI, FW_DEVICE_TITSB82AA2, "Texas Instruments TSB82AA2"}, + {FW_VENDORID_TI, FW_DEVICE_TIPCI4450, "Texas Instruments PCI4450"}, + {FW_VENDORID_TI, FW_DEVICE_TIPCI4410A, "Texas Instruments PCI4410A"}, + {FW_VENDORID_TI, FW_DEVICE_TIPCI4451, "Texas Instruments PCI4451"}, + {FW_VENDORID_VIA, FW_DEVICE_VT6306, "VIA Fire II (VT6306)"}, + {FW_VENDORID_RICOH, FW_DEVICE_R5C551, "Ricoh R5C551"}, + {FW_VENDORID_RICOH, FW_DEVICE_R5C552, "Ricoh R5C552"}, + {FW_VENDORID_APPLE, FW_DEVICE_PANGEA, "Apple Pangea"}, + {FW_VENDORID_APPLE, FW_DEVICE_UNINORTH, "Apple UniNorth"}, + {FW_VENDORID_LUCENT, FW_DEVICE_FW322, "Lucent FW322/323"}, + {FW_VENDORID_INTEL, FW_DEVICE_82372FB, "Intel 82372FB"}, + {FW_VENDORID_ADAPTEC, FW_DEVICE_AIC5800, "Adaptec AHA-894x/AIC-5800"}, + {FW_VENDORID_SUN, FW_DEVICE_PCIO2FW, "Sun PCIO-2"}, + {FW_VENDORID_SONY, FW_DEVICE_CXD3222, "Sony i.LINK (CXD3222)"}, + {0, 0, NULL} +}; + + +static int find_device_name(pci_info *info) +{ + struct supported_device *device; + for (device = supported_devices; device->name; device++) { + if (info->vendor_id == device->vendor_id + && info->device_id == device->device_id >> 16) { + dprintf("%s\n", device->name); + return 1; + } + } + return 0; +} + + +static status_t +fw_add_child(const char *childname, + const struct firewire_notify_hooks *hooks) +{ + status_t status; + int i; + TRACE("add child %s\n", childname); + for (i = 0; gFirewire_softc[i] != NULL; i++) { + status = firewire_add_child(gFirewire_softc[i], childname, hooks); + if (status != B_OK) + return status; + } + + return B_OK; +} + + +static status_t +fw_remove_child(const char *childname) +{ + status_t status; + int i; + TRACE("remove child %s\n", childname); + for (i = 0; gFirewire_softc[i] != NULL; i++) { + status = firewire_remove_child(gFirewire_softc[i], childname); + if (status != B_OK) + return status; + } + + return B_OK; +} + + +static int +fw_get_handle(int socket, struct firewire_softc **handle) +{ + if (handle == NULL) + return B_BAD_VALUE; + if (socket >= 0 && socket < MAX_CARDS && gFirewire_softc[socket]) { + *handle = gFirewire_softc[socket]; + return B_OK; + } + *handle = NULL; + return ENODEV; +} + + +static status_t +fw_module_init(void) +{ + status_t status; + pci_info *info = NULL; + int i, found; + fwohci_softc_t *fwohci_sc; + struct firewire_softc *fw_sc; + + info = malloc(sizeof(pci_info)); + if (!info) + return B_NO_MEMORY; + + if ((status = get_module(B_PCI_MODULE_NAME,(module_info **)&pci)) != B_OK) { + TRACE("pci module unavailable\n"); + free(info); + return status; + } + + if ((status = get_module(B_DPC_MODULE_NAME,(module_info **)&gDpc)) != B_OK) { + TRACE("pci module unavailable\n"); + free(info); + put_module(B_PCI_MODULE_NAME); + return status; + } + + memset(gFwohci_softc, 0, sizeof(gFwohci_softc)); + + // find devices + for (i = 0, found = 0; (status = pci->get_nth_pci_info(i, info)) == B_OK; i++) { + if (find_device_name(info) + || ((info->class_base == PCI_serial_bus) + && (info->class_sub == PCI_firewire) + && (info->class_api == PCI_INTERFACE_OHCI))) { + dprintf( "vendor=%x, device=%x, revision = %x\n", info->vendor_id, info->device_id, info->revision); + pciInfo[found] = info; + + fwohci_sc = malloc(sizeof(fwohci_softc_t)); + if (!fwohci_sc) { + free(info); + goto err_outofmem; + } + memset(fwohci_sc, 0, sizeof(fwohci_softc_t)); + gFwohci_softc[found] = fwohci_sc; + + fw_sc = malloc(sizeof(struct firewire_softc)); + if (!fw_sc) { + free(info); + free(fwohci_sc); + goto err_outofmem; + } + memset(fw_sc, 0, sizeof(struct firewire_softc)); + gFirewire_softc[found] = fw_sc; + gFirewire_softc[found+1] = NULL; + + found++; + info = malloc(sizeof(pci_info)); + if (!info) + goto err_outofmem; + + if (found == MAX_CARDS) + break; + + } + } + TRACE("found %d cards\n", found); + free(info); + +// if (!found) +// goto err_found; + + if ((status = initialize_timer()) != B_OK) { + ERROR("timer init failed\n"); + goto err_timer; + } + + for (i = 0; i < found; i++) { + if (fwohci_pci_attach(i) != B_OK) { + ERROR("fwohci_pci_attach failed\n"); + goto err_pci; + } + } + return B_OK; + +err_pci: + terminate_timer(); +err_timer: +err_found: +err_outofmem: + for (i = 0; i < found; i++) { + free(gFirewire_softc[i]); + free(gFwohci_softc[i]); + free(pciInfo[i]); + } + put_module(B_PCI_MODULE_NAME); + put_module(B_DPC_MODULE_NAME); + return B_ERROR; + +} + +static status_t +fw_module_uninit(void) +{ + int i; + + terminate_timer(); + + for (i = 0; gFirewire_softc[i] != NULL; i++) { + fwohci_pci_detach(i); + free(gFirewire_softc[i]); + free(gFwohci_softc[i]); + free(pciInfo[i]); + } + + put_module(B_PCI_MODULE_NAME); + put_module(B_DPC_MODULE_NAME); + + return B_OK; +} + + +static status_t +fw_module_std_ops(int32 op, ...) +{ + switch (op) { + case B_MODULE_INIT: + TRACE("fw_module_init\n"); + return fw_module_init(); + + case B_MODULE_UNINIT: + TRACE("fw_module_uninit\n"); + return fw_module_uninit(); + } + return B_BAD_VALUE; +} + +static struct fw_module_info gModuleInfo = { + { + { + FIREWIRE_MODULE_NAME, + 0, + fw_module_std_ops + }, + NULL + }, + fw_noderesolve_nodeid, + fw_noderesolve_eui64, + fw_asyreq, + fw_xferwake, + fw_xferwait, + fw_bindlookup, + fw_bindadd, + fw_bindremove, + fw_xferlist_add, + fw_xferlist_remove, + fw_xfer_alloc, + fw_xfer_alloc_buf, + fw_xfer_done, + fw_xfer_unload, + fw_xfer_free_buf, + fw_xfer_free, + fw_asy_callback_free, + fw_open_isodma, + fw_get_handle, + fwdma_malloc_multiseg, + fwdma_free_multiseg +}; + +module_info *modules[] = { + (module_info *)&gModuleInfo, + NULL +}; diff --git a/src/add-ons/kernel/bus_managers/firewire/firewire_phy.h b/src/add-ons/kernel/bus_managers/firewire/firewire_phy.h new file mode 100644 index 0000000000..bfa88c11d0 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/firewire_phy.h @@ -0,0 +1,85 @@ +/*- + * 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/firewire_phy.h,v 1.3 2005/01/06 01:42:41 imp Exp $ + * + */ + +#define FW_PHY_PHYSID_REG 0x00 +#define FW_PHY_PHYSID (63<<2) +#define FW_PHY_ROOT_REG 0x00 +#define FW_PHY_ROOT (1<<1) +#define FW_PHY_CPS_REG 0x00 +#define FW_PHY_CPS (1<<0) + +#define FW_PHY_RHB_REG 0x01 +#define FW_PHY_RHB (1<<7) +#define FW_PHY_IBR_REG 0x01 +#define FW_PHY_IBR (1<<6) +#define FW_PHY_ISBR_REG 0x05 +#define FW_PHY_ISBR (1<<6) +#define FW_PHY_GC_REG 0x01 + +#define FW_PHY_SPD_REG 0x02 +#define FW_PHY_SPD (3<<6) +#define FW_PHY_REV_REG 0x02 +#define FW_PHY_REV (3<<4) +#define FW_PHY_NP_REG 0x02 +#define FW_PHY_NP (15<<0) + +#define FW_PHY_P1_REG 0x03 +#define FW_PHY_P2_REG 0x04 +#define FW_PHY_P3_REG 0x05 + +#define FW_PHY_P_ASTAT (3<<6) +#define FW_PHY_P_BSTAT (3<<4) +#define FW_PHY_P_CH (1<<3) +#define FW_PHY_P_CON (1<<2) + +#define FW_PHY_LOOPINT_REG 0x06 +#define FW_PHY_LOOPINT (1<<7) +#define FW_PHY_CPSINT_REG 0x06 +#define FW_PHY_CPSNT (1<<6) +/* +#define FW_PHY_CPS_REG 0x06 +#define FW_PHY_CPS (1<<5) +*/ +#define FW_PHY_IR_REG 0x06 +#define FW_PHY_IR (1<<4) +#define FW_PHY_C_REG 0x06 +#define FW_PHY_C (1<<0) + +#define FW_PHY_ESPD_REG 0x03 +#define FW_PHY_ESPD (7<<5) + +#define FW_PHY_EDEL_REG 0x03 +#define FW_PHY_EDEL 15<<0 diff --git a/src/add-ons/kernel/bus_managers/firewire/fwdebug.h b/src/add-ons/kernel/bus_managers/firewire/fwdebug.h new file mode 100644 index 0000000000..b7131a3bd7 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwdebug.h @@ -0,0 +1,21 @@ +/* Kernel driver for firewire + * + * Copyright (C) 2007 JiSheng Zhang . All rights reserved + * Distributed under the terms of the MIT license. + */ + +#ifndef __FWDEBUG_H +#define __FWDEBUG_H + +#include +#define DEBUG 1 + +#ifdef DEBUG + #define TRACE(a...) dprintf("firewire: " a) +#else + #define TRACE(a...) +#endif + +#define ERROR(a...) dprintf("firewire: ERROR " a) + +#endif diff --git a/src/add-ons/kernel/bus_managers/firewire/fwdma.c b/src/add-ons/kernel/bus_managers/firewire/fwdma.c new file mode 100644 index 0000000000..f5693e03b5 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwdma.c @@ -0,0 +1,233 @@ +/*- + * 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. + * + */ + +#include +#include + +#include "queue.h" +#include "fwglue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwdma.h" +#include "util.h" + +#if 0 +static void +fwdma_map_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + bus_addr_t *baddr; + + if (error) + printf("fwdma_map_cb: error=%d\n", error); + baddr = (bus_addr_t *)arg; + *baddr = segs->ds_addr; +} + +void * +fwdma_malloc(struct firewire_comm *fc, int alignment, bus_size_t size, + struct fwdma_alloc *dma, int flag) +{ + int err; + + dma->v_addr = NULL; + err = bus_dma_tag_create( + /*parent*/ fc->dmat, + /*alignment*/ alignment, + /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ size, + /*nsegments*/ 1, + /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, + /*flags*/ BUS_DMA_ALLOCNOW, +#if defined(__FreeBSD__) && __FreeBSD_version >= 501102 + /*lockfunc*/busdma_lock_mutex, + /*lockarg*/FW_GMTX(fc), +#endif + &dma->dma_tag); + if (err) { + printf("fwdma_malloc: failed(1)\n"); + return(NULL); + } + + err = bus_dmamem_alloc(dma->dma_tag, &dma->v_addr, + flag, &dma->dma_map); + if (err) { + printf("fwdma_malloc: failed(2)\n"); + /* XXX destroy tag */ + return(NULL); + } + + bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->v_addr, + size, fwdma_map_cb, &dma->bus_addr, /*flags*/0); + + return(dma->v_addr); +} +//=>alloc_mem(void **v_addr, bus_addr, uint32 protection, const char*name) + +void +fwdma_free(struct firewire_comm *fc, struct fwdma_alloc *dma) +{ + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_free(dma->dma_tag, dma->v_addr, dma->dma_map); + bus_dma_tag_destroy(dma->dma_tag); +} +//=>delete_area(area_id area) + +void * +fwdma_malloc_size(bus_dma_tag_t dmat, bus_dmamap_t *dmamap, + bus_size_t size, bus_addr_t *bus_addr, int flag) +{ + void *v_addr; + + if (bus_dmamem_alloc(dmat, &v_addr, flag, dmamap)) { + printf("fwdma_malloc_size: failed(1)\n"); + return(NULL); + } + bus_dmamap_load(dmat, *dmamap, v_addr, size, + fwdma_map_cb, bus_addr, /*flags*/0); + return(v_addr); +} +//=>alloc_mem(void **v_addr, bus_addr, uint32 protection, const char*name) + + +void +fwdma_free_size(bus_dma_tag_t dmat, bus_dmamap_t dmamap, + void *vaddr, bus_size_t size) +{ + bus_dmamap_unload(dmat, dmamap); + bus_dmamem_free(dmat, vaddr, dmamap); +} +//=>delete_area(area_id area) +#endif + +/* + * Allocate multisegment dma buffers + * each segment size is eqaul to ssize except last segment. + */ +struct fwdma_alloc_multi * +fwdma_malloc_multiseg(int alignment, + int esize, int n) +{ + struct fwdma_alloc_multi *am; + struct fwdma_seg *seg; + int ssize; + int nseg, i; + void *buf_virt, *buf_phy; + + if (esize > B_PAGE_SIZE) { + /* round up to PAGE_SIZE */ + esize = ssize = roundup2(esize, B_PAGE_SIZE); + nseg = n; + } else { + /* allocate PAGE_SIZE segment for small elements */ + /*ssize = rounddown(PAGE_SIZE, esize); + nseg = howmany(n, ssize / esize);*/ + esize = roundup2(esize, alignment); + ssize = rounddown(B_PAGE_SIZE, esize); + nseg = howmany(n, ssize / esize); + + } + am = (struct fwdma_alloc_multi *)malloc(sizeof(struct fwdma_alloc_multi) + + sizeof(struct fwdma_seg)*nseg); + if (am == NULL) { + dprintf("fwdma_malloc_multiseg: malloc failed\n"); + return(NULL); + } + am->ssize = ssize; + am->esize = esize; +// am->nseg = 0; + am->nseg = nseg; + +#if 0 + if (bus_dma_tag_create( + /*parent*/ fc->dmat, + /*alignment*/ alignment, + /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ ssize, + /*nsegments*/ 1, + /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT, + /*flags*/ BUS_DMA_ALLOCNOW, +#if defined(__FreeBSD__) && __FreeBSD_version >= 501102 + /*lockfunc*/busdma_lock_mutex, + /*lockarg*/FW_GMTX(fc), +#endif + &am->dma_tag)) { + printf("fwdma_malloc_multiseg: tag_create failed\n"); + free(am, M_FW); + return(NULL); + } +#endif + am->Area = alloc_mem(&buf_virt, &buf_phy, nseg*ssize, 0, "fw multi dma buf"); + + +/* for (seg = &am->seg[0]; nseg --; seg ++) { + seg->v_addr = fwdma_malloc_size(am->dma_tag, &seg->dma_map, + ssize, &seg->bus_addr, flag); + if (seg->v_addr == NULL) { + printf("fwdma_malloc_multi: malloc_size failed %d\n", + am->nseg); + fwdma_free_multiseg(am); + return(NULL); + } + am->nseg++; + }*/ + for(i = 0, seg=&am->seg[0]; i < nseg; i++){ + seg->v_addr =(char *)buf_virt + (i * ssize); + seg->bus_addr = (bus_addr_t)((char *)buf_phy + (i * ssize)); + seg++; + } + return(am); +} + +void +fwdma_free_multiseg(struct fwdma_alloc_multi *am) +{ +/* struct fwdma_seg *seg; + + for (seg = &am->seg[0]; am->nseg --; seg ++) { + fwdma_free_size(am->dma_tag, seg->dma_map, + seg->v_addr, am->ssize); + } + bus_dma_tag_destroy(am->dma_tag);*/ + delete_area(am->Area); + + free(am); +} diff --git a/src/add-ons/kernel/bus_managers/firewire/fwmem.c b/src/add-ons/kernel/bus_managers/firewire/fwmem.c new file mode 100644 index 0000000000..da1f927213 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwmem.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2002-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. + * + */ + +#include +#include +#include + +#include +#include "queue.h" +#include "fwglue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwmem.h" + + +static int fwmem_debug=0; + +static struct fw_xfer * +fwmem_xfer_req( + struct fw_device *fwdev, + caddr_t sc, + int spd, + int slen, + int rlen, + void *hand) +{ + struct fw_xfer *xfer; + + xfer = fw_xfer_alloc(); + if (xfer == NULL) + return NULL; + + xfer->fc = fwdev->fc; + xfer->send.hdr.mode.hdr.dst = FWLOCALBUS | fwdev->dst; + if (spd < 0) + xfer->send.spd = fwdev->speed; + else + xfer->send.spd = min(spd, fwdev->speed); + xfer->hand = hand; + xfer->sc = sc; + xfer->send.pay_len = slen; + xfer->recv.pay_len = rlen; + + return xfer; +} + +struct fw_xfer * +fwmem_read_quad( + struct fw_device *fwdev, + caddr_t sc, + uint8_t spd, + uint16_t dst_hi, + uint32_t dst_lo, + void *data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, (void *)sc, spd, 0, 4, hand); + if (xfer == NULL) { + return NULL; + } + + fp = &xfer->send.hdr; + fp->mode.rreqq.tcode = FWTCODE_RREQQ; + fp->mode.rreqq.dest_hi = dst_hi; + fp->mode.rreqq.dest_lo = dst_lo; + + xfer->send.payload = NULL; + xfer->recv.payload = (uint32_t *)data; + + if (fwmem_debug) + printf("fwmem_read_quad: %d %04x:%08x\n", fwdev->dst, + dst_hi, dst_lo); + + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_write_quad( + struct fw_device *fwdev, + caddr_t sc, + uint8_t spd, + uint16_t dst_hi, + uint32_t dst_lo, + void *data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, 0, 0, hand); + if (xfer == NULL) + return NULL; + + fp = &xfer->send.hdr; + fp->mode.wreqq.tcode = FWTCODE_WREQQ; + fp->mode.wreqq.dest_hi = dst_hi; + fp->mode.wreqq.dest_lo = dst_lo; + fp->mode.wreqq.data = *(uint32_t *)data; + + xfer->send.payload = xfer->recv.payload = NULL; + + if (fwmem_debug) + printf("fwmem_write_quad: %d %04x:%08x %08x\n", fwdev->dst, + dst_hi, dst_lo, *(uint32_t *)data); + + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_read_block( + struct fw_device *fwdev, + caddr_t sc, + uint8_t spd, + uint16_t dst_hi, + uint32_t dst_lo, + int len, + void *data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, 0, roundup2(len, 4), hand); + if (xfer == NULL) + return NULL; + + fp = &xfer->send.hdr; + fp->mode.rreqb.tcode = FWTCODE_RREQB; + fp->mode.rreqb.dest_hi = dst_hi; + fp->mode.rreqb.dest_lo = dst_lo; + fp->mode.rreqb.len = len; + fp->mode.rreqb.extcode = 0; + + xfer->send.payload = NULL; + xfer->recv.payload = data; + + if (fwmem_debug) + printf("fwmem_read_block: %d %04x:%08x %d\n", fwdev->dst, + dst_hi, dst_lo, len); + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} + +struct fw_xfer * +fwmem_write_block( + struct fw_device *fwdev, + caddr_t sc, + uint8_t spd, + uint16_t dst_hi, + uint32_t dst_lo, + int len, + void *data, + void (*hand)(struct fw_xfer *)) +{ + struct fw_xfer *xfer; + struct fw_pkt *fp; + + xfer = fwmem_xfer_req(fwdev, sc, spd, len, 0, hand); + if (xfer == NULL) + return NULL; + + fp = &xfer->send.hdr; + fp->mode.wreqb.tcode = FWTCODE_WREQB; + fp->mode.wreqb.dest_hi = dst_hi; + fp->mode.wreqb.dest_lo = dst_lo; + fp->mode.wreqb.len = len; + fp->mode.wreqb.extcode = 0; + + xfer->send.payload = data; + xfer->recv.payload = NULL; + + if (fwmem_debug) + printf("fwmem_write_block: %d %04x:%08x %d\n", fwdev->dst, + dst_hi, dst_lo, len); + if (fw_asyreq(xfer->fc, -1, xfer) == 0) + return xfer; + + fw_xfer_free(xfer); + return NULL; +} diff --git a/src/add-ons/kernel/bus_managers/firewire/fwmem.h b/src/add-ons/kernel/bus_managers/firewire/fwmem.h new file mode 100644 index 0000000000..26f6e1821c --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwmem.h @@ -0,0 +1,55 @@ +/*- + * Copyright (C) 2002-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/fwmem.h,v 1.8 2005/01/06 01:42:41 imp Exp $ + */ + +struct fw_xfer *fwmem_read_quad(struct fw_device *, caddr_t, uint8_t, + uint16_t, uint32_t, void *, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_write_quad(struct fw_device *, caddr_t, uint8_t, + uint16_t, uint32_t, void *, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_read_block(struct fw_device *, caddr_t, uint8_t, + uint16_t, uint32_t, int, void *, void (*)(struct fw_xfer *)); +struct fw_xfer *fwmem_write_block(struct fw_device *, caddr_t, uint8_t, + uint16_t, uint32_t, int, void *, void (*)(struct fw_xfer *)); + +#ifndef __HAIKU__ +d_open_t fwmem_open; +d_close_t fwmem_close; +d_ioctl_t fwmem_ioctl; +d_read_t fwmem_read; +d_write_t fwmem_write; +d_poll_t fwmem_poll; +d_mmap_t fwmem_mmap; +d_strategy_t fwmem_strategy; +#endif diff --git a/src/add-ons/kernel/bus_managers/firewire/fwohci.c b/src/add-ons/kernel/bus_managers/firewire/fwohci.c new file mode 100644 index 0000000000..79aaff08c5 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwohci.c @@ -0,0 +1,2965 @@ +/*- + * 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/fwohci.c,v 1.93 2007/06/08 09:04:30 simokawa Exp $ + * + */ + +#define FW_TASKQ_PRI 80 +#define FW_TASKQ_SIZE 8 + +#define ATRQ_CH 0 +#define ATRS_CH 1 +#define ARRQ_CH 2 +#define ARRS_CH 3 +#define ITX_CH 4 +#define IRX_CH 0x24 + +#include +#include +#include +#include +#include +#include +#include + +#include "fwglue.h" +#include "queue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" +#include "fwdma.h" +#include "fwohcireg.h" +#include "fwohcivar.h" +#include "firewire_phy.h" +#include "util.h" + +#undef OHCI_DEBUG + +static int nocyclemaster = 0; +int firewire_phydma_enable = 1; + +static char dbcode[16][0x10]={"OUTM", "OUTL","INPM","INPL", + "STOR","LOAD","NOP ","STOP",}; + +static char dbkey[8][0x10]={"ST0", "ST1","ST2","ST3", + "UNDEF","REG","SYS","DEV"}; +static char dbcond[4][0x10]={"NEV","C=1", "C=0", "ALL"}; +char fwohcicode[32][0x20]={ + "No stat","Undef","long","miss Ack err", + "FIFO underrun","FIFO overrun","desc err", "data read err", + "data write err","bus reset","timeout","tcode err", + "Undef","Undef","unknown event","flushed", + "Undef","ack complete","ack pend","Undef", + "ack busy_X","ack busy_A","ack busy_B","Undef", + "Undef","Undef","Undef","ack tardy", + "Undef","ack data_err","ack type_err",""}; + +#define MAX_SPEED 3 +extern char *linkspeed[]; +uint32_t tagbit[4] = { 1 << 28, 1 << 29, 1 << 30, 1 << 31}; + +static struct tcode_info tinfo[] = { +/* hdr_len block flag valid_response */ +/* 0 WREQQ */ {16, FWTI_REQ | FWTI_TLABEL, FWTCODE_WRES}, +/* 1 WREQB */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY, FWTCODE_WRES}, +/* 2 WRES */ {12, FWTI_RES, 0xff}, +/* 3 XXX */ { 0, 0, 0xff}, +/* 4 RREQQ */ {12, FWTI_REQ | FWTI_TLABEL, FWTCODE_RRESQ}, +/* 5 RREQB */ {16, FWTI_REQ | FWTI_TLABEL, FWTCODE_RRESB}, +/* 6 RRESQ */ {16, FWTI_RES, 0xff}, +/* 7 RRESB */ {16, FWTI_RES | FWTI_BLOCK_ASY, 0xff}, +/* 8 CYCS */ { 0, 0, 0xff}, +/* 9 LREQ */ {16, FWTI_REQ | FWTI_TLABEL | FWTI_BLOCK_ASY, FWTCODE_LRES}, +/* a STREAM */ { 4, FWTI_REQ | FWTI_BLOCK_STR, 0xff}, +/* b LRES */ {16, FWTI_RES | FWTI_BLOCK_ASY, 0xff}, +/* c XXX */ { 0, 0, 0xff}, +/* d XXX */ { 0, 0, 0xff}, +/* e PHY */ {12, FWTI_REQ, 0xff}, +/* f XXX */ { 0, 0, 0xff} +}; + +#define OHCI_WRITE_SIGMASK 0xffff0000 +#define OHCI_READ_SIGMASK 0xffff0000 + +static void fwohci_ibr (struct firewire_comm *); +static void fwohci_db_init (struct fwohci_softc *, struct fwohci_dbch *); +static void fwohci_db_free (struct fwohci_dbch *); +static void fwohci_arcv (struct fwohci_softc *, struct fwohci_dbch *, int); +static void fwohci_txd (struct fwohci_softc *, struct fwohci_dbch *); +static void fwohci_start_atq (struct firewire_comm *); +static void fwohci_start_ats (struct firewire_comm *); +static void fwohci_start (struct fwohci_softc *, struct fwohci_dbch *); +static uint32_t fwphy_wrdata ( struct fwohci_softc *, uint32_t, uint32_t); +static uint32_t fwphy_rddata ( struct fwohci_softc *, uint32_t); +static int fwohci_rx_enable (struct fwohci_softc *, struct fwohci_dbch *); +static int fwohci_tx_enable (struct fwohci_softc *, struct fwohci_dbch *); +static int fwohci_irx_enable (struct firewire_comm *, int); +static int fwohci_irx_disable (struct firewire_comm *, int); +#if BYTE_ORDER == BIG_ENDIAN +static void fwohci_irx_post (struct firewire_comm *, uint32_t *); +#endif +static int fwohci_itxbuf_enable (struct firewire_comm *, int); +static int fwohci_itx_disable (struct firewire_comm *, int); +static void fwohci_timeout (void *); +static void fwohci_set_intr (struct firewire_comm *, int); + +static int fwohci_add_rx_buf (struct fwohci_dbch *, struct fwohcidb_tr *, int, struct fwdma_alloc *); +static int fwohci_add_tx_buf (struct fwohci_dbch *, struct fwohcidb_tr *, int); +static void dump_db (struct fwohci_softc *, uint32_t); +static void print_db (struct fwohcidb_tr *, struct fwohcidb *, uint32_t , uint32_t); +static void dump_dma (struct fwohci_softc *, uint32_t); +static uint32_t fwohci_cyctimer (struct firewire_comm *); +static void fwohci_rbuf_update (struct fwohci_softc *, int); +static void fwohci_tbuf_update (struct fwohci_softc *, int); +void fwohci_txbufdb (struct fwohci_softc *, int , struct fw_bulkxfer *); +static void fwohci_task_busreset(void *); +static void fwohci_task_sid(void *); +static void fwohci_task_dma(void *); + +/* + * memory allocated for DMA programs + */ +#define DMA_PROG_ALLOC (8 * PAGE_SIZE) + +#define NDB FWMAXQUEUE + +#define OHCI_VERSION 0x00 +#define OHCI_ATRETRY 0x08 +#define OHCI_CROMHDR 0x18 +#define OHCI_BUS_OPT 0x20 +#define OHCI_BUSIRMC (1 << 31) +#define OHCI_BUSCMC (1 << 30) +#define OHCI_BUSISC (1 << 29) +#define OHCI_BUSBMC (1 << 28) +#define OHCI_BUSPMC (1 << 27) +#define OHCI_BUSFNC OHCI_BUSIRMC | OHCI_BUSCMC | OHCI_BUSISC |\ + OHCI_BUSBMC | OHCI_BUSPMC + +#define OHCI_EUID_HI 0x24 +#define OHCI_EUID_LO 0x28 + +#define OHCI_CROMPTR 0x34 +#define OHCI_HCCCTL 0x50 +#define OHCI_HCCCTLCLR 0x54 +#define OHCI_AREQHI 0x100 +#define OHCI_AREQHICLR 0x104 +#define OHCI_AREQLO 0x108 +#define OHCI_AREQLOCLR 0x10c +#define OHCI_PREQHI 0x110 +#define OHCI_PREQHICLR 0x114 +#define OHCI_PREQLO 0x118 +#define OHCI_PREQLOCLR 0x11c +#define OHCI_PREQUPPER 0x120 + +#define OHCI_SID_BUF 0x64 +#define OHCI_SID_CNT 0x68 +#define OHCI_SID_ERR (1 << 31) +#define OHCI_SID_CNT_MASK 0xffc + +#define OHCI_IT_STAT 0x90 +#define OHCI_IT_STATCLR 0x94 +#define OHCI_IT_MASK 0x98 +#define OHCI_IT_MASKCLR 0x9c + +#define OHCI_IR_STAT 0xa0 +#define OHCI_IR_STATCLR 0xa4 +#define OHCI_IR_MASK 0xa8 +#define OHCI_IR_MASKCLR 0xac + +#define OHCI_LNKCTL 0xe0 +#define OHCI_LNKCTLCLR 0xe4 + +#define OHCI_PHYACCESS 0xec +#define OHCI_CYCLETIMER 0xf0 + +#define OHCI_DMACTL(off) (off) +#define OHCI_DMACTLCLR(off) (off + 4) +#define OHCI_DMACMD(off) (off + 0xc) +#define OHCI_DMAMATCH(off) (off + 0x10) + +#define OHCI_ATQOFF 0x180 +#define OHCI_ATQCTL OHCI_ATQOFF +#define OHCI_ATQCTLCLR (OHCI_ATQOFF + 4) +#define OHCI_ATQCMD (OHCI_ATQOFF + 0xc) +#define OHCI_ATQMATCH (OHCI_ATQOFF + 0x10) + +#define OHCI_ATSOFF 0x1a0 +#define OHCI_ATSCTL OHCI_ATSOFF +#define OHCI_ATSCTLCLR (OHCI_ATSOFF + 4) +#define OHCI_ATSCMD (OHCI_ATSOFF + 0xc) +#define OHCI_ATSMATCH (OHCI_ATSOFF + 0x10) + +#define OHCI_ARQOFF 0x1c0 +#define OHCI_ARQCTL OHCI_ARQOFF +#define OHCI_ARQCTLCLR (OHCI_ARQOFF + 4) +#define OHCI_ARQCMD (OHCI_ARQOFF + 0xc) +#define OHCI_ARQMATCH (OHCI_ARQOFF + 0x10) + +#define OHCI_ARSOFF 0x1e0 +#define OHCI_ARSCTL OHCI_ARSOFF +#define OHCI_ARSCTLCLR (OHCI_ARSOFF + 4) +#define OHCI_ARSCMD (OHCI_ARSOFF + 0xc) +#define OHCI_ARSMATCH (OHCI_ARSOFF + 0x10) + +#define OHCI_ITOFF(CH) (0x200 + 0x10 * (CH)) +#define OHCI_ITCTL(CH) (OHCI_ITOFF(CH)) +#define OHCI_ITCTLCLR(CH) (OHCI_ITOFF(CH) + 4) +#define OHCI_ITCMD(CH) (OHCI_ITOFF(CH) + 0xc) + +#define OHCI_IROFF(CH) (0x400 + 0x20 * (CH)) +#define OHCI_IRCTL(CH) (OHCI_IROFF(CH)) +#define OHCI_IRCTLCLR(CH) (OHCI_IROFF(CH) + 4) +#define OHCI_IRCMD(CH) (OHCI_IROFF(CH) + 0xc) +#define OHCI_IRMATCH(CH) (OHCI_IROFF(CH) + 0x10) + +/* + * Communication with PHY device + */ +/* XXX need lock for phy access */ +static uint32_t +fwphy_wrdata( struct fwohci_softc *sc, uint32_t addr, uint32_t data) +{ + uint32_t fun; + + addr &= 0xf; + data &= 0xff; + + fun = (PHYDEV_WRCMD | (addr << PHYDEV_REGADDR) | (data << PHYDEV_WRDATA)); + OWRITE(sc, OHCI_PHYACCESS, fun); + DELAY(100); + + return(fwphy_rddata( sc, addr)); +} + +static uint32_t +fwohci_set_bus_manager(struct firewire_comm *fc, u_int node) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int i; + uint32_t bm; + +#define OHCI_CSR_DATA 0x0c +#define OHCI_CSR_COMP 0x10 +#define OHCI_CSR_CONT 0x14 +#define OHCI_BUS_MANAGER_ID 0 + + OWRITE(sc, OHCI_CSR_DATA, node); + OWRITE(sc, OHCI_CSR_COMP, 0x3f); + OWRITE(sc, OHCI_CSR_CONT, OHCI_BUS_MANAGER_ID); + for (i = 0; !(OREAD(sc, OHCI_CSR_CONT) & (1<<31)) && (i < 1000); i++) + DELAY(10); + bm = OREAD(sc, OHCI_CSR_DATA); + if((bm & 0x3f) == 0x3f) + bm = node; + if (firewire_debug) + device_printf(sc->fc.dev, + "fw_set_bus_manager: %d->%d (loop=%d)\n", bm, node, i); + + return(bm); +} + +static uint32_t +fwphy_rddata(struct fwohci_softc *sc, uint32_t addr) +{ + uint32_t fun, stat; + u_int i, retry = 0; + + addr &= 0xf; +#define MAX_RETRY 100 +again: + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_REG_FAIL); + fun = PHYDEV_RDCMD | (addr << PHYDEV_REGADDR); + OWRITE(sc, OHCI_PHYACCESS, fun); + for ( i = 0 ; i < MAX_RETRY ; i ++ ){ + fun = OREAD(sc, OHCI_PHYACCESS); + if ((fun & PHYDEV_RDCMD) == 0 && (fun & PHYDEV_RDDONE) != 0) + break; + DELAY(100); + } + if(i >= MAX_RETRY) { + if (firewire_debug) + device_printf(sc->fc.dev, "phy read failed(1).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + /* Make sure that SCLK is started */ + stat = OREAD(sc, FWOHCI_INTSTAT); + if ((stat & OHCI_INT_REG_FAIL) != 0 || + ((fun >> PHYDEV_REGADDR) & 0xf) != addr) { + if (firewire_debug) + device_printf(sc->fc.dev, "phy read failed(2).\n"); + if (++retry < MAX_RETRY) { + DELAY(100); + goto again; + } + } + if (firewire_debug || retry >= MAX_RETRY) + device_printf(sc->fc.dev, + "fwphy_rddata: 0x%x loop=%d, retry=%d\n", addr, i, retry); +#undef MAX_RETRY + return((fun >> PHYDEV_RDDATA )& 0xff); +} +/* Device specific ioctl. */ +int +fwohci_ioctl (void *cookie, u_long cmd, void *data, size_t len) +{ +// struct firewire_softc *sc; +// int unit = DEV2UNIT(dev); + struct fwohci_softc *fc; + int err = 0; + struct fw_reg_req_t *reg = (struct fw_reg_req_t *) data; + uint32_t *dmach = (uint32_t *) data; + +/* sc = devclass_get_softc(firewire_devclass, unit); + if(sc == NULL){ + return(EINVAL); + } + fc = (struct fwohci_softc *)sc->fc;*/ + fc = (struct fwohci_softc *)cookie;//our cookie points to sc->fc not sc see fwraw.c + + if (!data) + return(EINVAL); + + switch (cmd) { + case FWOHCI_WRREG: +#define OHCI_MAX_REG 0x800 + if(reg->addr <= OHCI_MAX_REG){ + OWRITE(fc, reg->addr, reg->data); + reg->data = OREAD(fc, reg->addr); + }else{ + err = EINVAL; + } + break; + case FWOHCI_RDREG: + if(reg->addr <= OHCI_MAX_REG){ + reg->data = OREAD(fc, reg->addr); + }else{ + err = EINVAL; + } + break; +/* Read DMA descriptors for debug */ + case DUMPDMA: + if(*dmach <= OHCI_MAX_DMA_CH ){ + dump_dma(fc, *dmach); + dump_db(fc, *dmach); + }else{ + err = EINVAL; + } + break; +/* Read/Write Phy registers */ +#define OHCI_MAX_PHY_REG 0xf + case FWOHCI_RDPHYREG: + if (reg->addr <= OHCI_MAX_PHY_REG) + reg->data = fwphy_rddata(fc, reg->addr); + else + err = EINVAL; + break; + case FWOHCI_WRPHYREG: + if (reg->addr <= OHCI_MAX_PHY_REG) + reg->data = fwphy_wrdata(fc, reg->addr, reg->data); + else + err = EINVAL; + break; + default: + err = EINVAL; + break; + } + return err; +} + +static int +fwohci_probe_phy(struct fwohci_softc *sc) +{ + uint32_t reg, reg2; + int e1394a = 1; +/* + * probe PHY parameters + * 0. to prove PHY version, whether compliance of 1394a. + * 1. to probe maximum speed supported by the PHY and + * number of port supported by core-logic. + * It is not actually available port on your PC . + */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS); + DELAY(500); + + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + + if((reg >> 5) != 7 ){ + sc->fc.mode &= ~FWPHYASYST; + sc->fc.nport = reg & FW_PHY_NP; + sc->fc.speed = reg & FW_PHY_SPD >> 6; + if (sc->fc.speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + sc->fc.speed, MAX_SPEED); + sc->fc.speed = MAX_SPEED; + } + device_printf(dev, "Phy 1394 only %s, %d ports.\n", + linkspeed[sc->fc.speed], sc->fc.nport); + }else{ + reg2 = fwphy_rddata(sc, FW_PHY_ESPD_REG); + sc->fc.mode |= FWPHYASYST; + sc->fc.nport = reg & FW_PHY_NP; + sc->fc.speed = (reg2 & FW_PHY_ESPD) >> 5; + if (sc->fc.speed > MAX_SPEED) { + device_printf(dev, "invalid speed %d (fixed to %d).\n", + sc->fc.speed, MAX_SPEED); + sc->fc.speed = MAX_SPEED; + } + device_printf(dev, + "Phy 1394a available %s, %d ports.\n", + linkspeed[sc->fc.speed], sc->fc.nport); + + /* check programPhyEnable */ + reg2 = fwphy_rddata(sc, 5); +#if 0 + if (e1394a && (OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_PRPHY)) { +#else /* XXX force to enable 1394a */ + if (e1394a) { +#endif + if (firewire_debug) + device_printf(dev, + "Enable 1394a Enhancements\n"); + /* enable EAA EMC */ + reg2 |= 0x03; + /* set aPhyEnhanceEnable */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_PHYEN); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_PRPHY); + } else { + /* for safe */ + reg2 &= ~0x83; + } + reg2 = fwphy_wrdata(sc, 5, reg2); + } + + reg = fwphy_rddata(sc, FW_PHY_SPD_REG); + if((reg >> 5) == 7 ){ + reg = fwphy_rddata(sc, 4); + reg |= 1 << 6; + fwphy_wrdata(sc, 4, reg); + reg = fwphy_rddata(sc, 4); + } + return 0; +} + + +void +fwohci_reset(struct fwohci_softc *sc) +{ + int i, max_rec, speed; + uint32_t reg, reg2; + struct fwohcidb_tr *db_tr; + + /* Disable interrupts */ + OWRITE(sc, FWOHCI_INTMASKCLR, ~0); + + /* Now stopping all DMA channels */ + OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + + OWRITE(sc, OHCI_IR_MASKCLR, ~0); + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); + } + + /* FLUSH FIFO and reset Transmitter/Reciever */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); + if (firewire_debug) + device_printf(dev, + "resetting OHCI..."); + i = 0; + while(OREAD(sc, OHCI_HCCCTL) & OHCI_HCC_RESET) { + if (i++ > 100) break; + DELAY(1000); + } + if (firewire_debug) + printf("done (loop=%d)\n", i); + + /* Probe phy */ + //fwohci_probe_phy(sc, dev); + fwohci_probe_phy(sc); + + /* Probe link */ + reg = OREAD(sc, OHCI_BUS_OPT); + reg2 = reg | OHCI_BUSFNC; + max_rec = (reg & 0x0000f000) >> 12; + speed = (reg & 0x00000007); + device_printf(dev, "Link %s, max_rec %d bytes.\n", + linkspeed[speed], MAXREC(max_rec)); + /* XXX fix max_rec */ + sc->fc.maxrec = sc->fc.speed + 8; + if (max_rec != sc->fc.maxrec) { + reg2 = (reg2 & 0xffff0fff) | (sc->fc.maxrec << 12); + device_printf(dev, "max_rec %d -> %d\n", + MAXREC(max_rec), MAXREC(sc->fc.maxrec)); + } + if (firewire_debug) + device_printf(dev, "BUS_OPT 0x%x -> 0x%x\n", reg, reg2); + OWRITE(sc, OHCI_BUS_OPT, reg2); + + /* Initialize registers */ + OWRITE(sc, OHCI_CROMHDR, sc->fc.config_rom[0]); + OWRITE(sc, OHCI_CROMPTR, sc->crom_dma.bus_addr); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_BIGEND); + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_POSTWR); + OWRITE(sc, OHCI_SID_BUF, sc->sid_dma.bus_addr); + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_SID); + + /* Enable link */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LINKEN); + + /* Force to start async RX DMA */ + sc->arrq.xferq.flag &= ~FWXFERQ_RUNNING; + sc->arrs.xferq.flag &= ~FWXFERQ_RUNNING; + fwohci_rx_enable(sc, &sc->arrq); + fwohci_rx_enable(sc, &sc->arrs); + + /* Initialize async TX */ + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN | OHCI_CNTL_DMA_DEAD); + + /* AT Retries */ + OWRITE(sc, FWOHCI_RETRY, + /* CycleLimit PhyRespRetries ATRespRetries ATReqRetries */ + (0xffff << 16 ) | (0x0f << 8) | (0x0f << 4) | 0x0f) ; + + sc->atrq.top = STAILQ_FIRST(&sc->atrq.db_trq); + sc->atrs.top = STAILQ_FIRST(&sc->atrs.db_trq); + sc->atrq.bottom = sc->atrq.top; + sc->atrs.bottom = sc->atrs.top; + + for( i = 0, db_tr = sc->atrq.top; i < sc->atrq.ndb ; + i ++, db_tr = STAILQ_NEXT(db_tr, link)){ + db_tr->xfer = NULL; + } + for( i = 0, db_tr = sc->atrs.top; i < sc->atrs.ndb ; + i ++, db_tr = STAILQ_NEXT(db_tr, link)){ + db_tr->xfer = NULL; + } + + + /* Enable interrupts */ + sc->intmask = (OHCI_INT_ERR | OHCI_INT_PHY_SID + | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS + | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS + | OHCI_INT_PHY_BUS_R | OHCI_INT_PW_ERR); + sc->intmask |= OHCI_INT_DMA_IR | OHCI_INT_DMA_IT; + sc->intmask |= OHCI_INT_CYC_LOST | OHCI_INT_PHY_INT; + OWRITE(sc, FWOHCI_INTMASK, sc->intmask); + fwohci_set_intr(&sc->fc, 1); + +} + +int +fwohci_init(struct fwohci_softc *sc) +{ + int i, mver; + uint32_t reg; + uint8_t ui[8]; + void *buf_virt, *buf_phy; +/* OHCI version */ + reg = OREAD(sc, OHCI_VERSION); + mver = (reg >> 16) & 0xff; + device_printf(dev, "OHCI version %x.%x (ROM=%d)\n", + mver, reg & 0xff, (reg>>24) & 1); + if (mver < 1 || mver > 9) { + device_printf(dev, "invalid OHCI version\n"); + return (ENXIO); + } + +/* Available Isochronous DMA channel probe */ + OWRITE(sc, OHCI_IT_MASK, 0xffffffff); + OWRITE(sc, OHCI_IR_MASK, 0xffffffff); + reg = OREAD(sc, OHCI_IT_MASK) & OREAD(sc, OHCI_IR_MASK); + OWRITE(sc, OHCI_IT_MASKCLR, 0xffffffff); + OWRITE(sc, OHCI_IR_MASKCLR, 0xffffffff); + for (i = 0; i < 0x20; i++) + if ((reg & (1 << i)) == 0) + break; + sc->fc.nisodma = i; + device_printf(dev, "No. of Isochronous channels is %d.\n", i); + if (i == 0) + return (ENXIO); + + sc->fc.arq = &sc->arrq.xferq; + sc->fc.ars = &sc->arrs.xferq; + sc->fc.atq = &sc->atrq.xferq; + sc->fc.ats = &sc->atrs.xferq; + + sc->arrq.xferq.psize = roundup2(FWPMAX_S400, B_PAGE_SIZE); + sc->arrs.xferq.psize = roundup2(FWPMAX_S400, B_PAGE_SIZE); + sc->atrq.xferq.psize = roundup2(FWPMAX_S400, B_PAGE_SIZE); + sc->atrs.xferq.psize = roundup2(FWPMAX_S400, B_PAGE_SIZE); + + sc->arrq.xferq.start = NULL; + sc->arrs.xferq.start = NULL; + sc->atrq.xferq.start = fwohci_start_atq; + sc->atrs.xferq.start = fwohci_start_ats; + + sc->arrq.xferq.buf = NULL; + sc->arrs.xferq.buf = NULL; + sc->atrq.xferq.buf = NULL; + sc->atrs.xferq.buf = NULL; + + sc->arrq.xferq.dmach = -1; + sc->arrs.xferq.dmach = -1; + sc->atrq.xferq.dmach = -1; + sc->atrs.xferq.dmach = -1; + + sc->arrq.ndesc = 1; + sc->arrs.ndesc = 1; + sc->atrq.ndesc = 8; /* equal to maximum of mbuf chains */ + sc->atrs.ndesc = 2; + + sc->arrq.ndb = NDB; + sc->arrs.ndb = NDB / 2; + sc->atrq.ndb = NDB; + sc->atrs.ndb = NDB / 2; + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + sc->fc.it[i] = &sc->it[i].xferq; + sc->fc.ir[i] = &sc->ir[i].xferq; + sc->it[i].xferq.dmach = i; + sc->ir[i].xferq.dmach = i; + sc->it[i].ndb = 0; + sc->ir[i].ndb = 0; + } + + sc->fc.tcode = tinfo; +// sc->fc.dev = dev; + +/* sc->fc.config_rom = fwdma_malloc(&sc->fc, CROMSIZE, CROMSIZE, + &sc->crom_dma, BUS_DMA_WAITOK); + if(sc->fc.config_rom == NULL){ + device_printf(dev, "config_rom alloc failed."); + return ENOMEM; + }*/ + +#if 0 + bzero(&sc->fc.config_rom[0], CROMSIZE); + sc->fc.config_rom[1] = 0x31333934; + sc->fc.config_rom[2] = 0xf000a002; + sc->fc.config_rom[3] = OREAD(sc, OHCI_EUID_HI); + sc->fc.config_rom[4] = OREAD(sc, OHCI_EUID_LO); + sc->fc.config_rom[5] = 0; + sc->fc.config_rom[0] = (4 << 24) | (5 << 16); + + sc->fc.config_rom[0] |= fw_crc16(&sc->fc.config_rom[1], 5*4); +#endif + + +/* SID recieve buffer must align 2^11 */ +#define OHCI_SIDSIZE (1 << 11) + /*sc->sid_buf = fwdma_malloc(&sc->fc, OHCI_SIDSIZE, OHCI_SIDSIZE, + &sc->sid_dma, BUS_DMA_WAITOK); + if (sc->sid_buf == NULL) { + device_printf(dev, "sid_buf alloc failed."); + return ENOMEM; + } + + fwdma_malloc(&sc->fc, sizeof(uint32_t), sizeof(uint32_t), + &sc->dummy_dma, BUS_DMA_WAITOK); + + if (sc->dummy_dma.v_addr == NULL) { + device_printf(dev, "dummy_dma alloc failed."); + return ENOMEM; + }*/ + //crom_sid_Area means config rom and sid buf etc. + sc->fc.crom_sid_Area = alloc_mem(&buf_virt, &buf_phy, CROMSIZE+OHCI_SIDSIZE+sizeof(uint32_t), 0, "fwohci config etc. buf"); + if (sc->fc.crom_sid_Area < B_OK) + return B_NO_MEMORY; + sc->sid_buf = sc->sid_dma.v_addr = buf_virt; + sc->sid_dma.bus_addr = (bus_addr_t)buf_phy; + sc->fc.config_rom = sc->crom_dma.v_addr = buf_virt + OHCI_SIDSIZE; + sc->crom_dma.bus_addr = (bus_addr_t)((char *)buf_phy + OHCI_SIDSIZE); + sc->dummy_dma.v_addr = (char *)buf_virt + OHCI_SIDSIZE + sizeof(uint32_t); + sc->dummy_dma.bus_addr = (bus_addr_t)((char *)buf_phy + + OHCI_SIDSIZE + sizeof(uint32_t)); + + fwohci_db_init(sc, &sc->arrq); + if ((sc->arrq.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->arrs); + if ((sc->arrs.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->atrq); + if ((sc->atrq.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + fwohci_db_init(sc, &sc->atrs); + if ((sc->atrs.flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + sc->fc.eui.hi = OREAD(sc, FWOHCIGUID_H); + sc->fc.eui.lo = OREAD(sc, FWOHCIGUID_L); + for( i = 0 ; i < 8 ; i ++) + ui[i] = FW_EUI64_BYTE(&sc->fc.eui,i); + device_printf(dev, "EUI64 %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + ui[0], ui[1], ui[2], ui[3], ui[4], ui[5], ui[6], ui[7]); + + sc->fc.ioctl = fwohci_ioctl; + sc->fc.cyctimer = fwohci_cyctimer; + sc->fc.set_bmr = fwohci_set_bus_manager; + sc->fc.ibr = fwohci_ibr; + sc->fc.irx_enable = fwohci_irx_enable; + sc->fc.irx_disable = fwohci_irx_disable; + + sc->fc.itx_enable = fwohci_itxbuf_enable; + sc->fc.itx_disable = fwohci_itx_disable; +#if BYTE_ORDER == BIG_ENDIAN + sc->fc.irx_post = fwohci_irx_post; +#else + sc->fc.irx_post = NULL; +#endif + sc->fc.itx_post = NULL; + sc->fc.timeout = fwohci_timeout; + sc->fc.poll = fwohci_poll; + sc->fc.set_intr = fwohci_set_intr; + + sc->intmask = sc->irstat = sc->itstat = 0; + + /* Init task queue */ + /* + sc->fc.taskqueue = taskqueue_create_fast("fw_taskq", M_WAITOK, + taskqueue_thread_enqueue, &sc->fc.taskqueue); + taskqueue_start_threads(&sc->fc.taskqueue, 1, PI_NET, "fw%d_taskq", + device_get_unit(dev)); + TASK_INIT(&sc->fwohci_task_busreset, 2, fwohci_task_busreset, sc); + TASK_INIT(&sc->fwohci_task_sid, 1, fwohci_task_sid, sc); + TASK_INIT(&sc->fwohci_task_dma, 0, fwohci_task_dma, sc);*/ + sc->fc.taskqueue = gDpc->new_dpc_queue("fw_taskq", FW_TASKQ_PRI, FW_TASKQ_SIZE); + + fw_init(&sc->fc); + fwohci_reset(sc); + + return 0; +} + +void +fwohci_timeout(void *arg) +{ + struct fwohci_softc *sc; + + sc = (struct fwohci_softc *)arg; +} + +uint32_t +fwohci_cyctimer(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + return(OREAD(sc, OHCI_CYCLETIMER)); +} + +int +fwohci_detach(struct fwohci_softc *sc) +{ + int i; + +/* if (sc->sid_buf != NULL) + fwdma_free(&sc->fc, &sc->sid_dma); + if (sc->fc.config_rom != NULL) + fwdma_free(&sc->fc, &sc->crom_dma);*/ + delete_area(sc->fc.crom_sid_Area); + + fwohci_db_free(&sc->arrq); + fwohci_db_free(&sc->arrs); + + fwohci_db_free(&sc->atrq); + fwohci_db_free(&sc->atrs); + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + delete_sem(sc->it[i].xferq.Sem); + delete_sem(sc->ir[i].xferq.Sem); + fwohci_db_free(&sc->it[i]); + fwohci_db_free(&sc->ir[i]); + } + if (sc->fc.taskqueue != NULL) { +/* taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_busreset); + taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_sid); + taskqueue_drain(sc->fc.taskqueue, &sc->fwohci_task_dma); + taskqueue_drain(sc->fc.taskqueue, &sc->fc.task_timeout); + taskqueue_free(sc->fc.taskqueue);*/ + gDpc->delete_dpc_queue(sc->fc.taskqueue); + + sc->fc.taskqueue = NULL; + } + + return 0; +} + +#define LAST_DB(dbtr, db) do { \ + struct fwohcidb_tr *_dbtr = (dbtr); \ + int _cnt = _dbtr->dbcnt; \ + db = &_dbtr->db[ (_cnt > 2) ? (_cnt -1) : 0]; \ +} while (0) + +static void +fwohci_execute_db(void *arg, struct send_recv *info) +{ + struct fwohcidb_tr *db_tr; + struct fwohcidb *db; + uint16_t step = MIN(info->pay_len, MAX_REQCOUNT); + bus_addr_t bus_addr = info->bus_addr; + int j = info->pay_len / step; + int i; + + db_tr = (struct fwohcidb_tr *)arg; + db = &db_tr->db[db_tr->dbcnt]; + + for (i = 0; i < j; i++) { + FWOHCI_DMA_WRITE(db->db.desc.addr, bus_addr); + FWOHCI_DMA_WRITE(db->db.desc.cmd, step); + FWOHCI_DMA_WRITE(db->db.desc.res, 0); + bus_addr += step; + db++; + db_tr->dbcnt++; + } + if(info->pay_len % step){ + FWOHCI_DMA_WRITE(db->db.desc.addr, bus_addr); + FWOHCI_DMA_WRITE(db->db.desc.cmd, info->pay_len % step); + FWOHCI_DMA_WRITE(db->db.desc.res, 0); + db++; + db_tr->dbcnt++; + } +} +/* +static void +fwohci_execute_db2(void *arg, bus_dma_segment_t *segs, int nseg, + bus_size_t size, int error) +{ + fwohci_execute_db(arg, segs, nseg, error); +}*/ + +static void +fwohci_start(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int i, s; + int tcode, hdr_len, pl_off; + int fsegment = -1; + uint32_t off; + struct fw_xfer *xfer; + struct fw_pkt *fp; + struct fwohci_txpkthdr *ohcifp; + struct fwohcidb_tr *db_tr; + struct fwohcidb *db; + uint32_t *ld; + struct tcode_info *info; + static int maxdesc=0; + + FW_GLOCK_ASSERT(&sc->fc); + + if(&sc->atrq == dbch){ + off = OHCI_ATQOFF; + }else if(&sc->atrs == dbch){ + off = OHCI_ATSOFF; + }else{ + return; + } + + if (dbch->flags & FWOHCI_DBCH_FULL) + return; + + s = splfw(); + db_tr = dbch->top; +txloop: + xfer = STAILQ_FIRST(&dbch->xferq.q); + if(xfer == NULL){ + goto kick; + } +#if 0 + if(dbch->xferq.queued == 0 ){ + device_printf(sc->fc.dev, "TX queue empty\n"); + } +#endif + STAILQ_REMOVE_HEAD(&dbch->xferq.q, link); + db_tr->xfer = xfer; + xfer->flag = FWXF_START; + + fp = &xfer->send.hdr; + tcode = fp->mode.common.tcode; + + ohcifp = (struct fwohci_txpkthdr *) db_tr->db[1].db.immed; + info = &tinfo[tcode]; + hdr_len = pl_off = info->hdr_len; + + ld = &ohcifp->mode.ld[0]; + ld[0] = ld[1] = ld[2] = ld[3] = 0; + for( i = 0 ; i < pl_off ; i+= 4) + ld[i/4] = fp->mode.ld[i/4]; + + ohcifp->mode.common.spd = xfer->send.spd & 0x7; + if (tcode == FWTCODE_STREAM ){ + hdr_len = 8; + ohcifp->mode.stream.len = fp->mode.stream.len; + } else if (tcode == FWTCODE_PHY) { + hdr_len = 12; + ld[1] = fp->mode.ld[1]; + ld[2] = fp->mode.ld[2]; + ohcifp->mode.common.spd = 0; + ohcifp->mode.common.tcode = FWOHCITCODE_PHY; + } else { + ohcifp->mode.asycomm.dst = fp->mode.hdr.dst; + ohcifp->mode.asycomm.srcbus = OHCI_ASYSRCBUS; + ohcifp->mode.asycomm.tlrt |= FWRETRY_X; + } + db = &db_tr->db[0]; + FWOHCI_DMA_WRITE(db->db.desc.cmd, + OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | hdr_len); + FWOHCI_DMA_WRITE(db->db.desc.addr, 0); + FWOHCI_DMA_WRITE(db->db.desc.res, 0); +/* Specify bound timer of asy. responce */ + if(&sc->atrs == dbch){ + FWOHCI_DMA_WRITE(db->db.desc.res, + (OREAD(sc, OHCI_CYCLETIMER) >> 12) + (1 << 13)); + } +#if BYTE_ORDER == BIG_ENDIAN + if (tcode == FWTCODE_WREQQ || tcode == FWTCODE_RRESQ) + hdr_len = 12; + for (i = 0; i < hdr_len/4; i ++) + FWOHCI_DMA_WRITE(ld[i], ld[i]); +#endif + +//again: + db_tr->dbcnt = 2; + db = &db_tr->db[db_tr->dbcnt]; + if (xfer->send.pay_len > 0) { + /* handle payload */ + fwohci_execute_db(db_tr, &xfer->send); +#if 0 + if (xfer->mbuf == NULL) { + err = bus_dmamap_load(dbch->dmat, db_tr->dma_map, + &xfer->send.payload[0], xfer->send.pay_len, + fwohci_execute_db, db_tr, + /*flags*/0); + } else { + /* XXX we can handle only 6 (=8-2) mbuf chains */ + err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, + xfer->mbuf, + fwohci_execute_db2, db_tr, + /* flags */0); + if (err == EFBIG) { + struct mbuf *m0; + + if (firewire_debug) + device_printf(sc->fc.dev, "EFBIG.\n"); + m0 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (m0 != NULL) { + m_copydata(xfer->mbuf, 0, + xfer->mbuf->m_pkthdr.len, + mtod(m0, caddr_t)); + m0->m_len = m0->m_pkthdr.len = + xfer->mbuf->m_pkthdr.len; + m_freem(xfer->mbuf); + xfer->mbuf = m0; + goto again; + } + device_printf(sc->fc.dev, "m_getcl failed.\n"); + } + }//only use in ip over 1394 + if (err) + printf("dmamap_load: err=%d\n", err); + bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_PREWRITE); +#endif +#if 0 /* OHCI_OUTPUT_MODE == 0 */ + for (i = 2; i < db_tr->dbcnt; i++) + FWOHCI_DMA_SET(db_tr->db[i].db.desc.cmd, + OHCI_OUTPUT_MORE); +#endif + } + if (maxdesc < db_tr->dbcnt) { + maxdesc = db_tr->dbcnt; + if (firewire_debug) + printf("maxdesc: %d\n", maxdesc); + } + /* last db */ + LAST_DB(db_tr, db); + FWOHCI_DMA_SET(db->db.desc.cmd, + OHCI_OUTPUT_LAST | OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); + FWOHCI_DMA_WRITE(db->db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr); + + if(fsegment == -1 ) + fsegment = db_tr->dbcnt; + if (dbch->pdb_tr != NULL) { + LAST_DB(dbch->pdb_tr, db); + FWOHCI_DMA_SET(db->db.desc.depend, db_tr->dbcnt); + } + dbch->xferq.queued ++; + dbch->pdb_tr = db_tr; + db_tr = STAILQ_NEXT(db_tr, link); + if(db_tr != dbch->bottom){ + goto txloop; + } else { + device_printf(sc->fc.dev, "fwohci_start: lack of db_trq\n"); + dbch->flags |= FWOHCI_DBCH_FULL; + } +kick: + /* kick asy q */ +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + + if(dbch->xferq.flag & FWXFERQ_RUNNING) { + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); + } else { + if (firewire_debug) + device_printf(sc->fc.dev, "start AT DMA status=%x\n", + OREAD(sc, OHCI_DMACTL(off))); + OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | fsegment); + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); + dbch->xferq.flag |= FWXFERQ_RUNNING; + } + + dbch->top = db_tr; + splx(s); + return; +} + +static void +fwohci_start_atq(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + FW_GLOCK(&sc->fc); + fwohci_start( sc, &(sc->atrq)); + FW_GUNLOCK(&sc->fc); + return; +} + +static void +fwohci_start_ats(struct firewire_comm *fc) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + FW_GLOCK(&sc->fc); + fwohci_start( sc, &(sc->atrs)); + FW_GUNLOCK(&sc->fc); + return; +} + +void +fwohci_txd(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int s, ch, err = 0; + struct fwohcidb_tr *tr; + struct fwohcidb *db; + struct fw_xfer *xfer; + uint32_t off; + u_int stat, status; + int packets; + struct firewire_comm *fc = (struct firewire_comm *)sc; + + if(&sc->atrq == dbch){ + off = OHCI_ATQOFF; + ch = ATRQ_CH; + }else if(&sc->atrs == dbch){ + off = OHCI_ATSOFF; + ch = ATRS_CH; + }else{ + return; + } + s = splfw(); + tr = dbch->bottom; + packets = 0; +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); + while(dbch->xferq.queued > 0){ + LAST_DB(tr, db); + status = FWOHCI_DMA_READ(db->db.desc.res) >> OHCI_STATUS_SHIFT; + if(!(status & OHCI_CNTL_DMA_ACTIVE)){ + if (fc->status != FWBUSINIT) + /* maybe out of order?? */ + goto out; + } +/* bus_dmamap_sync(dbch->dmat, tr->dma_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dbch->dmat, tr->dma_map);*/ +#if 1 + if (firewire_debug > 1) + dump_db(sc, ch); +#endif + if(status & OHCI_CNTL_DMA_DEAD) { + /* Stop DMA */ + OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); + device_printf(sc->fc.dev, "force reset AT FIFO\n"); + OWRITE(sc, OHCI_HCCCTLCLR, OHCI_HCC_LINKEN); + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_LPS | OHCI_HCC_LINKEN); + OWRITE(sc, OHCI_DMACTLCLR(off), OHCI_CNTL_DMA_RUN); + } + stat = status & FWOHCIEV_MASK; + switch(stat){ + case FWOHCIEV_ACKPEND: + case FWOHCIEV_ACKCOMPL: + err = 0; + break; + case FWOHCIEV_ACKBSA: + case FWOHCIEV_ACKBSB: + case FWOHCIEV_ACKBSX: + device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]); + err = EBUSY; + break; + case FWOHCIEV_FLUSHED: + case FWOHCIEV_ACKTARD: + device_printf(sc->fc.dev, "txd err=%2x %s\n", stat, fwohcicode[stat]); + err = EAGAIN; + break; + case FWOHCIEV_MISSACK: + case FWOHCIEV_UNDRRUN: + case FWOHCIEV_OVRRUN: + case FWOHCIEV_DESCERR: + case FWOHCIEV_DTRDERR: + case FWOHCIEV_TIMEOUT: + case FWOHCIEV_TCODERR: + case FWOHCIEV_UNKNOWN: + case FWOHCIEV_ACKDERR: + case FWOHCIEV_ACKTERR: + default: + device_printf(sc->fc.dev, "txd err=%2x %s\n", + stat, fwohcicode[stat]); + err = EINVAL; + break; + } + if (tr->xfer != NULL) { + xfer = tr->xfer; + if (xfer->flag & FWXF_RCVD) { +#if 0 + if (firewire_debug) + printf("already rcvd\n"); +#endif + fw_xfer_done(xfer); + } else { +// microtime(&xfer->tv); + xfer->tv = system_time(); + + xfer->flag = FWXF_SENT; + if (err == EBUSY) { + xfer->flag = FWXF_BUSY; + xfer->resp = err; + xfer->recv.pay_len = 0; + fw_xfer_done(xfer); + } else if (stat != FWOHCIEV_ACKPEND) { + if (stat != FWOHCIEV_ACKCOMPL) + xfer->flag = FWXF_SENTERR; + xfer->resp = err; + xfer->recv.pay_len = 0; + fw_xfer_done(xfer); + } + } + /* + * The watchdog timer takes care of split + * transcation timeout for ACKPEND case. + */ + } else { + printf("this shouldn't happen\n"); + } + FW_GLOCK(fc); + dbch->xferq.queued --; + FW_GUNLOCK(fc); + tr->xfer = NULL; + + packets ++; + tr = STAILQ_NEXT(tr, link); + dbch->bottom = tr; + if (dbch->bottom == dbch->top) { + /* we reaches the end of context program */ + if (firewire_debug && dbch->xferq.queued > 0) + printf("queued > 0\n"); + break; + } + } +out: + if ((dbch->flags & FWOHCI_DBCH_FULL) && packets > 0) { + printf("make free slot\n"); + dbch->flags &= ~FWOHCI_DBCH_FULL; + FW_GLOCK(fc); + fwohci_start(sc, dbch); + FW_GUNLOCK(fc); + } + splx(s); +} + +static void +fwohci_db_free(struct fwohci_dbch *dbch) +{ + struct fwohcidb_tr *db_tr; + int idb; + + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return; + + for(db_tr = STAILQ_FIRST(&dbch->db_trq), idb = 0; idb < dbch->ndb; + db_tr = STAILQ_NEXT(db_tr, link), idb++){ +/* if ((dbch->xferq.flag & FWXFERQ_EXTBUF) == 0 && + db_tr->buf != NULL) { + fwdma_free_size(dbch->dmat, db_tr->dma_map, + db_tr->buf, dbch->xferq.psize); + db_tr->buf = NULL;*/ + if ((dbch->xferq.flag & FWXFERQ_EXTBUF) == 0 && + db_tr->Area > B_OK) { + delete_area(db_tr->Area); + db_tr->Area = -1; + + } +/* else if (db_tr->dma_map != NULL) + bus_dmamap_destroy(dbch->dmat, db_tr->dma_map);*/ + } + dbch->ndb = 0; + db_tr = STAILQ_FIRST(&dbch->db_trq); + fwdma_free_multiseg(dbch->am); + free(db_tr); + STAILQ_INIT(&dbch->db_trq); + dbch->flags &= ~FWOHCI_DBCH_INIT; +} + +static void +fwohci_db_init(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int idb; + struct fwohcidb_tr *db_tr; + + if ((dbch->flags & FWOHCI_DBCH_INIT) != 0) + goto out; + + /* create dma_tag for buffers */ +#if 0 +#define MAX_REQCOUNT 0xffff + if (bus_dma_tag_create(/*parent*/ sc->fc.dmat, + /*alignment*/ 1, /*boundary*/ 0, + /*lowaddr*/ BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/ BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ dbch->xferq.psize, + /*nsegments*/ dbch->ndesc > 3 ? dbch->ndesc - 2 : 1, + /*maxsegsz*/ MAX_REQCOUNT, + /*flags*/ 0, +#if defined(__FreeBSD__) && __FreeBSD_version >= 501102 + /*lockfunc*/busdma_lock_mutex, + /*lockarg*/FW_GMTX(&sc->fc), +#endif + &dbch->dmat)) + return; +#endif + /* allocate DB entries and attach one to each DMA channels */ + /* DB entry must start at 16 bytes bounary. */ + STAILQ_INIT(&dbch->db_trq); + db_tr = (struct fwohcidb_tr *) + malloc(sizeof(struct fwohcidb_tr) * dbch->ndb); + if(db_tr == NULL){ + printf("fwohci_db_init: malloc(1) failed\n"); + return; + } + memset(db_tr, 0, sizeof(struct fwohcidb_tr) * dbch->ndb); + +#define DB_SIZE(x) (sizeof(struct fwohcidb) * (x)->ndesc) + dbch->am = fwdma_malloc_multiseg(DB_SIZE(dbch), DB_SIZE(dbch), dbch->ndb); + if (dbch->am == NULL) { + printf("fwohci_db_init: fwdma_malloc_multiseg failed\n"); + free(db_tr); + return; + } + /* Attach DB to DMA ch. */ + for(idb = 0 ; idb < dbch->ndb ; idb++){ + db_tr->dbcnt = 0; + db_tr->db = (struct fwohcidb *)fwdma_v_addr(dbch->am, idb); + db_tr->bus_addr = fwdma_bus_addr(dbch->am, idb); + /* create dmamap for buffers */ + /* XXX do we need 4bytes alignment tag? */ + /* XXX don't alloc dma_map for AR */ +#if 0 + if (bus_dmamap_create(dbch->dmat, 0, &db_tr->dma_map) != 0) { + printf("bus_dmamap_create failed\n"); + dbch->flags = FWOHCI_DBCH_INIT; /* XXX fake */ + fwohci_db_free(dbch); + return; + } +#endif + STAILQ_INSERT_TAIL(&dbch->db_trq, db_tr, link); + if (dbch->xferq.flag & FWXFERQ_EXTBUF) { + if (idb % dbch->xferq.bnpacket == 0) + dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket + ].start = (caddr_t)db_tr; + if ((idb + 1) % dbch->xferq.bnpacket == 0) + dbch->xferq.bulkxfer[idb / dbch->xferq.bnpacket + ].end = (caddr_t)db_tr; + } + db_tr++; + } + STAILQ_LAST(&dbch->db_trq, fwohcidb_tr,link)->link.stqe_next + = STAILQ_FIRST(&dbch->db_trq); +out: + dbch->xferq.queued = 0; + dbch->pdb_tr = NULL; + dbch->top = STAILQ_FIRST(&dbch->db_trq); + dbch->bottom = dbch->top; + dbch->flags = FWOHCI_DBCH_INIT; +} + +static int +fwohci_itx_disable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + + OWRITE(sc, OHCI_ITCTLCLR(dmach), + OHCI_CNTL_DMA_RUN | OHCI_CNTL_CYCMATCH_S); + OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); + /* XXX we cannot free buffers until the DMA really stops */ +// pause("fwitxd", hz); + snooze(1000000); + fwohci_db_free(&sc->it[dmach]); + sc->it[dmach].xferq.flag &= ~FWXFERQ_RUNNING; + return 0; +} + +static int +fwohci_irx_disable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + + OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); + /* XXX we cannot free buffers until the DMA really stops */ +// pause("fwirxd", hz); + snooze(1000000); + fwohci_db_free(&sc->ir[dmach]); + sc->ir[dmach].xferq.flag &= ~FWXFERQ_RUNNING; + return 0; +} + +#if BYTE_ORDER == BIG_ENDIAN +static void +fwohci_irx_post (struct firewire_comm *fc , uint32_t *qld) +{ + qld[0] = FWOHCI_DMA_READ(qld[0]); + return; +} +#endif + +static int +fwohci_tx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int err = 0; + int idb, z, i, dmach = 0, ldesc; + uint32_t off = 0; + struct fwohcidb_tr *db_tr; + struct fwohcidb *db; + + if(!(dbch->xferq.flag & FWXFERQ_EXTBUF)){ + err = EINVAL; + return err; + } + z = dbch->ndesc; + for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){ + if( &sc->it[dmach] == dbch){ + off = OHCI_ITOFF(dmach); + break; + } + } + if(off == 0){ + err = EINVAL; + return err; + } + if(dbch->xferq.flag & FWXFERQ_RUNNING) + return err; + dbch->xferq.flag |= FWXFERQ_RUNNING; + for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){ + dbch->bottom = STAILQ_NEXT(dbch->bottom, link); + } + db_tr = dbch->top; + for (idb = 0; idb < dbch->ndb; idb ++) { + fwohci_add_tx_buf(dbch, db_tr, idb); + if(STAILQ_NEXT(db_tr, link) == NULL){ + break; + } + db = db_tr->db; + ldesc = db_tr->dbcnt - 1; + FWOHCI_DMA_WRITE(db[0].db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr | z); + db[ldesc].db.desc.depend = db[0].db.desc.depend; + if(dbch->xferq.flag & FWXFERQ_EXTBUF){ + if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){ + FWOHCI_DMA_SET( + db[ldesc].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + /* OHCI 1.1 and above */ + FWOHCI_DMA_SET( + db[0].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + } + } + db_tr = STAILQ_NEXT(db_tr, link); + } + FWOHCI_DMA_CLEAR( + dbch->bottom->db[dbch->bottom->dbcnt - 1].db.desc.depend, 0xf); + return err; +} + +static int +fwohci_rx_enable(struct fwohci_softc *sc, struct fwohci_dbch *dbch) +{ + int err = 0; + int idb, z, i, dmach = 0, ldesc; + uint32_t off = 0; + struct fwohcidb_tr *db_tr; + struct fwohcidb *db; + + z = dbch->ndesc; + if(&sc->arrq == dbch){ + off = OHCI_ARQOFF; + }else if(&sc->arrs == dbch){ + off = OHCI_ARSOFF; + }else{ + for(dmach = 0 ; dmach < sc->fc.nisodma ; dmach++){ + if( &sc->ir[dmach] == dbch){ + off = OHCI_IROFF(dmach); + break; + } + } + } + if(off == 0){ + err = EINVAL; + return err; + } + if(dbch->xferq.flag & FWXFERQ_STREAM){ + if(dbch->xferq.flag & FWXFERQ_RUNNING) + return err; + }else{ + if(dbch->xferq.flag & FWXFERQ_RUNNING){ + err = EBUSY; + return err; + } + } + dbch->xferq.flag |= FWXFERQ_RUNNING; + dbch->top = STAILQ_FIRST(&dbch->db_trq); + for( i = 0, dbch->bottom = dbch->top; i < (dbch->ndb - 1); i++){ + dbch->bottom = STAILQ_NEXT(dbch->bottom, link); + } + db_tr = dbch->top; + for (idb = 0; idb < dbch->ndb; idb ++) { + fwohci_add_rx_buf(dbch, db_tr, idb, &sc->dummy_dma); + if (STAILQ_NEXT(db_tr, link) == NULL) + break; + db = db_tr->db; + ldesc = db_tr->dbcnt - 1; + FWOHCI_DMA_WRITE(db[ldesc].db.desc.depend, + STAILQ_NEXT(db_tr, link)->bus_addr | z); + if(dbch->xferq.flag & FWXFERQ_EXTBUF){ + if(((idb + 1 ) % dbch->xferq.bnpacket) == 0){ + FWOHCI_DMA_SET( + db[ldesc].db.desc.cmd, + OHCI_INTERRUPT_ALWAYS); + FWOHCI_DMA_CLEAR( + db[ldesc].db.desc.depend, + 0xf); + } + } + db_tr = STAILQ_NEXT(db_tr, link); + } + FWOHCI_DMA_CLEAR( + dbch->bottom->db[db_tr->dbcnt - 1].db.desc.depend, 0xf); + dbch->buf_offset = 0; +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + if(dbch->xferq.flag & FWXFERQ_STREAM){ + return err; + }else{ + OWRITE(sc, OHCI_DMACMD(off), dbch->top->bus_addr | z); + } + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_RUN); + return err; +} + +static int +fwohci_next_cycle(struct firewire_comm *fc, int cycle_now) +{ + int sec, cycle, cycle_match; + + cycle = cycle_now & 0x1fff; + sec = cycle_now >> 13; +#define CYCLE_MOD 0x10 +#if 1 +#define CYCLE_DELAY 8 /* min delay to start DMA */ +#else +#define CYCLE_DELAY 7000 /* min delay to start DMA */ +#endif + cycle = cycle + CYCLE_DELAY; + if (cycle >= 8000) { + sec ++; + cycle -= 8000; + } + cycle = roundup2(cycle, CYCLE_MOD); + if (cycle >= 8000) { + sec ++; + if (cycle == 8000) + cycle = 0; + else + cycle = CYCLE_MOD; + } + cycle_match = ((sec << 13) | cycle) & 0x7ffff; + + return(cycle_match); +} + +static int +fwohci_itxbuf_enable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int err = 0; + unsigned short tag, ich; + struct fwohci_dbch *dbch; + int cycle_match, cycle_now, s, ldesc; + uint32_t stat; + struct fw_bulkxfer *first, *chunk, *prev; + struct fw_xferq *it; + + dbch = &sc->it[dmach]; + it = &dbch->xferq; + + tag = (it->flag >> 6) & 3; + ich = it->flag & 0x3f; + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) { + dbch->ndb = it->bnpacket * it->bnchunk; + dbch->ndesc = 3; + fwohci_db_init(sc, dbch); + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + + err = fwohci_tx_enable(sc, dbch); + } + if(err) + return err; + + ldesc = dbch->ndesc - 1; + s = splfw(); + FW_GLOCK(fc); + prev = STAILQ_LAST(&it->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&it->stvalid)) != NULL) { + struct fwohcidb *db; + +// fwdma_sync_multiseg(it->buf, chunk->poffset, it->bnpacket, +// BUS_DMASYNC_PREWRITE); + fwohci_txbufdb(sc, dmach, chunk); + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; +#if 0 /* XXX necessary? */ + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, + OHCI_BRANCH_ALWAYS); +#endif +#if 0 /* if bulkxfer->npacket changes */ + db[ldesc].db.desc.depend = db[0].db.desc.depend = + ((struct fwohcidb_tr *) + (chunk->start))->bus_addr | dbch->ndesc; +#else + FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); + FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); +#endif + } + STAILQ_REMOVE_HEAD(&it->stvalid, link); + STAILQ_INSERT_TAIL(&it->stdma, chunk, link); + prev = chunk; + } + FW_GUNLOCK(fc); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + splx(s); + stat = OREAD(sc, OHCI_ITCTL(dmach)); + if (firewire_debug && (stat & OHCI_CNTL_CYCMATCH_S)) + printf("stat 0x%x\n", stat); + + if (stat & (OHCI_CNTL_DMA_ACTIVE | OHCI_CNTL_CYCMATCH_S)) + return 0; + +#if 0 + OWRITE(sc, OHCI_ITCTLCLR(dmach), OHCI_CNTL_DMA_RUN); +#endif + OWRITE(sc, OHCI_IT_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_STATCLR, 1 << dmach); + OWRITE(sc, OHCI_IT_MASK, 1 << dmach); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IT); + + first = STAILQ_FIRST(&it->stdma); + OWRITE(sc, OHCI_ITCMD(dmach), + ((struct fwohcidb_tr *)(first->start))->bus_addr | dbch->ndesc); + if (firewire_debug > 1) { + printf("fwohci_itxbuf_enable: kick 0x%08x\n", stat); +#if 1 + dump_dma(sc, ITX_CH + dmach); +#endif + } + if ((stat & OHCI_CNTL_DMA_RUN) == 0) { +#if 1 + /* Don't start until all chunks are buffered */ + if (STAILQ_FIRST(&it->stfree) != NULL) + goto out; +#endif +#if 1 + /* Clear cycle match counter bits */ + OWRITE(sc, OHCI_ITCTLCLR(dmach), 0xffff0000); + + /* 2bit second + 13bit cycle */ + cycle_now = (fc->cyctimer(fc) >> 12) & 0x7fff; + cycle_match = fwohci_next_cycle(fc, cycle_now); + + OWRITE(sc, OHCI_ITCTL(dmach), + OHCI_CNTL_CYCMATCH_S | (cycle_match << 16) + | OHCI_CNTL_DMA_RUN); +#else + OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_RUN); +#endif + if (firewire_debug > 1) { + printf("cycle_match: 0x%04x->0x%04x\n", + cycle_now, cycle_match); + dump_dma(sc, ITX_CH + dmach); + dump_db(sc, ITX_CH + dmach); + } + } else if ((stat & OHCI_CNTL_CYCMATCH_S) == 0) { + device_printf(sc->fc.dev, + "IT DMA underrun (0x%08x)\n", stat); + OWRITE(sc, OHCI_ITCTL(dmach), OHCI_CNTL_DMA_WAKE); + } +out: + return err; +} + +static int +fwohci_irx_enable(struct firewire_comm *fc, int dmach) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + int err = 0, s, ldesc; + unsigned short tag, ich; + uint32_t stat; + struct fwohci_dbch *dbch; + struct fwohcidb_tr *db_tr; + struct fw_bulkxfer *first, *prev, *chunk; + struct fw_xferq *ir; + + dbch = &sc->ir[dmach]; + ir = &dbch->xferq; + + if ((ir->flag & FWXFERQ_RUNNING) == 0) { + tag = (ir->flag >> 6) & 3; + ich = ir->flag & 0x3f; + OWRITE(sc, OHCI_IRMATCH(dmach), tagbit[tag] | ich); + + ir->queued = 0; + dbch->ndb = ir->bnpacket * ir->bnchunk; + dbch->ndesc = 2; + fwohci_db_init(sc, dbch); + if ((dbch->flags & FWOHCI_DBCH_INIT) == 0) + return ENOMEM; + err = fwohci_rx_enable(sc, dbch); + } + if(err) + return err; + + first = STAILQ_FIRST(&ir->stfree); + if (first == NULL) { + device_printf(fc->dev, "IR DMA no free chunk\n"); + return 0; + } + + ldesc = dbch->ndesc - 1; + s = splfw(); + if ((ir->flag & FWXFERQ_HANDLER) == 0) + FW_GLOCK(fc); + prev = STAILQ_LAST(&ir->stdma, fw_bulkxfer, link); + while ((chunk = STAILQ_FIRST(&ir->stfree)) != NULL) { + struct fwohcidb *db; + +#if 0 /* XXX for if_fwe */ + if (chunk->mbuf != NULL) { + db_tr = (struct fwohcidb_tr *)(chunk->start); + db_tr->dbcnt = 1; + err = bus_dmamap_load_mbuf(dbch->dmat, db_tr->dma_map, + chunk->mbuf, fwohci_execute_db2, db_tr, + /* flags */0); + FWOHCI_DMA_SET(db_tr->db[1].db.desc.cmd, + OHCI_UPDATE | OHCI_INPUT_LAST | + OHCI_INTERRUPT_ALWAYS | OHCI_BRANCH_ALWAYS); + } +#endif + db = ((struct fwohcidb_tr *)(chunk->end))->db; + FWOHCI_DMA_WRITE(db[ldesc].db.desc.res, 0); + FWOHCI_DMA_CLEAR(db[ldesc].db.desc.depend, 0xf); + if (prev != NULL) { + db = ((struct fwohcidb_tr *)(prev->end))->db; + FWOHCI_DMA_SET(db[ldesc].db.desc.depend, dbch->ndesc); + } + STAILQ_REMOVE_HEAD(&ir->stfree, link); + STAILQ_INSERT_TAIL(&ir->stdma, chunk, link); + prev = chunk; + } + if ((ir->flag & FWXFERQ_HANDLER) == 0) + FW_GUNLOCK(fc); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREREAD); + splx(s); + stat = OREAD(sc, OHCI_IRCTL(dmach)); + if (stat & OHCI_CNTL_DMA_ACTIVE) + return 0; + if (stat & OHCI_CNTL_DMA_RUN) { + OWRITE(sc, OHCI_IRCTLCLR(dmach), OHCI_CNTL_DMA_RUN); + device_printf(sc->fc.dev, "IR DMA overrun (0x%08x)\n", stat); + } + + if (firewire_debug) + printf("start IR DMA 0x%x\n", stat); + OWRITE(sc, OHCI_IR_MASKCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_STATCLR, 1 << dmach); + OWRITE(sc, OHCI_IR_MASK, 1 << dmach); + OWRITE(sc, OHCI_IRCTLCLR(dmach), 0xf0000000); + OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_ISOHDR); + OWRITE(sc, OHCI_IRCMD(dmach), + ((struct fwohcidb_tr *)(first->start))->bus_addr + | dbch->ndesc); + OWRITE(sc, OHCI_IRCTL(dmach), OHCI_CNTL_DMA_RUN); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_DMA_IR); +#if 0 + dump_db(sc, IRX_CH + dmach); +#endif + return err; +} + +int +fwohci_stop(struct fwohci_softc *sc) +{ + u_int i; + +/* Now stopping all DMA channel */ + OWRITE(sc, OHCI_ARQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ARSCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + + for( i = 0 ; i < sc->fc.nisodma ; i ++ ){ + OWRITE(sc, OHCI_IRCTLCLR(i), OHCI_CNTL_DMA_RUN); + OWRITE(sc, OHCI_ITCTLCLR(i), OHCI_CNTL_DMA_RUN); + } + + if (sc->fc.arq !=0 && sc->fc.arq->maxq > 0) + fw_drain_txq(&sc->fc); + +#if 0 /* Let dcons(4) be accessed */ +/* Stop interrupt */ + OWRITE(sc, FWOHCI_INTMASKCLR, + OHCI_INT_EN | OHCI_INT_ERR | OHCI_INT_PHY_SID + | OHCI_INT_PHY_INT + | OHCI_INT_DMA_ATRQ | OHCI_INT_DMA_ATRS + | OHCI_INT_DMA_PRRQ | OHCI_INT_DMA_PRRS + | OHCI_INT_DMA_ARRQ | OHCI_INT_DMA_ARRS + | OHCI_INT_PHY_BUS_R); + +/* FLUSH FIFO and reset Transmitter/Reciever */ + OWRITE(sc, OHCI_HCCCTL, OHCI_HCC_RESET); +#endif + +/* XXX Link down? Bus reset? */ + return 0; +} + +#ifdef OHCI_DEBUG +static void +fwohci_dump_intr(struct fwohci_softc *sc, uint32_t stat) +{ + if(stat & OREAD(sc, FWOHCI_INTMASK)) + device_printf(fc->dev, "INTERRUPT < %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s> 0x%08x, 0x%08x\n", + stat & OHCI_INT_EN ? "DMA_EN ":"", + stat & OHCI_INT_PHY_REG ? "PHY_REG ":"", + stat & OHCI_INT_CYC_LONG ? "CYC_LONG ":"", + stat & OHCI_INT_ERR ? "INT_ERR ":"", + stat & OHCI_INT_CYC_ERR ? "CYC_ERR ":"", + stat & OHCI_INT_CYC_LOST ? "CYC_LOST ":"", + stat & OHCI_INT_CYC_64SECOND ? "CYC_64SECOND ":"", + stat & OHCI_INT_CYC_START ? "CYC_START ":"", + stat & OHCI_INT_PHY_INT ? "PHY_INT ":"", + stat & OHCI_INT_PHY_BUS_R ? "BUS_RESET ":"", + stat & OHCI_INT_PHY_SID ? "SID ":"", + stat & OHCI_INT_LR_ERR ? "DMA_LR_ERR ":"", + stat & OHCI_INT_PW_ERR ? "DMA_PW_ERR ":"", + stat & OHCI_INT_DMA_IR ? "DMA_IR ":"", + stat & OHCI_INT_DMA_IT ? "DMA_IT " :"", + stat & OHCI_INT_DMA_PRRS ? "DMA_PRRS " :"", + stat & OHCI_INT_DMA_PRRQ ? "DMA_PRRQ " :"", + stat & OHCI_INT_DMA_ARRS ? "DMA_ARRS " :"", + stat & OHCI_INT_DMA_ARRQ ? "DMA_ARRQ " :"", + stat & OHCI_INT_DMA_ATRS ? "DMA_ATRS " :"", + stat & OHCI_INT_DMA_ATRQ ? "DMA_ATRQ " :"", + stat, OREAD(sc, FWOHCI_INTMASK) + ); +} +#endif +static void +fwohci_intr_core(struct fwohci_softc *sc, uint32_t stat, int count) +{ + struct firewire_comm *fc = (struct firewire_comm *)sc; + uint32_t node_id, plen; + + if ((stat & OHCI_INT_PHY_BUS_R) && (fc->status != FWBUSRESET)) { + fc->status = FWBUSRESET; + /* Disable bus reset interrupt until sid recv. */ + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_PHY_BUS_R); + + device_printf(fc->dev, "BUS reset\n"); + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCSRC); + + OWRITE(sc, OHCI_ATQCTLCLR, OHCI_CNTL_DMA_RUN); + sc->atrq.xferq.flag &= ~FWXFERQ_RUNNING; + OWRITE(sc, OHCI_ATSCTLCLR, OHCI_CNTL_DMA_RUN); + sc->atrs.xferq.flag &= ~FWXFERQ_RUNNING; + +// if (!kdb_active) +// taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_busreset); + gDpc->queue_dpc(sc->fc.taskqueue, fwohci_task_busreset, sc); + } + if (stat & OHCI_INT_PHY_SID) { + /* Enable bus reset interrupt */ + OWRITE(sc, FWOHCI_INTSTATCLR, OHCI_INT_PHY_BUS_R); + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_PHY_BUS_R); + + /* Allow async. request to us */ + OWRITE(sc, OHCI_AREQHI, 1 << 31); + if (firewire_phydma_enable) { + /* allow from all nodes */ + OWRITE(sc, OHCI_PREQHI, 0x7fffffff); + OWRITE(sc, OHCI_PREQLO, 0xffffffff); + /* 0 to 4GB region */ + OWRITE(sc, OHCI_PREQUPPER, 0x10000); + } + /* Set ATRetries register */ + OWRITE(sc, OHCI_ATRETRY, 1<<(13+16) | 0xfff); + + /* + * Checking whether the node is root or not. If root, turn on + * cycle master. + */ + node_id = OREAD(sc, FWOHCI_NODEID); + plen = OREAD(sc, OHCI_SID_CNT); + + fc->nodeid = node_id & 0x3f; + device_printf(fc->dev, "node_id=0x%08x, gen=%d, ", + node_id, (plen >> 16) & 0xff); + if (!(node_id & OHCI_NODE_VALID)) { + printf("Bus reset failure\n"); + goto sidout; + } + + /* cycle timer */ + sc->cycle_lost = 0; + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_CYC_LOST); + if ((node_id & OHCI_NODE_ROOT) && !nocyclemaster) { + printf("CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTL, + OHCI_CNTL_CYCMTR | OHCI_CNTL_CYCTIMER); + } else { + printf("non CYCLEMASTER mode\n"); + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCMTR); + OWRITE(sc, OHCI_LNKCTL, OHCI_CNTL_CYCTIMER); + } + + fc->status = FWBUSINIT; + +// if (!kdb_active) +// taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_sid); + gDpc->queue_dpc(sc->fc.taskqueue, fwohci_task_sid, sc); + } +sidout: +// if ((stat & ~(OHCI_INT_PHY_BUS_R | OHCI_INT_PHY_SID)) && (!kdb_active)) +// taskqueue_enqueue(sc->fc.taskqueue, &sc->fwohci_task_dma); + if ((stat & ~(OHCI_INT_PHY_BUS_R | OHCI_INT_PHY_SID))) + gDpc->queue_dpc(sc->fc.taskqueue, fwohci_task_dma, sc); +} + +static void +fwohci_intr_dma(struct fwohci_softc *sc, uint32_t stat, int count) +{ + uint32_t irstat, itstat; + u_int i; + struct firewire_comm *fc = (struct firewire_comm *)sc; + + if (stat & OHCI_INT_DMA_IR) { + irstat = atomic_readandclear_int(&sc->irstat); + for(i = 0; i < fc->nisodma ; i++){ + struct fwohci_dbch *dbch; + + if((irstat & (1 << i)) != 0){ + dbch = &sc->ir[i]; + if ((dbch->xferq.flag & FWXFERQ_OPEN) == 0) { + device_printf(sc->fc.dev, + "dma(%d) not active\n", i); + continue; + } + fwohci_rbuf_update(sc, i); + } + } + } + if (stat & OHCI_INT_DMA_IT) { + itstat = atomic_readandclear_int(&sc->itstat); + for(i = 0; i < fc->nisodma ; i++){ + if((itstat & (1 << i)) != 0){ + fwohci_tbuf_update(sc, i); + } + } + } + if (stat & OHCI_INT_DMA_PRRS) { +#if 0 + dump_dma(sc, ARRS_CH); + dump_db(sc, ARRS_CH); +#endif + fwohci_arcv(sc, &sc->arrs, count); + } + if (stat & OHCI_INT_DMA_PRRQ) { +#if 0 + dump_dma(sc, ARRQ_CH); + dump_db(sc, ARRQ_CH); +#endif + fwohci_arcv(sc, &sc->arrq, count); + } + if (stat & OHCI_INT_CYC_LOST) { + if (sc->cycle_lost >= 0) + sc->cycle_lost ++; + if (sc->cycle_lost > 10) { + sc->cycle_lost = -1; +#if 0 + OWRITE(sc, OHCI_LNKCTLCLR, OHCI_CNTL_CYCTIMER); +#endif + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_CYC_LOST); + device_printf(sc->fc.dev, "too many cycle lost, " + "no cycle master presents?\n"); + } + } + if (stat & OHCI_INT_DMA_ATRQ) { + fwohci_txd(sc, &(sc->atrq)); + } + if (stat & OHCI_INT_DMA_ATRS) { + fwohci_txd(sc, &(sc->atrs)); + } + if (stat & OHCI_INT_PW_ERR) { + device_printf(sc->fc.dev, "posted write error\n"); + } + if (stat & OHCI_INT_ERR) { + device_printf(sc->fc.dev, "unrecoverable error\n"); + } + if (stat & OHCI_INT_PHY_INT) { + device_printf(sc->fc.dev, "phy int\n"); + } + + return; +} + +static void +fwohci_task_busreset(void *arg) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + + fw_busreset(&sc->fc, FWBUSRESET); + OWRITE(sc, OHCI_CROMHDR, ntohl(sc->fc.config_rom[0])); + OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->fc.config_rom[2])); +} + +static void +fwohci_task_sid(void *arg) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + struct firewire_comm *fc = &sc->fc; + uint32_t *buf; + int i, plen; + + + plen = OREAD(sc, OHCI_SID_CNT); + + if (plen & OHCI_SID_ERR) { + device_printf(sc->fc.dev, "SID Error\n"); + return; + } + plen &= OHCI_SID_CNT_MASK; + if (plen < 4 || plen > OHCI_SIDSIZE) { + device_printf(sc->fc.dev, "invalid SID len = %d\n", plen); + return; + } + plen -= 4; /* chop control info */ + buf = (uint32_t *)malloc(OHCI_SIDSIZE); + if (buf == NULL) { + device_printf(sc->fc.dev, "malloc failed\n"); + return; + } + for (i = 0; i < plen / 4; i ++) + buf[i] = FWOHCI_DMA_READ(sc->sid_buf[i+1]); +#if 1 /* XXX needed?? */ + /* pending all pre-bus_reset packets */ + fwohci_txd(sc, &sc->atrq); + fwohci_txd(sc, &sc->atrs); + fwohci_arcv(sc, &sc->arrs, -1); + fwohci_arcv(sc, &sc->arrq, -1); + fw_drain_txq(fc); +#endif + fw_sidrcv(fc, buf, plen); + free(buf); +} + +static void +fwohci_task_dma(void *arg) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + uint32_t stat; + +again: + stat = atomic_readandclear_int(&sc->intstat); + if (stat) + fwohci_intr_dma(sc, stat, -1); + else + return; + goto again; +} + +static int32 +fwohci_check_stat(struct fwohci_softc *sc) +{ + uint32_t stat, irstat, itstat; + + stat = OREAD(sc, FWOHCI_INTSTAT); + if (stat == 0xffffffff) { + device_printf(sc->fc.dev, + "device physically ejected?\n"); + return (B_UNHANDLED_INTERRUPT); + } + if (stat) + OWRITE(sc, FWOHCI_INTSTATCLR, stat & ~OHCI_INT_PHY_BUS_R); + + stat &= sc->intmask; + if (stat == 0) + return (B_UNHANDLED_INTERRUPT); + + atomic_set_int(&sc->intstat, stat); + if (stat & OHCI_INT_DMA_IR) { + irstat = OREAD(sc, OHCI_IR_STAT); + OWRITE(sc, OHCI_IR_STATCLR, irstat); + atomic_set_int(&sc->irstat, irstat); + } + if (stat & OHCI_INT_DMA_IT) { + itstat = OREAD(sc, OHCI_IT_STAT); + OWRITE(sc, OHCI_IT_STATCLR, itstat); + atomic_set_int(&sc->itstat, itstat); + } + + fwohci_intr_core(sc, stat, -1); + return B_HANDLED_INTERRUPT; +} + +int32 +fwohci_intr(void *arg) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)arg; + + return (fwohci_check_stat(sc)); +} + +void +fwohci_poll(struct firewire_comm *fc, int quick, int count) +{ + struct fwohci_softc *sc = (struct fwohci_softc *)fc; + fwohci_check_stat(sc); +} + +static void +fwohci_set_intr(struct firewire_comm *fc, int enable) +{ + struct fwohci_softc *sc; + + sc = (struct fwohci_softc *)fc; + if (firewire_debug) + device_printf(sc->fc.dev, "fwohci_set_intr: %d\n", enable); + if (enable) { + sc->intmask |= OHCI_INT_EN; + OWRITE(sc, FWOHCI_INTMASK, OHCI_INT_EN); + } else { + sc->intmask &= ~OHCI_INT_EN; + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN); + } +} + +static void +fwohci_tbuf_update(struct fwohci_softc *sc, int dmach) +{ + struct firewire_comm *fc = &sc->fc; + struct fwohcidb *db; + struct fw_bulkxfer *chunk; + struct fw_xferq *it; + uint32_t stat, count; + int s, w=0, ldesc; + + it = fc->it[dmach]; + ldesc = sc->it[dmach].ndesc - 1; + s = splfw(); /* unnecessary ? */ + FW_GLOCK(fc); +// fwdma_sync_multiseg_all(sc->it[dmach].am, BUS_DMASYNC_POSTREAD); + if (firewire_debug) + dump_db(sc, ITX_CH + dmach); + while ((chunk = STAILQ_FIRST(&it->stdma)) != NULL) { + db = ((struct fwohcidb_tr *)(chunk->end))->db; + stat = FWOHCI_DMA_READ(db[ldesc].db.desc.res) + >> OHCI_STATUS_SHIFT; + db = ((struct fwohcidb_tr *)(chunk->start))->db; + /* timestamp */ + count = FWOHCI_DMA_READ(db[ldesc].db.desc.res) + & OHCI_COUNT_MASK; + if (stat == 0) + break; + STAILQ_REMOVE_HEAD(&it->stdma, link); + switch (stat & FWOHCIEV_MASK){ + case FWOHCIEV_ACKCOMPL: +#if 0 + device_printf(fc->dev, "0x%08x\n", count); +#endif + break; + default: + printf("Isochronous transmit err %02x(%s)\n", + stat, fwohcicode[stat & 0x1f]); + } + STAILQ_INSERT_TAIL(&it->stfree, chunk, link); + w++; + } + FW_GUNLOCK(fc); + splx(s); + if (w) +// wakeup(it); + release_sem(it->Sem); +} + +static void +fwohci_rbuf_update(struct fwohci_softc *sc, int dmach) +{ + struct firewire_comm *fc = &sc->fc; + struct fwohcidb_tr *db_tr; + struct fw_bulkxfer *chunk; + struct fw_xferq *ir; + uint32_t stat; + int s, w = 0, ldesc; + + ir = fc->ir[dmach]; + ldesc = sc->ir[dmach].ndesc - 1; + +#if 0 + dump_db(sc, dmach); +#endif + s = splfw(); + if ((ir->flag & FWXFERQ_HANDLER) == 0) + FW_GLOCK(fc); +// fwdma_sync_multiseg_all(sc->ir[dmach].am, BUS_DMASYNC_POSTREAD); + while ((chunk = STAILQ_FIRST(&ir->stdma)) != NULL) { + db_tr = (struct fwohcidb_tr *)chunk->end; + stat = FWOHCI_DMA_READ(db_tr->db[ldesc].db.desc.res) + >> OHCI_STATUS_SHIFT; + if (stat == 0) + break; +#if 0 + if (chunk->mbuf != NULL) { + bus_dmamap_sync(sc->ir[dmach].dmat, db_tr->dma_map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->ir[dmach].dmat, db_tr->dma_map); + } else if (ir->buf != NULL) { + fwdma_sync_multiseg(ir->buf, chunk->poffset, + ir->bnpacket, BUS_DMASYNC_POSTREAD); + } else { + /* XXX */ + printf("fwohci_rbuf_update: this shouldn't happend\n"); + } +#endif + STAILQ_REMOVE_HEAD(&ir->stdma, link); + STAILQ_INSERT_TAIL(&ir->stvalid, chunk, link); + switch (stat & FWOHCIEV_MASK) { + case FWOHCIEV_ACKCOMPL: + chunk->resp = 0; + break; + default: + chunk->resp = EINVAL; + device_printf(fc->dev, + "Isochronous receive err %02x(%s)\n", + stat, fwohcicode[stat & 0x1f]); + } + w++; + } + if ((ir->flag & FWXFERQ_HANDLER) == 0) + FW_GUNLOCK(fc); + splx(s); + if (w == 0) + return; + + if (ir->flag & FWXFERQ_HANDLER) + ir->hand(ir); + else +// wakeup(ir); + release_sem(ir->Sem); +} + +void +dump_dma(struct fwohci_softc *sc, uint32_t ch) +{ + uint32_t off, cntl, stat, cmd, match; + + if(ch == 0){ + off = OHCI_ATQOFF; + }else if(ch == 1){ + off = OHCI_ATSOFF; + }else if(ch == 2){ + off = OHCI_ARQOFF; + }else if(ch == 3){ + off = OHCI_ARSOFF; + }else if(ch < IRX_CH){ + off = OHCI_ITCTL(ch - ITX_CH); + }else{ + off = OHCI_IRCTL(ch - IRX_CH); + } + cntl = stat = OREAD(sc, off); + cmd = OREAD(sc, off + 0xc); + match = OREAD(sc, off + 0x10); + + device_printf(sc->fc.dev, "ch %1x cntl:0x%08x cmd:0x%08x match:0x%08x\n", + ch, + cntl, + cmd, + match); + stat &= 0xffff ; + if (stat) { + device_printf(sc->fc.dev, "dma %d ch:%s%s%s%s%s%s %s(%x)\n", + ch, + stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", + stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", + stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", + stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", + stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", + stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", + fwohcicode[stat & 0x1f], + stat & 0x1f + ); + }else{ + device_printf(sc->fc.dev, "dma %d ch: Nostat\n", ch); + } +} + +void +dump_db(struct fwohci_softc *sc, uint32_t ch) +{ + struct fwohci_dbch *dbch; + struct fwohcidb_tr *cp = NULL, *pp, *np = NULL; + struct fwohcidb *curr = NULL, *prev, *next = NULL; + int idb, jdb; + uint32_t cmd, off; + if(ch == 0){ + off = OHCI_ATQOFF; + dbch = &sc->atrq; + }else if(ch == 1){ + off = OHCI_ATSOFF; + dbch = &sc->atrs; + }else if(ch == 2){ + off = OHCI_ARQOFF; + dbch = &sc->arrq; + }else if(ch == 3){ + off = OHCI_ARSOFF; + dbch = &sc->arrs; + }else if(ch < IRX_CH){ + off = OHCI_ITCTL(ch - ITX_CH); + dbch = &sc->it[ch - ITX_CH]; + }else { + off = OHCI_IRCTL(ch - IRX_CH); + dbch = &sc->ir[ch - IRX_CH]; + } + cmd = OREAD(sc, off + 0xc); + + if( dbch->ndb == 0 ){ + device_printf(sc->fc.dev, "No DB is attached ch=%d\n", ch); + return; + } + pp = dbch->top; + prev = pp->db; + for(idb = 0 ; idb < dbch->ndb ; idb ++ ){ + cp = STAILQ_NEXT(pp, link); + if(cp == NULL){ + curr = NULL; + goto outdb; + } + np = STAILQ_NEXT(cp, link); + for(jdb = 0 ; jdb < dbch->ndesc ; jdb ++ ){ + if ((cmd & 0xfffffff0) == cp->bus_addr) { + curr = cp->db; + if(np != NULL){ + next = np->db; + }else{ + next = NULL; + } + goto outdb; + } + } + pp = STAILQ_NEXT(pp, link); + if(pp == NULL){ + curr = NULL; + goto outdb; + } + prev = pp->db; + } +outdb: + if( curr != NULL){ +#if 0 + printf("Prev DB %d\n", ch); + print_db(pp, prev, ch, dbch->ndesc); +#endif + printf("Current DB %d\n", ch); + print_db(cp, curr, ch, dbch->ndesc); +#if 0 + printf("Next DB %d\n", ch); + print_db(np, next, ch, dbch->ndesc); +#endif + }else{ + printf("dbdump err ch = %d cmd = 0x%08x\n", ch, cmd); + } + return; +} + +void +print_db(struct fwohcidb_tr *db_tr, struct fwohcidb *db, + uint32_t ch, uint32_t max) +{ + fwohcireg_t stat; + int i, key; + uint32_t cmd, res; + + if(db == NULL){ + printf("No Descriptor is found\n"); + return; + } + + printf("ch = %d\n%8s %s %s %s %s %4s %8s %8s %4s:%4s\n", + ch, + "Current", + "OP ", + "KEY", + "INT", + "BR ", + "len", + "Addr", + "Depend", + "Stat", + "Cnt"); + for( i = 0 ; i <= max ; i ++){ + cmd = FWOHCI_DMA_READ(db[i].db.desc.cmd); + res = FWOHCI_DMA_READ(db[i].db.desc.res); + key = cmd & OHCI_KEY_MASK; + stat = res >> OHCI_STATUS_SHIFT; + printf("%08x %s %s %s %s %5d %08x %08x %04x:%04x", + db_tr->bus_addr, + dbcode[(cmd >> 28) & 0xf], + dbkey[(cmd >> 24) & 0x7], + dbcond[(cmd >> 20) & 0x3], + dbcond[(cmd >> 18) & 0x3], + cmd & OHCI_COUNT_MASK, + FWOHCI_DMA_READ(db[i].db.desc.addr), + FWOHCI_DMA_READ(db[i].db.desc.depend), + stat, + res & OHCI_COUNT_MASK); + if(stat & 0xff00){ + printf(" %s%s%s%s%s%s %s(%x)\n", + stat & OHCI_CNTL_DMA_RUN ? "RUN," : "", + stat & OHCI_CNTL_DMA_WAKE ? "WAKE," : "", + stat & OHCI_CNTL_DMA_DEAD ? "DEAD," : "", + stat & OHCI_CNTL_DMA_ACTIVE ? "ACTIVE," : "", + stat & OHCI_CNTL_DMA_BT ? "BRANCH," : "", + stat & OHCI_CNTL_DMA_BAD ? "BADDMA," : "", + fwohcicode[stat & 0x1f], + stat & 0x1f + ); + }else{ + printf(" Nostat\n"); + } + if(key == OHCI_KEY_ST2 ){ + printf("0x%08x 0x%08x 0x%08x 0x%08x\n", + FWOHCI_DMA_READ(db[i+1].db.immed[0]), + FWOHCI_DMA_READ(db[i+1].db.immed[1]), + FWOHCI_DMA_READ(db[i+1].db.immed[2]), + FWOHCI_DMA_READ(db[i+1].db.immed[3])); + } + if(key == OHCI_KEY_DEVICE){ + return; + } + if((cmd & OHCI_BRANCH_MASK) + == OHCI_BRANCH_ALWAYS){ + return; + } + if((cmd & OHCI_CMD_MASK) + == OHCI_OUTPUT_LAST){ + return; + } + if((cmd & OHCI_CMD_MASK) + == OHCI_INPUT_LAST){ + return; + } + if(key == OHCI_KEY_ST2 ){ + i++; + } + } + return; +} + +void +fwohci_ibr(struct firewire_comm *fc) +{ + struct fwohci_softc *sc; + uint32_t fun; + + device_printf(fc->dev, "Initiate bus reset\n"); + sc = (struct fwohci_softc *)fc; + + /* + * Make sure our cached values from the config rom are + * initialised. + */ + OWRITE(sc, OHCI_CROMHDR, ntohl(sc->fc.config_rom[0])); + OWRITE(sc, OHCI_BUS_OPT, ntohl(sc->fc.config_rom[2])); + + /* + * Set root hold-off bit so that non cyclemaster capable node + * shouldn't became the root node. + */ +#if 1 + fun = fwphy_rddata(sc, FW_PHY_IBR_REG); + fun |= FW_PHY_IBR | FW_PHY_RHB; + fun = fwphy_wrdata(sc, FW_PHY_IBR_REG, fun); +#else /* Short bus reset */ + fun = fwphy_rddata(sc, FW_PHY_ISBR_REG); + fun |= FW_PHY_ISBR | FW_PHY_RHB; + fun = fwphy_wrdata(sc, FW_PHY_ISBR_REG, fun); +#endif +} + +void +fwohci_txbufdb(struct fwohci_softc *sc, int dmach, struct fw_bulkxfer *bulkxfer) +{ + struct fwohcidb_tr *db_tr, *fdb_tr; + struct fwohci_dbch *dbch; + struct fwohcidb *db; + struct fw_pkt *fp; + struct fwohci_txpkthdr *ohcifp; + unsigned short chtag; + int idb; + + FW_GLOCK_ASSERT(&sc->fc); + + dbch = &sc->it[dmach]; + chtag = sc->it[dmach].xferq.flag & 0xff; + + db_tr = (struct fwohcidb_tr *)(bulkxfer->start); + fdb_tr = (struct fwohcidb_tr *)(bulkxfer->end); +/* +device_printf(sc->fc.dev, "DB %08x %08x %08x\n", bulkxfer, db_tr->bus_addr, fdb_tr->bus_addr); +*/ + for (idb = 0; idb < dbch->xferq.bnpacket; idb ++) { + db = db_tr->db; + fp = (struct fw_pkt *)db_tr->buf; + ohcifp = (struct fwohci_txpkthdr *) db[1].db.immed; + ohcifp->mode.ld[0] = fp->mode.ld[0]; + ohcifp->mode.common.spd = 0 & 0x7; + ohcifp->mode.stream.len = fp->mode.stream.len; + ohcifp->mode.stream.chtag = chtag; + ohcifp->mode.stream.tcode = 0xa; +#if BYTE_ORDER == BIG_ENDIAN + FWOHCI_DMA_WRITE(db[1].db.immed[0], db[1].db.immed[0]); + FWOHCI_DMA_WRITE(db[1].db.immed[1], db[1].db.immed[1]); +#endif + + FWOHCI_DMA_CLEAR(db[2].db.desc.cmd, OHCI_COUNT_MASK); + FWOHCI_DMA_SET(db[2].db.desc.cmd, fp->mode.stream.len); + FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); +#if 0 /* if bulkxfer->npackets changes */ + db[2].db.desc.cmd = OHCI_OUTPUT_LAST + | OHCI_UPDATE + | OHCI_BRANCH_ALWAYS; + db[0].db.desc.depend = + = db[dbch->ndesc - 1].db.desc.depend + = STAILQ_NEXT(db_tr, link)->bus_addr | dbch->ndesc; +#else + FWOHCI_DMA_SET(db[0].db.desc.depend, dbch->ndesc); + FWOHCI_DMA_SET(db[dbch->ndesc - 1].db.desc.depend, dbch->ndesc); +#endif + bulkxfer->end = (caddr_t)db_tr; + db_tr = STAILQ_NEXT(db_tr, link); + } + db = ((struct fwohcidb_tr *)bulkxfer->end)->db; + FWOHCI_DMA_CLEAR(db[0].db.desc.depend, 0xf); + FWOHCI_DMA_CLEAR(db[dbch->ndesc - 1].db.desc.depend, 0xf); +#if 0 /* if bulkxfer->npackets changes */ + db[dbch->ndesc - 1].db.desc.control |= OHCI_INTERRUPT_ALWAYS; + /* OHCI 1.1 and above */ + db[0].db.desc.control |= OHCI_INTERRUPT_ALWAYS; +#endif +/* + db_tr = (struct fwohcidb_tr *)bulkxfer->start; + fdb_tr = (struct fwohcidb_tr *)bulkxfer->end; +device_printf(sc->fc.dev, "DB %08x %3d %08x %08x\n", bulkxfer, bulkxfer->npacket, db_tr->bus_addr, fdb_tr->bus_addr); +*/ + return; +} + +static int +fwohci_add_tx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, + int poffset) +{ + struct fwohcidb *db = db_tr->db; + struct fw_xferq *it; + int err = 0; + + it = &dbch->xferq; + if(it->buf == 0){ + err = EINVAL; + return err; + } + db_tr->buf = fwdma_v_addr(it->buf, poffset); + db_tr->dbcnt = 3; + + FWOHCI_DMA_WRITE(db[0].db.desc.cmd, + OHCI_OUTPUT_MORE | OHCI_KEY_ST2 | 8); + FWOHCI_DMA_WRITE(db[0].db.desc.addr, 0); + bzero((void *)&db[1].db.immed[0], sizeof(db[1].db.immed)); + FWOHCI_DMA_WRITE(db[2].db.desc.addr, + fwdma_bus_addr(it->buf, poffset) + sizeof(uint32_t)); + + FWOHCI_DMA_WRITE(db[2].db.desc.cmd, + OHCI_OUTPUT_LAST | OHCI_UPDATE | OHCI_BRANCH_ALWAYS); +#if 1 + FWOHCI_DMA_WRITE(db[0].db.desc.res, 0); + FWOHCI_DMA_WRITE(db[2].db.desc.res, 0); +#endif + return 0; +} + +int +fwohci_add_rx_buf(struct fwohci_dbch *dbch, struct fwohcidb_tr *db_tr, + int poffset, struct fwdma_alloc *dummy_dma) +{ + struct fwohcidb *db = db_tr->db; + struct fw_xferq *ir; + int i, ldesc; + bus_addr_t dbuf[2]; + int dsiz[2]; + void * buf_virt, *buf_phy; + + ir = &dbch->xferq; + if (ir->buf == NULL && (dbch->xferq.flag & FWXFERQ_EXTBUF) == 0) { +/* db_tr->buf = fwdma_malloc_size(dbch->dmat, &db_tr->dma_map, + ir->psize, &dbuf[0], BUS_DMA_NOWAIT); + if (db_tr->buf == NULL) + return(ENOMEM);*/ + db_tr->Area = alloc_mem(&buf_virt, &buf_phy, + MIN(ir->psize, MAX_REQCOUNT), 0, "fw ir Area"); + if(db_tr->Area < B_OK) + return(ENOMEM); + db_tr->buf = (caddr_t)buf_virt; + dbuf[0] = (bus_addr_t)buf_phy; + + db_tr->dbcnt = 1; + dsiz[0] = ir->psize; +/* bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_PREREAD);*/ + } else { + db_tr->dbcnt = 0; + if (dummy_dma != NULL) { + dsiz[db_tr->dbcnt] = sizeof(uint32_t); + dbuf[db_tr->dbcnt++] = dummy_dma->bus_addr; + } + dsiz[db_tr->dbcnt] = ir->psize; + if (ir->buf != NULL) { + db_tr->buf = fwdma_v_addr(ir->buf, poffset); + dbuf[db_tr->dbcnt] = fwdma_bus_addr( ir->buf, poffset); + } + db_tr->dbcnt++; + } + for(i = 0 ; i < db_tr->dbcnt ; i++){ + FWOHCI_DMA_WRITE(db[i].db.desc.addr, dbuf[i]); + FWOHCI_DMA_WRITE(db[i].db.desc.cmd, OHCI_INPUT_MORE | dsiz[i]); + if (ir->flag & FWXFERQ_STREAM) { + FWOHCI_DMA_SET(db[i].db.desc.cmd, OHCI_UPDATE); + } + FWOHCI_DMA_WRITE(db[i].db.desc.res, dsiz[i]); + } + ldesc = db_tr->dbcnt - 1; + if (ir->flag & FWXFERQ_STREAM) { + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_INPUT_LAST); + } + FWOHCI_DMA_SET(db[ldesc].db.desc.cmd, OHCI_BRANCH_ALWAYS); + return 0; +} + + +static int +fwohci_arcv_swap(struct fw_pkt *fp, int len) +{ + struct fw_pkt *fp0; + uint32_t ld0; + int slen, hlen; +#if BYTE_ORDER == BIG_ENDIAN + int i; +#endif + + ld0 = FWOHCI_DMA_READ(fp->mode.ld[0]); +#if 0 + printf("ld0: x%08x\n", ld0); +#endif + fp0 = (struct fw_pkt *)&ld0; + /* determine length to swap */ + switch (fp0->mode.common.tcode) { + case FWTCODE_RREQQ: + case FWTCODE_WRES: + case FWTCODE_WREQQ: + case FWTCODE_RRESQ: + case FWOHCITCODE_PHY: + slen = 12; + break; + case FWTCODE_RREQB: + case FWTCODE_WREQB: + case FWTCODE_LREQ: + case FWTCODE_RRESB: + case FWTCODE_LRES: + slen = 16; + break; + default: + printf("Unknown tcode %d\n", fp0->mode.common.tcode); + return(0); + } + hlen = tinfo[fp0->mode.common.tcode].hdr_len; + if (hlen > len) { + if (firewire_debug) + printf("splitted header\n"); + return(-hlen); + } +#if BYTE_ORDER == BIG_ENDIAN + for(i = 0; i < slen/4; i ++) + fp->mode.ld[i] = FWOHCI_DMA_READ(fp->mode.ld[i]); +#endif + return(hlen); +} + +static int +fwohci_get_plen(struct fwohci_softc *sc, struct fwohci_dbch *dbch, struct fw_pkt *fp) +{ + struct tcode_info *info; + int r; + + info = &tinfo[fp->mode.common.tcode]; + r = info->hdr_len + sizeof(uint32_t); + if ((info->flag & FWTI_BLOCK_ASY) != 0) + r += roundup2(fp->mode.wreqb.len, sizeof(uint32_t)); + + if (r == sizeof(uint32_t)) { + /* XXX */ + device_printf(sc->fc.dev, "Unknown tcode %d\n", + fp->mode.common.tcode); + return (-1); + } + + if (r > dbch->xferq.psize) { + device_printf(sc->fc.dev, "Invalid packet length %d\n", r); + return (-1); + /* panic ? */ + } + + return r; +} + +static void +fwohci_arcv_free_buf(struct fwohci_softc *sc, struct fwohci_dbch *dbch, + struct fwohcidb_tr *db_tr, uint32_t off, int wake) +{ + struct fwohcidb *db = &db_tr->db[0]; + + FWOHCI_DMA_CLEAR(db->db.desc.depend, 0xf); + FWOHCI_DMA_WRITE(db->db.desc.res, dbch->xferq.psize); + FWOHCI_DMA_SET(dbch->bottom->db[0].db.desc.depend, 1); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_PREWRITE); + dbch->bottom = db_tr; + + if (wake) + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); +} + +static void//to be done +fwohci_arcv(struct fwohci_softc *sc, struct fwohci_dbch *dbch, int count) +{ + struct fwohcidb_tr *db_tr; + struct iovec vec[2]; + struct fw_pkt pktbuf; + int nvec; + struct fw_pkt *fp; + uint8_t *ld; + uint32_t stat, off, status, event; + u_int spd; + int len, plen, hlen, pcnt, offset; + int s; + caddr_t buf; + int resCount; + + if(&sc->arrq == dbch){ + off = OHCI_ARQOFF; + }else if(&sc->arrs == dbch){ + off = OHCI_ARSOFF; + }else{ + return; + } + + s = splfw(); + db_tr = dbch->top; + pcnt = 0; + /* XXX we cannot handle a packet which lies in more than two buf */ +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTREAD); +// fwdma_sync_multiseg_all(dbch->am, BUS_DMASYNC_POSTWRITE); + status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) >> OHCI_STATUS_SHIFT; + resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) & OHCI_COUNT_MASK; + while (status & OHCI_CNTL_DMA_ACTIVE) { +#if 0 + + if (off == OHCI_ARQOFF) + printf("buf 0x%08x, status 0x%04x, resCount 0x%04x\n", + db_tr->bus_addr, status, resCount); +#endif + len = dbch->xferq.psize - resCount; + ld = (uint8_t *)db_tr->buf; + if (dbch->pdb_tr == NULL) { + len -= dbch->buf_offset; + ld += dbch->buf_offset; + } +/* if (len > 0) + bus_dmamap_sync(dbch->dmat, db_tr->dma_map, + BUS_DMASYNC_POSTREAD);*/ + while (len > 0 ) { + if (count >= 0 && count-- == 0) + goto out; + if(dbch->pdb_tr != NULL){ + /* we have a fragment in previous buffer */ + int rlen; + + offset = dbch->buf_offset; + if (offset < 0) + offset = - offset; + buf = dbch->pdb_tr->buf + offset; + rlen = dbch->xferq.psize - offset; + if (firewire_debug) + printf("rlen=%d, offset=%d\n", + rlen, dbch->buf_offset); + if (dbch->buf_offset < 0) { + /* splitted in header, pull up */ + char *p; + + p = (char *)&pktbuf; + bcopy(buf, p, rlen); + p += rlen; + /* this must be too long but harmless */ + rlen = sizeof(pktbuf) - rlen; + if (rlen < 0) + printf("why rlen < 0\n"); + bcopy(db_tr->buf, p, rlen); + ld += rlen; + len -= rlen; + hlen = fwohci_arcv_swap(&pktbuf, sizeof(pktbuf)); + if (hlen <= 0) { + printf("hlen should be positive."); + goto err; + } + offset = sizeof(pktbuf); + vec[0].iov_base = (char *)&pktbuf; + vec[0].iov_len = offset; + } else { + /* splitted in payload */ + offset = rlen; + vec[0].iov_base = buf; + vec[0].iov_len = rlen; + } + fp=(struct fw_pkt *)vec[0].iov_base; + nvec = 1; + } else { + /* no fragment in previous buffer */ + fp=(struct fw_pkt *)ld; + hlen = fwohci_arcv_swap(fp, len); + if (hlen == 0) + goto err; + if (hlen < 0) { + dbch->pdb_tr = db_tr; + dbch->buf_offset = - dbch->buf_offset; + /* sanity check */ + if (resCount != 0) { + printf("resCount=%d hlen=%d\n", + resCount, hlen); + goto err; + } + goto out; + } + offset = 0; + nvec = 0; + } + plen = fwohci_get_plen(sc, dbch, fp) - offset; + if (plen < 0) { + /* minimum header size + trailer + = sizeof(fw_pkt) so this shouldn't happens */ + printf("plen(%d) is negative! offset=%d\n", + plen, offset); + goto err; + } + if (plen > 0) { + len -= plen; + if (len < 0) { + dbch->pdb_tr = db_tr; + if (firewire_debug) + printf("splitted payload\n"); + /* sanity check */ + if (resCount != 0) { + printf("resCount=%d plen=%d" + " len=%d\n", + resCount, plen, len); + goto err; + } + goto out; + } + vec[nvec].iov_base = ld; + vec[nvec].iov_len = plen; + nvec ++; + ld += plen; + } + dbch->buf_offset = ld - (uint8_t *)db_tr->buf; + if (nvec == 0) + printf("nvec == 0\n"); + +/* DMA result-code will be written at the tail of packet */ + stat = FWOHCI_DMA_READ(*(uint32_t *)(ld - sizeof(struct fwohci_trailer))); +#if 0 + printf("plen: %d, stat %x\n", + plen ,stat); +#endif + spd = (stat >> 21) & 0x3; + event = (stat >> 16) & 0x1f; + switch (event) { + case FWOHCIEV_ACKPEND: +#if 0 + printf("fwohci_arcv: ack pending tcode=0x%x..\n", fp->mode.common.tcode); +#endif + /* fall through */ + case FWOHCIEV_ACKCOMPL: + { + struct fw_rcv_buf rb; + + if ((vec[nvec-1].iov_len -= + sizeof(struct fwohci_trailer)) == 0) + nvec--; + rb.fc = &sc->fc; + rb.vec = vec; + rb.nvec = nvec; + rb.spd = spd; + fw_rcv(&rb); + break; + } + case FWOHCIEV_BUSRST: + if ((sc->fc.status != FWBUSRESET) && + (sc->fc.status != FWBUSINIT)) + printf("got BUSRST packet!?\n"); + break; + default: + device_printf(sc->fc.dev, + "Async DMA Receive error err=%02x %s" + " plen=%d offset=%d len=%d status=0x%08x" + " tcode=0x%x, stat=0x%08x\n", + event, fwohcicode[event], plen, + dbch->buf_offset, len, + OREAD(sc, OHCI_DMACTL(off)), + fp->mode.common.tcode, stat); +#if 1 /* XXX */ + goto err; +#endif + break; + } + pcnt ++; + if (dbch->pdb_tr != NULL) { + fwohci_arcv_free_buf(sc, dbch, dbch->pdb_tr, + off, 1); + dbch->pdb_tr = NULL; + } + + } +out: + if (resCount == 0) { + /* done on this buffer */ + if (dbch->pdb_tr == NULL) { + fwohci_arcv_free_buf(sc, dbch, db_tr, off, 1); + dbch->buf_offset = 0; + } else + if (dbch->pdb_tr != db_tr) + printf("pdb_tr != db_tr\n"); + db_tr = STAILQ_NEXT(db_tr, link); + status = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) + >> OHCI_STATUS_SHIFT; + resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) + & OHCI_COUNT_MASK; + /* XXX check buffer overrun */ + dbch->top = db_tr; + } else { + dbch->buf_offset = dbch->xferq.psize - resCount; + break; + } + /* XXX make sure DMA is not dead */ + } +#if 0 + if (pcnt < 1) + printf("fwohci_arcv: no packets\n"); +#endif + splx(s); + return; + +err: + device_printf(sc->fc.dev, "AR DMA status=%x, ", + OREAD(sc, OHCI_DMACTL(off))); + dbch->pdb_tr = NULL; + /* skip until resCount != 0 */ + printf(" skip buffer"); + while (resCount == 0) { + printf(" #"); + fwohci_arcv_free_buf(sc, dbch, db_tr, off, 0); + db_tr = STAILQ_NEXT(db_tr, link); + resCount = FWOHCI_DMA_READ(db_tr->db[0].db.desc.res) + & OHCI_COUNT_MASK; + } while (resCount == 0) + printf(" done\n"); + dbch->top = db_tr; + dbch->buf_offset = dbch->xferq.psize - resCount; + OWRITE(sc, OHCI_DMACTL(off), OHCI_CNTL_DMA_WAKE); + splx(s); +} diff --git a/src/add-ons/kernel/bus_managers/firewire/fwohci_pci.c b/src/add-ons/kernel/bus_managers/firewire/fwohci_pci.c new file mode 100644 index 0000000000..016aab33fa --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwohci_pci.c @@ -0,0 +1,223 @@ +/*- + * 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/fwohci_pci.c,v 1.60 2007/06/06 14:31:36 simokawa Exp $ + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "util.h" +#include "fwdebug.h" +#include "fwglue.h" +#include "queue.h" +#include "firewire.h" +#include "iec13213.h" +#include "firewirereg.h" + +#include "fwdma.h" +#include "fwohcireg.h" +#include "fwohcivar.h" + +#define PCIM_CMD_MEMEN 0x0002 +#define PCIM_CMD_BUSMASTEREN 0x0004 +#define PCIM_CMD_MWRICEN 0x0010 +#define PCIM_CMD_IOS 0x0001 +extern pci_module_info *pci; +extern pci_info *pciInfo[MAX_CARDS]; +extern fwohci_softc_t *gFwohci_softc[MAX_CARDS]; +extern struct firewire_softc *gFirewire_softc[MAX_CARDS]; + +status_t +fwohci_pci_detach(int index) +{ + fwohci_softc_t *sc = gFwohci_softc[index]; + int s; + + s = splfw(); + + fwohci_stop(sc); + +// bus_generic_detach(self); + firewire_detach(gFirewire_softc[index]); +/* if (sc->fc.bdev) { + device_delete_child(self, sc->fc.bdev); + sc->fc.bdev = NULL; + }*/ + + /* disable interrupts that might have been switched on */ + OWRITE(sc, FWOHCI_INTMASKCLR, OHCI_INT_EN); + remove_io_interrupt_handler (sc->irq, fwohci_intr, sc); + delete_area(sc->regArea); + + fwohci_detach(sc); + mtx_destroy(FW_GMTX(&sc->fc)); + splx(s); + return B_OK; +} + +static void +fwohci_pci_add_child(int index) +{ + struct fwohci_softc *sc; + int err = 0; + + sc = gFwohci_softc[index]; + +/* child = device_add_child(dev, name, unit); + if (child == NULL) + return (child); + + sc->fc.bdev = child; + device_set_ivars(child, (void *)&sc->fc);*/ + +// err = device_probe_and_attach(child); + err = firewire_attach(&sc->fc, gFirewire_softc[index]); + + if (err) { + device_printf(dev, "firewire_attach failed with err=%d\n", + err); + fwohci_pci_detach(index); +// device_delete_child(dev, child); + return; + } + /* XXX + * Clear the bus reset event flag to start transactions even when + * interrupt is disabled during the boot process. + */ +// if (cold) { +// int s; +// DELAY(250); /* 2 cycles */ +// s = splfw(); +// fwohci_poll((void *)sc, 0, -1); +// splx(s); +// } + +} + +status_t +fwohci_pci_attach(int index) +{ + fwohci_softc_t *sc = gFwohci_softc[index]; + pci_info *info = pciInfo[index]; + uint32 olatency, latency, ocache_line, cache_line; + uint32 val; + + mtx_init(FW_GMTX(&sc->fc), "firewire", NULL, MTX_DEF); + + val = pci->read_pci_config(info->bus, info->device, info->function, + PCI_command, 2); + val |= PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_MWRICEN; + +#if 1 /* for broken hardware */ + val &= ~PCIM_CMD_MWRICEN; +#endif + pci->write_pci_config(info->bus, info->device, info->function, + PCI_command, 2, val); + + /* + * Some Sun PCIO-2 FireWire controllers have their intpin register + * bogusly set to 0, although it should be 3. Correct that. + */ + if (info->vendor_id == FW_VENDORID_SUN && info->device_id == (FW_DEVICE_PCIO2FW >> 16) && + info->u.h0.interrupt_pin == 0) + info->u.h0.interrupt_pin = 3; + + latency = olatency = pci->read_pci_config(info->bus, info->device, info->function, + PCI_latency, 1); +#define DEF_LATENCY 0x20 + if (olatency < DEF_LATENCY) { + latency = DEF_LATENCY; + pci->write_pci_config(info->bus, info->device, info->function, + PCI_latency, 1, latency); + } + + cache_line = ocache_line = pci->read_pci_config(info->bus, info->device, + info->function, PCI_line_size, 1); +#define DEF_CACHE_LINE 8 + if (ocache_line < DEF_CACHE_LINE) { + cache_line = DEF_CACHE_LINE; + pci->write_pci_config(info->bus, info->device, info->function, + PCI_line_size, 1, cache_line); + } + TRACE("latency timer %x -> %x.\n", olatency, latency); + TRACE("cache size %x -> %x.\n", ocache_line, cache_line); + + // get IRQ + sc->irq = pci->read_pci_config(info->bus, info->device, info->function, + PCI_interrupt_line, 1); + if (sc->irq == 0 || sc->irq == 0xff) { + ERROR("no IRQ assigned\n"); + goto err; + } + TRACE("IRQ %d\n", sc->irq); + + // map registers into memory +// val = pci->read_pci_config(info->bus, info->device, info->function, 0x14, 4); +// val &= PCI_address_memory_32_mask; +// TRACE("hardware register address %p\n", (void *) val); + TRACE("hardware register address %x\n", info->u.h0.base_registers[0]); + sc->regArea = map_mem(&sc->regAddr, info->u.h0.base_registers[0], 0x800, + B_READ_AREA | B_WRITE_AREA, "fw ohci register"); + if (sc->regArea < B_OK) { + ERROR("can't map hardware registers\n"); + goto err; + } + TRACE("mapped registers to %p\n", sc->regAddr); + + // setup interrupt handler + if (install_io_interrupt_handler(sc->irq, fwohci_intr, sc, 0) < B_OK) { + ERROR("can't install interrupt handler\n"); + goto err; + } + + if (fwohci_init(sc) < B_OK){ + + ERROR("fwohci_init failed"); + goto err; + } + fwohci_pci_add_child(index); + return B_OK; +err: + delete_area(sc->regArea); + mtx_destroy(FW_GMTX(&sc->fc)); + return B_ERROR; +} diff --git a/src/add-ons/kernel/bus_managers/firewire/fwohcireg.h b/src/add-ons/kernel/bus_managers/firewire/fwohcireg.h new file mode 100644 index 0000000000..beb189ec29 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwohcireg.h @@ -0,0 +1,452 @@ +/*- + * 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/fwohcireg.h,v 1.23 2007/04/30 14:06:30 simokawa Exp $ + * + */ +#define PCI_CBMEM PCIR_BAR(0) + +#define FW_VENDORID_NATSEMI 0x100B +#define FW_VENDORID_NEC 0x1033 +#define FW_VENDORID_SIS 0x1039 +#define FW_VENDORID_TI 0x104c +#define FW_VENDORID_SONY 0x104d +#define FW_VENDORID_VIA 0x1106 +#define FW_VENDORID_RICOH 0x1180 +#define FW_VENDORID_APPLE 0x106b +#define FW_VENDORID_LUCENT 0x11c1 +#define FW_VENDORID_INTEL 0x8086 +#define FW_VENDORID_ADAPTEC 0x9004 +#define FW_VENDORID_SUN 0x108e + +#define FW_DEVICE_CS4210 (0x000f << 16) +#define FW_DEVICE_UPD861 (0x0063 << 16) +#define FW_DEVICE_UPD871 (0x00ce << 16) +#define FW_DEVICE_UPD72870 (0x00cd << 16) +#define FW_DEVICE_UPD72873 (0x00e7 << 16) +#define FW_DEVICE_UPD72874 (0x00f2 << 16) +#define FW_DEVICE_TITSB22 (0x8009 << 16) +#define FW_DEVICE_TITSB23 (0x8019 << 16) +#define FW_DEVICE_TITSB26 (0x8020 << 16) +#define FW_DEVICE_TITSB43 (0x8021 << 16) +#define FW_DEVICE_TITSB43A (0x8023 << 16) +#define FW_DEVICE_TITSB43AB23 (0x8024 << 16) +#define FW_DEVICE_TITSB82AA2 (0x8025 << 16) +#define FW_DEVICE_TITSB43AB21 (0x8026 << 16) +#define FW_DEVICE_TIPCI4410A (0x8017 << 16) +#define FW_DEVICE_TIPCI4450 (0x8011 << 16) +#define FW_DEVICE_TIPCI4451 (0x8027 << 16) +#define FW_DEVICE_CXD1947 (0x8009 << 16) +#define FW_DEVICE_CXD3222 (0x8039 << 16) +#define FW_DEVICE_VT6306 (0x3044 << 16) +#define FW_DEVICE_R5C551 (0x0551 << 16) +#define FW_DEVICE_R5C552 (0x0552 << 16) +#define FW_DEVICE_PANGEA (0x0030 << 16) +#define FW_DEVICE_UNINORTH (0x0031 << 16) +#define FW_DEVICE_AIC5800 (0x5800 << 16) +#define FW_DEVICE_FW322 (0x5811 << 16) +#define FW_DEVICE_7007 (0x7007 << 16) +#define FW_DEVICE_82372FB (0x7605 << 16) +#define FW_DEVICE_PCIO2FW (0x1102 << 16) + +#define PCI_INTERFACE_OHCI 0x10 + +#define FW_OHCI_BASE_REG 0x10 + +#define OHCI_DMA_ITCH 0x20 +#define OHCI_DMA_IRCH 0x20 + +#define OHCI_MAX_DMA_CH (0x4 + OHCI_DMA_ITCH + OHCI_DMA_IRCH) + + +typedef uint32_t fwohcireg_t; + +/* for PCI */ +#if BYTE_ORDER == BIG_ENDIAN +#ifdef __HAIKU__ +#define htole32(x) __swap_int32(x) +#endif +#define FWOHCI_DMA_WRITE(x, y) ((x) = htole32(y)) +#define FWOHCI_DMA_READ(x) le32toh(x) +#define FWOHCI_DMA_SET(x, y) ((x) |= htole32(y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= htole32(~(y))) +#else +#define FWOHCI_DMA_WRITE(x, y) ((x) = (y)) +#define FWOHCI_DMA_READ(x) (x) +#define FWOHCI_DMA_SET(x, y) ((x) |= (y)) +#define FWOHCI_DMA_CLEAR(x, y) ((x) &= ~(y)) +#endif + +struct fwohcidb { + union { + struct { + uint32_t cmd; + uint32_t addr; + uint32_t depend; + uint32_t res; + } desc; + uint32_t immed[4]; + } db; +#define OHCI_STATUS_SHIFT 16 +#define OHCI_COUNT_MASK 0xffff +#define OHCI_OUTPUT_MORE (0 << 28) +#define OHCI_OUTPUT_LAST (1 << 28) +#define OHCI_INPUT_MORE (2 << 28) +#define OHCI_INPUT_LAST (3 << 28) +#define OHCI_STORE_QUAD (4 << 28) +#define OHCI_LOAD_QUAD (5 << 28) +#define OHCI_NOP (6 << 28) +#define OHCI_STOP (7 << 28) +#define OHCI_STORE (8 << 28) +#define OHCI_CMD_MASK (0xf << 28) + +#define OHCI_UPDATE (1 << 27) + +#define OHCI_KEY_ST0 (0 << 24) +#define OHCI_KEY_ST1 (1 << 24) +#define OHCI_KEY_ST2 (2 << 24) +#define OHCI_KEY_ST3 (3 << 24) +#define OHCI_KEY_REGS (5 << 24) +#define OHCI_KEY_SYS (6 << 24) +#define OHCI_KEY_DEVICE (7 << 24) +#define OHCI_KEY_MASK (7 << 24) + +#define OHCI_INTERRUPT_NEVER (0 << 20) +#define OHCI_INTERRUPT_TRUE (1 << 20) +#define OHCI_INTERRUPT_FALSE (2 << 20) +#define OHCI_INTERRUPT_ALWAYS (3 << 20) + +#define OHCI_BRANCH_NEVER (0 << 18) +#define OHCI_BRANCH_TRUE (1 << 18) +#define OHCI_BRANCH_FALSE (2 << 18) +#define OHCI_BRANCH_ALWAYS (3 << 18) +#define OHCI_BRANCH_MASK (3 << 18) + +#define OHCI_WAIT_NEVER (0 << 16) +#define OHCI_WAIT_TRUE (1 << 16) +#define OHCI_WAIT_FALSE (2 << 16) +#define OHCI_WAIT_ALWAYS (3 << 16) +}; + +#define OHCI_SPD_S100 0x4 +#define OHCI_SPD_S200 0x1 +#define OHCI_SPD_S400 0x2 + + +#define FWOHCIEV_NOSTAT 0 +#define FWOHCIEV_LONGP 2 +#define FWOHCIEV_MISSACK 3 +#define FWOHCIEV_UNDRRUN 4 +#define FWOHCIEV_OVRRUN 5 +#define FWOHCIEV_DESCERR 6 +#define FWOHCIEV_DTRDERR 7 +#define FWOHCIEV_DTWRERR 8 +#define FWOHCIEV_BUSRST 9 +#define FWOHCIEV_TIMEOUT 0xa +#define FWOHCIEV_TCODERR 0xb +#define FWOHCIEV_UNKNOWN 0xe +#define FWOHCIEV_FLUSHED 0xf +#define FWOHCIEV_ACKCOMPL 0x11 +#define FWOHCIEV_ACKPEND 0x12 +#define FWOHCIEV_ACKBSX 0x14 +#define FWOHCIEV_ACKBSA 0x15 +#define FWOHCIEV_ACKBSB 0x16 +#define FWOHCIEV_ACKTARD 0x1b +#define FWOHCIEV_ACKDERR 0x1d +#define FWOHCIEV_ACKTERR 0x1e + +#define FWOHCIEV_MASK 0x1f + +struct ohci_dma{ + fwohcireg_t cntl; + +#define OHCI_CNTL_CYCMATCH_S (0x1 << 31) + +#define OHCI_CNTL_BUFFIL (0x1 << 31) +#define OHCI_CNTL_ISOHDR (0x1 << 30) +#define OHCI_CNTL_CYCMATCH_R (0x1 << 29) +#define OHCI_CNTL_MULTICH (0x1 << 28) + +#define OHCI_CNTL_DMA_RUN (0x1 << 15) +#define OHCI_CNTL_DMA_WAKE (0x1 << 12) +#define OHCI_CNTL_DMA_DEAD (0x1 << 11) +#define OHCI_CNTL_DMA_ACTIVE (0x1 << 10) +#define OHCI_CNTL_DMA_BT (0x1 << 8) +#define OHCI_CNTL_DMA_BAD (0x1 << 7) +#define OHCI_CNTL_DMA_STAT (0xff) + + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; + fwohcireg_t match; + fwohcireg_t dummy1; + fwohcireg_t dummy2; + fwohcireg_t dummy3; +}; + +struct ohci_itdma{ + fwohcireg_t cntl; + fwohcireg_t cntl_clr; + fwohcireg_t dummy0; + fwohcireg_t cmd; +}; + +struct ohci_registers { + fwohcireg_t ver; /* Version No. 0x0 */ + fwohcireg_t guid; /* GUID_ROM No. 0x4 */ + fwohcireg_t retry; /* AT retries 0x8 */ +#define FWOHCI_RETRY 0x8 + fwohcireg_t csr_data; /* CSR data 0xc */ + fwohcireg_t csr_cmp; /* CSR compare 0x10 */ + fwohcireg_t csr_cntl; /* CSR compare 0x14 */ + fwohcireg_t rom_hdr; /* config ROM ptr. 0x18 */ + fwohcireg_t bus_id; /* BUS_ID 0x1c */ + fwohcireg_t bus_opt; /* BUS option 0x20 */ +#define FWOHCIGUID_H 0x24 +#define FWOHCIGUID_L 0x28 + fwohcireg_t guid_hi; /* GUID hi 0x24 */ + fwohcireg_t guid_lo; /* GUID lo 0x28 */ + fwohcireg_t dummy0[2]; /* dummy 0x2c-0x30 */ + fwohcireg_t config_rom; /* config ROM map 0x34 */ + fwohcireg_t post_wr_lo; /* post write addr lo 0x38 */ + fwohcireg_t post_wr_hi; /* post write addr hi 0x3c */ + fwohcireg_t vender; /* vender ID 0x40 */ + fwohcireg_t dummy1[3]; /* dummy 0x44-0x4c */ + fwohcireg_t hcc_cntl_set; /* HCC control set 0x50 */ + fwohcireg_t hcc_cntl_clr; /* HCC control clr 0x54 */ +#define OHCI_HCC_BIBIV (1 << 31) /* BIBimage Valid */ +#define OHCI_HCC_BIGEND (1 << 30) /* noByteSwapData */ +#define OHCI_HCC_PRPHY (1 << 23) /* programPhyEnable */ +#define OHCI_HCC_PHYEN (1 << 22) /* aPhyEnhanceEnable */ +#define OHCI_HCC_LPS (1 << 19) /* LPS */ +#define OHCI_HCC_POSTWR (1 << 18) /* postedWriteEnable */ +#define OHCI_HCC_LINKEN (1 << 17) /* linkEnable */ +#define OHCI_HCC_RESET (1 << 16) /* softReset */ + fwohcireg_t dummy2[2]; /* dummy 0x58-0x5c */ + fwohcireg_t dummy3[1]; /* dummy 0x60 */ + fwohcireg_t sid_buf; /* self id buffer 0x64 */ + fwohcireg_t sid_cnt; /* self id count 0x68 */ + fwohcireg_t dummy4[1]; /* dummy 0x6c */ + fwohcireg_t ir_mask_hi_set; /* ir mask hi set 0x70 */ + fwohcireg_t ir_mask_hi_clr; /* ir mask hi set 0x74 */ + fwohcireg_t ir_mask_lo_set; /* ir mask hi set 0x78 */ + fwohcireg_t ir_mask_lo_clr; /* ir mask hi set 0x7c */ +#define FWOHCI_INTSTAT 0x80 +#define FWOHCI_INTSTATCLR 0x84 +#define FWOHCI_INTMASK 0x88 +#define FWOHCI_INTMASKCLR 0x8c + fwohcireg_t int_stat; /* 0x80 */ + fwohcireg_t int_clear; /* 0x84 */ + fwohcireg_t int_mask; /* 0x88 */ + fwohcireg_t int_mask_clear; /* 0x8c */ + fwohcireg_t it_int_stat; /* 0x90 */ + fwohcireg_t it_int_clear; /* 0x94 */ + fwohcireg_t it_int_mask; /* 0x98 */ + fwohcireg_t it_mask_clear; /* 0x9c */ + fwohcireg_t ir_int_stat; /* 0xa0 */ + fwohcireg_t ir_int_clear; /* 0xa4 */ + fwohcireg_t ir_int_mask; /* 0xa8 */ + fwohcireg_t ir_mask_clear; /* 0xac */ + fwohcireg_t dummy5[11]; /* dummy 0xb0-d8 */ + fwohcireg_t fairness; /* fairness control 0xdc */ + fwohcireg_t link_cntl; /* Chip control 0xe0*/ + fwohcireg_t link_cntl_clr; /* Chip control clear 0xe4*/ +#define FWOHCI_NODEID 0xe8 + fwohcireg_t node; /* Node ID 0xe8 */ +#define OHCI_NODE_VALID (1 << 31) +#define OHCI_NODE_ROOT (1 << 30) + +#define OHCI_ASYSRCBUS 1 + + fwohcireg_t phy_access; /* PHY cntl 0xec */ +#define PHYDEV_RDDONE (1<<31) +#define PHYDEV_RDCMD (1<<15) +#define PHYDEV_WRCMD (1<<14) +#define PHYDEV_REGADDR 8 +#define PHYDEV_WRDATA 0 +#define PHYDEV_RDADDR 24 +#define PHYDEV_RDDATA 16 + + fwohcireg_t cycle_timer; /* Cycle Timer 0xf0 */ + fwohcireg_t dummy6[3]; /* dummy 0xf4-fc */ + fwohcireg_t areq_hi; /* Async req. filter hi 0x100 */ + fwohcireg_t areq_hi_clr; /* Async req. filter hi 0x104 */ + fwohcireg_t areq_lo; /* Async req. filter lo 0x108 */ + fwohcireg_t areq_lo_clr; /* Async req. filter lo 0x10c */ + fwohcireg_t preq_hi; /* Async req. filter hi 0x110 */ + fwohcireg_t preq_hi_clr; /* Async req. filter hi 0x114 */ + fwohcireg_t preq_lo; /* Async req. filter lo 0x118 */ + fwohcireg_t preq_lo_clr; /* Async req. filter lo 0x11c */ + + fwohcireg_t pys_upper; /* Physical Upper bound 0x120 */ + + fwohcireg_t dummy7[23]; /* dummy 0x124-0x17c */ + + /* 0x180, 0x184, 0x188, 0x18c */ + /* 0x190, 0x194, 0x198, 0x19c */ + /* 0x1a0, 0x1a4, 0x1a8, 0x1ac */ + /* 0x1b0, 0x1b4, 0x1b8, 0x1bc */ + /* 0x1c0, 0x1c4, 0x1c8, 0x1cc */ + /* 0x1d0, 0x1d4, 0x1d8, 0x1dc */ + /* 0x1e0, 0x1e4, 0x1e8, 0x1ec */ + /* 0x1f0, 0x1f4, 0x1f8, 0x1fc */ + struct ohci_dma dma_ch[0x4]; + + /* 0x200, 0x204, 0x208, 0x20c */ + /* 0x210, 0x204, 0x208, 0x20c */ + struct ohci_itdma dma_itch[0x20]; + + /* 0x400, 0x404, 0x408, 0x40c */ + /* 0x410, 0x404, 0x408, 0x40c */ + struct ohci_dma dma_irch[0x20]; +}; + +struct fwohcidb_tr{ + STAILQ_ENTRY(fwohcidb_tr) link; + struct fw_xfer *xfer; + struct fwohcidb *db; +// bus_dmamap_t dma_map; + caddr_t buf; + bus_addr_t bus_addr; + int dbcnt; + area_id Area; +}; + +/* + * OHCI info structure. + */ +struct fwohci_txpkthdr{ + union{ + uint32_t ld[4]; + struct { +#if BYTE_ORDER == BIG_ENDIAN + uint32_t spd:16, /* XXX include reserved field */ + :8, + tcode:4, + :4; +#else + uint32_t :4, + tcode:4, + :8, + spd:16; /* XXX include reserved fields */ +#endif + }common; + struct { +#if BYTE_ORDER == BIG_ENDIAN + uint32_t :8, + srcbus:1, + :4, + spd:3, + tlrt:8, + tcode:4, + :4; +#else + uint32_t :4, + tcode:4, + tlrt:8, + spd:3, + :4, + srcbus:1, + :8; +#endif + BIT16x2(dst, ); + }asycomm; + struct { +#if BYTE_ORDER == BIG_ENDIAN + uint32_t :13, + spd:3, + chtag:8, + tcode:4, + sy:4; +#else + uint32_t sy:4, + tcode:4, + chtag:8, + spd:3, + :13; +#endif + BIT16x2(len, ); + }stream; + }mode; +}; +struct fwohci_trailer{ +#if BYTE_ORDER == BIG_ENDIAN + uint32_t stat:16, + time:16; +#else + uint32_t time:16, + stat:16; +#endif +}; + +#define OHCI_CNTL_CYCSRC (0x1 << 22) +#define OHCI_CNTL_CYCMTR (0x1 << 21) +#define OHCI_CNTL_CYCTIMER (0x1 << 20) +#define OHCI_CNTL_PHYPKT (0x1 << 10) +#define OHCI_CNTL_SID (0x1 << 9) + +#define OHCI_INT_DMA_ATRQ (0x1 << 0) +#define OHCI_INT_DMA_ATRS (0x1 << 1) +#define OHCI_INT_DMA_ARRQ (0x1 << 2) +#define OHCI_INT_DMA_ARRS (0x1 << 3) +#define OHCI_INT_DMA_PRRQ (0x1 << 4) +#define OHCI_INT_DMA_PRRS (0x1 << 5) +#define OHCI_INT_DMA_IT (0x1 << 6) +#define OHCI_INT_DMA_IR (0x1 << 7) +#define OHCI_INT_PW_ERR (0x1 << 8) +#define OHCI_INT_LR_ERR (0x1 << 9) + +#define OHCI_INT_PHY_SID (0x1 << 16) +#define OHCI_INT_PHY_BUS_R (0x1 << 17) + +#define OHCI_INT_REG_FAIL (0x1 << 18) + +#define OHCI_INT_PHY_INT (0x1 << 19) +#define OHCI_INT_CYC_START (0x1 << 20) +#define OHCI_INT_CYC_64SECOND (0x1 << 21) +#define OHCI_INT_CYC_LOST (0x1 << 22) +#define OHCI_INT_CYC_ERR (0x1 << 23) + +#define OHCI_INT_ERR (0x1 << 24) +#define OHCI_INT_CYC_LONG (0x1 << 25) +#define OHCI_INT_PHY_REG (0x1 << 26) + +#define OHCI_INT_EN (0x1 << 31) + +#define IP_CHANNELS 0x0234 +#define FWOHCI_MAXREC 2048 + +#define OHCI_ISORA 0x02 +#define OHCI_ISORB 0x04 + +#define FWOHCITCODE_PHY 0xe diff --git a/src/add-ons/kernel/bus_managers/firewire/fwohcivar.h b/src/add-ons/kernel/bus_managers/firewire/fwohcivar.h new file mode 100644 index 0000000000..e638ceefd7 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/fwohcivar.h @@ -0,0 +1,93 @@ +/*- + * 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/fwohcivar.h,v 1.16 2007/06/06 14:31:36 simokawa Exp $ + * + */ + + +typedef struct fwohci_softc { + struct firewire_comm fc; + uint8 irq; + void *regAddr; + area_id regArea; + struct fwohci_dbch{ + u_int ndb; + u_int ndesc; + STAILQ_HEAD(, fwohcidb_tr) db_trq; + struct fwohcidb_tr *top, *bottom, *pdb_tr; + struct fw_xferq xferq; + int flags; +#define FWOHCI_DBCH_INIT (1<<0) +#define FWOHCI_DBCH_FULL (1<<1) + /* used only in receive context */ + int buf_offset; /* signed */ +#define FWOHCI_DBCH_MAX_PAGES 32 + /* Context programs buffer */ + struct fwdma_alloc_multi *am; +#ifndef __HAIKU__ + bus_dma_tag_t dmat; +#endif + } arrq, arrs, atrq, atrs, it[OHCI_DMA_ITCH], ir[OHCI_DMA_IRCH]; + u_int maxrec; + uint32_t *sid_buf; + struct fwdma_alloc sid_dma; + struct fwdma_alloc crom_dma; + struct fwdma_alloc dummy_dma; + uint32_t intmask, irstat, itstat; + uint32_t intstat; +#ifndef __HAIKU__ + struct task fwohci_task_busreset; + struct task fwohci_task_sid; + struct task fwohci_task_dma; +#endif + int cycle_lost; +} fwohci_softc_t; + +#ifdef __HAIKU__ +int32 fwohci_intr (void *arg); +int fwohci_init (struct fwohci_softc *); +void fwohci_poll (struct firewire_comm *, int, int); +void fwohci_reset (struct fwohci_softc *); +int fwohci_detach (struct fwohci_softc *); +int fwohci_stop (struct fwohci_softc *); +#else +void fwohci_intr (void *arg); +int fwohci_filt (void *arg); +int fwohci_init (struct fwohci_softc *, device_t); +void fwohci_poll (struct firewire_comm *, int, int); +void fwohci_reset (struct fwohci_softc *, device_t); +int fwohci_detach (struct fwohci_softc *, device_t); +int fwohci_resume (struct fwohci_softc *, device_t); +int fwohci_stop (struct fwohci_softc *, device_t dev); +#endif diff --git a/src/add-ons/kernel/bus_managers/firewire/timer.c b/src/add-ons/kernel/bus_managers/firewire/timer.c new file mode 100644 index 0000000000..7299d9037b --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/timer.c @@ -0,0 +1,216 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include + +#include "debug.h" +#include "fwdebug.h" +#include "timer.h" + +#define MAX_TIMERS 8 + + +struct timer_info +{ + timer_id id; + timer_function func; + void * cookie; + bigtime_t next_event; + bigtime_t interval; + bool periodic; +}; + + +static struct timer_info sTimerData[MAX_TIMERS]; +static int sTimerCount; +static timer_id sTimerNextId; +static thread_id sTimerThread; +static sem_id sTimerSem; +static spinlock sTimerSpinlock; + + +static int32 +timer_thread(void *cookie) +{ + status_t status = 0; + + do { + bigtime_t timeout; + bigtime_t now; + cpu_status cpu; + timer_function func; + void * cookie; + int i; + int index; + + cpu = disable_interrupts(); + acquire_spinlock(&sTimerSpinlock); + + now = system_time(); + cookie = 0; + func = 0; + + // find timer with smallest event time + index = -1; + timeout = B_INFINITE_TIMEOUT; + for (i = 0; i < sTimerCount; i++) { + if (sTimerData[i].next_event < timeout) { + timeout = sTimerData[i].next_event; + index = i; + } + } + + if (timeout < now) { + // timer is ready for execution, load func and cookie + ASSERT(index >= 0 && index < sTimerCount); + func = sTimerData[index].func; + cookie = sTimerData[index].cookie; + if (sTimerData[index].periodic) { + // periodic timer is ready, update the entry + sTimerData[index].next_event += sTimerData[index].interval; + } else { + // single shot timer is ready, delete the entry + if (index != (sTimerCount - 1) && sTimerCount != 1) { + memcpy(&sTimerData[index], &sTimerData[sTimerCount - 1], sizeof(struct timer_info)); + } + sTimerCount--; + } + } + + release_spinlock(&sTimerSpinlock); + restore_interrupts(cpu); + + // execute timer hook + if (timeout < now) { + ASSERT(func); + func(cookie); + continue; + } + + status = acquire_sem_etc(sTimerSem, 1, B_ABSOLUTE_TIMEOUT, timeout); + } while (status != B_BAD_SEM_ID); + + return 0; +} + + +timer_id +create_timer(timer_function func, void *cookie, bigtime_t interval, uint32 flags) +{ + cpu_status cpu; + timer_id id; + + if (func == 0) + return -1; + + // Attention: flags are not real flags, as B_PERIODIC_TIMER is 3 + + cpu = disable_interrupts(); + acquire_spinlock(&sTimerSpinlock); + + if (sTimerCount < MAX_TIMERS) { + id = sTimerNextId; + sTimerData[sTimerCount].id = id; + sTimerData[sTimerCount].func = func; + sTimerData[sTimerCount].cookie = cookie; + sTimerData[sTimerCount].next_event = (flags == B_ONE_SHOT_ABSOLUTE_TIMER) ? interval : system_time() + interval; + sTimerData[sTimerCount].interval = interval; + sTimerData[sTimerCount].periodic = flags == B_PERIODIC_TIMER; + sTimerNextId++; + sTimerCount++; + } else { + id = -1; + } + + release_spinlock(&sTimerSpinlock); + restore_interrupts(cpu); + + if (id != -1) + release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE); + + return id; +} + + +status_t +delete_timer(timer_id id) +{ + cpu_status cpu; + bool deleted; + int i; + + deleted = false; + + cpu = disable_interrupts(); + acquire_spinlock(&sTimerSpinlock); + + for (i = 0; i < sTimerCount; i++) { + if (sTimerData[i].id == id) { + if (i != (sTimerCount - 1) && sTimerCount != 1) { + memcpy(&sTimerData[i], &sTimerData[sTimerCount - 1], sizeof(struct timer_info)); + } + sTimerCount--; + deleted = true; + break; + } + } + + release_spinlock(&sTimerSpinlock); + restore_interrupts(cpu); + + if (!deleted) + return B_ERROR; + + release_sem_etc(sTimerSem, 1, B_DO_NOT_RESCHEDULE); + return B_OK; +} + + +status_t +initialize_timer(void) +{ + sTimerCount = 0; + sTimerNextId = 1; + sTimerSpinlock = 0; + + sTimerThread = spawn_kernel_thread(timer_thread, "firewire timer", 80, 0); + sTimerSem = create_sem(0, "firewire timer"); + set_sem_owner(sTimerSem, B_SYSTEM_TEAM); + + if (sTimerSem < 0 || sTimerThread < 0) { + delete_sem(sTimerSem); + kill_thread(sTimerThread); + return B_ERROR; + } + + resume_thread(sTimerThread); + return B_OK; +} + + +status_t +terminate_timer(void) +{ + status_t thread_return_value; + + delete_sem(sTimerSem); + return wait_for_thread(sTimerThread, &thread_return_value); +} + diff --git a/src/add-ons/kernel/bus_managers/firewire/util.c b/src/add-ons/kernel/bus_managers/firewire/util.c new file mode 100644 index 0000000000..edf1a943c3 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/util.c @@ -0,0 +1,90 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include + +//#define DEBUG + +#include "fwdebug.h" +#include "util.h" + +static inline uint32 +round_to_pagesize(uint32 size) +{ + return (size + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1); +} + +area_id +alloc_mem(void **virt, void **phy, size_t size, uint32 protection, const char *name) +{ + physical_entry pe; + void * virtadr; + area_id areaid; + status_t rv; + + TRACE("allocating %ld bytes for %s\n", size, name); + + size = round_to_pagesize(size); + areaid = create_area(name, &virtadr, B_ANY_KERNEL_ADDRESS, size, B_FULL_LOCK | B_CONTIGUOUS, protection); + if (areaid < B_OK) { + ERROR("couldn't allocate area %s\n", name); + return B_ERROR; + } + rv = get_memory_map(virtadr, size, &pe, 1); + if (rv < B_OK) { + delete_area(areaid); + ERROR("couldn't get mapping for %s\n", name); + return B_ERROR; + } + memset(virtadr, 0, size); + if (virt) + *virt = virtadr; + if (phy) + *phy = pe.address; + TRACE("area = %ld, size = %ld, virt = %p, phy = %p\n", areaid, size, virtadr, pe.address); + return areaid; +} + +area_id +map_mem(void **virt, void *phy, size_t size, uint32 protection, const char *name) +{ + uint32 offset; + void *phyadr; + void *mapadr; + area_id area; + + TRACE("mapping physical address %p with %ld bytes for %s\n", phy, size, name); + + offset = (uint32)phy & (B_PAGE_SIZE - 1); + phyadr = (char *)phy - offset; + size = round_to_pagesize(size + offset); + area = map_physical_memory(name, phyadr, size, B_ANY_KERNEL_BLOCK_ADDRESS, protection, &mapadr); + if (area < B_OK) { + ERROR("mapping '%s' failed, error 0x%lx (%s)\n", name, area, strerror(area)); + return area; + } + + *virt = (char *)mapadr + offset; + + TRACE("physical = %p, virtual = %p, offset = %ld, phyadr = %p, mapadr = %p, size = %ld, area = 0x%08lx\n", + phy, *virt, offset, phyadr, mapadr, size, area); + + return area; +} diff --git a/src/add-ons/kernel/bus_managers/firewire/util.h b/src/add-ons/kernel/bus_managers/firewire/util.h new file mode 100644 index 0000000000..da5b892a7c --- /dev/null +++ b/src/add-ons/kernel/bus_managers/firewire/util.h @@ -0,0 +1,27 @@ +/* Realtek RTL8169 Family Driver + * Copyright (C) 2004 Marcus Overhagen . All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies, and that both the + * copyright notice and this permission notice appear in supporting documentation. + * + * Marcus Overhagen makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * + * MARCUS OVERHAGEN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL MARCUS + * OVERHAGEN BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef __UTIL_H +#define __UTIL_H + +#include + +area_id alloc_mem(void **virt, void **phy, size_t size, uint32 protection, const char *name); +area_id map_mem(void **virt, void *phy, size_t size, uint32 protection, const char *name); + +#endif diff --git a/src/add-ons/kernel/drivers/bus/firewire/Jamfile b/src/add-ons/kernel/drivers/bus/firewire/Jamfile index a016bb97cf..67fa24ea9b 100644 --- a/src/add-ons/kernel/drivers/bus/firewire/Jamfile +++ b/src/add-ons/kernel/drivers/bus/firewire/Jamfile @@ -1,7 +1,7 @@ SubDir HAIKU_TOP src add-ons kernel drivers bus firewire ; UseHeaders [ FDirName $(HAIKU_TOP) headers compatibility bsd ] : true ; -UsePrivateHeaders firewire ; +UsePrivateHeaders firewire kernel ; SubDirCcFlags [ FDefines _KERNEL=1 ] ; diff --git a/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c b/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c index 18b56de286..b40c6e3a41 100644 --- a/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c +++ b/src/add-ons/kernel/drivers/bus/firewire/fw_raw.c @@ -64,6 +64,9 @@ //#include "fwmem.h" #include "iec68113.h" +#define TRACE(x...) +//#define TRACE(x...) dprintf(x) + #include "firewire_module.h" #define FWNODE_INVAL 0xffff @@ -103,7 +106,7 @@ fwdev_allocbuf(struct fw_xferq *q, b->psize = roundup2(b->psize, sizeof(uint32_t)); q->buf = gFirewire->fwdma_malloc_multiseg(sizeof(uint32_t), b->psize, b->nchunk * b->npacket); - +TRACE("fwdev_allocbuf %d\n", b->psize*b->nchunk*b->npacket); if (q->buf == NULL) { free(q->bulkxfer); q->bulkxfer = NULL; @@ -169,7 +172,7 @@ fw_open (const char *name, uint32 flags, void **cookie) return B_BAD_VALUE; } - if (gFirewire->get_handle(count, sc) != B_OK) { + if (gFirewire->get_handle(count, &sc) != B_OK) { return ENODEV; } @@ -220,7 +223,7 @@ fw_free(void *cookie) struct fw_xfer *xfer; struct fw_bind *fwb; - + TRACE("fw_free\n"); d = (struct fw_drv1 *)cookie; delete_sem(d->rqSem); fc = d->fc; @@ -979,11 +982,12 @@ init_driver() #endif if ((err = get_module(FIREWIRE_MODULE_NAME, (module_info **)&gFirewire)) != B_OK) { + dprintf("fw_raw: couldn't load "FIREWIRE_MODULE_NAME"\n"); return err; } - + devices_count = 0; - while (gFirewire->get_handle(devices_count, sc)==B_OK) { + while (gFirewire->get_handle(devices_count, &sc)==B_OK) { devices_count++; } @@ -997,6 +1001,7 @@ init_driver() } devices[devices_count] = NULL; + TRACE("fw_raw init_driver returns OK\n"); return B_OK; } diff --git a/src/bin/fwcontrol/fwcontrol.c b/src/bin/fwcontrol/fwcontrol.c index cf668d6523..1f4a14767b 100644 --- a/src/bin/fwcontrol/fwcontrol.c +++ b/src/bin/fwcontrol/fwcontrol.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD: src/usr.sbin/fwcontrol/fwcontrol.c,v 1.23 2006/10/26 22:33:3 #ifdef __HAIKU__ #include #include +#include #include "eui64.h" #include "firewire.h" #include "iec13213.h" @@ -624,15 +625,21 @@ dump_phy_registers(int fd) static void open_dev(int *fd, char *devbase) { +#ifndef __HAIKU__ char name[256]; int i; +#endif if (*fd < 0) { +#ifdef __HAIKU__ + *fd = open(devbase, O_RDWR); +#else for (i = 0; i < 4; i++) { snprintf(name, sizeof(name), "%s.%d", devbase, i); if ((*fd = open(name, O_RDWR)) >= 0) break; } +#endif if (*fd < 0) err(1, "open"); @@ -665,7 +672,7 @@ detect_recv_fn(int fd, char ich) bufreq.tx.nchunk = 0; bufreq.tx.npacket = 0; bufreq.tx.psize = 0; - +printf("detect dv format\n"); if (ioctl(fd, FW_SSTBUF, &bufreq) < 0) err(1, "ioctl FW_SSTBUF"); @@ -701,7 +708,7 @@ main(int argc, char **argv) { u_int32_t crom_buf[1024/4]; #ifdef __HAIKU__ - char devbase[1024] = "/dev/fw"; + char devbase[1024] = "/dev/bus/fw/0"; #else char devbase[1024] = "/dev/fw0"; #endif @@ -788,7 +795,11 @@ main(int argc, char **argv) break; case 'u': tmp = strtol(optarg, NULL, 0); +#ifdef __HAIKU__ + snprintf(devbase, sizeof(devbase), "/dev/bus/fw/%ld", tmp); +#else snprintf(devbase, sizeof(devbase), "/dev/fw%ld", tmp); +#endif if (fd > 0) { close(fd); fd = -1; diff --git a/src/bin/fwcontrol/fwcrom.c b/src/bin/fwcontrol/fwcrom.c index c6205d3478..694e874776 100644 --- a/src/bin/fwcontrol/fwcrom.c +++ b/src/bin/fwcontrol/fwcrom.c @@ -54,6 +54,12 @@ __FBSDID("$FreeBSD: src/sys/dev/firewire/fwcrom.c,v 1.14 2006/02/04 21:37:39 imp #ifndef __HAIKU__ #include #include +#else +#include +#include +#include +#include +#include "fwglue.h" #endif #else #include diff --git a/src/bin/fwcontrol/fwdv.c b/src/bin/fwcontrol/fwdv.c index e8ecbb74c1..63e5897e42 100644 --- a/src/bin/fwcontrol/fwdv.c +++ b/src/bin/fwcontrol/fwdv.c @@ -54,6 +54,7 @@ #include #ifdef __HAIKU__ #include +#include #include "firewire.h" #include "iec68113.h" #else diff --git a/src/bin/fwcontrol/fwmpegts.c b/src/bin/fwcontrol/fwmpegts.c index 75cfbd8882..c7cdf32a84 100644 --- a/src/bin/fwcontrol/fwmpegts.c +++ b/src/bin/fwcontrol/fwmpegts.c @@ -54,6 +54,7 @@ #include #ifdef __HAIKU__ #include +#include #include "firewire.h" #include "iec68113.h" #else