patch-2.4.0-test2 linux/drivers/ieee1394/ohci1394.c

Next file: linux/drivers/ieee1394/ohci1394.h
Previous file: linux/drivers/ieee1394/ieee1394_types.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test1/linux/drivers/ieee1394/ohci1394.c linux/drivers/ieee1394/ohci1394.c
@@ -38,15 +38,25 @@
  * . 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
  */
 
 /* 
  * Acknowledgments:
  *
  * Emilie Chung	<emilie.chung@axis.com>
- *  .Tip on Async Request Filter
+ *  . Tip on Async Request Filter
  * Pascal Drolet <pascal.drolet@informission.ca>
- *  .Various tips for optimization and functionnalities
+ *  . Various tips for optimization and functionnalities
+ * Robert Ficklin <rficklin@westengineering.com>
+ *  . Loop in irq_handler
+ * James Goodwin <jamesg@Filanet.com>
+ *  . Various tips on initialization, self-id reception, etc.
+ * Albrecht Dress <ad@mpifr-bonn.mpg.de>
+ *  . Apple PowerBook detection
+ * Daniel Kobras <daniel.kobras@student.uni-tuebingen.de>
+ *  . Reset the board properly before leaving
  */
 
 #include <linux/config.h>
@@ -65,6 +75,7 @@
 #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>
@@ -118,6 +129,7 @@
 	{ PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_UPD72862 },
 	{ 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 },
 	{ -1, -1 }
 };
 
@@ -128,7 +140,619 @@
 static void remove_card(struct ti_ohci *ohci);
 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 *
@@ -220,6 +844,22 @@
 		      "Error in reception of self-id packets"
 		      "Self-id count: %08x q[0]: %08x",
 		      self_id_count, q[0]);
+
+		/*
+		 * Tip by James Goodwin <jamesg@Filanet.com>:
+		 * We had an error, generate another bus reset in response. 
+		 * TODO. Actually read the current value in the phy before 
+		 * generating a bus reset (read modify write). This way
+		 * we don't stomp any current gap count settings, etc.
+		 */
+		if (ohci->self_id_errors<OHCI1394_MAX_SELF_ID_ERRORS) {
+			reg_write(ohci, OHCI1394_PhyControl, 0x000041ff);      
+			ohci->self_id_errors++;
+		}
+		else {
+			PRINT(KERN_ERR, ohci->id, 
+			      "Timeout on self-id error reception");
+		}
 		return -1;
 	}
 	
@@ -411,15 +1051,34 @@
 
 	spin_lock_init(&ohci->phy_reg_lock);
   
+	/*
+	 * Tip by James Goodwin <jamesg@Filanet.com>:
+	 * We need to add delays after the soft reset, setting LPS, and
+	 * enabling our link. This might fixes the self-id reception 
+	 * problem at initialization.
+	 */ 
+
 	/* Soft reset */
 	if ((retval=ohci_soft_reset(ohci))<0) return retval;
-  
-	/* Set the bus number */
-	reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
 
+	/* 
+	 *Delay aftger soft reset to make sure everything has settled
+	 * down (sanity)
+	 */
+	mdelay(100);    
+  
 	/* Set Link Power Status (LPS) */
 	reg_write(ohci, OHCI1394_HCControlSet, 0x00080000);
 
+	/*
+	 * Delay after setting LPS in order to make sure link/phy
+	 * communication is established
+	 */
+	mdelay(100);   
+
+	/* Set the bus number */
+	reg_write(ohci, OHCI1394_NodeID, 0x0000ffc0);
+
 	/* Enable posted writes */
 	reg_write(ohci, OHCI1394_HCControlSet, 0x00040000);
 
@@ -464,9 +1123,6 @@
 	/* Don't accept phy packets into AR request context */ 
 	reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400);
 
-	/* Enable link */
-	reg_write(ohci, OHCI1394_HCControlSet, 0x00020000);
-
 	/* Initialize IR dma */
 	ohci->nb_iso_ctx = get_nb_iso_ctx(ohci);
 	PRINT(KERN_INFO, ohci->id, "%d iso contexts available",
@@ -477,7 +1133,18 @@
 		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;
+	}
+#endif
 	/* Set bufferFill, isochHeader, multichannel for IR context */
 	reg_write(ohci, OHCI1394_IrRcvContextControlSet, 0xd0000000);
 			
@@ -495,16 +1162,6 @@
 	   (thanks to Michael Greger for seeing that I forgot this) */
 	reg_write(ohci, OHCI1394_IsoRecvIntMaskSet, 0x00000001);
 
-	initialize_dma_rcv_ctx(ohci->ir_context);
-
-	/* 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);
-
 	/* 
 	 * Accept AT requests from all nodes. This probably 
 	 * will have to be controlled from the subsystem
@@ -539,6 +1196,20 @@
 		  OHCI1394_isochRx
 		);
 
+	/* 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;
 }
 
@@ -619,7 +1290,7 @@
 	struct ti_ohci *ohci = host->hostdata;
 	struct dma_trm_ctx *d;
 	unsigned char tcode;
-	int i=50;
+	int timeout=50;
 
 	if (packet->data_size >= ohci->max_packet_size) {
 		PRINT(KERN_ERR, ohci->id, 
@@ -642,8 +1313,9 @@
 	}
 	while (d->free_prgs<1) {
 		spin_unlock(&d->lock);
-		schedule();
-		if (i-- <0) {
+		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;
@@ -687,7 +1359,7 @@
 
 	switch (cmd) {
 	case RESET_BUS:
-		host->attempt_root=1;
+	        host->attempt_root=1;
 		PRINT(KERN_INFO, ohci->id, "resetting bus on request%s",
 		      (host->attempt_root ? " and attempting to become root"
 		       : ""));
@@ -743,6 +1415,11 @@
 
                 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);
@@ -845,137 +1522,179 @@
 	struct ti_ohci *ohci = (struct ti_ohci *)dev_id;
 	struct hpsb_host *host = ohci->host;
 	int phyid = -1, isroot = 0;
+	int timeout = 255;
 
-	/* read the interrupt event register */
-	event=reg_read(ohci, OHCI1394_IntEventSet);
-
-#if 0
-	/* 
-	 * clear the interrupt event register, except for the
-	 * bus reset event interrupt (if any). This is an
-	 * attempt to comply with ohci spec 7.2.3.2
-	 */
-	reg_write(ohci, OHCI1394_IntEventClear, event & (~OHCI1394_busReset));
+	do {
+		/* read the interrupt event register */
+		event=reg_read(ohci, OHCI1394_IntEventClear);
+
+		DBGMSG(ohci->id, "IntEvent: %08x",event);
+
+		if (!event) return;
+
+		/* clear the interrupt event register */
+		reg_write(ohci, OHCI1394_IntEventClear, event);
+
+		if (event & OHCI1394_busReset) {
+			if (!host->in_bus_reset) {
+				PRINT(KERN_INFO, ohci->id, "Bus reset");
+				
+				/* Wait for the AT fifo to be flushed */
+				dma_trm_reset(ohci->at_req_context);
+				dma_trm_reset(ohci->at_resp_context);
+
+				/* Subsystem call */
+				hpsb_bus_reset(ohci->host);
+				
+				ohci->NumBusResets++;
+			}
+		}
+		/*
+		 * Problem: How can I ensure that the AT bottom half will be
+		 * executed before the AR bottom half (both events may have
+		 * occured within a single irq event)
+		 * Quick hack: just launch it within the IRQ handler
+		 */
+		if (event & OHCI1394_reqTxComplete) { 
+			struct dma_trm_ctx *d = ohci->at_req_context;
+			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, 
+					     "reqTxComplete");
+			else
+				dma_trm_bh((void *)d);
+		}
+		if (event & OHCI1394_respTxComplete) { 
+			struct dma_trm_ctx *d = ohci->at_resp_context;
+			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, 
+					     "respTxComplete");
+			else
+				dma_trm_bh((void *)d);
+		}
+		if (event & OHCI1394_RQPkt) {
+			struct dma_rcv_ctx *d = ohci->ar_req_context;
+			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");
+			else {
+#if 1
+				queue_task(&d->task, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
 #else
-	/* The above attempt doesn't work */
-	reg_write(ohci, OHCI1394_IntEventClear, event);
+				dma_rcv_bh((void *)d);
 #endif
-	if (event & OHCI1394_busReset) {
-		if (!host->in_bus_reset) {
-			PRINT(KERN_INFO, ohci->id, "Bus reset");
-
-			/* Wait for the AT fifo to be flushed */
-			dma_trm_reset(ohci->at_req_context);
-			dma_trm_reset(ohci->at_resp_context);
-
-#if 0
-			/* clear the bus reset event */
-			reg_write(ohci, OHCI1394_IntEventClear,
-				  OHCI1394_busReset);
-#endif
-			/* Subsystem call */
-			hpsb_bus_reset(ohci->host);
-
-			ohci->NumBusResets++;
+			}
 		}
-	}
-	/*
-	 * Problem: How can I ensure that the AT bottom half will be
-	 * executed before the AR bottom half (both events may have
-	 * occured within a single irq event)
-	 * Quick hack: just launch it within the IRQ handler
-	 */
-	if (event & OHCI1394_reqTxComplete) { 
-		struct dma_trm_ctx *d = ohci->at_req_context;
-		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, "reqTxComplete");
-		else
-			dma_trm_bh((void *)d);
-	}
-	if (event & OHCI1394_respTxComplete) { 
-		struct dma_trm_ctx *d = ohci->at_resp_context;
-		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, "respTxComplete");
-		else
-			dma_trm_bh((void *)d);
-	}
-	if (event & OHCI1394_RQPkt) {
-		struct dma_rcv_ctx *d = ohci->ar_req_context;
-		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");
-		else {
-			queue_task(&d->task, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+		if (event & OHCI1394_RSPkt) {
+			struct dma_rcv_ctx *d = ohci->ar_resp_context;
+			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");
+			else {
+#if 1
+				queue_task(&d->task, &tq_immediate);
+				mark_bh(IMMEDIATE_BH);
+#else
+				dma_rcv_bh((void *)d);
+#endif
+			}
 		}
-	}
-	if (event & OHCI1394_RSPkt) {
-		struct dma_rcv_ctx *d = ohci->ar_resp_context;
-		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");
-		else {
-			queue_task(&d->task, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+		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,
+				  isoRecvIntEvent);
+			DBGMSG(ohci->id, "Got isochRx interrupt "
+			       "status=0x%08X isoRecvIntEvent=%08x", 
+			       reg_read(ohci, d->ctrlSet), isoRecvIntEvent);
+			if (isoRecvIntEvent & 0x1) {
+				if (reg_read(ohci, d->ctrlSet) & 0x800)
+					stop_context(ohci, d->ctrlClear, 
+						     "isochRx");
+				else {
+#if 1
+					queue_task(&d->task, &tq_immediate);
+					mark_bh(IMMEDIATE_BH);
+#else
+					dma_rcv_bh((void *)d);
+#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 (event & OHCI1394_isochRx) {
-		quadlet_t isoRecvIntEvent;
-		struct dma_rcv_ctx *d = ohci->ir_context;
-                isoRecvIntEvent = reg_read(ohci, OHCI1394_IsoRecvIntEventSet);
-		reg_write(ohci, OHCI1394_IsoRecvIntEventClear,
-			  isoRecvIntEvent);
-		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, "isochRx");
-		else {
-			queue_task(&d->task, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+		if (event & OHCI1394_selfIDComplete) {
+			if (host->in_bus_reset) {
+				node_id = reg_read(ohci, OHCI1394_NodeID);
+				if (node_id & 0x80000000) { /* NodeID valid */
+					phyid =  node_id & 0x0000003f;
+					isroot = (node_id & 0x40000000) != 0;
+
+					PRINT(KERN_INFO, ohci->id,
+					      "SelfID process finished "
+					      "(phyid %d, %s)", phyid, 
+					      (isroot ? "root" : "not root"));
+
+					handle_selfid(ohci, host, 
+						      phyid, isroot);
+				}
+				else 
+					PRINT(KERN_ERR, ohci->id, 
+					      "SelfID process finished but "
+					      "NodeID not valid: %08X",
+					      node_id);
+
+				/* Accept Physical requests from all nodes. */
+				reg_write(ohci,OHCI1394_AsReqFilterHiSet, 
+					  0xffffffff);
+				reg_write(ohci,OHCI1394_AsReqFilterLoSet, 
+					  0xffffffff);
+                       		/*
+				 * Tip by James Goodwin <jamesg@Filanet.com>
+				 * Turn on phys dma reception. We should
+				 * probably manage the filtering somehow, 
+				 * instead of blindly turning it on.
+				 */
+				reg_write(ohci,OHCI1394_PhyReqFilterHiSet,
+					  0xffffffff);
+				reg_write(ohci,OHCI1394_PhyReqFilterLoSet,
+					  0xffffffff);
+                        	reg_write(ohci,OHCI1394_PhyUpperBound,
+					  0xffff0000);
+			} 
+			else PRINT(KERN_ERR, ohci->id, 
+				   "self-id received outside of bus reset"
+				   "sequence");
 		}
-	}
-	if (event & OHCI1394_selfIDComplete) {
-		if (host->in_bus_reset) {
-			node_id = reg_read(ohci, OHCI1394_NodeID);
-			if (node_id & 0x80000000) { /* NodeID valid */
-				phyid =  node_id & 0x0000003f;
-				isroot = (node_id & 0x40000000) != 0;
-
-				PRINT(KERN_INFO, ohci->id,
-				      "SelfID process finished (phyid %d, %s)",
-				      phyid, (isroot ? "root" : "not root"));
-
-				handle_selfid(ohci, host, phyid, isroot);
+		if (event & OHCI1394_phyRegRcvd) {
+#if 1
+			if (host->in_bus_reset) {
+				PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", 
+				      reg_read(ohci, OHCI1394_PhyControl));
 			}
-			else 
-				PRINT(KERN_ERR, ohci->id, 
-				      "SelfID process finished but NodeID"
-				      " not valid: %08X",node_id);
-
-			/* Accept Physical requests from all nodes. */
-			reg_write(ohci,OHCI1394_AsReqFilterHiSet, 0xffffffff);
-			reg_write(ohci,OHCI1394_AsReqFilterLoSet, 0xffffffff);
-		} 
-		else PRINT(KERN_INFO, ohci->id, 
-			   "phy reg received without reset\n");
-	}
-	if (event & OHCI1394_phyRegRcvd) {
-#if 0
-		if (host->in_bus_reset) {
-			PRINT(KERN_INFO, ohci->id, "PhyControl: %08X", 
-			      reg_read(ohci, OHCI1394_PhyControl));
-		} else 
-			PRINT(KERN_ERR, ohci->id, 
-			      "phy reg received without reset");
+			else PRINT(KERN_ERR, ohci->id, 
+				   "phy reg received outside of bus reset"
+				   "sequence");
 #endif
-	}
+		}
+	} while (--timeout);
 
+	PRINT(KERN_ERR, ohci->id, "irq_handler timeout event=0x%08x", event);
 }
 
 /* Put the buffer back into the dma context */
@@ -1119,6 +1838,17 @@
 				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
@@ -1132,6 +1862,20 @@
 				       (d->spb[length/4-1]>>16)&0x1f,
 				       (d->spb[length/4-1]>>21)&0x3,
 				       tcode, length, d->spb[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) {
+					d->spb[0] |= (ALL_NODES<<16);
+				}
 				hpsb_packet_received(ohci->host, d->spb, 
 						     length);
 			}
@@ -1154,6 +1898,20 @@
 				       (buf_ptr[length/4-1]>>16)&0x1f,
 				       (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);
+				}
 				hpsb_packet_received(ohci->host, buf_ptr, 
 						     length);
 			}
@@ -1207,32 +1965,42 @@
 	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);
+
 	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);
 }
 
-static int free_dma_rcv_ctx(struct dma_rcv_ctx *d)
+static int free_dma_rcv_ctx(struct dma_rcv_ctx **d)
 {
 	int i;
+	struct ti_ohci *ohci;
 
-	if (d==NULL) return -1;
+	if (*d==NULL) return -1;
 
-	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) {
-		for (i=0; i<d->num_desc; i++) 
-			if (d->prg[i]) kfree(d->prg[i]);
-		kfree(d->prg);
-	}
-	if (d->spb) kfree(d->spb);
+	ohci = (struct ti_ohci *)(*d)->ohci;
+
+	DBGMSG(ohci->id, "Freeing dma_rcv_ctx %d",(*d)->ctx);
 	
-	kfree(d);
+	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) {
+		for (i=0; i<(*d)->num_desc; i++)
+			if ((*d)->prg[i]) kfree((*d)->prg[i]);
+		kfree((*d)->prg);
+	}
+	if ((*d)->spb) kfree((*d)->spb);
 	
+	kfree(*d);
+	*d = NULL;
+
 	return 0;
 }
 
@@ -1270,7 +2038,7 @@
 
 	if (d->buf == NULL) {
 		PRINT(KERN_ERR, ohci->id, "failed to allocate dma buffer");
-		free_dma_rcv_ctx(d);
+		free_dma_rcv_ctx(&d);
 		return NULL;
 	}
 	memset(d->buf, 0, d->num_desc * sizeof(quadlet_t*));
@@ -1279,7 +2047,7 @@
 
 	if (d->prg == NULL) {
 		PRINT(KERN_ERR, ohci->id, "failed to allocate dma prg");
-		free_dma_rcv_ctx(d);
+		free_dma_rcv_ctx(&d);
 		return NULL;
 	}
 	memset(d->prg, 0, d->num_desc * sizeof(struct dma_cmd*));
@@ -1288,7 +2056,7 @@
 
 	if (d->spb == NULL) {
 		PRINT(KERN_ERR, ohci->id, "failed to allocate split buffer");
-		free_dma_rcv_ctx(d);
+		free_dma_rcv_ctx(&d);
 		return NULL;
 	}
 
@@ -1300,7 +2068,7 @@
 		} else {
 			PRINT(KERN_ERR, ohci->id, 
 			      "failed to allocate dma buffer");
-			free_dma_rcv_ctx(d);
+			free_dma_rcv_ctx(&d);
 			return NULL;
 		}
 
@@ -1311,7 +2079,7 @@
 		} else {
 			PRINT(KERN_ERR, ohci->id, 
 			      "failed to allocate dma prg");
-			free_dma_rcv_ctx(d);
+			free_dma_rcv_ctx(&d);
 			return NULL;
 		}
 	}
@@ -1319,17 +2087,29 @@
         spin_lock_init(&d->lock);
 
         /* initialize bottom handler */
+        d->task.sync = 0;
+        d->task.next = NULL;
         d->task.routine = dma_rcv_bh;
         d->task.data = (void*)d;
 
 	return d;
 }
 
-static int free_dma_trm_ctx(struct dma_trm_ctx *d)
+static int free_dma_trm_ctx(struct dma_trm_ctx **d)
 {
-	if (d==NULL) return -1;
-	if (d->prg) kfree(d->prg);
-	kfree(d);
+	struct ti_ohci *ohci;
+
+	if (*d==NULL) return -1;
+
+	ohci = (struct ti_ohci *)(*d)->ohci;
+
+	DBGMSG(ohci->id, "Freeing dma_trm_ctx %d",(*d)->ctx);
+
+	stop_context(ohci, (*d)->ctrlClear, NULL);
+
+	if ((*d)->prg) kfree((*d)->prg);
+	kfree(*d);
+	*d = NULL;
 	return 0;
 }
 
@@ -1359,7 +2139,7 @@
 
 	if (d->prg == NULL) {
 		PRINT(KERN_ERR, ohci->id, "failed to allocate at dma prg");
-		free_dma_trm_ctx(d);
+		free_dma_trm_ctx(&d);
 		return NULL;
 	}
 	memset(d->prg, 0, d->num_desc * sizeof(struct at_dma_prg));
@@ -1370,6 +2150,8 @@
         d->task.routine = dma_trm_bh;
         d->task.data = (void*)d;
 
+        init_waitqueue_head(&d->waitq);
+
 	return d;
 }
 
@@ -1385,10 +2167,12 @@
 		return 1;
 	}
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
-	/* XXX check return value */
-	pci_enable_device(dev);
-#endif
+        if (pci_enable_device(dev)) {
+                PRINT_G(KERN_NOTICE, "failed to enable OHCI hardware %d",
+                        num_of_cards);
+                return 1;
+        }
+        pci_set_master(dev);
 
 	ohci = &cards[num_of_cards++];
 
@@ -1397,13 +2181,6 @@
 	
 	ohci->state = 0;
 
-	if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
-			 OHCI1394_DRIVER_NAME, ohci)) {
-		PRINT(KERN_INFO, ohci->id, "allocated interrupt %d", dev->irq);
-	} else {
-		FAIL("failed to allocate shared interrupt %d", dev->irq);
-	}
-
 	/* csr_config rom allocation */
 	ohci->csr_config_rom = kmalloc(1024, GFP_KERNEL);
 	if (ohci->csr_config_rom == NULL) {
@@ -1437,7 +2214,9 @@
 				  OHCI1394_AsRspRcvContextControlClear,
 				  OHCI1394_AsRspRcvCommandPtr);
 	
-	if (ohci->ar_resp_context == NULL) return 1;
+	if (ohci->ar_resp_context == NULL) {
+		FAIL("failed to allocate AR Resp context");
+	}
 
 	ohci->at_req_context = 
 		alloc_dma_trm_ctx(ohci, 0, AT_REQ_NUM_DESC,
@@ -1445,7 +2224,9 @@
 				  OHCI1394_AsReqTrContextControlClear,
 				  OHCI1394_AsReqTrCommandPtr);
 	
-	if (ohci->at_req_context == NULL) return 1;
+	if (ohci->at_req_context == NULL) {
+		FAIL("failed to allocate AT Req context");
+	}
 
 	ohci->at_resp_context = 
 		alloc_dma_trm_ctx(ohci, 1, AT_RESP_NUM_DESC,
@@ -1453,7 +2234,9 @@
 				  OHCI1394_AsRspTrContextControlClear,
 				  OHCI1394_AsRspTrCommandPtr);
 	
-	if (ohci->at_resp_context == NULL) return 1;
+	if (ohci->at_resp_context == NULL) {
+		FAIL("failed to allocate AT Resp context");
+	}
 				      
 	ohci->ir_context =
 		alloc_dma_rcv_ctx(ohci, 2, IR_NUM_DESC,
@@ -1462,7 +2245,9 @@
 				  OHCI1394_IrRcvContextControlClear,
 				  OHCI1394_IrRcvCommandPtr);
 
-	if (ohci->ir_context == NULL) return 1;
+	if (ohci->ir_context == NULL) {
+		FAIL("failed to allocate IR context");
+	}
 
         ohci->IR_channel_usage= 0x0000000000000000;
         spin_lock_init(&ohci->IR_channel_lock);
@@ -1482,6 +2267,13 @@
 	PRINT(KERN_INFO, ohci->id, "remapped memory spaces reg 0x%p",
 	      ohci->registers);
 
+	if (!request_irq(dev->irq, ohci_irq_handler, SA_SHIRQ,
+			 OHCI1394_DRIVER_NAME, ohci)) {
+		PRINT(KERN_INFO, ohci->id, "allocated interrupt %d", dev->irq);
+	} else {
+		FAIL("failed to allocate shared interrupt %d", dev->irq);
+	}
+
 	return 0;
 #undef FAIL
 }
@@ -1507,6 +2299,10 @@
 	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", 
@@ -1535,6 +2331,27 @@
 		     host->is_busmgr ? "bus_mgr" : "");
   
 	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++) {
@@ -1621,7 +2438,8 @@
 		     dt->branchAddrPtr);
 	p += sprintf(p, "AT resp queue: first: %p last: %p\n",
 		     dt->first, dt->last);
-
+#endif
+	
 	/* ----- Register Dump ----- */
 	p += sprintf(p,"\n### HC Register dump ###\n");
 	SR("Version     : %08x  GUID_ROM    : %08x  ATRetries   : %08x\n",
@@ -1736,19 +2554,34 @@
 
 static void remove_card(struct ti_ohci *ohci)
 {
-	if (ohci->registers) 
-		iounmap(ohci->registers);
+#ifdef _VIDEO_1394_H
+	int i;
+#endif
 
 	/* Free AR dma */
-	free_dma_rcv_ctx(ohci->ar_req_context);
-	free_dma_rcv_ctx(ohci->ar_resp_context);
+	free_dma_rcv_ctx(&ohci->ar_req_context);
+	free_dma_rcv_ctx(&ohci->ar_resp_context);
 
 	/* Free AT dma */
-	free_dma_trm_ctx(ohci->at_req_context);
-	free_dma_trm_ctx(ohci->at_resp_context);
+	free_dma_trm_ctx(&ohci->at_req_context);
+	free_dma_trm_ctx(&ohci->at_resp_context);
 
 	/* Free IR dma */
-	free_dma_rcv_ctx(ohci->ir_context);
+	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)
@@ -1761,6 +2594,9 @@
 	/* Free the IRQ */
 	free_irq(ohci->dev->irq, ohci);
 
+	if (ohci->registers) 
+		iounmap(ohci->registers);
+
 	ohci->state = 0;
 }
 
@@ -1858,16 +2694,30 @@
 	proc_unregister(&proc_root, ohci_proc_entry.low_ino);
 #endif
 #endif
+
+#ifdef _VIDEO_1394_H
+        unregister_chrdev(OHCI1394_MAJOR, "ohci1394");
+#endif
+	
 	PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n");
 }
 
 int init_module(void)
 {
-
+	memset(cards, 0, MAX_OHCI1394_CARDS * sizeof (struct ti_ohci));
+	
 	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;
 	}
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)