diff options
Diffstat (limited to 'drivers/ieee1394')
-rw-r--r-- | drivers/ieee1394/Config.in | 4 | ||||
-rw-r--r-- | drivers/ieee1394/Makefile | 11 | ||||
-rw-r--r-- | drivers/ieee1394/aic5800.c | 2 | ||||
-rw-r--r-- | drivers/ieee1394/csr.c | 3 | ||||
-rw-r--r-- | drivers/ieee1394/guid.c | 3 | ||||
-rw-r--r-- | drivers/ieee1394/highlevel.c | 3 | ||||
-rw-r--r-- | drivers/ieee1394/hosts.c | 3 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_core.c | 16 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_core.h | 10 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_syms.c | 3 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_transactions.c | 79 | ||||
-rw-r--r-- | drivers/ieee1394/ieee1394_types.h | 156 | ||||
-rw-r--r-- | drivers/ieee1394/ohci1394.c | 1552 | ||||
-rw-r--r-- | drivers/ieee1394/ohci1394.h | 133 | ||||
-rw-r--r-- | drivers/ieee1394/pcilynx.c | 185 | ||||
-rw-r--r-- | drivers/ieee1394/pcilynx.h | 31 | ||||
-rw-r--r-- | drivers/ieee1394/raw1394.c | 57 | ||||
-rw-r--r-- | drivers/ieee1394/raw1394.h | 28 | ||||
-rw-r--r-- | drivers/ieee1394/video1394.c | 1266 | ||||
-rw-r--r-- | drivers/ieee1394/video1394.h | 15 |
20 files changed, 2364 insertions, 1196 deletions
diff --git a/drivers/ieee1394/Config.in b/drivers/ieee1394/Config.in index df0e3cfd2..bdb3002d2 100644 --- a/drivers/ieee1394/Config.in +++ b/drivers/ieee1394/Config.in @@ -4,18 +4,20 @@ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then mainmenu_option next_comment comment 'IEEE 1394 (FireWire) support' - tristate 'IEEE 1394 (FireWire) support (EXPERIMENTAL)' CONFIG_IEEE1394 $CONFIG_PCI + dep_tristate 'IEEE 1394 (FireWire) support (EXPERIMENTAL)' CONFIG_IEEE1394 $CONFIG_PCI if [ "$CONFIG_IEEE1394" != "n" ]; then dep_tristate 'Texas Instruments PCILynx support' CONFIG_IEEE1394_PCILYNX $CONFIG_IEEE1394 if [ "$CONFIG_IEEE1394_PCILYNX" != "n" ]; then bool ' Use PCILynx local RAM' CONFIG_IEEE1394_PCILYNX_LOCALRAM + bool ' Support for non-IEEE1394 local ports' CONFIG_IEEE1394_PCILYNX_PORTS fi dep_tristate 'Adaptec AIC-5800 (AHA-89xx) support' CONFIG_IEEE1394_AIC5800 $CONFIG_IEEE1394 dep_tristate 'OHCI (Open Host Controller Interface) support' CONFIG_IEEE1394_OHCI1394 $CONFIG_IEEE1394 + dep_tristate 'Video1394 support' CONFIG_IEEE1394_VIDEO1394 $CONFIG_IEEE1394_OHCI1394 dep_tristate 'Raw IEEE1394 I/O support' CONFIG_IEEE1394_RAWIO $CONFIG_IEEE1394 diff --git a/drivers/ieee1394/Makefile b/drivers/ieee1394/Makefile index c89374f45..cc6b40ee4 100644 --- a/drivers/ieee1394/Makefile +++ b/drivers/ieee1394/Makefile @@ -53,13 +53,20 @@ else endif ifeq ($(CONFIG_IEEE1394_OHCI1394),y) -L_OBJS += ohci1394.o +LX_OBJS += ohci1394.o else ifeq ($(CONFIG_IEEE1394_OHCI1394),m) - M_OBJS += ohci1394.o + MX_OBJS += ohci1394.o endif endif +ifeq ($(CONFIG_IEEE1394_VIDEO1394),y) +L_OBJS += video1394.o +else + ifeq ($(CONFIG_IEEE1394_VIDEO1394),m) + M_OBJS += video1394.o + endif +endif ifeq ($(CONFIG_IEEE1394_RAWIO),y) L_OBJS += raw1394.o diff --git a/drivers/ieee1394/aic5800.c b/drivers/ieee1394/aic5800.c index 6cf3779d5..2748c21b6 100644 --- a/drivers/ieee1394/aic5800.c +++ b/drivers/ieee1394/aic5800.c @@ -646,7 +646,7 @@ static void aic_irq_handler(int irq, void *dev_id, struct pt_regs *regs) phyid = phyid & 0x3F; handle_selfid(aic, host, phyid, isroot, rcv_bytes); } else { - hpsb_packet_received(host, aic->rcv_page, rcv_bytes); + hpsb_packet_received(host, aic->rcv_page, rcv_bytes, 0); }; } else { PRINT(KERN_ERR, aic->id, diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c index a884f2a1c..486ad1ad4 100644 --- a/drivers/ieee1394/csr.c +++ b/drivers/ieee1394/csr.c @@ -4,6 +4,9 @@ * CSR implementation, iso/bus manager implementation. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/string.h> diff --git a/drivers/ieee1394/guid.c b/drivers/ieee1394/guid.c index 36ac332c5..1aa453292 100644 --- a/drivers/ieee1394/guid.c +++ b/drivers/ieee1394/guid.c @@ -4,6 +4,9 @@ * GUID collection and management * * Copyright (C) 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/kernel.h> diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c index 3e20824aa..130a40527 100644 --- a/drivers/ieee1394/highlevel.c +++ b/drivers/ieee1394/highlevel.c @@ -2,6 +2,9 @@ * IEEE 1394 for Linux * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 9a9951204..8e0e80da1 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -5,6 +5,9 @@ * * Copyright (C) 1999 Andreas E. Bombe * Copyright (C) 1999 Emanuel Pirker + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index ffcfc7f52..e0641271b 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -5,6 +5,9 @@ * highlevel or lowlevel code * * Copyright (C) 1999, 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/config.h> @@ -534,7 +537,7 @@ struct hpsb_packet *create_reply_packet(struct hpsb_host *host, quadlet_t *data, if (packet == NULL) break void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, - size_t size) + size_t size, int write_acked) { struct hpsb_packet *packet; int length, rcode, extcode; @@ -548,7 +551,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_write(host, source, data+3, addr, 4); - if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) { + if (!write_acked + && ((data[0] >> 16) & NODE_MASK) != NODE_MASK) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); fill_async_write_resp(packet, rcode); @@ -561,7 +565,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, rcode = highlevel_write(host, source, data+4, addr, data[3]>>16); - if (((data[0] >> 16) & NODE_MASK) != NODE_MASK) { + if (!write_acked + && ((data[0] >> 16) & NODE_MASK) != NODE_MASK) { /* not a broadcast write, reply */ PREP_REPLY_PACKET(0); fill_async_write_resp(packet, rcode); @@ -644,7 +649,8 @@ void handle_incoming_packet(struct hpsb_host *host, int tcode, quadlet_t *data, #undef PREP_REPLY_PACKET -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size) +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked) { int tcode; @@ -672,7 +678,7 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size) case TCODE_READQ: case TCODE_READB: case TCODE_LOCK_REQUEST: - handle_incoming_packet(host, tcode, data, size); + handle_incoming_packet(host, tcode, data, size, write_acked); break; diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h index 636aef40e..faeeca45d 100644 --- a/drivers/ieee1394/ieee1394_core.h +++ b/drivers/ieee1394/ieee1394_core.h @@ -52,8 +52,7 @@ struct hpsb_packet { * overwritten to allow in-place byte swapping. Neither of these is * CRCed (the sizes also don't include CRC), but contain space for at * least one additional quadlet to allow in-place CRCing. The memory is - * also guaranteed to have physical mapping (virt_to_bus() is meaningful - * on these pointers). + * also guaranteed to be DMA mappable. */ quadlet_t *header; quadlet_t *data; @@ -145,7 +144,12 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, * immediately), with the header (i.e. the first four quadlets) in machine byte * order and the data block in big endian. *data can be safely overwritten * after this call. + * + * If the packet is a write request, write_acked is to be set to true if it was + * ack_complete'd already, false otherwise. This arg is ignored for any other + * packet type. */ -void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size); +void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, + int write_acked); #endif /* _IEEE1394_CORE_H */ diff --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c index c1ebadd10..39a61a01b 100644 --- a/drivers/ieee1394/ieee1394_syms.c +++ b/drivers/ieee1394/ieee1394_syms.c @@ -4,6 +4,9 @@ * Exported symbols for module usage. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/types.h> diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index c5c5cc4ad..179834ac7 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -4,6 +4,9 @@ * Transaction support. * * Copyright (C) 1999 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/sched.h> @@ -152,38 +155,58 @@ void fill_iso_packet(struct hpsb_packet *packet, int length, int channel, * Return value: The allocated transaction label or -1 if there was no free * tlabel and @wait is false. */ +static int __get_tlabel(struct hpsb_host *host, nodeid_t nodeid) +{ + int tlabel; + + if (host->tlabel_count) { + host->tlabel_count--; + + if (host->tlabel_pool[0] != ~0) { + tlabel = ffz(host->tlabel_pool[0]); + host->tlabel_pool[0] |= 1 << tlabel; + } else { + tlabel = ffz(host->tlabel_pool[1]); + host->tlabel_pool[1] |= 1 << tlabel; + tlabel += 32; + } + return tlabel; + } + return -1; +} + int get_tlabel(struct hpsb_host *host, nodeid_t nodeid, int wait) { unsigned long flags; int tlabel; - - while (1) { - spin_lock_irqsave(&host->tlabel_lock, flags); - - if (host->tlabel_count) { - host->tlabel_count--; - - if (host->tlabel_pool[0] != ~0) { - tlabel = ffz(host->tlabel_pool[0]); - host->tlabel_pool[0] |= 1 << tlabel; - } else { - tlabel = ffz(host->tlabel_pool[1]); - host->tlabel_pool[1] |= 1 << tlabel; - tlabel += 32; - } - - spin_unlock_irqrestore(&host->tlabel_lock, flags); - return tlabel; - } - - spin_unlock_irqrestore(&host->tlabel_lock, flags); - - if (wait) { - sleep_on(&host->tlabel_wait); - } else { - return -1; - } - } + wait_queue_t wq; + + spin_lock_irqsave(&host->tlabel_lock, flags); + + tlabel = __get_tlabel(host, nodeid); + if (tlabel != -1 || !wait) { + spin_unlock_irqrestore(&host->tlabel_lock, flags); + return tlabel; + } + + init_waitqueue_entry(&wq, current); + add_wait_queue(&host->tlabel_wait, &wq); + + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + tlabel = __get_tlabel(host, nodeid); + if (tlabel != -1) break; + + spin_unlock_irqrestore(&host->tlabel_lock, flags); + schedule(); + spin_lock_irqsave(&host->tlabel_lock, flags); + } + + spin_unlock_irqrestore(&host->tlabel_lock, flags); + set_current_state(TASK_RUNNING); + remove_wait_queue(&host->tlabel_wait, &wq); + + return tlabel; } /** diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h index 411b79b36..84be6aa05 100644 --- a/drivers/ieee1394/ieee1394_types.h +++ b/drivers/ieee1394/ieee1394_types.h @@ -11,15 +11,23 @@ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#include <linux/wait.h> #define DECLARE_WAITQUEUE(name, task) struct wait_queue name = { task, NULL } typedef struct wait_queue *wait_queue_head_t; +typedef struct wait_queue wait_queue_t; inline static void init_waitqueue_head(wait_queue_head_t *wh) { *wh = NULL; } +inline static void init_waitqueue_entry(wait_queue_t *wq, struct task_struct *p) +{ + wq->task = p; + wq->next = NULL; +} + static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); @@ -30,6 +38,22 @@ static __inline__ void list_add_tail(struct list_head *new, struct list_head *he #define set_current_state(state_value) \ do { current->state = (state_value); } while (0) + +#include <asm/page.h> +/* Pure 2^n version of get_order */ +extern __inline__ int get_order(unsigned long size) +{ + int order; + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + + #include <linux/pci.h> inline static int pci_enable_device(struct pci_dev *dev) { @@ -39,8 +63,134 @@ inline static int pci_enable_device(struct pci_dev *dev) return 0; } +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 +#define PCI_ROM_RESOURCE 6 +#define pci_resource_start(dev, bar) ((bar) == PCI_ROM_RESOURCE \ + ? (dev)->rom_address \ + : (dev)->base_address[(bar)]) +#define BUG() *(int *)0 = 0 + +#include <asm/io.h> +typedef u32 dma_addr_t; + +extern inline int pci_dma_supported(struct pci_dev *hwdev, dma_addr_t mask) +{ + return 1; +} + +extern inline void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + void *ret; + ret = (void *)__get_free_pages(GFP_ATOMIC, get_order(size)); + if (ret) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +extern inline void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} + +extern inline dma_addr_t pci_map_single(struct pci_dev *hwdev, void *ptr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + return virt_to_bus(ptr); +} + +extern inline void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +struct scatterlist {}; +extern inline int pci_map_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); + return nents; +} + +extern inline void pci_unmap_sg(struct pci_dev *hwdev, struct scatterlist *sg, + int nents, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +extern inline void pci_dma_sync_single(struct pci_dev *hwdev, + dma_addr_t dma_handle, + size_t size, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + +extern inline void pci_dma_sync_sg(struct pci_dev *hwdev, + struct scatterlist *sg, + int nelems, int direction) +{ + if (direction == PCI_DMA_NONE) + BUG(); +} + + +#ifndef _LINUX_DEVFS_FS_KERNEL_H +typedef struct devfs_entry * devfs_handle_t; +#define DEVFS_FL_NONE 0 + +static inline devfs_handle_t devfs_register (devfs_handle_t dir, + const char *name, + unsigned int flags, + unsigned int major, + unsigned int minor, + umode_t mode, + void *ops, void *info) +{ + return NULL; +} +static inline void devfs_unregister (devfs_handle_t de) +{ + return; +} +static inline int devfs_register_chrdev (unsigned int major, const char *name, + struct file_operations *fops) +{ + return register_chrdev (major, name, fops); +} +static inline int devfs_unregister_chrdev (unsigned int major,const char *name) +{ + return unregister_chrdev (major, name); +} +#endif /* _LINUX_DEVFS_FS_KERNEL_H */ + + +#define V22_COMPAT_MOD_INC_USE_COUNT MOD_INC_USE_COUNT +#define V22_COMPAT_MOD_DEC_USE_COUNT MOD_DEC_USE_COUNT +#define OWNER_THIS_MODULE + +#else /* Linux version < 2.3 */ + +#define V22_COMPAT_MOD_INC_USE_COUNT do {} while (0) +#define V22_COMPAT_MOD_DEC_USE_COUNT do {} while (0) +#define OWNER_THIS_MODULE owner: THIS_MODULE, + #endif /* Linux version < 2.3 */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,18) #include <asm/spinlock.h> #else @@ -55,9 +205,9 @@ inline static int pci_enable_device(struct pci_dev *dev) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #endif -typedef __u32 quadlet_t; -typedef __u64 octlet_t; -typedef __u16 nodeid_t; +typedef u32 quadlet_t; +typedef u64 octlet_t; +typedef u16 nodeid_t; #define BUS_MASK 0xffc0 #define NODE_MASK 0x003f diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index a0933c3e4..05e1063d7 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -25,10 +25,10 @@ * . Async Request Receive * . Async Response Transmit * . Iso Receive + * . DMA mmap for iso receive * * Things not implemented: * . Iso Transmit - * . DMA to user's space in iso receive mode * . DMA error recovery * * Things to be fixed: @@ -38,7 +38,6 @@ * . Self-id are sometimes not received properly * if card is initialized with no other nodes * on the bus - * . SONY CXD3222 chip is not working properly * . Apple PowerBook detected but not working yet */ @@ -56,7 +55,7 @@ * Albrecht Dress <ad@mpifr-bonn.mpg.de> * . Apple PowerBook detection * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> - * . Reset the board properly before leaving + * . Reset the board properly before leaving + misc cleanups */ #include <linux/config.h> @@ -130,6 +129,8 @@ int supported_chips[][2] = { { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72870 }, { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72871 }, { PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_FW }, + { PCI_VENDOR_ID_AL, PCI_DEVICE_ID_ALI_OHCI1394_M5251 }, + { PCI_VENDOR_ID_LUCENT, PCI_DEVICE_ID_LUCENT_FW323 }, { -1, -1 } }; @@ -142,617 +143,6 @@ static int init_driver(void); static void dma_trm_bh(void *data); static void dma_rcv_bh(void *data); static void dma_trm_reset(struct dma_trm_ctx *d); -static void stop_context(struct ti_ohci *ohci, int reg, char *msg); - -#ifdef _VIDEO_1394_H - -/* Taken from bttv.c */ -/*******************************/ -/* Memory management functions */ -/*******************************/ - -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guarenteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) - ret = (pte_page(pte)|(adr&(PAGE_SIZE-1))); - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long kvirt_to_bus(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - -static void * rvmalloc(unsigned long size) -{ - void * mem; - unsigned long adr, page; - - mem=vmalloc(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_reserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr, page; - - if (mem) - { - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_unreserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } -} - -static int free_dma_fbuf_ctx(struct dma_fbuf_ctx **d) -{ - int i; - struct ti_ohci *ohci; - - if ((*d)==NULL) return -1; - - ohci = (struct ti_ohci *)(*d)->ohci; - - DBGMSG(ohci->id, "Freeing dma_fbuf_ctx %d", (*d)->ctx); - - stop_context(ohci, (*d)->ctrlClear, NULL); - - if ((*d)->buf) rvfree((void *)(*d)->buf, - (*d)->num_desc * (*d)->buf_size); - - if ((*d)->prg) { - for (i=0;i<(*d)->num_desc;i++) - if ((*d)->prg[i]) kfree((*d)->prg[i]); - kfree((*d)->prg); - } - - if ((*d)->buffer_status) - kfree((*d)->buffer_status); - - kfree(*d); - *d = NULL; - - return 0; -} - -static struct dma_fbuf_ctx * -alloc_dma_fbuf_ctx(struct ti_ohci *ohci, int ctx, int num_desc, - int buf_size, int channel) -{ - struct dma_fbuf_ctx *d=NULL; - int i; - - d = (struct dma_fbuf_ctx *)kmalloc(sizeof(struct dma_fbuf_ctx), - GFP_KERNEL); - - if (d==NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma_fbuf_ctx"); - return NULL; - } - - d->ohci = (void *)ohci; - d->ctx = ctx; - d->channel = channel; - d->num_desc = num_desc; - d->frame_size = buf_size; - if (buf_size%PAGE_SIZE) - d->buf_size = buf_size + PAGE_SIZE - (buf_size%PAGE_SIZE); - else - d->buf_size = buf_size; - d->ctrlSet = OHCI1394_IrRcvContextControlSet+32*d->ctx; - d->ctrlClear = OHCI1394_IrRcvContextControlClear+32*d->ctx; - d->cmdPtr = OHCI1394_IrRcvCommandPtr+32*d->ctx; - d->ctxMatch = OHCI1394_IrRcvContextMatch+32*d->ctx; - d->nb_cmd = d->buf_size / PAGE_SIZE + 1; - d->last_buffer = 0; - d->buf = NULL; - d->prg = NULL; - init_waitqueue_head(&d->waitq); - - d->buf = rvmalloc(d->num_desc * d->buf_size); - - if (d->buf == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuffer"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->buf, 0, d->num_desc * d->buf_size); - - d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *), - GFP_KERNEL); - - if (d->prg == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd *)); - - for (i=0;i<d->num_desc;i++) { - d->prg[i] = kmalloc(d->nb_cmd * sizeof(struct dma_cmd), - GFP_KERNEL); - if (d->prg[i] == NULL) { - PRINT(KERN_ERR, ohci->id, - "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - } - - d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), - GFP_KERNEL); - - if (d->buffer_status == NULL) { - PRINT(KERN_ERR, ohci->id, "failed to allocate dma fbuf prg"); - free_dma_fbuf_ctx(&d); - return NULL; - } - memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); - - PRINT(KERN_INFO, ohci->id, "Iso DMA to User's Space: %d buffers " - "of size %d allocated for a frame size %d, each with %d prgs", - d->num_desc, d->buf_size, d->frame_size, d->nb_cmd); - - return d; -} - -static void initialize_dma_fbuf_prg(struct dma_cmd *prg, int n, - int frame_size, unsigned long buf) -{ - int i; - int leftsize = (frame_size%PAGE_SIZE) ? - frame_size%PAGE_SIZE : PAGE_SIZE; - - /* the first descriptor will sync and read only 4 bytes */ - prg[0].control = (0x280F << 16) | 4; - prg[0].address = kvirt_to_bus(buf); - prg[0].branchAddress = (virt_to_bus(&(prg[1].control)) - & 0xfffffff0) | 0x1; - prg[0].status = 0; - - /* the second descriptor will read PAGE_SIZE-4 bytes */ - prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); - prg[1].address = kvirt_to_bus(buf+4); - prg[1].branchAddress = (virt_to_bus(&(prg[2].control)) - & 0xfffffff0) | 0x1; - prg[1].status = 0; - - for (i=2;i<n-1;i++) { - prg[i].control = (0x280C << 16) | PAGE_SIZE; - prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); - - prg[i].branchAddress = - (virt_to_bus(&(prg[i+1].control)) - & 0xfffffff0) | 0x1; - - prg[i].status = 0; - } - - /* the last descriptor will generate an interrupt */ - prg[i].control = (0x283C << 16) | leftsize; - prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); - prg[i].status = 0; -} - -static void initialize_dma_fbuf_ctx(struct dma_fbuf_ctx *d, int tag) -{ - struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; - int i; - - stop_context(ohci, d->ctrlClear, NULL); - - for (i=0;i<d->num_desc;i++) { - initialize_dma_fbuf_prg(d->prg[i], d->nb_cmd, d->frame_size, - (unsigned long)d->buf+i*d->buf_size); - } - - /* Set bufferFill, no header */ - reg_write(ohci, d->ctrlSet, 0x80000000); - - /* Set the context match register to match on all tags, - sync for sync tag, and listen to d->channel */ - reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); - - /* Set up isoRecvIntMask to generate interrupts */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); -} - -/* find which context is listening to this channel */ -int fbuf_ctx_listening(struct ti_ohci *ohci, int channel) -{ - int i; - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]) { - if (ohci->fbuf_context[i]->channel==channel) - return i; - } - - PRINT(KERN_ERR, ohci->id, - "no iso context is listening to channel %d", - channel); - return -1; -} - -static int ohci_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - - switch(cmd) - { - case VIDEO1394_LISTEN_CHANNEL: - { - struct video1394_mmap v; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { - PRINT(KERN_ERR, ohci->id, - "iso channel %d out of bound", v.channel); - return -EFAULT; - } - if (test_and_set_bit(v.channel, &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is already taken", v.channel); - return -EFAULT; - } - - /* find a free iso context */ - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]==NULL) break; - - if (i==(ohci->nb_iso_ctx-1)) { - PRINT(KERN_ERR, ohci->id, "no iso context available"); - return -EFAULT; - } - - if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { - PRINT(KERN_ERR, ohci->id, - "%d buffers of size %d bytes is too big", - v.nb_buffers, v.buf_size); - return -EFAULT; - } - - ohci->fbuf_context[i] = - alloc_dma_fbuf_ctx(ohci, i+1, v.nb_buffers, - v.buf_size, v.channel); - - if (ohci->fbuf_context[i] == NULL) { - PRINT(KERN_ERR, ohci->id, - "Couldn't allocate fbuf context"); - return -EFAULT; - } - initialize_dma_fbuf_ctx(ohci->fbuf_context[i], v.sync_tag); - - ohci->current_fbuf_ctx = ohci->fbuf_context[i]; - - v.buf_size = ohci->fbuf_context[i]->buf_size; - - PRINT(KERN_INFO, ohci->id, - "iso context %d listen on channel %d", i+1, - v.channel); - - if(copy_to_user((void *)arg, &v, sizeof(v))) - return -EFAULT; - - return 0; - } - case VIDEO1394_UNLISTEN_CHANNEL: - { - int channel; - int i; - - if(copy_from_user(&channel, (void *)arg, sizeof(int))) - return -EFAULT; - - if (!test_and_clear_bit(channel, &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is not being used", channel); - return -EFAULT; - } - - i = fbuf_ctx_listening(ohci, channel); - if (i<0) return -EFAULT; - - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - - PRINT(KERN_INFO, ohci->id, - "iso context %d stop listening on channel %d", - i+1, channel); - - return 0; - } - case VIDEO1394_QUEUE_BUFFER: - { - struct video1394_wait v; - struct dma_fbuf_ctx *d; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - - i = fbuf_ctx_listening(ohci, v.channel); - if (i<0) return -EFAULT; - d = ohci->fbuf_context[i]; - - if ((v.buffer<0) || (v.buffer>d->num_desc)) { - PRINT(KERN_ERR, ohci->id, - "buffer %d out of range",v.buffer); - return -EFAULT; - } - - if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { - PRINT(KERN_ERR, ohci->id, - "buffer %d is already used",v.buffer); - return -EFAULT; - } - - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; - - d->prg[d->last_buffer][d->nb_cmd-1].branchAddress = - (virt_to_bus(&(d->prg[v.buffer][0].control)) - & 0xfffffff0) | 0x1; - - d->last_buffer = v.buffer; - - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) - { - DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx); - - /* Tell the controller where the first program is */ - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[v.buffer][0])) | 0x1 ); - - /* Run IR context */ - reg_write(ohci, d->ctrlSet, 0x8000); - } - else { - /* Wake up dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { - PRINT(KERN_INFO, ohci->id, - "Waking up iso dma ctx=%d", d->ctx); - reg_write(ohci, d->ctrlSet, 0x1000); - } - } - return 0; - - } - case VIDEO1394_WAIT_BUFFER: - { - struct video1394_wait v; - struct dma_fbuf_ctx *d; - int i; - - if(copy_from_user(&v, (void *)arg, sizeof(v))) - return -EFAULT; - - i = fbuf_ctx_listening(ohci, v.channel); - if (i<0) return -EFAULT; - d = ohci->fbuf_context[i]; - - if ((v.buffer<0) || (v.buffer>d->num_desc)) { - PRINT(KERN_ERR, ohci->id, - "buffer %d out of range",v.buffer); - return -EFAULT; - } - - switch(d->buffer_status[v.buffer]) { - case VIDEO1394_BUFFER_READY: - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - case VIDEO1394_BUFFER_QUEUED: - while(d->buffer_status[v.buffer]!= - VIDEO1394_BUFFER_READY) { - interruptible_sleep_on(&d->waitq); - if(signal_pending(current)) return -EINTR; - } - d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; - return 0; - default: - PRINT(KERN_ERR, ohci->id, - "buffer %d is not queued",v.buffer); - return -EFAULT; - } - } - default: - return -EINVAL; - } -} - -/* - * This maps the vmalloced and reserved fbuffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - remap_page_range is kind of inefficient for page by page remapping. - * But e.g. pte_alloc() does not work in modules ... :-( - */ - -static int do_fbuf_mmap(struct ti_ohci *ohci, struct dma_fbuf_ctx *d, - const char *adr, unsigned long size) -{ - unsigned long start=(unsigned long) adr; - unsigned long page,pos; - - if (size>d->num_desc * d->buf_size) { - PRINT(KERN_ERR, ohci->id, - "fbuf context %d buf size is different from mmap size", - d->ctx); - return -EINVAL; - } - if (!d->buf) { - PRINT(KERN_ERR, ohci->id, - "fbuf context %d is not allocated", d->ctx); - return -EINVAL; - } - - pos=(unsigned long) d->buf; - while (size > 0) { - page = kvirt_to_pa(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - size-=PAGE_SIZE; - } - return 0; -} - -int ohci_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ti_ohci *ohci=&cards[MINOR(file->f_dentry->d_inode->i_rdev)]; - PRINT(KERN_INFO, ohci->id, "mmap"); - if (ohci->current_fbuf_ctx == NULL) { - PRINT(KERN_ERR, ohci->id, "current fbuf context not set"); - return -EINVAL; - } - - return do_fbuf_mmap(ohci, ohci->current_fbuf_ctx, - (char *)vma->vm_start, - (unsigned long)(vma->vm_end-vma->vm_start)); - return 0; -} - -static int ohci_open(struct inode *inode, struct file *file) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - PRINT(KERN_INFO, ohci->id, "open"); - return 0; -} - -static int ohci_release(struct inode *inode, struct file *file) -{ - struct ti_ohci *ohci=&cards[MINOR(inode->i_rdev)]; - int i; - - PRINT(KERN_INFO, ohci->id, "release"); - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (ohci->fbuf_context[i]) { - if (!test_and_clear_bit(ohci->fbuf_context[i]->channel, - &ohci->IR_channel_usage)) { - PRINT(KERN_ERR, ohci->id, - "channel %d is not being used", - ohci->fbuf_context[i]->channel); - } - PRINT(KERN_INFO, ohci->id, - "iso context %d stop listening on channel %d", - i+1, ohci->fbuf_context[i]->channel); - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - } - return 0; -} - -static struct file_operations ohci_fops= -{ - owner: THIS_MODULE, - ioctl: ohci_ioctl, - mmap: ohci_mmap, - open: ohci_open, - release: ohci_release -}; - -int wakeup_dma_fbuf_ctx(struct ti_ohci *ohci, struct dma_fbuf_ctx *d) -{ - int i; - - if (d==NULL) { - PRINT(KERN_ERR, ohci->id, "Iso receive event received but " - "context not allocated"); - return -EFAULT; - } - - for (i=0;i<d->num_desc;i++) { - if (d->prg[i][d->nb_cmd-1].status) { - d->prg[i][d->nb_cmd-1].status=0; - d->buffer_status[i] = VIDEO1394_BUFFER_READY; - } - } - if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); - return 0; -} - -#endif - - /*********************************** * IEEE-1394 functionality section * @@ -827,14 +217,14 @@ static int set_phy_reg(struct ti_ohci *ohci, int addr, unsigned char data) { inline static int handle_selfid(struct ti_ohci *ohci, struct hpsb_host *host, int phyid, int isroot) { - quadlet_t *q = ohci->self_id_buffer; + quadlet_t *q = ohci->selfid_buf_cpu; quadlet_t self_id_count=reg_read(ohci, OHCI1394_SelfIDCount); size_t size; quadlet_t lsid; /* Self-id handling seems much easier than for the aic5800 chip. All the self-id packets, including this device own self-id, - should be correctly arranged in the self_id_buffer at this + should be correctly arranged in the selfid buffer at this stage */ /* Check status of self-id reception */ @@ -952,55 +342,36 @@ static int run_context(struct ti_ohci *ohci, int reg, char *msg) return 0; } -static void stop_context(struct ti_ohci *ohci, int reg, char *msg) -{ - int i=0; - - /* stop the channel program if it's still running */ - reg_write(ohci, reg, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, reg) & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, ohci->id, - "runaway loop while stopping context..."); - break; - } - } - if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg); -} - /* Generate the dma receive prgs and start the context */ static void initialize_dma_rcv_ctx(struct dma_rcv_ctx *d) { struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); int i; - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); for (i=0; i<d->num_desc; i++) { /* end of descriptor list? */ if ((i+1) < d->num_desc) { - d->prg[i]->control = (0x283C << 16) | d->buf_size; - d->prg[i]->branchAddress = - (virt_to_bus(d->prg[i+1]) & 0xfffffff0) | 0x1; + d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; + d->prg_cpu[i]->branchAddress = + (d->prg_bus[i+1] & 0xfffffff0) | 0x1; } else { - d->prg[i]->control = (0x283C << 16) | d->buf_size; - d->prg[i]->branchAddress = - (virt_to_bus(d->prg[0]) & 0xfffffff0); + d->prg_cpu[i]->control = (0x283C << 16) | d->buf_size; + d->prg_cpu[i]->branchAddress = + d->prg_bus[0] & 0xfffffff0; } - d->prg[i]->address = virt_to_bus(d->buf[i]); - d->prg[i]->status = d->buf_size; + d->prg_cpu[i]->address = d->buf_bus[i]; + d->prg_cpu[i]->status = d->buf_size; } d->buf_ind = 0; d->buf_offset = 0; /* Tell the controller where the first AR program is */ - reg_write(ohci, d->cmdPtr, virt_to_bus(d->prg[0]) | 0x1); + reg_write(ohci, d->cmdPtr, d->prg_bus[0] | 0x1); /* Run AR context */ reg_write(ohci, d->ctrlSet, 0x00008000); @@ -1014,26 +385,30 @@ static void initialize_dma_trm_ctx(struct dma_trm_ctx *d) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); /* Stop the context */ - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); d->prg_ind = 0; d->sent_ind = 0; d->free_prgs = d->num_desc; d->branchAddrPtr = NULL; - d->first = NULL; - d->last = NULL; + d->fifo_first = NULL; + d->fifo_last = NULL; + d->pending_first = NULL; + d->pending_last = NULL; PRINT(KERN_INFO, ohci->id, "AT dma ctx=%d initialized", d->ctx); } /* Count the number of available iso contexts */ -static int get_nb_iso_ctx(struct ti_ohci *ohci) +static int get_nb_iso_ctx(struct ti_ohci *ohci, int reg) { int i,ctx=0; u32 tmp; - reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0xffffffff); - tmp = reg_read(ohci, OHCI1394_IsoRecvIntMaskSet); + reg_write(ohci, reg, 0xffffffff); + tmp = reg_read(ohci, reg); + + DBGMSG(ohci->id,"Iso contexts reg: %08x implemented: %08x", reg, tmp); /* Count the number of contexts */ for(i=0; i<32; i++) { @@ -1062,7 +437,7 @@ static int ohci_initialize(struct hpsb_host *host) if ((retval=ohci_soft_reset(ohci))<0) return retval; /* - *Delay aftger soft reset to make sure everything has settled + * Delay after soft reset to make sure everything has settled * down (sanity) */ mdelay(100); @@ -1089,31 +464,33 @@ static int ohci_initialize(struct hpsb_host *host) reg_write(ohci, OHCI1394_LinkControlSet, 0x00300000); /* Clear interrupt registers */ - reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); reg_write(ohci, OHCI1394_IntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IntEventClear, 0xffffffff); /* Set up self-id dma buffer */ - reg_write(ohci, OHCI1394_SelfIDBuffer, - virt_to_bus(ohci->self_id_buffer)); + reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); /* enable self-id dma */ reg_write(ohci, OHCI1394_LinkControlSet, 0x00000200); /* Set the configuration ROM mapping register */ - reg_write(ohci, OHCI1394_ConfigROMmap, - virt_to_bus(ohci->csr_config_rom)); - - /* Write the config ROM header */ - reg_write(ohci, OHCI1394_ConfigROMhdr, - cpu_to_be32(ohci->csr_config_rom[0])); + reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); /* Set bus options */ reg_write(ohci, OHCI1394_BusOptions, - cpu_to_be32(ohci->csr_config_rom[2])); - + cpu_to_be32(ohci->csr_config_rom_cpu[2])); + +#if 0 /* Write the GUID into the csr config rom */ - ohci->csr_config_rom[3] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi)); - ohci->csr_config_rom[4] = be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo)); + ohci->csr_config_rom_cpu[3] = + be32_to_cpu(reg_read(ohci, OHCI1394_GUIDHi)); + ohci->csr_config_rom_cpu[4] = + be32_to_cpu(reg_read(ohci, OHCI1394_GUIDLo)); +#endif + + /* Write the config ROM header */ + reg_write(ohci, OHCI1394_ConfigROMhdr, + cpu_to_be32(ohci->csr_config_rom_cpu[0])); ohci->max_packet_size = 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); @@ -1124,39 +501,56 @@ static int ohci_initialize(struct hpsb_host *host) reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); /* Initialize IR dma */ - ohci->nb_iso_ctx = get_nb_iso_ctx(ohci); - PRINT(KERN_INFO, ohci->id, "%d iso contexts available", - ohci->nb_iso_ctx); - for (i=0;i<ohci->nb_iso_ctx;i++) { - reg_write(ohci, OHCI1394_IrRcvContextControlClear+32*i, + ohci->nb_iso_rcv_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoRecvIntMaskSet); + PRINT(KERN_INFO, ohci->id, "%d iso receive contexts available", + ohci->nb_iso_rcv_ctx); + for (i=0;i<ohci->nb_iso_rcv_ctx;i++) { + reg_write(ohci, OHCI1394_IsoRcvContextControlClear+32*i, 0xffffffff); - reg_write(ohci, OHCI1394_IrRcvContextMatch+32*i, 0); - reg_write(ohci, OHCI1394_IrRcvCommandPtr+32*i, 0); - } -#ifdef _VIDEO_1394_H - ohci->fbuf_context = (struct dma_fbuf_ctx **) - kmalloc((ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *), - GFP_KERNEL); - if (ohci->fbuf_context) - memset(ohci->fbuf_context, 0, - (ohci->nb_iso_ctx-1)*sizeof(struct dma_fbuf_ctx *)); - else { - PRINT(KERN_ERR, ohci->id, "Cannot allocate fbuf_context"); - return -1; + reg_write(ohci, OHCI1394_IsoRcvContextMatch+32*i, 0); + reg_write(ohci, OHCI1394_IsoRcvCommandPtr+32*i, 0); } -#endif + /* Set bufferFill, isochHeader, multichannel for IR context */ - reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000); + reg_write(ohci, OHCI1394_IsoRcvContextControlSet, 0xd0000000); /* Set the context match register to match on all tags */ - reg_write(ohci, OHCI1394_IrRcvContextMatch, 0xf0000000); + reg_write(ohci, OHCI1394_IsoRcvContextMatch, 0xf0000000); + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); + + /* Initialize IT dma */ + ohci->nb_iso_xmit_ctx = + get_nb_iso_ctx(ohci, OHCI1394_IsoXmitIntMaskSet); + PRINT(KERN_INFO, ohci->id, "%d iso transmit contexts available", + ohci->nb_iso_xmit_ctx); + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) { + reg_write(ohci, OHCI1394_IsoXmitContextControlClear+32*i, + 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitCommandPtr+32*i, 0); + } + + /* Clear the interrupt mask */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, 0xffffffff); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, 0xffffffff); /* Clear the multi channel mask high and low registers */ reg_write(ohci, OHCI1394_IRMultiChanMaskHiClear, 0xffffffff); reg_write(ohci, OHCI1394_IRMultiChanMaskLoClear, 0xffffffff); - /* Clear the interrupt mask */ - reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); + /* Initialize AR dma */ + initialize_dma_rcv_ctx(ohci->ar_req_context); + initialize_dma_rcv_ctx(ohci->ar_resp_context); + + /* Initialize AT dma */ + initialize_dma_trm_ctx(ohci->at_req_context); + initialize_dma_trm_ctx(ohci->at_resp_context); + + /* Initialize IR dma */ + initialize_dma_rcv_ctx(ohci->ir_context); /* Set up isoRecvIntMask to generate interrupts for context 0 (thanks to Michael Greger for seeing that I forgot this) */ @@ -1193,23 +587,13 @@ static int ohci_initialize(struct hpsb_host *host) OHCI1394_ARRQ | OHCI1394_respTxComplete | OHCI1394_reqTxComplete | - OHCI1394_isochRx + OHCI1394_isochRx | + OHCI1394_isochTx ); /* Enable link */ reg_write(ohci, OHCI1394_HCControlSet, 0x00020000); - /* Initialize AR dma */ - initialize_dma_rcv_ctx(ohci->ar_req_context); - initialize_dma_rcv_ctx(ohci->ar_resp_context); - - /* Initialize AT dma */ - initialize_dma_trm_ctx(ohci->at_req_context); - initialize_dma_trm_ctx(ohci->at_resp_context); - - /* Initialize IR dma */ - initialize_dma_rcv_ctx(ohci->ir_context); - return 1; } @@ -1223,74 +607,132 @@ static void ohci_remove(struct hpsb_host *host) } } -/* Insert a packet in the AT DMA fifo and generate the DMA prg */ +/* + * Insert a packet in the AT DMA fifo and generate the DMA prg + * FIXME: rewrite the program in order to accept packets crossing + * page boundaries. + * check also that a single dma descriptor doesn't cross a + * page boundary. + */ static void insert_packet(struct ti_ohci *ohci, struct dma_trm_ctx *d, struct hpsb_packet *packet) { u32 cycleTimer; int idx = d->prg_ind; - d->prg[idx].begin.address = 0; - d->prg[idx].begin.branchAddress = 0; + d->prg_cpu[idx]->begin.address = 0; + d->prg_cpu[idx]->begin.branchAddress = 0; if (d->ctx==1) { /* * For response packets, we need to put a timeout value in * the 16 lower bits of the status... let's try 1 sec timeout */ cycleTimer = reg_read(ohci, OHCI1394_IsochronousCycleTimer); - d->prg[idx].begin.status = + d->prg_cpu[idx]->begin.status = (((((cycleTimer>>25)&0x7)+1)&0x7)<<13) | ((cycleTimer&0x01fff000)>>12); DBGMSG(ohci->id, "cycleTimer: %08x timeStamp: %08x", - cycleTimer, d->prg[idx].begin.status); + cycleTimer, d->prg_cpu[idx]->begin.status); } else - d->prg[idx].begin.status = 0; + d->prg_cpu[idx]->begin.status = 0; - d->prg[idx].data[0] = packet->speed_code<<16 | + d->prg_cpu[idx]->data[0] = packet->speed_code<<16 | (packet->header[0] & 0xFFFF); - d->prg[idx].data[1] = (packet->header[1] & 0xFFFF) | + d->prg_cpu[idx]->data[1] = (packet->header[1] & 0xFFFF) | (packet->header[0] & 0xFFFF0000); - d->prg[idx].data[2] = packet->header[2]; - d->prg[idx].data[3] = packet->header[3]; + d->prg_cpu[idx]->data[2] = packet->header[2]; + d->prg_cpu[idx]->data[3] = packet->header[3]; if (packet->data_size) { /* block transmit */ - d->prg[idx].begin.control = OUTPUT_MORE_IMMEDIATE | 0x10; - d->prg[idx].end.control = OUTPUT_LAST | packet->data_size; - d->prg[idx].end.address = virt_to_bus(packet->data); - d->prg[idx].end.branchAddress = 0; - d->prg[idx].end.status = 0x4000; + d->prg_cpu[idx]->begin.control = OUTPUT_MORE_IMMEDIATE | 0x10; + d->prg_cpu[idx]->end.control = OUTPUT_LAST | packet->data_size; + /* + * FIXME: check that the packet data buffer + * do not cross a page boundary + */ + d->prg_cpu[idx]->end.address = + pci_map_single(ohci->dev, packet->data, + packet->data_size, PCI_DMA_TODEVICE); + d->prg_cpu[idx]->end.branchAddress = 0; + d->prg_cpu[idx]->end.status = 0; if (d->branchAddrPtr) - *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x3; - d->branchAddrPtr = &(d->prg[idx].end.branchAddress); + *(d->branchAddrPtr) = d->prg_bus[idx] | 0x3; + d->branchAddrPtr = &(d->prg_cpu[idx]->end.branchAddress); } else { /* quadlet transmit */ - d->prg[idx].begin.control = + d->prg_cpu[idx]->begin.control = OUTPUT_LAST_IMMEDIATE | packet->header_size; if (d->branchAddrPtr) - *(d->branchAddrPtr) = virt_to_bus(d->prg+idx) | 0x2; - d->branchAddrPtr = &(d->prg[idx].begin.branchAddress); + *(d->branchAddrPtr) = d->prg_bus[idx] | 0x2; + d->branchAddrPtr = &(d->prg_cpu[idx]->begin.branchAddress); } d->free_prgs--; /* queue the packet in the appropriate context queue */ - if (d->last) { - d->last->xnext = packet; - d->last = packet; + if (d->fifo_last) { + d->fifo_last->xnext = packet; + d->fifo_last = packet; + } + else { + d->fifo_first = packet; + d->fifo_last = packet; + } + d->prg_ind = (d->prg_ind+1)%d->num_desc; +} + +/* + * This function fills the AT FIFO with the (eventual) pending packets + * and runs or wake up the AT DMA prg if necessary. + * The function MUST be called with the d->lock held. + */ +static int dma_trm_flush(struct ti_ohci *ohci, struct dma_trm_ctx *d) +{ + int idx,z; + + if (d->pending_first == NULL || d->free_prgs == 0) + return 0; + + idx = d->prg_ind; + z = (d->pending_first->data_size) ? 3 : 2; + + /* insert the packets into the at dma fifo */ + while (d->free_prgs>0 && d->pending_first) { + insert_packet(ohci, d, d->pending_first); + d->pending_first = d->pending_first->xnext; + } + if (d->pending_first == NULL) + d->pending_last = NULL; + else + PRINT(KERN_INFO, ohci->id, + "AT DMA FIFO ctx=%d full... waiting",d->ctx); + + /* Is the context running ? (should be unless it is + the first packet to be sent in this context) */ + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { + DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx); + reg_write(ohci, d->cmdPtr, d->prg_bus[idx]|z); + run_context(ohci, d->ctrlSet, NULL); } else { - d->first = packet; - d->last = packet; + DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx); + /* wake up the dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) + reg_write(ohci, d->ctrlSet, 0x1000); } + return 1; } +/* + * Transmission of an async packet + */ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) { struct ti_ohci *ohci = host->hostdata; struct dma_trm_ctx *d; unsigned char tcode; - int timeout=50; + unsigned long flags; if (packet->data_size >= ohci->max_packet_size) { PRINT(KERN_ERR, ohci->id, @@ -1305,48 +747,21 @@ static int ohci_transmit(struct hpsb_host *host, struct hpsb_packet *packet) if (tcode & 0x02) d = ohci->at_resp_context; else d = ohci->at_req_context; - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock,flags); - if (d->free_prgs<1) { - PRINT(KERN_INFO, ohci->id, - "AT DMA ctx=%d Running out of prgs... waiting",d->ctx); - } - while (d->free_prgs<1) { - spin_unlock(&d->lock); - interruptible_sleep_on(&d->waitq); - if(signal_pending(current)) return -EINTR; - if (timeout--<0) { - stop_context(ohci, d->ctrlClear, - "AT DMA runaway loop... bailing out"); - return 0; - } - spin_lock(&d->lock); - } - - insert_packet(ohci, d, packet); - - /* Is the context running ? (should be unless it is - the first packet to be sent in this context) */ - if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) { - DBGMSG(ohci->id,"Starting AT DMA ctx=%d",d->ctx); - if (packet->data_size) - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[d->prg_ind])) | 0x3); - else - reg_write(ohci, d->cmdPtr, - virt_to_bus(&(d->prg[d->prg_ind])) | 0x2); - - run_context(ohci, d->ctrlSet, NULL); + /* queue the packet for later insertion into to dma fifo */ + if (d->pending_last) { + d->pending_last->xnext = packet; + d->pending_last = packet; } else { - DBGMSG(ohci->id,"Waking AT DMA ctx=%d",d->ctx); - /* wake up the dma context if necessary */ - if (!(reg_read(ohci, d->ctrlSet) & 0x400)) - reg_write(ohci, d->ctrlSet, 0x1000); + d->pending_first = packet; + d->pending_last = packet; } + + dma_trm_flush(ohci, d); - d->prg_ind = (d->prg_ind+1)%d->num_desc; - spin_unlock(&d->lock); + spin_unlock_irqrestore(&d->lock,flags); return 1; } @@ -1359,7 +774,10 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) switch (cmd) { case RESET_BUS: - host->attempt_root=1; + /* + * FIXME: this flag might be necessary in some case + */ + /* host->attempt_root = 1; */ PRINT(KERN_INFO, ohci->id, "resetting bus on request%s", (host->attempt_root ? " and attempting to become root" : "")); @@ -1415,14 +833,9 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) spin_lock_irqsave(&ohci->IR_channel_lock, flags); -#if 0 - PRINT(KERN_INFO, ohci->id, "!!! try listen on channel %d !!!", - arg); -#endif - if (!test_and_set_bit(arg, &ohci->IR_channel_usage)) { - PRINT(KERN_INFO, ohci->id, - "listening enabled on channel %d", arg); + DBGMSG(ohci->id, + "listening enabled on channel %d", arg); if (arg > 31) { u32 setMask= 0x00000001; @@ -1447,8 +860,8 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) spin_lock_irqsave(&ohci->IR_channel_lock, flags); if (test_and_clear_bit(arg, &ohci->IR_channel_usage)) { - PRINT(KERN_INFO, ohci->id, - "listening disabled on iso channel %d", arg); + DBGMSG(ohci->id, + "listening disabled on iso channel %d", arg); if (arg > 31) { u32 clearMask= 0x00000001; @@ -1490,29 +903,42 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) static void dma_trm_reset(struct dma_trm_ctx *d) { struct ti_ohci *ohci; + unsigned long flags; if (d==NULL) { PRINT_G(KERN_ERR, "dma_trm_reset called with NULL arg"); return; } ohci = (struct ti_ohci *)(d->ohci); - stop_context(ohci, d->ctrlClear, NULL); + ohci1394_stop_context(ohci, d->ctrlClear, NULL); - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock,flags); + + /* is there still any packet pending in the fifo ? */ + while(d->fifo_first) { + PRINT(KERN_INFO, ohci->id, + "AT dma reset ctx=%d, aborting transmission", + d->ctx); + hpsb_packet_sent(ohci->host, d->fifo_first, ACKX_ABORTED); + d->fifo_first = d->fifo_first->xnext; + } + d->fifo_first = d->fifo_last = NULL; /* is there still any packet pending ? */ - while(d->first) { + while(d->pending_first) { PRINT(KERN_INFO, ohci->id, "AT dma reset ctx=%d, aborting transmission", d->ctx); - hpsb_packet_sent(ohci->host, d->first, ACKX_ABORTED); - d->first = d->first->xnext; + hpsb_packet_sent(ohci->host, d->pending_first, + ACKX_ABORTED); + d->pending_first = d->pending_first->xnext; } - d->first = d->last = NULL; + d->pending_first = d->pending_last = NULL; + d->branchAddrPtr=NULL; d->sent_ind = d->prg_ind; d->free_prgs = d->num_desc; - spin_unlock(&d->lock); + spin_unlock_irqrestore(&d->lock,flags); } static void ohci_irq_handler(int irq, void *dev_id, @@ -1528,10 +954,10 @@ static void ohci_irq_handler(int irq, void *dev_id, /* read the interrupt event register */ event=reg_read(ohci, OHCI1394_IntEventClear); - DBGMSG(ohci->id, "IntEvent: %08x",event); - if (!event) return; + DBGMSG(ohci->id, "IntEvent: %08x",event); + /* clear the interrupt event register */ reg_write(ohci, OHCI1394_IntEventClear, event); @@ -1560,7 +986,7 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got reqTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "reqTxComplete"); else dma_trm_bh((void *)d); @@ -1570,7 +996,7 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got respTxComplete interrupt " "status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "respTxComplete"); else dma_trm_bh((void *)d); @@ -1580,9 +1006,9 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got RQPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RQPkt"); + ohci1394_stop_context(ohci, d->ctrlClear, "RQPkt"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1595,9 +1021,9 @@ static void ohci_irq_handler(int irq, void *dev_id, DBGMSG(ohci->id, "Got RSPkt interrupt status=0x%08X", reg_read(ohci, d->ctrlSet)); if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, "RSPkt"); + ohci1394_stop_context(ohci, d->ctrlClear, "RSPkt"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1608,9 +1034,6 @@ static void ohci_irq_handler(int irq, void *dev_id, if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; struct dma_rcv_ctx *d = ohci->ir_context; -#ifdef _VIDEO_1394_H - int i; -#endif isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, @@ -1620,10 +1043,10 @@ static void ohci_irq_handler(int irq, void *dev_id, reg_read(ohci, d->ctrlSet), isoRecvIntEvent); if (isoRecvIntEvent & 0x1) { if (reg_read(ohci, d->ctrlSet) & 0x800) - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "isochRx"); else { -#if 1 +#if IEEE1394_USE_BOTTOM_HALVES queue_task(&d->task, &tq_immediate); mark_bh(IMMEDIATE_BH); #else @@ -1631,12 +1054,21 @@ static void ohci_irq_handler(int irq, void *dev_id, #endif } } -#ifdef _VIDEO_1394_H - for (i=0;i<ohci->nb_iso_ctx-1;i++) - if (isoRecvIntEvent & (1<<(i+1))) - wakeup_dma_fbuf_ctx( - ohci,ohci->fbuf_context[i]); -#endif + if (ohci->video_tmpl) + ohci->video_tmpl->irq_handler(ohci->id, + isoRecvIntEvent, + 0); + } + if (event & OHCI1394_isochTx) { + quadlet_t isoXmitIntEvent; + isoXmitIntEvent = + reg_read(ohci, OHCI1394_IsoXmitIntEventSet); + reg_write(ohci, OHCI1394_IsoXmitIntEventClear, + isoXmitIntEvent); + DBGMSG(ohci->id, "Got isochTx interrupt"); + if (ohci->video_tmpl) + ohci->video_tmpl->irq_handler(ohci->id, 0, + isoXmitIntEvent); } if (event & OHCI1394_selfIDComplete) { if (host->in_bus_reset) { @@ -1703,10 +1135,10 @@ static void insert_dma_buffer(struct dma_rcv_ctx *d, int idx) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); DBGMSG(ohci->id, "Inserting dma buf ctx=%d idx=%d", d->ctx, idx); - d->prg[idx]->status = d->buf_size; - d->prg[idx]->branchAddress &= 0xfffffff0; + d->prg_cpu[idx]->status = d->buf_size; + d->prg_cpu[idx]->branchAddress &= 0xfffffff0; idx = (idx + d->num_desc - 1 ) % d->num_desc; - d->prg[idx]->branchAddress |= 0x1; + d->prg_cpu[idx]->branchAddress |= 0x1; /* wake up the dma context if necessary */ if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { @@ -1724,7 +1156,7 @@ static int block_length(struct dma_rcv_ctx *d, int idx, /* Where is the data length ? */ if (offset+12>=d->buf_size) - length = (d->buf[(idx+1)%d->num_desc] + length = (d->buf_cpu[(idx+1)%d->num_desc] [3-(d->buf_size-offset)/4]>>16); else length = (buf_ptr[3]>>16); @@ -1769,7 +1201,7 @@ static void dma_rcv_bh(void *data) struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); unsigned int split_left, idx, offset, rescount; unsigned char tcode; - int length, bytes_left; + int length, bytes_left, ack; quadlet_t *buf_ptr; char *split_ptr; char msg[256]; @@ -1778,9 +1210,9 @@ static void dma_rcv_bh(void *data) idx = d->buf_ind; offset = d->buf_offset; - buf_ptr = d->buf[idx] + offset/4; + buf_ptr = d->buf_cpu[idx] + offset/4; - rescount = d->prg[idx]->status&0xffff; + rescount = d->prg_cpu[idx]->status&0xffff; bytes_left = d->buf_size - rescount - offset; while (bytes_left>0) { @@ -1790,21 +1222,22 @@ static void dma_rcv_bh(void *data) if (length<4) { /* something is wrong */ sprintf(msg,"unexpected tcode 0x%X in AR ctx=%d", tcode, d->ctx); - stop_context(ohci, d->ctrlClear, msg); + ohci1394_stop_context(ohci, d->ctrlClear, msg); spin_unlock(&d->lock); return; } if ((offset+length)>d->buf_size) { /* Split packet */ if (length>d->split_buf_size) { - stop_context(ohci, d->ctrlClear, + ohci1394_stop_context(ohci, d->ctrlClear, "split packet size exceeded"); d->buf_ind = idx; d->buf_offset = offset; spin_unlock(&d->lock); return; } - if (d->prg[(idx+1)%d->num_desc]->status==d->buf_size) { + if (d->prg_cpu[(idx+1)%d->num_desc]->status + ==d->buf_size) { /* other part of packet not written yet */ /* this should never happen I think */ /* anyway we'll get it on the next call */ @@ -1822,7 +1255,7 @@ static void dma_rcv_bh(void *data) split_ptr += d->buf_size-offset; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; offset=0; while (split_left >= d->buf_size) { memcpy(split_ptr,buf_ptr,d->buf_size); @@ -1830,7 +1263,7 @@ static void dma_rcv_bh(void *data) split_left -= d->buf_size; insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; } if (split_left>0) { memcpy(split_ptr, buf_ptr, split_left); @@ -1838,17 +1271,6 @@ static void dma_rcv_bh(void *data) buf_ptr += offset/4; } - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * We need to handle write requests that are received - * to our middle address space (posted writes). - * In this case, the hardware generates an - * ack_complete... but, if we pass the packet up to - * the subsystem, it will try and send a response - * (which it shouldn't), because it assumes we - * returned ack_pending. - */ - /* * We get one phy packet for each bus reset. * we know that from now on the bus topology may @@ -1862,22 +1284,12 @@ static void dma_rcv_bh(void *data) (d->spb[length/4-1]>>16)&0x1f, (d->spb[length/4-1]>>21)&0x3, tcode, length, d->spb[3], d->ctx); + + ack = (((d->spb[length/4-1]>>16)&0x1f) + == 0x11) ? 1 : 0; - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * Handle case of posted writes. If we receive - * an ack_complete, we should not send a - * response. Fake out upper layers by turning - * the packet into a broadcast packet... we - * should really modify the core stack to - * accept an ack received argument and figure - * out whether to reply. - */ - if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { - d->spb[0] |= (ALL_NODES<<16); - } hpsb_packet_received(ohci->host, d->spb, - length); + length, ack); } else PRINT(KERN_INFO, ohci->id, @@ -1899,21 +1311,11 @@ static void dma_rcv_bh(void *data) (buf_ptr[length/4-1]>>21)&0x3, tcode, length, buf_ptr[3], d->ctx); - /* - * Tip by James Goodwin <jamesg@Filanet.com> - * Handle case of posted writes. If we receive - * an ack_complete, we should not send a - * response. Fake out upper layers by turning - * the packet into a broadcast packet... we - * should really modify the core stack to - * accept an ack received argument and figure - * out whether to reply. - */ - if (((d->spb[length/4-1]>>16)&0x1f) == 0x11) { - buf_ptr[0] |= (ALL_NODES<<16); - } + ack = (((buf_ptr[length/4-1]>>16)&0x1f) + == 0x11) ? 1 : 0; + hpsb_packet_received(ohci->host, buf_ptr, - length); + length, ack); } else PRINT(KERN_INFO, ohci->id, @@ -1924,11 +1326,11 @@ static void dma_rcv_bh(void *data) if (offset==d->buf_size) { insert_dma_buffer(d, idx); idx = (idx+1) % d->num_desc; - buf_ptr = d->buf[idx]; + buf_ptr = d->buf_cpu[idx]; offset=0; } } - rescount = d->prg[idx]->status & 0xffff; + rescount = d->prg_cpu[idx]->status & 0xffff; bytes_left = d->buf_size - rescount - offset; } @@ -1945,32 +1347,51 @@ static void dma_trm_bh(void *data) struct dma_trm_ctx *d = (struct dma_trm_ctx*)data; struct ti_ohci *ohci = (struct ti_ohci*)(d->ohci); struct hpsb_packet *packet; + unsigned long flags; u32 ack; - spin_lock(&d->lock); + spin_lock_irqsave(&d->lock, flags); - if (d->first==NULL) { - stop_context(ohci, d->ctrlClear, + if (d->fifo_first==NULL) { +#if 0 + ohci1394_stop_context(ohci, d->ctrlClear, "Packet sent ack received but queue is empty"); - spin_unlock(&d->lock); +#endif + spin_unlock_irqrestore(&d->lock, flags); return; } - packet = d->first; - d->first = d->first->xnext; - if (d->first==NULL) d->last=NULL; - if (packet->data_size) - ack = d->prg[d->sent_ind].end.status>>16; - else - ack = d->prg[d->sent_ind].begin.status>>16; - d->sent_ind = (d->sent_ind+1)%d->num_desc; - d->free_prgs++; - spin_unlock(&d->lock); - if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + while (d->fifo_first) { + packet = d->fifo_first; + if (packet->data_size) + ack = d->prg_cpu[d->sent_ind]->end.status>>16; + else + ack = d->prg_cpu[d->sent_ind]->begin.status>>16; + + if (ack==0) + /* this packet hasn't been sent yet*/ + break; + + DBGMSG(ohci->id, + "Packet sent to node %d ack=0x%X spd=%d ctx=%d", + (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, + d->ctx); + hpsb_packet_sent(ohci->host, packet, ack&0xf); + + if (packet->data_size) + pci_unmap_single(ohci->dev, + d->prg_cpu[d->sent_ind]->end.address, + packet->data_size, PCI_DMA_TODEVICE); + + d->sent_ind = (d->sent_ind+1)%d->num_desc; + d->free_prgs++; + d->fifo_first = d->fifo_first->xnext; + } + if (d->fifo_first==NULL) d->fifo_last=NULL; + + dma_trm_flush(ohci, d); - DBGMSG(ohci->id, "Packet sent to node %d ack=0x%X spd=%d ctx=%d", - (packet->header[0]>>16)&0x3f, ack&0x1f, (ack>>5)&0x3, d->ctx); - hpsb_packet_sent(ohci->host, packet, ack&0xf); + spin_unlock_irqrestore(&d->lock, flags); } static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) @@ -1984,17 +1405,25 @@ static int free_dma_rcv_ctx(struct dma_rcv_ctx **d) DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx); - stop_context(ohci, (*d)->ctrlClear, NULL); + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); - if ((*d)->buf) { - for (i=0; i<(*d)->num_desc; i++) - if ((*d)->buf[i]) kfree((*d)->buf[i]); - kfree((*d)->buf); - } - if ((*d)->prg) { + if ((*d)->buf_cpu) { for (i=0; i<(*d)->num_desc; i++) - if ((*d)->prg[i]) kfree((*d)->prg[i]); - kfree((*d)->prg); + if ((*d)->buf_cpu[i] && (*d)->buf_bus[i]) + pci_free_consistent( + ohci->dev, (*d)->buf_size, + (*d)->buf_cpu[i], (*d)->buf_bus[i]); + kfree((*d)->buf_cpu); + kfree((*d)->buf_bus); + } + if ((*d)->prg_cpu) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) + pci_free_consistent( + ohci->dev, sizeof(struct dma_cmd), + (*d)->prg_cpu[i], (*d)->prg_bus[i]); + kfree((*d)->prg_cpu); + kfree((*d)->prg_bus); } if ((*d)->spb) kfree((*d)->spb); @@ -2030,27 +1459,34 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; - d->buf = NULL; - d->prg = NULL; + d->buf_cpu = NULL; + d->buf_bus = NULL; + d->prg_cpu = NULL; + d->prg_bus = NULL; d->spb = NULL; - d->buf = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); + d->buf_cpu = kmalloc(d->num_desc * sizeof(quadlet_t*), GFP_KERNEL); + d->buf_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->buf == NULL) { + if (d->buf_cpu == NULL || d->buf_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); free_dma_rcv_ctx(&d); return NULL; } - memset(d->buf, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_cpu, 0, d->num_desc * sizeof(quadlet_t*)); + memset(d->buf_bus, 0, d->num_desc * sizeof(dma_addr_t)); - d->prg = kmalloc(d->num_desc * sizeof(struct dma_cmd*), GFP_KERNEL); + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct dma_cmd*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->prg == NULL) { + if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); free_dma_rcv_ctx(&d); return NULL; } - memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct dma_cmd*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); d->spb = kmalloc(d->split_buf_size, GFP_KERNEL); @@ -2061,10 +1497,12 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, } for (i=0; i<d->num_desc; i++) { - d->buf[i] = kmalloc(d->buf_size, GFP_KERNEL); + d->buf_cpu[i] = pci_alloc_consistent(ohci->dev, + d->buf_size, + d->buf_bus+i); - if (d->buf[i] != NULL) { - memset(d->buf[i], 0, d->buf_size); + if (d->buf_cpu[i] != NULL) { + memset(d->buf_cpu[i], 0, d->buf_size); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); @@ -2072,10 +1510,13 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, return NULL; } - d->prg[i]= kmalloc(sizeof(struct dma_cmd), GFP_KERNEL); + + d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, + sizeof(struct dma_cmd), + d->prg_bus+i); - if (d->prg[i] != NULL) { - memset(d->prg[i], 0, sizeof(struct dma_cmd)); + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct dma_cmd)); } else { PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg"); @@ -2098,6 +1539,7 @@ alloc_dma_rcv_ctx(struct ti_ohci *ohci, int ctx, int num_desc, static int free_dma_trm_ctx(struct dma_trm_ctx **d) { struct ti_ohci *ohci; + int i; if (*d==NULL) return -1; @@ -2105,9 +1547,18 @@ static int free_dma_trm_ctx(struct dma_trm_ctx **d) DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx); - stop_context(ohci, (*d)->ctrlClear, NULL); + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->prg_cpu) { + for (i=0; i<(*d)->num_desc; i++) + if ((*d)->prg_cpu[i] && (*d)->prg_bus[i]) + pci_free_consistent( + ohci->dev, sizeof(struct at_dma_prg), + (*d)->prg_cpu[i], (*d)->prg_bus[i]); + kfree((*d)->prg_cpu); + kfree((*d)->prg_bus); + } - if ((*d)->prg) kfree((*d)->prg); kfree(*d); *d = NULL; return 0; @@ -2118,6 +1569,7 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, int ctrlSet, int ctrlClear, int cmdPtr) { struct dma_trm_ctx *d=NULL; + int i; d = (struct dma_trm_ctx *)kmalloc(sizeof(struct dma_trm_ctx), GFP_KERNEL); @@ -2133,16 +1585,35 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->ctrlSet = ctrlSet; d->ctrlClear = ctrlClear; d->cmdPtr = cmdPtr; - d->prg = NULL; + d->prg_cpu = NULL; + d->prg_bus = NULL; - d->prg = kmalloc(d->num_desc * sizeof(struct at_dma_prg), GFP_KERNEL); + d->prg_cpu = kmalloc(d->num_desc * sizeof(struct at_dma_prg*), + GFP_KERNEL); + d->prg_bus = kmalloc(d->num_desc * sizeof(dma_addr_t), GFP_KERNEL); - if (d->prg == NULL) { + if (d->prg_cpu == NULL || d->prg_bus == NULL) { PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg"); free_dma_trm_ctx(&d); return NULL; } - memset(d->prg, 0, d->num_desc * sizeof(struct at_dma_prg)); + memset(d->prg_cpu, 0, d->num_desc * sizeof(struct at_dma_prg*)); + memset(d->prg_bus, 0, d->num_desc * sizeof(dma_addr_t)); + + for (i=0; i<d->num_desc; i++) { + d->prg_cpu[i] = pci_alloc_consistent(ohci->dev, + sizeof(struct at_dma_prg), + d->prg_bus+i); + + if (d->prg_cpu[i] != NULL) { + memset(d->prg_cpu[i], 0, sizeof(struct at_dma_prg)); + } else { + PRINT(KERN_ERR, ohci->id, + "failed to allocate at dma prg"); + free_dma_trm_ctx(&d); + return NULL; + } + } spin_lock_init(&d->lock); @@ -2150,15 +1621,42 @@ alloc_dma_trm_ctx(struct ti_ohci *ohci, int ctx, int num_desc, d->task.routine = dma_trm_bh; d->task.data = (void*)d; - init_waitqueue_head(&d->waitq); - return d; } +static u32 ohci_crc16(unsigned *data, int length) +{ + int check=0, i; + int shift, sum, next=0; + + for (i = length; i; i--) { + for (next = check, shift = 28; shift >= 0; shift -= 4 ) { + sum = ((next >> 12) ^ (*data >> shift)) & 0xf; + next = (next << 4) ^ (sum << 12) ^ (sum << 5) ^ (sum); + } + check = next & 0xffff; + data++; + } + + return check; +} + +static void ohci_init_config_rom(struct ti_ohci *ohci) +{ + int i; + + ohci_csr_rom[3] = reg_read(ohci, OHCI1394_GUIDHi); + ohci_csr_rom[4] = reg_read(ohci, OHCI1394_GUIDLo); + + ohci_csr_rom[0] = 0x04040000 | ohci_crc16(ohci_csr_rom+1, 4); + + for (i=0;i<sizeof(ohci_csr_rom)/4;i++) + ohci->csr_config_rom_cpu[i] = cpu_to_be32(ohci_csr_rom[i]); +} + static int add_card(struct pci_dev *dev) { struct ti_ohci *ohci; /* shortcut to currently handled device */ - int i; if (num_of_cards == MAX_OHCI1394_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " @@ -2182,21 +1680,29 @@ static int add_card(struct pci_dev *dev) ohci->state = 0; /* csr_config rom allocation */ - ohci->csr_config_rom = kmalloc(1024, GFP_KERNEL); - if (ohci->csr_config_rom == NULL) { + ohci->csr_config_rom_cpu = + pci_alloc_consistent(ohci->dev, sizeof(ohci_csr_rom), + &ohci->csr_config_rom_bus); + if (ohci->csr_config_rom_cpu == NULL) { FAIL("failed to allocate buffer config rom"); } - for (i=0;i<sizeof(ohci_csr_rom)/4;i++) - ohci->csr_config_rom[i] = cpu_to_be32(ohci_csr_rom[i]); DBGMSG(ohci->id, "The 1st byte at offset 0x404 is: 0x%02x", - *((char *)ohci->csr_config_rom+4)); + *((char *)ohci->csr_config_rom_cpu+4)); - /* self-id dma buffer allocation */ - ohci->self_id_buffer = kmalloc(2048, GFP_KERNEL); - if (ohci->self_id_buffer == NULL) { + /* + * self-id dma buffer allocation + * FIXME: some early chips may need 8KB alignment for the + * selfid buffer + */ + ohci->selfid_buf_cpu = + pci_alloc_consistent(ohci->dev, 2048, &ohci->selfid_buf_bus); + if (ohci->selfid_buf_cpu == NULL) { FAIL("failed to allocate DMA buffer for self-id packets"); } + if ((unsigned long)ohci->selfid_buf_cpu & 0xfff) + PRINT(KERN_INFO, ohci->id, "Selfid buffer %p not aligned on " + "8Kb boundary", ohci->selfid_buf_cpu); ohci->ar_req_context = alloc_dma_rcv_ctx(ohci, 0, AR_REQ_NUM_DESC, @@ -2241,9 +1747,9 @@ static int add_card(struct pci_dev *dev) ohci->ir_context = alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC, IR_BUF_SIZE, IR_SPLIT_BUF_SIZE, - OHCI1394_IrRcvContextControlSet, - OHCI1394_IrRcvContextControlClear, - OHCI1394_IrRcvCommandPtr); + OHCI1394_IsoRcvContextControlSet, + OHCI1394_IsoRcvContextControlClear, + OHCI1394_IsoRcvCommandPtr); if (ohci->ir_context == NULL) { FAIL("failed to allocate IR context"); @@ -2274,6 +1780,8 @@ static int add_card(struct pci_dev *dev) FAIL("failed to allocate shared interrupt %d", dev->irq); } + ohci_init_config_rom(ohci); + return 0; #undef FAIL } @@ -2297,12 +1805,9 @@ int ohci_get_info(char *buf, char **start, off_t fpos, //unsigned char phyreg; //int i, nports; int i; + struct dma_rcv_ctx *d=NULL; struct dma_trm_ctx *dt=NULL; -#ifdef _VIDEO_1394_H - int j; - struct dma_fbuf_ctx *f=ohci->fbuf_context[0]; -#endif p += sprintf(p,"IEEE-1394 OHCI Driver status report:\n"); p += sprintf(p," bus number: 0x%x Node ID: 0x%x\n", @@ -2332,26 +1837,6 @@ int ohci_get_info(char *buf, char **start, off_t fpos, p += sprintf(p,"\n---Iso Receive DMA---\n"); -#ifdef _VIDEO_1394_H - -#if 0 - if (f!=NULL) { - for (i=0; i<f->num_desc; i++) { - for (j=0;j<f->nb_cmd;j++) { - p += sprintf(p, - "prg[%d][%d]: %p %08x %08x %08x %08x\n", - i,j,virt_to_bus(&(f->prg[i][j])), - f->prg[i][j].control, - f->prg[i][j].address, - f->prg[i][j].branchAddress, - f->prg[i][j].status); - } - } - } -#endif - -#else - d = ohci->ir_context; #if 0 for (i=0; i<d->num_desc; i++) { @@ -2389,7 +1874,7 @@ int ohci_get_info(char *buf, char **start, off_t fpos, dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT req queue: first: %p last: %p\n", - dt->first, dt->last); + dt->fifo_first, dt->fifo_last); dt = ohci->at_resp_context; #if 0 for (i=0; i<dt->num_desc; i++) { @@ -2437,15 +1922,14 @@ int ohci_get_info(char *buf, char **start, off_t fpos, dt->prg_ind, dt->sent_ind, dt->free_prgs, dt->branchAddrPtr); p += sprintf(p, "AT resp queue: first: %p last: %p\n", - dt->first, dt->last); -#endif + dt->fifo_first, dt->fifo_last); /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", OHCI1394_Version, OHCI1394_GUID_ROM, OHCI1394_ATRetries); - SR("CSRReadData : %08x CSRCompData : %08x CSRControl : %08x\n", - OHCI1394_CSRReadData, OHCI1394_CSRCompareData, OHCI1394_CSRControl); + SR("CSRData : %08x CSRCompData : %08x CSRControl : %08x\n", + OHCI1394_CSRData, OHCI1394_CSRCompareData, OHCI1394_CSRControl); SR("ConfigROMhdr: %08x BusID : %08x BusOptions : %08x\n", OHCI1394_ConfigROMhdr, OHCI1394_BusID, OHCI1394_BusOptions); SR("GUIDHi : %08x GUIDLo : %08x ConfigROMmap: %08x\n", @@ -2481,14 +1965,21 @@ int ohci_get_info(char *buf, char **start, off_t fpos, SR("AsRsRvCtxCtl: %08x AsRsRvCmdPtr: %08x IntEvent : %08x\n", OHCI1394_AsRspRcvContextControlSet, OHCI1394_AsRspRcvCommandPtr, OHCI1394_IntEventSet); - for (i=0;i<4;i++) { + for (i=0;i<ohci->nb_iso_rcv_ctx;i++) { p += sprintf(p,"IsoRCtxCtl%02d: %08x IsoRCmdPtr%02d: %08x" " IsoRCxtMch%02d: %08x\n", i, reg_read(ohci, - OHCI1394_IrRcvContextControlSet+32*i), - i,reg_read(ohci, OHCI1394_IrRcvCommandPtr+32*i), + OHCI1394_IsoRcvContextControlSet+32*i), + i,reg_read(ohci, OHCI1394_IsoRcvCommandPtr+32*i), i,reg_read(ohci, - OHCI1394_IrRcvContextMatch+32*i)); + OHCI1394_IsoRcvContextMatch+32*i)); + } + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) { + p += sprintf(p,"IsoTCtxCtl%02d: %08x IsoTCmdPtr%02d: %08x\n", + i, + reg_read(ohci, + OHCI1394_IsoXmitContextControlSet+32*i), + i,reg_read(ohci,OHCI1394_IsoXmitCommandPtr+32*i)); } #if 0 @@ -2554,9 +2045,11 @@ struct proc_dir_entry ohci_proc_entry = static void remove_card(struct ti_ohci *ohci) { -#ifdef _VIDEO_1394_H - int i; -#endif + /* + * Reset the board properly before leaving + * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> + */ + ohci_soft_reset(ohci); /* Free AR dma */ free_dma_rcv_ctx(&ohci->ar_req_context); @@ -2569,28 +2062,18 @@ static void remove_card(struct ti_ohci *ohci) /* Free IR dma */ free_dma_rcv_ctx(&ohci->ir_context); -#ifdef _VIDEO_1394_H - /* Free the frame buffer context */ - if (ohci->fbuf_context) - for (i=0;i<ohci->nb_iso_ctx-1;i++) { - free_dma_fbuf_ctx(&ohci->fbuf_context[i]); - } -#endif - - /* - * Reset the board properly before leaving - * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de> - */ - ohci_soft_reset(ohci); - /* Free self-id buffer */ - if (ohci->self_id_buffer) - kfree(ohci->self_id_buffer); + if (ohci->selfid_buf_cpu) + pci_free_consistent(ohci->dev, 2048, + ohci->selfid_buf_cpu, + ohci->selfid_buf_bus); /* Free config rom */ - if (ohci->csr_config_rom) - kfree(ohci->csr_config_rom); - + if (ohci->csr_config_rom_cpu) + pci_free_consistent(ohci->dev, sizeof(ohci_csr_rom), + ohci->csr_config_rom_cpu, + ohci->csr_config_rom_bus); + /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); @@ -2646,9 +2129,9 @@ static size_t get_ohci_rom(struct hpsb_host *host, const quadlet_t **ptr) struct ti_ohci *ohci=host->hostdata; DBGMSG(ohci->id, "request csr_rom address: %08X", - (u32)ohci->csr_config_rom); + (u32)ohci->csr_config_rom_cpu); - *ptr = ohci->csr_config_rom; + *ptr = ohci->csr_config_rom_cpu; return sizeof(ohci_csr_rom); } @@ -2675,6 +2158,118 @@ struct hpsb_host_template *get_ohci_template(void) return &tmpl; } +void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) +{ + int i=0; + + /* stop the channel program if it's still running */ + reg_write(ohci, reg, 0x8000); + + /* Wait until it effectively stops */ + while (reg_read(ohci, reg) & 0x400) { + i++; + if (i>5000) { + PRINT(KERN_ERR, ohci->id, + "runaway loop while stopping context..."); + break; + } + } + if (msg) PRINT(KERN_ERR, ohci->id, "%s\n dma prg stopped\n", msg); +} + +struct ti_ohci *ohci1394_get_struct(int card_num) +{ + if (card_num>=0 && card_num<num_of_cards) + return &cards[card_num]; + return NULL; +} + +int ohci1394_register_video(struct ti_ohci *ohci, + struct video_template *tmpl) +{ + if (ohci->video_tmpl) + return -ENFILE; + ohci->video_tmpl = tmpl; + MOD_INC_USE_COUNT; + return 0; +} + +void ohci1394_unregister_video(struct ti_ohci *ohci, + struct video_template *tmpl) +{ + if (ohci->video_tmpl != tmpl) { + PRINT(KERN_ERR, ohci->id, + "Trying to unregister wrong video device"); + } + else { + ohci->video_tmpl = NULL; + MOD_DEC_USE_COUNT; + } +} + +#if 0 +int ohci_compare_swap(struct ti_ohci *ohci, quadlet_t *data, + quadlet_t compare, int sel) +{ + int timeout = 255; + reg_write(ohci, OHCI1394_CSRData, *data); + reg_write(ohci, OHCI1394_CSRCompareData, compare); + reg_write(ohci, OHCI1394_CSRControl, sel); + while(!(reg_read(ohci, OHCI1394_CSRControl)&0x80000000)) { + if (timeout--) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + } + *data = reg_read(ohci, OHCI1394_CSRData); + return 0; +} + +int ohci1394_request_channel(struct ti_ohci *ohci, int channel) +{ + int csrSel; + quadlet_t chan, data1=0, data2=0; + int timeout = 32; + + if (channel<32) { + chan = 1<<channel; + csrSel = 2; + } + else { + chan = 1<<(channel-32); + csrSel = 3; + } + if (ohci_compare_swap(ohci, &data1, 0, csrSel)<0) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + while (timeout--) { + if (data1 & chan) { + PRINT(KERN_INFO, ohci->id, + "request channel %d failed", channel); + return -1; + } + data2 = data1; + data1 |= chan; + if (ohci_compare_swap(ohci, &data1, data2, csrSel)<0) { + PRINT(KERN_INFO, ohci->id, "request_channel timeout"); + return -1; + } + if (data1==data2) { + PRINT(KERN_INFO, ohci->id, + "request channel %d succeded", channel); + return 0; + } + } + PRINT(KERN_INFO, ohci->id, "request channel %d failed", channel); + return -1; +} +#endif + +EXPORT_SYMBOL(ohci1394_stop_context); +EXPORT_SYMBOL(ohci1394_get_struct); +EXPORT_SYMBOL(ohci1394_register_video); +EXPORT_SYMBOL(ohci1394_unregister_video); #ifdef MODULE @@ -2695,10 +2290,6 @@ void cleanup_module(void) #endif #endif -#ifdef _VIDEO_1394_H - unregister_chrdev(OHCI1394_MAJOR, "ohci1394"); -#endif - PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } @@ -2709,17 +2300,8 @@ int init_module(void) if (hpsb_register_lowlevel(get_ohci_template())) { PRINT_G(KERN_ERR, "registering failed\n"); return -ENXIO; - } else { -#ifdef _VIDEO_1394_H - if (register_chrdev(OHCI1394_MAJOR, "ohci1394", &ohci_fops)) - { - printk("ohci1394: unable to get major %d\n", - OHCI1394_MAJOR); - return -EIO; - } -#endif - return 0; - } + } + return 0; } #endif /* MODULE */ diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index d778cbe7d..af1aeb2a6 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -22,8 +22,8 @@ #define _OHCI1394_H #include "ieee1394_types.h" -/* include this for the video frame grabber */ -/* #include "video1394.h" */ + +#define IEEE1394_USE_BOTTOM_HALVES 0 #define OHCI1394_DRIVER_NAME "ohci1394" @@ -71,6 +71,18 @@ #define PCI_DEVICE_ID_APPLE_UNI_N_FW 0x0018 #endif +#ifndef PCI_DEVICE_ID_ALI_OHCI1394_M5251 +#define PCI_DEVICE_ID_ALI_OHCI1394_M5251 0x5251 +#endif + +#ifndef PCI_VENDOR_ID_LUCENT +#define PCI_VENDOR_ID_LUCENT 0x11c1 +#endif + +#ifndef PCI_DEVICE_ID_LUCENT_FW323 +#define PCI_DEVICE_ID_LUCENT_FW323 0x5811 +#endif + #define MAX_OHCI1394_CARDS 4 #define OHCI1394_MAX_AT_REQ_RETRIES 0x2 @@ -87,8 +99,8 @@ #define AR_RESP_SPLIT_BUF_SIZE 4096 /* split packet buffer */ #define IR_NUM_DESC 16 /* number of IR descriptors */ -#define IR_BUF_SIZE 6480 /* 6480 bytes/buffer */ -#define IR_SPLIT_BUF_SIZE 8192 /* split packet buffer */ +#define IR_BUF_SIZE 4096 /* 6480 bytes/buffer */ +#define IR_SPLIT_BUF_SIZE 4096 /* split packet buffer */ #define AT_REQ_NUM_DESC 32 /* number of AT req descriptors */ #define AT_RESP_NUM_DESC 32 /* number of AT resp descriptors */ @@ -113,8 +125,15 @@ struct dma_rcv_ctx { unsigned int num_desc; unsigned int buf_size; unsigned int split_buf_size; - struct dma_cmd **prg; - quadlet_t **buf; + + /* dma block descriptors */ + struct dma_cmd **prg_cpu; + dma_addr_t *prg_bus; + + /* dma buffers */ + quadlet_t **buf_cpu; + dma_addr_t *buf_bus; + unsigned int buf_ind; unsigned int buf_offset; quadlet_t *spb; @@ -130,45 +149,37 @@ struct dma_trm_ctx { void *ohci; int ctx; unsigned int num_desc; - struct at_dma_prg *prg; + + /* dma block descriptors */ + struct at_dma_prg **prg_cpu; + dma_addr_t *prg_bus; + unsigned int prg_ind; unsigned int sent_ind; int free_prgs; quadlet_t *branchAddrPtr; - struct hpsb_packet *first; - struct hpsb_packet *last; + + /* list of packets inserted in the AT FIFO */ + struct hpsb_packet *fifo_first; + struct hpsb_packet *fifo_last; + + /* list of pending packets to be inserted in the AT FIFO */ + struct hpsb_packet *pending_first; + struct hpsb_packet *pending_last; + spinlock_t lock; struct tq_struct task; int ctrlClear; int ctrlSet; int cmdPtr; - wait_queue_head_t waitq; }; -#ifdef _VIDEO_1394_H - -#define OHCI1394_MAJOR 172 -#define ISO_CHANNELS 64 - -struct dma_fbuf_ctx { - void *ohci; - int ctx; - int channel; - int last_buffer; - unsigned int num_desc; - unsigned int buf_size; - unsigned int frame_size; - unsigned int nb_cmd; - unsigned char *buf; - struct dma_cmd **prg; - unsigned int *buffer_status; - int ctrlClear; - int ctrlSet; - int cmdPtr; - int ctxMatch; - wait_queue_head_t waitq; +/* video device template */ +struct video_template { + void (*irq_handler) (int card, quadlet_t isoRecvEvent, + quadlet_t isoXmitEvent); }; -#endif + struct ti_ohci { int id; /* sequential card number */ @@ -180,8 +191,13 @@ struct ti_ohci { /* remapped memory spaces */ void *registers; - quadlet_t *self_id_buffer; /* dma buffer for self-id packets */ - quadlet_t *csr_config_rom; /* buffer for csr config rom */ + /* dma buffer for self-id packets */ + quadlet_t *selfid_buf_cpu; + dma_addr_t selfid_buf_bus; + + /* buffer for csr config rom */ + quadlet_t *csr_config_rom_cpu; + dma_addr_t csr_config_rom_bus; unsigned int max_packet_size; @@ -197,13 +213,10 @@ struct ti_ohci { struct dma_rcv_ctx *ir_context; u64 IR_channel_usage; spinlock_t IR_channel_lock; - int nb_iso_ctx; + int nb_iso_rcv_ctx; -#ifdef _VIDEO_1394_H - /* frame buffer context */ - struct dma_fbuf_ctx **fbuf_context; - struct dma_fbuf_ctx *current_fbuf_ctx; -#endif + /* iso transmit */ + int nb_iso_xmit_ctx; /* IEEE-1394 part follows */ struct hpsb_host *host; @@ -214,8 +227,23 @@ struct ti_ohci { int self_id_errors; int NumBusResets; + + /* video device */ + struct video_template *video_tmpl; }; +inline static int cross_bound(unsigned long addr, unsigned int size) +{ + int cross=0; + if (size>PAGE_SIZE) { + cross = size/PAGE_SIZE; + size -= cross*PAGE_SIZE; + } + if ((PAGE_SIZE-addr%PAGE_SIZE)<size) + cross++; + return cross; +} + /* * Register read and write helper functions. */ @@ -310,7 +338,7 @@ quadlet_t ohci_csr_rom[] = { #define OHCI1394_Version 0x000 #define OHCI1394_GUID_ROM 0x004 #define OHCI1394_ATRetries 0x008 -#define OHCI1394_CSRReadData 0x00C +#define OHCI1394_CSRData 0x00C #define OHCI1394_CSRCompareData 0x010 #define OHCI1394_CSRControl 0x014 #define OHCI1394_ConfigROMhdr 0x018 @@ -370,12 +398,18 @@ quadlet_t ohci_csr_rom[] = { #define OHCI1394_AsRspRcvContextControlClear 0x1E4 #define OHCI1394_AsRspRcvCommandPtr 0x1EC +/* Isochronous transmit registers */ +/* Add (32 * n) for context n */ +#define OHCI1394_IsoXmitContextControlSet 0x200 +#define OHCI1394_IsoXmitContextControlClear 0x204 +#define OHCI1394_IsoXmitCommandPtr 0x20C + /* Isochronous receive registers */ /* Add (32 * n) for context n */ -#define OHCI1394_IrRcvContextControlSet 0x400 -#define OHCI1394_IrRcvContextControlClear 0x404 -#define OHCI1394_IrRcvCommandPtr 0x40C -#define OHCI1394_IrRcvContextMatch 0x410 +#define OHCI1394_IsoRcvContextControlSet 0x400 +#define OHCI1394_IsoRcvContextControlClear 0x404 +#define OHCI1394_IsoRcvCommandPtr 0x40C +#define OHCI1394_IsoRcvContextMatch 0x410 /* Interrupts Mask/Events */ @@ -410,5 +444,12 @@ quadlet_t ohci_csr_rom[] = { #define DMA_SPEED_200 0x1 #define DMA_SPEED_400 0x2 +void ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); +struct ti_ohci *ohci1394_get_struct(int card_num); +int ohci1394_register_video(struct ti_ohci *ohci, + struct video_template *tmpl); +void ohci1394_unregister_video(struct ti_ohci *ohci, + struct video_template *tmpl); + #endif diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c index 368c5137b..16aeaa92d 100644 --- a/drivers/ieee1394/pcilynx.c +++ b/drivers/ieee1394/pcilynx.c @@ -82,7 +82,7 @@ static pcl_t alloc_pcl(struct ti_lynx *lynx) spin_lock(&lynx->lock); /* FIXME - use ffz() to make this readable */ - for (i = 0; i < LOCALRAM_SIZE; i++) { + for (i = 0; i < (LOCALRAM_SIZE / 1024); i++) { m = lynx->pcl_bmap[i]; for (j = 0; j < 8; j++) { if (m & 1<<j) { @@ -385,11 +385,22 @@ static void handle_selfid(struct ti_lynx *lynx, struct hpsb_host *host, size_t s -/* This must be called with the async_queue_lock held. */ +/* This must be called with the async.queue_lock held. */ static void send_next_async(struct ti_lynx *lynx) { struct ti_pcl pcl; - struct hpsb_packet *packet = lynx->async_queue; + struct hpsb_packet *packet = lynx->async.queue; + + lynx->async.header_dma = pci_map_single(lynx->dev, packet->header, + packet->header_size, + PCI_DMA_TODEVICE); + if (packet->data_size) { + lynx->async.data_dma = pci_map_single(lynx->dev, packet->data, + packet->data_size, + PCI_DMA_TODEVICE); + } else { + lynx->async.data_dma = 0; + } pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID; @@ -400,16 +411,16 @@ static void send_next_async(struct ti_lynx *lynx) pcl.buffer[0].control = PCL_CMD_XMT | packet->speed_code << 14 | packet->header_size | PCL_BIGENDIAN; #endif - pcl.buffer[0].pointer = virt_to_bus(packet->header); + pcl.buffer[0].pointer = lynx->async.header_dma; pcl.buffer[1].control = PCL_LAST_BUFF | packet->data_size; - pcl.buffer[1].pointer = virt_to_bus(packet->data); + pcl.buffer[1].pointer = lynx->async.data_dma; if (!packet->data_be) { pcl.buffer[1].control |= PCL_BIGENDIAN; } - put_pcl(lynx, lynx->async_pcl, &pcl); - run_pcl(lynx, lynx->async_pcl_start, 3); + put_pcl(lynx, lynx->async.pcl, &pcl); + run_pcl(lynx, lynx->async.pcl_start, 3); } @@ -440,8 +451,8 @@ static int lynx_initialize(struct hpsb_host *host) int i; u32 *pcli; - lynx->async_queue = NULL; - spin_lock_init(&lynx->async_queue_lock); + lynx->async.queue = NULL; + spin_lock_init(&lynx->async.queue_lock); spin_lock_init(&lynx->phy_reg_lock); pcl.next = pcl_bus(lynx, lynx->rcv_pcl); @@ -456,13 +467,13 @@ static int lynx_initialize(struct hpsb_host *host) pcl.buffer[0].control = PCL_CMD_RCV | PCL_BIGENDIAN | 16; pcl.buffer[1].control = PCL_LAST_BUFF | 4080; #endif - pcl.buffer[0].pointer = virt_to_bus(lynx->rcv_page); - pcl.buffer[1].pointer = virt_to_bus(lynx->rcv_page) + 16; + pcl.buffer[0].pointer = lynx->rcv_page_dma; + pcl.buffer[1].pointer = lynx->rcv_page_dma + 16; put_pcl(lynx, lynx->rcv_pcl, &pcl); - pcl.next = pcl_bus(lynx, lynx->async_pcl); - pcl.async_error_next = pcl_bus(lynx, lynx->async_pcl); - put_pcl(lynx, lynx->async_pcl_start, &pcl); + pcl.next = pcl_bus(lynx, lynx->async.pcl); + pcl.async_error_next = pcl_bus(lynx, lynx->async.pcl); + put_pcl(lynx, lynx->async.pcl_start, &pcl); pcl.next = PCL_NEXT_INVALID; pcl.async_error_next = PCL_NEXT_INVALID; @@ -476,7 +487,7 @@ static int lynx_initialize(struct hpsb_host *host) int page = i / ISORCV_PER_PAGE; int sec = i % ISORCV_PER_PAGE; - pcl.buffer[0].pointer = virt_to_bus(lynx->iso_rcv.page[page]) + pcl.buffer[0].pointer = lynx->iso_rcv.page_dma[page] + sec * MAX_ISORCV_SIZE; pcl.buffer[1].pointer = pcl.buffer[0].pointer + 4; put_pcl(lynx, lynx->iso_rcv.pcl[i], &pcl); @@ -542,7 +553,9 @@ static void lynx_release(struct hpsb_host *host) lynx = host->hostdata; remove_card(lynx); } else { +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS unregister_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME); +#endif } } @@ -568,13 +581,13 @@ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) cpu_to_be32s(&packet->header[3]); } - spin_lock_irqsave(&lynx->async_queue_lock, flags); + spin_lock_irqsave(&lynx->async.queue_lock, flags); - if (lynx->async_queue == NULL) { - lynx->async_queue = packet; + if (lynx->async.queue == NULL) { + lynx->async.queue = packet; send_next_async(lynx); } else { - p = lynx->async_queue; + p = lynx->async.queue; while (p->xnext != NULL) { p = p->xnext; } @@ -582,7 +595,7 @@ static int lynx_transmit(struct hpsb_host *host, struct hpsb_packet *packet) p->xnext = packet; } - spin_unlock_irqrestore(&lynx->async_queue_lock, flags); + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); return 1; } @@ -637,13 +650,13 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) break; case CANCEL_REQUESTS: - spin_lock_irqsave(&lynx->async_queue_lock, flags); + spin_lock_irqsave(&lynx->async.queue_lock, flags); reg_write(lynx, DMA3_CHAN_CTRL, 0); - packet = lynx->async_queue; - lynx->async_queue = NULL; + packet = lynx->async.queue; + lynx->async.queue = NULL; - spin_unlock_irqrestore(&lynx->async_queue_lock, flags); + spin_unlock_irqrestore(&lynx->async.queue_lock, flags); while (packet != NULL) { lastpacket = packet; @@ -696,7 +709,7 @@ static int lynx_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) * IEEE-1394 functionality section END * ***************************************/ - +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS /* VFS functions for local bus / aux device access. Access to those * is implemented as a character device instead of block devices * because buffers are not wanted for this. Therefore llseek (from @@ -710,7 +723,7 @@ static ssize_t mem_write(struct file*, const char*, size_t, loff_t*); static struct file_operations aux_ops = { - owner: THIS_MODULE, + OWNER_THIS_MODULE /* FIXME: should have custom llseek with bounds checking */ read: mem_read, write: mem_write, @@ -735,18 +748,23 @@ static int mem_open(struct inode *inode, struct file *file) enum { rom, aux, ram } type; struct memdata *md; + V22_COMPAT_MOD_INC_USE_COUNT; + if (cid < PCILYNX_MINOR_AUX_START) { /* just for completeness */ + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } else if (cid < PCILYNX_MINOR_ROM_START) { cid -= PCILYNX_MINOR_AUX_START; if (cid >= num_of_cards || !cards[cid].aux_port) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = aux; } else if (cid < PCILYNX_MINOR_RAM_START) { cid -= PCILYNX_MINOR_ROM_START; if (cid >= num_of_cards || !cards[cid].local_rom) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = rom; @@ -755,6 +773,7 @@ static int mem_open(struct inode *inode, struct file *file) * It is currently used inside the driver! */ cid -= PCILYNX_MINOR_RAM_START; if (cid >= num_of_cards || !cards[cid].local_ram) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENXIO; } type = ram; @@ -762,6 +781,7 @@ static int mem_open(struct inode *inode, struct file *file) md = (struct memdata *)vmalloc(sizeof(struct memdata)); if (md == NULL) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -793,6 +813,7 @@ static int mem_release(struct inode *inode, struct file *file) vfree(md); + V22_COMPAT_MOD_DEC_USE_COUNT; return 0; } @@ -801,16 +822,15 @@ static unsigned int aux_poll(struct file *file, poll_table *pt) struct memdata *md = (struct memdata *)file->private_data; int cid = md->cid; unsigned int mask; - int intr_seen; /* reading and writing is always allowed */ mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; if (md->type == aux) { poll_wait(file, &cards[cid].aux_intr_wait, pt); - intr_seen = atomic_read(&cards[cid].aux_intr_seen); - if (atomic_read(&md->aux_intr_last_seen) != intr_seen) { + if (atomic_read(&md->aux_intr_last_seen) + != atomic_read(&cards[cid].aux_intr_seen)) { mask |= POLLPRI; atomic_inc(&md->aux_intr_last_seen); } @@ -956,7 +976,7 @@ static ssize_t mem_read(struct file *file, char *buffer, size_t count, } while (bcount >= 4) { - retval = mem_dmaread(md, virt_to_phys(md->lynx->mem_dma_buffer) + retval = mem_dmaread(md, md->lynx->mem_dma_buffer_dma + count - bcount, bcount, off); if (retval < 0) return retval; @@ -1008,7 +1028,7 @@ static ssize_t mem_write(struct file *file, const char *buffer, size_t count, file->f_pos += count; return count; } - +#endif /* CONFIG_IEEE1394_PCILYNX_PORTS */ /******************************************************** @@ -1028,6 +1048,7 @@ static void lynx_irq_handler(int irq, void *dev_id, reg_write(lynx, LINK_INT_STATUS, linkint); //printk("-%d- one interrupt: 0x%08x / 0x%08x\n", lynx->id, intmask, linkint); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (intmask & PCI_INT_AUX_INT) { atomic_inc(&lynx->aux_intr_seen); wake_up_interruptible(&lynx->aux_intr_wait); @@ -1036,6 +1057,7 @@ static void lynx_irq_handler(int irq, void *dev_id, if (intmask & PCI_INT_DMA0_HLT) { wake_up_interruptible(&lynx->mem_dma_intr_wait); } +#endif if (intmask & PCI_INT_1394) { @@ -1108,17 +1130,24 @@ static void lynx_irq_handler(int irq, void *dev_id, u32 ack; struct hpsb_packet *packet; - spin_lock(&lynx->async_queue_lock); + spin_lock(&lynx->async.queue_lock); ack = reg_read(lynx, DMA3_CHAN_STAT); - packet = lynx->async_queue; - lynx->async_queue = packet->xnext; + packet = lynx->async.queue; + lynx->async.queue = packet->xnext; + + pci_unmap_single(lynx->dev, lynx->async.header_dma, + packet->header_size, PCI_DMA_TODEVICE); + if (packet->data_size) { + pci_unmap_single(lynx->dev, lynx->async.data_dma, + packet->data_size, PCI_DMA_TODEVICE); + } - if (lynx->async_queue != NULL) { + if (lynx->async.queue != NULL) { send_next_async(lynx); } - spin_unlock(&lynx->async_queue_lock); + spin_unlock(&lynx->async.queue_lock); if (ack & DMA_CHAN_STAT_SPECIALACK) { ack = (ack >> 15) & 0xf; @@ -1150,7 +1179,7 @@ static void lynx_irq_handler(int irq, void *dev_id, || (*q_data >> 4 & 0xf) == TCODE_WRITEQ) { cpu_to_be32s(q_data + 3); } - hpsb_packet_received(host, q_data, stat & 0x1fff); + hpsb_packet_received(host, q_data, stat & 0x1fff, 0); } run_pcl(lynx, lynx->rcv_pcl_start, 1); @@ -1184,7 +1213,8 @@ static void iso_rcv_bh(struct ti_lynx *lynx) "iso receive error on %d to 0x%p", idx, data); } else { hpsb_packet_received(lynx->host, data, - lynx->iso_rcv.stat[idx] & 0x1fff); + lynx->iso_rcv.stat[idx] & 0x1fff, + 0); } spin_lock_irqsave(&lynx->iso_rcv.lock, flags); @@ -1210,12 +1240,11 @@ static int add_card(struct pci_dev *dev) } while (0) struct ti_lynx *lynx; /* shortcut to currently handled device */ - unsigned long page; unsigned int i; if (num_of_cards == MAX_PCILYNX_CARDS) { PRINT_G(KERN_WARNING, "cannot handle more than %d cards. " - "Adjust MAX_PCILYNX_CARDS in ti_pcilynx.h.", + "Adjust MAX_PCILYNX_CARDS in pcilynx.h.", MAX_PCILYNX_CARDS); return 1; } @@ -1225,6 +1254,10 @@ static int add_card(struct pci_dev *dev) lynx->id = num_of_cards-1; lynx->dev = dev; + if (!pci_dma_supported(dev, 0xffffffff)) { + FAIL("DMA address limits not supported for PCILynx hardware %d", + lynx->id); + } if (pci_enable_device(dev)) { FAIL("failed to enable PCILynx hardware %d", lynx->id); } @@ -1239,61 +1272,50 @@ static int add_card(struct pci_dev *dev) } #ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM - lynx->pcl_mem = kmalloc(8 * sizeof(lynx->pcl_bmap) - * sizeof(struct ti_pcl), GFP_KERNEL); + lynx->pcl_mem = pci_alloc_consistent(dev, LOCALRAM_SIZE, + &lynx->pcl_mem_dma); if (lynx->pcl_mem != NULL) { lynx->state = have_pcl_mem; PRINT(KERN_INFO, lynx->id, - "allocated PCL memory %d Bytes @ 0x%p", - 8 * sizeof(lynx->pcl_bmap) * sizeof(struct ti_pcl), + "allocated PCL memory %d Bytes @ 0x%p", LOCALRAM_SIZE, lynx->pcl_mem); } else { FAIL("failed to allocate PCL memory area"); } #endif - lynx->mem_dma_buffer = kmalloc(65536, GFP_KERNEL); - if (lynx->mem_dma_buffer != NULL) { - lynx->state = have_aux_buf; - } else { +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + lynx->mem_dma_buffer = pci_alloc_consistent(dev, 65536, + &lynx->mem_dma_buffer_dma); + if (lynx->mem_dma_buffer == NULL) { FAIL("failed to allocate DMA buffer for aux"); } + lynx->state = have_aux_buf; +#endif - page = get_free_page(GFP_KERNEL); - if (page != 0) { - lynx->rcv_page = (void *)page; - lynx->state = have_1394_buffers; - } else { + lynx->rcv_page = pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->rcv_page_dma); + if (lynx->rcv_page == NULL) { FAIL("failed to allocate receive buffer"); } + lynx->state = have_1394_buffers; for (i = 0; i < ISORCV_PAGES; i++) { - page = get_free_page(GFP_KERNEL); - if (page != 0) { - lynx->iso_rcv.page[i] = (void *)page; - } else { + lynx->iso_rcv.page[i] = + pci_alloc_consistent(dev, PAGE_SIZE, + &lynx->iso_rcv.page_dma[i]); + if (lynx->iso_rcv.page[i] == NULL) { FAIL("failed to allocate iso receive buffers"); } } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) - lynx->registers = ioremap_nocache(dev->base_address[0], - PCILYNX_MAX_REGISTER); - lynx->local_ram = ioremap(dev->base_address[1], PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(dev->base_address[2], PCILYNX_MAX_MEMORY); -#else lynx->registers = ioremap_nocache(pci_resource_start(dev,0), PCILYNX_MAX_REGISTER); lynx->local_ram = ioremap(pci_resource_start(dev,1), PCILYNX_MAX_MEMORY); - lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); -#endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,15) - lynx->local_rom = ioremap(dev->rom_address, PCILYNX_MAX_MEMORY); -#else + lynx->aux_port = ioremap(pci_resource_start(dev,2), PCILYNX_MAX_MEMORY); lynx->local_rom = ioremap(pci_resource_start(dev,PCI_ROM_RESOURCE), PCILYNX_MAX_MEMORY); -#endif lynx->state = have_iomappings; if (lynx->registers == NULL) { @@ -1309,15 +1331,17 @@ static int add_card(struct pci_dev *dev) /* alloc_pcl return values are not checked, it is expected that the * provided PCL space is sufficient for the initial allocations */ +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (lynx->aux_port != NULL) { lynx->dmem_pcl = alloc_pcl(lynx); aux_setup_pcls(lynx); sema_init(&lynx->mem_dma_mutex, 1); } +#endif lynx->rcv_pcl = alloc_pcl(lynx); lynx->rcv_pcl_start = alloc_pcl(lynx); - lynx->async_pcl = alloc_pcl(lynx); - lynx->async_pcl_start = alloc_pcl(lynx); + lynx->async.pcl = alloc_pcl(lynx); + lynx->async.pcl_start = alloc_pcl(lynx); for (i = 0; i < NUM_ISORCV_PCL; i++) { lynx->iso_rcv.pcl[i] = alloc_pcl(lynx); @@ -1330,8 +1354,10 @@ static int add_card(struct pci_dev *dev) reg_write(lynx, PCI_INT_ENABLE, PCI_INT_AUX_INT | PCI_INT_DMA_ALL); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS init_waitqueue_head(&lynx->mem_dma_intr_wait); init_waitqueue_head(&lynx->aux_intr_wait); +#endif lynx->iso_rcv.tq.routine = (void (*)(void*))iso_rcv_bh; lynx->iso_rcv.tq.data = lynx; @@ -1372,15 +1398,22 @@ static void remove_card(struct ti_lynx *lynx) case have_1394_buffers: for (i = 0; i < ISORCV_PAGES; i++) { if (lynx->iso_rcv.page[i]) { - free_page((unsigned long)lynx->iso_rcv.page[i]); + pci_free_consistent(lynx->dev, PAGE_SIZE, + lynx->iso_rcv.page[i], + lynx->iso_rcv.page_dma[i]); } } - free_page((unsigned long)lynx->rcv_page); + pci_free_consistent(lynx->dev, PAGE_SIZE, lynx->rcv_page, + lynx->rcv_page_dma); case have_aux_buf: - kfree(lynx->mem_dma_buffer); +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS + pci_free_consistent(lynx->dev, 65536, lynx->mem_dma_buffer, + lynx->mem_dma_buffer_dma); +#endif case have_pcl_mem: #ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM - kfree(lynx->pcl_mem); + pci_free_consistent(lynx->dev, LOCALRAM_SIZE, lynx->pcl_mem, + lynx->pcl_mem_dma); #endif case have_intr: free_irq(lynx->dev->irq, lynx); @@ -1416,11 +1449,13 @@ static int init_driver() return -ENXIO; } +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS if (register_chrdev(PCILYNX_MAJOR, PCILYNX_DRIVER_NAME, &aux_ops)) { PRINT_G(KERN_ERR, "allocation of char major number %d failed", PCILYNX_MAJOR); return -EBUSY; } +#endif return 0; } diff --git a/drivers/ieee1394/pcilynx.h b/drivers/ieee1394/pcilynx.h index cf45d8d0a..ccb5a9f0b 100644 --- a/drivers/ieee1394/pcilynx.h +++ b/drivers/ieee1394/pcilynx.h @@ -12,7 +12,7 @@ #define PCI_DEVICE_ID_TI_PCILYNX 0x8000 #define MAX_PCILYNX_CARDS 4 -#define LOCALRAM_SIZE 64 +#define LOCALRAM_SIZE 4096 #define NUM_ISORCV_PCL 4 #define MAX_ISORCV_SIZE 2048 @@ -50,23 +50,27 @@ struct ti_lynx { void *aux_port; +#ifdef CONFIG_IEEE1394_PCILYNX_PORTS atomic_t aux_intr_seen; wait_queue_head_t aux_intr_wait; void *mem_dma_buffer; + dma_addr_t mem_dma_buffer_dma; struct semaphore mem_dma_mutex; wait_queue_head_t mem_dma_intr_wait; +#endif /* - * use local RAM of LOCALRAM_SIZE (in kB) for PCLs, which allows for + * use local RAM of LOCALRAM_SIZE bytes for PCLs, which allows for * LOCALRAM_SIZE * 8 PCLs (each sized 128 bytes); * the following is an allocation bitmap */ - u8 pcl_bmap[LOCALRAM_SIZE]; + u8 pcl_bmap[LOCALRAM_SIZE / 1024]; -#ifndef CONFIG_IEEE1394_LYNXRAM +#ifndef CONFIG_IEEE1394_PCILYNX_LOCALRAM /* point to PCLs memory area if needed */ void *pcl_mem; + dma_addr_t pcl_mem_dma; #endif /* PCLs for local mem / aux transfers */ @@ -81,16 +85,21 @@ struct ti_lynx { pcl_t rcv_pcl_start, rcv_pcl; void *rcv_page; + dma_addr_t rcv_page_dma; int rcv_active; - pcl_t async_pcl_start, async_pcl; - struct hpsb_packet *async_queue; - spinlock_t async_queue_lock; + struct { + pcl_t pcl_start, pcl; + struct hpsb_packet *queue; + spinlock_t queue_lock; + dma_addr_t header_dma, data_dma; + } async; struct { pcl_t pcl[NUM_ISORCV_PCL]; u32 stat[NUM_ISORCV_PCL]; void *page[ISORCV_PAGES]; + dma_addr_t page_dma[ISORCV_PAGES]; pcl_t pcl_start; int chan_count; int next, last, used, running; @@ -381,11 +390,7 @@ inline static void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,13) - return lynx->dev->base_address[1] + pclid * sizeof(struct ti_pcl); -#else - return lynx->dev->resource[1].start + pclid * sizeof(struct ti_pcl); -#endif + return pci_resource_start(lynx->dev, 1) + pclid * sizeof(struct ti_pcl); } #else /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ @@ -407,7 +412,7 @@ inline static void get_pcl(const struct ti_lynx *lynx, pcl_t pclid, inline static u32 pcl_bus(const struct ti_lynx *lynx, pcl_t pclid) { - return virt_to_bus(lynx->pcl_mem) + pclid * sizeof(struct ti_pcl); + return lynx->pcl_mem_dma + pclid * sizeof(struct ti_pcl); } #endif /* CONFIG_IEEE1394_PCILYNX_LOCALRAM */ diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 523f5d393..83c1169fe 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -4,6 +4,9 @@ * Raw interface to the bus * * Copyright (C) 1999, 2000 Andreas E. Bombe + * + * This code is licensed under the GPL. See the file COPYING in the root + * directory of the kernel sources for details. */ #include <linux/kernel.h> @@ -13,8 +16,13 @@ #include <linux/fs.h> #include <linux/poll.h> #include <linux/module.h> +#include <linux/version.h> #include <asm/uaccess.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) +#include <linux/devfs_fs_kernel.h> +#endif + #include "ieee1394.h" #include "ieee1394_types.h" #include "ieee1394_core.h" @@ -24,6 +32,8 @@ #include "raw1394.h" +static devfs_handle_t devfs_handle = NULL; + LIST_HEAD(host_info_list); static int host_count = 0; spinlock_t host_info_lock = SPIN_LOCK_UNLOCKED; @@ -257,7 +267,7 @@ static void iso_receive(struct hpsb_host *host, int channel, quadlet_t *data, req->req.type = RAW1394_REQ_ISO_RECEIVE; req->req.generation = get_hpsb_generation(); req->req.misc = 0; - req->req.recvb = fi->iso_buffer; + req->req.recvb = (u64)fi->iso_buffer; req->req.length = MIN(length, fi->iso_buffer_length); list_add_tail(&req->list, &reqs); @@ -326,7 +336,7 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction, req->req.type = RAW1394_REQ_FCP_REQUEST; req->req.generation = get_hpsb_generation(); req->req.misc = nodeid | (direction << 16); - req->req.recvb = (quadlet_t *)fi->fcp_buffer; + req->req.recvb = (u64)fi->fcp_buffer; req->req.length = length; list_add_tail(&req->list, &reqs); @@ -343,7 +353,7 @@ static void fcp_request(struct hpsb_host *host, int nodeid, int direction, } -static int dev_read(struct file *file, char *buffer, size_t count, +static ssize_t dev_read(struct file *file, char *buffer, size_t count, loff_t *offset_is_ignored) { struct file_info *fi = (struct file_info *)file->private_data; @@ -376,7 +386,8 @@ static int dev_read(struct file *file, char *buffer, size_t count, req = list_entry(lh, struct pending_request, list); if (req->req.length) { - if (copy_to_user(req->req.recvb, req->data, req->req.length)) { + if (copy_to_user((void *)req->req.recvb, req->data, + req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; } } @@ -507,7 +518,7 @@ static void handle_iso_listen(struct file_info *fi, struct pending_request *req) } else { fi->listen_channels |= 1ULL << channel; hpsb_listen_channel(hl_handle, fi->host, channel); - fi->iso_buffer = req->req.recvb; + fi->iso_buffer = (void *)req->req.recvb; fi->iso_buffer_length = req->req.length; } } else { @@ -563,7 +574,7 @@ static int handle_local_request(struct file_info *fi, break; case RAW1394_REQ_ASYNC_WRITE: - if (copy_from_user(req->data, req->req.sendb, + if (copy_from_user(req->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -588,7 +599,7 @@ static int handle_local_request(struct file_info *fi, } } - if (copy_from_user(req->data, req->req.sendb, + if (copy_from_user(req->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -646,7 +657,7 @@ static int handle_remote_request(struct file_info *fi, if (req->req.length == 4) { quadlet_t x; - if (copy_from_user(&x, req->req.sendb, 4)) { + if (copy_from_user(&x, (void *)req->req.sendb, 4)) { req->req.error = RAW1394_ERROR_MEMFAULT; } @@ -658,7 +669,7 @@ static int handle_remote_request(struct file_info *fi, req->req.length); if (!packet) return -ENOMEM; - if (copy_from_user(packet->data, req->req.sendb, + if (copy_from_user(packet->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; } @@ -684,7 +695,7 @@ static int handle_remote_request(struct file_info *fi, req->req.misc); if (!packet) return -ENOMEM; - if (copy_from_user(packet->data, req->req.sendb, + if (copy_from_user(packet->data, (void *)req->req.sendb, req->req.length)) { req->req.error = RAW1394_ERROR_MEMFAULT; break; @@ -761,12 +772,12 @@ static int state_connected(struct file_info *fi, struct pending_request *req) } -static int dev_write(struct file *file, const char *buffer, size_t count, +static ssize_t dev_write(struct file *file, const char *buffer, size_t count, loff_t *offset_is_ignored) { struct file_info *fi = (struct file_info *)file->private_data; struct pending_request *req; - int retval = 0; + ssize_t retval = 0; if (count != sizeof(struct raw1394_request)) { return -EINVAL; @@ -828,8 +839,11 @@ static int dev_open(struct inode *inode, struct file *file) return -ENXIO; } + V22_COMPAT_MOD_INC_USE_COUNT; + fi = kmalloc(sizeof(struct file_info), SLAB_KERNEL); if (fi == NULL) { + V22_COMPAT_MOD_DEC_USE_COUNT; return -ENOMEM; } @@ -898,6 +912,7 @@ static int dev_release(struct inode *inode, struct file *file) kfree(fi); + V22_COMPAT_MOD_DEC_USE_COUNT; return 0; } @@ -910,7 +925,7 @@ static struct hpsb_highlevel_ops hl_ops = { }; static struct file_operations file_ops = { - owner: THIS_MODULE, + OWNER_THIS_MODULE read: dev_read, write: dev_write, poll: dev_poll, @@ -926,18 +941,24 @@ int init_raw1394(void) return -ENOMEM; } - if (register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, - &file_ops)) { - HPSB_ERR("raw1394 failed to allocate device major"); + devfs_handle = devfs_register(NULL, RAW1394_DEVICE_NAME, DEVFS_FL_NONE, + RAW1394_DEVICE_MAJOR, 0, + S_IFCHR | S_IRUSR | S_IWUSR, &file_ops, + NULL); + + if (devfs_register_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME, + &file_ops)) { + HPSB_ERR("raw1394 failed to register /dev/raw1394 device"); return -EBUSY; } - + printk(KERN_INFO "raw1394: /dev/%s device initialized\n", RAW1394_DEVICE_NAME); return 0; } void cleanup_raw1394(void) { - unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME); + devfs_unregister_chrdev(RAW1394_DEVICE_MAJOR, RAW1394_DEVICE_NAME); + devfs_unregister(devfs_handle); hpsb_unregister_highlevel(hl_handle); } diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h index f23bfc051..b0d819429 100644 --- a/drivers/ieee1394/raw1394.h +++ b/drivers/ieee1394/raw1394.h @@ -1,11 +1,10 @@ - #ifndef IEEE1394_RAW1394_H #define IEEE1394_RAW1394_H #define RAW1394_DEVICE_MAJOR 171 #define RAW1394_DEVICE_NAME "raw1394" -#define RAW1394_KERNELAPI_VERSION 2 +#define RAW1394_KERNELAPI_VERSION 3 /* state: opened */ #define RAW1394_REQ_INITIALIZE 1 @@ -45,24 +44,27 @@ #define RAW1394_ERROR_TIMEOUT (-1102) +#include <asm/types.h> + struct raw1394_request { - int type; - int error; - int misc; + __u32 type; + __s32 error; + __u32 misc; + + __u32 generation; + __u32 length; - unsigned int generation; - octlet_t address; + __u64 address; - unsigned long tag; + __u64 tag; - size_t length; - quadlet_t *sendb; - quadlet_t *recvb; + __u64 sendb; + __u64 recvb; }; struct raw1394_khost_list { - int nodes; - char name[32]; + __u32 nodes; + __u8 name[32]; }; #ifdef __KERNEL__ diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c new file mode 100644 index 000000000..9f00fd3f0 --- /dev/null +++ b/drivers/ieee1394/video1394.c @@ -0,0 +1,1266 @@ +/* + * video1394.c - video driver for OHCI 1394 boards + * Copyright (C)1999,2000 Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <asm/byteorder.h> +#include <asm/atomic.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/proc_fs.h> +#include <linux/tqueue.h> +#include <linux/delay.h> + +#include <asm/pgtable.h> +#include <asm/page.h> +#include <linux/sched.h> +#include <asm/segment.h> +#include <linux/types.h> +#include <linux/wrapper.h> +#include <linux/vmalloc.h> + +#include "ieee1394.h" +#include "ieee1394_types.h" +#include "hosts.h" +#include "ieee1394_core.h" +#include "video1394.h" + +#include "ohci1394.h" + +#define VIDEO1394_MAJOR 172 +#define ISO_CHANNELS 64 +#define ISO_RECEIVE 0 +#define ISO_TRANSMIT 1 + +struct it_dma_prg { + struct dma_cmd begin; + quadlet_t data[4]; + struct dma_cmd end; + quadlet_t pad[4]; /* FIXME: quick hack for memory alignment */ +}; + +struct dma_iso_ctx { + struct ti_ohci *ohci; + int ctx; + int channel; + int last_buffer; + unsigned int num_desc; + unsigned int buf_size; + unsigned int frame_size; + unsigned int packet_size; + unsigned int left_size; + unsigned int nb_cmd; + unsigned char *buf; + struct dma_cmd **ir_prg; + struct it_dma_prg **it_prg; + unsigned int *buffer_status; + int ctrlClear; + int ctrlSet; + int cmdPtr; + int ctxMatch; + wait_queue_head_t waitq; +}; + +struct video_card { + struct ti_ohci *ohci; + + struct dma_iso_ctx **ir_context; + struct dma_iso_ctx **it_context; + struct dma_iso_ctx *current_ctx; +}; + +#ifdef CONFIG_IEEE1394_VERBOSEDEBUG +#define VIDEO1394_DEBUG +#endif + +#ifdef DBGMSG +#undef DBGMSG +#endif + +#ifdef VIDEO1394_DEBUG +#define DBGMSG(card, fmt, args...) \ +printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args) +#else +#define DBGMSG(card, fmt, args...) +#endif + +/* print general (card independent) information */ +#define PRINT_G(level, fmt, args...) \ +printk(level "video1394: " fmt "\n" , ## args) + +/* print card specific information */ +#define PRINT(level, card, fmt, args...) \ +printk(level "video1394_%d: " fmt "\n" , card , ## args) + +void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent); + +static struct video_card video_cards[MAX_OHCI1394_CARDS]; +static int num_of_video_cards = 0; +static struct video_template video_tmpl = { irq_handler }; + +/* Taken from bttv.c */ +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define page_address(x) (x) +#endif + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) + ret = (page_address(pte_page(pte))| + (adr&(PAGE_SIZE-1))); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void * rvmalloc(unsigned long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, + no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, unsigned long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + +static int free_dma_iso_ctx(struct dma_iso_ctx **d) +{ + int i; + struct ti_ohci *ohci; + + if ((*d)==NULL) return -1; + + ohci = (struct ti_ohci *)(*d)->ohci; + + DBGMSG(ohci->id, "Freeing dma_iso_ctx %d", (*d)->ctx); + + ohci1394_stop_context(ohci, (*d)->ctrlClear, NULL); + + if ((*d)->buf) rvfree((void *)(*d)->buf, + (*d)->num_desc * (*d)->buf_size); + + if ((*d)->ir_prg) { + for (i=0;i<(*d)->num_desc;i++) + if ((*d)->ir_prg[i]) kfree((*d)->ir_prg[i]); + kfree((*d)->ir_prg); + } + + if ((*d)->it_prg) { + for (i=0;i<(*d)->num_desc;i++) + if ((*d)->it_prg[i]) kfree((*d)->it_prg[i]); + kfree((*d)->it_prg); + } + + if ((*d)->buffer_status) + kfree((*d)->buffer_status); + + kfree(*d); + *d = NULL; + + return 0; +} + +static struct dma_iso_ctx * +alloc_dma_iso_ctx(struct ti_ohci *ohci, int type, int ctx, int num_desc, + int buf_size, int channel, unsigned int packet_size) +{ + struct dma_iso_ctx *d=NULL; + int i; + + d = (struct dma_iso_ctx *)kmalloc(sizeof(struct dma_iso_ctx), + GFP_KERNEL); + memset(d, 0, sizeof(struct dma_iso_ctx)); + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma_iso_ctx"); + return NULL; + } + + d->ohci = (void *)ohci; + d->ctx = ctx; + d->channel = channel; + d->num_desc = num_desc; + d->frame_size = buf_size; + if (buf_size%PAGE_SIZE) + d->buf_size = buf_size + PAGE_SIZE - (buf_size%PAGE_SIZE); + else + d->buf_size = buf_size; + d->last_buffer = -1; + d->buf = NULL; + d->ir_prg = NULL; + init_waitqueue_head(&d->waitq); + + d->buf = rvmalloc(d->num_desc * d->buf_size); + + if (d->buf == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->buf, 0, d->num_desc * d->buf_size); + + if (type == ISO_RECEIVE) { + d->ctrlSet = OHCI1394_IsoRcvContextControlSet+32*d->ctx; + d->ctrlClear = OHCI1394_IsoRcvContextControlClear+32*d->ctx; + d->cmdPtr = OHCI1394_IsoRcvCommandPtr+32*d->ctx; + d->ctxMatch = OHCI1394_IsoRcvContextMatch+32*d->ctx; + + d->ir_prg = kmalloc(d->num_desc * sizeof(struct dma_cmd *), + GFP_KERNEL); + + if (d->ir_prg == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->ir_prg, 0, d->num_desc * sizeof(struct dma_cmd *)); + + d->nb_cmd = d->buf_size / PAGE_SIZE + 1; + d->left_size = (d->frame_size % PAGE_SIZE) ? + d->frame_size % PAGE_SIZE : PAGE_SIZE; + + for (i=0;i<d->num_desc;i++) { + d->ir_prg[i] = kmalloc(d->nb_cmd * + sizeof(struct dma_cmd), + GFP_KERNEL); + if (d->ir_prg[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + } + } + else { /* ISO_TRANSMIT */ + d->ctrlSet = OHCI1394_IsoXmitContextControlSet+16*d->ctx; + d->ctrlClear = OHCI1394_IsoXmitContextControlClear+16*d->ctx; + d->cmdPtr = OHCI1394_IsoXmitCommandPtr+16*d->ctx; + + d->it_prg = kmalloc(d->num_desc * sizeof(struct it_dma_prg *), + GFP_KERNEL); + + if (d->it_prg == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma it prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->it_prg, 0, d->num_desc*sizeof(struct it_dma_prg *)); + + d->packet_size = packet_size; + + if (PAGE_SIZE % packet_size || packet_size>2048) { + PRINT(KERN_ERR, ohci->id, + "Packet size %d not yet supported\n", + packet_size); + free_dma_iso_ctx(&d); + return NULL; + } + + d->nb_cmd = d->frame_size / d->packet_size; + if (d->frame_size % d->packet_size) { + d->nb_cmd++; + d->left_size = d->frame_size % d->packet_size; + } + else + d->left_size = d->packet_size; + + for (i=0;i<d->num_desc;i++) { + d->it_prg[i] = kmalloc(d->nb_cmd * + sizeof(struct it_dma_prg), + GFP_KERNEL); + if (d->it_prg[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "failed to allocate dma it prg"); + free_dma_iso_ctx(&d); + return NULL; + } + } + } + + d->buffer_status = kmalloc(d->num_desc * sizeof(unsigned int), + GFP_KERNEL); + + if (d->buffer_status == NULL) { + PRINT(KERN_ERR, ohci->id, "failed to allocate dma ir prg"); + free_dma_iso_ctx(&d); + return NULL; + } + memset(d->buffer_status, 0, d->num_desc * sizeof(unsigned int)); + + PRINT(KERN_INFO, ohci->id, "Iso %s DMA: %d buffers " + "of size %d allocated for a frame size %d, each with %d prgs", + (type==ISO_RECEIVE) ? "receive" : "transmit", + d->num_desc, d->buf_size, d->frame_size, d->nb_cmd); + + return d; +} + +static void reset_ir_status(struct dma_iso_ctx *d, int n) +{ + int i; + d->ir_prg[n][0].status = 4; + d->ir_prg[n][1].status = PAGE_SIZE-4; + for (i=2;i<d->nb_cmd-1;i++) + d->ir_prg[n][i].status = PAGE_SIZE; + d->ir_prg[n][i].status = d->left_size; +} + +static void initialize_dma_ir_prg(struct dma_iso_ctx *d, int n) +{ + struct dma_cmd *ir_prg = d->ir_prg[n]; + unsigned long buf = (unsigned long)d->buf+n*d->buf_size; + int i; + + /* the first descriptor will sync and read only 4 bytes */ + ir_prg[0].control = (0x280F << 16) | 4; + ir_prg[0].address = kvirt_to_bus(buf); + ir_prg[0].branchAddress = (virt_to_bus(&(ir_prg[1].control)) + & 0xfffffff0) | 0x1; + + /* the second descriptor will read PAGE_SIZE-4 bytes */ + ir_prg[1].control = (0x280C << 16) | (PAGE_SIZE-4); + ir_prg[1].address = kvirt_to_bus(buf+4); + ir_prg[1].branchAddress = (virt_to_bus(&(ir_prg[2].control)) + & 0xfffffff0) | 0x1; + + for (i=2;i<d->nb_cmd-1;i++) { + ir_prg[i].control = (0x280C << 16) | PAGE_SIZE; + ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); + + ir_prg[i].branchAddress = + (virt_to_bus(&(ir_prg[i+1].control)) + & 0xfffffff0) | 0x1; + } + + /* the last descriptor will generate an interrupt */ + ir_prg[i].control = (0x283C << 16) | d->left_size; + ir_prg[i].address = kvirt_to_bus(buf+(i-1)*PAGE_SIZE); +} + +static void initialize_dma_ir_ctx(struct dma_iso_ctx *d, int tag) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;i<d->num_desc;i++) { + initialize_dma_ir_prg(d, i); + reset_ir_status(d, i); + } + + /* Set bufferFill, no header */ + reg_write(ohci, d->ctrlSet, 0x80000000); + + /* Set the context match register to match on all tags, + sync for sync tag, and listen to d->channel */ + reg_write(ohci, d->ctxMatch, 0xf0000000|((tag&0xf)<<8)|d->channel); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 1<<d->ctx); +} + +/* find which context is listening to this channel */ +int ir_ctx_listening(struct video_card *video, int channel) +{ + int i; + struct ti_ohci *ohci = video->ohci; + + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]) { + if (video->ir_context[i]->channel==channel) + return i; + } + + PRINT(KERN_ERR, ohci->id, + "no iso context is listening to channel %d", + channel); + return -1; +} + +int it_ctx_talking(struct video_card *video, int channel) +{ + int i; + struct ti_ohci *ohci = video->ohci; + + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]) { + if (video->it_context[i]->channel==channel) + return i; + } + + PRINT(KERN_ERR, ohci->id, + "no iso context is talking to channel %d", + channel); + return -1; +} + +int wakeup_dma_ir_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) +{ + int i; + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "Iso receive event received but " + "context not allocated"); + return -EFAULT; + } + + for (i=0;i<d->num_desc;i++) { + if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) { + reset_ir_status(d, i); + d->buffer_status[i] = VIDEO1394_BUFFER_READY; + } + } + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + return 0; +} + +int wakeup_dma_it_ctx(struct ti_ohci *ohci, struct dma_iso_ctx *d) +{ + int i; + + if (d==NULL) { + PRINT(KERN_ERR, ohci->id, "Iso transmit event received but " + "context not allocated"); + return -EFAULT; + } + + for (i=0;i<d->num_desc;i++) { + if (d->it_prg[i][d->nb_cmd-1].end.status & 0xFFFF0000) { + d->it_prg[i][d->nb_cmd-1].end.status = 0; + d->buffer_status[i] = VIDEO1394_BUFFER_READY; + } + } + if (waitqueue_active(&d->waitq)) wake_up_interruptible(&d->waitq); + return 0; +} + +static void initialize_dma_it_prg(struct dma_iso_ctx *d, int n, int sync_tag) +{ + struct it_dma_prg *it_prg = d->it_prg[n]; + unsigned long buf = (unsigned long)d->buf+n*d->buf_size; + int i; + + for (i=0;i<d->nb_cmd;i++) { + + it_prg[i].begin.control = OUTPUT_MORE_IMMEDIATE | 8 ; + it_prg[i].begin.address = 0; + + it_prg[i].begin.status = 0; + + /* FIXME: what is the tag value + speed selection */ + it_prg[i].data[0] = + (DMA_SPEED_400<<16) | (d->channel<<8) | 0xa0; + if (i==0) it_prg[i].data[0] |= sync_tag; + it_prg[i].data[1] = d->packet_size << 16; + it_prg[i].data[2] = 0; + it_prg[i].data[3] = 0; + + it_prg[i].end.control = 0x100c0000; + it_prg[i].end.address = + kvirt_to_bus(buf+i*d->packet_size); + + if (i<d->nb_cmd-1) { + it_prg[i].end.control |= d->packet_size; + it_prg[i].begin.branchAddress = + (virt_to_bus(&(it_prg[i+1].begin.control)) + & 0xfffffff0) | 0x3; + it_prg[i].end.branchAddress = + (virt_to_bus(&(it_prg[i+1].begin.control)) + & 0xfffffff0) | 0x3; + } + else { + /* the last prg generates an interrupt */ + it_prg[i].end.control |= 0x08300000 | d->left_size; + /* the last prg doesn't branch */ + it_prg[i].begin.branchAddress = 0; + it_prg[i].end.branchAddress = 0; + } + it_prg[i].end.status = 0; + +#if 0 + printk("%d:%d: %08x-%08x ctrl %08x brch %08x d0 %08x d1 %08x\n",n,i, + virt_to_bus(&(it_prg[i].begin.control)), + virt_to_bus(&(it_prg[i].end.control)), + it_prg[i].end.control, + it_prg[i].end.branchAddress, + it_prg[i].data[0], it_prg[i].data[1]); +#endif + } +} + +static void initialize_dma_it_ctx(struct dma_iso_ctx *d, int sync_tag) +{ + struct ti_ohci *ohci = (struct ti_ohci *)d->ohci; + int i; + + ohci1394_stop_context(ohci, d->ctrlClear, NULL); + + for (i=0;i<d->num_desc;i++) + initialize_dma_it_prg(d, i, sync_tag); + + /* Set up isoRecvIntMask to generate interrupts */ + reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, 1<<d->ctx); +} + +static int do_iso_mmap(struct ti_ohci *ohci, struct dma_iso_ctx *d, + const char *adr, unsigned long size) +{ + unsigned long start=(unsigned long) adr; + unsigned long page,pos; + + if (size>d->num_desc * d->buf_size) { + PRINT(KERN_ERR, ohci->id, + "iso context %d buf size is different from mmap size", + d->ctx); + return -EINVAL; + } + if (!d->buf) { + PRINT(KERN_ERR, ohci->id, + "iso context %d is not allocated", d->ctx); + return -EINVAL; + } + + pos=(unsigned long) d->buf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; +} + +static int video1394_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct video_card *video = &video_cards[MINOR(inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + + switch(cmd) + { + case VIDEO1394_LISTEN_CHANNEL: + case VIDEO1394_TALK_CHANNEL: + { + struct video1394_mmap v; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + if (v.channel<0 || v.channel>(ISO_CHANNELS-1)) { + PRINT(KERN_ERR, ohci->id, + "iso channel %d out of bound", v.channel); + return -EFAULT; + } + if (test_and_set_bit(v.channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is already taken", v.channel); + return -EFAULT; + } + + if (v.buf_size<=0) { + PRINT(KERN_ERR, ohci->id, + "Invalid %d length buffer requested",v.buf_size); + return -EFAULT; + } + + if (v.nb_buffers<=0) { + PRINT(KERN_ERR, ohci->id, + "Invalid %d buffers requested",v.nb_buffers); + return -EFAULT; + } + + if (v.nb_buffers * v.buf_size > VIDEO1394_MAX_SIZE) { + PRINT(KERN_ERR, ohci->id, + "%d buffers of size %d bytes is too big", + v.nb_buffers, v.buf_size); + return -EFAULT; + } + + if (cmd == VIDEO1394_LISTEN_CHANNEL) { + /* find a free iso receive context */ + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]==NULL) break; + + if (i==(ohci->nb_iso_rcv_ctx-1)) { + PRINT(KERN_ERR, ohci->id, + "no iso context available"); + return -EFAULT; + } + + video->ir_context[i] = + alloc_dma_iso_ctx(ohci, ISO_RECEIVE, i+1, + v.nb_buffers, v.buf_size, + v.channel, 0); + + if (video->ir_context[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "Couldn't allocate ir context"); + return -EFAULT; + } + initialize_dma_ir_ctx(video->ir_context[i], + v.sync_tag); + + video->current_ctx = video->ir_context[i]; + + v.buf_size = video->ir_context[i]->buf_size; + + PRINT(KERN_INFO, ohci->id, + "iso context %d listen on channel %d", i+1, + v.channel); + } + else { + /* find a free iso transmit context */ + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]==NULL) break; + + if (i==ohci->nb_iso_xmit_ctx) { + PRINT(KERN_ERR, ohci->id, + "no iso context available"); + return -EFAULT; + } + + video->it_context[i] = + alloc_dma_iso_ctx(ohci, ISO_TRANSMIT, i, + v.nb_buffers, v.buf_size, + v.channel, v.packet_size); + + if (video->it_context[i] == NULL) { + PRINT(KERN_ERR, ohci->id, + "Couldn't allocate it context"); + return -EFAULT; + } + initialize_dma_it_ctx(video->it_context[i], + v.sync_tag); + + video->current_ctx = video->it_context[i]; + + v.buf_size = video->it_context[i]->buf_size; + + PRINT(KERN_INFO, ohci->id, + "iso context %d talk on channel %d", i, + v.channel); + } + + if(copy_to_user((void *)arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDEO1394_UNLISTEN_CHANNEL: + case VIDEO1394_UNTALK_CHANNEL: + { + int channel; + int i; + + if(copy_from_user(&channel, (void *)arg, sizeof(int))) + return -EFAULT; + + if (!test_and_clear_bit(channel, &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", channel); + return -EFAULT; + } + + if (cmd == VIDEO1394_UNLISTEN_CHANNEL) { + i = ir_ctx_listening(video, channel); + if (i<0) return -EFAULT; + + free_dma_iso_ctx(&video->ir_context[i]); + + PRINT(KERN_INFO, ohci->id, + "iso context %d stop listening on channel %d", + i+1, channel); + } + else { + i = it_ctx_talking(video, channel); + if (i<0) return -EFAULT; + + free_dma_iso_ctx(&video->it_context[i]); + + PRINT(KERN_INFO, ohci->id, + "iso context %d stop talking on channel %d", + i, channel); + } + + return 0; + } + case VIDEO1394_LISTEN_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = ir_ctx_listening(video, v.channel); + if (i<0) return -EFAULT; + d = video->ir_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->id, + "buffer %d is already used",v.buffer); + return -EFAULT; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + if (d->last_buffer>=0) + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = + (virt_to_bus(&(d->ir_prg[v.buffer][0].control)) + & 0xfffffff0) | 0x1; + + d->last_buffer = v.buffer; + + d->ir_prg[d->last_buffer][d->nb_cmd-1].branchAddress = 0; + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->id, "Starting iso DMA ctx=%d",d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + virt_to_bus(&(d->ir_prg[v.buffer][0]))|0x1); + + /* Run IR context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, ohci->id, + "Waking up iso dma ctx=%d", d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_LISTEN_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = ir_ctx_listening(video, v.channel); + if (i<0) return -EFAULT; + d = video->ir_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: +#if 1 + while(d->buffer_status[v.buffer]!= + VIDEO1394_BUFFER_READY) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + } +#else + if (wait_event_interruptible(d->waitq, + d->buffer_status[v.buffer] + == VIDEO1394_BUFFER_READY) + == -ERESTARTSYS) + return -EINTR; +#endif + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->id, + "buffer %d is not queued",v.buffer); + return -EFAULT; + } + } + case VIDEO1394_TALK_QUEUE_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = it_ctx_talking(video, v.channel); + if (i<0) return -EFAULT; + d = video->it_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + if (d->buffer_status[v.buffer]!=VIDEO1394_BUFFER_FREE) { + PRINT(KERN_ERR, ohci->id, + "buffer %d is already used",v.buffer); + return -EFAULT; + } + + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_QUEUED; + + if (d->last_buffer>=0) { + d->it_prg[d->last_buffer] + [d->nb_cmd-1].end.branchAddress = + (virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) + & 0xfffffff0) | 0x3; + + d->it_prg[d->last_buffer] + [d->nb_cmd-1].begin.branchAddress = + (virt_to_bus(&(d->it_prg[v.buffer][0].begin.control)) + & 0xfffffff0) | 0x3; + } + d->last_buffer = v.buffer; + + d->it_prg[d->last_buffer][d->nb_cmd-1].end.branchAddress = 0; + + if (!(reg_read(ohci, d->ctrlSet) & 0x8000)) + { + DBGMSG(ohci->id, "Starting iso transmit DMA ctx=%d", + d->ctx); + + /* Tell the controller where the first program is */ + reg_write(ohci, d->cmdPtr, + virt_to_bus(&(d->it_prg[v.buffer][0]))|0x3); + + /* Run IT context */ + reg_write(ohci, d->ctrlSet, 0x8000); + } + else { + /* Wake up dma context if necessary */ + if (!(reg_read(ohci, d->ctrlSet) & 0x400)) { + PRINT(KERN_INFO, ohci->id, + "Waking up iso transmit dma ctx=%d", + d->ctx); + reg_write(ohci, d->ctrlSet, 0x1000); + } + } + return 0; + + } + case VIDEO1394_TALK_WAIT_BUFFER: + { + struct video1394_wait v; + struct dma_iso_ctx *d; + int i; + + if(copy_from_user(&v, (void *)arg, sizeof(v))) + return -EFAULT; + + i = it_ctx_talking(video, v.channel); + if (i<0) return -EFAULT; + d = video->it_context[i]; + + if ((v.buffer<0) || (v.buffer>d->num_desc)) { + PRINT(KERN_ERR, ohci->id, + "buffer %d out of range",v.buffer); + return -EFAULT; + } + + switch(d->buffer_status[v.buffer]) { + case VIDEO1394_BUFFER_READY: + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + case VIDEO1394_BUFFER_QUEUED: +#if 1 + while(d->buffer_status[v.buffer]!= + VIDEO1394_BUFFER_READY) { + interruptible_sleep_on(&d->waitq); + if(signal_pending(current)) return -EINTR; + } +#else + if (wait_event_interruptible(d->waitq, + d->buffer_status[v.buffer] + == VIDEO1394_BUFFER_READY) + == -ERESTARTSYS) + return -EINTR; +#endif + d->buffer_status[v.buffer]=VIDEO1394_BUFFER_FREE; + return 0; + default: + PRINT(KERN_ERR, ohci->id, + "buffer %d is not queued",v.buffer); + return -EFAULT; + } + } + default: + return -EINVAL; + } +} + +/* + * This maps the vmalloced and reserved buffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +int video1394_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_card *video = + &video_cards[MINOR(file->f_dentry->d_inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + + PRINT(KERN_INFO, ohci->id, "mmap"); + if (video->current_ctx == NULL) { + PRINT(KERN_ERR, ohci->id, "current iso context not set"); + return -EINVAL; + } + + return do_iso_mmap(ohci, video->current_ctx, + (char *)vma->vm_start, + (unsigned long)(vma->vm_end-vma->vm_start)); + return 0; +} + +static int video1394_open(struct inode *inode, struct file *file) +{ + int i = MINOR(inode->i_rdev); + + if (i<0 || i>=num_of_video_cards) { + PRINT(KERN_ERR, i, "ohci card %d not found", i); + return -EIO; + } + + V22_COMPAT_MOD_INC_USE_COUNT; + + PRINT(KERN_INFO, i, "open"); + + return 0; +} + +static int video1394_release(struct inode *inode, struct file *file) +{ + struct video_card *video = &video_cards[MINOR(inode->i_rdev)]; + struct ti_ohci *ohci= video->ohci; + int i; + + for (i=0;i<ohci->nb_iso_rcv_ctx-1;i++) + if (video->ir_context[i]) { + if (!test_and_clear_bit( + video->ir_context[i]->channel, + &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", + video->ir_context[i]->channel); + } + PRINT(KERN_INFO, ohci->id, + "iso receive context %d stop listening " + "on channel %d", i+1, + video->ir_context[i]->channel); + free_dma_iso_ctx(&video->ir_context[i]); + } + + for (i=0;i<ohci->nb_iso_xmit_ctx;i++) + if (video->it_context[i]) { + if (!test_and_clear_bit( + video->it_context[i]->channel, + &ohci->IR_channel_usage)) { + PRINT(KERN_ERR, ohci->id, + "channel %d is not being used", + video->it_context[i]->channel); + } + PRINT(KERN_INFO, ohci->id, + "iso transmit context %d stop talking " + "on channel %d", i+1, + video->it_context[i]->channel); + free_dma_iso_ctx(&video->it_context[i]); + } + + + V22_COMPAT_MOD_DEC_USE_COUNT; + + PRINT(KERN_INFO, ohci->id, "release"); + return 0; +} + +void irq_handler(int card, quadlet_t isoRecvIntEvent, + quadlet_t isoXmitIntEvent) +{ + int i; + struct video_card *video = &video_cards[card]; + + DBGMSG(card, "Iso event Recv: %08x Xmit: %08x", + isoRecvIntEvent, isoXmitIntEvent); + + for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++) + if (isoRecvIntEvent & (1<<(i+1))) + wakeup_dma_ir_ctx(video->ohci, + video->ir_context[i]); + + for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++) + if (isoXmitIntEvent & (1<<i)) + wakeup_dma_it_ctx(video->ohci, + video->it_context[i]); +} + +static struct file_operations video1394_fops= +{ + OWNER_THIS_MODULE + ioctl: video1394_ioctl, + mmap: video1394_mmap, + open: video1394_open, + release: video1394_release +}; + +static int video1394_init(int i, struct ti_ohci *ohci) +{ + struct video_card *video = &video_cards[i]; + + if (ohci1394_register_video(ohci, &video_tmpl)<0) { + PRINT(KERN_ERR, i, "register_video failed"); + return -1; + } + + video->ohci = ohci; + + /* Iso receive dma contexts */ + video->ir_context = (struct dma_iso_ctx **) + kmalloc((ohci->nb_iso_rcv_ctx-1)* + sizeof(struct dma_iso_ctx *), GFP_KERNEL); + if (video->ir_context) + memset(video->ir_context, 0, + (ohci->nb_iso_rcv_ctx-1)*sizeof(struct dma_iso_ctx *)); + else { + PRINT(KERN_ERR, ohci->id, "Cannot allocate ir_context"); + return -1; + } + + /* Iso transmit dma contexts */ + video->it_context = (struct dma_iso_ctx **) + kmalloc(ohci->nb_iso_xmit_ctx * + sizeof(struct dma_iso_ctx *), GFP_KERNEL); + if (video->it_context) + memset(video->it_context, 0, + ohci->nb_iso_xmit_ctx * sizeof(struct dma_iso_ctx *)); + else { + PRINT(KERN_ERR, ohci->id, "Cannot allocate it_context"); + return -1; + } + + return 0; +} + +static void remove_card(struct video_card *video) +{ + int i; + + ohci1394_unregister_video(video->ohci, &video_tmpl); + + /* Free the iso receive contexts */ + if (video->ir_context) { + for (i=0;i<video->ohci->nb_iso_rcv_ctx-1;i++) { + free_dma_iso_ctx(&video->ir_context[i]); + } + kfree(video->ir_context); + } + + /* Free the iso transmit contexts */ + if (video->it_context) { + for (i=0;i<video->ohci->nb_iso_xmit_ctx;i++) { + free_dma_iso_ctx(&video->it_context[i]); + } + kfree(video->it_context); + } +} + +#ifdef MODULE + +/* EXPORT_NO_SYMBOLS; */ + +MODULE_AUTHOR("Sebastien Rougeaux <sebastien.rougeaux@anu.edu.au>"); +MODULE_DESCRIPTION("driver for digital video on OHCI board"); +MODULE_SUPPORTED_DEVICE("video1394"); + +void cleanup_module(void) +{ + int i; + unregister_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME); + + for (i=0; i<num_of_video_cards; i++) + remove_card(&video_cards[i]); + + printk(KERN_INFO "removed " VIDEO1394_DRIVER_NAME " module\n"); +} + +int init_module(void) +{ + struct ti_ohci *ohci; + int i; + + memset(video_cards, 0, MAX_OHCI1394_CARDS * sizeof(struct video_card)); + num_of_video_cards = 0; + + for (i=0; i<MAX_OHCI1394_CARDS; i++) { + ohci=ohci1394_get_struct(i); + if (ohci) { + num_of_video_cards++; + video1394_init(i, ohci); + } + } + + if (!num_of_video_cards) { + PRINT_G(KERN_INFO, "no ohci card found... init failed"); + return -EIO; + } + + if (register_chrdev(VIDEO1394_MAJOR, VIDEO1394_DRIVER_NAME, + &video1394_fops)) { + printk("video1394: unable to get major %d\n", + VIDEO1394_MAJOR); + return -EIO; + } + + PRINT_G(KERN_INFO, "initialized with %d ohci cards", + num_of_video_cards); + + return 0; +} + +#endif /* MODULE */ + + diff --git a/drivers/ieee1394/video1394.h b/drivers/ieee1394/video1394.h index 47b8e64a2..dc6e5c981 100644 --- a/drivers/ieee1394/video1394.h +++ b/drivers/ieee1394/video1394.h @@ -20,6 +20,8 @@ #ifndef _VIDEO_1394_H #define _VIDEO_1394_H +#define VIDEO1394_DRIVER_NAME "video1394" + #define VIDEO1394_MAX_SIZE 0x400000 enum { @@ -31,8 +33,12 @@ enum { enum { VIDEO1394_LISTEN_CHANNEL = 0, VIDEO1394_UNLISTEN_CHANNEL, - VIDEO1394_QUEUE_BUFFER, - VIDEO1394_WAIT_BUFFER + VIDEO1394_LISTEN_QUEUE_BUFFER, + VIDEO1394_LISTEN_WAIT_BUFFER, + VIDEO1394_TALK_CHANNEL, + VIDEO1394_UNTALK_CHANNEL, + VIDEO1394_TALK_QUEUE_BUFFER, + VIDEO1394_TALK_WAIT_BUFFER }; struct video1394_mmap { @@ -40,6 +46,8 @@ struct video1394_mmap { int sync_tag; int nb_buffers; int buf_size; + int packet_size; + int fps; }; struct video1394_wait { @@ -47,4 +55,5 @@ struct video1394_wait { int buffer; }; -#endif + +#endif |