diff -Nru a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
--- a/Documentation/filesystems/Locking	Sun May  5 20:39:49 2002
+++ b/Documentation/filesystems/Locking	Sun May  5 20:39:49 2002
@@ -137,6 +137,9 @@
 	int (*writepage)(struct page *);
 	int (*readpage)(struct file *, struct page *);
 	int (*sync_page)(struct page *);
+	int (*writeback_mapping)(struct address_space *, int *nr_to_write);
+	int (*vm_writeback)(struct page *, int *nr_to_write);
+	int (*set_page_dirty)(struct page *page);
 	int (*prepare_write)(struct file *, struct page *, unsigned, unsigned);
 	int (*commit_write)(struct file *, struct page *, unsigned, unsigned);
 	int (*bmap)(struct address_space *, long);
@@ -145,32 +148,71 @@
 	int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int);
 
 locking rules:
-	All may block
-		BKL	PageLocked(page)
-writepage:	no	yes, unlocks
-readpage:	no	yes, unlocks
-sync_page:	no	maybe
-prepare_write:	no	yes
-commit_write:	no	yes
-bmap:		yes
-flushpage:	no	yes
-releasepage:	no	yes
+	All except set_page_dirty may block
+
+			BKL	PageLocked(page)
+writepage:		no	yes, unlocks
+readpage:		no	yes, unlocks
+sync_page:		no	maybe
+writeback_mapping:	no
+vm_writeback:		no	yes
+set_page_dirty		no	no
+prepare_write:		no	yes
+commit_write:		no	yes
+bmap:			yes
+flushpage:		no	yes
+releasepage:		no	yes
 
 	->prepare_write(), ->commit_write(), ->sync_page() and ->readpage()
 may be called from the request handler (/dev/loop).
-	->readpage() and ->writepage() unlock the page.
+
+	->readpage() unlocks the page, either synchronously or via I/O
+completion.
+
+	->writepage() unlocks the page synchronously, before returning to
+the caller.  If the page has write I/O underway against it, writepage()
+should run SetPageWriteback() against the page prior to unlocking it.
+The write I/O completion handler should run ClearPageWriteback against
+the page.
+
+	That is: after 2.5.12, pages which are under writeout are *not*
+locked.
+
 	->sync_page() locking rules are not well-defined - usually it is called
 with lock on page, but that is not guaranteed. Considering the currently
 existing instances of this method ->sync_page() itself doesn't look
 well-defined...
+
+	->writeback_mapping() is used for periodic writeback and for
+systemcall-initiated sync operations. The address_space should start
+I/O against at least *nr_to_write pages.  *nr_to_write must be decremented
+for each page which is written.  *nr_to_write must not go negative (this
+will be relaxed later).  If nr_to_write is NULL, all dirty pages must
+be written.
+
+	->vm_writeback() is called from the VM.  The address_space should
+start I/O against at least *nr_to_write pages, including the passed page. As
+each page is written its PG_launder flag must be set (inside the page lock).
+
+	The vm_writeback() function is provided so that filesytems can perform
+clustered writeback around the page which the VM is trying to clean.
+If a_ops.vm_writeback is NULL the VM will fall back to single-page writepage().
+
+	->set_page_dirty() is called from various places in the kernel
+when the target page is marked as needing writeback.  It may be called
+under spinlock (it cannot block) and is sometimes called with the page
+not locked.
+
 	->bmap() is currently used by legacy ioctl() (FIBMAP) provided by some
 filesystems and by the swapper. The latter will eventually go away. All
 instances do not actually need the BKL. Please, keep it that way and don't
 breed new callers.
+
 	->flushpage() is called when the filesystem must attempt to drop
 some or all of the buffers from the page when it is being truncated.  It
 returns zero on success.  If ->flushpage is zero, the kernel uses
 block_flushpage() instead.
+
 	->releasepage() is called when the kernel is about to try to drop the
 buffers from the page in preparation for freeing it.  It returns zero to
 indicate that the buffers are (or may be) freeable.  If ->releasepage is zero,
diff -Nru a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt
--- a/Documentation/filesystems/ntfs.txt	Sun May  5 20:39:50 2002
+++ b/Documentation/filesystems/ntfs.txt	Sun May  5 20:39:50 2002
@@ -262,6 +262,13 @@
 
 Note that a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog.
 
+2.0.6:
+	- Major bugfix to make compatible with other kernel changes. This fixes
+	  the hangs/oopses on umount.
+	- Locking cleanup in directory operations (remove BKL usage).
+2.0.5:
+	- Major buffer overflow bug fix.
+	- Minor cleanups and updates for kernel 2.5.12.
 2.0.4:
 	- Cleanups and updates for kernel 2.5.11.
 2.0.3:
diff -Nru a/Documentation/i810_rng.txt b/Documentation/i810_rng.txt
--- a/Documentation/i810_rng.txt	Sun May  5 20:39:50 2002
+++ b/Documentation/i810_rng.txt	Sun May  5 20:39:50 2002
@@ -70,6 +70,9 @@
 
 Change history:
 
+	Version 0.9.8:
+	* Support other i8xx chipsets by adding 82801E detection
+
 	Version 0.9.7:
 	* Support other i8xx chipsets too (by adding 82801BA(M) and
 	  82801CA(M) detection)
diff -Nru a/Makefile b/Makefile
--- a/Makefile	Sun May  5 20:39:49 2002
+++ b/Makefile	Sun May  5 20:39:49 2002
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 13
+SUBLEVEL = 14
 EXTRAVERSION =
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
diff -Nru a/arch/alpha/config.in b/arch/alpha/config.in
--- a/arch/alpha/config.in	Sun May  5 20:39:49 2002
+++ b/arch/alpha/config.in	Sun May  5 20:39:49 2002
@@ -372,9 +372,7 @@
 source drivers/usb/Config.in
 source drivers/input/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -Nru a/arch/alpha/kernel/core_irongate.c b/arch/alpha/kernel/core_irongate.c
--- a/arch/alpha/kernel/core_irongate.c	Sun May  5 20:39:49 2002
+++ b/arch/alpha/kernel/core_irongate.c	Sun May  5 20:39:49 2002
@@ -421,6 +421,7 @@
 		     unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 
 	address &= ~PMD_MASK;
 	end = address + size;
@@ -428,17 +429,17 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("irongate_remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, 
-			mk_pte_phys(phys_addr, 
-				    __pgprot(_PAGE_VALID | _PAGE_ASM | 
-					     _PAGE_KRE | _PAGE_KWE | flags)));
+		set_pte(pte, pfn_pte(pfn,
+				     __pgprot(_PAGE_VALID | _PAGE_ASM | 
+					      _PAGE_KRE | _PAGE_KWE | flags)));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/arch/alpha/mm/init.c b/arch/alpha/mm/init.c
--- a/arch/alpha/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/alpha/mm/init.c	Sun May  5 20:39:49 2002
@@ -36,8 +36,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-unsigned long totalram_pages;
-
 extern void die_if_kernel(char *,struct pt_regs *,long);
 
 static struct pcb_struct original_pcb;
@@ -250,12 +248,12 @@
 		/* Set up the third level PTEs and update the virtual
 		   addresses of the CRB entries.  */
 		for (i = 0; i < crb->map_entries; ++i) {
-			unsigned long paddr = crb->map[i].pa;
+			unsigned long pfn = crb->map[i].pa >> PAGE_SHIFT;
 			crb->map[i].va = vaddr;
 			for (j = 0; j < crb->map[i].count; ++j) {
 				set_pte(pte_offset_kernel(pmd, vaddr),
-					mk_pte_phys(paddr, PAGE_KERNEL));
-				paddr += PAGE_SIZE;
+					pfn_pte(pfn, PAGE_KERNEL));
+				pfn++;
 				vaddr += PAGE_SIZE;
 			}
 		}
@@ -390,15 +388,3 @@
 	printk ("Freeing initrd memory: %ldk freed\n", (end - __start) >> 10);
 }
 #endif
-
-void
-si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	val->mem_unit = PAGE_SIZE;
-}
diff -Nru a/arch/alpha/mm/numa.c b/arch/alpha/mm/numa.c
--- a/arch/alpha/mm/numa.c	Sun May  5 20:39:49 2002
+++ b/arch/alpha/mm/numa.c	Sun May  5 20:39:49 2002
@@ -358,7 +358,6 @@
 	extern int page_is_ram(unsigned long) __init;
 	extern char _text, _etext, _data, _edata;
 	extern char __init_begin, __init_end;
-	extern unsigned long totalram_pages;
 	unsigned long nid, i;
 	mem_map_t * lmem_map;
 
diff -Nru a/arch/arm/config.in b/arch/arm/config.in
--- a/arch/arm/config.in	Sun May  5 20:39:49 2002
+++ b/arch/arm/config.in	Sun May  5 20:39:49 2002
@@ -655,9 +655,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -Nru a/arch/arm/mach-arc/mm.c b/arch/arm/mach-arc/mm.c
--- a/arch/arm/mach-arc/mm.c	Sun May  5 20:39:49 2002
+++ b/arch/arm/mach-arc/mm.c	Sun May  5 20:39:49 2002
@@ -138,7 +138,7 @@
 	page_nr = max_low_pfn;
 
 	pte = alloc_bootmem_low_pages(PTRS_PER_PTE * sizeof(pte_t));
-	pte[0] = mk_pte_phys(PAGE_OFFSET + 491520, PAGE_READONLY);
+	pte[0] = pfn_pte((PAGE_OFFSET + 491520) >> PAGE_SHIFT, PAGE_READONLY);
 	pmd_populate(&init_mm, pmd_offset(swapper_pg_dir, 0), pte);
 
 	for (i = 1; i < PTRS_PER_PGD; i++)
diff -Nru a/arch/arm/mach-arc/small_page.c b/arch/arm/mach-arc/small_page.c
--- a/arch/arm/mach-arc/small_page.c	Sun May  5 20:39:49 2002
+++ b/arch/arm/mach-arc/small_page.c	Sun May  5 20:39:49 2002
@@ -150,8 +150,8 @@
 	unsigned long flags;
 	struct page *page;
 
-	page = virt_to_page(spage);
-	if (VALID_PAGE(page)) {
+	if (virt_addr_valid(spage)) {
+		page = virt_to_page(spage);
 
 		/*
 		 * The container-page must be marked Reserved
diff -Nru a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c
--- a/arch/arm/mm/fault-armv.c	Sun May  5 20:39:49 2002
+++ b/arch/arm/mm/fault-armv.c	Sun May  5 20:39:49 2002
@@ -240,9 +240,13 @@
  */
 void update_mmu_cache(struct vm_area_struct *vma, unsigned long addr, pte_t pte)
 {
-	struct page *page = pte_page(pte);
+	unsigned long pfn = pte_pfn(pte);
+	struct page *page;
 
-	if (VALID_PAGE(page) && page->mapping) {
+	if (!pfn_valid(pfn))
+		return;
+	page = pfn_to_page(pfn);
+	if (page->mapping) {
 		if (test_and_clear_bit(PG_dcache_dirty, &page->flags))
 			__flush_dcache_page(page);
 
diff -Nru a/arch/arm/mm/init.c b/arch/arm/mm/init.c
--- a/arch/arm/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/arm/mm/init.c	Sun May  5 20:39:49 2002
@@ -48,7 +48,6 @@
 
 #define TABLE_SIZE	((TABLE_OFFSET + PTRS_PER_PTE) * sizeof(pte_t))
 
-static unsigned long totalram_pages;
 extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
 extern char _stext, _text, _etext, _end, __init_begin, __init_end;
 
@@ -631,14 +630,3 @@
 
 __setup("keepinitrd", keepinitrd_setup);
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram  = totalram_pages;
-	val->sharedram = 0;
-	val->freeram   = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh  = 0;
-	val->mem_unit  = PAGE_SIZE;
-}
diff -Nru a/arch/arm/mm/ioremap.c b/arch/arm/mm/ioremap.c
--- a/arch/arm/mm/ioremap.c	Sun May  5 20:39:50 2002
+++ b/arch/arm/mm/ioremap.c	Sun May  5 20:39:50 2002
@@ -51,7 +51,7 @@
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, pgprot));
+		set_pte(pte, pfn_pte(phys_addr >> PAGE_SHIFT, pgprot));
 		address += PAGE_SIZE;
 		phys_addr += PAGE_SIZE;
 		pte++;
diff -Nru a/arch/arm/mm/minicache.c b/arch/arm/mm/minicache.c
--- a/arch/arm/mm/minicache.c	Sun May  5 20:39:49 2002
+++ b/arch/arm/mm/minicache.c	Sun May  5 20:39:49 2002
@@ -43,7 +43,7 @@
  */
 unsigned long map_page_minicache(unsigned long virt)
 {
-	set_pte(minicache_pte, mk_pte_phys(__pa(virt), minicache_pgprot));
+	set_pte(minicache_pte, pfn_pte(__pa(virt) >> PAGE_SHIFT, minicache_pgprot));
 	flush_tlb_kernel_page(minicache_address);
 
 	return minicache_address;
diff -Nru a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c
--- a/arch/arm/mm/mm-armv.c	Sun May  5 20:39:50 2002
+++ b/arch/arm/mm/mm-armv.c	Sun May  5 20:39:50 2002
@@ -198,7 +198,7 @@
 	}
 	ptep = pte_offset_kernel(pmdp, virt);
 
-	set_pte(ptep, mk_pte_phys(phys, __pgprot(prot)));
+	set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, __pgprot(prot)));
 }
 
 /*
diff -Nru a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c
--- a/arch/cris/drivers/ide.c	Sun May  5 20:39:49 2002
+++ b/arch/cris/drivers/ide.c	Sun May  5 20:39:49 2002
@@ -191,7 +191,7 @@
 #define ATA_PIO0_STROBE 19
 #define ATA_PIO0_HOLD    4
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
+static int e100_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq);
 static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
 			  void *buffer, unsigned int length);
 
@@ -278,7 +278,7 @@
 
 		hwif->chipset = ide_etrax100;
 		hwif->tuneproc = &tune_e100_ide;
-		hwif->dmaproc = &e100_dmaproc;
+		hwif->udma = &e100_dmaproc;
 		hwif->ata_read = e100_ide_input_data;
 		hwif->ata_write = e100_ide_output_data;
 		hwif->atapi_read = e100_atapi_read;
@@ -564,13 +564,14 @@
 static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS];
 static unsigned int ata_tot_size;
 
+
 /*
- * e100_ide_build_dmatable() prepares a dma request.
- * Returns 0 if all went okay, returns 1 otherwise.
+ * This prepares a dma request.  Returns 0 if all went okay, returns 1
+ * otherwise.
  */
-static int e100_ide_build_dmatable (ide_drive_t *drive)
+
+static int e100_udma_new_table(struct ata_channel *ch, struct request *rq)
 {
-	struct request *rq = HWGROUP(drive)->rq;
 	struct buffer_head *bh = rq->bh;
 	unsigned long size, addr;
 	unsigned int count = 0;
@@ -602,7 +603,7 @@
 		/* did we run out of descriptors? */
 
 		if(count >= MAX_DMA_DESCRS) {
-			printk("%s: too few DMA descriptors\n", drive->name);
+			printk("%s: too few DMA descriptors\n", ch->name);
 			return 1;
 		}
 
@@ -623,7 +624,7 @@
                    size > 131072 only one split is necessary */
 
 		if(size > 65536) {
- 		        /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */
+		        /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */
                         ata_descrs[count].sw_len = 0;  /* 0 means 65536, this is a 16-bit field */
                         ata_descrs[count].ctrl = 0;
                         ata_descrs[count].buf = addr;
@@ -656,7 +657,7 @@
 		return 0;
 	}
 
-	printk("%s: empty DMA table?\n", drive->name);
+	printk("%s: empty DMA table?\n", ch->name);
 	return 1;	/* let the PIO routines handle this weirdness */
 }
 
@@ -695,7 +696,7 @@
 	LED_DISK_READ(0);
 	LED_DISK_WRITE(0);
 
-	dma_stat = drive->channel->dmaproc(ide_dma_end, drive);
+	dma_stat = drive->channel->udma(ide_dma_end, drive, rq);
 	stat = GET_STAT();			/* get drive status */
 	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if (!dma_stat) {
@@ -726,7 +727,7 @@
  * the caller should revert to PIO for the current request.
  */
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+static int e100_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
 {
         static unsigned int reading; /* static to support ide_dma_begin semantics */
 	int atapi = 0;
@@ -786,24 +787,21 @@
 
 		/* set up the Etrax DMA descriptors */
 
-		if(e100_ide_build_dmatable (drive))
+		if(e100_udma_new_table(drive->channel, rq))
 			return 1;
 
 		if(!atapi) {
 			/* set the irq handler which will finish the request when DMA is done */
-		
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
-			
+
 			/* issue cmd to drive */
-			
 			OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
 		}
 
 		/* begin DMA */
-	      
 		*R_DMA_CH3_FIRST = virt_to_phys(ata_descrs);
 		*R_DMA_CH3_CMD   = IO_STATE(R_DMA_CH3_CMD, cmd, start);
-		
+
 		/* initiate a multi word dma read using DMA handshaking */
 		
 		*R_ATA_TRANSFER_CNT =
@@ -820,7 +818,7 @@
 		LED_DISK_READ(1);
 
 		D(printk("dma read of %d bytes.\n", ata_tot_size));
- 
+
 	} else {
 		/* writing */
 
@@ -829,29 +827,25 @@
 
 		/* set up the Etrax DMA descriptors */
 
-		if(e100_ide_build_dmatable (drive))
+		if(e100_udma_new_table(drive->channel, rq))
 			return 1;
 
 		if(!atapi) {
 			/* set the irq handler which will finish the request when DMA is done */
-				
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
-			
+
 			/* issue cmd to drive */
-			
 			OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
 		}
 
 		/* begin DMA */
-		
 		*R_DMA_CH2_FIRST = virt_to_phys(ata_descrs);
 		*R_DMA_CH2_CMD   = IO_STATE(R_DMA_CH2_CMD, cmd, start);
-		
+
 		/* initiate a multi word dma write using DMA handshaking */
-		
 		*R_ATA_TRANSFER_CNT =
 			IO_FIELD(R_ATA_TRANSFER_CNT, count, ata_tot_size >> 1);
-		
+
 		*R_ATA_CTRL_DATA =
 			IO_FIELD(R_ATA_CTRL_DATA, data,     IDE_DATA_REG) |
 			IO_STATE(R_ATA_CTRL_DATA, rw,       write) |
@@ -878,7 +872,7 @@
  * --- Marcin Dalecki
  */
 
-void ide_release_dma(struct ata_channel *hwif)
+void ide_release_dma(struct ata_channel *ch)
 {
 	/* empty */
 }
diff -Nru a/arch/cris/mm/init.c b/arch/cris/mm/init.c
--- a/arch/cris/mm/init.c	Sun May  5 20:39:50 2002
+++ b/arch/cris/mm/init.c	Sun May  5 20:39:50 2002
@@ -114,8 +114,6 @@
 #include <asm/io.h>
 #include <asm/mmu_context.h>
 
-static unsigned long totalram_pages;
-
 struct pgtable_cache_struct quicklists;  /* see asm/pgalloc.h */
 
 const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n";
@@ -470,16 +468,4 @@
         }
         printk ("Freeing unused kernel memory: %luk freed\n", 
 		(&__init_end - &__init_begin) >> 10);
-}
-
-void 
-si_meminfo(struct sysinfo *val)
-{
-        val->totalram = totalram_pages;
-        val->sharedram = 0;
-        val->freeram = nr_free_pages();
-        val->bufferram = atomic_read(&buffermem_pages);
-        val->totalhigh = 0;
-        val->freehigh = 0;
-        val->mem_unit = PAGE_SIZE;
 }
diff -Nru a/arch/cris/mm/ioremap.c b/arch/cris/mm/ioremap.c
--- a/arch/cris/mm/ioremap.c	Sun May  5 20:39:49 2002
+++ b/arch/cris/mm/ioremap.c	Sun May  5 20:39:49 2002
@@ -17,6 +17,7 @@
 	unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 
 	address &= ~PMD_MASK;
 	end = address + size;
@@ -24,16 +25,17 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | __READABLE | 
-							     __WRITEABLE | _PAGE_GLOBAL |
-							     _PAGE_KERNEL | flags)));
+		set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | __READABLE | 
+						   __WRITEABLE | _PAGE_GLOBAL |
+						   _PAGE_KERNEL | flags)));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/arch/i386/config.in b/arch/i386/config.in
--- a/arch/i386/config.in	Sun May  5 20:39:49 2002
+++ b/arch/i386/config.in	Sun May  5 20:39:49 2002
@@ -392,9 +392,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -Nru a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c
--- a/arch/i386/kernel/acpi.c	Sun May  5 20:39:50 2002
+++ b/arch/i386/kernel/acpi.c	Sun May  5 20:39:50 2002
@@ -550,7 +550,7 @@
 
 	/* fill page with low mapping */
 	for (i = 0; i < PTRS_PER_PTE; i++)
-		set_pte(ptep + i, mk_pte_phys(i << PAGE_SHIFT, PAGE_SHARED));
+		set_pte(ptep + i, pfn_pte(i, PAGE_SHARED));
 
 	pgd = pgd_offset(current->active_mm, 0);
 	pmd = pmd_alloc(current->mm,pgd, 0);
diff -Nru a/arch/i386/mm/init.c b/arch/i386/mm/init.c
--- a/arch/i386/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/i386/mm/init.c	Sun May  5 20:39:49 2002
@@ -41,8 +41,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 unsigned long highstart_pfn, highend_pfn;
-static unsigned long totalram_pages;
-static unsigned long totalhigh_pages;
 
 /*
  * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the
@@ -122,7 +120,7 @@
 	}
 	pte = pte_offset_kernel(pmd, vaddr);
 	/* <phys,flags> stored as-is, to permit clearing entries */
-	set_pte(pte, mk_pte_phys(phys, flags));
+	set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
 
 	/*
 	 * It's enough to flush this one mapping.
@@ -239,7 +237,7 @@
 				vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
 				if (end && (vaddr >= end))
 					break;
-				*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
+				*pte = pfn_pte(__pa(vaddr) >> PAGE_SHIFT, PAGE_KERNEL);
 			}
 			set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base)));
 			if (pte_base != pte_offset_kernel(pmd, 0))
@@ -375,7 +373,7 @@
 	pmd = pmd_offset(pgd, vaddr);
 	pte = pte_offset_kernel(pmd, vaddr);
 	old_pte = *pte;
-	*pte = mk_pte_phys(0, PAGE_READONLY);
+	*pte = pfn_pte(0, PAGE_READONLY);
 	local_flush_tlb();
 
 	boot_cpu_data.wp_works_ok = do_test_wp_bit(vaddr);
@@ -560,18 +558,6 @@
 	}
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = totalhigh_pages;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
-	return;
-}
 
 #if defined(CONFIG_X86_PAE)
 static struct kmem_cache_s *pae_pgd_cachep;
diff -Nru a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c
--- a/arch/i386/mm/ioremap.c	Sun May  5 20:39:49 2002
+++ b/arch/i386/mm/ioremap.c	Sun May  5 20:39:49 2002
@@ -20,6 +20,7 @@
 	unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 
 	address &= ~PMD_MASK;
 	end = address + size;
@@ -27,15 +28,16 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | 
+		set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | _PAGE_RW | 
 					_PAGE_DIRTY | _PAGE_ACCESSED | flags)));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/arch/ia64/config.in b/arch/ia64/config.in
--- a/arch/ia64/config.in	Sun May  5 20:39:50 2002
+++ b/arch/ia64/config.in	Sun May  5 20:39:50 2002
@@ -226,9 +226,7 @@
 
 source lib/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 fi # !HP_SIM
 
diff -Nru a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c
--- a/arch/ia64/kernel/efi.c	Sun May  5 20:39:49 2002
+++ b/arch/ia64/kernel/efi.c	Sun May  5 20:39:49 2002
@@ -268,7 +268,7 @@
 		 */
 		ia64_clear_ic(flags);
 		ia64_itr(0x1, IA64_TR_PALCODE, vaddr & mask,
-			 pte_val(mk_pte_phys(md->phys_addr, PAGE_KERNEL)), IA64_GRANULE_SHIFT);
+			 pte_val(pfn_pte(md->phys_addr >> PAGE_SHIFT, PAGE_KERNEL)), IA64_GRANULE_SHIFT);
 		local_irq_restore(flags);
 		ia64_srlz_i();
 	}
diff -Nru a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
--- a/arch/ia64/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/ia64/mm/init.c	Sun May  5 20:39:49 2002
@@ -36,8 +36,6 @@
 
 unsigned long MAX_DMA_ADDRESS = PAGE_OFFSET + 0x100000000UL;
 
-static unsigned long totalram_pages;
-
 static int pgt_cache_water[2] = { 25, 50 };
 
 void
@@ -109,6 +107,7 @@
 void
 free_initrd_mem (unsigned long start, unsigned long end)
 {
+	struct page *page;
 	/*
 	 * EFI uses 4KB pages while the kernel can use 4KB  or bigger.
 	 * Thus EFI and the kernel may have different page sizes. It is
@@ -147,29 +146,17 @@
 		printk(KERN_INFO "Freeing initrd memory: %ldkB freed\n", (end - start) >> 10);
 
 	for (; start < end; start += PAGE_SIZE) {
-		if (!VALID_PAGE(virt_to_page(start)))
+		if (!virt_addr_valid(start))
 			continue;
-		clear_bit(PG_reserved, &virt_to_page(start)->flags);
-		set_page_count(virt_to_page(start), 1);
-		free_page(start);
+		page = virt_to_page(start);
+		clear_bit(PG_reserved, &page->flags);
+		set_page_count(page, 1);
+		__free_page(page);
 		++totalram_pages;
 	}
 }
 
 void
-si_meminfo (struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	val->mem_unit = PAGE_SIZE;
-	return;
-}
-
-void
 show_mem(void)
 {
 	int i, total = 0, reserved = 0;
@@ -289,7 +276,7 @@
 	ia64_srlz_d();
 
 	ia64_itr(0x2, IA64_TR_PERCPU_DATA, PERCPU_ADDR,
-		 pte_val(mk_pte_phys(__pa(my_cpu_data), PAGE_KERNEL)), PAGE_SHIFT);
+		 pte_val(pfn_pte(__pa(my_cpu_data) >> PAGE_SHIFT, PAGE_KERNEL)), PAGE_SHIFT);
 
 	__restore_flags(flags);
 	ia64_srlz_i();
diff -Nru a/arch/ia64/sn/kernel/misctest.c b/arch/ia64/sn/kernel/misctest.c
--- a/arch/ia64/sn/kernel/misctest.c	Sun May  5 20:39:50 2002
+++ b/arch/ia64/sn/kernel/misctest.c	Sun May  5 20:39:50 2002
@@ -89,7 +89,7 @@
 			printk("zzzspec: probe %ld, 0x%lx\n", res, val);
 			ia64_clear_ic(flags);
 			ia64_itc(0x2, 0xe00000ff00000000UL,
-			          pte_val(mk_pte_phys(0xff00000000UL,
+			          pte_val(pfn_pte(0xff00000000UL >> PAGE_SHIFT,
 				  __pgprot(__DIRTY_BITS|_PAGE_PL_0|_PAGE_AR_RW))), _PAGE_SIZE_256M);
 			local_irq_restore(flags);
 			ia64_srlz_i ();
diff -Nru a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c
--- a/arch/m68k/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/m68k/mm/init.c	Sun May  5 20:39:49 2002
@@ -35,8 +35,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-unsigned long totalram_pages = 0;
-
 int do_check_pgt_cache(int low, int high)
 {
 	int freed = 0;
@@ -202,18 +200,3 @@
 	printk ("Freeing initrd memory: %dk freed\n", pages);
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-    unsigned long i;
-
-    i = max_mapnr;
-    val->totalram = totalram_pages;
-    val->sharedram = 0;
-    val->freeram = nr_free_pages();
-    val->bufferram = atomic_read(&buffermem_pages);
-    val->totalhigh = 0;
-    val->freehigh = 0;
-    val->mem_unit = PAGE_SIZE;
-    return;
-}
diff -Nru a/arch/m68k/mm/motorola.c b/arch/m68k/mm/motorola.c
--- a/arch/m68k/mm/motorola.c	Sun May  5 20:39:50 2002
+++ b/arch/m68k/mm/motorola.c	Sun May  5 20:39:50 2002
@@ -286,7 +286,6 @@
 }
 
 extern char __init_begin, __init_end;
-extern unsigned long totalram_pages;
 
 void free_initmem(void)
 {
diff -Nru a/arch/mips/gt64120/momenco_ocelot/setup.c b/arch/mips/gt64120/momenco_ocelot/setup.c
--- a/arch/mips/gt64120/momenco_ocelot/setup.c	Sun May  5 20:39:50 2002
+++ b/arch/mips/gt64120/momenco_ocelot/setup.c	Sun May  5 20:39:50 2002
@@ -78,7 +78,7 @@
 
 static char reset_reason;
 
-#define ENTRYLO(x) ((pte_val(mk_pte_phys((x), PAGE_KERNEL_UNCACHED)) >> 6)|1)
+#define ENTRYLO(x) ((pte_val(pfn_pte((x) >> PAGE_SHIFT, PAGE_KERNEL_UNCACHED)) >> 6)|1)
 
 static void __init setup_l3cache(unsigned long size);
 
diff -Nru a/arch/mips/mm/init.c b/arch/mips/mm/init.c
--- a/arch/mips/mm/init.c	Sun May  5 20:39:50 2002
+++ b/arch/mips/mm/init.c	Sun May  5 20:39:50 2002
@@ -45,8 +45,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-static unsigned long totalram_pages;
-
 extern void prom_free_prom_memory(void);
 
 
@@ -264,17 +262,4 @@
 	}
 	printk("Freeing unused kernel memory: %dk freed\n",
 	       (&__init_end - &__init_begin) >> 10);
-}
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = atomic_read(&shmem_nrpages);
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
-
-	return;
 }
diff -Nru a/arch/mips/mm/ioremap.c b/arch/mips/mm/ioremap.c
--- a/arch/mips/mm/ioremap.c	Sun May  5 20:39:50 2002
+++ b/arch/mips/mm/ioremap.c	Sun May  5 20:39:50 2002
@@ -18,6 +18,7 @@
 	unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 	pgprot_t pgprot = __pgprot(_PAGE_GLOBAL | _PAGE_PRESENT | __READABLE
 	                           | __WRITEABLE | flags);
 
@@ -27,14 +28,15 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, pgprot));
+		set_pte(pte, pfn_pte(pfn, pgprot));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/arch/mips/mm/umap.c b/arch/mips/mm/umap.c
--- a/arch/mips/mm/umap.c	Sun May  5 20:39:50 2002
+++ b/arch/mips/mm/umap.c	Sun May  5 20:39:50 2002
@@ -116,8 +116,12 @@
 static inline void free_pte(pte_t page)
 {
 	if (pte_present(page)) {
-		struct page *ptpage = pte_page(page);
-		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+		unsigned long pfn = pte_pfn(page);
+		struct page *ptpage;
+		if (!pfn_valid(pfn))
+			return;
+		ptpage = pfn_to_page(pfn);
+		if (PageReserved(ptpage))
 			return;
 		__free_page(ptpage);
 		if (current->mm->rss <= 0)
diff -Nru a/arch/mips64/mm/init.c b/arch/mips64/mm/init.c
--- a/arch/mips64/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/mips64/mm/init.c	Sun May  5 20:39:49 2002
@@ -40,8 +40,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-unsigned long totalram_pages;
-
 void pgd_init(unsigned long page)
 {
 	unsigned long *p, *end;
@@ -457,18 +455,4 @@
 	}
 	printk("Freeing unused kernel memory: %ldk freed\n",
 	       (&__init_end - &__init_begin) >> 10);
-}
-
-void
-si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = atomic_read(&shmem_nrpages);
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
-
-	return;
 }
diff -Nru a/arch/mips64/mm/umap.c b/arch/mips64/mm/umap.c
--- a/arch/mips64/mm/umap.c	Sun May  5 20:39:49 2002
+++ b/arch/mips64/mm/umap.c	Sun May  5 20:39:49 2002
@@ -115,8 +115,12 @@
 static inline void free_pte(pte_t page)
 {
 	if (pte_present(page)) {
-		struct page *ptpage = pte_page(page);
-		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+		unsigned long pfn = pte_pfn(page);
+		struct page *ptpage;
+		if (!pfn_valid(pfn))
+			return;
+		ptpage = pfn_to_page(pfn);
+		if (PageReserved(ptpage))
 			return;
 		__free_page(ptpage);
 		if (current->mm->rss <= 0)
diff -Nru a/arch/mips64/sgi-ip27/ip27-memory.c b/arch/mips64/sgi-ip27/ip27-memory.c
--- a/arch/mips64/sgi-ip27/ip27-memory.c	Sun May  5 20:39:49 2002
+++ b/arch/mips64/sgi-ip27/ip27-memory.c	Sun May  5 20:39:49 2002
@@ -265,7 +265,6 @@
 {
 	extern char _stext, _etext, _fdata, _edata;
 	extern char __init_begin, __init_end;
-	extern unsigned long totalram_pages;
 	extern unsigned long setup_zero_pages(void);
 	cnodeid_t nid;
 	unsigned long tmp;
diff -Nru a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
--- a/arch/parisc/mm/init.c	Sun May  5 20:39:50 2002
+++ b/arch/parisc/mm/init.c	Sun May  5 20:39:50 2002
@@ -20,7 +20,6 @@
 
 #include <asm/pgalloc.h>
 
-static unsigned long totalram_pages;
 extern unsigned long max_pfn, mem_max;
 
 void free_initmem(void)  {
@@ -451,29 +450,3 @@
 #endif
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	int i;
-
-	i = max_mapnr;
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-#if 0
-	while (i-- > 0)  {
-		if (PageReserved(mem_map+i))
-			continue;
-		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
-			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
-	}
-	val->totalram <<= PAGE_SHIFT;
-	val->sharedram <<= PAGE_SHIFT;
-#endif
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	return;
-}
diff -Nru a/arch/ppc/config.in b/arch/ppc/config.in
--- a/arch/ppc/config.in	Sun May  5 20:39:49 2002
+++ b/arch/ppc/config.in	Sun May  5 20:39:49 2002
@@ -589,9 +589,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 source lib/Config.in
 
diff -Nru a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c
--- a/arch/ppc/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/ppc/mm/init.c	Sun May  5 20:39:49 2002
@@ -70,8 +70,6 @@
 int mem_init_done;
 int init_bootmem_done;
 int boot_mapsize;
-unsigned long totalram_pages;
-unsigned long totalhigh_pages;
 #ifdef CONFIG_ALL_PPC
 unsigned long agp_special_page;
 #endif
@@ -143,17 +141,6 @@
 	printk("%d pages shared\n",shared);
 	printk("%d pages swap cached\n",cached);
 	printk("%ld buffermem pages\n", nr_buffermem_pages());
-}
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = totalhigh_pages;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
 }
 
 /* Free up now-unused memory */
diff -Nru a/arch/ppc/mm/pgtable.c b/arch/ppc/mm/pgtable.c
--- a/arch/ppc/mm/pgtable.c	Sun May  5 20:39:49 2002
+++ b/arch/ppc/mm/pgtable.c	Sun May  5 20:39:49 2002
@@ -237,7 +237,7 @@
 	pg = pte_alloc_kernel(&init_mm, pd, va);
 	if (pg != 0) {
 		err = 0;
-		set_pte(pg, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags)));
+		set_pte(pg, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags)));
 		if (mem_init_done)
 			flush_HPTE(0, va, pmd_val(*pd));
 	}
diff -Nru a/arch/ppc64/mm/init.c b/arch/ppc64/mm/init.c
--- a/arch/ppc64/mm/init.c	Sun May  5 20:39:50 2002
+++ b/arch/ppc64/mm/init.c	Sun May  5 20:39:50 2002
@@ -73,7 +73,6 @@
 unsigned long ioremap_bot = IMALLOC_BASE;
 
 static int boot_mapsize;
-static unsigned long totalram_pages;
 
 extern pgd_t swapper_pg_dir[];
 extern char __init_begin, __init_end;
@@ -138,17 +137,6 @@
 	printk("%ld buffermem pages\n", nr_buffermem_pages());
 }
 
-void si_meminfo(struct sysinfo *val)
-{
- 	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	val->mem_unit = PAGE_SIZE;
-}
-
 void *
 ioremap(unsigned long addr, unsigned long size)
 {
@@ -240,7 +228,7 @@
 		ptep = pte_alloc_kernel(&ioremap_mm, pmdp, ea);
 
 		pa = absolute_to_phys(pa);
-		set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags)));
+		set_pte(ptep, pfn_pte(pa >> PAGE_SHIFT, __pgprot(flags)));
 		spin_unlock(&ioremap_mm.page_table_lock);
 	} else {
 		/* If the mm subsystem is not fully up, we cannot create a
diff -Nru a/arch/s390/mm/init.c b/arch/s390/mm/init.c
--- a/arch/s390/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/s390/mm/init.c	Sun May  5 20:39:49 2002
@@ -39,8 +39,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-static unsigned long totalram_pages;
-
 pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE)));
 char  empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
 
@@ -118,9 +116,8 @@
         pte_t   pte;
 	int     i;
         unsigned long tmp;
-        unsigned long address=0;
+        unsigned long pfn = 0;
         unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
-	unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE);
         static const int ssm_mask = 0x04000000L;
 
 	/* unmap whole virtual address space */
@@ -136,7 +133,7 @@
 
         pg_dir = swapper_pg_dir;
 
-        while (address < end_mem) {
+        while (pfn < max_low_pfn) {
                 /*
                  * pg_table is physical at this point
                  */
@@ -149,11 +146,11 @@
                 pg_dir++;
 
                 for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
-                        pte = mk_pte_phys(address, PAGE_KERNEL);
-                        if (address >= end_mem)
+                        pte = pfn_pte(pfn, PAGE_KERNEL);
+                        if (pfn >= max_low_pfn)
                                 pte_clear(&pte);
                         set_pte(pg_table, pte);
-                        address += PAGE_SIZE;
+                        pfn++;
                 }
         }
 
@@ -231,14 +228,3 @@
         }
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	val->mem_unit = PAGE_SIZE;
-}
diff -Nru a/arch/s390/mm/ioremap.c b/arch/s390/mm/ioremap.c
--- a/arch/s390/mm/ioremap.c	Sun May  5 20:39:49 2002
+++ b/arch/s390/mm/ioremap.c	Sun May  5 20:39:49 2002
@@ -21,6 +21,7 @@
         unsigned long phys_addr, unsigned long flags)
 {
         unsigned long end;
+        unsigned long pfn;
 
         address &= ~PMD_MASK;
         end = address + size;
@@ -28,15 +29,15 @@
                 end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+        pfn = phys_addr >> PAGE_SHIFT;
         do {
                 if (!pte_none(*pte)) {
                         printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-                set_pte(pte, mk_pte_phys(phys_addr,
-                                         __pgprot(_PAGE_PRESENT | flags)));
+                set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | flags)));
                 address += PAGE_SIZE;
-                phys_addr += PAGE_SIZE;
+                pfn++;
                 pte++;
         } while (address && (address < end));
 }
diff -Nru a/arch/s390x/mm/init.c b/arch/s390x/mm/init.c
--- a/arch/s390x/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/s390x/mm/init.c	Sun May  5 20:39:49 2002
@@ -39,8 +39,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-static unsigned long totalram_pages;
-
 pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE)));
 char  empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
 
@@ -116,10 +114,9 @@
         pte_t * pt_dir;
         pte_t   pte;
 	int     i,j,k;
-        unsigned long address=0;
+        unsigned long pfn = 0;
         unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) |
           _KERN_REGION_TABLE;
-	unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE);
 	static const int ssm_mask = 0x04000000L;
 
 	unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};
@@ -147,7 +144,7 @@
 	
         for (i = 0 ; i < PTRS_PER_PGD ; i++,pg_dir++) {
 
-                if (address >= end_mem) {
+                if (pfn >= max_low_pfn) {
                         pgd_clear(pg_dir);
                         continue;
                 }          
@@ -156,7 +153,7 @@
                 pgd_populate(&init_mm, pg_dir, pm_dir);
 
                 for (j = 0 ; j < PTRS_PER_PMD ; j++,pm_dir++) {
-                        if (address >= end_mem) {
+                        if (pfn >= max_low_pfn) {
                                 pmd_clear(pm_dir);
                                 continue; 
                         }          
@@ -165,13 +162,13 @@
                         pmd_populate(&init_mm, pm_dir, pt_dir);
 	
                         for (k = 0 ; k < PTRS_PER_PTE ; k++,pt_dir++) {
-                                pte = mk_pte_phys(address, PAGE_KERNEL);
-                                if (address >= end_mem) {
+                                pte = mk_pte_phys(pfn, PAGE_KERNEL);
+                                if (pfn >= max_low_pfn) {
                                         pte_clear(&pte); 
                                         continue;
                                 }
                                 set_pte(pt_dir, pte);
-                                address += PAGE_SIZE;
+                                pfn++;
                         }
                 }
         }
@@ -243,17 +240,6 @@
 	}
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-        val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = 0;
-	val->mem_unit = PAGE_SIZE;
-}
 
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
diff -Nru a/arch/s390x/mm/ioremap.c b/arch/s390x/mm/ioremap.c
--- a/arch/s390x/mm/ioremap.c	Sun May  5 20:39:50 2002
+++ b/arch/s390x/mm/ioremap.c	Sun May  5 20:39:50 2002
@@ -21,6 +21,7 @@
         unsigned long phys_addr, unsigned long flags)
 {
         unsigned long end;
+        unsigned long pfn;
 
         address &= ~PMD_MASK;
         end = address + size;
@@ -28,15 +29,15 @@
                 end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+        pfn = phys_addr >> PAGE_SHIFT;
         do {
                 if (!pte_none(*pte)) {
                         printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-                set_pte(pte, mk_pte_phys(phys_addr,
-                                         __pgprot(_PAGE_PRESENT | flags)));
+                set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | flags)));
                 address += PAGE_SIZE;
-                phys_addr += PAGE_SIZE;
+                pfn++;
                 pte++;
         } while (address && (address < end));
 }
diff -Nru a/arch/sh/mm/cache-sh4.c b/arch/sh/mm/cache-sh4.c
--- a/arch/sh/mm/cache-sh4.c	Sun May  5 20:39:49 2002
+++ b/arch/sh/mm/cache-sh4.c	Sun May  5 20:39:49 2002
@@ -398,7 +398,7 @@
 		pte_t entry;
 		unsigned long flags;
 
-		entry = mk_pte_phys(phys_addr, pgprot);
+		entry = pfn_pte(phys_addr >> PAGE_SHIFT, pgprot);
 		down(&p3map_sem[(address & CACHE_ALIAS)>>12]);
 		set_pte(pte, entry);
 		save_and_cli(flags);
@@ -437,7 +437,7 @@
 		pte_t entry;
 		unsigned long flags;
 
-		entry = mk_pte_phys(phys_addr, pgprot);
+		entry = pfn_pte(phys_addr >> PAGE_SHIFT, pgprot);
 		down(&p3map_sem[(address & CACHE_ALIAS)>>12]);
 		set_pte(pte, entry);
 		save_and_cli(flags);
diff -Nru a/arch/sh/mm/fault.c b/arch/sh/mm/fault.c
--- a/arch/sh/mm/fault.c	Sun May  5 20:39:49 2002
+++ b/arch/sh/mm/fault.c	Sun May  5 20:39:49 2002
@@ -290,6 +290,7 @@
 	unsigned long vpn;
 #if defined(__SH4__)
 	struct page *page;
+	unsigned long pfn;
 	unsigned long ptea;
 #endif
 
@@ -298,11 +299,14 @@
 		return;
 
 #if defined(__SH4__)
-	page = pte_page(pte);
-	if (VALID_PAGE(page) && !test_bit(PG_mapped, &page->flags)) {
-		unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
-		__flush_wback_region((void *)P1SEGADDR(phys), PAGE_SIZE);
-		__set_bit(PG_mapped, &page->flags);
+	pfn = pte_pfn(pte);
+	if (pfn_valid(pfn)) {
+		page = pfn_to_page(pfn);
+		if (!test_bit(PG_mapped, &page->flags)) {
+			unsigned long phys = pte_val(pte) & PTE_PHYS_MASK;
+			__flush_wback_region((void *)P1SEGADDR(phys), PAGE_SIZE);
+			__set_bit(PG_mapped, &page->flags);
+		}
 	}
 #endif
 
diff -Nru a/arch/sh/mm/init.c b/arch/sh/mm/init.c
--- a/arch/sh/mm/init.c	Sun May  5 20:39:50 2002
+++ b/arch/sh/mm/init.c	Sun May  5 20:39:50 2002
@@ -48,9 +48,6 @@
 bootmem_data_t discontig_node_bdata[NR_NODES];
 #endif
 
-static unsigned long totalram_pages;
-static unsigned long totalhigh_pages;
-
 void show_mem(void)
 {
 	int i, total = 0, reserved = 0;
@@ -203,15 +200,3 @@
 	printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = totalhigh_pages;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
-	return;
-}
diff -Nru a/arch/sh/mm/ioremap.c b/arch/sh/mm/ioremap.c
--- a/arch/sh/mm/ioremap.c	Sun May  5 20:39:49 2002
+++ b/arch/sh/mm/ioremap.c	Sun May  5 20:39:49 2002
@@ -17,6 +17,7 @@
 	unsigned long size, unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 	pgprot_t pgprot = __pgprot(_PAGE_PRESENT | _PAGE_RW |
 				   _PAGE_DIRTY | _PAGE_ACCESSED |
 				   _PAGE_HW_SHARED | _PAGE_FLAGS_HARD | flags);
@@ -27,14 +28,15 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, pgprot));
+		set_pte(pte, pfn_pte(pfn, pgprot));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
--- a/arch/sparc/kernel/sun4d_smp.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc/kernel/sun4d_smp.c	Sun May  5 20:39:49 2002
@@ -63,8 +63,6 @@
 extern volatile int smp_commenced;
 extern int __smp4d_processor_id(void);
 
-extern unsigned long totalram_pages;
-
 /* #define SMP_DEBUG */
 
 #ifdef SMP_DEBUG
diff -Nru a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
--- a/arch/sparc/kernel/sun4m_smp.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc/kernel/sun4m_smp.c	Sun May  5 20:39:49 2002
@@ -59,8 +59,6 @@
 extern volatile int smp_commenced;
 extern int __smp4m_processor_id(void);
 
-extern unsigned long totalram_pages;
-
 /*#define SMP_DEBUG*/
 
 #ifdef SMP_DEBUG
diff -Nru a/arch/sparc/mm/generic.c b/arch/sparc/mm/generic.c
--- a/arch/sparc/mm/generic.c	Sun May  5 20:39:50 2002
+++ b/arch/sparc/mm/generic.c	Sun May  5 20:39:50 2002
@@ -19,8 +19,12 @@
 	if (pte_none(page))
 		return;
 	if (pte_present(page)) {
-		struct page *ptpage = pte_page(page);
-		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+		unsigned long pfn = pte_pfn(page);
+		struct page *ptpage;
+		if (!pfn_valid(pfn))
+			return;
+		ptpage = pfn_to_page(pfn);
+		if (PageReserved(ptpage))
 			return;
 		page_cache_release(ptpage);
 		return;
diff -Nru a/arch/sparc/mm/init.c b/arch/sparc/mm/init.c
--- a/arch/sparc/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc/mm/init.c	Sun May  5 20:39:49 2002
@@ -55,8 +55,6 @@
 extern unsigned int sparc_ramdisk_size;
 
 unsigned long highstart_pfn, highend_pfn;
-unsigned long totalram_pages;
-unsigned long totalhigh_pages;
 
 pte_t *kmap_pte;
 pgprot_t kmap_prot;
@@ -504,18 +502,6 @@
 	}
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = totalhigh_pages;
-	val->freehigh = nr_free_highpages();
-
-	val->mem_unit = PAGE_SIZE;
-}
 
 void flush_page_to_ram(struct page *page)
 {
diff -Nru a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
--- a/arch/sparc/mm/srmmu.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc/mm/srmmu.c	Sun May  5 20:39:49 2002
@@ -1105,7 +1105,6 @@
 extern void sparc_context_init(int);
 
 extern int linux_num_cpus;
-extern unsigned long totalhigh_pages;
 
 void (*poke_srmmu)(void) __initdata = NULL;
 
@@ -2043,7 +2042,7 @@
 	BTFIXUPSET_CALL(pgd_clear, srmmu_pgd_clear, BTFIXUPCALL_SWAPO0G0);
 
 	BTFIXUPSET_CALL(mk_pte, srmmu_mk_pte, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(mk_pte_phys, srmmu_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pfn_pte, srmmu_pfn_pte, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(mk_pte_io, srmmu_mk_pte_io, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(pgd_set, srmmu_pgd_set, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(pmd_set, srmmu_pmd_set, BTFIXUPCALL_NORM);
diff -Nru a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c
--- a/arch/sparc/mm/sun4c.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc/mm/sun4c.c	Sun May  5 20:39:49 2002
@@ -1327,7 +1327,7 @@
 	unsigned long page;
 
 	page = ((unsigned long)bufptr) & PAGE_MASK;
-	if (!VALID_PAGE(virt_to_page(page))) {
+	if (!virt_addr_valid(page)) {
 		sun4c_flush_page(page);
 		return (__u32)bufptr; /* already locked */
 	}
@@ -2106,7 +2106,7 @@
 static int sun4c_pmd_bad(pmd_t pmd)
 {
 	return (((pmd_val(pmd) & ~PAGE_MASK) != PGD_TABLE) ||
-		(!VALID_PAGE(virt_to_page(pmd_val(pmd)))));
+		(!virt_addr_valid(pmd_val(pmd))));
 }
 
 static int sun4c_pmd_present(pmd_t pmd)
@@ -2526,7 +2526,7 @@
 	BTFIXUPSET_CALL(pgd_clear, sun4c_pgd_clear, BTFIXUPCALL_NOP);
 
 	BTFIXUPSET_CALL(mk_pte, sun4c_mk_pte, BTFIXUPCALL_NORM);
-	BTFIXUPSET_CALL(mk_pte_phys, sun4c_mk_pte_phys, BTFIXUPCALL_NORM);
+	BTFIXUPSET_CALL(pfn_pte, sun4c_pfn_pte, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(mk_pte_io, sun4c_mk_pte_io, BTFIXUPCALL_NORM);
 	
 	BTFIXUPSET_INT(pte_modify_mask, _SUN4C_PAGE_CHG_MASK);
diff -Nru a/arch/sparc64/config.in b/arch/sparc64/config.in
--- a/arch/sparc64/config.in	Sun May  5 20:39:49 2002
+++ b/arch/sparc64/config.in	Sun May  5 20:39:49 2002
@@ -267,9 +267,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Watchdog'
diff -Nru a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c
--- a/arch/sparc64/kernel/traps.c	Sun May  5 20:39:50 2002
+++ b/arch/sparc64/kernel/traps.c	Sun May  5 20:39:50 2002
@@ -1312,10 +1312,8 @@
 			}
 
 			if (recoverable) {
-				struct page *page = virt_to_page(__va(afar));
-
-				if (VALID_PAGE(page))
-					get_page(page);
+				if (pfn_valid(afar >> PAGE_SHIFT))
+					get_page(pfn_to_page(afar >> PAGE_SHIFT));
 				else
 					recoverable = 0;
 
diff -Nru a/arch/sparc64/mm/generic.c b/arch/sparc64/mm/generic.c
--- a/arch/sparc64/mm/generic.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc64/mm/generic.c	Sun May  5 20:39:49 2002
@@ -20,8 +20,12 @@
 	if (pte_none(page))
 		return;
 	if (pte_present(page)) {
-		struct page *ptpage = pte_page(page);
-		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+		unsigned long pfn = pte_pfn(page);
+		struct page *ptpage;
+		if (!pfn_valid(pfn))
+			return;
+		ptpage = pfn_to_page(page);
+		if (PageReserved(ptpage))
 			return;
 		page_cache_release(ptpage);
 		return;
diff -Nru a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
--- a/arch/sparc64/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/sparc64/mm/init.c	Sun May  5 20:39:49 2002
@@ -187,11 +187,13 @@
 
 void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t pte)
 {
-	struct page *page = pte_page(pte);
+	struct page *page;
+	unsigned long pfn;
 	unsigned long pg_flags;
 
-	if (VALID_PAGE(page) &&
-	    page->mapping &&
+	pfn = pte_pfn(pte);
+	if (pfn_valid(pfn) &&
+	    (page = pfn_to_page(pfn), page->mapping) &&
 	    ((pg_flags = page->flags) & (1UL << PG_dcache_dirty))) {
 		int cpu = ((pg_flags >> 24) & (NR_CPUS - 1UL));
 
@@ -260,10 +262,14 @@
 			continue;
 
 		if (pte_present(pte) && pte_dirty(pte)) {
-			struct page *page = pte_page(pte);
+			struct page *page;
 			unsigned long pgaddr, uaddr;
+			unsigned long pfn = pte_pfn(pte);
 
-			if (!VALID_PAGE(page) || PageReserved(page) || !page->mapping)
+			if (!pfn_valid(pfn))
+				continue;
+			page = pfn_to_page(pfn);
+			if (PageReserved(page) || !page->mapping)
 				continue;
 			pgaddr = (unsigned long) page_address(page);
 			uaddr = address + offset;
@@ -1784,17 +1790,3 @@
 	}
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = num_physpages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-
-	/* These are always zero on Sparc64. */
-	val->totalhigh = 0;
-	val->freehigh = 0;
-
-	val->mem_unit = PAGE_SIZE;
-}
diff -Nru a/arch/x86_64/config.in b/arch/x86_64/config.in
--- a/arch/x86_64/config.in	Sun May  5 20:39:49 2002
+++ b/arch/x86_64/config.in	Sun May  5 20:39:49 2002
@@ -199,9 +199,7 @@
 
 source drivers/usb/Config.in
 
-if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-   source net/bluetooth/Config.in
-fi
+source net/bluetooth/Config.in
 
 mainmenu_option next_comment
 comment 'Kernel hacking'
diff -Nru a/arch/x86_64/mm/init.c b/arch/x86_64/mm/init.c
--- a/arch/x86_64/mm/init.c	Sun May  5 20:39:49 2002
+++ b/arch/x86_64/mm/init.c	Sun May  5 20:39:49 2002
@@ -39,8 +39,6 @@
 
 mmu_gather_t mmu_gathers[NR_CPUS];
 
-static unsigned long totalram_pages;
-
 /*
  * NOTE: pagetable_init alloc all the fixmap pagetables contiguous on the
  * physical space so we can cache the place of the first one and move
@@ -125,7 +123,7 @@
 	pte = pte_offset_kernel(pmd, vaddr);
 	if (pte_val(*pte))
 		pte_ERROR(*pte);
-	set_pte(pte, mk_pte_phys(phys, prot));
+	set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot));
 
 	/*
 	 * It's enough to flush this one mapping.
@@ -385,15 +383,3 @@
 	}
 }
 #endif
-
-void si_meminfo(struct sysinfo *val)
-{
-	val->totalram = totalram_pages;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	val->totalhigh = 0;
-	val->freehigh = nr_free_highpages();
-	val->mem_unit = PAGE_SIZE;
-	return;
-}
diff -Nru a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c
--- a/arch/x86_64/mm/ioremap.c	Sun May  5 20:39:49 2002
+++ b/arch/x86_64/mm/ioremap.c	Sun May  5 20:39:49 2002
@@ -20,6 +20,7 @@
 	unsigned long phys_addr, unsigned long flags)
 {
 	unsigned long end;
+	unsigned long pfn;
 
 	address &= ~PMD_MASK;
 	end = address + size;
@@ -27,15 +28,16 @@
 		end = PMD_SIZE;
 	if (address >= end)
 		BUG();
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
 		if (!pte_none(*pte)) {
 			printk("remap_area_pte: page already exists\n");
 			BUG();
 		}
-		set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | _PAGE_RW | 
+		set_pte(pte, pfn_pte(pfn, __pgprot(_PAGE_PRESENT | _PAGE_RW | 
 					_PAGE_GLOBAL | _PAGE_DIRTY | _PAGE_ACCESSED | flags)));
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
--- a/drivers/block/ll_rw_blk.c	Sun May  5 20:39:49 2002
+++ b/drivers/block/ll_rw_blk.c	Sun May  5 20:39:49 2002
@@ -1598,10 +1598,12 @@
 	BUG_ON(!bh->b_end_io);
 
 	if ((rw == READ || rw == READA) && buffer_uptodate(bh))
-		printk("%s: read of uptodate buffer\n", __FUNCTION__);
+		buffer_error();
 	if (rw == WRITE && !buffer_uptodate(bh))
-		printk("%s: write of non-uptodate buffer\n", __FUNCTION__);
-		
+		buffer_error();
+	if (rw == READ && buffer_dirty(bh))
+		buffer_error();
+				
 	set_buffer_req(bh);
 
 	/*
diff -Nru a/drivers/bluetooth/Config.help b/drivers/bluetooth/Config.help
--- a/drivers/bluetooth/Config.help	Sun May  5 20:39:49 2002
+++ b/drivers/bluetooth/Config.help	Sun May  5 20:39:49 2002
@@ -1,11 +1,23 @@
+HCI UART driver
 CONFIG_BLUEZ_HCIUART
   Bluetooth HCI UART driver.
   This driver is required if you want to use Bluetooth devices with
-  serial port interface.
+  serial port interface. You will also need this driver if you have 
+  UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card 
+  adapter and BrainBoxes Bluetooth PC Card.
 
   Say Y here to compile support for Bluetooth UART devices into the
   kernel or say M to compile it as module (hci_uart.o).
 
+HCI UART (H4) protocol support 
+CONFIG_BLUEZ_HCIUART_H4
+  UART (H4) is serial protocol for communication between Bluetooth 
+  device and host. This protocol is required for most UART based 
+  Bluetooth device (including PCMCIA and CF). 
+
+  Say Y here to compile support for HCI UART (H4) protocol.
+
+HCI USB driver
 CONFIG_BLUEZ_HCIUSB
   Bluetooth HCI USB driver.
   This driver is required if you want to use Bluetooth devices with
@@ -14,6 +26,24 @@
   Say Y here to compile support for Bluetooth USB devices into the
   kernel or say M to compile it as module (hci_usb.o).
 
+HCI USB firmware download support
+CONFIG_BLUEZ_USB_FW_LOAD
+  Firmware download support for Bluetooth USB devices.
+  This support is required for devices like Broadcom BCM2033.
+
+  HCI USB driver uses external firmware downloader program provided 
+  in BlueFW package.
+  For more information, see <http://bluez.sf.net/>.
+
+HCI USB zero packet support
+CONFIG_BLUEZ_USB_ZERO_PACKET
+  Support for USB zero packets. 
+  This option is provided only as a work around for buggy Bluetooth USB 
+  devices. Do _not_ enable it unless you know for sure that your device 
+  requires zero packets.
+  Most people should say N here.
+
+HCI VHCI Virtual HCI device driver
 CONFIG_BLUEZ_HCIVHCI
   Bluetooth Virtual HCI device driver.
   This driver is required if you want to use HCI Emulation software.
@@ -21,3 +51,24 @@
   Say Y here to compile support for virtual HCI devices into the
   kernel or say M to compile it as module (hci_vhci.o).
 
+HCI DTL1 (PC Card) device driver
+CONFIG_BLUEZ_HCIDTL1
+  Bluetooth HCI DTL1 (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  Nokia DTL1 interface:
+     Nokia Bluetooth Card
+     Socket Bluetooth CF Card
+
+  Say Y here to compile support for HCI DTL1 devices into the
+  kernel or say M to compile it as module (dtl1_cs.o).
+
+HCI BlueCard (PC Card) device driver
+CONFIG_BLUEZ_HCIBLUECARD
+  Bluetooth HCI BlueCard (PC Card) driver.
+  This driver provides support for Bluetooth PCMCIA devices with
+  Anycom BlueCard interface:
+     Anycom Bluetooth PC Card
+     Anycom Bluetooth CF Card
+
+  Say Y here to compile support for HCI BlueCard devices into the
+  kernel or say M to compile it as module (bluecard_cs.o).
diff -Nru a/drivers/bluetooth/Config.in b/drivers/bluetooth/Config.in
--- a/drivers/bluetooth/Config.in	Sun May  5 20:39:50 2002
+++ b/drivers/bluetooth/Config.in	Sun May  5 20:39:50 2002
@@ -2,7 +2,20 @@
 comment 'Bluetooth device drivers'
 
 dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
+if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
+   bool '  Firmware download support'  CONFIG_BLUEZ_USB_FW_LOAD
+   bool '  USB zero packet support'  CONFIG_BLUEZ_USB_ZERO_PACKET
+fi
+
 dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
-dep_tristate 'HCI VHCI virtual HCI device driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
+if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
+   bool '  UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
+fi
+
+dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI BlueCard (PC Card) driver' CONFIG_BLUEZ_HCIBLUECARD $CONFIG_PCMCIA $CONFIG_BLUEZ
+
+dep_tristate 'HCI VHCI (Virtual HCI device) driver' CONFIG_BLUEZ_HCIVHCI $CONFIG_BLUEZ
 
 endmenu
diff -Nru a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
--- a/drivers/bluetooth/Makefile	Sun May  5 20:39:49 2002
+++ b/drivers/bluetooth/Makefile	Sun May  5 20:39:49 2002
@@ -4,8 +4,19 @@
 
 O_TARGET	:= bluetooth.o
 
+list-multi	:= hci_uart.o
+
 obj-$(CONFIG_BLUEZ_HCIUSB)	+= hci_usb.o
-obj-$(CONFIG_BLUEZ_HCIUART)	+= hci_uart.o
 obj-$(CONFIG_BLUEZ_HCIVHCI)	+= hci_vhci.o
 
+obj-$(CONFIG_BLUEZ_HCIUART)	+= hci_uart.o
+uart-y				:= hci_ldisc.o
+uart-$(CONFIG_BLUEZ_HCIUART_H4)	+= hci_h4.o
+
+obj-$(CONFIG_BLUEZ_HCIDTL1)	+= dtl1_cs.o
+obj-$(CONFIG_BLUEZ_HCIBLUECARD)	+= bluecard_cs.o
+
 include $(TOPDIR)/Rules.make
+
+hci_uart.o: $(uart-y)
+	$(LD) -r -o $@ $(uart-y)
diff -Nru a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/bluecard_cs.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,1124 @@
+/*
+ *
+ *  Bluetooth driver for the Anycom BlueCard (LSE039/LSE041)
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0x86bc;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for the Anycom BlueCard (LSE039/LSE041)");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct bluecard_info_t {
+	dev_link_t link;
+	dev_node_t node;
+
+	struct hci_dev hdev;
+
+	spinlock_t lock;		/* For serializing operations */
+	struct timer_list timer;	/* For LED control */
+
+	struct sk_buff_head txq;
+	unsigned long tx_state;
+
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+
+	unsigned char ctrl_reg;
+	unsigned long hw_state;		/* Status of the hardware and LED control */
+} bluecard_info_t;
+
+
+void bluecard_config(dev_link_t * link);
+void bluecard_release(u_long arg);
+int bluecard_event(event_t event, int priority, event_callback_args_t * args);
+
+static dev_info_t dev_info = "bluecard_cs";
+
+dev_link_t *bluecard_attach(void);
+void bluecard_detach(dev_link_t *);
+
+dev_link_t *dev_list = NULL;
+
+
+/* Default baud rate: 57600, 115200, 230400 or 460800 */
+#define DEFAULT_BAUD_RATE  230400
+
+
+/* Hardware states */
+#define CARD_READY             1
+#define CARD_HAS_PCCARD_ID     4
+#define CARD_HAS_POWER_LED     5
+#define CARD_HAS_ACTIVITY_LED  6
+
+/* Transmit states  */
+#define XMIT_SENDING         1
+#define XMIT_WAKEUP          2
+#define XMIT_BUFFER_NUMBER   5	/* unset = buffer one, set = buffer two */
+#define XMIT_BUF_ONE_READY   6
+#define XMIT_BUF_TWO_READY   7
+#define XMIT_SENDING_READY   8
+
+/* Receiver states */
+#define RECV_WAIT_PACKET_TYPE   0
+#define RECV_WAIT_EVENT_HEADER  1
+#define RECV_WAIT_ACL_HEADER    2
+#define RECV_WAIT_SCO_HEADER    3
+#define RECV_WAIT_DATA          4
+
+/* Special packet types */
+#define PKT_BAUD_RATE_57600   0x80
+#define PKT_BAUD_RATE_115200  0x81
+#define PKT_BAUD_RATE_230400  0x82
+#define PKT_BAUD_RATE_460800  0x83
+
+
+/* These are the register offsets */
+#define REG_COMMAND     0x20
+#define REG_INTERRUPT   0x21
+#define REG_CONTROL     0x22
+#define REG_RX_CONTROL  0x24
+#define REG_CARD_RESET  0x30
+#define REG_LED_CTRL    0x30
+
+/* REG_COMMAND */
+#define REG_COMMAND_TX_BUF_ONE  0x01
+#define REG_COMMAND_TX_BUF_TWO  0x02
+#define REG_COMMAND_RX_BUF_ONE  0x04
+#define REG_COMMAND_RX_BUF_TWO  0x08
+#define REG_COMMAND_RX_WIN_ONE  0x00
+#define REG_COMMAND_RX_WIN_TWO  0x10
+
+/* REG_CONTROL */
+#define REG_CONTROL_BAUD_RATE_57600   0x00
+#define REG_CONTROL_BAUD_RATE_115200  0x01
+#define REG_CONTROL_BAUD_RATE_230400  0x02
+#define REG_CONTROL_BAUD_RATE_460800  0x03
+#define REG_CONTROL_RTS               0x04
+#define REG_CONTROL_BT_ON             0x08
+#define REG_CONTROL_BT_RESET          0x10
+#define REG_CONTROL_BT_RES_PU         0x20
+#define REG_CONTROL_INTERRUPT         0x40
+#define REG_CONTROL_CARD_RESET        0x80
+
+/* REG_RX_CONTROL */
+#define RTS_LEVEL_SHIFT_BITS  0x02
+
+
+
+/* ======================== LED handling routines ======================== */
+
+
+void bluecard_activity_led_timeout(u_long arg)
+{
+	bluecard_info_t *info = (bluecard_info_t *) arg;
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Disable activity LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+	} else {
+		/* Disable power LED */
+		outb(0x00, iobase + 0x30);
+	}
+}
+
+
+static void bluecard_enable_activity_led(bluecard_info_t * info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (test_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state))) {
+		/* Enable activity LED */
+		outb(0x10 | 0x40, iobase + 0x30);
+
+		/* Stop the LED after HZ/4 */
+		mod_timer(&(info->timer), jiffies + HZ / 4);
+	} else {
+		/* Enable power LED */
+		outb(0x08 | 0x20, iobase + 0x30);
+
+		/* Stop the LED after HZ/2 */
+		mod_timer(&(info->timer), jiffies + HZ / 2);
+	}
+}
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int bluecard_write(unsigned int iobase, unsigned int offset,
+			  __u8 * buf, int len)
+{
+	int i, actual;
+
+	actual = (len > 15) ? 15 : len;
+
+	outb_p(actual, iobase + offset);
+
+	for (i = 0; i < actual; i++)
+		outb_p(buf[i], iobase + offset + i + 1);
+
+	return actual;
+}
+
+
+static void bluecard_write_wakeup(bluecard_info_t * info)
+{
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of write_wakeup for unknown device.\n");
+		return;
+	}
+
+	if (!test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		return;
+
+	if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+		set_bit(XMIT_WAKEUP, &(info->tx_state));
+		return;
+	}
+
+	do {
+		register unsigned int iobase = info->link.io.BasePort1;
+		register unsigned int offset;
+		register unsigned char command;
+		register unsigned long ready_bit;
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+		if (!(info->link.state & DEV_PRESENT))
+			return;
+
+		if (test_bit(XMIT_BUFFER_NUMBER, &(info->tx_state))) {
+			if (!test_bit
+			    (XMIT_BUF_TWO_READY, &(info->tx_state)))
+				break;
+			offset = 0x10;
+			command = REG_COMMAND_TX_BUF_TWO;
+			ready_bit = XMIT_BUF_TWO_READY;
+		} else {
+			if (!test_bit
+			    (XMIT_BUF_ONE_READY, &(info->tx_state)))
+				break;
+			offset = 0x00;
+			command = REG_COMMAND_TX_BUF_ONE;
+			ready_bit = XMIT_BUF_ONE_READY;
+		}
+
+		if (!(skb = skb_dequeue(&(info->txq))))
+			break;
+
+		if (skb->pkt_type & 0x80) {
+
+			/* Disable RTS */
+			info->ctrl_reg |= REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+		}
+
+		/* Activate LED */
+		bluecard_enable_activity_led(info);
+
+		/* Send frame */
+		len = bluecard_write(iobase, offset, skb->data, skb->len);
+
+		/* Tell the FPGA to send the data */
+		outb_p(command, iobase + REG_COMMAND);
+
+		/* Mark the buffer as dirty */
+		clear_bit(ready_bit, &(info->tx_state));
+
+		if (skb->pkt_type & 0x80) {
+
+			wait_queue_head_t wait;
+			unsigned char baud_reg;
+
+			switch (skb->pkt_type) {
+			case PKT_BAUD_RATE_460800:
+				baud_reg = REG_CONTROL_BAUD_RATE_460800;
+				break;
+			case PKT_BAUD_RATE_230400:
+				baud_reg = REG_CONTROL_BAUD_RATE_230400;
+				break;
+			case PKT_BAUD_RATE_115200:
+				baud_reg = REG_CONTROL_BAUD_RATE_115200;
+				break;
+			case PKT_BAUD_RATE_57600:
+				/* Fall through... */
+			default:
+				baud_reg = REG_CONTROL_BAUD_RATE_57600;
+				break;
+			}
+
+			/* Wait until the command reaches the baseband */
+			init_waitqueue_head(&wait);
+			interruptible_sleep_on_timeout(&wait, HZ / 10);
+
+			/* Set baud on baseband */
+			info->ctrl_reg &= ~0x03;
+			info->ctrl_reg |= baud_reg;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Enable RTS */
+			info->ctrl_reg &= ~REG_CONTROL_RTS;
+			outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+			/* Wait before the next HCI packet can be send */
+			interruptible_sleep_on_timeout(&wait, HZ);
+
+		}
+
+		if (len == skb->len) {
+			kfree_skb(skb);
+		} else {
+			skb_pull(skb, len);
+			skb_queue_head(&(info->txq), skb);
+		}
+
+		info->hdev.stat.byte_tx += len;
+
+		/* Change buffer */
+		change_bit(XMIT_BUFFER_NUMBER, &(info->tx_state));
+
+	} while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+	clear_bit(XMIT_SENDING, &(info->tx_state));
+}
+
+
+static int bluecard_read(unsigned int iobase, unsigned int offset,
+			 __u8 * buf, int size)
+{
+	int i, n, len;
+
+	outb(REG_COMMAND_RX_WIN_ONE, iobase + REG_COMMAND);
+
+	len = inb(iobase + offset);
+	n = 0;
+	i = 1;
+
+	while (n < len) {
+
+		if (i == 16) {
+			outb(REG_COMMAND_RX_WIN_TWO, iobase + REG_COMMAND);
+			i = 0;
+		}
+
+		buf[n] = inb(iobase + offset + i);
+
+		n++;
+		i++;
+
+	}
+
+	return len;
+}
+
+
+static void bluecard_receive(bluecard_info_t * info, unsigned int offset)
+{
+	unsigned int iobase;
+	unsigned char buf[31];
+	int i, len;
+
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of receive for unknown device.\n");
+		return;
+	}
+
+	iobase = info->link.io.BasePort1;
+
+	if (test_bit(XMIT_SENDING_READY, &(info->tx_state)))
+		bluecard_enable_activity_led(info);
+
+	len = bluecard_read(iobase, offset, buf, sizeof(buf));
+
+	for (i = 0; i < len; i++) {
+
+		/* Allocate packet */
+		if (info->rx_skb == NULL) {
+			info->rx_state = RECV_WAIT_PACKET_TYPE;
+			info->rx_count = 0;
+			if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+				printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
+				return;
+			}
+		}
+
+		if (info->rx_state == RECV_WAIT_PACKET_TYPE) {
+
+			info->rx_skb->dev = (void *) &(info->hdev);
+			info->rx_skb->pkt_type = buf[i];
+
+			switch (info->rx_skb->pkt_type) {
+
+			case 0x00:
+				/* init packet */
+				if (offset != 0x00) {
+					set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+					set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+					set_bit(XMIT_SENDING_READY, &(info->tx_state));
+					bluecard_write_wakeup(info);
+				}
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			case HCI_EVENT_PKT:
+				info->rx_state = RECV_WAIT_EVENT_HEADER;
+				info->rx_count = HCI_EVENT_HDR_SIZE;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				info->rx_state = RECV_WAIT_ACL_HEADER;
+				info->rx_count = HCI_ACL_HDR_SIZE;
+				break;
+
+			case HCI_SCODATA_PKT:
+				info->rx_state = RECV_WAIT_SCO_HEADER;
+				info->rx_count = HCI_SCO_HDR_SIZE;
+				break;
+
+			default:
+				/* unknown packet */
+				printk(KERN_WARNING "bluecard_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+				info->hdev.stat.err_rx++;
+
+				kfree_skb(info->rx_skb);
+				info->rx_skb = NULL;
+				break;
+
+			}
+
+		} else {
+
+			*skb_put(info->rx_skb, 1) = buf[i];
+			info->rx_count--;
+
+			if (info->rx_count == 0) {
+
+				int dlen;
+				hci_event_hdr *eh;
+				hci_acl_hdr *ah;
+				hci_sco_hdr *sh;
+
+				switch (info->rx_state) {
+
+				case RECV_WAIT_EVENT_HEADER:
+					eh = (hci_event_hdr *) (info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = eh->plen;
+					break;
+
+				case RECV_WAIT_ACL_HEADER:
+					ah = (hci_acl_hdr *) (info->rx_skb->data);
+					dlen = __le16_to_cpu(ah->dlen);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = dlen;
+					break;
+
+				case RECV_WAIT_SCO_HEADER:
+					sh = (hci_sco_hdr *) (info->rx_skb->data);
+					info->rx_state = RECV_WAIT_DATA;
+					info->rx_count = sh->dlen;
+					break;
+
+				case RECV_WAIT_DATA:
+					hci_recv_frame(info->rx_skb);
+					info->rx_skb = NULL;
+					break;
+
+				}
+
+			}
+
+		}
+
+
+	}
+
+	info->hdev.stat.byte_rx += len;
+}
+
+
+void bluecard_interrupt(int irq, void *dev_inst, struct pt_regs *regs)
+{
+	bluecard_info_t *info = dev_inst;
+	unsigned int iobase;
+	unsigned char reg;
+
+	if (!info) {
+		printk(KERN_WARNING "bluecard_cs: Call of irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (!test_bit(CARD_READY, &(info->hw_state)))
+		return;
+
+	iobase = info->link.io.BasePort1;
+
+	spin_lock(&(info->lock));
+
+	/* Disable interrupt */
+	info->ctrl_reg &= ~REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	reg = inb(iobase + REG_INTERRUPT);
+
+	if ((reg != 0x00) && (reg != 0xff)) {
+
+		if (reg & 0x04) {
+			bluecard_receive(info, 0x00);
+			outb(0x04, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x08) {
+			bluecard_receive(info, 0x10);
+			outb(0x08, iobase + REG_INTERRUPT);
+			outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+		}
+
+		if (reg & 0x01) {
+			set_bit(XMIT_BUF_ONE_READY, &(info->tx_state));
+			outb(0x01, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+		if (reg & 0x02) {
+			set_bit(XMIT_BUF_TWO_READY, &(info->tx_state));
+			outb(0x02, iobase + REG_INTERRUPT);
+			bluecard_write_wakeup(info);
+		}
+
+	}
+
+	/* Enable interrupt */
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	spin_unlock(&(info->lock));
+}
+
+
+
+/* ======================== Device specific HCI commands ======================== */
+
+
+static int bluecard_hci_set_baud_rate(struct hci_dev *hdev, int baud)
+{
+	bluecard_info_t *info = (bluecard_info_t *) (hdev->driver_data);
+	struct sk_buff *skb;
+	int i;
+
+	/* Ericsson baud rate command */
+	unsigned char cmd[] = { HCI_COMMAND_PKT, 0x09, 0xfc, 0x01, 0x03 };
+
+	if (!(skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+		printk(KERN_WARNING "bluecard_cs: Can't allocate mem for new packet.\n");
+		return -1;
+	}
+
+	switch (baud) {
+	case 460800:
+		cmd[4] = 0x00;
+		skb->pkt_type = PKT_BAUD_RATE_460800;
+		break;
+	case 230400:
+		cmd[4] = 0x01;
+		skb->pkt_type = PKT_BAUD_RATE_230400;
+		break;
+	case 115200:
+		cmd[4] = 0x02;
+		skb->pkt_type = PKT_BAUD_RATE_115200;
+		break;
+	case 57600:
+		/* Fall through... */
+	default:
+		cmd[4] = 0x03;
+		skb->pkt_type = PKT_BAUD_RATE_57600;
+		break;
+	}
+
+	for (i = 0; i < sizeof(cmd); i++)
+		*skb_put(skb, 1) = cmd[i];
+
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int bluecard_hci_flush(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *) (hdev->driver_data);
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	return 0;
+}
+
+
+static int bluecard_hci_open(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *) (hdev->driver_data);
+	unsigned int iobase = info->link.io.BasePort1;
+
+	bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
+
+	if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	/* Enable LED */
+	outb(0x08 | 0x20, iobase + 0x30);
+
+	return 0;
+}
+
+
+static int bluecard_hci_close(struct hci_dev *hdev)
+{
+	bluecard_info_t *info = (bluecard_info_t *) (hdev->driver_data);
+	unsigned int iobase = info->link.io.BasePort1;
+
+	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+		return 0;
+
+	bluecard_hci_flush(hdev);
+
+	/* Disable LED */
+	outb(0x00, iobase + 0x30);
+
+	return 0;
+}
+
+
+static int bluecard_hci_send_frame(struct sk_buff *skb)
+{
+	bluecard_info_t *info;
+	struct hci_dev *hdev = (struct hci_dev *) (skb->dev);
+
+	if (!hdev) {
+		printk(KERN_WARNING "bluecard_cs: Frame for unknown HCI device (hdev=NULL).");
+		return -ENODEV;
+	}
+
+	info = (bluecard_info_t *) (hdev->driver_data);
+
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+	case HCI_SCODATA_PKT:
+		hdev->stat.sco_tx++;
+		break;
+	};
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &(skb->pkt_type), 1);
+	skb_queue_tail(&(info->txq), skb);
+
+	bluecard_write_wakeup(info);
+
+	return 0;
+}
+
+
+static void bluecard_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+
+static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd,
+			      unsigned long arg)
+{
+	return -ENOIOCTLCMD;
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int bluecard_open(bluecard_info_t * info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev;
+	unsigned char id;
+
+	spin_lock_init(&(info->lock));
+
+	init_timer(&(info->timer));
+	info->timer.function = &bluecard_activity_led_timeout;
+	info->timer.data = (u_long) info;
+
+	skb_queue_head_init(&(info->txq));
+
+	info->rx_state = RECV_WAIT_PACKET_TYPE;
+	info->rx_count = 0;
+	info->rx_skb = NULL;
+
+	id = inb(iobase + 0x30);
+
+	if ((id & 0x0f) == 0x02)
+		set_bit(CARD_HAS_PCCARD_ID, &(info->hw_state));
+
+	if (id & 0x10)
+		set_bit(CARD_HAS_POWER_LED, &(info->hw_state));
+
+	if (id & 0x20)
+		set_bit(CARD_HAS_ACTIVITY_LED, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	/* Wait some time */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout(HZ / 100);
+
+	/* Turn FPGA on */
+	outb(0x00, iobase + 0x30);
+
+	/* Activate card */
+	info->ctrl_reg = REG_CONTROL_BT_ON | REG_CONTROL_BT_RES_PU;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Enable interrupt */
+	outb(0xff, iobase + REG_INTERRUPT);
+	info->ctrl_reg |= REG_CONTROL_INTERRUPT;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Start the RX buffers */
+	outb(REG_COMMAND_RX_BUF_ONE, iobase + REG_COMMAND);
+	outb(REG_COMMAND_RX_BUF_TWO, iobase + REG_COMMAND);
+
+	/* Signal that the hardware is ready */
+	set_bit(CARD_READY, &(info->hw_state));
+
+	/* Drop TX queue */
+	skb_queue_purge(&(info->txq));
+
+	/* Control the point at which RTS is enabled */
+	outb((0x0f << RTS_LEVEL_SHIFT_BITS) | 1, iobase + REG_RX_CONTROL);
+
+	/* Timeout before it is safe to send the first HCI packet */
+	set_current_state(TASK_INTERRUPTIBLE);
+	schedule_timeout((HZ * 5) / 4);	// or set it to 3/2
+
+	/* Initialize and register HCI device */
+
+	hdev = &(info->hdev);
+
+	hdev->type = HCI_PCCARD;
+	hdev->driver_data = info;
+
+	hdev->open = bluecard_hci_open;
+	hdev->close = bluecard_hci_close;
+	hdev->flush = bluecard_hci_flush;
+	hdev->send = bluecard_hci_send_frame;
+	hdev->destruct = bluecard_hci_destruct;
+	hdev->ioctl = bluecard_hci_ioctl;
+
+	if (hci_register_dev(hdev) < 0) {
+		printk(KERN_WARNING "bluecard_cs: Can't register HCI device %s.\n", hdev->name);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+
+int bluecard_close(bluecard_info_t * info)
+{
+	unsigned int iobase = info->link.io.BasePort1;
+	struct hci_dev *hdev = &(info->hdev);
+
+	bluecard_hci_close(hdev);
+
+	clear_bit(CARD_READY, &(info->hw_state));
+
+	/* Reset card */
+	info->ctrl_reg = REG_CONTROL_BT_RESET | REG_CONTROL_CARD_RESET;
+	outb(info->ctrl_reg, iobase + REG_CONTROL);
+
+	/* Turn FPGA off */
+	outb(0x80, iobase + 0x30);
+
+	if (hci_unregister_dev(hdev) < 0)
+		printk(KERN_WARNING "bluecard_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+	return 0;
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret)
+{
+	error_info_t err = { func, ret };
+
+	CardServices(ReportError, handle, &err);
+}
+
+
+dev_link_t *bluecard_attach(void)
+{
+	bluecard_info_t *info;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	int i, ret;
+
+	/* Create new info device */
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+	memset(info, 0, sizeof(*info));
+
+	link = &info->link;
+	link->priv = info;
+
+	link->release.function = &bluecard_release;
+	link->release.data = (u_long) link;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.NumPorts1 = 8;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	if (irq_list[0] == -1)
+		link->irq.IRQInfo2 = irq_mask;
+	else
+		for (i = 0; i < 4; i++)
+			link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+	link->irq.Handler = bluecard_interrupt;
+	link->irq.Instance = info;
+
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+	client_reg.EventMask =
+	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &bluecard_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+
+	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	if (ret != CS_SUCCESS) {
+		cs_error(link->handle, RegisterClient, ret);
+		bluecard_detach(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+
+void bluecard_detach(dev_link_t * link)
+{
+	bluecard_info_t *info = link->priv;
+	dev_link_t **linkp;
+	int ret;
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link)
+			break;
+
+	if (*linkp == NULL)
+		return;
+
+	del_timer(&link->release);
+	if (link->state & DEV_CONFIG)
+		bluecard_release((u_long) link);
+
+	if (link->handle) {
+		ret = CardServices(DeregisterClient, link->handle);
+		if (ret != CS_SUCCESS)
+			cs_error(link->handle, DeregisterClient, ret);
+	}
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+
+	kfree(info);
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t * tuple,
+		     cisparse_t * parse)
+{
+	int i;
+
+	i = CardServices(fn, handle, tuple);
+	if (i != CS_SUCCESS)
+		return CS_NO_MORE_ITEMS;
+
+	i = CardServices(GetTupleData, handle, tuple);
+	if (i != CS_SUCCESS)
+		return i;
+
+	return CardServices(ParseTuple, handle, tuple, parse);
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void bluecard_config(dev_link_t * link)
+{
+	client_handle_t handle = link->handle;
+	bluecard_info_t *info = link->priv;
+	tuple_t tuple;
+	u_short buf[256];
+	cisparse_t parse;
+	config_info_t config;
+	int i, n, last_ret, last_fn;
+
+	tuple.TupleData = (cisdata_t *) buf;
+	tuple.TupleOffset = 0;
+	tuple.TupleDataMax = 255;
+	tuple.Attributes = 0;
+
+	/* Get configuration register information */
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	last_ret = first_tuple(handle, &tuple, &parse);
+	if (last_ret != CS_SUCCESS) {
+		last_fn = ParseTuple;
+		goto cs_failed;
+	}
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+	i = CardServices(GetConfigurationInfo, handle, &config);
+	link->conf.Vcc = config.Vcc;
+
+	link->conf.ConfigIndex = 0x20;
+	link->io.NumPorts1 = 64;
+	link->io.IOAddrLines = 6;
+
+	for (n = 0; n < 0x400; n += 0x40) {
+		link->io.BasePort1 = n ^ 0x300;
+		i = CardServices(RequestIO, link->handle, &link->io);
+		if (i == CS_SUCCESS)
+			break;
+	}
+
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+
+	i = CardServices(RequestIRQ, link->handle, &link->irq);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIRQ, i);
+		link->irq.AssignedIRQ = 0;
+	}
+
+	i = CardServices(RequestConfiguration, link->handle, &link->conf);
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestConfiguration, i);
+		goto failed;
+	}
+
+
+	MOD_INC_USE_COUNT;
+
+	if (bluecard_open(info) != 0)
+		goto failed;
+
+	link->dev = &info->node;
+	link->state &= ~DEV_CONFIG_PENDING;
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+
+failed:
+	bluecard_release((u_long) link);
+}
+
+
+void bluecard_release(u_long arg)
+{
+	dev_link_t *link = (dev_link_t *) arg;
+	bluecard_info_t *info = link->priv;
+
+	if (link->state & DEV_PRESENT)
+		bluecard_close(info);
+
+	MOD_DEC_USE_COUNT;
+
+	link->dev = NULL;
+
+	CardServices(ReleaseConfiguration, link->handle);
+	CardServices(ReleaseIO, link->handle, &link->io);
+	CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+
+int bluecard_event(event_t event, int priority,
+		   event_callback_args_t * args)
+{
+	dev_link_t *link = args->client_data;
+	bluecard_info_t *info = link->priv;
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG) {
+			bluecard_close(info);
+			mod_timer(&link->release, jiffies + HZ / 20);
+		}
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		bluecard_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG)
+			CardServices(ReleaseConfiguration, link->handle);
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (DEV_OK(link))
+			CardServices(RequestConfiguration, link->handle,
+				     &link->conf);
+		break;
+	}
+
+	return 0;
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_bluecard_cs(void)
+{
+	servinfo_t serv;
+	int err;
+
+	CardServices(GetCardServicesInfo, &serv);
+	if (serv.Revision != CS_RELEASE_CODE) {
+		printk(KERN_NOTICE "bluecard_cs: Card Services release does not match!\n");
+		return -1;
+	}
+
+	err = register_pccard_driver(&dev_info, &bluecard_attach, &bluecard_detach);
+
+	return err;
+}
+
+
+void __exit exit_bluecard_cs(void)
+{
+	unregister_pccard_driver(&dev_info);
+
+	while (dev_list != NULL)
+		bluecard_detach(dev_list);
+}
+
+
+module_init(init_bluecard_cs);
+module_exit(exit_bluecard_cs);
+
+EXPORT_NO_SYMBOLS;
diff -Nru a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/dtl1_cs.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,939 @@
+/*
+ *
+ *  A driver for Nokia Connectivity Card DTL-1 devices
+ *
+ *  Copyright (C) 2001-2002  Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation;
+ *
+ *  Software distributed under the License is distributed on an "AS
+ *  IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ *  implied. See the License for the specific language governing
+ *  rights and limitations under the License.
+ *
+ *  The initial developer of the original code is David A. Hinds
+ *  <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
+ *  are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+
+#include <linux/skbuff.h>
+#include <linux/string.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+
+
+/* ======================== Module parameters ======================== */
+
+
+/* Bit map of interrupts to choose from */
+static u_int irq_mask = 0xffff;
+static int irq_list[4] = { -1 };
+
+MODULE_PARM(irq_mask, "i");
+MODULE_PARM(irq_list, "1-4i");
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ driver for Nokia Connectivity Card DTL-1");
+MODULE_LICENSE("GPL");
+
+
+
+/* ======================== Local structures ======================== */
+
+
+typedef struct dtl1_info_t {
+  dev_link_t link;
+  dev_node_t node;
+
+  struct hci_dev hdev;
+
+  spinlock_t lock;          /* For serializing operations */
+
+  unsigned long flowmask;   /* HCI flow mask */
+  int ri_latch;
+
+  struct sk_buff_head txq;
+  unsigned long tx_state;
+
+  unsigned long rx_state;
+  unsigned long rx_count;
+  struct sk_buff *rx_skb;
+
+} dtl1_info_t;
+
+
+void dtl1_config(dev_link_t *link);
+void dtl1_release(u_long arg);
+int dtl1_event(event_t event, int priority, event_callback_args_t *args);
+
+static dev_info_t dev_info = "dtl1_cs";
+
+dev_link_t *dtl1_attach(void);
+void dtl1_detach(dev_link_t *);
+
+dev_link_t *dev_list = NULL;
+
+
+/* Transmit states  */
+#define XMIT_SENDING  1
+#define XMIT_WAKEUP   2
+#define XMIT_WAITING  8
+
+/* Receiver States */
+#define RECV_WAIT_NSH   0
+#define RECV_WAIT_DATA  1
+
+
+typedef struct {
+  u8 type;
+  u8 zero;
+  u16 len;
+} __attribute__ ((packed)) nsh_t;   /* Nokia Specific Header */
+
+#define NSHL  4    /* Nokia Specific Header Length */
+
+
+
+/* ======================== Interrupt handling ======================== */
+
+
+static int dtl1_write(unsigned int iobase, int fifo_size, __u8 *buf, int len) {
+
+  int actual = 0;
+
+
+  /* Tx FIFO should be empty */
+  if (!(inb(iobase + UART_LSR) & UART_LSR_THRE))
+    return 0;
+
+
+  /* Fill FIFO with current frame */
+  while ((fifo_size-- > 0) && (actual < len)) {
+    /* Transmit next byte */
+    outb(buf[actual], iobase + UART_TX);
+    actual++;
+  }
+
+
+  return actual;
+
+}
+
+
+static void dtl1_write_wakeup(dtl1_info_t *info) {
+
+  if (!info) {
+    printk(KERN_WARNING "dtl1_cs: Call of write_wakeup for unknown device.\n");
+    return;
+  }
+
+
+  if (test_bit(XMIT_WAITING, &(info->tx_state))) {
+    set_bit(XMIT_WAKEUP, &(info->tx_state));
+    return;
+  }
+
+  if (test_and_set_bit(XMIT_SENDING, &(info->tx_state))) {
+    set_bit(XMIT_WAKEUP, &(info->tx_state));
+    return;
+  }
+
+
+  do {
+    register unsigned int iobase = info->link.io.BasePort1;
+    register struct sk_buff *skb;
+    register int len;
+
+    clear_bit(XMIT_WAKEUP, &(info->tx_state));
+
+    if (!(info->link.state & DEV_PRESENT))
+      return;
+
+
+    if (!(skb = skb_dequeue(&(info->txq))))
+      break;
+
+
+    /* Send frame */
+    len = dtl1_write(iobase, 32, skb->data, skb->len);
+
+    if (len == skb->len) {
+      set_bit(XMIT_WAITING, &(info->tx_state));
+      kfree_skb(skb);
+    }
+    else {
+      skb_pull(skb, len);
+      skb_queue_head(&(info->txq), skb);
+    }
+
+    info->hdev.stat.byte_tx += len;
+
+  } while (test_bit(XMIT_WAKEUP, &(info->tx_state)));
+
+
+  clear_bit(XMIT_SENDING, &(info->tx_state));
+
+}
+
+
+static void dtl1_control(dtl1_info_t *info, struct sk_buff *skb) {
+
+  u8 flowmask = *(u8 *)skb->data;
+  int i;
+
+
+  printk(KERN_INFO "dtl1_cs: Nokia control data = ");
+  for (i = 0; i < skb->len; i++) {
+    printk("%02x ", skb->data[i]);
+  }
+  printk("\n");
+
+
+  /* transition to active state */
+  if (((info->flowmask & 0x07) == 0) && ((flowmask & 0x07) != 0)) {
+    clear_bit(XMIT_WAITING, &(info->tx_state));
+    dtl1_write_wakeup(info);
+  }
+
+  info->flowmask = flowmask;
+
+
+  kfree_skb(skb);
+
+}
+
+
+static void dtl1_receive(dtl1_info_t *info) {
+
+  unsigned int iobase;
+  nsh_t *nsh;
+  int boguscount = 0;
+
+
+  if (!info) {
+    printk(KERN_WARNING "dtl1_cs: Call of receive for unknown device.\n");
+    return;
+  }
+
+
+  iobase = info->link.io.BasePort1;
+
+  do {
+    info->hdev.stat.byte_rx++;
+
+    /* Allocate packet */
+    if (info->rx_skb == NULL)
+      if (!(info->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
+        printk(KERN_WARNING "dtl1_cs: Can't allocate mem for new packet.\n");
+        info->rx_state = RECV_WAIT_NSH;
+        info->rx_count = NSHL;
+        return;
+    }
+
+
+    *skb_put(info->rx_skb, 1) = inb(iobase + UART_RX);
+    nsh = (nsh_t *)info->rx_skb->data;
+
+    info->rx_count--;
+
+
+    if (info->rx_count == 0) {
+
+      switch (info->rx_state) {
+      case RECV_WAIT_NSH:
+        info->rx_state = RECV_WAIT_DATA;
+        info->rx_count = nsh->len + (nsh->len & 0x0001);
+        break;
+      case RECV_WAIT_DATA:
+        info->rx_skb->pkt_type = nsh->type;
+
+        /* remove PAD byte if it exists */
+        if (nsh->len & 0x0001) {
+          info->rx_skb->tail--;
+          info->rx_skb->len--;
+        }
+
+        /* remove NSH */
+        skb_pull(info->rx_skb, NSHL);
+
+
+        switch (info->rx_skb->pkt_type) {
+        case 0x80:
+          /* control data for the Nokia Card */
+          dtl1_control(info, info->rx_skb);
+          break;
+        case 0x82:
+        case 0x83:
+        case 0x84:
+          /* send frame to the HCI layer */
+          info->rx_skb->dev = (void *)&(info->hdev);
+          info->rx_skb->pkt_type &= 0x0f;
+          hci_recv_frame(info->rx_skb);
+          break;
+        default:
+          /* unknown packet */
+          printk(KERN_WARNING "dtl1_cs: Unknown HCI packet with type 0x%02x received.\n", info->rx_skb->pkt_type);
+          kfree_skb(info->rx_skb);
+          break;
+        }
+
+
+        info->rx_state = RECV_WAIT_NSH;
+        info->rx_count = NSHL;
+        info->rx_skb = NULL;
+        break;
+      }
+
+    }
+
+
+    /* Make sure we don't stay here to long */
+    if (boguscount++ > 32)
+      break;
+
+  } while (inb(iobase + UART_LSR) & UART_LSR_DR);
+
+
+}
+
+
+void dtl1_interrupt(int irq, void *dev_inst, struct pt_regs *regs) {
+
+  dtl1_info_t *info = dev_inst;
+  unsigned int iobase;
+  unsigned char msr;
+  int boguscount = 0;
+  int iir, lsr;
+
+
+  if (!info) {
+    printk(KERN_WARNING "dtl1_cs: Call of irq %d for unknown device.\n", irq);
+    return;
+  }
+
+
+  iobase = info->link.io.BasePort1;
+
+
+  spin_lock(&(info->lock));
+
+  iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+  while (iir) {
+
+    /* Clear interrupt */
+    lsr = inb(iobase + UART_LSR);
+
+    switch (iir) {
+    case UART_IIR_RLSI:
+      printk(KERN_NOTICE "dtl1_cs: RLSI\n");
+      break;
+    case UART_IIR_RDI:
+      /* Receive interrupt */
+      dtl1_receive(info);
+      break;
+    case UART_IIR_THRI:
+      if (lsr & UART_LSR_THRE) {
+        /* Transmitter ready for data */
+        dtl1_write_wakeup(info);
+      }
+      break;
+    default:
+      printk(KERN_NOTICE "dtl1_cs: Unhandled IIR=%#x\n", iir);
+      break;
+    }
+
+    /* Make sure we don't stay here to long */
+    if (boguscount++ > 100)
+      break;
+
+    iir = inb(iobase + UART_IIR) & UART_IIR_ID;
+
+  }
+
+
+  msr = inb(iobase + UART_MSR);
+
+  if (info->ri_latch ^ (msr & UART_MSR_RI)) {
+    info->ri_latch = msr & UART_MSR_RI;
+    clear_bit(XMIT_WAITING, &(info->tx_state));
+    dtl1_write_wakeup(info);
+  }
+
+  spin_unlock(&(info->lock));
+
+}
+
+
+
+/* ======================== HCI interface ======================== */
+
+
+static int dtl1_hci_open(struct hci_dev *hdev) {
+
+  set_bit(HCI_RUNNING, &(hdev->flags));
+
+
+  return 0;
+
+}
+
+
+static int dtl1_hci_flush(struct hci_dev *hdev) {
+
+  dtl1_info_t *info = (dtl1_info_t *)(hdev->driver_data);
+
+
+  /* Drop TX queue */
+  skb_queue_purge(&(info->txq));
+
+
+  return 0;
+
+}
+
+
+static int dtl1_hci_close(struct hci_dev *hdev) {
+
+  if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
+    return 0;
+
+
+  dtl1_hci_flush(hdev);
+
+
+  return 0;
+
+}
+
+
+static int dtl1_hci_send_frame(struct sk_buff *skb) {
+
+  dtl1_info_t *info;
+  struct hci_dev* hdev = (struct hci_dev *)(skb->dev);
+  struct sk_buff *s;
+  nsh_t nsh;
+
+
+  if (!hdev) {
+    printk(KERN_WARNING "dtl1_cs: Frame for unknown HCI device (hdev=NULL).");
+    return -ENODEV;
+  }
+
+  info = (dtl1_info_t *)(hdev->driver_data);
+
+
+  switch (skb->pkt_type) {
+  case HCI_COMMAND_PKT:
+    hdev->stat.cmd_tx++;
+    nsh.type = 0x81;
+    break;
+  case HCI_ACLDATA_PKT:
+    hdev->stat.acl_tx++;
+    nsh.type = 0x82;
+    break;
+  case HCI_SCODATA_PKT:
+    hdev->stat.sco_tx++;
+    nsh.type = 0x83;
+    break;
+  };
+
+  nsh.zero = 0;
+  nsh.len = skb->len;
+
+  s = bluez_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+  skb_reserve(s, NSHL);
+  memcpy(skb_put(s, skb->len), skb->data, skb->len);
+  if (skb->len & 0x0001)
+    *skb_put(s, 1) = 0;  /* PAD */
+
+  /* Prepend skb with Nokia frame header and queue */
+  memcpy(skb_push(s, NSHL), &nsh, NSHL);
+  skb_queue_tail(&(info->txq), s);
+
+
+  dtl1_write_wakeup(info);
+
+  kfree_skb(skb);
+
+
+  return 0;
+
+}
+
+
+static void dtl1_hci_destruct(struct hci_dev *hdev) {
+}
+
+
+static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) {
+
+  return -ENOIOCTLCMD;
+
+}
+
+
+
+/* ======================== Card services HCI interaction ======================== */
+
+
+int dtl1_open(dtl1_info_t *info) {
+
+  unsigned long flags;
+  unsigned int iobase = info->link.io.BasePort1;
+  struct hci_dev *hdev;
+
+
+  spin_lock_init(&(info->lock));
+
+  skb_queue_head_init(&(info->txq));
+
+  info->rx_state = RECV_WAIT_NSH;
+  info->rx_count = NSHL;
+  info->rx_skb = NULL;
+
+  set_bit(XMIT_WAITING, &(info->tx_state));
+
+
+  spin_lock_irqsave(&(info->lock), flags);
+
+  /* Reset UART */
+  outb(0, iobase + UART_MCR);
+
+  /* Turn off interrupts */
+  outb(0, iobase + UART_IER);
+
+  /* Initialize UART */
+  outb(UART_LCR_WLEN8, iobase + UART_LCR);  /* Reset DLAB */
+  outb((UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2), iobase + UART_MCR);
+
+  info->ri_latch = inb(info->link.io.BasePort1 + UART_MSR) & UART_MSR_RI;
+
+  /* Turn on interrupts */
+  outb(UART_IER_RLSI | UART_IER_RDI | UART_IER_THRI, iobase + UART_IER);
+
+  spin_unlock_irqrestore(&(info->lock), flags);
+
+
+  /* Timeout before it is safe to send the first HCI packet */
+  set_current_state(TASK_INTERRUPTIBLE);
+  schedule_timeout(HZ * 2);
+
+
+  /* Initialize and register HCI device */
+
+  hdev = &(info->hdev);
+
+  hdev->type = HCI_PCCARD;
+  hdev->driver_data = info;
+
+  hdev->open     = dtl1_hci_open;
+  hdev->close    = dtl1_hci_close;
+  hdev->flush    = dtl1_hci_flush;
+  hdev->send     = dtl1_hci_send_frame;
+  hdev->destruct = dtl1_hci_destruct;
+  hdev->ioctl    = dtl1_hci_ioctl;
+
+  if (hci_register_dev(hdev) < 0) {
+    printk(KERN_WARNING "dtl1_cs: Can't register HCI device %s.\n", hdev->name);
+    return -ENODEV;
+  }
+
+
+  return 0;
+
+}
+
+
+int dtl1_close(dtl1_info_t *info) {
+
+  unsigned long flags;
+  unsigned int iobase = info->link.io.BasePort1;
+  struct hci_dev *hdev = &(info->hdev);
+
+
+  dtl1_hci_close(hdev);
+
+
+  spin_lock_irqsave(&(info->lock), flags);
+
+  /* Reset UART */
+  outb(0, iobase + UART_MCR);
+
+  /* Turn off interrupts */
+  outb(0, iobase + UART_IER);
+
+  spin_unlock_irqrestore(&(info->lock), flags);
+
+
+  if (hci_unregister_dev(hdev) < 0)
+    printk(KERN_WARNING "dtl1_cs: Can't unregister HCI device %s.\n", hdev->name);
+
+
+  return 0;
+
+}
+
+
+
+/* ======================== Card services ======================== */
+
+
+static void cs_error(client_handle_t handle, int func, int ret) {
+
+  error_info_t err = { func, ret };
+
+
+  CardServices(ReportError, handle, &err);
+
+}
+
+
+dev_link_t *dtl1_attach(void) {
+
+  dtl1_info_t *info;
+  client_reg_t client_reg;
+  dev_link_t *link;
+  int i, ret;
+
+
+  /* Create new info device */
+  info = kmalloc(sizeof(*info), GFP_KERNEL);
+  if (!info)
+    return NULL;
+  memset(info, 0, sizeof(*info));
+
+
+  link = &info->link;
+  link->priv = info;
+
+  link->release.function = &dtl1_release;
+  link->release.data = (u_long)link;
+  link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+  link->io.NumPorts1 = 8;
+  link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+  link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+  if (irq_list[0] == -1)
+    link->irq.IRQInfo2 = irq_mask;
+  else
+    for (i = 0; i < 4; i++)
+      link->irq.IRQInfo2 |= 1 << irq_list[i];
+
+  link->irq.Handler = dtl1_interrupt;
+  link->irq.Instance = info;
+
+  link->conf.Attributes = CONF_ENABLE_IRQ;
+  link->conf.Vcc = 50;
+  link->conf.IntType = INT_MEMORY_AND_IO;
+
+
+  /* Register with Card Services */
+  link->next = dev_list;
+  dev_list = link;
+  client_reg.dev_info = &dev_info;
+  client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
+  client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+                         CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+                         CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+  client_reg.event_handler = &dtl1_event;
+  client_reg.Version = 0x0210;
+  client_reg.event_callback_args.client_data = link;
+
+  ret = CardServices(RegisterClient, &link->handle, &client_reg);
+  if (ret != CS_SUCCESS) {
+    cs_error(link->handle, RegisterClient, ret);
+    dtl1_detach(link);
+    return NULL;
+  }
+
+
+  return link;
+
+}
+
+
+void dtl1_detach(dev_link_t *link) {
+
+  dtl1_info_t *info = link->priv;
+  dev_link_t **linkp;
+  int ret;
+
+
+  /* Locate device structure */
+  for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+    if (*linkp == link)
+      break;
+
+  if (*linkp == NULL)
+    return;
+
+
+  del_timer(&link->release);
+  if (link->state & DEV_CONFIG)
+    dtl1_release((u_long)link);
+
+
+  if (link->handle) {
+    ret = CardServices(DeregisterClient, link->handle);
+    if (ret != CS_SUCCESS)
+      cs_error(link->handle, DeregisterClient, ret);
+  }
+
+
+  /* Unlink device structure, free bits */
+  *linkp = link->next;
+
+  kfree(info);
+
+}
+
+
+static int get_tuple(int fn, client_handle_t handle, tuple_t *tuple,
+		     cisparse_t *parse) {
+
+  int i;
+
+
+  i = CardServices(fn, handle, tuple);
+  if (i != CS_SUCCESS)
+    return CS_NO_MORE_ITEMS;
+
+  i = CardServices(GetTupleData, handle, tuple);
+  if (i != CS_SUCCESS)
+    return i;
+
+  return CardServices(ParseTuple, handle, tuple, parse);
+
+}
+
+
+#define first_tuple(a, b, c) get_tuple(GetFirstTuple, a, b, c)
+#define next_tuple(a, b, c) get_tuple(GetNextTuple, a, b, c)
+
+void dtl1_config(dev_link_t *link) {
+
+  client_handle_t handle = link->handle;
+  dtl1_info_t *info = link->priv;
+  tuple_t tuple;
+  u_short buf[256];
+  cisparse_t parse;
+  cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+  config_info_t config;
+  int i, last_ret, last_fn;
+
+
+  tuple.TupleData = (cisdata_t *)buf;
+  tuple.TupleOffset = 0;
+  tuple.TupleDataMax = 255;
+  tuple.Attributes = 0;
+
+  /* Get configuration register information */
+  tuple.DesiredTuple = CISTPL_CONFIG;
+  last_ret = first_tuple(handle, &tuple, &parse);
+  if (last_ret != CS_SUCCESS) {
+    last_fn = ParseTuple;
+    goto cs_failed;
+  }
+  link->conf.ConfigBase = parse.config.base;
+  link->conf.Present = parse.config.rmask[0];
+
+
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+  i = CardServices(GetConfigurationInfo, handle, &config);
+  link->conf.Vcc = config.Vcc;
+
+  tuple.TupleData = (cisdata_t *)buf;
+  tuple.TupleOffset = 0; tuple.TupleDataMax = 255;
+  tuple.Attributes = 0;
+  tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+  /* Look for a generic full-sized window */
+  link->io.NumPorts1 = 8;
+  i = first_tuple(handle, &tuple, &parse);
+  while (i != CS_NO_MORE_ITEMS) {
+    if ((i == CS_SUCCESS) && (cf->io.nwin == 1) && (cf->io.win[0].len > 8)) {
+      link->conf.ConfigIndex = cf->index;
+      link->io.BasePort1 = cf->io.win[0].base;
+      link->io.NumPorts1 = cf->io.win[0].len; /*yo*/
+      link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+      i = CardServices(RequestIO, link->handle, &link->io);
+      if (i == CS_SUCCESS)
+        break;
+    }
+    i = next_tuple(handle, &tuple, &parse);
+  }
+
+  if (i != CS_SUCCESS) {
+    cs_error(link->handle, RequestIO, i);
+    goto failed;
+  }
+
+  i = CardServices(RequestIRQ, link->handle, &link->irq);
+  if (i != CS_SUCCESS) {
+    cs_error(link->handle, RequestIRQ, i);
+    link->irq.AssignedIRQ = 0;
+  }
+
+  i = CardServices(RequestConfiguration, link->handle, &link->conf);
+  if (i != CS_SUCCESS) {
+    cs_error(link->handle, RequestConfiguration, i);
+    goto failed;
+  }
+
+
+  MOD_INC_USE_COUNT;
+
+  if (dtl1_open(info) != 0)
+    goto failed;
+
+
+  link->dev = &info->node;
+  link->state &= ~DEV_CONFIG_PENDING;
+
+
+  return;
+
+
+cs_failed:
+  cs_error(link->handle, last_fn, last_ret);
+failed:
+  dtl1_release((u_long)link);
+
+}
+
+
+void dtl1_release(u_long arg) {
+
+  dev_link_t *link = (dev_link_t *)arg;
+  dtl1_info_t *info = link->priv;
+
+
+  if (link->state & DEV_PRESENT)
+    dtl1_close(info);
+
+  MOD_DEC_USE_COUNT;
+
+
+  link->dev = NULL;
+
+  CardServices(ReleaseConfiguration, link->handle);
+  CardServices(ReleaseIO, link->handle, &link->io);
+  CardServices(ReleaseIRQ, link->handle, &link->irq);
+
+  link->state &= ~DEV_CONFIG;
+
+}
+
+
+int dtl1_event(event_t event, int priority, event_callback_args_t *args) {
+
+  dev_link_t *link = args->client_data;
+  dtl1_info_t *info = link->priv;
+
+
+  switch (event) {
+  case CS_EVENT_CARD_REMOVAL:
+    link->state &= ~DEV_PRESENT;
+    if (link->state & DEV_CONFIG) {
+      dtl1_close(info);
+      mod_timer(&link->release, jiffies + HZ/20);
+    }
+    break;
+  case CS_EVENT_CARD_INSERTION:
+    link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+    dtl1_config(link);
+    break;
+  case CS_EVENT_PM_SUSPEND:
+    link->state |= DEV_SUSPEND;
+    /* Fall through... */
+  case CS_EVENT_RESET_PHYSICAL:
+    if (link->state & DEV_CONFIG)
+      CardServices(ReleaseConfiguration, link->handle);
+    break;
+  case CS_EVENT_PM_RESUME:
+    link->state &= ~DEV_SUSPEND;
+    /* Fall through... */
+  case CS_EVENT_CARD_RESET:
+    if (DEV_OK(link))
+      CardServices(RequestConfiguration, link->handle, &link->conf);
+    break;
+  }
+
+
+  return 0;
+
+}
+
+
+
+/* ======================== Module initialization ======================== */
+
+
+int __init init_dtl1_cs(void) {
+
+  servinfo_t serv;
+  int err;
+
+
+  CardServices(GetCardServicesInfo, &serv);
+  if (serv.Revision != CS_RELEASE_CODE) {
+    printk(KERN_NOTICE "dtl1_cs: Card Services release does not match!\n");
+    return -1;
+  }
+
+
+  err = register_pccard_driver(&dev_info, &dtl1_attach, &dtl1_detach);
+
+
+  return err;
+
+}
+
+void __exit exit_dtl1_cs(void) {
+
+  unregister_pccard_driver(&dev_info);
+
+  while (dev_list != NULL)
+    dtl1_detach(dev_list);
+
+}
+
+
+module_init(init_dtl1_cs);
+module_exit(exit_dtl1_cs);
+
+EXPORT_NO_SYMBOLS;
diff -Nru a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_h4.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,273 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ HCI UART(H4) protocol.
+ *
+ * $Id: hci_h4.c,v 1.2 2002/04/17 17:37:20 maxk Exp $    
+ */
+#define VERSION "1.1"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+#include "hci_h4.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+/* Initialize protocol */
+static int h4_open(struct n_hci *n_hci)
+{
+	struct h4_struct *h4;
+	
+	BT_DBG("n_hci %p", n_hci);
+	
+	h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
+	if (!h4)
+		return -ENOMEM;
+	memset(h4, 0, sizeof(*h4));
+
+	n_hci->priv = h4;
+	return 0;
+}
+
+/* Flush protocol data */
+static int h4_flush(struct n_hci *n_hci)
+{
+	BT_DBG("n_hci %p", n_hci);
+	return 0;
+}
+
+/* Close protocol */
+static int h4_close(struct n_hci *n_hci)
+{
+	struct h4_struct *h4 = n_hci->priv;
+	n_hci->priv = NULL;
+
+	BT_DBG("n_hci %p", n_hci);
+
+	if (h4->rx_skb)
+		kfree_skb(h4->rx_skb);
+
+	kfree(h4);
+	return 0;
+}
+
+/* Send data */
+static int h4_send(struct n_hci *n_hci, void *data, int len)
+{
+	struct tty_struct *tty = n_hci->tty;
+	
+	BT_DBG("n_hci %p len %d", n_hci, len);
+
+	/* Send frame to TTY driver */
+	tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+	return tty->driver.write(tty, 0, data, len);
+}
+
+/* Init frame before queueing (padding, crc, etc) */
+static struct sk_buff* h4_preq(struct n_hci *n_hci, struct sk_buff *skb)
+{
+	BT_DBG("n_hci %p skb %p", n_hci, skb);
+
+	/* Prepend skb with frame type */
+	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
+	return skb;
+}
+
+static inline int h4_check_data_len(struct h4_struct *h4, int len)
+{
+	register int room = skb_tailroom(h4->rx_skb);
+
+	BT_DBG("len %d room %d", len, room);
+	if (!len) {
+		BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
+		hci_recv_frame(h4->rx_skb);
+	} else if (len > room) {
+		BT_ERR("Data length is to large");
+		kfree_skb(h4->rx_skb);
+	} else {
+		h4->rx_state = H4_W4_DATA;
+		h4->rx_count = len;
+		return len;
+	}
+
+	h4->rx_state = H4_W4_PACKET_TYPE;
+	h4->rx_skb   = NULL;
+	h4->rx_count = 0;
+	return 0;
+}
+
+/* Recv data */
+static int h4_recv(struct n_hci *n_hci, void *data, int count)
+{
+	struct h4_struct *h4 = n_hci->priv;
+	register char *ptr;
+	hci_event_hdr *eh;
+	hci_acl_hdr   *ah;
+	hci_sco_hdr   *sh;
+	register int len, type, dlen;
+
+	BT_DBG("n_hci %p count %d rx_state %ld rx_count %ld", n_hci, count, h4->rx_state, h4->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (h4->rx_count) {
+			len = MIN(h4->rx_count, count);
+			memcpy(skb_put(h4->rx_skb, len), ptr, len);
+			h4->rx_count -= len; count -= len; ptr += len;
+
+			if (h4->rx_count)
+				continue;
+
+			switch (h4->rx_state) {
+			case H4_W4_DATA:
+				BT_DBG("Complete data");
+
+				BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
+
+				hci_recv_frame(h4->rx_skb);
+
+				h4->rx_state = H4_W4_PACKET_TYPE;
+				h4->rx_skb = NULL;
+				continue;
+
+			case H4_W4_EVENT_HDR:
+				eh = (hci_event_hdr *) h4->rx_skb->data;
+
+				BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
+
+				h4_check_data_len(h4, eh->plen);
+				continue;
+
+			case H4_W4_ACL_HDR:
+				ah = (hci_acl_hdr *) h4->rx_skb->data;
+				dlen = __le16_to_cpu(ah->dlen);
+
+				BT_DBG("ACL header: dlen %d", dlen);
+
+				h4_check_data_len(h4, dlen);
+				continue;
+
+			case H4_W4_SCO_HDR:
+				sh = (hci_sco_hdr *) h4->rx_skb->data;
+
+				BT_DBG("SCO header: dlen %d", sh->dlen);
+
+				h4_check_data_len(h4, sh->dlen);
+				continue;
+			};
+		}
+
+		/* H4_W4_PACKET_TYPE */
+		switch (*ptr) {
+		case HCI_EVENT_PKT:
+			BT_DBG("Event packet");
+			h4->rx_state = H4_W4_EVENT_HDR;
+			h4->rx_count = HCI_EVENT_HDR_SIZE;
+			type = HCI_EVENT_PKT;
+			break;
+
+		case HCI_ACLDATA_PKT:
+			BT_DBG("ACL packet");
+			h4->rx_state = H4_W4_ACL_HDR;
+			h4->rx_count = HCI_ACL_HDR_SIZE;
+			type = HCI_ACLDATA_PKT;
+			break;
+
+		case HCI_SCODATA_PKT:
+			BT_DBG("SCO packet");
+			h4->rx_state = H4_W4_SCO_HDR;
+			h4->rx_count = HCI_SCO_HDR_SIZE;
+			type = HCI_SCODATA_PKT;
+			break;
+
+		default:
+			BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
+			n_hci->hdev.stat.err_rx++;
+			ptr++; count--;
+			continue;
+		};
+		ptr++; count--;
+
+		/* Allocate packet */
+		h4->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
+		if (!h4->rx_skb) {
+			BT_ERR("Can't allocate mem for new packet");
+			h4->rx_state = H4_W4_PACKET_TYPE;
+			h4->rx_count = 0;
+			return 0;
+		}
+		h4->rx_skb->dev = (void *) &n_hci->hdev;
+		h4->rx_skb->pkt_type = type;
+	}
+	return count;
+}
+
+static struct hci_uart_proto h4p = {
+	id:    HCI_UART_H4,
+	open:  h4_open,
+	close: h4_close,
+	send:  h4_send,
+	recv:  h4_recv,
+	preq:  h4_preq,
+	flush: h4_flush,
+};
+	      
+int h4_init(void)
+{
+	return hci_uart_register_proto(&h4p);
+}
+
+int h4_deinit(void)
+{
+	return hci_uart_unregister_proto(&h4p);
+}
diff -Nru a/drivers/bluetooth/hci_h4.h b/drivers/bluetooth/hci_h4.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_h4.h	Sun May  5 20:39:50 2002
@@ -0,0 +1,43 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_h4.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifdef __KERNEL__
+struct h4_struct {
+	unsigned long rx_state;
+	unsigned long rx_count;
+	struct sk_buff *rx_skb;
+};
+
+/* H4 receiver States */
+#define H4_W4_PACKET_TYPE 0
+#define H4_W4_EVENT_HDR	  1
+#define H4_W4_ACL_HDR     2
+#define H4_W4_SCO_HDR     3
+#define H4_W4_DATA        4
+
+#endif /* __KERNEL__ */
diff -Nru a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_ldisc.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,554 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ HCI UART driver.
+ *
+ * $Id: hci_ldisc.c,v 1.2 2002/04/17 17:37:20 maxk Exp $    
+ */
+#define VERSION "2.0"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+static struct hci_uart_proto *hup[HCI_UART_MAX_PROTO];
+
+int hci_uart_register_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (hup[p->id])
+		return -EEXIST;
+
+	hup[p->id] = p;
+	return 0;
+}
+
+int hci_uart_unregister_proto(struct hci_uart_proto *p)
+{
+	if (p->id >= HCI_UART_MAX_PROTO)
+		return -EINVAL;
+
+	if (!hup[p->id])
+		return -EINVAL;
+
+	hup[p->id] = NULL;
+	return 0;
+}
+
+static struct hci_uart_proto *n_hci_get_proto(unsigned int id)
+{
+	if (id >= HCI_UART_MAX_PROTO)
+		return NULL;
+	return hup[id];
+}
+
+/* ------- Interface to HCI layer ------ */
+/* Initialize device */
+static int n_hci_open(struct hci_dev *hdev)
+{
+	BT_DBG("%s %p", hdev->name, hdev);
+
+	/* Nothing to do for UART driver */
+
+	set_bit(HCI_RUNNING, &hdev->flags);
+	return 0;
+}
+
+/* Reset device */
+static int n_hci_flush(struct hci_dev *hdev)
+{
+	struct n_hci *n_hci  = (struct n_hci *) hdev->driver_data;
+	struct tty_struct *tty = n_hci->tty;
+
+	BT_DBG("hdev %p tty %p", hdev, tty);
+
+	/* Drop TX queue */
+	skb_queue_purge(&n_hci->txq);
+
+	/* Flush any pending characters in the driver and discipline. */
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+
+	if (n_hci->proto->flush)
+		n_hci->proto->flush(n_hci);
+
+	return 0;
+}
+
+/* Close device */
+static int n_hci_close(struct hci_dev *hdev)
+{
+	BT_DBG("hdev %p", hdev);
+
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
+	n_hci_flush(hdev);
+	return 0;
+}
+
+static int n_hci_tx_wakeup(struct n_hci *n_hci)
+{
+	struct hci_dev *hdev = &n_hci->hdev;
+	
+	if (test_and_set_bit(N_HCI_SENDING, &n_hci->tx_state)) {
+		set_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
+		return 0;
+	}
+
+	BT_DBG("");
+	do {
+		register struct sk_buff *skb;
+		register int len;
+
+		clear_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
+
+		if (!(skb = skb_dequeue(&n_hci->txq)))
+			break;
+
+		len = n_hci->proto->send(n_hci, skb->data, skb->len);
+		n_hci->hdev.stat.byte_tx += len;
+
+		if (len == skb->len) {
+			/* Complete frame was sent */
+
+			switch (skb->pkt_type) {
+			case HCI_COMMAND_PKT:
+				hdev->stat.cmd_tx++;
+				break;
+
+			case HCI_ACLDATA_PKT:
+				hdev->stat.acl_tx++;
+				break;
+
+			case HCI_SCODATA_PKT:
+				hdev->stat.cmd_tx++;
+				break;
+			};
+
+			kfree_skb(skb);
+		} else {
+			/* Subtract sent part and requeue  */
+			skb_pull(skb, len);
+			skb_queue_head(&n_hci->txq, skb);
+		}
+	} while (test_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state));
+	clear_bit(N_HCI_SENDING, &n_hci->tx_state);
+	return 0;
+}
+
+/* Send frames from HCI layer */
+static int n_hci_send_frame(struct sk_buff *skb)
+{
+	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
+	struct tty_struct *tty;
+	struct n_hci *n_hci;
+
+	if (!hdev) {
+		BT_ERR("Frame for uknown device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
+	n_hci = (struct n_hci *) hdev->driver_data;
+	tty = n_hci->tty;
+
+	BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+	if (n_hci->proto->preq) {
+		skb = n_hci->proto->preq(n_hci, skb);
+		if (!skb)
+			return 0;
+	}
+	
+	skb_queue_tail(&n_hci->txq, skb);
+	n_hci_tx_wakeup(n_hci);
+	return 0;
+}
+
+static void n_hci_destruct(struct hci_dev *hdev)
+{
+	struct n_hci *n_hci;
+
+	if (!hdev) return;
+
+	BT_DBG("%s", hdev->name);
+
+	n_hci = (struct n_hci *) hdev->driver_data;
+	kfree(n_hci);
+
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------ LDISC part ------ */
+/* n_hci_tty_open
+ * 
+ *     Called when line discipline changed to N_HCI.
+ *
+ * Arguments:
+ *     tty    pointer to tty info structure
+ * Return Value:    
+ *     0 if success, otherwise error code
+ */
+static int n_hci_tty_open(struct tty_struct *tty)
+{
+	struct n_hci *n_hci = (void *)tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	if (n_hci)
+		return -EEXIST;
+
+	if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
+		BT_ERR("Can't allocate controll structure");
+		return -ENFILE;
+	}
+	memset(n_hci, 0, sizeof(struct n_hci));
+
+	tty->disc_data = n_hci;
+	n_hci->tty = tty;
+
+	spin_lock_init(&n_hci->rx_lock);
+	skb_queue_head_init(&n_hci->txq);
+
+	/* Flush any pending characters in the driver and line discipline */
+	if (tty->ldisc.flush_buffer)
+		tty->ldisc.flush_buffer(tty);
+
+	if (tty->driver.flush_buffer)
+		tty->driver.flush_buffer(tty);
+	
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/* n_hci_tty_close()
+ *
+ *    Called when the line discipline is changed to something
+ *    else, the tty is closed, or the tty detects a hangup.
+ */
+static void n_hci_tty_close(struct tty_struct *tty)
+{
+	struct n_hci *n_hci = (void *)tty->disc_data;
+
+	BT_DBG("tty %p", tty);
+
+	/* Detach from the tty */
+	tty->disc_data = NULL;
+
+	if (n_hci) {
+		struct hci_dev *hdev = &n_hci->hdev;
+		n_hci_close(hdev);
+
+		if (test_and_clear_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
+			n_hci->proto->close(n_hci);
+			hci_unregister_dev(hdev);
+		}
+				
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+/* n_hci_tty_wakeup()
+ *
+ *    Callback for transmit wakeup. Called when low level
+ *    device driver can accept more send data.
+ *
+ * Arguments:        tty    pointer to associated tty instance data
+ * Return Value:    None
+ */
+static void n_hci_tty_wakeup( struct tty_struct *tty )
+{
+	struct n_hci *n_hci = (void *)tty->disc_data;
+
+	BT_DBG("");
+
+	if (!n_hci)
+		return;
+
+	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+
+	if (tty != n_hci->tty)
+		return;
+
+	n_hci_tx_wakeup(n_hci);
+}
+
+/* n_hci_tty_room()
+ * 
+ *    Callback function from tty driver. Return the amount of 
+ *    space left in the receiver's buffer to decide if remote
+ *    transmitter is to be throttled.
+ *
+ * Arguments:        tty    pointer to associated tty instance data
+ * Return Value:    number of bytes left in receive buffer
+ */
+static int n_hci_tty_room (struct tty_struct *tty)
+{
+	return 65536;
+}
+
+/* n_hci_tty_receive()
+ * 
+ *     Called by tty low level driver when receive data is
+ *     available.
+ *     
+ * Arguments:  tty          pointer to tty isntance data
+ *             data         pointer to received data
+ *             flags        pointer to flags for data
+ *             count        count of received data in bytes
+ *     
+ * Return Value:    None
+ */
+static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
+{
+	struct n_hci *n_hci = (void *)tty->disc_data;
+	
+	if (!n_hci || tty != n_hci->tty)
+		return;
+
+	if (!test_bit(N_HCI_PROTO_SET, &n_hci->flags))
+		return;
+	
+	spin_lock(&n_hci->rx_lock);
+	n_hci->proto->recv(n_hci, (void *) data, count);
+	n_hci->hdev.stat.byte_rx += count;
+	spin_unlock(&n_hci->rx_lock);
+
+	if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
+		tty->driver.unthrottle(tty);
+}
+
+static int n_hci_register_dev(struct n_hci *n_hci)
+{
+	struct hci_dev *hdev;
+
+	BT_DBG("");
+
+	/* Initialize and register HCI device */
+	hdev = &n_hci->hdev;
+
+	hdev->type = HCI_UART;
+	hdev->driver_data = n_hci;
+
+	hdev->open  = n_hci_open;
+	hdev->close = n_hci_close;
+	hdev->flush = n_hci_flush;
+	hdev->send  = n_hci_send_frame;
+	hdev->destruct = n_hci_destruct;
+
+	if (hci_register_dev(hdev) < 0) {
+		BT_ERR("Can't register HCI device %s", hdev->name);
+		return -ENODEV;
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static int n_hci_set_proto(struct n_hci *n_hci, int id)
+{
+	struct hci_uart_proto *p;
+	int err;	
+	
+	p = n_hci_get_proto(id);
+	if (!p)
+		return -EPROTONOSUPPORT;
+
+	err = p->open(n_hci);
+	if (err)
+		return err;
+
+	n_hci->proto = p;
+
+	err = n_hci_register_dev(n_hci);
+	if (err) {
+		p->close(n_hci);
+		return err;
+	}
+	return 0;
+}
+
+/* n_hci_tty_ioctl()
+ *
+ *    Process IOCTL system call for the tty device.
+ *
+ * Arguments:
+ *
+ *    tty        pointer to tty instance data
+ *    file       pointer to open file object for device
+ *    cmd        IOCTL command code
+ *    arg        argument for IOCTL call (cmd dependent)
+ *
+ * Return Value:    Command dependent
+ */
+static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file,
+                            unsigned int cmd, unsigned long arg)
+{
+	struct n_hci *n_hci = (void *)tty->disc_data;
+	int err = 0;
+
+	BT_DBG("");
+
+	/* Verify the status of the device */
+	if (!n_hci)
+		return -EBADF;
+
+	switch (cmd) {
+	case HCIUARTSETPROTO:
+		if (!test_and_set_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
+			err = n_hci_set_proto(n_hci, arg);
+			if (err) {
+				clear_bit(N_HCI_PROTO_SET, &n_hci->flags);
+				return err;
+			}
+			tty->low_latency = 1;
+		} else	
+			return -EBUSY;
+
+	case HCIUARTGETPROTO:
+		if (test_bit(N_HCI_PROTO_SET, &n_hci->flags))
+			return n_hci->proto->id;
+		return -EUNATCH;
+		
+	default:
+		err = n_tty_ioctl(tty, file, cmd, arg);
+		break;
+	};
+
+	return err;
+}
+
+/*
+ * We don't provide read/write/poll interface for user space.
+ */
+static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
+{
+	return 0;
+}
+static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
+{
+	return 0;
+}
+static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
+{
+	return 0;
+}
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+int h4_init(void);
+int h4_deinit(void);
+#endif
+
+int __init n_hci_init(void)
+{
+	static struct tty_ldisc n_hci_ldisc;
+	int err;
+
+	BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", 
+		VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+
+	/* Register the tty discipline */
+
+	memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
+	n_hci_ldisc.magic       = TTY_LDISC_MAGIC;
+	n_hci_ldisc.name        = "n_hci";
+	n_hci_ldisc.open        = n_hci_tty_open;
+	n_hci_ldisc.close       = n_hci_tty_close;
+	n_hci_ldisc.read        = n_hci_tty_read;
+	n_hci_ldisc.write       = n_hci_tty_write;
+	n_hci_ldisc.ioctl       = n_hci_tty_ioctl;
+	n_hci_ldisc.poll        = n_hci_tty_poll;
+	n_hci_ldisc.receive_room= n_hci_tty_room;
+	n_hci_ldisc.receive_buf = n_hci_tty_receive;
+	n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
+
+	if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
+		BT_ERR("Can't register HCI line discipline (%d)", err);
+		return err;
+	}
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+	h4_init();
+#endif
+	
+	return 0;
+}
+
+void n_hci_cleanup(void)
+{
+	int err;
+
+#ifdef CONFIG_BLUEZ_HCIUART_H4
+	h4_deinit();
+#endif
+
+	/* Release tty registration of line discipline */
+	if ((err = tty_register_ldisc(N_HCI, NULL)))
+		BT_ERR("Can't unregister HCI line discipline (%d)", err);
+}
+
+module_init(n_hci_init);
+module_exit(n_hci_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/bluetooth/hci_uart.c b/drivers/bluetooth/hci_uart.c
--- a/drivers/bluetooth/hci_uart.c	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,580 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ HCI UART driver.
- *
- * $Id: hci_uart.c,v 1.5 2001/07/05 18:42:44 maxk Exp $    
- */
-#define VERSION "1.0"
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/version.h>
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/fcntl.h>
-#include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/poll.h>
-
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/signal.h>
-#include <linux/ioctl.h>
-#include <linux/skbuff.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_uart.h>
-
-#ifndef HCI_UART_DEBUG
-#undef  DBG
-#define DBG( A... )
-#undef  DMP
-#define DMP( A... )
-#endif
-
-/* ------- Interface to HCI layer ------ */
-/* Initialize device */
-int n_hci_open(struct hci_dev *hdev)
-{
-	DBG("%s %p", hdev->name, hdev);
-
-	/* Nothing to do for UART driver */
-
-	hdev->flags |= HCI_RUNNING;
-
-	return 0;
-}
-
-/* Reset device */
-int n_hci_flush(struct hci_dev *hdev)
-{
-	struct n_hci *n_hci  = (struct n_hci *) hdev->driver_data;
-	struct tty_struct *tty = n_hci->tty;
-
-	DBG("hdev %p tty %p", hdev, tty);
-
-	/* Drop TX queue */
-	skb_queue_purge(&n_hci->txq);
-
-	/* Flush any pending characters in the driver and discipline. */
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-
-	return 0;
-}
-
-/* Close device */
-int n_hci_close(struct hci_dev *hdev)
-{
-	DBG("hdev %p", hdev);
-
-	hdev->flags &= ~HCI_RUNNING;
-
-	n_hci_flush(hdev);
-
-	return 0;
-}
-
-int n_hci_tx_wakeup(struct n_hci *n_hci)
-{
-	register struct tty_struct *tty = n_hci->tty;
-
-	if (test_and_set_bit(TRANS_SENDING, &n_hci->tx_state)) {
-		set_bit(TRANS_WAKEUP, &n_hci->tx_state);
-		return 0;
-	}
-
-	DBG("");
-	do {
-		register struct sk_buff *skb;
-		register int len;
-
-		clear_bit(TRANS_WAKEUP, &n_hci->tx_state);
-
-		if (!(skb = skb_dequeue(&n_hci->txq)))
-			break;
-
-		DMP(skb->data, skb->len);
-
-		/* Send frame to TTY driver */
-		tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-		len = tty->driver.write(tty, 0, skb->data, skb->len);
-
-		n_hci->hdev.stat.byte_tx += len;
-
-		DBG("sent %d", len);
-
-		if (len == skb->len) {
-			/* Full frame was sent */
-			kfree_skb(skb);
-		} else {
-			/* Subtract sent part and requeue  */
-			skb_pull(skb, len);
-			skb_queue_head(&n_hci->txq, skb);
-		}
-	} while (test_bit(TRANS_WAKEUP, &n_hci->tx_state));
-	clear_bit(TRANS_SENDING, &n_hci->tx_state);
-
-	return 0;
-}
-
-/* Send frames from HCI layer */
-int n_hci_send_frame(struct sk_buff *skb)
-{
-	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
-	struct tty_struct *tty;
-	struct n_hci *n_hci;
-
-	if (!hdev) {
-		ERR("Frame for uknown device (hdev=NULL)");
-		return -ENODEV;
-	}
-
-	if (!(hdev->flags & HCI_RUNNING))
-		return -EBUSY;
-
-	n_hci = (struct n_hci *) hdev->driver_data;
-	tty = n_hci2tty(n_hci);
-
-	DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
-
-	switch (skb->pkt_type) {
-		case HCI_COMMAND_PKT:
-			hdev->stat.cmd_tx++;
-			break;
-
-                case HCI_ACLDATA_PKT:
-			hdev->stat.acl_tx++;
-                        break;
-
-		case HCI_SCODATA_PKT:
-			hdev->stat.cmd_tx++;
-                        break;
-	};
-
-	/* Prepend skb with frame type and queue */
-	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
-	skb_queue_tail(&n_hci->txq, skb);
-
-	n_hci_tx_wakeup(n_hci);
-
-	return 0;
-}
-
-/* ------ LDISC part ------ */
-
-/* n_hci_tty_open
- * 
- *     Called when line discipline changed to N_HCI.
- *     
- * Arguments:    
- *     tty    pointer to tty info structure
- * Return Value:    
- *     0 if success, otherwise error code
- */
-static int n_hci_tty_open(struct tty_struct *tty)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	struct hci_dev *hdev;
-
-	DBG("tty %p", tty);
-
-	if (n_hci)
-		return -EEXIST;
-
-	if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
-		ERR("Can't allocate controll structure");
-		return -ENFILE;
-	}
-	memset(n_hci, 0, sizeof(struct n_hci));
-
-	/* Initialize and register HCI device */
-	hdev = &n_hci->hdev;
-
-	hdev->type = HCI_UART;
-	hdev->driver_data = n_hci;
-
-	hdev->open  = n_hci_open;
-	hdev->close = n_hci_close;
-	hdev->flush = n_hci_flush;
-	hdev->send  = n_hci_send_frame;
-
-	if (hci_register_dev(hdev) < 0) {
-		ERR("Can't register HCI device %s", hdev->name);
-		kfree(n_hci);
-		return -ENODEV;
-	}
-
-	tty->disc_data = n_hci;
-	n_hci->tty = tty;
-
-	spin_lock_init(&n_hci->rx_lock);
-	n_hci->rx_state = WAIT_PACKET_TYPE;
-
-	skb_queue_head_init(&n_hci->txq);
-
-	MOD_INC_USE_COUNT;
-
-	/* Flush any pending characters in the driver and discipline. */
-	if (tty->ldisc.flush_buffer)
-		tty->ldisc.flush_buffer(tty);
-
-	if (tty->driver.flush_buffer)
-		tty->driver.flush_buffer(tty);
-
-	return 0;
-}
-
-/* n_hci_tty_close()
- *
- *    Called when the line discipline is changed to something
- *    else, the tty is closed, or the tty detects a hangup.
- */
-static void n_hci_tty_close(struct tty_struct *tty)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	struct hci_dev *hdev = &n_hci->hdev;
-
-	DBG("tty %p hdev %p", tty, hdev);
-
-	if (n_hci != NULL) {
-		n_hci_close(hdev);
-
-		if (hci_unregister_dev(hdev) < 0) {
-			ERR("Can't unregister HCI device %s",hdev->name);
-		}
-
-		hdev->driver_data = NULL;
-		tty->disc_data = NULL;
-		kfree(n_hci);
-
-		MOD_DEC_USE_COUNT;
-	}
-}
-
-/* n_hci_tty_wakeup()
- *
- *    Callback for transmit wakeup. Called when low level
- *    device driver can accept more send data.
- *
- * Arguments:        tty    pointer to associated tty instance data
- * Return Value:    None
- */
-static void n_hci_tty_wakeup( struct tty_struct *tty )
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-
-	DBG("");
-
-	if (!n_hci)
-		return;
-
-	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
-	if (tty != n_hci->tty)
-		return;
-
-	n_hci_tx_wakeup(n_hci);
-}
-
-/* n_hci_tty_room()
- * 
- *    Callback function from tty driver. Return the amount of 
- *    space left in the receiver's buffer to decide if remote
- *    transmitter is to be throttled.
- *
- * Arguments:        tty    pointer to associated tty instance data
- * Return Value:    number of bytes left in receive buffer
- */
-static int n_hci_tty_room (struct tty_struct *tty)
-{
-	return 65536;
-}
-
-static inline int n_hci_check_data_len(struct n_hci *n_hci, int len)
-{
-	register int room = skb_tailroom(n_hci->rx_skb);
-
-	DBG("len %d room %d", len, room);
-	if (!len) {
-		DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
-		hci_recv_frame(n_hci->rx_skb);
-	} else if (len > room) {
-		ERR("Data length is to large");
-		kfree_skb(n_hci->rx_skb);
-		n_hci->hdev.stat.err_rx++;
-	} else {
-		n_hci->rx_state = WAIT_DATA;
-		n_hci->rx_count = len;
-		return len;
-	}
-
-	n_hci->rx_state = WAIT_PACKET_TYPE;
-	n_hci->rx_skb   = NULL;
-	n_hci->rx_count = 0;
-	return 0;
-}
-
-static inline void n_hci_rx(struct n_hci *n_hci, const __u8 * data, char *flags, int count)
-{
-	register const char *ptr;
-	hci_event_hdr *eh;
-	hci_acl_hdr   *ah;
-	hci_sco_hdr   *sh;
-	register int len, type, dlen;
-
-	DBG("count %d state %ld rx_count %ld", count, n_hci->rx_state, n_hci->rx_count);
-
-	n_hci->hdev.stat.byte_rx += count;
-
-	ptr = data;
-	while (count) {
-		if (n_hci->rx_count) {
-			len = MIN(n_hci->rx_count, count);
-			memcpy(skb_put(n_hci->rx_skb, len), ptr, len);
-			n_hci->rx_count -= len; count -= len; ptr += len;
-
-			if (n_hci->rx_count)
-				continue;
-
-			switch (n_hci->rx_state) {
-				case WAIT_DATA:
-					DBG("Complete data");
-
-					DMP(n_hci->rx_skb->data, n_hci->rx_skb->len);
-
-					hci_recv_frame(n_hci->rx_skb);
-
-					n_hci->rx_state = WAIT_PACKET_TYPE;
-					n_hci->rx_skb = NULL;
-					continue;
-
-				case WAIT_EVENT_HDR:
-					eh = (hci_event_hdr *) n_hci->rx_skb->data;
-
-					DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen);
-
-					n_hci_check_data_len(n_hci, eh->plen);
-					continue;
-
-				case WAIT_ACL_HDR:
-					ah = (hci_acl_hdr *) n_hci->rx_skb->data;
-					dlen = __le16_to_cpu(ah->dlen);
-
-					DBG("ACL header: dlen %d", dlen);
-
-					n_hci_check_data_len(n_hci, dlen);
-					continue;
-
-				case WAIT_SCO_HDR:
-					sh = (hci_sco_hdr *) n_hci->rx_skb->data;
-
-					DBG("SCO header: dlen %d", sh->dlen);
-
-					n_hci_check_data_len(n_hci, sh->dlen);
-					continue;
-			};
-		}
-
-		/* WAIT_PACKET_TYPE */
-		switch (*ptr) {
-			case HCI_EVENT_PKT:
-				DBG("Event packet");
-				n_hci->rx_state = WAIT_EVENT_HDR;
-				n_hci->rx_count = HCI_EVENT_HDR_SIZE;
-				type = HCI_EVENT_PKT;
-				break;
-
-			case HCI_ACLDATA_PKT:
-				DBG("ACL packet");
-				n_hci->rx_state = WAIT_ACL_HDR;
-				n_hci->rx_count = HCI_ACL_HDR_SIZE;
-				type = HCI_ACLDATA_PKT;
-				break;
-
-			case HCI_SCODATA_PKT:
-				DBG("SCO packet");
-				n_hci->rx_state = WAIT_SCO_HDR;
-				n_hci->rx_count = HCI_SCO_HDR_SIZE;
-				type = HCI_SCODATA_PKT;
-				break;
-
-			default:
-				ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
-				n_hci->hdev.stat.err_rx++;
-				ptr++; count--;
-				continue;
-		};
-		ptr++; count--;
-
-		/* Allocate packet */
-		if (!(n_hci->rx_skb = bluez_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC))) {
-			ERR("Can't allocate mem for new packet");
-
-			n_hci->rx_state = WAIT_PACKET_TYPE;
-			n_hci->rx_count = 0;
-			return;
-		}
-		n_hci->rx_skb->dev = (void *) &n_hci->hdev;
-		n_hci->rx_skb->pkt_type = type;
-	}
-}
-
-/* n_hci_tty_receive()
- * 
- *     Called by tty low level driver when receive data is
- *     available.
- *     
- * Arguments:  tty          pointer to tty isntance data
- *             data         pointer to received data
- *             flags        pointer to flags for data
- *             count        count of received data in bytes
- *     
- * Return Value:    None
- */
-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-
-	if (!n_hci || tty != n_hci->tty)
-		return;
-
-	spin_lock(&n_hci->rx_lock);
-	n_hci_rx(n_hci, data, flags, count);
-	spin_unlock(&n_hci->rx_lock);
-
-	if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
-		tty->driver.unthrottle(tty);
-}
-
-/* n_hci_tty_ioctl()
- *
- *    Process IOCTL system call for the tty device.
- *
- * Arguments:
- *
- *    tty        pointer to tty instance data
- *    file       pointer to open file object for device
- *    cmd        IOCTL command code
- *    arg        argument for IOCTL call (cmd dependent)
- *
- * Return Value:    Command dependent
- */
-static int n_hci_tty_ioctl (struct tty_struct *tty, struct file * file,
-                            unsigned int cmd, unsigned long arg)
-{
-	struct n_hci *n_hci = tty2n_hci(tty);
-	int error = 0;
-
-	DBG("");
-
-	/* Verify the status of the device */
-	if (!n_hci)
-		return -EBADF;
-
-	switch (cmd) {
-		default:
-			error = n_tty_ioctl(tty, file, cmd, arg);
-			break;
-	};
-
-	return error;
-}
-
-/*
- * We don't provide read/write/poll interface for user space.
- */
-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
-{
-	return 0;
-}
-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
-{
-	return 0;
-}
-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
-{
-	return 0;
-}
-
-int __init n_hci_init(void)
-{
-	static struct tty_ldisc n_hci_ldisc;
-	int err;
-
-	INF("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", 
-		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
-
-	/* Register the tty discipline */
-
-	memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
-	n_hci_ldisc.magic       = TTY_LDISC_MAGIC;
-	n_hci_ldisc.name        = "n_hci";
-	n_hci_ldisc.open        = n_hci_tty_open;
-	n_hci_ldisc.close       = n_hci_tty_close;
-	n_hci_ldisc.read        = n_hci_tty_read;
-	n_hci_ldisc.write       = n_hci_tty_write;
-	n_hci_ldisc.ioctl       = n_hci_tty_ioctl;
-	n_hci_ldisc.poll        = n_hci_tty_poll;
-	n_hci_ldisc.receive_room= n_hci_tty_room;
-	n_hci_ldisc.receive_buf = n_hci_tty_receive;
-	n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
-
-	if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
-		ERR("Can't register HCI line discipline (%d)", err);
-		return err;
-	}
-
-	return 0;
-}
-
-void n_hci_cleanup(void)
-{
-	int err;
-
-	/* Release tty registration of line discipline */
-	if ((err = tty_register_ldisc(N_HCI, NULL)))
-		ERR("Can't unregister HCI line discipline (%d)", err);
-}
-
-module_init(n_hci_init);
-module_exit(n_hci_cleanup);
-
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
-MODULE_LICENSE("GPL");
diff -Nru a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_uart.h	Sun May  5 20:39:49 2002
@@ -0,0 +1,80 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_uart.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifndef N_HCI
+#define N_HCI	15
+#endif
+
+/* Ioctls */
+#define HCIUARTSETPROTO	_IOW('U', 200, int)
+#define HCIUARTGETPROTO	_IOR('U', 201, int)
+
+/* UART protocols */
+#define HCI_UART_MAX_PROTO	3
+
+#define HCI_UART_H4	0
+#define HCI_UART_BCSP	1
+#define HCI_UART_NCSP	2
+
+#ifdef __KERNEL__
+struct n_hci;
+
+struct hci_uart_proto {
+	unsigned int id;
+	int (*open)(struct n_hci *n_hci);
+	int (*recv)(struct n_hci *n_hci, void *data, int len);
+	int (*send)(struct n_hci *n_hci, void *data, int len);
+	int (*close)(struct n_hci *n_hci);
+	int (*flush)(struct n_hci *n_hci);
+	struct sk_buff* (*preq)(struct n_hci *n_hci, struct sk_buff *skb);
+};
+
+struct n_hci {
+	struct tty_struct  *tty;
+	struct hci_dev     hdev;
+	unsigned long      flags;
+
+	struct hci_uart_proto *proto;
+	void               *priv;
+	
+	struct sk_buff_head txq;
+	unsigned long       tx_state;
+	spinlock_t          rx_lock;
+};
+
+/* N_HCI flag bits */
+#define N_HCI_PROTO_SET		0x00
+
+/* TX states  */
+#define N_HCI_SENDING		1
+#define N_HCI_TX_WAKEUP		2
+
+int hci_uart_register_proto(struct hci_uart_proto *p);
+int hci_uart_unregister_proto(struct hci_uart_proto *p);
+
+#endif /* __KERNEL__ */
diff -Nru a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
--- a/drivers/bluetooth/hci_usb.c	Sun May  5 20:39:50 2002
+++ b/drivers/bluetooth/hci_usb.c	Sun May  5 20:39:50 2002
@@ -28,570 +28,755 @@
  *    Copyright (c) 2000 Greg Kroah-Hartman        <greg@kroah.com>
  *    Copyright (c) 2000 Mark Douglas Corner       <mcorner@umich.edu>
  *
- * $Id: hci_usb.c,v 1.5 2001/07/05 18:42:44 maxk Exp $    
+ * $Id: hci_usb.c,v 1.6 2002/04/17 17:37:20 maxk Exp $    
  */
-#define VERSION "1.0"
+#define VERSION "2.0"
 
 #include <linux/config.h>
 #include <linux/module.h>
 
+#define __KERNEL_SYSCALLS__
+
 #include <linux/version.h>
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
+#include <linux/unistd.h>
 #include <linux/types.h>
-#include <linux/fcntl.h>
 #include <linux/interrupt.h>
-#include <linux/ptrace.h>
-#include <linux/poll.h>
 
 #include <linux/slab.h>
-#include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/string.h>
-#include <linux/signal.h>
-#include <linux/ioctl.h>
 #include <linux/skbuff.h>
+#include <linux/kmod.h>
 
 #include <linux/usb.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_usb.h>
+#include "hci_usb.h"
+
+#define HCI_MAX_PENDING (HCI_MAX_BULK_RX + HCI_MAX_BULK_TX + 1)
 
 #ifndef HCI_USB_DEBUG
-#undef  DBG
-#define DBG( A... )
-#undef  DMP
-#define DMP( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
 #endif
 
+#ifndef CONFIG_BLUEZ_USB_ZERO_PACKET
+#undef  USB_ZERO_PACKET
+#define USB_ZERO_PACKET 0
+#endif
+
+static struct usb_driver hci_usb_driver; 
+
 static struct usb_device_id usb_bluetooth_ids [] = {
+	/* Generic Bluetooth USB device */
 	{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
+
+	/* Ericsson with non-standard id */
+	{ USB_DEVICE(0x0bdb, 0x1002) },
+
 	{ }	/* Terminating entry */
 };
 
 MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
 
-static int hci_usb_ctrl_msg(struct hci_usb *husb,  struct sk_buff *skb);
-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb);
+static void hci_usb_interrupt(struct urb *urb);
+static void hci_usb_rx_complete(struct urb *urb);
+static void hci_usb_tx_complete(struct urb *urb);
 
-static void hci_usb_unlink_urbs(struct hci_usb *husb)
+static struct urb *hci_usb_get_completed(struct hci_usb *husb)
 {
-	usb_unlink_urb(husb->read_urb);
-	usb_unlink_urb(husb->intr_urb);
-	usb_unlink_urb(husb->ctrl_urb);
-	usb_unlink_urb(husb->write_urb);
+	struct sk_buff *skb;
+	struct urb *urb = NULL;
+
+	skb = skb_dequeue(&husb->completed_q);
+	if (skb) {
+		urb = ((struct hci_usb_scb *) skb->cb)->urb;
+		kfree_skb(skb);
+	}
+
+	BT_DBG("%s urb %p", husb->hdev.name, urb);
+	return urb;
 }
 
-static void hci_usb_free_bufs(struct hci_usb *husb)
+static int hci_usb_enable_intr(struct hci_usb *husb)
 {
-	if (husb->read_urb) {
-		if (husb->read_urb->transfer_buffer)
-			kfree(husb->read_urb->transfer_buffer);
-		usb_free_urb(husb->read_urb);
-	}
+	struct urb *urb;
+	int pipe, size;
+	void *buf;
 
-	if (husb->intr_urb) {
-		if (husb->intr_urb->transfer_buffer)
-			kfree(husb->intr_urb->transfer_buffer);
-		usb_free_urb(husb->intr_urb);
-	}
+	BT_DBG("%s", husb->hdev.name);
 
-	if (husb->ctrl_urb)
-		usb_free_urb(husb->ctrl_urb);
+ 	if (!(urb = usb_alloc_urb(0, GFP_KERNEL)))
+		return -ENOMEM;
 
-	if (husb->write_urb)
-		usb_free_urb(husb->write_urb);
+	if (!(buf = kmalloc(HCI_MAX_EVENT_SIZE, GFP_KERNEL))) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
 
-	if (husb->intr_skb)
-		kfree_skb(husb->intr_skb);
+	husb->intr_urb = urb;
+	
+        pipe = usb_rcvintpipe(husb->udev, husb->intr_ep);
+        size = usb_maxpacket(husb->udev, pipe, usb_pipeout(pipe));
+	FILL_INT_URB(urb, husb->udev, pipe, buf, size, 
+			hci_usb_interrupt, husb, husb->intr_interval);
+	
+	return usb_submit_urb(urb, GFP_KERNEL);
 }
 
-/* ------- Interface to HCI layer ------ */
-/* Initialize device */
-int hci_usb_open(struct hci_dev *hdev)
+static int hci_usb_disable_intr(struct hci_usb *husb)
 {
-	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
-	int status;
-
-	DBG("%s", hdev->name);
+	struct urb *urb = husb->intr_urb;
+	struct sk_buff *skb;
 
-	husb->read_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->read_urb, GFP_KERNEL)))
-		DBG("read submit failed. %d", status);
+	BT_DBG("%s", husb->hdev.name);
 
-	husb->intr_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->intr_urb, GFP_KERNEL)))
-		DBG("interrupt submit failed. %d", status);
+	usb_unlink_urb(urb); usb_free_urb(urb);
+	husb->intr_urb = NULL;
 
-	hdev->flags |= HCI_RUNNING;
+	skb = husb->intr_skb;
+	if (skb) {
+		husb->intr_skb = NULL;
+		kfree_skb(skb);
+	}
 
 	return 0;
 }
 
-/* Reset device */
-int hci_usb_flush(struct hci_dev *hdev)
+static int hci_usb_rx_submit(struct hci_usb *husb, struct urb *urb)
 {
-	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	struct hci_usb_scb *scb;
+	struct sk_buff *skb;
+	int    pipe, size, err;
 
-	DBG("%s", hdev->name);
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
 
-	/* Drop TX queues */
-	skb_queue_purge(&husb->tx_ctrl_q);
-	skb_queue_purge(&husb->tx_write_q);
+        size = HCI_MAX_FRAME_SIZE;
 
-	return 0;
+	if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC))) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	
+	BT_DBG("%s urb %p", husb->hdev.name, urb);
+
+	skb->dev = (void *) &husb->hdev;
+	skb->pkt_type = HCI_ACLDATA_PKT;
+
+	scb = (struct hci_usb_scb *) skb->cb;
+	scb->urb = urb;
+
+        pipe = usb_rcvbulkpipe(husb->udev, husb->bulk_in_ep);
+
+        FILL_BULK_URB(urb, husb->udev, pipe, skb->data, size, hci_usb_rx_complete, skb);
+        urb->transfer_flags = USB_QUEUE_BULK;
+
+	skb_queue_tail(&husb->pending_q, skb);
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk rx submit failed urb %p err %d",
+				husb->hdev.name, urb, err);
+		skb_unlink(skb);
+		usb_free_urb(urb);
+	}
+	return err;
 }
 
-/* Close device */
-int hci_usb_close(struct hci_dev *hdev)
+/* Initialize device */
+static int hci_usb_open(struct hci_dev *hdev)
 {
 	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	int i, err;
+	long flags;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
 
-	hdev->flags &= ~HCI_RUNNING;
-	hci_usb_unlink_urbs(husb);
+	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
 
-	hci_usb_flush(hdev);
+	write_lock_irqsave(&husb->completion_lock, flags);
 
-	return 0;
+	err = hci_usb_enable_intr(husb);
+	if (!err) {
+		for (i = 0; i < HCI_MAX_BULK_TX; i++)
+			hci_usb_rx_submit(husb, NULL);
+	} else 
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		
+	write_unlock_irqrestore(&husb->completion_lock, flags);
+	return err;
 }
 
-void hci_usb_ctrl_wakeup(struct hci_usb *husb)
+/* Reset device */
+static int hci_usb_flush(struct hci_dev *hdev)
 {
-	struct sk_buff *skb;
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
 
-	if (test_and_set_bit(HCI_TX_CTRL, &husb->tx_state))
-		return;
+	BT_DBG("%s", hdev->name);
 
-	DBG("%s", husb->hdev.name);
+	skb_queue_purge(&husb->cmd_q);
+	skb_queue_purge(&husb->acl_q);
+	return 0;
+}
 
-	if (!(skb = skb_dequeue(&husb->tx_ctrl_q)))
-		goto done;
+static inline void hci_usb_unlink_urbs(struct hci_usb *husb)
+{
+	struct sk_buff *skb;
+	struct urb *urb;
 
-	if (hci_usb_ctrl_msg(husb, skb)){
+	BT_DBG("%s", husb->hdev.name);
+
+	while ((skb = skb_dequeue(&husb->pending_q))) {
+		urb = ((struct hci_usb_scb *) skb->cb)->urb;
+		usb_unlink_urb(urb);
 		kfree_skb(skb);
-		goto done;
 	}
 
-	DMP(skb->data, skb->len);
+	while ((urb = hci_usb_get_completed(husb)))
+		usb_free_urb(urb);
+}
 
-	husb->hdev.stat.byte_tx += skb->len;
-	return;
+/* Close device */
+static int hci_usb_close(struct hci_dev *hdev)
+{
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	long flags;
+	
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
 
-done:
-	clear_bit(HCI_TX_CTRL, &husb->tx_state);
-	return;
+	BT_DBG("%s", hdev->name);
+
+	write_lock_irqsave(&husb->completion_lock, flags);
+	
+	hci_usb_disable_intr(husb);
+	hci_usb_unlink_urbs(husb);
+	hci_usb_flush(hdev);
+
+	write_unlock_irqrestore(&husb->completion_lock, flags);
+	return 0;
 }
 
-void hci_usb_write_wakeup(struct hci_usb *husb)
+static inline int hci_usb_send_ctrl(struct hci_usb *husb, struct sk_buff *skb)
 {
-	struct sk_buff *skb;
+	struct hci_usb_scb *scb = (void *) skb->cb;
+	struct urb *urb = hci_usb_get_completed(husb);
+	struct usb_ctrlrequest *cr;
+	int pipe, err;
 
-	if (test_and_set_bit(HCI_TX_WRITE, &husb->tx_state))
-		return;
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
 
-	DBG("%s", husb->hdev.name);
+	if (!(cr = kmalloc(sizeof(*cr), GFP_ATOMIC))) {
+		usb_free_urb(urb);
+		return -ENOMEM;
+	}
+	
+	pipe = usb_sndctrlpipe(husb->udev, 0);
 
-	if (!(skb = skb_dequeue(&husb->tx_write_q)))
-		goto done;
+	cr->bRequestType = HCI_CTRL_REQ;
+	cr->bRequest = 0;
+	cr->wIndex   = 0;
+	cr->wValue   = 0;
+	cr->wLength  = __cpu_to_le16(skb->len);
 
-	if (hci_usb_write_msg(husb, skb)) {
-		skb_queue_head(&husb->tx_write_q, skb);
-		goto done;
-	}
+	FILL_CONTROL_URB(urb, husb->udev, pipe, (void *) cr,
+			skb->data, skb->len, hci_usb_tx_complete, skb);
 
-	DMP(skb->data, skb->len);
+	BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len);
 
-	husb->hdev.stat.byte_tx += skb->len;
-	return;
+	scb->urb = urb;
 
-done:
-	clear_bit(HCI_TX_WRITE, &husb->tx_state);
-	return;
+	skb_queue_tail(&husb->pending_q, skb);
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s ctrl tx submit failed urb %p err %d", 
+				husb->hdev.name, urb, err);
+		skb_unlink(skb);
+		usb_free_urb(urb); kfree(cr);
+	}
+	return err;
 }
 
-/* Send frames from HCI layer */
-int hci_usb_send_frame(struct sk_buff *skb)
+static inline int hci_usb_send_bulk(struct hci_usb *husb, struct sk_buff *skb)
 {
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-	struct hci_usb *husb;
-
-	if (!hdev) {
-		ERR("frame for uknown device (hdev=NULL)");
-		return -ENODEV;
-	}
+	struct hci_usb_scb *scb = (void *) skb->cb;
+	struct urb *urb = hci_usb_get_completed(husb);
+	int pipe, err;
 
-	if (!(hdev->flags & HCI_RUNNING))
-		return 0;
+	if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
+		return -ENOMEM;
 
-	husb = (struct hci_usb *) hdev->driver_data;
+	pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep);
+        
+	FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
+	              hci_usb_tx_complete, skb);
+	urb->transfer_flags = USB_QUEUE_BULK | USB_ZERO_PACKET;
 
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+	BT_DBG("%s urb %p len %d", husb->hdev.name, urb, skb->len);
 
-	switch (skb->pkt_type) {
-		case HCI_COMMAND_PKT:
-			skb_queue_tail(&husb->tx_ctrl_q, skb);
-			hci_usb_ctrl_wakeup(husb);
-			hdev->stat.cmd_tx++;
-			return 0;
-
-		case HCI_ACLDATA_PKT:
-			skb_queue_tail(&husb->tx_write_q, skb);
-			hci_usb_write_wakeup(husb);
-			hdev->stat.acl_tx++;
-			return 0;
-
-		case HCI_SCODATA_PKT:
-			return -EOPNOTSUPP;
-	};
+	scb->urb = urb;
 
-	return 0;
+	skb_queue_tail(&husb->pending_q, skb);
+	err = usb_submit_urb(urb, GFP_ATOMIC);
+	if (err) {
+		BT_ERR("%s bulk tx submit failed urb %p err %d", 
+				husb->hdev.name, urb, err);
+		skb_unlink(skb);
+		usb_free_urb(urb);
+	}
+	return err;
 }
 
-/* ---------- USB ------------- */
-
-static void hci_usb_ctrl(struct urb *urb)
+static void hci_usb_tx_process(struct hci_usb *husb)
 {
-	struct sk_buff *skb = (struct sk_buff *) urb->context;
-	struct hci_dev *hdev;
-	struct hci_usb *husb;
-
-	if (!skb)
-		return;
-	hdev = (struct hci_dev *) skb->dev;
-	husb = (struct hci_usb *) hdev->driver_data;
+	struct sk_buff *skb;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", husb->hdev.name);
 
-	if (urb->status)
-		DBG("%s ctrl status: %d", hdev->name, urb->status);
+	do {
+		clear_bit(HCI_USB_TX_WAKEUP, &husb->state);
+		
+		/* Process ACL queue */
+		while (skb_queue_len(&husb->pending_q) < HCI_MAX_PENDING &&
+				(skb = skb_dequeue(&husb->acl_q))) {
+			if (hci_usb_send_bulk(husb, skb) < 0) {
+				skb_queue_head(&husb->acl_q, skb);
+				break;
+			}
+		}
 
-	clear_bit(HCI_TX_CTRL, &husb->tx_state);
-	kfree_skb(skb);
+		/* Process command queue */
+		if (!test_bit(HCI_USB_CTRL_TX, &husb->state) &&
+			(skb = skb_dequeue(&husb->cmd_q)) != NULL) {
+			set_bit(HCI_USB_CTRL_TX, &husb->state);
+			if (hci_usb_send_ctrl(husb, skb) < 0) {
+				skb_queue_head(&husb->cmd_q, skb);
+				clear_bit(HCI_USB_CTRL_TX, &husb->state);
+			}
+		}
+	} while(test_bit(HCI_USB_TX_WAKEUP, &husb->state));
+}
 
-	/* Wake up device */
-	hci_usb_ctrl_wakeup(husb);
+static inline void hci_usb_tx_wakeup(struct hci_usb *husb)
+{
+	/* Serialize TX queue processing to avoid data reordering */
+	if (!test_and_set_bit(HCI_USB_TX_PROCESS, &husb->state)) {
+		hci_usb_tx_process(husb);
+		clear_bit(HCI_USB_TX_PROCESS, &husb->state);
+	} else
+		set_bit(HCI_USB_TX_WAKEUP, &husb->state);
 }
 
-static void hci_usb_bulk_write(struct urb *urb)
+/* Send frames from HCI layer */
+int hci_usb_send_frame(struct sk_buff *skb)
 {
-	struct sk_buff *skb = (struct sk_buff *) urb->context;
-	struct hci_dev *hdev;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 	struct hci_usb *husb;
 
-	if (!skb)
-		return;
-	hdev = (struct hci_dev *) skb->dev;
+	if (!hdev) {
+		BT_ERR("frame for uknown device (hdev=NULL)");
+		return -ENODEV;
+	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -EBUSY;
+
 	husb = (struct hci_usb *) hdev->driver_data;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-	if (urb->status)
-		DBG("%s bulk write status: %d", hdev->name, urb->status);
+	read_lock(&husb->completion_lock);
 
-	clear_bit(HCI_TX_WRITE, &husb->tx_state);
-	kfree_skb(skb);
+	switch (skb->pkt_type) {
+	case HCI_COMMAND_PKT:
+		skb_queue_tail(&husb->cmd_q, skb);
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		skb_queue_tail(&husb->acl_q, skb);
+		hdev->stat.acl_tx++;
+		break;
 
-	/* Wake up device */
-	hci_usb_write_wakeup(husb);
+	case HCI_SCODATA_PKT:
+	default:
+		kfree_skb(skb);
+		break;
+	}
+	hci_usb_tx_wakeup(husb);
 
-	return;
+	read_unlock(&husb->completion_lock);
+	return 0;
 }
 
-static void hci_usb_intr(struct urb *urb)
+static void hci_usb_interrupt(struct urb *urb)
 {
-	struct hci_usb *husb = (struct hci_usb *) urb->context;
-	unsigned char *data = urb->transfer_buffer;
-	register int count  = urb->actual_length;
-	register struct sk_buff *skb = husb->intr_skb;
+	struct hci_usb *husb = (void *) urb->context;
+	struct hci_usb_scb *scb;
+	struct sk_buff *skb;
 	hci_event_hdr *eh;
-	register int len;
+	__u8 *data = urb->transfer_buffer;
+	int count = urb->actual_length;
+	int len = HCI_EVENT_HDR_SIZE;
 
-	if (!husb)
-		return;
+	BT_DBG("%s urb %p count %d", husb->hdev.name, urb, count);
 
-	DBG("%s count %d", husb->hdev.name, count);
+	if (!test_bit(HCI_RUNNING, &husb->hdev.flags))
+		return;
 
 	if (urb->status || !count) {
-		DBG("%s intr status %d, count %d", husb->hdev.name, urb->status, count);
+		BT_DBG("%s intr status %d, count %d", 
+				husb->hdev.name, urb->status, count);
 		return;
 	}
 
-	/* Do we really have to handle continuations here ? */
-	if (!skb) {
-		/* New frame */
-		if (count < HCI_EVENT_HDR_SIZE) {
-			DBG("%s bad frame len %d", husb->hdev.name, count);
-			return;
-		}
+	read_lock(&husb->completion_lock);
+	
+	husb->hdev.stat.byte_rx += count;
+
+	if (!(skb = husb->intr_skb)) {
+		/* Start of the frame */
+		if (count < HCI_EVENT_HDR_SIZE)
+			goto bad_len;
 
-		eh = (hci_event_hdr *) data;
+		eh  = (hci_event_hdr *) data;
 		len = eh->plen + HCI_EVENT_HDR_SIZE;
 
-		if (count > len) {
-			DBG("%s corrupted frame, len %d", husb->hdev.name, count);
-			return;
-		}
+		if (count > len)
+			goto bad_len;
 
-		/* Allocate skb */
-		if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
-			ERR("Can't allocate mem for new packet");
-			return;
+		skb = bluez_skb_alloc(len, GFP_ATOMIC);
+		if (!skb) {
+			BT_ERR("%s no memory for event packet", husb->hdev.name);
+			goto done;
 		}
+		scb = (void *) skb->cb;
+
 		skb->dev = (void *) &husb->hdev;
 		skb->pkt_type = HCI_EVENT_PKT;
 
 		husb->intr_skb = skb;
-		husb->intr_count = len;
+		scb->intr_len  = len;
 	} else {
 		/* Continuation */
-		if (count > husb->intr_count) {
-			ERR("%s bad frame len %d (expected %d)", husb->hdev.name, count, husb->intr_count);
-
-			kfree_skb(skb);
+		scb = (void *) skb->cb;
+		len = scb->intr_len;
+		if (count > len) {
 			husb->intr_skb = NULL;
-			husb->intr_count = 0;
-			return;
+			kfree_skb(skb);
+			goto bad_len;
 		}
 	}
 
 	memcpy(skb_put(skb, count), data, count);
-	husb->intr_count -= count;
+	scb->intr_len -= count;
 
-	DMP(data, count);
+	if (!scb->intr_len) {
+		/* Complete frame */
+		husb->intr_skb = NULL;
+		hci_recv_frame(skb);
+	}
 
-	if (!husb->intr_count) {
-		/* Got complete frame */
+done:
+	read_unlock(&husb->completion_lock);
+	return;
 
-		husb->hdev.stat.byte_rx += skb->len;
-		hci_recv_frame(skb);
+bad_len:
+	BT_ERR("%s bad frame len %d expected %d", husb->hdev.name, count, len);
+	husb->hdev.stat.err_rx++;
+	read_unlock(&husb->completion_lock);
+}
 
-		husb->intr_skb = NULL;
+static void hci_usb_tx_complete(struct urb *urb)
+{
+	struct sk_buff *skb  = (struct sk_buff *) urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+
+	BT_DBG("%s urb %p status %d flags %x", husb->hdev.name, urb,
+			urb->status, urb->transfer_flags);
+
+	if (urb->pipe == usb_sndctrlpipe(husb->udev, 0)) {
+		kfree(urb->setup_packet);
+		clear_bit(HCI_USB_CTRL_TX, &husb->state);
 	}
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return;
+
+	read_lock(&husb->completion_lock);
+	
+	if (!urb->status)
+		husb->hdev.stat.byte_tx += skb->len;
+	else
+		husb->hdev.stat.err_tx++;
+
+	skb_unlink(skb);
+	skb_queue_tail(&husb->completed_q, skb);
+	hci_usb_tx_wakeup(husb);
+	
+	read_unlock(&husb->completion_lock);
+	return;
 }
 
-static void hci_usb_bulk_read(struct urb *urb)
+static void hci_usb_rx_complete(struct urb *urb)
 {
-	struct hci_usb *husb = (struct hci_usb *) urb->context;
-	unsigned char *data = urb->transfer_buffer;
-	int count = urb->actual_length, status;
-	struct sk_buff *skb;
+	struct sk_buff *skb  = (struct sk_buff *) urb->context;
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
+	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
+	int status, count = urb->actual_length;
 	hci_acl_hdr *ah;
-	register __u16 dlen;
+	int dlen, size;
 
-	if (!husb)
+	BT_DBG("%s urb %p status %d count %d flags %x", husb->hdev.name, urb,
+			urb->status, count, urb->transfer_flags);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return;
 
-	DBG("%s status %d, count %d, flags %x", husb->hdev.name, urb->status, count, urb->transfer_flags);
+	read_lock(&husb->completion_lock);
 
-	if (urb->status) {
-		/* Do not re-submit URB on critical errors */
-		switch (urb->status) {
-			case -ENOENT:
-				return;
-			default:
-				goto resubmit;
-		};
-	}
-	if (!count)
+	if (urb->status || !count)
 		goto resubmit;
 
-	DMP(data, count);
+	husb->hdev.stat.byte_rx += count;
 
-	ah = (hci_acl_hdr *) data;
-	dlen = le16_to_cpu(ah->dlen);
+	ah   = (hci_acl_hdr *) skb->data;
+	dlen = __le16_to_cpu(ah->dlen);
+	size = HCI_ACL_HDR_SIZE + dlen;
 
 	/* Verify frame len and completeness */
-	if ((count - HCI_ACL_HDR_SIZE) != dlen) {
-		ERR("%s corrupted ACL packet: count %d, plen %d", husb->hdev.name, count, dlen);
-		goto resubmit;
-	}
-
-	/* Allocate packet */
-	if (!(skb = bluez_skb_alloc(count, GFP_ATOMIC))) {
-		ERR("Can't allocate mem for new packet");
+	if (count != size) {
+		BT_ERR("%s corrupted ACL packet: count %d, dlen %d",
+				husb->hdev.name, count, dlen);
+		bluez_dump("hci_usb", skb->data, count);
+		husb->hdev.stat.err_rx++;
 		goto resubmit;
 	}
 
-	memcpy(skb_put(skb, count), data, count);
-	skb->dev = (void *) &husb->hdev;
-	skb->pkt_type = HCI_ACLDATA_PKT;
-
-	husb->hdev.stat.byte_rx += skb->len;
-
+	skb_unlink(skb);
+	skb_put(skb, count);
 	hci_recv_frame(skb);
 
-resubmit:
-	husb->read_urb->dev = husb->udev;
-	if ((status = usb_submit_urb(husb->read_urb, GFP_KERNEL)))
-		DBG("%s read URB submit failed %d", husb->hdev.name, status);
+	hci_usb_rx_submit(husb, urb);
 
-	DBG("%s read URB re-submited", husb->hdev.name);
+	read_unlock(&husb->completion_lock);
+	return;
+		
+resubmit:
+	urb->dev = husb->udev;
+	status   = usb_submit_urb(urb, GFP_ATOMIC);
+	BT_DBG("%s URB resubmit status %d", husb->hdev.name, status);
+	read_unlock(&husb->completion_lock);
 }
 
-static int hci_usb_ctrl_msg(struct hci_usb *husb, struct sk_buff *skb)
+static void hci_usb_destruct(struct hci_dev *hdev)
 {
-	struct urb *urb = husb->ctrl_urb;
-	struct usb_ctrlrequest *dr  = &husb->dev_req;
-	int pipe, status;
+	struct hci_usb *husb;
 
-	DBG("%s len %d", husb->hdev.name, skb->len);
+	if (!hdev) return;
 
-	pipe = usb_sndctrlpipe(husb->udev, 0);
+	BT_DBG("%s", hdev->name);
 
-	dr->bRequestType = HCI_CTRL_REQ;
-	dr->bRequest = 0;
-	dr->wIndex   = 0;
-	dr->wValue   = 0;
-	dr->wLength  = cpu_to_le16(skb->len);
-
-	FILL_CONTROL_URB(urb, husb->udev, pipe, (void*)dr, skb->data, skb->len,
-	                 hci_usb_ctrl, skb);
-
-	if ((status = usb_submit_urb(urb, GFP_KERNEL))) {
-		DBG("%s control URB submit failed %d", husb->hdev.name, status);
-		return status;
-	}
+	husb = (struct hci_usb *) hdev->driver_data;
+	kfree(husb);
 
-	return 0;
+	MOD_DEC_USE_COUNT;
 }
 
-static int hci_usb_write_msg(struct hci_usb *husb, struct sk_buff *skb)
+#ifdef CONFIG_BLUEZ_USB_FW_LOAD
+
+/* Support for user mode Bluetooth USB firmware loader */
+
+#define FW_LOADER "/sbin/bluefw"
+static int errno;
+
+static int hci_usb_fw_exec(void *dev)
 {
-	struct urb *urb = husb->write_urb;
-	int pipe, status;
+	char *envp[] = { "HOME=/", "TERM=linux", 
+			 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+	char *argv[] = { FW_LOADER, dev, NULL };
+	int err;
 
-	DBG("%s len %d", husb->hdev.name, skb->len);
+	err = exec_usermodehelper(FW_LOADER, argv, envp);
+	if (err)
+		BT_ERR("failed to exec %s %s", FW_LOADER, (char *)dev);
+	return err;
+}
 
-	pipe = usb_sndbulkpipe(husb->udev, husb->bulk_out_ep_addr);
+static int hci_usb_fw_load(struct usb_device *udev)
+{
+	sigset_t tmpsig;
+	char dev[16];
+	pid_t pid;
+	int result;
 
-	FILL_BULK_URB(urb, husb->udev, pipe, skb->data, skb->len,
-	              hci_usb_bulk_write, skb);
-	urb->transfer_flags |= USB_QUEUE_BULK;
+	/* Check if root fs is mounted */
+	if (!current->fs->root) {
+		BT_ERR("root fs not mounted");
+		return -EPERM;
+	}
 
-	if ((status = usb_submit_urb(urb, GFP_KERNEL))) {
-		DBG("%s write URB submit failed %d", husb->hdev.name, status);
-		return status;
+	sprintf(dev, "%3.3d/%3.3d", udev->bus->busnum, udev->devnum);
+
+	pid = kernel_thread(hci_usb_fw_exec, (void *)dev, 0);
+	if (pid < 0) {
+		BT_ERR("fork failed, errno %d\n", -pid);
+		return pid;
 	}
 
+	/* Block signals, everything but SIGKILL/SIGSTOP */
+	spin_lock_irq(&current->sigmask_lock);
+	tmpsig = current->blocked;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+	recalc_sigpending();
+	spin_unlock_irq(&current->sigmask_lock);
+
+	result = waitpid(pid, NULL, __WCLONE);
+
+	/* Allow signals again */
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = tmpsig;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sigmask_lock);
+
+	if (result != pid) {
+		BT_ERR("waitpid failed pid %d errno %d\n", pid, -result);
+		return -result;
+	}
 	return 0;
 }
 
+#endif /* CONFIG_BLUEZ_USB_FW_LOAD */
+
 static void * hci_usb_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
 {
-	struct usb_endpoint_descriptor *bulk_out_ep, *intr_in_ep, *bulk_in_ep;
+	struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *isoc_in_ep[HCI_MAX_IFACE_NUM];
+	struct usb_endpoint_descriptor *intr_in_ep[HCI_MAX_IFACE_NUM];
 	struct usb_interface_descriptor *uif;
 	struct usb_endpoint_descriptor *ep;
+	struct usb_interface *iface, *isoc_iface;
 	struct hci_usb *husb;
 	struct hci_dev *hdev;
-	int i, size, pipe;
-	__u8 * buf;
+	int i, a, e, size, ifn, isoc_ifnum, isoc_alts;
 
-	DBG("udev %p ifnum %d", udev, ifnum);
+	BT_DBG("udev %p ifnum %d", udev, ifnum);
 
-	/* Check device signature */
-	if ((udev->descriptor.bDeviceClass    != HCI_DEV_CLASS)   ||
-	    (udev->descriptor.bDeviceSubClass != HCI_DEV_SUBCLASS)||
-	    (udev->descriptor.bDeviceProtocol != HCI_DEV_PROTOCOL) )
+	/* Check number of endpoints */
+	if (udev->actconfig->interface[ifnum].altsetting[0].bNumEndpoints < 3)
 		return NULL;
 
 	MOD_INC_USE_COUNT;
 
-	uif = &udev->actconfig->interface[ifnum].altsetting[0];
-
-	if (uif->bNumEndpoints != 3) {
-		DBG("Wrong number of endpoints %d", uif->bNumEndpoints);
-		MOD_DEC_USE_COUNT;
-		return NULL;
-	}
-
-	bulk_out_ep = intr_in_ep = bulk_in_ep = NULL;
+#ifdef CONFIG_BLUEZ_USB_FW_LOAD
+	hci_usb_fw_load(udev);
+#endif
 
+	memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
+	memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
+	memset(bulk_in_ep,  0, sizeof(bulk_in_ep));
+	memset(isoc_in_ep,  0, sizeof(isoc_in_ep));
+	memset(intr_in_ep,  0, sizeof(intr_in_ep));
+
+	size = 0; 
+	isoc_iface = NULL;
+	isoc_alts  = isoc_ifnum = 0;
+	
 	/* Find endpoints that we need */
-	for ( i = 0; i < uif->bNumEndpoints; ++i) {
-		ep = &uif->endpoint[i];
 
-		switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
-			case USB_ENDPOINT_XFER_BULK:
-				if (ep->bEndpointAddress & USB_DIR_IN)
-					bulk_in_ep  = ep;
-				else
-					bulk_out_ep = ep;
-				break;
+	ifn = MIN(udev->actconfig->bNumInterfaces, HCI_MAX_IFACE_NUM);
+	for (i = 0; i < ifn; i++) {
+		iface = &udev->actconfig->interface[i];
+		for (a = 0; a < iface->num_altsetting; a++) {
+			uif = &iface->altsetting[a];
+			for (e = 0; e < uif->bNumEndpoints; e++) {
+				ep = &uif->endpoint[e];
+
+				switch (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+				case USB_ENDPOINT_XFER_INT:
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						intr_in_ep[i] = ep;
+					break;
+
+				case USB_ENDPOINT_XFER_BULK:
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						bulk_in_ep[i]  = ep;
+					else
+						bulk_out_ep[i] = ep;
+					break;
+
+				case USB_ENDPOINT_XFER_ISOC:
+					if (ep->wMaxPacketSize < size)
+						break;
+					size = ep->wMaxPacketSize;
+
+					isoc_iface = iface;
+					isoc_alts  = a;
+					isoc_ifnum = i;
+
+					if (ep->bEndpointAddress & USB_DIR_IN)
+						isoc_in_ep[i]  = ep;
+					else
+						isoc_out_ep[i] = ep;
+					break;
+				}
+			}
+		}
+	}
 
-			case USB_ENDPOINT_XFER_INT:
-				intr_in_ep = ep;
-				break;
-		};
+	if (!bulk_in_ep[0] || !bulk_out_ep[0] || !intr_in_ep[0]) {
+		BT_DBG("Bulk endpoints not found");
+		goto done;
 	}
 
-	if (!bulk_in_ep || !bulk_out_ep || !intr_in_ep) {
-		DBG("Endpoints not found: %p %p %p", bulk_in_ep, bulk_out_ep, intr_in_ep);
-		MOD_DEC_USE_COUNT;
-		return NULL;
+	if (!isoc_in_ep[1] || !isoc_out_ep[1]) {
+		BT_DBG("Isoc endpoints not found");
+		isoc_iface = NULL;
 	}
 
 	if (!(husb = kmalloc(sizeof(struct hci_usb), GFP_KERNEL))) {
-		ERR("Can't allocate: control structure");
-		MOD_DEC_USE_COUNT;
-		return NULL;
+		BT_ERR("Can't allocate: control structure");
+		goto done;
 	}
 
 	memset(husb, 0, sizeof(struct hci_usb));
 
 	husb->udev = udev;
-	husb->bulk_out_ep_addr = bulk_out_ep->bEndpointAddress;
-
-	if (!(husb->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL))) {
-		ERR("Can't allocate: control URB");
-		goto probe_error;
-	}
-
-	if (!(husb->write_urb = usb_alloc_urb(0, GFP_KERNEL))) {
-		ERR("Can't allocate: write URB");
-		goto probe_error;
-	}
-
-	if (!(husb->read_urb = usb_alloc_urb(0, GFP_KERNEL))) {
-		ERR("Can't allocate: read URB");
-		goto probe_error;
-	}
-
-	ep = bulk_in_ep;
-	pipe = usb_rcvbulkpipe(udev, ep->bEndpointAddress);
-	size = HCI_MAX_FRAME_SIZE;
-
-	if (!(buf = kmalloc(size, GFP_KERNEL))) {
-		ERR("Can't allocate: read buffer");
-		goto probe_error;
-	}
-
-	FILL_BULK_URB(husb->read_urb, udev, pipe, buf, size, hci_usb_bulk_read, husb);
-	husb->read_urb->transfer_flags |= USB_QUEUE_BULK;
+	husb->bulk_out_ep = bulk_out_ep[0]->bEndpointAddress;
+	husb->bulk_in_ep  = bulk_in_ep[0]->bEndpointAddress;
 
-	ep = intr_in_ep;
-	pipe = usb_rcvintpipe(udev, ep->bEndpointAddress);
-	size = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+	husb->intr_ep = intr_in_ep[0]->bEndpointAddress;
+	husb->intr_interval = intr_in_ep[0]->bInterval;
 
-	if (!(husb->intr_urb = usb_alloc_urb(0, GFP_KERNEL))) {
-		ERR("Can't allocate: interrupt URB");
-		goto probe_error;
-	}
+	if (isoc_iface) {
+		if (usb_set_interface(udev, isoc_ifnum, isoc_alts)) {
+			BT_ERR("Can't set isoc interface settings");
+			isoc_iface = NULL;
+		}
+		usb_driver_claim_interface(&hci_usb_driver, isoc_iface, husb);
+		husb->isoc_iface  = isoc_iface;
 
-	if (!(buf = kmalloc(size, GFP_KERNEL))) {
-		ERR("Can't allocate: interrupt buffer");
-		goto probe_error;
+		husb->isoc_in_ep  = isoc_in_ep[1]->bEndpointAddress;
+		husb->isoc_out_ep = isoc_in_ep[1]->bEndpointAddress;
 	}
 
-	FILL_INT_URB(husb->intr_urb, udev, pipe, buf, size, hci_usb_intr, husb, ep->bInterval);
-
-	skb_queue_head_init(&husb->tx_ctrl_q);
-	skb_queue_head_init(&husb->tx_write_q);
+	husb->completion_lock = RW_LOCK_UNLOCKED;
+	
+	skb_queue_head_init(&husb->acl_q);
+	skb_queue_head_init(&husb->cmd_q);
+	skb_queue_head_init(&husb->pending_q);
+	skb_queue_head_init(&husb->completed_q);
 
 	/* Initialize and register HCI device */
 	hdev = &husb->hdev;
@@ -602,18 +787,20 @@
 	hdev->open  = hci_usb_open;
 	hdev->close = hci_usb_close;
 	hdev->flush = hci_usb_flush;
-	hdev->send	= hci_usb_send_frame;
+	hdev->send  = hci_usb_send_frame;
+	hdev->destruct = hci_usb_destruct;
 
 	if (hci_register_dev(hdev) < 0) {
-		ERR("Can't register HCI device %s", hdev->name);
+		BT_ERR("Can't register HCI device");
 		goto probe_error;
 	}
 
 	return husb;
 
 probe_error:
-	hci_usb_free_bufs(husb);
 	kfree(husb);
+
+done:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
@@ -626,22 +813,18 @@
 	if (!husb)
 		return;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
 
 	hci_usb_close(hdev);
 
-	if (hci_unregister_dev(hdev) < 0) {
-		ERR("Can't unregister HCI device %s", hdev->name);
-	}
+	if (husb->isoc_iface)
+		usb_driver_release_interface(&hci_usb_driver, husb->isoc_iface);
 
-	hci_usb_free_bufs(husb);
-	kfree(husb);
-
-	MOD_DEC_USE_COUNT;
+	if (hci_unregister_dev(hdev) < 0)
+		BT_ERR("Can't unregister HCI device %s", hdev->name);
 }
 
-static struct usb_driver hci_usb_driver =
-{
+static struct usb_driver hci_usb_driver = {
 	name:           "hci_usb",
 	probe:          hci_usb_probe,
 	disconnect:     hci_usb_disconnect,
@@ -652,12 +835,12 @@
 {
 	int err;
 
-	INF("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
+	BT_INFO("BlueZ HCI USB driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
 		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	if ((err = usb_register(&hci_usb_driver)) < 0)
-		ERR("Failed to register HCI USB driver");
+		BT_ERR("Failed to register HCI USB driver");
 
 	return err;
 }
diff -Nru a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_usb.h	Sun May  5 20:39:50 2002
@@ -0,0 +1,79 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_usb.h,v 1.2 2002/03/18 19:10:04 maxk Exp $
+ */
+
+#ifdef __KERNEL__
+
+/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
+#define HCI_DEV_CLASS        0xe0	/* Wireless class */
+#define HCI_DEV_SUBCLASS     0x01	/* RF subclass */
+#define HCI_DEV_PROTOCOL     0x01	/* Bluetooth programming protocol */
+
+#define HCI_CTRL_REQ	     0x20
+
+#define HCI_MAX_IFACE_NUM	3 
+
+#define HCI_MAX_BULK_TX     	4
+#define HCI_MAX_BULK_RX     	1
+
+struct hci_usb {
+	struct hci_dev		hdev;
+
+	unsigned long		state;
+	
+	struct usb_device 	*udev;
+	struct usb_interface    *isoc_iface;
+	
+	__u8			bulk_out_ep;
+	__u8			bulk_in_ep;
+	__u8			isoc_out_ep;
+	__u8			isoc_in_ep;
+
+	__u8			intr_ep;
+	__u8			intr_interval;
+	struct urb *		intr_urb;
+	struct sk_buff *	intr_skb;
+
+	rwlock_t		completion_lock;
+	
+	struct sk_buff_head	cmd_q;	     // TX Commands
+	struct sk_buff_head	acl_q;	     // TX ACLs
+	struct sk_buff_head	pending_q;   // Pending requests
+	struct sk_buff_head	completed_q; // Completed requests
+};
+
+struct hci_usb_scb {
+	struct urb *urb;
+	int    intr_len;
+};
+
+/* States  */
+#define HCI_USB_TX_PROCESS	1
+#define HCI_USB_TX_WAKEUP	2
+#define HCI_USB_CTRL_TX		3
+
+#endif /* __KERNEL__ */
diff -Nru a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
--- a/drivers/bluetooth/hci_vhci.c	Sun May  5 20:39:50 2002
+++ b/drivers/bluetooth/hci_vhci.c	Sun May  5 20:39:50 2002
@@ -25,9 +25,9 @@
 /*
  * BlueZ HCI virtual device driver.
  *
- * $Id: hci_vhci.c,v 1.3 2001/08/03 04:19:50 maxk Exp $ 
+ * $Id: hci_vhci.c,v 1.3 2002/04/17 17:37:20 maxk Exp $ 
  */
-#define VERSION "1.0"
+#define VERSION "1.1"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -49,43 +49,56 @@
 #include <asm/uaccess.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/hci_vhci.h>
+#include "hci_vhci.h"
 
 /* HCI device part */
 
-int hci_vhci_open(struct hci_dev *hdev)
+static int hci_vhci_open(struct hci_dev *hdev)
 {
-	hdev->flags |= HCI_RUNNING;
+	set_bit(HCI_RUNNING, &hdev->flags);
 	return 0;
 }
 
-int hci_vhci_flush(struct hci_dev *hdev)
+static int hci_vhci_flush(struct hci_dev *hdev)
 {
 	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
 	skb_queue_purge(&hci_vhci->readq);
 	return 0;
 }
 
-int hci_vhci_close(struct hci_dev *hdev)
+static int hci_vhci_close(struct hci_dev *hdev)
 {
-	hdev->flags &= ~HCI_RUNNING;
+	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+		return 0;
+
 	hci_vhci_flush(hdev);
 	return 0;
 }
 
-int hci_vhci_send_frame(struct sk_buff *skb)
+static void hci_vhci_destruct(struct hci_dev *hdev)
+{
+	struct hci_vhci_struct *vhci;
+
+	if (!hdev) return;
+
+	vhci = (struct hci_vhci_struct *) hdev->driver_data;
+	kfree(vhci);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static int hci_vhci_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
 	struct hci_vhci_struct *hci_vhci;
 
 	if (!hdev) {
-		ERR("Frame for uknown device (hdev=NULL)");
+		BT_ERR("Frame for uknown device (hdev=NULL)");
 		return -ENODEV;
 	}
 
-	if (!(hdev->flags & HCI_RUNNING))
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
 	hci_vhci = (struct hci_vhci_struct *) hdev->driver_data;
@@ -188,7 +201,7 @@
 
 	add_wait_queue(&hci_vhci->read_wait, &wait);
 	while (count) {
-		current->state = TASK_INTERRUPTIBLE;
+		set_current_state(TASK_INTERRUPTIBLE);
 
 		/* Read frames from device queue */
 		if (!(skb = skb_dequeue(&hci_vhci->readq))) {
@@ -214,13 +227,17 @@
 		kfree_skb(skb);
 		break;
 	}
-
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&hci_vhci->read_wait, &wait);
 
 	return ret;
 }
 
+static loff_t hci_vhci_chr_lseek(struct file * file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
 static int hci_vhci_chr_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	return -EINVAL;
@@ -265,11 +282,13 @@
 	hdev->close = hci_vhci_close;
 	hdev->flush = hci_vhci_flush;
 	hdev->send  = hci_vhci_send_frame;
+	hdev->destruct = hci_vhci_destruct;
 
 	if (hci_register_dev(hdev) < 0) {
 		kfree(hci_vhci);
 		return -EBUSY;
 	}
+	MOD_INC_USE_COUNT;
 
 	file->private_data = hci_vhci;
 	return 0;   
@@ -280,18 +299,16 @@
 	struct hci_vhci_struct *hci_vhci = (struct hci_vhci_struct *) file->private_data;
 
 	if (hci_unregister_dev(&hci_vhci->hdev) < 0) {
-		ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
+		BT_ERR("Can't unregister HCI device %s", hci_vhci->hdev.name);
 	}
 
-	kfree(hci_vhci);
 	file->private_data = NULL;
-
 	return 0;
 }
 
 static struct file_operations hci_vhci_fops = {
 	owner:	THIS_MODULE,	
-	llseek:	no_llseek,
+	llseek:	hci_vhci_chr_lseek,
 	read:	hci_vhci_chr_read,
 	write:	hci_vhci_chr_write,
 	poll:	hci_vhci_chr_poll,
@@ -310,12 +327,12 @@
 
 int __init hci_vhci_init(void)
 {
-	INF("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
+	BT_INFO("BlueZ VHCI driver ver %s Copyright (C) 2000,2001 Qualcomm Inc",  
 		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	if (misc_register(&hci_vhci_miscdev)) {
-		ERR("Can't register misc device %d\n", VHCI_MINOR);
+		BT_ERR("Can't register misc device %d\n", VHCI_MINOR);
 		return -EIO;
 	}
 
@@ -332,4 +349,4 @@
 
 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
 MODULE_DESCRIPTION("BlueZ VHCI driver ver " VERSION);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL"); 
diff -Nru a/drivers/bluetooth/hci_vhci.h b/drivers/bluetooth/hci_vhci.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_vhci.h	Sun May  5 20:39:50 2002
@@ -0,0 +1,50 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_vhci.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifndef __HCI_VHCI_H
+#define __HCI_VHCI_H
+
+#ifdef __KERNEL__
+
+struct hci_vhci_struct {
+	struct hci_dev       hdev;
+	__u32                flags;
+	wait_queue_head_t    read_wait;
+	struct sk_buff_head  readq;
+	struct fasync_struct *fasync;
+};
+
+/* VHCI device flags */
+#define VHCI_FASYNC		0x0010
+
+#endif /* __KERNEL__ */
+
+#define VHCI_DEV	"/dev/vhci"
+#define VHCI_MINOR	250
+
+#endif /* __HCI_VHCI_H */
diff -Nru a/drivers/char/i810-tco.c b/drivers/char/i810-tco.c
--- a/drivers/char/i810-tco.c	Sun May  5 20:39:49 2002
+++ b/drivers/char/i810-tco.c	Sun May  5 20:39:49 2002
@@ -1,5 +1,5 @@
 /*
- *	i810-tco 0.03:	TCO timer driver for i810 chipsets
+ *	i810-tco 0.04:	TCO timer driver for i8xx chipsets
  *
  *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
  *				http://www.kernelconcepts.de
@@ -17,20 +17,25 @@
  *				developed for
  *                              Jentro AG, Haar/Munich (Germany)
  *
- *	TCO timer driver for i810/i815 chipsets
+ *	TCO timer driver for i8xx chipsets
  *	based on softdog.c by Alan Cox <alan@redhat.com>
  *
- *	The TCO timer is implemented in the 82801AA (82801AB) chip,
- *	see intel documentation from http://developer.intel.com,
- *	order number 290655-003
+ *	The TCO timer is implemented in the following I/O controller hubs:
+ *	(See the intel documentation on http://developer.intel.com.)
+ *	82801AA & 82801AB  chip : document number 290655-003, 290677-004,
+ *	82801BA & 82801BAM chip : document number 290687-002, 298242-005,
+ *	82801CA & 82801CAM chip : document number 290716-001, 290718-001
  *
  *  20000710 Nils Faerber
  *	Initial Version 0.01
  *  20000728 Nils Faerber
  *      0.02 Fix for SMI_EN->TCO_EN bit, some cleanups
  *  20011214 Matt Domsch <Matt_Domsch@dell.com>
- *      0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
- *           Didn't add timeout option as i810_margin already exists.
+ *	0.03 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
+ *	     Didn't add timeout option as i810_margin already exists.
+ *  20020224 Joel Becker, Wim Van Sebroeck
+ *	0.04 Support for 82801CA(M) chipset, timer margin needs to be > 3,
+ *	     add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT.
  */
  
 #include <linux/module.h>
@@ -49,13 +54,13 @@
 #include "i810-tco.h"
 
 
-/* Just in case that the PCI vendor and device IDs are not yet defined */
-#ifndef PCI_DEVICE_ID_INTEL_82801AA_0
-#define PCI_DEVICE_ID_INTEL_82801AA_0	0x2410
-#endif
+/* Module and version information */
+#define TCO_VERSION "0.04"
+#define TCO_MODULE_NAME "i810 TCO timer"
+#define TCO_DRIVER_NAME   TCO_MODULE_NAME " , " TCO_VERSION
 
 /* Default expire timeout */
-#define TIMER_MARGIN	50	/* steps of 0.6sec, 2<n<64. Default is 30 seconds */
+#define TIMER_MARGIN	50	/* steps of 0.6sec, 3<n<64. Default is 30 seconds */
 
 static unsigned int ACPIBASE;
 static spinlock_t tco_lock;	/* Guards the hardware */
@@ -63,7 +68,7 @@
 static int i810_margin = TIMER_MARGIN;	/* steps of 0.6sec */
 
 MODULE_PARM (i810_margin, "i");
-MODULE_PARM_DESC(i810_margin, "Watchdog timeout in steps of 0.6sec, 2<n<64. Default = 50 (30 seconds)");
+MODULE_PARM_DESC(i810_margin, "Watchdog timeout in steps of 0.6sec, 3<n<64. Default = 50 (30 seconds)");
 
 
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
@@ -84,7 +89,7 @@
 static int boot_status;
 
 /*
- * Some i810 specific functions
+ * Some TCO specific functions
  */
 
 
@@ -135,7 +140,7 @@
 
 	/* from the specs: */
 	/* "Values of 0h-3h are ignored and should not be attempted" */
-	if (tmrval > 0x3f || tmrval < 0x03)
+	if (tmrval > 0x3f || tmrval < 0x04)
 		return -1;
 	
 	spin_lock(&tco_lock);
@@ -226,8 +231,10 @@
 static int i810tco_ioctl (struct inode *inode, struct file *file,
 			  unsigned int cmd, unsigned long arg)
 {
+	int new_margin, u_margin;
+
 	static struct watchdog_info ident = {
-		0,
+		WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 		0,
 		"i810 TCO timer"
 	};
@@ -247,6 +254,19 @@
 	case WDIOC_KEEPALIVE:
 		tco_timer_reload ();
 		return 0;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(u_margin, (int *) arg))
+			return -EFAULT;
+		new_margin = (u_margin * 10 + 5) / 6;
+		if ((new_margin < 4) || (new_margin > 63))
+			return -EINVAL;
+		if (tco_timer_settimer((unsigned char)new_margin))
+		    return -EINVAL;
+		i810_margin = new_margin;
+		tco_timer_reload();
+		/* Fall */
+	case WDIOC_GETTIMEOUT:
+		return put_user((int)(i810_margin * 6 / 10), (int *) arg);
 	}
 }
 
@@ -263,6 +283,8 @@
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0,	PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,	PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10,	PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,	PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12,	PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0, },
 };
 MODULE_DEVICE_TABLE (pci, i810tco_pci_tbl);
@@ -297,7 +319,7 @@
 		ACPIBASE = badr;
 		/* Something's wrong here, ACPIBASE has to be set */
 		if (badr == 0x0001 || badr == 0x0000) {
-			printk (KERN_ERR "i810tco init: failed to get TCOBASE address\n");
+			printk (KERN_ERR TCO_MODULE_NAME " init: failed to get TCOBASE address\n");
 			return 0;
 		}
 		/*
@@ -309,7 +331,7 @@
 			pci_write_config_byte (i810tco_pci, 0xd4, val1);
 			pci_read_config_byte (i810tco_pci, 0xd4, &val1);
 			if (val1 & 0x02) {
-				printk (KERN_ERR "i810tco init: failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
+				printk (KERN_ERR TCO_MODULE_NAME " init: failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
 				return 0;	/* Cannot reset NO_REBOOT bit */
 			}
 		}
@@ -346,22 +368,22 @@
 	if (!i810tco_getdevice () || i810tco_pci == NULL)
 		return -ENODEV;
 	if (!request_region (TCOBASE, 0x10, "i810 TCO")) {
-		printk (KERN_ERR
-			"i810 TCO timer: I/O address 0x%04x already in use\n",
+		printk (KERN_ERR TCO_MODULE_NAME
+			": I/O address 0x%04x already in use\n",
 			TCOBASE);
 		return -EIO;
 	}
 	if (misc_register (&i810tco_miscdev) != 0) {
 		release_region (TCOBASE, 0x10);
-		printk (KERN_ERR "i810 TCO timer: cannot register miscdev\n");
+		printk (KERN_ERR TCO_MODULE_NAME ": cannot register miscdev\n");
 		return -EIO;
 	}
 	tco_timer_settimer ((unsigned char) i810_margin);
 	tco_timer_reload ();
 
-	printk (KERN_INFO
-		"i810 TCO timer: V0.03, timer margin: %d sec (0x%04x), nowayout: %d\n",
-		(int) (i810_margin * 6 / 10), TCOBASE, nowayout);
+	printk (KERN_INFO TCO_DRIVER_NAME
+		": timer margin: %d sec (0x%04x)\n",
+		(int) (i810_margin * 6 / 10), TCOBASE);
 	return 0;
 }
 
diff -Nru a/drivers/char/i810-tco.h b/drivers/char/i810-tco.h
--- a/drivers/char/i810-tco.h	Sun May  5 20:39:49 2002
+++ b/drivers/char/i810-tco.h	Sun May  5 20:39:49 2002
@@ -1,5 +1,5 @@
 /*
- *	i810-tco 0.02:	TCO timer driver for i810 chipsets
+ *	i810-tco 0.04:	TCO timer driver for i8xx chipsets
  *
  *	(c) Copyright 2000 kernel concepts <nils@kernelconcepts.de>, All Rights Reserved.
  *				http://www.kernelconcepts.de
@@ -17,12 +17,14 @@
  *				developed for
  *                              Jentro AG, Haar/Munich (Germany)
  *
- *	TCO timer driver for i810 chipsets
+ *	TCO timer driver for i8xx chipsets
  *	based on softdog.c by Alan Cox <alan@redhat.com>
  *
- *      The TCO timer is implemented in the 82801AA (82801AB) chip,
- *	see intel documentation from http://developer.intel.com,
- *	order number 290655-003
+ *	The TCO timer is implemented in the following I/O controller hubs:
+ *	(See the intel documentation on http://developer.intel.com.)
+ *	82801AA & 82801AB  chip : document number 290655-003, 290677-004,
+ *	82801BA & 82801BAM chip : document number 290687-002, 298242-005,
+ *	82801CA & 82801CAM chip : document number 290716-001, 290718-001
  *
  *	For history see i810-tco.c
  */
diff -Nru a/drivers/char/i810_rng.c b/drivers/char/i810_rng.c
--- a/drivers/char/i810_rng.c	Sun May  5 20:39:49 2002
+++ b/drivers/char/i810_rng.c	Sun May  5 20:39:49 2002
@@ -35,7 +35,7 @@
 /*
  * core module and version information
  */
-#define RNG_VERSION "0.9.7"
+#define RNG_VERSION "0.9.8"
 #define RNG_MODULE_NAME "i810_rng"
 #define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
 #define PFX RNG_MODULE_NAME ": "
@@ -336,6 +336,7 @@
 	{ 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0, },
 };
 MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
diff -Nru a/drivers/ide/Config.help b/drivers/ide/Config.help
--- a/drivers/ide/Config.help	Sun May  5 20:39:50 2002
+++ b/drivers/ide/Config.help	Sun May  5 20:39:50 2002
@@ -751,6 +751,37 @@
 
   Generally say N here.
 
+CONFIG_BLK_DEV_IDE_TCQ
+  Support for tagged command queueing on ATA disk drives. This enables
+  the IDE layer to have multiple in-flight requests on hardware that
+  supports it. For now this includes the IBM Deskstar series drives,
+  such as the 22GXP, 75GXP, 40GV, 60GXP, and 120GXP (ie any Deskstar made
+  in the last couple of years), and at least some of the Western
+  Digital drives in the Expert series (by nature of really being IBM
+  drives).
+
+  If you have such a drive, say Y here.
+
+CONFIG_BLK_DEV_IDE_TCQ_DEPTH
+  Maximum size of commands to enable per-drive. Any value between 1
+  and 32 is valid, with 32 being the maxium that the hardware supports.
+
+  You probably just want the default of 32 here. If you enter an invalid
+  number, the default value will be used.
+
+CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+  Enabled tagged command queueing unconditionally on drives that report
+  support for it. Regardless of the chosen value here, tagging can be
+  controlled at run time:
+
+  echo "using_tcq:32" > /proc/ide/hdX/settings
+
+  where any value between 1-32 selects chosen queue depth and enables
+  TCQ, and 0 disables it. hdparm version 4.7 an above also support
+  TCQ manipulations.
+
+  Generally say Y here.
+
 CONFIG_BLK_DEV_IT8172
   Say Y here to support the on-board IDE controller on the Integrated
   Technology Express, Inc. ITE8172 SBC.  Vendor page at
diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in
--- a/drivers/ide/Config.in	Sun May  5 20:39:50 2002
+++ b/drivers/ide/Config.in	Sun May  5 20:39:50 2002
@@ -33,20 +33,25 @@
    dep_tristate '  Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE
    dep_tristate '  SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE $CONFIG_SCSI
 
-   comment 'IDE chipset support'
+   comment 'ATA host chipset support'
    dep_bool '  CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86
    dep_bool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
-   dep_bool '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
+   dep_bool '  ISA-PNP support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
    if [ "$CONFIG_PCI" = "y" ]; then
       dep_bool '  RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86
-      bool '  Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+      bool '  PCI host chipset support' CONFIG_BLK_DEV_IDEPCI
       if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
 	 bool '    Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD
-	 bool '    Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
+	 bool '    Sharing PCI ATA interrupts support' CONFIG_IDEPCI_SHARE_IRQ
 	 bool '    Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI
 	 dep_bool '      Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI
          dep_bool '    Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO
 	 define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI
+	 dep_bool '    ATA tagged command queueing (EXPERIMENTAL)' CONFIG_BLK_DEV_IDE_TCQ $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL
+	 dep_bool '      TCQ on by default' CONFIG_BLK_DEV_IDE_TCQ_DEFAULT $CONFIG_BLK_DEV_IDE_TCQ
+	 if [ "$CONFIG_BLK_DEV_IDE_TCQ" != "n" ]; then
+	    int '      Default queue depth' CONFIG_BLK_DEV_IDE_TCQ_DEPTH 32
+	 fi
 	 dep_bool '    Good-Bad DMA Model-Firmware (EXPERIMENTAL)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_EXPERIMENTAL
 	 dep_bool '    AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI
 	 dep_mbool '      AEC62XX Tuning support' CONFIG_AEC62XX_TUNING $CONFIG_BLK_DEV_AEC62XX
@@ -77,11 +82,9 @@
 	 dep_bool '    SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86
 	 dep_bool '    Tekram TRM290 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_TRM290 $CONFIG_BLK_DEV_IDEDMA_PCI
 	 dep_bool '    VIA chipset support' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDEDMA_PCI
+	 dep_bool '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 $CONFIG_BLK_DEV_IDEDMA_PCI
       fi
 
-      if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then
-	 bool '    Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105
-      fi
    fi
    if [ "$CONFIG_ALL_PPC" = "y" ]; then
       bool '    Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile
--- a/drivers/ide/Makefile	Sun May  5 20:39:50 2002
+++ b/drivers/ide/Makefile	Sun May  5 20:39:50 2002
@@ -10,7 +10,7 @@
 
 O_TARGET := idedriver.o
 
-export-objs		:= ide-taskfile.o ide.o ide-features.o ide-probe.o ataraid.o
+export-objs	:= ide-taskfile.o ide.o ide-features.o ide-probe.o ide-dma.o ataraid.o
 
 obj-y		:=
 obj-m		:=
@@ -44,6 +44,7 @@
 ide-obj-$(CONFIG_BLK_DEV_HT6560B)	+= ht6560b.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)	+= icside.o
 ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI)	+= ide-dma.o
+ide-obj-$(CONFIG_BLK_DEV_IDE_TCQ)	+= tcq.o
 ide-obj-$(CONFIG_BLK_DEV_IDEPCI)	+= ide-pci.o
 ide-obj-$(CONFIG_BLK_DEV_ISAPNP)	+= ide-pnp.o
 ide-obj-$(CONFIG_BLK_DEV_IDE_PMAC)	+= ide-pmac.o
diff -Nru a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c
--- a/drivers/ide/aec62xx.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/aec62xx.c	Sun May  5 20:39:49 2002
@@ -310,7 +310,7 @@
 	byte speed		= -1;
 
 	if (drive->type != ATA_DISK)
-		return ide_dma_off_quietly;
+		return 0;
 
 	if (((id->dma_ultra & 0x0010) ||
 	     (id->dma_ultra & 0x0008) ||
@@ -333,17 +333,17 @@
 	} else if (id->dma_1word & 0x0001) {
 		speed = XFER_SW_DMA_0;
 	} else {
- 		return ((int) ide_dma_off_quietly);
- 	}
+		return 0;
+	}
 
 	outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
 	(void) aec6210_tune_chipset(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 11) & 3) ? 0 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
 static int config_aec6260_chipset_for_dma (ide_drive_t *drive, byte ultra)
@@ -356,7 +356,7 @@
 	byte ultra66		= eighty_ninty_three(drive);
 
 	if (drive->type != ATA_DISK)
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	if ((id->dma_ultra & 0x0010) && (ultra) && (ultra66)) {
 		speed = XFER_UDMA_4;
@@ -381,17 +381,17 @@
 	} else if (id->dma_1word & 0x0001) {
 		speed = XFER_SW_DMA_0;
 	} else {
-		return ((int) ide_dma_off_quietly);
+		return 0;
 	}
 
 	outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
 	(void) aec6260_tune_chipset(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 11) & 3) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
 static int config_chipset_for_dma (ide_drive_t *drive, byte ultra)
@@ -403,7 +403,7 @@
 		case PCI_DEVICE_ID_ARTOP_ATP860R:
 			return config_aec6260_chipset_for_dma(drive, ultra);
 		default:
-			return ((int) ide_dma_off_quietly);
+			return 0;
 	}
 }
 
@@ -433,21 +433,23 @@
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 
 	if (id && (id->capability & 1) && drive->channel->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x001F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
+				on = config_chipset_for_dma(drive, 1);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -455,62 +457,38 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, 0);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, 0);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		aec62xx_tune_drive(drive, 5);
 	}
-	return drive->channel->udma(dma_func, drive, NULL);
+	udma_enable(drive, on, verbose);
+	return 0;
 }
 
-/*
- * aec62xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-int aec62xx_dmaproc (ide_dma_action_t func, struct ata_device *drive, struct request *rq)
-{
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_lostirq:
-		case ide_dma_timeout:
-			switch(drive->channel->pci_dev->device) {
-				case PCI_DEVICE_ID_ARTOP_ATP860:
-				case PCI_DEVICE_ID_ARTOP_ATP860R:
-//					{
-//						int i = 0;
-//						byte reg49h = 0;
-//						pci_read_config_byte(drive->channel->pci_dev, 0x49, &reg49h);
-//						for (i=0;i<256;i++)
-//							pci_write_config_byte(drive->channel->pci_dev, 0x49, reg49h|0x10);
-//						pci_write_config_byte(drive->channel->pci_dev, 0x49, reg49h & ~0x10);
-//					}
-//					return 0;
-				default:
-					break;
-			}
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+int aec62xx_dmaproc(struct ata_device *drive)
+{
+	return config_drive_xfer_rate(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
-#endif /* CONFIG_AEC62XX_TUNING */
+#endif
+#endif
 
 unsigned int __init pci_init_aec62xx (struct pci_dev *dev)
 {
@@ -546,7 +524,7 @@
 	hwif->speedproc = aec62xx_tune_chipset;
 # ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base)
-		hwif->udma = aec62xx_dmaproc;
+		hwif->XXX_udma = aec62xx_dmaproc;
 	hwif->highmem = 1;
 # else
 	hwif->drives[0].autotune = 1;
diff -Nru a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c
--- a/drivers/ide/alim15x3.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/alim15x3.c	Sun May  5 20:39:50 2002
@@ -297,7 +297,7 @@
 			pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0);
 		}
 	}
-	
+
 	pci_write_config_byte(dev, port, s_clc);
 	pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc);
 	__restore_flags(flags);
@@ -391,7 +391,7 @@
 	} else if (id->dma_1word & 0x0001) {
 		speed = XFER_SW_DMA_0;
 	} else {
-		return ((int) ide_dma_off_quietly);
+		return 0;
 	}
 
 	(void) ali15x3_tune_chipset(drive, speed);
@@ -399,11 +399,11 @@
 	if (!drive->init_speed)
 		drive->init_speed = speed;
 
-	rval = (int)(	((id->dma_ultra >> 11) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	rval = (int)(	((id->dma_ultra >> 11) & 3) ? 1:
+			((id->dma_ultra >> 8) & 7) ? 1:
+			((id->dma_mword >> 8) & 7) ? 1:
+			((id->dma_1word >> 8) & 7) ? 1:
+						     0);
 
 	return rval;
 }
@@ -433,25 +433,29 @@
 {
 	struct hd_driveid *id = drive->id;
 	struct ata_channel *hwif = drive->channel;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 	byte can_ultra_dma = ali15x3_can_ultra(drive);
 
-	if ((m5229_revision<=0x20) && (drive->type != ATA_DISK))
-		return hwif->udma(ide_dma_off_quietly, drive, NULL);
+	if ((m5229_revision<=0x20) && (drive->type != ATA_DISK)) {
+		udma_enable(drive, 0, 0);
+		return 0;
+	}
 
 	if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) {
 			if (id->dma_ultra & 0x003F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
+				on = config_chipset_for_dma(drive, can_ultra_dma);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -459,45 +463,47 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, can_ultra_dma);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, can_ultra_dma);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, can_ultra_dma);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, can_ultra_dma);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		config_chipset_for_pio(drive);
 	}
-	return hwif->udma(dma_func, drive, NULL);
+
+	udma_enable(drive, on, verbose);
+
+	return 0;
 }
 
-static int ali15x3_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int ali15x3_udma_write(struct ata_device *drive, struct request *rq)
 {
-	switch(func) {
-		case ide_dma_check:
-			return ali15x3_config_drive_for_dma(drive);
-		case ide_dma_write:
-			if ((m5229_revision < 0xC2) && (drive->type != ATA_DISK))
-				return 1;	/* try PIO instead of DMA */
-			break;
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+	if ((m5229_revision < 0xC2) && (drive->type != ATA_DISK))
+		return 1;	/* try PIO instead of DMA */
+
+	return ata_do_udma(0, drive, rq);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+static int ali15x3_dmaproc(struct ata_device *drive)
+{
+	return ali15x3_config_drive_for_dma(drive);
+}
+#endif
 
 unsigned int __init pci_init_ali15x3(struct pci_dev *dev)
 {
@@ -679,7 +685,8 @@
 		/*
 		 * M1543C or newer for DMAing
 		 */
-		hwif->udma = ali15x3_dmaproc;
+		hwif->udma_write = ali15x3_udma_write;
+		hwif->XXX_udma = ali15x3_dmaproc;
 		hwif->autodma = 1;
 	}
 
diff -Nru a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c
--- a/drivers/ide/amd74xx.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/amd74xx.c	Sun May  5 20:39:50 2002
@@ -275,36 +275,23 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-
-/*
- * amd74xx_dmaproc() is a callback from upper layers that can do
- * a lot, but we use it for DMA/PIO tuning only, delegating everything
- * else to the default ide_dmaproc().
- */
-
-int amd74xx_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+int amd74xx_dmaproc(struct ata_device *drive)
 {
+	short w80 = drive->channel->udma_four;
 
-	if (func == ide_dma_check) {
-
-		short w80 = drive->channel->udma_four;
-
-		short speed = ata_timing_mode(drive,
+	short speed = ata_timing_mode(drive,
 			XFER_PIO | XFER_EPIO | XFER_MWDMA | XFER_UDMA |
 			((amd_config->flags & AMD_BAD_SWDMA) ? 0 : XFER_SWDMA) |
 			(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_66 ? XFER_UDMA_66 : 0) |
 			(w80 && (amd_config->flags & AMD_UDMA) >= AMD_UDMA_100 ? XFER_UDMA_100 : 0));
 
-		amd_set_drive(drive, speed);
+	amd_set_drive(drive, speed);
 
-		func = (drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO)
-			? ide_dma_on : ide_dma_off_quietly;
-	}
+	udma_enable(drive, drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO, 0);
 
-	return ide_dmaproc(func, drive, rq);
+	return 0;
 }
-
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * The initialization callback. Here we determine the IDE chip type
@@ -433,7 +420,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->highmem = 1;
-		hwif->udma = amd74xx_dmaproc;
+		hwif->XXX_udma = amd74xx_dmaproc;
 # ifdef CONFIG_IDEDMA_AUTO
 		if (!noautodma)
 			hwif->autodma = 1;
diff -Nru a/drivers/ide/ata-timing.h b/drivers/ide/ata-timing.h
--- a/drivers/ide/ata-timing.h	Sun May  5 20:39:50 2002
+++ b/drivers/ide/ata-timing.h	Sun May  5 20:39:50 2002
@@ -74,12 +74,12 @@
  * It's a bit elaborate due to the legacy we have to bear.
  */
 
-extern short ata_timing_mode(ide_drive_t *drive, int map);
+extern short ata_timing_mode(struct ata_device *drive, int map);
 extern void ata_timing_quantize(struct ata_timing *t, struct ata_timing *q,
 		int T, int UT);
 extern void ata_timing_merge(struct ata_timing *a, struct ata_timing *b,
 		struct ata_timing *m, unsigned int what);
 extern struct ata_timing* ata_timing_data(short speed);
-extern int ata_timing_compute(ide_drive_t *drive,
+extern int ata_timing_compute(struct ata_device *drive,
 		short speed, struct ata_timing *t, int T, int UT);
 #endif
diff -Nru a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
--- a/drivers/ide/cmd64x.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/cmd64x.c	Sun May  5 20:39:49 2002
@@ -208,10 +208,10 @@
  * This routine writes the prepared setup/active/recovery counts
  * for a drive into the cmd646 chipset registers to active them.
  */
-static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count)
+static void program_drive_counts(struct ata_device *drive, int setup_count, int active_count, int recovery_count)
 {
 	unsigned long flags;
-	ide_drive_t *drives = drive->channel->drives;
+	struct ata_device *drives = drive->channel->drives;
 	byte temp_b;
 	static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
 	static const byte recovery_counts[] =
@@ -277,7 +277,7 @@
  * 8: prefetch off, 9: prefetch on, 255: auto-select best mode.
  * Called with 255 at boot time.
  */
-static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted)
+static void cmd64x_tuneproc(struct ata_device *drive, byte mode_wanted)
 {
 	int recovery_time, clock_time;
 	byte recovery_count2, cycle_count;
@@ -351,7 +351,7 @@
 	}
 }
 
-static void cmd680_tuneproc (ide_drive_t *drive, byte mode_wanted)
+static void cmd680_tuneproc(struct ata_device *drive, byte mode_wanted)
 {
 	struct ata_channel *hwif = drive->channel;
 	struct pci_dev *dev	= hwif->pci_dev;
@@ -381,7 +381,7 @@
 	pci_write_config_word(dev, drive_pci, speedt);
 }
 
-static void config_cmd64x_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+static void config_cmd64x_chipset_for_pio(struct ata_device *drive, byte set_speed)
 {
 	byte speed	= 0x00;
 	byte set_pio	= ata_timing_mode(drive, XFER_PIO | XFER_EPIO) - XFER_PIO_0;
@@ -392,7 +392,7 @@
 		(void) ide_config_drive_speed(drive, speed);
 }
 
-static void config_cmd680_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+static void config_cmd680_chipset_for_pio(struct ata_device *drive, byte set_speed)
 {
 	struct ata_channel *hwif = drive->channel;
 	struct pci_dev *dev	= hwif->pci_dev;
@@ -418,7 +418,7 @@
 	}
 }
 
-static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+static void config_chipset_for_pio(struct ata_device *drive, byte set_speed)
 {
         if (drive->channel->pci_dev->device == PCI_DEVICE_ID_CMD_680) {
 		config_cmd680_chipset_for_pio(drive, set_speed);
@@ -427,7 +427,7 @@
 	}
 }
 
-static int cmd64x_tune_chipset (ide_drive_t *drive, byte speed)
+static int cmd64x_tune_chipset(struct ata_device *drive, byte speed)
 {
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	struct ata_channel *hwif = drive->channel;
@@ -496,7 +496,7 @@
 	return err;
 }
 
-static int cmd680_tune_chipset (ide_drive_t *drive, byte speed)
+static int cmd680_tune_chipset(struct ata_device *drive, byte speed)
 {
 	struct ata_channel *hwif = drive->channel;
 	struct pci_dev *dev	= hwif->pci_dev;
@@ -612,7 +612,7 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-static int config_cmd64x_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
+static int config_cmd64x_chipset_for_dma(struct ata_device *drive, unsigned int rev, byte ultra_66)
 {
 	struct hd_driveid *id	= drive->id;
 	struct ata_channel *hwif = drive->channel;
@@ -636,7 +636,7 @@
 
 	if (drive->type != ATA_DISK) {
 		cmdprintk("CMD64X: drive is not a disk at double check, inital check failed!!\n");
-		return ((int) ide_dma_off);
+		return 0;
 	}
 
 	/* UltraDMA only supported on PCI646U and PCI646U2,
@@ -683,21 +683,21 @@
 	config_chipset_for_pio(drive, set_pio);
 
 	if (set_pio)
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	if (cmd64x_tune_chipset(drive, speed))
-		return ((int) ide_dma_off);
+		return 0;
 
-	rval = (int)(	((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	rval = (int)(	((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 
 	return rval;
 }
 
-static int config_cmd680_chipset_for_dma (ide_drive_t *drive)
+static int config_cmd680_chipset_for_dma(struct ata_device *drive)
 {
 	struct hd_driveid *id	= drive->id;
 	byte udma_66		= eighty_ninty_three(drive);
@@ -725,28 +725,28 @@
 	config_chipset_for_pio(drive, set_pio);
 
 	if (set_pio)
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	if (cmd680_tune_chipset(drive, speed))
-		return ((int) ide_dma_off);
+		return 0;
 
-	rval = (int)(	((id->dma_ultra >> 14) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	rval = (int)(	((id->dma_ultra >> 14) & 3) ? 1 :
+			((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 	return rval;
 }
 
-static int config_chipset_for_dma (ide_drive_t *drive, unsigned int rev, byte ultra_66)
+static int config_chipset_for_dma(struct ata_device *drive, unsigned int rev, byte ultra_66)
 {
 	if (drive->channel->pci_dev->device == PCI_DEVICE_ID_CMD_680)
 		return (config_cmd680_chipset_for_dma(drive));
 	return (config_cmd64x_chipset_for_dma(drive, rev, ultra_66));
 }
 
-static int cmd64x_config_drive_for_dma (ide_drive_t *drive)
+static int cmd64x_config_drive_for_dma(struct ata_device *drive)
 {
 	struct hd_driveid *id	= drive->id;
 	struct ata_channel *hwif = drive->channel;
@@ -756,7 +756,8 @@
 	byte can_ultra_66	= 0;
 	byte can_ultra_100	= 0;
 	byte can_ultra_133	= 0;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 
 	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
 	class_rev &= 0xff;
@@ -777,23 +778,26 @@
 			can_ultra_100 = 0;
 			break;
 		default:
-			return hwif->udma(ide_dma_off, drive, NULL);
+			udma_enable(drive, 0, 1);
+
+			return 0;
 	}
 
 	if ((id != NULL) && ((id->capability & 1) != 0) &&
 	    hwif->autodma && (drive->type == ATA_DISK)) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if ((id->field_valid & 4) && (can_ultra_33)) {
 			if (id->dma_ultra & 0x007F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, class_rev, can_ultra_66);
+				on = config_chipset_for_dma(drive, class_rev, can_ultra_66);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -801,161 +805,158 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, class_rev, 0);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, class_rev, 0);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, class_rev, 0);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, class_rev, 0);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		config_chipset_for_pio(drive, 1);
 	}
-	return drive->channel->udma(dma_func, drive, NULL);
+
+	udma_enable(drive, on, verbose);
+
+	return 0;
 }
 
-static int cmd680_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int cmd680_dmaproc(struct ata_device *drive)
 {
-	switch (func) {
-		case ide_dma_check:
-			return cmd64x_config_drive_for_dma(drive);
-		default:
-			break;
-	}
-	/* Other cases are done by generic IDE-DMA code. */
-        return ide_dmaproc(func, drive, rq);
+	return cmd64x_config_drive_for_dma(drive);
 }
 
-static int cmd64x_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int cmd64x_udma_stop(struct ata_device *drive)
 {
-	byte dma_stat		= 0;
-	byte dma_alt_stat	= 0;
-	byte mask		= (drive->channel->unit) ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
-	unsigned long dma_base	= drive->channel->dma_base;
-	struct pci_dev *dev	= drive->channel->pci_dev;
-	byte jack_slap		= ((dev->device == PCI_DEVICE_ID_CMD_648) || (dev->device == PCI_DEVICE_ID_CMD_649)) ? 1 : 0;
-
-	switch (func) {
-		case ide_dma_check:
-			return cmd64x_config_drive_for_dma(drive);
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-			if (jack_slap) {
-				byte dma_intr	= 0;
-				byte dma_mask	= (drive->channel->unit) ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0;
-				byte dma_reg	= (drive->channel->unit) ? ARTTIM2 : CFR;
-				(void) pci_read_config_byte(dev, dma_reg, &dma_intr);
-				/*
-				 * DAMN BMIDE is not connected to PCI space!
-				 * Have to manually jack-slap that bitch!
-				 * To allow the PCI side to read incoming interrupts.
-				 */
-				(void) pci_write_config_byte(dev, dma_reg, dma_intr|dma_mask);	/* clear the INTR bit */
-			}
-			ide_destroy_dmatable(drive);		/* purge DMA mappings */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		case ide_dma_test_irq:	/* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
-			(void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat);
+	struct ata_channel *ch = drive->channel;
+	u8 dma_stat = 0;
+	unsigned long dma_base	= ch->dma_base;
+	struct pci_dev *dev	= ch->pci_dev;
+	u8 jack_slap		= ((dev->device == PCI_DEVICE_ID_CMD_648) || (dev->device == PCI_DEVICE_ID_CMD_649)) ? 1 : 0;
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	if (jack_slap) {
+		byte dma_intr = 0;
+		byte dma_mask = (ch->unit) ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0;
+		byte dma_reg = (ch->unit) ? ARTTIM2 : CFR;
+		(void) pci_read_config_byte(dev, dma_reg, &dma_intr);
+		/*
+		 * DAMN BMIDE is not connected to PCI space!
+		 * Have to manually jack-slap that bitch!
+		 * To allow the PCI side to read incoming interrupts.
+		 */
+		(void) pci_write_config_byte(dev, dma_reg, dma_intr|dma_mask);	/* clear the INTR bit */
+	}
+	udma_destroy_table(ch);	/* purge DMA mappings */
+	return (dma_stat & 7) != 4;		/* verify good DMA status */
+}
+
+static int cmd64x_udma_irq_status(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	u8 dma_stat = 0;
+	u8 dma_alt_stat	= 0;
+	unsigned long dma_base	= ch->dma_base;
+	struct pci_dev *dev	= ch->pci_dev;
+	u8 mask	= (ch->unit) ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
+
+	dma_stat = inb(dma_base+2);
+	(void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat);
 #ifdef DEBUG
-			printk("%s: dma_stat: 0x%02x dma_alt_stat: 0x%02x mask: 0x%02x\n", drive->name, dma_stat, dma_alt_stat, mask);
+	printk("%s: dma_stat: 0x%02x dma_alt_stat: 0x%02x mask: 0x%02x\n", drive->name, dma_stat, dma_alt_stat, mask);
 #endif
-			if (!(dma_alt_stat & mask)) {
-				return 0;
-			}
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-		default:
-			break;
+	if (!(dma_alt_stat & mask)) {
+		return 0;
 	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive, rq);
+	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+}
+
+static int cmd64x_dmaproc(struct ata_device *drive)
+{
+	return cmd64x_config_drive_for_dma(drive);
+}
+
+static int cmd646_1_udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	drive->waiting_for_dma = 0;
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* and free any DMA resources */
+	return (dma_stat & 7) != 4;		/* verify good DMA status */
 }
 
 /*
  * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old
  * event order for DMA transfers.
  */
-static int cmd646_1_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int cmd646_1_dmaproc(struct ata_device *drive)
 {
-	struct ata_channel *hwif = drive->channel;
-	unsigned long dma_base = hwif->dma_base;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_check:
-			return cmd64x_config_drive_for_dma(drive);
-		case ide_dma_end:
-			drive->waiting_for_dma = 0;
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);		/* and free any DMA resources */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		default:
-			break;
-	}
-
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive, rq);
+	return cmd64x_config_drive_for_dma(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
-static int cmd680_busproc (ide_drive_t * drive, int state)
+static int cmd680_busproc(struct ata_device * drive, int state)
 {
 #if 0
-	struct ata_channel *hwif	= drive->channel;
-	u8 addr_mask		= (hwif->unit) ? 0xB0 : 0xA0;
+	struct ata_channel *ch	= drive->channel;
+	u8 addr_mask		= (ch->unit) ? 0xB0 : 0xA0;
 	u32 stat_config		= 0;
 
-        pci_read_config_dword(hwif->pci_dev, addr_mask, &stat_config);
+        pci_read_config_dword(ch->pci_dev, addr_mask, &stat_config);
 
-	if (!hwif)
+	if (!ch)
 		return -EINVAL;
 
 	switch (state) {
 		case BUSSTATE_ON:
-			hwif->drives[0].failures = 0;
-			hwif->drives[1].failures = 0;
+			ch->drives[0].failures = 0;
+			ch->drives[1].failures = 0;
 			break;
 		case BUSSTATE_OFF:
-			hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
-			hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
+			ch->drives[0].failures = ch->drives[0].max_failures + 1;
+			ch->drives[1].failures = ch->drives[1].max_failures + 1;
 			break;
 		case BUSSTATE_TRISTATE:
-			hwif->drives[0].failures = hwif->drives[0].max_failures + 1;
-			hwif->drives[1].failures = hwif->drives[1].max_failures + 1;
+			ch->drives[0].failures = ch->drives[0].max_failures + 1;
+			ch->drives[1].failures = ch->drives[1].max_failures + 1;
 			break;
 		default:
 			return 0;
 	}
-	hwif->bus_state = state;
+	ch->bus_state = state;
 #endif
 	return 0;
 }
 
-static void cmd680_reset (ide_drive_t *drive)
+static void cmd680_reset(struct ata_device *drive)
 {
 #if 0
-	struct ata_channel *hwif = drive->channel;
-	u8 addr_mask		= (hwif->unit) ? 0xB0 : 0xA0;
-	byte reset		= 0;
+	struct ata_channel *ch = drive->channel;
+	u8 addr_mask = (ch->unit) ? 0xB0 : 0xA0;
+	u8 reset = 0;
 
-	pci_read_config_byte(hwif->pci_dev, addr_mask, &reset);
-	pci_write_config_byte(hwif->pci_dev, addr_mask, reset|0x03);
+	pci_read_config_byte(ch->pci_dev, addr_mask, &reset);
+	pci_write_config_byte(ch->pci_dev, addr_mask, reset|0x03);
 #endif
 }
 
@@ -1124,28 +1125,33 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	switch(dev->device) {
 		case PCI_DEVICE_ID_CMD_680:
-			hwif->busproc	= &cmd680_busproc;
-			hwif->udma	= &cmd680_dmaproc;
-			hwif->resetproc = &cmd680_reset;
-			hwif->speedproc	= &cmd680_tune_chipset;
-			hwif->tuneproc	= &cmd680_tuneproc;
+			hwif->busproc	= cmd680_busproc;
+			hwif->XXX_udma	= cmd680_dmaproc;
+			hwif->resetproc = cmd680_reset;
+			hwif->speedproc	= cmd680_tune_chipset;
+			hwif->tuneproc	= cmd680_tuneproc;
 			break;
 		case PCI_DEVICE_ID_CMD_649:
 		case PCI_DEVICE_ID_CMD_648:
 		case PCI_DEVICE_ID_CMD_643:
-			hwif->udma	= &cmd64x_dmaproc;
-			hwif->tuneproc	= &cmd64x_tuneproc;
-			hwif->speedproc = &cmd64x_tune_chipset;
+			hwif->udma_stop	= cmd64x_udma_stop;
+			hwif->udma_irq_status = cmd64x_udma_irq_status;
+			hwif->XXX_udma	= cmd64x_dmaproc;
+			hwif->tuneproc	= cmd64x_tuneproc;
+			hwif->speedproc = cmd64x_tune_chipset;
 			break;
 		case PCI_DEVICE_ID_CMD_646:
 			hwif->chipset = ide_cmd646;
 			if (class_rev == 0x01) {
-				hwif->udma = &cmd646_1_dmaproc;
+				hwif->udma_stop = cmd646_1_udma_stop;
+				hwif->XXX_udma = cmd646_1_dmaproc;
 			} else {
-				hwif->udma = &cmd64x_dmaproc;
+				hwif->udma_stop = cmd64x_udma_stop;
+				hwif->udma_irq_status = cmd64x_udma_irq_status;
+				hwif->XXX_udma = cmd64x_dmaproc;
 			}
-			hwif->tuneproc	= &cmd64x_tuneproc;
-			hwif->speedproc	= &cmd64x_tune_chipset;
+			hwif->tuneproc	= cmd64x_tuneproc;
+			hwif->speedproc	= cmd64x_tune_chipset;
 			break;
 		default:
 			break;
diff -Nru a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c
--- a/drivers/ide/cs5530.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/cs5530.c	Sun May  5 20:39:50 2002
@@ -144,7 +144,7 @@
 	/*
 	 * Default to DMA-off in case we run into trouble here.
 	 */
-	hwif->udma(ide_dma_off_quietly, drive, NULL);
+	udma_enable(drive, 0, 0);
 	outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */
 
 	/*
@@ -158,7 +158,7 @@
 	 */
 	if (mate->present) {
 		struct hd_driveid *mateid = mate->id;
-		if (mateid && (mateid->capability & 1) && !hwif->udma(ide_dma_bad_drive, mate, NULL)) {
+		if (mateid && (mateid->capability & 1) && !udma_black_list(mate)) {
 			if ((mateid->field_valid & 4) && (mateid->dma_ultra & 7))
 				udma_ok = 1;
 			else if ((mateid->field_valid & 2) && (mateid->dma_mword & 7))
@@ -172,7 +172,7 @@
 	 * Now see what the current drive is capable of,
 	 * selecting UDMA only if the mate said it was ok.
 	 */
-	if (id && (id->capability & 1) && hwif->autodma && !hwif->udma(ide_dma_bad_drive, drive, NULL)) {
+	if (id && (id->capability & 1) && hwif->autodma && !udma_black_list(drive)) {
 		if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) {
 			if      (id->dma_ultra & 4)
 				mode = XFER_UDMA_2;
@@ -229,26 +229,16 @@
 	/*
 	 * Finally, turn DMA on in software, and exit.
 	 */
-	return hwif->udma(ide_dma_on, drive, NULL);	/* success */
+	udma_enable(drive, 1, 1);	/* success */
+
+	return 0;
 }
 
-/*
- * This is a CS5530-specific wrapper for the standard ide_dmaproc().
- * We need it for our custom "ide_dma_check" function.
- * All other requests are forwarded to the standard ide_dmaproc().
- */
-int cs5530_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+int cs5530_dmaproc(struct ata_device *drive)
 {
-	switch (func) {
-		case ide_dma_check:
-			return cs5530_config_dma(drive);
-		default:
-			break;
-	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive, rq);
+	return cs5530_config_dma(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * Initialize the cs5530 bridge for reliable IDE DMA operation.
@@ -353,11 +343,11 @@
 		unsigned int basereg, d0_timings;
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-	hwif->udma = cs5530_dmaproc;
+	hwif->XXX_udma = cs5530_dmaproc;
 	hwif->highmem = 1;
 #else
 	hwif->autodma = 0;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 		hwif->tuneproc = &cs5530_tuneproc;
 		basereg = CS5530_BASEREG(hwif);
diff -Nru a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c
--- a/drivers/ide/cy82c693.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/cy82c693.c	Sun May  5 20:39:49 2002
@@ -8,7 +8,7 @@
  *
  * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards.
  * Writting the driver was quite simple, since most of the job is
- * done by the generic pci-ide support. 
+ * done by the generic pci-ide support.
  * The hard part was finding the CY82C693's datasheet on Cypress's
  * web page :-(. But Altavista solved this problem :-).
  *
@@ -17,12 +17,12 @@
  * - I recently got a 16.8G IBM DTTA, so I was able to test it with
  *   a large and fast disk - the results look great, so I'd say the
  *   driver is working fine :-)
- *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA 
- * - this is my first linux driver, so there's probably a lot  of room 
+ *   hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA
+ * - this is my first linux driver, so there's probably a lot  of room
  *   for optimizations and bug fixing, so feel free to do it.
  * - use idebus=xx parameter to set PCI bus speed - needed to calc
  *   timings for PIO modes (default will be 40)
- * - if using PIO mode it's a good idea to set the PIO mode and 
+ * - if using PIO mode it's a good idea to set the PIO mode and
  *   32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda
  * - I had some problems with my IBM DHEA with PIO modes < 2
  *   (lost interrupts) ?????
@@ -71,7 +71,7 @@
  * note: the value for busmaster timeout is tricky and i got it by trial and error !
  *       using a to low value will cause DMA timeouts and drop IDE performance
  *       using a to high value will cause audio playback to scatter
- *       if you know a better value or how to calc it, please let me know 
+ *       if you know a better value or how to calc it, please let me know
  */
 #define BUSMASTER_TIMEOUT	0x50	/* twice the value written in cy82c693ub datasheet */
 /*
@@ -81,12 +81,12 @@
 /* here are the offset definitions for the registers */
 #define CY82_IDE_CMDREG		0x04
 #define CY82_IDE_ADDRSETUP	0x48
-#define CY82_IDE_MASTER_IOR	0x4C	
-#define CY82_IDE_MASTER_IOW	0x4D	
-#define CY82_IDE_SLAVE_IOR	0x4E	
+#define CY82_IDE_MASTER_IOR	0x4C
+#define CY82_IDE_MASTER_IOW	0x4D
+#define CY82_IDE_SLAVE_IOR	0x4E
 #define CY82_IDE_SLAVE_IOW	0x4F
-#define CY82_IDE_MASTER_8BIT	0x50	
-#define CY82_IDE_SLAVE_8BIT	0x51	
+#define CY82_IDE_MASTER_8BIT	0x50
+#define CY82_IDE_SLAVE_8BIT	0x51
 
 #define CY82_INDEX_PORT		0x22
 #define CY82_DATA_PORT		0x23
@@ -188,14 +188,14 @@
 
         if (mode>2)	/* make sure we set a valid mode */
 		mode = 2;
-			   
+
 	if (mode > drive->id->tDMA)  /* to be absolutly sure we have a valid mode */
 		mode = drive->id->tDMA;
-	
+
         index = (drive->channel->unit == 0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1;
 
 #if CY82C693_DEBUG_LOGS
-       	/* for debug let's show the previous values */
+	/* for debug let's show the previous values */
 
 	OUT_BYTE(index, CY82_INDEX_PORT);
 	data = IN_BYTE(CY82_DATA_PORT);
@@ -212,7 +212,7 @@
 	printk (KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", drive->name, drive->channel->unit, drive->select.b.unit, mode, single);
 #endif /* CY82C693_DEBUG_INFO */
 
-	/* 
+	/*
 	 * note: below we set the value for Bus Master IDE TimeOut Register
 	 * I'm not absolutly sure what this does, but it solved my problem
 	 * with IDE DMA and sound, so I now can play sound and work with
@@ -226,45 +226,44 @@
 	OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT);
 	OUT_BYTE(data, CY82_DATA_PORT);
 
-#if CY82C693_DEBUG_INFO	
+#if CY82C693_DEBUG_INFO
 	printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", drive->name, data);
 #endif /* CY82C693_DEBUG_INFO */
 }
 
-/* 
+/*
  * used to set DMA mode for CY82C693 (single and multi modes)
  */
-static int cy82c693_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int cy82c693_dmaproc(struct ata_device *drive)
 {
 	/*
-	 * if the function is dma on, set dma mode for drive everything
-	 * else is done by the defaul func
+	 * Set dma mode for drive everything else is done by the defaul func.
 	 */
-	if (func == ide_dma_on) {
-		struct hd_driveid *id = drive->id;
+	struct hd_driveid *id = drive->id;
 
 #if CY82C693_DEBUG_INFO
-		printk (KERN_INFO "dma_on: %s\n", drive->name);
-#endif /* CY82C693_DEBUG_INFO */
+	printk (KERN_INFO "dma_on: %s\n", drive->name);
+#endif
 
-		if (id != NULL) {
-                       /* Enable DMA on any drive that has DMA (multi or single) enabled */
-                       if (id->field_valid & 2) {       /* regular DMA */
-			       int mmode, smode;
-
-			       mmode = id->dma_mword & (id->dma_mword >> 8);
-			       smode = id->dma_1word & (id->dma_1word >> 8);
-
-		               if (mmode != 0)
-				     cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
-			       else if (smode != 0)
-				     cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
-			}
+	if (id != NULL) {
+		/* Enable DMA on any drive that has DMA (multi or single) enabled */
+		if (id->field_valid & 2) {       /* regular DMA */
+			int mmode, smode;
+
+			mmode = id->dma_mword & (id->dma_mword >> 8);
+			smode = id->dma_1word & (id->dma_1word >> 8);
+
+			if (mmode != 0)
+				cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */
+			else if (smode != 0)
+				cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */
 		}
 	}
-        return ide_dmaproc(func, drive, rq);
+	udma_enable(drive, 1, 1);
+
+	return 0;
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * tune ide drive - set PIO mode
@@ -287,14 +286,14 @@
 
 #if CY82C693_DEBUG_LOGS
 	/* for debug let's show the register values */
-	
-       	if (drive->select.b.unit == 0) {
+
+	if (drive->select.b.unit == 0) {
 		/*
-		 * get master drive registers               	
+		 * get master drive registers
 		 * address setup control register
 		 * is 32 bit !!!
-		 */ 
-	  	pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);                
+		 */
+		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
 		addrCtrl &= 0x0F;
 
 		/* now let's get the remaining registers */
@@ -306,7 +305,7 @@
 		 * set slave drive registers
 		 * address setup control register
 		 * is 32 bit !!!
-		 */ 
+		 */
 		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
 
 		addrCtrl &= 0xF0;
@@ -336,9 +335,9 @@
 		 * set master drive
 		 * address setup control register
 		 * is 32 bit !!!
-		 */ 
+		 */
 		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
-		
+
 		addrCtrl &= (~0xF);
 		addrCtrl |= (unsigned int)pclk.address_time;
 		pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl);
@@ -347,14 +346,14 @@
 		pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r);
 		pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w);
 		pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8);
-		
+
 		addrCtrl &= 0xF;
 	} else {
 		/*
 		 * set slave drive
 		 * address setup control register
 		 * is 32 bit !!!
-		 */ 
+		 */
 		pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl);
 
 		addrCtrl &= (~0xF0);
@@ -368,7 +367,7 @@
 
 		addrCtrl >>= 4;
 		addrCtrl &= 0xF;
-	}	
+	}
 
 #if CY82C693_DEBUG_INFO
 	printk (KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to (addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", drive->name, hwif->unit, drive->select.b.unit, addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8);
@@ -388,14 +387,14 @@
 {
 #ifdef CY82C693_SETDMA_CLOCK
         byte data;
-#endif /* CY82C693_SETDMA_CLOCK */ 
+#endif /* CY82C693_SETDMA_CLOCK */
 
 	/* write info about this verion of the driver */
 	printk (KERN_INFO CY82_VERSION "\n");
 
 #ifdef CY82C693_SETDMA_CLOCK
        /* okay let's set the DMA clock speed */
-        
+
         OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT);
         data = IN_BYTE(CY82_DATA_PORT);
 
@@ -406,11 +405,11 @@
         /*
 	 * for some reason sometimes the DMA controller
 	 * speed is set to ATCLK/2 ???? - we fix this here
-	 * 
+	 *
 	 * note: i don't know what causes this strange behaviour,
 	 *       but even changing the dma speed doesn't solve it :-(
-	 *       the ide performance is still only half the normal speed 
-	 * 
+	 *       the ide performance is still only half the normal speed
+	 *
 	 *       if anybody knows what goes wrong with my machine, please
 	 *       let me know - ASK
          */
@@ -442,7 +441,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->highmem = 1;
-		hwif->udma = cy82c693_dmaproc;
+		hwif->XXX_udma = cy82c693_dmaproc;
 		if (!noautodma)
 			hwif->autodma = 1;
 	}
diff -Nru a/drivers/ide/hpt34x.c b/drivers/ide/hpt34x.c
--- a/drivers/ide/hpt34x.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/hpt34x.c	Sun May  5 20:39:49 2002
@@ -210,7 +210,7 @@
 	byte speed		= 0x00;
 
 	if (drive->type != ATA_DISK)
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	hpt34x_clear_chipset(drive);
 
@@ -237,36 +237,38 @@
 	} else if (id->dma_1word & 0x0001) {
 		speed = XFER_SW_DMA_0;
         } else {
-		return ((int) ide_dma_off_quietly);
+		return 0;
 	}
 
 	(void) hpt34x_tune_chipset(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 11) & 3) ? ide_dma_off :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 11) & 3) ? 0 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
-static int config_drive_xfer_rate (ide_drive_t *drive)
+static int config_drive_xfer_rate(struct ata_device *drive)
 {
 	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 
 	if (id && (id->capability & 1) && drive->channel->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x0007) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
+				on = config_chipset_for_dma(drive, 1);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -274,82 +276,97 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, 0);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, 0);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		config_chipset_for_pio(drive);
 	}
 
 #ifndef CONFIG_HPT34X_AUTODMA
-	if (dma_func == ide_dma_on)
-		dma_func = ide_dma_off;
-#endif /* CONFIG_HPT34X_AUTODMA */
+	if (on)
+		on = 0;
+#endif
+	udma_enable(drive, on, verbose);
 
-	return drive->channel->dmaproc(dma_func, drive);
+	return 0;
+}
+
+static int hpt34x_udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4;		/* verify good DMA status */
+}
+
+static int do_udma(unsigned int reading, struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	unsigned int count;
+
+	if (!(count = udma_new_table(ch, rq)))
+		return 1;	/* try PIO instead of DMA */
+
+	outl(ch->dmatable_dma, dma_base + 4); /* PRD table */
+	reading |= 0x01;
+	outb(reading, dma_base);		/* specify r/w */
+	outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
+	drive->waiting_for_dma = 1;
+
+	if (drive->type != ATA_DISK)
+		return 0;
+
+	ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
+	OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+
+	return 0;
+}
+
+static int hpt34x_udma_read(struct ata_device *drive, struct request *rq)
+{
+	return do_udma(1 << 3, drive, rq);
+}
+
+static int hpt34x_udma_write(struct ata_device *drive, struct request *rq)
+{
+	return do_udma(0, drive, rq);
 }
 
 /*
- * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- *
  * This is specific to the HPT343 UDMA bios-less chipset
  * and HPT345 UDMA bios chipset (stamped HPT363)
  * by HighPoint|Triones Technologies, Inc.
  */
-
-int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+static int hpt34x_dmaproc(struct ata_device *drive)
 {
-	struct ata_channel *hwif = drive->channel;
-	unsigned long dma_base = hwif->dma_base;
-	unsigned int count, reading = 0;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_read:
-			reading = 1 << 3;
-		case ide_dma_write:
-			if (!(count = ide_build_dmatable(drive, func)))
-				return 1;	/* try PIO instead of DMA */
-			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
-			reading |= 0x01;
-			outb(reading, dma_base);		/* specify r/w */
-			outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
-			drive->waiting_for_dma = 1;
-			if (drive->type != ATA_DISK)
-				return 0;
-			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);	/* issue cmd to drive */
-			OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-			return 0;
-		case ide_dma_end:	/* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);		/* purge DMA mappings */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive);	/* use standard DMA stuff */
+	return config_drive_xfer_rate(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * If the BIOS does not set the IO base addaress to XX00, 343 will fail.
@@ -423,7 +440,10 @@
 		else
 			hwif->autodma = 0;
 
-		hwif->dmaproc = &hpt34x_dmaproc;
+		hwif->udma_stop = hpt34x_udma_stop;
+		hwif->udma_read = hpt34x_udma_read;
+		hwif->udma_write = hpt34x_udma_write;
+		hwif->XXX_udma = hpt34x_dmaproc;
 		hwif->highmem = 1;
 	} else {
 		hwif->drives[0].autotune = 1;
diff -Nru a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c
--- a/drivers/ide/hpt366.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/hpt366.c	Sun May  5 20:39:49 2002
@@ -346,8 +346,6 @@
 static unsigned int pci_rev_check_hpt3xx(struct pci_dev *dev);
 static unsigned int pci_rev2_check_hpt3xx(struct pci_dev *dev);
 byte hpt366_proc = 0;
-byte hpt363_shared_irq;
-byte hpt363_shared_pin;
 extern char *ide_xfer_verbose (byte xfer_rate);
 
 #if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS)
@@ -663,7 +661,7 @@
 	int  rval;
 
 	if ((drive->type != ATA_DISK) && (speed < XFER_SW_DMA_0))
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	if ((id->dma_ultra & 0x0020) &&
 	    (!check_in_drive_lists(drive, bad_ata100_5)) &&
@@ -696,15 +694,15 @@
 	} else if (id->dma_mword & 0x0001) {
 		speed = XFER_MW_DMA_0;
 	} else {
-		return ((int) ide_dma_off_quietly);
+		return 0;
 	}
 
 	(void) hpt3xx_tune_chipset(drive, speed);
 
-	rval = (int)(	((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	rval = (int)(	((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+						     0);
 	return rval;
 }
 
@@ -746,146 +744,165 @@
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 
 	if (id && (id->capability & 1) && drive->channel->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x002F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive);
+				on = config_chipset_for_dma(drive);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
 try_dma_modes:
 			if (id->dma_mword & 0x0007) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 
 		config_chipset_for_pio(drive);
 	}
-	return drive->channel->udma(dma_func, drive, NULL);
+	udma_enable(drive, on, verbose);
+
+	return 0;
+}
+
+static void hpt366_udma_irq_lost(struct ata_device *drive)
+{
+	u8 reg50h = 0, reg52h = 0, reg5ah = 0;
+
+	pci_read_config_byte(drive->channel->pci_dev, 0x50, &reg50h);
+	pci_read_config_byte(drive->channel->pci_dev, 0x52, &reg52h);
+	pci_read_config_byte(drive->channel->pci_dev, 0x5a, &reg5ah);
+	printk("%s: (%s)  reg50h=0x%02x, reg52h=0x%02x, reg5ah=0x%02x\n",
+			drive->name, __FUNCTION__, reg50h, reg52h, reg5ah);
+	if (reg5ah & 0x10)
+		pci_write_config_byte(drive->channel->pci_dev, 0x5a, reg5ah & ~0x10);
 }
 
 /*
- * hpt366_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- *
  * This is specific to the HPT366 UDMA bios chipset
  * by HighPoint|Triones Technologies, Inc.
  */
-int hpt366_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int hpt366_dmaproc(struct ata_device *drive)
 {
-	byte reg50h = 0, reg52h = 0, reg5ah = 0, dma_stat = 0;
-	unsigned long dma_base = drive->channel->dma_base;
+	return config_drive_xfer_rate(drive);
+}
 
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_test_irq:	/* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-		case ide_dma_lostirq:
-			pci_read_config_byte(drive->channel->pci_dev, 0x50, &reg50h);
-			pci_read_config_byte(drive->channel->pci_dev, 0x52, &reg52h);
-			pci_read_config_byte(drive->channel->pci_dev, 0x5a, &reg5ah);
-			printk("%s: (ide_dma_lostirq)  reg50h=0x%02x, reg52h=0x%02x, reg5ah=0x%02x\n",
-				drive->name, reg50h, reg52h, reg5ah);
-			if (reg5ah & 0x10)
-				pci_write_config_byte(drive->channel->pci_dev, 0x5a, reg5ah & ~0x10);
-			/* fall through to a reset */
-#if 0
-		case ide_dma_begin:
-		case ide_dma_end:
-			/* reset the chips state over and over.. */
-			pci_write_config_byte(drive->channel->pci_dev, 0x51, 0x13);
-#endif
-			break;
-		case ide_dma_timeout:
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+static void do_udma_start(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+
+	u8 regstate = ch->unit ? 0x54 : 0x50;
+	pci_write_config_byte(ch->pci_dev, regstate, 0x37);
+	udelay(10);
 }
 
-int hpt370_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int hpt370_udma_start(struct ata_device *drive, struct request *__rq)
 {
-	struct ata_channel *hwif = drive->channel;
-	unsigned long dma_base = hwif->dma_base;
-	byte regstate = hwif->unit ? 0x54 : 0x50;
-	byte reginfo = hwif->unit ? 0x56 : 0x52;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_test_irq:	/* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-
-		case ide_dma_end:
-			dma_stat = inb(dma_base + 2);
-			if (dma_stat & 0x01) {
-				udelay(20); /* wait a little */
-				dma_stat = inb(dma_base + 2);
-			}
-			if ((dma_stat & 0x01) == 0) 
-				break;
+	struct ata_channel *ch = drive->channel;
 
-			func = ide_dma_timeout;
-			/* fallthrough */
+	do_udma_start(drive);
 
-		case ide_dma_timeout:
-		case ide_dma_lostirq:
-			pci_read_config_byte(hwif->pci_dev, reginfo, 
-					     &dma_stat); 
-			printk("%s: %d bytes in FIFO\n", drive->name, 
-			       dma_stat);
-			pci_write_config_byte(hwif->pci_dev, regstate, 0x37);
-			udelay(10);
-			dma_stat = inb(dma_base);
-			outb(dma_stat & ~0x1, dma_base); /* stop dma */
-			dma_stat = inb(dma_base + 2); 
-			outb(dma_stat | 0x6, dma_base+2); /* clear errors */
-			/* fallthrough */
+	/* Note that this is done *after* the cmd has been issued to the drive,
+	 * as per the BM-IDE spec.  The Promise Ultra33 doesn't work correctly
+	 * when we do this part before issuing the drive cmd.
+	 */
 
-#ifdef HPT_RESET_STATE_ENGINE
-	        case ide_dma_begin:
-#endif
-			pci_write_config_byte(hwif->pci_dev, regstate, 0x37);
-			udelay(10);
-			break;
+	outb(inb(ch->dma_base) | 1, ch->dma_base);	/* start DMA */
 
-		default:
-			break;
+	return 0;
+}
+
+static void do_timeout_irq(struct ata_device *drive)
+{
+	u8 dma_stat;
+	u8 regstate = drive->channel->unit ? 0x54 : 0x50;
+	u8 reginfo = drive->channel->unit ? 0x56 : 0x52;
+	unsigned long dma_base = drive->channel->dma_base;
+
+	pci_read_config_byte(drive->channel->pci_dev, reginfo, &dma_stat);
+	printk(KERN_INFO "%s: %d bytes in FIFO\n", drive->name, dma_stat);
+	pci_write_config_byte(drive->channel->pci_dev, regstate, 0x37);
+	udelay(10);
+	dma_stat = inb(dma_base);
+	outb(dma_stat & ~0x1, dma_base); /* stop dma */
+	dma_stat = inb(dma_base + 2);
+	outb(dma_stat | 0x6, dma_base+2); /* clear errors */
+
+}
+
+static void hpt370_udma_timeout(struct ata_device *drive)
+{
+	do_timeout_irq(drive);
+	do_udma_start(drive);
+}
+
+static void hpt370_udma_irq_lost(struct ata_device *drive)
+{
+	do_timeout_irq(drive);
+	do_udma_start(drive);
+}
+
+static int hpt370_udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	dma_stat = inb(dma_base + 2);
+	if (dma_stat & 0x01) {
+		udelay(20); /* wait a little */
+		dma_stat = inb(dma_base + 2);
+	}
+	if ((dma_stat & 0x01) != 0) {
+		do_timeout_irq(drive);
+		do_udma_start(drive);
 	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
+}
+
+
+static int hpt370_dmaproc(struct ata_device *drive)
+{
+	return config_drive_xfer_rate(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * Since SUN Cobalt is attempting to do this operation, I should disclose
@@ -1185,9 +1202,14 @@
 			pci_read_config_byte(hwif->pci_dev, 0x5a, &reg5ah);
 			if (reg5ah & 0x10)	/* interrupt force enable */
 				pci_write_config_byte(hwif->pci_dev, 0x5a, reg5ah & ~0x10);
-			hwif->udma = hpt370_dmaproc;
+			hwif->udma_start = hpt370_udma_start;
+			hwif->udma_stop = hpt370_udma_stop;
+			hwif->udma_timeout = hpt370_udma_timeout;
+			hwif->udma_irq_lost = hpt370_udma_irq_lost;
+			hwif->XXX_udma = hpt370_dmaproc;
 		} else {
-			hwif->udma = hpt366_dmaproc;
+			hwif->udma_irq_lost = hpt366_udma_irq_lost;
+			hwif->XXX_udma = hpt366_dmaproc;
 		}
 		if (!noautodma)
 			hwif->autodma = 1;
diff -Nru a/drivers/ide/icside.c b/drivers/ide/icside.c
--- a/drivers/ide/icside.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/icside.c	Sun May  5 20:39:49 2002
@@ -242,9 +242,9 @@
 }
 
 static int
-icside_build_dmatable(ide_drive_t *drive, int reading)
+icside_udma_new_table(struct ata_channel *ch, struct request *rq)
 {
-	return drive->channel->sg_nents = ide_build_sglist(drive->channel, HWGROUP(drive)->rq);
+	return ch->sg_nents = ide_build_sglist(ch, rq);
 }
 
 /* Teardown mappings after DMA has completed.  */
@@ -332,7 +332,7 @@
 	int i;
 	byte stat, dma_stat;
 
-	dma_stat = drive->channel->dmaproc(ide_dma_end, drive);
+	dma_stat = drive->channel->udma(ide_dma_end, drive, rq);
 	stat = GET_STAT();			/* get drive status */
 	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if (!dma_stat) {
@@ -350,7 +350,7 @@
 
 
 static int
-icside_dma_check(ide_drive_t *drive)
+icside_dma_check(struct ata_device *drive, struct request *rq)
 {
 	struct hd_driveid *id = drive->id;
 	struct ata_channel *hwif = drive->channel;
@@ -381,14 +381,7 @@
 out:
 	func = icside_config_if(drive, xfer_mode);
 
-	return hwif->dmaproc(func, drive);
-}
-
-static int
-icside_dma_verbose(ide_drive_t *drive)
-{
-	printk(", DMA");
-	return 1;
+	return hwif->udma(func, drive, rq);
 }
 
 static int
@@ -413,12 +406,12 @@
 		return 0;
 
 	case ide_dma_check:
-		return icside_dma_check(drive);
+		return icside_dma_check(drive, rq);
 
 	case ide_dma_read:
 		reading = 1;
 	case ide_dma_write:
-		count = icside_build_dmatable(drive, reading);
+		count = icside_udma_new_table(hwif, rq);
 		if (!count)
 			return 1;
 		disable_dma(hwif->hw.dma);
@@ -457,9 +450,6 @@
 
 	case ide_dma_test_irq:
 		return inb((unsigned long)hwif->hw.priv) & 1;
-
-	case ide_dma_verbose:
-		return icside_dma_verbose(drive);
 
 	case ide_dma_timeout:
 	default:
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-cd.c	Sun May  5 20:39:49 2002
@@ -737,9 +737,9 @@
 
 	if (info->dma) {
 		if (info->cmd == READ)
-			info->dma = !drive->channel->udma(ide_dma_read, drive, rq);
+			info->dma = !udma_read(drive, rq);
 		else if (info->cmd == WRITE)
-			info->dma = !drive->channel->udma(ide_dma_write, drive, rq);
+			info->dma = !udma_write(drive, rq);
 		else
 			printk("ide-cd: DMA set, but not allowed\n");
 	}
@@ -755,7 +755,7 @@
 		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
 
 	if (info->dma)
-		drive->channel->udma(ide_dma_begin, drive, NULL);
+		udma_start(drive, rq);
 
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		ide_set_handler(drive, handler, WAIT_CMD, cdrom_timer_expiry);
@@ -905,8 +905,8 @@
 	/* Check for errors. */
 	if (dma) {
 		info->dma = 0;
-		if ((dma_error = drive->channel->udma(ide_dma_end, drive, NULL)))
-			drive->channel->udma(ide_dma_off, drive, NULL);
+		if ((dma_error = udma_stop(drive)))
+			udma_enable(drive, 0, 1);
 	}
 
 	if (cdrom_decode_status(&startstop, drive, rq, 0, &stat))
@@ -1480,9 +1480,9 @@
 	/* Check for errors. */
 	if (dma) {
 		info->dma = 0;
-		if ((dma_error = drive->channel->udma(ide_dma_end, drive, NULL))) {
+		if ((dma_error = udma_stop(drive))) {
 			printk("ide-cd: write dma error\n");
-			drive->channel->udma(ide_dma_off, drive, NULL);
+			udma_enable(drive, 0, 1);
 		}
 	}
 
@@ -2652,7 +2652,7 @@
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (drive->using_dma)
-		(void) drive->channel->udma(ide_dma_verbose, drive, NULL);
+		udma_print(drive);
 #endif
 	printk("\n");
 
diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-disk.c	Sun May  5 20:39:49 2002
@@ -1,11 +1,12 @@
-/*
- *  Copyright (C) 1994-1998  Linus Torvalds and authors:
+/***** vi:set ts=8 sts=8 sw=8:************************************************
+ *
+ *  Copyright (C) 1994-1998,2002  Linus Torvalds and authors:
  *
- *	Mark Lord <mlord@pobox.com>
- *	Gadi Oxman <gadio@netvision.net.il>
- *	Andre Hedrick <andre@linux-ide.org>
- *	Jens Axboe <axboe@suse.de>
- *	Marcin Dalecki <dalecki@evision.ag>
+ *	Mark Lord	<mlord@pobox.com>
+ *	Gadi Oxman	<gadio@netvision.net.il>
+ *	Andre Hedrick	<andre@linux-ide.org>
+ *	Jens Axboe	<axboe@suse.de>
+ *	Marcin Dalecki	<martin@dalecki.de>
  *
  * This is the ATA disk device driver, as evolved from hd.c and ide.c.
  */
@@ -88,7 +89,7 @@
 	return 0;	/* lba_capacity value may be bad */
 }
 
-static u8 get_command(ide_drive_t *drive, int cmd)
+static u8 get_command(struct ata_device *drive, int cmd)
 {
 	int lba48bit = (drive->id->cfs_enable_2 & 0x0400) ? 1 : 0;
 
@@ -98,6 +99,8 @@
 
 	if (lba48bit) {
 		if (cmd == READ) {
+			if (drive->using_tcq)
+				return WIN_READDMA_QUEUED_EXT;
 			if (drive->using_dma)
 				return WIN_READDMA_EXT;
 			else if (drive->mult_count)
@@ -105,6 +108,8 @@
 			else
 				return WIN_READ_EXT;
 		} else if (cmd == WRITE) {
+			if (drive->using_tcq)
+				return WIN_WRITEDMA_QUEUED_EXT;
 			if (drive->using_dma)
 				return WIN_WRITEDMA_EXT;
 			else if (drive->mult_count)
@@ -114,6 +119,8 @@
 		}
 	} else {
 		if (cmd == READ) {
+			if (drive->using_tcq)
+				return WIN_READDMA_QUEUED;
 			if (drive->using_dma)
 				return WIN_READDMA;
 			else if (drive->mult_count)
@@ -121,6 +128,8 @@
 			else
 				return WIN_READ;
 		} else if (cmd == WRITE) {
+			if (drive->using_tcq)
+				return WIN_WRITEDMA_QUEUED;
 			if (drive->using_dma)
 				return WIN_WRITEDMA;
 			else if (drive->mult_count)
@@ -148,7 +157,11 @@
 
 	memset(&args, 0, sizeof(args));
 
-	args.taskfile.sector_count = sectors;
+	if (blk_rq_tagged(rq)) {
+		args.taskfile.feature = sectors;
+		args.taskfile.sector_count = rq->tag << 3;
+	} else
+		args.taskfile.sector_count = sectors;
 
 	args.taskfile.sector_number = sect;
 	args.taskfile.low_cylinder = cyl;
@@ -184,7 +197,12 @@
 
 	memset(&args, 0, sizeof(args));
 
-	args.taskfile.sector_count = sectors;
+	if (blk_rq_tagged(rq)) {
+		args.taskfile.feature = sectors;
+		args.taskfile.sector_count = rq->tag << 3;
+	} else
+		args.taskfile.sector_count = sectors;
+
 	args.taskfile.sector_number = block;
 	args.taskfile.low_cylinder = (block >>= 8);
 
@@ -226,8 +244,14 @@
 
 	memset(&args, 0, sizeof(args));
 
-	args.taskfile.sector_count = sectors;
-	args.hobfile.sector_count = sectors >> 8;
+	if (blk_rq_tagged(rq)) {
+		args.taskfile.feature = sectors;
+		args.hobfile.feature = sectors >> 8;
+		args.taskfile.sector_count = rq->tag << 3;
+	} else {
+		args.taskfile.sector_count = sectors;
+		args.hobfile.sector_count = sectors >> 8;
+	}
 
 	args.taskfile.sector_number = block;		/* low lba */
 	args.taskfile.low_cylinder = (block >>= 8);	/* mid lba */
@@ -285,6 +309,30 @@
 		return promise_rw_disk(drive, rq, block);
 	}
 
+	/*
+	 * start a tagged operation
+	 */
+	if (drive->using_tcq) {
+		unsigned long flags;
+		int ret;
+
+		spin_lock_irqsave(&ide_lock, flags);
+
+		ret = blk_queue_start_tag(&drive->queue, rq);
+
+		if (ata_pending_commands(drive) > drive->max_depth)
+			drive->max_depth = ata_pending_commands(drive);
+		if (ata_pending_commands(drive) > drive->max_last_depth)
+			drive->max_last_depth = ata_pending_commands(drive);
+
+		spin_unlock_irqrestore(&ide_lock, flags);
+
+		if (ret) {
+			BUG_ON(!ata_pending_commands(drive));
+			return ide_started;
+		}
+	}
+
 	/* 48-bit LBA */
 	if ((drive->id->cfs_enable_2 & 0x0400) && (drive->addressing))
 		return lba48_do_request(drive, rq, block);
@@ -297,7 +345,7 @@
 	return chs_do_request(drive, rq, block);
 }
 
-static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+static int idedisk_open (struct inode *inode, struct file *filp, struct ata_device *drive)
 {
 	MOD_INC_USE_COUNT;
 	if (drive->removable && drive->usage == 1) {
@@ -322,7 +370,7 @@
 	return 0;
 }
 
-static int idedisk_flushcache(ide_drive_t *drive)
+static int idedisk_flushcache(struct ata_device *drive)
 {
 	struct ata_taskfile args;
 
@@ -338,7 +386,7 @@
 	return ide_raw_taskfile(drive, &args, NULL);
 }
 
-static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+static void idedisk_release (struct inode *inode, struct file *filp, struct ata_device *drive)
 {
 	if (drive->removable && !drive->usage) {
 		struct ata_taskfile args;
@@ -360,7 +408,7 @@
 	MOD_DEC_USE_COUNT;
 }
 
-static int idedisk_check_media_change (ide_drive_t *drive)
+static int idedisk_check_media_change(struct ata_device *drive)
 {
 	/* if removable, always assume it was changed */
 	return drive->removable;
@@ -426,7 +474,7 @@
 	return IS_PDC4030_DRIVE ? ide_stopped : ide_started;
 }
 
-static void idedisk_pre_reset (ide_drive_t *drive)
+static void idedisk_pre_reset(struct ata_device *drive)
 {
 	int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1;
 
@@ -442,7 +490,7 @@
 
 #ifdef CONFIG_PROC_FS
 
-static int smart_enable(ide_drive_t *drive)
+static int smart_enable(struct ata_device *drive)
 {
 	struct ata_taskfile args;
 
@@ -456,7 +504,7 @@
 	return ide_raw_taskfile(drive, &args, NULL);
 }
 
-static int get_smart_values(ide_drive_t *drive, u8 *buf)
+static int get_smart_values(struct ata_device *drive, u8 *buf)
 {
 	struct ata_taskfile args;
 
@@ -473,7 +521,7 @@
 	return ide_raw_taskfile(drive, &args, buf);
 }
 
-static int get_smart_thresholds(ide_drive_t *drive, u8 *buf)
+static int get_smart_thresholds(struct ata_device *drive, u8 *buf)
 {
 	struct ata_taskfile args;
 
@@ -493,7 +541,7 @@
 static int proc_idedisk_read_cache
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
-	ide_drive_t	*drive = (ide_drive_t *) data;
+	struct ata_device *drive = (struct ata_device *) data;
 	char		*out = page;
 	int		len;
 
@@ -507,7 +555,7 @@
 static int proc_idedisk_read_smart_thresholds
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
-	ide_drive_t	*drive = (ide_drive_t *)data;
+	struct ata_device *drive = (struct ata_device *)data;
 	int		len = 0, i = 0;
 
 	if (!get_smart_thresholds(drive, page)) {
@@ -526,8 +574,8 @@
 static int proc_idedisk_read_smart_values
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
-	ide_drive_t	*drive = (ide_drive_t *)data;
-	int		len = 0, i = 0;
+	struct ata_device *drive = (struct ata_device *)data;
+	int len = 0, i = 0;
 
 	if (!get_smart_values(drive, page)) {
 		unsigned short *val = (unsigned short *) page;
@@ -542,11 +590,61 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+static int proc_idedisk_read_tcq
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct ata_device *drive = (struct ata_device *) data;
+	char		*out = page;
+	int		len, cmds, i;
+	unsigned long	flags;
+
+	if (!blk_queue_tagged(&drive->queue)) {
+		len = sprintf(out, "not configured\n");
+		PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+	}
+
+	spin_lock_irqsave(&ide_lock, flags);
+
+	len = sprintf(out, "TCQ currently on:\t%s\n", drive->using_tcq ? "yes" : "no");
+	len += sprintf(out+len, "Max queue depth:\t%d\n",drive->queue_depth);
+	len += sprintf(out+len, "Max achieved depth:\t%d\n",drive->max_depth);
+	len += sprintf(out+len, "Max depth since last:\t%d\n",drive->max_last_depth);
+	len += sprintf(out+len, "Current depth:\t\t%d\n", ata_pending_commands(drive));
+	len += sprintf(out+len, "Active tags:\t\t[ ");
+	for (i = 0, cmds = 0; i < drive->queue_depth; i++) {
+		struct request *rq = blk_queue_tag_request(&drive->queue, i);
+
+		if (!rq)
+			continue;
+
+		len += sprintf(out+len, "%d, ", i);
+		cmds++;
+	}
+	len += sprintf(out+len, "]\n");
+
+	len += sprintf(out+len, "Queue:\t\t\treleased [ %lu ] - started [ %lu ]\n", drive->immed_rel, drive->immed_comp);
+
+	if (ata_pending_commands(drive) != cmds)
+		len += sprintf(out+len, "pending request and queue count mismatch (counted: %d)\n", cmds);
+
+	len += sprintf(out+len, "DMA status:\t\t%srunning\n", test_bit(IDE_DMA, &HWGROUP(drive)->flags) ? "" : "not ");
+
+	drive->max_last_depth = 0;
+
+	spin_unlock_irqrestore(&ide_lock, flags);
+	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
+}
+#endif
+
 static ide_proc_entry_t idedisk_proc[] = {
 	{ "cache",		S_IFREG|S_IRUGO,	proc_idedisk_read_cache,		NULL },
 	{ "geometry",		S_IFREG|S_IRUGO,	proc_ide_read_geometry,			NULL },
 	{ "smart_values",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_values,		NULL },
 	{ "smart_thresholds",	S_IFREG|S_IRUSR,	proc_idedisk_read_smart_thresholds,	NULL },
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+	{ "tcq",		S_IFREG|S_IRUSR,	proc_idedisk_read_tcq,			NULL },
+#endif
 	{ NULL, 0, NULL, NULL }
 };
 
@@ -560,7 +658,7 @@
  * This is tightly woven into the driver->special can not touch.
  * DON'T do it again until a total personality rewrite is committed.
  */
-static int set_multcount(ide_drive_t *drive, int arg)
+static int set_multcount(struct ata_device *drive, int arg)
 {
 	struct request rq;
 
@@ -572,12 +670,12 @@
 	drive->mult_req = arg;
 	drive->special_cmd |= ATA_SPECIAL_MMODE;
 
-	ide_do_drive_cmd (drive, &rq, ide_wait);
+	ide_do_drive_cmd(drive, &rq, ide_wait);
 
 	return (drive->mult_count == arg) ? 0 : -EIO;
 }
 
-static int set_nowerr(ide_drive_t *drive, int arg)
+static int set_nowerr(struct ata_device *drive, int arg)
 {
 	if (ide_spin_wait_hwgroup(drive))
 		return -EBUSY;
@@ -588,7 +686,7 @@
 	return 0;
 }
 
-static int write_cache(ide_drive_t *drive, int arg)
+static int write_cache(struct ata_device *drive, int arg)
 {
 	struct ata_taskfile args;
 
@@ -606,7 +704,7 @@
 	return 0;
 }
 
-static int idedisk_standby(ide_drive_t *drive)
+static int idedisk_standby(struct ata_device *drive)
 {
 	struct ata_taskfile args;
 
@@ -617,7 +715,7 @@
 	return ide_raw_taskfile(drive, &args, NULL);
 }
 
-static int set_acoustic(ide_drive_t *drive, int arg)
+static int set_acoustic(struct ata_device *drive, int arg)
 {
 	struct ata_taskfile args;
 
@@ -633,7 +731,33 @@
 	return 0;
 }
 
-static int probe_lba_addressing (ide_drive_t *drive, int arg)
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+static int set_using_tcq(struct ata_device *drive, int arg)
+{
+	if (!drive->driver)
+		return -EPERM;
+	if (!drive->channel->XXX_udma)
+		return -EPERM;
+	if (arg == drive->queue_depth && drive->using_tcq)
+		return 0;
+
+	/*
+	 * set depth, but check also id for max supported depth
+	 */
+	drive->queue_depth = arg ? arg : 1;
+	if (drive->id) {
+		if (drive->queue_depth > drive->id->queue_depth + 1)
+			drive->queue_depth = drive->id->queue_depth + 1;
+	}
+
+	if (udma_tcq_enable(drive, arg))
+		return -EIO;
+
+	return 0;
+}
+#endif
+
+static int probe_lba_addressing(struct ata_device *drive, int arg)
 {
 	drive->addressing =  0;
 
@@ -644,12 +768,12 @@
 	return 0;
 }
 
-static int set_lba_addressing (ide_drive_t *drive, int arg)
+static int set_lba_addressing(struct ata_device *drive, int arg)
 {
 	return (probe_lba_addressing(drive, arg));
 }
 
-static void idedisk_add_settings(ide_drive_t *drive)
+static void idedisk_add_settings(struct ata_device *drive)
 {
 	struct hd_driveid *id = drive->id;
 
@@ -664,11 +788,14 @@
 	ide_add_setting(drive,	"acoustic",		SETTING_RW,					HDIO_GET_ACOUSTIC,	HDIO_SET_ACOUSTIC,	TYPE_BYTE,	0,	254,				1,	1,	&drive->acoustic,		set_acoustic);
 	ide_add_setting(drive,	"failures",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->failures,		NULL);
 	ide_add_setting(drive,	"max_failures",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->max_failures,		NULL);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+	ide_add_setting(drive,	"using_tcq",		SETTING_RW,					HDIO_GET_QDMA,		HDIO_SET_QDMA,		TYPE_BYTE,	0,	IDE_MAX_TAG,			1,		1,		&drive->using_tcq,		set_using_tcq);
+#endif
 }
 
 static int idedisk_suspend(struct device *dev, u32 state, u32 level)
 {
-	ide_drive_t *drive = dev->driver_data;
+	struct ata_device *drive = dev->driver_data;
 
 	/* I hope that every freeze operations from the upper levels have
 	 * already been done...
@@ -695,7 +822,7 @@
 
 static int idedisk_resume(struct device *dev, u32 level)
 {
-	ide_drive_t *drive = dev->driver_data;
+	struct ata_device *drive = dev->driver_data;
 
 	if (level != RESUME_RESTORE_STATE)
 		return 0;
@@ -789,7 +916,7 @@
  * Sets maximum virtual LBA address of the drive.
  * Returns new maximum virtual LBA address (> 0) or 0 on failure.
  */
-static sector_t set_max_address(ide_drive_t *drive, sector_t addr_req)
+static sector_t set_max_address(struct ata_device *drive, sector_t addr_req)
 {
 	struct ata_taskfile args;
 	sector_t addr_set = 0;
@@ -818,7 +945,7 @@
 	return addr_set;
 }
 
-static u64 set_max_address_ext(ide_drive_t *drive, u64 addr_req)
+static u64 set_max_address_ext(struct ata_device *drive, u64 addr_req)
 {
 	struct ata_taskfile args;
 	u64 addr_set = 0;
@@ -1050,7 +1177,7 @@
 	       drive->bios_cyl, drive->bios_head, drive->bios_sect);
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (drive->using_dma)
-		(void) drive->channel->udma(ide_dma_verbose, drive, NULL);
+		udma_print(drive);
 #endif
 	printk("\n");
 
@@ -1086,7 +1213,7 @@
 	probe_lba_addressing(drive, 1);
 }
 
-static int idedisk_cleanup(ide_drive_t *drive)
+static int idedisk_cleanup(struct ata_device *drive)
 {
 	if (!drive)
 	    return 0;
@@ -1123,7 +1250,7 @@
 
 static void __exit idedisk_exit (void)
 {
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	int failed = 0;
 
 	while ((drive = ide_scan_devices(ATA_DISK, "ide-disk", &idedisk_driver, failed)) != NULL) {
@@ -1140,9 +1267,9 @@
 	}
 }
 
-int idedisk_init (void)
+int idedisk_init(void)
 {
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	int failed = 0;
 
 	MOD_INC_USE_COUNT;
diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
--- a/drivers/ide/ide-dma.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-dma.c	Sun May  5 20:39:49 2002
@@ -1,4 +1,5 @@
-/*
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
  *  Copyright (c) 1999-2000  Andre Hedrick <andre@linux-ide.org>
  *  Copyright (c) 1995-1998  Mark Lord
  *
@@ -63,12 +64,12 @@
  *
  * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports.
  *
- * check_drive_lists(ide_drive_t *drive, int good_bad)
- *
  * ATA-66/100 and recovery functions, I forgot the rest......
  */
 
 #include <linux/config.h>
+#define __NO_VERSION__
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
@@ -192,19 +193,19 @@
 #endif
 
 /*
- * dma_intr() is the handler for disk read/write DMA interrupts
+ * This is the handler for disk read/write DMA interrupts.
  */
 ide_startstop_t ide_dma_intr(struct ata_device *drive, struct request *rq)
 {
-	byte stat, dma_stat;
+	u8 stat, dma_stat;
 
-	dma_stat = drive->channel->udma(ide_dma_end, drive, rq);
+	dma_stat = udma_stop(drive);
 	if (OK_STAT(stat = GET_STAT(),DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if (!dma_stat) {
 			__ide_end_request(drive, rq, 1, rq->nr_sectors);
 			return ide_stopped;
 		}
-		printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n",
+		printk(KERN_ERR "%s: dma_intr: bad DMA status (dma_stat=%x)\n",
 		       drive->name, dma_stat);
 	}
 	return ide_error(drive, "dma_intr", stat);
@@ -270,106 +271,11 @@
 }
 
 /*
- * This prepares a dma request.  Returns 0 if all went okay, returns 1
- * otherwise.  May also be invoked from trm290.c
- */
-int ide_build_dmatable(struct ata_device *drive, ide_dma_action_t func)
-{
-	struct ata_channel *ch = drive->channel;
-	unsigned int *table = ch->dmatable_cpu;
-#ifdef CONFIG_BLK_DEV_TRM290
-	unsigned int is_trm290_chipset = (ch->chipset == ide_trm290);
-#else
-	const int is_trm290_chipset = 0;
-#endif
-	unsigned int count = 0;
-	int i;
-	struct scatterlist *sg;
-
-	ch->sg_nents = i = build_sglist(ch, HWGROUP(drive)->rq);
-	if (!i)
-		return 0;
-
-	sg = ch->sg_table;
-	while (i) {
-		u32 cur_addr;
-		u32 cur_len;
-
-		cur_addr = sg_dma_address(sg);
-		cur_len = sg_dma_len(sg);
-
-		/*
-		 * Fill in the dma table, without crossing any 64kB boundaries.
-		 * Most hardware requires 16-bit alignment of all blocks,
-		 * but the trm290 requires 32-bit alignment.
-		 */
-
-		while (cur_len) {
-			u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
-
-			if (count++ >= PRD_ENTRIES) {
-				printk("ide-dma: count %d, sg_nents %d, cur_len %d, cur_addr %u\n",
-						count, ch->sg_nents, cur_len, cur_addr);
-				BUG();
-			}
-
-			if (bcount > cur_len)
-				bcount = cur_len;
-			*table++ = cpu_to_le32(cur_addr);
-			xcount = bcount & 0xffff;
-			if (is_trm290_chipset)
-				xcount = ((xcount >> 2) - 1) << 16;
-			if (xcount == 0x0000) {
-		        /*
-			 * Most chipsets correctly interpret a length of
-			 * 0x0000 as 64KB, but at least one (e.g. CS5530)
-			 * misinterprets it as zero (!). So here we break
-			 * the 64KB entry into two 32KB entries instead.
-			 */
-				if (count++ >= PRD_ENTRIES) {
-					pci_unmap_sg(ch->pci_dev, sg,
-						     ch->sg_nents,
-						     ch->sg_dma_direction);
-					return 0;
-				}
-
-				*table++ = cpu_to_le32(0x8000);
-				*table++ = cpu_to_le32(cur_addr + 0x8000);
-				xcount = 0x8000;
-			}
-			*table++ = cpu_to_le32(xcount);
-			cur_addr += bcount;
-			cur_len -= bcount;
-		}
-
-		sg++;
-		i--;
-	}
-
-	if (!count)
-		printk("%s: empty DMA table?\n", drive->name);
-	else if (!is_trm290_chipset)
-		*--table |= cpu_to_le32(0x80000000);
-
-	return count;
-}
-
-/* Teardown mappings after DMA has completed.  */
-void ide_destroy_dmatable (ide_drive_t *drive)
-{
-	struct pci_dev *dev = drive->channel->pci_dev;
-	struct scatterlist *sg = drive->channel->sg_table;
-	int nents = drive->channel->sg_nents;
-
-	pci_unmap_sg(dev, sg, nents, drive->channel->sg_dma_direction);
-}
-
-/*
  *  For both Blacklisted and Whitelisted drives.
  *  This is setup to be called as an extern for future support
  *  to other special driver code.
  */
-int check_drive_lists (ide_drive_t *drive, int good_bad)
+int check_drive_lists(struct ata_device *drive, int good_bad)
 {
 	struct hd_driveid *id = drive->id;
 
@@ -382,7 +288,7 @@
 			printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model);
 		return(blacklist);
 	}
-#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+#else
 	const char **list;
 
 	if (good_bad) {
@@ -403,84 +309,67 @@
 			}
 		}
 	}
-#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */
+#endif
 	return 0;
 }
 
-int report_drive_dmaing (ide_drive_t *drive)
-{
-	struct hd_driveid *id = drive->id;
-
-	if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
-	    (id->dma_ultra & (id->dma_ultra >> 14) & 3)) {
-		if ((id->dma_ultra >> 15) & 1) {
-			printk(", UDMA(mode 7)");	/* UDMA BIOS-enabled! */
-		} else {
-			printk(", UDMA(133)");	/* UDMA BIOS-enabled! */
-		}
-	} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
-	  	  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
-		if ((id->dma_ultra >> 13) & 1) {
-			printk(", UDMA(100)");	/* UDMA BIOS-enabled! */
-		} else if ((id->dma_ultra >> 12) & 1) {
-			printk(", UDMA(66)");	/* UDMA BIOS-enabled! */
-		} else {
-			printk(", UDMA(44)");	/* UDMA BIOS-enabled! */
-		}
-	} else if ((id->field_valid & 4) &&
-		   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
-		if ((id->dma_ultra >> 10) & 1) {
-			printk(", UDMA(33)");	/* UDMA BIOS-enabled! */
-		} else if ((id->dma_ultra >> 9) & 1) {
-			printk(", UDMA(25)");	/* UDMA BIOS-enabled! */
-		} else {
-			printk(", UDMA(16)");	/* UDMA BIOS-enabled! */
-		}
-	} else if (id->field_valid & 4) {
-		printk(", (U)DMA");	/* Can be BIOS-enabled! */
-	} else {
-		printk(", DMA");
-	}
-	return 1;
-}
-
-static int config_drive_for_dma (ide_drive_t *drive)
+static int config_drive_for_dma(struct ata_device *drive)
 {
 	int config_allows_dma = 1;
 	struct hd_driveid *id = drive->id;
-	struct ata_channel *hwif = drive->channel;
+	struct ata_channel *ch = drive->channel;
 
 #ifdef CONFIG_IDEDMA_ONLYDISK
 	if (drive->type != ATA_DISK)
 		config_allows_dma = 0;
 #endif
 
-	if (id && (id->capability & 1) && hwif->autodma && config_allows_dma) {
+	if (id && (id->capability & 1) && ch->autodma && config_allows_dma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL))
-			return hwif->udma(ide_dma_off, drive, NULL);
+		if (udma_black_list(drive)) {
+			udma_enable(drive, 0, 1);
+
+			return 0;
+		}
 
 		/* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */
 		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
-			if ((id->dma_ultra & (id->dma_ultra >> 14) & 2))
-				return hwif->udma(ide_dma_on, drive, NULL);
+			if ((id->dma_ultra & (id->dma_ultra >> 14) & 2)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
 		/* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */
 		if ((id->field_valid & 4) && (eighty_ninty_three(drive)))
-			if ((id->dma_ultra & (id->dma_ultra >> 11) & 7))
-				return hwif->udma(ide_dma_on, drive, NULL);
+			if ((id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
 		/* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */
 		if (id->field_valid & 4)	/* UltraDMA */
-			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7))
-				return hwif->udma(ide_dma_on, drive, NULL);
+			if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
 		/* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */
 		if (id->field_valid & 2)	/* regular DMA */
-			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404)
-				return hwif->udma(ide_dma_on, drive, NULL);
+			if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) {
+				udma_enable(drive, 1, 1);
+
+				return 0;
+			}
 		/* Consult the list of known "good" drives */
-		if (ide_dmaproc(ide_dma_good_drive, drive, NULL))
-			return hwif->udma(ide_dma_on, drive, NULL);
+		if (udma_white_list(drive)) {
+			udma_enable(drive, 1, 1);
+
+			return 0;
+		}
 	}
-	return hwif->udma(ide_dma_off_quietly, drive, NULL);
+	udma_enable(drive, 0, 0);
+
+	return 0;
 }
 
 /*
@@ -508,18 +397,24 @@
 	return 0;
 }
 
-static void ide_toggle_bounce(ide_drive_t *drive, int on)
+int ata_start_dma(struct ata_device *drive, struct request *rq)
 {
-	u64 addr = BLK_BOUNCE_HIGH;
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	unsigned int reading = 0;
 
-	if (on && drive->type == ATA_DISK && drive->channel->highmem) {
-		if (!PCI_DMA_BUS_IS_PHYS)
-			addr = BLK_BOUNCE_ANY;
-		else
-			addr = drive->channel->pci_dev->dma_mask;
-	}
+	if (rq_data_dir(rq) == READ)
+		reading = 1 << 3;
 
-	blk_queue_bounce_limit(&drive->queue, addr);
+	/* try PIO instead of DMA */
+	if (!udma_new_table(ch, rq))
+		return 1;
+
+	outl(ch->dmatable_dma, dma_base + 4); /* PRD table */
+	outb(reading, dma_base);		/* specify r/w */
+	outb(inb(dma_base+2)|6, dma_base+2);	/* clear INTR & ERROR flags */
+	drive->waiting_for_dma = 1;
+	return 0;
 }
 
 /*
@@ -531,162 +426,76 @@
  *
  * For ATAPI devices, we just prepare for DMA and return. The caller should
  * then issue the packet command to the drive and call us again with
- * ide_dma_begin afterwards.
+ * udma_start afterwards.
  *
  * Returns 0 if all went well.
  * Returns 1 if DMA read/write could not be started, in which case
  * the caller should revert to PIO for the current request.
  * May also be invoked from trm290.c
  */
-int ide_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+int XXX_ide_dmaproc(struct ata_device *drive)
 {
-	struct ata_channel *hwif = drive->channel;
-	unsigned long dma_base = hwif->dma_base;
-	byte unit = (drive->select.b.unit & 0x01);
-	unsigned int count, reading = 0, set_high = 1;
-	byte dma_stat;
-
-	switch (func) {
-		case ide_dma_off:
-			printk("%s: DMA disabled\n", drive->name);
-		case ide_dma_off_quietly:
-			set_high = 0;
-			outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
-		case ide_dma_on:
-			ide_toggle_bounce(drive, set_high);
-			drive->using_dma = (func == ide_dma_on);
-			if (drive->using_dma)
-				outb(inb(dma_base+2)|(1<<(5+unit)), dma_base+2);
-			return 0;
-		case ide_dma_check:
-			return config_drive_for_dma (drive);
-		case ide_dma_read:
-			reading = 1 << 3;
-		case ide_dma_write:
-			/* active tuning based on IO direction */
-			if (hwif->rwproc)
-				hwif->rwproc(drive, func);
-
-			if (!(count = ide_build_dmatable(drive, func)))
-				return 1;	/* try PIO instead of DMA */
-			outl(hwif->dmatable_dma, dma_base + 4); /* PRD table */
-			outb(reading, dma_base);			/* specify r/w */
-			outb(inb(dma_base+2)|6, dma_base+2);		/* clear INTR & ERROR flags */
-			drive->waiting_for_dma = 1;
-			if (drive->type != ATA_DISK)
-				return 0;
-
-			ide_set_handler(drive, ide_dma_intr, WAIT_CMD, dma_timer_expiry);	/* issue cmd to drive */
-			if ((rq->flags & REQ_DRIVE_ACB) && (drive->addressing == 1)) {
-				struct ata_taskfile *args = rq->special;
-
-				OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
-			} else if (drive->addressing) {
-				OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
-			} else {
-				OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-			}
-			return drive->channel->udma(ide_dma_begin, drive, NULL);
-		case ide_dma_begin:
-			/* Note that this is done *after* the cmd has
-			 * been issued to the drive, as per the BM-IDE spec.
-			 * The Promise Ultra33 doesn't work correctly when
-			 * we do this part before issuing the drive cmd.
-			 */
-			outb(inb(dma_base)|1, dma_base);		/* start DMA */
-			return 0;
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
-			dma_stat = inb(dma_base+2);		/* get DMA status */
-			outb(dma_stat|6, dma_base+2);	/* clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);	/* purge DMA mappings */
-			return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
-		case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = inb(dma_base+2);
-#if 0  /* do not set unless you know what you are doing */
-			if (dma_stat & 4) {
-				byte stat = GET_STAT();
-				outb(dma_base+2, dma_stat & 0xE4);
-			}
-#endif
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-		case ide_dma_bad_drive:
-		case ide_dma_good_drive:
-			return check_drive_lists(drive, (func == ide_dma_good_drive));
-		case ide_dma_verbose:
-			return report_drive_dmaing(drive);
-		case ide_dma_timeout:
-			printk(KERN_ERR "%s: DMA timeout occured!\n", __FUNCTION__);
-			return 1;
-		case ide_dma_retune:
-		case ide_dma_lostirq:
-			printk(KERN_ERR "%s: chipset supported func only: %d\n", __FUNCTION__,  func);
-			return 1;
-		default:
-			printk(KERN_ERR "%s: unsupported func: %d\n", __FUNCTION__, func);
-			return 1;
-	}
+	return config_drive_for_dma(drive);
 }
 
 /*
  * Needed for allowing full modular support of ide-driver
  */
-void ide_release_dma(struct ata_channel *hwif)
+void ide_release_dma(struct ata_channel *ch)
 {
-	if (!hwif->dma_base)
+	if (!ch->dma_base)
 		return;
 
-	if (hwif->dmatable_cpu) {
-		pci_free_consistent(hwif->pci_dev,
+	if (ch->dmatable_cpu) {
+		pci_free_consistent(ch->pci_dev,
 				    PRD_ENTRIES * PRD_BYTES,
-				    hwif->dmatable_cpu,
-				    hwif->dmatable_dma);
-		hwif->dmatable_cpu = NULL;
-	}
-	if (hwif->sg_table) {
-		kfree(hwif->sg_table);
-		hwif->sg_table = NULL;
-	}
-	if ((hwif->dma_extra) && (hwif->unit == 0))
-		release_region((hwif->dma_base + 16), hwif->dma_extra);
-	release_region(hwif->dma_base, 8);
-	hwif->dma_base = 0;
+				    ch->dmatable_cpu,
+				    ch->dmatable_dma);
+		ch->dmatable_cpu = NULL;
+	}
+	if (ch->sg_table) {
+		kfree(ch->sg_table);
+		ch->sg_table = NULL;
+	}
+	if ((ch->dma_extra) && (ch->unit == 0))
+		release_region((ch->dma_base + 16), ch->dma_extra);
+	release_region(ch->dma_base, 8);
+	ch->dma_base = 0;
 }
 
 /*
  * This can be called for a dynamically installed interface. Don't __init it
  */
-void ide_setup_dma(struct ata_channel *hwif, unsigned long dma_base, unsigned int num_ports)
+void ide_setup_dma(struct ata_channel *ch, unsigned long dma_base, unsigned int num_ports)
 {
-	printk("    %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1);
+	printk("    %s: BM-DMA at 0x%04lx-0x%04lx", ch->name, dma_base, dma_base + num_ports - 1);
 	if (check_region(dma_base, num_ports)) {
 		printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n");
 		return;
 	}
-	request_region(dma_base, num_ports, hwif->name);
-	hwif->dma_base = dma_base;
-	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
+	request_region(dma_base, num_ports, ch->name);
+	ch->dma_base = dma_base;
+	ch->dmatable_cpu = pci_alloc_consistent(ch->pci_dev,
 						  PRD_ENTRIES * PRD_BYTES,
-						  &hwif->dmatable_dma);
-	if (hwif->dmatable_cpu == NULL)
+						  &ch->dmatable_dma);
+	if (ch->dmatable_cpu == NULL)
 		goto dma_alloc_failure;
 
-	hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+	ch->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
 				 GFP_KERNEL);
-	if (hwif->sg_table == NULL) {
-		pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES,
-				    hwif->dmatable_cpu, hwif->dmatable_dma);
+	if (ch->sg_table == NULL) {
+		pci_free_consistent(ch->pci_dev, PRD_ENTRIES * PRD_BYTES,
+				    ch->dmatable_cpu, ch->dmatable_dma);
 		goto dma_alloc_failure;
 	}
 
-	hwif->udma = ide_dmaproc;
+	ch->XXX_udma = XXX_ide_dmaproc;
 
-	if (hwif->chipset != ide_trm290) {
-		byte dma_stat = inb(dma_base+2);
+	if (ch->chipset != ide_trm290) {
+		u8 dma_stat = inb(dma_base+2);
 		printk(", BIOS settings: %s:%s, %s:%s",
-		       hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
-		       hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
+		       ch->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio",
+		       ch->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio");
 	}
 	printk("\n");
 	return;
@@ -694,3 +503,351 @@
 dma_alloc_failure:
 	printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n");
 }
+
+/****************************************************************************
+ * UDMA function which should have architecture specific counterparts where
+ * neccessary.
+ *
+ * The intention is that at some point in time we will move this whole to
+ * architecture specific kernel sections. For now I would love the architecture
+ * maintainers to just #ifdef #endif this stuff directly here. I have for now
+ * tryed to update as much as I could in the architecture specific code.  But
+ * of course I may have done mistakes, so please bear with me and update it
+ * here the proper way.
+ *
+ * Thank you a lot in advance!
+ *
+ * Sat May  4 20:29:46 CEST 2002 Marcin Dalecki.
+ */
+
+/*
+ * This is the generic part of the DMA setup used by the host chipset drivers
+ * in the corresponding DMA setup method.
+ *
+ * FIXME: there are some places where this gets used driectly for "error
+ * recovery" in the ATAPI drivers. This was just plain wrong before, in esp.
+ * not portable, and just got uncovered now.
+ */
+void udma_enable(struct ata_device *drive, int on, int verbose)
+{
+	struct ata_channel *ch = drive->channel;
+	int set_high = 1;
+	u8 unit = (drive->select.b.unit & 0x01);
+	u64 addr = BLK_BOUNCE_HIGH;
+
+	if (!on) {
+		if (verbose)
+			printk("%s: DMA disabled\n", drive->name);
+		set_high = 0;
+		outb(inb(ch->dma_base + 2) & ~(1 << (5 + unit)), ch->dma_base + 2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+		udma_tcq_enable(drive, 0);
+#endif
+	}
+
+	/* toggle bounce buffers */
+
+	if (on && drive->type == ATA_DISK && drive->channel->highmem) {
+		if (!PCI_DMA_BUS_IS_PHYS)
+			addr = BLK_BOUNCE_ANY;
+		else
+			addr = drive->channel->pci_dev->dma_mask;
+	}
+
+	blk_queue_bounce_limit(&drive->queue, addr);
+
+	drive->using_dma = on;
+
+	if (on) {
+		outb(inb(ch->dma_base + 2) | (1 << (5 + unit)), ch->dma_base + 2);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+		udma_tcq_enable(drive, 1);
+#endif
+	}
+}
+
+/*
+ * This prepares a dma request.  Returns 0 if all went okay, returns 1
+ * otherwise.  May also be invoked from trm290.c
+ */
+int udma_new_table(struct ata_channel *ch, struct request *rq)
+{
+	unsigned int *table = ch->dmatable_cpu;
+#ifdef CONFIG_BLK_DEV_TRM290
+	unsigned int is_trm290_chipset = (ch->chipset == ide_trm290);
+#else
+	const int is_trm290_chipset = 0;
+#endif
+	unsigned int count = 0;
+	int i;
+	struct scatterlist *sg;
+
+	ch->sg_nents = i = build_sglist(ch, rq);
+	if (!i)
+		return 0;
+
+	sg = ch->sg_table;
+	while (i) {
+		u32 cur_addr;
+		u32 cur_len;
+
+		cur_addr = sg_dma_address(sg);
+		cur_len = sg_dma_len(sg);
+
+		/*
+		 * Fill in the dma table, without crossing any 64kB boundaries.
+		 * Most hardware requires 16-bit alignment of all blocks,
+		 * but the trm290 requires 32-bit alignment.
+		 */
+
+		while (cur_len) {
+			u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff);
+
+			if (count++ >= PRD_ENTRIES) {
+				printk("ide-dma: count %d, sg_nents %d, cur_len %d, cur_addr %u\n",
+						count, ch->sg_nents, cur_len, cur_addr);
+				BUG();
+			}
+
+			if (bcount > cur_len)
+				bcount = cur_len;
+			*table++ = cpu_to_le32(cur_addr);
+			xcount = bcount & 0xffff;
+			if (is_trm290_chipset)
+				xcount = ((xcount >> 2) - 1) << 16;
+			if (xcount == 0x0000) {
+		        /*
+			 * Most chipsets correctly interpret a length of
+			 * 0x0000 as 64KB, but at least one (e.g. CS5530)
+			 * misinterprets it as zero (!). So here we break
+			 * the 64KB entry into two 32KB entries instead.
+			 */
+				if (count++ >= PRD_ENTRIES) {
+					pci_unmap_sg(ch->pci_dev, sg,
+						     ch->sg_nents,
+						     ch->sg_dma_direction);
+					return 0;
+				}
+
+				*table++ = cpu_to_le32(0x8000);
+				*table++ = cpu_to_le32(cur_addr + 0x8000);
+				xcount = 0x8000;
+			}
+			*table++ = cpu_to_le32(xcount);
+			cur_addr += bcount;
+			cur_len -= bcount;
+		}
+
+		sg++;
+		i--;
+	}
+
+	if (!count)
+		printk(KERN_ERR "%s: empty DMA table?\n", ch->name);
+	else if (!is_trm290_chipset)
+		*--table |= cpu_to_le32(0x80000000);
+
+	return count;
+}
+
+/* Teardown mappings after DMA has completed.  */
+void udma_destroy_table(struct ata_channel *ch)
+{
+	pci_unmap_sg(ch->pci_dev, ch->sg_table, ch->sg_nents, ch->sg_dma_direction);
+}
+
+void udma_print(struct ata_device *drive)
+{
+#ifdef CONFIG_ARCH_ACORN
+	printk(", DMA");
+#else
+	struct hd_driveid *id = drive->id;
+	char *str = NULL;
+
+	if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
+	    (id->dma_ultra & (id->dma_ultra >> 14) & 3)) {
+		if ((id->dma_ultra >> 15) & 1)
+			str = ", UDMA(mode 7)";	/* UDMA BIOS-enabled! */
+		else
+			str = ", UDMA(133)";	/* UDMA BIOS-enabled! */
+	} else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) &&
+		  (id->dma_ultra & (id->dma_ultra >> 11) & 7)) {
+		if ((id->dma_ultra >> 13) & 1) {
+			str = ", UDMA(100)";	/* UDMA BIOS-enabled! */
+		} else if ((id->dma_ultra >> 12) & 1) {
+			str = ", UDMA(66)";	/* UDMA BIOS-enabled! */
+		} else {
+			str = ", UDMA(44)";	/* UDMA BIOS-enabled! */
+		}
+	} else if ((id->field_valid & 4) &&
+		   (id->dma_ultra & (id->dma_ultra >> 8) & 7)) {
+		if ((id->dma_ultra >> 10) & 1) {
+			str = ", UDMA(33)";	/* UDMA BIOS-enabled! */
+		} else if ((id->dma_ultra >> 9) & 1) {
+			str = ", UDMA(25)";	/* UDMA BIOS-enabled! */
+		} else {
+			str = ", UDMA(16)";	/* UDMA BIOS-enabled! */
+		}
+	} else if (id->field_valid & 4)
+		str = ", (U)DMA";	/* Can be BIOS-enabled! */
+	else
+		str = ", DMA";
+
+	printk(str);
+#endif
+}
+
+/*
+ * Drive back/white list handling for UDMA capability:
+ */
+
+int udma_black_list(struct ata_device *drive)
+{
+	return check_drive_lists(drive, 0);
+}
+
+int udma_white_list(struct ata_device *drive)
+{
+	return check_drive_lists(drive, 1);
+}
+
+/*
+ * Generic entry points for functions provided possibly by the host chip set
+ * drivers.
+ */
+
+/*
+ * Prepare the channel for a DMA startfer. Please note that only the broken
+ * Pacific Digital host chip needs the reques to be passed there to decide
+ * about addressing modes.
+ */
+
+int udma_start(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+
+	if (ch->udma_start)
+		return ch->udma_start(drive, rq);
+
+	/* Note that this is done *after* the cmd has
+	 * been issued to the drive, as per the BM-IDE spec.
+	 * The Promise Ultra33 doesn't work correctly when
+	 * we do this part before issuing the drive cmd.
+	 */
+	outb(inb(dma_base)|1, dma_base);		/* start DMA */
+	return 0;
+}
+
+int udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	if (ch->udma_stop)
+		return ch->udma_stop(drive);
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
+}
+
+/*
+ * This is the default read write function.
+ *
+ * It's exported only for host chips which use it for fallback or (too) late
+ * capability checking.
+ */
+
+int ata_do_udma(unsigned int reading, struct ata_device *drive, struct request *rq)
+{
+	if (ata_start_dma(drive, rq))
+		return 1;
+
+	if (drive->type != ATA_DISK)
+		return 0;
+
+	reading <<= 3;
+
+	ide_set_handler(drive, ide_dma_intr, WAIT_CMD, dma_timer_expiry);	/* issue cmd to drive */
+	if ((rq->flags & REQ_DRIVE_ACB) && (drive->addressing == 1)) {
+		struct ata_taskfile *args = rq->special;
+
+		OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
+	} else if (drive->addressing) {
+		OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
+	} else {
+		OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+	}
+
+	return udma_start(drive, rq);
+}
+
+int udma_read(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+
+	if (ch->udma_read)
+		return ch->udma_read(drive, rq);
+
+	return ata_do_udma(1, drive, rq);
+}
+
+int udma_write(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+
+	if (ch->udma_write)
+		return ch->udma_write(drive, rq);
+
+	return ata_do_udma(0, drive, rq);
+}
+
+/*
+ * FIXME: This should be attached to a channel as we can see now!
+ */
+int udma_irq_status(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	u8 dma_stat;
+
+	if (ch->udma_irq_status)
+		return ch->udma_irq_status(drive);
+
+	/* default action */
+	dma_stat = inb(ch->dma_base + 2);
+
+	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+}
+
+void udma_timeout(struct ata_device *drive)
+{
+	printk(KERN_ERR "ATA: UDMA timeout occured %s!\n", drive->name);
+
+	/* Invoke the chipset specific handler now. */
+	if (drive->channel->udma_timeout)
+		drive->channel->udma_timeout(drive);
+
+}
+
+void udma_irq_lost(struct ata_device *drive)
+{
+	if (drive->channel->udma_irq_lost)
+		drive->channel->udma_irq_lost(drive);
+}
+
+EXPORT_SYMBOL(udma_enable);
+EXPORT_SYMBOL(udma_start);
+EXPORT_SYMBOL(udma_stop);
+EXPORT_SYMBOL(udma_read);
+EXPORT_SYMBOL(udma_write);
+EXPORT_SYMBOL(ata_do_udma);
+EXPORT_SYMBOL(udma_irq_status);
+EXPORT_SYMBOL(udma_print);
+EXPORT_SYMBOL(udma_black_list);
+EXPORT_SYMBOL(udma_white_list);
diff -Nru a/drivers/ide/ide-features.c b/drivers/ide/ide-features.c
--- a/drivers/ide/ide-features.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/ide-features.c	Sun May  5 20:39:50 2002
@@ -44,29 +44,38 @@
  */
 char *ide_xfer_verbose (byte xfer_rate)
 {
-	switch(xfer_rate) {
-		case XFER_UDMA_7:	return("UDMA 7");
-		case XFER_UDMA_6:	return("UDMA 6");
-		case XFER_UDMA_5:	return("UDMA 5");
-		case XFER_UDMA_4:	return("UDMA 4");
-		case XFER_UDMA_3:	return("UDMA 3");
-		case XFER_UDMA_2:	return("UDMA 2");
-		case XFER_UDMA_1:	return("UDMA 1");
-		case XFER_UDMA_0:	return("UDMA 0");
-		case XFER_MW_DMA_2:	return("MW DMA 2");
-		case XFER_MW_DMA_1:	return("MW DMA 1");
-		case XFER_MW_DMA_0:	return("MW DMA 0");
-		case XFER_SW_DMA_2:	return("SW DMA 2");
-		case XFER_SW_DMA_1:	return("SW DMA 1");
-		case XFER_SW_DMA_0:	return("SW DMA 0");
-		case XFER_PIO_4:	return("PIO 4");
-		case XFER_PIO_3:	return("PIO 3");
-		case XFER_PIO_2:	return("PIO 2");
-		case XFER_PIO_1:	return("PIO 1");
-		case XFER_PIO_0:	return("PIO 0");
-		case XFER_PIO_SLOW:	return("PIO SLOW");
-		default:		return("XFER ERROR");
-	}
+	static struct ide_xfer_par {
+		byte rate;
+		char *name;
+	} xfer_verbose[] = {
+		{ XFER_UDMA_7,		"UDMA 7" },
+		{ XFER_UDMA_6,		"UDMA 6" },
+		{ XFER_UDMA_5,		"UDMA 5" },
+		{ XFER_UDMA_4,		"UDMA 4" },
+		{ XFER_UDMA_3,		"UDMA 3" },
+		{ XFER_UDMA_2,		"UDMA 2" },
+		{ XFER_UDMA_1,		"UDMA 1" },
+		{ XFER_UDMA_0,		"UDMA 0" },
+		{ XFER_MW_DMA_2,	"MW DMA 2" },
+		{ XFER_MW_DMA_1,	"MW DMA 1" },
+		{ XFER_MW_DMA_0,	"MW DMA 0" },
+		{ XFER_SW_DMA_2,	"SW DMA 2" },
+		{ XFER_SW_DMA_1,	"SW DMA 1" },
+		{ XFER_SW_DMA_0,	"SW DMA 0" },
+		{ XFER_PIO_4,		"PIO 4" },
+		{ XFER_PIO_3,		"PIO 3" },
+		{ XFER_PIO_2,		"PIO 2" },
+		{ XFER_PIO_1,		"PIO 1" },
+		{ XFER_PIO_0,		"PIO 0" },
+		{ XFER_PIO_SLOW,	"PIO SLOW" },
+	};
+
+	int i = 0;
+
+	for (; i < ARRAY_SIZE(xfer_verbose); i++)
+		if (xfer_verbose[i].rate == xfer_rate)
+			return xfer_verbose[i].name;
+	return "XFER ERROR";
 }
 
 byte ide_auto_reduce_xfer (ide_drive_t *drive)
diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
--- a/drivers/ide/ide-floppy.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-floppy.c	Sun May  5 20:39:49 2002
@@ -901,7 +901,7 @@
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		if (drive->channel->udma(ide_dma_end, drive, NULL)) {
+		if (udma_stop(drive)) {
 			set_bit (PC_DMA_ERROR, &pc->flags);
 		} else {
 			pc->actually_transferred=pc->request_transfer;
@@ -944,10 +944,11 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
-		drive->channel->udma(ide_dma_off, drive, NULL);
+		udma_enable(drive, 0, 1);
+
 		return ide_stopped;
 	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
 	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);			/* on this interrupt */
 	ireason.all=IN_BYTE (IDE_IREASON_REG);
@@ -1119,11 +1120,15 @@
 	bcount.all = min(pc->request_transfer, 63 * 1024);
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
-		(void) drive->channel->udma(ide_dma_off, drive, NULL);
+	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags))
+		udma_enable(drive, 0, 1);
+
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) {
+		if (test_bit (PC_WRITING, &pc->flags))
+			dma_ok = !udma_write(drive, rq);
+		else
+			dma_ok = !udma_read(drive, rq);
 	}
-	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
-		dma_ok=!drive->channel->udma(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive, rq);
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 
 	if (IDE_CONTROL_REG)
@@ -1136,7 +1141,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (dma_ok) {							/* Begin DMA, if necessary */
 		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-		(void) drive->channel->udma(ide_dma_begin, drive, NULL);
+		udma_start(drive, rq);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 
diff -Nru a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c
--- a/drivers/ide/ide-pci.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/ide-pci.c	Sun May  5 20:39:50 2002
@@ -1,6 +1,8 @@
-/*
- *  Copyright (c) 1998-2000  Andre Hedrick <andre@linux-ide.org>
- *  Copyright (c) 1995-1998  Mark Lord
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
+ *  Copyright (C) 2002	     Marcin Dalecki <martin@dalecki.de>
+ *  Copyright (C) 1998-2000  Andre Hedrick <andre@linux-ide.org>
+ *  Copyright (C) 1995-1998  Mark Lord
  *
  *  May be copied or modified under the terms of the GNU General Public License
  */
@@ -81,17 +83,10 @@
 #endif
 
 #ifdef CONFIG_BLK_DEV_HPT366
-extern byte hpt363_shared_irq;
-extern byte hpt363_shared_pin;
-
 extern unsigned int pci_init_hpt366(struct pci_dev *);
 extern unsigned int ata66_hpt366(struct ata_channel *);
 extern void ide_init_hpt366(struct ata_channel *);
 extern void ide_dmacapable_hpt366(struct ata_channel *, unsigned long);
-#else
-/* FIXME: those have to be killed */
-static byte hpt363_shared_irq;
-static byte hpt363_shared_pin;
 #endif
 
 #ifdef CONFIG_BLK_DEV_NS87415
@@ -177,7 +172,7 @@
 #define ATA_F_PHACK	0x40	/* apply PROMISE hacks */
 #define ATA_F_HPTHACK	0x80	/* apply HPT366 hacks */
 
-typedef struct ide_pci_device_s {
+struct ata_pci_device {
 	unsigned short		vendor;
 	unsigned short		device;
 	unsigned int		(*init_chipset)(struct pci_dev *dev);
@@ -188,9 +183,9 @@
 	unsigned int		bootable;
 	unsigned int		extra;
 	unsigned int		flags;
-} ide_pci_device_t;
+};
 
-static ide_pci_device_t pci_chipsets[] __initdata = {
+static struct ata_pci_device pci_chipsets[] __initdata = {
 #ifdef CONFIG_BLK_DEV_PIIX
 	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1, pci_init_piix, ata66_piix, ide_init_piix, ide_dmacapable_piix, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 },
 	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, pci_init_piix, ata66_piix, ide_init_piix, ide_dmacapable_piix, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0, 0 },
@@ -319,7 +314,7 @@
  * settings of split-mirror pci-config space, place chipset into init-mode,
  * and/or preserve an interrupt if the card is not native ide support.
  */
-static unsigned int __init trust_pci_irq(ide_pci_device_t *d, struct pci_dev *dev)
+static unsigned int __init trust_pci_irq(struct ata_pci_device *d, struct pci_dev *dev)
 {
 	if (d->flags & ATA_F_IRQ)
 		return dev->irq;
@@ -484,7 +479,7 @@
  * Setup DMA transfers on a channel.
  */
 static void __init setup_channel_dma(struct ata_channel *hwif, struct pci_dev *dev,
-		ide_pci_device_t *d,
+		struct ata_pci_device *d,
 		int port,
 		u8 class_rev,
 		int pciirq,
@@ -534,7 +529,7 @@
  * This gets called once for the master and for the slave interface.
  */
 static int __init setup_host_channel(struct pci_dev *dev,
-		ide_pci_device_t *d,
+		struct ata_pci_device *d,
 		int port,
 		u8 class_rev,
 		int pciirq,
@@ -648,17 +643,16 @@
 }
 
 /*
- * Looks at the primary/secondary channels on a PCI IDE device and, if they
- * are enabled, prepares the IDE driver for use with them.  This generic code
- * works for most PCI chipsets.
+ * Looks at the primary/secondary channels on a PCI IDE device and, if they are
+ * enabled, prepares the IDE driver for use with them.  This generic code works
+ * for most PCI chipsets.
  *
  * One thing that is not standardized is the location of the primary/secondary
  * interface "enable/disable" bits.  For chipsets that we "know" about, this
- * information is in the ide_pci_device_t struct; for all other chipsets, we
- * just assume both interfaces are enabled.
+ * information is in the struct ata_pci_device struct; for all other chipsets,
+ * we just assume both interfaces are enabled.
  */
-
-static void __init setup_pci_device(struct pci_dev *dev, ide_pci_device_t *d)
+static void __init setup_pci_device(struct pci_dev *dev, struct ata_pci_device *d)
 {
 	int autodma = 0;
 	int pciirq = 0;
@@ -715,7 +709,7 @@
 		d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
 	}
 
-	printk("%s: chipset revision %d\n", dev->name, class_rev);
+	printk(KERN_INFO "ATA: chipset rev.: %d\n", class_rev);
 
 	/*
 	 * Can we trust the reported IRQ?
@@ -728,11 +722,11 @@
 		   to act otherwise on those. The Supertrak however we need
 		   to skip */
 		if (d->vendor == PCI_VENDOR_ID_PROMISE && d->device == PCI_DEVICE_ID_PROMISE_20265) {
-			printk(KERN_INFO "ide: Found promise 20265 in RAID mode.\n");
+			printk(KERN_INFO "ATA: Found promise 20265 in RAID mode.\n");
 			if(dev->bus->self && dev->bus->self->vendor == PCI_VENDOR_ID_INTEL &&
 				dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960)
 			{
-				printk(KERN_INFO "ide: Skipping Promise PDC20265 attached to I2O RAID controller.\n");
+				printk(KERN_INFO "ATA: Skipping Promise PDC20265 attached to I2O RAID controller.\n");
 				return;
 			}
 		}
@@ -740,9 +734,10 @@
 		   Suspect a fastrak and fall through */
 	}
 	if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) {
-		printk("%s: not 100%% native mode: will probe irqs later\n", dev->name);
+		printk(KERN_INFO "ATA: non-legacy mode: IRQ probe delayed\n");
+
 		/*
-		 * This allows off board ide-pci cards the enable a BIOS,
+		 * This allows off board ide-pci cards to enable a BIOS,
 		 * verify interrupt settings of split-mirror pci-config
 		 * space, place chipset into init-mode, and/or preserve
 		 * an interrupt if the card is not native ide support.
@@ -752,19 +747,18 @@
 		else
 			pciirq = trust_pci_irq(d, dev);
 	} else if (tried_config) {
-		printk("%s: will probe IRQs later\n", dev->name);
+		printk(KERN_INFO "ATA: will probe IRQs later\n");
 		pciirq = 0;
 	} else if (!pciirq) {
-		printk("%s: bad IRQ (%d): will probe later\n", dev->name, pciirq);
+		printk(KERN_INFO "ATA: invalid IRQ (%d): will probe later\n", pciirq);
 		pciirq = 0;
 	} else {
 		if (d->init_chipset)
 			d->init_chipset(dev);
 #ifdef __sparc__
-		printk("%s: 100%% native mode on irq %s\n",
-		       dev->name, __irq_itoa(pciirq));
+		printk(KERN_INFO "ATA: 100%% native mode on irq\n", __irq_itoa(pciirq));
 #else
-		printk("%s: 100%% native mode on irq %d\n", dev->name, pciirq);
+		printk(KERN_INFO "ATA: 100%% native mode on irq %d\n", pciirq);
 #endif
 	}
 
@@ -775,10 +769,11 @@
 	setup_host_channel(dev, d, ATA_SECONDARY, class_rev, pciirq, autodma, &pcicmd);
 }
 
-static void __init pdc20270_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d)
+static void __init pdc20270_device_order_fixup (struct pci_dev *dev, struct ata_pci_device *d)
 {
-	struct pci_dev *dev2 = NULL, *findev;
-	ide_pci_device_t *d2;
+	struct pci_dev *dev2 = NULL;
+	struct pci_dev *findev;
+	struct ata_pci_device *d2;
 
 	if (dev->bus->self &&
 	    dev->bus->self->vendor == PCI_VENDOR_ID_DEC &&
@@ -814,10 +809,10 @@
 	setup_pci_device(dev2, d2);
 }
 
-static void __init hpt366_device_order_fixup (struct pci_dev *dev, ide_pci_device_t *d)
+static void __init hpt366_device_order_fixup (struct pci_dev *dev, struct ata_pci_device *d)
 {
 	struct pci_dev *dev2 = NULL, *findev;
-	ide_pci_device_t *d2;
+	struct ata_pci_device *d2;
 	unsigned char pin1 = 0, pin2 = 0;
 	unsigned int class_rev;
 
@@ -843,9 +838,7 @@
 		    (PCI_FUNC(findev->devfn) & 1)) {
 			dev2 = findev;
 			pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2);
-			hpt363_shared_pin = (pin1 != pin2) ? 1 : 0;
-			hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0;
-			if (hpt363_shared_pin && hpt363_shared_irq) {
+			if ((pin1 != pin2) && (dev->irq == dev2->irq)) {
 				d->bootable = ON_BOARD;
 				printk("%s: onboard version of chipset, pin1=%d pin2=%d\n", dev->name, pin1, pin2);
 			}
@@ -869,7 +862,7 @@
 {
 	unsigned short vendor;
 	unsigned short device;
-	ide_pci_device_t *d;
+	struct ata_pci_device *d;
 
 	vendor = dev->vendor;
 	device = dev->device;
@@ -881,7 +874,7 @@
 		++d;
 
 	if (d->init_channel == ATA_PCI_IGNORE)
-		printk("%s: has been ignored by PCI bus scan\n", dev->name);
+		printk(KERN_INFO "ATA: %s: ignored by PCI bus scan\n", dev->name);
 	else if ((d->vendor == PCI_VENDOR_ID_OPTI && d->device == PCI_DEVICE_ID_OPTI_82C558) && !(PCI_FUNC(dev->devfn) & 1))
 		return;
 	else if ((d->vendor == PCI_VENDOR_ID_CONTAQ && d->device == PCI_DEVICE_ID_CONTAQ_82C693) && (!(PCI_FUNC(dev->devfn) & 1) || !((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))
@@ -896,10 +889,10 @@
 		pdc20270_device_order_fixup(dev, d);
 	else if (!(d->vendor == 0 && d->device == 0) || (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
 		if (d->vendor == 0 && d->device == 0)
-			printk("%s: unknown IDE controller on PCI slot %s, vendor=%04x, device=%04x\n",
-			       dev->name, dev->slot_name, vendor, device);
+			printk(KERN_INFO "ATA: unknown interface: %s (%04x:%04x) on PCI slot %s\n",
+			       dev->name, vendor, device, dev->slot_name);
 		else
-			printk("%s: IDE controller on PCI slot %s\n", dev->name, dev->slot_name);
+			printk(KERN_INFO "ATA: interface: %s, on PCI slot %s\n", dev->name, dev->slot_name);
 		setup_pci_device(dev, d);
 	}
 }
diff -Nru a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c
--- a/drivers/ide/ide-pmac.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/ide-pmac.c	Sun May  5 20:39:50 2002
@@ -1164,9 +1164,9 @@
 
 /* Teardown mappings after DMA has completed.  */
 static void
-pmac_ide_destroy_dmatable (ide_drive_t *drive, int ix)
+pmac_ide_destroy_dmatable(struct ata_channel *ch, int ix)
 {
-	struct pci_dev *dev = drive->channel->pci_dev;
+	struct pci_dev *dev = ch->pci_dev;
 	struct scatterlist *sg = pmac_ide[ix].sg_table;
 	int nents = pmac_ide[ix].sg_nents;
 
@@ -1367,10 +1367,6 @@
 		break;
 	case ide_dma_read:
 	case ide_dma_write:
-		/* this almost certainly isn't needed since we don't
-		   appear to have a rwproc */
-		if (drive->channel->rwproc)
-			drive->channel->rwproc(drive, func);
 		reading = (func == ide_dma_read);
 		if (!pmac_ide_build_dmatable(drive, rq, ix, !reading))
 			return 1;
@@ -1404,7 +1400,7 @@
 		drive->waiting_for_dma = 0;
 		dstat = in_le32(&dma->status);
 		out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
-		pmac_ide_destroy_dmatable(drive, ix);
+		pmac_ide_destroy_dmatable(drive->channel, ix);
 		/* verify good dma status */
 		return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
 	case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
@@ -1453,9 +1449,6 @@
 	case ide_dma_bad_drive:
 	case ide_dma_good_drive:
 		return check_drive_lists(drive, (func == ide_dma_good_drive));
-	case ide_dma_verbose:
-		return report_drive_dmaing(drive);
-	case ide_dma_retune:
 	case ide_dma_lostirq:
 	case ide_dma_timeout:
 		printk(KERN_WARNING "ide_pmac_dmaproc: chipset supported func only: %d\n", func);
diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-probe.c	Sun May  5 20:39:49 2002
@@ -1,4 +1,5 @@
-/*
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  *
  *  Mostly written by Mark Lord <mlord@pobox.com>
@@ -47,7 +48,7 @@
 #include <asm/uaccess.h>
 #include <asm/io.h>
 
-static inline void do_identify (ide_drive_t *drive, byte cmd)
+static inline void do_identify(struct ata_device *drive, u8 cmd)
 {
 	int bswap = 1;
 	struct hd_driveid *id;
@@ -121,7 +122,7 @@
 	drive->present = 1;
 
 	/*
-	 * Check for an ATAPI device
+	 * Check for an ATAPI device:
 	 */
 	if (cmd == WIN_PIDENTIFY) {
 		byte type = (id->config >> 8) & 0x1f;
@@ -172,7 +173,7 @@
 	}
 
 	/*
-	 * Not an ATAPI device: looks like a "regular" hard disk
+	 * Not an ATAPI device: looks like a "regular" hard disk:
 	 */
 	if (id->config & (1<<7))
 		drive->removable = 1;
@@ -185,7 +186,7 @@
 	 */
 
 	if (drive_is_flashcard(drive)) {
-		ide_drive_t *mate = &drive->channel->drives[1 ^ drive->select.b.unit];
+		struct ata_device *mate = &drive->channel->drives[1 ^ drive->select.b.unit];
 		if (!mate->ata_flash) {
 			mate->present = 0;
 			mate->noprobe = 1;
@@ -198,32 +199,53 @@
 	if (drive->channel->quirkproc)
 		drive->quirk_list = drive->channel->quirkproc(drive);
 
+	/* Initialize queue depth settings */
+	drive->queue_depth = 1;
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEPTH
+	drive->queue_depth = CONFIG_BLK_DEV_IDE_TCQ_DEPTH;
+#else
+	drive->queue_depth = drive->id->queue_depth + 1;
+#endif
+	if (drive->queue_depth < 1 || drive->queue_depth > IDE_MAX_TAG)
+		drive->queue_depth = IDE_MAX_TAG;
+
 	return;
 
 err_misc:
 	kfree(id);
 err_kmalloc:
 	drive->present = 0;
+
 	return;
 }
 
 /*
- * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
- * and waits for a response.  It also monitors irqs while this is
- * happening, in hope of automatically determining which one is
- * being used by the interface.
+ * Sends an ATA(PI) IDENTIFY request to a drive and wait for a response.  It
+ * also monitor irqs while this is happening, in hope of automatically
+ * determining which one is being used by the interface.
  *
  * Returns:	0  device was identified
  *		1  device timed-out (no response to identify request)
  *		2  device aborted the command (refused to identify itself)
  */
-static int actual_try_to_identify (ide_drive_t *drive, byte cmd)
+static int identify(struct ata_device *drive, u8 cmd)
 {
 	int rc;
+	int autoprobe = 0;
+	unsigned long cookie = 0;
 	ide_ioreg_t hd_status;
 	unsigned long timeout;
-	byte s, a;
+	u8 s;
+	u8 a;
+
 
+	if (IDE_CONTROL_REG && !drive->channel->irq) {
+		autoprobe = 1;
+		cookie = probe_irq_on();
+		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
+	}
+
+	rc = 1;
 	if (IDE_CONTROL_REG) {
 		/* take a deep breath */
 		ide_delay_50ms();
@@ -247,19 +269,18 @@
 #if CONFIG_BLK_DEV_PDC4030
 	if (drive->channel->chipset == ide_pdc4030) {
 		/* DC4030 hosted drives need their own identify... */
-		extern int pdc4030_identify(ide_drive_t *);
-		if (pdc4030_identify(drive)) {
-			return 1;
-		}
+		extern int pdc4030_identify(struct ata_device *);
+
+		if (pdc4030_identify(drive))
+			goto out;
 	} else
-#endif /* CONFIG_BLK_DEV_PDC4030 */
+#endif
 		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
 	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
 	timeout += jiffies;
 	do {
-		if (0 < (signed long)(jiffies - timeout)) {
-			return 1;	/* drive timed-out */
-		}
+		if (time_after(jiffies, timeout))
+			goto out;	/* drive timed-out */
 		ide_delay_50ms();		/* give drive a breather */
 	} while (IN_BYTE(hd_status) & BUSY_STAT);
 
@@ -274,23 +295,8 @@
 		__restore_flags(flags);	/* local CPU only */
 	} else
 		rc = 2;			/* drive refused ID */
-	return rc;
-}
-
-static int try_to_identify (ide_drive_t *drive, byte cmd)
-{
-	int retval;
-	int autoprobe = 0;
-	unsigned long cookie = 0;
-
-	if (IDE_CONTROL_REG && !drive->channel->irq) {
-		autoprobe = 1;
-		cookie = probe_irq_on();
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
-	}
-
-	retval = actual_try_to_identify(drive, cmd);
 
+out:
 	if (autoprobe) {
 		int irq;
 		OUT_BYTE(drive->ctl | 0x02, IDE_CONTROL_REG);	/* mask device irq */
@@ -304,7 +310,8 @@
 				printk("%s: IRQ probe failed (0x%lx)\n", drive->name, cookie);
 		}
 	}
-	return retval;
+
+	return rc;
 }
 
 
@@ -324,10 +331,11 @@
  *		3  bad status from device (possible for ATAPI drives)
  *		4  probe was not attempted because failure was obvious
  */
-static int do_probe (ide_drive_t *drive, byte cmd)
+static int do_probe(struct ata_device *drive, byte cmd)
 {
 	int rc;
 	struct ata_channel *hwif = drive->channel;
+
 	if (drive->present) {	/* avoid waiting for inappropriate probes */
 		if ((drive->type != ATA_DISK) && (cmd == WIN_IDENTIFY))
 			return 4;
@@ -348,11 +356,10 @@
 		return 3;    /* no i/f present: mmm.. this should be a 4 -ml */
 	}
 
-	if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
-	 || drive->present || cmd == WIN_PIDENTIFY)
+	if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT) || drive->present || cmd == WIN_PIDENTIFY)
 	{
-		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
-			rc = try_to_identify(drive,cmd); /* failed: try again */
+		if ((rc = identify(drive,cmd)))   /* send cmd and wait */
+			rc = identify(drive,cmd); /* failed: try again */
 		if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) {
 			unsigned long timeout;
 			printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT());
@@ -363,14 +370,14 @@
 			timeout = jiffies;
 			while ((GET_STAT() & BUSY_STAT) && time_before(jiffies, timeout + WAIT_WORSTCASE))
 				ide_delay_50ms();
-			rc = try_to_identify(drive, cmd);
+			rc = identify(drive, cmd);
 		}
 		if (rc == 1)
 			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
 		(void) GET_STAT();		/* ensure drive irq is clear */
-	} else {
+	} else
 		rc = 3;				/* not present or maybe ATAPI */
-	}
+
 	if (drive->select.b.unit != 0) {
 		SELECT_DRIVE(hwif,&hwif->drives[0]);	/* exit with drive0 selected */
 		ide_delay_50ms();
@@ -379,10 +386,7 @@
 	return rc;
 }
 
-/*
- *
- */
-static void enable_nest(ide_drive_t *drive)
+static void enable_nest(struct ata_device *drive)
 {
 	unsigned long timeout;
 
@@ -392,7 +396,7 @@
 	OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
 	timeout = jiffies + WAIT_WORSTCASE;
 	do {
-		if (jiffies > timeout) {
+		if (time_after(jiffies, timeout)) {
 			printk("failed (timeout)\n");
 			return;
 		}
@@ -411,17 +415,21 @@
 /*
  * Tests for existence of a given drive using do_probe().
  */
-static inline void probe_for_drive (ide_drive_t *drive)
+static inline void probe_for_drive(struct ata_device *drive)
 {
 	if (drive->noprobe)			/* skip probing? */
 		return;
+
 	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
 		do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
 	}
+
 	if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
 		enable_nest(drive);
+
 	if (!drive->present)
 		return;			/* drive not found */
+
 	if (drive->id == NULL) {		/* identification failed? */
 		if (drive->type == ATA_DISK) {
 			printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
@@ -440,7 +448,7 @@
  */
 static void channel_probe(struct ata_channel *ch)
 {
-	unsigned int unit;
+	unsigned int i;
 	unsigned long flags;
 	int error;
 
@@ -455,8 +463,8 @@
 	/*
 	 * Check for the presence of a channel by probing for drives on it.
 	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		struct ata_device *drive = &ch->drives[unit];
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		struct ata_device *drive = &ch->drives[i];
 
 		probe_for_drive(drive);
 
@@ -475,23 +483,8 @@
 		error += !request_region(ch->io_ports[IDE_DATA_OFFSET], 8, ch->name);
 		ch->straight8 = 1;
 	} else {
-		if (ch->io_ports[IDE_DATA_OFFSET])
-			error += !request_region(ch->io_ports[IDE_DATA_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_ERROR_OFFSET])
-			error += !request_region(ch->io_ports[IDE_ERROR_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_NSECTOR_OFFSET])
-			error += !request_region(ch->io_ports[IDE_NSECTOR_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_SECTOR_OFFSET])
-			error += !request_region(ch->io_ports[IDE_SECTOR_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_LCYL_OFFSET])
-			error += !request_region(ch->io_ports[IDE_LCYL_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_HCYL_OFFSET])
-			error += !request_region(ch->io_ports[IDE_HCYL_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_SELECT_OFFSET])
-			error += !request_region(ch->io_ports[IDE_SELECT_OFFSET], 1, ch->name);
-		if (ch->io_ports[IDE_STATUS_OFFSET])
-			error += !request_region(ch->io_ports[IDE_STATUS_OFFSET], 1, ch->name);
-
+		for (i = 0; i < 8; i++)
+			error += !request_region(ch->io_ports[i], 1, ch->name);
 	}
 	if (ch->io_ports[IDE_CONTROL_OFFSET])
 		error += !request_region(ch->io_ports[IDE_CONTROL_OFFSET], 1, ch->name);
@@ -545,7 +538,7 @@
 		do {
 			ide_delay_50ms();
 			stat = IN_BYTE(ch->io_ports[IDE_STATUS_OFFSET]);
-		} while ((stat & BUSY_STAT) && 0 < (signed long)(timeout - jiffies));
+		} while ((stat & BUSY_STAT) && time_before(jiffies, timeout));
 	}
 
 	__restore_flags(flags);	/* local CPU only */
@@ -553,8 +546,8 @@
 	/*
 	 * Now setup the PIO transfer modes of the drives on this channel.
 	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		struct ata_device *drive = &ch->drives[unit];
+	for (i = 0; i < MAX_DRIVES; ++i) {
+		struct ata_device *drive = &ch->drives[i];
 
 		if (drive->present && (drive->autotune == 1)) {
 			if (drive->channel->tuneproc)
@@ -569,60 +562,6 @@
 }
 
 /*
- * init request queue
- */
-static void init_device_queue(struct ata_device *drive)
-{
-	request_queue_t *q = &drive->queue;
-	int max_sectors = 255;
-
-	q->queuedata = drive->channel;
-	blk_init_queue(q, do_ide_request, &ide_lock);
-	blk_queue_segment_boundary(q, 0xffff);
-
-	/* IDE can do up to 128K per request, pdc4030 needs smaller limit */
-#ifdef CONFIG_BLK_DEV_PDC4030
-	if (drive->channel->chipset == ide_pdc4030)
-		max_sectors = 127;
-#endif
-	blk_queue_max_sectors(q, max_sectors);
-
-	/* IDE DMA can do PRD_ENTRIES number of segments. */
-	blk_queue_max_hw_segments(q, PRD_SEGMENTS);
-
-	/* This is a driver limit and could be eliminated. */
-	blk_queue_max_phys_segments(q, PRD_SEGMENTS);
-}
-
-#if MAX_HWIFS > 1
-
-/*
- * This is used to simplify logic in init_irq() below.
- *
- * A loophole here is that we may not know about a particular hwif's irq until
- * after that hwif is actually probed/initialized..  This could be a problem
- * for the case where an hwif is on a dual interface that requires
- * serialization (eg. cmd640) and another hwif using one of the same irqs is
- * initialized beforehand.
- *
- * This routine detects and reports such situations, but does not fix them.
- */
-static void save_match(struct ata_channel *hwif, struct ata_channel *new,
-		struct ata_channel **match)
-{
-	struct ata_channel *m = *match;
-
-	if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
-		if (!new->hwgroup)
-			return;
-		printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
-	}
-	if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
-		*match = new;
-}
-#endif
-
-/*
  * This routine sets up the irq for an ide interface, and creates a new hwgroup
  * for the irq/channel if none was previously assigned.
  *
@@ -643,10 +582,8 @@
 	ide_hwgroup_t *new_hwgroup;
 	struct ata_channel *match = NULL;
 
-
-	/* Allocate the buffer and potentially sleep first */
-
-	new_hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL);
+	/* Spare allocation before sleep. */
+	new_hwgroup = kmalloc(sizeof(*hwgroup), GFP_KERNEL);
 
 	spin_lock_irqsave(&ide_lock, flags);
 	ch->hwgroup = NULL;
@@ -658,19 +595,22 @@
 	for (i = 0; i < MAX_HWIFS; ++i) {
 		struct ata_channel *h = &ide_hwifs[i];
 
-		if (h->hwgroup) {  /* scan only initialized channels */
-			if (ch->irq == h->irq) {
-				ch->sharing_irq = h->sharing_irq = 1;
-				if (ch->chipset != ide_pci || h->chipset != ide_pci)
-					save_match(ch, h, &match);
-
-				/* FIXME: This is still confusing. What would
-				 * happen if we match-ed two times?
-				 */
-
-				if (ch->serialized || h->serialized)
-					save_match(ch, h, &match);
-			}
+		/* scan only initialized channels */
+		if (!h->hwgroup)
+			continue;
+
+		if (ch->irq != h->irq)
+		        continue;
+
+		ch->sharing_irq = h->sharing_irq = 1;
+
+		if (ch->chipset != ide_pci || h->chipset != ide_pci ||
+		     ch->serialized || h->serialized) {
+			if (match && match->hwgroup && match->hwgroup != h->hwgroup)
+				printk("%s: potential irq problem with %s and %s\n", ch->name, h->name, match->name);
+			/* don't undo a prior perfect match */
+			if (!match || match->irq != ch->irq)
+				match = h;
 		}
 	}
 #endif
@@ -721,6 +661,8 @@
 	ch->hwgroup = hwgroup;
 	for (i = 0; i < MAX_DRIVES; ++i) {
 		struct ata_device *drive = &ch->drives[i];
+		request_queue_t *q;
+		int max_sectors = 255;
 
 		if (!drive->present)
 			continue;
@@ -728,7 +670,27 @@
 		if (!hwgroup->XXX_drive)
 			hwgroup->XXX_drive = drive;
 
-		init_device_queue(drive);
+		/*
+		 * Init the per device request queue
+		 */
+
+		q = &drive->queue;
+		q->queuedata = drive->channel;
+		blk_init_queue(q, do_ide_request, &ide_lock);
+		blk_queue_segment_boundary(q, 0xffff);
+
+		/* ATA can do up to 128K per request, pdc4030 needs smaller limit */
+#ifdef CONFIG_BLK_DEV_PDC4030
+		if (drive->channel->chipset == ide_pdc4030)
+			max_sectors = 127;
+#endif
+		blk_queue_max_sectors(q, max_sectors);
+
+		/* IDE DMA can do PRD_ENTRIES number of segments. */
+		blk_queue_max_hw_segments(q, PRD_ENTRIES);
+
+		/* FIXME: This is a driver limit and could be eliminated. */
+		blk_queue_max_phys_segments(q, PRD_ENTRIES);
 	}
 	spin_unlock_irqrestore(&ide_lock, flags);
 
@@ -755,80 +717,10 @@
 }
 
 /*
- * init_gendisk() (as opposed to ide_geninit) is called for each major device,
- * after probing for drives, to allocate partition tables and other data
- * structures needed for the routines in genhd.c.  ide_geninit() gets called
- * somewhat later, during the partition check.
- */
-static void init_gendisk(struct ata_channel *hwif)
-{
-	struct gendisk *gd;
-	unsigned int unit, minors, i;
-	extern devfs_handle_t ide_devfs_handle;
-
-	minors = MAX_DRIVES * (1 << PARTN_BITS);
-
-	gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
-	if (!gd)
-		goto err_kmalloc_gd;
-
-	gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
-	if (!gd->sizes)
-		goto err_kmalloc_gd_sizes;
-
-	gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
-	if (!gd->part)
-		goto err_kmalloc_gd_part;
-	memset(gd->part, 0, minors * sizeof(struct hd_struct));
-
-	for (unit = 0; unit < MAX_DRIVES; ++unit)
-		hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
-
-	gd->major	= hwif->major;		/* our major device number */
-	gd->major_name	= IDE_MAJOR_NAME;	/* treated special in genhd.c */
-	gd->minor_shift	= PARTN_BITS;		/* num bits for partitions */
-	gd->nr_real	= MAX_DRIVES;		/* current num real drives */
-	gd->next	= NULL;			/* linked list of major devs */
-	gd->fops        = ide_fops;             /* file operations */
-	gd->de_arr	= kmalloc(sizeof(*gd->de_arr) * MAX_DRIVES, GFP_KERNEL);
-	gd->flags	= kmalloc(sizeof(*gd->flags) * MAX_DRIVES, GFP_KERNEL);
-	if (gd->de_arr)
-		memset(gd->de_arr, 0, sizeof(*gd->de_arr) * MAX_DRIVES);
-	if (gd->flags)
-		memset(gd->flags, 0, sizeof(*gd->flags) * MAX_DRIVES);
-
-	hwif->gd = gd;
-	add_gendisk(gd);
-
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		char name[80];
-
-		ide_add_generic_settings(hwif->drives + unit);
-		hwif->drives[unit].dn = ((hwif->unit ? 2 : 0) + unit);
-		sprintf (name, "host%d/bus%d/target%d/lun%d",
-			hwif->index, hwif->unit, unit, hwif->drives[unit].lun);
-		if (hwif->drives[unit].present)
-			hwif->drives[unit].de = devfs_mk_dir(ide_devfs_handle, name, NULL);
-	}
-	return;
-
-err_kmalloc_bs:
-	kfree(gd->part);
-err_kmalloc_gd_part:
-	kfree(gd->sizes);
-err_kmalloc_gd_sizes:
-	kfree(gd);
-err_kmalloc_gd:
-	printk(KERN_CRIT "(ide::init_gendisk) Out of memory\n");
-	return;
-}
-
-/*
  * Returns the queue which corresponds to a given device.
  *
  * FIXME: this should take struct block_device * as argument in future.
  */
-
 static request_queue_t *ata_get_queue(kdev_t dev)
 {
 	struct ata_channel *ch = (struct ata_channel *)blk_dev[major(dev)].data;
@@ -837,8 +729,15 @@
 	return &ch->drives[DEVICE_NR(dev) & 1].queue;
 }
 
+/* Number of minor numbers we consume par channel. */
+#define ATA_MINORS	(MAX_DRIVES * (1 << PARTN_BITS))
+
 static void channel_init(struct ata_channel *ch)
 {
+	struct gendisk *gd;
+	unsigned int unit;
+	extern devfs_handle_t ide_devfs_handle;
+
 	if (!ch->present)
 		return;
 
@@ -888,33 +787,106 @@
 		printk(KERN_INFO "%s: probed IRQ %d failed, using default.\n", ch->name, ch->irq);
 	}
 
-	init_gendisk(ch);
+	/* Initialize partition and global device data.  ide_geninit() gets
+	 * called somewhat later, during the partition check.
+	 */
+
+	gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+	if (!gd)
+		goto err_kmalloc_gd;
+
+	gd->sizes = kmalloc(ATA_MINORS * sizeof(int), GFP_KERNEL);
+	if (!gd->sizes)
+		goto err_kmalloc_gd_sizes;
+
+	gd->part = kmalloc(ATA_MINORS * sizeof(struct hd_struct), GFP_KERNEL);
+	if (!gd->part)
+		goto err_kmalloc_gd_part;
+	memset(gd->part, 0, ATA_MINORS * sizeof(struct hd_struct));
+
+	gd->de_arr = kmalloc (sizeof(*gd->de_arr) * MAX_DRIVES, GFP_KERNEL);
+	if (!gd->de_arr)
+		goto err_kmalloc_gd_de_arr;
+	memset(gd->de_arr, 0, sizeof(*gd->de_arr) * MAX_DRIVES);
+
+	gd->flags = kmalloc (sizeof(*gd->flags) * MAX_DRIVES, GFP_KERNEL);
+	if (!gd->flags)
+		goto err_kmalloc_gd_flags;
+	memset(gd->flags, 0, sizeof(*gd->flags) * MAX_DRIVES);
+
+	for (unit = 0; unit < MAX_DRIVES; ++unit)
+		ch->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+	gd->major	= ch->major;		/* our major device number */
+	gd->major_name	= IDE_MAJOR_NAME;	/* treated special in genhd.c */
+	gd->minor_shift	= PARTN_BITS;		/* num bits for partitions */
+	gd->nr_real	= MAX_DRIVES;		/* current num real drives */
+	gd->next	= NULL;			/* linked list of major devs */
+	gd->fops        = ide_fops;             /* file operations */
+
+	gd->de_arr	= kmalloc(sizeof(*gd->de_arr) * MAX_DRIVES, GFP_KERNEL);
+	if (gd->de_arr)
+		memset(gd->de_arr, 0, sizeof(*gd->de_arr) * MAX_DRIVES);
+	else
+	    goto err_kmalloc_gd_de_arr;
+
+	gd->flags	= kmalloc(sizeof(*gd->flags) * MAX_DRIVES, GFP_KERNEL);
+	if (gd->flags)
+		memset(gd->flags, 0, sizeof(*gd->flags) * MAX_DRIVES);
+	else
+	    goto err_kmalloc_gd_flags;
+
+	ch->gd = gd;
+	add_gendisk(gd);
+
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		char name[80];
+
+		ide_add_generic_settings(ch->drives + unit);
+		ch->drives[unit].dn = ((ch->unit ? 2 : 0) + unit);
+		sprintf(name, "host%d/bus%d/target%d/lun%d",
+			ch->index, ch->unit, unit, ch->drives[unit].lun);
+		if (ch->drives[unit].present)
+			ch->drives[unit].de = devfs_mk_dir(ide_devfs_handle, name, NULL);
+	}
+
 	blk_dev[ch->major].data = ch;
 	blk_dev[ch->major].queue = ata_get_queue;
 
-	/* all went well, flag this channel entry as valid */
+	/* All went well, flag this channel entry as valid again. */
 	ch->present = 1;
 
 	return;
+
+err_kmalloc_gd_flags:
+	kfree(gd->de_arr);
+err_kmalloc_gd_de_arr:
+	kfree(gd->part);
+err_kmalloc_gd_part:
+	kfree(gd->sizes);
+err_kmalloc_gd_sizes:
+	kfree(gd);
+err_kmalloc_gd:
+	printk(KERN_CRIT "(%s) Out of memory\n", __FUNCTION__);
 }
 
 int ideprobe_init (void)
 {
-	unsigned int index;
+	unsigned int i;
 	int probe[MAX_HWIFS];
 
-	memset(probe, 0, MAX_HWIFS * sizeof(int));
-	for (index = 0; index < MAX_HWIFS; ++index)
-		probe[index] = !ide_hwifs[index].present;
+	for (i = 0; i < MAX_HWIFS; ++i)
+		probe[i] = !ide_hwifs[i].present;
 
 	/*
 	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
 	 */
-	for (index = 0; index < MAX_HWIFS; ++index)
-		if (probe[index])
-			channel_probe(&ide_hwifs[index]);
-	for (index = 0; index < MAX_HWIFS; ++index)
-		if (probe[index])
-			channel_init(&ide_hwifs[index]);
+	for (i = 0; i < MAX_HWIFS; ++i)
+		if (probe[i])
+			channel_probe(&ide_hwifs[i]);
+	for (i = 0; i < MAX_HWIFS; ++i)
+		if (probe[i])
+			channel_init(&ide_hwifs[i]);
+
 	return 0;
 }
diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
--- a/drivers/ide/ide-tape.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide-tape.c	Sun May  5 20:39:49 2002
@@ -2058,7 +2058,7 @@
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
-		if (drive->channel->udma(ide_dma_end, drive, NULL)) {
+		if (udma_stop(drive)) {
 			/*
 			 * A DMA error is sometimes expected. For example,
 			 * if the tape is crossing a filemark during a
@@ -2132,7 +2132,8 @@
 	if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
 		printk (KERN_ERR "ide-tape: The tape wants to issue more interrupts in DMA mode\n");
 		printk (KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
-		drive->channel->udma(ide_dma_off, drive, NULL);
+		udma_enable(drive, 0, 1);
+
 		return ide_stopped;
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
@@ -2309,11 +2310,15 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) {
 		printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n");
-		(void) drive->channel->udma(ide_dma_off, drive, NULL);
+		udma_enable(drive, 0, 1);
 	}
-	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
-		dma_ok = !drive->channel->udma(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive, rq);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) {
+		if (test_bit (PC_WRITING, &pc->flags))
+			dma_ok = !udma_write(drive, rq);
+		else
+			dma_ok = !udma_read(drive, rq);
+	}
+#endif
 
 	if (IDE_CONTROL_REG)
 		OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
@@ -2324,7 +2329,7 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (dma_ok) {						/* Begin DMA, if necessary */
 		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
-		(void) drive->channel->udma(ide_dma_begin, drive, NULL);
+		udma_start(drive, rq);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA */
 	if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) {
diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
--- a/drivers/ide/ide-taskfile.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/ide-taskfile.c	Sun May  5 20:39:50 2002
@@ -130,12 +130,12 @@
 }
 #endif
 
-static void ata_read_16(ide_drive_t *drive, void *buffer, unsigned int wcount)
+static void ata_read_16(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
 	insw(IDE_DATA_REG, buffer, wcount<<1);
 }
 
-static void ata_write_16(ide_drive_t *drive, void *buffer, unsigned int wcount)
+static void ata_write_16(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
 	outsw(IDE_DATA_REG, buffer, wcount<<1);
 }
@@ -143,7 +143,7 @@
 /*
  * This is used for most PIO data transfers *from* the device.
  */
-void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount)
+void ata_read(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
 	int io_32bit;
 
@@ -178,7 +178,7 @@
 /*
  * This is used for most PIO data transfers *to* the device interface.
  */
-void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount)
+void ata_write(struct ata_device *drive, void *buffer, unsigned int wcount)
 {
 	int io_32bit;
 
@@ -202,7 +202,7 @@
 			ata_write_slow(drive, buffer, wcount);
 		else
 #endif
-			ata_write_16(drive, buffer, wcount<<1);
+			ata_write_16(drive, buffer, wcount);
 	}
 }
 
@@ -213,7 +213,7 @@
  * so if an odd bytecount is specified, be sure that there's at least one
  * extra byte allocated for the buffer.
  */
-void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount)
+void atapi_read(struct ata_device *drive, void *buffer, unsigned int bytecount)
 {
 	if (drive->channel->atapi_read) {
 		drive->channel->atapi_read(drive, buffer, bytecount);
@@ -233,7 +233,7 @@
 		insw(IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
 }
 
-void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount)
+void atapi_write(struct ata_device *drive, void *buffer, unsigned int bytecount)
 {
 	if (drive->channel->atapi_write) {
 		drive->channel->atapi_write(drive, buffer, bytecount);
@@ -256,11 +256,11 @@
 /*
  * Needed for PCI irq sharing
  */
-int drive_is_ready(ide_drive_t *drive)
+int drive_is_ready(struct ata_device *drive)
 {
 	byte stat = 0;
 	if (drive->waiting_for_dma)
-		return drive->channel->udma(ide_dma_test_irq, drive, NULL);
+		return udma_irq_status(drive);
 #if 0
 	/* need to guarantee 400ns since last command was issued */
 	udelay(1);
@@ -290,7 +290,7 @@
  * Stuff the first sector(s) by implicitly calling the handler driectly
  * therafter.
  */
-void ata_poll_drive_ready(ide_drive_t *drive)
+void ata_poll_drive_ready(struct ata_device *drive)
 {
 	int i;
 
@@ -399,7 +399,7 @@
 	return ide_started;
 }
 
-ide_startstop_t ata_taskfile(ide_drive_t *drive,
+ide_startstop_t ata_taskfile(struct ata_device *drive,
 		struct ata_taskfile *args, struct request *rq)
 {
 	struct hd_driveid *id = drive->id;
@@ -456,11 +456,33 @@
 		if (args->prehandler != NULL)
 			return args->prehandler(drive, rq);
 	} else {
-		/* for dma commands we down set the handler */
-		if (drive->using_dma &&
-		!(drive->channel->udma(((args->taskfile.command == WIN_WRITEDMA)
-					|| (args->taskfile.command == WIN_WRITEDMA_EXT))
-					? ide_dma_write : ide_dma_read, drive, rq)));
+		/*
+		 * FIXME: this is a gross hack, need to unify tcq dma proc and
+		 * regular dma proc -- basically split stuff that needs to act
+		 * on a request from things like ide_dma_check etc.
+		 */
+
+		if (!drive->using_dma)
+			return ide_started;
+
+		/* for dma commands we don't set the handler */
+		if (args->taskfile.command == WIN_WRITEDMA
+		 || args->taskfile.command == WIN_WRITEDMA_EXT)
+			udma_write(drive, rq);
+		else if (args->taskfile.command == WIN_READDMA
+		      || args->taskfile.command == WIN_READDMA_EXT)
+			udma_read(drive, rq);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+		else if (args->taskfile.command == WIN_WRITEDMA_QUEUED
+		      || args->taskfile.command == WIN_WRITEDMA_QUEUED_EXT
+		      || args->taskfile.command == WIN_READDMA_QUEUED
+		      || args->taskfile.command == WIN_READDMA_QUEUED_EXT)
+			return udma_tcq_taskfile(drive, rq);
+#endif
+		else {
+			printk("ata_taskfile: unknown command %x\n", args->taskfile.command);
+			return ide_stopped;
+		}
 	}
 
 	return ide_started;
@@ -523,7 +545,7 @@
 	ide__sti();	/* local CPU only */
 
 	if (!OK_STAT(stat = GET_STAT(), READY_STAT, BAD_STAT)) {
-		/* Keep quite for NOP becouse they are expected to fail. */
+		/* Keep quiet for NOP because it is expected to fail. */
 		if (args && args->taskfile.command != WIN_NOP)
 			return ide_error(drive, "task_no_data_intr", stat);
 	}
@@ -872,7 +894,7 @@
 	}
 }
 
-int ide_raw_taskfile(ide_drive_t *drive, struct ata_taskfile *args, byte *buf)
+int ide_raw_taskfile(struct ata_device *drive, struct ata_taskfile *args, byte *buf)
 {
 	struct request rq;
 
@@ -898,7 +920,7 @@
  * interface.
  */
 
-int ide_cmd_ioctl(ide_drive_t *drive, unsigned long arg)
+int ide_cmd_ioctl(struct ata_device *drive, unsigned long arg)
 {
 	int err = 0;
 	u8 vals[4];
diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c
--- a/drivers/ide/ide.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ide.c	Sun May  5 20:39:49 2002
@@ -1,15 +1,19 @@
-/*
- *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
+/**** vi:set ts=8 sts=8 sw=8:************************************************
+ *
+ *  Copyright (C) 1994-1998,2002  Linus Torvalds and authors:
  *
- *  Mostly written by Mark Lord  <mlord@pobox.com>
- *                and Gadi Oxman <gadio@netvision.net.il>
- *                and Andre Hedrick <andre@linux-ide.org>
+ *	Mark Lord	<mlord@pobox.com>
+ *      Gadi Oxman	<gadio@netvision.net.il>
+ *      Andre Hedrick	<andre@linux-ide.org>
+ *	Jens Axboe	<axboe@suse.de>
+ *      Marcin Dalecki	<martin@dalecki.de>
  *
  *  See linux/MAINTAINERS for address of current maintainer.
  *
- * This is the multiple IDE interface driver, as evolved from hd.c.
- * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15).
- * There can be up to two drives per interface, as per the ATA-2 spec.
+ * This is the basic common code of the ATA interface drivers.
+ *
+ * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14
+ * & 15).  There can be up to two drives per interface, as per the ATA-7 spec.
  *
  * Primary:    ide0, port 0x1f0; major=3;  hda is minor=0; hdb is minor=64
  * Secondary:  ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64
@@ -17,102 +21,15 @@
  * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64
  * ...
  *
- *  From hd.c:
- *  |
- *  | It traverses the request-list, using interrupts to jump between functions.
- *  | As nearly all functions can be called within interrupts, we may not sleep.
- *  | Special care is recommended.  Have Fun!
- *  |
- *  | modified by Drew Eckhardt to check nr of hd's from the CMOS.
- *  |
- *  | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
- *  | in the early extended-partition checks and added DM partitions.
- *  |
- *  | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
- *  |
- *  | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
- *  | and general streamlining by Mark Lord (mlord@pobox.com).
- *
- *  October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
- *
- *	Mark Lord	(mlord@pobox.com)		(IDE Perf.Pkg)
- *	Delman Lee	(delman@ieee.org)		("Mr. atdisk2")
- *	Scott Snyder	(snyder@fnald0.fnal.gov)	(ATAPI IDE cd-rom)
- *
- *  This was a rewrite of just about everything from hd.c, though some original
- *  code is still sprinkled about.  Think of it as a major evolution, with
- *  inspiration from lots of linux users, esp.  hamish@zot.apana.org.au
- *
- *  Version 1.0 ALPHA	initial code, primary i/f working okay
- *  Version 1.3 BETA	dual i/f on shared irq tested & working!
- *  Version 1.4 BETA	added auto probing for irq(s)
- *  Version 1.5 BETA	added ALPHA (untested) support for IDE cd-roms,
- *  ...
- * Version 5.50		allow values as small as 20 for idebus=
- * Version 5.51		force non io_32bit in drive_cmd_intr()
- *			change delay_10ms() to delay_50ms() to fix problems
- * Version 5.52		fix incorrect invalidation of removable devices
- *			add "hdx=slow" command line option
- * Version 5.60		start to modularize the driver; the disk and ATAPI
- *			 drivers can be compiled as loadable modules.
- *			move IDE probe code to ide-probe.c
- *			move IDE disk code to ide-disk.c
- *			add support for generic IDE device subdrivers
- *			add m68k code from Geert Uytterhoeven
- *			probe all interfaces by default
- *			add ioctl to (re)probe an interface
- * Version 6.00		use per device request queues
- *			attempt to optimize shared hwgroup performance
- *			add ioctl to manually adjust bandwidth algorithms
- *			add kerneld support for the probe module
- *			fix bug in ide_error()
- *			fix bug in the first ide_get_lock() call for Atari
- *			don't flush leftover data for ATAPI devices
- * Version 6.01		clear hwgroup->active while the hwgroup sleeps
- *			support HDIO_GETGEO for floppies
- * Version 6.02		fix ide_ack_intr() call
- *			check partition table on floppies
- * Version 6.03		handle bad status bit sequencing in ide_wait_stat()
- * Version 6.10		deleted old entries from this list of updates
- *			replaced triton.c with ide-dma.c generic PCI DMA
- *			added support for BIOS-enabled UltraDMA
- *			rename all "promise" things to "pdc4030"
- *			fix EZ-DRIVE handling on small disks
- * Version 6.11		fix probe error in ide_scan_devices()
- *			fix ancient "jiffies" polling bugs
- *			mask all hwgroup interrupts on each irq entry
- * Version 6.12		integrate ioctl and proc interfaces
- *			fix parsing of "idex=" command line parameter
- * Version 6.13		add support for ide4/ide5 courtesy rjones@orchestream.com
- * Version 6.14		fixed IRQ sharing among PCI devices
- * Version 6.15		added SMP awareness to IDE drivers
- * Version 6.16		fixed various bugs; even more SMP friendly
- * Version 6.17		fix for newest EZ-Drive problem
- * Version 6.18		default unpartitioned-disk translation now "BIOS LBA"
- * Version 6.19		Re-design for a UNIFORM driver for all platforms,
- *			  model based on suggestions from Russell King and
- *			  Geert Uytterhoeven
- *			Promise DC4030VL now supported.
- *			add support for ide6/ide7
- *			delay_50ms() changed to ide_delay_50ms() and exported.
- * Version 6.20		Added/Fixed Generic ATA-66 support and hwif detection.
- *			Added hdx=flash to allow for second flash disk
- *			  detection w/o the hang loop.
- *			Added support for ide8/ide9
- *			Added idex=ata66 for the quirky chipsets that are
- *			  ATA-66 compliant, but have yet to determine a method
- *			  of verification of the 80c cable presence.
- *			  Specifically Promise's PDC20262 chipset.
- * Version 6.21		Fixing/Fixed SMP spinlock issue with insight from an old
- *			  hat that clarified original low level driver design.
- * Version 6.30		Added SMP support; fixed multmode issues.  -ml
- * Version 6.31		Debug Share INTR's and request queue streaming
- *			Native ATA-100 support
- *			Prep for Cascades Project
- * Version 6.32		4GB highmem support for DMA, and mapping of those for
- *			PIO transfer (Jens Axboe)
+ *  Contributors:
  *
- *  Some additional driver compile-time options are in ./include/linux/ide.h
+ *	Drew Eckhardt
+ *	Branko Lankester	<lankeste@fwi.uva.nl>
+ *	Mika Liljeberg
+ *	Delman Lee		<delman@ieee.org>
+ *	Scott Snyder		<snyder@fnald0.fnal.gov>
+ *
+ *  Some additional driver compile-time options are in <linux/ide.h>
  */
 
 #define	VERSION	"7.0.0"
@@ -253,10 +170,7 @@
 #endif
 }
 
-/*
- * Do not even *think* about calling this!
- */
-static void init_hwif_data(struct ata_channel *hwif, unsigned int index)
+static void init_hwif_data(struct ata_channel *ch, unsigned int index)
 {
 	static const byte ide_major[] = {
 		IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR,
@@ -266,30 +180,30 @@
 	unsigned int unit;
 	hw_regs_t hw;
 
-	/* bulk initialize hwif & drive info with zeros */
-	memset(hwif, 0, sizeof(struct ata_channel));
+	/* bulk initialize channel & drive info with zeros */
+	memset(ch, 0, sizeof(struct ata_channel));
 	memset(&hw, 0, sizeof(hw_regs_t));
 
 	/* fill in any non-zero initial values */
-	hwif->index     = index;
-	ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq);
-	memcpy(&hwif->hw, &hw, sizeof(hw));
-	memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports));
-	hwif->noprobe	= !hwif->io_ports[IDE_DATA_OFFSET];
+	ch->index     = index;
+	ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &ch->irq);
+	memcpy(&ch->hw, &hw, sizeof(hw));
+	memcpy(ch->io_ports, hw.io_ports, sizeof(hw.io_ports));
+	ch->noprobe	= !ch->io_ports[IDE_DATA_OFFSET];
 #ifdef CONFIG_BLK_DEV_HD
-	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
-		hwif->noprobe = 1; /* may be overridden by ide_setup() */
+	if (ch->io_ports[IDE_DATA_OFFSET] == HD_DATA)
+		ch->noprobe = 1; /* may be overridden by ide_setup() */
 #endif /* CONFIG_BLK_DEV_HD */
-	hwif->major = ide_major[index];
-	sprintf(hwif->name, "ide%d", index);
-	hwif->bus_state = BUSSTATE_ON;
+	ch->major = ide_major[index];
+	sprintf(ch->name, "ide%d", index);
+	ch->bus_state = BUSSTATE_ON;
 
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		struct ata_device *drive = &hwif->drives[unit];
+		struct ata_device *drive = &ch->drives[unit];
 
 		drive->type			= ATA_DISK;
 		drive->select.all		= (unit<<4)|0xa0;
-		drive->channel			= hwif;
+		drive->channel			= ch;
 		drive->ctl			= 0x08;
 		drive->ready_stat		= READY_STAT;
 		drive->bad_wstat		= BAD_W_STAT;
@@ -312,7 +226,7 @@
  * of the kernel (such as memory allocation) to be functioning yet.
  *
  * This is too bad, as otherwise we could dynamically allocate the
- * ide_drive_t structs as needed, rather than always consuming memory
+ * ata_device structs as needed, rather than always consuming memory
  * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
  */
 #define MAGIC_COOKIE 0x12345678
@@ -349,24 +263,29 @@
  * At that time, we might also consider parameterizing the timeouts and retries,
  * since these are MUCH faster than mechanical drives.	-M.Lord
  */
-int drive_is_flashcard (ide_drive_t *drive)
+int drive_is_flashcard(struct ata_device *drive)
 {
 	struct hd_driveid *id = drive->id;
+	int i;
+
+	char *flashcards[] = {
+		"KODAK ATA_FLASH",
+		"Hitachi CV",
+		"SunDisk SDCFB",
+		"HAGIWARA HPC",
+		"LEXAR ATA_FLASH",
+		"ATA_FLASH"		/* Simple Tech */
+	};
 
 	if (drive->removable && id != NULL) {
 		if (id->config == 0x848a)
 			return 1;	/* CompactFlash */
-		if (!strncmp(id->model, "KODAK ATA_FLASH", 15)	/* Kodak */
-		 || !strncmp(id->model, "Hitachi CV", 10)	/* Hitachi */
-		 || !strncmp(id->model, "SunDisk SDCFB", 13)	/* SunDisk */
-		 || !strncmp(id->model, "HAGIWARA HPC", 12)	/* Hagiwara */
-		 || !strncmp(id->model, "LEXAR ATA_FLASH", 15)	/* Lexar */
-		 || !strncmp(id->model, "ATA_FLASH", 9))	/* Simple Tech */
-		{
-			return 1;	/* yes, it is a flash memory card */
-		}
+		for (i = 0; i < ARRAY_SIZE(flashcards); i++)
+			if (!strncmp(id->model, flashcards[i],
+				     strlen(flashcards[i])))
+				return 1;
 	}
-	return 0;	/* no, it is not a flash memory card */
+	return 0;
 }
 
 int __ide_end_request(struct ata_device *drive, struct request *rq, int uptodate, int nr_secs)
@@ -392,12 +311,15 @@
 
 	if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) {
 		drive->state = 0;
-		drive->channel->udma(ide_dma_on, drive, rq);
+		udma_enable(drive, 1, 1);
 	}
 
 	if (!end_that_request_first(rq, uptodate, nr_secs)) {
 		add_blkdev_randomness(major(rq->rq_dev));
-		blkdev_dequeue_request(rq);
+		if (!blk_rq_tagged(rq))
+			blkdev_dequeue_request(rq);
+		else
+			blk_queue_end_tag(&drive->queue, rq);
 		HWGROUP(drive)->rq = NULL;
 		end_that_request_last(rq);
 		ret = 0;
@@ -442,13 +364,13 @@
 
 	/* check the DMA crc count */
 	if (drive->crc_count) {
-		drive->channel->udma(ide_dma_off_quietly, drive, NULL);
+		udma_enable(drive, 0, 0);
 		if ((drive->channel->speedproc) != NULL)
 		        drive->channel->speedproc(drive, ide_auto_reduce_xfer(drive));
 		if (drive->current_speed >= XFER_SW_DMA_0)
-			drive->channel->udma(ide_dma_on, drive, NULL);
+			udma_enable(drive, 1, 1);
 	} else
-		drive->channel->udma(ide_dma_off, drive, NULL);
+		udma_enable(drive, 0, 1);
 }
 
 /*
@@ -529,7 +451,7 @@
 	}
 }
 
-static ide_startstop_t do_reset1(ide_drive_t *, int);		/* needed below */
+static ide_startstop_t do_reset1(struct ata_device *, int); /* needed below */
 
 /*
  * Poll the interface for completion every 50ms during an ATAPI drive reset
@@ -629,7 +551,7 @@
  * for it, we set a timer to poll at 50ms intervals.
  */
 
-static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+static ide_startstop_t do_reset1(struct ata_device *drive, int do_not_try_atapi)
 {
 	unsigned int unit;
 	unsigned long flags;
@@ -697,7 +619,7 @@
 	return ide_started;
 }
 
-static inline u32 read_24 (ide_drive_t *drive)
+static inline u32 read_24(struct ata_device *drive)
 {
 	return  (IN_BYTE(IDE_HCYL_REG)<<16) |
 		(IN_BYTE(IDE_LCYL_REG)<<8) |
@@ -761,7 +683,7 @@
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
  */
-byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
+byte ide_dump_status(struct ata_device *drive, const char *msg, byte stat)
 {
 	unsigned long flags;
 	byte err = 0;
@@ -853,7 +775,7 @@
  * condition by reading a sector's worth of data from the drive.  Of course,
  * this may not help if the drive is *waiting* for data from *us*.
  */
-static void try_to_flush_leftover_data (ide_drive_t *drive)
+static void try_to_flush_leftover_data(struct ata_device *drive)
 {
 	int i;
 
@@ -870,7 +792,7 @@
 /*
  * Take action based on the error returned by the drive.
  */
-ide_startstop_t ide_error(ide_drive_t *drive, const char *msg, byte stat)
+ide_startstop_t ide_error(struct ata_device *drive, const char *msg, byte stat)
 {
 	struct request *rq;
 	byte err;
@@ -927,7 +849,7 @@
 /*
  * Issue a simple drive command.  The drive must be selected beforehand.
  */
-void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ata_handler_t handler)
+void ide_cmd(struct ata_device *drive, byte cmd, byte nsect, ata_handler_t handler)
 {
 	ide_set_handler (drive, handler, WAIT_CMD, NULL);
 	if (IDE_CONTROL_REG)
@@ -972,7 +894,7 @@
  * setting a timer to wake up at half second intervals thereafter, until
  * timeout is achieved, before timing out.
  */
-int ide_wait_stat(ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) {
+int ide_wait_stat(ide_startstop_t *startstop, struct ata_device *drive, byte good, byte bad, unsigned long timeout) {
 	byte stat;
 	int i;
 	unsigned long flags;
@@ -1016,7 +938,7 @@
 /*
  * This initiates handling of a new I/O request.
  */
-static ide_startstop_t start_request(ide_drive_t *drive, struct request *rq)
+static ide_startstop_t start_request(struct ata_device *drive, struct request *rq)
 {
 	sector_t block;
 	unsigned int minor = minor(rq->rq_dev);
@@ -1164,7 +1086,7 @@
 	return ide_stopped;
 }
 
-ide_startstop_t restart_request(ide_drive_t *drive)
+ide_startstop_t restart_request(struct ata_device *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned long flags;
@@ -1183,7 +1105,7 @@
  * This is used by a drive to give excess bandwidth back to the hwgroup by
  * sleeping for timeout jiffies.
  */
-void ide_stall_queue(ide_drive_t *drive, unsigned long timeout)
+void ide_stall_queue(struct ata_device *drive, unsigned long timeout)
 {
 	if (timeout > WAIT_WORSTCASE)
 		timeout = WAIT_WORSTCASE;
@@ -1258,7 +1180,7 @@
 
 			/* This device still wants to remain idle.
 			 */
-			if (drive->sleep && time_after(jiffies, drive->sleep))
+			if (drive->sleep && time_after(drive->sleep, jiffies))
 				continue;
 
 			/* Take this device, if there is no device choosen thus far or
@@ -1285,8 +1207,8 @@
 		 * want to hog the cpu too much.
 		 */
 
-		if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
-		    sleep = jiffies + WAIT_MIN_SLEEP;
+		if (time_after(jiffies, sleep - WAIT_MIN_SLEEP))
+			sleep = jiffies + WAIT_MIN_SLEEP;
 #if 1
 		if (timer_pending(&channel->hwgroup->timer))
 			printk(KERN_ERR "ide_set_handler: timer already active\n");
@@ -1305,11 +1227,6 @@
 }
 
 
-/* Place holders for later expansion of functionality.
- */
-#define ata_pending_commands(drive)	(0)
-#define ata_can_queue(drive)		(1)
-
 /*
  * Feed commands to a drive until it barfs.  Called with ide_lock/DRIVE_LOCK
  * held and busy channel.
@@ -1349,7 +1266,7 @@
 		 * still a severe BUG!
 		 */
 		if (blk_queue_plugged(&drive->queue)) {
-			BUG();
+			BUG_ON(!drive->using_tcq);
 			break;
 		}
 
@@ -1485,18 +1402,11 @@
  */
 static void dma_timeout_retry(struct ata_device *drive, struct request *rq)
 {
-	struct ata_channel *ch = drive->channel;
-
 	/*
 	 * end current dma transaction
 	 */
-	ch->udma(ide_dma_end, drive, rq);
-
-	/*
-	 * complain a little, later we might remove some of this verbosity
-	 */
-	printk("%s: timeout waiting for DMA\n", drive->name);
-	ch->udma(ide_dma_timeout, drive, rq);
+	udma_stop(drive);
+	udma_timeout(drive);
 
 	/*
 	 * Disable dma for now, but remember that we did so because of
@@ -1505,7 +1415,7 @@
 	 */
 	drive->retry_pio++;
 	drive->state = DMA_PIO_RETRY;
-	ch->udma(ide_dma_off_quietly, drive, rq);
+	udma_enable(drive, 0, 0);
 
 	/*
 	 * un-busy drive etc (hwgroup->busy is cleared on return) and
@@ -1593,7 +1503,7 @@
 				startstop = handler(drive, ch->hwgroup->rq);
 			} else if (drive_is_ready(drive)) {
 				if (drive->waiting_for_dma)
-					ch->udma(ide_dma_lostirq, drive, ch->hwgroup->rq);
+					udma_irq_lost(drive);
 				(void) ide_ack_intr(ch);
 				printk("%s: lost interrupt\n", drive->name);
 				startstop = handler(drive, ch->hwgroup->rq);
@@ -1761,17 +1671,18 @@
 		} else {
 			printk("%s: %s: huh? expected NULL handler on exit\n", drive->name, __FUNCTION__);
 		}
-	}
+	} else if (startstop == ide_released)
+		queue_commands(drive, ch->irq);
 
 out_lock:
 	spin_unlock_irqrestore(&ide_lock, flags);
 }
 
 /*
- * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * get_info_ptr() returns the (struct ata_device *) for a given device number.
  * It returns NULL if the given device number does not match any present drives.
  */
-ide_drive_t *get_info_ptr(kdev_t i_rdev)
+struct ata_device *get_info_ptr(kdev_t i_rdev)
 {
 	unsigned int major = major(i_rdev);
 	int h;
@@ -2099,25 +2010,13 @@
 	if (ch->straight8) {
 		release_region(ch->io_ports[IDE_DATA_OFFSET], 8);
 	} else {
-		if (ch->io_ports[IDE_DATA_OFFSET])
-			release_region(ch->io_ports[IDE_DATA_OFFSET], 1);
-		if (ch->io_ports[IDE_ERROR_OFFSET])
-			release_region(ch->io_ports[IDE_ERROR_OFFSET], 1);
-		if (ch->io_ports[IDE_NSECTOR_OFFSET])
-			release_region(ch->io_ports[IDE_NSECTOR_OFFSET], 1);
-		if (ch->io_ports[IDE_SECTOR_OFFSET])
-			release_region(ch->io_ports[IDE_SECTOR_OFFSET], 1);
-		if (ch->io_ports[IDE_LCYL_OFFSET])
-			release_region(ch->io_ports[IDE_LCYL_OFFSET], 1);
-		if (ch->io_ports[IDE_HCYL_OFFSET])
-			release_region(ch->io_ports[IDE_HCYL_OFFSET], 1);
-		if (ch->io_ports[IDE_SELECT_OFFSET])
-			release_region(ch->io_ports[IDE_SELECT_OFFSET], 1);
-		if (ch->io_ports[IDE_STATUS_OFFSET])
-			release_region(ch->io_ports[IDE_STATUS_OFFSET], 1);
+		for (i = 0; i < 8; i++)
+			if (ch->io_ports[i])
+				release_region(ch->io_ports[i], 1);
 	}
 	if (ch->io_ports[IDE_CONTROL_OFFSET])
 		release_region(ch->io_ports[IDE_CONTROL_OFFSET], 1);
+/* FIXME: check if we can remove this ifdef */
 #if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
 	if (ch->io_ports[IDE_IRQ_OFFSET])
 		release_region(ch->io_ports[IDE_IRQ_OFFSET], 1);
@@ -2216,12 +2115,18 @@
 	ch->intrproc = old.intrproc;
 	ch->maskproc = old.maskproc;
 	ch->quirkproc = old.quirkproc;
-	ch->rwproc	= old.rwproc;
 	ch->ata_read = old.ata_read;
 	ch->ata_write = old.ata_write;
 	ch->atapi_read = old.atapi_read;
 	ch->atapi_write = old.atapi_write;
-	ch->udma = old.udma;
+	ch->XXX_udma = old.XXX_udma;
+	ch->udma_start = old.udma_start;
+	ch->udma_stop = old.udma_stop;
+	ch->udma_read = old.udma_read;
+	ch->udma_write = old.udma_write;
+	ch->udma_irq_status = old.udma_irq_status;
+	ch->udma_timeout = old.udma_timeout;
+	ch->udma_irq_lost = old.udma_irq_lost;
 	ch->busproc = old.busproc;
 	ch->bus_state = old.bus_state;
 	ch->dma_base = old.dma_base;
@@ -2259,24 +2164,18 @@
 	int i;
 
 	for (i = 0; i < IDE_NR_PORTS; i++) {
-		if (offsets[i] == -1) {
-			switch(i) {
-				case IDE_CONTROL_OFFSET:
-					hw->io_ports[i] = ctrl;
-					break;
-#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
-				case IDE_IRQ_OFFSET:
-					hw->io_ports[i] = intr;
-					break;
-#endif
-				default:
-					hw->io_ports[i] = 0;
-					break;
-			}
-		} else {
+		if (offsets[i] != -1)
 			hw->io_ports[i] = base + offsets[i];
-		}
+		else
+			hw->io_ports[i] = 0;
 	}
+	if (offsets[IDE_CONTROL_OFFSET] == -1)
+		hw->io_ports[IDE_CONTROL_OFFSET] = ctrl;
+/* FIMXE: check if we can remove this ifdef */
+#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC)
+	if (offsets[IDE_IRQ_OFFSET] == -1)
+		hw->io_ports[IDE_IRQ_OFFSET] = intr;
+#endif
 	hw->irq = irq;
 	hw->dma = NO_DMA;
 	hw->ack_intr = ack_intr;
@@ -2348,7 +2247,7 @@
 	return ide_register_hw(&hw, NULL);
 }
 
-void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+void ide_add_setting(struct ata_device *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
 {
 	ide_settings_t **p = &drive->settings;
 	ide_settings_t *setting = NULL;
@@ -2375,7 +2274,7 @@
 		kfree(setting);
 }
 
-void ide_remove_setting (ide_drive_t *drive, char *name)
+void ide_remove_setting(struct ata_device *drive, char *name)
 {
 	ide_settings_t **p = &drive->settings, *setting;
 
@@ -2388,7 +2287,7 @@
 	kfree(setting);
 }
 
-static void auto_remove_settings (ide_drive_t *drive)
+static void auto_remove_settings(struct ata_device *drive)
 {
 	ide_settings_t *setting;
 repeat:
@@ -2402,10 +2301,10 @@
 	}
 }
 
-int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
+int ide_read_setting(struct ata_device *drive, ide_settings_t *setting)
 {
-	int		val = -EINVAL;
-	unsigned long	flags;
+	int val = -EINVAL;
+	unsigned long flags;
 
 	if ((setting->rw & SETTING_READ)) {
 		spin_lock_irqsave(&ide_lock, flags);
@@ -2426,7 +2325,7 @@
 	return val;
 }
 
-int ide_spin_wait_hwgroup (ide_drive_t *drive)
+int ide_spin_wait_hwgroup(struct ata_device *drive)
 {
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	unsigned long timeout = jiffies + (3 * HZ);
@@ -2454,7 +2353,7 @@
  * to the driver to change settings, and then wait on a semaphore for completion.
  * The current scheme of polling is kludgey, though safe enough.
  */
-int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
+int ide_write_setting(struct ata_device *drive, ide_settings_t *setting, int val)
 {
 	int i;
 	u32 *p;
@@ -2499,18 +2398,19 @@
 	return 0;
 }
 
-static int set_using_dma(ide_drive_t *drive, int arg)
+static int set_using_dma(struct ata_device *drive, int arg)
 {
 	if (!drive->driver)
 		return -EPERM;
-	if (!drive->id || !(drive->id->capability & 1) || !drive->channel->udma)
+
+	if (!drive->id || !(drive->id->capability & 1) || !drive->channel->XXX_udma)
 		return -EPERM;
-	if (drive->channel->udma(arg ? ide_dma_on : ide_dma_off, drive, NULL))
-		return -EIO;
+
+	udma_enable(drive, arg, 1);
 	return 0;
 }
 
-static int set_pio_mode(ide_drive_t *drive, int arg)
+static int set_pio_mode(struct ata_device *drive, int arg)
 {
 	struct request rq;
 
@@ -2528,7 +2428,7 @@
 	return 0;
 }
 
-void ide_add_generic_settings (ide_drive_t *drive)
+void ide_add_generic_settings(struct ata_device *drive)
 {
 /*			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function */
 	ide_add_setting(drive,	"io_32bit",		drive->channel->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->channel->io_32bit,		set_io_32bit);
@@ -2563,7 +2463,7 @@
 static int ide_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	int err = 0, major, minor;
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	struct request rq;
 	kdev_t dev;
 	ide_settings_t *setting;
@@ -2717,7 +2617,7 @@
 
 static int ide_check_media_change (kdev_t i_rdev)
 {
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	int res = 0; /* not changed */
 
 	drive = get_info_ptr(i_rdev);
@@ -2861,7 +2761,7 @@
  * "hdx=flash"		: allows for more than one ata_flash disk to be
  *				registered. In most cases, only one device
  *				will be present.
- * "hdx=scsi"		: the return of the ide-scsi flag, this is useful for
+ * "hdx=ide-scsi"	: the return of the ide-scsi flag, this is useful for
  *				allowwing ide-floppy, ide-tape, and ide-cdrom|writers
  *				to use ide-scsi emulation on a device specific option.
  * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
@@ -2915,7 +2815,7 @@
 {
 	int i, vals[3];
 	struct ata_channel *hwif;
-	ide_drive_t *drive;
+	struct ata_device *drive;
 	unsigned int hw, unit;
 	const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
 	const char max_hwif  = '0' + (MAX_HWIFS - 1);
@@ -2928,14 +2828,14 @@
 	    strncmp(s,"hd",2))		/* hdx= & hdxlun= */
 		return 0;
 
-	printk("ide_setup: %s", s);
+	printk(KERN_INFO  "ide_setup: %s", s);
 	init_ide_data ();
 
 #ifdef CONFIG_BLK_DEV_IDEDOUBLER
 	if (!strcmp(s, "ide=doubler")) {
 		extern int ide_doubler;
 
-		printk(" : Enabled support for IDE doublers\n");
+		printk(KERN_INFO" : Enabled support for IDE doublers\n");
 		ide_doubler = 1;
 
 		return 1;
@@ -2943,7 +2843,7 @@
 #endif
 
 	if (!strcmp(s, "ide=nodma")) {
-		printk("IDE: Prevented DMA\n");
+		printk(KERN_INFO "ATA: Prevented DMA\n");
 		noautodma = 1;
 
 		return 1;
@@ -3231,7 +3131,7 @@
 /*
  * Lookup ATA devices, which requested a particular driver.
  */
-ide_drive_t *ide_scan_devices(byte type, const char *name, struct ata_operations *driver, int n)
+struct ata_device *ide_scan_devices(byte type, const char *name, struct ata_operations *driver, int n)
 {
 	unsigned int unit, index, i;
 
@@ -3258,7 +3158,7 @@
 /*
  * This is in fact registering a drive not a driver.
  */
-int ide_register_subdriver(ide_drive_t *drive, struct ata_operations *driver)
+int ide_register_subdriver(struct ata_device *drive, struct ata_operations *driver)
 {
 	unsigned long flags;
 
@@ -3278,7 +3178,7 @@
 	restore_flags(flags);		/* all CPUs */
 	/* FIXME: Check what this magic number is supposed to be about? */
 	if (drive->autotune != 2) {
-		if (drive->channel->udma) {
+		if (drive->channel->XXX_udma) {
 
 			/*
 			 * Force DMAing for the beginning of the check.  Some
@@ -3288,8 +3188,11 @@
 			 *   PARANOIA!!!
 			 */
 
-			drive->channel->udma(ide_dma_off_quietly, drive, NULL);
-			drive->channel->udma(ide_dma_check, drive, NULL);
+			udma_enable(drive, 0, 0);
+			drive->channel->XXX_udma(drive);
+#ifdef CONFIG_BLK_DEV_IDE_TCQ_DEFAULT
+			udma_tcq_enable(drive, 1);
+#endif
 		}
 
 		/* Only CD-ROMs and tape drives support DSC overlap.  But only
@@ -3322,7 +3225,7 @@
  *
  * FIXME: Check whatever we maybe don't call it twice!.
  */
-int ide_unregister_subdriver(ide_drive_t *drive)
+int ide_unregister_subdriver(struct ata_device *drive)
 {
 	unsigned long flags;
 
@@ -3497,7 +3400,7 @@
 {
 	int h;
 
-	printk(KERN_INFO "Uniform Multi-Platform E-IDE driver ver.:" VERSION "\n");
+	printk(KERN_INFO "ATA/ATAPI driver v" VERSION "\n");
 
 	ide_devfs_handle = devfs_mk_dir (NULL, "ide", NULL);
 
@@ -3519,7 +3422,7 @@
 	    system_bus_speed = 33;
 #endif
 
-	printk("ide: system bus speed %dMHz\n", system_bus_speed);
+	printk(KERN_INFO "ATA: system bus speed %dMHz\n", system_bus_speed);
 
 	init_ide_data ();
 
@@ -3640,27 +3543,23 @@
 MODULE_PARM(options,"s");
 MODULE_LICENSE("GPL");
 
-static void __init parse_options (char *line)
+static int __init init_ata(void)
 {
-	char *next = line;
 
-	if (line == NULL || !*line)
-		return;
-	while ((line = next) != NULL) {
-		if ((next = strchr(line,' ')) != NULL)
-			*next++ = 0;
-		if (!ide_setup(line))
-			printk ("Unknown option '%s'\n", line);
-	}
-}
+	if (options != NULL && *options) {
+		char *next = options;
 
-static int __init init_ata (void)
-{
-	parse_options(options);
+		while ((options = next) != NULL) {
+			if ((next = strchr(options,' ')) != NULL)
+				*next++ = 0;
+			if (!ide_setup(options))
+				printk(KERN_ERR "Unknown option '%s'\n", options);
+		}
+	}
 	return ata_module_init();
 }
 
-static void __exit cleanup_ata (void)
+static void __exit cleanup_ata(void)
 {
 	int h;
 
diff -Nru a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c
--- a/drivers/ide/ns87415.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/ns87415.c	Sun May  5 20:39:49 2002
@@ -82,35 +82,57 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-static int ns87415_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+
+static int ns87415_udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	drive->waiting_for_dma = 0;
+	dma_stat = inb(ch->dma_base+2);
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	outb(inb(dma_base)|6, dma_base);	/* from ERRATA: clear the INTR & ERROR bits */
+	udma_destroy_table(ch);				/* and free any DMA resources */
+
+	return (dma_stat & 7) != 4;	/* verify good DMA status */
+
+}
+
+static int ns87415_udma_read(struct ata_device *drive, struct request *rq)
 {
-	struct ata_channel *hwif = drive->channel;
-	byte		dma_stat;
+	ns87415_prepare_drive(drive, 1);	/* select DMA xfer */
 
-	switch (func) {
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			drive->waiting_for_dma = 0;
-			dma_stat = inb(hwif->dma_base+2);
-			outb(inb(hwif->dma_base)&~1, hwif->dma_base);	/* stop DMA */
-			outb(inb(hwif->dma_base)|6, hwif->dma_base);	/* from ERRATA: clear the INTR & ERROR bits */
-			ide_destroy_dmatable(drive);			/* and free any DMA resources */
-			return (dma_stat & 7) != 4;		/* verify good DMA status */
-		case ide_dma_write:
-		case ide_dma_read:
-			ns87415_prepare_drive(drive, 1);	/* select DMA xfer */
-			if (!ide_dmaproc(func, drive, rq))		/* use standard DMA stuff */
-				return 0;
-			ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
-			return 1;
-		case ide_dma_check:
-			if (drive->type != ATA_DISK)
-				return ide_dmaproc(ide_dma_off_quietly, drive, rq);
-			/* Fallthrough... */
-		default:
-			return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+	if (!ata_do_udma(1, drive, rq))		/* use standard DMA stuff */
+		return 0;
+
+	ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
+
+	return 1;
+}
+
+static int ns87415_udma_write(struct ata_device *drive, struct request *rq)
+{
+	ns87415_prepare_drive(drive, 1);	/* select DMA xfer */
+
+	if (!ata_do_udma(0, drive, rq))		/* use standard DMA stuff */
+		return 0;
+
+	ns87415_prepare_drive(drive, 0);	/* DMA failed: select PIO xfer */
+
+	return 1;
+}
+
+static int ns87415_dmaproc(struct ata_device *drive)
+{
+	if (drive->type != ATA_DISK) {
+		udma_enable(drive, 0, 0);
+
+		return 0;
 	}
+	return XXX_ide_dmaproc(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 void __init ide_init_ns87415(struct ata_channel *hwif)
 {
@@ -205,8 +227,12 @@
 	}
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-	if (hwif->dma_base)
-		hwif->udma = ns87415_dmaproc;
+	if (hwif->dma_base) {
+		hwif->udma_stop = ns87415_udma_stop;
+		hwif->udma_read = ns87415_udma_read;
+		hwif->udma_write = ns87415_udma_write;
+		hwif->XXX_udma = ns87415_dmaproc;
+	}
 #endif
 
 	hwif->selectproc = &ns87415_selectproc;
diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c
--- a/drivers/ide/pdc202xx.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/pdc202xx.c	Sun May  5 20:39:50 2002
@@ -829,7 +829,7 @@
 
 	if (jumpbit) {
 		if (drive->type != ATA_DISK)
-			return ide_dma_off_quietly;
+			return 0;
 		if (id->capability & 4) {	/* IORDY_EN & PREFETCH_EN */
 			OUT_BYTE((iordy + adj), indexreg);
 			OUT_BYTE((IN_BYTE(datareg)|0x03), datareg);
@@ -873,13 +873,13 @@
 				pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN);
 			break;
 		default:
-			return ide_dma_off;
+			return 0;
 	}
 
 chipset_is_set:
 
 	if (drive->type != ATA_DISK)
-		return ide_dma_off_quietly;
+		return 0;
 
 	pci_read_config_byte(dev, (drive_pci), &AP);
 	if (id->capability & 4)	/* IORDY_EN */
@@ -907,39 +907,41 @@
 		/* restore original pci-config space */
 		if (!jumpbit)
 			pci_write_config_dword(dev, drive_pci, drive_conf);
-		return ide_dma_off_quietly;
+		return 0;
 	}
 
 	outb(inb(dma_base+2) & ~(1<<(5+unit)), dma_base+2);
 	(void) hwif->speedproc(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 14) & 3) ? ide_dma_on :
-			((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on : 
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 14) & 3) ? 1 :
+			((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
 	struct hd_driveid *id = drive->id;
 	struct ata_channel *hwif = drive->channel;
-	ide_dma_action_t dma_func = ide_dma_off_quietly;
+	int on = 0;
+	int verbose = 1;
 
 	if (id && (id->capability & 1) && hwif->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x007F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
+				on = config_chipset_for_dma(drive, 1);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -947,29 +949,32 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, 0);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, 0);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		(void) config_chipset_for_pio(drive, 5);
 	}
 
-	return drive->channel->udma(dma_func, drive, NULL);
+	udma_enable(drive, on, verbose);
+
+	return 0;
 }
 
 int pdc202xx_quirkproc (ide_drive_t *drive)
@@ -977,21 +982,15 @@
 	return ((int) check_in_drive_lists(drive, pdc_quirk_drives));
 }
 
-/*
- * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive.
- */
-int pdc202xx_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int pdc202xx_udma_start(struct ata_device *drive, struct request *rq)
 {
-	byte dma_stat		= 0;
-	byte sc1d		= 0;
-	byte newchip		= 0;
-	byte clock		= 0;
-	byte hardware48hack	= 0;
-	struct ata_channel *hwif = drive->channel;
-	struct pci_dev *dev	= hwif->pci_dev;
+	u8 clock		= 0;
+	u8 hardware48hack	= 0;
+	struct ata_channel *ch = drive->channel;
+	struct pci_dev *dev	= ch->pci_dev;
 	unsigned long high_16	= pci_resource_start(dev, 4);
-	unsigned long atapi_reg	= high_16 + (hwif->unit ? 0x24 : 0x00);
-	unsigned long dma_base	= hwif->dma_base;
+	unsigned long atapi_reg	= high_16 + (ch->unit ? 0x24 : 0x00);
+
 
 	switch (dev->device) {
 		case PCI_DEVICE_ID_PROMISE_20275:
@@ -999,7 +998,6 @@
 		case PCI_DEVICE_ID_PROMISE_20269:
 		case PCI_DEVICE_ID_PROMISE_20268R:
 		case PCI_DEVICE_ID_PROMISE_20268:
-			newchip = 1;
 			break;
 		case PCI_DEVICE_ID_PROMISE_20267:
 		case PCI_DEVICE_ID_PROMISE_20265:
@@ -1010,59 +1008,136 @@
 			break;
 	}
 
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_begin:
-			/* Note that this is done *after* the cmd has
-			 * been issued to the drive, as per the BM-IDE spec.
-			 * The Promise Ultra33 doesn't work correctly when
-			 * we do this part before issuing the drive cmd.
-			 */
-			if ((drive->addressing) && (hardware48hack)) {
-				struct request *rq = HWGROUP(drive)->rq;
-				unsigned long word_count = 0;
-
-				outb(clock|(hwif->unit ? 0x08 : 0x02), high_16 + 0x11);
-				word_count = (rq->nr_sectors << 8);
-				word_count = (rq_data_dir(rq) == READ) ? word_count | 0x05000000 : word_count | 0x06000000;
-				outl(word_count, atapi_reg);
-			}
+	if ((drive->addressing) && (hardware48hack)) {
+		unsigned long word_count = 0;
+
+		outb(clock|(ch->unit ? 0x08 : 0x02), high_16 + 0x11);
+		word_count = (rq->nr_sectors << 8);
+		word_count = (rq_data_dir(rq) == READ) ? word_count | 0x05000000 : word_count | 0x06000000;
+		outl(word_count, atapi_reg);
+	}
+
+	/* Note that this is done *after* the cmd has been issued to the drive,
+	 * as per the BM-IDE spec.  The Promise Ultra33 doesn't work correctly
+	 * when we do this part before issuing the drive cmd.
+	 */
+
+	outb(inb(ch->dma_base) | 1, ch->dma_base); /* start DMA */
+
+	return 0;
+}
+
+int pdc202xx_udma_stop(struct ata_device *drive)
+{
+	u8 newchip		= 0;
+	u8 clock		= 0;
+	u8 hardware48hack	= 0;
+	struct ata_channel *ch = drive->channel;
+	struct pci_dev *dev	= ch->pci_dev;
+	unsigned long high_16	= pci_resource_start(dev, 4);
+	unsigned long atapi_reg	= high_16 + (ch->unit ? 0x24 : 0x00);
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
+
+	switch (dev->device) {
+		case PCI_DEVICE_ID_PROMISE_20275:
+		case PCI_DEVICE_ID_PROMISE_20276:
+		case PCI_DEVICE_ID_PROMISE_20269:
+		case PCI_DEVICE_ID_PROMISE_20268R:
+		case PCI_DEVICE_ID_PROMISE_20268:
+			newchip = 1;
 			break;
-		case ide_dma_end:
-			if ((drive->addressing) && (hardware48hack)) {
-				outl(0, atapi_reg);	/* zero out extra */
-				clock = IN_BYTE(high_16 + 0x11);
-				OUT_BYTE(clock & ~(hwif->unit ? 0x08:0x02), high_16 + 0x11);
-			}
+		case PCI_DEVICE_ID_PROMISE_20267:
+		case PCI_DEVICE_ID_PROMISE_20265:
+		case PCI_DEVICE_ID_PROMISE_20262:
+			hardware48hack = 1;
+			clock = IN_BYTE(high_16 + 0x11);
+ 		default:
 			break;
-		case ide_dma_test_irq:	/* returns 1 if dma irq issued, 0 otherwise */
-			dma_stat = IN_BYTE(dma_base+2);
-			if (newchip)
-				return (dma_stat & 4) == 4;
-
-			sc1d = IN_BYTE(high_16 + 0x001d);
-			if (drive->channel->unit) {
-				if ((sc1d & 0x50) == 0x50) goto somebody_else;
-				else if ((sc1d & 0x40) == 0x40)
-					return (dma_stat & 4) == 4;
-			} else {
-				if ((sc1d & 0x05) == 0x05) goto somebody_else;
-				else if ((sc1d & 0x04) == 0x04)
-					return (dma_stat & 4) == 4;
-			}
-somebody_else:
-			return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
-		case ide_dma_lostirq:
-		case ide_dma_timeout:
-			if (drive->channel->resetproc != NULL)
-				drive->channel->resetproc(drive);
+	}
+	if ((drive->addressing) && (hardware48hack)) {
+		outl(0, atapi_reg);	/* zero out extra */
+		clock = IN_BYTE(high_16 + 0x11);
+		OUT_BYTE(clock & ~(ch->unit ? 0x08:0x02), high_16 + 0x11);
+	}
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
+}
+
+static int pdc202xx_udma_irq_status(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	u8 dma_stat = 0;
+	u8 sc1d	= 0;
+	u8 newchip = 0;
+	u8 clock = 0;
+	u8 hardware48hack = 0;
+	struct pci_dev *dev = ch->pci_dev;
+	unsigned long high_16 = pci_resource_start(dev, 4);
+	unsigned long dma_base = ch->dma_base;
+
+	switch (dev->device) {
+		case PCI_DEVICE_ID_PROMISE_20275:
+		case PCI_DEVICE_ID_PROMISE_20276:
+		case PCI_DEVICE_ID_PROMISE_20269:
+		case PCI_DEVICE_ID_PROMISE_20268R:
+		case PCI_DEVICE_ID_PROMISE_20268:
+			newchip = 1;
+			break;
+		case PCI_DEVICE_ID_PROMISE_20267:
+		case PCI_DEVICE_ID_PROMISE_20265:
+		case PCI_DEVICE_ID_PROMISE_20262:
+			hardware48hack = 1;
+			clock = IN_BYTE(high_16 + 0x11);
 		default:
 			break;
 	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+
+	dma_stat = IN_BYTE(dma_base + 2);
+	if (newchip)
+		return (dma_stat & 4) == 4;
+
+	sc1d = IN_BYTE(high_16 + 0x001d);
+	if (ch->unit) {
+		if ((sc1d & 0x50) == 0x50) goto somebody_else;
+		else if ((sc1d & 0x40) == 0x40)
+			return (dma_stat & 4) == 4;
+	} else {
+		if ((sc1d & 0x05) == 0x05) goto somebody_else;
+		else if ((sc1d & 0x04) == 0x04)
+			return (dma_stat & 4) == 4;
+	}
+somebody_else:
+	return (dma_stat & 4) == 4;	/* return 1 if INTR asserted */
+}
+
+static void pdc202xx_udma_timeout(struct ata_device *drive)
+{
+	if (!drive->channel->resetproc)
+		return;
+	/* Assume naively that resetting the drive may help. */
+	drive->channel->resetproc(drive);
+}
+
+static void pdc202xx_udma_irq_lost(struct ata_device *drive)
+{
+	if (!drive->channel->resetproc)
+		return;
+	/* Assume naively that resetting the drive may help. */
+	drive->channel->resetproc(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+static int pdc202xx_dmaproc(struct ata_device *drive)
+{
+	return config_drive_xfer_rate(drive);
+}
+#endif
 
 void pdc202xx_new_reset (ide_drive_t *drive)
 {
@@ -1268,7 +1343,12 @@
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
-		hwif->udma = pdc202xx_dmaproc;
+		hwif->udma_start = pdc202xx_udma_start;
+		hwif->udma_stop = pdc202xx_udma_stop;
+		hwif->udma_irq_status = pdc202xx_udma_irq_status;
+		hwif->udma_irq_lost = pdc202xx_udma_irq_lost;
+		hwif->udma_timeout = pdc202xx_udma_timeout;
+		hwif->XXX_udma = pdc202xx_dmaproc;
 		hwif->highmem = 1;
 		if (!noautodma)
 			hwif->autodma = 1;
diff -Nru a/drivers/ide/pdcadma.c b/drivers/ide/pdcadma.c
--- a/drivers/ide/pdcadma.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/pdcadma.c	Sun May  5 20:39:49 2002
@@ -58,15 +58,11 @@
 /*
  * This initiates/aborts (U)DMA read/write operations on a drive.
  */
-int pdcadma_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int pdcadma_dmaproc(struct ata_device *drive)
 {
-	switch (func) {
-		case ide_dma_check:
-			func = ide_dma_off_quietly;
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+	udma_enable(drive, 0, 0);
+
+	return 0;
 }
 #endif
 
@@ -96,7 +92,7 @@
 //	hwif->speedproc = &pdcadma_tune_chipset;
 
 //	if (hwif->dma_base) {
-//		hwif->dmaproc = &pdcadma_dmaproc;
+//		hwif->XXX_dmaproc = &pdcadma_dmaproc;
 //		hwif->autodma = 1;
 //	}
 }
diff -Nru a/drivers/ide/piix.c b/drivers/ide/piix.c
--- a/drivers/ide/piix.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/piix.c	Sun May  5 20:39:49 2002
@@ -376,38 +376,26 @@
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 
-/*
- * piix_dmaproc() is a callback from upper layers that can do
- * a lot, but we use it for DMA/PIO tuning only, delegating everything
- * else to the default ide_dmaproc().
- */
-
-int piix_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+int piix_dmaproc(struct ata_device *drive)
 {
+	short w80 = drive->channel->udma_four;
 
-	if (func == ide_dma_check) {
-
-		short w80 = drive->channel->udma_four;
-
-		short speed = ata_timing_mode(drive,
-			XFER_PIO | XFER_EPIO | 
+	short speed = ata_timing_mode(drive,
+			XFER_PIO | XFER_EPIO |
 			(piix_config->flags & PIIX_NODMA ? 0 : (XFER_SWDMA | XFER_MWDMA |
 			(piix_config->flags & PIIX_UDMA ? XFER_UDMA : 0) |
 			(w80 && (piix_config->flags & PIIX_UDMA) >= PIIX_UDMA_66 ? XFER_UDMA_66 : 0) |
 			(w80 && (piix_config->flags & PIIX_UDMA) >= PIIX_UDMA_100 ? XFER_UDMA_100 : 0) |
 			(w80 && (piix_config->flags & PIIX_UDMA) >= PIIX_UDMA_133 ? XFER_UDMA_133 : 0))));
 
-		piix_set_drive(drive, speed);
+	piix_set_drive(drive, speed);
 
-		func = (drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO)
-			? ide_dma_on : ide_dma_off_quietly;
+	udma_enable(drive, drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO, 0);
 
-	}
-
-	return ide_dmaproc(func, drive, rq);
+	return 0;
 }
 
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * The initialization callback. Here we determine the IDE chip type
@@ -566,13 +554,13 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->highmem = 1;
-		hwif->udma = piix_dmaproc;
-#ifdef CONFIG_IDEDMA_AUTO
+		hwif->XXX_udma = piix_dmaproc;
+# ifdef CONFIG_IDEDMA_AUTO
 		if (!noautodma)
 			hwif->autodma = 1;
-#endif
+# endif
 	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 }
 
 /*
diff -Nru a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c
--- a/drivers/ide/serverworks.c	Sun May  5 20:39:50 2002
+++ b/drivers/ide/serverworks.c	Sun May  5 20:39:50 2002
@@ -424,31 +424,33 @@
 
 	(void) svwks_tune_chipset(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
-static int config_drive_xfer_rate (ide_drive_t *drive)
+static int config_drive_xfer_rate(struct ata_device *drive)
 {
 	struct hd_driveid *id = drive->id;
-	ide_dma_action_t dma_func = ide_dma_on;
+	int on = 1;
+	int verbose = 1;
 
 	if (id && (id->capability & 1) && drive->channel->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x003F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive);
+				on = config_chipset_for_dma(drive);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -456,76 +458,82 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if (ide_dmaproc(ide_dma_good_drive, drive, NULL)) {
+		} else if (udma_white_list(drive)) {
 			if (id->eide_dma_time > 150) {
 				goto no_dma_set;
 			}
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		config_chipset_for_pio(drive);
 	}
-	return drive->channel->udma(dma_func, drive, NULL);
+
+	udma_enable(drive, on, verbose);
+
+	return 0;
 }
 
-static int svwks_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int svwks_udma_stop(struct ata_device *drive)
 {
-	switch (func) {
-		case ide_dma_check:
-			return config_drive_xfer_rate(drive);
-		case ide_dma_end:
-		{
-			struct ata_channel *hwif = drive->channel;
-			unsigned long dma_base		= hwif->dma_base;
+	struct ata_channel *ch = drive->channel;
+	unsigned long dma_base = ch->dma_base;
+	u8 dma_stat;
 
-			if(inb(dma_base+0x02)&1)
-			{
+	if(inb(dma_base+0x02)&1)
+	{
 #if 0
-				int i;
-				printk(KERN_ERR "Curious - OSB4 thinks the DMA is still running.\n");
-				for(i=0;i<10;i++)
-				{
-					if(!(inb(dma_base+0x02)&1))
-					{
-						printk(KERN_ERR "OSB4 now finished.\n");
-						break;
-					}
-					udelay(5);
-				}
+		int i;
+		printk(KERN_ERR "Curious - OSB4 thinks the DMA is still running.\n");
+		for(i=0;i<10;i++)
+		{
+			if(!(inb(dma_base+0x02)&1))
+			{
+				printk(KERN_ERR "OSB4 now finished.\n");
+				break;
+			}
+			udelay(5);
+		}
 #endif
-				printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n");
-				printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n");
-				printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n");
+		printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n");
+		printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n");
+		printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n");
 #if 0
-				/* Panic might sys_sync -> death by corrupt disk */
-				panic("OSB4: continuing might cause disk corruption.\n");
+		/* Panic might sys_sync -> death by corrupt disk */
+		panic("OSB4: continuing might cause disk corruption.\n");
 #else
-				printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n");
-				while(1)
-					cpu_relax();
+		printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n");
+		while(1)
+			cpu_relax();
 #endif
-			}
-			/* and drop through */
-		}
-		default:
-			break;
 	}
-	/* Other cases are done by generic IDE-DMA code. */
-	return ide_dmaproc(func, drive, rq);
+
+	drive->waiting_for_dma = 0;
+	outb(inb(dma_base)&~1, dma_base);	/* stop DMA */
+	dma_stat = inb(dma_base+2);		/* get DMA status */
+	outb(dma_stat|6, dma_base+2);		/* clear the INTR & ERROR bits */
+	udma_destroy_table(ch);			/* purge DMA mappings */
+
+	return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0;	/* verify good DMA status */
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+
+static int svwks_dmaproc(struct ata_device *drive)
+{
+	return config_drive_xfer_rate(drive);
+}
+#endif
 
 unsigned int __init pci_init_svwks(struct pci_dev *dev)
 {
@@ -645,7 +653,8 @@
 		if (!noautodma)
 			hwif->autodma = 1;
 #endif
-		hwif->udma = svwks_dmaproc;
+		hwif->udma_stop = svwks_udma_stop;
+		hwif->XXX_udma = svwks_dmaproc;
 		hwif->highmem = 1;
 	} else {
 		hwif->autodma = 0;
diff -Nru a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c
--- a/drivers/ide/sis5513.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/sis5513.c	Sun May  5 20:39:49 2002
@@ -556,7 +556,7 @@
 		case 1:		drive_pci = 0x42; break;
 		case 2:		drive_pci = 0x44; break;
 		case 3:		drive_pci = 0x46; break;
-		default:	return ide_dma_off;
+		default:	return 0;
 	}
 
 #ifdef BROKEN_LEVEL
@@ -663,39 +663,41 @@
 	else if (id->dma_1word & 0x0001)
 		speed = XFER_SW_DMA_0;
 	else
-		return ((int) ide_dma_off_quietly);
+		return 0;
 
 	outb(inb(hwif->dma_base+2)|(1<<(5+unit)), hwif->dma_base+2);
 
 	sis5513_tune_chipset(drive, speed);
 
-	return ((int)	((id->dma_ultra >> 11) & 7) ? ide_dma_on :
-			((id->dma_ultra >> 8) & 7) ? ide_dma_on :
-			((id->dma_mword >> 8) & 7) ? ide_dma_on :
-			((id->dma_1word >> 8) & 7) ? ide_dma_on :
-						     ide_dma_off_quietly);
+	return ((int)	((id->dma_ultra >> 11) & 7) ? 1 :
+			((id->dma_ultra >> 8) & 7) ? 1 :
+			((id->dma_mword >> 8) & 7) ? 1 :
+			((id->dma_1word >> 8) & 7) ? 1 :
+						     0);
 }
 
 static int config_drive_xfer_rate (ide_drive_t *drive)
 {
-	struct hd_driveid *id		= drive->id;
-	ide_dma_action_t dma_func	= ide_dma_off_quietly;
+	struct hd_driveid *id = drive->id;
+	int on = 0;
+	int verbose = 1;
 
 	config_chipset_for_pio(drive, 5);
 
 	if (id && (id->capability & 1) && drive->channel->autodma) {
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive, NULL)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			goto fast_ata_pio;
 		}
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 		if (id->field_valid & 4) {
 			if (id->dma_ultra & 0x003F) {
 				/* Force if Capable UltraDMA */
-				dma_func = config_chipset_for_dma(drive, 1);
+				on = config_chipset_for_dma(drive, 1);
 				if ((id->field_valid & 2) &&
-				    (dma_func != ide_dma_on))
+				    (!on))
 					goto try_dma_modes;
 			}
 		} else if (id->field_valid & 2) {
@@ -703,43 +705,40 @@
 			if ((id->dma_mword & 0x0007) ||
 			    (id->dma_1word & 0x0007)) {
 				/* Force if Capable regular DMA modes */
-				dma_func = config_chipset_for_dma(drive, 0);
-				if (dma_func != ide_dma_on)
+				on = config_chipset_for_dma(drive, 0);
+				if (!on)
 					goto no_dma_set;
 			}
-		} else if ((ide_dmaproc(ide_dma_good_drive, drive, NULL)) &&
+		} else if ((udma_white_list(drive)) &&
 			   (id->eide_dma_time > 150)) {
 			/* Consult the list of known "good" drives */
-			dma_func = config_chipset_for_dma(drive, 0);
-			if (dma_func != ide_dma_on)
+			on = config_chipset_for_dma(drive, 0);
+			if (!on)
 				goto no_dma_set;
 		} else {
 			goto fast_ata_pio;
 		}
 	} else if ((id->capability & 8) || (id->field_valid & 2)) {
 fast_ata_pio:
-		dma_func = ide_dma_off_quietly;
+		on = 0;
+		verbose = 0;
 no_dma_set:
 		(void) config_chipset_for_pio(drive, 5);
 	}
 
-	return drive->channel->udma(dma_func, drive, NULL);
+	udma_enable(drive, on, verbose);
+
+	return 0;
 }
 
-/* initiates/aborts (U)DMA read/write operations on a drive. */
-int sis5513_dmaproc (ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int sis5513_dmaproc(struct ata_device *drive)
 {
-	switch (func) {
-		case ide_dma_check:
-			config_drive_art_rwp(drive);
-			config_art_rwp_pio(drive, 5);
-			return config_drive_xfer_rate(drive);
-		default:
-			break;
-	}
-	return ide_dmaproc(func, drive, rq);	/* use standard DMA stuff */
+	config_drive_art_rwp(drive);
+	config_art_rwp_pio(drive, 5);
+
+	return config_drive_xfer_rate(drive);
 }
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /* Chip detection and general config */
 unsigned int __init pci_init_sis5513(struct pci_dev *dev)
@@ -852,7 +851,7 @@
 		if (chipset_family > ATA_16) {
 			hwif->autodma = noautodma ? 0 : 1;
 			hwif->highmem = 1;
-			hwif->udma = sis5513_dmaproc;
+			hwif->XXX_udma = sis5513_dmaproc;
 		} else {
 #endif
 			hwif->autodma = 0;
diff -Nru a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
--- a/drivers/ide/sl82c105.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/sl82c105.c	Sun May  5 20:39:49 2002
@@ -116,7 +116,7 @@
  */
 static int sl82c105_check_drive(ide_drive_t *drive)
 {
-	ide_dma_action_t dma_func = ide_dma_off_quietly;
+	int on = 0;
 
 	do {
 		struct hd_driveid *id = drive->id;
@@ -129,46 +129,39 @@
 			break;
 
 		/* Consult the list of known "bad" drives */
-		if (ide_dmaproc(ide_dma_bad_drive, drive)) {
-			dma_func = ide_dma_off;
+		if (udma_black_list(drive)) {
+			on = 0;
 			break;
 		}
 
 		if (id->field_valid & 2) {
 			if  (id->dma_mword & 7 || id->dma_1word & 7)
-				dma_func = ide_dma_on;
+				on = 1;
 			break;
 		}
 
-		if (ide_dmaproc(ide_dma_good_drive, drive)) {
-			dma_func = ide_dma_on;
+		if (udma_white_list(drive)) {
+			on = 1;
 			break;
 		}
 	} while (0);
+	if (on)
+		config_for_dma(drive);
+	else
+		config_for_pio(drive, 4, 0);
+
+	udma_enable(drive, on, 0);
+
 
-	return drive->channel->dmaproc(dma_func, drive);
+	return 0;
 }
 
 /*
  * Our own dmaproc, only to intercept ide_dma_check
  */
-static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+static int sl82c105_dmaproc(struct ata_device *drive)
 {
-	switch (func) {
-	case ide_dma_check:
-		return sl82c105_check_drive(drive);
-	case ide_dma_on:
-		if (config_for_dma(drive))
-			func = ide_dma_off;
-		/* fall through */
-	case ide_dma_off_quietly:
-	case ide_dma_off:
-		config_for_pio(drive, 4, 0);
-		break;
-	default:
-		break;
-	}
-	return ide_dmaproc(func, drive);
+	return sl82c105_check_drive(drive);
 }
 
 /*
@@ -183,8 +176,8 @@
 	 * We support 32-bit I/O on this interface, and it
 	 * doesn't have problems with interrupts.
 	 */
-	drive->io_32bit = 1;
-	drive->unmask = 1;
+	drive->channel->io_32bit = 1;
+	drive->channel->unmask = 1;
 }
 
 /*
@@ -252,10 +245,10 @@
 	}
 	outb(dma_state, dma_base + 2);
 
-	hwif->dmaproc = NULL;
+	hwif->XXX_udma = NULL;
 	ide_setup_dma(hwif, dma_base, 8);
-	if (hwif->dmaproc)
-		hwif->dmaproc = sl82c105_dmaproc;
+	if (hwif->XXX_udma)
+		hwif->XXX_udma = sl82c105_dmaproc;
 }
 
 /*
diff -Nru a/drivers/ide/tcq.c b/drivers/ide/tcq.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/ide/tcq.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C) 2001, 2002 Jens Axboe <axboe@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ */
+
+/*
+ * Support for the DMA queued protocol, which enables ATA disk drives to
+ * use tagged command queueing.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ide.h>
+
+#include <asm/delay.h>
+
+/*
+ * warning: it will be _very_ verbose if defined
+ */
+#undef IDE_TCQ_DEBUG
+
+#ifdef IDE_TCQ_DEBUG
+#define TCQ_PRINTK printk
+#else
+#define TCQ_PRINTK(x...)
+#endif
+
+/*
+ * use nIEN or not
+ */
+#undef IDE_TCQ_NIEN
+
+/*
+ * We are leaving the SERVICE interrupt alone, IBM drives have it
+ * on per default and it can't be turned off. Doesn't matter, this
+ * is the sane config.
+ */
+#undef IDE_TCQ_FIDDLE_SI
+
+static ide_startstop_t ide_dmaq_intr(struct ata_device *drive, struct request *rq);
+static ide_startstop_t service(struct ata_device *drive);
+
+static inline void drive_ctl_nien(struct ata_device *drive, int set)
+{
+#ifdef IDE_TCQ_NIEN
+	if (IDE_CONTROL_REG) {
+		int mask = set ? 0x02 : 0x00;
+
+		OUT_BYTE(drive->ctl | mask, IDE_CONTROL_REG);
+	}
+#endif
+}
+
+static ide_startstop_t tcq_nop_handler(struct ata_device *drive, struct request *rq)
+{
+	struct ata_taskfile *args = rq->special;
+
+	ide__sti();
+	ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+	kfree(args);
+	return ide_stopped;
+}
+
+/*
+ * If we encounter _any_ error doing I/O to one of the tags, we must
+ * invalidate the pending queue. Clear the software busy queue and requeue
+ * on the request queue for restart. Issue a WIN_NOP to clear hardware queue.
+ */
+static void tcq_invalidate_queue(struct ata_device *drive)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	request_queue_t *q = &drive->queue;
+	struct ata_taskfile *args;
+	struct request *rq;
+	unsigned long flags;
+
+	printk(KERN_INFO "ATA: %s: invalidating pending queue (%d)\n", drive->name, ata_pending_commands(drive));
+
+	spin_lock_irqsave(&ide_lock, flags);
+
+	del_timer(&hwgroup->timer);
+
+	if (test_bit(IDE_DMA, &hwgroup->flags))
+		udma_stop(drive);
+
+	blk_queue_invalidate_tags(q);
+
+	drive->using_tcq = 0;
+	drive->queue_depth = 1;
+	clear_bit(IDE_BUSY, &hwgroup->flags);
+	clear_bit(IDE_DMA, &hwgroup->flags);
+	hwgroup->handler = NULL;
+
+	/*
+	 * Do some internal stuff -- we really need this command to be
+	 * executed before any new commands are started. issue a NOP
+	 * to clear internal queue on drive.
+	 */
+	args = kmalloc(sizeof(*args), GFP_ATOMIC);
+	if (!args) {
+		printk(KERN_ERR "ATA: %s: failed to issue NOP\n", drive->name);
+		goto out;
+	}
+
+	rq = blk_get_request(&drive->queue, READ, GFP_ATOMIC);
+	if (!rq)
+		rq = blk_get_request(&drive->queue, WRITE, GFP_ATOMIC);
+
+	/*
+	 * blk_queue_invalidate_tags() just added back at least one command
+	 * to the free list, so there _must_ be at least one free.
+	 */
+	BUG_ON(!rq);
+
+	rq->special = args;
+	args->taskfile.command = WIN_NOP;
+	args->handler = tcq_nop_handler;
+	args->command_type = IDE_DRIVE_TASK_NO_DATA;
+
+	rq->rq_dev = mk_kdev(drive->channel->major, (drive->select.b.unit)<<PARTN_BITS);
+	_elv_add_request(q, rq, 0, 0);
+
+	/*
+	 * make sure that nIEN is cleared
+	 */
+out:
+	drive_ctl_nien(drive, 0);
+
+	/*
+	 * start doing stuff again
+	 */
+	q->request_fn(q);
+	spin_unlock_irqrestore(&ide_lock, flags);
+	printk(KERN_DEBUG "ATA: tcq_invalidate_queue: done\n");
+}
+
+static void ata_tcq_irq_timeout(unsigned long data)
+{
+	struct ata_device *drive = (struct ata_device *) data;
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	unsigned long flags;
+
+	printk(KERN_ERR "ATA: %s: timeout waiting for interrupt...\n", __FUNCTION__);
+
+	spin_lock_irqsave(&ide_lock, flags);
+
+	if (test_and_set_bit(IDE_BUSY, &hwgroup->flags))
+		printk(KERN_ERR "ATA: %s: hwgroup not busy\n", __FUNCTION__);
+	if (hwgroup->handler == NULL)
+		printk(KERN_ERR "ATA: %s: missing isr!\n", __FUNCTION__);
+
+	spin_unlock_irqrestore(&ide_lock, flags);
+
+	/*
+	 * if pending commands, try service before giving up
+	 */
+	if (ata_pending_commands(drive) && (GET_STAT() & SERVICE_STAT))
+		if (service(drive) == ide_started)
+			return;
+
+	if (drive)
+		tcq_invalidate_queue(drive);
+}
+
+static void set_irq(struct ata_device *drive, ata_handler_t *handler)
+{
+	ide_hwgroup_t *hwgroup = HWGROUP(drive);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ide_lock, flags);
+
+	/*
+	 * always just bump the timer for now, the timeout handling will
+	 * have to be changed to be per-command
+	 */
+	hwgroup->timer.function = ata_tcq_irq_timeout;
+	hwgroup->timer.data = (unsigned long) hwgroup->XXX_drive;
+	mod_timer(&hwgroup->timer, jiffies + 5 * HZ);
+
+	hwgroup->handler = handler;
+	spin_unlock_irqrestore(&ide_lock, flags);
+}
+
+/*
+ * wait 400ns, then poll for busy_mask to clear from alt status
+ */
+#define IDE_TCQ_WAIT	(10000)
+static int wait_altstat(struct ata_device *drive, u8 *stat, u8 busy_mask)
+{
+	int i = 0;
+
+	udelay(1);
+
+	while ((*stat = GET_ALTSTAT()) & busy_mask) {
+		if (unlikely(i++ > IDE_TCQ_WAIT))
+			return 1;
+
+		udelay(10);
+	}
+
+	return 0;
+}
+
+static ide_startstop_t udma_tcq_start(struct ata_device *drive, struct request *rq);
+
+/*
+ * issue SERVICE command to drive -- drive must have been selected first,
+ * and it must have reported a need for service (status has SERVICE_STAT set)
+ *
+ * Also, nIEN must be set as not to need protection against ide_dmaq_intr
+ */
+static ide_startstop_t service(struct ata_device *drive)
+{
+	struct request *rq;
+	u8 feat;
+	u8 stat;
+	int tag;
+
+	TCQ_PRINTK("%s: started service\n", drive->name);
+
+	/*
+	 * Could be called with IDE_DMA in-progress from invalidate
+	 * handler, refuse to do anything.
+	 */
+	if (test_bit(IDE_DMA, &HWGROUP(drive)->flags))
+		return ide_stopped;
+
+	/*
+	 * need to select the right drive first...
+	 */
+	if (drive != HWGROUP(drive)->XXX_drive) {
+		SELECT_DRIVE(drive->channel, drive);
+		udelay(10);
+	}
+
+	drive_ctl_nien(drive, 1);
+
+	/*
+	 * send SERVICE, wait 400ns, wait for BUSY_STAT to clear
+	 */
+	OUT_BYTE(WIN_QUEUED_SERVICE, IDE_COMMAND_REG);
+
+	if (wait_altstat(drive, &stat, BUSY_STAT)) {
+		printk(KERN_ERR"%s: BUSY clear took too long\n", __FUNCTION__);
+		ide_dump_status(drive, __FUNCTION__, stat);
+		tcq_invalidate_queue(drive);
+		return ide_stopped;
+	}
+
+	drive_ctl_nien(drive, 0);
+
+	/*
+	 * FIXME, invalidate queue
+	 */
+	if (stat & ERR_STAT) {
+		ide_dump_status(drive, __FUNCTION__, stat);
+		tcq_invalidate_queue(drive);
+		return ide_stopped;
+	}
+
+	/*
+	 * should not happen, a buggy device could introduce loop
+	 */
+	if ((feat = GET_FEAT()) & NSEC_REL) {
+		HWGROUP(drive)->rq = NULL;
+		printk("%s: release in service\n", drive->name);
+		return ide_stopped;
+	}
+
+	tag = feat >> 3;
+
+	TCQ_PRINTK("%s: stat %x, feat %x\n", __FUNCTION__, stat, feat);
+
+	rq = blk_queue_tag_request(&drive->queue, tag);
+	if (!rq) {
+		printk(KERN_ERR"%s: missing request for tag %d\n", __FUNCTION__, tag);
+		return ide_stopped;
+	}
+
+	HWGROUP(drive)->rq = rq;
+
+	/*
+	 * we'll start a dma read or write, device will trigger
+	 * interrupt to indicate end of transfer, release is not allowed
+	 */
+	TCQ_PRINTK("%s: starting command %x\n", __FUNCTION__, stat);
+	return udma_tcq_start(drive, rq);
+}
+
+static ide_startstop_t check_service(struct ata_device *drive)
+{
+	u8 stat;
+
+	TCQ_PRINTK("%s: %s\n", drive->name, __FUNCTION__);
+
+	if (!ata_pending_commands(drive))
+		return ide_stopped;
+
+	if ((stat = GET_STAT()) & SERVICE_STAT)
+		return service(drive);
+
+	/*
+	 * we have pending commands, wait for interrupt
+	 */
+	set_irq(drive, ide_dmaq_intr);
+
+	return ide_started;
+}
+
+ide_startstop_t ide_dmaq_complete(struct ata_device *drive, struct request *rq, u8 stat)
+{
+	u8 dma_stat;
+
+	/*
+	 * transfer was in progress, stop DMA engine
+	 */
+	dma_stat = udma_stop(drive);
+
+	/*
+	 * must be end of I/O, check status and complete as necessary
+	 */
+	if (unlikely(!OK_STAT(stat, READY_STAT, drive->bad_wstat | DRQ_STAT))) {
+		printk(KERN_ERR "%s: %s: error status %x\n", __FUNCTION__, drive->name,stat);
+		ide_dump_status(drive, __FUNCTION__, stat);
+		tcq_invalidate_queue(drive);
+		return ide_stopped;
+	}
+
+	if (dma_stat)
+		printk("%s: bad DMA status (dma_stat=%x)\n", drive->name, dma_stat);
+
+	TCQ_PRINTK("%s: ending %p, tag %d\n", __FUNCTION__, rq, rq->tag);
+	__ide_end_request(drive, rq, !dma_stat, rq->nr_sectors);
+
+	/*
+	 * we completed this command, check if we can service a new command
+	 */
+	return check_service(drive);
+}
+
+/*
+ * intr handler for queued dma operations. this can be entered for two
+ * reasons:
+ *
+ * 1) device has completed dma transfer
+ * 2) service request to start a command
+ *
+ * if the drive has an active tag, we first complete that request before
+ * processing any pending SERVICE.
+ */
+static ide_startstop_t ide_dmaq_intr(struct ata_device *drive, struct request *rq)
+{
+	u8 stat = GET_STAT();
+
+	TCQ_PRINTK("%s: stat=%x\n", __FUNCTION__, stat);
+
+	/*
+	 * if a command completion interrupt is pending, do that first and
+	 * check service afterwards
+	 */
+	if (rq)
+		return ide_dmaq_complete(drive, rq, stat);
+
+	/*
+	 * service interrupt
+	 */
+	if (stat & SERVICE_STAT) {
+		TCQ_PRINTK("%s: SERV (stat=%x)\n", __FUNCTION__, stat);
+		return service(drive);
+	}
+
+	printk("%s: stat=%x, not expected\n", __FUNCTION__, stat);
+	return check_service(drive);
+}
+
+/*
+ * Check if the ata adapter this drive is attached to supports the
+ * NOP auto-poll for multiple tcq enabled drives on one channel.
+ */
+static int check_autopoll(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+	struct ata_taskfile args;
+	int drives = 0, i;
+
+	/*
+	 * only need to probe if both drives on a channel support tcq
+	 */
+	for (i = 0; i < MAX_DRIVES; i++)
+		if (drive->channel->drives[i].present &&drive->type == ATA_DISK)
+			drives++;
+
+	if (drives <= 1)
+		return 0;
+
+	memset(&args, 0, sizeof(args));
+
+	args.taskfile.feature = 0x01;
+	args.taskfile.command = WIN_NOP;
+	ide_cmd_type_parser(&args);
+
+	/*
+	 * do taskfile and check ABRT bit -- intelligent adapters will not
+	 * pass NOP with sub-code 0x01 to device, so the command will not
+	 * fail there
+	 */
+	ide_raw_taskfile(drive, &args, NULL);
+	if (args.taskfile.feature & ABRT_ERR)
+		return 1;
+
+	ch->auto_poll = 1;
+	printk("%s: NOP Auto-poll enabled\n", ch->name);
+	return 0;
+}
+
+/*
+ * configure the drive for tcq
+ */
+static int configure_tcq(struct ata_device *drive)
+{
+	int tcq_mask = 1 << 1 | 1 << 14;
+	int tcq_bits = tcq_mask | 1 << 15;
+	struct ata_taskfile args;
+
+	/*
+	 * bit 14 and 1 must be set in word 83 of the device id to indicate
+	 * support for dma queued protocol, and bit 15 must be cleared
+	 */
+	if ((drive->id->command_set_2 & tcq_bits) ^ tcq_mask)
+		return -EIO;
+
+	memset(&args, 0, sizeof(args));
+	args.taskfile.feature = SETFEATURES_EN_WCACHE;
+	args.taskfile.command = WIN_SETFEATURES;
+	ide_cmd_type_parser(&args);
+
+	if (ide_raw_taskfile(drive, &args, NULL)) {
+		printk("%s: failed to enable write cache\n", drive->name);
+		return 1;
+	}
+
+	/*
+	 * disable RELease interrupt, it's quicker to poll this after
+	 * having sent the command opcode
+	 */
+	memset(&args, 0, sizeof(args));
+	args.taskfile.feature = SETFEATURES_DIS_RI;
+	args.taskfile.command = WIN_SETFEATURES;
+	ide_cmd_type_parser(&args);
+
+	if (ide_raw_taskfile(drive, &args, NULL)) {
+		printk("%s: disabling release interrupt fail\n", drive->name);
+		return 1;
+	}
+
+#ifdef IDE_TCQ_FIDDLE_SI
+	/*
+	 * enable SERVICE interrupt
+	 */
+	memset(&args, 0, sizeof(args));
+	args.taskfile.feature = SETFEATURES_EN_SI;
+	args.taskfile.command = WIN_SETFEATURES;
+	ide_cmd_type_parser(&args);
+
+	if (ide_raw_taskfile(drive, &args, NULL)) {
+		printk("%s: enabling service interrupt fail\n", drive->name);
+		return 1;
+	}
+#endif
+
+	return 0;
+}
+
+static int tcq_wait_dataphase(struct ata_device *drive)
+{
+	u8 stat;
+	int i;
+
+	while ((stat = GET_STAT()) & BUSY_STAT)
+		udelay(10);
+
+	if (OK_STAT(stat, READY_STAT | DRQ_STAT, drive->bad_wstat))
+		return 0;
+
+	i = 0;
+	udelay(1);
+	while (!OK_STAT(GET_STAT(), READY_STAT | DRQ_STAT, drive->bad_wstat)) {
+		if (unlikely(i++ > IDE_TCQ_WAIT))
+			return 1;
+
+		udelay(10);
+	}
+
+	return 0;
+}
+
+/****************************************************************************
+ * UDMA transfer handling functions.
+ */
+
+/*
+ * Invoked from a SERVICE interrupt, command etc already known.  Just need to
+ * start the dma engine for this tag.
+ */
+static ide_startstop_t udma_tcq_start(struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+
+	TCQ_PRINTK("%s: setting up queued %d\n", __FUNCTION__, rq->tag);
+	if (!test_bit(IDE_BUSY, &ch->hwgroup->flags))
+		printk("queued_rw: IDE_BUSY not set\n");
+
+	if (tcq_wait_dataphase(drive))
+		return ide_stopped;
+
+	if (ata_start_dma(drive, rq))
+		return ide_stopped;
+
+	set_irq(drive, ide_dmaq_intr);
+	if (!udma_start(drive, rq))
+		return ide_started;
+
+	return ide_stopped;
+}
+
+/*
+ * Start a queued command from scratch.
+ */
+ide_startstop_t udma_tcq_taskfile(struct ata_device *drive, struct request *rq)
+{
+	u8 stat;
+	u8 feat;
+
+	struct ata_taskfile *args = rq->special;
+
+	TCQ_PRINTK("%s: start tag %d\n", drive->name, rq->tag);
+
+	/*
+	 * set nIEN, tag start operation will enable again when
+	 * it is safe
+	 */
+	drive_ctl_nien(drive, 1);
+
+	OUT_BYTE(args->taskfile.command, IDE_COMMAND_REG);
+
+	if (wait_altstat(drive, &stat, BUSY_STAT)) {
+		ide_dump_status(drive, "queued start", stat);
+		tcq_invalidate_queue(drive);
+		return ide_stopped;
+	}
+
+	drive_ctl_nien(drive, 0);
+
+	if (stat & ERR_STAT) {
+		ide_dump_status(drive, "tcq_start", stat);
+		return ide_stopped;
+	}
+
+	/*
+	 * drive released the bus, clear active tag and
+	 * check for service
+	 */
+	if ((feat = GET_FEAT()) & NSEC_REL) {
+		drive->immed_rel++;
+		HWGROUP(drive)->rq = NULL;
+		set_irq(drive, ide_dmaq_intr);
+
+		TCQ_PRINTK("REL in queued_start\n");
+
+		if ((stat = GET_STAT()) & SERVICE_STAT)
+			return service(drive);
+
+		return ide_released;
+	}
+
+	TCQ_PRINTK("IMMED in queued_start\n");
+	drive->immed_comp++;
+
+	return udma_tcq_start(drive, rq);
+}
+
+/*
+ * For now assume that command list is always as big as we need and don't
+ * attempt to shrink it on tcq disable.
+ */
+int udma_tcq_enable(struct ata_device *drive, int on)
+{
+	int depth = drive->using_tcq ? drive->queue_depth : 0;
+
+	/*
+	 * disable or adjust queue depth
+	 */
+	if (!on) {
+		if (drive->using_tcq)
+			printk("%s: TCQ disabled\n", drive->name);
+		drive->using_tcq = 0;
+		return 0;
+	}
+
+	if (configure_tcq(drive)) {
+		drive->using_tcq = 0;
+		return 1;
+	}
+
+	/*
+	 * enable block tagging
+	 */
+	if (!blk_queue_tagged(&drive->queue))
+		blk_queue_init_tags(&drive->queue, IDE_MAX_TAG);
+
+	/*
+	 * check auto-poll support
+	 */
+	check_autopoll(drive);
+
+	if (depth != drive->queue_depth)
+		printk("%s: tagged command queueing enabled, command queue depth %d\n", drive->name, drive->queue_depth);
+
+	drive->using_tcq = 1;
+	return 0;
+}
diff -Nru a/drivers/ide/trm290.c b/drivers/ide/trm290.c
--- a/drivers/ide/trm290.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/trm290.c	Sun May  5 20:39:49 2002
@@ -173,43 +173,76 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-static int trm290_dmaproc (ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int trm290_udma_start(struct ata_device *drive, struct request *__rq)
 {
-	struct ata_channel *hwif = drive->channel;
-	unsigned int count, reading = 2, writing = 0;
+	/* Nothing to be done here. */
+	return 0;
+}
+
+static int trm290_udma_stop(struct ata_device *drive)
+{
+	struct ata_channel *ch = drive->channel;
+
+	drive->waiting_for_dma = 0;
+	udma_destroy_table(ch);	/* purge DMA mappings */
+	return (inw(ch->dma_base + 2) != 0x00ff);
+}
+
+static int do_udma(unsigned int reading, struct ata_device *drive, struct request *rq)
+{
+	struct ata_channel *ch = drive->channel;
+	unsigned int count, writing;
 
-	switch (func) {
-		case ide_dma_write:
-			reading = 0;
-			writing = 1;
+	if (!reading) {
+		reading = 0;
+		writing = 1;
 #ifdef TRM290_NO_DMA_WRITES
-			break;	/* always use PIO for writes */
+		trm290_prepare_drive(drive, 0);	/* select PIO xfer */
+
+		return 1;
 #endif
-		case ide_dma_read:
-			if (!(count = ide_build_dmatable(drive, func)))
-				break;		/* try PIO instead of DMA */
-			trm290_prepare_drive(drive, 1);	/* select DMA xfer */
-			outl(hwif->dmatable_dma|reading|writing, hwif->dma_base);
-			drive->waiting_for_dma = 1;
-			outw((count * 2) - 1, hwif->dma_base+2); /* start DMA */
-			if (drive->type != ATA_DISK)
-				return 0;
-			ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
-			OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
-			return 0;
-		case ide_dma_begin:
-			return 0;
-		case ide_dma_end:
-			drive->waiting_for_dma = 0;
-			ide_destroy_dmatable(drive);		/* purge DMA mappings */
-			return (inw(hwif->dma_base+2) != 0x00ff);
-		case ide_dma_test_irq:
-			return (inw(hwif->dma_base+2) == 0x00ff);
-		default:
-			return ide_dmaproc(func, drive, rq);
+	} else {
+		reading = 2;
+		writing = 0;
+	}
+
+	if (!(count = udma_new_table(ch, rq))) {
+		trm290_prepare_drive(drive, 0);	/* select PIO xfer */
+		return 1;	/* try PIO instead of DMA */
 	}
-	trm290_prepare_drive(drive, 0);	/* select PIO xfer */
-	return 1;
+
+	trm290_prepare_drive(drive, 1);	/* select DMA xfer */
+	outl(ch->dmatable_dma|reading|writing, ch->dma_base);
+	drive->waiting_for_dma = 1;
+	outw((count * 2) - 1, ch->dma_base+2); /* start DMA */
+
+	if (drive->type != ATA_DISK)
+		return 0;
+
+	ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL);
+	OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+
+	return 0;
+}
+
+static int trm290_udma_read(struct ata_device *drive, struct request *rq)
+{
+	return do_udma(1, drive, rq);
+}
+
+static int trm290_udma_write(struct ata_device *drive, struct request *rq)
+{
+	return do_udma(0, drive, rq);
+}
+
+static int trm290_udma_irq_status(struct ata_device *drive)
+{
+	return (inw(drive->channel->dma_base + 2) == 0x00ff);
+}
+
+static int trm290_dmaproc(struct ata_device *drive)
+{
+	return XXX_ide_dmaproc(drive);
 }
 #endif
 
@@ -263,7 +296,12 @@
 	ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->unit ? 0x0080 : 0x0000), 3);
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-	hwif->udma = trm290_dmaproc;
+	hwif->udma_start = trm290_udma_start;
+	hwif->udma_stop = trm290_udma_stop;
+	hwif->udma_read = trm290_udma_read;
+	hwif->udma_write = trm290_udma_write;
+	hwif->udma_irq_status = trm290_udma_irq_status;
+	hwif->XXX_udma = trm290_dmaproc;
 #endif
 
 	hwif->selectproc = &trm290_selectproc;
diff -Nru a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c
--- a/drivers/ide/via82cxxx.c	Sun May  5 20:39:49 2002
+++ b/drivers/ide/via82cxxx.c	Sun May  5 20:39:49 2002
@@ -356,37 +356,24 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
-
-/*
- * via82cxxx_dmaproc() is a callback from upper layers that can do
- * a lot, but we use it for DMA/PIO tuning only, delegating everything
- * else to the default ide_dmaproc().
- */
-
-int via82cxxx_dmaproc(ide_dma_action_t func, struct ata_device *drive, struct request *rq)
+static int via82cxxx_dmaproc(struct ata_device *drive)
 {
+	short w80 = drive->channel->udma_four;
 
-	if (func == ide_dma_check) {
-
-		short w80 = drive->channel->udma_four;
-
-		short speed = ata_timing_mode(drive,
+	short speed = ata_timing_mode(drive,
 			XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA |
 			(via_config->flags & VIA_UDMA ? XFER_UDMA : 0) |
 			(w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) |
 			(w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0) |
 			(w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_133 ? XFER_UDMA_133 : 0));
 
-		via_set_drive(drive, speed);
+	via_set_drive(drive, speed);
 
-		func = (drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO)
-			? ide_dma_on : ide_dma_off_quietly;
-	}
+	udma_enable(drive, drive->channel->autodma && (speed & XFER_MODE) != XFER_PIO, 0);
 
-	return ide_dmaproc(func, drive, rq);
+	return 0;
 }
-
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 
 /*
  * The initialization callback. Here we determine the IDE chip type
@@ -546,13 +533,13 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	if (hwif->dma_base) {
 		hwif->highmem = 1;
-		hwif->udma = &via82cxxx_dmaproc;
-#ifdef CONFIG_IDEDMA_AUTO
+		hwif->XXX_udma = &via82cxxx_dmaproc;
+# ifdef CONFIG_IDEDMA_AUTO
 		if (!noautodma)
 			hwif->autodma = 1;
-#endif
+# endif
 	}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
+#endif
 }
 
 /*
diff -Nru a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in
--- a/drivers/net/appletalk/Config.in	Sun May  5 20:39:49 2002
+++ b/drivers/net/appletalk/Config.in	Sun May  5 20:39:49 2002
@@ -9,13 +9,13 @@
    tristate '  Apple/Farallon LocalTalk PC support' CONFIG_LTPC
    tristate '  COPS LocalTalk PC support' CONFIG_COPS
    if [ "$CONFIG_COPS" != "n" ]; then
-      bool '    Dayna firmware support' CONFIG_COPS_DAYNA $CONFIG_COPS
-      bool '    Tangent firmware support' CONFIG_COPS_TANGENT $CONFIG_COPS
+      bool '    Dayna firmware support' CONFIG_COPS_DAYNA
+      bool '    Tangent firmware support' CONFIG_COPS_TANGENT
    fi
    dep_tristate '  Appletalk-IP driver support' CONFIG_IPDDP $CONFIG_ATALK
    if [ "$CONFIG_IPDDP" != "n" ]; then
-      bool '    IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP $CONFIG_IPDDP
-      bool '    Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP $CONFIG_IPDDP
+      bool '    IP to Appletalk-IP Encapsulation support' CONFIG_IPDDP_ENCAP
+      bool '    Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP
    fi
 fi
 endmenu
diff -Nru a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in
--- a/drivers/net/wan/Config.in	Sun May  5 20:39:49 2002
+++ b/drivers/net/wan/Config.in	Sun May  5 20:39:49 2002
@@ -69,13 +69,14 @@
 
       dep_tristate '    Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)' CONFIG_PC300 $CONFIG_HDLC $CONFIG_PCI
       if [ "$CONFIG_PC300" != "n" ]; then
-            if [ "$CONFIG_PPP" != "n" -a "$CONFIG_PPP_MULTLINK" != "n" -a "$CONFIG_PPP_SYNCTTY" != "n" -a "$CONFIG_HDLC_PPP" = "y" ]; then
-                    bool '      Cyclades-PC300 MLPPP support' CONFIG_PC300_MLPPP
-            else
-                    comment '     Cyclades-PC300 MLPPP support is disabled.'+                       comment '     Refer to the file README.mlppp, provided by PC300 package.'
-            fi
+         if [ "$CONFIG_PPP" != "n" -a "$CONFIG_PPP_MULTLINK" != "n" -a \
+	      "$CONFIG_PPP_SYNCTTY" != "n" -a "$CONFIG_HDLC_PPP" = "y" ]; then
+	    bool '      Cyclades-PC300 MLPPP support' CONFIG_PC300_MLPPP
+	 else
+	    comment 'Cyclades-PC300 MLPPP support is disabled.'
+	    comment 'Refer to the file README.mlppp, provided by PC300 package.'
+	 fi
       fi
-
 
       dep_tristate '    SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
       dep_tristate '    Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids
--- a/drivers/pci/pci.ids	Sun May  5 20:39:49 2002
+++ b/drivers/pci/pci.ids	Sun May  5 20:39:49 2002
@@ -5087,22 +5087,35 @@
 		11d4 0048  SoundMAX Integrated Digital Audio
 	2426  82801AB AC'97 Modem
 	2428  82801AB PCI Bridge
-	2440  82820 820 (Camino 2) Chipset ISA Bridge (ICH2)
-	2442  82820 820 (Camino 2) Chipset USB (Hub A)
-	2443  82820 820 (Camino 2) Chipset SMBus
-	2444  82820 820 (Camino 2) Chipset USB (Hub B)
-	2445  82820 820 (Camino 2) Chipset AC'97 Audio Controller
-	2446  82820 820 (Camino 2) Chipset AC'97 Modem Controller
-	2448  82820 820 (Camino 2) Chipset PCI (-M)
-	2449  82820 (ICH2) Chipset Ethernet Controller
-	244a  82820 820 (Camino 2) Chipset IDE U100 (-M)
-	244b  82820 820 (Camino 2) Chipset IDE U100
-	244c  82820 820 (Camino 2) Chipset ISA Bridge (ICH2-M)
-	244e  82820 820 (Camino 2) Chipset PCI
-	245b  82801E C-ICH IDE
-	2485  AC'97 Audio Controller
-	248a  82801CAM ICH3-M IDE
-	248b  82801CA ICH3 IDE
+	2440  82801BA ISA Bridge (LPC)
+	2442  82801BA/BAM USB (Hub #1)
+	2443  82801BA/BAM SMBus
+	2444  82801BA/BAM USB (Hub #2)
+	2445  82801BA/BAM AC'97 Audio
+	2446  82801BA/BAM AC'97 Modem
+	2448  82801BAM/CAM PCI Bridge
+	2449  82801BA/BAM/CA/CAM Ethernet Controller
+	244a  82801BAM IDE U100
+	244b  82801BA IDE U100
+	244c  82801BAM ISA Bridge (LPC)
+	244e  82801BA/CA PCI Bridge
+	2450  82801E ISA Bridge (LPC)
+	2452  82801E USB
+	2453  82801E SMBus
+	2459  82801E Ethernet Controller 0
+	245b  82801E IDE U100
+	245d  82801E Ethernet Controller 1
+	245e  82801E PCI Bridge
+	2480  82801CA ISA Bridge (LPC)
+	2482  82801CA/CAM USB (Hub #1)
+	2483  82801CA/CAM SMBus
+	2484  82801CA/CAM USB (Hub #2)
+	2485  82801CA/CAM AC'97 Audio
+	2486  82801CA/CAM AC'97 Modem
+	2487  82801CA/CAM USB (Hub #3)
+	248a  82801CAM IDE U100
+	248b  82801CA IDE U100
+	248c  82801CAM ISA Bridge (LPC)
 	24cb  82801DB ICH4 IDE
 	2500  82820 820 (Camino) Chipset Host Bridge (MCH)
 		1043 801c  P3C-2000 system chipset
diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
--- a/drivers/scsi/ide-scsi.c	Sun May  5 20:39:49 2002
+++ b/drivers/scsi/ide-scsi.c	Sun May  5 20:39:49 2002
@@ -328,7 +328,7 @@
 		printk ("ide-scsi: %s: DMA complete\n", drive->name);
 #endif /* IDESCSI_DEBUG_LOG */
 		pc->actually_transferred=pc->request_transfer;
-		(void) drive->channel->udma(ide_dma_end, drive, NULL);
+		udma_stop(drive);
 	}
 
 	status = GET_STAT();						/* Clear the interrupt */
@@ -429,8 +429,12 @@
 	pc->current_position=pc->buffer;
 	bcount = min(pc->request_transfer, 63 * 1024);		/* Request to transfer the entire buffer at once */
 
-	if (drive->using_dma && rq->bio)
-		dma_ok = !drive->channel->udma(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive, rq);
+	if (drive->using_dma && rq->bio) {
+		if (test_bit (PC_WRITING, &pc->flags))
+			dma_ok = !udma_write(drive, rq);
+		else
+			dma_ok = !udma_read(drive, rq);
+	}
 
 	SELECT_DRIVE(drive->channel, drive);
 	if (IDE_CONTROL_REG)
@@ -441,7 +445,7 @@
 
 	if (dma_ok) {
 		set_bit(PC_DMA_IN_PROGRESS, &pc->flags);
-		(void) drive->channel->udma(ide_dma_begin, drive, NULL);
+		udma_start(drive, rq);
 	}
 	if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
 		ide_set_handler(drive, idescsi_transfer_pc, get_timeout(pc), NULL);
diff -Nru a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
--- a/drivers/scsi/scsi_debug.c	Sun May  5 20:39:50 2002
+++ b/drivers/scsi/scsi_debug.c	Sun May  5 20:39:50 2002
@@ -32,6 +32,7 @@
 #include <linux/init.h>
 #include <linux/proc_fs.h>
 #include <linux/smp_lock.h>
+#include <linux/vmalloc.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -40,7 +41,7 @@
 #include "scsi.h"
 #include "hosts.h"
 
-#include<linux/stat.h>
+#include <linux/stat.h>
 
 #ifndef LINUX_VERSION_CODE
 #include <linux/version.h>
diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c	Sun May  5 20:39:49 2002
+++ b/drivers/scsi/sd.c	Sun May  5 20:39:49 2002
@@ -41,7 +41,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
-
+#include <linux/vmalloc.h>
 #include <linux/smp.h>
 
 #include <asm/uaccess.h>
diff -Nru a/drivers/video/Makefile b/drivers/video/Makefile
--- a/drivers/video/Makefile	Sun May  5 20:39:49 2002
+++ b/drivers/video/Makefile	Sun May  5 20:39:49 2002
@@ -1,5 +1,5 @@
 # Makefile for the Linux video drivers.
-# 5 Aug 1999, James Simmons, <mailto:jsimmons@edgeglobal.com>
+# 5 Aug 1999, James Simmons, <mailto:jsimmons@users.sf.net>
 # Rewritten to use lists instead of if-statements.
 
 O_TARGET := video.o
@@ -10,7 +10,7 @@
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
 export-objs    := fbmem.o fbcmap.o fbcon.o fbmon.o modedb.o \
-		  fbcon-afb.o fbcon-ilbm.o fbgen.o \
+		  fbcon-afb.o fbcon-ilbm.o fbcon-accel.o \
 		  fbcon-vga.o fbcon-iplan2p2.o fbcon-iplan2p4.o \
 		  fbcon-iplan2p8.o fbcon-vga-planes.o fbcon-cfb16.o \
 		  fbcon-cfb2.o fbcon-cfb24.o fbcon-cfb32.o fbcon-cfb4.o \
diff -Nru a/drivers/video/atafb.c b/drivers/video/atafb.c
--- a/drivers/video/atafb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/atafb.c	Sun May  5 20:39:49 2002
@@ -1596,7 +1596,7 @@
 		var->xoffset = up(var->xoffset, 2);
 	}
 	par->hw.falcon.line_offset = bpp *
-	       	(fb_display[fb_info.currcon].var.xres_virtual - fb_display[currcon].var.xres) / 16;
+	       	(fb_display[fb_info.currcon].var.xres_virtual - fb_display[fb_info.currcon].var.xres) / 16;
 	if (par->hw.falcon.xoffset)
 		par->hw.falcon.line_offset -= bpp;
 	xoffset = var->xoffset - par->hw.falcon.xoffset;
@@ -2840,8 +2840,8 @@
 
     if (!options || !*options)
 		return 0;
-     
-    for(this_opt=strtok(options,","); this_opt; this_opt=strtok(NULL,",")) {
+    
+    while ((this_opt = strsep(options, ",")) != NULL) {	 
 	if (!*this_opt) continue;
 	if ((temp=get_video_mode(this_opt)))
 		default_par=temp;
diff -Nru a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
--- a/drivers/video/aty/atyfb_base.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/aty/atyfb_base.c	Sun May  5 20:39:49 2002
@@ -87,6 +87,9 @@
 #include <linux/adb.h>
 #include <linux/pmu.h>
 #endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
 #ifdef CONFIG_NVRAM
 #include <linux/nvram.h>
 #endif
@@ -815,6 +818,13 @@
 	display_info.disp_reg_address = info->ati_regbase_phys;
     }
 #endif /* CONFIG_FB_COMPAT_XPMAC */
+#ifdef CONFIG_BOOTX_TEXT
+	btext_update_display(info->frame_buffer_phys,
+			(((par->crtc.h_tot_disp>>16) & 0xff)+1)*8,
+			((par->crtc.v_tot_disp>>16) & 0x7ff)+1,
+			par->crtc.bpp,
+			par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
 }
 
 static int atyfb_decode_var(const struct fb_var_screeninfo *var,
diff -Nru a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c
--- a/drivers/video/aty128fb.c	Sun May  5 20:39:50 2002
+++ b/drivers/video/aty128fb.c	Sun May  5 20:39:50 2002
@@ -70,6 +70,9 @@
 #ifdef CONFIG_FB_COMPAT_XPMAC
 #include <asm/vc_ioctl.h>
 #endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif /* CONFIG_BOOTX_TEXT */
 
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
@@ -154,6 +157,7 @@
     {"Rage128 RL (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RL, rage_128},
     {"Rage128 Pro PF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_PF, rage_128_pro},
     {"Rage128 Pro PR (PCI)", PCI_DEVICE_ID_ATI_RAGE128_PR, rage_128_pro},
+    {"Rage128 Pro TR (AGP)", PCI_DEVICE_ID_ATI_RAGE128_U3, rage_128_pro},	
     {"Rage Mobility M3 (PCI)", PCI_DEVICE_ID_ATI_RAGE128_LE, rage_M3},
     {"Rage Mobility M3 (AGP)", PCI_DEVICE_ID_ATI_RAGE128_LF, rage_M3},
     {NULL, 0, rage_128}
@@ -216,9 +220,13 @@
 static char fontname[40] __initdata = { 0 };
 
 static int  noaccel __initdata = 0;
+#ifdef MODULE
 static char *font __initdata = NULL;
 static char *mode __initdata = NULL;
+#ifdef CONFIG_MTRR
 static int  nomtrr __initdata = 0;
+#endif
+#endif /* MODULE */
 
 static char *mode_option __initdata = NULL;
 
@@ -418,6 +426,7 @@
 	fb_set_cmap:	gen_set_cmap,
 	fb_setcolreg:	aty128fb_setcolreg,
 	fb_pan_display:	aty128fb_pan_display,
+	fb_blank:	aty128fb_blank,
 	fb_rasterimg:	aty128fb_rasterimg,
 };
 
@@ -1243,6 +1252,13 @@
 	display_info.disp_reg_address = info->regbase_phys;
     }
 #endif /* CONFIG_FB_COMPAT_XPMAC */
+#if defined(CONFIG_BOOTX_TEXT)
+	btext_update_display(info->frame_buffer_phys,
+			(((par->crtc.h_total>>16) & 0xff)+1)*8,
+			((par->crtc.v_total>>16) & 0x7ff)+1,
+			par->crtc.bpp,
+			par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
 }
 
     /*
@@ -1584,7 +1600,7 @@
     if (!options || !*options)
 	return 0;
 
-    while (this_opt = strsep(&options, ",")) {
+    while ((this_opt = strsep(&options, ",")) != NULL) {
 	if (!strncmp(this_opt, "font:", 5)) {
 	    char *p;
 	    int i;
@@ -1687,6 +1703,29 @@
             if (default_vmode <= 0 || default_vmode > VMODE_MAX)
                 default_vmode = VMODE_1024_768_60;
 
+	    /* iMacs need that resolution
+	     * PowerMac2,1 first r128 iMacs
+	     * PowerMac2,2 summer 2000 iMacs
+	     * PowerMac4,1 january 2001 iMacs "flower power"
+	     */
+	    if (machine_is_compatible("PowerMac2,1") ||
+		machine_is_compatible("PowerMac2,2") ||
+		machine_is_compatible("PowerMac4,1"))
+		default_vmode = VMODE_1024_768_75;
+
+	    /* iBook SE */
+	    if (machine_is_compatible("PowerBook2,2"))
+		default_vmode = VMODE_800_600_60;
+
+	    /* PowerBook Firewire (Pismo), iBook Dual USB */
+	    if (machine_is_compatible("PowerBook3,1") ||
+		machine_is_compatible("PowerBook4,1"))
+		default_vmode = VMODE_1024_768_60;
+
+	    /* PowerBook Titanium */
+	    if (machine_is_compatible("PowerBook3,2"))
+		default_vmode = VMODE_1152_768_60;
+	
             if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
                 default_cmode = CMODE_8;
 
diff -Nru a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c
--- a/drivers/video/chipsfb.c	Sun May  5 20:39:50 2002
+++ b/drivers/video/chipsfb.c	Sun May  5 20:39:50 2002
@@ -74,7 +74,7 @@
 
 #define write_ind(num, val, ap, dp)	do { \
 	outb((num), (ap)); outb((val), (dp)); \
-} while (0)
+} while (0);
 #define read_ind(num, var, ap, dp)	do { \
 	outb((num), (ap)); var = inb((dp)); \
 } while (0);
@@ -312,7 +312,7 @@
 	struct fb_var_screeninfo* var = &p->var;
 	
 	if (bpp == 16) {
-		if (con == info->currcon) {
+		if (con == p->info.currcon) {
 			write_cr(0x13, 200);		// Set line length (doublewords)
 			write_xr(0x81, 0x14);		// 15 bit (555) color mode
 			write_xr(0x82, 0x00);		// Disable palettes
@@ -334,7 +334,7 @@
 		disp->dispsw = &fbcon_dummy;
 #endif
 	} else if (bpp == 8) {
-		if (con == info->currcon) {
+		if (con == p->info.currcon) {
 			write_cr(0x13, 100);		// Set line length (doublewords)
 			write_xr(0x81, 0x12);		// 8 bit color mode
 			write_xr(0x82, 0x08);		// Graphics gamma enable
diff -Nru a/drivers/video/controlfb.c b/drivers/video/controlfb.c
--- a/drivers/video/controlfb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/controlfb.c	Sun May  5 20:39:49 2002
@@ -225,7 +225,7 @@
 	fb_set_var:	control_set_var,
 	fb_get_cmap:	control_get_cmap,
 	fb_set_cmap:	gen_set_cmap,
-	fb_setcolreg:	control_setcolreg,
+	fb_setcolreg:	controlfb_setcolreg,
 	fb_pan_display:	control_pan_display,
 	fb_blank:	controlfb_blank,
 	fb_mmap:	control_mmap,
@@ -1238,7 +1238,7 @@
     u8 *dest;
     int bytes = p->next_line, rows;
 
-    dest = p->info.screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2;
+    dest = p->fb_info->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p)*2;
     for (rows = fontheight(p); rows--; dest += bytes) {
        switch (fontwidth(p)) {
        case 16:
@@ -1261,7 +1261,7 @@
     u8 *dest;
     int bytes = p->next_line, rows;
 
-    dest = p->info.screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4;
+    dest = p->fb_info->screen_base + yy * fontheight(p) * bytes + xx * fontwidth(p) * 4;
     for (rows = fontheight(p); rows--; dest += bytes) {
        switch (fontwidth(p)) {
        case 16:
diff -Nru a/drivers/video/cyber2000fb.c b/drivers/video/cyber2000fb.c
--- a/drivers/video/cyber2000fb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/cyber2000fb.c	Sun May  5 20:39:49 2002
@@ -1111,13 +1111,13 @@
 static struct fb_ops cyber2000fb_ops = {
 	owner:		THIS_MODULE,
 	fb_set_var:	cyber2000fb_set_var,
-	fb_set_cmap:	gen_set_cmap,
 	fb_setcolreg:	cyber2000fb_setcolreg,
 	fb_pan_display:	cyber2000fb_pan_display,
 	fb_blank:	cyber2000fb_blank,
 	fb_get_fix:	gen_get_fix,
 	fb_get_var:	gen_get_var,
 	fb_get_cmap:	gen_get_cmap,
+	fb_set_cmap:	gen_set_cmap,
 };
 
 /*
diff -Nru a/drivers/video/cyberfb.c b/drivers/video/cyberfb.c
--- a/drivers/video/cyberfb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/cyberfb.c	Sun May  5 20:39:49 2002
@@ -604,6 +604,7 @@
 	}
 #endif
 	DPRINTK("EXIT\n");
+	return 0;
 }
 
 
diff -Nru a/drivers/video/fbgen.c b/drivers/video/fbgen.c
--- a/drivers/video/fbgen.c	Sun May  5 20:39:50 2002
+++ b/drivers/video/fbgen.c	Sun May  5 20:39:50 2002
@@ -21,6 +21,14 @@
 #include <asm/io.h>
 
 #include <video/fbcon.h>
+#include <video/fbcon-mfb.h>
+#include <video/fbcon-cfb2.h>
+#include <video/fbcon-cfb4.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+#include "fbcon-accel.h"
 
 /* ---- `Generic' versions of the frame buffer device operations ----------- */
 
@@ -59,7 +67,7 @@
 int gen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
 	*fix = info->fix;
-	return 0;
+	return 0;	
 }
 
 /**
@@ -92,7 +100,7 @@
 int gen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
 	*var = info->var;
-	return 0;
+	return 0;	
 }
 
 /**
@@ -113,6 +121,7 @@
     struct fb_info_gen *info2 = (struct fb_info_gen *)info;
     int err;
     int oldxres, oldyres, oldbpp, oldxres_virtual, oldyres_virtual, oldyoffset;
+    struct fb_bitfield oldred, oldgreen, oldblue;
 
     if ((err = fbgen_do_set_var(var, con == info->currcon, info2)))
 	return err;
@@ -122,12 +131,18 @@
 	oldxres_virtual = fb_display[con].var.xres_virtual;
 	oldyres_virtual = fb_display[con].var.yres_virtual;
 	oldbpp = fb_display[con].var.bits_per_pixel;
+	oldred = fb_display[con].var.red;
+	oldgreen = fb_display[con].var.green;
+	oldblue = fb_display[con].var.blue;
 	oldyoffset = fb_display[con].var.yoffset;
 	fb_display[con].var = *var;
 	if (oldxres != var->xres || oldyres != var->yres ||
 	    oldxres_virtual != var->xres_virtual ||
 	    oldyres_virtual != var->yres_virtual ||
 	    oldbpp != var->bits_per_pixel ||
+	    (!(memcmp(&oldred, &(var->red), sizeof(struct fb_bitfield)))) || 
+	    (!(memcmp(&oldgreen, &(var->green), sizeof(struct fb_bitfield)))) ||
+	    (!(memcmp(&oldblue, &(var->blue), sizeof(struct fb_bitfield)))) ||
 	    oldyoffset != var->yoffset) {
 	    fbgen_set_disp(con, info2);
 	    if (info->changevar)
@@ -142,6 +157,40 @@
 }
 
 
+int gen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
+{
+	int err;
+
+	if (con < 0 || (memcmp(&info->var, var, sizeof(struct fb_var_screeninfo)))) {
+		if (!info->fbops->fb_check_var) {
+			*var = info->var;
+			return 0;
+		}
+		
+		if ((err = info->fbops->fb_check_var(var, info)))
+			return err;
+
+		if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+			info->var = *var;
+			
+			if (con == info->currcon) {
+				if (info->fbops->fb_set_par)
+					info->fbops->fb_set_par(info);
+
+				if (info->fbops->fb_pan_display)
+					info->fbops->fb_pan_display(&info->var, con, info);
+
+				gen_set_disp(con, info);
+				fb_set_cmap(&info->cmap, 1, info);
+			}
+		
+			if (info->changevar)
+				info->changevar(con);
+		}
+	}
+	return 0;
+}
+
 /**
  *	fbgen_get_cmap - get the colormap
  *	@cmap: frame buffer colormap structure
@@ -181,7 +230,7 @@
 }
 
 /**
- *	gen_set_cmap - set the colormap
+ *	fbgen_set_cmap - set the colormap
  *	@cmap: frame buffer colormap structure
  *	@kspc: boolean, 0 copy local, 1 get_user() function
  *	@con: virtual console number
@@ -194,23 +243,47 @@
  *
  */
 
-int gen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-		 struct fb_info *info)
+int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+		   struct fb_info *info)
 {
-    struct display *disp = (con < 0) ? info->disp: &fb_display[con]; 	
-    int err, size = disp->var.bits_per_pixel == 16 ? 32 : 256;
+    int err;
 
-    if (!disp->cmap.len) {	/* no colormap allocated ? */
-	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
+    if (!fb_display[con].cmap.len) {	/* no colormap allocated ? */
+	int size = fb_display[con].var.bits_per_pixel == 16 ? 64 : 256;
+	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
     if (con == info->currcon)			/* current console ? */
 	return fb_set_cmap(cmap, kspc, info);
     else
-	fb_copy_cmap(cmap, &disp->cmap, kspc ? 0 : 1);
+	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
     return 0;
 }
 
+int gen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+		 struct fb_info *info)
+{
+	struct display *disp = (con < 0) ? info->disp : (fb_display + con);
+	struct fb_cmap *dcmap = &disp->cmap;
+	int err = 0;
+
+	/* No colormap allocated ? */
+	if (!dcmap->len) {
+		int size = info->cmap.len;
+
+		err = fb_alloc_cmap(dcmap, size, 0);
+	}
+ 	
+
+	if (!err && con == info->currcon) {
+		err = fb_set_cmap(cmap, kspc, info);
+		dcmap = &info->cmap;
+	}
+	
+	if (!err)
+		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
+	return err;
+}
 
 /**
  *	fbgen_pan_display - pan or wrap the display
@@ -292,7 +365,6 @@
     return 0;
 }
 
-
 /**
  *	fbgen_set_disp - set generic display
  *	@con: virtual console number
@@ -340,6 +412,52 @@
 #endif
 }
 
+void gen_set_disp(int con, struct fb_info *info)
+{
+	struct display *display;
+
+	if (con >= 0)
+		display = fb_display + con;
+	else
+		display = info->disp;
+
+	display->visual = info->fix.visual;
+	display->type	= info->fix.type;
+	display->type_aux = info->fix.type_aux;
+	display->ypanstep = info->fix.ypanstep;
+    	display->ywrapstep = info->fix.ywrapstep;
+    	display->line_length = info->fix.line_length;
+	if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR ||
+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+		display->can_soft_blank = info->fbops->fb_blank ? 1 : 0;
+		display->dispsw_data = NULL;
+	} else {
+		display->can_soft_blank = 0;
+		display->dispsw_data = info->pseudo_palette;
+	}
+	display->var = info->var;
+
+	/*
+	 * If we are setting all the virtual consoles, also set
+	 * the defaults used to create new consoles.
+	 */
+	if (con < 0 || info->var.activate & FB_ACTIVATE_ALL)
+		info->disp->var = info->var;	
+
+	if (info->var.bits_per_pixel == 24) {
+#ifdef FBCON_HAS_CFB24
+		display->scrollmode = SCROLL_YREDRAW;		
+		display->dispsw = &fbcon_cfb24;
+		return;
+#endif
+	}
+
+#ifdef FBCON_HAS_ACCEL
+	display->scrollmode = SCROLL_YNOMOVE;
+	display->dispsw = &fbcon_accel;
+#endif
+	return;
+}
 
 /**
  *	do_install_cmap - install the current colormap
@@ -390,6 +508,19 @@
     return 0;
 }
 
+int gen_update_var(int con, struct fb_info *info)
+{
+	int err;
+    
+	if (con == info->currcon) {
+		if (info->fbops->fb_pan_display) {
+			if ((err = info->fbops->fb_pan_display(&info->var, con, info)))
+				return err;
+		}
+	}	
+	return 0;
+}
+
 
 /**
  *	fbgen_switch - switch to a different virtual console.
@@ -419,6 +550,46 @@
 }
 
 
+int gen_switch(int con, struct fb_info *info)
+{
+	struct display *disp;
+	struct fb_cmap *cmap;
+	
+	if (info->currcon >= 0) {
+		disp = fb_display + info->currcon;
+	
+		/*
+		 * Save the old colormap and graphics mode.
+		 */
+		disp->var = info->var;
+		if (disp->cmap.len)
+			fb_copy_cmap(&info->cmap, &disp->cmap, 0);
+	}
+	
+	info->currcon = con;
+	disp = fb_display + con;
+	
+	/*
+	 * Install the new colormap and change the graphics mode. By default
+	 * fbcon sets all the colormaps and graphics modes to the default
+	 * values at bootup.
+	 *
+	 * Really, we want to set the colormap size depending on the
+	 * depth of the new grpahics mode. For now, we leave it as its
+	 * default 256 entry.
+	 */
+	if (disp->cmap.len)
+		cmap = &disp->cmap;
+	else
+		cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
+	
+	fb_copy_cmap(cmap, &info->cmap, 0);
+	
+	disp->var.activate = FB_ACTIVATE_NOW;
+	info->fbops->fb_set_var(&disp->var, con, info);
+ 	return 0;	  	
+}
+
 /**
  *	fbgen_blank - blank the screen
  *	@blank: boolean, 0 unblank, 1 blank
@@ -436,7 +607,7 @@
     struct fb_cmap cmap;
 
     if (fbhw->blank && !fbhw->blank(blank, info2))
-	return 1;
+	return 0;
     if (blank) {
 	memset(black, 0, 16*sizeof(u16));
 	cmap.red = black;
@@ -451,13 +622,16 @@
     return 0;	
 }
 
+/* generic frame buffer operations */
 EXPORT_SYMBOL(fbgen_get_fix);
 EXPORT_SYMBOL(gen_get_fix);
 EXPORT_SYMBOL(fbgen_get_var);
 EXPORT_SYMBOL(gen_get_var);
 EXPORT_SYMBOL(fbgen_set_var);
+EXPORT_SYMBOL(gen_set_var);
 EXPORT_SYMBOL(fbgen_get_cmap);
 EXPORT_SYMBOL(gen_get_cmap);
+EXPORT_SYMBOL(fbgen_set_cmap);
 EXPORT_SYMBOL(gen_set_cmap);
 EXPORT_SYMBOL(fbgen_pan_display);
 /* helper functions */
@@ -465,7 +639,9 @@
 EXPORT_SYMBOL(fbgen_set_disp);
 EXPORT_SYMBOL(do_install_cmap);
 EXPORT_SYMBOL(fbgen_update_var);
+EXPORT_SYMBOL(gen_update_var);
 EXPORT_SYMBOL(fbgen_switch);
+EXPORT_SYMBOL(gen_switch);
 EXPORT_SYMBOL(fbgen_blank);
 
 MODULE_LICENSE("GPL");
diff -Nru a/drivers/video/fbmem.c b/drivers/video/fbmem.c
--- a/drivers/video/fbmem.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/fbmem.c	Sun May  5 20:39:49 2002
@@ -522,7 +522,7 @@
 			set_con2fb_map(i, con2fb.framebuffer);
 		return 0;
 	case FBIOBLANK:
-		if (fb->fb_blank == 0)
+		if (fb->fb_blank == NULL)
 			return -EINVAL;
 		return fb->fb_blank(arg, info);
 	default:
diff -Nru a/drivers/video/imsttfb.c b/drivers/video/imsttfb.c
--- a/drivers/video/imsttfb.c	Sun May  5 20:39:50 2002
+++ b/drivers/video/imsttfb.c	Sun May  5 20:39:50 2002
@@ -1908,7 +1908,7 @@
 			p->ramdac = TVP;
 			break;
 		default:
-			printk(KERN_INFO "imsttfb: Device 0x%lx unknown, "
+			printk(KERN_INFO "imsttfb: Device 0x%x unknown, "
 					 "contact maintainer.\n", pdev->device);
 			return -ENODEV;
 	}
diff -Nru a/drivers/video/neofb.c b/drivers/video/neofb.c
--- a/drivers/video/neofb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/neofb.c	Sun May  5 20:39:49 2002
@@ -423,8 +423,8 @@
 /*
  *    Set a single color register. Return != 0 for invalid regno.
  */
-static int neo_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
-			 u_int transp, struct fb_info *fb)
+static int neofb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+			   u_int transp, struct fb_info *fb)
 {
   struct neofb_info *info = (struct neofb_info *)fb;
 
@@ -831,46 +831,6 @@
 }
 
 /*
- * Set the Colormap
- */
-static int neofb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
-			  struct fb_info *fb)
-{
-  struct neofb_info *info = (struct neofb_info *)fb;
-  struct display* disp = (con < 0) ? fb->disp : (fb_display + con);
-  struct fb_cmap *dcmap = &disp->cmap;
-  int err = 0;
-
-  /* no colormap allocated? */
-  if (!dcmap->len)
-    {
-      int size;
-
-      if (fb->var.bits_per_pixel == 8)
-	size = NR_PALETTE;
-      else
-	size = 32;
-
-      err = fb_alloc_cmap (dcmap, size, 0);
-    }
-
-  /*
-   * we should be able to remove this test once fbcon has been
-   * "improved" --rmk
-   */
-  if (!err && con == info->fb.currcon)
-    {
-      err = fb_set_cmap (cmap, kspc, neo_setcolreg, fb);
-      dcmap = &fb->cmap;
-    }
-
-  if (!err)
-    fb_copy_cmap (cmap, dcmap, kspc ? 0 : 1);
-
-  return err;
-}
-
-/*
  * neoCalcVCLK --
  *
  * Determine the closest clock frequency to the one requested.
@@ -1570,7 +1530,7 @@
         neofb_set_par (info, &par);
 
       neofb_update_start (info, var);
-      fb_set_cmap (&fb->cmap, 1, neo_setcolreg, fb);
+      fb_set_cmap (&fb->cmap, 1, fb);
 
       if (var->accel_flags & FB_ACCELF_TEXT)
 	neo2200_accel_init (info, var);
@@ -1673,7 +1633,7 @@
 /*
  *    (Un)Blank the display.
  */
-static void neofb_blank (int blank, struct fb_info *fb)
+static int neofb_blank (int blank, struct fb_info *fb)
 {
   //  struct neofb_info *info = (struct neofb_info *)fb;
 
@@ -1706,40 +1666,16 @@
     default: /* case 0, or anything else: unblank */
       break;
     }
-}
-
-/*
- * Get the currently displayed virtual consoles colormap.
- */
-static int gen_get_cmap (struct fb_cmap *cmap, int kspc, int con, struct fb_info *fb)
-{
-  fb_copy_cmap (&fb->cmap, cmap, kspc ? 0 : 2);
-  return 0;
-}
-
-/*
- * Get the currently displayed virtual consoles fixed part of the display.
- */
-static int gen_get_fix (struct fb_fix_screeninfo *fix, int con, struct fb_info *fb)
-{
-  *fix = fb->fix;
-  return 0;
-}
-
-/*
- * Get the current user defined part of the display.
- */
-static int gen_get_var (struct fb_var_screeninfo *var, int con, struct fb_info *fb)
-{
-  *var = fb->var;
-  return 0;
+   return 0; 	
 }
 
 static struct fb_ops neofb_ops = {
   owner:          THIS_MODULE,
   fb_set_var:     neofb_set_var,
-  fb_set_cmap:    neofb_set_cmap,
+  fb_set_cmap:    gen_set_cmap,
+  fb_setcolreg:	  neofb_setcolreg,	
   fb_pan_display: neofb_pan_display,
+  fb_blank:	  neofb_blank,	
   fb_get_fix:     gen_get_fix,
   fb_get_var:     gen_get_var,
   fb_get_cmap:    gen_get_cmap,
@@ -2166,7 +2102,6 @@
   info->fb.changevar      = NULL;
   info->fb.switch_con     = neofb_switch;
   info->fb.updatevar      = neofb_updatevar;
-  info->fb.blank          = neofb_blank;
   info->fb.flags          = FBINFO_FLAG_DEFAULT;
   info->fb.disp           = (struct display *)(info + 1);
   info->fb.pseudo_palette = (void *)(info->fb.disp + 1);
diff -Nru a/drivers/video/offb.c b/drivers/video/offb.c
--- a/drivers/video/offb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/offb.c	Sun May  5 20:39:49 2002
@@ -49,7 +49,8 @@
 	cmap_r128,	/* ATI Rage128 */
 	cmap_M3A,	/* ATI Rage Mobility M3 Head A */
 	cmap_M3B,	/* ATI Rage Mobility M3 Head B */
-	cmap_radeon	/* ATI Radeon */
+	cmap_radeon,	/* ATI Radeon */
+	cmap_gxt2000,	/* IBM GXT2000 */
 };
 
 struct fb_info_offb {
@@ -61,6 +62,7 @@
     volatile unsigned char *cmap_adr;
     volatile unsigned char *cmap_data;
     int cmap_type;
+    int blanked;
     union {
 #ifdef FBCON_HAS_CFB16
 	u16 cfb16[16];
@@ -207,9 +209,11 @@
 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-    if (con == info->currcon) /* current console? */
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    if (con == info->currcon && !info2->blanked) /* current console? */		
 	return fb_get_cmap(cmap, kspc, offb_getcolreg, info);
-    else if (fb_display[con].cmap.len) /* non default colormap? */
+    if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
     {
@@ -237,7 +241,7 @@
 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
-    if (con == info->currcon)			/* current console? */
+    if (con == info->currcon && !info2->blanked)	/* current console? */
 	return fb_set_cmap(cmap, kspc, info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
@@ -254,7 +258,17 @@
     int i, j;
 
     if (!info2->cmap_adr)
-	return;
+	return 0;
+
+    if (!info2->blanked) {
+       if (!blank)
+	   return 0;
+       if (fb_display[info->currcon].cmap.len)
+   	   fb_get_cmap(&fb_display[info->currcon].cmap, 1, offb_getcolreg,info);
+    }
+
+    info2->blanked = blank;
+	
 
     if (blank)
 	for (i = 0; i < 256; i++) {
@@ -288,6 +302,9 @@
     	        out_8(info2->cmap_adr + 0xb0, i);
 	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
 	    	break;
+	    case cmap_gxt2000:
+		out_le32((unsigned *)info2->cmap_adr + i, 0);
+		break;
 	    }
 	}
     else
@@ -496,6 +513,10 @@
 		info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
 		info->cmap_data = info->cmap_adr + 1;
 		info->cmap_type = cmap_m64;
+	} else if (device_is_compatible(dp, "pci1014,b7")) {
+		unsigned long regbase = dp->addrs[0].address;
+		info->cmap_adr = ioremap(regbase + 0x6000, 0x1000);
+		info->cmap_type = cmap_gxt2000;
 	}
         fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
 				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
@@ -664,11 +685,12 @@
 #endif /* CONFIG_FB_COMPAT_XPMAC) */
 }
 
-
 static int offbcon_switch(int con, struct fb_info *info)
 {
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;	
+
     /* Do we have to save the colormap? */
-    if (fb_display[info->currcon].cmap.len)
+    if (fb_display[info->currcon].cmap.len && !info2->blanked)
 	fb_get_cmap(&fb_display[info->currcon].cmap, 1, offb_getcolreg, info);
 
     info->currcon = con;
@@ -765,6 +787,10 @@
 	out_8(info2->cmap_adr + 0xb0, regno);
   	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
     		(red << 16 | green << 8 | blue));
+	break;
+    case cmap_gxt2000:
+	out_le32((unsigned *)info2->cmap_adr + regno,
+		(red << 16 | green << 8 | blue));
 	break;
     }
 
diff -Nru a/drivers/video/platinumfb.c b/drivers/video/platinumfb.c
--- a/drivers/video/platinumfb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/platinumfb.c	Sun May  5 20:39:49 2002
@@ -329,10 +329,10 @@
 	struct fb_info_platinum *info = (struct fb_info_platinum *) fb;
 	struct fb_par_platinum par;
 
-	if (fb_display[info->currcon].cmap.len)
-		fb_get_cmap(&fb_display[info->currcon].cmap, 1, platinum_getcolreg,
+	if (fb_display[fb->currcon].cmap.len)
+		fb_get_cmap(&fb_display[fb->currcon].cmap, 1, platinum_getcolreg,
 			    fb);
-	info->currcon = con;
+	fb->currcon = con;
 
 	platinum_var_to_par(&fb_display[con].var, &par, info);
 	platinum_set_par(&par, info);
@@ -375,7 +375,7 @@
 	out_le32(&info->platinum_regs->ctrl.r, ctrl);
 */
 /* TODO: Figure out how the heck to powerdown this thing! */
-    return 0;
+	return 0;
 }
 
 static int platinum_getcolreg(u_int regno, u_int *red, u_int *green,
diff -Nru a/drivers/video/valkyriefb.c b/drivers/video/valkyriefb.c
--- a/drivers/video/valkyriefb.c	Sun May  5 20:39:49 2002
+++ b/drivers/video/valkyriefb.c	Sun May  5 20:39:49 2002
@@ -260,7 +260,7 @@
 			    fb);
 	fb->currcon = con;
 #if 1
-	valkyrie_var_to_par(&fb_display[currcon].var, &par, fb);
+	valkyrie_var_to_par(&fb_display[fb->currcon].var, &par, fb);
 	valkyrie_set_par(&par, info);
 	do_install_cmap(con, fb);
 #else
diff -Nru a/fs/buffer.c b/fs/buffer.c
--- a/fs/buffer.c	Sun May  5 20:39:49 2002
+++ b/fs/buffer.c	Sun May  5 20:39:49 2002
@@ -49,15 +49,6 @@
 } ____cacheline_aligned_in_smp bh_wait_queue_heads[1<<BH_WAIT_TABLE_ORDER];
 
 /*
- * Several of these buffer list functions are exported to filesystems,
- * so we do funny things with the spinlocking to support those
- * filesystems while still using inode->i_bufferlist_lock for
- * most applications.
- * FIXME: put a spinlock in the reiserfs journal and kill this lock.
- */
-static spinlock_t global_bufferlist_lock = SPIN_LOCK_UNLOCKED;
-
-/*
  * Debug/devel support stuff
  */
 
@@ -186,6 +177,12 @@
 	page_cache_release(page);
 }
 
+static void buffer_io_error(struct buffer_head *bh)
+{
+	printk(KERN_ERR "Buffer I/O error on device %s, logical block %ld\n",
+			bdevname(bh->b_bdev), bh->b_blocknr);
+}
+
 /*
  * Default synchronous end-of-IO handler..  Just mark it up-to-date and
  * unlock the buffer. This is what ll_rw_block uses too.
@@ -193,7 +190,7 @@
 void end_buffer_io_sync(struct buffer_head *bh, int uptodate)
 {
 	if (!uptodate)
-		printk("%s: I/O error\n", __FUNCTION__);
+		buffer_io_error(bh);
 	if (uptodate)
 		set_buffer_uptodate(bh);
 	else
@@ -442,8 +439,6 @@
 void buffer_insert_list(spinlock_t *lock,
 		struct buffer_head *bh, struct list_head *list)
 {
-	if (lock == NULL)
-		lock = &global_bufferlist_lock;
 	spin_lock(lock);
 	list_del(&bh->b_inode_buffers);
 	list_add(&bh->b_inode_buffers, list);
@@ -537,7 +532,11 @@
 	yield();
 }
 
-static void end_buffer_io_async(struct buffer_head *bh, int uptodate)
+/*
+ * I/O completion handler for block_read_full_page() and brw_page() - pages
+ * which come unlocked at the end of I/O.
+ */
+static void end_buffer_async_read(struct buffer_head *bh, int uptodate)
 {
 	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
 	unsigned long flags;
@@ -545,16 +544,18 @@
 	struct page *page;
 	int page_uptodate = 1;
 
+	BUG_ON(!buffer_async_read(bh));
+
 	if (!uptodate)
-		printk("%s: I/O error\n", __FUNCTION__);
+		buffer_io_error(bh);
 
-	if (uptodate)
+	page = bh->b_page;
+	if (uptodate) {
 		set_buffer_uptodate(bh);
-	else
+	} else {
 		clear_buffer_uptodate(bh);
-	page = bh->b_page;
-	if (!uptodate)
 		SetPageError(page);
+	}
 
 	/*
 	 * Be _very_ careful from here on. Bad things can happen if
@@ -562,13 +563,13 @@
 	 * decide that the page is now completely done.
 	 */
 	spin_lock_irqsave(&page_uptodate_lock, flags);
-	clear_buffer_async(bh);
+	clear_buffer_async_read(bh);
 	unlock_buffer(bh);
 	tmp = bh;
 	do {
 		if (!buffer_uptodate(tmp))
 			page_uptodate = 0;
-		if (buffer_async(tmp)) {
+		if (buffer_async_read(tmp)) {
 			if (buffer_locked(tmp))
 				goto still_busy;
 			if (!buffer_mapped(bh))
@@ -584,13 +585,53 @@
 	 */
 	if (page_uptodate && !PageError(page))
 		SetPageUptodate(page);
-	if (PageWriteback(page)) {
-		/* It was a write */
-		end_page_writeback(page);
+	unlock_page(page);
+	return;
+
+still_busy:
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	return;
+}
+
+/*
+ * Completion handler for block_write_full_page() - pages which are unlocked
+ * during I/O, and which have PageWriteback cleared upon I/O completion.
+ */
+static void end_buffer_async_write(struct buffer_head *bh, int uptodate)
+{
+	static spinlock_t page_uptodate_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flags;
+	struct buffer_head *tmp;
+	struct page *page;
+
+	BUG_ON(!buffer_async_write(bh));
+
+	if (!uptodate)
+		buffer_io_error(bh);
+
+	page = bh->b_page;
+	if (uptodate) {
+		set_buffer_uptodate(bh);
 	} else {
-		/* read */
-		unlock_page(page);
+		clear_buffer_uptodate(bh);
+		SetPageError(page);
 	}
+
+	spin_lock_irqsave(&page_uptodate_lock, flags);
+	clear_buffer_async_write(bh);
+	unlock_buffer(bh);
+	tmp = bh->b_this_page;
+	while (tmp != bh) {
+		if (buffer_async_write(tmp)) {
+			if (buffer_locked(tmp))
+				goto still_busy;
+			if (!buffer_mapped(bh))
+				BUG();
+		}
+		tmp = tmp->b_this_page;
+	}
+	spin_unlock_irqrestore(&page_uptodate_lock, flags);
+	end_page_writeback(page);
 	return;
 
 still_busy:
@@ -599,26 +640,39 @@
 }
 
 /*
- * If a page's buffers are under async writeout (end_buffer_io_async
+ * If a page's buffers are under async readin (end_buffer_async_read
  * completion) then there is a possibility that another thread of
  * control could lock one of the buffers after it has completed
  * but while some of the other buffers have not completed.  This
- * locked buffer would confuse end_buffer_io_async() into not unlocking
- * the page.  So the absence of BH_Async tells end_buffer_io_async()
+ * locked buffer would confuse end_buffer_async_read() into not unlocking
+ * the page.  So the absence of BH_Async_Read tells end_buffer_async_read()
  * that this buffer is not under async I/O.
  *
  * The page comes unlocked when it has no locked buffer_async buffers
  * left.
  *
- * The page lock prevents anyone starting new async I/O against any of
+ * PageLocked prevents anyone starting new async I/O reads any of
  * the buffers.
+ *
+ * PageWriteback is used to prevent simultaneous writeout of the same
+ * page.
+ *
+ * PageLocked prevents anyone from starting writeback of a page which is
+ * under read I/O (PageWriteback is only ever set against a locked page).
  */
-inline void set_buffer_async_io(struct buffer_head *bh)
+inline void mark_buffer_async_read(struct buffer_head *bh)
+{
+	bh->b_end_io = end_buffer_async_read;
+	set_buffer_async_read(bh);
+}
+EXPORT_SYMBOL(mark_buffer_async_read);
+
+inline void mark_buffer_async_write(struct buffer_head *bh)
 {
-	bh->b_end_io = end_buffer_io_async;
-	set_buffer_async(bh);
+	bh->b_end_io = end_buffer_async_write;
+	set_buffer_async_write(bh);
 }
-EXPORT_SYMBOL(set_buffer_async_io);
+EXPORT_SYMBOL(mark_buffer_async_write);
 
 /*
  * osync is designed to support O_SYNC io.  It waits synchronously for
@@ -636,14 +690,10 @@
 	struct list_head *p;
 	int err = 0;
 
-	if (lock == NULL)
-		lock = &global_bufferlist_lock;
-
 	spin_lock(lock);
 repeat:
-	for (p = list->prev; 
-	     bh = BH_ENTRY(p), p != list;
-	     p = bh->b_inode_buffers.prev) {
+	list_for_each_prev(p, list) {
+		bh = BH_ENTRY(p);
 		if (buffer_locked(bh)) {
 			get_bh(bh);
 			spin_unlock(lock);
@@ -684,9 +734,6 @@
 	struct list_head tmp;
 	int err = 0, err2;
 
-	if (lock == NULL)
-		lock = &global_bufferlist_lock;
-	
 	INIT_LIST_HEAD(&tmp);
 
 	spin_lock(lock);
@@ -1091,17 +1138,15 @@
 /*
  * Called when truncating a buffer on a page completely.
  */
-static void discard_buffer(struct buffer_head * bh)
+static /* inline */ void discard_buffer(struct buffer_head * bh)
 {
-	if (buffer_mapped(bh)) {
-		clear_buffer_dirty(bh);
-		lock_buffer(bh);
-		bh->b_bdev = NULL;
-		clear_buffer_mapped(bh);
-		clear_buffer_req(bh);
-		clear_buffer_new(bh);
-		unlock_buffer(bh);
-	}
+	lock_buffer(bh);
+	clear_buffer_dirty(bh);
+	bh->b_bdev = NULL;
+	clear_buffer_mapped(bh);
+	clear_buffer_req(bh);
+	clear_buffer_new(bh);
+	unlock_buffer(bh);
 }
 
 /**
@@ -1209,10 +1254,13 @@
 	tail->b_this_page = head;
 
 	spin_lock(&page->mapping->host->i_bufferlist_lock);
-	if (PageDirty(page)) {
+	if (PageUptodate(page) || PageDirty(page)) {
 		bh = head;
 		do {
-			set_buffer_dirty(bh);
+			if (PageDirty(page))
+				set_buffer_dirty(bh);
+			if (PageUptodate(page))
+				set_buffer_uptodate(bh);
 			bh = bh->b_this_page;
 		} while (bh != head);
 	}
@@ -1290,6 +1338,16 @@
 					(1 << BH_Dirty)|(1 << BH_Uptodate));
 	}
 
+	/*
+	 * Be very careful.  We have no exclusion from __set_page_dirty_buffers
+	 * here, and the (potentially unmapped) buffers may become dirty at
+	 * any time.  If a buffer becomes dirty here after we've inspected it
+	 * then we just miss that fact, and the page stays dirty.
+	 *
+	 * Buffers outside i_size may be dirtied by __set_page_dirty_buffers;
+	 * handle that here by just cleaning them.
+	 */
+
 	block = page->index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
 	head = page_buffers(page);
 	bh = head;
@@ -1300,16 +1358,18 @@
 	 */
 	do {
 		if (block > last_block) {
-			if (buffer_dirty(bh))
-				buffer_error();
-			if (buffer_mapped(bh))
-				buffer_error();
 			/*
-			 * NOTE: this buffer can only be marked uptodate
-			 * because we know that block_write_full_page has
-			 * zeroed it out.  That seems unnecessary and may go
-			 * away.
+			 * mapped buffers outside i_size will occur, because
+			 * this page can be outside i_size when there is a
+			 * truncate in progress.
+			 *
+			 * if (buffer_mapped(bh))
+			 *	buffer_error();
 			 */
+			/*
+			 * The buffer was zeroed by block_write_full_page()
+			 */
+			clear_buffer_dirty(bh);
 			set_buffer_uptodate(bh);
 		} else if (!buffer_mapped(bh) && buffer_dirty(bh)) {
 			if (buffer_new(bh))
@@ -1329,14 +1389,12 @@
 
 	do {
 		get_bh(bh);
-		if (buffer_dirty(bh)) {
+		if (buffer_mapped(bh) && buffer_dirty(bh)) {
 			lock_buffer(bh);
-			if (buffer_dirty(bh)) {
-				if (!buffer_mapped(bh))
-					buffer_error();
+			if (test_clear_buffer_dirty(bh)) {
 				if (!buffer_uptodate(bh))
 					buffer_error();
-				set_buffer_async_io(bh);
+				mark_buffer_async_write(bh);
 			} else {
 				unlock_buffer(bh);
 			}
@@ -1354,8 +1412,7 @@
 	 */
 	do {
 		struct buffer_head *next = bh->b_this_page;
-		if (buffer_async(bh)) {
-			clear_buffer_dirty(bh);
+		if (buffer_async_write(bh)) {
 			submit_bh(WRITE, bh);
 			nr_underway++;
 		}
@@ -1397,7 +1454,7 @@
 	do {
 		if (buffer_mapped(bh)) {
 			lock_buffer(bh);
-			set_buffer_async_io(bh);
+			mark_buffer_async_write(bh);
 		} else {
 			/*
 			 * The buffer may have been set dirty during
@@ -1409,7 +1466,7 @@
 	} while (bh != head);
 	do {
 		struct buffer_head *next = bh->b_this_page;
-		if (buffer_mapped(bh)) {
+		if (buffer_async_write(bh)) {
 			set_buffer_uptodate(bh);
 			clear_buffer_dirty(bh);
 			submit_bh(WRITE, bh);
@@ -1630,13 +1687,9 @@
 
 	/* Stage two: lock the buffers */
 	for (i = 0; i < nr; i++) {
-		struct buffer_head * bh = arr[i];
+		bh = arr[i];
 		lock_buffer(bh);
-		if (buffer_uptodate(bh))
-			buffer_error();
-		if (buffer_dirty(bh))
-			buffer_error();
-		set_buffer_async_io(bh);
+		mark_buffer_async_read(bh);
 	}
 
 	/*
@@ -1645,9 +1698,9 @@
 	 * the underlying blockdev brought it uptodate (the sct fix).
 	 */
 	for (i = 0; i < nr; i++) {
-		struct buffer_head * bh = arr[i];
+		bh = arr[i];
 		if (buffer_uptodate(bh))
-			end_buffer_io_async(bh, 1);
+			end_buffer_async_read(bh, 1);
 		else
 			submit_bh(READ, bh);
 	}
@@ -2073,9 +2126,15 @@
 		bh->b_blocknr = *(b++);
 		bh->b_bdev = bdev;
 		set_buffer_mapped(bh);
-		if (rw == WRITE)
+		if (rw == WRITE) {
 			set_buffer_uptodate(bh);
-		set_buffer_async_io(bh);
+			clear_buffer_dirty(bh);
+		}
+		/*
+		 * Swap pages are locked during writeout, so use
+		 * buffer_async_read in strange ways.
+		 */
+		mark_buffer_async_read(bh);
 		bh = bh->b_this_page;
 	} while (bh != head);
 
@@ -2161,14 +2220,6 @@
  * total exclusion from __set_page_dirty_buffers().  That is obtained with
  * i_bufferlist_lock.
  *
- * Nobody should be calling try_to_free_buffers against a page which is
- * eligible for set_page_dirty() treatment anyway - the page is clearly
- * not freeable.  So we could just test page_count(page) here and complain
- * then scram if it's wrong.
- *
- * If any buffer is not uptodate then the entire page is set not uptodate,
- * as the partial uptodateness information is about to be lost.
- *
  * try_to_free_buffers() is non-blocking.
  */
 static inline int buffer_busy(struct buffer_head *bh)
@@ -2224,8 +2275,17 @@
 	inode = page->mapping->host;
 	spin_lock(&inode->i_bufferlist_lock);
 	ret = drop_buffers(page);
-	if (ret)
+	if (ret && !PageSwapCache(page)) {
+		/*
+		 * If the filesystem writes its buffers by hand (eg ext3)
+		 * then we can have clean buffers against a dirty page.  We
+		 * clean the page here; otherwise later reattachment of buffers
+		 * could encounter a non-uptodate page, which is unresolvable.
+		 * This only applies in the rare case where try_to_free_buffers
+		 * succeeds but the page is not freed.
+		 */
 		ClearPageDirty(page);
+	}
 	spin_unlock(&inode->i_bufferlist_lock);
 	return ret;
 }
diff -Nru a/fs/fs-writeback.c b/fs/fs-writeback.c
--- a/fs/fs-writeback.c	Sun May  5 20:39:50 2002
+++ b/fs/fs-writeback.c	Sun May  5 20:39:50 2002
@@ -17,6 +17,7 @@
 #include <linux/spinlock.h>
 #include <linux/sched.h>
 #include <linux/fs.h>
+#include <linux/mm.h>
 #include <linux/writeback.h>
 
 /**
@@ -135,7 +136,7 @@
 	if (mapping->a_ops->writeback_mapping)
 		mapping->a_ops->writeback_mapping(mapping, nr_to_write);
 	else
-		filemap_fdatawrite(mapping);
+		generic_writeback_mapping(mapping, NULL);
 
 	/* Don't write the inode if only I_DIRTY_PAGES was set */
 	if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC))
diff -Nru a/fs/jbd/commit.c b/fs/jbd/commit.c
--- a/fs/jbd/commit.c	Sun May  5 20:39:49 2002
+++ b/fs/jbd/commit.c	Sun May  5 20:39:49 2002
@@ -267,7 +267,7 @@
 sync_datalist_empty:
 	/*
 	 * Wait for all the async writepage data.  As they become unlocked
-	 * in end_buffer_io_async(), the only place where they can be
+	 * in end_buffer_async_write(), the only place where they can be
 	 * reaped is in try_to_free_buffers(), and we're locked against
 	 * that.
 	 */
diff -Nru a/fs/jbd/transaction.c b/fs/jbd/transaction.c
--- a/fs/jbd/transaction.c	Sun May  5 20:39:49 2002
+++ b/fs/jbd/transaction.c	Sun May  5 20:39:49 2002
@@ -905,8 +905,8 @@
  * The buffer is placed on the transaction's data list and is marked as
  * belonging to the transaction.
  *
- * If `async' is set then the writebask will be initiated by the caller
- * using submit_bh -> end_buffer_io_async.  We put the buffer onto
+ * If `async' is set then the writebabk will be initiated by the caller
+ * using submit_bh -> end_buffer_async_write.  We put the buffer onto
  * t_async_datalist.
  * 
  * Returns error number or 0 on success.  
@@ -1548,7 +1548,7 @@
 	__blist_del_buffer(list, jh);
 	jh->b_jlist = BJ_None;
 	if (test_and_clear_bit(BH_JBDDirty, &jh2bh(jh)->b_state))
-		set_buffer_dirty(jh2bh(jh));
+		mark_buffer_dirty(jh2bh(jh));	/* Expose it to the VM */
 }
 
 void journal_unfile_buffer(struct journal_head *jh)
@@ -1587,7 +1587,7 @@
 		goto out;
 	}
 
-	if (!buffer_uptodate(bh))
+	if (!buffer_uptodate(bh))	/* AKPM: why? */
 		goto out;
 
 	if (jh->b_next_transaction != 0)
@@ -1775,9 +1775,6 @@
 
 	BUFFER_TRACE(bh, "entry");
 
-	if (!buffer_mapped(bh))
-		return 1;
-
 	/* It is safe to proceed here without the
 	 * journal_datalist_spinlock because the buffers cannot be
 	 * stolen by try_to_free_buffers as long as we are holding the
@@ -1854,8 +1851,7 @@
 	}
 
 zap_buffer:	
-	if (buffer_dirty(bh))
-		clear_buffer_dirty(bh);
+	clear_buffer_dirty(bh);
 	J_ASSERT_BH(bh, !buffer_jdirty(bh));
 	clear_buffer_mapped(bh);
 	clear_buffer_req(bh);
diff -Nru a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c
--- a/fs/nfs/nfsroot.c	Sun May  5 20:39:50 2002
+++ b/fs/nfs/nfsroot.c	Sun May  5 20:39:50 2002
@@ -374,8 +374,8 @@
 {
 	struct sockaddr_in sin;
 
-	printk(KERN_NOTICE "Looking up port of RPC %d/%d on %s\n",
-		program, version, in_ntoa(servaddr));
+	printk(KERN_NOTICE "Looking up port of RPC %d/%d on %u.%u.%u.%u\n",
+		program, version, NIPQUAD(servaddr));
 	set_sockaddr(&sin, servaddr, 0);
 	return rpc_getport_external(&sin, program, version, proto);
 }
diff -Nru a/fs/ntfs/ChangeLog b/fs/ntfs/ChangeLog
--- a/fs/ntfs/ChangeLog	Sun May  5 20:39:49 2002
+++ b/fs/ntfs/ChangeLog	Sun May  5 20:39:49 2002
@@ -14,11 +14,6 @@
 	  strictly a speed optimization. Obviously need to keep the ->run_list
 	  locked or RACE. load_attribute_list() already performs such an
 	  optimization so use the same optimization where desired.
-	- Optimize all our readpage functions to not do i/o on buffer heads
-	  beyond initialized_size, just zero the buffer heads instead.
-	  Question: How to setup the buffer heads so they point to the on disk
-	  location correctly (after all they are allocated) but are not read
-	  from disk?
 	- Consider if ntfs_file_read_compressed_block() shouldn't be coping
 	  with initialized_size < data_size. I don't think it can happen but
 	  it requires more careful consideration.
@@ -26,6 +21,24 @@
 	  several copies of almost identicall functions and the functions are
 	  quite big. Modularising them a bit, e.g. a-la get_block(), will make
 	  them cleaner and make code reuse easier.
+	- Want to use dummy inodes for address space i/o. We need some VFS
+	  changes first, which are currently under discussion.
+
+2.0.6 - Major bugfix to make compatible with other kernel changes.
+
+	- Initialize the mftbmp address space properly now that there are more
+	  fields in the struct address_space. This was leading to hangs and
+	  oopses on umount since 2.5.12 because of changes to other parts of
+	  the kernel. We probably want a kernel generic init_address_space()
+	  function...
+	- Drop BKL from ntfs_readdir() after consultation with Al Viro. The
+	  only caller of ->readdir() is vfs_readdir() which holds i_sem during
+	  the call, and i_sem is sufficient protection against changes in the
+	  directory inode (including ->i_size).
+	- Use generic_file_llseek() for directories (as opposed to
+	  default_llseek()) as this downs i_sem instead of the BKL which is
+	  what we now need for exclusion against ->f_pos changes considering we
+	  no longer take the BKL in ntfs_readdir().
 
 2.0.5 - Major bugfix. Buffer overflow in extent inode handling.
 
diff -Nru a/fs/ntfs/Makefile b/fs/ntfs/Makefile
--- a/fs/ntfs/Makefile	Sun May  5 20:39:49 2002
+++ b/fs/ntfs/Makefile	Sun May  5 20:39:49 2002
@@ -7,7 +7,7 @@
 
 obj-m   := $(O_TARGET)
 
-EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.5\"
+EXTRA_CFLAGS = -DNTFS_VERSION=\"2.0.6\"
 
 ifeq ($(CONFIG_NTFS_DEBUG),y)
 EXTRA_CFLAGS += -DDEBUG
diff -Nru a/fs/ntfs/compress.c b/fs/ntfs/compress.c
--- a/fs/ntfs/compress.c	Sun May  5 20:39:50 2002
+++ b/fs/ntfs/compress.c	Sun May  5 20:39:50 2002
@@ -763,9 +763,10 @@
 		 * ntfs_decompess.
 		 */
 		if (err) {
-			ntfs_error(vol->sb, "ntfs_decompress() failed with "
-					"error code %i. Skipping this "
-					"compression block.\n", -err);
+			ntfs_error(vol->sb, "ntfs_decompress() failed in inode "
+					"0x%Lx with error code %i. Skipping "
+					"this compression block.\n",
+					(unsigned long long)ni->mft_no, -err);
 			/* Release the unfinished pages. */
 			for (; prev_cur_page < cur_page; prev_cur_page++) {
 				page = pages[prev_cur_page];
diff -Nru a/fs/ntfs/dir.c b/fs/ntfs/dir.c
--- a/fs/ntfs/dir.c	Sun May  5 20:39:49 2002
+++ b/fs/ntfs/dir.c	Sun May  5 20:39:49 2002
@@ -500,7 +500,12 @@
 }
 
 /*
- * VFS calls readdir with BKL held so no possible RACE conditions.
+ * VFS calls readdir without BKL but with i_sem held. This protects the VFS
+ * parts (e.g. ->f_pos and ->i_size, and it also protects against directory
+ * modifications). Together with the rw semaphore taken by the call to
+ * map_mft_record(), the directory is truly locked down so we have a race free
+ * ntfs_readdir() without the BKL. (-:
+ *
  * We use the same basic approach as the old NTFS driver, i.e. we parse the
  * index root entries and then the index allocation entries that are marked
  * as in use in the index bitmap.
@@ -525,7 +530,6 @@
 	u8 *kaddr, *bmp, *index_end;
 	attr_search_context *ctx;
 
-	lock_kernel();
 	ntfs_debug("Entering for inode 0x%Lx, f_pos 0x%Lx.",
 			(unsigned long long)ndir->mft_no, filp->f_pos);
 	rc = err = 0;
@@ -794,7 +798,6 @@
 		ntfs_debug("filldir returned %i, f_pos 0x%Lx, returning 0.",
 				rc, filp->f_pos);
 #endif
-	unlock_kernel();
 	return 0;
 map_page_err_out:
 	ntfs_error(sb, "Reading index allocation data failed.");
@@ -817,7 +820,8 @@
 }
 
 struct file_operations ntfs_dir_ops = {
-	read:			generic_read_dir,	/* Return -EISDIR. */
-	readdir:		ntfs_readdir,		/* Read directory. */
+	llseek:		generic_file_llseek,	/* Seek inside directory. */
+	read:		generic_read_dir,	/* Return -EISDIR. */
+	readdir:	ntfs_readdir,		/* Read directory contents. */
 };
 
diff -Nru a/fs/ntfs/super.c b/fs/ntfs/super.c
--- a/fs/ntfs/super.c	Sun May  5 20:39:49 2002
+++ b/fs/ntfs/super.c	Sun May  5 20:39:49 2002
@@ -1126,8 +1126,7 @@
 	down_write(&vol->mftbmp_lock);
 	/*
 	 * Clean up mft bitmap address space. Ignore the _inode_ bit in the
-	 * name of the function... FIXME: What does this do with dirty pages?
-	 * (ask Al Viro)
+	 * name of the function... FIXME: This destroys dirty pages!!! (AIA)
 	 */
 	truncate_inode_pages(&vol->mftbmp_mapping, 0);
 	vol->mftbmp_mapping.a_ops = NULL;
@@ -1493,22 +1492,6 @@
 	vol->sb = sb;
 	vol->upcase = NULL;
 	vol->mft_ino = NULL;
-	init_rwsem(&vol->mftbmp_lock);
-	INIT_LIST_HEAD(&vol->mftbmp_mapping.clean_pages);
-	INIT_LIST_HEAD(&vol->mftbmp_mapping.dirty_pages);
-	INIT_LIST_HEAD(&vol->mftbmp_mapping.locked_pages);
-	vol->mftbmp_mapping.a_ops = NULL;
-	vol->mftbmp_mapping.host = NULL;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,6)
-	vol->mftbmp_mapping.i_mmap = NULL;
-	vol->mftbmp_mapping.i_mmap_shared = NULL;
-#else
-	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap);
-	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap_shared);
-#endif
-	spin_lock_init(&vol->mftbmp_mapping.i_shared_lock);
-	init_run_list(&vol->mftbmp_rl);
 	vol->mftmirr_ino = NULL;
 	vol->lcnbmp_ino = NULL;
 	init_rwsem(&vol->lcnbmp_lock);
@@ -1519,6 +1502,26 @@
 	vol->on_errors = 0;
 	vol->mft_zone_multiplier = 0;
 	vol->nls_map = NULL;
+	init_rwsem(&vol->mftbmp_lock);
+	init_run_list(&vol->mftbmp_rl);
+
+	/* Initialize the mftbmp address space mapping.  */
+	INIT_RADIX_TREE(&vol->mftbmp_mapping.page_tree, GFP_ATOMIC);
+	rwlock_init(&vol->mftbmp_mapping.page_lock);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.clean_pages);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.dirty_pages);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.locked_pages);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.io_pages);
+	vol->mftbmp_mapping.nrpages = 0;
+	vol->mftbmp_mapping.a_ops = NULL;
+	vol->mftbmp_mapping.host = NULL;
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap);
+	INIT_LIST_HEAD(&vol->mftbmp_mapping.i_mmap_shared);
+	spin_lock_init(&vol->mftbmp_mapping.i_shared_lock);
+	vol->mftbmp_mapping.dirtied_when = 0;
+	vol->mftbmp_mapping.gfp_mask = GFP_HIGHUSER;
+	vol->mftbmp_mapping.ra_pages =
+			sb->s_bdev->bd_inode->i_mapping->ra_pages;
 
 	/*
 	 * Default is group and other don't have any access to files or
diff -Nru a/fs/proc/array.c b/fs/proc/array.c
--- a/fs/proc/array.c	Sun May  5 20:39:49 2002
+++ b/fs/proc/array.c	Sun May  5 20:39:49 2002
@@ -416,6 +416,7 @@
 	do {
 		pte_t page = *pte;
 		struct page *ptpage;
+		unsigned long pfn;
 
 		address += PAGE_SIZE;
 		pte++;
@@ -424,8 +425,11 @@
 		++*total;
 		if (!pte_present(page))
 			continue;
-		ptpage = pte_page(page);
-		if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
+		pfn = pte_pfn(page);
+		if (!pfn_valid(pfn))
+			continue;
+		ptpage = pfn_to_page(pfn);
+		if (PageReserved(ptpage))
 			continue;
 		++*pages;
 		if (pte_dirty(page))
diff -Nru a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
--- a/fs/reiserfs/inode.c	Sun May  5 20:39:49 2002
+++ b/fs/reiserfs/inode.c	Sun May  5 20:39:49 2002
@@ -105,9 +105,9 @@
 }
 
 static void add_to_flushlist(struct inode *inode, struct buffer_head *bh) {
-    struct list_head *list = &(SB_JOURNAL(inode->i_sb)->j_dirty_buffers) ;
+    struct reiserfs_journal *j = SB_JOURNAL(inode->i_sb) ;
 
-    buffer_insert_list(NULL, bh, list) ;
+    buffer_insert_list(&j->j_dirty_buffers_lock, bh, &j->j_dirty_buffers) ;
 }
 
 //
@@ -1916,7 +1916,7 @@
     for(i = 0 ; i < nr ; i++) {
         bh = bhp[i] ;
 	lock_buffer(bh) ;
-	set_buffer_async_io(bh) ;
+	mark_buffer_async_write(bh) ;
 	/* submit_bh doesn't care if the buffer is dirty, but nobody
 	** later on in the call chain will be cleaning it.  So, we
 	** clean the buffer here, it still gets written either way.
diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
--- a/fs/reiserfs/journal.c	Sun May  5 20:39:49 2002
+++ b/fs/reiserfs/journal.c	Sun May  5 20:39:49 2002
@@ -2131,6 +2131,7 @@
 
   INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_bitmap_nodes) ;
   INIT_LIST_HEAD(&SB_JOURNAL(p_s_sb)->j_dirty_buffers) ;
+  spin_lock_init(&SB_JOURNAL(p_s_sb)->j_dirty_buffers_lock) ;
   reiserfs_allocate_list_bitmaps(p_s_sb, SB_JOURNAL(p_s_sb)->j_list_bitmap, 
                                  SB_BMAP_NR(p_s_sb)) ;
   allocate_bitmap_nodes(p_s_sb) ;
@@ -3125,7 +3126,8 @@
   SB_JOURNAL_LIST_INDEX(p_s_sb) = jindex ;
 
   /* write any buffers that must hit disk before this commit is done */
-  fsync_buffers_list(NULL, &(SB_JOURNAL(p_s_sb)->j_dirty_buffers)) ;
+  fsync_buffers_list(&(SB_JOURNAL(p_s_sb)->j_dirty_buffers_lock),
+		     &(SB_JOURNAL(p_s_sb)->j_dirty_buffers)) ;
 
   /* honor the flush and async wishes from the caller */
   if (flush) {
diff -Nru a/fs/ufs/super.c b/fs/ufs/super.c
--- a/fs/ufs/super.c	Sun May  5 20:39:49 2002
+++ b/fs/ufs/super.c	Sun May  5 20:39:49 2002
@@ -657,12 +657,12 @@
 			goto failed;
 	}
 	if (uspi->s_fsize < 512) {
-		printk("ufs_read_super: fragment size %u is too small\n"
+		printk("ufs_read_super: fragment size %u is too small\n",
 			uspi->s_fsize);
 		goto failed;
 	}
 	if (uspi->s_fsize > 4096) {
-		printk("ufs_read_super: fragment size %u is too large\n"
+		printk("ufs_read_super: fragment size %u is too large\n",
 			uspi->s_fsize);
 		goto failed;
 	}
@@ -672,12 +672,12 @@
 		goto failed;
 	}
 	if (uspi->s_bsize < 4096) {
-		printk("ufs_read_super: block size %u is too small\n"
+		printk("ufs_read_super: block size %u is too small\n",
 			uspi->s_fsize);
 		goto failed;
 	}
 	if (uspi->s_bsize / uspi->s_fsize > 8) {
-		printk("ufs_read_super: too many fragments per block (%u)\n"
+		printk("ufs_read_super: too many fragments per block (%u)\n",
 			uspi->s_bsize / uspi->s_fsize);
 		goto failed;
 	}
diff -Nru a/include/asm-cris/processor.h b/include/asm-cris/processor.h
--- a/include/asm-cris/processor.h	Sun May  5 20:39:50 2002
+++ b/include/asm-cris/processor.h	Sun May  5 20:39:50 2002
@@ -102,7 +102,7 @@
         unsigned long eip = 0;   \
         unsigned long regs = (unsigned long)user_regs(tsk); \
         if (regs > PAGE_SIZE && \
-            VALID_PAGE(virt_to_page(regs))) \
+            virt_addr_valid(regs)) \
               eip = ((struct pt_regs *)regs)->irp; \
         eip; })
 
diff -Nru a/include/asm-i386/page.h b/include/asm-i386/page.h
--- a/include/asm-i386/page.h	Sun May  5 20:39:49 2002
+++ b/include/asm-i386/page.h	Sun May  5 20:39:49 2002
@@ -131,8 +131,12 @@
 #define MAXMEM			((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE))
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
-#define virt_to_page(kaddr)	(mem_map + (__pa(kaddr) >> PAGE_SHIFT))
-#define VALID_PAGE(page)	((page - mem_map) < max_mapnr)
+#define pfn_to_page(pfn)	(mem_map + (pfn))
+#define page_to_pfn(page)	((unsigned long)((page) - mem_map))
+#define virt_to_page(kaddr)	pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
+
+#define pfn_valid(pfn)		((pfn) < max_mapnr)
+#define virt_addr_valid(kaddr)	pfn_valid(__pa(kaddr) >> PAGE_SHIFT)
 
 #define VM_DATA_DEFAULT_FLAGS	(VM_READ | VM_WRITE | VM_EXEC | \
 				 VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
diff -Nru a/include/asm-i386/pgtable-2level.h b/include/asm-i386/pgtable-2level.h
--- a/include/asm-i386/pgtable-2level.h	Sun May  5 20:39:49 2002
+++ b/include/asm-i386/pgtable-2level.h	Sun May  5 20:39:49 2002
@@ -56,8 +56,9 @@
 }
 #define ptep_get_and_clear(xp)	__pte(xchg(&(xp)->pte_low, 0))
 #define pte_same(a, b)		((a).pte_low == (b).pte_low)
-#define pte_page(x)		(mem_map+((unsigned long)(((x).pte_low >> PAGE_SHIFT))))
+#define pte_page(x)		pfn_to_page(pte_pfn(x))
 #define pte_none(x)		(!(x).pte_low)
-#define __mk_pte(page_nr,pgprot) __pte(((page_nr) << PAGE_SHIFT) | pgprot_val(pgprot))
+#define pte_pfn(x)		((unsigned long)(((x).pte_low >> PAGE_SHIFT)))
+#define pfn_pte(pfn, prot)	__pte(((pfn) << PAGE_SHIFT) | pgprot_val(prot))
 
 #endif /* _I386_PGTABLE_2LEVEL_H */
diff -Nru a/include/asm-i386/pgtable-3level.h b/include/asm-i386/pgtable-3level.h
--- a/include/asm-i386/pgtable-3level.h	Sun May  5 20:39:50 2002
+++ b/include/asm-i386/pgtable-3level.h	Sun May  5 20:39:50 2002
@@ -86,10 +86,11 @@
 	return a.pte_low == b.pte_low && a.pte_high == b.pte_high;
 }
 
-#define pte_page(x)	(mem_map+(((x).pte_low >> PAGE_SHIFT) | ((x).pte_high << (32 - PAGE_SHIFT))))
+#define pte_page(x)	pfn_to_page(pte_pfn(x))
 #define pte_none(x)	(!(x).pte_low && !(x).pte_high)
+#define pte_pfn(x)	(((x).pte_low >> PAGE_SHIFT) | ((x).pte_high << (32 - PAGE_SHIFT)))
 
-static inline pte_t __mk_pte(unsigned long page_nr, pgprot_t pgprot)
+static inline pte_t pfn_pte(unsigned long page_nr, pgprot_t pgprot)
 {
 	pte_t pte;
 
diff -Nru a/include/asm-i386/pgtable.h b/include/asm-i386/pgtable.h
--- a/include/asm-i386/pgtable.h	Sun May  5 20:39:50 2002
+++ b/include/asm-i386/pgtable.h	Sun May  5 20:39:50 2002
@@ -229,10 +229,7 @@
  * and a page entry and page directory to the page they refer to.
  */
 
-#define mk_pte(page, pgprot)	__mk_pte((page) - mem_map, (pgprot))
-
-/* This takes a physical page address that is used by the remapping functions */
-#define mk_pte_phys(physpage, pgprot)	__mk_pte((physpage) >> PAGE_SHIFT, pgprot)
+#define mk_pte(page, pgprot)	pfn_pte(page_to_pfn(page), (pgprot))
 
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
diff -Nru a/include/asm-m68k/processor.h b/include/asm-m68k/processor.h
--- a/include/asm-m68k/processor.h	Sun May  5 20:39:50 2002
+++ b/include/asm-m68k/processor.h	Sun May  5 20:39:50 2002
@@ -139,7 +139,7 @@
     ({			\
 	unsigned long eip = 0;	 \
 	if ((tsk)->thread.esp0 > PAGE_SIZE && \
-	    (VALID_PAGE(virt_to_page((tsk)->thread.esp0)))) \
+	    (virt_addr_valid((tsk)->thread.esp0))) \
 	      eip = ((struct pt_regs *) (tsk)->thread.esp0)->pc; \
 	eip; })
 #define	KSTK_ESP(tsk)	((tsk) == current ? rdusp() : (tsk)->thread.usp)
diff -Nru a/include/asm-sh/pgalloc.h b/include/asm-sh/pgalloc.h
--- a/include/asm-sh/pgalloc.h	Sun May  5 20:39:50 2002
+++ b/include/asm-sh/pgalloc.h	Sun May  5 20:39:50 2002
@@ -105,10 +105,13 @@
 
 	pte_clear(ptep);
 	if (!pte_not_present(pte)) {
-		struct page *page = pte_page(pte);
-		if (VALID_PAGE(page)&&
-		    (!page->mapping || !(page->mapping->i_mmap_shared)))
-			__clear_bit(PG_mapped, &page->flags);
+		struct page *page;
+		unsigned long pfn = pte_pfn(pte);
+		if (pfn_valid(pfn)) {
+			page = pfn_to_page(page);
+			if (!page->mapping || !page->mapping->i_mmap_shared)
+				__clear_bit(PG_mapped, &page->flags);
+		}
 	}
 	return pte;
 }
diff -Nru a/include/linux/buffer_head.h b/include/linux/buffer_head.h
--- a/include/linux/buffer_head.h	Sun May  5 20:39:49 2002
+++ b/include/linux/buffer_head.h	Sun May  5 20:39:49 2002
@@ -1,24 +1,24 @@
 /*
  * include/linux/buffer_head.h
  *
- * Everything to do with buffer_head.b_state.
+ * Everything to do with buffer_heads.
  */
 
 #ifndef BUFFER_FLAGS_H
 #define BUFFER_FLAGS_H
 
-/* bh state bits */
 enum bh_state_bits {
-	BH_Uptodate,	/* 1 if the buffer contains valid data */
-	BH_Dirty,	/* 1 if the buffer is dirty */
-	BH_Lock,	/* 1 if the buffer is locked */
-	BH_Req,		/* 0 if the buffer has been invalidated */
-
-	BH_Mapped,	/* 1 if the buffer has a disk mapping */
-	BH_New,		/* 1 if the buffer is new and not yet written out */
-	BH_Async,	/* 1 if the buffer is under end_buffer_io_async I/O */
-	BH_JBD,		/* 1 if it has an attached journal_head */
+	BH_Uptodate,	/* Contains valid data */
+	BH_Dirty,	/* Is dirty */
+	BH_Lock,	/* Is locked */
+	BH_Req,		/* Has been submitted for I/O */
+
+	BH_Mapped,	/* Has a disk mapping */
+	BH_New,		/* Disk mapping was newly created by get_block */
+	BH_Async_Read,	/* Is under end_buffer_async_read I/O */
+	BH_Async_Write,	/* Is under end_buffer_async_write I/O */
 
+	BH_JBD,		/* Has an attached ext3 journal_head */
 	BH_PrivateStart,/* not a state bit, but the first bit available
 			 * for private allocation by other entities
 			 */
@@ -32,28 +32,22 @@
 typedef void (bh_end_io_t)(struct buffer_head *bh, int uptodate);
 
 /*
- * Try to keep the most commonly used fields in single cache lines (16
- * bytes) to improve performance.  This ordering should be
- * particularly beneficial on 32-bit processors.
- * 
- * We use the first 16 bytes for the data which is used in searches
- * over the block hash lists (ie. getblk() and friends).
- * 
- * The second 16 bytes we use for lru buffer scans, as used by
- * sync_buffers() and refill_freelist().  -- sct
+ * Keep related fields in common cachelines.  The most commonly accessed
+ * field (b_state) goes at the start so the compiler does not generate
+ * indexed addressing for it.
  */
 struct buffer_head {
 	/* First cache line: */
-	sector_t b_blocknr;		/* block number */
-	unsigned short b_size;		/* block size */
-	struct block_device *b_bdev;
-
-	atomic_t b_count;		/* users using this block */
 	unsigned long b_state;		/* buffer state bitmap (see above) */
+	atomic_t b_count;		/* users using this block */
 	struct buffer_head *b_this_page;/* circular list of page's buffers */
 	struct page *b_page;		/* the page this bh is mapped to */
 
-	char * b_data;			/* pointer to data block */
+	sector_t b_blocknr;		/* block number */
+	unsigned short b_size;		/* block size */
+	char *b_data;			/* pointer to data block */
+
+	struct block_device *b_bdev;
 	bh_end_io_t *b_end_io;		/* I/O completion */
  	void *b_private;		/* reserved for b_end_io */
 	struct list_head     b_inode_buffers; /* list of inode dirty buffers */
@@ -91,6 +85,11 @@
 	return test_and_clear_bit(BH_##bit, &(bh)->b_state);		\
 }									\
 
+/*
+ * Emit the buffer bitops functions.   Note that there are also functions
+ * of the form "mark_buffer_foo()".  These are higher-level functions which
+ * do something in addition to setting a b_state bit.
+ */
 BUFFER_FNS(Uptodate, uptodate)
 BUFFER_FNS(Dirty, dirty)
 TAS_BUFFER_FNS(Dirty, dirty)
@@ -99,15 +98,12 @@
 BUFFER_FNS(Req, req)
 BUFFER_FNS(Mapped, mapped)
 BUFFER_FNS(New, new)
-BUFFER_FNS(Async, async)
-
-/*
- * Utility macros
- */
+BUFFER_FNS(Async_Read, async_read)
+BUFFER_FNS(Async_Write, async_write)
 
 /*
  * FIXME: this is used only by bh_kmap, which is used only by RAID5.
- * Clean this up with blockdev-in-highmem infrastructure.
+ * Move all that stuff into raid5.c
  */
 #define bh_offset(bh)		((unsigned long)(bh)->b_data & ~PAGE_MASK)
 
@@ -152,8 +148,8 @@
 void buffer_insert_list(spinlock_t *lock,
 			struct buffer_head *, struct list_head *);
 
-/* reiserfs_writepage needs this */
-void set_buffer_async_io(struct buffer_head *bh);
+void mark_buffer_async_read(struct buffer_head *bh);
+void mark_buffer_async_write(struct buffer_head *bh);
 void invalidate_inode_buffers(struct inode *);
 void invalidate_bdev(struct block_device *, int);
 void __invalidate_buffers(kdev_t dev, int);
diff -Nru a/include/linux/ide.h b/include/linux/ide.h
--- a/include/linux/ide.h	Sun May  5 20:39:49 2002
+++ b/include/linux/ide.h	Sun May  5 20:39:49 2002
@@ -140,7 +140,6 @@
  */
 #define PRD_BYTES	8
 #define PRD_ENTRIES	(PAGE_SIZE / (2 * PRD_BYTES))
-#define PRD_SEGMENTS	32
 
 /*
  * Some more useful definitions
@@ -298,6 +297,7 @@
 	u8 tune_req;			/* requested drive tuning setting */
 
 	byte     using_dma;		/* disk is using dma for read/write */
+	byte	 using_tcq;		/* disk is using queueing */
 	byte	 retry_pio;		/* retrying dma capable host in pio */
 	byte	 state;			/* retry state */
 	byte     dsc_overlap;		/* flag: DSC overlap */
@@ -360,30 +360,19 @@
 	byte		dn;		/* now wide spread use */
 	byte		wcache;		/* status of write cache */
 	byte		acoustic;	/* acoustic management */
+	byte		queue_depth;	/* max queue depth */
 	unsigned int	failures;	/* current failure count */
 	unsigned int	max_failures;	/* maximum allowed failure count */
 	struct device	device;		/* global device tree handle */
+	/*
+	 * tcq statistics
+	 */
+	unsigned long	immed_rel;
+	unsigned long	immed_comp;
+	int		max_last_depth;
+	int		max_depth;
 } ide_drive_t;
 
-/*
- * This initiates/aborts DMA read/write operations on a drive.
- *
- * The caller is assumed to have selected the drive and programmed the drive's
- * sector address using CHS or LBA.  All that remains is to prepare for DMA
- * and then issue the actual read/write DMA/PIO command to the drive.
- *
- * Returns 0 if all went well.
- * Returns 1 if DMA read/write could not be started, in which case the caller
- * should either try again later, or revert to PIO for the current request.
- */
-typedef enum {	ide_dma_read,	ide_dma_write,		ide_dma_begin,
-		ide_dma_end,	ide_dma_check,		ide_dma_on,
-		ide_dma_off,	ide_dma_off_quietly,	ide_dma_test_irq,
-		ide_dma_bad_drive,			ide_dma_good_drive,
-		ide_dma_verbose,			ide_dma_retune,
-		ide_dma_lostirq,			ide_dma_timeout
-} ide_dma_action_t;
-
 enum {
 	ATA_PRIMARY	= 0,
 	ATA_SECONDARY	= 1
@@ -400,7 +389,7 @@
 #ifdef CONFIG_BLK_DEV_IDEPCI
 	struct pci_dev	*pci_dev;	/* for pci chipsets */
 #endif
-	ide_drive_t	drives[MAX_DRIVES];	/* drive info */
+	struct ata_device drives[MAX_DRIVES];	/* drive info */
 	struct gendisk	*gd;		/* gendisk structure */
 
 	/*
@@ -409,34 +398,43 @@
 	 * A value of 255 indicates that the function should choose the optimal
 	 * mode itself.
 	 */
-	void (*tuneproc) (ide_drive_t *, byte pio);
-	int (*speedproc) (ide_drive_t *, byte pio);
+	void (*tuneproc) (struct ata_device *, byte pio);
+	int (*speedproc) (struct ata_device *, byte pio);
 
 	/* tweaks hardware to select drive */
-	void (*selectproc) (ide_drive_t *);
+	void (*selectproc) (struct ata_device *);
 
 	/* routine to reset controller after a disk reset */
-	void (*resetproc) (ide_drive_t *);
+	void (*resetproc) (struct ata_device *);
 
 	/* special interrupt handling for shared pci interrupts */
-	void (*intrproc) (ide_drive_t *);
+	void (*intrproc) (struct ata_device *);
 
 	/* special host masking for drive selection */
-	void (*maskproc) (ide_drive_t *, int);
-
-	/* adjust timing based upon rq->cmd direction */
-	void (*rwproc) (ide_drive_t *, ide_dma_action_t);
+	void (*maskproc) (struct ata_device *, int);
 
 	/* check host's drive quirk list */
-	int (*quirkproc) (ide_drive_t *);
+	int (*quirkproc) (struct ata_device *);
 
 	/* CPU-polled transfer routines */
-	void (*ata_read)(ide_drive_t *, void *, unsigned int);
-	void (*ata_write)(ide_drive_t *, void *, unsigned int);
-	void (*atapi_read)(ide_drive_t *, void *, unsigned int);
-	void (*atapi_write)(ide_drive_t *, void *, unsigned int);
+	void (*ata_read)(struct ata_device *, void *, unsigned int);
+	void (*ata_write)(struct ata_device *, void *, unsigned int);
+	void (*atapi_read)(struct ata_device *, void *, unsigned int);
+	void (*atapi_write)(struct ata_device *, void *, unsigned int);
+
+	int (*XXX_udma)(struct ata_device *);
+
+	int (*udma_start) (struct ata_device *, struct request *rq);
+	int (*udma_stop) (struct ata_device *);
+
+	int (*udma_read) (struct ata_device *, struct request *rq);
+	int (*udma_write) (struct ata_device *, struct request *rq);
+
+	int (*udma_irq_status) (struct ata_device *);
+
+	void (*udma_timeout) (struct ata_device *);
+	void (*udma_irq_lost) (struct ata_device *);
 
-	int (*udma)(ide_dma_action_t, struct ata_device *, struct request *); /* dma read/write/abort routine */
 	unsigned int	*dmatable_cpu;	/* dma physical region descriptor table (cpu view) */
 	dma_addr_t	dmatable_dma;	/* dma physical region descriptor table (dma view) */
 	struct scatterlist *sg_table;	/* Scatter-gather list used to build the above */
@@ -460,8 +458,10 @@
 	unsigned autodma	: 1;	/* automatically try to enable DMA at boot */
 	unsigned udma_four	: 1;	/* 1=ATA-66 capable, 0=default */
 	unsigned highmem	: 1;	/* can do full 32-bit dma */
+	unsigned straight8	: 1;	/* Alan's straight 8 check */
 	unsigned no_io_32bit	: 1;	/* disallow enabling 32bit I/O */
 	unsigned no_unmask	: 1;	/* disallow setting unmask bit */
+	unsigned auto_poll	: 1;	/* supports nop auto-poll */
 	byte		io_32bit;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
 	byte		unmask;		/* flag: okay to unmask other irqs */
 	byte		slow;		/* flag: slow data port */
@@ -469,8 +469,8 @@
 #if (DISK_RECOVERY_TIME > 0)
 	unsigned long	last_time;	/* time when previous rq was done */
 #endif
-	byte		straight8;	/* Alan's straight 8 check */
-	int (*busproc)(ide_drive_t *, int);	/* driver soft-power interface */
+	/* driver soft-power interface */
+	int (*busproc)(struct ata_device *, int);
 	byte		bus_state;	/* power state of the IDE bus */
 };
 
@@ -501,6 +501,29 @@
 #define IDE_SLEEP	1
 #define IDE_DMA		2	/* DMA in progress */
 
+#define IDE_MAX_TAG	32
+
+#ifdef CONFIG_BLK_DEV_IDE_TCQ
+static inline int ata_pending_commands(struct ata_device *drive)
+{
+	if (drive->using_tcq)
+		return blk_queue_tag_depth(&drive->queue);
+
+	return 0;
+}
+
+static inline int ata_can_queue(struct ata_device *drive)
+{
+	if (drive->using_tcq)
+		return blk_queue_tag_queue(&drive->queue);
+
+	return 1;
+}
+#else
+#define ata_pending_commands(drive)	(0)
+#define ata_can_queue(drive)		(1)
+#endif
+
 typedef struct hwgroup_s {
 	ide_startstop_t (*handler)(struct ata_device *, struct request *);	/* irq handler, if active */
 	unsigned long flags;		/* BUSY, SLEEPING */
@@ -526,7 +549,7 @@
 #define SETTING_WRITE	(1 << 1)
 #define SETTING_RW	(SETTING_READ | SETTING_WRITE)
 
-typedef int (ide_procset_t)(ide_drive_t *, int);
+typedef int (ide_procset_t)(struct ata_device *, int);
 typedef struct ide_settings_s {
 	char			*name;
 	int			rw;
@@ -543,11 +566,11 @@
 	struct ide_settings_s	*next;
 } ide_settings_t;
 
-void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set);
-void ide_remove_setting(ide_drive_t *drive, char *name);
-int ide_read_setting(ide_drive_t *t, ide_settings_t *setting);
-int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val);
-void ide_add_generic_settings(ide_drive_t *drive);
+extern void ide_add_setting(struct ata_device *, const char *, int, int, int, int, int, int, int, int, void *, ide_procset_t *);
+extern void ide_remove_setting(struct ata_device *, char *);
+extern int ide_read_setting(struct ata_device *, ide_settings_t *);
+extern int ide_write_setting(struct ata_device *, ide_settings_t *, int);
+extern void ide_add_generic_settings(struct ata_device *);
 
 /*
  * /proc/ide interface
@@ -660,19 +683,19 @@
 /*
  * Error reporting, in human readable form (luxurious, but a memory hog).
  */
-byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
+extern byte ide_dump_status(struct ata_device *, const char *, byte);
 
 /*
  * ide_error() takes action based on the error returned by the controller.
  * The caller should return immediately after invoking this.
  */
-ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat);
+extern ide_startstop_t ide_error(struct ata_device *, const char *, byte);
 
 /*
  * Issue a simple drive command
  * The drive must be selected beforehand.
  */
-void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ata_handler_t handler);
+void ide_cmd(struct ata_device *, byte, byte, ata_handler_t);
 
 /*
  * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
@@ -690,9 +713,9 @@
  * caller should return the updated value of "startstop" in this case.
  * "startstop" is unchanged when the function returns 0;
  */
-int ide_wait_stat(ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+extern int ide_wait_stat(ide_startstop_t *, struct ata_device *, byte, byte, unsigned long);
 
-int ide_wait_noerr(ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+extern int ide_wait_noerr(struct ata_device *, byte, byte, unsigned long);
 
 /*
  * This routine is called from the partition-table code in genhd.c
@@ -701,15 +724,15 @@
 int ide_xlate_1024(kdev_t, int, int, const char *);
 
 /*
- * Convert kdev_t structure into ide_drive_t * one.
+ * Convert kdev_t structure into struct ata_device * one.
  */
-ide_drive_t *get_info_ptr(kdev_t i_rdev);
+struct ata_device *get_info_ptr(kdev_t i_rdev);
 
 /*
  * Re-Start an operation for an IDE interface.
  * The caller should return immediately after invoking this.
  */
-ide_startstop_t restart_request(ide_drive_t *);
+ide_startstop_t restart_request(struct ata_device *);
 
 /*
  * This function is intended to be used prior to invoking ide_do_drive_cmd().
@@ -731,12 +754,12 @@
  */
 #define ide_rq_offset(rq) (((rq)->hard_cur_sectors - (rq)->current_nr_sectors) << 9)
 
-extern int ide_do_drive_cmd(ide_drive_t *drive, struct request *rq, ide_action_t action);
+extern int ide_do_drive_cmd(struct ata_device *, struct request *, ide_action_t);
 
 /*
  * Clean up after success/failure of an explicit drive cmd.
  */
-void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+extern void ide_end_drive_cmd(struct ata_device *, byte, byte);
 
 struct ata_taskfile {
 	struct hd_drive_task_hdr taskfile;
@@ -746,14 +769,14 @@
 	ide_startstop_t (*handler)(struct ata_device *, struct request *);
 };
 
-extern void ata_read(ide_drive_t *drive, void *buffer, unsigned int wcount);
-extern void ata_write(ide_drive_t *drive, void *buffer, unsigned int wcount);
+extern void ata_read(struct ata_device *, void *, unsigned int);
+extern void ata_write(struct ata_device *, void *, unsigned int);
 
-extern void atapi_read(ide_drive_t *drive, void *buffer, unsigned int bytecount);
-extern void atapi_write(ide_drive_t *drive, void *buffer, unsigned int bytecount);
+extern void atapi_read(struct ata_device *, void *, unsigned int);
+extern void atapi_write(struct ata_device *, void *, unsigned int);
 
-extern ide_startstop_t ata_taskfile(ide_drive_t *drive,
-		struct ata_taskfile *args, struct request *rq);
+extern ide_startstop_t ata_taskfile(struct ata_device *,
+	struct ata_taskfile *, struct request *);
 
 /*
  * Special Flagged Register Validation Caller
@@ -773,31 +796,20 @@
 
 void ide_delay_50ms(void);
 
-byte ide_auto_reduce_xfer (ide_drive_t *drive);
-int ide_driveid_update (ide_drive_t *drive);
-int ide_ata66_check (ide_drive_t *drive, struct ata_taskfile *args);
-int ide_config_drive_speed (ide_drive_t *drive, byte speed);
-byte eighty_ninty_three (ide_drive_t *drive);
-int set_transfer (ide_drive_t *drive, struct ata_taskfile *args);
+extern byte ide_auto_reduce_xfer(struct ata_device *);
+extern int ide_driveid_update(struct ata_device *);
+extern int ide_ata66_check(struct ata_device *, struct ata_taskfile *);
+extern int ide_config_drive_speed(struct ata_device *, byte);
+extern byte eighty_ninty_three(struct ata_device *);
+extern int set_transfer(struct ata_device *, struct ata_taskfile *);
 
 extern int system_bus_speed;
 
 /*
- * idedisk_input_data() is a wrapper around ide_input_data() which copes
- * with byte-swapping the input data if required.
- */
-extern void idedisk_input_data(ide_drive_t *drive, void *buffer, unsigned int wcount);
-
-/*
  * ide_stall_queue() can be used by a drive to give excess bandwidth back
  * to the hwgroup by sleeping for timeout jiffies.
  */
-void ide_stall_queue (ide_drive_t *drive, unsigned long timeout);
-
-/*
- * ide_get_queue() returns the queue which corresponds to a given device.
- */
-request_queue_t *ide_get_queue(kdev_t dev);
+void ide_stall_queue(struct ata_device *, unsigned long);
 
 /*
  * CompactFlash cards and their brethern pretend to be removable hard disks,
@@ -806,9 +818,9 @@
  * config bits.
  */
 
-extern int drive_is_flashcard(ide_drive_t *drive);
+extern int drive_is_flashcard(struct ata_device *);
 
-int ide_spin_wait_hwgroup (ide_drive_t *drive);
+int ide_spin_wait_hwgroup(struct ata_device *);
 void ide_timer_expiry (unsigned long data);
 extern void ata_irq_request(int irq, void *data, struct pt_regs *regs);
 void do_ide_request (request_queue_t * q);
@@ -838,9 +850,9 @@
 extern int idescsi_init (void);
 #endif
 
-ide_drive_t *ide_scan_devices (byte media, const char *name, struct ata_operations *driver, int n);
-extern int ide_register_subdriver(ide_drive_t *drive, struct ata_operations *driver);
-extern int ide_unregister_subdriver(ide_drive_t *drive);
+extern struct ata_device *ide_scan_devices(byte, const char *, struct ata_operations *, int);
+extern int ide_register_subdriver(struct ata_device *, struct ata_operations *);
+extern int ide_unregister_subdriver(struct ata_device *drive);
 
 #ifdef CONFIG_BLK_DEV_IDEPCI
 # define ON_BOARD		1
@@ -854,21 +866,40 @@
 void __init ide_scan_pcibus(int scan_direction);
 #endif
 #ifdef CONFIG_BLK_DEV_IDEDMA
-int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func);
-void ide_destroy_dmatable (ide_drive_t *drive);
+
+extern int udma_new_table(struct ata_channel *, struct request *);
+extern void udma_destroy_table(struct ata_channel *);
+extern void udma_print(struct ata_device *);
+
+extern void udma_enable(struct ata_device *, int, int);
+extern int udma_black_list(struct ata_device *);
+extern int udma_white_list(struct ata_device *);
+extern void udma_timeout(struct ata_device *);
+extern void udma_irq_lost(struct ata_device *);
+extern int udma_start(struct ata_device *, struct request *rq);
+extern int udma_stop(struct ata_device *);
+extern int udma_read(struct ata_device *, struct request *rq);
+extern int udma_write(struct ata_device *, struct request *rq);
+extern int udma_irq_status(struct ata_device *);
+
+extern int ata_do_udma(unsigned int reading, struct ata_device *drive, struct request *rq);
+
+extern ide_startstop_t udma_tcq_taskfile(struct ata_device *, struct request *);
+extern int udma_tcq_enable(struct ata_device *, int);
+
 extern ide_startstop_t ide_dma_intr(struct ata_device *, struct request *);
-int check_drive_lists (ide_drive_t *drive, int good_bad);
-int ide_dmaproc (ide_dma_action_t func, struct ata_device *drive, struct request *);
-extern void ide_release_dma(struct ata_channel *hwif);
-extern void ide_setup_dma(struct ata_channel *hwif,
-		unsigned long dmabase, unsigned int num_ports) __init;
+extern int check_drive_lists(struct ata_device *, int good_bad);
+extern int XXX_ide_dmaproc(struct ata_device *);
+extern void ide_release_dma(struct ata_channel *);
+extern void ide_setup_dma(struct ata_channel *,	unsigned long, unsigned int) __init;
+extern int ata_start_dma(struct ata_device *, struct request *rq);
 #endif
 
 extern spinlock_t ide_lock;
 
 #define DRIVE_LOCK(drive)		((drive)->queue.queue_lock)
 
-extern int drive_is_ready(ide_drive_t *drive);
+extern int drive_is_ready(struct ata_device *drive);
 extern void revalidate_drives(void);
 
 #endif
diff -Nru a/include/linux/kd.h b/include/linux/kd.h
--- a/include/linux/kd.h	Sun May  5 20:39:50 2002
+++ b/include/linux/kd.h	Sun May  5 20:39:50 2002
@@ -26,8 +26,8 @@
 #define KDGETLED	0x4B31	/* return current led state */
 #define KDSETLED	0x4B32	/* set led state [lights, not flags] */
 #define 	LED_SCR		0x01	/* scroll lock led */
-#define 	LED_CAP		0x04	/* caps lock led */
 #define 	LED_NUM		0x02	/* num lock led */
+#define 	LED_CAP		0x04	/* caps lock led */
 
 #define KDGKBTYPE	0x4B33	/* get keyboard type */
 #define 	KB_84		0x01
@@ -89,10 +89,10 @@
 #define KDSKBMETA	0x4B63	/* sets meta key handling mode */
 
 #define		K_SCROLLLOCK	0x01
-#define		K_CAPSLOCK	0x02
-#define		K_NUMLOCK	0x04
+#define		K_NUMLOCK	0x02
+#define		K_CAPSLOCK	0x04
 #define	KDGKBLED	0x4B64	/* get led flags (not lights) */
-#define KDSKBLED	0x4B65	/* set led flags (not lights) */
+#define	KDSKBLED	0x4B65	/* set led flags (not lights) */
 
 struct kbentry {
 	unsigned char kb_table;
diff -Nru a/include/linux/list.h b/include/linux/list.h
--- a/include/linux/list.h	Sun May  5 20:39:49 2002
+++ b/include/linux/list.h	Sun May  5 20:39:49 2002
@@ -153,6 +153,14 @@
 #define list_for_each(pos, head) \
 	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
         	pos = pos->next, prefetch(pos->next))
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+        	pos = pos->prev, prefetch(pos->prev))
         	
 /**
  * list_for_each_safe	-	iterate over a list safe against removal of list entry
diff -Nru a/include/linux/page-flags.h b/include/linux/page-flags.h
--- a/include/linux/page-flags.h	Sun May  5 20:39:49 2002
+++ b/include/linux/page-flags.h	Sun May  5 20:39:49 2002
@@ -174,6 +174,7 @@
 
 #define PageLaunder(page)	test_bit(PG_launder, &(page)->flags)
 #define SetPageLaunder(page)	set_bit(PG_launder, &(page)->flags)
+#define ClearPageLaunder(page)	clear_bit(PG_launder, &(page)->flags)
 
 #define SetPagePrivate(page)	set_bit(PG_private, &(page)->flags)
 #define ClearPagePrivate(page)	clear_bit(PG_private, &(page)->flags)
diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h
--- a/include/linux/reiserfs_fs_sb.h	Sun May  5 20:39:50 2002
+++ b/include/linux/reiserfs_fs_sb.h	Sun May  5 20:39:50 2002
@@ -241,6 +241,7 @@
   int j_used_bitmap_nodes ;
   struct list_head j_bitmap_nodes ;
   struct list_head j_dirty_buffers ;
+  spinlock_t j_dirty_buffers_lock ; /* protects j_dirty_buffers */
   struct reiserfs_list_bitmap j_list_bitmap[JOURNAL_NUM_BITMAPS] ;	/* array of bitmaps to record the deleted blocks */
   struct reiserfs_journal_list j_journal_list[JOURNAL_LIST_COUNT] ;	    /* array of all the journal lists */
   struct reiserfs_journal_cnode *j_hash_table[JOURNAL_HASH_SIZE] ; 	    /* hash table for real buffer heads in current trans */ 
diff -Nru a/include/linux/sched.h b/include/linux/sched.h
--- a/include/linux/sched.h	Sun May  5 20:39:49 2002
+++ b/include/linux/sched.h	Sun May  5 20:39:49 2002
@@ -369,6 +369,7 @@
 #define PF_MEMDIE	0x00001000	/* Killed for out-of-memory */
 #define PF_FREE_PAGES	0x00002000	/* per process page freeing */
 #define PF_FLUSHER	0x00004000	/* responsible for disk writeback */
+#define PF_RADIX_TREE	0x00008000	/* debug: performing radix tree alloc */
 
 /*
  * Ptrace flags
diff -Nru a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h
--- a/include/linux/sunrpc/xprt.h	Sun May  5 20:39:50 2002
+++ b/include/linux/sunrpc/xprt.h	Sun May  5 20:39:50 2002
@@ -120,6 +120,11 @@
 #define rq_rnr			rq_rcv_buf.io_nr
 #define rq_rlen			rq_rcv_buf.io_len
 
+#define XPRT_LAST_FRAG		(1 << 0)
+#define XPRT_COPY_RECM		(1 << 1)
+#define XPRT_COPY_XID		(1 << 2)
+#define XPRT_COPY_DATA		(1 << 3)
+
 struct rpc_xprt {
 	struct socket *		sock;		/* BSD socket layer */
 	struct sock *		inet;		/* INET layer */
@@ -140,18 +145,17 @@
 	unsigned long		sockstate;	/* Socket state */
 	unsigned char		shutdown   : 1,	/* being shut down */
 				nocong	   : 1,	/* no congestion control */
-				stream     : 1,	/* TCP */
-				tcp_more   : 1;	/* more record fragments */
+				stream     : 1;	/* TCP */
 
 	/*
 	 * State of TCP reply receive stuff
 	 */
-	u32			tcp_recm;	/* Fragment header */
-	u32			tcp_xid;	/* Current XID */
-	unsigned int		tcp_reclen,	/* fragment length */
-				tcp_offset,	/* fragment offset */
-				tcp_copied;	/* copied to request */
-	struct list_head	rx_pending;	/* receive pending list */
+	u32			tcp_recm,	/* Fragment header */
+				tcp_xid,	/* Current XID */
+				tcp_reclen,	/* fragment length */
+				tcp_offset;	/* fragment offset */
+	unsigned long		tcp_copied,	/* copied to request */
+				tcp_flags;
 
 	/*
 	 * Send stuff
@@ -185,8 +189,6 @@
 void			xprt_release(struct rpc_task *);
 void			xprt_reconnect(struct rpc_task *);
 int			xprt_clear_backlog(struct rpc_xprt *);
-int			xprt_tcp_pending(void);
-void			__rpciod_tcp_dispatcher(void);
 
 #define XPRT_WSPACE	0
 #define XPRT_CONNECT	1
@@ -199,13 +201,6 @@
 #define xprt_set_connected(xp)		(set_bit(XPRT_CONNECT, &(xp)->sockstate))
 #define xprt_test_and_set_connected(xp)	(test_and_set_bit(XPRT_CONNECT, &(xp)->sockstate))
 #define xprt_clear_connected(xp)	(clear_bit(XPRT_CONNECT, &(xp)->sockstate))
-
-static inline
-void rpciod_tcp_dispatcher(void)
-{
-	if (xprt_tcp_pending())
-		__rpciod_tcp_dispatcher();
-}
 
 #endif /* __KERNEL__*/
 
diff -Nru a/include/linux/swap.h b/include/linux/swap.h
--- a/include/linux/swap.h	Sun May  5 20:39:49 2002
+++ b/include/linux/swap.h	Sun May  5 20:39:49 2002
@@ -96,6 +96,8 @@
 /* Swap 50% full? Release swapcache more aggressively.. */
 #define vm_swap_full() (nr_swap_pages*2 < total_swap_pages)
 
+extern unsigned long totalram_pages;
+extern unsigned long totalhigh_pages;
 extern unsigned int nr_free_pages(void);
 extern unsigned int nr_free_buffer_pages(void);
 extern unsigned int nr_free_pagecache_pages(void);
diff -Nru a/include/linux/watchdog.h b/include/linux/watchdog.h
--- a/include/linux/watchdog.h	Sun May  5 20:39:50 2002
+++ b/include/linux/watchdog.h	Sun May  5 20:39:50 2002
@@ -25,7 +25,8 @@
 #define	WDIOC_GETTEMP		_IOR(WATCHDOG_IOCTL_BASE, 3, int)
 #define	WDIOC_SETOPTIONS	_IOR(WATCHDOG_IOCTL_BASE, 4, int)
 #define	WDIOC_KEEPALIVE		_IOR(WATCHDOG_IOCTL_BASE, 5, int)
-#define	WDIOC_SETTIMEOUT        _IOW(WATCHDOG_IOCTL_BASE, 6, int)
+#define	WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
+#define	WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
 
 #define	WDIOF_UNKNOWN		-1	/* Unknown flag error */
 #define	WDIOS_UNKNOWN		-1	/* Unknown status error */
@@ -37,6 +38,7 @@
 #define	WDIOF_POWERUNDER	0x0010	/* Power bad/power fault */
 #define	WDIOF_CARDRESET		0x0020	/* Card previously reset the CPU */
 #define WDIOF_POWEROVER		0x0040	/* Power over voltage */
+#define WDIOF_SETTIMEOUT	0x0080  /* Set timeout (in seconds) */
 #define	WDIOF_KEEPALIVEPING	0x8000	/* Keep alive ping reply */
 
 #define	WDIOS_DISABLECARD	0x0001	/* Turn off the watchdog timer */
diff -Nru a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
--- a/include/net/bluetooth/bluetooth.h	Sun May  5 20:39:49 2002
+++ b/include/net/bluetooth/bluetooth.h	Sun May  5 20:39:49 2002
@@ -23,7 +23,7 @@
 */
 
 /*
- *  $Id: bluetooth.h,v 1.6 2001/08/03 04:19:49 maxk Exp $
+ *  $Id: bluetooth.h,v 1.8 2002/04/17 17:37:20 maxk Exp $
  */
 
 #ifndef __BLUETOOTH_H
@@ -31,17 +31,61 @@
 
 #include <asm/types.h>
 #include <asm/byteorder.h>
+#include <linux/poll.h>
+#include <net/sock.h>
 
 #ifndef AF_BLUETOOTH
 #define AF_BLUETOOTH	31
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #endif
 
+/* Reserv for core and drivers use */
+#define BLUEZ_SKB_RESERVE       8
+
+#ifndef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
 #define BTPROTO_L2CAP   0
 #define BTPROTO_HCI     1
+#define BTPROTO_SCO   	2
+#define BTPROTO_RFCOMM	3
 
 #define SOL_HCI     0
 #define SOL_L2CAP   6
+#define SOL_SCO     17
+#define SOL_RFCOMM  18
+
+/* Debugging */
+#ifdef CONFIG_BLUEZ_DEBUG
+
+#define HCI_CORE_DEBUG		1
+#define HCI_SOCK_DEBUG		1
+#define HCI_UART_DEBUG		1
+#define HCI_USB_DEBUG		1
+//#define HCI_DATA_DUMP		1
+
+#define L2CAP_DEBUG		1
+#define SCO_DEBUG		1
+#define AF_BLUETOOTH_DEBUG	1
+
+#endif /* CONFIG_BLUEZ_DEBUG */
+
+extern void bluez_dump(char *pref, __u8 *buf, int count);
+
+#if __GNUC__ <= 2 && __GNUC_MINOR__ < 95
+#define __func__ __FUNCTION__
+#endif
+
+#define BT_INFO(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
+#define BT_DBG(fmt, arg...)  printk(KERN_INFO "%s: " fmt "\n" , __func__ , ## arg)
+#define BT_ERR(fmt, arg...)  printk(KERN_ERR  "%s: " fmt "\n" , __func__ , ## arg)
+
+#ifdef HCI_DATA_DUMP
+#define BT_DMP(buf, len)    bluez_dump(__func__, buf, len)
+#else
+#define BT_DMP(D...)
+#endif
 
 /* Connection and socket states */
 enum {
@@ -50,6 +94,7 @@
 	BT_BOUND,
 	BT_LISTEN,
 	BT_CONNECT,
+	BT_CONNECT2,
 	BT_CONFIG,
 	BT_DISCONN,
 	BT_CLOSED
@@ -66,7 +111,8 @@
 	__u8 b[6];
 } __attribute__((packed)) bdaddr_t;
 
-#define BDADDR_ANY ((bdaddr_t *)"\000\000\000\000\000")
+#define BDADDR_ANY   (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}})
+#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff}})
 
 /* Copy, swap, convert BD Address */
 static inline int bacmp(bdaddr_t *ba1, bdaddr_t *ba2)
@@ -82,6 +128,89 @@
 char *batostr(bdaddr_t *ba);
 bdaddr_t *strtoba(char *str);
 
+/* Common socket structures and functions */
+
+#define bluez_sk(__sk) ((struct bluez_sock *) __sk)
+
+struct bluez_sock {
+	struct sock sk;
+	bdaddr_t    src;
+	bdaddr_t    dst;
+	struct list_head accept_q;
+	struct sock *parent;
+};
+
+struct bluez_sock_list {
+	struct sock *head;
+	rwlock_t     lock;
+};
+
+int  bluez_sock_register(int proto, struct net_proto_family *ops);
+int  bluez_sock_unregister(int proto);
+struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int prio);
+void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
+void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
+int  bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm);
+uint bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait);
+int  bluez_sock_w4_connect(struct sock *sk, int flags);
+
+void bluez_accept_enqueue(struct sock *parent, struct sock *sk);
+struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock);
+
+/* Skb helpers */
+struct bluez_skb_cb {
+	int    incomming;
+};
+#define bluez_cb(skb)	((struct bluez_skb_cb *)(skb->cb)) 
+
+static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
+{
+	struct sk_buff *skb;
+
+	if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
+		skb_reserve(skb, BLUEZ_SKB_RESERVE);
+		bluez_cb(skb)->incomming  = 0;
+	}
+	return skb;
+}
+
+static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, 
+						       int nb, int *err)
+{
+	struct sk_buff *skb;
+
+	if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
+		skb_reserve(skb, BLUEZ_SKB_RESERVE);
+		bluez_cb(skb)->incomming  = 0;
+	}
+
+	return skb;
+}
+
+static inline int skb_frags_no(struct sk_buff *skb)
+{
+	register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
+	register int n = 1;
+
+	for (; frag; frag=frag->next, n++);
+	return n;
+}
+
+int hci_core_init(void);
+int hci_core_cleanup(void);
+int hci_sock_init(void);
+int hci_sock_cleanup(void);
+
 int bterr(__u16 code);
+
+#ifndef MODULE_LICENSE
+#define MODULE_LICENSE(x)
+#endif
+
+#ifndef list_for_each_safe
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+#endif
 
 #endif /* __BLUETOOTH_H */
diff -Nru a/include/net/bluetooth/bluez.h b/include/net/bluetooth/bluez.h
--- a/include/net/bluetooth/bluez.h	Sun May  5 20:39:49 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,124 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- *  $Id: bluez.h,v 1.4 2001/08/03 04:19:49 maxk Exp $
- */
-
-#ifndef __IF_BLUEZ_H
-#define __IF_BLUEZ_H
-
-#include <net/sock.h>
-
-#define BLUEZ_MAX_PROTO 	2
-
-/* Reserv for core and drivers use */
-#define BLUEZ_SKB_RESERVE	8
-
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-/* Debugging */
-#ifdef BLUEZ_DEBUG
-
-#define HCI_CORE_DEBUG		1
-#define HCI_SOCK_DEBUG		1
-#define HCI_UART_DEBUG		1
-#define HCI_USB_DEBUG		1
-//#define HCI_DATA_DUMP		1
-
-#define L2CAP_DEBUG			1
-
-#endif /* BLUEZ_DEBUG */
-
-extern void bluez_dump(char *pref, __u8 *buf, int count);
-
-#define INF(fmt, arg...) printk(KERN_INFO fmt "\n" , ## arg)
-#define DBG(fmt, arg...) printk(KERN_INFO __FUNCTION__ ": " fmt "\n" , ## arg)
-#define ERR(fmt, arg...) printk(KERN_ERR  __FUNCTION__ ": " fmt "\n" , ## arg)
-
-#ifdef HCI_DATA_DUMP
-#define DMP(buf, len)    bluez_dump(__FUNCTION__, buf, len)
-#else
-#define DMP(D...)
-#endif
-
-/* ----- Sockets ------ */
-struct bluez_sock_list {
-	struct sock *head;
-	rwlock_t     lock;
-};
-
-extern int  bluez_sock_register(int proto, struct net_proto_family *ops);
-extern int  bluez_sock_unregister(int proto);
-
-extern void bluez_sock_link(struct bluez_sock_list *l, struct sock *s);
-extern void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *s);
-
-/* ----- SKB helpers ----- */
-struct bluez_skb_cb {
-	int    incomming;
-};
-#define bluez_cb(skb)	((struct bluez_skb_cb *)(skb->cb)) 
-
-static inline struct sk_buff *bluez_skb_alloc(unsigned int len, int how)
-{
-	struct sk_buff *skb;
-
-	if ((skb = alloc_skb(len + BLUEZ_SKB_RESERVE, how))) {
-		skb_reserve(skb, BLUEZ_SKB_RESERVE);
-		bluez_cb(skb)->incomming  = 0;
-	}
-	return skb;
-}
-
-static inline struct sk_buff *bluez_skb_send_alloc(struct sock *sk, unsigned long len, 
-						       int nb, int *err)
-{
-	struct sk_buff *skb;
-
-	if ((skb = sock_alloc_send_skb(sk, len + BLUEZ_SKB_RESERVE, nb, err))) {
-		skb_reserve(skb, BLUEZ_SKB_RESERVE);
-		bluez_cb(skb)->incomming  = 0;
-	}
-
-	return skb;
-}
-
-static inline int skb_frags_no(struct sk_buff *skb)
-{
-	register struct sk_buff *frag = skb_shinfo(skb)->frag_list;
-	register int n = 1;
-
-	for (; frag; frag=frag->next, n++);
-	return n;
-}
-
-extern int hci_core_init(void);
-extern int hci_core_cleanup(void);
-extern int hci_sock_init(void);
-extern int hci_sock_cleanup(void);
-
-#endif /* __IF_BLUEZ_H */
diff -Nru a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
--- a/include/net/bluetooth/hci.h	Sun May  5 20:39:49 2002
+++ b/include/net/bluetooth/hci.h	Sun May  5 20:39:49 2002
@@ -23,16 +23,16 @@
 */
 
 /*
- *  $Id: hci.h,v 1.15 2001/08/05 06:02:15 maxk Exp $
+ *  $Id: hci.h,v 1.4 2002/04/18 22:26:15 maxk Exp $
  */
 
 #ifndef __HCI_H
 #define __HCI_H
 
-#include <asm/byteorder.h>
-
-#define HCI_MAX_DEV 	8
-#define HCI_MAX_FRAME_SIZE	2048
+#define HCI_MAX_ACL_SIZE	1024
+#define HCI_MAX_SCO_SIZE	255
+#define HCI_MAX_EVENT_SIZE	260
+#define HCI_MAX_FRAME_SIZE	(HCI_MAX_ACL_SIZE + 4)
 
 /* HCI dev events */
 #define HCI_DEV_REG	1
@@ -41,41 +41,54 @@
 #define HCI_DEV_DOWN	4
 
 /* HCI device types */
-#define HCI_UART 	0
+#define HCI_VHCI	0
 #define HCI_USB		1
-#define HCI_VHCI	2
-
-/* HCI device modes */
-#define HCI_NORMAL 	0x0001
-#define HCI_RAW		0x0002
-#define HCI_MODE_MASK   (HCI_NORMAL | HCI_RAW)
-#define HCI_SOCK	0x1000
-
-/* HCI device states */
-#define HCI_INIT	0x0010
-#define HCI_UP 		0x0020
-#define HCI_RUNNING	0x0040
+#define HCI_PCCARD	2
+#define HCI_UART 	3
+#define HCI_RS232 	4
 
 /* HCI device flags */
-#define HCI_PSCAN	0x0100
-#define HCI_ISCAN	0x0200
-#define HCI_AUTH	0x0400
+enum {
+	HCI_UP,
+	HCI_INIT,
+	HCI_RUNNING,
+
+	HCI_PSCAN,
+	HCI_ISCAN,
+	HCI_AUTH,
+	HCI_ENCRYPT,
+	HCI_INQUIRY,
 
-/* HCI Ioctl defines */
+	HCI_RAW
+};
+
+/* HCI ioctl defines */
 #define HCIDEVUP        _IOW('H', 201, int)
 #define HCIDEVDOWN      _IOW('H', 202, int)
 #define HCIDEVRESET     _IOW('H', 203, int)
-#define HCIRESETSTAT    _IOW('H', 204, int)
-#define HCIGETINFO      _IOR('H', 205, int)
-#define HCIGETDEVLIST   _IOR('H', 206, int)
-#define HCISETRAW       _IOW('H', 207, int)
-#define HCISETSCAN      _IOW('H', 208, int)
-#define HCISETAUTH      _IOW('H', 209, int)
-#define HCIINQUIRY      _IOR('H', 210, int)
-#define HCISETPTYPE     _IOW('H', 211, int)
+#define HCIDEVRESTAT    _IOW('H', 204, int)
+
+#define HCIGETDEVLIST   _IOR('H', 210, int)
+#define HCIGETDEVINFO   _IOR('H', 211, int)
 #define HCIGETCONNLIST  _IOR('H', 212, int)
+#define HCIGETCONNINFO  _IOR('H', 213, int)
 
-#ifndef __NO_HCI_DEFS
+#define HCISETRAW       _IOW('H', 220, int)
+#define HCISETSCAN      _IOW('H', 221, int)
+#define HCISETAUTH      _IOW('H', 222, int)
+#define HCISETENCRYPT   _IOW('H', 223, int)
+#define HCISETPTYPE     _IOW('H', 224, int)
+#define HCISETLINKPOL   _IOW('H', 225, int)
+#define HCISETLINKMODE  _IOW('H', 226, int)
+#define HCISETACLMTU    _IOW('H', 227, int)
+#define HCISETSCOMTU    _IOW('H', 228, int)
+
+#define HCIINQUIRY      _IOR('H', 240, int)
+
+/* HCI timeouts */
+#define HCI_CONN_TIMEOUT 	(HZ * 40)
+#define HCI_DISCONN_TIMEOUT 	(HZ * 2)
+#define HCI_CONN_IDLE_TIMEOUT	(HZ * 60)
 
 /* HCI Packet types */
 #define HCI_COMMAND_PKT		0x01
@@ -92,6 +105,13 @@
 #define HCI_DH3 	0x0800
 #define HCI_DH5 	0x8000
 
+#define HCI_HV1		0x0020
+#define HCI_HV2		0x0040
+#define HCI_HV3		0x0080
+
+#define SCO_PTYPE_MASK	(HCI_HV1 | HCI_HV2 | HCI_HV3)
+#define ACL_PTYPE_MASK	(~SCO_PTYPE_MASK)
+
 /* ACL flags */
 #define ACL_CONT		0x0001
 #define ACL_START		0x0002
@@ -125,6 +145,19 @@
 #define LMP_PSCHEME	0x02
 #define LMP_PCONTROL	0x04
 
+/* Link policies */
+#define HCI_LP_RSWITCH	0x0001
+#define HCI_LP_HOLD	0x0002
+#define HCI_LP_SNIFF	0x0004
+#define HCI_LP_PARK	0x0008
+
+/* Link mode */
+#define HCI_LM_ACCEPT	0x8000
+#define HCI_LM_MASTER	0x0001
+#define HCI_LM_AUTH	0x0002
+#define HCI_LM_ENCRYPT	0x0004
+#define HCI_LM_TRUSTED	0x0008
+
 /* -----  HCI Commands ----- */
 /* OGF & OCF values */
 
@@ -137,9 +170,10 @@
 	__u8  hci_ver;
 	__u16 hci_rev;
 	__u8  lmp_ver;
-	__u16 man_name;
-	__u16 lmp_sub;
+	__u16 manufacturer;
+	__u16 lmp_subver;
 } __attribute__ ((packed)) read_local_version_rp;
+#define READ_LOCAL_VERSION_RP_SIZE 9
 
 #define OCF_READ_LOCAL_FEATURES	0x0003
 typedef struct {
@@ -165,18 +199,24 @@
 /* Host Controller and Baseband */
 #define OGF_HOST_CTL	0x03
 #define OCF_RESET		0x0003
+#define OCF_READ_AUTH_ENABLE	0x001F
 #define OCF_WRITE_AUTH_ENABLE	0x0020
-	#define AUTH_DISABLED			0x00
-	#define AUTH_ENABLED			0x01
+	#define AUTH_DISABLED		0x00
+	#define AUTH_ENABLED		0x01
+
+#define OCF_READ_ENCRYPT_MODE	0x0021
+#define OCF_WRITE_ENCRYPT_MODE	0x0022
+	#define ENCRYPT_DISABLED	0x00
+	#define ENCRYPT_P2P		0x01
+	#define ENCRYPT_BOTH		0x02
 
 #define OCF_WRITE_CA_TIMEOUT  	0x0016	
 #define OCF_WRITE_PG_TIMEOUT  	0x0018
 
 #define OCF_WRITE_SCAN_ENABLE 	0x001A
-	#define SCANS_DISABLED		0x00
-	#define IS_ENA_PS_DIS		0x01
-	#define IS_DIS_PS_ENA		0x02
-	#define IS_ENA_PS_ENA		0x03
+	#define SCAN_DISABLED		0x00
+	#define SCAN_INQUIRY		0x01
+	#define SCAN_PAGE		0x02
 
 #define OCF_SET_EVENT_FLT	0x0005
 typedef struct {
@@ -226,9 +266,18 @@
 } __attribute__ ((packed)) write_class_of_dev_cp;
 #define WRITE_CLASS_OF_DEV_CP_SIZE 3
 
+#define OCF_HOST_BUFFER_SIZE	0x0033
+typedef struct {
+	__u16 	acl_mtu;
+	__u8 	sco_mtu;
+	__u16 	acl_max_pkt;
+	__u16	sco_max_pkt;
+} __attribute__ ((packed)) host_buffer_size_cp;
+#define HOST_BUFFER_SIZE_CP_SIZE 7
+
 /* Link Control */
 #define OGF_LINK_CTL	0x01 
-#define OCF_CREATE_CONN	0x0005
+#define OCF_CREATE_CONN		0x0005
 typedef struct {
 	bdaddr_t bdaddr;
 	__u16 	pkt_type;
@@ -246,6 +295,13 @@
 } __attribute__ ((packed)) accept_conn_req_cp;
 #define ACCEPT_CONN_REQ_CP_SIZE	7
 
+#define OCF_REJECT_CONN_REQ	0x000a
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8 	reason;
+} __attribute__ ((packed)) reject_conn_req_cp;
+#define REJECT_CONN_REQ_CP_SIZE	7
+
 #define OCF_DISCONNECT	0x0006
 typedef struct {
 	__u16 	handle;
@@ -253,17 +309,134 @@
 } __attribute__ ((packed)) disconnect_cp;
 #define DISCONNECT_CP_SIZE 3
 
+#define OCF_ADD_SCO	0x0007
+typedef struct {
+	__u16 	handle;
+	__u16 	pkt_type;
+} __attribute__ ((packed)) add_sco_cp;
+#define ADD_SCO_CP_SIZE 4
+
 #define OCF_INQUIRY		0x0001
 typedef struct {
 	__u8 	lap[3];
-	__u8 	lenght;
+	__u8 	length;
 	__u8	num_rsp;
 } __attribute__ ((packed)) inquiry_cp;
 #define INQUIRY_CP_SIZE 5
 
-#define OGF_LINK_POLICY	 	0x02   /* Link Policy */
+typedef struct {
+	__u8     status;
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) status_bdaddr_rp;
+#define STATUS_BDADDR_RP_SIZE 7
+
+#define OCF_LINK_KEY_REPLY	0x000B
+#define OCF_LINK_KEY_NEG_REPLY	0x000C
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     link_key[16];
+} __attribute__ ((packed)) link_key_reply_cp;
+#define LINK_KEY_REPLY_CP_SIZE 22
+
+#define OCF_PIN_CODE_REPLY	0x000D
+#define OCF_PIN_CODE_NEG_REPLY	0x000E
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8	 pin_len;
+	__u8	 pin_code[16];
+} __attribute__ ((packed)) pin_code_reply_cp;
+#define PIN_CODE_REPLY_CP_SIZE 23
+
+#define OCF_CHANGE_CONN_PTYPE	0x000F
+typedef struct {
+	__u16	 handle;
+	__u16	 pkt_type;
+} __attribute__ ((packed)) change_conn_ptype_cp;
+#define CHANGE_CONN_PTYPE_CP_SIZE 4
 
-/* --------- HCI Events --------- */
+#define OCF_AUTH_REQUESTED	0x0011
+typedef struct {
+	__u16	 handle;
+} __attribute__ ((packed)) auth_requested_cp;
+#define AUTH_REQUESTED_CP_SIZE 2
+
+#define OCF_SET_CONN_ENCRYPT	0x0013
+typedef struct {
+	__u16	 handle;
+	__u8	 encrypt;
+} __attribute__ ((packed)) set_conn_encrypt_cp;
+#define SET_CONN_ENCRYPT_CP_SIZE 3
+
+#define OCF_REMOTE_NAME_REQ	0x0019
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     pscan_rep_mode;
+	__u8     pscan_mode;
+	__u16    clock_offset;
+} __attribute__ ((packed)) remote_name_req_cp;
+#define REMOTE_NAME_REQ_CP_SIZE 10
+
+#define OCF_READ_REMOTE_FEATURES 0x001B
+typedef struct {
+	__u16   handle;
+} __attribute__ ((packed)) read_remote_features_cp;
+#define READ_REMOTE_FEATURES_CP_SIZE 2
+
+#define OCF_READ_REMOTE_VERSION 0x001D
+typedef struct {
+	__u16   handle;
+} __attribute__ ((packed)) read_remote_version_cp;
+#define READ_REMOTE_VERSION_CP_SIZE 2
+
+/* Link Policy */
+#define OGF_LINK_POLICY	 0x02   
+#define OCF_ROLE_DISCOVERY	0x0009
+typedef struct {
+	__u16	handle;
+} __attribute__ ((packed)) role_discovery_cp;
+#define ROLE_DISCOVERY_CP_SIZE 2
+typedef struct {
+	__u8    status;
+	__u16	handle;
+	__u8    role;
+} __attribute__ ((packed)) role_discovery_rp;
+#define ROLE_DISCOVERY_RP_SIZE 4
+
+#define OCF_READ_LINK_POLICY	0x000C
+typedef struct {
+	__u16	handle;
+} __attribute__ ((packed)) read_link_policy_cp;
+#define READ_LINK_POLICY_CP_SIZE 2
+typedef struct {
+	__u8    status;
+	__u16	handle;
+	__u16   policy;
+} __attribute__ ((packed)) read_link_policy_rp;
+#define READ_LINK_POLICY_RP_SIZE 5
+
+#define OCF_SWITCH_ROLE	0x000B
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8     role;
+} __attribute__ ((packed)) switch_role_cp;
+#define SWITCH_ROLE_CP_SIZE 7
+
+#define OCF_WRITE_LINK_POLICY	0x000D
+typedef struct {
+	__u16	handle;
+	__u16   policy;
+} __attribute__ ((packed)) write_link_policy_cp;
+#define WRITE_LINK_POLICY_CP_SIZE 4
+typedef struct {
+	__u8    status;
+	__u16	handle;
+} __attribute__ ((packed)) write_link_policy_rp;
+#define WRITE_LINK_POLICY_RP_SIZE 3
+
+/* Status params */
+#define OGF_STATUS_PARAM 	0x05
+
+/* ---- HCI Events ---- */
 #define EVT_INQUIRY_COMPLETE 	0x01
 
 #define EVT_INQUIRY_RESULT 	0x02
@@ -272,7 +445,7 @@
 	__u8	pscan_rep_mode;
 	__u8	pscan_period_mode;
 	__u8	pscan_mode;
-	__u8	class[3];
+	__u8	dev_class[3];
 	__u16	clock_offset;
 } __attribute__ ((packed)) inquiry_info;
 #define INQUIRY_INFO_SIZE 14
@@ -303,6 +476,44 @@
 } __attribute__ ((packed)) evt_disconn_complete;
 #define EVT_DISCONN_COMPLETE_SIZE 4
 
+#define EVT_AUTH_COMPLETE	0x06
+typedef struct {
+	__u8 	status;
+	__u16 	handle;
+} __attribute__ ((packed)) evt_auth_complete;
+#define EVT_AUTH_COMPLETE_SIZE 3
+
+#define EVT_REMOTE_NAME_REQ_COMPLETE	0x07
+typedef struct {
+	__u8 	 status;
+	bdaddr_t bdaddr;
+	__u8 	 name[248];
+} __attribute__ ((packed)) evt_remote_name_req_complete;
+#define EVT_REMOTE_NAME_REQ_COMPLETE_SIZE 255
+
+#define EVT_ENCRYPT_CHANGE	0x08
+typedef struct {
+	__u8 	status;
+	__u16 	handle;
+	__u8	encrypt;
+} __attribute__ ((packed)) evt_encrypt_change;
+#define EVT_ENCRYPT_CHANGE_SIZE 5
+
+#define EVT_QOS_SETUP_COMPLETE 0x0D
+typedef struct {
+	__u8	service_type;
+	__u32	token_rate;
+	__u32	peak_bandwidth;
+	__u32	latency;
+	__u32	delay_variation;
+} __attribute__ ((packed)) hci_qos;
+typedef struct {
+	__u8	status;
+	__u16	handle;
+	hci_qos qos;
+} __attribute__ ((packed)) evt_qos_setup_complete;
+#define EVT_QOS_SETUP_COMPLETE_SIZE 20
+
 #define EVT_CMD_COMPLETE 	0x0e
 typedef struct {
 	__u8 	ncmd;
@@ -321,16 +532,78 @@
 #define EVT_NUM_COMP_PKTS	0x13
 typedef struct {
 	__u8 	num_hndl;
-	/* variable lenght part */
+	/* variable length part */
 } __attribute__ ((packed)) evt_num_comp_pkts;
 #define EVT_NUM_COMP_PKTS_SIZE 1
 
-#define EVT_HCI_DEV_EVENT	0xfd
+#define EVT_ROLE_CHANGE		0x12
+typedef struct {
+	__u8 	 status;
+	bdaddr_t bdaddr;
+	__u8     role;
+} __attribute__ ((packed)) evt_role_change;
+#define EVT_ROLE_CHANGE_SIZE 1
+
+#define EVT_PIN_CODE_REQ        0x16
+typedef struct {
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_pin_code_req;
+#define EVT_PIN_CODE_REQ_SIZE 6
+
+#define EVT_LINK_KEY_REQ        0x17
+typedef struct {
+	bdaddr_t bdaddr;
+} __attribute__ ((packed)) evt_link_key_req;
+#define EVT_LINK_KEY_REQ_SIZE 6
+
+#define EVT_LINK_KEY_NOTIFY	0x18
+typedef struct {
+	bdaddr_t bdaddr;
+	__u8	 link_key[16];
+	__u8	 key_type;
+} __attribute__ ((packed)) evt_link_key_notify;
+#define EVT_LINK_KEY_NOTIFY_SIZE 23
+
+#define EVT_READ_REMOTE_FEATURES_COMPLETE 0x0B
+typedef struct {
+	__u8    status;
+	__u16   handle;
+	__u8    features[8];
+} __attribute__ ((packed)) evt_read_remote_features_complete;
+#define EVT_READ_REMOTE_FEATURES_COMPLETE_SIZE 11
+
+#define EVT_READ_REMOTE_VERSION_COMPLETE 0x0C
+typedef struct {
+	__u8    status;
+	__u16   handle;
+	__u8    lmp_ver;
+	__u16   manufacturer;
+	__u16   lmp_subver;
+} __attribute__ ((packed)) evt_read_remote_version_complete;
+#define EVT_READ_REMOTE_VERSION_COMPLETE_SIZE 8
+
+/* Internal events generated by BlueZ stack */
+#define EVT_STACK_INTERNAL	0xfd
+typedef struct {
+	__u16   type;
+	__u8 	data[0];
+} __attribute__ ((packed)) evt_stack_internal;
+#define EVT_STACK_INTERNAL_SIZE 2
+
+#define EVT_SI_DEVICE  	0x01
+typedef struct {
+	__u16   event;
+	__u16 	dev_id;
+} __attribute__ ((packed)) evt_si_device;
+#define EVT_SI_DEVICE_SIZE 4
+
+#define EVT_SI_SECURITY	0x02
 typedef struct {
 	__u16 	event;
-	__u16 	param;
-} __attribute__ ((packed)) evt_hci_dev_event;
-#define EVT_HCI_DEV_EVENT_SIZE 4
+	__u16   proto;
+	__u16   subproto;
+	__u8    incomming;
+} __attribute__ ((packed)) evt_si_security;
 
 /* --------  HCI Packet structures  -------- */
 #define HCI_TYPE_LEN	1
@@ -369,14 +642,14 @@
 #define acl_handle(h)		(h & 0x0fff)
 #define acl_flags(h)		(h >> 12)
 
-#endif /* _NO_HCI_DEFS */
-
 /* HCI Socket options */
-#define HCI_DATA_DIR	0x0001
-#define HCI_FILTER	0x0002
+#define HCI_DATA_DIR	1
+#define HCI_FILTER	2
+#define HCI_TIME_STAMP	3
 
 /* HCI CMSG flags */
 #define HCI_CMSG_DIR	0x0001
+#define HCI_CMSG_TSTAMP	0x0002
 
 struct sockaddr_hci {
 	sa_family_t    hci_family;
@@ -387,27 +660,29 @@
 struct hci_filter {
 	__u32 type_mask;
 	__u32 event_mask[2];
+	__u16 opcode;
 };
 
-struct hci_dev_req {
-	__u16 dev_id;
-	__u32 dev_opt;
-};
-
-struct hci_dev_list_req {
-	__u16  dev_num;
-	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
-};
-
-struct hci_inquiry_req {
-	__u16 dev_id;
-	__u16 flags;
-	__u8  lap[3];
-	__u8  length;
-	__u8  num_rsp;
-};
-#define IREQ_CACHE_FLUSH 0x0001
+#define HCI_FLT_TYPE_BITS	31
+#define HCI_FLT_EVENT_BITS	63
+#define HCI_FLT_OGF_BITS	63
+#define HCI_FLT_OCF_BITS	127
+
+#if BITS_PER_LONG == 64
+static inline void hci_set_bit(int nr, void *addr)
+{
+	*((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
+}
+static inline int hci_test_bit(int nr, void *addr)
+{
+	return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
+}
+#else
+#define hci_set_bit	set_bit
+#define hci_test_bit	test_bit
+#endif
 
+/* Ioctl requests structures */
 struct hci_dev_stats {
 	__u32 err_rx;
 	__u32 err_tx;
@@ -433,11 +708,13 @@
 	__u8  features[8];
 
 	__u32 pkt_type;
+	__u32 link_policy;
+	__u32 link_mode;
 
 	__u16 acl_mtu;
-	__u16 acl_max;
+	__u16 acl_pkts;
 	__u16 sco_mtu;
-	__u16 sco_max;
+	__u16 sco_pkts;
 
 	struct hci_dev_stats stat;
 };
@@ -445,12 +722,48 @@
 struct hci_conn_info {
 	__u16    handle;
 	bdaddr_t bdaddr;
+	__u8	 type;
+	__u8	 out;
+	__u16	 state;
+	__u32	 link_mode;
+};
+
+struct hci_dev_req {
+	__u16 dev_id;
+	__u32 dev_opt;
+};
+
+struct hci_dev_list_req {
+	__u16  dev_num;
+	struct hci_dev_req dev_req[0];	/* hci_dev_req structures */
 };
 
 struct hci_conn_list_req {
 	__u16  dev_id;
 	__u16  conn_num;
 	struct hci_conn_info conn_info[0];
+};
+
+struct hci_conn_info_req {
+	bdaddr_t bdaddr;
+	__u8     type;
+	struct   hci_conn_info conn_info[0];
+};
+
+struct hci_inquiry_req {
+	__u16 dev_id;
+	__u16 flags;
+	__u8  lap[3];
+	__u8  length;
+	__u8  num_rsp;
+};
+#define IREQ_CACHE_FLUSH 0x0001
+
+struct hci_remotename_req {
+	__u16 dev_id;
+	__u16 flags;
+	bdaddr_t bdaddr;
+	__u8  name[248];
 };
 
 #endif /* __HCI_H */
diff -Nru a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
--- a/include/net/bluetooth/hci_core.h	Sun May  5 20:39:50 2002
+++ b/include/net/bluetooth/hci_core.h	Sun May  5 20:39:50 2002
@@ -23,7 +23,7 @@
 */
 
 /* 
- * $Id: hci_core.h,v 1.11 2001/08/05 06:02:15 maxk Exp $ 
+ * $Id: hci_core.h,v 1.3 2002/04/17 18:55:21 maxk Exp $ 
  */
 
 #ifndef __HCI_CORE_H
@@ -32,14 +32,12 @@
 #include <net/bluetooth/hci.h>
 
 /* HCI upper protocols */
-#define HCI_MAX_PROTO 	1
 #define HCI_PROTO_L2CAP	0
+#define HCI_PROTO_SCO	1
 
 #define HCI_INIT_TIMEOUT (HZ * 10)
 
-/* ----- Inquiry cache ----- */
-#define INQUIRY_CACHE_AGE_MAX   (HZ*5)    // 5 seconds
-#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+/* HCI Core structures */
 
 struct inquiry_entry {
 	struct inquiry_entry 	*next;
@@ -53,111 +51,180 @@
 	struct inquiry_entry 	*list;
 };
 
-static inline void inquiry_cache_init(struct inquiry_cache *cache)
-{
-	spin_lock_init(&cache->lock);
-	cache->list = NULL;
-}
+struct conn_hash {
+	struct list_head list;
+	spinlock_t       lock;
+	unsigned int     num;
+};
 
-static inline void inquiry_cache_lock(struct inquiry_cache *cache)
-{
-	spin_lock(&cache->lock);
-}
+struct hci_dev {
+	struct list_head list;
+	spinlock_t	lock;
+	atomic_t 	refcnt;
 
-static inline void inquiry_cache_unlock(struct inquiry_cache *cache)
-{
-	spin_unlock(&cache->lock);
-}
+	char		name[8];
+	unsigned long	flags;
+	__u16		id;
+	__u8	 	type;
+	bdaddr_t	bdaddr;
+	__u8		features[8];
 
-static inline void inquiry_cache_lock_bh(struct inquiry_cache *cache)
-{
-	spin_lock_bh(&cache->lock);
-}
+	__u16		pkt_type;
+	__u16		link_policy;
+	__u16		link_mode;
+	
+	atomic_t 	cmd_cnt;
+	unsigned int 	acl_cnt;
+	unsigned int 	sco_cnt;
 
-static inline void inquiry_cache_unlock_bh(struct inquiry_cache *cache)
-{
-	spin_unlock_bh(&cache->lock);
-}
+	unsigned int	acl_mtu;
+	unsigned int 	sco_mtu;
+	unsigned int	acl_pkts;
+	unsigned int	sco_pkts;
 
-static inline long inquiry_cache_age(struct inquiry_cache *cache)
-{
-	return jiffies - cache->timestamp;
-}
+	unsigned long   cmd_last_tx;
+	unsigned long   acl_last_tx;
+	unsigned long   sco_last_tx;
+	
+	struct tasklet_struct 	cmd_task;
+	struct tasklet_struct	rx_task;
+	struct tasklet_struct 	tx_task;
 
-static inline long inquiry_entry_age(struct inquiry_entry *e)
-{
-	return jiffies - e->timestamp;
-}
-extern void inquiry_cache_flush(struct inquiry_cache *cache);
+	struct sk_buff_head	rx_q;
+	struct sk_buff_head 	raw_q;
+	struct sk_buff_head 	cmd_q;
 
-struct hci_dev;
+	struct sk_buff     	*sent_cmd;
+
+	struct semaphore	req_lock;
+	wait_queue_head_t	req_wait_q;
+	__u32			req_status;
+	__u32			req_result;
+
+	struct inquiry_cache 	inq_cache;
+	struct conn_hash 	conn_hash;
+
+	struct hci_dev_stats 	stat;
+
+	void			*driver_data;
+	void			*core_data;
+
+	atomic_t 		promisc;
+
+	int (*open)(struct hci_dev *hdev);
+	int (*close)(struct hci_dev *hdev);
+	int (*flush)(struct hci_dev *hdev);
+	int (*send)(struct sk_buff *skb);
+	void (*destruct)(struct hci_dev *hdev);
+	int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg);
+};
 
-/* ----- HCI Connections ----- */
 struct hci_conn {
 	struct list_head list;
+
+	atomic_t	 refcnt;
+	spinlock_t	 lock;
+
 	bdaddr_t         dst;
 	__u16            handle;
+	__u16            state;
 	__u8		 type;
-	unsigned int     sent;
+	__u8		 out;
+	__u32		 link_mode;
+	unsigned long	 pend;
+	
+	unsigned int	 sent;
+	
+	struct sk_buff_head data_q;
 
+	struct timer_list timer;
+	
 	struct hci_dev 	*hdev;
 	void		*l2cap_data;
+	void		*sco_data;
 	void		*priv;
 
-	struct sk_buff_head data_q;
+	struct hci_conn *link;
 };
 
-struct conn_hash {
-	struct list_head list;
-	spinlock_t       lock;
-	unsigned int     num;
-};
+extern struct hci_proto *hci_proto[];
+extern struct list_head hdev_list;
+extern spinlock_t hdev_list_lock;
+
+/* ----- Inquiry cache ----- */
+#define INQUIRY_CACHE_AGE_MAX   (HZ*30)   // 30 seconds
+#define INQUIRY_ENTRY_AGE_MAX   (HZ*60)   // 60 seconds
+
+#define inquiry_cache_lock(c)		spin_lock(&c->lock)
+#define inquiry_cache_unlock(c)		spin_unlock(&c->lock)
+#define inquiry_cache_lock_bh(c)	spin_lock_bh(&c->lock)
+#define inquiry_cache_unlock_bh(c)	spin_unlock_bh(&c->lock)
 
-static inline void conn_hash_init(struct conn_hash *h)
+static inline void inquiry_cache_init(struct hci_dev *hdev)
 {
-	INIT_LIST_HEAD(&h->list);
-	spin_lock_init(&h->lock);
-	h->num = 0;	
+	struct inquiry_cache *c = &hdev->inq_cache;
+	spin_lock_init(&c->lock);
+	c->list = NULL;
 }
 
-static inline void conn_hash_lock(struct conn_hash *h)
+static inline long inquiry_cache_age(struct hci_dev *hdev)
 {
-	spin_lock(&h->lock);
+	struct inquiry_cache *c = &hdev->inq_cache;
+	return jiffies - c->timestamp;
 }
 
-static inline void conn_hash_unlock(struct conn_hash *h)
+static inline long inquiry_entry_age(struct inquiry_entry *e)
 {
-	spin_unlock(&h->lock);
+	return jiffies - e->timestamp;
 }
 
-static inline void __conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr);
+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info);
+void inquiry_cache_flush(struct hci_dev *hdev);
+int  inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf);
+
+/* ----- HCI Connections ----- */
+enum {
+	HCI_CONN_AUTH_PEND,
+	HCI_CONN_ENCRYPT_PEND
+};
+
+#define hci_conn_lock(c)	spin_lock(&c->lock)
+#define hci_conn_unlock(c)	spin_unlock(&c->lock)
+#define hci_conn_lock_bh(c)	spin_lock_bh(&c->lock)
+#define hci_conn_unlock_bh(c)	spin_unlock_bh(&c->lock)
+
+#define conn_hash_lock(d)	spin_lock(&d->conn_hash->lock)
+#define conn_hash_unlock(d)	spin_unlock(&d->conn_hash->lock)
+#define conn_hash_lock_bh(d)	spin_lock_bh(&d->conn_hash->lock)
+#define conn_hash_unlock_bh(d)	spin_unlock_bh(&d->conn_hash->lock)
+
+static inline void conn_hash_init(struct hci_dev *hdev)
 {
-	list_add(&c->list, &h->list);
-	h->num++;
+	struct conn_hash *h = &hdev->conn_hash;
+	INIT_LIST_HEAD(&h->list);
+	spin_lock_init(&h->lock);
+	h->num = 0;	
 }
 
-static inline void conn_hash_add(struct conn_hash *h, __u16 handle, struct hci_conn *c)
+static inline void conn_hash_add(struct hci_dev *hdev, struct hci_conn *c)
 {
-	conn_hash_lock(h);
-	__conn_hash_add(h, handle, c);
-	conn_hash_unlock(h);
+	struct conn_hash *h = &hdev->conn_hash;
+	list_add(&c->list, &h->list);
+	h->num++;
 }
 
-static inline void __conn_hash_del(struct conn_hash *h, struct hci_conn *c)
+static inline void conn_hash_del(struct hci_dev *hdev, struct hci_conn *c)
 {
+	struct conn_hash *h = &hdev->conn_hash;
 	list_del(&c->list);
 	h->num--;
 }
 
-static inline void conn_hash_del(struct conn_hash *h, struct hci_conn *c)
-{
-	conn_hash_lock(h);
-	__conn_hash_del(h, c);
-	conn_hash_unlock(h);
-}
-
-static inline  struct hci_conn *__conn_hash_lookup(struct conn_hash *h, __u16 handle)
+static inline struct hci_conn *conn_hash_lookup_handle(struct hci_dev *hdev,
+	       				__u16 handle)
 {
+	register struct conn_hash *h = &hdev->conn_hash;
 	register struct list_head *p;
 	register struct hci_conn  *c;
 
@@ -169,101 +236,89 @@
         return NULL;
 }
 
-static inline struct hci_conn *conn_hash_lookup(struct conn_hash *h, __u16 handle)
+static inline struct hci_conn *conn_hash_lookup_ba(struct hci_dev *hdev,
+					__u8 type, bdaddr_t *ba)
 {
-	struct hci_conn *conn;
+	register struct conn_hash *h = &hdev->conn_hash;
+	register struct list_head *p;
+	register struct hci_conn  *c;
 
-	conn_hash_lock(h);
-	conn = __conn_hash_lookup(h, handle);
-	conn_hash_unlock(h);
-	return conn;
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == type && !bacmp(&c->dst, ba))
+			return c;
+	}
+        return NULL;
 }
 
-/* ----- HCI Devices ----- */
-struct hci_dev {
-	atomic_t 	refcnt;
-
-	char		name[8];
-	__u32	 	flags;
-	__u16		id;
-	__u8	 	type;
-	bdaddr_t	bdaddr;
-	__u8		features[8];
-
-	__u16		pkt_type;
+void hci_acl_connect(struct hci_conn *conn);
+void hci_acl_disconn(struct hci_conn *conn, __u8 reason);
+void hci_add_sco(struct hci_conn *conn, __u16 handle);
 
-	atomic_t 	cmd_cnt;
-	unsigned int 	acl_cnt;
-	unsigned int 	sco_cnt;
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst);
+int    hci_conn_del(struct hci_conn *conn);
+void   hci_conn_hash_flush(struct hci_dev *hdev);
 
-	unsigned int	acl_mtu;
-	unsigned int 	sco_mtu;
-	unsigned int	acl_max;
-	unsigned int	sco_max;
-
-	void		*driver_data;
-	void		*l2cap_data;
-	void		*priv;
-
-	struct tasklet_struct 	cmd_task;
-	struct tasklet_struct	rx_task;
-	struct tasklet_struct 	tx_task;
-
-	struct sk_buff_head	rx_q;
-	struct sk_buff_head 	raw_q;
-	struct sk_buff_head 	cmd_q;
-
-	struct sk_buff     	*sent_cmd;
-
-	struct semaphore	req_lock;
-	wait_queue_head_t	req_wait_q;
-	__u32			req_status;
-	__u32			req_result;
-
-	struct inquiry_cache 	inq_cache;
-
-	struct conn_hash 	conn_hash;
+struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *src);
+int hci_conn_auth(struct hci_conn *conn);
+int hci_conn_encrypt(struct hci_conn *conn);
 
-	struct hci_dev_stats 	stat;
-
-	int (*open)(struct hci_dev *hdev);
-	int (*close)(struct hci_dev *hdev);
-	int (*flush)(struct hci_dev *hdev);
-	int (*send)(struct sk_buff *skb);
-};
+static inline void hci_conn_set_timer(struct hci_conn *conn, long timeout)
+{
+	mod_timer(&conn->timer, jiffies + timeout);
+}
 
-static inline void hci_dev_hold(struct hci_dev *hdev)
+static inline void hci_conn_del_timer(struct hci_conn *conn)
 {
-	atomic_inc(&hdev->refcnt);
+	del_timer(&conn->timer);
 }
 
-static inline void hci_dev_put(struct hci_dev *hdev)
+static inline void hci_conn_hold(struct hci_conn *conn)
 {
-	atomic_dec(&hdev->refcnt);
+	atomic_inc(&conn->refcnt);
+	hci_conn_del_timer(conn);
 }
 
-extern struct hci_dev *hci_dev_get(int index);
-extern int hci_register_dev(struct hci_dev *hdev);
-extern int hci_unregister_dev(struct hci_dev *hdev);
-extern int hci_dev_open(__u16 dev);
-extern int hci_dev_close(__u16 dev);
-extern int hci_dev_reset(__u16 dev);
-extern int hci_dev_reset_stat(__u16 dev);
-extern int hci_dev_info(unsigned long arg);
-extern int hci_dev_list(unsigned long arg);
-extern int hci_dev_setscan(unsigned long arg);
-extern int hci_dev_setauth(unsigned long arg);
-extern int hci_dev_setptype(unsigned long arg);
-extern int hci_conn_list(unsigned long arg);
-extern int hci_inquiry(unsigned long arg);
+static inline void hci_conn_put(struct hci_conn *conn)
+{
+	if (atomic_dec_and_test(&conn->refcnt) && conn->out)
+		hci_conn_set_timer(conn, HCI_DISCONN_TIMEOUT);
+}
 
-extern __u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode);
-extern __u32 hci_dev_getmode(struct hci_dev *hdev);
+/* ----- HCI Devices ----- */
+static inline void hci_dev_put(struct hci_dev *d)
+{ 
+	if (atomic_dec_and_test(&d->refcnt))
+		d->destruct(d);
+}
+#define hci_dev_hold(d)		atomic_inc(&d->refcnt)
+
+#define hci_dev_lock(d)		spin_lock(&d->lock)
+#define hci_dev_unlock(d)	spin_unlock(&d->lock)
+#define hci_dev_lock_bh(d)	spin_lock_bh(&d->lock)
+#define hci_dev_unlock_bh(d)	spin_unlock_bh(&d->lock)
+
+struct hci_dev *hci_dev_get(int index);
+struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst);
+int hci_register_dev(struct hci_dev *hdev);
+int hci_unregister_dev(struct hci_dev *hdev);
+int hci_dev_open(__u16 dev);
+int hci_dev_close(__u16 dev);
+int hci_dev_reset(__u16 dev);
+int hci_dev_reset_stat(__u16 dev);
+int hci_dev_cmd(unsigned int cmd, unsigned long arg);
+int hci_get_dev_list(unsigned long arg);
+int hci_get_dev_info(unsigned long arg);
+int hci_get_conn_list(unsigned long arg);
+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg);
+int hci_inquiry(unsigned long arg);
 
-extern int hci_recv_frame(struct sk_buff *skb);
+int  hci_recv_frame(struct sk_buff *skb);
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 /* ----- LMP capabilities ----- */
 #define lmp_rswitch_capable(dev) (dev->features[0] & LMP_RSWITCH)
+#define lmp_encrypt_capable(dev) (dev->features[0] & LMP_ENCRYPT)
 
 /* ----- HCI tasks ----- */
 static inline void hci_sched_cmd(struct hci_dev *hdev)
@@ -289,38 +344,127 @@
 
 	void		*priv;
 
-	int (*connect_ind) 	(struct hci_dev *hdev, bdaddr_t *bdaddr);
-	int (*connect_cfm)	(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *conn);
+	int (*connect_ind) 	(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
+	int (*connect_cfm)	(struct hci_conn *conn, __u8 status);
 	int (*disconn_ind)	(struct hci_conn *conn, __u8 reason);
-	int (*recv_acldata)	(struct hci_conn *conn, struct sk_buff *skb , __u16 flags);
+	int (*recv_acldata)	(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
 	int (*recv_scodata)	(struct hci_conn *conn, struct sk_buff *skb);
+	int (*auth_cfm)		(struct hci_conn *conn, __u8 status);
+	int (*encrypt_cfm)	(struct hci_conn *conn, __u8 status);
 };
 
-extern int hci_register_proto(struct hci_proto *hproto);
-extern int hci_unregister_proto(struct hci_proto *hproto);
-extern int hci_register_notifier(struct notifier_block *nb);
-extern int hci_unregister_notifier(struct notifier_block *nb);
-extern int hci_connect(struct hci_dev * hdev, bdaddr_t * bdaddr);
-extern int hci_disconnect(struct hci_conn *conn, __u8 reason);
-extern int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void * param);
-extern int hci_send_raw(struct sk_buff *skb);
-extern int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
-extern int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	register struct hci_proto *hp;
+	int mask = 0;
+	
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_ind)
+		mask |= hp->connect_ind(hdev, bdaddr, type);
+
+	return mask;
+}
+
+static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->connect_cfm)
+		hp->connect_cfm(conn, status);
+}
+
+static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->disconn_ind)
+		hp->disconn_ind(conn, reason);
+}
+
+static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->auth_cfm)
+		hp->auth_cfm(conn, status);
+}
+
+static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status)
+{
+	register struct hci_proto *hp;
+
+	hp = hci_proto[HCI_PROTO_L2CAP];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+
+	hp = hci_proto[HCI_PROTO_SCO];
+	if (hp && hp->encrypt_cfm)
+		hp->encrypt_cfm(conn, status);
+}
+
+int hci_register_proto(struct hci_proto *hproto);
+int hci_unregister_proto(struct hci_proto *hproto);
+int hci_register_notifier(struct notifier_block *nb);
+int hci_unregister_notifier(struct notifier_block *nb);
+
+int hci_send_cmd(struct hci_dev *hdev, __u16 ogf, __u16 ocf, __u32 plen, void *param);
+int hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+int hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
+int hci_send_raw(struct sk_buff *skb);
+
+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf);
+
+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data);
 
 /* ----- HCI Sockets ----- */
-extern void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
+void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
 
 /* HCI info for socket */
-#define hci_pi(sk)	((struct hci_pinfo *) &sk->protinfo)
+#define hci_pi(sk)	((struct hci_pinfo *) sk->protinfo)
 struct hci_pinfo {
 	struct hci_dev 	  *hdev;
 	struct hci_filter filter;
 	__u32             cmsg_mask;
 };
 
+/* HCI security filter */
+#define HCI_SFLT_MAX_OGF 4
+
+struct hci_sec_filter {
+	__u32 type_mask;
+	__u32 event_mask[2];
+	__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
+};
+
+
 /* ----- HCI requests ----- */
 #define HCI_REQ_DONE	  0
 #define HCI_REQ_PEND	  1
 #define HCI_REQ_CANCELED  2
+
+#define hci_req_lock(d)		down(&d->req_lock)
+#define hci_req_unlock(d)	up(&d->req_lock)
+
+void hci_req_complete(struct hci_dev *hdev, int result);
+void hci_req_cancel(struct hci_dev *hdev, int err);
 
 #endif /* __HCI_CORE_H */
diff -Nru a/include/net/bluetooth/hci_uart.h b/include/net/bluetooth/hci_uart.h
--- a/include/net/bluetooth/hci_uart.h	Sun May  5 20:39:49 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,62 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_uart.h,v 1.2 2001/06/02 01:40:08 maxk Exp $
- */
-
-#ifndef N_HCI
-#define N_HCI	15
-#endif
-
-#ifdef __KERNEL__
-
-#define tty2n_hci(tty)  ((struct n_hci *)((tty)->disc_data))
-#define n_hci2tty(n_hci) ((n_hci)->tty)
-
-struct n_hci {
-	struct tty_struct *tty;
-	struct hci_dev hdev;
-
-	struct sk_buff_head txq;
-	unsigned long tx_state;
-
-	spinlock_t rx_lock;
-	unsigned long rx_state;
-	unsigned long rx_count;
-	struct sk_buff *rx_skb;
-};
-
-/* Transmit states  */
-#define TRANS_SENDING		1
-#define TRANS_WAKEUP		2
-
-/* Receiver States */
-#define WAIT_PACKET_TYPE	0
-#define WAIT_EVENT_HDR	 	1
-#define WAIT_ACL_HDR		2
-#define WAIT_SCO_HDR		3
-#define WAIT_DATA	        4
-
-#endif /* __KERNEL__ */
diff -Nru a/include/net/bluetooth/hci_usb.h b/include/net/bluetooth/hci_usb.h
--- a/include/net/bluetooth/hci_usb.h	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,68 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_usb.h,v 1.3 2001/06/02 01:40:08 maxk Exp $
- */
-
-#ifdef __KERNEL__
-
-/* Class, SubClass, and Protocol codes that describe a Bluetooth device */
-#define HCI_DEV_CLASS        0xe0	/* Wireless class */
-#define HCI_DEV_SUBCLASS     0x01	/* RF subclass */
-#define HCI_DEV_PROTOCOL     0x01	/* Bluetooth programming protocol */
-
-#define HCI_CTRL_REQ	     0x20
-
-struct hci_usb {
-	struct usb_device 	*udev;
-
-	struct usb_ctrlrequest	dev_req;
-	struct urb 		*ctrl_urb;
-	struct urb		*intr_urb;
-	struct urb		*read_urb;
-	struct urb		*write_urb;
-
-	__u8			*read_buf;
-	__u8			*intr_buf;
-	struct sk_buff		*intr_skb;
-	int			intr_count;
-
-	__u8			bulk_out_ep_addr;
-	__u8			bulk_in_ep_addr;
-	__u8			intr_in_ep_addr;
-	__u8			intr_in_interval;
-
-	struct hci_dev		hdev;
-
-	unsigned long		tx_state;
-	struct sk_buff_head	tx_ctrl_q;
-	struct sk_buff_head	tx_write_q;
-};
-
-/* Transmit states  */
-#define HCI_TX_CTRL	1
-#define HCI_TX_WRITE	2
-
-#endif /* __KERNEL__ */
diff -Nru a/include/net/bluetooth/hci_vhci.h b/include/net/bluetooth/hci_vhci.h
--- a/include/net/bluetooth/hci_vhci.h	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,50 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * $Id: hci_vhci.h,v 1.2 2001/08/01 01:02:20 maxk Exp $
- */
-
-#ifndef __HCI_VHCI_H
-#define __HCI_VHCI_H
-
-#ifdef __KERNEL__
-
-struct hci_vhci_struct {
-	struct hci_dev       hdev;
-	__u32                flags;
-	wait_queue_head_t    read_wait;
-	struct sk_buff_head  readq;
-	struct fasync_struct *fasync;
-};
-
-/* VHCI device flags */
-#define VHCI_FASYNC		0x0010
-
-#endif /* __KERNEL__ */
-
-#define VHCI_DEV	"/dev/vhci"
-#define VHCI_MINOR	250
-
-#endif /* __HCI_VHCI_H */
diff -Nru a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
--- a/include/net/bluetooth/l2cap.h	Sun May  5 20:39:49 2002
+++ b/include/net/bluetooth/l2cap.h	Sun May  5 20:39:49 2002
@@ -23,22 +23,17 @@
 */
 
 /*
- *  $Id: l2cap.h,v 1.5 2001/06/14 21:28:26 maxk Exp $
+ *  $Id: l2cap.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
  */
 
 #ifndef __L2CAP_H
 #define __L2CAP_H
 
-#include <asm/types.h>
-#include <asm/byteorder.h>
-
 /* L2CAP defaults */
 #define L2CAP_DEFAULT_MTU 	672
 #define L2CAP_DEFAULT_FLUSH_TO	0xFFFF
 
 #define L2CAP_CONN_TIMEOUT 	(HZ * 40)
-#define L2CAP_DISCONN_TIMEOUT 	(HZ * 2)
-#define L2CAP_CONN_IDLE_TIMEOUT	(HZ * 60)
 
 /* L2CAP socket address */
 struct sockaddr_l2 {
@@ -47,17 +42,12 @@
 	bdaddr_t	l2_bdaddr;
 };
 
-/* set/get sockopt defines */
-#define L2CAP_OPTIONS  0x01
+/* Socket options */
+#define L2CAP_OPTIONS	0x01
 struct l2cap_options {
 	__u16 omtu;
 	__u16 imtu;
 	__u16 flush_to;
-	__u32 token_rate;
-	__u32 bucket_size;
-	__u32 pick_band;
-	__u32 latency;
-	__u32 delay_var;
 };
 
 #define L2CAP_CONNINFO  0x02
@@ -65,6 +55,26 @@
 	__u16 hci_handle;
 };
 
+#define L2CAP_LM	0x03
+#define L2CAP_LM_MASTER		0x0001
+#define L2CAP_LM_AUTH		0x0002
+#define L2CAP_LM_ENCRYPT	0x0004
+#define L2CAP_LM_TRUSTED	0x0008
+
+#define L2CAP_QOS	0x04
+struct l2cap_qos {
+	__u16 service_type;
+	__u32 token_rate;
+	__u32 token_bucket_size;
+	__u32 peak_bandwidth;
+	__u32 latency;
+	__u32 delay_variation;
+};
+
+#define L2CAP_SERV_NO_TRAFFIC	0x00
+#define L2CAP_SERV_BEST_EFFORT	0x01
+#define L2CAP_SERV_GUARANTEED	0x02
+
 /* L2CAP command codes */
 #define L2CAP_COMMAND_REJ 0x01
 #define L2CAP_CONN_REQ    0x02
@@ -79,7 +89,6 @@
 #define L2CAP_INFO_RSP    0x0b
 
 /* L2CAP structures */
-
 typedef struct {
 	__u16      len;
 	__u16      cid;
@@ -112,11 +121,17 @@
 } __attribute__ ((packed))	l2cap_conn_rsp;
 #define L2CAP_CONN_RSP_SIZE	8
 
-#define L2CAP_CONN_SUCCESS    0x0000
-#define L2CAP_CONN_PEND       0x0001
-#define L2CAP_CONN_BAD_PSM    0x0002
-#define L2CAP_CONN_SEC_BLOCK  0x0003
-#define L2CAP_CONN_NO_MEM     0x0004
+/* connect result */
+#define L2CAP_CR_SUCCESS    0x0000
+#define L2CAP_CR_PEND       0x0001
+#define L2CAP_CR_BAD_PSM    0x0002
+#define L2CAP_CR_SEC_BLOCK  0x0003
+#define L2CAP_CR_NO_MEM     0x0004
+
+/* connect status */
+#define L2CAP_CS_NO_INFO      0x0000
+#define L2CAP_CS_AUTHEN_PEND  0x0001
+#define L2CAP_CS_AUTHOR_PEND  0x0002
 
 typedef struct {
 	__u16      dcid;
@@ -147,6 +162,8 @@
 #define L2CAP_CONF_FLUSH_TO	0x02
 #define L2CAP_CONF_QOS		0x03
 
+#define L2CAP_CONF_MAX_SIZE	22
+
 typedef struct {
 	__u16      dcid;
 	__u16      scid;
@@ -158,5 +175,71 @@
 	__u16      scid;
 } __attribute__ ((packed)) 	l2cap_disconn_rsp;
 #define L2CAP_DISCONN_RSP_SIZE	4
+
+typedef struct {
+	__u16       type;
+	__u8        data[0];
+} __attribute__ ((packed))	l2cap_info_req;
+#define L2CAP_INFO_REQ_SIZE	2
+
+typedef struct {
+	__u16       type;
+	__u16       result;
+	__u8        data[0];
+} __attribute__ ((packed))	l2cap_info_rsp;
+#define L2CAP_INFO_RSP_SIZE	4
+
+/* ----- L2CAP connections ----- */
+struct l2cap_chan_list {
+	struct sock	*head;
+	rwlock_t	lock;
+	long		num;
+};
+
+struct l2cap_conn {
+	struct hci_conn	*hcon;
+
+	bdaddr_t 	*dst;
+	bdaddr_t 	*src;
+	
+	unsigned int    mtu;
+
+	spinlock_t	lock;
+	
+	struct sk_buff *rx_skb;
+	__u32		rx_len;
+	__u8		rx_ident;
+	__u8		tx_ident;
+
+	struct l2cap_chan_list chan_list;
+};
+
+/* ----- L2CAP channel and socket info ----- */
+#define l2cap_pi(sk)   ((struct l2cap_pinfo *) sk->protinfo)
+
+struct l2cap_pinfo {
+	__u16		psm;
+	__u16		dcid;
+	__u16		scid;
+
+	__u16		imtu;
+	__u16		omtu;
+	__u16		flush_to;
+	
+	__u32		link_mode;
+
+	__u8		conf_state;
+	__u16		conf_mtu;
+
+	__u8		ident;
+
+	struct l2cap_conn 	*conn;
+	struct sock 		*next_c;
+	struct sock 		*prev_c;
+};
+
+#define CONF_REQ_SENT    0x01
+#define CONF_INPUT_DONE  0x02
+#define CONF_OUTPUT_DONE 0x04
 
 #endif /* __L2CAP_H */
diff -Nru a/include/net/bluetooth/l2cap_core.h b/include/net/bluetooth/l2cap_core.h
--- a/include/net/bluetooth/l2cap_core.h	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,144 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- *  $Id: l2cap_core.h,v 1.6 2001/08/03 04:19:49 maxk Exp $
- */
-
-#ifndef __L2CAP_CORE_H
-#define __L2CAP_CORE_H
-
-#ifdef __KERNEL__
-
-/* ----- L2CAP interface ----- */
-struct l2cap_iff {
-	struct list_head list;
-	struct hci_dev   *hdev;
-	bdaddr_t         *bdaddr;
-	__u16            mtu;
-	spinlock_t       lock;
-	struct list_head conn_list;
-};
-
-static inline void l2cap_iff_lock(struct l2cap_iff *iff)
-{
-	spin_lock(&iff->lock);
-}
-
-static inline void l2cap_iff_unlock(struct l2cap_iff *iff)
-{
-	spin_unlock(&iff->lock);
-}
-
-/* ----- L2CAP connections ----- */
-struct l2cap_chan_list {
-	struct sock	*head;
-	rwlock_t	lock;
-	long		num;
-};
-
-struct l2cap_conn {
-	struct l2cap_iff *iff;
-	struct list_head list;
-
-	struct hci_conn	 *hconn;
-
-	__u16		state;
-	__u8		out;
-	bdaddr_t	src;
-	bdaddr_t	dst;
-
-	spinlock_t	lock;
-	atomic_t	refcnt;
-
-	struct sk_buff *rx_skb;
-	__u32		rx_len;
-	__u8		rx_ident;
-	__u8		tx_ident;
-
-	struct l2cap_chan_list chan_list;
-
-	struct timer_list timer;
-};
-
-static inline void __l2cap_conn_link(struct l2cap_iff *iff, struct l2cap_conn *c)
-{
-	list_add(&c->list, &iff->conn_list);
-}
-
-static inline void __l2cap_conn_unlink(struct l2cap_iff *iff, struct l2cap_conn *c)
-{
-	list_del(&c->list);
-}
-
-/* ----- L2CAP channel and socket info ----- */
-#define l2cap_pi(sk)   ((struct l2cap_pinfo *) &sk->protinfo)
-
-struct l2cap_accept_q {
-	struct sock 	*head;
-	struct sock 	*tail;
-};
-
-struct l2cap_pinfo {
-	bdaddr_t	src;
-	bdaddr_t	dst;
-	__u16		psm;
-	__u16		dcid;
-	__u16		scid;
-	__u32		flags;
-
-	__u16		imtu;
-	__u16		omtu;
-	__u16		flush_to;
-
-	__u8		conf_state;
-	__u16		conf_mtu;
-
-	__u8		ident;
-
-	struct l2cap_conn 	*conn;
-	struct sock 		*next_c;
-	struct sock 		*prev_c;
-
-	struct sock *parent;
-	struct sock *next_q;
-	struct sock *prev_q;
-
-	struct l2cap_accept_q accept_q;
-};
-
-#define CONF_REQ_SENT    0x01
-#define CONF_INPUT_DONE  0x02
-#define CONF_OUTPUT_DONE 0x04
-
-extern struct bluez_sock_list l2cap_sk_list;
-extern struct list_head  l2cap_iff_list;
-extern rwlock_t l2cap_rt_lock;
-
-extern void l2cap_register_proc(void);
-extern void l2cap_unregister_proc(void);
-
-#endif /* __KERNEL__ */
-
-#endif /* __L2CAP_CORE_H */
diff -Nru a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/net/bluetooth/sco.h	Sun May  5 20:39:50 2002
@@ -0,0 +1,81 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ *  $Id: sco.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ */
+
+#ifndef __SCO_H
+#define __SCO_H
+
+/* SCO defaults */
+#define SCO_DEFAULT_MTU 	500
+#define SCO_DEFAULT_FLUSH_TO	0xFFFF
+
+#define SCO_CONN_TIMEOUT 	(HZ * 40)
+#define SCO_DISCONN_TIMEOUT 	(HZ * 2)
+#define SCO_CONN_IDLE_TIMEOUT	(HZ * 60)
+
+/* SCO socket address */
+struct sockaddr_sco {
+	sa_family_t	sco_family;
+	bdaddr_t	sco_bdaddr;
+};
+
+/* set/get sockopt defines */
+#define SCO_OPTIONS  0x01
+struct sco_options {
+	__u16 mtu;
+};
+
+#define SCO_CONNINFO  0x02
+struct sco_conninfo {
+	__u16 hci_handle;
+};
+
+/* ---- SCO connections ---- */
+struct sco_conn {
+	struct hci_conn	*hcon;
+
+	bdaddr_t 	*dst;
+	bdaddr_t 	*src;
+	
+	spinlock_t	lock;
+	struct sock 	*sk;
+
+	unsigned int    mtu;
+};
+
+#define sco_conn_lock(c)	spin_lock(&c->lock);
+#define sco_conn_unlock(c)	spin_unlock(&c->lock);
+
+/* ----- SCO socket info ----- */
+#define sco_pi(sk)   ((struct sco_pinfo *) sk->protinfo)
+
+struct sco_pinfo {
+	__u32		flags;
+	struct sco_conn	*conn;
+};
+
+#endif /* __SCO_H */
diff -Nru a/init/do_mounts.c b/init/do_mounts.c
--- a/init/do_mounts.c	Sun May  5 20:39:49 2002
+++ b/init/do_mounts.c	Sun May  5 20:39:49 2002
@@ -374,7 +374,7 @@
 	va_start(args, fmt);
 	vsprintf(buf, fmt, args);
 	va_end(args);
-	fd = open("/dev/root", O_RDWR, 0);
+	fd = open("/dev/root", O_RDWR | O_NDELAY, 0);
 	if (fd >= 0) {
 		sys_ioctl(fd, FDEJECT, 0);
 		close(fd);
@@ -791,7 +791,7 @@
 			error = sys_ioctl(fd, BLKFLSBUF, 0);
 			close(fd);
 		}
-		printk(error ? "okay\n" : "failed\n");
+		printk(!error ? "okay\n" : "failed\n");
 	}
 #endif
 }
diff -Nru a/mm/filemap.c b/mm/filemap.c
--- a/mm/filemap.c	Sun May  5 20:39:49 2002
+++ b/mm/filemap.c	Sun May  5 20:39:49 2002
@@ -43,8 +43,7 @@
  *  pagemap_lru_lock
  *  ->i_shared_lock		(vmtruncate)
  *    ->i_bufferlist_lock	(__free_pte->__set_page_dirty_buffers)
- *      ->unused_list_lock	(try_to_free_buffers)
- *        ->mapping->page_lock
+ *      ->mapping->page_lock
  *      ->inode_lock		(__mark_inode_dirty)
  *        ->sb_lock		(fs/fs-writeback.c)
  */
@@ -201,18 +200,18 @@
 			int failed;
 
 			page_cache_get(page);
-			if (PageWriteback(page)) {
-				/*
-				 * urgggh. This function is utterly foul,
-				 * and this addition doesn't help.  Kill.
-				 */
+			failed = TestSetPageLocked(page);
+			if (!failed && PageWriteback(page)) {
+				unlock_page(page);
+				list_del(head);
+				list_add_tail(head, curr);
 				write_unlock(&mapping->page_lock);
 				wait_on_page_writeback(page);
+				page_cache_release(page);
 				unlocked = 1;
 				write_lock(&mapping->page_lock);
 				goto restart;
 			}
-			failed = TestSetPageLocked(page);
 
 			list_del(head);
 			if (!failed)
@@ -287,11 +286,11 @@
 	clean_list_pages(mapping, &mapping->io_pages, start);
 	clean_list_pages(mapping, &mapping->dirty_pages, start);
 	do {
-		unlocked |= truncate_list_pages(mapping,
+		unlocked = truncate_list_pages(mapping,
 				&mapping->io_pages, start, &partial);
 		unlocked |= truncate_list_pages(mapping,
 				&mapping->dirty_pages, start, &partial);
-		unlocked = truncate_list_pages(mapping,
+		unlocked |= truncate_list_pages(mapping,
 				&mapping->clean_pages, start, &partial);
 		unlocked |= truncate_list_pages(mapping,
 				&mapping->locked_pages, start, &partial);
@@ -659,7 +658,6 @@
 void unlock_page(struct page *page)
 {
 	wait_queue_head_t *waitqueue = page_waitqueue(page);
-	clear_bit(PG_launder, &(page)->flags);
 	smp_mb__before_clear_bit();
 	if (!TestClearPageLocked(page))
 		BUG();
@@ -674,7 +672,7 @@
 void end_page_writeback(struct page *page)
 {
 	wait_queue_head_t *waitqueue = page_waitqueue(page);
-	clear_bit(PG_launder, &(page)->flags);
+	ClearPageLaunder(page);
 	smp_mb__before_clear_bit();
 	if (!TestClearPageWriteback(page))
 		BUG();
diff -Nru a/mm/memory.c b/mm/memory.c
--- a/mm/memory.c	Sun May  5 20:39:50 2002
+++ b/mm/memory.c	Sun May  5 20:39:50 2002
@@ -76,8 +76,12 @@
  */
 void __free_pte(pte_t pte)
 {
-	struct page *page = pte_page(pte);
-	if ((!VALID_PAGE(page)) || PageReserved(page))
+	struct page *page;
+	unsigned long pfn = pte_pfn(pte);
+	if (!pfn_valid(pfn))
+		return;
+	page = pfn_to_page(pfn);
+	if (PageReserved(page))
 		return;
 	if (pte_dirty(pte))
 		set_page_dirty(page);		
@@ -269,6 +273,7 @@
 			do {
 				pte_t pte = *src_pte;
 				struct page *ptepage;
+				unsigned long pfn;
 				
 				/* copy_one_pte */
 
@@ -278,9 +283,11 @@
 					swap_duplicate(pte_to_swp_entry(pte));
 					goto cont_copy_pte_range;
 				}
-				ptepage = pte_page(pte);
-				if ((!VALID_PAGE(ptepage)) || 
-				    PageReserved(ptepage))
+				pfn = pte_pfn(pte);
+				if (!pfn_valid(pfn))
+					goto cont_copy_pte_range;
+				ptepage = pfn_to_page(pfn);
+				if (PageReserved(ptepage))
 					goto cont_copy_pte_range;
 
 				/* If it's a COW mapping, write protect it both in the parent and the child */
@@ -356,9 +363,13 @@
 		if (pte_none(pte))
 			continue;
 		if (pte_present(pte)) {
-			struct page *page = pte_page(pte);
-			if (VALID_PAGE(page) && !PageReserved(page))
-				freed ++;
+			struct page *page;
+			unsigned long pfn = pte_pfn(pte);
+			if (pfn_valid(pfn)) {
+				page = pfn_to_page(pfn);
+				if (!PageReserved(page))
+					freed++;
+			}
 			/* This will eventually call __free_pte on the pte. */
 			tlb_remove_page(tlb, ptep, address + offset);
 		} else {
@@ -451,6 +462,7 @@
 	pgd_t *pgd;
 	pmd_t *pmd;
 	pte_t *ptep, pte;
+	unsigned long pfn;
 
 	pgd = pgd_offset(mm, address);
 	if (pgd_none(*pgd) || pgd_bad(*pgd))
@@ -472,8 +484,11 @@
 	preempt_enable();
 	if (pte_present(pte)) {
 		if (!write ||
-		    (pte_write(pte) && pte_dirty(pte)))
-			return pte_page(pte);
+		    (pte_write(pte) && pte_dirty(pte))) {
+			pfn = pte_pfn(pte);
+			if (pfn_valid(pfn))
+				return pfn_to_page(pfn);
+		}
 	}
 
 out:
@@ -488,8 +503,6 @@
 
 static inline struct page * get_page_map(struct page *page)
 {
-	if (!VALID_PAGE(page))
-		return 0;
 	return page;
 }
 
@@ -853,22 +866,21 @@
 	unsigned long phys_addr, pgprot_t prot)
 {
 	unsigned long end;
+	unsigned long pfn;
 
 	address &= ~PMD_MASK;
 	end = address + size;
 	if (end > PMD_SIZE)
 		end = PMD_SIZE;
+	pfn = phys_addr >> PAGE_SHIFT;
 	do {
-		struct page *page;
-		pte_t oldpage;
-		oldpage = ptep_get_and_clear(pte);
-
-		page = virt_to_page(__va(phys_addr));
-		if ((!VALID_PAGE(page)) || PageReserved(page))
- 			set_pte(pte, mk_pte_phys(phys_addr, prot));
+		pte_t oldpage = ptep_get_and_clear(pte);
+
+		if (!pfn_valid(pfn) || PageReserved(pfn_to_page(pfn)))
+ 			set_pte(pte, pfn_pte(pfn, prot));
 		forget_pte(oldpage);
 		address += PAGE_SIZE;
-		phys_addr += PAGE_SIZE;
+		pfn++;
 		pte++;
 	} while (address && (address < end));
 }
@@ -977,10 +989,11 @@
 	unsigned long address, pte_t *page_table, pmd_t *pmd, pte_t pte)
 {
 	struct page *old_page, *new_page;
+	unsigned long pfn = pte_pfn(pte);
 
-	old_page = pte_page(pte);
-	if (!VALID_PAGE(old_page))
+	if (!pfn_valid(pfn))
 		goto bad_wp_page;
+	old_page = pfn_to_page(pfn);
 
 	if (!TestSetPageLocked(old_page)) {
 		int reuse = can_share_swap_page(old_page);
@@ -1029,7 +1042,7 @@
 bad_wp_page:
 	pte_unmap(page_table);
 	spin_unlock(&mm->page_table_lock);
-	printk("do_wp_page: bogus page at address %08lx (page 0x%lx)\n",address,(unsigned long)old_page);
+	printk(KERN_ERR "do_wp_page: bogus page at address %08lx\n", address);
 	return -1;
 no_mem:
 	page_cache_release(old_page);
diff -Nru a/mm/msync.c b/mm/msync.c
--- a/mm/msync.c	Sun May  5 20:39:49 2002
+++ b/mm/msync.c	Sun May  5 20:39:49 2002
@@ -26,10 +26,14 @@
 	pte_t pte = *ptep;
 
 	if (pte_present(pte) && pte_dirty(pte)) {
-		struct page *page = pte_page(pte);
-		if (VALID_PAGE(page) && !PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
-			flush_tlb_page(vma, address);
-			set_page_dirty(page);
+		struct page *page;
+		unsigned long pfn = pte_pfn(pte);
+		if (pfn_valid(pfn)) {
+			page = pfn_to_page(pfn);
+			if (!PageReserved(page) && ptep_test_and_clear_dirty(ptep)) {
+				flush_tlb_page(vma, address);
+				set_page_dirty(page);
+			}
 		}
 	}
 	return 0;
diff -Nru a/mm/page-writeback.c b/mm/page-writeback.c
--- a/mm/page-writeback.c	Sun May  5 20:39:49 2002
+++ b/mm/page-writeback.c	Sun May  5 20:39:49 2002
@@ -354,6 +354,8 @@
 		lock_page(page);
 
 		if (TestClearPageDirty(page)) {
+			if (current->flags & PF_MEMALLOC)
+				SetPageLaunder(page);
 			err = writepage(page);
 			if (!ret)
 				ret = err;
@@ -454,6 +456,9 @@
  *
  * FIXME: may need to call ->reservepage here as well.  That's rather up to the
  * address_space though.
+ *
+ * For now, we treat swapper_space specially.  It doesn't use the normal
+ * block a_ops.
  */
 int __set_page_dirty_buffers(struct page *page)
 {
@@ -470,13 +475,15 @@
 
 	spin_lock(&inode->i_bufferlist_lock);
 
-	if (page_has_buffers(page)) {
+	if (page_has_buffers(page) && !PageSwapCache(page)) {
 		struct buffer_head *head = page_buffers(page);
 		struct buffer_head *bh = head;
 
 		do {
 			if (buffer_uptodate(bh))
 				set_buffer_dirty(bh);
+			else
+				buffer_error();
 			bh = bh->b_this_page;
 		} while (bh != head);
 	}
diff -Nru a/mm/page_alloc.c b/mm/page_alloc.c
--- a/mm/page_alloc.c	Sun May  5 20:39:49 2002
+++ b/mm/page_alloc.c	Sun May  5 20:39:49 2002
@@ -23,6 +23,8 @@
 #include <linux/compiler.h>
 #include <linux/module.h>
 
+unsigned long totalram_pages;
+unsigned long totalhigh_pages;
 int nr_swap_pages;
 int nr_active_pages;
 int nr_inactive_pages;
@@ -101,8 +103,6 @@
 		BUG();
 	if (page->mapping)
 		BUG();
-	if (!VALID_PAGE(page))
-		BUG();
 	if (PageLocked(page))
 		BUG();
 	if (PageLRU(page))
@@ -295,8 +295,6 @@
 						BUG();
 					if (page->mapping)
 						BUG();
-					if (!VALID_PAGE(page))
-						BUG();
 					if (PageLocked(page))
 						BUG();
 					if (PageLRU(page))
@@ -396,8 +394,11 @@
 				return page;
 		}
 nopage:
-		printk("%s: page allocation failure. order:%d, mode:0x%x\n",
-			current->comm, order, gfp_mask);
+		if (!(current->flags & PF_RADIX_TREE)) {
+			printk("%s: page allocation failure."
+				" order:%d, mode:0x%x\n",
+				current->comm, order, gfp_mask);
+		}
 		return NULL;
 	}
 
@@ -477,8 +478,10 @@
 
 void free_pages(unsigned long addr, unsigned int order)
 {
-	if (addr != 0)
+	if (addr != 0) {
+		BUG_ON(!virt_addr_valid(addr));
 		__free_pages(virt_to_page(addr), order);
+	}
 }
 
 /*
@@ -603,6 +606,22 @@
 
 	get_page_state(&ps);
 	return ps.nr_pagecache;
+}
+
+void si_meminfo(struct sysinfo *val)
+{
+	val->totalram = totalram_pages;
+	val->sharedram = 0;
+	val->freeram = nr_free_pages();
+	val->bufferram = atomic_read(&buffermem_pages);
+#ifdef CONFIG_HIGHMEM
+	val->totalhigh = totalhigh_pages;
+	val->freehigh = nr_free_highpages();
+#else
+	val->totalhigh = 0;
+	val->freehigh = 0;
+#endif
+	val->mem_unit = PAGE_SIZE;
 }
 
 #define K(x) ((x) << (PAGE_SHIFT-10))
diff -Nru a/mm/slab.c b/mm/slab.c
--- a/mm/slab.c	Sun May  5 20:39:50 2002
+++ b/mm/slab.c	Sun May  5 20:39:50 2002
@@ -1415,15 +1415,16 @@
 #if DEBUG
 # define CHECK_NR(pg)						\
 	do {							\
-		if (!VALID_PAGE(pg)) {				\
+		if (!virt_addr_valid(pg)) {			\
 			printk(KERN_ERR "kfree: out of range ptr %lxh.\n", \
 				(unsigned long)objp);		\
 			BUG();					\
 		} \
 	} while (0)
-# define CHECK_PAGE(page)					\
+# define CHECK_PAGE(addr)					\
 	do {							\
-		CHECK_NR(page);					\
+		struct page *page = virt_to_page(addr);		\
+		CHECK_NR(addr);					\
 		if (!PageSlab(page)) {				\
 			printk(KERN_ERR "kfree: bad ptr %lxh.\n", \
 				(unsigned long)objp);		\
@@ -1439,7 +1440,7 @@
 {
 	slab_t* slabp;
 
-	CHECK_PAGE(virt_to_page(objp));
+	CHECK_PAGE(objp);
 	/* reduces memory footprint
 	 *
 	if (OPTIMIZE(cachep))
@@ -1519,7 +1520,7 @@
 #ifdef CONFIG_SMP
 	cpucache_t *cc = cc_data(cachep);
 
-	CHECK_PAGE(virt_to_page(objp));
+	CHECK_PAGE(objp);
 	if (cc) {
 		int batchcount;
 		if (cc->avail < cc->limit) {
@@ -1601,7 +1602,7 @@
 {
 	unsigned long flags;
 #if DEBUG
-	CHECK_PAGE(virt_to_page(objp));
+	CHECK_PAGE(objp);
 	if (cachep != GET_PAGE_CACHE(virt_to_page(objp)))
 		BUG();
 #endif
@@ -1626,7 +1627,7 @@
 	if (!objp)
 		return;
 	local_irq_save(flags);
-	CHECK_PAGE(virt_to_page(objp));
+	CHECK_PAGE(objp);
 	c = GET_PAGE_CACHE(virt_to_page(objp));
 	__kmem_cache_free(c, (void*)objp);
 	local_irq_restore(flags);
diff -Nru a/mm/swapfile.c b/mm/swapfile.c
--- a/mm/swapfile.c	Sun May  5 20:39:49 2002
+++ b/mm/swapfile.c	Sun May  5 20:39:49 2002
@@ -308,13 +308,13 @@
 	retval = 0;
 	if (p->swap_map[SWP_OFFSET(entry)] == 1) {
 		/* Recheck the page count with the pagecache lock held.. */
-		read_lock(&swapper_space.page_lock);
+		write_lock(&swapper_space.page_lock);
 		if (page_count(page) - !!PagePrivate(page) == 2) {
 			__delete_from_swap_cache(page);
 			SetPageDirty(page);
 			retval = 1;
 		}
-		read_unlock(&swapper_space.page_lock);
+		write_unlock(&swapper_space.page_lock);
 	}
 	swap_info_put(p);
 
diff -Nru a/mm/vmalloc.c b/mm/vmalloc.c
--- a/mm/vmalloc.c	Sun May  5 20:39:49 2002
+++ b/mm/vmalloc.c	Sun May  5 20:39:49 2002
@@ -45,8 +45,12 @@
 		if (pte_none(page))
 			continue;
 		if (pte_present(page)) {
-			struct page *ptpage = pte_page(page);
-			if (VALID_PAGE(ptpage) && (!PageReserved(ptpage)))
+			struct page *ptpage;
+			unsigned long pfn = pte_pfn(page);
+			if (!pfn_valid(pfn))
+				continue;
+			ptpage = pfn_to_page(pfn);
+			if (!PageReserved(ptpage))
 				__free_page(ptpage);
 			continue;
 		}
diff -Nru a/mm/vmscan.c b/mm/vmscan.c
--- a/mm/vmscan.c	Sun May  5 20:39:49 2002
+++ b/mm/vmscan.c	Sun May  5 20:39:49 2002
@@ -58,6 +58,7 @@
 	int ret;
 
 	current->flags &= ~PF_MEMALLOC;
+	current->flags |= PF_RADIX_TREE;
 	ret = add_to_swap_cache(page, entry);
 	current->flags = flags;
 	return ret;
@@ -216,9 +217,10 @@
 
 	do {
 		if (pte_present(*pte)) {
-			struct page *page = pte_page(*pte);
+			unsigned long pfn = pte_pfn(*pte);
+			struct page *page = pfn_to_page(pfn);
 
-			if (VALID_PAGE(page) && !PageReserved(page)) {
+			if (pfn_valid(pfn) && !PageReserved(page)) {
 				count -= try_to_swap_out(mm, vma, address, pte, page, classzone);
 				if (!count) {
 					address += PAGE_SIZE;
diff -Nru a/net/bluetooth/Config.help b/net/bluetooth/Config.help
--- a/net/bluetooth/Config.help	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/Config.help	Sun May  5 20:39:49 2002
@@ -1,3 +1,4 @@
+Bluetooth subsystem support
 CONFIG_BLUEZ
   Bluetooth is low-cost, low-power, short-range wireless technology.
   It was designed as a replacement for cables and other short-range
@@ -6,11 +7,12 @@
   Bluetooth can be found at <http://www.bluetooth.com/>.
 
   Linux Bluetooth subsystem consist of several layers:
-               HCI Core (device and connection manager, scheduler)
+               BlueZ Core (HCI device and connection manager, scheduler)
                HCI Device drivers (interface to the hardware)
                L2CAP Module (L2CAP protocol)
+               SCO Module (SCO links)
 
-  Say Y here to enable Linux Bluetooth support and to build HCI Core
+  Say Y here to enable Linux Bluetooth support and to build BlueZ Core
   layer.
 
   To use Linux Bluetooth subsystem, you will need several user-space
@@ -18,8 +20,9 @@
   Bluetooth kernel modules are provided in the BlueZ package.
   For more information, see <http://bluez.sourceforge.net/>.
 
-  If you want to compile HCI Core as module (hci.o) say M here.
+  If you want to compile BlueZ Core as module (bluez.o) say M here.
 
+L2CAP protocol support
 CONFIG_BLUEZ_L2CAP
   L2CAP (Logical Link Control and Adaptation Protocol) provides
   connection oriented and connection-less data transport.  L2CAP
@@ -27,4 +30,12 @@
 
   Say Y here to compile L2CAP support into the kernel or say M to
   compile it as module (l2cap.o).
+
+SCO links support
+CONFIG_BLUEZ_SCO
+  SCO link provides voice transport over Bluetooth. SCO support is
+  required for voice applications like Headset and Audio.
+
+  Say Y here to compile SCO support into the kernel or say M to
+  compile it as module (sco.o).
 
diff -Nru a/net/bluetooth/Config.in b/net/bluetooth/Config.in
--- a/net/bluetooth/Config.in	Sun May  5 20:39:50 2002
+++ b/net/bluetooth/Config.in	Sun May  5 20:39:50 2002
@@ -9,6 +9,7 @@
 
    if [ "$CONFIG_BLUEZ" != "n" ]; then
       dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
+      dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
       source drivers/bluetooth/Config.in
    fi
    endmenu
diff -Nru a/net/bluetooth/Makefile b/net/bluetooth/Makefile
--- a/net/bluetooth/Makefile	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/Makefile	Sun May  5 20:39:49 2002
@@ -1,14 +1,17 @@
 #
 # Makefile for the Bluetooth subsystem
 #
-
 O_TARGET	:= bluetooth.o
 
+list-multi	:= bluez.o
 export-objs	:= syms.o
-hci-objs	:= af_bluetooth.o hci_core.o hci_sock.o lib.o syms.o
-l2cap-objs	:= l2cap_core.o l2cap_proc.o
+bluez-objs	:= af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
 
-obj-$(CONFIG_BLUEZ) += hci.o
+obj-$(CONFIG_BLUEZ) += bluez.o
 obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
+obj-$(CONFIG_BLUEZ_SCO) += sco.o
 
 include $(TOPDIR)/Rules.make
+
+bluez.o: $(bluez-objs)
+	$(LD) -r -o $@ $(bluez-objs)
diff -Nru a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
--- a/net/bluetooth/af_bluetooth.c	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/af_bluetooth.c	Sun May  5 20:39:49 2002
@@ -25,14 +25,15 @@
 /*
  * BlueZ Bluetooth address family and sockets.
  *
- * $Id: af_bluetooth.c,v 1.4 2001/07/05 18:42:44 maxk Exp $
+ * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
  */
-#define VERSION "1.1"
+#define VERSION "2.0"
 
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/types.h>
+#include <linux/list.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/major.h>
@@ -40,6 +41,7 @@
 #include <linux/slab.h>
 #include <linux/skbuff.h>
 #include <linux/init.h>
+#include <linux/poll.h>
 #include <linux/proc_fs.h>
 #include <net/sock.h>
 
@@ -48,32 +50,39 @@
 #endif
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
+
+#ifndef AF_BLUETOOTH_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
 
 /* Bluetooth sockets */
-static struct net_proto_family *bluez_sock[BLUEZ_MAX_PROTO];
+#define BLUEZ_MAX_PROTO	4
+static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
+
+static kmem_cache_t *bluez_sock_cache;
 
 int bluez_sock_register(int proto, struct net_proto_family *ops)
 {
-	if (proto > BLUEZ_MAX_PROTO)
+	if (proto >= BLUEZ_MAX_PROTO)
 		return -EINVAL;
 
-	if (bluez_sock[proto])
+	if (bluez_proto[proto])
 		return -EEXIST;
 
-	bluez_sock[proto] = ops;
+	bluez_proto[proto] = ops;
 	return 0;
 }
 
 int bluez_sock_unregister(int proto)
 {
-	if (proto > BLUEZ_MAX_PROTO)
+	if (proto >= BLUEZ_MAX_PROTO)
 		return -EINVAL;
 
-	if (!bluez_sock[proto])
+	if (!bluez_proto[proto])
 		return -ENOENT;
 
-	bluez_sock[proto] = NULL;
+	bluez_proto[proto] = NULL;
 	return 0;
 }
 
@@ -83,27 +92,54 @@
 		return -EINVAL;
 
 #if defined(CONFIG_KMOD)
-	if (!bluez_sock[proto]) {
+	if (!bluez_proto[proto]) {
 		char module_name[30];
 		sprintf(module_name, "bt-proto-%d", proto);
 		request_module(module_name);
 	}
 #endif
 
-	if (!bluez_sock[proto])
+	if (!bluez_proto[proto])
 		return -ENOENT;
 
-	return bluez_sock[proto]->create(sock, proto);
+	return bluez_proto[proto]->create(sock, proto);
+}
+
+struct sock *bluez_sock_alloc(struct socket *sock, int proto, int pi_size, int prio)
+{
+	struct sock *sk;
+	void *pi;
+
+	sk = sk_alloc(PF_BLUETOOTH, prio, sizeof(struct bluez_sock), bluez_sock_cache);
+	if (!sk)
+		return NULL;
+	
+	if (pi_size) {
+		pi = kmalloc(pi_size, prio);
+		if (!pi) {
+			sk_free(sk);
+			return NULL;
+		}
+		memset(pi, 0, pi_size);
+		sk->protinfo = pi;
+	}
+
+	sock_init_data(sock, sk);
+	INIT_LIST_HEAD(&bluez_sk(sk)->accept_q);
+	
+	sk->zapped   = 0;
+	sk->protocol = proto;
+	sk->state    = BT_OPEN;
+
+	return sk;
 }
 
 void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
 {
 	write_lock(&l->lock);
-
 	sk->next = l->head;
 	l->head = sk;
 	sock_hold(sk);
-
 	write_unlock(&l->lock);
 }
 
@@ -122,39 +158,197 @@
 	write_unlock(&l->lock);
 }
 
-struct net_proto_family bluez_sock_family_ops = {
-	family:	PF_BLUETOOTH,
-	create:	bluez_sock_create,
+void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
+{
+	BT_DBG("parent %p, sk %p", parent, sk);
+
+	sock_hold(sk);
+	list_add_tail(&bluez_sk(sk)->accept_q, &bluez_sk(parent)->accept_q);
+	bluez_sk(sk)->parent = parent;
+	parent->ack_backlog++;
+}
+
+static void bluez_accept_unlink(struct sock *sk)
+{
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	list_del_init(&bluez_sk(sk)->accept_q);
+	bluez_sk(sk)->parent->ack_backlog--;
+	bluez_sk(sk)->parent = NULL;
+	sock_put(sk);
+}
+
+struct sock *bluez_accept_dequeue(struct sock *parent, struct socket *newsock)
+{
+	struct list_head *p, *n;
+	struct sock *sk;
+	
+	BT_DBG("parent %p", parent);
+
+	list_for_each_safe(p, n, &bluez_sk(parent)->accept_q) {
+		sk = (struct sock *) list_entry(p, struct bluez_sock, accept_q);
+		
+		lock_sock(sk);
+		if (sk->state == BT_CLOSED) {
+			release_sock(sk);
+			bluez_accept_unlink(sk);
+			continue;
+		}
+		
+		if (sk->state == BT_CONNECTED || !newsock) {
+			bluez_accept_unlink(sk);
+			if (newsock)
+				sock_graft(sk, newsock);
+			release_sock(sk);
+			return sk;
+		}
+		release_sock(sk);
+	}
+	return NULL;
+}
+
+int bluez_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
+{
+	int noblock = flags & MSG_DONTWAIT;
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int copied, err;
+
+	BT_DBG("sock %p sk %p len %d", sock, sk, len);
+
+	if (flags & (MSG_OOB))
+		return -EOPNOTSUPP;
+
+	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err))) {
+		if (sk->shutdown & RCV_SHUTDOWN)
+			return 0;
+		return err;
+	}
+
+	msg->msg_namelen = 0;
+
+	copied = skb->len;
+	if (len < copied) {
+		msg->msg_flags |= MSG_TRUNC;
+		copied = len;
+	}
+
+	skb->h.raw = skb->data;
+	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+	skb_free_datagram(sk, skb);
+
+	return err ? : copied;
+}
+
+unsigned int bluez_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
+{
+	struct sock *sk = sock->sk;
+	unsigned int mask;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	poll_wait(file, sk->sleep, wait);
+	mask = 0;
+
+	if (sk->err || !skb_queue_empty(&sk->error_queue))
+		mask |= POLLERR;
+
+	if (sk->shutdown == SHUTDOWN_MASK)
+		mask |= POLLHUP;
+
+	if (!skb_queue_empty(&sk->receive_queue) || 
+			!list_empty(&bluez_sk(sk)->accept_q) ||
+			(sk->shutdown & RCV_SHUTDOWN))
+		mask |= POLLIN | POLLRDNORM;
+
+	if (sk->state == BT_CLOSED)
+		mask |= POLLHUP;
+
+	if (sock_writeable(sk))
+		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+	else
+		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
+
+	return mask;
+}
+
+int bluez_sock_w4_connect(struct sock *sk, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	add_wait_queue(sk->sleep, &wait);
+	while (sk->state != BT_CONNECTED) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		err = 0;
+		if (sk->state == BT_CONNECTED)
+			break;
+
+		if (sk->err) {
+			err = sock_error(sk);
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+	return err;
+}
+
+struct net_proto_family bluez_sock_family_ops =
+{
+	PF_BLUETOOTH, bluez_sock_create
 };
 
-int bluez_init(void)
+static int __init bluez_init(void)
 {
-	INF("BlueZ HCI Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
+	BT_INFO("BlueZ Core ver %s Copyright (C) 2000,2001 Qualcomm Inc",
 		 VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
 
 	proc_mkdir("bluetooth", NULL);
 
+	/* Init socket cache */
+	bluez_sock_cache = kmem_cache_create("bluez_sock",
+			sizeof(struct bluez_sock), 0,
+			SLAB_HWCACHE_ALIGN, 0, 0);
+
+	if (!bluez_sock_cache) {
+		BT_ERR("BlueZ socket cache creation failed");
+		return -ENOMEM;
+	}
+	
 	sock_register(&bluez_sock_family_ops);
 
-	/* Init HCI Core */
 	hci_core_init();
-
-	/* Init sockets */
 	hci_sock_init();
-
 	return 0;
 }
 
-void bluez_cleanup(void)
+static void __exit bluez_cleanup(void)
 {
-	/* Release socket */
 	hci_sock_cleanup();
-
-	/* Release core */
 	hci_core_cleanup();
 
 	sock_unregister(PF_BLUETOOTH);
+	kmem_cache_destroy(bluez_sock_cache);
 
 	remove_proc_entry("bluetooth", NULL);
 }
@@ -164,5 +358,6 @@
 module_exit(bluez_cleanup);
 
 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ HCI Core ver " VERSION);
+MODULE_DESCRIPTION("BlueZ Core ver " VERSION);
+MODULE_LICENSE("GPL");
 #endif
diff -Nru a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/hci_conn.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,437 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * HCI Connection handling.
+ *
+ * $Id: hci_conn.c,v 1.2 2002/04/17 17:37:16 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef HCI_CORE_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+void hci_acl_connect(struct hci_conn *conn)
+{
+	struct hci_dev *hdev = conn->hdev;
+	struct inquiry_entry *ie;
+	create_conn_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_CONNECT;
+	conn->out   = 1;
+	conn->link_mode = HCI_LM_MASTER;
+
+	memset(&cp, 0, sizeof(cp));
+	bacpy(&cp.bdaddr, &conn->dst);
+
+	if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
+			inquiry_entry_age(ie) > INQUIRY_ENTRY_AGE_MAX) {
+		cp.pscan_rep_mode = ie->info.pscan_rep_mode;
+		cp.pscan_mode     = ie->info.pscan_mode;
+		cp.clock_offset   = ie->info.clock_offset | __cpu_to_le16(0x8000);
+	}
+
+	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK);
+	if (lmp_rswitch_capable(hdev) && !(hdev->link_mode & HCI_LM_MASTER))
+		cp.role_switch	= 0x01;
+	else
+		cp.role_switch	= 0x00;
+		
+	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN,
+				CREATE_CONN_CP_SIZE, &cp);
+}
+
+void hci_acl_disconn(struct hci_conn *conn, __u8 reason)
+{
+	disconnect_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_DISCONN;
+
+	cp.handle = __cpu_to_le16(conn->handle);
+	cp.reason = reason;
+	hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT,
+				DISCONNECT_CP_SIZE, &cp);
+}
+
+void hci_add_sco(struct hci_conn *conn, __u16 handle)
+{
+	struct hci_dev *hdev = conn->hdev;
+	add_sco_cp cp;
+
+	BT_DBG("%p", conn);
+
+	conn->state = BT_CONNECT;
+	conn->out = 1;
+
+	cp.pkt_type = __cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+	cp.handle   = __cpu_to_le16(handle);
+
+	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ADD_SCO, ADD_SCO_CP_SIZE, &cp);
+}
+
+static void hci_conn_timeout(unsigned long arg)
+{
+	struct hci_conn *conn = (void *)arg;
+	struct hci_dev  *hdev = conn->hdev;
+
+	BT_DBG("conn %p state %d", conn, conn->state);
+
+	if (atomic_read(&conn->refcnt))
+		return;
+
+	hci_dev_lock(hdev);
+ 	if (conn->state == BT_CONNECTED)
+		hci_acl_disconn(conn, 0x13);
+	else
+		conn->state = BT_CLOSED;
+	hci_dev_unlock(hdev);
+	return;
+}
+
+static void hci_conn_init_timer(struct hci_conn *conn)
+{
+	init_timer(&conn->timer);
+	conn->timer.function = hci_conn_timeout;
+	conn->timer.data = (unsigned long)conn;
+}
+
+struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
+{
+	struct hci_conn *conn;
+
+	BT_DBG("%s dst %s", hdev->name, batostr(dst));
+
+	if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct hci_conn));
+
+	bacpy(&conn->dst, dst);
+	conn->type   = type;
+	conn->hdev   = hdev;
+	conn->state  = BT_OPEN;
+
+	skb_queue_head_init(&conn->data_q);
+	hci_conn_init_timer(conn);
+
+	atomic_set(&conn->refcnt, 0);
+
+	hci_dev_hold(hdev);
+
+	tasklet_disable(&hdev->tx_task);
+	conn_hash_add(hdev, conn);
+	tasklet_enable(&hdev->tx_task);
+
+	return conn;
+}
+
+int hci_conn_del(struct hci_conn *conn)
+{
+	struct hci_dev  *hdev = conn->hdev;
+
+	BT_DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
+	
+	hci_conn_del_timer(conn);
+
+	if (conn->type == SCO_LINK) {
+		struct hci_conn *acl = conn->link;
+		if (acl) {
+			acl->link = NULL;
+			hci_conn_put(acl);
+		}
+
+		/* Unacked frames */
+		hdev->sco_cnt += conn->sent;
+	} else {
+		struct hci_conn *sco = conn->link;
+		if (sco)
+			sco->link = NULL;
+
+		/* Unacked frames */
+		hdev->acl_cnt += conn->sent;
+	}
+
+	tasklet_disable(&hdev->tx_task);
+	conn_hash_del(hdev, conn);
+	tasklet_enable(&hdev->tx_task);
+
+	skb_queue_purge(&conn->data_q);
+
+	hci_dev_put(hdev);
+
+	kfree(conn);
+	return 0;
+}
+
+struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src)
+{
+	int use_src = bacmp(src, BDADDR_ANY);
+	struct hci_dev *hdev = NULL;
+	struct list_head *p;
+
+	BT_DBG("%s -> %s", batostr(src), batostr(dst));
+
+	spin_lock_bh(&hdev_list_lock);
+
+	list_for_each(p, &hdev_list) {
+		struct hci_dev *d;
+		d = list_entry(p, struct hci_dev, list);
+		
+		if (!test_bit(HCI_UP, &d->flags))
+			continue;
+
+		/* Simple routing: 
+	 	 * 	No source address - find interface with bdaddr != dst 
+	 	 *	Source address 	  - find interface with bdaddr == src 
+	 	 */
+
+		if (use_src) {
+			if (!bacmp(&d->bdaddr, src)) {
+				hdev = d; break;
+			}
+		} else {
+			if (bacmp(&d->bdaddr, dst)) {
+				hdev = d; break;
+			}
+		}
+	}
+
+	if (hdev)
+		hci_dev_hold(hdev);
+
+	spin_unlock_bh(&hdev_list_lock);
+	return hdev;
+}
+
+/* Create SCO or ACL connection.
+ * Device _must_ be locked */
+struct hci_conn * hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst)
+{
+	struct hci_conn *acl;
+
+	BT_DBG("%s dst %s", hdev->name, batostr(dst));
+
+	if (!(acl = conn_hash_lookup_ba(hdev, ACL_LINK, dst))) {
+		if (!(acl = hci_conn_add(hdev, ACL_LINK, dst)))
+			return NULL;
+	}
+
+	hci_conn_hold(acl);
+
+	if (acl->state == BT_OPEN || acl->state == BT_CLOSED)
+		hci_acl_connect(acl);
+
+	if (type == SCO_LINK) {
+		struct hci_conn *sco;
+
+		if (!(sco = conn_hash_lookup_ba(hdev, SCO_LINK, dst))) {
+			if (!(sco = hci_conn_add(hdev, SCO_LINK, dst))) {
+				hci_conn_put(acl);
+				return NULL;
+			}
+		}
+		acl->link = sco;
+		sco->link = acl;
+
+		hci_conn_hold(sco);
+
+		if (acl->state == BT_CONNECTED && 
+				(sco->state == BT_OPEN || sco->state == BT_CLOSED))
+			hci_add_sco(sco, acl->handle);
+
+		return sco;
+	} else {
+		return acl;
+	}
+}
+
+/* Authenticate remote device */
+int hci_conn_auth(struct hci_conn *conn)
+{
+	BT_DBG("conn %p", conn);
+	
+	if (conn->link_mode & HCI_LM_AUTH)
+		return 1;
+	
+	if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+		auth_requested_cp ar;
+		ar.handle = __cpu_to_le16(conn->handle);
+		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_AUTH_REQUESTED,
+				AUTH_REQUESTED_CP_SIZE, &ar);
+	}
+	return 0;
+}
+
+/* Enable encryption */
+int hci_conn_encrypt(struct hci_conn *conn)
+{
+	BT_DBG("conn %p", conn);
+	
+	if (conn->link_mode & HCI_LM_ENCRYPT)
+		return 1;
+	
+	if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+		return 0;
+
+	if (hci_conn_auth(conn)) {
+		set_conn_encrypt_cp ce;
+		ce.handle  = __cpu_to_le16(conn->handle);
+		ce.encrypt = 1; 
+		hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_SET_CONN_ENCRYPT,
+				SET_CONN_ENCRYPT_CP_SIZE, &ce);
+	}
+	return 0;
+}
+
+/* Drop all connection on the device */
+void hci_conn_hash_flush(struct hci_dev *hdev)
+{
+	struct conn_hash *h = &hdev->conn_hash;
+        struct list_head *p;
+
+	BT_DBG("hdev %s", hdev->name);
+
+	p = h->list.next;
+	while (p != &h->list) {
+		struct hci_conn *c;
+
+		c = list_entry(p, struct hci_conn, list);
+		p = p->next;
+
+		c->state = BT_CLOSED;
+
+		hci_proto_disconn_ind(c, 0x16);
+		hci_conn_del(c);
+	}
+}
+
+int hci_get_conn_list(unsigned long arg)
+{
+	struct hci_conn_list_req req, *cl;
+	struct hci_conn_info *ci;
+	struct hci_dev *hdev;
+	struct list_head *p;
+	int n = 0, size;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	if (!(hdev = hci_dev_get(req.dev_id)))
+		return -ENODEV;
+
+	size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
+
+	if (verify_area(VERIFY_WRITE, (void *)arg, size))
+		return -EFAULT;
+
+	if (!(cl = (void *) kmalloc(size, GFP_KERNEL)))
+		return -ENOMEM;
+	ci = cl->conn_info;
+
+	hci_dev_lock_bh(hdev);
+	list_for_each(p, &hdev->conn_hash.list) {
+		register struct hci_conn *c;
+		c = list_entry(p, struct hci_conn, list);
+
+		bacpy(&(ci + n)->bdaddr, &c->dst);
+		(ci + n)->handle = c->handle;
+		(ci + n)->type  = c->type;
+		(ci + n)->out   = c->out;
+		(ci + n)->state = c->state;
+		(ci + n)->link_mode = c->link_mode;
+		n++;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	cl->dev_id = hdev->id;
+	cl->conn_num = n;
+	size = n * sizeof(struct hci_conn_info) + sizeof(req);
+
+	hci_dev_put(hdev);
+
+	copy_to_user((void *) arg, cl, size);
+	kfree(cl);
+
+	return 0;
+}
+
+int hci_get_conn_info(struct hci_dev *hdev, unsigned long arg)
+{
+	struct hci_conn_info_req req;
+	struct hci_conn_info ci;
+	struct hci_conn *conn;
+	char *ptr = (void *) arg + sizeof(req);
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	if (verify_area(VERIFY_WRITE, ptr, sizeof(ci)))
+		return -EFAULT;
+
+	hci_dev_lock_bh(hdev);
+	conn = conn_hash_lookup_ba(hdev, req.type, &req.bdaddr);
+	if (conn) {
+		bacpy(&ci.bdaddr, &conn->dst);
+		ci.handle = conn->handle;
+		ci.type  = conn->type;
+		ci.out   = conn->out;
+		ci.state = conn->state;
+		ci.link_mode = conn->link_mode;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	if (!conn)
+		return -ENOENT;
+
+	copy_to_user(ptr, &ci, sizeof(ci));
+	return 0;
+}
diff -Nru a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
--- a/net/bluetooth/hci_core.c	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/hci_core.c	Sun May  5 20:39:49 2002
@@ -25,7 +25,7 @@
 /*
  * BlueZ HCI Core.
  *
- * $Id: hci_core.c,v 1.22 2001/08/03 04:19:50 maxk Exp $
+ * $Id: hci_core.c,v 1.6 2002/04/17 17:37:16 maxk Exp $
  */
 
 #include <linux/config.h>
@@ -50,12 +50,11 @@
 #include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 #ifndef HCI_CORE_DEBUG
-#undef  DBG
-#define DBG( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
 #endif
 
 static void hci_cmd_task(unsigned long arg);
@@ -63,279 +62,43 @@
 static void hci_tx_task(unsigned long arg);
 static void hci_notify(struct hci_dev *hdev, int event);
 
-static rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
+rwlock_t hci_task_lock = RW_LOCK_UNLOCKED;
 
 /* HCI device list */
-struct hci_dev *hdev_list[HCI_MAX_DEV];
+LIST_HEAD(hdev_list);
 spinlock_t hdev_list_lock;
-#define GET_HDEV(a) (hdev_list[a])
 
-/* HCI protocol list */
-struct hci_proto *hproto_list[HCI_MAX_PROTO];
-#define GET_HPROTO(a) (hproto_list[a])
+/* HCI protocols */
+#define HCI_MAX_PROTO	2
+struct hci_proto *hci_proto[HCI_MAX_PROTO];
 
 /* HCI notifiers list */
-struct notifier_block *hci_dev_notifier;
+static struct notifier_block *hci_notifier;
 
-/* HCI device notifications */
-int hci_register_notifier(struct notifier_block *nb)
-{
-	int err, i;
-	struct hci_dev *hdev;
-
-	if ((err = notifier_chain_register(&hci_dev_notifier, nb)))
-		return err;
 
-	/* Notify about already registered devices */
-	spin_lock(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (!(hdev = GET_HDEV(i)))
-			continue;
-		if (hdev->flags & HCI_UP)
-			(*nb->notifier_call)(nb, HCI_DEV_UP, hdev);
-	}
-	spin_unlock(&hdev_list_lock);
-
-	return 0;
-}
-
-int hci_unregister_notifier(struct notifier_block *nb)
-{
-	return notifier_chain_unregister(&hci_dev_notifier, nb);
-}
+/* ---- HCI notifications ---- */
 
-static inline void hci_notify(struct hci_dev *hdev, int event)
-{
-	notifier_call_chain(&hci_dev_notifier, event, hdev);
-}
-
-/* Get HCI device by index (device is locked on return)*/
-struct hci_dev *hci_dev_get(int index)
-{
-	struct hci_dev *hdev;
-	DBG("%d", index);
-
-	if (index < 0 || index >= HCI_MAX_DEV)
-		return NULL;
-
-	spin_lock(&hdev_list_lock);
-	if ((hdev = GET_HDEV(index)))
-		hci_dev_hold(hdev);
-	spin_unlock(&hdev_list_lock);
-
-	return hdev;
-}
-
-/* Flush inquiry cache */
-void inquiry_cache_flush(struct inquiry_cache *cache)
-{
-	struct inquiry_entry *next = cache->list, *e;
-
-	DBG("cache %p", cache);
-
-	cache->list = NULL;
-	while ((e = next)) {
-		next = e->next;
-		kfree(e);
-	}
-}
-
-/* Lookup by bdaddr.
- * Cache must be locked. */
-static struct inquiry_entry * __inquiry_cache_lookup(struct inquiry_cache *cache, bdaddr_t *bdaddr)
-{
-	struct inquiry_entry *e;
-
-	DBG("cache %p, %s", cache, batostr(bdaddr));
-
-	for (e = cache->list; e; e = e->next)
-		if (!bacmp(&e->info.bdaddr, bdaddr))
-			break;
-
-	return e;
-}
-
-static void inquiry_cache_update(struct inquiry_cache *cache, inquiry_info *info)
-{
-	struct inquiry_entry *e;
-
-	DBG("cache %p, %s", cache, batostr(&info->bdaddr));
-
-	inquiry_cache_lock(cache);
-
-	if (!(e = __inquiry_cache_lookup(cache, &info->bdaddr))) {
-		/* Entry not in the cache. Add new one. */
-		if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
-			goto unlock;
-		memset(e, 0, sizeof(struct inquiry_entry));
-		e->next     = cache->list;
-		cache->list = e;
-	}
-
-	memcpy(&e->info, info, sizeof(inquiry_info));
-	e->timestamp = jiffies;
-	cache->timestamp = jiffies;
-unlock:
-	inquiry_cache_unlock(cache);
-}
-
-static int inquiry_cache_dump(struct inquiry_cache *cache, int num, __u8 *buf)
-{
-	inquiry_info *info = (inquiry_info *) buf;
-	struct inquiry_entry *e;
-	int copied = 0;
-
-	inquiry_cache_lock(cache);
-
-	for (e = cache->list; e && copied < num; e = e->next, copied++)
-		memcpy(info++, &e->info, sizeof(inquiry_info));
-
-	inquiry_cache_unlock(cache);
-
-	DBG("cache %p, copied %d", cache, copied);
-	return copied;
-}
-
-/* --------- BaseBand connections --------- */
-static struct hci_conn *hci_conn_add(struct hci_dev *hdev, __u16 handle, __u8 type, bdaddr_t *dst)
-{
-	struct hci_conn *conn;
-
-	DBG("%s handle %d dst %s", hdev->name, handle, batostr(dst));
-
-	if ( conn_hash_lookup(&hdev->conn_hash, handle)) {
-		ERR("%s handle 0x%x already exists", hdev->name, handle);
-		return NULL;
-	}
-
-	if (!(conn = kmalloc(sizeof(struct hci_conn), GFP_ATOMIC)))
-		return NULL;
-	memset(conn, 0, sizeof(struct hci_conn));
-
-	bacpy(&conn->dst, dst);
-	conn->handle = handle;
-	conn->type   = type;
-	conn->hdev   = hdev;
-
-	skb_queue_head_init(&conn->data_q);
-
-	hci_dev_hold(hdev);
-	conn_hash_add(&hdev->conn_hash, handle, conn);
-
-	return conn;
-}
-
-static int hci_conn_del(struct hci_dev *hdev, struct hci_conn *conn)
-{
-	DBG("%s conn %p handle %d", hdev->name, conn, conn->handle);
-
-	conn_hash_del(&hdev->conn_hash, conn);
-	hci_dev_put(hdev);
-
-	/* Unacked frames */
-	hdev->acl_cnt += conn->sent;
-
-	skb_queue_purge(&conn->data_q);
-
-	kfree(conn);
-	return 0;
-}
-
-/* Drop all connection on the device */
-static void hci_conn_hash_flush(struct hci_dev *hdev)
+int hci_register_notifier(struct notifier_block *nb)
 {
-	struct conn_hash *h = &hdev->conn_hash;
-	struct hci_proto *hp;
-        struct list_head *p;
-
-	DBG("hdev %s", hdev->name);
-
-	p = h->list.next;
-	while (p != &h->list) {
-		struct hci_conn *c;
-
-		c = list_entry(p, struct hci_conn, list);
-		p = p->next;
-
-		if (c->type == ACL_LINK) {
-			/* ACL link notify L2CAP layer */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
-				hp->disconn_ind(c, 0x16);
-		} else {
-			/* SCO link (no notification) */
-		}
-
-		hci_conn_del(hdev, c);
-	}
+	return notifier_chain_register(&hci_notifier, nb);
 }
 
-int hci_connect(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_unregister_notifier(struct notifier_block *nb)
 {
-	struct inquiry_cache *cache = &hdev->inq_cache;
-	struct inquiry_entry *e;
-	create_conn_cp cc;
-	__u16 clock_offset;
-
-	DBG("%s bdaddr %s", hdev->name, batostr(bdaddr));
-
-	if (!(hdev->flags & HCI_UP))
-		return -ENODEV;
-
-	inquiry_cache_lock_bh(cache);
-
-	if (!(e = __inquiry_cache_lookup(cache, bdaddr)) || inquiry_entry_age(e) > INQUIRY_ENTRY_AGE_MAX) {
-		cc.pscan_rep_mode = 0;
-		cc.pscan_mode     = 0;
-		clock_offset      = 0;
-	} else {
-		cc.pscan_rep_mode = e->info.pscan_rep_mode;
-		cc.pscan_mode     = e->info.pscan_mode;
-		clock_offset      = __le16_to_cpu(e->info.clock_offset) & 0x8000;
-	}
-
-	inquiry_cache_unlock_bh(cache);
-
-	bacpy(&cc.bdaddr, bdaddr);
-	cc.pkt_type	= __cpu_to_le16(hdev->pkt_type);
-	cc.clock_offset	= __cpu_to_le16(clock_offset);
-
-	if (lmp_rswitch_capable(hdev))
-		cc.role_switch	= 0x01;
-	else
-		cc.role_switch	= 0x00;
-		
-	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CREATE_CONN, CREATE_CONN_CP_SIZE, &cc);
-
-	return 0;
+	return notifier_chain_unregister(&hci_notifier, nb);
 }
 
-int hci_disconnect(struct hci_conn *conn, __u8 reason)
+void hci_notify(struct hci_dev *hdev, int event)
 {
-	disconnect_cp dc;
-
-	DBG("conn %p handle %d", conn, conn->handle);
-
-	dc.handle = __cpu_to_le16(conn->handle);
-	dc.reason = reason;
-	hci_send_cmd(conn->hdev, OGF_LINK_CTL, OCF_DISCONNECT, DISCONNECT_CP_SIZE, &dc);
-
-	return 0;
+	notifier_call_chain(&hci_notifier, event, hdev);
 }
 
-/* --------- HCI request handling ------------ */
-static inline void hci_req_lock(struct hci_dev *hdev)
-{
-	down(&hdev->req_lock);
-}
 
-static inline void hci_req_unlock(struct hci_dev *hdev)
-{
-	up(&hdev->req_lock);
-}
+/* ---- HCI requests ---- */
 
-static inline void hci_req_complete(struct hci_dev *hdev, int result)
+void hci_req_complete(struct hci_dev *hdev, int result)
 {
-	DBG("%s result 0x%2.2x", hdev->name, result);
+	BT_DBG("%s result 0x%2.2x", hdev->name, result);
 
 	if (hdev->req_status == HCI_REQ_PEND) {
 		hdev->req_result = result;
@@ -344,9 +107,9 @@
 	}
 }
 
-static inline void hci_req_cancel(struct hci_dev *hdev, int err)
+void hci_req_cancel(struct hci_dev *hdev, int err)
 {
-	DBG("%s err 0x%2.2x", hdev->name, err);
+	BT_DBG("%s err 0x%2.2x", hdev->name, err);
 
 	if (hdev->req_status == HCI_REQ_PEND) {
 		hdev->req_result = err;
@@ -356,23 +119,22 @@
 }
 
 /* Execute request and wait for completion. */
-static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt),
-                         unsigned long opt, __u32 timeout)
+static int __hci_request(struct hci_dev *hdev, void (*req)(struct hci_dev *hdev, unsigned long opt), unsigned long opt, __u32 timeout)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	int err = 0;
 
-	DBG("%s start", hdev->name);
+	BT_DBG("%s start", hdev->name);
 
 	hdev->req_status = HCI_REQ_PEND;
 
 	add_wait_queue(&hdev->req_wait_q, &wait);
-	current->state = TASK_INTERRUPTIBLE;
+	set_current_state(TASK_INTERRUPTIBLE);
 
 	req(hdev, opt);
 	schedule_timeout(timeout);
 
-	current->state = TASK_RUNNING;
+	set_current_state(TASK_RUNNING);
 	remove_wait_queue(&hdev->req_wait_q, &wait);
 
 	if (signal_pending(current))
@@ -394,7 +156,7 @@
 
 	hdev->req_status = hdev->req_result = 0;
 
-	DBG("%s end: err %d", hdev->name, err);
+	BT_DBG("%s end: err %d", hdev->name, err);
 
 	return err;
 }
@@ -412,10 +174,9 @@
 	return ret;
 }
 
-/* --------- HCI requests ---------- */
 static void hci_reset_req(struct hci_dev *hdev, unsigned long opt)
 {
-	DBG("%s %ld", hdev->name, opt);
+	BT_DBG("%s %ld", hdev->name, opt);
 
 	/* Reset device */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_RESET, 0, NULL);
@@ -423,10 +184,10 @@
 
 static void hci_init_req(struct hci_dev *hdev, unsigned long opt)
 {
-	set_event_flt_cp ec;
+	set_event_flt_cp ef;
 	__u16 param;
 
-	DBG("%s %ld", hdev->name, opt);
+	BT_DBG("%s %ld", hdev->name, opt);
 
 	/* Mandatory initialization */
 
@@ -436,14 +197,27 @@
 	/* Read Buffer Size (ACL mtu, max pkt, etc.) */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BUFFER_SIZE, 0, NULL);
 
+#if 0
+	/* Host buffer size */
+	{
+		host_buffer_size_cp bs;
+		bs.acl_mtu = __cpu_to_le16(HCI_MAX_ACL_SIZE);
+		bs.sco_mtu = HCI_MAX_SCO_SIZE;
+		bs.acl_max_pkt = __cpu_to_le16(0xffff);
+		bs.sco_max_pkt = __cpu_to_le16(0xffff);
+		hci_send_cmd(hdev, OGF_HOST_CTL, OCF_HOST_BUFFER_SIZE,
+				HOST_BUFFER_SIZE_CP_SIZE, &bs);
+	}
+#endif
+
 	/* Read BD Address */
 	hci_send_cmd(hdev, OGF_INFO_PARAM, OCF_READ_BD_ADDR, 0, NULL);
 
 	/* Optional initialization */
 
 	/* Clear Event Filters */
-	ec.flt_type  = FLT_CLEAR_ALL;
-	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ec);
+	ef.flt_type  = FLT_CLEAR_ALL;
+	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_SET_EVENT_FLT, 1, &ef);
 
 	/* Page timeout ~20 secs */
 	param = __cpu_to_le16(0x8000);
@@ -458,7 +232,7 @@
 {
 	__u8 scan = opt;
 
-	DBG("%s %x", hdev->name, scan);
+	BT_DBG("%s %x", hdev->name, scan);
 
 	/* Inquiry and Page scans */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE, 1, &scan);
@@ -468,27 +242,186 @@
 {
 	__u8 auth = opt;
 
-	DBG("%s %x", hdev->name, auth);
+	BT_DBG("%s %x", hdev->name, auth);
 
 	/* Authentication */
 	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE, 1, &auth);
 }
 
+static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt)
+{
+	__u8 encrypt = opt;
+
+	BT_DBG("%s %x", hdev->name, encrypt);
+
+	/* Authentication */
+	hci_send_cmd(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE, 1, &encrypt);
+}
+
+/* Get HCI device by index. 
+ * Device is locked on return. */
+struct hci_dev *hci_dev_get(int index)
+{
+	struct hci_dev *hdev;
+	struct list_head *p;
+
+	BT_DBG("%d", index);
+
+	if (index < 0)
+		return NULL;
+
+	spin_lock(&hdev_list_lock);
+	list_for_each(p, &hdev_list) {
+		hdev = list_entry(p, struct hci_dev, list);
+		if (hdev->id == index) {
+			hci_dev_hold(hdev);
+			goto done;
+		}
+	}
+	hdev = NULL;
+done:
+	spin_unlock(&hdev_list_lock);
+	return hdev;
+}
+
+/* ---- Inquiry support ---- */
+void inquiry_cache_flush(struct hci_dev *hdev)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *next  = cache->list, *e;
+
+	BT_DBG("cache %p", cache);
+
+	cache->list = NULL;
+	while ((e = next)) {
+		next = e->next;
+		kfree(e);
+	}
+}
+
+struct inquiry_entry *inquiry_cache_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *e;
+
+	BT_DBG("cache %p, %s", cache, batostr(bdaddr));
+
+	for (e = cache->list; e; e = e->next)
+		if (!bacmp(&e->info.bdaddr, bdaddr))
+			break;
+	return e;
+}
+
+void inquiry_cache_update(struct hci_dev *hdev, inquiry_info *info)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	struct inquiry_entry *e;
+
+	BT_DBG("cache %p, %s", cache, batostr(&info->bdaddr));
+
+	if (!(e = inquiry_cache_lookup(hdev, &info->bdaddr))) {
+		/* Entry not in the cache. Add new one. */
+		if (!(e = kmalloc(sizeof(struct inquiry_entry), GFP_ATOMIC)))
+			return;
+		memset(e, 0, sizeof(struct inquiry_entry));
+		e->next     = cache->list;
+		cache->list = e;
+	}
+
+	memcpy(&e->info, info, sizeof(inquiry_info));
+	e->timestamp = jiffies;
+	cache->timestamp = jiffies;
+}
+
+int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf)
+{
+	struct inquiry_cache *cache = &hdev->inq_cache;
+	inquiry_info *info = (inquiry_info *) buf;
+	struct inquiry_entry *e;
+	int copied = 0;
+
+	for (e = cache->list; e && copied < num; e = e->next, copied++)
+		memcpy(info++, &e->info, sizeof(inquiry_info));
+
+	BT_DBG("cache %p, copied %d", cache, copied);
+	return copied;
+}
+
 static void hci_inq_req(struct hci_dev *hdev, unsigned long opt)
 {
 	struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt;
 	inquiry_cp ic;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
+
+	if (test_bit(HCI_INQUIRY, &hdev->flags))
+		return;
 
 	/* Start Inquiry */
 	memcpy(&ic.lap, &ir->lap, 3);
-	ic.lenght  = ir->length;
+	ic.length  = ir->length;
 	ic.num_rsp = ir->num_rsp;
 	hci_send_cmd(hdev, OGF_LINK_CTL, OCF_INQUIRY, INQUIRY_CP_SIZE, &ic);
 }
 
-/* HCI ioctl helpers */
+int hci_inquiry(unsigned long arg)
+{
+	struct hci_inquiry_req ir;
+	struct hci_dev *hdev;
+	int err = 0, do_inquiry = 0;
+	long timeo;
+	__u8 *buf, *ptr;
+
+	ptr = (void *) arg;
+	if (copy_from_user(&ir, ptr, sizeof(ir)))
+		return -EFAULT;
+
+	if (!(hdev = hci_dev_get(ir.dev_id)))
+		return -ENODEV;
+
+	hci_dev_lock_bh(hdev);
+	if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || 
+					ir.flags & IREQ_CACHE_FLUSH) {
+		inquiry_cache_flush(hdev);
+		do_inquiry = 1;
+	}
+	hci_dev_unlock_bh(hdev);
+
+	timeo = ir.length * 2 * HZ;
+	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
+		goto done;
+
+	/* cache_dump can't sleep. Therefore we allocate temp buffer and then
+	 * copy it to the user space.
+	 */
+	if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
+		err = -ENOMEM;
+		goto done;
+	}
+
+	hci_dev_lock_bh(hdev);
+	ir.num_rsp = inquiry_cache_dump(hdev, ir.num_rsp, buf);
+	hci_dev_unlock_bh(hdev);
+
+	BT_DBG("num_rsp %d", ir.num_rsp);
+
+	if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + 
+				(sizeof(inquiry_info) * ir.num_rsp))) {
+		copy_to_user(ptr, &ir, sizeof(ir));
+		ptr += sizeof(ir);
+	        copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
+	} else 
+		err = -EFAULT;
+
+	kfree(buf);
+
+done:
+	hci_dev_put(hdev);
+	return err;
+}
+
+/* ---- HCI ioctl helpers ---- */
+
 int hci_dev_open(__u16 dev)
 {
 	struct hci_dev *hdev;
@@ -497,11 +430,11 @@
 	if (!(hdev = hci_dev_get(dev)))
 		return -ENODEV;
 
-	DBG("%s %p", hdev->name, hdev);
+	BT_DBG("%s %p", hdev->name, hdev);
 
 	hci_req_lock(hdev);
 
-	if (hdev->flags & HCI_UP) {
+	if (test_bit(HCI_UP, &hdev->flags)) {
 		ret = -EALREADY;
 		goto done;
 	}
@@ -511,18 +444,18 @@
 		goto done;
 	}
 
-	if (hdev->flags & HCI_NORMAL) {
+	if (!test_bit(HCI_RAW, &hdev->flags)) {
 		atomic_set(&hdev->cmd_cnt, 1);
-		hdev->flags |= HCI_INIT;
+		set_bit(HCI_INIT, &hdev->flags);
 
 		//__hci_request(hdev, hci_reset_req, 0, HZ);
 		ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT);
        
-		hdev->flags &= ~HCI_INIT;
+		clear_bit(HCI_INIT, &hdev->flags);
 	}
 
 	if (!ret) {
-		hdev->flags |= HCI_UP;
+		set_bit(HCI_UP, &hdev->flags);
 		hci_notify(hdev, HCI_DEV_UP);
 	} else {	
 		/* Init failed, cleanup */
@@ -542,42 +475,36 @@
 		}
 
 		hdev->close(hdev);
+		hdev->flags = 0;
 	}
 
 done:
 	hci_req_unlock(hdev);
 	hci_dev_put(hdev);
-
 	return ret;
 }
 
-int hci_dev_close(__u16 dev)
+static int hci_dev_do_close(struct hci_dev *hdev)
 {
-	struct hci_dev *hdev;
-
-	if (!(hdev = hci_dev_get(dev)))
-		return -ENODEV;
-
-	DBG("%s %p", hdev->name, hdev);
+	BT_DBG("%s %p", hdev->name, hdev);
 
 	hci_req_cancel(hdev, ENODEV);
 	hci_req_lock(hdev);
 
-	if (!(hdev->flags & HCI_UP))
-		goto done;
+	if (!test_and_clear_bit(HCI_UP, &hdev->flags)) {
+		hci_req_unlock(hdev);
+		return 0;
+	}
 
 	/* Kill RX and TX tasks */
 	tasklet_kill(&hdev->rx_task);
 	tasklet_kill(&hdev->tx_task);
 
-	inquiry_cache_flush(&hdev->inq_cache);
-
+	hci_dev_lock_bh(hdev);
+	inquiry_cache_flush(hdev);
 	hci_conn_hash_flush(hdev);
-
-	/* Clear flags */
-	hdev->flags &= HCI_SOCK;
-	hdev->flags |= HCI_NORMAL;
-
+	hci_dev_unlock_bh(hdev);
+	
 	hci_notify(hdev, HCI_DEV_DOWN);
 
 	if (hdev->flush)
@@ -586,9 +513,9 @@
 	/* Reset device */
 	skb_queue_purge(&hdev->cmd_q);
 	atomic_set(&hdev->cmd_cnt, 1);
-	hdev->flags |= HCI_INIT;
-	__hci_request(hdev, hci_reset_req, 0, HZ);
-	hdev->flags &= ~HCI_INIT;
+	set_bit(HCI_INIT, &hdev->flags);
+	__hci_request(hdev, hci_reset_req, 0, HZ/4);
+	clear_bit(HCI_INIT, &hdev->flags);
 
 	/* Kill cmd task */
 	tasklet_kill(&hdev->cmd_task);
@@ -605,17 +532,28 @@
 	}
 
 	/* After this point our queues are empty
-	 * and no tasks are scheduled.
-	 */
+	 * and no tasks are scheduled. */
 	hdev->close(hdev);
 
-done:
-	hci_req_unlock(hdev);
-	hci_dev_put(hdev);
+	/* Clear flags */
+	hdev->flags = 0;
 
+	hci_req_unlock(hdev);
 	return 0;
 }
 
+int hci_dev_close(__u16 dev)
+{
+	struct hci_dev *hdev;
+	int err;
+	
+	if (!(hdev = hci_dev_get(dev)))
+		return -ENODEV;
+	err = hci_dev_do_close(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
 int hci_dev_reset(__u16 dev)
 {
 	struct hci_dev *hdev;
@@ -627,16 +565,17 @@
 	hci_req_lock(hdev);
 	tasklet_disable(&hdev->tx_task);
 
-	if (!(hdev->flags & HCI_UP))
+	if (!test_bit(HCI_UP, &hdev->flags))
 		goto done;
 
 	/* Drop queues */
 	skb_queue_purge(&hdev->rx_q);
 	skb_queue_purge(&hdev->cmd_q);
 
-	inquiry_cache_flush(&hdev->inq_cache);
-
+	hci_dev_lock_bh(hdev);
+	inquiry_cache_flush(hdev);
 	hci_conn_hash_flush(hdev);
+	hci_dev_unlock_bh(hdev);
 
 	if (hdev->flush)
 		hdev->flush(hdev);
@@ -650,7 +589,6 @@
 	tasklet_enable(&hdev->tx_task);
 	hci_req_unlock(hdev);
 	hci_dev_put(hdev);
-
 	return ret;
 }
 
@@ -669,30 +607,11 @@
 	return ret;
 }
 
-int hci_dev_setauth(unsigned long arg)
-{
-	struct hci_dev *hdev;
-	struct hci_dev_req dr;
-	int ret = 0;
-
-	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
-		return -EFAULT;
-
-	if (!(hdev = hci_dev_get(dr.dev_id)))
-		return -ENODEV;
-
-	ret = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
-
-	hci_dev_put(hdev);
-
-	return ret;
-}
-
-int hci_dev_setscan(unsigned long arg)
+int hci_dev_cmd(unsigned int cmd, unsigned long arg)
 {
 	struct hci_dev *hdev;
 	struct hci_dev_req dr;
-	int ret = 0;
+	int err = 0;
 
 	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
 		return -EFAULT;
@@ -700,45 +619,75 @@
 	if (!(hdev = hci_dev_get(dr.dev_id)))
 		return -ENODEV;
 
-	ret = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
-
-	hci_dev_put(hdev);
+	switch (cmd) {
+	case HCISETAUTH:
+		err = hci_request(hdev, hci_auth_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
 
-	return ret;
-}
+	case HCISETENCRYPT:
+		if (!lmp_encrypt_capable(hdev)) {
+			err = -EOPNOTSUPP;
+			break;
+		}
 
-int hci_dev_setptype(unsigned long arg)
-{
-	struct hci_dev *hdev;
-	struct hci_dev_req dr;
-	int ret = 0;
+		if (!test_bit(HCI_AUTH, &hdev->flags)) {
+			/* Auth must be enabled first */
+			err = hci_request(hdev, hci_auth_req,
+					dr.dev_opt, HCI_INIT_TIMEOUT);
+			if (err)
+				break;
+		}
+			
+		err = hci_request(hdev, hci_encrypt_req,
+					dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
+	
+	case HCISETSCAN:
+		err = hci_request(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT);
+		break;
+	
+	case HCISETPTYPE:
+		hdev->pkt_type = (__u16) dr.dev_opt;
+		break;
+		
+	case HCISETLINKPOL:
+		hdev->link_policy = (__u16) dr.dev_opt;
+		break;
 
-	if (copy_from_user(&dr, (void *) arg, sizeof(dr)))
-		return -EFAULT;
+	case HCISETLINKMODE:
+		hdev->link_mode = ((__u16) dr.dev_opt) & (HCI_LM_MASTER | HCI_LM_ACCEPT);
+		break;
 
-	if (!(hdev = hci_dev_get(dr.dev_id)))
-		return -ENODEV;
+	case HCISETACLMTU:
+		hdev->acl_mtu  = *((__u16 *)&dr.dev_opt + 1);
+		hdev->acl_pkts = *((__u16 *)&dr.dev_opt + 0);
+		break;
 
-	hdev->pkt_type = (__u16) dr.dev_opt;
+	case HCISETSCOMTU:
+		hdev->sco_mtu  = *((__u16 *)&dr.dev_opt + 1);
+		hdev->sco_pkts = *((__u16 *)&dr.dev_opt + 0);
+		break;
 
+	default:
+		err = -EINVAL;
+		break;
+	}	
 	hci_dev_put(hdev);
-
-	return ret;
+	return err;
 }
 
-int hci_dev_list(unsigned long arg)
+int hci_get_dev_list(unsigned long arg)
 {
 	struct hci_dev_list_req *dl;
 	struct hci_dev_req *dr;
-	struct hci_dev *hdev;
-	int i, n, size;
+	struct list_head *p;
+	int n = 0, size;
 	__u16 dev_num;
 
 	if (get_user(dev_num, (__u16 *) arg))
 		return -EFAULT;
 
-	/* Avoid long loop, overflow */
-	if (dev_num > 2048)
+	if (!dev_num)
 		return -EINVAL;
 	
 	size = dev_num * sizeof(struct hci_dev_req) + sizeof(__u16);
@@ -751,12 +700,13 @@
 	dr = dl->dev_req;
 
 	spin_lock_bh(&hdev_list_lock);
-	for (i = 0, n = 0; i < HCI_MAX_DEV && n < dev_num; i++) {
-		if ((hdev = hdev_list[i])) {
-			(dr + n)->dev_id  = hdev->id;
-			(dr + n)->dev_opt = hdev->flags;
-			n++;
-		}
+	list_for_each(p, &hdev_list) {
+		struct hci_dev *hdev;
+		hdev = list_entry(p, struct hci_dev, list);
+		(dr + n)->dev_id  = hdev->id;
+		(dr + n)->dev_opt = hdev->flags;
+		if (++n >= dev_num)
+			break;
 	}
 	spin_unlock_bh(&hdev_list_lock);
 
@@ -764,11 +714,12 @@
 	size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
 
 	copy_to_user((void *) arg, dl, size);
+	kfree(dl);
 
 	return 0;
 }
 
-int hci_dev_info(unsigned long arg)
+int hci_get_dev_info(unsigned long arg)
 {
 	struct hci_dev *hdev;
 	struct hci_dev_info di;
@@ -786,9 +737,11 @@
 	di.flags    = hdev->flags;
 	di.pkt_type = hdev->pkt_type;
 	di.acl_mtu  = hdev->acl_mtu;
-	di.acl_max  = hdev->acl_max;
+	di.acl_pkts = hdev->acl_pkts;
 	di.sco_mtu  = hdev->sco_mtu;
-	di.sco_max  = hdev->sco_max;
+	di.sco_pkts = hdev->sco_pkts;
+	di.link_policy = hdev->link_policy;
+	di.link_mode   = hdev->link_mode;
 
 	memcpy(&di.stat, &hdev->stat, sizeof(di.stat));
 	memcpy(&di.features, &hdev->features, sizeof(di.features));
@@ -801,258 +754,149 @@
 	return err;
 }
 
-__u32 hci_dev_setmode(struct hci_dev *hdev, __u32 mode)
-{
-	__u32 omode = hdev->flags & HCI_MODE_MASK;
-
-	hdev->flags &= ~HCI_MODE_MASK;
-	hdev->flags |= (mode & HCI_MODE_MASK);
 
-	return omode;
-}
-
-__u32 hci_dev_getmode(struct hci_dev *hdev)
-{
-	return hdev->flags & HCI_MODE_MASK;
-}
+/* ---- Interface to HCI drivers ---- */
 
-int hci_conn_list(unsigned long arg)
+/* Register HCI device */
+int hci_register_dev(struct hci_dev *hdev)
 {
-	struct hci_conn_list_req req, *cl;
-	struct hci_conn_info *ci;
-	struct hci_dev *hdev;
-	struct list_head *p;
-	int n = 0, size;
+	struct list_head *head = &hdev_list, *p;
+	int id = 0;
 
-	if (copy_from_user(&req, (void *) arg, sizeof(req)))
-		return -EFAULT;
+	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
 
-	if (!(hdev = hci_dev_get(req.dev_id)))
-		return -ENODEV;
-
-	/* Set a limit to avoid overlong loops, and also numeric overflow - AC */
-	if(req.conn_num < 2048)
+	if (!hdev->open || !hdev->close || !hdev->destruct)
 		return -EINVAL;
-	
-	size = req.conn_num * sizeof(struct hci_conn_info) + sizeof(req);
-
-	if (!(cl = kmalloc(size, GFP_KERNEL)))
-		return -ENOMEM;
-	ci = cl->conn_info;
-
-	local_bh_disable();
-	conn_hash_lock(&hdev->conn_hash);
-	list_for_each(p, &hdev->conn_hash.list) {
-		register struct hci_conn *c;
-		c = list_entry(p, struct hci_conn, list);
-
-		(ci + n)->handle = c->handle;
-		bacpy(&(ci + n)->bdaddr, &c->dst);
-		n++;
-	}
-	conn_hash_unlock(&hdev->conn_hash);
-	local_bh_enable();
-
-	cl->dev_id = hdev->id;
-	cl->conn_num = n;
-	size = n * sizeof(struct hci_conn_info) + sizeof(req);
-
-	hci_dev_put(hdev);
-
-	if(copy_to_user((void *) arg, cl, size))
-		return -EFAULT;
-	return 0;
-}
-
-int hci_inquiry(unsigned long arg)
-{
-	struct inquiry_cache *cache;
-	struct hci_inquiry_req ir;
-	struct hci_dev *hdev;
-	int err = 0, do_inquiry = 0;
-	long timeo;
-	__u8 *buf, *ptr;
 
-	ptr = (void *) arg;
-	if (copy_from_user(&ir, ptr, sizeof(ir)))
-		return -EFAULT;
+	spin_lock_bh(&hdev_list_lock);
 
-	if (!(hdev = hci_dev_get(ir.dev_id)))
-		return -ENODEV;
+	/* Find first available device id */
+	list_for_each(p, &hdev_list) {
+	       	if (list_entry(p, struct hci_dev, list)->id != id)
+			break;
+		head = p; id++;
+	}
+	
+	sprintf(hdev->name, "hci%d", id);
+	hdev->id = id;
+	list_add(&hdev->list, head);
 
-	cache = &hdev->inq_cache;
+	atomic_set(&hdev->refcnt, 1);
+	spin_lock_init(&hdev->lock);
+			
+	hdev->flags = 0;
+	hdev->pkt_type  = (HCI_DM1 | HCI_DH1 | HCI_HV1);
+	hdev->link_mode = (HCI_LM_ACCEPT);
 
-	inquiry_cache_lock(cache);
-	if (inquiry_cache_age(cache) > INQUIRY_CACHE_AGE_MAX || ir.flags & IREQ_CACHE_FLUSH) {
-		inquiry_cache_flush(cache);
-		do_inquiry = 1;
-	}
-	inquiry_cache_unlock(cache);
+	tasklet_init(&hdev->cmd_task, hci_cmd_task,(unsigned long) hdev);
+	tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
+	tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
 
-	/* Limit inquiry time, also avoid overflows */
+	skb_queue_head_init(&hdev->rx_q);
+	skb_queue_head_init(&hdev->cmd_q);
+	skb_queue_head_init(&hdev->raw_q);
 
-	if(ir.length > 2048 || ir.num_rsp > 2048)
-	{
-		err = -EINVAL;
-		goto done;
-	}
+	init_waitqueue_head(&hdev->req_wait_q);
+	init_MUTEX(&hdev->req_lock);
 
-	timeo = ir.length * 2 * HZ;
-	if (do_inquiry && (err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo)) < 0)
-		goto done;
+	inquiry_cache_init(hdev);
 
-	/* cache_dump can't sleep. Therefore we allocate temp buffer and then
-	 * copy it to the user space.
-	 */
-	if (!(buf = kmalloc(sizeof(inquiry_info) * ir.num_rsp, GFP_KERNEL))) {
-		err = -ENOMEM;
-		goto done;
-	}
-	ir.num_rsp = inquiry_cache_dump(cache, ir.num_rsp, buf);
+	conn_hash_init(hdev);
 
-	DBG("num_rsp %d", ir.num_rsp);
+	memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
 
-	if (!verify_area(VERIFY_WRITE, ptr, sizeof(ir) + (sizeof(inquiry_info) * ir.num_rsp))) {
-		copy_to_user(ptr, &ir, sizeof(ir));
-		ptr += sizeof(ir);
-	        copy_to_user(ptr, buf, sizeof(inquiry_info) * ir.num_rsp);
-	} else 
-		err = -EFAULT;
+	atomic_set(&hdev->promisc, 0);
+		
+	hci_notify(hdev, HCI_DEV_REG);
 
-	kfree(buf);
+	MOD_INC_USE_COUNT;
 
-done:
-	hci_dev_put(hdev);
+	spin_unlock_bh(&hdev_list_lock);
 
-	return err;
+	return id;
 }
 
-/* Interface to HCI drivers */
-
-/* Register HCI device */
-int hci_register_dev(struct hci_dev *hdev)
+/* Unregister HCI device */
+int hci_unregister_dev(struct hci_dev *hdev)
 {
-	int i;
-
-	DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
+	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
 
-	/* Find free slot */
 	spin_lock_bh(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (!hdev_list[i]) {
-			hdev_list[i] = hdev;
-
-			sprintf(hdev->name, "hci%d", i);
-			atomic_set(&hdev->refcnt, 0);
-			hdev->id    = i;
-			hdev->flags = HCI_NORMAL;
-
-			hdev->pkt_type = (HCI_DM1 | HCI_DH1);
-
-			tasklet_init(&hdev->cmd_task, hci_cmd_task, (unsigned long) hdev);
-			tasklet_init(&hdev->rx_task, hci_rx_task, (unsigned long) hdev);
-			tasklet_init(&hdev->tx_task, hci_tx_task, (unsigned long) hdev);
-
-			skb_queue_head_init(&hdev->rx_q);
-			skb_queue_head_init(&hdev->cmd_q);
-			skb_queue_head_init(&hdev->raw_q);
-
-			init_waitqueue_head(&hdev->req_wait_q);
-			init_MUTEX(&hdev->req_lock);
-
-			inquiry_cache_init(&hdev->inq_cache);
-
-			conn_hash_init(&hdev->conn_hash);
-
-			memset(&hdev->stat, 0, sizeof(struct hci_dev_stats));
+	list_del(&hdev->list);
+	spin_unlock_bh(&hdev_list_lock);
 
-			hci_notify(hdev, HCI_DEV_REG);
+	hci_dev_do_close(hdev);
 
-			MOD_INC_USE_COUNT;
-			break;
-		}
-	}
-	spin_unlock_bh(&hdev_list_lock);
+	hci_notify(hdev, HCI_DEV_UNREG);
+	hci_dev_put(hdev);
 
-	return (i == HCI_MAX_DEV) ? -1 : i;
+	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
-/* Unregister HCI device */
-int hci_unregister_dev(struct hci_dev *hdev)
+/* Receive frame from HCI drivers */
+int hci_recv_frame(struct sk_buff *skb)
 {
-	int i;
-
-	DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
-
-	if (hdev->flags & HCI_UP)
-		hci_dev_close(hdev->id);
+	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
 
-	/* Find device slot */
-	spin_lock(&hdev_list_lock);
-	for (i = 0; i < HCI_MAX_DEV; i++) {
-		if (hdev_list[i] == hdev) {
-			hdev_list[i] = NULL;
-			MOD_DEC_USE_COUNT;
-			break;
-		}
+	if (!hdev || (!test_bit(HCI_UP, &hdev->flags) && 
+				!test_bit(HCI_INIT, &hdev->flags)) ) {
+		kfree_skb(skb);
+		return -1;
 	}
-	spin_unlock(&hdev_list_lock);
 
-	hci_notify(hdev, HCI_DEV_UNREG);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-	/* Sleep while device is in use */
-	while (atomic_read(&hdev->refcnt)) {
-		int sleep_cnt = 100;
+	/* Incomming skb */
+	bluez_cb(skb)->incomming = 1;
 
-		DBG("%s sleeping on lock %d", hdev->name, atomic_read(&hdev->refcnt));
-
-		sleep_on_timeout(&hdev->req_wait_q, HZ*10);
-		if (!(--sleep_cnt))
-			break;
-	}
+	/* Time stamp */
+	do_gettimeofday(&skb->stamp);
 
+	/* Queue frame for rx task */
+	skb_queue_tail(&hdev->rx_q, skb);
+	hci_sched_rx(hdev);
 	return 0;
 }
 
-/* Interface to upper protocols */
+/* ---- Interface to upper protocols ---- */
 
 /* Register/Unregister protocols.
- * hci_task_lock is used to ensure that no tasks are running.
- */
-int hci_register_proto(struct hci_proto *hproto)
+ * hci_task_lock is used to ensure that no tasks are running. */
+int hci_register_proto(struct hci_proto *hp)
 {
 	int err = 0;
 
-	DBG("%p name %s", hproto, hproto->name);
+	BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
 
-	if (hproto->id >= HCI_MAX_PROTO)
+	if (hp->id >= HCI_MAX_PROTO)
 		return -EINVAL;
 
 	write_lock_bh(&hci_task_lock);
 
-	if (!hproto_list[hproto->id])
-		hproto_list[hproto->id] = hproto;
+	if (!hci_proto[hp->id])
+		hci_proto[hp->id] = hp;
 	else
-		err = -1;
+		err = -EEXIST;
 
 	write_unlock_bh(&hci_task_lock);
 
 	return err;
 }
 
-int hci_unregister_proto(struct hci_proto *hproto)
+int hci_unregister_proto(struct hci_proto *hp)
 {
 	int err = 0;
 
-	DBG("%p name %s", hproto, hproto->name);
+	BT_DBG("%p name %s id %d", hp, hp->name, hp->id);
 
-	if (hproto->id > HCI_MAX_PROTO)
+	if (hp->id >= HCI_MAX_PROTO)
 		return -EINVAL;
 
 	write_lock_bh(&hci_task_lock);
 
-	if (hproto_list[hproto->id])
-		hproto_list[hproto->id] = NULL;
+	if (hci_proto[hp->id])
+		hci_proto[hp->id] = NULL;
 	else
 		err = -ENOENT;
 
@@ -1070,10 +914,14 @@
 		return -ENODEV;
 	}
 
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+
+	if (atomic_read(&hdev->promisc)) {
+		/* Time stamp */
+	        do_gettimeofday(&skb->stamp);
 
-	if (hdev->flags & HCI_SOCK)
 		hci_send_to_sock(hdev, skb);
+	}
 
 	/* Get rid of skb owner, prior to sending to the driver. */
 	skb_orphan(skb);
@@ -1081,94 +929,6 @@
 	return hdev->send(skb);
 }
 
-/* Connection scheduler */
-static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
-{
-	struct conn_hash *h = &hdev->conn_hash;
-	struct hci_conn *conn = NULL;
-	int num = 0, min = 0xffff;
-        struct list_head *p;
-
-	conn_hash_lock(h);
-	list_for_each(p, &h->list) {
-		register struct hci_conn *c;
-
-		c = list_entry(p, struct hci_conn, list);
-
-		if (c->type != type || skb_queue_empty(&c->data_q))
-			continue;
-		num++;
-
-		if (c->sent < min) {
-			min  = c->sent;
-			conn = c;
-		}
-	}
-	conn_hash_unlock(h);
-
-	if (conn) {
-		int q = hdev->acl_cnt / num;
-		*quote = q ? q : 1;
-	} else
-		*quote = 0;
-
-	DBG("conn %p quote %d", conn, *quote);
-
-	return conn;
-}
-
-static inline void hci_sched_acl(struct hci_dev *hdev)
-{
-	struct hci_conn *conn;
-	struct sk_buff *skb;
-	int quote;
-
-	DBG("%s", hdev->name);
-
-	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
-		while (quote && (skb = skb_dequeue(&conn->data_q))) {
-			DBG("skb %p len %d", skb, skb->len);
-
-			hci_send_frame(skb);
-
-			conn->sent++;
-			hdev->acl_cnt--;
-			quote--;
-		}
-	}
-}
-
-/* Schedule SCO */
-static inline void hci_sched_sco(struct hci_dev *hdev)
-{
-	/* FIXME: For now we queue SCO packets to the raw queue 
-
-		while (hdev->sco_cnt && (skb = skb_dequeue(&conn->data_q))) {
-			hci_send_frame(skb);
-			conn->sco_sent++;
-			hdev->sco_cnt--;
-		}
-	*/
-}
-
-/* Get data from the previously sent command */
-static void * hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
-{
-	hci_command_hdr *hc;
-
-	if (!hdev->sent_cmd)
-		return NULL;
-
-	hc = (void *) hdev->sent_cmd->data;
-
-	if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
-		return NULL;
-
-	DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
-
-	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
-}
-
 /* Send raw HCI frame */
 int hci_send_raw(struct sk_buff *skb)
 {
@@ -1179,9 +939,9 @@
 		return -ENODEV;
 	}
 
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
+	BT_DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-	if (hdev->flags & HCI_NORMAL) {
+	if (!test_bit(HCI_RAW, &hdev->flags)) {
 		/* Queue frame according it's type */
 		switch (skb->pkt_type) {
 		case HCI_COMMAND_PKT:
@@ -1210,10 +970,10 @@
 	hci_command_hdr *hc;
 	struct sk_buff *skb;
 
-	DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
+	BT_DBG("%s ogf 0x%x ocf 0x%x plen %d", hdev->name, ogf, ocf, plen);
 
 	if (!(skb = bluez_skb_alloc(len, GFP_ATOMIC))) {
-		ERR("%s Can't allocate memory for HCI command", hdev->name);
+		BT_ERR("%s Can't allocate memory for HCI command", hdev->name);
 		return -ENOMEM;
 	}
 	
@@ -1224,7 +984,7 @@
 	if (plen)
 		memcpy(skb_put(skb, plen), param, plen);
 
-	DBG("skb len %d", skb->len);
+	BT_DBG("skb len %d", skb->len);
 
 	skb->pkt_type = HCI_COMMAND_PKT;
 	skb->dev = (void *) hdev;
@@ -1234,10 +994,28 @@
 	return 0;
 }
 
+/* Get data from the previously sent command */
+void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 ogf, __u16 ocf)
+{
+	hci_command_hdr *hc;
+
+	if (!hdev->sent_cmd)
+		return NULL;
+
+	hc = (void *) hdev->sent_cmd->data;
+
+	if (hc->opcode != __cpu_to_le16(cmd_opcode_pack(ogf, ocf)))
+		return NULL;
+
+	BT_DBG("%s ogf 0x%x ocf 0x%x", hdev->name, ogf, ocf);
+
+	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
+}
+
 /* Send ACL data */
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 {
-	int len = skb->len;	
+	int len = skb->len;
 	hci_acl_hdr *ah;
 
 	ah = (hci_acl_hdr *) skb_push(skb, HCI_ACL_HDR_SIZE);
@@ -1252,7 +1030,7 @@
 	struct hci_dev *hdev = conn->hdev;
 	struct sk_buff *list;
 
-	DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
+	BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags);
 
 	skb->dev = (void *) hdev;
 	skb->pkt_type = HCI_ACLDATA_PKT;
@@ -1260,12 +1038,12 @@
 
 	if (!(list = skb_shinfo(skb)->frag_list)) {
 		/* Non fragmented */
-		DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
+		BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len);
 		
 		skb_queue_tail(&conn->data_q, skb);
 	} else {
 		/* Fragmented */
-		DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+		BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
 		skb_shinfo(skb)->frag_list = NULL;
 
@@ -1280,7 +1058,7 @@
 			skb->pkt_type = HCI_ACLDATA_PKT;
 			hci_add_acl_hdr(skb, conn->handle, flags | ACL_CONT);
 		
-			DBG("%s frag %p len %d", hdev->name, skb, skb->len);
+			BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len);
 
 			__skb_queue_tail(&conn->data_q, skb);
 		} while (list);
@@ -1298,7 +1076,7 @@
 	struct hci_dev *hdev = conn->hdev;
 	hci_sco_hdr hs;
 
-	DBG("%s len %d", hdev->name, skb->len);
+	BT_DBG("%s len %d", hdev->name, skb->len);
 
 	if (skb->len > hdev->sco_mtu) {
 		kfree_skb(skb);
@@ -1315,544 +1093,120 @@
 	skb->pkt_type = HCI_SCODATA_PKT;
 	skb_queue_tail(&conn->data_q, skb);
 	hci_sched_tx(hdev);
-
 	return 0;
 }
 
-/* Handle HCI Event packets */
-
-/* Command Complete OGF LINK_CTL  */
-static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF LINK_POLICY  */
-static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s: Command complete: ogf LINK_POLICY ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF HOST_CTL  */
-static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	__u8 status, param;
-	void *sent;
-
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_RESET:
-		status = *((__u8 *) skb->data);
-
-		hci_req_complete(hdev, status);
-		break;
-
-	case OCF_SET_EVENT_FLT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
-		} else {
-			DBG("%s SET_EVENT_FLT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_AUTH_ENABLE:
-		if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE)))
-			break;
-
-		status = *((__u8 *) skb->data);
-		param  = *((__u8 *) sent);
-
-		if (!status) {
-			if (param == AUTH_ENABLED)
-				hdev->flags |= HCI_AUTH;
-			else
-				hdev->flags &= ~HCI_AUTH;
-		}
-		hci_req_complete(hdev, status);
-		break;
-
-	case OCF_WRITE_CA_TIMEOUT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
-		} else {
-			DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_PG_TIMEOUT:
-		status = *((__u8 *) skb->data);
-
-		if (status) {
-			DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
-		} else {
-			DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
-		}
-		break;
-
-	case OCF_WRITE_SCAN_ENABLE:
-		if (!(sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE)))
-			break;
-		status = *((__u8 *) skb->data);
-		param  = *((__u8 *) sent);
-
-		DBG("param 0x%x", param);
-
-		if (!status) {
-			switch (param) {
-			case IS_ENA_PS_ENA:
-				hdev->flags |=  HCI_PSCAN | HCI_ISCAN;
-				break;
-
-			case IS_ENA_PS_DIS:
-				hdev->flags &= ~HCI_PSCAN;
-				hdev->flags |=  HCI_ISCAN;
-				break;
-
-			case IS_DIS_PS_ENA:
-				hdev->flags &= ~HCI_ISCAN;
-				hdev->flags |=  HCI_PSCAN;
-				break;
-
-			default:
-				hdev->flags &= ~(HCI_ISCAN | HCI_PSCAN);
-				break;
-			};
-		}
-		hci_req_complete(hdev, status);
-		break;
+/* ---- HCI TX task (outgoing data) ---- */
 
-	default:
-		DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Complete OGF INFO_PARAM  */
-static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
-{
-	read_local_features_rp *lf;
-	read_buffer_size_rp *bs;
-	read_bd_addr_rp *ba;
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_READ_LOCAL_FEATURES:
-		lf = (read_local_features_rp *) skb->data;
-
-		if (lf->status) {
-			DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
-			break;
-		}
-
-		memcpy(hdev->features, lf->features, sizeof(hdev->features));
-
-		/* Adjust default settings according to features 
-		 * supported by device. */
-		if (hdev->features[0] & LMP_3SLOT)
-			hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
-
-		if (hdev->features[0] & LMP_5SLOT)
-			hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
-
-		DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
-
-		break;
-
-	case OCF_READ_BUFFER_SIZE:
-		bs = (read_buffer_size_rp *) skb->data;
-
-		if (bs->status) {
-			DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
-			break;
-		}
-
-		hdev->acl_mtu = __le16_to_cpu(bs->acl_mtu);
-		hdev->sco_mtu = bs->sco_mtu;
-		hdev->acl_max = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
-		hdev->sco_max = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
-
-		DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
-		    hdev->acl_mtu, hdev->sco_mtu, hdev->acl_max, hdev->sco_max);
-
-		break;
-
-	case OCF_READ_BD_ADDR:
-		ba = (read_bd_addr_rp *) skb->data;
-
-		if (!ba->status) {
-			bacpy(&hdev->bdaddr, &ba->bdaddr);
-		} else {
-			DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
-		}
-
-		hci_req_complete(hdev, ba->status);
-		break;
-
-	default:
-		DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF LINK_CTL  */
-static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+/* HCI Connection scheduler */
+static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote)
 {
-	struct hci_proto * hp;
-
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	case OCF_CREATE_CONN:
-		if (status) {
-			create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
+	struct conn_hash *h = &hdev->conn_hash;
+	struct hci_conn  *conn = NULL;
+	int num = 0, min = 0xffff;
+        struct list_head *p;
 
-			if (!cc)
-				break;
+	/* We don't have to lock device here. Connections are always 
+	 * added and removed with TX task disabled. */
+	list_for_each(p, &h->list) {
+		struct hci_conn *c;
 
-			DBG("%s Create connection error: status 0x%x %s", hdev->name,
-			    status, batostr(&cc->bdaddr));
+		c = list_entry(p, struct hci_conn, list);
 
-			/* Notify upper protocols */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm) {
-				tasklet_disable(&hdev->tx_task);
-				hp->connect_cfm(hdev, &cc->bdaddr, status, NULL);
-				tasklet_enable(&hdev->tx_task);
-			}
-		}
-		break;
+		if (c->type != type || c->state != BT_CONNECTED
+				|| skb_queue_empty(&c->data_q))
+			continue;
+		num++;
 
-	case OCF_INQUIRY:
-		if (status) {
-			DBG("%s Inquiry error: status 0x%x", hdev->name, status);
-			hci_req_complete(hdev, status);
+		if (c->sent < min || type == SCO_LINK) {
+			min  = c->sent;
+			conn = c;
 		}
-		break;
-
-	default:
-		DBG("%s Command status: ogf LINK_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF LINK_POLICY */
-static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF HOST_CTL */
-static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Command Status OGF INFO_PARAM  */
-static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
-{
-	DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
-
-	switch (ocf) {
-	default:
-		DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
-		break;
-	};
-}
-
-/* Inquiry Complete */
-static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	__u8 status = *((__u8 *) skb->data);
+	}
 
-	DBG("%s status %d", hdev->name, status);
+	if (conn) {
+		int q = hdev->acl_cnt / num;
+		*quote = q ? q : 1;
+	} else
+		*quote = 0;
 
-	hci_req_complete(hdev, status);
+	BT_DBG("conn %p quote %d", conn, *quote);
+	return conn;
 }
 
-/* Inquiry Result */
-static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static inline void hci_sched_acl(struct hci_dev *hdev)
 {
-	inquiry_info *info = (inquiry_info *) (skb->data + 1);
-	int num_rsp = *((__u8 *) skb->data);
-
-	DBG("%s num_rsp %d", hdev->name, num_rsp);
+	struct hci_conn *conn;
+	struct sk_buff *skb;
+	int quote;
 
-	for (; num_rsp; num_rsp--)
-		inquiry_cache_update(&hdev->inq_cache, info++);
-}
+	BT_DBG("%s", hdev->name);
 
-/* Connect Request */
-static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	evt_conn_request *cr = (evt_conn_request *) skb->data;
-	struct hci_proto *hp;
-	accept_conn_req_cp ac;
-	int accept = 0;
+	if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > HZ*5) {
+		BT_ERR("%s ACL tx timeout", hdev->name);
+		hdev->acl_cnt++;
+	}
+	
+	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
+		while (quote && (skb = skb_dequeue(&conn->data_q))) {
+			BT_DBG("skb %p len %d", skb, skb->len);
 
-	DBG("%s Connection request: %s type 0x%x", hdev->name, batostr(&cr->bdaddr), cr->link_type);
+			hci_send_frame(skb);
+			hdev->acl_last_tx = jiffies;
 
-	/* Notify upper protocols */
-	if (cr->link_type == ACL_LINK) {
-		/* ACL link notify L2CAP */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_ind) {
-			tasklet_disable(&hdev->tx_task);
-			accept = hp->connect_ind(hdev, &cr->bdaddr);
-			tasklet_enable(&hdev->tx_task);
+			conn->sent++;
+			hdev->acl_cnt--;
+			quote--;
 		}
-	} else {
-		/* SCO link (no notification) */
-		/* FIXME: Should be accept it here or let the requester (app) accept it ? */
-		accept = 1;
-	}
-
-	if (accept) {
-		/* Connection accepted by upper layer */
-		bacpy(&ac.bdaddr, &cr->bdaddr);
-		ac.role = 0x01; /* Remain slave */
-		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, ACCEPT_CONN_REQ_CP_SIZE, &ac);
-	} else {
-		/* Connection rejected by upper layer */
-		/* FIXME: 
-		 * Should we use HCI reject here ?
-		 */
-		return;
 	}
 }
 
-/* Connect Complete */
-static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+/* Schedule SCO */
+static inline void hci_sched_sco(struct hci_dev *hdev)
 {
-	evt_conn_complete *cc = (evt_conn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	struct hci_proto *hp;
-
-	DBG("%s", hdev->name);
-
-	tasklet_disable(&hdev->tx_task);
+	struct hci_conn *conn;
+	struct sk_buff *skb;
+	int quote;
 
-	if (!cc->status)
- 		conn = hci_conn_add(hdev, __le16_to_cpu(cc->handle), cc->link_type, &cc->bdaddr);
+	BT_DBG("%s", hdev->name);
 
-	/* Notify upper protocols */
-	if (cc->link_type == ACL_LINK) {
-		/* ACL link notify L2CAP layer */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->connect_cfm)
-			hp->connect_cfm(hdev, &cc->bdaddr, cc->status, conn);
-	} else {
-		/* SCO link (no notification) */
-	}
+	while ((conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
+		while (quote && (skb = skb_dequeue(&conn->data_q))) {
+			BT_DBG("skb %p len %d", skb, skb->len);
 
-	tasklet_enable(&hdev->tx_task);
-}
+			hci_send_frame(skb);
 
-/* Disconnect Complete */
-static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
-	struct hci_conn *conn = NULL;
-	struct hci_proto *hp;
-	__u16 handle = __le16_to_cpu(dc->handle);
-
-	DBG("%s", hdev->name);
-
-	if (!dc->status && (conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
-		tasklet_disable(&hdev->tx_task);
-
-		/* Notify upper protocols */
-		if (conn->type == ACL_LINK) {
-			/* ACL link notify L2CAP layer */
-			if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->disconn_ind)
-				hp->disconn_ind(conn, dc->reason);
-		} else {
-			/* SCO link (no notification) */
+			//conn->sent++;
+			//hdev->sco_cnt--;
+			quote--;
 		}
-
-		hci_conn_del(hdev, conn);
-
-		tasklet_enable(&hdev->tx_task);
 	}
 }
 
-/* Number of completed packets */
-static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
+static void hci_tx_task(unsigned long arg)
 {
-	evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
-	__u16 *ptr;
-	int i;
-
-	skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
-
-	DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
+	struct hci_dev *hdev = (struct hci_dev *) arg;
+	struct sk_buff *skb;
 
-	if (skb->len < nc->num_hndl * 4) {
-		DBG("%s bad parameters", hdev->name);
-		return;
-	}
+	read_lock(&hci_task_lock);
 
-	tasklet_disable(&hdev->tx_task);
+	BT_DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
 
-	for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
-		struct hci_conn *conn;
-		__u16 handle, count;
+	/* Schedule queues and send stuff to HCI driver */
 
-		handle = __le16_to_cpu(get_unaligned(ptr++));
-		count  = __le16_to_cpu(get_unaligned(ptr++));
+	hci_sched_acl(hdev);
 
-		hdev->acl_cnt += count;
+	hci_sched_sco(hdev);
 
-		if ((conn = conn_hash_lookup(&hdev->conn_hash, handle)))
-			conn->sent -= count;
-	}
+	/* Send next queued raw (unknown type) packet */
+	while ((skb = skb_dequeue(&hdev->raw_q)))
+		hci_send_frame(skb);
 
-	tasklet_enable(&hdev->tx_task);
-	
-	hci_sched_tx(hdev);
+	read_unlock(&hci_task_lock);
 }
 
-static inline void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
-{
-	hci_event_hdr *he = (hci_event_hdr *) skb->data;
-	evt_cmd_status *cs;
-	evt_cmd_complete *ec;
-	__u16 opcode, ocf, ogf;
-
-	skb_pull(skb, HCI_EVENT_HDR_SIZE);
-
-	DBG("%s evt 0x%x", hdev->name, he->evt);
-
-	switch (he->evt) {
-	case EVT_NUM_COMP_PKTS:
-		hci_num_comp_pkts_evt(hdev, skb);
-		break;
-
-	case EVT_INQUIRY_COMPLETE:
-		hci_inquiry_complete_evt(hdev, skb);
-		break;
-
-	case EVT_INQUIRY_RESULT:
-		hci_inquiry_result_evt(hdev, skb);
-		break;
-
-	case EVT_CONN_REQUEST:
-		hci_conn_request_evt(hdev, skb);
-		break;
-
-	case EVT_CONN_COMPLETE:
-		hci_conn_complete_evt(hdev, skb);
-		break;
-
-	case EVT_DISCONN_COMPLETE:
-		hci_disconn_complete_evt(hdev, skb);
-		break;
-
-	case EVT_CMD_STATUS:
-		cs = (evt_cmd_status *) skb->data;
-		skb_pull(skb, EVT_CMD_STATUS_SIZE);
-				
-		opcode = __le16_to_cpu(cs->opcode);
-		ogf = cmd_opcode_ogf(opcode);
-		ocf = cmd_opcode_ocf(opcode);
-
-		switch (ogf) {
-		case OGF_INFO_PARAM:
-			hci_cs_info_param(hdev, ocf, cs->status);
-			break;
 
-		case OGF_HOST_CTL:
-			hci_cs_host_ctl(hdev, ocf, cs->status);
-			break;
-
-		case OGF_LINK_CTL:
-			hci_cs_link_ctl(hdev, ocf, cs->status);
-			break;
-
-		case OGF_LINK_POLICY:
-			hci_cs_link_policy(hdev, ocf, cs->status);
-			break;
-
-		default:
-			DBG("%s Command Status OGF %x", hdev->name, ogf);
-			break;
-		};
-
-		if (cs->ncmd) {
-			atomic_set(&hdev->cmd_cnt, 1);
-			if (!skb_queue_empty(&hdev->cmd_q))
-				hci_sched_cmd(hdev);
-		}
-		break;
-
-	case EVT_CMD_COMPLETE:
-		ec = (evt_cmd_complete *) skb->data;
-		skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
-
-		opcode = __le16_to_cpu(ec->opcode);
-		ogf = cmd_opcode_ogf(opcode);
-		ocf = cmd_opcode_ocf(opcode);
-
-		switch (ogf) {
-		case OGF_INFO_PARAM:
-			hci_cc_info_param(hdev, ocf, skb);
-			break;
-
-		case OGF_HOST_CTL:
-			hci_cc_host_ctl(hdev, ocf, skb);
-			break;
-
-		case OGF_LINK_CTL:
-			hci_cc_link_ctl(hdev, ocf, skb);
-			break;
-
-		case OGF_LINK_POLICY:
-			hci_cc_link_policy(hdev, ocf, skb);
-			break;
-
-		default:
-			DBG("%s Command Completed OGF %x", hdev->name, ogf);
-			break;
-		};
-
-		if (ec->ncmd) {
-			atomic_set(&hdev->cmd_cnt, 1);
-			if (!skb_queue_empty(&hdev->cmd_q))
-				hci_sched_cmd(hdev);
-		}
-		break;
-	};
-
-	kfree_skb(skb);
-	hdev->stat.evt_rx++;
-}
+/* ----- HCI RX task (incomming data proccessing) ----- */
 
 /* ACL data packet */
 static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1867,51 +1221,86 @@
 	flags  = acl_flags(handle);
 	handle = acl_handle(handle);
 
-	DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
+	BT_DBG("%s len %d handle 0x%x flags 0x%x", hdev->name, skb->len, handle, flags);
 
-	if ((conn = conn_hash_lookup(&hdev->conn_hash, handle))) {
+	hdev->stat.acl_rx++;
+
+	hci_dev_lock(hdev);
+	conn = conn_hash_lookup_handle(hdev, handle);
+	hci_dev_unlock(hdev);
+	
+	if (conn) {
 		register struct hci_proto *hp;
 
 		/* Send to upper protocol */
-		if ((hp = GET_HPROTO(HCI_PROTO_L2CAP)) && hp->recv_acldata) {
+		if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
 			hp->recv_acldata(conn, skb, flags);
-			goto sent;
+			return;
 		}
 	} else {
-		ERR("%s ACL packet for unknown connection handle %d", hdev->name, handle);
+		BT_ERR("%s ACL packet for unknown connection handle %d", 
+			hdev->name, handle);
 	}
 
 	kfree_skb(skb);
-sent:
-	hdev->stat.acl_rx++;
 }
 
 /* SCO data packet */
 static inline void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	DBG("%s len %d", hdev->name, skb->len);
+	hci_sco_hdr *sh = (void *) skb->data;
+	struct hci_conn *conn;
+	__u16 handle;
+
+	skb_pull(skb, HCI_SCO_HDR_SIZE);
+
+	handle = __le16_to_cpu(sh->handle);
+
+	BT_DBG("%s len %d handle 0x%x", hdev->name, skb->len, handle);
 
-	kfree_skb(skb);
 	hdev->stat.sco_rx++;
+
+	hci_dev_lock(hdev);
+	conn = conn_hash_lookup_handle(hdev, handle);
+	hci_dev_unlock(hdev);
+	
+	if (conn) {
+		register struct hci_proto *hp;
+
+		/* Send to upper protocol */
+		if ((hp = hci_proto[HCI_PROTO_SCO]) && hp->recv_scodata) {
+			hp->recv_scodata(conn, skb);
+			return;
+		}
+	} else {
+		BT_ERR("%s SCO packet for unknown connection handle %d", 
+			hdev->name, handle);
+	}
+
+	kfree_skb(skb);
 }
 
-/* ----- HCI tasks ----- */
 void hci_rx_task(unsigned long arg)
 {
 	struct hci_dev *hdev = (struct hci_dev *) arg;
 	struct sk_buff *skb;
 
-	DBG("%s", hdev->name);
+	BT_DBG("%s", hdev->name);
 
 	read_lock(&hci_task_lock);
 
 	while ((skb = skb_dequeue(&hdev->rx_q))) {
-		if (hdev->flags & HCI_SOCK) {
+		if (atomic_read(&hdev->promisc)) {
 			/* Send copy to the sockets */
 			hci_send_to_sock(hdev, skb);
 		}
 
-		if (hdev->flags & HCI_INIT) {
+		if (test_bit(HCI_RAW, &hdev->flags)) {
+			kfree_skb(skb);
+			continue;
+		}
+
+		if (test_bit(HCI_INIT, &hdev->flags)) {
 			/* Don't process data packets in this states. */
 			switch (skb->pkt_type) {
 			case HCI_ACLDATA_PKT:
@@ -1921,64 +1310,43 @@
 			};
 		}
 
-		if (hdev->flags & HCI_NORMAL) {
-			/* Process frame */
-			switch (skb->pkt_type) {
-			case HCI_EVENT_PKT:
-				hci_event_packet(hdev, skb);
-				break;
+		/* Process frame */
+		switch (skb->pkt_type) {
+		case HCI_EVENT_PKT:
+			hci_event_packet(hdev, skb);
+			break;
 
-			case HCI_ACLDATA_PKT:
-				DBG("%s ACL data packet", hdev->name);
-				hci_acldata_packet(hdev, skb);
-				break;
+		case HCI_ACLDATA_PKT:
+			BT_DBG("%s ACL data packet", hdev->name);
+			hci_acldata_packet(hdev, skb);
+			break;
 
-			case HCI_SCODATA_PKT:
-				DBG("%s SCO data packet", hdev->name);
-				hci_scodata_packet(hdev, skb);
-				break;
+		case HCI_SCODATA_PKT:
+			BT_DBG("%s SCO data packet", hdev->name);
+			hci_scodata_packet(hdev, skb);
+			break;
 
-			default:
-				kfree_skb(skb);
-				break;
-			};
-		} else {
+		default:
 			kfree_skb(skb);
+			break;
 		}
 	}
 
 	read_unlock(&hci_task_lock);
 }
 
-static void hci_tx_task(unsigned long arg)
-{
-	struct hci_dev *hdev = (struct hci_dev *) arg;
-	struct sk_buff *skb;
-
-	read_lock(&hci_task_lock);
-
-	DBG("%s acl %d sco %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt);
-
-	/* Schedule queues and send stuff to HCI driver */
-
-	hci_sched_acl(hdev);
-
-	hci_sched_sco(hdev);
-
-	/* Send next queued raw (unknown type) packet */
-	while ((skb = skb_dequeue(&hdev->raw_q)))
-		hci_send_frame(skb);
-
-	read_unlock(&hci_task_lock);
-}
-
 static void hci_cmd_task(unsigned long arg)
 {
 	struct hci_dev *hdev = (struct hci_dev *) arg;
 	struct sk_buff *skb;
 
-	DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
+	BT_DBG("%s cmd %d", hdev->name, atomic_read(&hdev->cmd_cnt));
 
+	if (!atomic_read(&hdev->cmd_cnt) && (jiffies - hdev->cmd_last_tx) > HZ) {
+		BT_ERR("%s command tx timeout", hdev->name);
+		atomic_set(&hdev->cmd_cnt, 1);
+	}
+	
 	/* Send queued commands */
 	if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
 		if (hdev->sent_cmd)
@@ -1987,6 +1355,7 @@
 		if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
 			atomic_dec(&hdev->cmd_cnt);
 			hci_send_frame(skb);
+			hdev->cmd_last_tx = jiffies;
 		} else {
 			skb_queue_head(&hdev->cmd_q, skb);
 			hci_sched_cmd(hdev);
@@ -1994,27 +1363,7 @@
 	}
 }
 
-/* Receive frame from HCI drivers */
-int hci_recv_frame(struct sk_buff *skb)
-{
-	struct hci_dev *hdev = (struct hci_dev *) skb->dev;
-
-	if (!hdev || !(hdev->flags & (HCI_UP | HCI_INIT))) {
-		kfree_skb(skb);
-		return -1;
-	}
-
-	DBG("%s type %d len %d", hdev->name, skb->pkt_type, skb->len);
-
-	/* Incomming skb */
-	bluez_cb(skb)->incomming = 1;
-
-	/* Queue frame for rx task */
-	skb_queue_tail(&hdev->rx_q, skb);
-	hci_sched_rx(hdev);
-
-	return 0;
-}
+/* ---- Initialization ---- */
 
 int hci_core_init(void)
 {
@@ -2028,5 +1377,3 @@
 {
 	return 0;
 }
-
-MODULE_LICENSE("GPL");
diff -Nru a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/hci_event.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,874 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * HCI Events.
+ *
+ * $Id: hci_event.c,v 1.3 2002/04/17 17:37:16 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/notifier.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#ifndef HCI_CORE_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+/* Handle HCI Event packets */
+
+/* Command Complete OGF LINK_CTL  */
+static void hci_cc_link_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command complete: ogf LINK_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF LINK_POLICY  */
+static void hci_cc_link_policy(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	struct hci_conn *conn;
+	role_discovery_rp *rd;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_ROLE_DISCOVERY: 
+		rd = (void *) skb->data;
+
+		if (rd->status)
+			break;
+		
+		hci_dev_lock(hdev);
+	
+		conn = conn_hash_lookup_handle(hdev, __le16_to_cpu(rd->handle));
+		if (conn) {
+			if (rd->role)
+				conn->link_mode &= ~HCI_LM_MASTER;
+			else
+				conn->link_mode |= HCI_LM_MASTER;
+		}
+			
+		hci_dev_unlock(hdev);
+		break;
+
+	default:
+		BT_DBG("%s: Command complete: ogf LINK_POLICY ocf %x", 
+				hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF HOST_CTL  */
+static void hci_cc_host_ctl(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	__u8 status, param;
+	void *sent;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_RESET:
+		status = *((__u8 *) skb->data);
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_SET_EVENT_FLT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s SET_EVENT_FLT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s SET_EVENT_FLT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_AUTH_ENABLE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_AUTH_ENABLE);
+		if (!sent)
+			break;
+
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		if (!status) {
+			if (param == AUTH_ENABLED)
+				set_bit(HCI_AUTH, &hdev->flags);
+			else
+				clear_bit(HCI_AUTH, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_WRITE_ENCRYPT_MODE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_ENCRYPT_MODE);
+		if (!sent)
+			break;
+
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		if (!status) {
+			if (param)
+				set_bit(HCI_ENCRYPT, &hdev->flags);
+			else
+				clear_bit(HCI_ENCRYPT, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_WRITE_CA_TIMEOUT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_WRITE_CA_TIMEOUT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s OCF_WRITE_CA_TIMEOUT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_PG_TIMEOUT:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_WRITE_PG_TIMEOUT failed %d", hdev->name, status);
+		} else {
+			BT_DBG("%s: OCF_WRITE_PG_TIMEOUT succeseful", hdev->name);
+		}
+		break;
+
+	case OCF_WRITE_SCAN_ENABLE:
+		sent = hci_sent_cmd_data(hdev, OGF_HOST_CTL, OCF_WRITE_SCAN_ENABLE);
+		if (!sent)
+			break;
+		status = *((__u8 *) skb->data);
+		param  = *((__u8 *) sent);
+
+		BT_DBG("param 0x%x", param);
+
+		if (!status) {
+			clear_bit(HCI_PSCAN, &hdev->flags);
+			clear_bit(HCI_ISCAN, &hdev->flags);
+			if (param & SCAN_INQUIRY) 
+				set_bit(HCI_ISCAN, &hdev->flags);
+
+			if (param & SCAN_PAGE) 
+				set_bit(HCI_PSCAN, &hdev->flags);
+		}
+		hci_req_complete(hdev, status);
+		break;
+
+	case OCF_HOST_BUFFER_SIZE:
+		status = *((__u8 *) skb->data);
+		if (status) {
+			BT_DBG("%s OCF_BUFFER_SIZE failed %d", hdev->name, status);
+			hci_req_complete(hdev, status);
+		}
+		break;
+
+	default:
+		BT_DBG("%s Command complete: ogf HOST_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Complete OGF INFO_PARAM  */
+static void hci_cc_info_param(struct hci_dev *hdev, __u16 ocf, struct sk_buff *skb)
+{
+	read_local_features_rp *lf;
+	read_buffer_size_rp *bs;
+	read_bd_addr_rp *ba;
+
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_READ_LOCAL_FEATURES:
+		lf = (read_local_features_rp *) skb->data;
+
+		if (lf->status) {
+			BT_DBG("%s READ_LOCAL_FEATURES failed %d", hdev->name, lf->status);
+			break;
+		}
+
+		memcpy(hdev->features, lf->features, sizeof(hdev->features));
+
+		/* Adjust default settings according to features 
+		 * supported by device. */
+		if (hdev->features[0] & LMP_3SLOT)
+			hdev->pkt_type |= (HCI_DM3 | HCI_DH3);
+
+		if (hdev->features[0] & LMP_5SLOT)
+			hdev->pkt_type |= (HCI_DM5 | HCI_DH5);
+
+		if (hdev->features[1] & LMP_HV2)
+			hdev->pkt_type |= (HCI_HV2);
+
+		if (hdev->features[1] & LMP_HV3)
+			hdev->pkt_type |= (HCI_HV3);
+
+		BT_DBG("%s: features 0x%x 0x%x 0x%x", hdev->name, lf->features[0], lf->features[1], lf->features[2]);
+
+		break;
+
+	case OCF_READ_BUFFER_SIZE:
+		bs = (read_buffer_size_rp *) skb->data;
+
+		if (bs->status) {
+			BT_DBG("%s READ_BUFFER_SIZE failed %d", hdev->name, bs->status);
+			hci_req_complete(hdev, bs->status);
+			break;
+		}
+
+		hdev->acl_mtu  = __le16_to_cpu(bs->acl_mtu);
+		hdev->sco_mtu  = bs->sco_mtu ? bs->sco_mtu : 64;
+		hdev->acl_pkts = hdev->acl_cnt = __le16_to_cpu(bs->acl_max_pkt);
+		hdev->sco_pkts = hdev->sco_cnt = __le16_to_cpu(bs->sco_max_pkt);
+
+		BT_DBG("%s mtu: acl %d, sco %d max_pkt: acl %d, sco %d", hdev->name,
+		    hdev->acl_mtu, hdev->sco_mtu, hdev->acl_pkts, hdev->sco_pkts);
+		break;
+
+	case OCF_READ_BD_ADDR:
+		ba = (read_bd_addr_rp *) skb->data;
+
+		if (!ba->status) {
+			bacpy(&hdev->bdaddr, &ba->bdaddr);
+		} else {
+			BT_DBG("%s: READ_BD_ADDR failed %d", hdev->name, ba->status);
+		}
+
+		hci_req_complete(hdev, ba->status);
+		break;
+
+	default:
+		BT_DBG("%s Command complete: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF LINK_CTL  */
+static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status)
+{
+	struct hci_conn *conn;
+	create_conn_cp *cc = hci_sent_cmd_data(hdev, OGF_LINK_CTL, OCF_CREATE_CONN);
+
+	if (!cc)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, ACL_LINK, &cc->bdaddr);
+
+	BT_DBG("%s status 0x%x bdaddr %s conn %p", hdev->name, 
+			status, batostr(&cc->bdaddr), conn);
+
+	if (status) {
+		if (conn) {
+			conn->state = BT_CLOSED;
+			hci_proto_connect_cfm(conn, status);
+			hci_conn_del(conn);
+		}
+	} else {
+		if (!conn) {
+			conn = hci_conn_add(hdev, ACL_LINK, &cc->bdaddr);
+			if (conn) {
+				conn->out = 1;
+				conn->link_mode |= HCI_LM_MASTER;
+			} else
+				BT_ERR("No memmory for new connection");
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+static void hci_cs_link_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	case OCF_CREATE_CONN:
+		hci_cs_create_conn(hdev, status);
+		break;
+
+	case OCF_ADD_SCO:
+		if (status) {
+			struct hci_conn *acl, *sco;
+			add_sco_cp *cp = hci_sent_cmd_data(hdev, 
+						OGF_LINK_CTL, OCF_ADD_SCO);
+			__u16 handle;
+
+			if (!cp)
+				break;
+
+			handle = __le16_to_cpu(cp->handle);
+
+			BT_DBG("%s Add SCO error: handle %d status 0x%x", hdev->name, handle, status);
+
+			hci_dev_lock(hdev);
+	
+			acl = conn_hash_lookup_handle(hdev, handle);
+			if (!acl || !(sco = acl->link)) {
+				hci_dev_unlock(hdev);
+				break;
+			}
+
+			sco->state = BT_CLOSED;
+
+			hci_proto_connect_cfm(sco, status);
+			hci_conn_del(sco);
+
+			hci_dev_unlock(hdev);
+		}
+		break;
+
+	case OCF_INQUIRY:
+		if (status) {
+			BT_DBG("%s Inquiry error: status 0x%x", hdev->name, status);
+			hci_req_complete(hdev, status);
+		} else {
+			set_bit(HCI_INQUIRY, &hdev->flags);
+		}
+		break;
+
+	default:
+		BT_DBG("%s Command status: ogf LINK_CTL ocf %x status %d", 
+			hdev->name, ocf, status);
+		break;
+	};
+}
+
+/* Command Status OGF LINK_POLICY */
+static void hci_cs_link_policy(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf HOST_POLICY ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF HOST_CTL */
+static void hci_cs_host_ctl(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf HOST_CTL ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Command Status OGF INFO_PARAM  */
+static void hci_cs_info_param(struct hci_dev *hdev, __u16 ocf, __u8 status)
+{
+	BT_DBG("%s: hci_cs_info_param: ocf 0x%x", hdev->name, ocf);
+
+	switch (ocf) {
+	default:
+		BT_DBG("%s Command status: ogf INFO_PARAM ocf %x", hdev->name, ocf);
+		break;
+	};
+}
+
+/* Inquiry Complete */
+static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	__u8 status = *((__u8 *) skb->data);
+
+	BT_DBG("%s status %d", hdev->name, status);
+
+	clear_bit(HCI_INQUIRY, &hdev->flags);
+	hci_req_complete(hdev, status);
+}
+
+/* Inquiry Result */
+static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	inquiry_info *info = (inquiry_info *) (skb->data + 1);
+	int num_rsp = *((__u8 *) skb->data);
+
+	BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
+
+	hci_dev_lock(hdev);
+	for (; num_rsp; num_rsp--)
+		inquiry_cache_update(hdev, info++);
+	hci_dev_unlock(hdev);
+}
+
+/* Connect Request */
+static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_conn_request *cr = (evt_conn_request *) skb->data;
+	int mask = hdev->link_mode;
+
+	BT_DBG("%s Connection request: %s type 0x%x", hdev->name,
+			batostr(&cr->bdaddr), cr->link_type);
+
+	mask |= hci_proto_connect_ind(hdev, &cr->bdaddr, cr->link_type);
+
+	if (mask & HCI_LM_ACCEPT) {
+		/* Connection accepted */
+		struct hci_conn *conn;
+		accept_conn_req_cp ac;
+
+		hci_dev_lock(hdev);
+		conn = conn_hash_lookup_ba(hdev, cr->link_type, &cr->bdaddr);
+		if (!conn) {
+			if (!(conn = hci_conn_add(hdev, cr->link_type, &cr->bdaddr))) {
+				BT_ERR("No memmory for new connection");
+				hci_dev_unlock(hdev);
+				return;
+			}
+		}
+		conn->state = BT_CONNECT;
+		hci_dev_unlock(hdev);
+
+		bacpy(&ac.bdaddr, &cr->bdaddr);
+	
+		if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER))
+			ac.role = 0x00; /* Become master */
+		else
+			ac.role = 0x01; /* Remain slave */
+
+		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_ACCEPT_CONN_REQ, 
+				ACCEPT_CONN_REQ_CP_SIZE, &ac);
+	} else {
+		/* Connection rejected */
+		reject_conn_req_cp rc;
+
+		bacpy(&rc.bdaddr, &cr->bdaddr);
+		rc.reason = 0x0f;
+		hci_send_cmd(hdev, OGF_LINK_CTL, OCF_REJECT_CONN_REQ,
+				REJECT_CONN_REQ_CP_SIZE, &rc);
+	}
+}
+
+/* Connect Complete */
+static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_conn_complete *cc = (evt_conn_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+
+	BT_DBG("%s", hdev->name);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, cc->link_type, &cc->bdaddr);
+	if (!conn) {
+		hci_dev_unlock(hdev);
+		return;
+	}
+
+	if (!cc->status) {
+		conn->handle = __le16_to_cpu(cc->handle);
+		conn->state  = BT_CONNECTED;
+
+		if (test_bit(HCI_AUTH, &hdev->flags))
+			conn->link_mode |= HCI_LM_AUTH;
+		
+		if (test_bit(HCI_ENCRYPT, &hdev->flags))
+			conn->link_mode |= HCI_LM_ENCRYPT;
+
+
+		/* Set link policy */
+		if (conn->type == ACL_LINK && hdev->link_policy) {
+			write_link_policy_cp lp;
+			lp.handle = cc->handle;
+			lp.policy = __cpu_to_le16(hdev->link_policy);
+			hci_send_cmd(hdev, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
+				WRITE_LINK_POLICY_CP_SIZE, &lp);
+		}
+
+		/* Set packet type for incomming connection */
+		if (!conn->out) {
+			change_conn_ptype_cp cp;
+			cp.handle = cc->handle;
+			cp.pkt_type = (conn->type == ACL_LINK) ? 
+				__cpu_to_le16(hdev->pkt_type & ACL_PTYPE_MASK):
+				__cpu_to_le16(hdev->pkt_type & SCO_PTYPE_MASK);
+
+			hci_send_cmd(hdev, OGF_LINK_CTL, OCF_CHANGE_CONN_PTYPE,
+				CHANGE_CONN_PTYPE_CP_SIZE, &cp);
+		}
+	} else
+		conn->state = BT_CLOSED;
+
+	if (conn->type == ACL_LINK) {
+		struct hci_conn *sco = conn->link;
+		if (sco) {
+			if (!cc->status)
+				hci_add_sco(sco, conn->handle);
+			else {
+				hci_proto_connect_cfm(sco, cc->status);
+				hci_conn_del(sco);
+			}
+		}
+	}
+
+	hci_proto_connect_cfm(conn, cc->status);
+	if (cc->status)
+		hci_conn_del(conn);
+
+	hci_dev_unlock(hdev);
+}
+
+/* Disconnect Complete */
+static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_disconn_complete *dc = (evt_disconn_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(dc->handle);
+
+	BT_DBG("%s status %d", hdev->name, dc->status);
+
+	if (dc->status)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		conn->state = BT_CLOSED;
+		hci_proto_disconn_ind(conn, dc->reason);
+		hci_conn_del(conn);
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Number of completed packets */
+static inline void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_num_comp_pkts *nc = (evt_num_comp_pkts *) skb->data;
+	__u16 *ptr;
+	int i;
+
+	skb_pull(skb, EVT_NUM_COMP_PKTS_SIZE);
+
+	BT_DBG("%s num_hndl %d", hdev->name, nc->num_hndl);
+
+	if (skb->len < nc->num_hndl * 4) {
+		BT_DBG("%s bad parameters", hdev->name);
+		return;
+	}
+
+	tasklet_disable(&hdev->tx_task);
+
+	for (i = 0, ptr = (__u16 *) skb->data; i < nc->num_hndl; i++) {
+		struct hci_conn *conn;
+		__u16  handle, count;
+
+		handle = __le16_to_cpu(get_unaligned(ptr++));
+		count  = __le16_to_cpu(get_unaligned(ptr++));
+
+		conn = conn_hash_lookup_handle(hdev, handle);
+		if (conn) {
+			conn->sent -= count;
+
+			if (conn->type == SCO_LINK) {
+				if ((hdev->sco_cnt += count) > hdev->sco_pkts)
+					hdev->sco_cnt = hdev->sco_pkts;
+			} else {
+				if ((hdev->acl_cnt += count) > hdev->acl_pkts)
+					hdev->acl_cnt = hdev->acl_pkts;
+			}
+		}
+	}
+	hci_sched_tx(hdev);
+
+	tasklet_enable(&hdev->tx_task);
+}
+
+/* Role Change */
+static inline void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_role_change *rc = (evt_role_change *) skb->data;
+	struct hci_conn *conn = NULL;
+
+	BT_DBG("%s status %d", hdev->name, rc->status);
+
+	if (rc->status)
+		return;
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_ba(hdev, ACL_LINK, &rc->bdaddr);
+	if (conn) {
+		if (rc->role)
+			conn->link_mode &= ~HCI_LM_MASTER;
+		else 
+			conn->link_mode |= HCI_LM_MASTER;
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Authentication Complete */
+static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_auth_complete *ac = (evt_auth_complete *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(ac->handle);
+
+	BT_DBG("%s status %d", hdev->name, ac->status);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		if (!ac->status)
+			conn->link_mode |= HCI_LM_AUTH;
+		clear_bit(HCI_CONN_AUTH_PEND, &conn->pend);
+
+		hci_proto_auth_cfm(conn, ac->status);
+		
+		if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend)) {
+			if (!ac->status) {
+				set_conn_encrypt_cp ce;
+				ce.handle  = __cpu_to_le16(conn->handle);
+				ce.encrypt = 1;
+				hci_send_cmd(conn->hdev, OGF_LINK_CTL,
+						OCF_SET_CONN_ENCRYPT,
+						SET_CONN_ENCRYPT_CP_SIZE, &ce);
+			} else {
+				clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+				hci_proto_encrypt_cfm(conn, ac->status);
+			}
+		}
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+/* Encryption Change */
+static inline void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	evt_encrypt_change *ec = (evt_encrypt_change *) skb->data;
+	struct hci_conn *conn = NULL;
+	__u16 handle = __le16_to_cpu(ec->handle);
+
+	BT_DBG("%s status %d", hdev->name, ec->status);
+
+	hci_dev_lock(hdev);
+	
+	conn = conn_hash_lookup_handle(hdev, handle);
+	if (conn) {
+		if (!ec->status) {
+		       	if (ec->encrypt)
+				conn->link_mode |= HCI_LM_ENCRYPT;
+			else
+				conn->link_mode &= ~HCI_LM_ENCRYPT;
+		}
+		clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend);
+		
+		hci_proto_encrypt_cfm(conn, ec->status);
+	}
+
+	hci_dev_unlock(hdev);
+}
+
+void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	hci_event_hdr *he = (hci_event_hdr *) skb->data;
+	evt_cmd_status *cs;
+	evt_cmd_complete *ec;
+	__u16 opcode, ocf, ogf;
+
+	skb_pull(skb, HCI_EVENT_HDR_SIZE);
+
+	BT_DBG("%s evt 0x%x", hdev->name, he->evt);
+
+	switch (he->evt) {
+	case EVT_NUM_COMP_PKTS:
+		hci_num_comp_pkts_evt(hdev, skb);
+		break;
+
+	case EVT_INQUIRY_COMPLETE:
+		hci_inquiry_complete_evt(hdev, skb);
+		break;
+
+	case EVT_INQUIRY_RESULT:
+		hci_inquiry_result_evt(hdev, skb);
+		break;
+
+	case EVT_CONN_REQUEST:
+		hci_conn_request_evt(hdev, skb);
+		break;
+
+	case EVT_CONN_COMPLETE:
+		hci_conn_complete_evt(hdev, skb);
+		break;
+
+	case EVT_DISCONN_COMPLETE:
+		hci_disconn_complete_evt(hdev, skb);
+		break;
+
+	case EVT_ROLE_CHANGE:
+		hci_role_change_evt(hdev, skb);
+		break;
+
+	case EVT_AUTH_COMPLETE:
+		hci_auth_complete_evt(hdev, skb);
+		break;
+
+	case EVT_ENCRYPT_CHANGE:
+		hci_encrypt_change_evt(hdev, skb);
+		break;
+
+	case EVT_CMD_STATUS:
+		cs = (evt_cmd_status *) skb->data;
+		skb_pull(skb, EVT_CMD_STATUS_SIZE);
+				
+		opcode = __le16_to_cpu(cs->opcode);
+		ogf = cmd_opcode_ogf(opcode);
+		ocf = cmd_opcode_ocf(opcode);
+
+		switch (ogf) {
+		case OGF_INFO_PARAM:
+			hci_cs_info_param(hdev, ocf, cs->status);
+			break;
+
+		case OGF_HOST_CTL:
+			hci_cs_host_ctl(hdev, ocf, cs->status);
+			break;
+
+		case OGF_LINK_CTL:
+			hci_cs_link_ctl(hdev, ocf, cs->status);
+			break;
+
+		case OGF_LINK_POLICY:
+			hci_cs_link_policy(hdev, ocf, cs->status);
+			break;
+
+		default:
+			BT_DBG("%s Command Status OGF %x", hdev->name, ogf);
+			break;
+		};
+
+		if (cs->ncmd) {
+			atomic_set(&hdev->cmd_cnt, 1);
+			if (!skb_queue_empty(&hdev->cmd_q))
+				hci_sched_cmd(hdev);
+		}
+		break;
+
+	case EVT_CMD_COMPLETE:
+		ec = (evt_cmd_complete *) skb->data;
+		skb_pull(skb, EVT_CMD_COMPLETE_SIZE);
+
+		opcode = __le16_to_cpu(ec->opcode);
+		ogf = cmd_opcode_ogf(opcode);
+		ocf = cmd_opcode_ocf(opcode);
+
+		switch (ogf) {
+		case OGF_INFO_PARAM:
+			hci_cc_info_param(hdev, ocf, skb);
+			break;
+
+		case OGF_HOST_CTL:
+			hci_cc_host_ctl(hdev, ocf, skb);
+			break;
+
+		case OGF_LINK_CTL:
+			hci_cc_link_ctl(hdev, ocf, skb);
+			break;
+
+		case OGF_LINK_POLICY:
+			hci_cc_link_policy(hdev, ocf, skb);
+			break;
+
+		default:
+			BT_DBG("%s Command Completed OGF %x", hdev->name, ogf);
+			break;
+		};
+
+		if (ec->ncmd) {
+			atomic_set(&hdev->cmd_cnt, 1);
+			if (!skb_queue_empty(&hdev->cmd_q))
+				hci_sched_cmd(hdev);
+		}
+		break;
+	};
+
+	kfree_skb(skb);
+	hdev->stat.evt_rx++;
+}
+
+/* General internal stack event */
+void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
+{
+	hci_event_hdr *eh;
+	evt_stack_internal *si;
+	struct sk_buff *skb;
+	int size;
+	void *ptr;
+
+	size = HCI_EVENT_HDR_SIZE + EVT_STACK_INTERNAL_SIZE + dlen;
+	skb  = bluez_skb_alloc(size, GFP_ATOMIC);
+	if (!skb)
+		return;
+
+	ptr = skb_put(skb, size);
+
+	eh = ptr;
+       	eh->evt  = EVT_STACK_INTERNAL;
+	eh->plen = EVT_STACK_INTERNAL_SIZE + dlen;
+	ptr += HCI_EVENT_HDR_SIZE;
+
+	si = ptr;
+	si->type = type;
+	memcpy(si->data, data, dlen);
+	
+	skb->pkt_type = HCI_EVENT_PKT;
+	skb->dev = (void *) hdev;
+	hci_send_to_sock(hdev, skb);
+	kfree_skb(skb);
+}
diff -Nru a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
--- a/net/bluetooth/hci_sock.c	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/hci_sock.c	Sun May  5 20:39:49 2002
@@ -25,7 +25,7 @@
 /*
  * BlueZ HCI socket layer.
  *
- * $Id: hci_sock.c,v 1.9 2001/08/05 06:02:16 maxk Exp $
+ * $Id: hci_sock.c,v 1.4 2002/04/18 22:26:14 maxk Exp $
  */
 
 #include <linux/config.h>
@@ -51,43 +51,48 @@
 #include <asm/uaccess.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 #ifndef HCI_SOCK_DEBUG
-#undef  DBG
-#define DBG( A... )
+#undef  BT_DBG
+#define BT_DBG( A... )
 #endif
 
-/* HCI socket interface */
+/* ----- HCI socket interface ----- */
+
+/* Security filter */
+static struct hci_sec_filter hci_sec_filter = {
+	/* Packet types */
+	0x10,
+	/* Events */
+	{ 0xd9fe, 0x0 },
+	/* Commands */
+	{
+		/* OGF_LINK_CTL */
+		{ 0x2a000002, 0x0, 0x0, 0x0 },
+		/* OGF_LINK_POLICY */
+		{ 0x1200, 0x0, 0x0, 0x0     },
+		/* OGF_HOST_CTL */
+		{ 0x80100000, 0xa, 0x0, 0x0 },
+		/* OGF_INFO_PARAM */
+		{ 0x22a, 0x0, 0x0, 0x0      }
+	}
+};
 
 static struct bluez_sock_list hci_sk_list = {
 	lock: RW_LOCK_UNLOCKED
 };
 
-static struct sock *hci_sock_lookup(struct hci_dev *hdev)
-{
-	struct sock *sk;
-
-	read_lock(&hci_sk_list.lock);
-	for (sk = hci_sk_list.head; sk; sk = sk->next) {
-		if (hci_pi(sk)->hdev == hdev)
-			break;
-	}
-	read_unlock(&hci_sk_list.lock);
-	return sk;
-}
-
 /* Send frame to RAW socket */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 {
 	struct sock * sk;
 
-	DBG("hdev %p len %d", hdev, skb->len);
+	BT_DBG("hdev %p len %d", hdev, skb->len);
 
 	read_lock(&hci_sk_list.lock);
 	for (sk = hci_sk_list.head; sk; sk = sk->next) {
-		struct hci_filter *flt;	
+		struct hci_filter *flt;
 		struct sk_buff *nskb;
 
 		if (sk->state != BT_BOUND || hci_pi(sk)->hdev != hdev)
@@ -100,13 +105,19 @@
 		/* Apply filter */
 		flt = &hci_pi(sk)->filter;
 
-		if (!test_bit(skb->pkt_type, &flt->type_mask))
+		if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
 			continue;
 
 		if (skb->pkt_type == HCI_EVENT_PKT) {
-			register int evt = (*(__u8 *)skb->data & 63);
+			register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
+			
+			if (!hci_test_bit(evt, &flt->event_mask))
+				continue;
 
-			if (!test_bit(evt, &flt->event_mask))
+			if (flt->opcode && ((evt == EVT_CMD_COMPLETE && 
+					flt->opcode != *(__u16 *)(skb->data + 3)) ||
+					(evt == EVT_CMD_STATUS && 
+					flt->opcode != *(__u16 *)(skb->data + 4))))
 				continue;
 		}
 
@@ -116,8 +127,9 @@
 		/* Put type byte before the data */
 		memcpy(skb_push(nskb, 1), &nskb->pkt_type, 1);
 
-		skb_queue_tail(&sk->receive_queue, nskb);
-		sk->data_ready(sk, nskb->len);
+		if (sock_queue_rcv_skb(sk, nskb))
+			kfree_skb(nskb);
+		
 	}
 	read_unlock(&hci_sk_list.lock);
 }
@@ -127,7 +139,7 @@
 	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = hci_pi(sk)->hdev;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
 
 	if (!sk)
 		return 0;
@@ -135,9 +147,7 @@
 	bluez_sock_unlink(&hci_sk_list, sk);
 
 	if (hdev) {
-		if (!hci_sock_lookup(hdev))
-			hdev->flags &= ~HCI_SOCK;
-
+		atomic_dec(&hdev->promisc);
 		hci_dev_put(hdev);
 	}
 
@@ -149,24 +159,55 @@
 	sock_put(sk);
 
 	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
-static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+/* Ioctls that require bound socket */ 
+static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
 {
-	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = hci_pi(sk)->hdev;
-	__u32 mode;
 
-	DBG("cmd %x arg %lx", cmd, arg);
+	if (!hdev)
+		return -EBADFD;
 
 	switch (cmd) {
-	case HCIGETINFO:
-		return hci_dev_info(arg);
+	case HCISETRAW:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
 
+		if (arg)
+			set_bit(HCI_RAW, &hdev->flags);
+		else
+			clear_bit(HCI_RAW, &hdev->flags);
+
+		return 0;
+
+	case HCIGETCONNINFO:
+		return hci_get_conn_info(hdev, arg);
+
+	default:
+		if (hdev->ioctl)
+			return hdev->ioctl(hdev, cmd, arg);
+		return -EINVAL;
+	}
+}
+
+static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
 	case HCIGETDEVLIST:
-		return hci_dev_list(arg);
+		return hci_get_dev_list(arg);
+
+	case HCIGETDEVINFO:
+		return hci_get_dev_info(arg);
+
+	case HCIGETCONNLIST:
+		return hci_get_conn_list(arg);
 
 	case HCIDEVUP:
 		if (!capable(CAP_NET_ADMIN))
@@ -183,48 +224,31 @@
 			return -EACCES;
 		return hci_dev_reset(arg);
 
-	case HCIRESETSTAT:
+	case HCIDEVRESTAT:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 		return hci_dev_reset_stat(arg);
 
 	case HCISETSCAN:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-		return hci_dev_setscan(arg);
-
 	case HCISETAUTH:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-		return hci_dev_setauth(arg);
-
-	case HCISETRAW:
-		if (!capable(CAP_NET_ADMIN))
-			return -EACCES;
-
-		if (!hdev)
-			return -EBADFD;
-
-		if (arg)
-			mode = HCI_RAW;
-		else
-			mode = HCI_NORMAL;
-
-		return hci_dev_setmode(hdev, mode);
-
+	case HCISETENCRYPT:
 	case HCISETPTYPE:
+	case HCISETLINKPOL:
+	case HCISETLINKMODE:
+	case HCISETACLMTU:
+	case HCISETSCOMTU:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
-		return hci_dev_setptype(arg);
+		return hci_dev_cmd(cmd, arg);
 
 	case HCIINQUIRY:
 		return hci_inquiry(arg);
 
-	case HCIGETCONNLIST:
-		return hci_conn_list(arg);
-
 	default:
-		return -EINVAL;
+		lock_sock(sk);
+		err = hci_sock_bound_ioctl(sk, cmd, arg);
+		release_sock(sk);
+		return err;
 	};
 }
 
@@ -233,28 +257,35 @@
 	struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
 	struct sock *sk = sock->sk;
 	struct hci_dev *hdev = NULL;
+	int err = 0;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
 
 	if (!haddr || haddr->hci_family != AF_BLUETOOTH)
 		return -EINVAL;
 
+	lock_sock(sk);
+
 	if (hci_pi(sk)->hdev) {
-		/* Already bound */
-		return 0;
+		err = -EALREADY;
+		goto done;
 	}
 
 	if (haddr->hci_dev != HCI_DEV_NONE) {
-		if (!(hdev = hci_dev_get(haddr->hci_dev)))
-			return -ENODEV;
+		if (!(hdev = hci_dev_get(haddr->hci_dev))) {
+			err = -ENODEV;
+			goto done;
+		}
 
-		hdev->flags |= HCI_SOCK;
+		atomic_inc(&hdev->promisc);
 	}
 
 	hci_pi(sk)->hdev = hdev;
 	sk->state = BT_BOUND;
 
-	return 0;
+done:
+	release_sock(sk);
+	return err;
 }
 
 static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int peer)
@@ -262,73 +293,44 @@
 	struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr;
 	struct sock *sk = sock->sk;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	lock_sock(sk);
 
 	*addr_len = sizeof(*haddr);
 	haddr->hci_family = AF_BLUETOOTH;
 	haddr->hci_dev    = hci_pi(sk)->hdev->id;
 
+	release_sock(sk);
 	return 0;
 }
 
-static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
-                            struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	struct hci_dev *hdev = hci_pi(sk)->hdev;
-	struct sk_buff *skb;
-	int err;
-
-	DBG("sock %p sk %p", sock, sk);
-
-	if (msg->msg_flags & MSG_OOB)
-		return -EOPNOTSUPP;
-
-	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
-		return -EINVAL;
-
-	if (!hdev)
-		return -EBADFD;
-
-	if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
-		return err;
-
-	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
-		kfree_skb(skb);
-		return -EFAULT;
-	}
-
-	skb->dev = (void *) hdev;
-	skb->pkt_type = *((unsigned char *) skb->data);
-	skb_pull(skb, 1);
-
-	/* Send frame to HCI core */
-	hci_send_raw(skb);
-
-	return len;
-}
-
 static inline void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
 {
 	__u32 mask = hci_pi(sk)->cmsg_mask;
 
 	if (mask & HCI_CMSG_DIR)
         	put_cmsg(msg, SOL_HCI, HCI_CMSG_DIR, sizeof(int), &bluez_cb(skb)->incomming);
+
+	if (mask & HCI_CMSG_TSTAMP)
+        	put_cmsg(msg, SOL_HCI, HCI_CMSG_TSTAMP, sizeof(skb->stamp), &skb->stamp);
 }
  
-static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len,
-                            int flags, struct scm_cookie *scm)
+static int hci_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
 {
 	int noblock = flags & MSG_DONTWAIT;
 	struct sock *sk = sock->sk;
 	struct sk_buff *skb;
 	int copied, err;
 
-	DBG("sock %p sk %p", sock, sk);
+	BT_DBG("sock %p, sk %p", sock, sk);
 
-	if (flags & (MSG_OOB | MSG_PEEK))
+	if (flags & (MSG_OOB))
 		return -EOPNOTSUPP;
 
+	if (sk->state == BT_CLOSED)
+		return 0;
+
 	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
 		return err;
 
@@ -343,28 +345,95 @@
 	skb->h.raw = skb->data;
 	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
 
-	if (hci_pi(sk)->cmsg_mask)
-		hci_sock_cmsg(sk, msg, skb);
-
+	hci_sock_cmsg(sk, msg, skb);
+	
 	skb_free_datagram(sk, skb);
 
 	return err ? : copied;
 }
 
+static int hci_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+                            struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct hci_dev *hdev;
+	struct sk_buff *skb;
+	int err;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+		return -EINVAL;
+
+	if (len < 4)
+		return -EINVAL;
+	
+	lock_sock(sk);
+
+	if (!(hdev = hci_pi(sk)->hdev)) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	if (!(skb = bluez_skb_send_alloc(sk, len, msg->msg_flags & MSG_DONTWAIT, &err)))
+		goto done;
+
+	if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+		err = -EFAULT;
+		goto drop;
+	}
+
+	skb->pkt_type = *((unsigned char *) skb->data);
+	skb_pull(skb, 1);
+
+	if (!capable(CAP_NET_RAW)) {
+		err = -EPERM;
+
+		if (skb->pkt_type == HCI_COMMAND_PKT) {
+			__u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
+			__u16 ogf = cmd_opcode_ogf(opcode) - 1;
+			__u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
+
+			if (ogf > HCI_SFLT_MAX_OGF ||
+					!hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf]))
+				goto drop;
+		} else
+			goto drop;
+	}
+		
+	/* Send frame to HCI core */
+	skb->dev = (void *) hdev;
+	hci_send_raw(skb);
+	err = len;
+
+done:
+	release_sock(sk);
+	return err;
+
+drop:
+	kfree_skb(skb);
+	goto done;
+}
+
 int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
 {
 	struct sock *sk = sock->sk;
-	struct hci_filter flt;
+	struct hci_filter flt = { opcode: 0 };
 	int err = 0, opt = 0;
 
-	DBG("sk %p, opt %d", sk, optname);
+	BT_DBG("sk %p, opt %d", sk, optname);
 
 	lock_sock(sk);
 
 	switch (optname) {
 	case HCI_DATA_DIR:
-		if (get_user(opt, (int *)optval))
-			return -EFAULT;
+		if (get_user(opt, (int *)optval)) {
+			err = -EFAULT;
+			break;
+		}
 
 		if (opt)
 			hci_pi(sk)->cmsg_mask |= HCI_CMSG_DIR;
@@ -372,12 +441,31 @@
 			hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_DIR;
 		break;
 
+	case HCI_TIME_STAMP:
+		if (get_user(opt, (int *)optval)) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (opt)
+			hci_pi(sk)->cmsg_mask |= HCI_CMSG_TSTAMP;
+		else
+			hci_pi(sk)->cmsg_mask &= ~HCI_CMSG_TSTAMP;
+		break;
+
 	case HCI_FILTER:
 		len = MIN(len, sizeof(struct hci_filter));
 		if (copy_from_user(&flt, optval, len)) {
 			err = -EFAULT;
 			break;
 		}
+
+		if (!capable(CAP_NET_RAW)) {
+			flt.type_mask     &= hci_sec_filter.type_mask;
+			flt.event_mask[0] &= hci_sec_filter.event_mask[0];
+			flt.event_mask[1] &= hci_sec_filter.event_mask[1];
+		}
+		
 		memcpy(&hci_pi(sk)->filter, &flt, len);
 		break;
 
@@ -409,6 +497,16 @@
 			return -EFAULT;
 		break;
 
+	case HCI_TIME_STAMP:
+		if (hci_pi(sk)->cmsg_mask & HCI_CMSG_TSTAMP)
+			opt = 1;
+		else 
+			opt = 0;
+
+		if (put_user(opt, optval))
+			return -EFAULT;
+		break;
+
 	case HCI_FILTER:
 		len = MIN(len, sizeof(struct hci_filter));
 		if (copy_to_user(optval, &hci_pi(sk)->filter, len))
@@ -446,62 +544,45 @@
 {
 	struct sock *sk;
 
-	DBG("sock %p", sock);
+	BT_DBG("sock %p", sock);
 
 	if (sock->type != SOCK_RAW)
 		return -ESOCKTNOSUPPORT;
 
 	sock->ops = &hci_sock_ops;
 
-	if (!(sk = sk_alloc(PF_BLUETOOTH, GFP_KERNEL, 1, NULL)))
+	sk = bluez_sock_alloc(sock, protocol, sizeof(struct hci_pinfo), GFP_KERNEL);
+	if (!sk)
 		return -ENOMEM;
 
 	sock->state = SS_UNCONNECTED;
-	sock_init_data(sock, sk);
-
-	memset(&sk->protinfo, 0, sizeof(struct hci_pinfo));
-	sk->destruct = NULL;
-	sk->protocol = protocol;
-	sk->state    = BT_OPEN;
-
-	/* Initialize filter */
-	hci_pi(sk)->filter.type_mask  = (1<<HCI_EVENT_PKT);
-	hci_pi(sk)->filter.event_mask[0] = ~0L;
-	hci_pi(sk)->filter.event_mask[1] = ~0L;
+	sk->state   = BT_OPEN;
 
 	bluez_sock_link(&hci_sk_list, sk);
 
 	MOD_INC_USE_COUNT;
-
 	return 0;
 }
 
 static int hci_sock_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
 	struct hci_dev *hdev = (struct hci_dev *) ptr;
-	struct sk_buff *skb;
-
-	DBG("hdev %s event %ld", hdev->name, event);
+	evt_si_device sd;
+	
+	BT_DBG("hdev %s event %ld", hdev->name, event);
 
 	/* Send event to sockets */
-	if ((skb = bluez_skb_alloc(HCI_EVENT_HDR_SIZE + EVT_HCI_DEV_EVENT_SIZE, GFP_ATOMIC))) {
-		hci_event_hdr eh = { EVT_HCI_DEV_EVENT, EVT_HCI_DEV_EVENT_SIZE };
-		evt_hci_dev_event he = { event, hdev->id };
-
-		skb->pkt_type = HCI_EVENT_PKT;
-		memcpy(skb_put(skb, HCI_EVENT_HDR_SIZE), &eh, HCI_EVENT_HDR_SIZE);
-		memcpy(skb_put(skb, EVT_HCI_DEV_EVENT_SIZE), &he, EVT_HCI_DEV_EVENT_SIZE);
-
-		hci_send_to_sock(NULL, skb);
-		kfree_skb(skb);
-	}
-
+	sd.event  = event;
+	sd.dev_id = hdev->id;
+	hci_si_event(NULL, EVT_SI_DEVICE, EVT_SI_DEVICE_SIZE, &sd);
+	
 	if (event == HCI_DEV_UNREG) {
 		struct sock *sk;
 
 		/* Detach sockets from device */
 		read_lock(&hci_sk_list.lock);
 		for (sk = hci_sk_list.head; sk; sk = sk->next) {
+			bh_lock_sock(sk);
 			if (hci_pi(sk)->hdev == hdev) {
 				hci_pi(sk)->hdev = NULL;
 				sk->err = EPIPE;
@@ -510,6 +591,7 @@
 
 				hci_dev_put(hdev);
 			}
+			bh_unlock_sock(sk);
 		}
 		read_unlock(&hci_sk_list.lock);
 	}
@@ -519,7 +601,7 @@
 
 struct net_proto_family hci_sock_family_ops = {
 	family: PF_BLUETOOTH,
-	create: hci_sock_create,
+	create: hci_sock_create
 };
 
 struct notifier_block hci_sock_nblock = {
@@ -529,21 +611,19 @@
 int hci_sock_init(void)
 {
 	if (bluez_sock_register(BTPROTO_HCI, &hci_sock_family_ops)) {
-		ERR("Can't register HCI socket");
+		BT_ERR("Can't register HCI socket");
 		return -EPROTO;
 	}
 
 	hci_register_notifier(&hci_sock_nblock);
-
 	return 0;
 }
 
 int hci_sock_cleanup(void)
 {
 	if (bluez_sock_unregister(BTPROTO_HCI))
-		ERR("Can't unregister HCI socket");
+		BT_ERR("Can't unregister HCI socket");
 
 	hci_unregister_notifier(&hci_sock_nblock);
-
 	return 0;
 }
diff -Nru a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/l2cap.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,2048 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ L2CAP core and sockets.
+ *
+ * $Id: l2cap.c,v 1.8 2002/04/19 00:01:39 maxk Exp $
+ */
+#define VERSION "2.0"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#ifndef L2CAP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static struct proto_ops l2cap_sock_ops;
+
+struct bluez_sock_list l2cap_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static int l2cap_conn_del(struct hci_conn *conn, int err);
+
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
+static void l2cap_chan_del(struct sock *sk, int err);
+static int  l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
+
+static void __l2cap_sock_close(struct sock *sk, int reason);
+static void l2cap_sock_close(struct sock *sk);
+static void l2cap_sock_kill(struct sock *sk);
+
+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
+
+/* ----- L2CAP timers ------ */
+static void l2cap_sock_timeout(unsigned long arg)
+{
+	struct sock *sk = (struct sock *) arg;
+
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	bh_lock_sock(sk);
+	__l2cap_sock_close(sk, ETIMEDOUT);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	sock_put(sk);
+}
+
+static void l2cap_sock_set_timer(struct sock *sk, long timeout)
+{
+	BT_DBG("sk %p state %d timeout %ld", sk, sk->state, timeout);
+
+	if (!mod_timer(&sk->timer, jiffies + timeout))
+		sock_hold(sk);
+}
+
+static void l2cap_sock_clear_timer(struct sock *sk)
+{
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+		__sock_put(sk);
+}
+
+static void l2cap_sock_init_timer(struct sock *sk)
+{
+	init_timer(&sk->timer);
+	sk->timer.function = l2cap_sock_timeout;
+	sk->timer.data = (unsigned long)sk;
+}
+
+/* -------- L2CAP connections --------- */
+static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_conn *conn;
+
+	if ((conn = hcon->l2cap_data))
+		return conn;
+
+	if (status)
+		return conn;
+
+	if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct l2cap_conn));
+
+	hcon->l2cap_data = conn;
+	conn->hcon = hcon;
+	
+	conn->mtu = hcon->hdev->acl_mtu;
+	conn->src = &hcon->hdev->bdaddr;
+	conn->dst = &hcon->dst;
+	
+	spin_lock_init(&conn->lock);
+	conn->chan_list.lock = RW_LOCK_UNLOCKED;
+
+	BT_DBG("hcon %p conn %p", hcon, conn);
+
+	MOD_INC_USE_COUNT;
+	return conn;
+}
+
+static int l2cap_conn_del(struct hci_conn *hcon, int err)
+{
+	struct l2cap_conn *conn;
+	struct sock *sk;
+
+	if (!(conn = hcon->l2cap_data)) 
+		return 0;
+
+	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+	if (conn->rx_skb)
+		kfree_skb(conn->rx_skb);
+
+	/* Kill channels */
+	while ((sk = conn->chan_list.head)) {
+		bh_lock_sock(sk);
+		l2cap_chan_del(sk, err);
+		bh_unlock_sock(sk);
+		l2cap_sock_kill(sk);
+	}
+
+	hcon->l2cap_data = NULL;
+	kfree(conn);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int l2cap_connect(struct sock *sk)
+{
+	bdaddr_t *src = &bluez_sk(sk)->src;
+	bdaddr_t *dst = &bluez_sk(sk)->dst;
+	struct l2cap_conn *conn;
+	struct hci_conn   *hcon;
+	struct hci_dev    *hdev;
+	int err = 0;
+
+	BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
+
+	if (!(hdev = hci_get_route(dst, src)))
+		return -EHOSTUNREACH;
+
+	hci_dev_lock_bh(hdev);
+
+	err = -ENOMEM;
+
+	hcon = hci_connect(hdev, ACL_LINK, dst);
+	if (!hcon)
+		goto done;
+
+	conn = l2cap_conn_add(hcon, 0);
+	if (!conn) {
+		hci_conn_put(hcon);
+		goto done;
+	}
+
+	err = 0;
+
+	/* Update source addr of the socket */
+	bacpy(src, conn->src);
+
+	l2cap_chan_add(conn, sk, NULL);
+
+	sk->state = BT_CONNECT;
+	l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+	if (hcon->state == BT_CONNECTED) {
+		if (sk->type == SOCK_SEQPACKET) {
+			l2cap_conn_req req;
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.psm  = l2cap_pi(sk)->psm;
+			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+		} else {
+			l2cap_sock_clear_timer(sk);
+			sk->state = BT_CONNECTED;
+		}
+	}
+
+done:
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
+/* -------- Socket interface ---------- */
+static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
+{
+	bdaddr_t *src = &addr->l2_bdaddr;
+	__u16 psm = addr->l2_psm;
+	struct sock *sk;
+
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (l2cap_pi(sk)->psm == psm &&
+		    !bacmp(&bluez_sk(sk)->src, src))
+			break;
+	}
+
+	return sk;
+}
+
+/* Find socket listening on psm and source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	read_lock(&l2cap_sk_list.lock);
+
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (sk->state != BT_LISTEN)
+			continue;
+
+		if (l2cap_pi(sk)->psm == psm) {
+			/* Exact match. */
+			if (!bacmp(&bluez_sk(sk)->src, src))
+				break;
+
+			/* Closest match */
+			if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY))
+				sk1 = sk;
+		}
+	}
+
+	read_unlock(&l2cap_sk_list.lock);
+	return sk ? sk : sk1;
+}
+
+static void l2cap_sock_destruct(struct sock *sk)
+{
+	BT_DBG("sk %p", sk);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+	
+	if (sk->protinfo)
+		kfree(sk->protinfo);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static void l2cap_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted channels */
+	while ((sk = bluez_accept_dequeue(parent, NULL)))
+		l2cap_sock_close(sk);
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void l2cap_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&l2cap_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+/* Close socket.
+ */
+static void __l2cap_sock_close(struct sock *sk, int reason)
+{
+	BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		l2cap_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECTED:
+	case BT_CONFIG:
+		if (sk->type == SOCK_SEQPACKET) {
+			struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+			l2cap_disconn_req req;
+
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, HZ * 5);
+
+			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+		} else {
+			l2cap_chan_del(sk, reason);
+		}
+		break;
+
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_DISCONN:
+		l2cap_chan_del(sk, reason);
+		break;
+
+	default:
+		sk->zapped = 1;
+		break;
+	};
+}
+
+/* Must be called on unlocked socket. */
+static void l2cap_sock_close(struct sock *sk)
+{
+	l2cap_sock_clear_timer(sk);
+
+	lock_sock(sk);
+	__l2cap_sock_close(sk, ECONNRESET);
+	release_sock(sk);
+
+	l2cap_sock_kill(sk);
+}
+
+static void l2cap_sock_init(struct sock *sk, struct sock *parent)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+
+	BT_DBG("sk %p", sk);
+
+	if (parent) {
+		sk->type = parent->type;
+
+		pi->imtu = l2cap_pi(parent)->imtu;
+		pi->omtu = l2cap_pi(parent)->omtu;
+		pi->link_mode = l2cap_pi(parent)->link_mode;
+	} else {
+		pi->imtu = L2CAP_DEFAULT_MTU;
+		pi->omtu = 0;
+		pi->link_mode = 0;
+	}
+
+	/* Default config options */
+	pi->conf_mtu = L2CAP_DEFAULT_MTU;
+	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
+}
+
+static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct sock *sk;
+
+	sk = bluez_sock_alloc(sock, proto, sizeof(struct l2cap_pinfo), prio);
+	if (!sk)
+		return NULL;
+
+	sk->destruct = l2cap_sock_destruct;
+	sk->sndtimeo = L2CAP_CONN_TIMEOUT;
+	sk->state    = BT_OPEN;
+
+	l2cap_sock_init_timer(sk);
+
+	bluez_sock_link(&l2cap_sk_list, sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int l2cap_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->state = SS_UNCONNECTED;
+	sock->ops   = &l2cap_sock_ops;
+
+	sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL);
+	if (!sk)
+		return -ENOMEM;
+
+	l2cap_sock_init(sk, NULL);
+	return 0;
+}
+
+static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock(&l2cap_sk_list.lock);
+
+	if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
+		err = -EADDRINUSE;
+		goto unlock;
+	}
+
+	/* Save source address */
+	bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
+	l2cap_pi(sk)->psm = la->l2_psm;
+	sk->state = BT_BOUND;
+
+unlock:
+	write_unlock(&l2cap_sk_list.lock);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	lock_sock(sk);
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	switch(sk->state) {
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_CONFIG:
+		/* Already connecting */
+		goto wait;
+
+	case BT_CONNECTED:
+		/* Already connected */
+		goto done;
+
+	case BT_OPEN:
+	case BT_BOUND:
+		/* Can connect */
+		break;
+
+	default:
+		err = -EBADFD;
+		goto done;
+	}
+
+	/* Set destination address and psm */
+	bacpy(&bluez_sk(sk)->dst, &la->l2_bdaddr);
+	l2cap_pi(sk)->psm = la->l2_psm;
+
+	if ((err = l2cap_connect(sk)))
+		goto done;
+
+wait:
+	err = bluez_sock_w4_connect(sk, flags);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int l2cap_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	if (!l2cap_pi(sk)->psm) {
+		err = -EINVAL;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *nsk;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", nsk);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	addr->sa_family = AF_BLUETOOTH;
+	*len = sizeof(struct sockaddr_l2);
+
+	if (peer)
+		bacpy(&la->l2_bdaddr, &bluez_sk(sk)->dst);
+	else
+		bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src);
+
+	la->l2_psm = l2cap_pi(sk)->psm;
+
+	return 0;
+}
+
+static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (sk->err)
+		return sock_error(sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	lock_sock(sk);
+
+	if (sk->state == BT_CONNECTED)
+		err = l2cap_chan_send(sk, msg, len);
+	else
+		err = -ENOTCONN;
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	struct l2cap_options opts;
+	int err = 0, len;
+	__u32 opt;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case L2CAP_OPTIONS:
+		len = MIN(sizeof(opts), optlen);
+		if (copy_from_user((char *)&opts, optval, len)) {
+			err = -EFAULT;
+			break;
+		}
+		l2cap_pi(sk)->imtu  = opts.imtu;
+		l2cap_pi(sk)->omtu  = opts.omtu;
+		break;
+
+	case L2CAP_LM:
+		if (get_user(opt, (__u32 *)optval)) {
+			err = -EFAULT;
+			break;
+		}
+
+		l2cap_pi(sk)->link_mode = opt;
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct l2cap_options opts;
+	struct l2cap_conninfo cinfo;
+	int len, err = 0; 
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case L2CAP_OPTIONS:
+		opts.imtu     = l2cap_pi(sk)->imtu;
+		opts.omtu     = l2cap_pi(sk)->omtu;
+		opts.flush_to = l2cap_pi(sk)->flush_to;
+
+		len = MIN(len, sizeof(opts));
+		if (copy_to_user(optval, (char *)&opts, len))
+			err = -EFAULT;
+
+		break;
+
+	case L2CAP_LM:
+		if (put_user(l2cap_pi(sk)->link_mode, (__u32 *)optval))
+			err = -EFAULT;
+		break;
+
+	case L2CAP_CONNINFO:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+
+		cinfo.hci_handle = l2cap_pi(sk)->conn->hcon->handle;
+
+		len = MIN(len, sizeof(cinfo));
+		if (copy_to_user(optval, (char *)&cinfo, len))
+			err = -EFAULT;
+
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int l2cap_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	l2cap_sock_close(sk);
+	return 0;
+}
+
+/* --------- L2CAP channels --------- */
+static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+
+	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+		if (l2cap_pi(s)->dcid == cid)
+			break;
+	}
+
+	return s;
+}
+
+static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+
+	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
+		if (l2cap_pi(s)->scid == cid)
+			break;
+	}
+
+	return s;
+}
+
+/* Find channel with given SCID.
+ * Returns locked socket */
+static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
+{
+	struct sock *s;
+	read_lock(&l->lock);
+	s = __l2cap_get_chan_by_scid(l, cid);
+	if (s) bh_lock_sock(s);
+	read_unlock(&l->lock);
+	return s;
+}
+
+static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
+{
+	__u16 cid = 0x0040;
+
+	for (; cid < 0xffff; cid++) {
+		if(!__l2cap_get_chan_by_scid(l, cid))
+			return cid;
+	}
+
+	return 0;
+}
+
+static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
+{
+	sock_hold(sk);
+
+	if (l->head)
+		l2cap_pi(l->head)->prev_c = sk;
+
+	l2cap_pi(sk)->next_c = l->head;
+	l2cap_pi(sk)->prev_c = NULL;
+	l->head = sk;
+}
+
+static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
+{
+	struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
+
+	write_lock(&l->lock);
+	if (sk == l->head)
+		l->head = next;
+
+	if (next)
+		l2cap_pi(next)->prev_c = prev;
+	if (prev)
+		l2cap_pi(prev)->next_c = next;
+	write_unlock(&l->lock);
+
+	__sock_put(sk);
+}
+
+static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+
+	BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+
+	l2cap_pi(sk)->conn = conn;
+
+	if (sk->type == SOCK_SEQPACKET) {
+		/* Alloc CID for normal socket */
+		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+	} else {
+		/* Raw socket can send only signalling messages */
+		l2cap_pi(sk)->scid = 0x0001;
+		l2cap_pi(sk)->dcid = 0x0001;
+		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
+	}
+
+	__l2cap_chan_link(l, sk);
+
+	if (parent)
+		bluez_accept_enqueue(parent, sk);
+}
+
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	write_lock(&l->lock);
+	__l2cap_chan_add(conn, sk, parent);
+	write_unlock(&l->lock);
+}
+
+/* Delete channel. 
+ * Must be called on the locked socket. */
+static void l2cap_chan_del(struct sock *sk, int err)
+{
+	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+	struct sock *parent = bluez_sk(sk)->parent;
+
+	l2cap_sock_clear_timer(sk);
+
+	BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+
+	if (conn) { 
+		/* Unlink from channel list */
+		l2cap_chan_unlink(&conn->chan_list, sk);
+		l2cap_pi(sk)->conn = NULL;
+		hci_conn_put(conn->hcon);
+	}
+
+	sk->state = BT_CLOSED;
+	sk->err   = err;
+	sk->zapped = 1;
+
+	if (parent)
+		parent->data_ready(parent, 0);
+	else
+		sk->state_change(sk);
+}
+
+static void l2cap_conn_ready(struct l2cap_conn *conn)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	struct sock *sk;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->type != SOCK_SEQPACKET) {
+			l2cap_sock_clear_timer(sk);
+			sk->state = BT_CONNECTED;
+			sk->state_change(sk);
+		} else if (sk->state == BT_CONNECT) {
+			l2cap_conn_req req;
+			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+			req.psm  = l2cap_pi(sk)->psm;
+			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
+		}
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+}
+
+static void l2cap_chan_ready(struct sock *sk)
+{
+	struct sock *parent = bluez_sk(sk)->parent;
+
+	BT_DBG("sk %p, parent %p", sk, parent);
+
+	l2cap_pi(sk)->conf_state = 0;
+	l2cap_sock_clear_timer(sk);
+
+	if (!parent) {
+		/* Outgoing channel.
+		 * Wake up socket sleeping on connect.
+		 */
+		sk->state = BT_CONNECTED;
+		sk->state_change(sk);
+	} else {
+		/* Incomming channel.
+		 * Wake up socket sleeping on accept.
+		 */
+		parent->data_ready(parent, 0);
+	}
+}
+
+/* Copy frame to all raw sockets on that connection */
+void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	struct sk_buff *nskb;
+	struct sock * sk;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		if (sk->type != SOCK_RAW)
+			continue;
+
+		/* Don't send frame to the socket it came from */
+		if (skb->sk == sk)
+			continue;
+
+		if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
+			continue;
+
+		if (sock_queue_rcv_skb(sk, nskb))
+			kfree_skb(nskb);
+	}
+	read_unlock(&l->lock);
+}
+
+static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
+{
+	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+	struct sk_buff *skb, **frag;
+	int err, size, count, sent=0;
+	l2cap_hdr *lh;
+
+	/* Check outgoing MTU */
+	if (len > l2cap_pi(sk)->omtu)
+		return -EINVAL;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	/* First fragment (with L2CAP header) */
+	count = MIN(conn->mtu - L2CAP_HDR_SIZE, len);
+	size  = L2CAP_HDR_SIZE + count;
+	if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
+		return err;
+
+	/* Create L2CAP header */
+	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+	lh->len = __cpu_to_le16(len);
+	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+
+	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+		err = -EFAULT;
+		goto fail;
+	}
+
+	sent += count;
+	len  -= count;
+
+	/* Continuation fragments (no L2CAP header) */
+	frag = &skb_shinfo(skb)->frag_list;
+	while (len) {
+		count = MIN(conn->mtu, len);
+
+		*frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
+		if (!*frag)
+			goto fail;
+		
+		if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
+			err = -EFAULT;
+			goto fail;
+		}
+
+		sent += count;
+		len  -= count;
+
+		frag = &(*frag)->next;
+	}
+
+	if ((err = hci_send_acl(conn->hcon, skb, 0)) < 0)
+		goto fail;
+
+	return sent;
+
+fail:
+	kfree_skb(skb);
+	return err;
+}
+
+/* --------- L2CAP signalling commands --------- */
+static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
+{
+	__u8 id;
+
+	/* Get next available identificator.
+	 *    1 - 199 are used by kernel.
+	 *  200 - 254 are used by utilities like l2ping, etc 
+	 */
+
+	spin_lock(&conn->lock);
+
+	if (++conn->tx_ident > 199)
+		conn->tx_ident = 1;
+
+	id = conn->tx_ident;
+
+	spin_unlock(&conn->lock);
+
+	return id;
+}
+
+static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
+				__u8 code, __u8 ident, __u16 dlen, void *data)
+{
+	struct sk_buff *skb, **frag;
+	l2cap_cmd_hdr *cmd;
+	l2cap_hdr *lh;
+	int len, count;
+
+	BT_DBG("conn %p, code 0x%2.2x, ident 0x%2.2x, len %d", conn, code, ident, dlen);
+
+	len = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + dlen;
+	count = MIN(conn->mtu, len);
+	
+	skb = bluez_skb_alloc(count, GFP_ATOMIC);
+	if (!skb)
+		return NULL;
+
+	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+	lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + dlen);
+	lh->cid = __cpu_to_le16(0x0001);
+
+	cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
+	cmd->code  = code;
+	cmd->ident = ident;
+	cmd->len   = __cpu_to_le16(dlen);
+
+	if (dlen) {
+		count -= L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE;
+		memcpy(skb_put(skb, count), data, count);
+		data += count;
+	}
+
+	len -= skb->len;
+	
+	/* Continuation fragments (no L2CAP header) */
+	frag = &skb_shinfo(skb)->frag_list;
+	while (len) {
+		count = MIN(conn->mtu, len);
+
+		*frag = bluez_skb_alloc(count, GFP_ATOMIC);
+		if (!*frag)
+			goto fail;
+		
+		memcpy(skb_put(*frag, count), data, count);
+
+		len  -= count;
+		data += count;
+		
+		frag = &(*frag)->next;
+	}
+
+	return skb;
+
+fail:
+	kfree_skb(skb);
+	return NULL;
+}
+
+static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
+{
+	__u8 ident = l2cap_get_ident(conn);
+	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+
+	BT_DBG("code 0x%2.2x", code);
+
+	if (!skb)
+		return -ENOMEM;
+	return hci_send_acl(conn->hcon, skb, 0);
+}
+
+static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
+{
+	struct sk_buff *skb = l2cap_build_cmd(conn, code, ident, len, data);
+
+	BT_DBG("code 0x%2.2x", code);
+
+	if (!skb)
+		return -ENOMEM;
+	return hci_send_acl(conn->hcon, skb, 0);
+}
+
+static inline int l2cap_get_conf_opt(void **ptr, int *type, int *olen, unsigned long *val)
+{
+	l2cap_conf_opt *opt = *ptr;
+	int len;
+
+	len = L2CAP_CONF_OPT_SIZE + opt->len;
+	*ptr += len;
+
+	*type = opt->type;
+	*olen = opt->len;
+
+	switch (opt->len) {
+	case 1:
+		*val = *((__u8 *) opt->val);
+		break;
+
+	case 2:
+		*val = __le16_to_cpu(*((__u16 *)opt->val));
+		break;
+
+	case 4:
+		*val = __le32_to_cpu(*((__u32 *)opt->val));
+		break;
+
+	default:
+		*val = (unsigned long) opt->val;
+		break;
+	};
+
+	BT_DBG("type 0x%2.2x len %d val 0x%lx", *type, opt->len, *val);
+	return len;
+}
+
+static inline void l2cap_parse_conf_req(struct sock *sk, void *data, int len)
+{
+	int type, hint, olen; 
+	unsigned long val;
+	void *ptr = data;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	while (len >= L2CAP_CONF_OPT_SIZE) {
+		len -= l2cap_get_conf_opt(&ptr, &type, &olen, &val);
+
+		hint  = type & 0x80;
+		type &= 0x7f;
+
+		switch (type) {
+		case L2CAP_CONF_MTU:
+			l2cap_pi(sk)->conf_mtu = val;
+			break;
+
+		case L2CAP_CONF_FLUSH_TO:
+			l2cap_pi(sk)->flush_to = val;
+			break;
+
+		case L2CAP_CONF_QOS:
+			break;
+		
+		default:
+			if (hint)
+				break;
+
+			/* FIXME: Reject unknown option */
+			break;
+		};
+	}
+}
+
+static void l2cap_add_conf_opt(void **ptr, __u8 type, __u8 len, unsigned long val)
+{
+	register l2cap_conf_opt *opt = *ptr;
+
+	BT_DBG("type 0x%2.2x len %d val 0x%lx", type, len, val);
+
+	opt->type = type;
+	opt->len  = len;
+
+	switch (len) {
+	case 1:
+		*((__u8 *) opt->val)  = val;
+		break;
+
+	case 2:
+		*((__u16 *) opt->val) = __cpu_to_le16(val);
+		break;
+
+	case 4:
+		*((__u32 *) opt->val) = __cpu_to_le32(val);
+		break;
+
+	default:
+		memcpy(opt->val, (void *) val, len);
+		break;
+	};
+
+	*ptr += L2CAP_CONF_OPT_SIZE + len;
+}
+
+static int l2cap_build_conf_req(struct sock *sk, void *data)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+	l2cap_conf_req *req = (l2cap_conf_req *) data;
+	void *ptr = req->data;
+
+	BT_DBG("sk %p", sk);
+
+	if (pi->imtu != L2CAP_DEFAULT_MTU)
+		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+
+	/* FIXME. Need actual value of the flush timeout */
+	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
+	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
+
+	req->dcid  = __cpu_to_le16(pi->dcid);
+	req->flags = __cpu_to_le16(0);
+
+	return ptr - data;
+}
+
+static inline int l2cap_conf_output(struct sock *sk, void **ptr)
+{
+	struct l2cap_pinfo *pi = l2cap_pi(sk);
+	int result = 0;
+
+	/* Configure output options and let the other side know
+	 * which ones we don't like.
+	 */
+	if (pi->conf_mtu < pi->omtu) {
+		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, pi->omtu);
+		result = L2CAP_CONF_UNACCEPT;
+	} else {
+		pi->omtu = pi->conf_mtu;
+	}
+
+	BT_DBG("sk %p result %d", sk, result);
+	return result;
+}
+
+static int l2cap_build_conf_rsp(struct sock *sk, void *data, int *result)
+{
+	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
+	void *ptr = rsp->data;
+
+	BT_DBG("sk %p complete %d", sk, result ? 1 : 0);
+
+	if (result)
+		*result = l2cap_conf_output(sk, &ptr);
+
+	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	rsp->result = __cpu_to_le16(result ? *result : 0);
+	rsp->flags  = __cpu_to_le16(0);
+
+	return ptr - data;
+}
+
+static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	struct l2cap_chan_list *list = &conn->chan_list;
+	l2cap_conn_req *req = (l2cap_conn_req *) data;
+	l2cap_conn_rsp rsp;
+	struct sock *sk, *parent;
+	int result = 0, status = 0;
+
+	__u16 dcid = 0, scid = __le16_to_cpu(req->scid);
+	__u16 psm  = req->psm;
+
+	BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
+
+	/* Check if we have socket listening on psm */
+	if (!(parent = l2cap_get_sock_listen(conn->src, psm))) {
+		result = L2CAP_CR_BAD_PSM;
+		goto resp;
+	}
+
+	write_lock(&list->lock);
+	bh_lock_sock(parent);
+
+	result = L2CAP_CR_NO_MEM;
+
+	/* Check if we already have channel with that dcid */
+	if (__l2cap_get_chan_by_dcid(list, scid))
+		goto unlock;
+
+	/* Check for backlog size */
+	if (parent->ack_backlog > parent->max_ack_backlog) {
+		BT_DBG("backlog full %d", parent->ack_backlog); 
+		goto unlock;
+	}
+
+	if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
+		goto unlock;
+
+	l2cap_sock_init(sk, parent);
+
+	bacpy(&bluez_sk(sk)->src, conn->src);
+	bacpy(&bluez_sk(sk)->dst, conn->dst);
+	l2cap_pi(sk)->psm  = psm;
+	l2cap_pi(sk)->dcid = scid;
+	
+	hci_conn_hold(conn->hcon);
+
+	__l2cap_chan_add(conn, sk, parent);
+	dcid = l2cap_pi(sk)->scid;
+
+	l2cap_sock_set_timer(sk, sk->sndtimeo);
+
+	/* Service level security */
+	result = L2CAP_CR_PEND;
+	status = L2CAP_CS_AUTHEN_PEND;
+	sk->state = BT_CONNECT2;
+	l2cap_pi(sk)->ident = cmd->ident;
+	
+	if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
+		if (!hci_conn_encrypt(conn->hcon))
+			goto unlock;
+	} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
+		if (!hci_conn_auth(conn->hcon))
+			goto unlock;
+	}
+
+	sk->state = BT_CONFIG;
+	result = status = 0;
+
+unlock:
+	bh_unlock_sock(parent);
+	write_unlock(&list->lock);
+
+resp:
+	rsp.scid   = __cpu_to_le16(scid);
+	rsp.dcid   = __cpu_to_le16(dcid);
+	rsp.result = __cpu_to_le16(result);
+	rsp.status = __cpu_to_le16(status);
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
+	return 0;
+}
+
+static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
+	__u16 scid, dcid, result, status;
+	struct sock *sk;
+	char req[128];
+
+	scid   = __le16_to_cpu(rsp->scid);
+	dcid   = __le16_to_cpu(rsp->dcid);
+	result = __le16_to_cpu(rsp->result);
+	status = __le16_to_cpu(rsp->status);
+
+	BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return -ENOENT;
+
+	switch (result) {
+	case L2CAP_CR_SUCCESS:
+		sk->state = BT_CONFIG;
+		l2cap_pi(sk)->dcid = dcid;
+		l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
+
+		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+		break;
+
+	case L2CAP_CR_PEND:
+		break;
+
+	default:
+		l2cap_chan_del(sk, ECONNREFUSED);
+		break;
+	}
+
+	bh_unlock_sock(sk);
+	return 0;
+}
+
+static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conf_req * req = (l2cap_conf_req *) data;
+	__u16 dcid, flags;
+	__u8 rsp[64];
+	struct sock *sk;
+	int result;
+
+	dcid  = __le16_to_cpu(req->dcid);
+	flags = __le16_to_cpu(req->flags);
+
+	BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+		return -ENOENT;
+
+	l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
+
+	if (flags & 0x01) {
+		/* Incomplete config. Send empty response. */
+		l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
+		goto unlock;
+	}
+
+	/* Complete config. */
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
+
+	if (result)
+		goto unlock;
+
+	/* Output config done */
+	l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
+
+	if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
+		sk->state = BT_CONNECTED;
+		l2cap_chan_ready(sk);
+	} else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
+		char req[64];
+		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
+	}
+
+unlock:
+	bh_unlock_sock(sk);
+	return 0;
+}
+
+static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
+	__u16 scid, flags, result;
+	struct sock *sk;
+	int err = 0;
+
+	scid   = __le16_to_cpu(rsp->scid);
+	flags  = __le16_to_cpu(rsp->flags);
+	result = __le16_to_cpu(rsp->result);
+
+	BT_DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return -ENOENT;
+
+	if (result) {
+		l2cap_disconn_req req;
+
+		/* They didn't like our options. Well... we do not negotiate.
+		 * Close channel.
+		 */
+		sk->state = BT_DISCONN;
+		l2cap_sock_set_timer(sk, HZ * 5);
+
+		req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+		req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
+		l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
+		goto done;
+	}
+
+	if (flags & 0x01)
+		goto done;
+
+	/* Input config done */
+	l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
+
+	if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
+		sk->state = BT_CONNECTED;
+		l2cap_chan_ready(sk);
+	}
+
+done:
+	bh_unlock_sock(sk);
+	return err;
+}
+
+static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_disconn_req *req = (l2cap_disconn_req *) data;
+	l2cap_disconn_rsp rsp;
+	__u16 dcid, scid;
+	struct sock *sk;
+
+	scid = __le16_to_cpu(req->scid);
+	dcid = __le16_to_cpu(req->dcid);
+
+	BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
+		return 0;
+
+	rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
+	rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
+
+	sk->shutdown = SHUTDOWN_MASK;
+	
+	l2cap_chan_del(sk, ECONNRESET);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	return 0;
+}
+
+static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
+{
+	l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
+	__u16 dcid, scid;
+	struct sock *sk;
+
+	scid = __le16_to_cpu(rsp->scid);
+	dcid = __le16_to_cpu(rsp->dcid);
+
+	BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
+
+	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
+		return 0;
+	l2cap_chan_del(sk, ECONNABORTED);
+	bh_unlock_sock(sk);
+
+	l2cap_sock_kill(sk);
+	return 0;
+}
+
+static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	__u8 *data = skb->data;
+	int len = skb->len;
+	l2cap_cmd_hdr cmd;
+	int err = 0;
+
+	while (len >= L2CAP_CMD_HDR_SIZE) {
+		memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
+		data += L2CAP_CMD_HDR_SIZE;
+		len  -= L2CAP_CMD_HDR_SIZE;
+
+		cmd.len = __le16_to_cpu(cmd.len);
+
+		BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
+
+		if (cmd.len > len || !cmd.ident) {
+			BT_DBG("corrupted command");
+			break;
+		}
+
+		switch (cmd.code) {
+		case L2CAP_CONN_REQ:
+			err = l2cap_connect_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONN_RSP:
+			err = l2cap_connect_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONF_REQ:
+			err = l2cap_config_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_CONF_RSP:
+			err = l2cap_config_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_DISCONN_REQ:
+			err = l2cap_disconnect_req(conn, &cmd, data);
+			break;
+
+		case L2CAP_DISCONN_RSP:
+			err = l2cap_disconnect_rsp(conn, &cmd, data);
+			break;
+
+		case L2CAP_COMMAND_REJ:
+			/* FIXME: We should process this */
+			l2cap_raw_recv(conn, skb);
+			break;
+
+		case L2CAP_ECHO_REQ:
+			l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
+			break;
+
+		case L2CAP_ECHO_RSP:
+		case L2CAP_INFO_REQ:
+		case L2CAP_INFO_RSP:
+			l2cap_raw_recv(conn, skb);
+			break;
+
+		default:
+			BT_ERR("Uknown signaling command 0x%2.2x", cmd.code);
+			err = -EINVAL;
+			break;
+		};
+
+		if (err) {
+			l2cap_cmd_rej rej;
+			BT_DBG("error %d", err);
+
+			/* FIXME: Map err to a valid reason. */
+			rej.reason = __cpu_to_le16(0);
+			l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
+		}
+
+		data += cmd.len;
+		len  -= cmd.len;
+	}
+
+	kfree_skb(skb);
+}
+
+static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
+{
+	struct sock *sk;
+
+	sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
+	if (!sk) {
+		BT_DBG("unknown cid 0x%4.4x", cid);
+		goto drop;
+	}
+
+	BT_DBG("sk %p, len %d", sk, skb->len);
+
+	if (sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (l2cap_pi(sk)->imtu < skb->len)
+		goto drop;
+
+	/* If socket recv buffers overflows we drop data here 
+	 * which is *bad* because L2CAP has to be reliable. 
+	 * But we don't have any other choice. L2CAP doesn't 
+	 * provide flow control mechanism */ 
+	
+	if (!sock_queue_rcv_skb(sk, skb))
+		goto done;
+
+drop:
+	kfree_skb(skb);
+
+done:
+	if (sk) bh_unlock_sock(sk);
+	return 0;
+}
+
+static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	l2cap_hdr *lh = (l2cap_hdr *) skb->data;
+	__u16 cid, len;
+
+	skb_pull(skb, L2CAP_HDR_SIZE);
+	cid = __le16_to_cpu(lh->cid);
+	len = __le16_to_cpu(lh->len);
+
+	BT_DBG("len %d, cid 0x%4.4x", len, cid);
+
+	if (cid == 0x0001)
+		l2cap_sig_channel(conn, skb);
+	else	
+		l2cap_data_channel(conn, cid, skb);
+}
+
+/* ------------ L2CAP interface with lower layer (HCI) ------------- */
+
+static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	int exact = 0, lm1 = 0, lm2 = 0;
+	register struct sock *sk;
+
+	if (type != ACL_LINK)
+		return 0;
+
+	BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+
+	/* Find listening sockets and check their link_mode */
+	read_lock(&l2cap_sk_list.lock);
+	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
+		if (sk->state != BT_LISTEN)
+			continue;
+
+		if (!bacmp(&bluez_sk(sk)->src, bdaddr)) {
+			lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
+			exact++;
+		} else if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY))
+			lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
+	}
+	read_unlock(&l2cap_sk_list.lock);
+
+	return exact ? lm1 : lm2;
+}
+
+static int l2cap_connect_cfm(struct hci_conn *hcon, __u8 status)
+{
+	BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+
+	if (hcon->type != ACL_LINK)
+		return 0;
+
+	if (!status) {
+		struct l2cap_conn *conn;
+
+		conn = l2cap_conn_add(hcon, status);
+		if (conn)
+			l2cap_conn_ready(conn);
+	} else 
+		l2cap_conn_del(hcon, bterr(status));
+	
+	return 0;
+}
+
+static int l2cap_disconn_ind(struct hci_conn *hcon, __u8 reason)
+{
+	BT_DBG("hcon %p reason %d", hcon, reason);
+
+	if (hcon->type != ACL_LINK)
+		return 0;
+
+	l2cap_conn_del(hcon, bterr(reason));
+	return 0;
+}
+
+static int l2cap_auth_cfm(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_chan_list *l;
+	struct l2cap_conn *conn;
+	l2cap_conn_rsp rsp;
+	struct sock *sk;
+	int result;
+	
+	if (!(conn = hcon->l2cap_data))
+		return 0;
+	l = &conn->chan_list;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->state != BT_CONNECT2 ||
+				(l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT)) {
+			bh_unlock_sock(sk);
+			continue;
+		}
+
+		if (!status) {
+			sk->state = BT_CONFIG;
+			result = 0;
+		} else {
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, HZ/10);
+			result = L2CAP_CR_SEC_BLOCK;
+		}
+
+		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = __cpu_to_le16(result);
+		rsp.status = __cpu_to_le16(0);
+		l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP,
+			L2CAP_CONN_RSP_SIZE, &rsp);
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+	return 0;
+}
+
+static int l2cap_encrypt_cfm(struct hci_conn *hcon, __u8 status)
+{
+	struct l2cap_chan_list *l;
+	struct l2cap_conn *conn;
+	l2cap_conn_rsp rsp;
+	struct sock *sk;
+	int result;
+	
+	if (!(conn = hcon->l2cap_data))
+		return 0;
+	l = &conn->chan_list;
+
+	BT_DBG("conn %p", conn);
+
+	read_lock(&l->lock);
+
+	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+		bh_lock_sock(sk);
+
+		if (sk->state != BT_CONNECT2) {
+			bh_unlock_sock(sk);
+			continue;
+		}
+
+		if (!status) {
+			sk->state = BT_CONFIG;
+			result = 0;
+		} else {
+			sk->state = BT_DISCONN;
+			l2cap_sock_set_timer(sk, HZ/10);
+			result = L2CAP_CR_SEC_BLOCK;
+		}
+
+		rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
+		rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
+		rsp.result = __cpu_to_le16(result);
+		rsp.status = __cpu_to_le16(0);
+		l2cap_send_rsp(conn, l2cap_pi(sk)->ident, L2CAP_CONN_RSP, 
+			L2CAP_CONN_RSP_SIZE, &rsp);
+
+		bh_unlock_sock(sk);
+	}
+
+	read_unlock(&l->lock);
+	return 0;
+}
+
+static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, __u16 flags)
+{
+	struct l2cap_conn *conn = hcon->l2cap_data;
+
+	if (!conn && !(conn = l2cap_conn_add(hcon, 0)))
+		goto drop;
+
+	BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
+
+	if (flags & ACL_START) {
+		int flen, tlen, size;
+		l2cap_hdr *lh;
+
+		if (conn->rx_len) {
+			BT_ERR("Unexpected start frame (len %d)", skb->len);
+			kfree_skb(conn->rx_skb);
+			conn->rx_skb = NULL;
+			conn->rx_len = 0;
+		}
+
+		if (skb->len < L2CAP_HDR_SIZE) {
+			BT_ERR("Frame is too small (len %d)", skb->len);
+			goto drop;
+		}
+
+		lh = (l2cap_hdr *)skb->data;
+		tlen = __le16_to_cpu(lh->len);
+		flen = skb->len - L2CAP_HDR_SIZE;
+
+		BT_DBG("Start: total len %d, frag len %d", tlen, flen);
+
+		if (flen == tlen) {
+			/* Complete frame received */
+			l2cap_recv_frame(conn, skb);
+			return 0;
+		}
+
+		/* Allocate skb for the complete frame (with header) */
+		size = L2CAP_HDR_SIZE + tlen;
+		if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+			goto drop;
+
+		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		conn->rx_len = tlen - flen;
+	} else {
+		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
+
+		if (!conn->rx_len) {
+			BT_ERR("Unexpected continuation frame (len %d)", skb->len);
+			goto drop;
+		}
+
+		if (skb->len > conn->rx_len) {
+			BT_ERR("Fragment is too large (len %d, expect %d)",
+					skb->len, conn->rx_len);
+			kfree_skb(conn->rx_skb);
+			conn->rx_skb = NULL;
+			conn->rx_len = 0;
+			goto drop;
+		}
+
+		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
+		conn->rx_len -= skb->len;
+
+		if (!conn->rx_len) {
+			/* Complete frame received */
+			l2cap_recv_frame(conn, conn->rx_skb);
+			conn->rx_skb = NULL;
+		}
+	}
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+/* ----- Proc fs support ------ */
+static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
+{
+	struct l2cap_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	write_lock(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = l2cap_pi(sk);
+		ptr += sprintf(ptr, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
+				batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst), 
+				sk->state, pi->psm, pi->scid, pi->dcid, pi->imtu, pi->omtu,
+				pi->link_mode);
+	}
+
+	write_unlock(&list->lock);
+
+	ptr += sprintf(ptr, "\n");
+	return ptr - buf;
+}
+
+static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static struct proto_ops l2cap_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	l2cap_sock_release,
+	bind:		l2cap_sock_bind,
+	connect:	l2cap_sock_connect,
+	listen:		l2cap_sock_listen,
+	accept:		l2cap_sock_accept,
+	getname:	l2cap_sock_getname,
+	sendmsg:	l2cap_sock_sendmsg,
+	recvmsg:	bluez_sock_recvmsg,
+	poll:		bluez_sock_poll,
+	socketpair:	sock_no_socketpair,
+	ioctl:		sock_no_ioctl,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	l2cap_sock_setsockopt,
+	getsockopt:	l2cap_sock_getsockopt,
+	mmap:		sock_no_mmap
+};
+
+static struct net_proto_family l2cap_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		l2cap_sock_create
+};
+
+static struct hci_proto l2cap_hci_proto = {
+	name:		"L2CAP",
+	id:		HCI_PROTO_L2CAP,
+	connect_ind:	l2cap_connect_ind,
+	connect_cfm:	l2cap_connect_cfm,
+	disconn_ind:	l2cap_disconn_ind,
+	recv_acldata:	l2cap_recv_acldata,
+	auth_cfm:	l2cap_auth_cfm,
+	encrypt_cfm:	l2cap_encrypt_cfm
+};
+
+int __init l2cap_init(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops))) {
+		BT_ERR("Can't register L2CAP socket");
+		return err;
+	}
+
+	if ((err = hci_register_proto(&l2cap_hci_proto))) {
+		BT_ERR("Can't register L2CAP protocol");
+		return err;
+	}
+
+	create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
+
+	BT_INFO("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	return 0;
+}
+
+void l2cap_cleanup(void)
+{
+	remove_proc_entry("bluetooth/l2cap", NULL);
+
+	/* Unregister socket and protocol */
+	if (bluez_sock_unregister(BTPROTO_L2CAP))
+		BT_ERR("Can't unregister L2CAP socket");
+
+	if (hci_unregister_proto(&l2cap_hci_proto))
+		BT_ERR("Can't unregister L2CAP protocol");
+}
+
+module_init(l2cap_init);
+module_exit(l2cap_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
+MODULE_LICENSE("GPL");
diff -Nru a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
--- a/net/bluetooth/l2cap_core.c	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,2316 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ L2CAP core and sockets.
- *
- * $Id: l2cap_core.c,v 1.19 2001/08/03 04:19:50 maxk Exp $
- */
-#define VERSION "1.1"
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/fcntl.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/list.h>
-#include <net/sock.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap.h>
-#include <net/bluetooth/l2cap_core.h>
-
-#ifndef L2CAP_DEBUG
-#undef  DBG
-#define DBG( A... )
-#endif
-
-struct proto_ops l2cap_sock_ops;
-
-struct bluez_sock_list l2cap_sk_list = {
-	lock: RW_LOCK_UNLOCKED
-};
-
-struct list_head l2cap_iff_list = LIST_HEAD_INIT(l2cap_iff_list);
-rwlock_t l2cap_rt_lock = RW_LOCK_UNLOCKED;
-
-static int  l2cap_conn_del(struct l2cap_conn *conn, int err);
-
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
-static void l2cap_chan_del(struct sock *sk, int err);
-static int  l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
-
-static void l2cap_sock_close(struct sock *sk);
-static void l2cap_sock_kill(struct sock *sk);
-
-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data);
-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data);
-
-/* -------- L2CAP interfaces & routing --------- */
-/* Add/delete L2CAP interface.
- * Must be called with locked rt_lock
- */ 
-
-static void l2cap_iff_add(struct hci_dev *hdev)
-{
-	struct l2cap_iff *iff;
-
-	DBG("%s", hdev->name);
-
-	DBG("iff_list %p next %p prev %p", &l2cap_iff_list, l2cap_iff_list.next, l2cap_iff_list.prev);
-
-	/* Allocate new interface and lock HCI device */
-	if (!(iff = kmalloc(sizeof(struct l2cap_iff), GFP_KERNEL))) {
-		ERR("Can't allocate new interface %s", hdev->name);
-		return;
-	}
-	memset(iff, 0, sizeof(struct l2cap_iff));
-
-	hci_dev_hold(hdev);
-	hdev->l2cap_data = iff;
-	iff->hdev   = hdev;
-	iff->mtu    = hdev->acl_mtu - HCI_ACL_HDR_SIZE;
-	iff->bdaddr = &hdev->bdaddr;
-
-	spin_lock_init(&iff->lock);
-	INIT_LIST_HEAD(&iff->conn_list);
-
-	list_add(&iff->list, &l2cap_iff_list);
-}
-
-static void l2cap_iff_del(struct hci_dev *hdev)
-{
-	struct l2cap_iff *iff;
-
-	if (!(iff = hdev->l2cap_data))
-		return;
-
-	DBG("%s iff %p", hdev->name, iff);
-
-	list_del(&iff->list);
-
-	l2cap_iff_lock(iff);
-
-	/* Drop connections */
-	while (!list_empty(&iff->conn_list)) {
-		struct l2cap_conn *c;
-
-		c = list_entry(iff->conn_list.next, struct l2cap_conn, list);
-		l2cap_conn_del(c, ENODEV);
-	}
-
-	l2cap_iff_unlock(iff);
-
-	/* Unlock HCI device */
-	hdev->l2cap_data = NULL;
-	hci_dev_put(hdev);
-
-	kfree(iff);
-}
-
-/* Get route. Returns L2CAP interface.
- * Must be called with locked rt_lock
- */
-static struct l2cap_iff *l2cap_get_route(bdaddr_t *src, bdaddr_t *dst)
-{
-	struct list_head *p;
-	int use_src;
-
-	DBG("%s -> %s", batostr(src), batostr(dst));
-
-	use_src = bacmp(src, BDADDR_ANY) ? 0 : 1;
-	
-	/* Simple routing: 
-	 * 	No source address - find interface with bdaddr != dst 
-	 *	Source address 	  - find interface with bdaddr == src 
-	 */
-
-	list_for_each(p, &l2cap_iff_list) {
-		struct l2cap_iff *iff;
-
-		iff = list_entry(p, struct l2cap_iff, list);
-
-		if (use_src && !bacmp(iff->bdaddr, src))
-			return iff;
-		else if (bacmp(iff->bdaddr, dst))
-			return iff;
-	}
-	return NULL;
-}
-
-/* ----- L2CAP timers ------ */
-static void l2cap_sock_timeout(unsigned long arg)
-{
-	struct sock *sk = (struct sock *) arg;
-
-	DBG("sock %p state %d", sk, sk->state);
-
-	bh_lock_sock(sk);
-	switch (sk->state) {
-	case BT_DISCONN:
-		l2cap_chan_del(sk, ETIMEDOUT);
-		break;
-
-	default:
-		sk->err = ETIMEDOUT;
-		sk->state_change(sk);
-		break;
-	};
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-	sock_put(sk);
-}
-
-static void l2cap_sock_set_timer(struct sock *sk, long timeout)
-{
-	DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
-
-	if (!mod_timer(&sk->timer, jiffies + timeout))
-		sock_hold(sk);
-}
-
-static void l2cap_sock_clear_timer(struct sock *sk)
-{
-	DBG("sock %p state %d", sk, sk->state);
-
-	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
-		__sock_put(sk);
-}
-
-static void l2cap_sock_init_timer(struct sock *sk)
-{
-	init_timer(&sk->timer);
-	sk->timer.function = l2cap_sock_timeout;
-	sk->timer.data = (unsigned long)sk;
-}
-
-static void l2cap_conn_timeout(unsigned long arg)
-{
-	struct l2cap_conn *conn = (void *)arg;
-	
-	DBG("conn %p state %d", conn, conn->state);
-
-	if (conn->state == BT_CONNECTED) {
-		hci_disconnect(conn->hconn, 0x13);
-	}
-		
-	return;
-}
-
-static void l2cap_conn_set_timer(struct l2cap_conn *conn, long timeout)
-{
-	DBG("conn %p state %d timeout %ld", conn, conn->state, timeout);
-
-	mod_timer(&conn->timer, jiffies + timeout);
-}
-
-static void l2cap_conn_clear_timer(struct l2cap_conn *conn)
-{
-	DBG("conn %p state %d", conn, conn->state);
-
-	del_timer(&conn->timer);
-}
-
-static void l2cap_conn_init_timer(struct l2cap_conn *conn)
-{
-	init_timer(&conn->timer);
-	conn->timer.function = l2cap_conn_timeout;
-	conn->timer.data = (unsigned long)conn;
-}
-
-/* -------- L2CAP connections --------- */
-/* Add new connection to the interface.
- * Interface must be locked
- */
-static struct l2cap_conn *l2cap_conn_add(struct l2cap_iff *iff, bdaddr_t *dst)
-{
-	struct l2cap_conn *conn;
-	bdaddr_t *src = iff->bdaddr;
-
-	if (!(conn = kmalloc(sizeof(struct l2cap_conn), GFP_KERNEL)))
-		return NULL;
-
-	memset(conn, 0, sizeof(struct l2cap_conn));
-
-	conn->state = BT_OPEN;
-	conn->iff   = iff;
-	bacpy(&conn->src, src);
-	bacpy(&conn->dst, dst);
-
-	spin_lock_init(&conn->lock);
-	conn->chan_list.lock = RW_LOCK_UNLOCKED;
-
-	l2cap_conn_init_timer(conn);
-	
-	__l2cap_conn_link(iff, conn);
-
-	DBG("%s -> %s, %p", batostr(src), batostr(dst), conn);
-
-	MOD_INC_USE_COUNT;
-
-	return conn;
-}
-
-/* Delete connection on the interface.
- * Interface must be locked
- */
-static int l2cap_conn_del(struct l2cap_conn *conn, int err)
-{
-	struct sock *sk;
-
-	DBG("conn %p, state %d, err %d", conn, conn->state, err);
-
-	l2cap_conn_clear_timer(conn);
-	__l2cap_conn_unlink(conn->iff, conn);
-
-	conn->state = BT_CLOSED;
-
-	if (conn->rx_skb)
-		kfree_skb(conn->rx_skb);
-
-	/* Kill channels */
-	while ((sk = conn->chan_list.head)) {
-		bh_lock_sock(sk);
-		l2cap_sock_clear_timer(sk);
-		l2cap_chan_del(sk, err);
-		bh_unlock_sock(sk);
-
-		l2cap_sock_kill(sk);
-	}
-
-	kfree(conn);
-
-	MOD_DEC_USE_COUNT;
-	return 0;
-}
-
-static inline struct l2cap_conn *l2cap_get_conn_by_addr(struct l2cap_iff *iff, bdaddr_t *dst)
-{
-	struct list_head *p;
-
-	list_for_each(p, &iff->conn_list) {
-		struct l2cap_conn *c;
-
-		c = list_entry(p, struct l2cap_conn, list);
-		if (!bacmp(&c->dst, dst))
-			return c;
-	}
-	return NULL;
-}
-
-int l2cap_connect(struct sock *sk)
-{
-	bdaddr_t *src = &l2cap_pi(sk)->src;
-	bdaddr_t *dst = &l2cap_pi(sk)->dst;
-	struct l2cap_conn *conn;
-	struct l2cap_iff *iff;
-	int err = 0;
-
-	DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
-
-	read_lock_bh(&l2cap_rt_lock);
-
-	/* Get route to remote BD address */
-	if (!(iff = l2cap_get_route(src, dst))) {
-		err = -EHOSTUNREACH;
-		goto done;
-	}
-
-	/* Update source addr of the socket */
-	bacpy(src, iff->bdaddr);
-
-	l2cap_iff_lock(iff);
-
-	if (!(conn = l2cap_get_conn_by_addr(iff, dst))) {
-		/* Connection doesn't exist */
-		if (!(conn = l2cap_conn_add(iff, dst))) {
-			l2cap_iff_unlock(iff);
-			err = -ENOMEM;
-			goto done;
-		}
-		conn->out = 1;
-	}
-
-	l2cap_iff_unlock(iff);
-
-	l2cap_chan_add(conn, sk, NULL);
-
-	sk->state = BT_CONNECT;
-	l2cap_sock_set_timer(sk, sk->sndtimeo);
-
-	switch (conn->state) {
-	case BT_CONNECTED:
-		if (sk->type == SOCK_SEQPACKET) {
-			l2cap_conn_req req;
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			req.psm  = l2cap_pi(sk)->psm;
-			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
-		} else {
-			l2cap_sock_clear_timer(sk);
-			sk->state = BT_CONNECTED;
-		}
-		break;
-
-	case BT_CONNECT:
-		break;
-
-	default:
-		/* Create ACL connection */
-		conn->state = BT_CONNECT;
-		hci_connect(iff->hdev, dst);
-		break;
-	};
-
-done:
-	read_unlock_bh(&l2cap_rt_lock);
-	return err;
-}
-
-/* ------ Channel queues for listening sockets ------ */
-void l2cap_accept_queue(struct sock *parent, struct sock *sk)
-{
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-
-	DBG("parent %p, sk %p", parent, sk);
-
-	sock_hold(sk);
-	l2cap_pi(sk)->parent = parent;
-	l2cap_pi(sk)->next_q = NULL;
-
-	if (!q->head) {
-		q->head = q->tail = sk;
-	} else {
-		struct sock *tail = q->tail;
-
-		l2cap_pi(sk)->prev_q = tail;
-		l2cap_pi(tail)->next_q = sk;
-		q->tail = sk;
-	}
-
-	parent->ack_backlog++;
-}
-
-void l2cap_accept_unlink(struct sock *sk)
-{
-	struct sock *parent = l2cap_pi(sk)->parent;
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-	struct sock *next, *prev;
-
-	DBG("sk %p", sk);
-
-	next = l2cap_pi(sk)->next_q;
-	prev = l2cap_pi(sk)->prev_q;
-
-	if (sk == q->head)
-		q->head = next;
-	if (sk == q->tail)
-		q->tail = prev;
-
-	if (next)
-		l2cap_pi(next)->prev_q = prev;
-	if (prev)
-		l2cap_pi(prev)->next_q = next;
-
-	l2cap_pi(sk)->parent = NULL;
-
-	parent->ack_backlog--;
-	__sock_put(sk);
-}
-
-/* Get next connected channel in queue. */
-struct sock *l2cap_accept_dequeue(struct sock *parent, int state)
-{
-	struct l2cap_accept_q *q = &l2cap_pi(parent)->accept_q;
-	struct sock *sk;
-
-	for (sk = q->head; sk; sk = l2cap_pi(sk)->next_q) {
-		if (!state || sk->state == state) {
-			l2cap_accept_unlink(sk);
-			break;
-		}
-	}
-
-	DBG("parent %p, sk %p", parent, sk);
-
-	return sk;
-}
-
-/* -------- Socket interface ---------- */
-static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
-{
-	bdaddr_t *src = &addr->l2_bdaddr;
-	__u16 psm = addr->l2_psm;
-	struct sock *sk;
-
-	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
-		if (l2cap_pi(sk)->psm == psm &&
-		    !bacmp(&l2cap_pi(sk)->src, src))
-			break;
-	}
-
-	return sk;
-}
-
-/* Find socket listening on psm and source bdaddr.
- * Returns closest match.
- */
-static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
-{
-	struct sock *sk, *sk1 = NULL;
-
-	read_lock(&l2cap_sk_list.lock);
-
-	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
-		struct l2cap_pinfo *pi;
-
-		if (sk->state != BT_LISTEN)
-			continue;
-
-		pi = l2cap_pi(sk);
-
-		if (pi->psm == psm) {
-			/* Exact match. */
-			if (!bacmp(&pi->src, src))
-				break;
-
-			/* Closest match */
-			if (!bacmp(&pi->src, BDADDR_ANY))
-				sk1 = sk;
-		}
-	}
-
-	read_unlock(&l2cap_sk_list.lock);
-
-	return sk ? sk : sk1;
-}
-
-static void l2cap_sock_destruct(struct sock *sk)
-{
-	DBG("sk %p", sk);
-
-	skb_queue_purge(&sk->receive_queue);
-	skb_queue_purge(&sk->write_queue);
-
-	MOD_DEC_USE_COUNT;
-}
-
-static void l2cap_sock_cleanup_listen(struct sock *parent)
-{
-	struct sock *sk;
-
-	DBG("parent %p", parent);
-
-	/* Close not yet accepted channels */
-	while ((sk = l2cap_accept_dequeue(parent, 0)))
-		l2cap_sock_close(sk);
-
-	parent->state  = BT_CLOSED;
-	parent->zapped = 1;
-}
-
-/* Kill socket (only if zapped and orphan)
- * Must be called on unlocked socket.
- */
-static void l2cap_sock_kill(struct sock *sk)
-{
-	if (!sk->zapped || sk->socket)
-		return;
-
-	DBG("sk %p state %d", sk, sk->state);
-
-	/* Kill poor orphan */
-	bluez_sock_unlink(&l2cap_sk_list, sk);
-	sk->dead = 1;
-	sock_put(sk);
-}
-
-/* Close socket.
- * Must be called on unlocked socket.
- */
-static void l2cap_sock_close(struct sock *sk)
-{
-	struct l2cap_conn *conn;
-
-	l2cap_sock_clear_timer(sk);
-
-	lock_sock(sk);
-
-	conn = l2cap_pi(sk)->conn;
-
-	DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
-
-	switch (sk->state) {
-	case BT_LISTEN:
-		l2cap_sock_cleanup_listen(sk);
-		break;
-
-	case BT_CONNECTED:
-	case BT_CONFIG:
-		if (sk->type == SOCK_SEQPACKET) {
-			l2cap_disconn_req req;
-
-			sk->state = BT_DISCONN;
-
-			req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
-
-			l2cap_sock_set_timer(sk, sk->sndtimeo);
-		} else {
-			l2cap_chan_del(sk, ECONNRESET);
-		}
-		break;
-
-	case BT_CONNECT:
-	case BT_DISCONN:
-		l2cap_chan_del(sk, ECONNRESET);
-		break;
-
-	default:
-		sk->zapped = 1;
-		break;
-	};
-
-	release_sock(sk);
-
-	l2cap_sock_kill(sk);
-}
-
-static void l2cap_sock_init(struct sock *sk, struct sock *parent)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-
-	DBG("sk %p", sk);
-
-	if (parent) {
-		sk->type = parent->type;
-
-		pi->imtu = l2cap_pi(parent)->imtu;
-		pi->omtu = l2cap_pi(parent)->omtu;
-	} else {
-		pi->imtu = L2CAP_DEFAULT_MTU;
-		pi->omtu = 0;
-	}
-
-	/* Default config options */
-	pi->conf_mtu = L2CAP_DEFAULT_MTU;
-	pi->flush_to = L2CAP_DEFAULT_FLUSH_TO;
-}
-
-static struct sock *l2cap_sock_alloc(struct socket *sock, int proto, int prio)
-{
-	struct sock *sk;
-
-	if (!(sk = sk_alloc(PF_BLUETOOTH, prio, 1, NULL)))
-		return NULL;
-
-	sock_init_data(sock, sk);
-
-	sk->zapped   = 0;
-
-	sk->destruct = l2cap_sock_destruct;
-	sk->sndtimeo = L2CAP_CONN_TIMEOUT;
-
-	sk->protocol = proto;
-	sk->state    = BT_OPEN;
-
-	l2cap_sock_init_timer(sk);
-
-	bluez_sock_link(&l2cap_sk_list, sk);
-
-	MOD_INC_USE_COUNT;
-
-	return sk;
-}
-
-static int l2cap_sock_create(struct socket *sock, int protocol)
-{
-	struct sock *sk;
-
-	DBG("sock %p", sock);
-
-	sock->state = SS_UNCONNECTED;
-
-	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
-		return -ESOCKTNOSUPPORT;
-
-	sock->ops = &l2cap_sock_ops;
-
-	if (!(sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL)))
-		return -ENOMEM;
-
-	l2cap_sock_init(sk, NULL);
-
-	return 0;
-}
-
-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
-
-	if (!addr || addr->sa_family != AF_BLUETOOTH)
-		return -EINVAL;
-
-	lock_sock(sk);
-
-	if (sk->state != BT_OPEN) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	write_lock(&l2cap_sk_list.lock);
-
-	if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
-		err = -EADDRINUSE;
-		goto unlock;
-	}
-
-	/* Save source address */
-	bacpy(&l2cap_pi(sk)->src, &la->l2_bdaddr);
-	l2cap_pi(sk)->psm = la->l2_psm;
-	sk->state = BT_BOUND;
-
-unlock:
-	write_unlock(&l2cap_sk_list.lock);
-
-done:
-	release_sock(sk);
-
-	return err;
-}
-
-static int l2cap_sock_w4_connect(struct sock *sk, int flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	long timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
-	int err = 0;
-
-	DBG("sk %p", sk);
-
-	add_wait_queue(sk->sleep, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-
-	while (sk->state != BT_CONNECTED) {
-		if (!timeo) {
-			err = -EAGAIN;
-			break;
-		}
-
-		release_sock(sk);
-		timeo = schedule_timeout(timeo);
-		lock_sock(sk);
-
-		err = 0;
-		if (sk->state == BT_CONNECTED)
-			break;
-
-		if (sk->err) {
-			err = sock_error(sk);
-			break;
-		}
-
-		if (signal_pending(current)) {
-			err = sock_intr_errno(timeo);
-			break;
-		}
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sleep, &wait);
-
-	return err;
-}
-
-static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	lock_sock(sk);
-
-	DBG("sk %p", sk);
-
-	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	if (sk->state != BT_OPEN && sk->state != BT_BOUND) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	if (sk->type == SOCK_SEQPACKET && !la->l2_psm) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	/* Set destination address and psm */
-	bacpy(&l2cap_pi(sk)->dst, &la->l2_bdaddr);
-	l2cap_pi(sk)->psm = la->l2_psm;
-
-	if ((err = l2cap_connect(sk)))
-		goto done;
-
-	err = l2cap_sock_w4_connect(sk, flags);
-
-done:
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_listen(struct socket *sock, int backlog)
-{
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sk %p backlog %d", sk, backlog);
-
-	lock_sock(sk);
-
-	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	if (!l2cap_pi(sk)->psm) {
-		err = -EINVAL;
-		goto done;
-	}
-
-	sk->max_ack_backlog = backlog;
-	sk->ack_backlog = 0;
-	sk->state = BT_LISTEN;
-
-done:
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int flags)
-{
-	DECLARE_WAITQUEUE(wait, current);
-	struct sock *sk = sock->sk, *ch;
-	long timeo;
-	int err = 0;
-
-	lock_sock(sk);
-
-	if (sk->state != BT_LISTEN) {
-		err = -EBADFD;
-		goto done;
-	}
-
-	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
-
-	DBG("sk %p timeo %ld", sk, timeo);
-
-	/* Wait for an incoming connection. (wake-one). */
-	add_wait_queue_exclusive(sk->sleep, &wait);
-	current->state = TASK_INTERRUPTIBLE;
-	while (!(ch = l2cap_accept_dequeue(sk, BT_CONNECTED))) {
-		if (!timeo) {
-			err = -EAGAIN;
-			break;
-		}
-
-		release_sock(sk);
-		timeo = schedule_timeout(timeo);
-		lock_sock(sk);
-
-		if (sk->state != BT_LISTEN) {
-			err = -EBADFD;
-			break;
-		}
-
-		if (signal_pending(current)) {
-			err = sock_intr_errno(timeo);
-			break;
-		}
-	}
-	current->state = TASK_RUNNING;
-	remove_wait_queue(sk->sleep, &wait);
-
-	if (err)
-		goto done;
-
-	sock_graft(ch, newsock);
-	newsock->state = SS_CONNECTED;
-
-	DBG("new socket %p", ch);
-
-done:
-	release_sock(sk);
-
-	return err;
-}
-
-static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
-{
-	struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
-	struct sock *sk = sock->sk;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	addr->sa_family = AF_BLUETOOTH;
-	*len = sizeof(struct sockaddr_l2);
-
-	if (peer)
-		bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->dst);
-	else
-		bacpy(&la->l2_bdaddr, &l2cap_pi(sk)->src);
-
-	la->l2_psm = l2cap_pi(sk)->psm;
-
-	return 0;
-}
-
-static int l2cap_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	int err = 0;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (sk->err)
-		return sock_error(sk);
-
-	if (msg->msg_flags & MSG_OOB)
-		return -EOPNOTSUPP;
-
-	lock_sock(sk);
-
-	if (sk->state == BT_CONNECTED)
-		err = l2cap_chan_send(sk, msg, len);
-	else
-		err = -ENOTCONN;
-
-	release_sock(sk);
-	return err;
-}
-
-static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, int len, int flags, struct scm_cookie *scm)
-{
-	struct sock *sk = sock->sk;
-	int noblock = flags & MSG_DONTWAIT;
-	int copied, err;
-	struct sk_buff *skb;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (flags & (MSG_OOB))
-		return -EOPNOTSUPP;
-
-	if (sk->state == BT_CLOSED)
-		return 0;
-
-	if (!(skb = skb_recv_datagram(sk, flags, noblock, &err)))
-		return err;
-
-	msg->msg_namelen = 0;
-
-	copied = skb->len;
-	if (len < copied) {
-		msg->msg_flags |= MSG_TRUNC;
-		copied = len;
-	}
-
-	skb->h.raw = skb->data;
-	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
-
-	skb_free_datagram(sk, skb);
-
-	return err ? : copied;
-}
-
-int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_options opts;
-	int err = 0;
-
-	DBG("sk %p", sk);
-
-	lock_sock(sk);
-
-	switch (optname) {
-	case L2CAP_OPTIONS:
-		if (copy_from_user((char *)&opts, optval, optlen)) {
-			err = -EFAULT;
-			break;
-		}
-		l2cap_pi(sk)->imtu = opts.imtu;
-		l2cap_pi(sk)->omtu = opts.omtu;
-		break;
-
-	default:
-		err = -ENOPROTOOPT;
-		break;
-	};
-
-	release_sock(sk);
-	return err;
-}
-
-int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_options opts;
-	struct l2cap_conninfo cinfo;
-	int len, err = 0; 
-
-	if (get_user(len, optlen))
-		return -EFAULT;
-
-	lock_sock(sk);
-
-	switch (optname) {
-	case L2CAP_OPTIONS:
-		opts.imtu     = l2cap_pi(sk)->imtu;
-		opts.omtu     = l2cap_pi(sk)->omtu;
-		opts.flush_to = l2cap_pi(sk)->flush_to;
-
-		len = MIN(len, sizeof(opts));
-		if (copy_to_user(optval, (char *)&opts, len))
-			err = -EFAULT;
-
-		break;
-
-	case L2CAP_CONNINFO:
-		if (sk->state != BT_CONNECTED) {
-			err = -ENOTCONN;
-			break;
-		}
-
-		cinfo.hci_handle = l2cap_pi(sk)->conn->hconn->handle;
-
-		len = MIN(len, sizeof(cinfo));
-		if (copy_to_user(optval, (char *)&cinfo, len))
-			err = -EFAULT;
-
-		break;
-
-	default:
-		err = -ENOPROTOOPT;
-		break;
-	};
-
-	release_sock(sk);
-	return err;
-}
-
-static unsigned int l2cap_sock_poll(struct file * file, struct socket *sock, poll_table *wait)
-{
-	struct sock *sk = sock->sk;
-	struct l2cap_accept_q *aq;
-	unsigned int mask;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	poll_wait(file, sk->sleep, wait);
-	mask = 0;
-
-	if (sk->err || !skb_queue_empty(&sk->error_queue))
-		mask |= POLLERR;
-
-	if (sk->shutdown == SHUTDOWN_MASK)
-		mask |= POLLHUP;
-
-	aq = &l2cap_pi(sk)->accept_q;
-	if (!skb_queue_empty(&sk->receive_queue) || aq->head || (sk->shutdown & RCV_SHUTDOWN))
-		mask |= POLLIN | POLLRDNORM;
-
-	if (sk->state == BT_CLOSED)
-		mask |= POLLHUP;
-
-	if (sock_writeable(sk))
-		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
-	else
-		set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
-
-	return mask;
-}
-
-static int l2cap_sock_release(struct socket *sock)
-{
-	struct sock *sk = sock->sk;
-
-	DBG("sock %p, sk %p", sock, sk);
-
-	if (!sk)
-		return 0;
-
-	sock_orphan(sk);
-
-	l2cap_sock_close(sk);
-
-	return 0;
-}
-
-/* --------- L2CAP channels --------- */
-static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->dcid == cid)
-			break;
-	}
-
-	return s;
-}
-
-static inline struct sock *l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_dcid(l, cid);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->scid == cid)
-			break;
-	}
-
-	return s;
-}
-static inline struct sock *l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_scid(l, cid);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static struct sock *__l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
-{
-	struct sock *s;
-
-	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
-		if (l2cap_pi(s)->ident == ident)
-			break;
-	}
-
-	return s;
-}
-
-static inline struct sock *l2cap_get_chan_by_ident(struct l2cap_chan_list *l, __u8 ident)
-{
-	struct sock *s;
-
-	read_lock(&l->lock);
-	s = __l2cap_get_chan_by_ident(l, ident);
-	read_unlock(&l->lock);
-
-	return s;
-}
-
-static __u16 l2cap_alloc_cid(struct l2cap_chan_list *l)
-{
-	__u16 cid = 0x0040;
-
-	for (; cid < 0xffff; cid++) {
-		if(!__l2cap_get_chan_by_scid(l, cid))
-			return cid;
-	}
-
-	return 0;
-}
-
-static inline void __l2cap_chan_link(struct l2cap_chan_list *l, struct sock *sk)
-{
-	sock_hold(sk);
-
-	if (l->head)
-		l2cap_pi(l->head)->prev_c = sk;
-
-	l2cap_pi(sk)->next_c = l->head;
-	l2cap_pi(sk)->prev_c = NULL;
-	l->head = sk;
-}
-
-static inline void l2cap_chan_unlink(struct l2cap_chan_list *l, struct sock *sk)
-{
-	struct sock *next = l2cap_pi(sk)->next_c, *prev = l2cap_pi(sk)->prev_c;
-
-	write_lock(&l->lock);
-	if (sk == l->head)
-		l->head = next;
-
-	if (next)
-		l2cap_pi(next)->prev_c = prev;
-	if (prev)
-		l2cap_pi(prev)->next_c = next;
-	write_unlock(&l->lock);
-
-	__sock_put(sk);
-}
-
-static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-
-	DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
-
-	l2cap_conn_clear_timer(conn);
-
-	atomic_inc(&conn->refcnt);
-	l2cap_pi(sk)->conn = conn;
-
-	if (sk->type == SOCK_SEQPACKET) {
-		/* Alloc CID for normal socket */
-		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
-	} else {
-		/* Raw socket can send only signalling messages */
-		l2cap_pi(sk)->scid = 0x0001;
-		l2cap_pi(sk)->dcid = 0x0001;
-		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
-	}
-
-	__l2cap_chan_link(l, sk);
-
-	if (parent)
-		l2cap_accept_queue(parent, sk);
-}
-
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-
-	write_lock(&l->lock);
-	__l2cap_chan_add(conn, sk, parent);
-	write_unlock(&l->lock);
-}
-
-/* Delete channel. 
- * Must be called on the locked socket. */
-static void l2cap_chan_del(struct sock *sk, int err)
-{
-	struct l2cap_conn *conn;
-	struct sock *parent;
-
-	conn = l2cap_pi(sk)->conn;
-	parent = l2cap_pi(sk)->parent;
-
-	DBG("sk %p, conn %p, err %d", sk, conn, err);
-
-	if (parent) {
-		/* Unlink from parent accept queue */
-		bh_lock_sock(parent);
-		l2cap_accept_unlink(sk);
-		bh_unlock_sock(parent);
-	}
-
-	if (conn) { 
-		long timeout;
-
-		/* Unlink from channel list */
-		l2cap_chan_unlink(&conn->chan_list, sk);
-		l2cap_pi(sk)->conn = NULL;
-
-		if (conn->out)
-			timeout = L2CAP_DISCONN_TIMEOUT;
-		else
-			timeout = L2CAP_CONN_IDLE_TIMEOUT;
-		
-		if (atomic_dec_and_test(&conn->refcnt) && conn->state == BT_CONNECTED) {
-			/* Schedule Baseband disconnect */
-			l2cap_conn_set_timer(conn, timeout);
-		}
-	}
-
-	sk->state  = BT_CLOSED;
-	sk->err    = err;
-	sk->state_change(sk);
-
-	sk->zapped = 1;
-}
-
-static void l2cap_conn_ready(struct l2cap_conn *conn)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-	struct sock *sk;
-
-	DBG("conn %p", conn);
-
-	read_lock(&l->lock);
-
-	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
-		bh_lock_sock(sk);
-
-		if (sk->type != SOCK_SEQPACKET) {
-			sk->state = BT_CONNECTED;
-			sk->state_change(sk);
-			l2cap_sock_clear_timer(sk);
-		} else if (sk->state == BT_CONNECT) {
-			l2cap_conn_req req;
-			req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-			req.psm  = l2cap_pi(sk)->psm;
-			l2cap_send_req(conn, L2CAP_CONN_REQ, L2CAP_CONN_REQ_SIZE, &req);
-
-			l2cap_sock_set_timer(sk, sk->sndtimeo);
-		}
-
-		bh_unlock_sock(sk);
-	}
-
-	read_unlock(&l->lock);
-}
-
-static void l2cap_chan_ready(struct sock *sk)
-{
-	struct sock *parent = l2cap_pi(sk)->parent;
-
-	DBG("sk %p, parent %p", sk, parent);
-
-	l2cap_pi(sk)->conf_state = 0;
-	l2cap_sock_clear_timer(sk);
-
-	if (!parent) {
-		/* Outgoing channel.
-		 * Wake up socket sleeping on connect.
-		 */
-		sk->state = BT_CONNECTED;
-		sk->state_change(sk);
-	} else {
-		/* Incomming channel.
-		 * Wake up socket sleeping on accept.
-		 */
-		parent->data_ready(parent, 1);
-	}
-}
-
-/* Copy frame to all raw sockets on that connection */
-void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-	struct sk_buff *nskb;
-	struct sock * sk;
-
-	DBG("conn %p", conn);
-
-	read_lock(&l->lock);
-	for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
-		if (sk->type != SOCK_RAW)
-			continue;
-
-		/* Don't send frame to the socket it came from */
-		if (skb->sk == sk)
-			continue;
-
-		if (!(nskb = skb_clone(skb, GFP_ATOMIC)))
-			continue;
-
-		skb_queue_tail(&sk->receive_queue, nskb);
-		sk->data_ready(sk, nskb->len);
-	}
-	read_unlock(&l->lock);
-}
-
-static int l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len)
-{
-	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
-	struct sk_buff *skb, **frag;
-	int err, size, count, sent=0;
-	l2cap_hdr *lh;
-
-	/* Check outgoing MTU */
-	if (len > l2cap_pi(sk)->omtu)
-		return -EINVAL;
-
-	DBG("sk %p len %d", sk, len);
-
-	/* First fragment (with L2CAP header) */
-	count = MIN(conn->iff->mtu - L2CAP_HDR_SIZE, len);
-	size  = L2CAP_HDR_SIZE + count;
-	if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
-		return err;
-
-	/* Create L2CAP header */
-	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(len);
-	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-
-	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
-		err = -EFAULT;
-		goto fail;
-	}
-
-	sent += count;
-	len  -= count;
-
-	/* Continuation fragments (no L2CAP header) */
-	frag = &skb_shinfo(skb)->frag_list;
-	while (len) {
-		count = MIN(conn->iff->mtu, len);
-
-		*frag = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
-		if (!*frag)
-			goto fail;
-		
-		if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
-			err = -EFAULT;
-			goto fail;
-		}
-
-		sent += count;
-		len  -= count;
-
-		frag = &(*frag)->next;
-	}
-
-	if ((err = hci_send_acl(conn->hconn, skb, 0)) < 0)
-		goto fail;
-
-	return sent;
-
-fail:
-	kfree_skb(skb);
-	return err;
-}
-
-/* --------- L2CAP signalling commands --------- */
-static inline __u8 l2cap_get_ident(struct l2cap_conn *conn)
-{
-	__u8 id;
-
-	/* Get next available identificator.
-	 *    1 - 199 are used by kernel.
-	 *  200 - 254 are used by utilities like l2ping, etc 
-	 */
-
-	spin_lock(&conn->lock);
-
-	if (++conn->tx_ident > 199)
-		conn->tx_ident = 1;
-
-	id = conn->tx_ident;
-
-	spin_unlock(&conn->lock);
-
-	return id;
-}
-
-static inline struct sk_buff *l2cap_build_cmd(__u8 code, __u8 ident, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-	l2cap_cmd_hdr *cmd;
-	l2cap_hdr *lh;
-	int size;
-
-	DBG("code 0x%2.2x, ident 0x%2.2x, len %d", code, ident, len);
-
-	size = L2CAP_HDR_SIZE + L2CAP_CMD_HDR_SIZE + len;
-	if (!(skb = bluez_skb_alloc(size, GFP_ATOMIC)))
-		return NULL;
-
-	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(L2CAP_CMD_HDR_SIZE + len);
-	lh->cid = __cpu_to_le16(0x0001);
-
-	cmd = (l2cap_cmd_hdr *) skb_put(skb, L2CAP_CMD_HDR_SIZE);
-	cmd->code  = code;
-	cmd->ident = ident;
-	cmd->len   = __cpu_to_le16(len);
-
-	if (len)
-		memcpy(skb_put(skb, len), data, len);
-
-	return skb;
-}
-
-static int l2cap_send_req(struct l2cap_conn *conn, __u8 code, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-	__u8 ident;
-
-	DBG("code 0x%2.2x", code);
-
-	ident = l2cap_get_ident(conn);
-	if (!(skb = l2cap_build_cmd(code, ident, len, data)))
-		return -ENOMEM;
-	return hci_send_acl(conn->hconn, skb, 0);
-}
-
-static int l2cap_send_rsp(struct l2cap_conn *conn, __u8 ident, __u8 code, __u16 len, void *data)
-{
-	struct sk_buff *skb;
-
-	DBG("code 0x%2.2x", code);
-
-	if (!(skb = l2cap_build_cmd(code, ident, len, data)))
-		return -ENOMEM;
-	return hci_send_acl(conn->hconn, skb, 0);
-}
-
-static inline int l2cap_get_conf_opt(__u8 **ptr, __u8 *type, __u32 *val)
-{
-	l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
-	int len;
-
-	*type = opt->type;
-	switch (opt->len) {
-	case 1:
-		*val = *((__u8 *) opt->val);
-		break;
-
-	case 2:
-		*val = __le16_to_cpu(*((__u16 *)opt->val));
-		break;
-
-	case 4:
-		*val = __le32_to_cpu(*((__u32 *)opt->val));
-		break;
-
-	default:
-		*val = 0L;
-		break;
-	};
-
-	DBG("type 0x%2.2x len %d val 0x%8.8x", *type, opt->len, *val);
-
-	len = L2CAP_CONF_OPT_SIZE + opt->len;
-
-	*ptr += len;
-
-	return len;
-}
-
-static inline void l2cap_parse_conf_req(struct sock *sk, char *data, int len)
-{
-	__u8 type, hint; __u32 val;
-	__u8 *ptr = data;
-
-	DBG("sk %p len %d", sk, len);
-
-	while (len >= L2CAP_CONF_OPT_SIZE) {
-		len -= l2cap_get_conf_opt(&ptr, &type, &val);
-
-		hint  = type & 0x80;
-		type &= 0x7f;
-
-		switch (type) {
-		case L2CAP_CONF_MTU:
-			l2cap_pi(sk)->conf_mtu = val;
-			break;
-
-		case L2CAP_CONF_FLUSH_TO:
-			l2cap_pi(sk)->flush_to = val;
-			break;
-
-		case L2CAP_CONF_QOS:
-			break;
-		
-		default:
-			if (hint)
-				break;
-
-			/* FIXME: Reject unknon option */
-			break;
-		};
-	}
-}
-
-static inline void l2cap_add_conf_opt(__u8 **ptr, __u8 type, __u8 len, __u32 val)
-{
-	register l2cap_conf_opt *opt = (l2cap_conf_opt *) (*ptr);
-
-	DBG("type 0x%2.2x len %d val 0x%8.8x", type, len, val);
-
-	opt->type = type;
-	opt->len  = len;
-	switch (len) {
-	case 1:
-		*((__u8 *) opt->val)  = val;
-		break;
-
-	case 2:
-		*((__u16 *) opt->val) = __cpu_to_le16(val);
-		break;
-
-	case 4:
-		*((__u32 *) opt->val) = __cpu_to_le32(val);
-		break;
-	};
-
-	*ptr += L2CAP_CONF_OPT_SIZE + len;
-}
-
-static int l2cap_build_conf_req(struct sock *sk, __u8 *data)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-	l2cap_conf_req *req = (l2cap_conf_req *) data;
-	__u8 *ptr = req->data;
-
-	DBG("sk %p", sk);
-
-	if (pi->imtu != L2CAP_DEFAULT_MTU)
-		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
-
-	/* FIXME. Need actual value of the flush timeout */
-	//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
-	//   l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO, 2, pi->flush_to);
-
-	req->dcid  = __cpu_to_le16(pi->dcid);
-	req->flags = __cpu_to_le16(0);
-
-	return ptr - data;
-}
-
-static int l2cap_conf_output(struct sock *sk, __u8 **ptr)
-{
-	struct l2cap_pinfo *pi = l2cap_pi(sk);
-	int result = 0;
-
-	/* Configure output options and let other side know
-	 * which ones we don't like.
-	 */
-	if (pi->conf_mtu < pi->omtu) {
-		l2cap_add_conf_opt(ptr, L2CAP_CONF_MTU, 2, l2cap_pi(sk)->omtu);
-		result = L2CAP_CONF_UNACCEPT;
-	} else {
-		pi->omtu = pi->conf_mtu;
-	}
-
-	DBG("sk %p result %d", sk, result);
-	return result;
-}
-
-static int l2cap_build_conf_rsp(struct sock *sk, __u8 *data, int *result)
-{
-	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *) data;
-	__u8 *ptr = rsp->data;
-
-	DBG("sk %p complete %d", sk, result ? 1 : 0);
-
-	if (result)
-		*result = l2cap_conf_output(sk, &ptr);
-
-	rsp->scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	rsp->result = __cpu_to_le16(result ? *result : 0);
-	rsp->flags  = __cpu_to_le16(0);
-
-	return ptr - data;
-}
-
-static inline int l2cap_connect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	struct l2cap_chan_list *list = &conn->chan_list;
-	l2cap_conn_req *req = (l2cap_conn_req *) data;
-	l2cap_conn_rsp rsp;
-	struct sock *sk, *parent;
-
-	__u16 scid = __le16_to_cpu(req->scid);
-	__u16 psm  = req->psm;
-
-	DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
-
-	/* Check if we have socket listening on psm */
-	if (!(parent = l2cap_get_sock_listen(&conn->src, psm)))
-		goto reject;
-
-	bh_lock_sock(parent);
-	write_lock(&list->lock);
-
-	/* Check if we already have channel with that dcid */
-	if (__l2cap_get_chan_by_dcid(list, scid))
-		goto unlock;
-
-	/* Check for backlog size */
-	if (parent->ack_backlog > parent->max_ack_backlog)
-		goto unlock;
-
-	if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
-		goto unlock;
-
-	l2cap_sock_init(sk, parent);
-
-	bacpy(&l2cap_pi(sk)->src, &conn->src);
-	bacpy(&l2cap_pi(sk)->dst, &conn->dst);
-	l2cap_pi(sk)->psm  = psm;
-	l2cap_pi(sk)->dcid = scid;
-
-	__l2cap_chan_add(conn, sk, parent);
-	sk->state = BT_CONFIG;
-
-	write_unlock(&list->lock);
-	bh_unlock_sock(parent);
-
-	rsp.dcid   = __cpu_to_le16(l2cap_pi(sk)->scid);
-	rsp.scid   = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	rsp.result = __cpu_to_le16(0);
-	rsp.status = __cpu_to_le16(0);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
-
-	return 0;
-
-unlock:
-	write_unlock(&list->lock);
-	bh_unlock_sock(parent);
-
-reject:
-	rsp.scid   = __cpu_to_le16(scid);
-	rsp.dcid   = __cpu_to_le16(0);
-	rsp.status = __cpu_to_le16(0);
-	rsp.result = __cpu_to_le16(L2CAP_CONN_NO_MEM);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONN_RSP, L2CAP_CONN_RSP_SIZE, &rsp);
-
-	return 0;
-}
-
-static inline int l2cap_connect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conn_rsp *rsp = (l2cap_conn_rsp *) data;
-	__u16 scid, dcid, result, status;
-	struct sock *sk;
-
-	scid   = __le16_to_cpu(rsp->scid);
-	dcid   = __le16_to_cpu(rsp->dcid);
-	result = __le16_to_cpu(rsp->result);
-	status = __le16_to_cpu(rsp->status);
-
-	DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", dcid, scid, result, status);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	if (!result) {
-		char req[64];
-
-		sk->state = BT_CONFIG;
-		l2cap_pi(sk)->dcid = dcid;
-		l2cap_pi(sk)->conf_state |= CONF_REQ_SENT;
-
-		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
-	} else {
-		l2cap_chan_del(sk, ECONNREFUSED);
-	}
-
-	bh_unlock_sock(sk);
-	return 0;
-}
-
-static inline int l2cap_config_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conf_req * req = (l2cap_conf_req *) data;
-	__u16 dcid, flags;
-	__u8 rsp[64];
-	struct sock *sk;
-	int result;
-
-	dcid  = __le16_to_cpu(req->dcid);
-	flags = __le16_to_cpu(req->flags);
-
-	DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	l2cap_parse_conf_req(sk, req->data, cmd->len - L2CAP_CONF_REQ_SIZE);
-
-	if (flags & 0x01) {
-		/* Incomplete config. Send empty response. */
-		l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, NULL), rsp);
-		goto unlock;
-	}
-
-	/* Complete config. */
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_CONF_RSP, l2cap_build_conf_rsp(sk, rsp, &result), rsp);
-
-	if (result)
-		goto unlock;
-
-	/* Output config done */
-	l2cap_pi(sk)->conf_state |= CONF_OUTPUT_DONE;
-
-	if (l2cap_pi(sk)->conf_state & CONF_INPUT_DONE) {
-		sk->state = BT_CONNECTED;
-		l2cap_chan_ready(sk);
-	} else if (!(l2cap_pi(sk)->conf_state & CONF_REQ_SENT)) {
-		char req[64];
-		l2cap_send_req(conn, L2CAP_CONF_REQ, l2cap_build_conf_req(sk, req), req);
-	}
-
-unlock:
-	bh_unlock_sock(sk);
-
-	return 0;
-}
-
-static inline int l2cap_config_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_conf_rsp *rsp = (l2cap_conf_rsp *)data;
-	__u16 scid, flags, result;
-	struct sock *sk;
-	int err = 0;
-
-	scid   = __le16_to_cpu(rsp->scid);
-	flags  = __le16_to_cpu(rsp->flags);
-	result = __le16_to_cpu(rsp->result);
-
-	DBG("scid 0x%4.4x flags 0x%2.2x result 0x%2.2x", scid, flags, result);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-
-	if (result) {
-		l2cap_disconn_req req;
-
-		/* They didn't like our options. Well... we do not negotiate.
-		 * Close channel.
-		 */
-		sk->state = BT_DISCONN;
-
-		req.dcid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-		req.scid = __cpu_to_le16(l2cap_pi(sk)->scid);
-		l2cap_send_req(conn, L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ_SIZE, &req);
-
-		l2cap_sock_set_timer(sk, sk->sndtimeo);
-		goto done;
-	}
-
-	if (flags & 0x01)
-		goto done;
-
-	/* Input config done */
-	l2cap_pi(sk)->conf_state |= CONF_INPUT_DONE;
-
-	if (l2cap_pi(sk)->conf_state & CONF_OUTPUT_DONE) {
-		sk->state = BT_CONNECTED;
-		l2cap_chan_ready(sk);
-	}
-
-done:
-	bh_unlock_sock(sk);
-
-	return err;
-}
-
-static inline int l2cap_disconnect_req(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_disconn_req *req = (l2cap_disconn_req *) data;
-	l2cap_disconn_rsp rsp;
-	__u16 dcid, scid;
-	struct sock *sk;
-
-	scid = __le16_to_cpu(req->scid);
-	dcid = __le16_to_cpu(req->dcid);
-
-	DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, dcid)))
-		return 0;
-
-	bh_lock_sock(sk);
-
-	rsp.dcid = __cpu_to_le16(l2cap_pi(sk)->scid);
-	rsp.scid = __cpu_to_le16(l2cap_pi(sk)->dcid);
-	l2cap_send_rsp(conn, cmd->ident, L2CAP_DISCONN_RSP, L2CAP_DISCONN_RSP_SIZE, &rsp);
-
-	l2cap_chan_del(sk, ECONNRESET);
-
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-
-	return 0;
-}
-
-static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, l2cap_cmd_hdr *cmd, __u8 *data)
-{
-	l2cap_disconn_rsp *rsp = (l2cap_disconn_rsp *) data;
-	__u16 dcid, scid;
-	struct sock *sk;
-
-	scid = __le16_to_cpu(rsp->scid);
-	dcid = __le16_to_cpu(rsp->dcid);
-
-	DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid);
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, scid)))
-		return -ENOENT;
-
-	bh_lock_sock(sk);
-	l2cap_sock_clear_timer(sk);
-	l2cap_chan_del(sk, ECONNABORTED);
-	bh_unlock_sock(sk);
-
-	l2cap_sock_kill(sk);
-
-	return 0;
-}
-
-static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	__u8 *data = skb->data;
-	int len = skb->len;
-	l2cap_cmd_hdr cmd;
-	int err = 0;
-
-	while (len >= L2CAP_CMD_HDR_SIZE) {
-		memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE);
-		data += L2CAP_CMD_HDR_SIZE;
-		len  -= L2CAP_CMD_HDR_SIZE;
-
-		cmd.len = __le16_to_cpu(cmd.len);
-
-		DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd.len, cmd.ident);
-
-		if (cmd.len > len || !cmd.ident) {
-			DBG("corrupted command");
-			break;
-		}
-
-		switch (cmd.code) {
-		case L2CAP_CONN_REQ:
-			err = l2cap_connect_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONN_RSP:
-			err = l2cap_connect_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONF_REQ:
-			err = l2cap_config_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_CONF_RSP:
-			err = l2cap_config_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_DISCONN_REQ:
-			err = l2cap_disconnect_req(conn, &cmd, data);
-			break;
-
-		case L2CAP_DISCONN_RSP:
-			err = l2cap_disconnect_rsp(conn, &cmd, data);
-			break;
-
-		case L2CAP_COMMAND_REJ:
-			/* FIXME: We should process this */
-			l2cap_raw_recv(conn, skb);
-			break;
-
-		case L2CAP_ECHO_REQ:
-			l2cap_send_rsp(conn, cmd.ident, L2CAP_ECHO_RSP, cmd.len, data);
-			break;
-
-		case L2CAP_ECHO_RSP:
-		case L2CAP_INFO_REQ:
-		case L2CAP_INFO_RSP:
-			l2cap_raw_recv(conn, skb);
-			break;
-
-		default:
-			ERR("Uknown signaling command 0x%2.2x", cmd.code);
-			err = -EINVAL;
-			break;
-		};
-
-		if (err) {
-			l2cap_cmd_rej rej;
-			DBG("error %d", err);
-
-			/* FIXME: Map err to a valid reason. */
-			rej.reason = __cpu_to_le16(0);
-			l2cap_send_rsp(conn, cmd.ident, L2CAP_COMMAND_REJ, L2CAP_CMD_REJ_SIZE, &rej);
-		}
-
-		data += cmd.len;
-		len  -= cmd.len;
-	}
-
-	kfree_skb(skb);
-}
-
-static inline int l2cap_data_channel(struct l2cap_conn *conn, __u16 cid, struct sk_buff *skb)
-{
-	struct sock *sk;
-
-	if (!(sk = l2cap_get_chan_by_scid(&conn->chan_list, cid))) {
-		DBG("unknown cid 0x%4.4x", cid);
-		goto drop;
-	}
-
-	DBG("sk %p, len %d", sk, skb->len);
-
-	if (sk->state != BT_CONNECTED)
-		goto drop;
-
-	if (l2cap_pi(sk)->imtu < skb->len)
-		goto drop;
-
-	skb_queue_tail(&sk->receive_queue, skb);
-	sk->data_ready(sk, skb->len);
-
-	return 0;
-
-drop:
-	kfree_skb(skb);
-
-	return 0;
-}
-
-static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
-{
-	l2cap_hdr *lh = (l2cap_hdr *) skb->data;
-	__u16 cid, len;
-
-	skb_pull(skb, L2CAP_HDR_SIZE);
-	cid = __le16_to_cpu(lh->cid);
-	len = __le16_to_cpu(lh->len);
-
-	DBG("len %d, cid 0x%4.4x", len, cid);
-
-	if (cid == 0x0001)
-		l2cap_sig_channel(conn, skb);
-	else	
-		l2cap_data_channel(conn, cid, skb);
-}
-
-/* ------------ L2CAP interface with lower layer (HCI) ------------- */
-static int l2cap_dev_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
-	struct hci_dev *hdev = (struct hci_dev *) ptr;
-
-	DBG("hdev %s, event %ld", hdev->name, event);
-
-	write_lock(&l2cap_rt_lock);
-
-	switch (event) {
-	case HCI_DEV_UP:
-		l2cap_iff_add(hdev);
-		break;
-
-	case HCI_DEV_DOWN:
-		l2cap_iff_del(hdev);
-		break;
-	};
-
-	write_unlock(&l2cap_rt_lock);
-
-	return NOTIFY_DONE;
-}
-
-int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
-{
-	struct l2cap_iff *iff;
-
-	DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
-
-	if (!(iff = hdev->l2cap_data)) {
-		ERR("unknown interface");
-		return 0;
-	}
-
-	/* Always accept connection */
-	return 1;
-}
-
-int l2cap_connect_cfm(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 status, struct hci_conn *hconn)
-{
-	struct l2cap_conn *conn;
-	struct l2cap_iff *iff;
-	int err = 0;
-
-	DBG("hdev %s bdaddr %s hconn %p", hdev->name, batostr(bdaddr), hconn);
-
-	if (!(iff = hdev->l2cap_data)) {
-		ERR("unknown interface");
-		return 0;
-	}
-
-	l2cap_iff_lock(iff);
-
-	conn = l2cap_get_conn_by_addr(iff, bdaddr);
-
-	if (conn) {
-		/* Outgoing connection */
-		DBG("Outgoing connection: %s -> %s, %p, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), conn, status);
-
-		if (!status && hconn) {
-			conn->state = BT_CONNECTED;
-			conn->hconn = hconn;
-
-			hconn->l2cap_data = (void *)conn;
-
-			/* Establish channels */
-			l2cap_conn_ready(conn);
-		} else {
-			l2cap_conn_del(conn, bterr(status));
-		}
-	} else {
-		/* Incomming connection */
-		DBG("Incomming connection: %s -> %s, %2.2x", batostr(iff->bdaddr), batostr(bdaddr), status);
-	
-		if (status || !hconn)
-			goto done;
-
-		if (!(conn = l2cap_conn_add(iff, bdaddr))) {
-			err = -ENOMEM;
-			goto done;
-		}
-
-		conn->hconn = hconn;
-		hconn->l2cap_data = (void *)conn;
-
-		conn->state = BT_CONNECTED;
-	}
-
-done:
-	l2cap_iff_unlock(iff);
-
-	return err;
-}
-
-int l2cap_disconn_ind(struct hci_conn *hconn, __u8 reason)
-{
-	struct l2cap_conn *conn = hconn->l2cap_data;
-
-	DBG("hconn %p reason %d", hconn, reason);
-
-	if (!conn) {
-		ERR("unknown connection");
-		return 0;
-	}
-	conn->hconn = NULL;
-
-	l2cap_iff_lock(conn->iff);
-	l2cap_conn_del(conn, bterr(reason));
-	l2cap_iff_unlock(conn->iff);
-
-	return 0;
-}
-
-int l2cap_recv_acldata(struct hci_conn *hconn, struct sk_buff *skb, __u16 flags)
-{
-	struct l2cap_conn *conn = hconn->l2cap_data;
-
-	if (!conn) {
-		ERR("unknown connection %p", hconn);
-		goto drop;
-	}
-
-	DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
-
-	if (flags & ACL_START) {
-		int flen, tlen, size;
-		l2cap_hdr *lh;
-
-		if (conn->rx_len) {
-			ERR("Unexpected start frame (len %d)", skb->len);
-			kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
-			conn->rx_len = 0;
-		}
-
-		if (skb->len < L2CAP_HDR_SIZE) {
-			ERR("Frame is too small (len %d)", skb->len);
-			goto drop;
-		}
-
-		lh = (l2cap_hdr *)skb->data;
-		tlen = __le16_to_cpu(lh->len);
-		flen = skb->len - L2CAP_HDR_SIZE;
-
-		DBG("Start: total len %d, frag len %d", tlen, flen);
-
-		if (flen == tlen) {
-			/* Complete frame received */
-			l2cap_recv_frame(conn, skb);
-			return 0;
-		}
-
-		/* Allocate skb for the complete frame (with header) */
-		size = L2CAP_HDR_SIZE + tlen;
-		if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
-			goto drop;
-
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
-
-		conn->rx_len = tlen - flen;
-	} else {
-		DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
-
-		if (!conn->rx_len) {
-			ERR("Unexpected continuation frame (len %d)", skb->len);
-			goto drop;
-		}
-
-		if (skb->len > conn->rx_len) {
-			ERR("Fragment is too large (len %d)", skb->len);
-			kfree_skb(conn->rx_skb); conn->rx_skb = NULL;
-			goto drop;
-		}
-
-		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
-		conn->rx_len -= skb->len;
-
-		if (!conn->rx_len) {
-			/* Complete frame received */
-			l2cap_recv_frame(conn, conn->rx_skb);
-			conn->rx_skb = NULL;
-		}
-	}
-
-drop:
-	kfree_skb(skb);
-	return 0;
-}
-
-struct proto_ops l2cap_sock_ops = {
-	family:		PF_BLUETOOTH,
-	release:	l2cap_sock_release,
-	bind:		l2cap_sock_bind,
-	connect:	l2cap_sock_connect,
-	listen:		l2cap_sock_listen,
-	accept:		l2cap_sock_accept,
-	getname:	l2cap_sock_getname,
-	sendmsg:	l2cap_sock_sendmsg,
-	recvmsg:	l2cap_sock_recvmsg,
-	poll:		l2cap_sock_poll,
-	socketpair:	sock_no_socketpair,
-	ioctl:		sock_no_ioctl,
-	shutdown:	sock_no_shutdown,
-	setsockopt:	l2cap_sock_setsockopt,
-	getsockopt:	l2cap_sock_getsockopt,
-	mmap:		sock_no_mmap
-};
-
-struct net_proto_family l2cap_sock_family_ops = {
-	family:		PF_BLUETOOTH,
-	create:		l2cap_sock_create,
-};
-
-struct hci_proto l2cap_hci_proto = {
-	name:		"L2CAP",
-	id:		HCI_PROTO_L2CAP,
-	connect_ind:	l2cap_connect_ind,
-	connect_cfm:	l2cap_connect_cfm,
-	disconn_ind:	l2cap_disconn_ind,
-	recv_acldata:	l2cap_recv_acldata,
-};
-
-struct notifier_block l2cap_nblock = {
-	notifier_call: l2cap_dev_event
-};
-
-int __init l2cap_init(void)
-{
-	INF("BlueZ L2CAP ver %s Copyright (C) 2000,2001 Qualcomm Inc",
-		VERSION);
-	INF("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
-
-	if (bluez_sock_register(BTPROTO_L2CAP, &l2cap_sock_family_ops)) {
-		ERR("Can't register L2CAP socket");
-		return -EPROTO;
-	}
-
-	if (hci_register_proto(&l2cap_hci_proto) < 0) {
-		ERR("Can't register L2CAP protocol");
-		return -EPROTO;
-	}
-
-	hci_register_notifier(&l2cap_nblock);
-
-	l2cap_register_proc();
-
-	return 0;
-}
-
-void l2cap_cleanup(void)
-{
-	l2cap_unregister_proc();
-
-	/* Unregister socket, protocol and notifier */
-	if (bluez_sock_unregister(BTPROTO_L2CAP))
-		ERR("Can't unregister L2CAP socket");
-
-	if (hci_unregister_proto(&l2cap_hci_proto) < 0)
-		ERR("Can't unregister L2CAP protocol");
-
-	hci_unregister_notifier(&l2cap_nblock);
-
-	/* We _must_ not have any sockets and/or connections
-	 * at this stage.
-	 */
-
-	/* Free interface list and unlock HCI devices */
-	{
-		struct list_head *list = &l2cap_iff_list;
-
-		while (!list_empty(list)) {
-			struct l2cap_iff *iff;
-
-			iff = list_entry(list->next, struct l2cap_iff, list);
-			l2cap_iff_del(iff->hdev);
-		}
-	}
-}
-
-module_init(l2cap_init);
-module_exit(l2cap_cleanup);
-
-MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
-MODULE_DESCRIPTION("BlueZ L2CAP ver " VERSION);
-MODULE_LICENSE("GPL");
-
diff -Nru a/net/bluetooth/l2cap_proc.c b/net/bluetooth/l2cap_proc.c
--- a/net/bluetooth/l2cap_proc.c	Sun May  5 20:39:50 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,165 +0,0 @@
-/* 
-   BlueZ - Bluetooth protocol stack for Linux
-   Copyright (C) 2000-2001 Qualcomm Incorporated
-
-   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License version 2 as
-   published by the Free Software Foundation;
-
-   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
-   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
-   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
-   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
-   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
-   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
-   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
-   SOFTWARE IS DISCLAIMED.
-*/
-
-/*
- * BlueZ L2CAP proc fs support.
- *
- * $Id: l2cap_proc.c,v 1.2 2001/06/02 01:40:09 maxk Exp $
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/fcntl.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/interrupt.h>
-#include <linux/socket.h>
-#include <linux/skbuff.h>
-#include <linux/proc_fs.h>
-#include <linux/list.h>
-#include <net/sock.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#include <net/bluetooth/bluez.h>
-#include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/hci_core.h>
-#include <net/bluetooth/l2cap_core.h>
-
-#ifndef L2CAP_DEBUG
-#undef  DBG
-#define DBG( A... )
-#endif
-
-/* ----- PROC fs support ----- */
-static int l2cap_conn_dump(char *buf, struct l2cap_iff *iff)
-{
-	struct list_head *p;
-	char *ptr = buf;
-
-	list_for_each(p, &iff->conn_list) {
-		struct l2cap_conn *c;
-
-		c = list_entry(p, struct l2cap_conn, list);
-		ptr += sprintf(ptr, "    %p %d %p %p %s %s\n", 
-				c, c->state, c->iff, c->hconn, batostr(&c->src), batostr(&c->dst));
-	}
-
-	return ptr - buf;
-}
-
-static int l2cap_iff_dump(char *buf)
-{
-	struct list_head *p;
-	char *ptr = buf;
-
-	ptr += sprintf(ptr, "Interfaces:\n");
-
-	write_lock(&l2cap_rt_lock);
-
-	list_for_each(p, &l2cap_iff_list) {
-		struct l2cap_iff *iff;
-
-		iff = list_entry(p, struct l2cap_iff, list);
-
-		ptr += sprintf(ptr, "  %s %p %p\n", iff->hdev->name, iff, iff->hdev);
-		
-		l2cap_iff_lock(iff);
-		ptr += l2cap_conn_dump(ptr, iff);
-		l2cap_iff_unlock(iff);
-	}
-
-	write_unlock(&l2cap_rt_lock);
-
-	ptr += sprintf(ptr, "\n");
-
-	return ptr - buf;
-}
-
-static int l2cap_sock_dump(char *buf, struct bluez_sock_list *list)
-{
-	struct l2cap_pinfo *pi;
-	struct sock *sk;
-	char *ptr = buf;
-
-	ptr += sprintf(ptr, "Sockets:\n");
-
-	write_lock(&list->lock);
-
-	for (sk = list->head; sk; sk = sk->next) {
-		pi = l2cap_pi(sk);
-		ptr += sprintf(ptr, "  %p %d %p %d %s %s 0x%4.4x 0x%4.4x %d %d\n", sk, sk->state, pi->conn, pi->psm,
-		               batostr(&pi->src), batostr(&pi->dst), pi->scid, pi->dcid, pi->imtu, pi->omtu );
-	}
-
-	write_unlock(&list->lock);
-
-	ptr += sprintf(ptr, "\n");
-
-	return ptr - buf;
-}
-
-static int l2cap_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
-{
-	char *ptr = buf;
-	int len;
-
-	DBG("count %d, offset %ld", count, offset);
-
-	ptr += l2cap_iff_dump(ptr);
-	ptr += l2cap_sock_dump(ptr, &l2cap_sk_list);
-	len  = ptr - buf;
-
-	if (len <= count + offset)
-		*eof = 1;
-
-	*start = buf + offset;
-	len -= offset;
-
-	if (len > count)
-		len = count;
-	if (len < 0)
-		len = 0;
-
-	return len;
-}
-
-void l2cap_register_proc(void)
-{
-	create_proc_read_entry("bluetooth/l2cap", 0, 0, l2cap_read_proc, NULL);
-}
-
-void l2cap_unregister_proc(void)
-{
-	remove_proc_entry("bluetooth/l2cap", NULL);
-}
diff -Nru a/net/bluetooth/lib.c b/net/bluetooth/lib.c
--- a/net/bluetooth/lib.c	Sun May  5 20:39:49 2002
+++ b/net/bluetooth/lib.c	Sun May  5 20:39:49 2002
@@ -25,7 +25,7 @@
 /*
  * BlueZ kernel library.
  *
- * $Id: lib.c,v 1.3 2001/06/22 23:14:23 maxk Exp $
+ * $Id: lib.c,v 1.1 2002/03/08 21:06:59 maxk Exp $
  */
 
 #include <linux/kernel.h>
diff -Nru a/net/bluetooth/sco.c b/net/bluetooth/sco.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/sco.c	Sun May  5 20:39:50 2002
@@ -0,0 +1,1011 @@
+/* 
+   BlueZ - Bluetooth protocol stack for Linux
+   Copyright (C) 2000-2001 Qualcomm Incorporated
+
+   Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 as
+   published by the Free Software Foundation;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * BlueZ SCO sockets.
+ *
+ * $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $
+ */
+#define VERSION "0.2"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/sco.h>
+
+#ifndef SCO_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static struct proto_ops sco_sock_ops;
+
+static struct bluez_sock_list sco_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
+static void sco_chan_del(struct sock *sk, int err);
+static inline struct sock * sco_chan_get(struct sco_conn *conn);
+
+static int  sco_conn_del(struct hci_conn *conn, int err);
+
+static void sco_sock_close(struct sock *sk);
+static void sco_sock_kill(struct sock *sk);
+
+/* ----- SCO timers ------ */
+static void sco_sock_timeout(unsigned long arg)
+{
+	struct sock *sk = (struct sock *) arg;
+
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	bh_lock_sock(sk);
+	sk->err = ETIMEDOUT;
+	sk->state_change(sk);
+	bh_unlock_sock(sk);
+
+	sco_sock_kill(sk);
+	sock_put(sk);
+}
+
+static void sco_sock_set_timer(struct sock *sk, long timeout)
+{
+	BT_DBG("sock %p state %d timeout %ld", sk, sk->state, timeout);
+
+	if (!mod_timer(&sk->timer, jiffies + timeout))
+		sock_hold(sk);
+}
+
+static void sco_sock_clear_timer(struct sock *sk)
+{
+	BT_DBG("sock %p state %d", sk, sk->state);
+
+	if (timer_pending(&sk->timer) && del_timer(&sk->timer))
+		__sock_put(sk);
+}
+
+static void sco_sock_init_timer(struct sock *sk)
+{
+	init_timer(&sk->timer);
+	sk->timer.function = sco_sock_timeout;
+	sk->timer.data = (unsigned long)sk;
+}
+
+/* -------- SCO connections --------- */
+static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
+{
+	struct hci_dev *hdev = hcon->hdev;
+	struct sco_conn *conn;
+
+	if ((conn = hcon->sco_data))
+		return conn;
+
+	if (status)
+		return conn;
+
+	if (!(conn = kmalloc(sizeof(struct sco_conn), GFP_ATOMIC)))
+		return NULL;
+	memset(conn, 0, sizeof(struct sco_conn));
+
+	spin_lock_init(&conn->lock);
+
+	hcon->sco_data = conn;
+	conn->hcon = hcon;
+
+	conn->src = &hdev->bdaddr;
+	conn->dst = &hcon->dst;
+	
+	if (hdev->sco_mtu > 0)
+		conn->mtu = hdev->sco_mtu;
+	else
+		conn->mtu = 60;
+
+	BT_DBG("hcon %p conn %p", hcon, conn);
+
+	MOD_INC_USE_COUNT;
+	return conn;
+}
+
+static int sco_conn_del(struct hci_conn *hcon, int err)
+{
+	struct sco_conn *conn;
+	struct sock *sk;
+
+	if (!(conn = hcon->sco_data)) 
+		return 0;
+
+	BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+	/* Kill socket */
+	if ((sk = sco_chan_get(conn))) {
+		bh_lock_sock(sk);
+		sco_sock_clear_timer(sk);
+		sco_chan_del(sk, err);
+		bh_unlock_sock(sk);
+		sco_sock_kill(sk);
+	}
+
+	hcon->sco_data = NULL;
+	kfree(conn);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+int sco_connect(struct sock *sk)
+{
+	bdaddr_t *src = &bluez_sk(sk)->src;
+	bdaddr_t *dst = &bluez_sk(sk)->dst;
+	struct sco_conn *conn;
+	struct hci_conn *hcon;
+	struct hci_dev  *hdev;
+	int err = 0;
+
+	BT_DBG("%s -> %s", batostr(src), batostr(dst));
+
+	if (!(hdev = hci_get_route(dst, src)))
+		return -EHOSTUNREACH;
+
+	hci_dev_lock_bh(hdev);
+
+	err = -ENOMEM;
+
+	hcon = hci_connect(hdev, SCO_LINK, dst);
+	if (!hcon)
+		goto done;
+
+	conn = sco_conn_add(hcon, 0);
+	if (!conn) {
+		hci_conn_put(hcon);
+		goto done;
+	}
+
+	/* Update source addr of the socket */
+	bacpy(src, conn->src);
+
+	err = sco_chan_add(conn, sk, NULL);
+	if (err)
+		goto done;
+
+	if (hcon->state == BT_CONNECTED) {
+		sco_sock_clear_timer(sk);
+		sk->state = BT_CONNECTED;
+	} else {
+		sk->state = BT_CONNECT;
+		sco_sock_set_timer(sk, sk->sndtimeo);
+	}
+done:
+	hci_dev_unlock_bh(hdev);
+	hci_dev_put(hdev);
+	return err;
+}
+
+static inline int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
+{
+	struct sco_conn *conn = sco_pi(sk)->conn;
+	struct sk_buff *skb;
+	int err, count;
+
+	/* Check outgoing MTU */
+	if (len > conn->mtu)
+		return -EINVAL;
+
+	BT_DBG("sk %p len %d", sk, len);
+
+	count = MIN(conn->mtu, len);
+	if (!(skb = bluez_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err)))
+		return err;
+
+	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
+		err = -EFAULT;
+		goto fail;
+	}
+
+	if ((err = hci_send_sco(conn->hcon, skb)) < 0)
+		goto fail;
+
+	return count;
+
+fail:
+	kfree_skb(skb);
+	return err;
+}
+
+static inline void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb)
+{
+	struct sock *sk = sco_chan_get(conn);
+
+	if (!sk)
+		goto drop;
+
+	BT_DBG("sk %p len %d", sk, skb->len);
+
+	if (sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (!sock_queue_rcv_skb(sk, skb))
+		return;
+
+drop:
+	kfree_skb(skb);
+	return;
+}
+
+/* -------- Socket interface ---------- */
+static struct sock *__sco_get_sock_by_addr(bdaddr_t *ba)
+{
+	struct sock *sk;
+
+	for (sk = sco_sk_list.head; sk; sk = sk->next) {
+		if (!bacmp(&bluez_sk(sk)->src, ba))
+			break;
+	}
+
+	return sk;
+}
+
+/* Find socket listening on source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *sco_get_sock_listen(bdaddr_t *src)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	read_lock(&sco_sk_list.lock);
+
+	for (sk = sco_sk_list.head; sk; sk = sk->next) {
+		if (sk->state != BT_LISTEN)
+			continue;
+
+		/* Exact match. */
+		if (!bacmp(&bluez_sk(sk)->src, src))
+			break;
+
+		/* Closest match */
+		if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY))
+			sk1 = sk;
+	}
+
+	read_unlock(&sco_sk_list.lock);
+
+	return sk ? sk : sk1;
+}
+
+static void sco_sock_destruct(struct sock *sk)
+{
+	BT_DBG("sk %p", sk);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+
+	if (sk->protinfo)
+		kfree(sk->protinfo);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static void sco_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted channels */
+	while ((sk = bluez_accept_dequeue(parent, NULL)))
+		sco_sock_close(sk);
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void sco_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d", sk, sk->state);
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&sco_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+/* Close socket.
+ * Must be called on unlocked socket.
+ */
+static void sco_sock_close(struct sock *sk)
+{
+	struct sco_conn *conn;
+
+	sco_sock_clear_timer(sk);
+
+	lock_sock(sk);
+
+	conn = sco_pi(sk)->conn;
+
+	BT_DBG("sk %p state %d conn %p socket %p", sk, sk->state, conn, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		sco_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECTED:
+	case BT_CONFIG:
+	case BT_CONNECT:
+	case BT_DISCONN:
+		sco_chan_del(sk, ECONNRESET);
+		break;
+
+	default:
+		sk->zapped = 1;
+		break;
+	};
+
+	release_sock(sk);
+
+	sco_sock_kill(sk);
+}
+
+static void sco_sock_init(struct sock *sk, struct sock *parent)
+{
+	BT_DBG("sk %p", sk);
+
+	if (parent) 
+		sk->type = parent->type;
+}
+
+static struct sock *sco_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct sock *sk;
+
+	sk = bluez_sock_alloc(sock, proto, sizeof(struct sco_pinfo), prio);
+	if (!sk)
+		return NULL;
+
+	sk->destruct = sco_sock_destruct;
+	sk->sndtimeo = SCO_CONN_TIMEOUT;
+	sk->state    = BT_OPEN;
+
+	sco_sock_init_timer(sk);
+
+	bluez_sock_link(&sco_sk_list, sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int sco_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_SEQPACKET)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &sco_sock_ops;
+
+	if (!(sk = sco_sock_alloc(sock, protocol, GFP_KERNEL)))
+		return -ENOMEM;
+
+	sco_sock_init(sk, NULL);
+	return 0;
+}
+
+static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+	bdaddr_t *src = &sa->sco_bdaddr;
+	int err = 0;
+
+	BT_DBG("sk %p %s", sk, batostr(&sa->sco_bdaddr));
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock(&sco_sk_list.lock);
+
+	if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
+		err = -EADDRINUSE;
+		goto unlock;
+	}
+
+	/* Save source address */
+	bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
+	sk->state = BT_BOUND;
+
+unlock:
+	write_unlock(&sco_sk_list.lock);
+
+done:
+	release_sock(sk);
+
+	return err;
+}
+
+static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_sco))
+		return -EINVAL;
+
+	if (sk->state != BT_OPEN && sk->state != BT_BOUND)
+		return -EBADFD;
+
+	if (sk->type != SOCK_SEQPACKET)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	/* Set destination address and psm */
+	bacpy(&bluez_sk(sk)->dst, &sa->sco_bdaddr);
+
+	if ((err = sco_connect(sk)))
+		goto done;
+
+	err = bluez_sock_w4_connect(sk, flags);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND || sock->type != SOCK_SEQPACKET) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *ch;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(ch = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", ch);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_sco *sa = (struct sockaddr_sco *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	addr->sa_family = AF_BLUETOOTH;
+	*len = sizeof(struct sockaddr_sco);
+
+	if (peer)
+		bacpy(&sa->sco_bdaddr, &bluez_sk(sk)->dst);
+	else
+		bacpy(&sa->sco_bdaddr, &bluez_sk(sk)->src);
+
+	return 0;
+}
+
+static int sco_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (sk->err)
+		return sock_error(sk);
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	lock_sock(sk);
+
+	if (sk->state == BT_CONNECTED)
+		err = sco_send_frame(sk, msg, len);
+	else
+		err = -ENOTCONN;
+
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+int sco_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct sco_options opts;
+	struct sco_conninfo cinfo;
+	int len, err = 0; 
+
+	BT_DBG("sk %p", sk);
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	case SCO_OPTIONS:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+		
+		opts.mtu = sco_pi(sk)->conn->mtu;
+
+		BT_INFO("mtu %d", opts.mtu);
+
+		len = MIN(len, sizeof(opts));
+		if (copy_to_user(optval, (char *)&opts, len))
+			err = -EFAULT;
+
+		break;
+
+	case SCO_CONNINFO:
+		if (sk->state != BT_CONNECTED) {
+			err = -ENOTCONN;
+			break;
+		}
+
+		cinfo.hci_handle = sco_pi(sk)->conn->hcon->handle;
+
+		len = MIN(len, sizeof(cinfo));
+		if (copy_to_user(optval, (char *)&cinfo, len))
+			err = -EFAULT;
+
+		break;
+
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int sco_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sco_sock_close(sk);
+
+	return 0;
+}
+
+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+	BT_DBG("conn %p", conn);
+
+	sco_pi(sk)->conn = conn;
+	conn->sk = sk;
+
+	if (parent)
+		bluez_accept_enqueue(parent, sk);
+}
+
+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+	int err = 0;
+
+	sco_conn_lock(conn);
+	if (conn->sk) {
+		err = -EBUSY;
+	} else {
+		__sco_chan_add(conn, sk, parent);
+	}
+	sco_conn_unlock(conn);
+	return err;
+}
+
+static inline struct sock * sco_chan_get(struct sco_conn *conn)
+{
+	struct sock *sk = NULL;
+	sco_conn_lock(conn);
+	sk = conn->sk;
+	sco_conn_unlock(conn);
+	return sk;
+}
+
+/* Delete channel. 
+ * Must be called on the locked socket. */
+static void sco_chan_del(struct sock *sk, int err)
+{
+	struct sco_conn *conn;
+
+	conn = sco_pi(sk)->conn;
+
+	BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
+
+	if (conn) { 
+		sco_conn_lock(conn);
+		conn->sk = NULL;
+		sco_pi(sk)->conn = NULL;
+		sco_conn_unlock(conn);
+		hci_conn_put(conn->hcon);
+	}
+
+	sk->state = BT_CLOSED;
+	sk->err   = err;
+	sk->state_change(sk);
+
+	sk->zapped = 1;
+}
+
+static void sco_conn_ready(struct sco_conn *conn)
+{
+	struct sock *parent, *sk;
+
+	BT_DBG("conn %p", conn);
+
+	sco_conn_lock(conn);
+
+	if ((sk = conn->sk)) {
+		sco_sock_clear_timer(sk);
+		bh_lock_sock(sk);
+		sk->state = BT_CONNECTED;
+		sk->state_change(sk);
+		bh_unlock_sock(sk);
+	} else {
+		parent = sco_get_sock_listen(conn->src);
+		if (!parent)
+			goto done;
+
+		bh_lock_sock(parent);
+
+		sk = sco_sock_alloc(NULL, BTPROTO_SCO, GFP_ATOMIC);
+		if (!sk) {
+			bh_unlock_sock(parent);
+                	goto done;
+		}
+
+		sco_sock_init(sk, parent);
+
+		bacpy(&bluez_sk(sk)->src, conn->src);
+		bacpy(&bluez_sk(sk)->dst, conn->dst);
+
+		hci_conn_hold(conn->hcon);
+        	__sco_chan_add(conn, sk, parent);
+
+        	sk->state = BT_CONNECTED;
+
+		/* Wake up parent */
+		parent->data_ready(parent, 1);
+	
+        	bh_unlock_sock(parent);
+	}
+
+done:
+	sco_conn_unlock(conn);
+}
+
+/* ----- SCO interface with lower layer (HCI) ----- */
+int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+{
+	BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+
+	/* Always accept connection */
+	return HCI_LM_ACCEPT;
+}
+
+int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+{
+	BT_DBG("hcon %p bdaddr %s status %d", hcon, batostr(&hcon->dst), status);
+
+	if (hcon->type != SCO_LINK)
+		return 0;
+
+	if (!status) {
+		struct sco_conn *conn;
+
+		conn = sco_conn_add(hcon, status);
+		if (conn)
+			sco_conn_ready(conn);
+	} else 
+		sco_conn_del(hcon, bterr(status));
+	
+	return 0;
+}
+
+int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
+{
+	BT_DBG("hcon %p reason %d", hcon, reason);
+
+	if (hcon->type != SCO_LINK)
+		return 0;
+
+	sco_conn_del(hcon, bterr(reason));
+	return 0;
+}
+
+int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
+{
+	struct sco_conn *conn = hcon->sco_data;
+
+	if (!conn)
+		goto drop;
+
+	BT_DBG("conn %p len %d", conn, skb->len);
+
+	if (skb->len) {
+		sco_recv_frame(conn, skb);
+		return 0;
+	}
+
+drop:
+	kfree_skb(skb);	
+	return 0;
+}
+
+/* ----- Proc fs support ------ */
+static int sco_sock_dump(char *buf, struct bluez_sock_list *list)
+{
+	struct sco_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	write_lock(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = sco_pi(sk);
+		ptr += sprintf(ptr, "%s %s %d\n",
+				batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst),
+				sk->state); 
+	}
+
+	write_unlock(&list->lock);
+
+	ptr += sprintf(ptr, "\n");
+
+	return ptr - buf;
+}
+
+static int sco_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += sco_sock_dump(ptr, &sco_sk_list);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static struct proto_ops sco_sock_ops = {
+	family:		PF_BLUETOOTH,
+	release:	sco_sock_release,
+	bind:		sco_sock_bind,
+	connect:	sco_sock_connect,
+	listen:		sco_sock_listen,
+	accept:		sco_sock_accept,
+	getname:	sco_sock_getname,
+	sendmsg:	sco_sock_sendmsg,
+	recvmsg:	bluez_sock_recvmsg,
+	poll:		bluez_sock_poll,
+	socketpair:	sock_no_socketpair,
+	ioctl:		sock_no_ioctl,
+	shutdown:	sock_no_shutdown,
+	setsockopt:	sco_sock_setsockopt,
+	getsockopt:	sco_sock_getsockopt,
+	mmap:		sock_no_mmap
+};
+
+static struct net_proto_family sco_sock_family_ops = {
+	family:		PF_BLUETOOTH,
+	create:		sco_sock_create
+};
+
+static struct hci_proto sco_hci_proto = {
+	name:		"SCO",
+	id:		HCI_PROTO_SCO,
+	connect_ind:	sco_connect_ind,
+	connect_cfm:	sco_connect_cfm,
+	disconn_ind:	sco_disconn_ind,
+	recv_scodata:	sco_recv_scodata,
+};
+
+int __init sco_init(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_SCO, &sco_sock_family_ops))) {
+		BT_ERR("Can't register SCO socket layer");
+		return err;
+	}
+
+	if ((err = hci_register_proto(&sco_hci_proto))) {
+		BT_ERR("Can't register SCO protocol");
+		return err;
+	}
+
+	create_proc_read_entry("bluetooth/sco", 0, 0, sco_read_proc, NULL);
+
+	BT_INFO("BlueZ SCO ver %s Copyright (C) 2000,2001 Qualcomm Inc", VERSION);
+	BT_INFO("Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>");
+	return 0;
+}
+
+void sco_cleanup(void)
+{
+	int err;
+
+	remove_proc_entry("bluetooth/sco", NULL);
+
+	/* Unregister socket, protocol and notifier */
+	if ((err = bluez_sock_unregister(BTPROTO_SCO)))
+		BT_ERR("Can't unregister SCO socket layer %d", err);
+
+	if ((err = hci_unregister_proto(&sco_hci_proto)))
+		BT_ERR("Can't unregister SCO protocol %d", err);
+}
+
+module_init(sco_init);
+module_exit(sco_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
+MODULE_DESCRIPTION("BlueZ SCO ver " VERSION);
+MODULE_LICENSE("GPL");
diff -Nru a/net/bluetooth/syms.c b/net/bluetooth/syms.c
--- a/net/bluetooth/syms.c	Sun May  5 20:39:50 2002
+++ b/net/bluetooth/syms.c	Sun May  5 20:39:50 2002
@@ -25,7 +25,7 @@
 /*
  * BlueZ symbols.
  *
- * $Id: syms.c,v 1.1 2001/07/12 19:31:24 maxk Exp $
+ * $Id: syms.c,v 1.1 2002/03/08 21:06:59 maxk Exp $
  */
 
 #include <linux/config.h>
@@ -39,7 +39,6 @@
 #include <linux/socket.h>
 
 #include <net/bluetooth/bluetooth.h>
-#include <net/bluetooth/bluez.h>
 #include <net/bluetooth/hci_core.h>
 
 /* HCI Core */
@@ -47,17 +46,18 @@
 EXPORT_SYMBOL(hci_unregister_dev);
 EXPORT_SYMBOL(hci_register_proto);
 EXPORT_SYMBOL(hci_unregister_proto);
-EXPORT_SYMBOL(hci_register_notifier);
-EXPORT_SYMBOL(hci_unregister_notifier);
 
+EXPORT_SYMBOL(hci_get_route);
 EXPORT_SYMBOL(hci_connect);
-EXPORT_SYMBOL(hci_disconnect);
 EXPORT_SYMBOL(hci_dev_get);
+EXPORT_SYMBOL(hci_conn_auth);
+EXPORT_SYMBOL(hci_conn_encrypt);
 
 EXPORT_SYMBOL(hci_recv_frame);
 EXPORT_SYMBOL(hci_send_acl);
 EXPORT_SYMBOL(hci_send_sco);
 EXPORT_SYMBOL(hci_send_raw);
+EXPORT_SYMBOL(hci_si_event);
 
 /* BlueZ lib */
 EXPORT_SYMBOL(bluez_dump);
@@ -68,5 +68,11 @@
 /* BlueZ sockets */
 EXPORT_SYMBOL(bluez_sock_register);
 EXPORT_SYMBOL(bluez_sock_unregister);
+EXPORT_SYMBOL(bluez_sock_alloc);
 EXPORT_SYMBOL(bluez_sock_link);
 EXPORT_SYMBOL(bluez_sock_unlink);
+EXPORT_SYMBOL(bluez_sock_recvmsg);
+EXPORT_SYMBOL(bluez_sock_poll);
+EXPORT_SYMBOL(bluez_accept_enqueue);
+EXPORT_SYMBOL(bluez_accept_dequeue);
+EXPORT_SYMBOL(bluez_sock_w4_connect);
diff -Nru a/net/sunrpc/sched.c b/net/sunrpc/sched.c
--- a/net/sunrpc/sched.c	Sun May  5 20:39:50 2002
+++ b/net/sunrpc/sched.c	Sun May  5 20:39:50 2002
@@ -704,9 +704,6 @@
 
 	dprintk("RPC:      rpc_schedule enter\n");
 	while (1) {
-		/* Ensure equal rights for tcp tasks... */
-		rpciod_tcp_dispatcher();
-
 		spin_lock_bh(&rpc_queue_lock);
 
 		task_for_first(task, &schedq.tasks) {
@@ -1030,7 +1027,7 @@
 static inline int
 rpciod_task_pending(void)
 {
-	return !list_empty(&schedq.tasks) || xprt_tcp_pending();
+	return !list_empty(&schedq.tasks);
 }
 
 
diff -Nru a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
--- a/net/sunrpc/xprt.c	Sun May  5 20:39:49 2002
+++ b/net/sunrpc/xprt.c	Sun May  5 20:39:49 2002
@@ -64,6 +64,7 @@
 #include <net/sock.h>
 #include <net/checksum.h>
 #include <net/udp.h>
+#include <net/tcp.h>
 
 #include <asm/uaccess.h>
 
@@ -88,7 +89,6 @@
 static void	xprt_reconn_status(struct rpc_task *task);
 static struct socket *xprt_create_socket(int, struct rpc_timeout *);
 static int	xprt_bind_socket(struct rpc_xprt *, struct socket *);
-static void	xprt_remove_pending(struct rpc_xprt *);
 
 #ifdef RPC_DEBUG_DATA
 /*
@@ -270,43 +270,6 @@
 }
 
 /*
- * Read data from socket
- */
-static int
-xprt_recvmsg(struct rpc_xprt *xprt, struct iovec *iov, int nr, unsigned len, unsigned shift)
-{
-	struct socket	*sock = xprt->sock;
-	struct msghdr	msg;
-	mm_segment_t	oldfs;
-	struct iovec	niv[MAX_IOVEC];
-	int		result;
-
-	if (!sock)
-		return -ENOTCONN;
-
-	msg.msg_flags   = MSG_DONTWAIT|MSG_NOSIGNAL;
-	msg.msg_iov	= iov;
-	msg.msg_iovlen	= nr;
-	msg.msg_name	= NULL;
-	msg.msg_namelen = 0;
-	msg.msg_control = NULL;
-	msg.msg_controllen = 0;
-
-	/* Adjust the iovec if we've already filled it */
-	if (shift)
-		xprt_move_iov(&msg, niv, shift);
-
-	oldfs = get_fs(); set_fs(get_ds());
-	result = sock_recvmsg(sock, &msg, len, MSG_DONTWAIT);
-	set_fs(oldfs);
-
-	dprintk("RPC:      xprt_recvmsg(iov %p, len %d) = %d\n",
-						iov, len, result);
-	return result;
-}
-
-
-/*
  * Adjust RPC congestion window
  * We use a time-smoothed congestion estimator to avoid heavy oscillation.
  */
@@ -423,7 +386,6 @@
 {
 	dprintk("RPC:      disconnected transport %p\n", xprt);
 	xprt_clear_connected(xprt);
-	xprt_remove_pending(xprt);
 	rpc_wake_up_status(&xprt->pending, -ENOTCONN);
 }
 
@@ -437,7 +399,7 @@
 {
 	struct rpc_xprt	*xprt = task->tk_xprt;
 	struct socket	*sock = xprt->sock;
-	struct sock	*inet = xprt->inet;
+	struct sock	*inet;
 	int		status;
 
 	dprintk("RPC: %4d xprt_reconnect %p connected %d\n",
@@ -458,8 +420,10 @@
 	if (xprt_connected(xprt))
 		goto out_write;
 
+	if (sock && sock->state != SS_UNCONNECTED)
+		xprt_close(xprt);
 	status = -ENOTCONN;
-	if (!inet) {
+	if (!(inet = xprt->inet)) {
 		/* Create an unconnected socket */
 		if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout)))
 			goto defer;
@@ -467,14 +431,6 @@
 		inet = sock->sk;
 	}
 
-	xprt_disconnect(xprt);
-
-	/* Reset TCP record info */
-	xprt->tcp_offset = 0;
-	xprt->tcp_reclen = 0;
-	xprt->tcp_copied = 0;
-	xprt->tcp_more = 0;
-
 	/* Now connect it asynchronously. */
 	dprintk("RPC: %4d connecting new socket\n", task->tk_pid);
 	status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr,
@@ -497,17 +453,21 @@
 			goto defer;
 		}
 
+		/* Protect against TCP socket state changes */
+		lock_sock(inet);
 		dprintk("RPC: %4d connect status %d connected %d\n",
 				task->tk_pid, status, xprt_connected(xprt));
 
-		spin_lock_bh(&xprt->sock_lock);
-		if (!xprt_connected(xprt)) {
+		if (inet->state != TCP_ESTABLISHED) {
 			task->tk_timeout = xprt->timeout.to_maxval;
+			/* if the socket is already closing, delay 5 secs */
+			if ((1<<inet->state) & ~(TCP_SYN_SENT|TCP_SYN_RECV))
+				task->tk_timeout = 5*HZ;
 			rpc_sleep_on(&xprt->sending, task, xprt_reconn_status, NULL);
-			spin_unlock_bh(&xprt->sock_lock);
+			release_sock(inet);
 			return;
 		}
-		spin_unlock_bh(&xprt->sock_lock);
+		release_sock(inet);
 	}
 defer:
 	if (status < 0) {
@@ -716,309 +676,229 @@
 		wake_up_interruptible(sk->sleep);
 }
 
+typedef struct {
+	struct sk_buff *skb;
+	unsigned offset;
+	size_t count;
+} skb_reader_t;
+
+/*
+ * Copy from an skb into memory and shrink the skb.
+ */
+static inline size_t
+tcp_copy_data(skb_reader_t *desc, void *p, size_t len)
+{
+	if (len > desc->count)
+		len = desc->count;
+	skb_copy_bits(desc->skb, desc->offset, p, len);
+	desc->offset += len;
+	desc->count -= len;
+	return len;
+}
+
 /*
  * TCP read fragment marker
  */
-static inline int
-tcp_read_fraghdr(struct rpc_xprt *xprt)
+static inline void
+tcp_read_fraghdr(struct rpc_xprt *xprt, skb_reader_t *desc)
 {
-	struct iovec	riov;
-	int		want, result;
-
-	if (xprt->tcp_offset >= sizeof(xprt->tcp_recm))
-		goto done;
+	size_t len, used;
+	char *p;
 
-	want = sizeof(xprt->tcp_recm) - xprt->tcp_offset;
-	dprintk("RPC:      reading header (%d bytes)\n", want);
-	do {
-		riov.iov_base = ((u8*) &xprt->tcp_recm) + xprt->tcp_offset;
-		riov.iov_len  = want;
-		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
-		if (result < 0)
-			return result;
-		xprt->tcp_offset += result;
-		want -= result;
-	} while (want);
-
-	/* Get the record length and mask out the last fragment bit */
+	p = ((char *) &xprt->tcp_recm) + xprt->tcp_offset;
+	len = sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+	used = tcp_copy_data(desc, p, len);
+	xprt->tcp_offset += used;
+	if (used != len)
+		return;
 	xprt->tcp_reclen = ntohl(xprt->tcp_recm);
-	xprt->tcp_more = (xprt->tcp_reclen & 0x80000000) ? 0 : 1;
+	if (xprt->tcp_reclen & 0x80000000)
+		xprt->tcp_flags |= XPRT_LAST_FRAG;
+	else
+		xprt->tcp_flags &= ~XPRT_LAST_FRAG;
 	xprt->tcp_reclen &= 0x7fffffff;
-
-	dprintk("RPC:      New record reclen %d morefrags %d\n",
-				   xprt->tcp_reclen, xprt->tcp_more);
- done:
-	return xprt->tcp_reclen + sizeof(xprt->tcp_recm) - xprt->tcp_offset;
+	xprt->tcp_flags &= ~XPRT_COPY_RECM;
+	xprt->tcp_offset = 0;
+	/* Sanity check of the record length */
+	if (xprt->tcp_reclen < 4) {
+		printk(KERN_ERR "RPC: Invalid TCP record fragment length\n");
+		xprt_disconnect(xprt);
+	}
+	dprintk("RPC:      reading TCP record fragment of length %d\n",
+			xprt->tcp_reclen);
 }
 
-/*
- * TCP read xid
- */
-static inline int
-tcp_read_xid(struct rpc_xprt *xprt, int avail)
+static void
+tcp_check_recm(struct rpc_xprt *xprt)
 {
-	struct iovec	riov;
-	int		want, result;
-
-	if (xprt->tcp_copied >= sizeof(xprt->tcp_xid) || !avail)
-		goto done;
-	want = min_t(unsigned int, sizeof(xprt->tcp_xid) - xprt->tcp_copied, avail);
-	do {
-		dprintk("RPC:      reading xid (%d bytes)\n", want);
-		riov.iov_base = ((u8*) &xprt->tcp_xid) + xprt->tcp_copied;
-		riov.iov_len  = want;
-		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
-		if (result < 0)
-			return result;
-		xprt->tcp_copied += result;
-		xprt->tcp_offset += result;
-		want  -= result;
-		avail -= result;
-	} while (want);
- done:
-	return avail;
+	if (xprt->tcp_offset == xprt->tcp_reclen) {
+		xprt->tcp_flags |= XPRT_COPY_RECM;
+		xprt->tcp_offset = 0;
+		if (xprt->tcp_flags & XPRT_LAST_FRAG) {
+			xprt->tcp_flags &= ~XPRT_COPY_DATA;
+			xprt->tcp_flags |= XPRT_COPY_XID;
+			xprt->tcp_copied = 0;
+		}
+	}
 }
 
 /*
- * TCP read and complete request
+ * TCP read xid
  */
-static inline int
-tcp_read_request(struct rpc_xprt *xprt, struct rpc_rqst *req, int avail)
+static inline void
+tcp_read_xid(struct rpc_xprt *xprt, skb_reader_t *desc)
 {
-	int	want, result;
-
-	if (req->rq_rlen <= xprt->tcp_copied || !avail)
-		goto done;
-	want = min_t(unsigned int, req->rq_rlen - xprt->tcp_copied, avail);
-	do {
-		dprintk("RPC: %4d TCP receiving %d bytes\n",
-			req->rq_task->tk_pid, want);
+	size_t len, used;
+	char *p;
 
-		result = xprt_recvmsg(xprt, req->rq_rvec, req->rq_rnr, want, xprt->tcp_copied);
-		if (result < 0)
-			return result;
-		xprt->tcp_copied += result;
-		xprt->tcp_offset += result;
-		avail  -= result;
-		want   -= result;
-	} while (want);
-
- done:
-	if (req->rq_rlen > xprt->tcp_copied && xprt->tcp_more)
-		return avail;
-	dprintk("RPC: %4d received reply complete\n", req->rq_task->tk_pid);
-	xprt_complete_rqst(xprt, req, xprt->tcp_copied);
-
-	return avail;
-}
-
-/*
- * TCP discard extra bytes from a short read
- */
-static inline int
-tcp_read_discard(struct rpc_xprt *xprt, int avail)
-{
-	struct iovec	riov;
-	static u8	dummy[64];
-	int		want, result = 0;
-
-	while (avail) {
-		want = min_t(unsigned int, avail, sizeof(dummy));
-		riov.iov_base = dummy;
-		riov.iov_len  = want;
-		dprintk("RPC:      TCP skipping %d bytes\n", want);
-		result = xprt_recvmsg(xprt, &riov, 1, want, 0);
-		if (result < 0)
-			return result;
-		xprt->tcp_offset += result;
-		avail  -= result;
-	}
-	return avail;
+	len = sizeof(xprt->tcp_xid) - xprt->tcp_offset;
+	dprintk("RPC:      reading XID (%Zu bytes)\n", len);
+	p = ((char *) &xprt->tcp_xid) + xprt->tcp_offset;
+	used = tcp_copy_data(desc, p, len);
+	xprt->tcp_offset += used;
+	if (used != len)
+		return;
+	xprt->tcp_flags &= ~XPRT_COPY_XID;
+	xprt->tcp_flags |= XPRT_COPY_DATA;
+	xprt->tcp_copied = 4;
+	dprintk("RPC:      reading reply for XID %08x\n", xprt->tcp_xid);
+	tcp_check_recm(xprt);
 }
 
 /*
- * TCP record receive routine
- * This is not the most efficient code since we call recvfrom thrice--
- * first receiving the record marker, then the XID, then the data.
- * 
- * The optimal solution would be a RPC support in the TCP layer, which
- * would gather all data up to the next record marker and then pass us
- * the list of all TCP segments ready to be copied.
+ * TCP read and complete request
  */
-static int
-tcp_input_record(struct rpc_xprt *xprt)
+static inline void
+tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc)
 {
-	struct rpc_rqst	*req = NULL;
-	struct rpc_task	*task = NULL;
-	int		avail, result;
-
-	dprintk("RPC:      tcp_input_record\n");
-
-	if (xprt->shutdown)
-		return -EIO;
-	if (!xprt_connected(xprt))
-		return -ENOTCONN;
-
-	/* Read in a new fragment marker if necessary */
-	/* Can we ever really expect to get completely empty fragments? */
-	if ((result = tcp_read_fraghdr(xprt)) < 0)
-		return result;
-	avail = result;
-
-	/* Read in the xid if necessary */
-	if ((result = tcp_read_xid(xprt, avail)) < 0)
-		return result;
-	if (!(avail = result))
-		goto out_ok;
+	struct rpc_rqst *req;
+	struct iovec *iov;
+	char *p;
+	unsigned long skip;
+	size_t len, used;
+	int n;
 
 	/* Find and lock the request corresponding to this xid */
 	req = xprt_lookup_rqst(xprt, xprt->tcp_xid);
-	if (req) {
-		task = req->rq_task;
-		/* Read in the request data */
-		result = tcp_read_request(xprt,  req, avail);
-		rpc_unlock_task(task);
-		if (result < 0)
-			return result;
-		avail = result;
+	if (!req) {
+		xprt->tcp_flags &= ~XPRT_COPY_DATA;
+		dprintk("RPC:      XID %08x request not found!\n",
+				xprt->tcp_xid);
+		return;
 	}
-
-	/* Skip over any trailing bytes on short reads */
-	if ((result = tcp_read_discard(xprt, avail)) < 0)
-		return result;
-
- out_ok:
-	dprintk("RPC:      tcp_input_record done (off %d reclen %d copied %d)\n",
-			xprt->tcp_offset, xprt->tcp_reclen, xprt->tcp_copied);
-	result = xprt->tcp_reclen;
-	xprt->tcp_reclen = 0;
-	xprt->tcp_offset = 0;
-	if (!xprt->tcp_more)
-		xprt->tcp_copied = 0;
-	return result;
-}
-
-/*
- *	TCP task queue stuff
- */
-LIST_HEAD(rpc_xprt_pending);	/* List of xprts having pending tcp requests */
-
-static inline
-void tcp_rpciod_queue(void)
-{
-	rpciod_wake_up();
-}
-
-int xprt_tcp_pending(void)
-{
-	int retval;
-
-	spin_lock_bh(&rpc_queue_lock);
-	retval = !list_empty(&rpc_xprt_pending);
-	spin_unlock_bh(&rpc_queue_lock);
-	return retval;
-}
-
-static inline
-void xprt_append_pending(struct rpc_xprt *xprt)
-{
-	spin_lock_bh(&rpc_queue_lock);
-	if (list_empty(&xprt->rx_pending)) {
-		list_add(&xprt->rx_pending, rpc_xprt_pending.prev);
-		dprintk("RPC:     xprt queue %p\n", xprt);
-		tcp_rpciod_queue();
+	skip = xprt->tcp_copied;
+	iov = req->rq_rvec;
+	for (n = req->rq_rnr; n != 0; n--, iov++) {
+		if (skip >= iov->iov_len) {
+			skip -= iov->iov_len;
+			continue;
+		}
+		p = iov->iov_base;
+		len = iov->iov_len;
+		if (skip) {
+			p += skip;
+			len -= skip;
+			skip = 0;
+		}
+		if (xprt->tcp_offset + len > xprt->tcp_reclen)
+			len = xprt->tcp_reclen - xprt->tcp_offset;
+		used = tcp_copy_data(desc, p, len);
+		xprt->tcp_copied += used;
+		xprt->tcp_offset += used;
+		if (used != len)
+			break;
+		if (xprt->tcp_copied == req->rq_rlen) {
+			xprt->tcp_flags &= ~XPRT_COPY_DATA;
+			break;
+		}
+		if (xprt->tcp_offset == xprt->tcp_reclen) {
+			if (xprt->tcp_flags & XPRT_LAST_FRAG)
+				xprt->tcp_flags &= ~XPRT_COPY_DATA;
+			break;
+		}
 	}
-	spin_unlock_bh(&rpc_queue_lock);
-}
 
-static
-void xprt_remove_pending(struct rpc_xprt *xprt)
-{
-	spin_lock_bh(&rpc_queue_lock);
-	if (!list_empty(&xprt->rx_pending)) {
-		list_del(&xprt->rx_pending);
-		INIT_LIST_HEAD(&xprt->rx_pending);
+	if (!(xprt->tcp_flags & XPRT_COPY_DATA)) {
+		dprintk("RPC: %4d received reply complete\n",
+				req->rq_task->tk_pid);
+		xprt_complete_rqst(xprt, req, xprt->tcp_copied);
 	}
-	spin_unlock_bh(&rpc_queue_lock);
+	rpc_unlock_task(req->rq_task); 
+	tcp_check_recm(xprt);
 }
 
-static inline
-struct rpc_xprt *xprt_remove_pending_next(void)
+/*
+ * TCP discard extra bytes from a short read
+ */
+static inline void
+tcp_read_discard(struct rpc_xprt *xprt, skb_reader_t *desc)
 {
-	struct rpc_xprt	*xprt = NULL;
+	size_t len;
 
-	spin_lock_bh(&rpc_queue_lock);
-	if (!list_empty(&rpc_xprt_pending)) {
-		xprt = list_entry(rpc_xprt_pending.next, struct rpc_xprt, rx_pending);
-		list_del(&xprt->rx_pending);
-		INIT_LIST_HEAD(&xprt->rx_pending);
-	}
-	spin_unlock_bh(&rpc_queue_lock);
-	return xprt;
+	len = xprt->tcp_reclen - xprt->tcp_offset;
+	if (len > desc->count)
+		len = desc->count;
+	desc->count -= len;
+	desc->offset += len;
+	xprt->tcp_offset += len;
+	tcp_check_recm(xprt);
 }
 
 /*
- *	This is protected from tcp_data_ready and the stack as its run
- *	inside of the RPC I/O daemon
+ * TCP record receive routine
+ * We first have to grab the record marker, then the XID, then the data.
  */
-void
-__rpciod_tcp_dispatcher(void)
+static int
+tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
+		unsigned int offset, size_t len)
 {
-	struct rpc_xprt *xprt;
-	int safe_retry = 0, result;
-
-	dprintk("rpciod_tcp_dispatcher: Queue Running\n");
-
-	/*
-	 *	Empty each pending socket
-	 */
-	while ((xprt = xprt_remove_pending_next()) != NULL) {
-		dprintk("rpciod_tcp_dispatcher: Processing %p\n", xprt);
+	struct rpc_xprt *xprt = (struct rpc_xprt *)rd_desc->buf;
+	skb_reader_t desc = { skb, offset, len };
 
-		do {
-			result = tcp_input_record(xprt);
-		} while (result >= 0);
-
-		if (safe_retry++ > 200) {
-			schedule();
-			safe_retry = 0;
+	dprintk("RPC:      tcp_data_recv\n");
+	do {
+		/* Read in a new fragment marker if necessary */
+		/* Can we ever really expect to get completely empty fragments? */
+		if (xprt->tcp_flags & XPRT_COPY_RECM) {
+			tcp_read_fraghdr(xprt, &desc);
+			continue;
 		}
-	}
+		/* Read in the xid if necessary */
+		if (xprt->tcp_flags & XPRT_COPY_XID) {
+			tcp_read_xid(xprt, &desc);
+			continue;
+		}
+		/* Read in the request data */
+		if (xprt->tcp_flags & XPRT_COPY_DATA) {
+			tcp_read_request(xprt, &desc);
+			continue;
+		}
+		/* Skip over any trailing bytes on short reads */
+		tcp_read_discard(xprt, &desc);
+	} while (desc.count && xprt_connected(xprt));
+	dprintk("RPC:      tcp_data_recv done\n");
+	return len - desc.count;
 }
 
-/*
- *	data_ready callback for TCP. We can't just jump into the
- *	tcp recvmsg functions inside of the network receive bh or
- * 	bad things occur. We queue it to pick up after networking
- *	is done.
- */
- 
-static void tcp_data_ready(struct sock *sk, int len)
+static void tcp_data_ready(struct sock *sk, int bytes)
 {
-	struct rpc_xprt	*xprt;
+	struct rpc_xprt *xprt;
+	read_descriptor_t rd_desc;
 
 	dprintk("RPC:      tcp_data_ready...\n");
-	if (!(xprt = xprt_from_sock(sk)))
-	{
-		printk("Not a socket with xprt %p\n", sk);
-		goto out;
+	if (!(xprt = xprt_from_sock(sk))) {
+		printk("RPC:      tcp_data_ready socket info not found!\n");
+		return;
 	}
-
 	if (xprt->shutdown)
-		goto out;
-
-	xprt_append_pending(xprt);
+		return;
 
-	dprintk("RPC:      tcp_data_ready client %p\n", xprt);
-	dprintk("RPC:      state %x conn %d dead %d zapped %d\n",
-				sk->state, xprt_connected(xprt),
-				sk->dead, sk->zapped);
- out:
-	if (sk->sleep && waitqueue_active(sk->sleep))
-		wake_up_interruptible(sk->sleep);
+	/* We use rd_desc to pass struct xprt to tcp_data_recv */
+	rd_desc.buf = (char *)xprt;
+	rd_desc.count = 65536;
+	tcp_read_sock(sk, &rd_desc, tcp_data_recv);
 }
 
-
 static void
 tcp_state_change(struct sock *sk)
 {
@@ -1035,6 +915,13 @@
 	case TCP_ESTABLISHED:
 		if (xprt_test_and_set_connected(xprt))
 			break;
+
+		/* Reset TCP record info */
+		xprt->tcp_offset = 0;
+		xprt->tcp_reclen = 0;
+		xprt->tcp_copied = 0;
+		xprt->tcp_flags = XPRT_COPY_RECM | XPRT_COPY_XID;
+
 		spin_lock(&xprt->sock_lock);
 		if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
 			rpc_wake_up_task(xprt->snd_task);
@@ -1057,7 +944,7 @@
  * low.
  */
 static void
-tcp_write_space(struct sock *sk)
+xprt_write_space(struct sock *sk)
 {
 	struct rpc_xprt	*xprt;
 	struct socket	*sock;
@@ -1086,31 +973,6 @@
 	}
 }
 
-static void
-udp_write_space(struct sock *sk)
-{
-	struct rpc_xprt *xprt;
-
-	if (!(xprt = xprt_from_sock(sk)))
-		return;
-	if (xprt->shutdown)
-		return;
-
-	/* Wait until we have enough socket memory. */
-	if (!sock_writeable(sk))
-		return;
-
-	if (!xprt_test_and_set_wspace(xprt)) {
-		spin_lock(&xprt->sock_lock);
-		if (xprt->snd_task && xprt->snd_task->tk_rpcwait == &xprt->sending)
-			rpc_wake_up_task(xprt->snd_task);
-		spin_unlock(&xprt->sock_lock);
-	}
-
-	if (sk->sleep && waitqueue_active(sk->sleep))
-		wake_up_interruptible(sk->sleep);
-}
-
 /*
  * RPC receive timeout handler.
  */
@@ -1483,8 +1345,6 @@
 	req->rq_next = NULL;
 	xprt->free = xprt->slot;
 
-	INIT_LIST_HEAD(&xprt->rx_pending);
-
 	dprintk("RPC:      created transport %p\n", xprt);
 	
 	xprt_bind_socket(xprt, sock);
@@ -1529,15 +1389,14 @@
 	xprt->old_write_space = sk->write_space;
 	if (xprt->prot == IPPROTO_UDP) {
 		sk->data_ready = udp_data_ready;
-		sk->write_space = udp_write_space;
 		sk->no_check = UDP_CSUM_NORCV;
 		xprt_set_connected(xprt);
 	} else {
 		sk->data_ready = tcp_data_ready;
 		sk->state_change = tcp_state_change;
-		sk->write_space = tcp_write_space;
 		xprt_clear_connected(xprt);
 	}
+	sk->write_space = xprt_write_space;
 
 	/* Reset to new socket */
 	xprt->sock = sock;
diff -Nru a/sound/core/Config.in b/sound/core/Config.in
--- a/sound/core/Config.in	Sun May  5 20:39:49 2002
+++ b/sound/core/Config.in	Sun May  5 20:39:49 2002
@@ -13,7 +13,7 @@
 if [ "$CONFIG_SND_SEQUENCER" != "n" ]; then
   dep_tristate '  Sequencer dummy client' CONFIG_SND_SEQ_DUMMY $CONFIG_SND_SEQUENCER
 fi
-bool '  OSS API emulation' CONFIG_SND_OSSEMUL $CONFIG_SND
+dep_bool '  OSS API emulation' CONFIG_SND_OSSEMUL $CONFIG_SND
 if [ "$CONFIG_SND_OSSEMUL" = "y" ]; then
   dep_tristate '    OSS Mixer API' CONFIG_SND_MIXER_OSS $CONFIG_SND
   dep_tristate '    OSS PCM API' CONFIG_SND_PCM_OSS $CONFIG_SND
