]> git.neil.brown.name Git - history.git/commitdiff
Import 2.3.99pre1 2.3.99pre1
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:32:54 +0000 (15:32 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:32:54 +0000 (15:32 -0500)
111 files changed:
CREDITS
Documentation/Configure.help
Documentation/DocBook/Makefile
Documentation/DocBook/parport-multi.fig [new file with mode: 0644]
Documentation/DocBook/parport-share.fig [new file with mode: 0644]
Documentation/DocBook/parport-structure.fig [new file with mode: 0644]
Documentation/DocBook/parportbook.sgml [new file with mode: 0644]
Documentation/DocBook/videobook.tmpl
MAINTAINERS
Makefile
arch/i386/kernel/entry.S
arch/i386/kernel/irq.c
arch/i386/kernel/mtrr.c
arch/mips/kernel/irixelf.c
arch/sparc/kernel/sys_sunos.c
arch/sparc/kernel/systbls.S
arch/sparc64/kernel/ioctl32.c
arch/sparc64/kernel/sys_sparc32.c
arch/sparc64/kernel/sys_sunos32.c
arch/sparc64/kernel/systbls.S
arch/sparc64/solaris/misc.c
arch/sparc64/solaris/systbl.S
drivers/char/drm/auth.c
drivers/char/drm/bufs.c
drivers/char/drm/context.c
drivers/char/drm/dma.c
drivers/char/drm/drawable.c
drivers/char/drm/drm.h
drivers/char/drm/drmP.h
drivers/char/drm/fops.c
drivers/char/drm/gamma_dma.c
drivers/char/drm/gamma_drv.c
drivers/char/drm/gamma_drv.h
drivers/char/drm/init.c
drivers/char/drm/ioctl.c
drivers/char/drm/lists.c
drivers/char/drm/lock.c
drivers/char/drm/memory.c
drivers/char/drm/proc.c
drivers/char/drm/tdfx_context.c
drivers/char/drm/tdfx_drv.c
drivers/char/drm/tdfx_drv.h
drivers/char/drm/vm.c
drivers/char/generic_serial.c
drivers/char/misc.c
drivers/char/ppdev.c
drivers/char/serial.c
drivers/ide/ide-floppy.c
drivers/net/ppp_async.c
drivers/net/shaper.c
drivers/parport/ChangeLog
drivers/parport/Config.in
drivers/parport/parport_pc.c
drivers/scsi/constants.c
drivers/scsi/eata_dma_proc.c
drivers/scsi/hosts.h
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_debug.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_syms.c
drivers/scsi/sd.c
drivers/scsi/sr.c
drivers/sound/sound_core.c
drivers/sound/sound_firmware.c
drivers/sound/soundcard.c
drivers/usb/Config.in
drivers/usb/dsbr100.c
drivers/usb/pegasus.c
drivers/usb/uhci.c
drivers/usb/uhci.h
drivers/usb/usb-storage.c
drivers/usb/usb-storage.h
drivers/usb/usb-uhci-debug.h
drivers/usb/usb-uhci.c
drivers/usb/usb-uhci.h
drivers/video/aty128.h
drivers/video/aty128fb.c
drivers/video/fbmem.c
fs/binfmt_elf.c
fs/ncpfs/inode.c
fs/openpromfs/inode.c
include/asm-alpha/parport.h
include/asm-arm/parport.h
include/asm-i386/mman.h
include/asm-i386/parport.h
include/asm-i386/unistd.h
include/asm-mips/parport.h
include/asm-mips64/parport.h
include/asm-sparc64/parport.h
include/linux/mm.h
include/linux/mmzone.h
include/linux/swap.h
include/linux/usb.h
ipc/shm.c
kernel/exit.c
kernel/fork.c
kernel/pm.c
mm/filemap.c
mm/mlock.c
mm/mmap.c
mm/mprotect.c
mm/mremap.c
mm/page_alloc.c
mm/vmscan.c
net/appletalk/ddp.c
net/econet/af_econet.c
net/packet/af_packet.c
scripts/docgen

diff --git a/CREDITS b/CREDITS
index b1b002f1d86b28959475c443c831606b752c6bef..b98bae8109859e1afe556e49ec3d9434236ded38 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -1035,7 +1035,8 @@ S: CV5 8BZ
 S: United Kingdom
 
 N: Ron Holt
-E: ron@sovereign.org
+E: ron@holt.org
+E: rholt@netcom.com
 W: http://www.holt.org/
 W: http://www.ronholt.com/
 D: Kernel development
index 76e6b6fd8452aae30106483117fb39c2322999fa..64bdd50f2ca204e45f8a0c252d8234f70b917e12 100644 (file)
@@ -2894,6 +2894,12 @@ CONFIG_PARPORT_PC_FIFO
   FIFO. See Documentation/parport.txt to find out how to specify
   which IRQ/DMA to use.
 
+SuperIO chipset support (EXPERIMENTAL)
+CONFIG_PARPORT_PC_SUPERIO
+  Saying Y here enables some probes for Super-IO chipsets in order to
+  find out things like base addresses, IRQ lines and DMA channels.  It
+  is safe to say N.
+
 Support for PCMCIA management for PC-style ports
 CONFIG_PARPORT_PC_PCMCIA
   Say Y here if you need PCMCIA support for your PC-style parallel
@@ -4495,18 +4501,6 @@ CONFIG_CHR_DEV_ST
   module, say M here and read Documentation/modules.txt and
   Documentation/scsi.txt .
 
-Extra SCSI Tapes
-CONFIG_ST_EXTRA_DEVS
-  This controls the amount of additional space allocated in tables for
-  drivers that are loaded as modules after the kernel is booted.  In the
-  event that the SCSI core itself was loaded as a module, this this value
-  is the number of additional tape devices that can be loaded after the
-  first host driver is loaded.
-
-  Admittedly this isn't pretty, but there are tons of race conditions
-  involved with resizing the internal arrays on the fly.  Someday this
-  flag will go away, and everything will work automatically.
-
 SCSI CDROM support
 CONFIG_BLK_DEV_SR
   If you want to use a SCSI CDROM under Linux, say Y and read the
index 31b2193f2d861e23c6f1d91469df182ac758d28a..6bc727e4391b3ef63d58def460f8ed8bbbe57729 100644 (file)
@@ -23,7 +23,7 @@ videobook.sgml: videobook.tmpl
 
 clean:
        rm -f core *~
-       rm -r$BOOKS
+       rm -r $(BOOKS)
 
 include $(TOPDIR)/Rules.make
 
diff --git a/Documentation/DocBook/parport-multi.fig b/Documentation/DocBook/parport-multi.fig
new file mode 100644 (file)
index 0000000..e0517b3
--- /dev/null
@@ -0,0 +1,59 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+6 1425 4350 5175 5475
+6 3450 5100 4425 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        4425 5475 4425 5100 3450 5100 3450 5475 4425 5475
+4 0 0 50 0 0 12 0.0000 4 135 510 3600 5400 Printer\001
+-6
+6 3375 4350 5175 4725
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        5175 4725 5175 4350 3375 4350 3375 4725 5175 4725
+4 0 0 50 0 0 12 0.0000 4 180 870 3825 4650 Multiplexor\001
+-6
+6 1425 4650 2775 5475
+6 1425 4650 2775 5475
+2 4 0 1 0 7 50 0 -1 0.000 0 0 6 0 0 5
+        2757 5475 2757 4650 1425 4650 1425 5475 2757 5475
+4 0 0 50 0 0 12 0.0000 4 180 735 1725 5100 Computer\001
+-6
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+        2775 4875 2700 4875 2700 5025 2775 5025 2775 4875
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+        2775 5175 2700 5175 2700 5325 2775 5325 2775 5175
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+        2775 4950 3600 4725
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+        2775 5250 3450 5325
+-6
+6 3150 2625 4125 3525
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        4125 3075 4125 2625 3150 2625 3150 3075 4125 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+        3675 3075 3675 3525
+4 0 0 50 0 0 12 0.0000 4 135 510 3300 2925 Printer\001
+-6
+6 4275 3450 5250 4350
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        5250 3900 5250 3450 4275 3450 4275 3900 5250 3900
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+        4800 3900 4800 4350
+4 0 0 50 0 0 12 0.0000 4 135 510 4425 3750 Printer\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        3900 4050 3900 3525 3375 3525 3375 4050 3900 4050
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 2
+        3675 4050 3675 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+        3600 4350 3750 4350 3750 4425 3600 4425 3600 4350
+2 2 0 1 0 7 50 0 -1 0.000 0 0 -1 0 0 5
+        4725 4350 4875 4350 4875 4425 4725 4425 4725 4350
+4 0 0 50 0 0 12 0.0000 4 135 285 3450 3900 ZIP\001
diff --git a/Documentation/DocBook/parport-share.fig b/Documentation/DocBook/parport-share.fig
new file mode 100644 (file)
index 0000000..fe4f373
--- /dev/null
@@ -0,0 +1,154 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #8e8e8e
+0 33 #8e8e8e
+0 34 #aeaaae
+0 35 #515551
+0 36 #414141
+0 37 #868286
+0 38 #8e8e8e
+0 39 #414141
+0 40 #868286
+0 41 #c7c3c7
+0 42 #e7e3e7
+0 43 #414141
+0 44 #868286
+0 45 #c7c3c7
+0 46 #e7e3e7
+0 47 #868286
+0 48 #c7c3c7
+0 49 #e7e3e7
+6 1200 3000 2250 4950
+6 1275 3150 2175 3675
+6 1312 3487 1837 3637
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 5
+        1312 3562 1312 3524 1474 3524 1474 3487 1675 3487
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+        1474 3637 1474 3562 1675 3562
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 2
+        1675 3524 1837 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+        1675 3487 1675 3524
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 2
+        1312 3562 1474 3562
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 5
+        1474 3637 1675 3637 1675 3562 1837 3562 1837 3524
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+        1716 3637 1797 3637 1797 3600
+2 1 0 1 7 -1 19 0 -1 0.000 2 0 -1 0 0 3
+        1716 3637 1716 3600 1797 3600
+-6
+6 1413 3345 2070 3397
+6 1994 3352 2070 3390
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+        1994 3390 1994 3352 2070 3352
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+        1994 3390 2070 3390 2070 3352
+-6
+6 1531 3353 1643 3389
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+        1568 3353 1606 3353 1606 3389 1568 3389 1568 3353
+2 2 0 0 40 39 19 0 20 0.000 2 0 -1 0 0 5
+        1606 3353 1643 3353 1643 3389 1606 3389 1606 3353
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+        1568 3353 1531 3353 1531 3389 1568 3389 1568 3353
+-6
+6 1413 3345 1465 3397
+1 3 0 0 0 39 18 0 20 0.000 1 0.0000 1439 3371 26 26 1439 3371 1439 3397
+1 3 0 0 40 41 18 0 20 0.000 1 0.0000 1439 3371 15 15 1439 3371 1443 3385
+-6
+2 2 0 0 40 7 19 0 20 0.000 2 0 -1 0 0 3
+        1950 3371 1875 3371 1950 3371
+2 2 0 0 40 41 19 0 20 0.000 2 0 -1 0 0 5
+        1945 3384 1896 3384 1896 3357 1945 3357 1945 3384
+-6
+6 1350 3183 2100 3300
+2 1 0 1 7 40 19 0 -1 0.000 2 0 -1 0 0 3
+        1350 3300 1350 3183 2100 3183
+2 1 0 1 40 -1 19 0 -1 0.000 2 0 -1 0 0 3
+        1350 3300 2100 3300 2100 3183
+-6
+2 1 0 1 7 7 19 0 -1 0.000 2 0 -1 0 0 5
+        1275 3675 1875 3675 1875 3450 2175 3450 2175 3150
+2 1 0 1 40 7 19 0 -1 0.000 2 0 -1 0 0 3
+        1275 3675 1275 3150 2175 3150
+-6
+6 1950 3750 2175 3975
+5 1 0 1 7 7 19 0 -1 0.000 0 0 0 0 2038.000 3900.000 1985 3953 1985 3847 2091 3847
+5 1 0 1 40 7 19 0 -1 0.000 0 1 0 0 2038.000 3900.000 1985 3953 2091 3953 2091 3847
+-6
+6 1200 4050 1800 4800
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4125 1725 4125
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4200 1725 4200
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4275 1725 4275
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4350 1725 4350
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4425 1725 4425
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4500 1725 4500
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4575 1725 4575
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4650 1725 4650
+2 1 0 2 40 7 19 0 -1 0.000 2 1 -1 0 0 2
+        1275 4725 1725 4725
+-6
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+        1200 4950 1425 4950 1425 4911 1200 4911 1200 4950
+2 2 0 1 0 39 20 0 20 0.000 2 0 -1 0 0 5
+        2025 4950 2250 4950 2250 4911 2025 4911 2025 4950
+2 2 0 1 0 42 20 0 20 0.000 2 0 -1 0 0 5
+        1200 4907 2250 4907 2250 3000 1200 3000 1200 4907
+-6
+6 2374 3225 3375 4050
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+        2374 3402 3139 3402 3257 4050
+        0.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 3
+        2374 3461 3096 3437 3198 4050
+        0.000 -1.000 0.000
+-6
+2 2 0 1 0 1 50 0 20 0.000 0 0 -1 0 0 5
+        2925 4575 4050 4575 4050 4875 2925 4875 2925 4575
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+        1200 3000 1575 2475 2400 2475 2250 3000 1200 3000
+2 3 0 1 0 8 50 0 20 0.000 0 0 -1 0 0 5
+        2925 4575 3000 4200 4050 4200 4050 4575 2925 4575
+2 2 0 1 0 0 50 0 20 0.000 0 0 -1 0 0 5
+        3075 4725 3900 4725 3900 4800 3075 4800 3075 4725
+2 2 0 1 0 46 50 0 20 0.000 0 0 -1 0 0 5
+        4800 3975 6450 3975 6450 4875 4800 4875 4800 3975
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+        5025 4575 6225 4575 6225 4725 5025 4725 5025 4575
+2 2 0 1 0 36 50 0 20 0.000 0 0 -1 0 0 5
+        5025 3975 6225 3975 6225 3300 5025 3300 5025 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+        4800 3975 4800 3825 5025 3825 5025 3975 4800 3975
+2 3 0 1 0 37 50 0 20 0.000 0 0 -1 0 0 5
+        6225 3825 6375 3825 6450 3975 6225 3975 6225 3825
+2 3 0 1 0 32 50 0 20 0.000 0 0 -1 0 0 5
+        2400 2475 2250 3000 2250 4875 2400 4350 2400 2475
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+        3075 4200 3075 4050 3300 4050 3375 4050 3375 4200 3075 4200
+2 3 0 1 0 37 50 0 -1 0.000 0 0 -1 0 0 6
+        3900 4200 3900 4050 3675 4050 3600 4050 3600 4200 3900 4200
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+        3705 4050 3825 3675 4185 3390 4590 3615 4800 4035
+        0.000 -1.000 -1.000 -1.000 0.000
+3 2 0 1 0 37 50 0 -1 0.000 0 0 0 5
+        3765 4050 3874 3708 4202 3449 4571 3654 4800 4185
+        0.000 -1.000 -1.000 -1.000 0.000
+4 0 0 50 0 0 12 0.0000 4 180 735 1350 5400 Computer\001
+4 0 0 50 0 0 12 0.0000 4 180 675 3150 5400 Zip drive\001
+4 0 0 50 0 0 12 0.0000 4 135 510 5325 5400 Printer\001
diff --git a/Documentation/DocBook/parport-structure.fig b/Documentation/DocBook/parport-structure.fig
new file mode 100644 (file)
index 0000000..4299ce6
--- /dev/null
@@ -0,0 +1,60 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter  
+100.00
+Single
+-2
+1200 2
+0 32 #414541
+0 33 #8e8e8e
+0 34 #414541
+0 35 #8e8e8e
+0 36 #414541
+0 37 #8e8e8e
+0 38 #414541
+0 39 #8e8e8e
+0 40 #414541
+0 41 #8e8e8e
+0 42 #414541
+0 43 #8e8e8e
+0 44 #414141
+0 45 #868286
+0 46 #c7c3c7
+0 47 #8e8e8e
+0 48 #414141
+0 49 #868286
+0 50 #c7c3c7
+0 51 #e7e3e7
+6 2025 1800 3075 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+        3045 2250 3045 1800 2025 1800 2025 2250 3045 2250
+4 0 0 50 0 14 12 0.0000 4 180 210 2400 2100 lp\001
+-6
+6 4125 1800 5175 2250
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+        5145 2250 5145 1800 4125 1800 4125 2250 5145 2250
+4 0 0 50 0 14 12 0.0000 4 135 315 4425 2100 ppa\001
+-6
+6 3225 3075 4275 3525
+6 3375 3225 4125 3450
+4 0 0 50 0 14 12 0.0000 4 165 735 3375 3375 parport\001
+-6
+2 4 0 1 0 7 50 0 -1 0.000 0 0 3 0 0 5
+        4245 3525 4245 3075 3225 3075 3225 3525 4245 3525
+-6
+6 3000 4350 4500 4800
+2 4 0 1 0 7 50 0 -1 0.000 0 0 7 0 0 5
+        4500 4800 4500 4350 3000 4350 3000 4800 4500 4800
+4 0 0 50 0 14 12 0.0000 4 165 1050 3225 4650 parport_pc\001
+-6
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        2550 2250 3600 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        4650 2250 3825 3075
+2 1 0 1 0 7 50 0 -1 0.000 0 0 -1 1 0 2
+       1 1 1.00 60.00 120.00
+        3750 3525 3750 4350
diff --git a/Documentation/DocBook/parportbook.sgml b/Documentation/DocBook/parportbook.sgml
new file mode 100644 (file)
index 0000000..1644748
--- /dev/null
@@ -0,0 +1,1747 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]>
+
+<book id="ParportGuide">
+ <bookinfo>
+  <title>The Parallel Port Subsystem</title>
+
+  <authorgroup>
+   <author>
+    <firstname>Tim</firstname>
+    <surname>Waugh</surname>
+    <affiliation>
+     <address>
+      <email>twaugh@redhat.com</email>
+     </address>
+    </affiliation>
+   </author>
+  </authorgroup>
+
+  <copyright>
+   <year>1999-2000</year>
+   <holder>Tim Waugh</holder>
+  </copyright>
+
+  <legalnotice>
+   <para>
+     This documentation is free software; you can redistribute
+     it and/or modify it under the terms of the GNU General Public
+     License as published by the Free Software Foundation; either
+     version 2 of the License, or (at your option) any later
+     version.
+   </para>
+      
+   <para>
+     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.
+   </para>
+      
+   <para>
+     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
+   </para>
+      
+   <para>
+     For more details see the file COPYING in the source
+     distribution of Linux.
+   </para>
+  </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+<chapter id="design">
+<title>Design goals</title>
+
+<sect1>
+<title>The problems</title>
+
+<!-- Short-comings -->
+<!-- How they are addressed -->
+
+<!-- Short-comings
+     - simplistic lp driver
+     - platform differences
+     - no support for Zip drive pass-through
+     - no support for readback? When did Carsten add it?
+     - more parallel port devices. Figures?
+     - IEEE 1284 transfer modes: no advanced modes
+  -->
+
+<para>The first parallel port support for Linux came with the line
+printer driver, <filename>lp</filename>.  The printer driver is a
+character special device, and (in Linux 2.0) had support for writing,
+via <function>write</function>, and configuration and statistics
+reporting via <function>ioctl</function>.</para>
+
+<para>The printer driver could be used on any computer that had an IBM
+PC-compatible parallel port.  Because some architectures have parallel
+ports that aren't really the same as PC-style ports, other variants of
+the printer driver were written in order to support Amiga and Atari
+parallel ports.</para>
+
+<para>When the Iomega Zip drive was released, and a driver written for
+it, a problem became apparent.  The Zip drive is a parallel port
+device that provides a parallel port of its own---it is designed to
+sit between a computer and an attached printer, with the printer
+plugged into the Zip drive, and the Zip drive plugged into the
+computer.</para>
+
+<para>The problem was that, although printers and Zip drives were both
+supported, for any given port only one could be used at a time.  Only
+one of the two drivers could be present in the kernel at once.  This
+was because of the fact that both drivers wanted to drive the same
+hardware---the parallel port.  When the printer driver initialised, it
+would call the <function>check_region</function> function to make sure
+that the IO region associated with the parallel port was free, and
+then it would call <function>request_region</function> to allocate it.
+The Zip drive used the same mechanism.  Whichever driver initialised
+first would gain exclusive control of the parallel port.</para>
+
+<para>The only way around this problem at the time was to make sure
+that both drivers were available as loadable kernel modules.  To use
+the printer, load the printer driver module; then for the Zip drive,
+unload the printer driver module and load the Zip driver
+module.</para>
+
+<para>The net effect was that printing a document that was stored on a Zip
+drive was a bit of an ordeal, at least if the Zip drive and printer
+shared a parallel port.  A better solution was needed.</para>
+
+<para>Zip drives are not the only devices that presented problems for
+Linux.  There are other devices with pass-through ports, for example
+parallel port CD-ROM drives.  There are also printers that report
+their status textually rather than using simple error pins: sending a
+command to the printer can cause it to report the number of pages that
+it has ever printed, or how much free memory it has, or whether it is
+running out of toner, and so on.  The printer driver didn't originally
+offer any facility for reading back this information (although Carsten
+Gross added nibble mode readback support for kernel 2.2).</para>
+
+<!-- IEEE 1284 transfer modes: no advanced modes --> 
+
+<para>The IEEE has issued a standards document called IEEE 1284, which
+documents existing practice for parallel port communications in a
+variety of modes.  Those modes are: <quote>compatibility</quote>,
+reverse nibble, reverse byte, ECP and EPP.  Newer devices often use
+the more advanced modes of transfer (ECP and EPP).  In Linux 2.0, the
+printer driver only supported <quote>compatibility mode</quote>
+(i.e. normal printer protocol) and reverse nibble mode.</para>
+
+</sect1>
+
+<sect1>
+<title>The solutions</title>
+
+<!-- How they are addressed
+     - sharing model
+     - overview of structure (i.e. port drivers) in 2.2 and 2.3.
+     - IEEE 1284 stuff
+     - whether or not 'platform independence' goal was met
+  -->
+
+<para>The <filename>parport</filename> code in Linux 2.2 was designed
+to meet these problems of architectural differences in parallel ports,
+of port-sharing between devices with pass-through ports, and of lack
+of support for IEEE 1284 transfer modes.</para>
+
+<!-- platform differences -->
+
+<para>There are two layers to the
+<filename>parport</filename> subsystem, only one of which deals
+directly with the hardware.  The other layer deals with sharing and
+IEEE 1284 transfer modes.  In this way, parallel support for a
+particular architecture comes in the form of a module which registers
+itself with the generic sharing layer.</para>
+
+<!-- sharing model -->
+
+<para>The sharing model provided by the <filename>parport</filename>
+subsystem is one of exclusive access.  A device driver, such as the
+printer driver, must ask the <filename>parport</filename> layer for
+access to the port, and can only use the port once access has been
+granted.  When it has finished a <quote>transaction</quote>, it can
+tell the <filename>parport</filename> layer that it may release the
+port for other device drivers to use.</para>
+
+<!-- talk a bit about how drivers can share devices on the same port -->
+
+<para>Devices with pass-through ports all manage to share a parallel
+port with other devices in generally the same way.  The device has a
+latch for each of the pins on its pass-through port.  The normal state
+of affairs is pass-through mode, with the device copying the signal
+lines between its host port and its pass-through port.  When the
+device sees a special signal from the host port, it latches the
+pass-through port so that devices further downstream don't get
+confused by the pass-through device's conversation with the host
+parallel port: the device connected to the pass-through port (and any
+devices connected in turn to it) are effectively cut off from the
+computer.  When the pass-through device has completed its transaction
+with the computer, it enables the pass-through port again.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata Align=center scalefit=1 fileref="parport-share.eps">
+</imageobject>
+</mediaobject>
+
+<para>This technique relies on certain <quote>special signals</quote>
+being invisible to devices that aren't watching for them.  This tends
+to mean only changing the data signals and leaving the control signals
+alone.  IEEE 1284.3 documents a standard protocol for daisy-chaining
+devices together with parallel ports.</para>
+
+<!-- transfer modes -->
+
+<para>Support for standard transfer modes are provided as operations
+that can be performed on a port, along with operations for setting the
+data lines, or the control lines, or reading the status lines.  These
+operations appear to the device driver as function pointers; more
+later.</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="transfermodes">
+<title>Standard transfer modes</title>
+
+<!-- Defined by IEEE, but in common use (even though there are widely -->
+<!-- varying implementations). -->
+
+<para>The <quote>standard</quote> transfer modes in use over the
+parallel port are <quote>defined</quote> by a document called IEEE
+1284.  It really just codifies existing practice and documents
+protocols (and variations on protocols) that have been in common use
+for quite some time.</para>
+
+<para>The original definitions of which pin did what were set out by
+Centronics Data Computer Corporation, but only the printer-side
+interface signals were specified.</para>
+
+<para>By the early 1980s, IBM's host-side implementation had become
+the most widely used.  New printers emerged that claimed Centronics
+compatibility, but although compatible with Centronics they differed
+from one another in a number of ways.</para>
+
+<para>As a result of this, when IEEE 1284 was published in 1994, all
+that it could really do was document the various protocols that are
+used for printers (there are about six variations on a theme).</para>
+
+<para>In addition to the protocol used to talk to
+Centronics-compatible printers, IEEE 1284 defined other protocols that
+are used for unidirectional peripheral-to-host transfers (reverse
+nibble and reverse byte) and for fast bidirectional transfers (ECP and
+EPP).</para>
+
+</chapter>
+
+<chapter id="structure">
+<title>Structure</title>
+
+<!-- Main structure
+     - sharing core
+     - parports and their IEEE 1284 overrides
+       - IEEE 1284 transfer modes for generic ports
+       - maybe mention muxes here
+     - pardevices
+     - IEEE 1284.3 API
+  -->
+
+<!-- Diagram -->
+
+<mediaobject>
+<imageobject>
+<imagedata Align=Center ScaleFit=1 fileref="parport-structure.eps">
+</imageobject>
+</mediaobject>
+
+<sect1>
+<title>Sharing core</title>
+
+<!-- sharing core -->
+
+<para>At the core of the <filename>parport</filename> subsystem is the
+sharing mechanism (see <filename>drivers/parport/share.c</filename>).
+This module, <filename>parport</filename>, is responsible for
+keeping track of which ports there are in the system, which device
+drivers might be interested in new ports, and whether or not each port
+is available for use (or if not, which driver is currently using
+it).</para>
+
+</sect1>
+
+<sect1>
+<title>Parports and their overrides</title>
+<!-- parports and their overrides -->
+
+<para>The generic <filename>parport</filename> sharing code doesn't
+directly handle the parallel port hardware.  That is done instead by
+<quote>low-level</quote> <filename>parport</filename> drivers.  The
+function of a low-level <filename>parport</filename> driver is to
+detect parallel ports, register them with the sharing code, and
+provide a list of access functions for each port.</para>
+
+<para>The most basic access functions that must be provided are ones
+for examining the status lines, for setting the control lines, and for
+setting the data lines.  There are also access functions for setting
+the direction of the data lines; normally they are in the
+<quote>forward</quote> direction (that is, the computer drives them),
+but some ports allow switching to <quote>reverse</quote> mode (driven
+by the peripheral).  There is an access function for examining the
+data lines once in reverse mode.</para>
+
+</sect1>
+
+<sect1>
+<title>IEEE 1284 transfer modes</title>
+<!-- IEEE 1284 transfer modes -->
+
+<para>Stacked on top of the sharing mechanism, but still in the
+<filename>parport</filename> module, are functions for transferring
+data.  They are provided for the device drivers to use, and are very
+much like library routines.  Since these transfer functions are
+provided by the generic <filename>parport</filename> core they must
+use the <quote>lowest common denominator</quote> set of access
+functions: they can set the control lines, examine the status lines,
+and use the data lines.  With some parallel ports the data lines can
+only be set and not examined, and with other ports accessing the data
+register causes control line activity; with these types of situations,
+the IEEE 1284 transfer functions make a best effort attempt to do the
+right thing.  In some cases, it is not physically possible to use
+particular IEEE 1284 transfer modes.</para>
+
+<para>The low-level <filename>parport</filename> drivers also provide
+IEEE 1284 transfer functions, as names in the access function list.
+The low-level driver can just name the generic IEEE 1284 transfer
+functions for this.  Some parallel ports can do IEEE 1284 transfers in
+hardware; for those ports, the low-level driver can provide functions
+to utilise that feature.</para>
+
+</sect1>
+
+<!-- muxes? -->
+
+<!-- pardevices and pardrivers -->
+
+<sect1>
+<title>Pardevices and parport_drivers</title>
+
+<para>When a parallel port device driver (such as
+<filename>lp</filename>) initialises it tells the sharing layer about
+itself using <function>parport_register_driver</function>.  The
+information is put into a <structname>struct
+parport_driver</structname>, which is put into a linked list.  The
+information in a <structname>struct parport_driver</structname> really
+just amounts to some function pointers to callbacks in the parallel
+port device driver.</para>
+
+<para>During its initialisation, a low-level port driver tells the
+sharing layer about all the ports that it has found (using
+<function>parport_register_port</function>), and the sharing layer
+creates a <structname>struct parport</structname> for each of them.
+Each <structname>struct parport</structname> contains (among other
+things) a pointer to a <structname>struct
+parport_operations</structname>, which is a list of function pointers
+for the various operations that can be performed on a port.  You can
+think of a <structname>struct parport</structname> as a parallel port
+<quote>object</quote>, if <quote>object-orientated</quote> programming
+is your thing.  The <structname>parport</structname> structures are
+chained in a linked list, whose head is <varname>portlist</varname>
+(in <filename>drivers/parport/share.c</filename>).</para>
+
+<para>Once the port has been registered, the low-level port driver
+announces it.  The <function>parport_announce_port</function> function
+walks down the list of parallel port device drivers
+(<structname>struct parport_driver</structname>s) calling the
+<function>attach</function> function of each.</para>
+
+<para>Similarly, a low-level port driver can undo the effect of
+registering a port with the
+<function>parport_unregister_port</function> function, and device
+drivers are notified using the <function>detach</function>
+callback.</para>
+
+<para>Device drivers can undo the effect of registering themselves
+with the <function>parport_unregister_driver</function>
+function.</para>
+
+</sect1>
+
+<!-- IEEE 1284.3 API -->
+
+<sect1>
+<title>The IEEE 1284.3 API</title>
+
+<para>The ability to daisy-chain devices is very useful, but if every
+device does it in a different way it could lead to lots of
+complications for device driver writers.  Fortunately, the IEEE are
+standardising it in IEEE 1284.3, which covers daisy-chain devices and
+port multiplexors.</para>
+
+<para>At the time of writing, IEEE 1284.3 has not been published, but
+the draft specifies the on-the-wire protocol for daisy-chaining and
+multiplexing, and also suggests a programming interface for using it.
+That interface (or most of it) has been implemented in the
+<filename>parport</filename> code in Linux.</para>
+
+<para>At initialisation of the parallel port <quote>bus</quote>, daisy-chained
+devices are assigned addresses starting from zero.  There can only be
+four devices with daisy-chain addresses, plus one device on the end
+that doesn't know about daisy-chaining and thinks it's connected
+directly to a computer.</para>
+
+<para>Another way of connecting more parallel port devices is to use a
+multiplexor.  The idea is to have a device that is connected directly
+to a parallel port on a computer, but has a number of parallel ports
+on the other side for other peripherals to connect to (two or four
+ports are allowed).  The multiplexor switches control to different
+ports under software control---it is, in effect, a programmable
+printer switch.</para>
+
+<para>Combining the ability of daisy-chaining five devices together
+with the ability to multiplex one parallel port between four gives the
+potential to have twenty peripherals connected to the same parallel
+port!</para>
+
+<para>In addition, of course, a single computer can have multiple
+parallel ports.  So, each parallel port peripheral in the system can
+be identified with three numbers, or co-ordinates: the parallel port,
+the multiplexed port, and the daisy-chain address.</para>
+
+<mediaobject>
+<imageobject>
+<imagedata align=center scalefit=1 fileref="parport-multi.eps">
+</imageobject>
+</mediaobject>
+
+<!-- x parport_open -->
+<!-- x parport_close -->
+<!-- x parport_device_id -->
+<!-- x parport_device_num -->
+<!-- x parport_device_coords -->
+<!-- x parport_find_device -->
+<!-- x parport_find_class -->
+
+<para>Each device in the system is numbered at initialisation (by
+<function>parport_daisy_init</function>).  You can convert between
+this device number and its co-ordinates with
+<function>parport_device_num</function> and
+<function>parport_device_coords</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_device_num</function></funcdef>
+  <paramdef>int <parameter>parport</parameter></paramdef>
+  <paramdef>int <parameter>mux</parameter></paramdef>
+  <paramdef>int <parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_device_coords</function></funcdef>
+  <paramdef>int <parameter>devnum</parameter></paramdef>
+  <paramdef>int *<parameter>parport</parameter></paramdef>
+  <paramdef>int *<parameter>mux</parameter></paramdef>
+  <paramdef>int *<parameter>daisy</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>Any parallel port peripheral will be connected directly or
+indirectly to a parallel port on the system, but it won't have a
+daisy-chain address if it does not know about daisy-chaining, and it
+won't be connected through a multiplexor port if there is no
+multiplexor.  The special co-ordinate value <constant>-1</constant> is
+used to indicate these cases.</para>
+
+<para>Two functions are provided for finding devices based on their
+IEEE 1284 Device ID: <function>parport_find_device</function> and
+<function>parport_find_class</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_find_device</function></funcdef>
+  <paramdef>const char *<parameter>mfg</parameter></paramdef>
+  <paramdef>const char *<parameter>mdl</parameter></paramdef>
+  <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_find_class</function></funcdef>
+  <paramdef>parport_device_class <parameter>cls</parameter></paramdef>
+  <paramdef>int <parameter>from</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>These functions take a device number (in addition to some other
+things), and return another device number.  They walk through the list
+of detected devices until they find one that matches the requirements,
+and then return that device number (or <constant>-1</constant> if
+there are no more such devices).  They start their search at the
+device after the one in the list with the number given (at
+<parameter>from</parameter>+1, in other words).</para>
+
+</sect1>
+
+</chapter>
+
+<chapter id="drivers">
+<title>Device driver's view</title>
+
+<!-- Cover:
+     - sharing interface, preemption, interrupts, wakeups...
+     - IEEE 1284.3 interface
+     - port operations
+       - why can read data but ctr is faked, etc.
+  -->
+
+<!-- I should take a look at the kernel hackers' guide bit I wrote, -->
+<!-- as that deals with a lot of this.  The main complaint with it  -->
+<!-- was that there weren't enough examples, but 'The printer -->
+<!-- driver' should deal with that later; might be worth mentioning -->
+<!-- in the text. -->
+
+<para>This section is written from the point of view of the device
+driver programmer, who might be writing a driver for a printer or a
+scanner or else anything that plugs into the parallel port.  It
+explains how to use the <filename>parport</filename> interface to find
+parallel ports, use them, and share them with other device
+drivers.</para>
+
+<para>We'll start out with a description of the various functions that
+can be called, and then look at a reasonably simple example of their
+use: the printer driver.</para>
+
+<para>The interactions between the device driver and the
+<filename>parport</filename> layer are as follows.  First, the device
+driver registers its existence with <filename>parport</filename>, in
+order to get told about any parallel ports that have been (or will be)
+detected.  When it gets told about a parallel port, it then tells
+<filename>parport</filename> that it wants to drive a device on that
+port.  Thereafter it can claim exclusive access to the port in order
+to talk to its device.</para>
+
+<para>So, the first thing for the device driver to do is tell
+<filename>parport</filename> that it wants to know what parallel ports
+are on the system.  To do this, it uses the
+<function>parport_register_device</function> function:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_driver {
+        const char *name;
+        void (*attach) (struct parport *);
+        void (*detach) (struct parport *);
+        struct parport_driver *next;
+};
+]]></programlisting>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_register_driver</function></funcdef>
+  <paramdef>struct parport_driver *<parameter>driver</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>In other words, the device driver passes pointers to a couple of
+functions to <filename>parport</filename>, and
+<filename>parport</filename> calls <function>attach</function> for
+each port that's detected (and <function>detach</function> for each
+port that disappears -- yes, this can happen).</para>
+
+<para>The next thing that happens is that the device driver tells
+<filename>parport</filename> that it thinks there's a device on the
+port that it can drive.  This typically will happen in the driver's
+<function>attach</function> function, and is done with
+<function>parport_register_device</function>:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const char *<parameter>name</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>void <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>void <parameter>(*irq_func)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>port</parameter> comes from the parameter supplied
+to the <function>attach</function> function when it is called, or
+alternatively can be found from the list of detected parallel ports
+directly with the (now deprecated)
+<function>parport_enumerate</function> function.</para>
+
+<para>The next three parameters, <parameter>pf</parameter>,
+<parameter>kf</parameter>, and <parameter>irq_func</parameter>, are
+more function pointers.  These callback functions get called under
+various circumstances, and are always given the
+<parameter>handle</parameter> as one of their parameters.</para>
+
+<para>The preemption callback, <parameter>pf</parameter>, is called
+when the driver has claimed access to the port but another device
+driver wants access.  If the driver is willing to let the port go, it
+should return zero and the port will be released on its behalf.  There
+is no need to call <function>parport_release</function>.  If
+<parameter>pf</parameter> gets called at a bad time for letting the
+port go, it should return non-zero and no action will be taken.  It is
+good manners for the driver to try to release the port at the earliest
+opportunity after its preemption callback is called.</para>
+
+<para>The <quote>kick</quote> callback, <parameter>kf</parameter>, is
+called when the port can be claimed for exclusive access; that is,
+<function>parport_claim</function> is guaranteed to succeed inside the
+<quote>kick</quote> callback.  If the driver wants to claim the port
+it should do so; otherwise, it need not take any action.</para>
+
+<para>The <parameter>irq_func</parameter> callback is called,
+predictably, when a parallel port interrupt is generated.  But it is
+not the only code that hooks on the interrupt.  The sequence is this:
+the lowlevel driver is the one that has done
+<function>request_irq</function>; it then does whatever
+hardware-specific things it needs to do to the parallel port hardware
+(for PC-style ports, there is nothing special to do); it then tells
+the IEEE 1284 code about the interrupt, which may involve reacting to
+an IEEE 1284 event, depending on the current IEEE 1284 phase; and
+finally the <parameter>irq_func</parameter> function is called.</para>
+
+<para>None of the callback functions are allowed to block.</para>
+
+<para>The <parameter>flags</parameter> are for telling
+<filename>parport</filename> any requirements or hints that are
+useful.  The only useful value here (other than
+<constant>0</constant>, which is the usual value) is
+<constant>PARPORT_DEV_EXCL</constant>.  The point of that flag is to
+request exclusive access at all times---once a driver has successfully
+called <function>parport_register_device</function> with that flag, no
+other device drivers will be able to register devices on that port
+(until the successful driver deregisters its device, of
+course).</para>
+
+<para>The <constant>PARPORT_DEV_EXCL</constant> flag is for preventing
+port sharing, and so should only be used when sharing the port with
+other device drivers is impossible and would lead to incorrect
+behaviour.  Use it sparingly!</para>
+
+<para>Devices can also be registered by device drivers based on their
+device numbers (the same device numbers as in the previous
+section).</para>
+
+<para>The <function>parport_open</function> function is similar to
+<function>parport_register_device</function>, and
+<function>parport_close</function> is the equivalent of
+<function>parport_unregister_device</function>.  The difference is
+that <function>parport_open</function> takes a device number rather
+than a pointer to a <structname>struct parport</structname>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_open</function></funcdef>
+  <paramdef>int <parameter>devnum</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*irqf)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_close</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>struct pardevice *<function>parport_register_device</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const char *<parameter>name</parameter></paramdef>
+  <paramdef>int <parameter>(*pf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*kf)</parameter>
+    <funcparams>void *</funcparams></paramdef>
+  <paramdef>int <parameter>(*irqf)</parameter>
+    <funcparams>int, void *, struct pt_regs *</funcparams></paramdef>
+  <paramdef>int <parameter>flags</parameter></paramdef>
+  <paramdef>void *<parameter>handle</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_unregister_device</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The intended use of these functions is during driver
+initialisation while the driver looks for devices that it supports, as
+demonstrated by the following code fragment:</para>
+
+<programlisting>
+<![CDATA[
+int devnum = -1;
+while ((devnum = parport_find_class (PARPORT_CLASS_DIGCAM,
+                                     devnum)) != -1) {
+    struct pardevice *dev = parport_open (devnum, ...);
+    ...
+}
+]]></programlisting>
+
+<para>Once your device driver has registered its device and been
+handed a pointer to a <structname>struct pardevice</structname>, the
+next thing you are likely to want to do is communicate with the device
+you think is there.  To do that you'll need to claim access to the
+port.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_claim</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_claim_or_block</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>void <function>parport_release</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>To claim access to the port, use
+<function>parport_claim</function> or
+<function>parport_claim_or_block</function>.  The first of these will
+not block, and so can be used from interrupt context.  If
+<function>parport_claim</function> succeeds it will return zero and
+the port is available to use.  It may fail (returning non-zero) if the
+port is in use by another driver and that driver is not willing to
+relinquish control of the port.</para>
+
+<para>The other function, <function>parport_claim_or_block</function>,
+will block if necessary to wait for the port to be free.  If it slept,
+it returns <constant>1</constant>; if it succeeded without needing to
+sleep it returns <constant>0</constant>.  If it fails it will return a
+negative error code.</para>
+
+<para>When you have finished communicating with the device, you can
+give up access to the port so that other drivers can communicate with
+their devices.  The <function>parport_release</function> function
+cannot fail, but it should not be called without the port claimed.
+Similarly, you should not try to claim the port if you already have it
+claimed.</para>
+
+<para>You may find that although there are convenient points for your
+driver to relinquish the parallel port and allow other drivers to talk
+to their devices, it would be preferable to keep hold of the port.
+The printer driver only needs the port when there is data to print,
+for example, but a network driver (such as PLIP) could be sent a
+remote packet at any time.  With PLIP, it is no huge catastrophe if a
+network packet is dropped, since it will likely be sent again, so it
+is possible for that kind of driver to share the port with other
+(pass-through) devices.</para>
+
+<para>The <function>parport_yield</function> and
+<function>parport_yield_blocking</function> functions are for marking
+points in the driver at which other drivers may claim the port and use
+their devices.  Yielding the port is similar to releasing it and
+reclaiming it, but it more efficient because nothing is done if there
+are no other devices needing the port.  In fact, nothing is done even
+if there are other devices waiting but the current device is still
+within its <quote>timeslice</quote>.  The default timeslice is half a
+second, but it can be adjusted via a <filename>/proc</filename>
+entry.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_yield</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_yield_blocking</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The first of these, <function>parport_yield</function>, will not
+block but as a result may fail.  The return value for
+<function>parport_yield</function> is the same as for
+<function>parport_claim</function>.  The blocking version,
+<function>parport_yield_blocking</function>, has the same return code
+as <function>parport_claim_or_block</function>.</para>
+
+<para>Once the port has been claimed, the device driver can use the
+functions in the <structname>struct parport_operations</structname>
+pointer in the <structname>struct parport</structname> it has a
+pointer to.  For example:</para>
+
+<programlisting>
+<![CDATA[
+port->ops->write_data (port, d);
+]]></programlisting>
+
+<para>Some of these operations have <quote>shortcuts</quote>.  For
+instance, <function>parport_write_data</function> is equivalent to the
+above, but may be a little bit faster (it's a macro that in some cases
+can avoid needing to indirect through <varname>port</varname> and
+<varname>ops</varname>).</para>
+
+</chapter>
+
+<chapter id="portdrivers">
+<title>Port drivers</title>
+
+<!-- What port drivers are for (i.e. implementing parport objects). -->
+
+<para>To recap, then:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+The device driver registers itself with <filename>parport</filename>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+A low-level driver finds a parallel port and registers it with
+<filename>parport</filename> (these first two things can happen in
+either order).  This registration creates a <structname>struct
+parport</structname> which is linked onto a list of known ports.
+</para>
+</listitem>
+
+<listitem>
+<para>
+<filename>parport</filename> calls the <function>attach</function>
+function of each registered device driver, passing it the pointer to
+the new <structname>struct parport</structname>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver gets a handle from <filename>parport</filename>, for
+use with
+<function>parport_claim</function>/<function>release</function>.  This
+handle takes the form of a pointer to a <structname>struct
+pardevice</structname>, representing a particular device on the
+parallel port, and is acquired using
+<function>parport_register_device</function>.
+</para>
+</listitem>
+
+<listitem>
+<para>
+The device driver claims the port using
+<function>parport_claim</function> (or
+<function>function_claim_or_block</function>).
+</para>
+</listitem>
+
+<listitem>
+<para>
+Then it goes ahead and uses the port.  When finished it releases the
+port.
+</para>
+</listitem>
+
+</itemizedlist>
+
+<para>The purpose of the low-level drivers, then, is to detect
+parallel ports and provide methods of accessing them
+(i.e. implementing the operations in <structname>struct
+parport_operations</structname>).</para>
+
+<!-- Interaction with sharing engine; port state -->
+<!-- What did I mean by that? -->
+
+<!-- Talk about parport_pc implementation, and contrast with e.g. amiga -->
+
+<para>A more complete description of which operation is supposed to do
+what is available in
+<filename>Documentation/parport-lowlevel.txt</filename>.</para>
+
+</chapter>
+
+<chapter id="lp">
+<title>The printer driver</title>
+
+<!-- Talk the reader through the printer driver. -->
+<!-- Could even talk about parallel port console here. -->
+
+<para>The printer driver, <filename>lp</filename> is a character
+special device driver and a <filename>parport</filename> client.  As a
+character special device driver it registers a <structname>struct
+file_operations</structname> using
+<function>register_chrdev</function>, with pointers filled in for
+<structfield>write</structfield>, <structfield>ioctl</structfield>,
+<structfield>open</structfield> and
+<structfield>release</structfield>.  As a client of
+<filename>parport</filename>, it registers a <structname>struct
+parport_driver</structname> using
+<function>parport_register_driver</function>, so that
+<filename>parport</filename> knows to call
+<function>lp_attach</function> when a new parallel port is discovered
+(and <function>lp_detach</function> when it goes away).</para>
+
+<para>The parallel port console functionality is also implemented in
+<filename>lp.c</filename>, but that won't be covered here (it's quite
+simple though).</para>
+
+<para>The initialisation of the driver is quite easy to understand
+(see <function>lp_init</function>).  The <varname>lp_table</varname>
+is an array of structures that contain information about a specific
+device (the <structname>struct pardevice</structname> associated with
+it, for example).  That array is initialised to sensible values first
+of all.</para>
+
+<para>Next, the printer driver calls
+<function>register_chrdev</function> passing it a pointer to
+<varname>lp_fops</varname>, which contains function pointers for the
+printer driver's implementation of <function>open</function>,
+<function>write</function>, and so on.  This part is the same as for
+any character special device driver.</para>
+
+<para>After successfully registering itself as a character special
+device driver, the printer driver registers itself as a
+<filename>parport</filename> client using
+<function>parport_register_driver</function>.  It passes a pointer to
+this structure:</para>
+
+<programlisting>
+<![CDATA[
+static struct parport_driver lp_driver = {
+        "lp",
+        lp_attach,
+        lp_detach,
+        NULL
+};
+]]></programlisting>
+
+<para>The <function>lp_detach</function> function is not very
+interesting (it does nothing); the interesting bit is
+<function>lp_attach</function>.  What goes on here depends on whether
+the user supplied any parameters.  The possibilities are: no
+parameters supplied, in which case the printer driver uses every port
+that is detected; the user supplied the parameter <quote>auto</quote>,
+in which case only ports on which the device ID string indicates a
+printer is present are used; or the user supplied a list of parallel
+port numbers to try, in which case only those are used.</para>
+
+<para>For each port that the printer driver wants to use (see
+<function>lp_register</function>), it calls
+<function>parport_register_device</function> and stores the resulting
+<structname>struct pardevice</structname> pointer in the
+<varname>lp_table</varname>.  If the user told it to do so, it then
+resets the printer.</para>
+
+<para>The other interesting piece of the printer driver, from the
+point of view of <filename>parport</filename>, is
+<function>lp_write</function>.  In this function, the user space
+process has data that it wants printed, and the printer driver hands
+it off to the <filename>parport</filename> code to deal with.</para>
+
+<para>The <filename>parport</filename> functions it uses that we have
+not seen yet are <function>parport_negotiate</function>,
+<function>parport_set_timeout</function>, and
+<function>parport_write</function>.  These functions are part of the
+IEEE 1284 implementation.</para>
+
+<para>The way the IEEE 1284 protocol works is that the host tells the
+peripheral what transfer mode it would like to use, and the peripheral
+either accepts that mode or rejects it; if the mode is rejected, the
+host can try again with a different mode.  This is the negotation
+phase.  Once the peripheral has accepted a particular transfer mode,
+data transfer can begin that mode.</para>
+
+<para>The particular transfer mode that the printer driver wants to
+use is named in IEEE 1284 as <quote>compatibility</quote> mode, and
+the function to request a particular mode is called
+<function>parport_negotiate</function>.</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>int <function>parport_negotiate</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>int <parameter>mode</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>The <parameter>modes</parameter> parameter is a symbolic
+constant representing an IEEE 1284 mode; in this instance, it is
+<constant>IEEE1284_MODE_COMPAT</constant>. (Compatibility mode is
+slightly different to the other modes---rather than being specifically
+requested, it is the default until another mode is selected.)</para>
+
+<para>Back to <function>lp_write</function> then.  First, access to
+the parallel port is secured with
+<function>parport_claim_or_block</function>.  At this point the driver
+might sleep, waiting for another driver (perhaps a Zip drive driver,
+for instance) to let the port go.  Next, it goes to compatibility mode
+using <function>parport_negotiate</function>.</para>
+
+<para>The main work is done in the write-loop.  In particular, the
+line that hands the data over to <filename>parport</filename>
+reads:</para>
+
+<programlisting>
+<![CDATA[
+        written = parport_write (port, kbuf, copy_size);
+]]></programlisting>
+
+<para>The <function>parport_write</function> function writes data to
+the peripheral using the currently selected transfer mode
+(compatibility mode, in this case).  It returns the number of bytes
+successfully written:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>ssize_t <function>parport_write</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>const void *<parameter>buf</parameter></paramdef>
+  <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<funcsynopsis><funcprototype>
+  <funcdef>ssize_t <function>parport_read</function></funcdef>
+  <paramdef>struct parport *<parameter>port</parameter></paramdef>
+  <paramdef>void *<parameter>buf</parameter></paramdef>
+  <paramdef>size_t <parameter>len</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>(<function>parport_read</function> does what it sounds like, but
+only works for modes in which reverse transfer is possible.  Of
+course, <function>parport_write</function> only works in modes in
+which forward transfer is possible, too.)</para>
+
+<para>The <parameter>buf</parameter> pointer should be to kernel space
+memory, and obviously the <parameter>len</parameter> parameter
+specifies the amount of data to transfer.</para>
+
+<para>In fact what <function>parport_write</function> does is call the
+appropriate block transfer function from the <structname>struct
+parport_operations</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct parport_operations {
+        [...]
+
+        /* Block read/write */
+        size_t (*epp_write_data) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*epp_read_data) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+        size_t (*epp_write_addr) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*epp_read_addr) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+
+        size_t (*ecp_write_data) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+        size_t (*ecp_read_data) (struct parport *port, void *buf, size_t len,
+                                 int flags);
+        size_t (*ecp_write_addr) (struct parport *port, const void *buf,
+                                  size_t len, int flags);
+
+        size_t (*compat_write_data) (struct parport *port, const void *buf,
+                                     size_t len, int flags);
+        size_t (*nibble_read_data) (struct parport *port, void *buf,
+                                    size_t len, int flags);
+        size_t (*byte_read_data) (struct parport *port, void *buf,
+                                  size_t len, int flags);
+};
+]]></programlisting>
+
+<para>The transfer code in <filename>parport</filename> will tolerate
+a data transfer stall only for so long, and this timeout can be
+specified with <function>parport_set_timeout</function>, which returns
+the previous timeout:</para>
+
+<funcsynopsis><funcprototype>
+  <funcdef>long <function>parport_set_timeout</function></funcdef>
+  <paramdef>struct pardevice *<parameter>dev</parameter></paramdef>
+  <paramdef>long <parameter>inactivity</parameter></paramdef>
+</funcprototype></funcsynopsis>
+
+<para>This timeout is specific to the device, and is restored on
+<function>parport_claim</function>.</para>
+
+</chapter>
+
+<chapter id="ppdev">
+<title>User-level device drivers</title>
+
+<!-- ppdev -->
+<sect1>
+<title>Introduction to ppdev</title>
+
+<para>The printer is accessible through <filename>/dev/lp0</filename>;
+in the same way, the parallel port itself is accessible through
+<filename>/dev/parport0</filename>.  The difference is in the level of
+control that you have over the wires in the parallel port
+cable.</para>
+
+<para>With the printer driver, a user-space program (such as the
+printer spooler) can send bytes in <quote>printer protocol</quote>.
+Briefly, this means that for each byte, the eight data lines are set
+up, then a <quote>strobe</quote> line tells the printer to look at the
+data lines, and the printer sets an <quote>acknowledgement</quote>
+line to say that it got the byte.  The printer driver also allows the
+user-space program to read bytes in <quote>nibble mode</quote>, which
+is a way of transferring data from the peripheral to the computer half
+a byte at a time (and so it's quite slow).</para>
+
+<para>In contrast, the <filename>ppdev</filename> driver (accessed via
+<filename>/dev/parport0</filename>) allows you to:</para>
+
+<itemizedlist spacing=compact>
+
+<listitem>
+<para>
+examine status lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set control lines,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set/examine data lines (and control the direction of the data lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+wait for an interrupt (triggered by one of the status lines),
+</para>
+</listitem>
+
+<listitem>
+<para>
+find out how many new interrupts have occurred,
+</para>
+</listitem>
+
+<listitem>
+<para>
+set up a response to an interrupt,
+</para>
+</listitem>
+
+<listitem>
+<para>
+use IEEE 1284 negotiation (for telling peripheral which transfer mode,
+to use)
+</para>
+</listitem>
+
+<listitem>
+<para>
+transfer data using a specified IEEE 1284 mode.
+</para>
+</listitem>
+
+</itemizedlist>
+
+</sect1>
+
+<sect1>
+<title>User-level or kernel-level driver?</title>
+
+<para>The decision of whether to choose to write a kernel-level device
+driver or a user-level device driver depends on several factors.  One
+of the main ones from a practical point of view is speed: kernel-level
+device drivers get to run faster because they are not preemptable,
+unlike user-level applications.</para>
+
+<para>Another factor is ease of development.  It is in general easier
+to write a user-level driver because (a) one wrong move does not
+result in a crashed machine, (b) you have access to user libraries
+(such as the C library), and (c) debugging is easier.</para>
+
+</sect1>
+
+<sect1>
+<title>Programming interface</title>
+
+<para>The <filename>ppdev</filename> interface is largely the same as
+that of other character special devices, in that it supports
+<function>open</function>, <function>close</function>,
+<function>read</function>, <function>write</function>, and
+<function>ioctl</function>.</para>
+
+<sect2>
+<title>Starting and stopping: <function>open</function> and
+<function>close</function></title>
+
+<para>The device node <filename>/dev/parport0</filename> represents
+any device that is connected to <filename>parport0</filename>, the
+first parallel port in the system.  Each time the device node is
+opened, it represents (to the process doing the opening) a different
+device.  It can be opened more than once, but only one instance can
+actually be in control of the parallel port at any time.  A process
+that has opened <filename>/dev/parport0</filename> shares the parallel
+port in the same way as any other device driver.  A user-land driver
+may be sharing the parallel port with in-kernel device drivers as well
+as other user-land drivers.</para>
+</sect2>
+
+<sect2>
+<title>Control: <function>ioctl</function></title>
+
+<para>Most of the control is done, naturally enough, via the
+<function>ioctl</function> call.  Using <function>ioctl</function>,
+the user-land driver can control both the <filename>ppdev</filename>
+driver in the kernel and the physical parallel port itself.  The
+<function>ioctl</function> call takes as parameters a file descriptor
+(the one returned from opening the device node), a command, and
+optionally (a pointer to) some data.</para>
+
+<variablelist>
+<varlistentry><term><constant>PPCLAIM</constant></term>
+<listitem>
+
+<para>Claims access to the port.  As a user-land device driver writer,
+you will need to do this before you are able to actually change the
+state of the parallel port in any way.  Note that some operations only
+affect the <filename>ppdev</filename> driver and not the port, such as
+<constant>PPSETMODE</constant>; they can be performed while access to
+the port is not claimed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPEXCL</constant></term>
+<listitem>
+
+<para>Instructs the kernel driver to forbid any sharing of the port
+with other drivers, i.e. it requests exclusivity.  The
+<constant>PPEXCL</constant> command is only valid when the port is not
+already claimed for use, and it may mean that the next
+<constant>PPCLAIM</constant> <function>ioctl</function> will fail:
+some other driver may already have registered itself on that
+port.</para>
+
+<para>Most device drivers don't need exclusive access to the port.
+It's only provided in case it is really needed, for example for
+devices where access to the port is required for extensive periods of
+time (many seconds).</para>
+
+<para>Note that the <constant>PPEXCL</constant>
+<function>ioctl</function> doesn't actually claim the port there and
+then---action is deferred until the <constant>PPCLAIM</constant>
+<function>ioctl</function> is performed.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRELEASE</constant></term>
+<listitem>
+
+<para>Releases the port.  Releasing the port undoes the effect of
+claiming the port.  It allows other device drivers to talk to their
+devices (assuming that there are any).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPYIELD</constant></term>
+<listitem>
+
+<para>Yields the port to another driver.  This
+<function>ioctl</function> is a kind of short-hand for releasing the
+port and immediately reclaiming it.  It gives other drivers a chance
+to talk to their devices, but afterwards claims the port back.  An
+example of using this would be in a user-land printer driver: once a
+few characters have been written we could give the port to another
+device driver for a while, but if we still have characters to send to
+the printer we would want the port back as soon as possible.</para>
+
+<para>It is important not to claim the parallel port for too long, as
+other device drivers will have no time to service their devices.  If
+your device does not allow for parallel port sharing at all, it is
+better to claim the parallel port exclusively (see
+<constant>PPEXCL</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPNEGOT</constant></term>
+<listitem>
+
+<para>Performs IEEE 1284 negotiation into a particular mode.  Briefly,
+negotiation is the method by which the host and the peripheral decide
+on a protocol to use when transferring data.</para>
+
+<para>An IEEE 1284 compliant device will start out in compatibility
+mode, and then the host can negotiate to another mode (such as
+ECP).</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>; values for this are in
+<filename>parport.h</filename> and include:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>IEEE1284_MODE_COMPAT</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_NIBBLE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_BYTE</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_EPP</constant></para></listitem>
+<listitem><para><constant>IEEE1284_MODE_ECP</constant></para></listitem>
+</itemizedlist>
+
+<para>The <constant>PPNEGOT</constant> <function>ioctl</function>
+actually does two things: it performs the on-the-wire negotiation, and
+it sets the behaviour of subsequent
+<function>read</function>/<function>write</function> calls so that
+they use that mode (but see <constant>PPSETMODE</constant>).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETMODE</constant></term>
+<listitem>
+
+<para>Sets which IEEE 1284 protocol to use for the
+<function>read</function> and <function>write</function> calls.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+an <type>int</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPGETTIME</constant></term>
+<listitem>
+
+<para>Retrieves the time-out value.  The <function>read</function> and
+<function>write</function> calls will time out if the peripheral
+doesn't respond quickly enough.  The <constant>PPGETTIME</constant>
+<function>ioctl</function> retrieves the length of time that the
+peripheral is allowed to have before giving up.</para>
+
+<para>The <function>ioctl</function> parameter should be a pointer to
+a <structname>struct timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPSETTIME</constant></term>
+<listitem>
+
+<para>Sets the time-out.  The <function>ioctl</function> parameter
+should be a pointer to a <structname>struct
+timeval</structname>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCONTROL</constant></term>
+<listitem>
+
+<para>Sets the control lines.  The <function>ioctl</function>
+parameter is a pointer to an <type>unsigned char</type>, the bitwise
+OR of the control line values in
+<filename>parport.h</filename>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRCONTROL</constant></term>
+<listitem>
+
+<para>Returns the last value written to the control register, in the
+form of an <type>unsigned char</type>: each bit corresponds to a
+control line (although some are unused).  The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+<para>This doesn't actually touch the hardware; the last value written
+is remembered in software.  This is because some parallel port
+hardware does not offer read access to the control register.</para>
+
+<para>The control lines bits are defined in
+<filename>parport.h</filename>:</para>
+
+<itemizedlist spacing=compact>
+<listitem><para><constant>PARPORT_CONTROL_STROBE</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_AUTOFD</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_SELECT</constant></para></listitem>
+<listitem><para><constant>PARPORT_CONTROL_INIT</constant></para></listitem>
+</itemizedlist>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPFCONTROL</constant></term>
+<listitem>
+
+<para>Frobs the control lines.  Since a common operation is to change
+one of the control signals while leaving the others alone, it would be
+quite inefficient for the user-land driver to have to use
+<constant>PPRCONTROL</constant>, make the change, and then use
+<constant>PPWCONTROL</constant>.  Of course, each driver could
+remember what state the control lines are supposed to be in (they are
+never changed by anything else), but in order to provide
+<constant>PPRCONTROL</constant>, <filename>ppdev</filename> must
+remember the state of the control lines anyway.</para>
+
+<para>The <constant>PPFCONTROL</constant> <function>ioctl</function>
+is for <quote>frobbing</quote> control lines, and is like
+<constant>PPWCONTROL</constant> but acts on a restricted set of
+control lines.  The <function>ioctl</function> parameter is a pointer
+to a <structname>struct ppdev_frob_struct</structname>:</para>
+
+<programlisting>
+<![CDATA[
+struct ppdev_frob_struct {
+        unsigned char mask;
+        unsigned char val;
+};
+]]>
+</programlisting>
+
+<para>The <structfield>mask</structfield> and
+<structfield>val</structfield> fields are bitwise ORs of control line
+names (such as in <constant>PPWCONTROL</constant>).  The operation
+performed by <constant>PPFCONTROL</constant> is:</para>
+
+<programlisting>
+<![CDATA[new_ctr = (old_ctr & ~mask) | val;]]>
+</programlisting>
+
+<para>In other words, the signals named in
+<structfield>mask</structfield> are set to the values in
+<structfield>val</structfield>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRSTATUS</constant></term>
+<listitem>
+
+<para>Returns an <type>unsigned char</type> containing bits set for
+each status line that is set (for instance,
+<constant>PARPORT_STATUS_BUSY</constant>).  The
+<function>ioctl</function> parameter should be a pointer to an
+<type>unsigned char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPDATADIR</constant></term>
+<listitem>
+
+<para>Controls the data line drivers.  Normally the computer's
+parallel port will drive the data lines, but for byte-wide transfers
+from the peripheral to the host it is useful to turn off those drivers
+and let the peripheral drive the signals. (If the drivers on the
+computer's parallel port are left on when this happens, the port might
+be damaged.)</para>
+
+<para>This is only needed in conjunction with
+<constant>PPWDATA</constant> or <constant>PPRDATA</constant>.</para>
+
+<para>The <function>ioctl</function> parameter is a pointer to an
+<type>int</type>.  If the <type>int</type> is zero, the drivers are
+turned on (forward direction); if non-zero, the drivers are turned off
+(reverse direction).</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWDATA</constant></term>
+<listitem>
+
+<para>Sets the data lines (if in forward mode).  The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPRDATA</constant></term>
+<listitem>
+
+<para>Reads the data lines (if in reverse mode).  The
+<function>ioctl</function> parameter is a pointer to an <type>unsigned
+char</type>.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPCLRIRQ</constant></term>
+<listitem>
+
+<para>Clears the interrupt count.  The <filename>ppdev</filename>
+driver keeps a count of interrupts as they are triggered.
+<constant>PPCLRIRQ</constant> stores this count in an
+<type>int</type>, a pointer to which is passed in as the
+<function>ioctl</function> parameter.</para>
+
+<para>In addition, the interrupt count is reset to zero.</para>
+
+</listitem></varlistentry>
+
+<varlistentry><term><constant>PPWCTLONIRQ</constant></term>
+<listitem>
+
+<para>Set a trigger response.  Afterwards when an interrupt is
+triggered, the interrupt handler will set the control lines as
+requested.  The <function>ioctl</function> parameter is a pointer to
+an <type>unsigned char</type>, which is interpreted in the same way as
+for <constant>PPWCONTROL</constant>.</para>
+
+<para>The reason for this <function>ioctl</function> is simply speed.
+Without this <function>ioctl</function>, responding to an interrupt
+would start in the interrupt handler, switch context to the user-land
+driver via <function>poll</function> or <function>select</function>,
+and then switch context back to the kernel in order to handle
+<constant>PPWCONTROL</constant>.  Doing the whole lot in the interrupt
+handler is a lot faster.</para>
+
+</listitem></varlistentry>
+
+<!-- PPSETPHASE? -->
+
+</variablelist>
+
+</sect2>
+
+<sect2>
+<title>Transferring data: <function>read</function> and
+<function>write</function></title>
+
+<para>Transferring data using <function>read</function> and
+<function>write</function> is straightforward.  The data is
+transferring using the current IEEE 1284 mode (see the
+<constant>PPSETMODE</constant> <function>ioctl</function>).  For modes
+which can only transfer data in one direction, only the appropriate
+function will work, of course.</para>
+</sect2>
+
+<sect2>
+<title>Waiting for events: <function>poll</function> and
+<function>select</function></title>
+
+<para>The <filename>ppdev</filename> driver provides user-land device
+drivers with the ability to wait for interrupts, and this is done
+using <function>poll</function> (and <function>select</function>,
+which is implemented in terms of <function>poll</function>).</para>
+
+<para>When a user-land device driver wants to wait for an interrupt,
+it sleeps with <function>poll</function>.  When the interrupt arrives,
+<filename>ppdev</filename> wakes it up (with a <quote>read</quote>
+event, although strictly speaking there is nothing to actually
+<function>read</function>).</para>
+
+</sect2>
+
+</sect1>
+
+<sect1>
+<title>Examples</title>
+
+<para>Presented here are two demonstrations of how to write a simple
+printer driver for <filename>ppdev</filename>.  Firstly we will use
+the <function>write</function> function, and after that we will drive
+the control and data lines directly.</para>
+
+<para>The first thing to do is to actually open the device.</para>
+
+<programlisting><![CDATA[
+int drive_printer (const char *name)
+{
+    int fd;
+    int mode; /* We'll need this later. */
+
+    fd = open (name, O_RDWR);
+    if (fd == -1) {
+        perror ("open");
+        return 1;
+    }
+]]></programlisting>
+
+<para>Here <varname>name</varname> should be something along the lines
+of <filename>"/dev/parport0"</filename>. (If you don't have any
+<filename>/dev/parport</filename> files, you can make them with
+<command>mknod</command>; they are character special device nodes with
+major 99.)</para>
+
+<para>In order to do anything with the port we need to claim access to
+it.</para>
+
+<programlisting><![CDATA[
+    if (ioctl (fd, PPCLAIM)) {
+        perror ("PPCLAIM");
+        close (fd);
+        return 1;
+    }
+]]></programlisting>
+
+<para>Our printer driver will copy its input (from
+<varname>stdin</varname>) to the printer, and it can do that it one of
+two ways.  The first way is to hand it all off to the kernel driver,
+with the knowledge that the protocol that the printer speaks is IEEE
+1284's <quote>compatibility</quote> mode.</para>
+
+<programlisting><![CDATA[
+    /* Switch to compatibility mode.  (In fact we don't need
+     * to do this, since we start off in compatibility mode
+     * anyway, but this demonstrates PPNEGOT.)
+    mode = IEEE1284_MODE_COMPAT;
+    if (ioctl (fd, PPNEGOT, &mode)) {
+        perror ("PPNEGOT");
+        close (fd);
+        return 1;
+    }
+
+    for (;;) {
+        char buffer[1000];
+        char *ptr = buffer;
+        size_t got;
+
+        got = read (0 /* stdin */, buffer, 1000);
+        if (got < 0) {
+            perror ("read");
+            close (fd);
+            return 1;
+        }
+
+        if (got == 0)
+            /* End of input */
+            break;
+
+        while (got > 0) {
+            int written = write_printer (fd, ptr, got);
+
+            if (written < 0) {
+                perror ("write");
+                close (fd);
+                return 1;
+            }
+
+            ptr += written;
+            got -= written;
+        }
+    }
+]]></programlisting>
+
+<para>The <function>write_printer</function> function is not pictured
+above.  This is because the main loop that is shown can be used for
+both methods of driving the printer.  Here is one implementation of
+<function>write_printer</function>:</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+    return write (fd, ptr, count);
+}
+]]></programlisting>
+
+<para>We hand the data to the kernel-level driver (using
+<function>write</function>) and it handles the printer
+protocol.</para>
+
+<para>Now let's do it the hard way!  In this particular example there
+is no practical reason to do anything other than just call
+<function>write</function>, because we know that the printer talks an
+IEEE 1284 protocol.  On the other hand, this particular example does
+not even need a user-land driver since there is already a kernel-level
+one; for the purpose of this discussion, try to imagine that the
+printer speaks a protocol that is not already implemented under
+Linux.</para>
+
+<para>So, here is the alternative implementation of
+<function>write_printer</function> (for brevity, error checking has
+been omitted):</para>
+
+<programlisting><![CDATA[
+ssize_t write_printer (int fd, const void *ptr, size_t count)
+{
+    ssize_t wrote = 0;
+
+    while (wrote < count) {
+        unsigned char status, control, data;
+        unsigned char mask = (PARPORT_STATUS_ERROR
+                              | PARPORT_STATUS_BUSY);
+        unsigned char val = (PARPORT_STATUS_ERROR
+                              | PARPORT_STATUS_BUSY);
+        struct parport_frob_struct frob;
+        struct timespec ts;
+
+        /* Wait for printer to be ready */
+        for (;;) {
+            ioctl (fd, PPRSTATUS, &status);
+
+            if ((status & mask) == val)
+                break;
+
+            ioctl (fd, PPRELEASE);
+            sleep (1);
+            ioctl (fd, PPCLAIM);
+        }
+
+        /* Set the data lines */
+        data = * ((char *) ptr)++;
+        ioctl (fd, PPWDATA, &data);
+
+        /* Delay for a bit */
+        ts.tv_sec = 0;
+        ts.tv_nsec = 1000;
+        nanosleep (&ts, NULL);
+
+        /* Pulse strobe */
+        frob.mask = PARPORT_CONTROL_STROBE;
+        frob.val = PARPORT_CONTROL_STROBE;
+        ioctl (fd, PPFCONTROL, &frob);
+        nanosleep (&ts, NULL);
+
+        /* End the pulse */
+        frob.val = 0;
+        ioctl (fd, PPFCONTROL, &frob);
+        nanosleep (&ts, NULL);
+
+        wrote++;
+    }
+
+    return wrote;
+}
+]]></programlisting>
+
+<para>To show a bit more of the <filename>ppdev</filename> interface,
+here is a small piece of code that is intended to mimic the printer's
+side of printer protocol.</para>
+
+<programlisting><![CDATA[
+  for (;;)
+    {
+      int irqc;
+      int busy = nAck | nFault;
+      int acking = nFault;
+      int ready = Busy | nAck | nFault;
+      char ch;
+
+      /* Set up the control lines when an interrupt happens. */
+      ioctl (fd, PPWCTLONIRQ, &busy);
+
+      /* Now we're ready. */
+      ioctl (fd, PPWCONTROL, &ready);
+
+      /* Wait for an interrupt. */
+      {
+        fd_set rfds;
+        FD_ZERO (&rfds);
+        FD_SET (fd, &rfds);
+        if (!select (fd + 1, &rfds, NULL, NULL, NULL))
+          /* Caught a signal? */
+          continue;
+      }
+
+      /* We are now marked as busy. */
+
+      /* Fetch the data. */
+      ioctl (fd, PPRDATA, &ch);
+
+      /* Clear the interrupt. */
+      ioctl (fd, PPCLRIRQ, &irqc);
+      if (irqc > 1)
+        fprintf (stderr, "Arghh! Missed %d interrupt%s!\n",
+         irqc - 1, irqc == 2 ? "s" : "");
+
+      /* Ack it. */
+      ioctl (fd, PPWCONTROL, &acking);
+      usleep (2);
+      ioctl (fd, PPWCONTROL, &busy);
+
+      putchar (ch);
+    }
+]]></programlisting>
+
+</sect1>
+
+</chapter>
+</book>
\ No newline at end of file
index e3678c04b22c852a6c9ee4a4e45131cb4ccb09ef..c152abf313470cfdf16f783f2983dd635239a03a 100644 (file)
   <chapter id="intro">
       <title>Introduction</title>
   <para>
-       Parts of this document first appeared in Linux Magazine under a
-       ninety day exclusivity.
+        Parts of this document first appeared in Linux Magazine under a
+        ninety day exclusivity.
   </para>
   <para>
-       Video4Linux is intended to provide a common programming interface
-       for the many TV and capture cards now on the market, as well as
-       parallel port and USB video cameras. Radio, teletext decoders and
-       vertical blanking data interfaces are also provided.
+        Video4Linux is intended to provide a common programming interface
+        for the many TV and capture cards now on the market, as well as
+        parallel port and USB video cameras. Radio, teletext decoders and
+        vertical blanking data interfaces are also provided.
   </para>
   </chapter>
   <chapter>
-       <title>Radio Devices</title>
+        <title>Radio Devices</title>
   <para>
-       There are a wide variety of radio interfaces available for PC's, and these
-       are generally very simple to program. The biggest problem with supporting
-       such devices is normally extracting documentation from the vendor.
+        There are a wide variety of radio interfaces available for PC's, and these
+        are generally very simple to program. The biggest problem with supporting
+        such devices is normally extracting documentation from the vendor.
   </para>
   <para>
-       The radio interface supports a simple set of control ioctls standardised
-       across all radio and tv interfaces. It does not support read or write, which
-       are used for video streams. The reason radio cards do not allow you to read
-       the audio stream into an application is that without exception they provide
-       a connection on to a soundcard. Soundcards can be used to read the radio
-       data just fine. 
+        The radio interface supports a simple set of control ioctls standardised
+        across all radio and tv interfaces. It does not support read or write, which
+        are used for video streams. The reason radio cards do not allow you to read
+        the audio stream into an application is that without exception they provide
+        a connection on to a soundcard. Soundcards can be used to read the radio
+        data just fine. 
   </para>
   <sect1 id="registerradio">
   <title>Registering Radio Devices</title>
   <para>
-       The Video4linux core provides an interface for registering devices. The
-       first step in writing our radio card driver is to register it.
+        The Video4linux core provides an interface for registering devices. The
+        first step in writing our radio card driver is to register it.
   </para>
   <programlisting>
 
 
 static struct video_device my_radio
 {
-       "My radio",
-       VID_TYPE_TUNER,
-       VID_HARDWARE_MYRADIO,
-       radio_open.
-       radio_close,
-       NULL,           /* no read */
-       NULL,           /* no write */
-       NULL,           /* no poll */
-       radio_ioctl,
-       NULL,           /* no special init function */
-       NULL            /* no private data */
+        "My radio",
+        VID_TYPE_TUNER,
+        VID_HARDWARE_MYRADIO,
+        radio_open.
+        radio_close,
+        NULL,                /* no read */
+        NULL,                 /* no write */
+        NULL,                /* no poll */
+        radio_ioctl,
+        NULL,                /* no special init function */
+        NULL                /* no private data */
 };
 
 
   </programlisting>
   <para>
-       This declares our video4linux device driver interface. The VID_TYPE_ value
-       defines what kind of an interface we are, and defines basic capabilities.
+        This declares our video4linux device driver interface. The VID_TYPE_ value
+        defines what kind of an interface we are, and defines basic capabilities.
   </para>
   <para>
-       The only defined value relevant for a radio card is VID_TYPE_TUNER which
-       indicates that the device can be tuned. Clearly our radio is going to have some
-       way to change channel so it is tuneable.
+        The only defined value relevant for a radio card is VID_TYPE_TUNER which
+        indicates that the device can be tuned. Clearly our radio is going to have some
+        way to change channel so it is tuneable.
   </para>
   <para>
-       The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
-       alan@redhat.com when device drivers are going to be released. Until then you
-       can pull a suitably large number out of your hat and use it. 10000 should be
-       safe for a very long time even allowing for the huge number of vendors
-       making new and different radio cards at the moment.
+        The VID_HARDWARE_ types are unique to each device. Numbers are assigned by
+        <email>alan@redhat.com</email> when device drivers are going to be released. Until then you
+        can pull a suitably large number out of your hat and use it. 10000 should be
+        safe for a very long time even allowing for the huge number of vendors
+        making new and different radio cards at the moment.
   </para>
   <para>
-       We declare an open and close routine, but we do not need read or write,
-       which are used to read and write video data to or from the card itself. As
-       we have no read or write there is no poll function.
+        We declare an open and close routine, but we do not need read or write,
+        which are used to read and write video data to or from the card itself. As
+        we have no read or write there is no poll function.
   </para>
   <para>
-       The private initialise function is run when the device is registered. In
-       this driver we've already done all the work needed. The final pointer is a
-       private data pointer that can be used by the device driver to attach and
-       retrieve private data structures. We set this field "priv" to NULL for
-       the moment.
+        The private initialise function is run when the device is registered. In
+        this driver we've already done all the work needed. The final pointer is a
+        private data pointer that can be used by the device driver to attach and
+        retrieve private data structures. We set this field "priv" to NULL for
+        the moment.
   </para>
   <para>
-       Having the structure defined is all very well but we now need to register it
-       with the kernel. 
+        Having the structure defined is all very well but we now need to register it
+        with the kernel. 
   </para>
   <programlisting>
 
@@ -146,75 +146,80 @@ static int io = 0x320;
 
 int __init myradio_init(struct video_init *v)
 {
-       if(check_region(io, MY_IO_SIZE))
-       {
-               printk(KERN_ERR "myradio: port 0x%03X is in use.\n", io);
-               return -EBUSY;
-       }
-
-       if(video_device_register(&amp;my_radio, VFL_TYPE_RADIO)==-1)
-               return -EINVAL;
-       request_region(io, MY_IO_SIZE, "myradio");
-       return 0;
+        if(check_region(io, MY_IO_SIZE))
+        {
+                printk(KERN_ERR 
+                    "myradio: port 0x%03X is in use.\n", io);
+                return -EBUSY;
+        }
+
+        if(video_device_register(&amp;my_radio, VFL_TYPE_RADIO)==-1)
+                return -EINVAL;
+        request_region(io, MY_IO_SIZE, "myradio");
+        return 0;
 }
 
   </programlisting>
   <para>
-       The first stage of the initialisation, as is normally the case, is to check 
-       that the I/O space we are about to fiddle with doesn't belong to some other 
-       driver. If it is we leave well alone. If the user gives the address of the 
-       wrong device then we will spot this. These policies will generally avoid 
-       crashing the machine.
-  </para>
-  <para>
-       Now we ask the Video4Linux layer to register the device for us. We hand it
-       our carefully designed video_device structure and also tell it which group
-       of devices we want it registered with. In this case VFL_TYPE_RADIO.
-  </para>
-  <para>
-       The types available are
-
-       VFL_TYPE_RADIO          /dev/radio{n}
-
-       Radio devices are assigned in this block. As with all of these
-       selections the actual number assignment is done by the video layer
-       accordijng to what is free.
-
-       VFL_TYPE_GRABBER        /dev/video{n}
-
-       Video capture devices and also -- counter-intuitively for the name --
-       hardware video playback devices such as MPEG2 cards.
-
-       VFL_TYPE_VBI            /dev/vbi{n}
-
-       The VBI devices capture the hidden lines on a television picture
-       that carry further information like closed caption data, teletext
-       (primarily in Europe) and now Intercast and the ATVEC internet
-       television encodings.
-
-       VFL_TYPE_VTX            /dev/vtx[n}
-
-       VTX is 'Videotext' also known as 'Teletext'. This is a system for
-       sending numbered, 40x25, mostly textual page images over the hidden
-       lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder 
-       chips. (The use of the word smart here has to be taken in context,
-       the smartest teletext chips are fairly dumb pieces of technology).
-
-  </para>
-  <para>
-       We are most definitely a radio.
-  </para>
-  <para>
-       Finally we allocate our I/O space so that nobody treads on us and return 0
-       to signify general happiness with the state of the universe.
+        The first stage of the initialisation, as is normally the case, is to check 
+        that the I/O space we are about to fiddle with doesn't belong to some other 
+        driver. If it is we leave well alone. If the user gives the address of the 
+        wrong device then we will spot this. These policies will generally avoid 
+        crashing the machine.
+  </para>
+  <para>
+        Now we ask the Video4Linux layer to register the device for us. We hand it
+        our carefully designed video_device structure and also tell it which group
+        of devices we want it registered with. In this case VFL_TYPE_RADIO.
+  </para>
+  <para>
+        The types available are
+  </para>
+   <table frame=all><title>Device Types</title>
+   <tgroup cols=3 align=left>
+   <tbody>
+   <row>
+        <entry>VFL_TYPE_RADIO</><>/dev/radio{n}</><>
+
+        Radio devices are assigned in this block. As with all of these
+        selections the actual number assignment is done by the video layer
+        accordijng to what is free.</entry>
+       </row><row>
+        <entry>VFL_TYPE_GRABBER</><>/dev/video{n}</><>
+        Video capture devices and also -- counter-intuitively for the name --
+        hardware video playback devices such as MPEG2 cards.</entry>
+       </row><row>
+        <entry>VFL_TYPE_VBI</><>/dev/vbi{n}</><>
+        The VBI devices capture the hidden lines on a television picture
+        that carry further information like closed caption data, teletext
+        (primarily in Europe) and now Intercast and the ATVEC internet
+        television encodings.</entry>
+       </row><row>
+        <entry>VFL_TYPE_VTX</><>/dev/vtx[n}</><>
+        VTX is 'Videotext' also known as 'Teletext'. This is a system for
+        sending numbered, 40x25, mostly textual page images over the hidden
+        lines. Unlike the /dev/vbi interfaces, this is for 'smart' decoder 
+        chips. (The use of the word smart here has to be taken in context,
+        the smartest teletext chips are fairly dumb pieces of technology).
+       </entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        We are most definitely a radio.
+  </para>
+  <para>
+        Finally we allocate our I/O space so that nobody treads on us and return 0
+        to signify general happiness with the state of the universe.
   </para>
   </sect1>
   <sect1 id="openradio">
   <title>Opening And Closing The Radio</title>
 
   <para>
-       The functions we declared in our video_device are mostly very simple.
-       Firstly we can drop in what is basically standard code for open and close. 
+        The functions we declared in our video_device are mostly very simple.
+        Firstly we can drop in what is basically standard code for open and close. 
   </para>
   <programlisting>
 
@@ -223,236 +228,272 @@ static int users = 0;
 
 static int radio_open(stuct video_device *dev, int flags)
 {
-       if(users)
-               return -EBUSY;
-       users++;
-       MOD_INC_USE_COUNT;
-       return 0;
+        if(users)
+                return -EBUSY;
+        users++;
+        MOD_INC_USE_COUNT;
+        return 0;
 }
 
   </programlisting>
   <para>
-       At open time we need to do nothing but check if someone else is also using
-       the radio card. If nobody is using it we make a note that we are using it,
-       then we ensure that nobody unloads our driver on us.
+        At open time we need to do nothing but check if someone else is also using
+        the radio card. If nobody is using it we make a note that we are using it,
+        then we ensure that nobody unloads our driver on us.
   </para>
   <programlisting>
 
 
 static int radio_close(struct video_device *dev)
 {
-       users--;
-       MOD_DEC_USE_COUNT;
+        users--;
+        MOD_DEC_USE_COUNT;
 }
 
   </programlisting>
   <para>
-       At close time we simply need to reduce the user count and allow the module
-       to become unloadable.
+        At close time we simply need to reduce the user count and allow the module
+        to become unloadable.
   </para>
   <para>
-       If you are sharp you will have noticed neither the open nor the close
-       routines attempt to reset or change the radio settings. This is intentional.
-       It allows an application to set up the radio and exit. It avoids a user
-       having to leave an application running all the time just to listen to the
-       radio. 
+        If you are sharp you will have noticed neither the open nor the close
+        routines attempt to reset or change the radio settings. This is intentional.
+        It allows an application to set up the radio and exit. It avoids a user
+        having to leave an application running all the time just to listen to the
+        radio. 
   </para>
   </sect1>
   <sect1 id="ioctlradio">
   <title>The Ioctl Interface</title>
   <para>
-       This leaves the ioctl routine, without which the driver will not be
-       terribly useful to anyone.
+        This leaves the ioctl routine, without which the driver will not be
+        terribly useful to anyone.
   </para>
   <programlisting>
 
 
 static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
-       switch(cmd)
-       {
-               case VIDIOCGCAP:
-               {
-                       struct video_capability v;
-                       v.type = VID_TYPE_TUNER;
-                       v.channels = 1;
-                       v.audios = 1;
-                       v.maxwidth = 0;
-                       v.minwidth = 0;
-                       v.maxheight = 0;
-                       v.minheight = 0;
-                       strcpy(v.name, "My Radio");
-                       if(copy_to_user(arg, &amp;v, sizeof(v)))
-                               return -EFAULT;
-                       return 0;
-               }
+        switch(cmd)
+        {
+                case VIDIOCGCAP:
+                {
+                        struct video_capability v;
+                        v.type = VID_TYPE_TUNER;
+                        v.channels = 1;
+                        v.audios = 1;
+                        v.maxwidth = 0;
+                        v.minwidth = 0;
+                        v.maxheight = 0;
+                        v.minheight = 0;
+                        strcpy(v.name, "My Radio");
+                        if(copy_to_user(arg, &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       VIDIOCGCAP is the first ioctl all video4linux devices must support. It
-       allows the applications to find out what sort of a card they have found and
-       to figure out what they want to do about it. The fields in the structure are
-  </para>
-  <para>
-       name            The device text name. This is intended for the user.
-
-       channels        The number of different channels you can tune on
-                       this card. It could even by zero for a card that has
-                       no tuning capability. For our simple FM radio it is 1. 
-                       An AM/FM radio would report 2.
-
-       audios          The number of audio inputs on this device. For our
-                       radio there is only one audio input.
-
-       minwidth,       The smallest size the card is capable of capturing
-       minheight       images in. We set these to zero. Radios do not
-                       capture pictures
-
-       maxwidth,       The largest image size the card is capable of
-       maxheight       capturing. For our radio we report 0.
-
-
-       type            This reports the capabilities of the device, and
-                       matches the field we filled in in the struct
-                       video_device when registering.
-
-  </para>
-  <para>
-       Having filled in the fields, we use copy_to_user to copy the structure into
-       the users buffer. If the copy fails we return an EFAULT to the application
-       so that it knows it tried to feed us garbage.
-  </para>
-  <para>
-       The next pair of ioctl operations select which tuner is to be used and let
-       the application find the tuner properties. We have only a single FM band
-       tuner in our example device.
+        VIDIOCGCAP is the first ioctl all video4linux devices must support. It
+        allows the applications to find out what sort of a card they have found and
+        to figure out what they want to do about it. The fields in the structure are
+  </para>
+   <table frame=all><title>struct video_capability fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>name</><>The device text name. This is intended for the user.</>
+       </row><row>
+        <entry>channels</><>The number of different channels you can tune on
+                        this card. It could even by zero for a card that has
+                        no tuning capability. For our simple FM radio it is 1. 
+                        An AM/FM radio would report 2.</entry>
+       </row><row>
+        <entry>audios</><>The number of audio inputs on this device. For our
+                        radio there is only one audio input.</entry>
+       </row><row>
+        <entry>minwidth,minheight</><>The smallest size the card is capable of capturing
+                       images in. We set these to zero. Radios do not
+                        capture pictures</entry>
+       </row><row>
+        <entry>maxwidth,maxheight</><>The largest image size the card is capable of
+                                      capturing. For our radio we report 0.
+                               </entry>
+       </row><row>
+        <entry>type</><>This reports the capabilities of the device, and
+                        matches the field we filled in in the struct
+                        video_device when registering.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        Having filled in the fields, we use copy_to_user to copy the structure into
+        the users buffer. If the copy fails we return an EFAULT to the application
+        so that it knows it tried to feed us garbage.
+  </para>
+  <para>
+        The next pair of ioctl operations select which tuner is to be used and let
+        the application find the tuner properties. We have only a single FM band
+        tuner in our example device.
   </para>
   <programlisting>
 
 
-               case VIDIOCGTUNER:
-               {
-                       struct video_tuner v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v))!=0)
-                               return -EFAULT;
-                       if(v.tuner)
-                               return -EINVAL;
-                       v.rangelow=(87*16000);
-                       v.rangehigh=(108*16000);
-                       v.flags = VIDEO_TUNER_LOW;
-                       v.mode = VIDEO_MODE_AUTO;
-                       v.signal = 0xFFFF;
-                       strcpy(v.name, "FM");
-                       if(copy_to_user(&amp;v, arg, sizeof(v))!=0)
-                               return -EFAULT;
-                       return 0;
-               }
+                case VIDIOCGTUNER:
+                {
+                        struct video_tuner v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v))!=0)
+                                return -EFAULT;
+                        if(v.tuner)
+                                return -EINVAL;
+                        v.rangelow=(87*16000);
+                        v.rangehigh=(108*16000);
+                        v.flags = VIDEO_TUNER_LOW;
+                        v.mode = VIDEO_MODE_AUTO;
+                        v.signal = 0xFFFF;
+                        strcpy(v.name, "FM");
+                        if(copy_to_user(&amp;v, arg, sizeof(v))!=0)
+                                return -EFAULT;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
-       sets the tuner field to the tuner number it wishes to query. The query does
-       not change the tuner that is being used, it merely enquires about the tuner
-       in question.
-  </para>
-  <para>
-       We have exactly one tuner so after copying the user buffer to our temporary
-       structure we complain if they asked for a tuner other than tuner 0. 
-  </para>
-  <para>
-       The video_tuner structure has the following fields
-  </para>
-  <para>
-       int tuner       The number of the tuner in question
-       char name[32]   A text description of this tuner. "FM" will do fine.
-                       This is intended for the application.
-       u32 flags       Information on the tuners capabilities
-
-               VIDEO_TUNER_PAL
-                       A PAL TV tuner
-               VIDEO_TUNER_NTSC
-                       An NTSC (US) TV tuner
-               VIDEO_TUNER_SECAM
-                       A SECAM (Frenc) TV tuner
-               VIDEO_TUNER_LOW
-                       The tuner frequency is scaled in 1/16th of a KHz
-                       steps. If not it is in 1/16th of a MHz steps
-               VIDEO_TUNER_NORM
-                       The tuner can set its format
-               VIDEO_TUNER_STEREO_ON
-                       The tuner is currently receiving a stereo signal
-
-       u16 mode        The current reception mode
-               
-               VIDEO_MODE_PAL          The TV formats
-               VIDEO_MODE_NTSC 
-               VIDEO_MODE_SECAM
-               VIDEO_MODE_AUTO         A device that does not need to do
-                                       TV format switching
-
-       u16 signal      The signal strength scaled between 0 and 65535. If
-                       a device cannot tell the signal strength it should
-                       report 65535. Many simple cards contain only a 
-                       signal/no signal bit. Such cards will report either
-                       0 or 65535.
-
-       u32 rangelow, rangehigh
-
-                       The range of frequencies supported by the radio
-                       or TV. It is scaled according to the VIDEO_TUNER_LOW
-                       flag.
-
-  </para>
-  <para>
-       The settings for the radio card are thus fairly simple. We report that we
-       are a tuner called "FM" for FM radio. In order to get the best tuning
-       resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
-       unlikely our card can do that resolution but it is a fair bet the card can
-       do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
-       radio usage.
-  </para>
-  <para>
-       We report that the tuner automatically handles deciding what format it is
-       receiving - true enough as it only handles FM radio. Our example card is
-       also incapable of detecting stereo or signal strengths so it reports a
-       strength of 0xFFFF (maximum) and no stereo detected.
-  </para>
-  <para>
-       To finish off we set the range that can be tuned to be 87-108Mhz, the normal
-       FM broadcast radio range. It is important to find out what the card is
-       actually capable of tuning. It is easy enough to simply use the FM broadcast
-       range. Unfortunately if you do this you will discover the FM broadcast
-       ranges in the USA, Europe and Japan are all subtly different and some users
-       cannot receive all the stations they wish.
-  </para>
-  <para>
-       The application also needs to be able to set the tuner it wishes to use. In
-       our case, with a single tuner this is rather simple to arrange.
+        The VIDIOCGTUNER ioctl allows applications to query a tuner. The application
+        sets the tuner field to the tuner number it wishes to query. The query does
+        not change the tuner that is being used, it merely enquires about the tuner
+        in question.
+  </para>
+  <para>
+        We have exactly one tuner so after copying the user buffer to our temporary
+        structure we complain if they asked for a tuner other than tuner 0. 
+  </para>
+  <para>
+        The video_tuner structure has the following fields
+  </para>
+   <table frame=all><title>struct video_tuner fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>int tuner</><entry>The number of the tuner in question</entry>
+   </row><row>
+        <entry>char name[32]</><entry>A text description of this tuner. "FM" will do fine.
+                        This is intended for the application.</entry>
+   </row><row>
+        <entry>u32 flags</>
+        <entry>Tuner capability flags</entry>
+   </row>
+   <row>
+        <entry>u16 mode</><entry>The current reception mode</entry>
+
+   </row><row>
+        <entry>u16 signal</><entry>The signal strength scaled between 0 and 65535. If
+                        a device cannot tell the signal strength it should
+                        report 65535. Many simple cards contain only a 
+                        signal/no signal bit. Such cards will report either
+                        0 or 65535.</entry>
+
+   </row><row>
+        <entry>u32 rangelow, rangehigh</><entry>
+                        The range of frequencies supported by the radio
+                        or TV. It is scaled according to the VIDEO_TUNER_LOW
+                        flag.</entry>
+
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+
+   <table frame=all><title>struct video_tuner flags</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+       <entry>VIDEO_TUNER_PAL</><entry>A PAL TV tuner</entry>
+       </row><row>
+        <entry>VIDEO_TUNER_NTSC</><entry>An NTSC (US) TV tuner</entry>
+       </row><row>
+        <entry>VIDEO_TUNER_SECAM</><entry>A SECAM (French) TV tuner</entry>
+       </row><row>
+        <entry>VIDEO_TUNER_LOW</><>
+             The tuner frequency is scaled in 1/16th of a KHz
+             steps. If not it is in 1/16th of a MHz steps
+       </entry>
+       </row><row>
+        <entry>VIDEO_TUNER_NORM</><entry>The tuner can set its format</entry>
+       </row><row>
+        <entry>VIDEO_TUNER_STEREO_ON</><entry>The tuner is currently receiving a stereo signal</entry>
+        </row>
+    </tbody>
+    </tgroup>
+    </table>
+
+   <table frame=all><title>struct video_tuner modes</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_MODE_PAL</><>PAL Format</entry>
+   </row><row>
+                <entry>VIDEO_MODE_NTSC</><>NTSC Format (USA)</entry>
+   </row><row>
+                <entry>VIDEO_MODE_SECAM</><>French Format</entry>
+   </row><row>
+                <entry>VIDEO_MODE_AUTO</><>A device that does not need to do
+                                        TV format switching</entry>
+   </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        The settings for the radio card are thus fairly simple. We report that we
+        are a tuner called "FM" for FM radio. In order to get the best tuning
+        resolution we report VIDEO_TUNER_LOW and select tuning to 1/16th of KHz. Its
+        unlikely our card can do that resolution but it is a fair bet the card can
+        do better than 1/16th of a MHz. VIDEO_TUNER_LOW is appropriate to almost all
+        radio usage.
+  </para>
+  <para>
+        We report that the tuner automatically handles deciding what format it is
+        receiving - true enough as it only handles FM radio. Our example card is
+        also incapable of detecting stereo or signal strengths so it reports a
+        strength of 0xFFFF (maximum) and no stereo detected.
+  </para>
+  <para>
+        To finish off we set the range that can be tuned to be 87-108Mhz, the normal
+        FM broadcast radio range. It is important to find out what the card is
+        actually capable of tuning. It is easy enough to simply use the FM broadcast
+        range. Unfortunately if you do this you will discover the FM broadcast
+        ranges in the USA, Europe and Japan are all subtly different and some users
+        cannot receive all the stations they wish.
+  </para>
+  <para>
+        The application also needs to be able to set the tuner it wishes to use. In
+        our case, with a single tuner this is rather simple to arrange.
   </para>
   <programlisting>
 
-               case VIDIOCSTUNER:
-               {
-                       struct video_tuner v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.tuner != 0)
-                               return -EINVAL;
-                       return 0;
-               }
+                case VIDIOCSTUNER:
+                {
+                        struct video_tuner v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.tuner != 0)
+                                return -EINVAL;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       We copy the user supplied structure into kernel memory so we can examine it. 
-       If the user has selected a tuner other than zero we reject the request. If 
-       they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
+        We copy the user supplied structure into kernel memory so we can examine it. 
+        If the user has selected a tuner other than zero we reject the request. If 
+        they wanted tuner 0 then, suprisingly enough, that is the current tuner already.
   </para>
   <para>
-       The next two ioctls we need to provide are to get and set the frequency of
-       the radio. These both use an unsigned long argument which is the frequency.
-       The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
-       mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
-       1/16ths of a KHz.
+        The next two ioctls we need to provide are to get and set the frequency of
+        the radio. These both use an unsigned long argument which is the frequency.
+        The scale of the frequency depends on the VIDEO_TUNER_LOW flag as I
+        mentioned earlier on. Since we have VIDEO_TUNER_LOW set this will be in
+        1/16ths of a KHz.
   </para>
   <programlisting>
 
@@ -460,194 +501,226 @@ static unsigned long current_freq;
 
 
 
-               case VIDIOCGFREQ:
-                       if(copy_to_user(arg, &amp;current_freq, 
-                               sizeof(unsigned long))
-                               return -EFAULT;
-                       return 0;
+                case VIDIOCGFREQ:
+                        if(copy_to_user(arg, &amp;current_freq, 
+                                sizeof(unsigned long))
+                                return -EFAULT;
+                        return 0;
 
   </programlisting>
   <para>
-       Querying the frequency in our case is relatively simple. Our radio card is
-       too dumb to let us query the signal strength so we remember our setting if 
-       we know it. All we have to do is copy it to the user.
+        Querying the frequency in our case is relatively simple. Our radio card is
+        too dumb to let us query the signal strength so we remember our setting if 
+        we know it. All we have to do is copy it to the user.
   </para>
   <programlisting>
 
 
-               case VIDIOCSFREQ:
-               {
-                       u32 freq;
-                       if(copy_from_user(arg, &amp;freq, 
-                               sizeof(unsigned long))!=0)
-                               return -EFAULT;
-                       if(hardware_set_freq(freq)<0)
-                               return -EINVAL;
-                       current_freq = freq;
-                       return 0;
-               }
+                case VIDIOCSFREQ:
+                {
+                        u32 freq;
+                        if(copy_from_user(arg, &amp;freq, 
+                                sizeof(unsigned long))!=0)
+                                return -EFAULT;
+                        if(hardware_set_freq(freq)<0)
+                                return -EINVAL;
+                        current_freq = freq;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       Setting the frequency is a little more complex. We begin by copying the
-       desired frequency into kernel space. Next we call a hardware specific routine
-       to set the radio up. This might be as simple as some scaling and a few
-       writes to an I/O port. For most radio cards it turns out a good deal more
-       complicated and may involve programming things like a phase locked loop on
-       the card. This is what documentation is for. 
+        Setting the frequency is a little more complex. We begin by copying the
+        desired frequency into kernel space. Next we call a hardware specific routine
+        to set the radio up. This might be as simple as some scaling and a few
+        writes to an I/O port. For most radio cards it turns out a good deal more
+        complicated and may involve programming things like a phase locked loop on
+        the card. This is what documentation is for. 
   </para>
   <para>
-       The final set of operations we need to provide for our radio are the 
-       volume controls. Not all radio cards can even do volume control. After all
-       there is a perfectly good volume control on the sound card. We will assume
-       our radio card has a simple 4 step volume control.
+        The final set of operations we need to provide for our radio are the 
+        volume controls. Not all radio cards can even do volume control. After all
+        there is a perfectly good volume control on the sound card. We will assume
+        our radio card has a simple 4 step volume control.
   </para>
   <para>
-       There are two ioctls with audio we need to support
+        There are two ioctls with audio we need to support
   </para>
   <programlisting>
 
 static int current_volume=0;
 
-               case VIDIOCGAUDIO:
-               {
-                       struct video_audio v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.audio != 0)
-                               return -EINVAL;
-                       v.volume = 16384*current_volume;
-                       v.step = 16384;
-                       strcpy(v.name, "Radio");
-                       v.mode = VIDEO_SOUND_MONO;
-                       v.balance = 0;
-                       v.base = 0;
-                       v.treble = 0;
-                       
-                       if(copy_to_user(arg. &amp;v, sizeof(v)))
-                               return -EFAULT;
-                       return 0;
-               }
+                case VIDIOCGAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio != 0)
+                                return -EINVAL;
+                        v.volume = 16384*current_volume;
+                        v.step = 16384;
+                        strcpy(v.name, "Radio");
+                        v.mode = VIDEO_SOUND_MONO;
+                        v.balance = 0;
+                        v.base = 0;
+                        v.treble = 0;
+                        
+                        if(copy_to_user(arg. &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       Much like the tuner we start by copying the user structure into kernel
-       space. Again we check if the user has asked for a valid audio input. We have
-       only input 0 and we punt if they ask for another input.
-  </para>
-  <para>
-       Then we fill in the video_audio structure. This has the following format
-  </para>
-  <para>
-
-       audio           The input the user wishes to query
-       volume          The volume setting on a scale of 0-65535
-       base            The base level on a scale of 0-65535
-       treble          The treble level on a scale of 0-65535
-
-       flags           The features this audio device supports
-
-               VIDEO_AUDIO_MUTE        The audio is currently muted. We
-                                       could fake this in our driver but we
-                                       choose not to bother.
-               VIDEO_AUDIO_MUTABLE     The input has a mute option
-               VIDEO_AUDIO_TREBLE      The  input has a treble control
-               VIDEO_AUDIO_BASS        The input has a base control
-
-       name            A text name to display to the user. We picked 
-                       "Radio" as it explains things quite nicely.
-
-       mode            The current reception mode for the audio
-
-               VIDEO_SOUND_MONO        Mono sound
-               VIDEO_SOUND_STEREO      Stereo sound
-               VIDEO_SOUND_LANG1       Alternative language 1 (TV specific)
-               VIDEO_SOUND_LANG2       Alternative language 2 (TV specific)
-
-               We report MONO because our card is too stupid to know if it is in
-               mono or stereo. 
-
-       balance         The stereo balance on a scale of 0-65535, 32768 is
-                       middle.
-
-       step            The step by which the volume control jumps. This is
-                       used to help make it easy for applications to set 
-                       slider behaviour.
-
-  </para>
-  <para>
-       Having filled in the structure we copy it back to user space.
-  </para>
-  <para>
-       The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
-       video_audio stucture. The driver does its best to honour the request.
+        Much like the tuner we start by copying the user structure into kernel
+        space. Again we check if the user has asked for a valid audio input. We have
+        only input 0 and we punt if they ask for another input.
+  </para>
+  <para>
+        Then we fill in the video_audio structure. This has the following format
+  </para>
+   <table frame=all><title>struct video_audio fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+   <entry>audio</><>The input the user wishes to query</>
+   </row><row>
+   <entry>volume</><>The volume setting on a scale of 0-65535</>
+   </row><row>
+   <entry>base</><>The base level on a scale of 0-65535</>
+   </row><row>
+   <entry>treble</><>The treble level on a scale of 0-65535</>
+   </row><row>
+   <entry>flags</><>The features this audio device supports
+   </entry>
+   </row><row>
+   <entry>name</><>A text name to display to the user. We picked 
+                        "Radio" as it explains things quite nicely.</>
+   </row><row>
+   <entry>mode</><>The current reception mode for the audio
+
+                We report MONO because our card is too stupid to know if it is in
+                mono or stereo. 
+   </entry>
+   </row><row>
+   <entry>balance</><>The stereo balance on a scale of 0-65535, 32768 is
+                        middle.</>
+   </row><row>
+   <entry>step</><>The step by which the volume control jumps. This is
+                        used to help make it easy for applications to set 
+                        slider behaviour.</>   
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+
+   <table frame=all><title>struct video_audio flags</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_AUDIO_MUTE</><>The audio is currently muted. We
+                                        could fake this in our driver but we
+                                        choose not to bother.</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_MUTABLE</><>The input has a mute option</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_TREBLE</><>The  input has a treble control</entry>
+   </row><row>
+                <entry>VIDEO_AUDIO_BASS</><>The input has a base control</entry>
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+
+   <table frame=all><title>struct video_audio modes</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+                <entry>VIDEO_SOUND_MONO</><>Mono sound</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_STEREO</><>Stereo sound</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_LANG1</><>Alternative language 1 (TV specific)</entry>
+   </row><row>
+                <entry>VIDEO_SOUND_LANG2</><>Alternative language 2 (TV specific)</entry>
+   </row>
+   </tbody>
+   </tgroup>
+   </table>
+  <para>
+        Having filled in the structure we copy it back to user space.
+  </para>
+  <para>
+        The VIDIOCSAUDIO ioctl allows the user to set the audio parameters in the
+        video_audio stucture. The driver does its best to honour the request.
   </para>
   <programlisting>
 
-               case VIDIOCSAUDIO:
-               {
-                       struct video_audio v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.audio)
-                               return -EINVAL;
-                       current_volume = v/16384;
-                       hardware_set_volume(current_volume);
-                       return 0;
-               }
+                case VIDIOCSAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio)
+                                return -EINVAL;
+                        current_volume = v/16384;
+                        hardware_set_volume(current_volume);
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       In our case there is very little that the user can set. The volume is
-       basically the limit. Note that we could pretend to have a mute feature
-       by rewriting this to 
+        In our case there is very little that the user can set. The volume is
+        basically the limit. Note that we could pretend to have a mute feature
+        by rewriting this to 
   </para>
   <programlisting>
 
-               case VIDIOCSAUDIO:
-               {
-                       struct video_audio v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.audio)
-                               return -EINVAL;
-                       current_volume = v/16384;
-                       if(v.flags&amp;VIDEO_AUDIO_MUTE)
-                               hardware_set_volume(0);
-                       else
-                               hardware_set_volume(current_volume);
-                       current_muted = v.flags&amp;VIDEO_AUDIO_MUTE;
-                       return 0;
-               }
+                case VIDIOCSAUDIO:
+                {
+                        struct video_audio v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.audio)
+                                return -EINVAL;
+                        current_volume = v/16384;
+                        if(v.flags&amp;VIDEO_AUDIO_MUTE)
+                                hardware_set_volume(0);
+                        else
+                                hardware_set_volume(current_volume);
+                        current_muted = v.flags &amp; 
+                                              VIDEO_AUDIO_MUTE;
+                        return 0;
+                }
 
   </programlisting>
   <para>
-       This with the corresponding changes to the VIDIOCGAUDIO code to report the
-       state of the mute flag we save and to report the card has a mute function,
-       will allow applications to use a mute facility with this card. It is
-       questionable whether this is a good idea however. User applications can already
-       fake this themselves and kernel space is precious.
+        This with the corresponding changes to the VIDIOCGAUDIO code to report the
+        state of the mute flag we save and to report the card has a mute function,
+        will allow applications to use a mute facility with this card. It is
+        questionable whether this is a good idea however. User applications can already
+        fake this themselves and kernel space is precious.
   </para>
   <para>
-       We now have a working radio ioctl handler. So we just wrap up the function
+        We now have a working radio ioctl handler. So we just wrap up the function
   </para>
   <programlisting>
 
 
-       }
-       return -ENOIOCTLCMD;
+        }
+        return -ENOIOCTLCMD;
 }
 
   </programlisting>
   <para>
-       and pass the Video4Linux layer back an error so that it knows we did not
-       understand the request we got passed.
+        and pass the Video4Linux layer back an error so that it knows we did not
+        understand the request we got passed.
   </para>
   </sect1>
   <sect1 id="modradio">
   <title>Module Wrapper</title>
   <para>
-       Finally we add in the usual module wrapping and the driver is done.
+        Finally we add in the usual module wrapping and the driver is done.
   </para>
   <programlisting>
 
@@ -669,157 +742,175 @@ EXPORT_NO_SYMBOLS;
 
 int init_module(void)
 {
-       if(io==-1)
-       {
-               printk(KERN_ERR "You must set an I/O address with io=0x???\n");
-               return -EINVAL;
-       }
-       return myradio_init(NULL);
+        if(io==-1)
+        {
+                printk(KERN_ERR 
+         "You must set an I/O address with io=0x???\n");
+                return -EINVAL;
+        }
+        return myradio_init(NULL);
 }
 
 void cleanup_module(void)
 {
-       video_unregister_device(&amp;my_radio);
-       release_region(io, MY_IO_SIZE);
+        video_unregister_device(&amp;my_radio);
+        release_region(io, MY_IO_SIZE);
 }
 
 #endif
 
   </programlisting>
   <para>
-       In this example we set the IO base by default if the driver is compiled into
-       the kernel where you cannot pass a parameter. For the module we require the
-       user sets the parameter. We set io to a nonsense port (-1) so that we can
-       tell if the user supplied an io parameter or not.
+        In this example we set the IO base by default if the driver is compiled into
+        the kernel where you cannot pass a parameter. For the module we require the
+        user sets the parameter. We set io to a nonsense port (-1) so that we can
+        tell if the user supplied an io parameter or not.
   </para>
   <para>
-       We use MODULE_ defines to give an author for the card driver and a
-       description. We also use them to declare that io is an integer and it is the
-       address of the card.
+        We use MODULE_ defines to give an author for the card driver and a
+        description. We also use them to declare that io is an integer and it is the
+        address of the card.
   </para>
   <para>
-       The clean-up routine unregisters the video_device we registered, and frees
-       up the I/O space. Note that the unregister takes the actual video_device
-       structure as its argument. Unlike the file operations structure which can be
-       shared by all instances of a device a video_device structure as an actual
-       instance of the device. If you are registering multiple radio devices you
-       need to fill in one structure per device (most likely by setting up a
-       template and copying it to each of the actual device structures).
+        The clean-up routine unregisters the video_device we registered, and frees
+        up the I/O space. Note that the unregister takes the actual video_device
+        structure as its argument. Unlike the file operations structure which can be
+        shared by all instances of a device a video_device structure as an actual
+        instance of the device. If you are registering multiple radio devices you
+        need to fill in one structure per device (most likely by setting up a
+        template and copying it to each of the actual device structures).
   </para>
   </sect1>
   </chapter>
   <chapter>
-       <title>Video Capture Devices</title>
+        <title>Video Capture Devices</title>
   <sect1 id="introvid">
   <title>Video Capture Device Types</title>
   <para>
-       The video capture devices share the same interfaces as radio devices. In
-       order to explain the video capture interface I will use the example of a
-       camera that has no tuners or audio input. This keeps the example relatively
-       clean. To get both combine the two driver examples.
+        The video capture devices share the same interfaces as radio devices. In
+        order to explain the video capture interface I will use the example of a
+        camera that has no tuners or audio input. This keeps the example relatively
+        clean. To get both combine the two driver examples.
   </para>
   <para>
-       Video capture devices divide into four categories. A little technology
-       backgrounder. Full motion video even at television resolution (which is
-       actually fairly low) is pretty resource-intensive. You are continually
-       passing megabytes of data every second from the capture card to the display. 
-       several alternative approaches have emerged because copying this through the 
-       processor and the user program is a particularly bad idea .
+        Video capture devices divide into four categories. A little technology
+        backgrounder. Full motion video even at television resolution (which is
+        actually fairly low) is pretty resource-intensive. You are continually
+        passing megabytes of data every second from the capture card to the display. 
+        several alternative approaches have emerged because copying this through the 
+        processor and the user program is a particularly bad idea .
   </para>
   <para>
-       The first is to add the television image onto the video output directly.
-       This is also how some 3D cards work. These basic cards can generally drop the
-       video into any chosen rectangle of the display. Cards like this, which
-       include most mpeg1 cards that used the feature connector,  aren't very
-       friendly in a windowing environment. They don't understand windows or
-       clipping. The video window is always on the top of the display.
+        The first is to add the television image onto the video output directly.
+        This is also how some 3D cards work. These basic cards can generally drop the
+        video into any chosen rectangle of the display. Cards like this, which
+        include most mpeg1 cards that used the feature connector,  aren't very
+        friendly in a windowing environment. They don't understand windows or
+        clipping. The video window is always on the top of the display.
   </para>
   <para>
-       Chroma keying is a technique used by cards to get around this. It is an old
-       television mixing trick where you mark all the areas you wish to replace
-       with a single clear colour that isn't used in the image - TV people use an
-       incredibly bright blue while computing people often use a paticularly
-       virulent purple. Bright blue occurs on the desktop. Anyone with virulent
-       purple windows has another problem besides their TV overlay.
+        Chroma keying is a technique used by cards to get around this. It is an old
+        television mixing trick where you mark all the areas you wish to replace
+        with a single clear colour that isn't used in the image - TV people use an
+        incredibly bright blue while computing people often use a paticularly
+        virulent purple. Bright blue occurs on the desktop. Anyone with virulent
+        purple windows has another problem besides their TV overlay.
   </para>
   <para>
-       The third approach is to copy the data from the capture card to the video
-       card, but to do it directly across the PCI bus. This relieves the processor
-       from doing the work but does require some smartness on the part of the video
-       capture chip, as well as a suitable video card. Programming this kind of
-       card and more so debugging it can be extremely tricky. There are some quite
-       complicated interactions with the display and you may also have to cope with
-       various chipset bugs that show up when PCI cards start talking to each
-       other. 
+        The third approach is to copy the data from the capture card to the video
+        card, but to do it directly across the PCI bus. This relieves the processor
+        from doing the work but does require some smartness on the part of the video
+        capture chip, as well as a suitable video card. Programming this kind of
+        card and more so debugging it can be extremely tricky. There are some quite
+        complicated interactions with the display and you may also have to cope with
+        various chipset bugs that show up when PCI cards start talking to each
+        other. 
   </para>
   <para>
-       To keep our example fairly simple we will assume a card that supports
-       overlaying a flat rectangular image onto the frame buffer output, and which
-       can also capture stuff into processor memory.
+        To keep our example fairly simple we will assume a card that supports
+        overlaying a flat rectangular image onto the frame buffer output, and which
+        can also capture stuff into processor memory.
   </para>
   </sect1>
   <sect1 id="regvid">
   <title>Registering Video Capture Devices</title>
   <para>
-       This time we need to add more functions for our camera device.
+        This time we need to add more functions for our camera device.
   </para>
   <programlisting>
 static struct video_device my_camera
 {
-       "My Camera",
-       VID_TYPE_OVERLAY|VID_TYPE_SCALES|VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
-       VID_HARDWARE_MYCAMERA,
-       camera_open.
-       camera_close,
-       camera_read,            /* no read */
-       NULL,                   /* no write */
-       camera_poll,            /* no poll */
-       camera_ioctl,
-       NULL,                   /* no special init function */
-       NULL                    /* no private data */
+        "My Camera",
+        VID_TYPE_OVERLAY|VID_TYPE_SCALES|\
+        VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY,
+        VID_HARDWARE_MYCAMERA,
+        camera_open.
+        camera_close,
+        camera_read,      /* no read */
+        NULL,             /* no write */
+        camera_poll,      /* no poll */
+        camera_ioctl,
+        NULL,             /* no special init function */
+        NULL              /* no private data */
 };
   </programlisting>
   <para>
-       We need a read() function which is used for capturing data from
-       the card, and we need a poll function so that a driver can wait for the next
-       frame to be captured.
-  </para>
-  <para>
-       We use the extra video capability flags that did not apply to the
-       radio interface. The video related flags are
-
-VID_TYPE_CAPTURE               We support image capture
-VID_TYPE_TELETEXT              A teletext capture device (vbi{n])
-VID_TYPE_OVERLAY               The image can be directly overlaid onto the
-                               frame buffer
-VID_TYPE_CHROMAKEY             Chromakey can be used to select which parts
-                               of the image to display
-VID_TYPE_CLIPPING              It is possible to give the board a list of
-                               rectangles to draw around. 
-VID_TYPE_FRAMERAM              The video capture goes into the video memory
-                               and actually changes it. Applications need
-                               to know this so they can clean up after the
-                               card
-VID_TYPE_SCALES                        The image can be scaled to various sizes,
-                               rather than being a single fixed size.
-VID_TYPE_MONOCHROME            The capture will be monochrome. This isn't a 
-                               complete answer to the question since a mono
-                               camera on a colour capture card will still
-                               produce mono output.
-VID_TYPE_SUBCAPTURE            The card allows only part of its field of
-                               view to be captured. This enables
-                               applications to avoid copying all of a large
-                               image into memory when only some section is
-                               relevant.
-
-       We set VID_TYPE_CAPTURE so that we are seen as a capture card,
-       VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
-       purple, and VID_TYPE_SCALES because we can be resized.
-  </para>
-  <para>
-       Our setup is fairly similar. This time we also want an interrupt line
-       for the 'frame captured' signal. Not all cards have this so some of them
-       cannot handle poll().
+        We need a read() function which is used for capturing data from
+        the card, and we need a poll function so that a driver can wait for the next
+        frame to be captured.
+  </para>
+  <para>
+        We use the extra video capability flags that did not apply to the
+        radio interface. The video related flags are
+  </para>
+   <table frame=all><title>Capture Capabilities</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+<entry>VID_TYPE_CAPTURE</><>We support image capture</>
+</row><row>
+<entry>VID_TYPE_TELETEXT</><>A teletext capture device (vbi{n])</>
+</row><row>
+<entry>VID_TYPE_OVERLAY</><>The image can be directly overlaid onto the
+                                frame buffer</>
+</row><row>
+<entry>VID_TYPE_CHROMAKEY</><>Chromakey can be used to select which parts
+                                of the image to display</>
+</row><row>
+<entry>VID_TYPE_CLIPPING</><>It is possible to give the board a list of
+                                rectangles to draw around. </>
+</row><row>
+<entry>VID_TYPE_FRAMERAM</><>The video capture goes into the video memory
+                                and actually changes it. Applications need
+                                to know this so they can clean up after the
+                                card</>
+</row><row>
+<entry>VID_TYPE_SCALES</><>The image can be scaled to various sizes,
+                                rather than being a single fixed size.</>
+</row><row>
+<entry>VID_TYPE_MONOCHROME</><>The capture will be monochrome. This isn't a 
+                                complete answer to the question since a mono
+                                camera on a colour capture card will still
+                                produce mono output.</>
+</row><row>
+<entry>VID_TYPE_SUBCAPTURE</><>The card allows only part of its field of
+                                view to be captured. This enables
+                                applications to avoid copying all of a large
+                                image into memory when only some section is
+                                relevant.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        We set VID_TYPE_CAPTURE so that we are seen as a capture card,
+        VID_TYPE_CHROMAKEY so the application knows it is time to draw in virulent
+        purple, and VID_TYPE_SCALES because we can be resized.
+  </para>
+  <para>
+        Our setup is fairly similar. This time we also want an interrupt line
+        for the 'frame captured' signal. Not all cards have this so some of them
+        cannot handle poll().
   </para>
   <programlisting>
 
@@ -829,22 +920,24 @@ static int irq = 11;
 
 int __init mycamera_init(struct video_init *v)
 {
-       if(check_region(io, MY_IO_SIZE))
-       {
-               printk(KERN_ERR "mycamera: port 0x%03X is in use.\n", io);
-               return -EBUSY;
-       }
-
-       if(video_device_register(&amp;my_camera, VFL_TYPE_GRABBER)==-1)
-               return -EINVAL;
-       request_region(io, MY_IO_SIZE, "mycamera");
-       return 0;
+        if(check_region(io, MY_IO_SIZE))
+        {
+                printk(KERN_ERR 
+                      "mycamera: port 0x%03X is in use.\n", io);
+                return -EBUSY;
+        }
+
+        if(video_device_register(&amp;my_camera, 
+            VFL_TYPE_GRABBER)==-1)
+                return -EINVAL;
+        request_region(io, MY_IO_SIZE, "mycamera");
+        return 0;
 }
 
   </programlisting>
   <para>
-       This is little changed from the needs of the radio card. We specify
-       VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
+        This is little changed from the needs of the radio card. We specify
+        VFL_TYPE_GRABBER this time as we want to be allocated a /dev/video name.
   </para>
   </sect1>
   <sect1 id="opvid">
@@ -856,41 +949,41 @@ static int users = 0;
 
 static int camera_open(stuct video_device *dev, int flags)
 {
-       if(users)
-               return -EBUSY;
-       if(request_irq(irq, camera_irq, 0, "camera", dev)&lt;0)
-               return -EBUSY;
-       users++;
-       MOD_INC_USE_COUNT;
-       return 0;
+        if(users)
+                return -EBUSY;
+        if(request_irq(irq, camera_irq, 0, "camera", dev)&lt;0)
+                return -EBUSY;
+        users++;
+        MOD_INC_USE_COUNT;
+        return 0;
 }
 
 
 static int camera_close(struct video_device *dev)
 {
-       users--;
-       free_irq(irq, dev);
-       MOD_DEC_USE_COUNT;
+        users--;
+        free_irq(irq, dev);
+        MOD_DEC_USE_COUNT;
 }
   </programlisting>
   <para>
-       The open and close routines are also quite similar. The only real change is
-       that we now request an interrupt for the camera device interrupt line. If we
-       cannot get the interrupt we report EBUSY to the application and give up.
+        The open and close routines are also quite similar. The only real change is
+        that we now request an interrupt for the camera device interrupt line. If we
+        cannot get the interrupt we report EBUSY to the application and give up.
   </para>
   </sect1>
   <sect1 id="irqvid">
   <title>Interrupt Handling</title>
   <para>
-       Our example handler is for an ISA bus device. If it was PCI you would be
-       able to share the interrupt and would have set SA_SHIRQ to indicate a 
-       shared IRQ. We pass the device pointer as the interrupt routine argument. We
-       don't need to since we only support one card but doing this will make it
-       easier to upgrade the driver for multiple devices in the future.
+        Our example handler is for an ISA bus device. If it was PCI you would be
+        able to share the interrupt and would have set SA_SHIRQ to indicate a 
+        shared IRQ. We pass the device pointer as the interrupt routine argument. We
+        don't need to since we only support one card but doing this will make it
+        easier to upgrade the driver for multiple devices in the future.
   </para>
   <para>
-       Our interrupt routine needs to do little if we assume the card can simply
-       queue one frame to be read after it captures it. 
+        Our interrupt routine needs to do little if we assume the card can simply
+        queue one frame to be read after it captures it. 
   </para>
   <programlisting>
 
@@ -898,40 +991,42 @@ static int camera_close(struct video_device *dev)
 static struct wait_queue *capture_wait;
 static int capture_ready = 0;
 
-static void camera_irq(int irq, void *dev_id, struct pt_regs *regs)
+static void camera_irq(int irq, void *dev_id, 
+                          struct pt_regs *regs)
 {
-       capture_ready=1;
-       wake_up_interruptible(&amp;capture_wait);
+        capture_ready=1;
+        wake_up_interruptible(&amp;capture_wait);
 }
   </programlisting>
   <para>
-       The interrupt handler is nice and simple for this card as we are assuming
-       the card is buffering the frame for us. This means we have little to do but
-       wake up anybody interested. We also set a capture_ready flag, as we may
-       capture a frame before an application needs it. In this case we need to know
-       that a frame is ready. If we had to collect the frame on the interrupt life
-       would be more complex.
+        The interrupt handler is nice and simple for this card as we are assuming
+        the card is buffering the frame for us. This means we have little to do but
+        wake up        anybody interested. We also set a capture_ready flag, as we may
+        capture a frame before an application needs it. In this case we need to know
+        that a frame is ready. If we had to collect the frame on the interrupt life
+        would be more complex.
   </para>
   <para>
-       The two new routines we need to supply are camera_read which returns a
-       frame, and camera_poll which waits for a frame to become ready.
+        The two new routines we need to supply are camera_read which returns a
+        frame, and camera_poll which waits for a frame to become ready.
   </para>
   <programlisting>
 
 
-static int camera_poll(struct video_device *dev, struct file *file, struct poll_table *wait)
+static int camera_poll(struct video_device *dev, 
+       struct file *file, struct poll_table *wait)
 {
-       poll_wait(file, &amp;capture_wait, wait);
-       if(capture_read)
-               return POLLIN|POLLRDNORM;
-       return 0;
+        poll_wait(file, &amp;capture_wait, wait);
+        if(capture_read)
+                return POLLIN|POLLRDNORM;
+        return 0;
 }
 
   </programlisting>
   <para>
-       Our wait queue for polling is the capture_wait queue. This will cause the
-       task to be woken up by our camera_irq routine. We check capture_read to see
-       if there is an image present and if so report that it is readable.
+        Our wait queue for polling is the capture_wait queue. This will cause the
+        task to be woken up by our camera_irq routine. We check capture_read to see
+        if there is an image present and if so report that it is readable.
   </para>
   </sect1>
   <sect1 id="rdvid">
@@ -940,99 +1035,99 @@ static int camera_poll(struct video_device *dev, struct file *file, struct poll_
 
 
 static long camera_read(struct video_device *dev, char *buf,
-                               unsigned long count)
+                                unsigned long count)
 {
-       struct wait_queue wait = { current, NULL };
-       u8 *ptr;
-       int len;
-       int i;
-
-       add_wait_queue(&amp;capture_wait, &amp;wait);
-
-       while(!capture_ready)
-       {
-               if(file->flags&amp;O_NDELAY)
-               {
-                       remove_wait_queue(&amp;capture_wait, &amp;wait);
-                       current->state = TASK_RUNNING;
-                       return -EWOULDBLOCK;
-               }
-               if(signal_pending(current))
-               {
-                       remove_wait_queue(&amp;capture_wait, &amp;wait);
-                       current->state = TASK_RUNNING;
-                       return -ERESTARTSYS;
-               }
-               schedule();
-               current->state = TASK_INTERRUPTIBLE;
-       }
-       remove_wait_queue(&amp;capture_wait, &amp;wait);
-       current->state = TASK_RUNNING;
+        struct wait_queue wait = { current, NULL };
+        u8 *ptr;
+        int len;
+        int i;
+
+        add_wait_queue(&amp;capture_wait, &amp;wait);
+
+        while(!capture_ready)
+        {
+                if(file->flags&amp;O_NDELAY)
+                {
+                        remove_wait_queue(&amp;capture_wait, &amp;wait);
+                        current->state = TASK_RUNNING;
+                        return -EWOULDBLOCK;
+                }
+                if(signal_pending(current))
+                {
+                        remove_wait_queue(&amp;capture_wait, &amp;wait);
+                        current->state = TASK_RUNNING;
+                        return -ERESTARTSYS;
+                }
+                schedule();
+                current->state = TASK_INTERRUPTIBLE;
+        }
+        remove_wait_queue(&amp;capture_wait, &amp;wait);
+        current->state = TASK_RUNNING;
 
   </programlisting>
   <para>
-       The first thing we have to do is to ensure that the application waits until
-       the next frame is ready. The code here is almost identical to the mouse code
-       we used earlier in this chapter. It is one of the common building blocks of
-       Linux device driver code and probably one which you will find occurs in any
-       drivers you write.
+        The first thing we have to do is to ensure that the application waits until
+        the next frame is ready. The code here is almost identical to the mouse code
+        we used earlier in this chapter. It is one of the common building blocks of
+        Linux device driver code and probably one which you will find occurs in any
+        drivers you write.
   </para>
   <para>
-       We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
-       signal occurs we need to return from the system call so that the signal can
-       be sent to the application itself. We also check to see if the user actually
-       wanted to avoid waiting - ie  if they are using non-blocking I/O and have other things 
-       to get on with.
+        We wait for a frame to be ready, or for a signal to interrupt our waiting. If a
+        signal occurs we need to return from the system call so that the signal can
+        be sent to the application itself. We also check to see if the user actually
+        wanted to avoid waiting - ie  if they are using non-blocking I/O and have other things 
+        to get on with.
   </para>
   <para>
-       Next we copy the data from the card to the user application. This is rarely
-       as easy as our example makes out. We will add capture_w, and capture_h here
-       to hold the width and height of the captured image. We assume the card only
-       supports 24bit RGB for now.
+        Next we copy the data from the card to the user application. This is rarely
+        as easy as our example makes out. We will add capture_w, and capture_h here
+        to hold the width and height of the captured image. We assume the card only
+        supports 24bit RGB for now.
   </para>
   <programlisting>
 
 
 
-       capture_ready = 0;
+        capture_ready = 0;
 
-       ptr=(u8 *)buf;
-       len = capture_w * 3 * capture_h;        /* 24bit RGB */
+        ptr=(u8 *)buf;
+        len = capture_w * 3 * capture_h; /* 24bit RGB */
 
-       if(len>count)
-               len=count;                      /* Doesn't all fit */
+        if(len>count)
+                len=count;  /* Doesn't all fit */
 
-       for(i=0; i&lt;len; i++)
-       {
-               put_user(inb(io+IMAGE_DATA), ptr);
-               ptr++;
-       }
+        for(i=0; i&lt;len; i++)
+        {
+                put_user(inb(io+IMAGE_DATA), ptr);
+                ptr++;
+        }
 
-       hardware_restart_capture();
-               
-       return i;
+        hardware_restart_capture();
+                
+        return i;
 }
 
   </programlisting>
   <para>
-       For a real hardware device you would try to avoid the loop with put_user().
-       Each call to put_user() has a time overhead checking whether the accesses to user
-       space are allowed. It would be better to read a line into a temporary buffer
-       then copy this to user space in one go.
+        For a real hardware device you would try to avoid the loop with put_user().
+        Each call to put_user() has a time overhead checking whether the accesses to user
+        space are allowed. It would be better to read a line into a temporary buffer
+        then copy this to user space in one go.
   </para>
   <para>
-       Having captured the image and put it into user space we can kick the card to
-       get the next frame acquired.
+        Having captured the image and put it into user space we can kick the card to
+        get the next frame acquired.
   </para>
   </sect1>
   <sect1 id="iocvid">
   <title>Video Ioctl Handling</title>
   <para>
-       As with the radio driver the major control interface is via the ioctl()
-       function. Video capture devices support the same tuner calls as a radio
-       device and also support additional calls to control how the video functions
-       are handled. In this simple example the card has no tuners to avoid making
-       the code complex. 
+        As with the radio driver the major control interface is via the ioctl()
+        function. Video capture devices support the same tuner calls as a radio
+        device and also support additional calls to control how the video functions
+        are handled. In this simple example the card has no tuners to avoid making
+        the code complex. 
   </para>
   <programlisting>
 
@@ -1040,434 +1135,498 @@ static long camera_read(struct video_device *dev, char *buf,
 
 static int camera_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
-       switch(cmd)
-       {
-               case VIDIOCGCAP:
-               {
-                       struct video_capability v;
-                       v.type = VID_TYPE_CAPTURE|VID_TYPE_CHROMAKEY|\
-                                       VID_TYPE_SCALES|VID_TYPE_OVERLAY;
-                       v.channels = 1;
-                       v.audios = 0;
-                       v.maxwidth = 640;
-                       v.minwidth = 16;
-                       v.maxheight = 480;
-                       v.minheight = 16;
-                       strcpy(v.name, "My Camera");
-                       if(copy_to_user(arg, &amp;v, sizeof(v)))
-                               return -EFAULT;
-                       return 0;
-               }
+        switch(cmd)
+        {
+                case VIDIOCGCAP:
+                {
+                        struct video_capability v;
+                        v.type = VID_TYPE_CAPTURE|\
+                                 VID_TYPE_CHROMAKEY|\
+                                 VID_TYPE_SCALES|\
+                                 VID_TYPE_OVERLAY;
+                        v.channels = 1;
+                        v.audios = 0;
+                        v.maxwidth = 640;
+                        v.minwidth = 16;
+                        v.maxheight = 480;
+                        v.minheight = 16;
+                        strcpy(v.name, "My Camera");
+                        if(copy_to_user(arg, &amp;v, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       The first ioctl we must support and which all video capture and radio
-       devices are required to support is VIDIOCGCAP. This behaves exactly the same
-       as with a radio device. This time, however, we report the extra capabilities
-       we outlined earlier on when defining our video_dev structure.
+        The first ioctl we must support and which all video capture and radio
+        devices are required to support is VIDIOCGCAP. This behaves exactly the same
+        as with a radio device. This time, however, we report the extra capabilities
+        we outlined earlier on when defining our video_dev structure.
   </para>
   <para>
-       We now set the video flags saying that we support overlay, capture,
-       scaling and chromakey. We also report size limits - our smallest image is
-       16x16 pixels, our largest is 640x480. 
+        We now set the video flags saying that we support overlay, capture,
+        scaling and chromakey. We also report size limits - our smallest image is
+        16x16 pixels, our largest is 640x480. 
   </para>
   <para>
-       To keep things simple we report no audio and no tuning capabilities at all.
+        To keep things simple we report no audio and no tuning capabilities at all.
   </para>
-  <programlisting>     
+  <programlisting>        
 
-               case VIDIOCGCHAN:
-               {
-                       struct video_channel v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.channel != 0)
-                               return -EINVAL;
-                       v.flags = 0;
-                       v.tuners = 0;
-                       v.type = VIDEO_TYPE_CAMERA;
-                       v.norm = VIDEO_MODE_AUTO;
-                       strcpy(v.name, "Camera Input");break;
-                       if(copy_to_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       return 0;
-               }
+                case VIDIOCGCHAN:
+                {
+                        struct video_channel v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.channel != 0)
+                                return -EINVAL;
+                        v.flags = 0;
+                        v.tuners = 0;
+                        v.type = VIDEO_TYPE_CAMERA;
+                        v.norm = VIDEO_MODE_AUTO;
+                        strcpy(v.name, "Camera Input");break;
+                        if(copy_to_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       This follows what is very much the standard way an ioctl handler looks
-       in Linux. We copy the data into a kernel space variable and we check that the
-       request is valid (in this case that the input is 0). Finally we copy the
-       camera info back to the user.
-  </para>
-  <para>
-       The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
-       inputs to the video card). Our example card has a single camera input. The
-       fields in the structure are
-
-       channel                 The channel number we are selecting
-       name                    The name for this channel. This is intended
-                               to describe the port to the user.
-                               Appropriate names are therefore things like
-                               "Camera" "SCART input"
-       flags
-
-       VIDEO_VC_TUNER          Channel has a tuner.
-       VIDEO_VC_AUDIO          Channel has audio.
-
-       type
-
-       VIDEO_TYPE_TV           Television input.
-       VIDEO_TYPE_CAMERA       Fixed camera input.
-
-       If the input type is unknown then zero is returned.
-
-       norm                    The current television encoding being used
-                               if relevant for this channel.
-
-       This is one of
-
-       VIDEO_MODE_PAL          PAL encoded Television
-       VIDEO_MODE_NTSC         NTSC (US) encoded Television
-       VIDEO_MODE_SECAM        SECAM (French) Televison 
-       VIDEO_MODE_AUTO         Automatic switching, or format does not
-                               matter
-
-
-
-       The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
-       request the norm is changed - for exaple to switch between a PAL or an NTSC
-       format camera.
+        This follows what is very much the standard way an ioctl handler looks
+        in Linux. We copy the data into a kernel space variable and we check that the
+        request is valid (in this case that the input is 0). Finally we copy the
+        camera info back to the user.
+  </para>
+  <para>
+        The VIDIOCGCHAN ioctl allows a user to ask about video channels (that is
+        inputs to the video card). Our example card has a single camera input. The
+        fields in the structure are
+  </para>
+   <table frame=all><title>struct video_channel fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+
+   <entry>channel</><>The channel number we are selecting</entry>
+   </row><row>
+   <entry>name</><>The name for this channel. This is intended
+                   to describe the port to the user.
+                   Appropriate names are therefore things like
+                   "Camera" "SCART input"</entry>
+   </row><row>
+   <entry>flags</><>Channel properties</entry>
+   </row><row>
+   <entry>type</><>Input type</entry>
+   </row><row>
+   <entry>norm</><>The current television encoding being used
+                   if relevant for this channel.
+    </entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel flags</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_VC_TUNER</><>Channel has a tuner.</entry>
+   </row><row>
+        <entry>VIDEO_VC_AUDIO</><>Channel has audio.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel types</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_TYPE_TV</><>Television input.</entry>
+   </row><row>
+        <entry>VIDEO_TYPE_CAMERA</><>Fixed camera input.</entry>
+   </row><row>
+       <entry>0</><>Type is unknown.</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <table frame=all><title>struct video_channel norms</title>
+    <tgroup cols=2 align=left>
+    <tbody>
+    <row>
+        <entry>VIDEO_MODE_PAL</><>PAL encoded Television</entry>
+   </row><row>
+        <entry>VIDEO_MODE_NTSC</><>NTSC (US) encoded Television</entry>
+   </row><row>
+        <entry>VIDEO_MODE_SECAM</><>SECAM (French) Televison </entry>
+   </row><row>
+        <entry>VIDEO_MODE_AUTO</><>Automatic switching, or format does not
+                                matter</entry>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        The corresponding VIDIOCSCHAN ioctl allows a user to change channel and to
+        request the norm is changed - for exaple to switch between a PAL or an NTSC
+        format camera.
   </para>
   <programlisting>
 
 
-               case VIDIOCSCHAN:
-               {
-                       struct video_channel v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.channel != 0)
-                               return -EINVAL;
-                       if(v.norm != VIDEO_MODE_AUTO)
-                               return -EINVAL;
-                       return 0;
-               }
+                case VIDIOCSCHAN:
+                {
+                        struct video_channel v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.channel != 0)
+                                return -EINVAL;
+                        if(v.norm != VIDEO_MODE_AUTO)
+                                return -EINVAL;
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       The implementation of this call in our driver is remarkably easy. Because we
-       are assuming fixed format hardware we need only check that the user has not
-       tried to change anything. 
+        The implementation of this call in our driver is remarkably easy. Because we
+        are assuming fixed format hardware we need only check that the user has not
+        tried to change anything. 
   </para>
   <para>
-       The user also needs to be able to configure and adjust the picture they are
-       seeing. This is much like adjusting a television set. A user application
-       also needs to know the palette being used so that it knows how to display
-       the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
-       calls provide this information.
+        The user also needs to be able to configure and adjust the picture they are
+        seeing. This is much like adjusting a television set. A user application
+        also needs to know the palette being used so that it knows how to display
+        the image that has been captured. The VIDIOCGPICT and VIDIOCSPICT ioctl
+        calls provide this information.
   </para>
   <programlisting>
 
 
-               case VIDIOCGPICT
-               {
-                       struct video_picture v;
-                       v.brightness = hardware_brightness();
-                       v.hue = hardware_hue();
-                       v.colour = hardware_saturation();
-                       v.contrast = hardware_brightness();
-                       v.whiteness = 32768;    /* Not settable */
-                       v.depth = 24;           /* 24bit */
-                       v.palette = VIDEO_PALETTE_RGB24;
-                       if(copy_to_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       return 0;
-               }
+                case VIDIOCGPICT
+                {
+                        struct video_picture v;
+                        v.brightness = hardware_brightness();
+                        v.hue = hardware_hue();
+                        v.colour = hardware_saturation();
+                        v.contrast = hardware_brightness();
+                        /* Not settable */
+                        v.whiteness = 32768;
+                        v.depth = 24;           /* 24bit */
+                        v.palette = VIDEO_PALETTE_RGB24;
+                        if(copy_to_user(&amp;v, arg, 
+                             sizeof(v)))
+                                return -EFAULT;
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       The brightness, hue, color, and contrast provide the picture controls that
-       are akin to a conventional television. Whiteness provides additional
-       control for greyscale images. All of these values are scaled between 0-65535
-       and have 32768 as the mid point setting. The scaling means that applications
-       do not have to worry about the capability range of the hardware but can let
-       it make a best effort attempt.
-  </para>
-  <para>
-       Our depth is 24, as this is in bits. We will be returing RGB24 format. This
-       has one byte of red, then one of green, then one of blue. This then repeats
-       for every other pixel in the image. The other common formats the interface 
-       defines are
-
-       GREY            Linear greyscale. This is for simple cameras and the
-                       like
-
-       RGB565          The top 5 bits hold 32 red levels, the next six bits 
-                       hold green and the low 5 bits hold blue. 
-
-       RGB555          The top bit is clear. The red green and blue levels
-                       each occupy five bits.
-
-
-       Additional modes are support for YUV capture formats. These are common for
-       TV and video conferencing applictations.
-  </para>
-  <para>
-       The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
-       Exactly which ones are supported depends heavily on the card itself. It is
-       possible to support many modes and effects in software. In general doing
-       this in the kernel is a bad idea. Video capture is a performance-sensitive
-       application and the programs can often do better if they aren't being
-       'helped' by an overkeen driver writer. Thus for our device we will report
-       RGB24 only and refuse to allow a change.
+        The brightness, hue, color, and contrast provide the picture controls that
+        are akin to a conventional television. Whiteness provides additional
+        control for greyscale images. All of these values are scaled between 0-65535
+        and have 32768 as the mid point setting. The scaling means that applications
+        do not have to worry about the capability range of the hardware but can let
+        it make a best effort attempt.
+  </para>
+  <para>
+        Our depth is 24, as this is in bits. We will be returing RGB24 format. This
+        has one byte of red, then one of green, then one of blue. This then repeats
+        for every other pixel in the image. The other common formats the interface 
+        defines are
+  </para>
+   <table frame=all><title>Framebuffer Encodings</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+   <entry>GREY</><>Linear greyscale. This is for simple cameras and the
+                        like</>
+   </row><row>
+   <entry>RGB565</><>The top 5 bits hold 32 red levels, the next six bits 
+                        hold green and the low 5 bits hold blue. </>
+   </row><row>
+   <entry>RGB555</><>The top bit is clear. The red green and blue levels
+                        each occupy five bits.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+  <para>
+        Additional modes are support for YUV capture formats. These are common for
+        TV and video conferencing applications.
+  </para>
+  <para>
+        The VIDIOCSPICT ioctl allows a user to set some of the picture parameters.
+        Exactly which ones are supported depends heavily on the card itself. It is
+        possible to support many modes and effects in software. In general doing
+        this in the kernel is a bad idea. Video capture is a performance-sensitive
+        application and the programs can often do better if they aren't being
+        'helped' by an overkeen driver writer. Thus for our device we will report
+        RGB24 only and refuse to allow a change.
   </para>
   <programlisting>
 
 
-               case VIDIOCSPICT:
-               {
-                       struct video_picture v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.depth!=24 || v.palette != VIDEO_PALETTE_RGB24)
-                               return -EINVAL;
-                       set_hardware_brightness(v.brightness);
-                       set_hardware_hue(v.hue);
-                       set_hardware_saturation(v.colour);
-                       set_hardware_brightness(v.contrast);
-                       return 0;
-               }
+                case VIDIOCSPICT:
+                {
+                        struct video_picture v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.depth!=24 || 
+                           v.palette != VIDEO_PALETTE_RGB24)
+                                return -EINVAL;
+                        set_hardware_brightness(v.brightness);
+                        set_hardware_hue(v.hue);
+                        set_hardware_saturation(v.colour);
+                        set_hardware_brightness(v.contrast);
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       We check the user has not tried to change the palette or the depth. We do
-       not want to carry out some of the changes and then return an error. This may
-       confuse the application which will be assuming no change occurred.
+        We check the user has not tried to change the palette or the depth. We do
+        not want to carry out some of the changes and then return an error. This may
+        confuse the application which will be assuming no change occurred.
   </para>
   <para>
-       In much the same way as you need to be able to set the picture controls to
-       get the right capture images, many cards need to know what they are
-       displaying onto when generating overlay output. In some cases getting this
-       wrong even makes a nasty mess or may crash the computer. For that reason
-       the VIDIOCSBUF ioctl used to set up the frame buffer information may well
-       only be usable by root.
+        In much the same way as you need to be able to set the picture controls to
+        get the right capture images, many cards need to know what they are
+        displaying onto when generating overlay output. In some cases getting this
+        wrong even makes a nasty mess or may crash the computer. For that reason
+        the VIDIOCSBUF ioctl used to set up the frame buffer information may well
+        only be usable by root.
   </para>
   <para>
-       We will assume our card is one of the old ISA devices with feature connector
-       and only supports a couple of standard video modes. Very common for older
-       cards although the PCI devices are way smarter than this.
+        We will assume our card is one of the old ISA devices with feature connector
+        and only supports a couple of standard video modes. Very common for older
+        cards although the PCI devices are way smarter than this.
   </para>
   <programlisting>
 
 
 static struct video_buffer capture_fb;
 
-               case VIDIOCGFBUF:
-               {
-                       if(copy_to_user(arg, &amp;capture_fb, sizeof(capture_fb)))
-                               return -EFAULT;
-                       return 0;
-                       
-               }
+                case VIDIOCGFBUF:
+                {
+                        if(copy_to_user(arg, &amp;capture_fb, 
+                             sizeof(capture_fb)))
+                                return -EFAULT;
+                        return 0;
+                        
+                }
 
 
   </programlisting>
   <para>
-       We keep the frame buffer information in the format the ioctl uses. This
-       makes it nice and easy to work with in the ioctl calls.
+        We keep the frame buffer information in the format the ioctl uses. This
+        makes it nice and easy to work with in the ioctl calls.
   </para>
   <programlisting>
 
-               case VIDIOCSFBUF:
-               {
-                       struct video_buffer v;
+                case VIDIOCSFBUF:
+                {
+                        struct video_buffer v;
 
-                       if(!capable(CAP_SYS_ADMIN))
-                               return -EPERM;
+                        if(!capable(CAP_SYS_ADMIN))
+                                return -EPERM;
 
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.width!=320 &amp;&amp; v.width!=640)
-                               return -EINVAL;
-                       if(v.height!=200 &amp;&amp; v.height!=240 &amp;&amp; v.height!=400
-                               &amp;&amp; v.height !=480)
-                               return -EINVAL;
-                       memcpy(&amp;capture_fb, &amp;v, sizeof(v));
-                       hardware_set_fb(&amp;v);
-                       return 0;
-               }
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.width!=320 &amp;&amp; v.width!=640)
+                                return -EINVAL;
+                        if(v.height!=200 &amp;&amp; v.height!=240 
+                                &amp;&amp; v.height!=400
+                                &amp;&amp; v.height !=480)
+                                return -EINVAL;
+                        memcpy(&amp;capture_fb, &amp;v, sizeof(v));
+                        hardware_set_fb(&amp;v);
+                        return 0;
+                }
 
 
 
   </programlisting>
   <para>
-       The capable() function checks a user has the required capability. The Linux
-       operating system has a set of about 30 capabilities indicating privileged
-       access to services. The default set up gives the superuser (uid 0) all of
-       them and nobody else has any.
+        The capable() function checks a user has the required capability. The Linux
+        operating system has a set of about 30 capabilities indicating privileged
+        access to services. The default set up gives the superuser (uid 0) all of
+        them and nobody else has any.
   </para>
   <para>
-       We check that the user has the SYS_ADMIN capability, that is they are
-       allowed to operate as the machine administrator. We don't want anyone but
-       the administrator making a mess of the display.
+        We check that the user has the SYS_ADMIN capability, that is they are
+        allowed to operate as the machine administrator. We don't want anyone but
+        the administrator making a mess of the display.
   </para>
   <para>
-       Next we check for standard PC video modes (320 or 640 wide with either
-       EGA or VGA depths). If the mode is not a standard video mode we reject it as
-       not supported by our card. If the mode is acceptable we save it so that
-       VIDIOCFBUF will give the right answer next time it is called.  The
-       hardware_set_fb() function is some undescribed card specific function to
-       program the card for the desired mode.
+        Next we check for standard PC video modes (320 or 640 wide with either
+        EGA or VGA depths). If the mode is not a standard video mode we reject it as
+        not supported by our card. If the mode is acceptable we save it so that
+        VIDIOCFBUF will give the right answer next time it is called.  The
+        hardware_set_fb() function is some undescribed card specific function to
+        program the card for the desired mode.
   </para>
   <para>
-       Before the driver can display an overlay window it needs to know where the
-       window should be placed, and also how large it should be. If the card
-       supports clipping it needs to know which rectangles to omit from the
-       display. The video_window structure is used to describe the way the image 
-       should be displayed. 
-
-       width           The width in pixels of the desired image. The card
-                       may use a smaller size if this size is not available
-
-       height          The height of the image. The card may use a smaller
-                       size if this size is not available.
-
-       x               The X position of the top left of the window. This
-                       is in pixels relative to the left hand edge of the
-                       picture. Not all cards can display images aligned on
-                       any pixel boundary. If the position is unsuitable
-                       the card adjusts the image right and reduces the
-                       width.
-
-       y               The Y position of the top left of the window. This
-                       is counted in pixels relative to the top edge of the
-                       picture. As with the width if the card cannot
-                       display  starting on this line it will adjust the
-                       values.
-
-       chromakey       The colour (expressed in RGB32 format) for the
-                       chromakey colour if chroma keying is being used. 
-
-       clips           An array of rectangles that must not be drawn over.
-
-       clipcount       The number of clips in this array.
-
-
-       Each clip is a struct video_clip which has the following fields
-
-       x, y            Co-ordinates relative to the display
-
-       width, height   Width and height in pixels
-
-       next            A spare field for the application to use
-
-
-       The driver is required to ensure it always draws in the area requested or a
-       smaller area, and that it never draws in any of the areas that are clipped.
-       This may well mean it has to leave alone. small areas the application wished to be
-       drawn.
+        Before the driver can display an overlay window it needs to know where the
+        window should be placed, and also how large it should be. If the card
+        supports clipping it needs to know which rectangles to omit from the
+        display. The video_window structure is used to describe the way the image 
+        should be displayed. 
+   </para>
+   <table frame=all><title>struct video_window fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>width</><>The width in pixels of the desired image. The card
+                        may use a smaller size if this size is not available</>
+       </row><row>
+        <entry>height</><>The height of the image. The card may use a smaller
+                        size if this size is not available.</>
+       </row><row>
+        <entry>x</><>   The X position of the top left of the window. This
+                        is in pixels relative to the left hand edge of the
+                        picture. Not all cards can display images aligned on
+                        any pixel boundary. If the position is unsuitable
+                        the card adjusts the image right and reduces the
+                        width.</>
+       </row><row>
+        <entry>y</><>   The Y position of the top left of the window. This
+                        is counted in pixels relative to the top edge of the
+                        picture. As with the width if the card cannot
+                        display  starting on this line it will adjust the
+                        values.</>
+       </row><row>
+        <entry>chromakey</><>The colour (expressed in RGB32 format) for the
+                        chromakey colour if chroma keying is being used. </>
+       </row><row>
+        <entry>clips</><>An array of rectangles that must not be drawn
+                       over.</>
+       </row><row>
+        <entry>clipcount</><>The number of clips in this array.</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        Each clip is a struct video_clip which has the following fields
+   </para>
+   <table frame=all><title>video_clip fields</title>
+   <tgroup cols=2 align=left>
+   <tbody>
+   <row>
+        <entry>x, y</><>Co-ordinates relative to the display</>
+       </row><row>
+        <entry>width, height</><>Width and height in pixels</>
+       </row><row>
+        <entry>next</><>A spare field for the application to use</>
+    </row>
+    </tbody>
+    </tgroup>
+    </table>
+    <para>
+        The driver is required to ensure it always draws in the area requested or a        smaller area, and that it never draws in any of the areas that are clipped.
+        This may well mean it has to leave alone. small areas the application wished to be
+        drawn.
   </para>
   <para>
-       Our example card uses chromakey so does not have to address most of the
-       clipping.  We will add a video_window structure to our global variables to
-       remember our parameters, as we did with the frame buffer.
+        Our example card uses chromakey so does not have to address most of the
+        clipping.  We will add a video_window structure to our global variables to
+        remember our parameters, as we did with the frame buffer.
   </para>
   <programlisting>
 
 
-               case VIDIOCGWIN:
-               {
-                       if(copy_to_user(arg, &amp;capture_win, sizeof(capture_win)))
-                               return -EFAULT;
-                       return 0;
-               }
-
-
-               case VIDIOCSWIN:
-               {
-                       struct video_window v;
-                       if(copy_from_user(&amp;v, arg, sizeof(v)))
-                               return -EFAULT;
-                       if(v.width > 640 || v.height > 480)
-                               return -EINVAL;
-                       if(v.width < 16 || v.height < 16)
-                               return -EINVAL;
-                       hardware_set_key(v.chromakey);
-                       hardware_set_window(v);
-                       memcpy(&amp;capture_win, &amp;v, sizeof(v));
-                       capture_w = v.width;
-                       capture_h = v.height;
-                       return 0;
-               }
+                case VIDIOCGWIN:
+                {
+                        if(copy_to_user(arg, &amp;capture_win, 
+                            sizeof(capture_win)))
+                                return -EFAULT;
+                        return 0;
+                }
+
+
+                case VIDIOCSWIN:
+                {
+                        struct video_window v;
+                        if(copy_from_user(&amp;v, arg, sizeof(v)))
+                                return -EFAULT;
+                        if(v.width > 640 || v.height > 480)
+                                return -EINVAL;
+                        if(v.width < 16 || v.height < 16)
+                                return -EINVAL;
+                        hardware_set_key(v.chromakey);
+                        hardware_set_window(v);
+                        memcpy(&amp;capture_win, &amp;v, sizeof(v));
+                        capture_w = v.width;
+                        capture_h = v.height;
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       Because we are using Chromakey our setup is fairly simple. Mostly we have to
-       check the values are sane and load them into the capture card.
+        Because we are using Chromakey our setup is fairly simple. Mostly we have to
+        check the values are sane and load them into the capture card.
   </para>
   <para>
-       With all the setup done we can now turn on the actual capture/overlay. This
-       is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
-       where 0 is on and 1 is off.
+        With all the setup done we can now turn on the actual capture/overlay. This
+        is done with the VIDIOCCAPTURE ioctl. This takes a single integer argument
+        where 0 is on and 1 is off.
   </para>
   <programlisting>
 
 
-               case VIDIOCCAPTURE:
-               {
-                       int v;
-                       if(get_user(v, (int *)arg))
-                               return -EFAULT;
-                       if(v==0)
-                               hardware_capture_off();
-                       else
-                       {
-                               if(capture_fb.width == 0 || capture_w == 0)
-                                       return -EINVAL;
-                               hardware_capture_on();
-                       }
-                       return 0;
-               }
+                case VIDIOCCAPTURE:
+                {
+                        int v;
+                        if(get_user(v, (int *)arg))
+                                return -EFAULT;
+                        if(v==0)
+                                hardware_capture_off();
+                        else
+                        {
+                                if(capture_fb.width == 0 
+                                    || capture_w == 0)
+                                        return -EINVAL;
+                                hardware_capture_on();
+                        }
+                        return 0;
+                }
 
 
   </programlisting>
   <para>
-       We grab the flag from user space and either enable or disable according to
-       its value. There is one small corner case we have to consider here. Suppose
-       that the capture was requested before the video window or the frame buffer
-       had been set up. In those cases there will be unconfigured fields in our
-       card data, as well as unconfigured hardware settings. We check for this case and
-       return an error if the frame buffer or the capture window width is zero.
+        We grab the flag from user space and either enable or disable according to
+        its value. There is one small corner case we have to consider here. Suppose
+        that the capture was requested before the video window or the frame buffer
+        had been set up. In those cases there will be unconfigured fields in our
+        card data, as well as unconfigured hardware settings. We check for this case and
+        return an error if the frame buffer or the capture window width is zero.
   </para>
   <programlisting>
 
 
-               default:
-                       return -ENOIOCTLCMD;
-       }
+                default:
+                        return -ENOIOCTLCMD;
+        }
 }
   </programlisting>
   <para>
 
-       We don't need to support any other ioctls, so if we get this far, it is time
-       to tell the video layer that we don't now what the user is talking about.
+        We don't need to support any other ioctls, so if we get this far, it is time
+        to tell the video layer that we don't now what the user is talking about.
   </para>
   </sect1>
   <sect1 id="endvid">
   <title>Other Functionality</title>
   <para>
-       The Video4Linux layer supports additional features, including a high
-       performance mmap() based capture mode and capturing part of the image. 
-       These features are out of the scope of the book.  You should however have enough 
-       example code to implement most simple video4linux devices for radio and TV
-       cards.
+        The Video4Linux layer supports additional features, including a high
+        performance mmap() based capture mode and capturing part of the image. 
+        These features are out of the scope of the book.  You should however have enough 
+        example code to implement most simple video4linux devices for radio and TV
+        cards.
   </para>
   </sect1>
   </chapter>
@@ -1478,21 +1637,21 @@ static struct video_buffer capture_fb;
     <varlistentry><term>Multiple Opens</term>
     <listitem>
     <para>
-       The driver assumes multiple opens should not be allowed. A driver
-       can work around this but not cleanly.
+        The driver assumes multiple opens should not be allowed. A driver
+        can work around this but not cleanly.
     </para>
     </listitem></varlistentry>
 
     <varlistentry><term>API Deficiences</term>
     <listitem>
     <para>
-       The existing API poorly reflects compression capable devices. There
-       are plans afoot to merge V4L, V4L2 and some other ideas into a
-       better interface.
+        The existing API poorly reflects compression capable devices. There
+        are plans afoot to merge V4L, V4L2 and some other ideas into a
+        better interface.
     </para>
     </listitem></varlistentry>
   </variablelist>
-       
+
   </para>
   </chapter>
 
index 04bac916a64aefe03d730b85ad301de2814faad0..6762ce048d95fb1dbf3c591be4ba57309699e3d9 100644 (file)
@@ -845,9 +845,7 @@ S:  Maintained
 RAGE128 FRAMEBUFFER DISPLAY DRIVER
 P:     Brad Douglas
 M:     brad@neruo.com
-P:     Anthony Tong
-M:     atong@uiuc.edu
-L:     linux-fbdev@vcuser.vc.union.edu
+L:     linux-fbdev@vuser.vc.union.edu
 S:     Maintained
 
 RAYLINK/WEBGEAR 802.11 WIRELESS LAN DRIVER
@@ -1117,6 +1115,12 @@ M:       weissg@vienna.at
 L:     linux-usb@suse.com
 S:     Maintained
 
+USB PEGASUS DRIVER
+P:     Petko Manolov
+M:     petkan@spct.net
+L:     linux-usb@suse.com
+S:     Maintained
+
 USB PRINTER DRIVER
 P:     Vojtech Pavlik
 M:     vojtech@suse.cz
index 784688b9b94585ace799a107d1129f536d34e424..c8bf2bcd3415375d2f4342d1d77e0cff8ff60a0d 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 3
-SUBLEVEL = 52
-EXTRAVERSION =
+SUBLEVEL = 99
+EXTRAVERSION = -pre1
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
 
@@ -116,149 +116,53 @@ DRIVERS          =drivers/block/block.a \
 LIBS           =$(TOPDIR)/lib/lib.a
 SUBDIRS                =kernel drivers mm fs net ipc lib
 
-ifdef CONFIG_DRM
-DRIVERS += drivers/char/drm/drm.o
-endif
-
-ifeq ($(CONFIG_AGP),y)
-DRIVERS += drivers/char/agp/agp.o
-endif
-
-ifdef CONFIG_NUBUS
-DRIVERS := $(DRIVERS) drivers/nubus/nubus.a
-endif
-
-ifeq ($(CONFIG_ISDN),y)
-DRIVERS := $(DRIVERS) drivers/isdn/isdn.a
-endif
-
-ifdef CONFIG_NET_FC
-DRIVERS := $(DRIVERS) drivers/net/fc/fc.a
-endif
-
-ifdef CONFIG_APPLETALK
-DRIVERS := $(DRIVERS) drivers/net/appletalk/appletalk.a
-endif
-
-ifdef CONFIG_TR
-DRIVERS := $(DRIVERS) drivers/net/tokenring/tr.a
-endif
-
-ifdef CONFIG_WAN
-DRIVERS := $(DRIVERS) drivers/net/wan/wan.a
-endif
-
-ifeq ($(CONFIG_ARCNET),y)
-DRIVERS := $(DRIVERS) drivers/net/arcnet/arcnet.a
-endif
-
-ifdef CONFIG_ATM
-DRIVERS := $(DRIVERS) drivers/atm/atm.a
-endif
-
-ifeq ($(CONFIG_IDE),y)
-DRIVERS := $(DRIVERS) drivers/ide/ide.a
-endif
-
-ifeq ($(CONFIG_SCSI),y)
-DRIVERS := $(DRIVERS) drivers/scsi/scsi.a
-endif
-
-ifeq ($(CONFIG_IEEE1394),y)
-DRIVERS := $(DRIVERS) drivers/ieee1394/ieee1394.a
-endif
+DRIVERS-n :=
+DRIVERS-y :=
+DRIVERS-m :=
+DRIVERS-  :=
+
+DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
+DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
+DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
+DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.a
+DRIVERS-$(CONFIG_APPLETALK) += drivers/net/appletalk/appletalk.a
+DRIVERS-$(CONFIG_TR) += drivers/net/tokenring/tr.a
+DRIVERS-$(CONFIG_WAN) += drivers/net/wan/wan.a
+DRIVERS-$(CONFIG_ARCNET) += drivers/net/arcnet/arcnet.a
+DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.a
+DRIVERS-$(CONFIG_IDE) += drivers/ide/ide.a
+DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsi.a
+DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394.a
 
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
-DRIVERS := $(DRIVERS) drivers/cdrom/cdrom.a
-endif
-
-ifeq ($(CONFIG_SOUND),y)
-DRIVERS := $(DRIVERS) drivers/sound/sounddrivers.o
-endif
-
-ifdef CONFIG_PCI
-DRIVERS := $(DRIVERS) drivers/pci/pci.a
-endif
-
-ifeq ($(CONFIG_PCMCIA),y)
-DRIVERS := $(DRIVERS) drivers/pcmcia/pcmcia.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_NETCARD),y)
-DRIVERS := $(DRIVERS) drivers/net/pcmcia/pcmcia_net.o
-endif
-
-ifeq ($(CONFIG_PCMCIA_CHRDEV),y)
-DRIVERS := $(DRIVERS) drivers/char/pcmcia/pcmcia_char.o
-endif
-
-ifdef CONFIG_DIO
-DRIVERS := $(DRIVERS) drivers/dio/dio.a
-endif
-
-ifdef CONFIG_SBUS
-DRIVERS := $(DRIVERS) drivers/sbus/sbus.a
-endif
-
-ifdef CONFIG_ZORRO
-DRIVERS := $(DRIVERS) drivers/zorro/zorro.a
-endif
-
-ifeq ($(CONFIG_FC4),y)
-DRIVERS := $(DRIVERS) drivers/fc4/fc4.a
-endif
-
-ifdef CONFIG_PPC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifdef CONFIG_MAC
-DRIVERS := $(DRIVERS) drivers/macintosh/macintosh.a
-endif
-
-ifeq ($(CONFIG_ISAPNP),y)
-DRIVERS := $(DRIVERS) drivers/pnp/pnp.o
-endif
-
-ifdef CONFIG_SGI_IP22
-DRIVERS := $(DRIVERS) drivers/sgi/sgi.a
-endif
-
-ifdef CONFIG_VT
-DRIVERS := $(DRIVERS) drivers/video/video.o
-endif
-
-ifeq ($(CONFIG_PARIDE),y)
-DRIVERS := $(DRIVERS) drivers/block/paride/paride.a
-endif
-
-ifdef CONFIG_HAMRADIO
-DRIVERS := $(DRIVERS) drivers/net/hamradio/hamradio.o
-endif
-
-ifeq ($(CONFIG_TC),y)
-DRIVERS := $(DRIVERS) drivers/tc/tc.a
-endif
-
-ifeq ($(CONFIG_USB),y)
-DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o
-endif
-
-ifeq ($(CONFIG_I2O),y)
-DRIVERS := $(DRIVERS) drivers/i2o/i2o.a
-endif
-
-ifeq ($(CONFIG_IRDA),y)
-DRIVERS := $(DRIVERS) drivers/net/irda/irda_drivers.a
-endif
-
-ifeq ($(CONFIG_I2C),y)
-DRIVERS := $(DRIVERS) drivers/i2c/i2c.a
-endif
-
-ifeq ($(CONFIG_PHONE),y)
-DRIVERS := $(DRIVERS) drivers/telephony/telephony.a
-endif
+DRIVERS-y += drivers/cdrom/cdrom.a
+endif
+
+DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
+DRIVERS-$(CONFIG_PCI) += drivers/pci/pci.a
+DRIVERS-$(CONFIG_PCMCIA) += drivers/pcmcia/pcmcia.o
+DRIVERS-$(CONFIG_PCMCIA_NETCARD) += drivers/net/pcmcia/pcmcia_net.o
+DRIVERS-$(CONFIG_PCMCIA_CHRDEV) += drivers/char/pcmcia/pcmcia_char.o
+DRIVERS-$(CONFIG_DIO) += drivers/dio/dio.a
+DRIVERS-$(CONFIG_SBUS) += drivers/sbus/sbus.a
+DRIVERS-$(CONFIG_ZORRO) += drivers/zorro/zorro.a
+DRIVERS-$(CONFIG_FC4) += drivers/fc4/fc4.a
+DRIVERS-$(CONFIG_PPC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_MAC) += drivers/macintosh/macintosh.a
+DRIVERS-$(CONFIG_ISAPNP) += drivers/pnp/pnp.o
+DRIVERS-$(CONFIG_SGI_IP22) += drivers/sgi/sgi.a
+DRIVERS-$(CONFIG_VT) += drivers/video/video.o
+DRIVERS-$(CONFIG_PARIDE) += drivers/block/paride/paride.a
+DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
+DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
+DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-$(CONFIG_I2O) += drivers/i2o/i2o.a
+DRIVERS-$(CONFIG_IRDA) += drivers/net/irda/irda_drivers.a
+DRIVERS-$(CONFIG_I2C) += drivers/i2c/i2c.a
+DRIVERS-$(CONFIG_PHONE) += drivers/telephony/telephony.a
+
+DRIVERS += $(DRIVERS-y)
 
 include arch/$(ARCH)/Makefile
 
index 1381fb84252a4b0b32ed25c6690d6e2d92f7724c..56500e466105fbe0aa3b70fcf13346be7591bc95 100644 (file)
@@ -639,6 +639,7 @@ ENTRY(sys_call_table)
        .long SYMBOL_NAME(sys_setfsgid)
        .long SYMBOL_NAME(sys_pivot_root)
        .long SYMBOL_NAME(sys_mincore)
+       .long SYMBOL_NAME(sys_madvise)
 
 
        /*
@@ -647,6 +648,6 @@ ENTRY(sys_call_table)
         * entries. Don't panic if you notice that this hasn't
         * been shrunk every time we add a new system call.
         */
-       .rept NR_syscalls-218
+       .rept NR_syscalls-219
                .long SYMBOL_NAME(sys_ni_syscall)
        .endr
index 120c861e7997579c579aac2f58951639f54b9454..d2ba2fd9c02feacd0c4196e795a1a1912cd541d3 100644 (file)
@@ -456,6 +456,18 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction *
  * hardware disable after having gotten the irq
  * controller lock. 
  */
+/**
+ *     disable_irq_nosync - disable an irq without waiting
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line. Disables of an interrupt
+ *     stack. Unlike disable_irq, this function does not ensure existing
+ *     instances of the irq handler have completed before returning.
+ *
+ *     This function may be called from IRQ context.
+ */
 void inline disable_irq_nosync(unsigned int irq)
 {
        irq_desc_t *desc = irq_desc + irq;
@@ -469,10 +481,19 @@ void inline disable_irq_nosync(unsigned int irq)
        spin_unlock_irqrestore(&desc->lock, flags);
 }
 
-/*
- * Synchronous version of the above, making sure the IRQ is
- * no longer running on any other IRQ..
+/**
+ *     disable_irq - disable an irq and wait for completion
+ *     @irq: Interrupt to disable
+ *
+ *     Disable the selected interrupt line. Disables of an interrupt
+ *     stack. That is for two disables you need two enables. This
+ *     function waits for any pending IRQ handlers for this interrupt
+ *     to complete before returning. If you use this function while
+ *     holding a resource the IRQ handler may need you will deadlock.
+ *
+ *     This function may be called - with care - from IRQ context.
  */
 void disable_irq(unsigned int irq)
 {
        disable_irq_nosync(irq);
@@ -484,6 +505,16 @@ void disable_irq(unsigned int irq)
        }
 }
 
+/**
+ *     enable_irq - enable interrupt handling on an irq
+ *     @irq: Interrupt to enable
+ *
+ *     Re-enables the processing of interrupts on this IRQ line
+ *     providing no disable_irq calls are now in effect.
+ *
+ *     This function may be called from IRQ context.
+ */
 void enable_irq(unsigned int irq)
 {
        irq_desc_t *desc = irq_desc + irq;
@@ -598,6 +629,38 @@ out:
        return 1;
 }
 
+/**
+ *     request_irq - allocate an interrupt line
+ *     @irq: Interrupt line to allocate
+ *     @handler: Function to be called when the IRQ occurs
+ *     @irqflags: Interrupt type flags
+ *     @devname: An ascii name for the claiming device
+ *     @dev_id: A cookie passed back to the handler function
+ *
+ *     This call allocates interrupt resources and enables the
+ *     interrupt line and IRQ handling. From the point this
+ *     call is made your handler function may be invoked. Since
+ *     your handler function must clear any interrupt the board 
+ *     raises, you must take care both to initialise your hardware
+ *     and to set up the interrupt handler in the right order.
+ *
+ *     Dev_id must be globally unique. Normally the address of the
+ *     device data structure is used as the cookie. Since the handler
+ *     receives this value it makes sense to use it.
+ *
+ *     If your interrupt is shared you must pass a non NULL dev_id
+ *     as this is required when freeing the interrupt.
+ *
+ *     Flags:
+ *
+ *     SA_SHIRQ                Interrupt is shared
+ *
+ *     SA_INTERRUPT            Disable local interrupts while processing
+ *
+ *     SA_SAMPLE_RANDOM        The interrupt can be used for entropy
+ *
+ */
 int request_irq(unsigned int irq, 
                void (*handler)(int, void *, struct pt_regs *),
                unsigned long irqflags, 
@@ -642,7 +705,25 @@ int request_irq(unsigned int irq,
                kfree(action);
        return retval;
 }
-               
+
+/**
+ *     free_irq - free an interrupt
+ *     @irq: Interrupt line to free
+ *     @dev_id: Device identity to free
+ *
+ *     Remove an interrupt handler. The handler is removed and if the
+ *     interrupt line is no longer in use by any driver it is disabled.
+ *     On a shared IRQ the caller must ensure the interrupt is disabled
+ *     on the card it drives before calling this function. The function
+ *     does not return until any executing interrupts for this IRQ
+ *     have completed.
+ *
+ *     This function may be called from interrupt context. 
+ *
+ *     Bugs: Attempting to free an irq in a handler for the same irq hangs
+ *           the machine.
+ */
 void free_irq(unsigned int irq, void *dev_id)
 {
        irq_desc_t *desc;
@@ -693,6 +774,15 @@ void free_irq(unsigned int irq, void *dev_id)
  * with "IRQ_WAITING" cleared and the interrupt
  * disabled.
  */
+
+/**
+ *     probe_irq_on    - begin an interrupt autodetect
+ *
+ *     Commence probing for an interrupt. The interrupts are scanned
+ *     and a mask of potential interrupt lines is returned.
+ *
+ */
 unsigned long probe_irq_on(void)
 {
        unsigned int i;
@@ -770,6 +860,16 @@ unsigned long probe_irq_on(void)
  * Return a mask of triggered interrupts (this
  * can handle only legacy ISA interrupts).
  */
+/**
+ *     probe_irq_mask
+ *     @val:   mask of interrupts to consider
+ *
+ *     Scan the ISA bus interrupt lines and return a bitmap of
+ *     active interrupts. The interrupt probe logic state is then
+ *     returned to its previous value.
+ */
 unsigned int probe_irq_mask(unsigned long val)
 {
        int i;
@@ -798,8 +898,27 @@ unsigned int probe_irq_mask(unsigned long val)
 
 /*
  * Return the one interrupt that triggered (this can
- * handle any interrupt source)
+ * handle any interrupt source).
+ */
+
+/**
+ *     probe_irq_off   - end an interrupt autodetect
+ *     @val: mask of potential interrupts (unused)
+ *
+ *     Scans the unused interrupt lines and returns the line which
+ *     appears to have triggered the interrupt. If no interrupt was
+ *     found then zero is returned. If more than one interrupt is
+ *     found then minus the first candidate is returned to indicate
+ *     their is doubt.
+ *
+ *     The interrupt probe logic state is returned to its previous
+ *     value.
+ *
+ *     BUGS: When used in a module (which arguably shouldnt happen)
+ *     nothing prevents two IRQ probe callers from overlapping. The
+ *     results of this are non-optimal.
  */
 int probe_irq_off(unsigned long val)
 {
        int i, irq_found, nr_irqs;
index cc9c7eafe684a4dbea49c3f9783c6627825609d3..1d6203f658fa7359808aff3a06dfc05ee669bb9a 100644 (file)
@@ -1101,8 +1101,44 @@ static int cyrix_get_free_region (unsigned long base, unsigned long size)
 static int (*get_free_region) (unsigned long base,
                               unsigned long size) = generic_get_free_region;
 
-int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
-             char increment)
+/**
+ *     mtrr_add - Add a memory type region
+ *     @base: Physical base address of region
+ *     @size: Physical size of region
+ *     @type: Type of MTRR desired
+ *     @increment: If this is true do usage counting on the region
+ *
+ *     Memory type region registers control the caching on newer Intel and
+ *     non Intel processors. This function allows drivers to request an
+ *     MTRR is added. The details and hardware specifics of each processors
+ *     implementation are hidden from the caller, but nevertheless the 
+ *     caller should expect to need to provide a power of two size on an
+ *     equivalent power of two boundary.
+ *
+ *     If the region cannot be added either because all regions are in use
+ *     or the CPU cannot support it a negative value is returned. On success
+ *     the register number for this entry is returned, but should be treated
+ *     as a cookie only.
+ *
+ *     On a multiprocessor machine the changes are made to all processors.
+ *     This is required on x86 by the Intel processors.
+ *
+ *     The available types are
+ *
+ *     MTRR_TYPE_UNCACHEABLE   -       No caching
+ *
+ *     MTRR_TYPE_WRITEBACK     -       Write data back in bursts whenever
+ *
+ *     MTRR_TYPE_WRCOMB        -       Write data back soon but allow bursts
+ *
+ *     MTRR_TYPE_WRTHROUGH     -       Cache reads but not writes
+ *
+ *     BUGS: Needs a quiet flag for the cases where drivers do not mind
+ *     failures and do not wish system log messages to be sent.
+ */
+
+int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment)
+{
 /*  [SUMMARY] Add an MTRR entry.
     <base> The starting (base) address of the region.
     <size> The size (in bytes) of the region.
@@ -1113,7 +1149,6 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
     the error code.
     [NOTE] This routine uses a spinlock.
 */
-{
     int i, max;
     mtrr_type ltype;
     unsigned long lbase, lsize, last;
@@ -1145,7 +1180,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
            if ( (boot_cpu_data.x86 == 6) && (boot_cpu_data.x86_model == 1) &&
                 (boot_cpu_data.x86_mask <= 7) && ( base & ( (1 << 22) -1 ) ) )
            {
-               printk ("mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
+               printk (KERN_WARNING "mtrr: base(0x%lx) is not 4 MiB aligned\n", base);
                return -EINVAL;
            }
        }
@@ -1162,13 +1197,13 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
        {
            if (type != MTRR_TYPE_WRCOMB)
            {
-               printk ("mtrr: only write-combining is supported\n");
+               printk (KERN_WARNING "mtrr: only write-combining is supported\n");
                return -EINVAL;
            }
        }
        else if (base + size < 0x100000)
        {
-           printk ("mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
+           printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx,0x%lx)\n",
                    base, size);
            return -EINVAL;
        }
@@ -1179,7 +1214,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
             lbase = lbase >> 1, last = last >> 1);
        if (lbase != last)
        {
-           printk ("mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
+           printk (KERN_WARNING "mtrr: base(0x%lx) is not aligned on a size(0x%lx) boundary\n",
                    base, size);
            return -EINVAL;
        }
@@ -1196,7 +1231,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
     /*  If the type is WC, check that this processor supports it  */
     if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () )
     {
-        printk ("mtrr: your processor doesn't support write-combining\n");
+        printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n");
         return -ENOSYS;
     }
     increment = increment ? 1 : 0;
@@ -1212,7 +1247,7 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
        if ( (base < lbase) || (base + size > lbase + lsize) )
        {
            up(&main_lock);
-           printk ("mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
+           printk (KERN_WARNING "mtrr: 0x%lx,0x%lx overlaps existing 0x%lx,0x%lx\n",
                    base, size, lbase, lsize);
            return -EINVAL;
        }
@@ -1245,6 +1280,21 @@ int mtrr_add (unsigned long base, unsigned long size, unsigned int type,
     return i;
 }   /*  End Function mtrr_add  */
 
+/**
+ *     mtrr_del
+ *     @reg: Register returned by mtrr_add
+ *     @base: Physical base address
+ *     @size: Size of region
+ *
+ *     If register is supplied then base and size are ignored. This is
+ *     how drivers should call it.
+ *
+ *     Releases an MTRR region. If the usage count drops to zero the 
+ *     register is freed and the region returns to default state.
+ *     On success the register is returned, on failure a negative error
+ *     code.
+ */
 int mtrr_del (int reg, unsigned long base, unsigned long size)
 /*  [SUMMARY] Delete MTRR/decrement usage count.
     <reg> The register. If this is less than 0 then <<base>> and <<size>> must
index b6cc30fed8f8b7f4a3db368a166c27a17052359c..df5f2a6549900641f3156ffcce9e364937dbf683 100644 (file)
@@ -35,8 +35,6 @@
 #include <asm/mipsregs.h>
 #include <asm/prctl.h>
 
-#include <linux/config.h>
-
 #define DLINFO_ITEMS 12
 
 #include <linux/elf.h>
index 1d6f208f66fdcb310ee6ceec95799393c3fdebd0..262f6afdd648779e90e42f62d29089b3374e6b67 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos.c,v 1.114 2000/03/07 22:27:27 davem Exp $
+/* $Id: sys_sunos.c,v 1.115 2000/03/13 21:57:23 davem Exp $
  * sys_sunos.c: SunOS specific syscall compatibility support.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -296,57 +296,6 @@ asmlinkage void sunos_madvise(unsigned long address, unsigned long len,
        unlock_kernel();
 }
 
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'.  -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one  == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(unsigned long addr, unsigned long len, char *array)
-{
-       pgd_t *pgdp;
-       pmd_t *pmdp;
-       pte_t *ptep;
-       unsigned long limit;
-       int num_pages, pnum, retval = -EINVAL;
-
-       lock_kernel();
-       if(addr & ~(PAGE_MASK))
-               goto out;
-
-       num_pages = (len / PAGE_SIZE);
-       retval = -EFAULT;
-       if(verify_area(VERIFY_WRITE, array, num_pages))
-               goto out;
-       retval = -ENOMEM;
-       if((addr >= PAGE_OFFSET) || ((addr + len) > PAGE_OFFSET))
-               goto out; /* I'm sure you're curious about kernel mappings.. */
-
-       /* Wheee, go through pte's */
-       pnum = 0;
-       for(limit = addr + len; addr < limit; addr += PAGE_SIZE, pnum++) {
-               pgdp = pgd_offset(current->mm, addr);
-               if(pgd_none(*pgdp))
-                       goto out; /* As per SunOS manpage */
-               pmdp = pmd_offset(pgdp, addr);
-               if(pmd_none(*pmdp))
-                       goto out; /* As per SunOS manpage */
-               ptep = pte_offset(pmdp, addr);
-               if(pte_none(*ptep))
-                       goto out; /* As per SunOS manpage */
-               /* Page in core or Swapped page? */
-               __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
-       }
-       retval = 0; /* Success... I think... */
-out:
-       unlock_kernel();
-       return retval;
-}
-
 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
  * resource limit and is for backwards compatibility with older sunos
  * revs.
index 8746958d738b24741f8fd518c5d039c73c3afd90..42c072164f8d36855f305ee20b6e494f35aefa0e 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.94 2000/02/16 07:31:30 davem Exp $
+/* $Id: systbls.S,v 1.95 2000/03/13 21:57:23 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -33,7 +33,7 @@ sys_call_table:
 /*60*/ .long sys_umask, sys_chroot, sys_newfstat, sys_fstat64, sys_getpagesize
 /*65*/ .long sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_geteuid
 /*70*/ .long sys_getegid, sys_mmap, sys_setreuid, sys_munmap, sys_mprotect
-/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_nis_syscall, sys_getgroups16
+/*75*/ .long sys_nis_syscall, sys_vhangup, sys_truncate64, sys_mincore, sys_getgroups16
 /*80*/ .long sys_setgroups16, sys_getpgrp, sys_setgroups, sys_setitimer, sys_ftruncate64
 /*85*/ .long sys_swapon, sys_getitimer, sys_setuid, sys_sethostname, sys_setgid
 /*90*/ .long sys_dup2, sys_setfsuid, sys_fcntl, sys_select, sys_setfsgid
@@ -104,7 +104,7 @@ sunos_sys_table:
        .long sunos_nosys, sunos_sbrk, sunos_sstk
        .long sunos_mmap, sunos_vadvise, sys_munmap
        .long sys_mprotect, sunos_madvise, sys_vhangup
-       .long sunos_nosys, sunos_mincore, sys_getgroups16
+       .long sunos_nosys, sys_mincore, sys_getgroups16
        .long sys_setgroups16, sys_getpgrp, sunos_setpgrp
        .long sys_setitimer, sunos_nosys, sys_swapon
        .long sys_getitimer, sys_gethostname, sys_sethostname
index b8fb7cfbd679aa22ed3a206bedc1d288a9a762ca..d3a3814a841eced55a3b57dd70a62d2119030d0c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.81 2000/03/12 04:02:07 davem Exp $
+/* $Id: ioctl32.c,v 1.82 2000/03/13 21:57:27 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -2335,6 +2335,10 @@ COMPATIBLE_IOCTL(PPPIOCSDEBUG)
 COMPATIBLE_IOCTL(PPPIOCNEWUNIT)
 COMPATIBLE_IOCTL(PPPIOCATTACH)
 COMPATIBLE_IOCTL(PPPIOCDETACH)
+COMPATIBLE_IOCTL(PPPIOCSMRRU)
+COMPATIBLE_IOCTL(PPPIOCCONNECT)
+COMPATIBLE_IOCTL(PPPIOCDISCONN)
+COMPATIBLE_IOCTL(PPPIOCATTCHAN)
 /* CDROM stuff */
 COMPATIBLE_IOCTL(CDROMPAUSE)
 COMPATIBLE_IOCTL(CDROMRESUME)
index 462d4c12925ddb107e08c53f7774b0db306d7c8a..d4ecb0f4fc66f52e0808aa24bb2f63bead54e620 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc32.c,v 1.135 2000/03/12 03:52:09 davem Exp $
+/* $Id: sys_sparc32.c,v 1.136 2000/03/13 21:57:29 davem Exp $
  * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -4218,3 +4218,12 @@ out_sem:
 out:
        return ret;       
 }
+
+extern asmlinkage long sys_mincore(unsigned long start, size_t len, unsigned char *vec);
+
+asmlinkage long sys32_mincore(unsigned long start, u32 __len, unsigned char *vec)
+{
+       size_t len = (size_t) __len;
+
+       return sys_mincore(start, len, vec);
+}
index 9673cdd36e9b83e8101435119966bc766f3c5894..f7f5964e90124dce93fc77f74783eec685e625ff 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: sys_sunos32.c,v 1.40 2000/03/07 22:27:31 davem Exp $
+/* $Id: sys_sunos32.c,v 1.41 2000/03/13 21:57:31 davem Exp $
  * sys_sunos32.c: SunOS binary compatability layer on sparc64.
  *
  * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -252,56 +252,6 @@ asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy)
        unlock_kernel();
 }
 
-/* Places into character array, the status of all the pages in the passed
- * range from 'addr' to 'addr + len'.  -1 on failure, 0 on success...
- * The encoding in each character is:
- * low-bit is zero == Page is not in physical ram right now
- * low-bit is one  == Page is currently residing in core
- * All other bits are undefined within the character so there...
- * Also, if you try to get stats on an area outside of the user vm area
- * *or* the passed base address is not aligned on a page boundary you
- * get an error.
- */
-asmlinkage int sunos_mincore(u32 __addr, u32 len, u32 u_array)
-{
-       pgd_t *pgdp;
-       pmd_t *pmdp;
-       pte_t *ptep;
-       unsigned long limit, addr = (unsigned long)__addr;
-       int num_pages, pnum, retval = -EINVAL;
-       char *array = (char *)A(u_array);
-
-       lock_kernel();
-       if(addr & ~(4096))
-               goto out;
-       num_pages = (len / 4096);
-       retval = -EFAULT;
-       if(verify_area(VERIFY_WRITE, array, num_pages))
-               goto out;
-       retval = -ENOMEM;
-       if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000))
-               goto out; /* I'm sure you're curious about kernel mappings.. */
-       /* Wheee, go through pte's */
-       pnum = 0;
-       for(limit = addr + len; addr < limit; addr += 4096, pnum++) {
-               pgdp = pgd_offset(current->mm, addr);
-               if(pgd_none(*pgdp))
-                       goto out; /* As per SunOS manpage */
-               pmdp = pmd_offset(pgdp, addr);
-               if(pmd_none(*pmdp))
-                       goto out; /* As per SunOS manpage */
-               ptep = pte_offset(pmdp, addr);
-               if(pte_none(*ptep))
-                       goto out; /* As per SunOS manpage */
-               /* Page in core or Swapped page? */
-               __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
-       }
-       retval = 0; /* Success... I think... */
-out:
-       unlock_kernel();
-       return retval;
-}
-
 /* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
  * resource limit and is for backwards compatibility with older sunos
  * revs.
index 1f7ab3fef35d68e1c294ba07a4125d1f2a1db051..0a0edbf8291228d3cda5e88307fa30bb5f7db46f 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: systbls.S,v 1.68 2000/02/16 07:31:38 davem Exp $
+/* $Id: systbls.S,v 1.69 2000/03/13 21:57:28 davem Exp $
  * systbls.S: System call entry point tables for OS compatibility.
  *            The native Linux system call table lives here also.
  *
@@ -34,7 +34,7 @@ sys_call_table32:
 /*60*/ .word sys_umask, sys_chroot, sys32_newfstat, sys_fstat64, sys_getpagesize
        .word sys_msync, sys_vfork, sys32_pread, sys32_pwrite, sys_geteuid
 /*70*/ .word sys_getegid, sys32_mmap, sys_setreuid, sys_munmap, sys_mprotect
-       .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys_nis_syscall, sys32_getgroups16
+       .word sys_nis_syscall, sys_vhangup, sys32_truncate64, sys32_mincore, sys32_getgroups16
 /*80*/ .word sys32_setgroups16, sys_getpgrp, sys_setgroups, sys32_setitimer, sys32_ftruncate64
        .word sys_swapon, sys32_getitimer, sys_setuid, sys_sethostname, sys_setgid
 /*90*/ .word sys_dup2, sys_setfsuid, sys32_fcntl, sys32_select, sys_setfsgid
@@ -93,7 +93,7 @@ sys_call_table:
 /*60*/ .word sys_umask, sys_chroot, sys_newfstat, sys_nis_syscall, sys_getpagesize
        .word sys_msync, sys_vfork, sys_pread, sys_pwrite, sys_nis_syscall
 /*70*/ .word sys_nis_syscall, sys_mmap, sys_nis_syscall, sys64_munmap, sys_mprotect
-       .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_nis_syscall, sys_getgroups
+       .word sys_nis_syscall, sys_vhangup, sys_nis_syscall, sys_mincore, sys_getgroups
 /*80*/ .word sys_setgroups, sys_getpgrp, sys_nis_syscall, sys_setitimer, sys_nis_syscall
        .word sys_swapon, sys_getitimer, sys_nis_syscall, sys_sethostname, sys_nis_syscall
 /*90*/ .word sys_dup2, sys_nis_syscall, sys_fcntl, sys_select, sys_nis_syscall
@@ -164,7 +164,7 @@ sunos_sys_table:
        .word sunos_nosys, sunos_sbrk, sunos_sstk
        .word sunos_mmap, sunos_vadvise, sys_munmap
        .word sys_mprotect, sunos_madvise, sys_vhangup
-       .word sunos_nosys, sunos_mincore, sys32_getgroups16
+       .word sunos_nosys, sys32_mincore, sys32_getgroups16
        .word sys32_setgroups16, sys_getpgrp, sunos_setpgrp
        .word sys32_setitimer, sunos_nosys, sys_swapon
        .word sys32_getitimer, sys_gethostname, sys_sethostname
index 9ac4027e4b9b8394ded3e679f8c84cec39eaf4b1..b77a27236dd5977b766cfa0bfbed9555ceacce7a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: misc.c,v 1.22 2000/02/16 07:31:41 davem Exp $
+/* $Id: misc.c,v 1.23 2000/03/13 21:57:34 davem Exp $
  * misc.c: Miscelaneous syscall emulation for Solaris
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
index 17562bafd282d0184b5eb24c8789fa3e6f319b73..ca78499b1e5f69a5724f895ea9d170d4fc06db77 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: systbl.S,v 1.10 2000/01/12 02:59:26 davem Exp $
+/* $Id: systbl.S,v 1.11 2000/03/13 21:57:35 davem Exp $
  * systbl.S: System call entry point table for Solaris compatibility.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -142,7 +142,7 @@ solaris_sys_table:
        .word solaris_unimplemented     /* async                111     */
        .word solaris_unimplemented     /* priocntlsys          112     */
        .word solaris_pathconf          /* pathconf     sd      113     */
-       .word solaris_unimplemented     /* mincore      xdx     114     */
+       .word CHAIN(mincore)            /* mincore      d       114     */
        .word solaris_mmap              /* mmap         xxxxdx  115     */
        .word CHAIN(mprotect)           /* mprotect     xdx     116     */
        .word CHAIN(munmap)             /* munmap       xd      117     */
index 865681956e32bcba626b9d798effcd384ccd229f..ebf0671f44209d38516e5dec72157b68b172e581 100644 (file)
@@ -1,6 +1,5 @@
 /* auth.c -- IOCTLs for authentication -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:31:48 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -23,9 +22,9 @@
  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/auth.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index a71d6dde98acb183702a72ade533f06bb8fd14e3..1bb7bd6124a161b0560d428362178d86da165722 100644 (file)
@@ -1,8 +1,7 @@
 /* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 12:11:11 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.8 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/bufs.c,v 1.1 1999/09/25 14:37:57 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
 #define __NO_VERSION__
-#include <linux/config.h>
 #include "drmP.h"
 #include "linux/un.h"
 
index d7f8bdf2bcc469af60e84cfcae9ee01b027701a9..a8919d83d9b0489dfc0fff5d621497ed3668702f 100644 (file)
@@ -1,6 +1,5 @@
 /* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 11:32:09 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/context.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index ea08a859e3762cc72d1dfc10e0c0e23bbafa14de..0ec14ede5d5bc34e3129e775b04208d4ca2416df 100644 (file)
@@ -1,6 +1,5 @@
 /* dma.c -- DMA IOCTL and function support -*- linux-c -*-
  * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:39 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.7 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/dma.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index c26953c1d8e2b174a844496a4bafb75fa239a80a..19e5da3b7baf0043eefbcfed7ba6f543766e6930 100644 (file)
@@ -1,6 +1,5 @@
 /* drawable.c -- IOCTLs for drawables -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:03 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drawable.c,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index 320db51ebe03c7400effc70ece0bc8062f2deadc..fe0f8defec232f8174cef19f7b52e07a535e215f 100644 (file)
@@ -1,6 +1,5 @@
 /* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 17:11:19 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.46 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drm.h,v 1.1 1999/09/25 14:37:58 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  * Acknowledgements:
  * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
index fce2df7ec942078d46116e0356b8320e733ce753..3a371c23d1d41d12d6fcd666acaf01ef3f9430c3 100644 (file)
@@ -1,6 +1,5 @@
 /* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:06:49 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.58 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/drmP.h,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  * 
  */
 
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
 #endif
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
+#include <asm/spinlock.h>
+#include <linux/poll.h>
+#endif
 #include "drm.h"
 
 #define DRM_DEBUG_CODE 2         /* Include debugging code (if > 1, then
@@ -475,6 +478,7 @@ extern int       drm_fasync(int fd, struct file *filp, int on);
 extern ssize_t      drm_read(struct file *filp, char *buf, size_t count,
                              loff_t *off);
 extern int          drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int  drm_poll(struct file *filp, struct poll_table_struct *wait);
 
                                /* Mapping support (vm.c) */
 #if LINUX_VERSION_CODE < 0x020317
index 24b17356bdd451ab47e299ca5932b2aeb913fb63..a823db35638e1bdc33d7823ebbfbfc6ea153e723 100644 (file)
@@ -1,8 +1,7 @@
 /* fops.c -- File operations for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 10:26:26 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
@@ -24,8 +23,9 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.3 1999/08/20 15:36:45 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/fops.c,v 1.1 1999/09/25 14:37:59 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  *
  */
 
@@ -222,3 +222,13 @@ int drm_write_string(drm_device_t *dev, const char *s)
        wake_up_interruptible(&dev->buf_readers);
        return 0;
 }
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+       drm_file_t   *priv = filp->private_data;
+       drm_device_t *dev  = priv->dev;
+
+       poll_wait(filp, &dev->buf_readers, wait);
+       if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+       return 0;
+}
index 3b1592180e0f3c531b5db8c8a32df2f57af40ef4..1f8c0a7de90e55b09030111a70164715f790664d 100644 (file)
@@ -1,6 +1,5 @@
 /* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
  * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
- * Revised: Thu Sep 16 12:55:37 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.9 1999/09/16 16:56:18 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_dma.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index 028772f26af692dbf12e978176567a28a53c28a2..6df4440f37d0eca3a56c2a449e80c33b2e0c0708 100644 (file)
@@ -1,8 +1,7 @@
 /* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:36 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.17 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.c,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
-#include <linux/config.h>
 #include "drmP.h"
 #include "gamma_drv.h"
 EXPORT_SYMBOL(gamma_init);
@@ -52,6 +50,7 @@ static struct file_operations gamma_fops = {
        mmap:    drm_mmap,
        read:    drm_read,
        fasync:  drm_fasync,
+       poll:    drm_poll,
 };
 
 static struct miscdevice      gamma_misc = {
index 15e77dca9ee6481eea27c79f98e7ebc27ccdf376..a87655cb975369100234ac0213b5d4c8454a45af 100644 (file)
@@ -1,6 +1,5 @@
 /* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
  * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:24:27 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.4 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/gamma_drv.h,v 1.1 1999/09/25 14:38:00 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  * 
  */
 
index f416a99afe723c920319e04b4476dcc586eb6a8e..7a0115e8651907ec9eca887b6b7f6630d0ccd6de 100644 (file)
@@ -1,6 +1,5 @@
 /* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.3 1999/08/20 15:07:01 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/init.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index 886ef661c59ba0993ef1e71c2370352b4df44950..13bb606595b54f34ab7fdf70764d8bd50d8396d8 100644 (file)
@@ -1,6 +1,5 @@
 /* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
  * Created: Fri Jan  8 09:01:26 1999 by faith@precisioninsight.com
- * Revised: Fri Aug 20 09:27:02 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.3 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/ioctl.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index b84561f2e519a4ae3e7459f5cfa0eb1aae0dbde1..212ed18edecb30b8aee1584ed1eac15b7aa6085c 100644 (file)
@@ -1,6 +1,5 @@
 /* lists.c -- Buffer list handling routines -*- linux-c -*-
  * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:04:44 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.3 1999/08/20 15:07:02 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lists.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
@@ -154,7 +153,7 @@ int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
        buf->list       = DRM_LIST_FREE;
        do {
                old       = bl->next;
-               bl->next  = old;
+               buf->next = old;
                prev      = cmpxchg(&bl->next, old, buf);
                if (++count > DRM_LOOPING_LIMIT) {
                        DRM_ERROR("Looping\n");
index e8c1eff1017ccb14186de0746bdd17d4e4fcf6be..2523eb21ad54e11a45366c65ff637f1d5f765ae9 100644 (file)
@@ -1,6 +1,5 @@
 /* lock.c -- IOCTLs for locking -*- linux-c -*-
  * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:04:44 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.5 1999/08/30 13:05:00 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/lock.c,v 1.1 1999/09/25 14:38:01 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index af8d510b583c85975dadb474ca8ca92c77ed3999..a778a153911da24fce09571f7f21e42c77874890 100644 (file)
@@ -1,6 +1,5 @@
 /* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
  * Created: Thu Feb  4 14:00:34 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 10:28:18 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.4 1999/08/20 20:00:53 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/memory.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index 33a5b20e817a919d3d7669399a27acda29052311..4d5d1a964b4fb8fe93a8873b535e14080fa51993 100644 (file)
@@ -1,6 +1,5 @@
 /* proc.c -- /proc support for DRM -*- linux-c -*-
  * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
- * Revised: Fri Dec  3 09:44:16 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.4 1999/08/20 15:36:46 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/proc.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
@@ -79,26 +78,26 @@ int drm_proc_init(drm_device_t *dev)
        struct proc_dir_entry *ent;
        int                   i, j;
 
-       drm_root = create_proc_entry("graphics", S_IFDIR, NULL);
+       drm_root = create_proc_entry("dri", S_IFDIR, NULL);
        if (!drm_root) {
-               DRM_ERROR("Cannot create /proc/graphics\n");
+               DRM_ERROR("Cannot create /proc/dri\n");
                return -1;
        }
 
                                /* Instead of doing this search, we should
-                                  add some global support for /proc/graphics. */
+                                  add some global support for /proc/dri. */
        for (i = 0; i < 8; i++) {
-               sprintf(drm_slot_name, "graphics/%d", i);
+               sprintf(drm_slot_name, "dri/%d", i);
                drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
                if (!drm_dev_root) {
                        DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
-                       remove_proc_entry("graphics", NULL);
+                       remove_proc_entry("dri", NULL);
                }
                if (drm_dev_root->nlink == 2) break;
                drm_dev_root = NULL;
        }
        if (!drm_dev_root) {
-               DRM_ERROR("Cannot find slot in /proc/graphics\n");
+               DRM_ERROR("Cannot find slot in /proc/dri\n");
                return -1;
        }
 
@@ -112,7 +111,7 @@ int drm_proc_init(drm_device_t *dev)
                                remove_proc_entry(drm_proc_list[i].name,
                                                  drm_dev_root);
                        remove_proc_entry(drm_slot_name, NULL);
-                       remove_proc_entry("graphics", NULL);
+                       remove_proc_entry("dri", NULL);
                        return -1;
                }
                ent->read_proc = drm_proc_list[i].f;
@@ -135,7 +134,7 @@ int drm_proc_cleanup(void)
                        }
                        remove_proc_entry(drm_slot_name, NULL);
                }
-               remove_proc_entry("graphics", NULL);
+               remove_proc_entry("dri", NULL);
                remove_proc_entry(DRM_NAME, NULL);
        }
        drm_root = drm_dev_root = NULL;
index 0c3c541d056a7961e5b72acff5cb73492f149293..22bf59ff39fd11674682e3044731ecddb8174802 100644 (file)
@@ -1,6 +1,5 @@
 /* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
  * Created: Thu Oct  7 10:50:22 1999 by faith@precisioninsight.com
- * Revised: Sat Oct  9 23:39:56 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI$
- * $XFree86$
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index f56e2af95bc9d1fc9eb8ebf745f4c7d3fcf1213a..82b2ac9a258807d8325157215c6f0629b9fa5719 100644 (file)
@@ -1,8 +1,7 @@
 /* tdfx.c -- tdfx driver -*- linux-c -*-
  * Created: Thu Oct  7 10:38:32 1999 by faith@precisioninsight.com
- * Revised: Tue Oct 12 08:51:35 1999 by faith@precisioninsight.com
  *
- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
- * 
- * $PI$
- * $XFree86$
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  *
  */
 
-#include <linux/config.h>
 #include "drmP.h"
 #include "tdfx_drv.h"
-EXPORT_SYMBOL(tdfx_init);
-EXPORT_SYMBOL(tdfx_cleanup);
 
 #define TDFX_NAME       "tdfx"
 #define TDFX_DESC       "tdfx"
@@ -53,6 +50,7 @@ static struct file_operations tdfx_fops = {
        mmap:    drm_mmap,
        read:    drm_read,
        fasync:  drm_fasync,
+       poll:    drm_poll,
 };
 
 static struct miscdevice      tdfx_misc = {
@@ -542,6 +540,12 @@ int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
 #endif
                }
         }
+
+       if (lock.context != tdfx_res_ctx.handle) {
+               current->counter = 5;
+               current->priority = DEF_PRIORITY/4;
+       }
+
         DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
 
 #if DRM_DMA_HISTOGRAM
@@ -582,6 +586,11 @@ int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
                }
        }
        
+       if (lock.context != tdfx_res_ctx.handle) {
+               current->counter = 5;
+               current->priority = DEF_PRIORITY;
+       }
+
        return 0;
 }
 
index bdff05ee19c34908f3fbab0a1359c7a4fd0dd328..4c0c3282b24d863373d3452ba389da71a8e5a272 100644 (file)
@@ -1,6 +1,5 @@
 /* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
  * Created: Thu Oct  7 10:40:04 1999 by faith@precisioninsight.com
- * Revised: Sat Oct  9 23:38:19 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All rights reserved.
@@ -24,8 +23,9 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI$
- * $XFree86$
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
+ *    Daryll Strauss <daryll@precisioninsight.com>
  * 
  */
 
index d649a6e75d80abc22f40e2ec91a0bb8c9b5ac809..b4c4c5bbf0a26ead6ad1c0c9291fee7e1ece3e3a 100644 (file)
@@ -1,6 +1,5 @@
 /* vm.c -- Memory mapping for DRM -*- linux-c -*-
  * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
- * Revised: Mon Dec  6 16:54:35 1999 by faith@precisioninsight.com
  *
  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
  * All Rights Reserved.
@@ -24,8 +23,8 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  * DEALINGS IN THE SOFTWARE.
  * 
- * $PI: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.7 1999/08/21 02:48:34 faith Exp $
- * $XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/vm.c,v 1.1 1999/09/25 14:38:02 dawes Exp $
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@precisioninsight.com>
  *
  */
 
index 870a8b6ffc4962a7d12ff3b571bc791dd9a8a579..212525df759d3946d253d1b3e75510f424354cbe 100644 (file)
@@ -50,6 +50,7 @@ int gs_debug = 0;
 #define RELEASEIT restore_flags (flags)
 #endif
 
+#define RS_EVENT_WRITE_WAKEUP  1
 
 #ifdef DEBUG
 static void my_hd (unsigned char *addr, int len)
index 6bc1c779015e0bb1ad500510cfe0bd361588b3a0..1337213e2c6b24fc70583f7e18a0b320936248ca 100644 (file)
@@ -122,6 +122,22 @@ static struct file_operations misc_fops = {
        open:           misc_open,
 };
 
+/**
+ *     misc_register   -       register a miscellaneous device
+ *     @misc: device structure
+ *     
+ *     Register a miscellaneous device with the kernel. If the minor
+ *     number is set to MISC_DYNAMIC_MINOR a minor number is assigned
+ *     and placed in the minor field of the structure. For other cases
+ *     the minor number requested is used.
+ *
+ *     The structure passed is linked into the kernel and may not be
+ *     destroyed until it has been unregistered
+ *
+ *     A zero is returned on success and a negative errno code for
+ *     failure.
+ */
 int misc_register(struct miscdevice * misc)
 {
        static devfs_handle_t devfs_handle = NULL;
@@ -157,6 +173,16 @@ int misc_register(struct miscdevice * misc)
        return 0;
 }
 
+/**
+ *     misc_deregister - unregister a miscellaneous device
+ *     @misc: device to unregister
+ *
+ *     Unregister a miscellaneous device that was previously
+ *     successfully registered with misc_register. Success
+ *     is indicated by a zero return, a negative errno code
+ *     indicates an error.
+ */
+
 int misc_deregister(struct miscdevice * misc)
 {
        int i = misc->minor;
index 59853da1f16f3fa21e6e3fd69394f790375a71e1..4336009fc1a27a79cd94e05fabdd96e7763b4ae4 100644 (file)
@@ -537,6 +537,17 @@ static int pp_release (struct inode * inode, struct file * file)
        unsigned int minor = MINOR (inode->i_rdev);
        struct pp_struct *pp = file->private_data;
 
+       if (pp->pdev->port->ieee1284.mode != IEEE1284_MODE_COMPAT) {
+               if (!(pp->flags & PP_CLAIMED)) {
+                       parport_claim_or_block (pp->pdev);
+                       pp->flags |= PP_CLAIMED;
+               }
+               parport_negotiate (pp->pdev->port, IEEE1284_MODE_COMPAT);
+               printk (KERN_DEBUG CHRDEV
+                       "%x: negotiated back to compatibility mode because "
+                       "user-space forgot\n", minor);
+       }
+
        if (pp->flags & PP_CLAIMED) {
                parport_release (pp->pdev);
                printk (KERN_DEBUG CHRDEV "%x: released pardevice because "
index 7e92c545be475b16fc1698a8118ef2cd1d591516..2d160acfd8cac19c09161d8bc425adbb671003f4 100644 (file)
@@ -4520,9 +4520,24 @@ int __init rs_init(void)
 }
 
 /*
- * register_serial and unregister_serial allows for serial ports to be
+ * register_serial and unregister_serial allows for 16x50 serial ports to be
  * configured at run-time, to support PCMCIA modems.
  */
+/**
+ *     register_serial - configure a 16x50 serial port at runtime
+ *     @req: request structure
+ *
+ *     Configure the serial port specified by the request. If the
+ *     port exists and is in use an error is returned. If the port
+ *     is not currently in the table it is added.
+ *
+ *     The port is then probed and if neccessary the IRQ is autodetected
+ *     If this fails an error is returned.
+ *
+ *     On success the port is ready to use and the line number is returned.    
+ */
 int register_serial(struct serial_struct *req)
 {
        int i;
@@ -4580,7 +4595,7 @@ int register_serial(struct serial_struct *req)
        if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state))
                state->irq = detect_uart_irq(state);
 
-       printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
+       printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n",
              state->line + SERIAL_DEV_OFFSET,
              state->iomem_base ? "iomem" : "port",
              state->iomem_base ? (unsigned long)state->iomem_base :
@@ -4593,6 +4608,15 @@ int register_serial(struct serial_struct *req)
        return state->line + SERIAL_DEV_OFFSET;
 }
 
+/**
+ *     unregister_serial - deconfigure a 16x50 serial port
+ *     @line: line to deconfigure
+ *
+ *     The port specified is deconfigured and its resources are freed. Any
+ *     user of the port is disconnected as if carrier was dropped. Line is
+ *     the port number returned by register_serial.
+ */
+
 void unregister_serial(int line)
 {
        unsigned long flags;
index bee6a4c6c9d7ac8440d1d22972ecf4cff1d86dcf..58eb2411d82ac46c30e85fdb6cce581a7e94ee36 100644 (file)
@@ -1226,10 +1226,10 @@ static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
        drive->bios_head = page->heads;
        drive->bios_sect = page->sectors;
        lba_capacity = floppy->blocks * floppy->block_size;
-       if (capacity != lba_capacity) {
-               printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
-                       drive->name, capacity, lba_capacity);
-               capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+       if (capacity < lba_capacity) {
+               printk (KERN_NOTICE "%s: The disk reports a capacity of %d bytes, "
+                       "but the drive only handles %d\n",
+                       drive->name, lba_capacity, capacity);
                floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
        }
        return 0;
index 15850a9f70eca86e8d5ab6a44c612bfb3d2a47df..3cbf0e5e4946a4759b331da52f0f88df26a4bcf2 100644 (file)
@@ -953,4 +953,3 @@ void __exit ppp_async_cleanup(void)
 
 module_init(ppp_async_init);
 module_exit(ppp_async_cleanup);
-EXPORT_SYMBOL(ppp_async_init); /* for debugging */
index 89cbb0e9e94b95dfe94d3a297b618d036a744476..9190b5a87818c472fec29577b63c63a1a90d75e4 100644 (file)
@@ -480,6 +480,8 @@ static void shaper_cache_update(struct hh_cache *hh, struct net_device *dev,
 }
 #endif
 
+#ifdef CONFIG_INET
+
 static int shaper_neigh_setup(struct neighbour *n)
 {
        if (n->nud_state == NUD_NONE) {
@@ -499,6 +501,15 @@ static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
        return 0;
 }
 
+#else /* !(CONFIG_INET) */
+
+static int shaper_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
+{
+       return 0;
+}
+
+#endif
+
 static int shaper_attach(struct net_device *shdev, struct shaper *sh, struct net_device *dev)
 {
        sh->dev = dev;
index 7429ec38bd2f115348063259042550fdc7e89a36..8d01df21e577542fbbbc200059e83dda4b67375d 100644 (file)
@@ -1,3 +1,24 @@
+2000-03-13    <twaugh@redhat.com>
+
+       * parport_pc.c (parport_pc_init): Moved from asm/parport.h.
+
+       * Config.in: CONFIG_PARPORT_PC_SUPERIO: new option.
+
+       * parport_pc.c (show_parconfig_smsc37c669): Make __devinit.
+       (show_parconfig_winbond): Likewise.
+       (decode_winbond): Likewise.
+       (decode_smsc): Likewise.
+       (winbond_check): Likewise.
+       (winbond_check2): Likewise.
+       (smsc_check): Likewise.
+       (detect_and_report_winbond): Likewise.
+       (detect_and_report_smsc): Likewise.
+       (get_superio_dma): Likewise.
+       (get_superio_irq): Likewise.
+       (parport_pc_find_isa_ports): New function.
+       (parport_pc_find_ports): New function.
+       (init_module): Make superio a config option, not a parameter.
+
 2000-03-10    <twaugh@redhat.com>
 
        * parport_pc.c (decode_winbond): Use correct 83877ATF chip ID.
index d4222f353f1730902ab2b2c5075c2dee682522ad..1e486c6b35045dc5ad30dc98c06bc9b23f8e04f2 100644 (file)
@@ -13,6 +13,9 @@ if [ "$CONFIG_PARPORT" != "n" ]; then
    dep_tristate '  PC-style hardware' CONFIG_PARPORT_PC $CONFIG_PARPORT
    if [ "$CONFIG_PARPORT_PC" != "n" ]; then
       bool '    Use FIFO/DMA if available' CONFIG_PARPORT_PC_FIFO
+      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+         bool '    SuperIO chipset support (EXPERIMENTAL)' CONFIG_PARPORT_PC_SUPERIO
+      fi
    fi
    if [ "$CONFIG_PARPORT_PC" = "y" ]; then
       # Don't bother with this if parport_pc is a module; it only affects
index 86fc6f874088b127ff9748adf74147d0b6df1b7b..7117c847ed9a41312c28a7632c5c4fa7ab70827d 100644 (file)
@@ -60,6 +60,8 @@
 #include <linux/parport_pc.h>
 #include <asm/parport.h>
 
+#define PARPORT_PC_MAX_PORTS PARPORT_MAX
+
 /* ECR modes */
 #define ECR_SPP 00
 #define ECR_PS2 01
@@ -84,8 +86,10 @@ static struct superio_struct {       /* For Super-IO chips autodetection */
        int io;
        int irq;
        int dma;
-} superios[NR_SUPERIOS]= { {0,},};
-       
+} superios[NR_SUPERIOS] __devinitdata = { {0,},};
+
+static int user_specified __devinitdata = 0;
+
 /* frob_control, but for ECR */
 static void frob_econtrol (struct parport *pb, unsigned char m,
                           unsigned char v)
@@ -1015,9 +1019,9 @@ struct parport_operations parport_pc_ops =
        parport_ieee1284_read_byte,
 };
 
+#ifdef CONFIG_PARPORT_PC_SUPERIO
 /* Super-IO chipset detection, Winbond, SMSC */
-
-static void show_parconfig_smsc37c669(int io, int key)
+static void __devinit show_parconfig_smsc37c669(int io, int key)
 {
        int cr1,cr4,cra,cr23,cr26,cr27,i=0;
        char *modes[]={ "SPP and Bidirectional (PS/2)", 
@@ -1092,7 +1096,7 @@ static void show_parconfig_smsc37c669(int io, int key)
 }
 
 
-static void show_parconfig_winbond(int io, int key)
+static void __devinit show_parconfig_winbond(int io, int key)
 {
        int cr30,cr60,cr61,cr70,cr74,crf0,i=0;
        char *modes[]={ "Standard (SPP) and Bidirectional(PS/2)", /* 0 */
@@ -1152,7 +1156,7 @@ static void show_parconfig_winbond(int io, int key)
        }
 }
 
-static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
+static void __devinit decode_winbond(int efer, int key, int devid, int devrev, int oldid)
 {
        char *type=NULL;
        int id,progif=2;
@@ -1188,7 +1192,7 @@ static void decode_winbond(int efer, int key, int devid, int devrev, int oldid)
        return;
 }
 
-static void decode_smsc(int efer, int key, int devid, int devrev)
+static void __devinit decode_smsc(int efer, int key, int devid, int devrev)
 {
         char *type=NULL;
        void (*func)(int io, int key);
@@ -1219,7 +1223,7 @@ static void decode_smsc(int efer, int key, int devid, int devrev)
 }
 
 
-static void winbond_check(int io, int key)
+static void __devinit winbond_check(int io, int key)
 {
        int devid,devrev,oldid;
 
@@ -1237,7 +1241,7 @@ static void winbond_check(int io, int key)
        decode_winbond(io,key,devid,devrev,oldid);
 }
 
-static void winbond_check2(int io,int key)
+static void __devinit winbond_check2(int io,int key)
 {
         int devid,devrev,oldid;
 
@@ -1254,7 +1258,7 @@ static void winbond_check2(int io,int key)
         decode_winbond(io,key,devid,devrev,oldid);
 }
 
-static void smsc_check(int io, int key)
+static void __devinit smsc_check(int io, int key)
 {
         int devid,devrev;
 
@@ -1271,7 +1275,7 @@ static void smsc_check(int io, int key)
 }
 
 
-static void detect_and_report_winbond (void)
+static void __devinit detect_and_report_winbond (void)
 { 
        printk("Winbond Super-IO detection, now testing ports 3F0,370,250,4E,2E ...\n");
 
@@ -1284,7 +1288,7 @@ static void detect_and_report_winbond (void)
        winbond_check2(0x250,0x89);
 }
 
-static void detect_and_report_smsc (void)
+static void __devinit detect_and_report_smsc (void)
 {
        printk("SMSC Super-IO detection, now testing Ports 2F0, 370 ...\n");
        smsc_check(0x3f0,0x55);
@@ -1292,8 +1296,9 @@ static void detect_and_report_smsc (void)
        smsc_check(0x3f0,0x44);
        smsc_check(0x370,0x44);
 }
+#endif /* CONFIG_PARPORT_PC_SUPERIO */
 
-static int get_superio_dma (struct parport *p)
+static int __devinit get_superio_dma (struct parport *p)
 {
        int i=0;
        while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -1303,7 +1308,7 @@ static int get_superio_dma (struct parport *p)
        return PARPORT_DMA_NONE;
 }
 
-static int get_superio_irq (struct parport *p)
+static int __devinit get_superio_irq (struct parport *p)
 {
        int i=0;
         while( (superios[i].io != p->base) && (i<NR_SUPERIOS))
@@ -2330,7 +2335,7 @@ static struct pci_driver parport_pc_pci_driver = {
        probe:          parport_pc_pci_probe,
 };
 
-static int __devinit parport_pc_init_superio (void)
+static int __init parport_pc_init_superio (void)
 {
 #ifdef CONFIG_PCI
        const struct pci_device_id *id;
@@ -2348,6 +2353,74 @@ static int __devinit parport_pc_init_superio (void)
        return 0; /* zero devices found */
 }
 
+/* This is called by parport_pc_find_nonpci_ports (in asm/parport.h) */
+static int __init __attribute__((unused))
+parport_pc_find_isa_ports (int autoirq, int autodma)
+{
+       int count = 0;
+
+       if (parport_pc_probe_port(0x3bc, 0x7bc, autoirq, autodma, NULL))
+               count++;
+       if (parport_pc_probe_port(0x378, 0x778, autoirq, autodma, NULL))
+               count++;
+       if (parport_pc_probe_port(0x278, 0x678, autoirq, autodma, NULL))
+               count++;
+
+       return count;
+}
+
+/* This function is called by parport_pc_init if the user didn't
+ * specify any ports to probe.  Its job is to find some ports.  Order
+ * is important here -- we want ISA ports to be registered first,
+ * followed by PCI cards (for least surprise), but before that we want
+ * to do chipset-specific tests for some onboard ports that we know
+ * about.
+ *
+ * autoirq is PARPORT_IRQ_NONE, PARPORT_IRQ_AUTO, or PARPORT_IRQ_PROBEONLY
+ * autodma is PARPORT_DMA_NONE or PARPORT_DMA_AUTO
+ */
+static int __init parport_pc_find_ports (int autoirq, int autodma)
+{
+       int count = 0, r;
+
+#ifdef CONFIG_PARPORT_PC_SUPERIO
+       detect_and_report_winbond ();
+       detect_and_report_smsc ();
+#endif
+
+       /* Onboard SuperIO chipsets that show themselves on the PCI bus. */
+       count += parport_pc_init_superio ();
+
+       /* ISA ports and whatever (see asm/parport.h). */
+       count += parport_pc_find_nonpci_ports (autoirq, autodma);
+
+       r = pci_register_driver (&parport_pc_pci_driver);
+       if (r > 0)
+               count += r;
+
+       return count;
+}
+
+int __init parport_pc_init (int *io, int *io_hi, int *irq, int *dma)
+{
+       int count = 0, i = 0;
+
+       if (io && *io) {
+               /* Only probe the ports we were given. */
+               user_specified = 1;
+               do {
+                       if (!*io_hi) *io_hi = 0x400 + *io;
+                       if (parport_pc_probe_port(*(io++), *(io_hi++),
+                                                 *(irq++), *(dma++), NULL))
+                               count++;
+               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
+       } else {
+               count += parport_pc_find_ports (irq[0], dma[0]);
+       }
+
+       return count;
+}
+
 /* Exported symbols. */
 #ifdef CONFIG_PARPORT_PC_PCMCIA
 
@@ -2367,7 +2440,6 @@ static int dmaval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PAR
 static int irqval[PARPORT_PC_MAX_PORTS] = { [0 ... PARPORT_PC_MAX_PORTS-1] = PARPORT_IRQ_PROBEONLY };
 static const char *irq[PARPORT_PC_MAX_PORTS] = { NULL, };
 static const char *dma[PARPORT_PC_MAX_PORTS] = { NULL, };
-static int superio = 0;
 
 MODULE_AUTHOR("Phil Blundell, Tim Waugh, others");
 MODULE_DESCRIPTION("PC-style parallel port driver");
@@ -2379,18 +2451,12 @@ MODULE_PARM_DESC(irq, "IRQ line");
 MODULE_PARM(irq, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
 MODULE_PARM_DESC(dma, "DMA channel");
 MODULE_PARM(dma, "1-" __MODULE_STRING(PARPORT_PC_MAX_PORTS) "s");
-MODULE_PARM_DESC(superio, "Enable Super-IO chipset probe");
-MODULE_PARM(superio, "i");
 
 int init_module(void)
 {      
        /* Work out how many ports we have, then get parport_share to parse
           the irq values. */
-       unsigned int i, n;
-       if (superio) {
-               detect_and_report_winbond ();
-               detect_and_report_smsc ();
-       }
+       unsigned int i;
        for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
        if (i) {
                if (parport_parse_irqs(i, irq, irqval)) return 1;
@@ -2415,12 +2481,7 @@ int init_module(void)
                        }
        }
 
-       n = parport_pc_init_superio ();
-       n += parport_pc_init (io, io_hi, irqval, dmaval);
-       i = pci_register_driver (&parport_pc_pci_driver);
-
-       if (i > 0) n += i;
-       return !n;
+       return !parport_pc_init (io, io_hi, irqval, dmaval);
 }
 
 void cleanup_module(void)
index 849ce76c9fac89f7093005089152a78a8d317245..5a74bc2df9a5a93fb02957bb20ecc9688bdbfa5b 100644 (file)
@@ -158,16 +158,20 @@ void print_status (int status) {
 }
 
 #if (CONSTANTS & CONST_XSENSE)
-#define D 0x001  /* DIRECT ACCESS DEVICE (disk) */
-#define T 0x002  /* SEQUENTIAL ACCESS DEVICE (tape) */
-#define L 0x004  /* PRINTER DEVICE */
-#define P 0x008  /* PROCESSOR DEVICE */
-#define W 0x010  /* WRITE ONCE READ MULTIPLE DEVICE */
-#define R 0x020  /* READ ONLY (CD-ROM) DEVICE */
-#define S 0x040  /* SCANNER DEVICE */
-#define O 0x080  /* OPTICAL MEMORY DEVICE */
-#define M 0x100  /* MEDIA CHANGER DEVICE */
-#define C 0x200  /* COMMUNICATION DEVICE */
+#define D 0x0001  /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x0002  /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x0004  /* PRINTER DEVICE */
+#define P 0x0008  /* PROCESSOR DEVICE */
+#define W 0x0010  /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x0020  /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x0040  /* SCANNER DEVICE */
+#define O 0x0080  /* OPTICAL MEMORY DEVICE */
+#define M 0x0100  /* MEDIA CHANGER DEVICE */
+#define C 0x0200  /* COMMUNICATION DEVICE */
+#define A 0x0400  /* ARRAY STORAGE */
+#define E 0x0800  /* ENCLOSURE SERVICES DEVICE */
+#define B 0x1000  /* SIMPLIFIED DIRECT ACCESS DEVICE */
+#define K 0x2000  /* OPTICAL CARD READER/WRITER DEVICE */
 
 struct error_info{
     unsigned char code1, code2;
@@ -192,131 +196,213 @@ static struct error_info2 additional2[] =
 
 static struct error_info additional[] =
 {
+  {0x00,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"No additional sense information"},
   {0x00,0x01,T,"Filemark detected"},
   {0x00,0x02,T|S,"End-of-partition/medium detected"},
   {0x00,0x03,T,"Setmark detected"},
   {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
-  {0x00,0x05,T|S,"End-of-data detected"},
-  {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+  {0x00,0x05,T|L|S,"End-of-data detected"},
+  {0x00,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"I/O process terminated"},
   {0x00,0x11,R,"Audio play operation in progress"},
   {0x00,0x12,R,"Audio play operation paused"},
   {0x00,0x13,R,"Audio play operation successfully completed"},
   {0x00,0x14,R,"Audio play operation stopped due to error"},
   {0x00,0x15,R,"No current audio status to return"},
-  {0x01,0x00,D|W|O,"No index/sector signal"},
-  {0x02,0x00,D|W|R|O|M,"No seek complete"},
-  {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+  {0x00,0x16,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Operation in progress"},
+  {0x00,0x17,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning requested"},
+  {0x01,0x00,D|W|O|B|K,"No index/sector signal"},
+  {0x02,0x00,D|W|R|O|M|B|K,"No seek complete"},
+  {0x03,0x00,D|T|L|W|S|O|B|K,"Peripheral device write fault"},
   {0x03,0x01,T,"No write current"},
   {0x03,0x02,T,"Excessive write errors"},
-  {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, cause not reportable"},
-  {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit is in process of becoming ready"},
-  {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, initializing command required"},
-  {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
-     "Logical unit not ready, manual intervention required"},
-  {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
-  {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
-  {0x06,0x00,D|W|R|O|M,"No reference position found"},
-  {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
-  {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
-  {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
-  {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
-  {0x09,0x00,D|T|W|R|O,"Track following error"},
-  {0x09,0x01,W|R|O,"Tracking servo failure"},
-  {0x09,0x02,W|R|O,"Focus servo failure"},
+  {0x04,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,cause not reportable"},
+  {0x04,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit is in process of becoming ready"},
+  {0x04,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,initializing cmd. required"},
+  {0x04,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,manual intervention required"},
+  {0x04,0x04,D|T|L|R|O|B,"Logical unit not ready,format in progress"},
+  {0x04,0x05,D|T|W|O|M|C|A|B|K,"Logical unit not ready,rebuild in progress"},
+  {0x04,0x06,D|T|W|O|M|C|A|B|K,"Logical unit not ready,recalculation in progress"},
+  {0x04,0x07,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,operation in progress"},
+  {0x04,0x08,R,"Logical unit not ready,long write in progress"},
+  {0x04,0x09,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not ready,self-test in progress"},
+  {0x05,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit does not respond to selection"},
+  {0x06,0x00,D|W|R|O|M|B|K,"No reference position found"},
+  {0x07,0x00,D|T|L|W|R|S|O|M|B|K,"Multiple peripheral devices selected"},
+  {0x08,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication failure"},
+  {0x08,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication time-out"},
+  {0x08,0x02,D|T|L|W|R|S|O|M|C|A|E|B|K,"Logical unit communication parity error"},
+  {0x08,0x03,D|T|R|O|M|B|K,"Logical unit communication CRC error (Ultra-DMA/32)"},
+  {0x08,0x04,D|T|L|P|W|R|S|O|C|K,"Unreachable copy target"},
+  {0x09,0x00,D|T|W|R|O|B,"Track following error"},
+  {0x09,0x01,W|R|O|K,"Tracking servo failure"},
+  {0x09,0x02,W|R|O|K,"Focus servo failure"},
   {0x09,0x03,W|R|O,"Spindle servo failure"},
-  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
-  {0x0C,0x00,T|S,"Write error"},
-  {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
-  {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
-  {0x10,0x00,D|W|O,"Id crc or ecc error"},
-  {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
-  {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
-  {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
-  {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
-  {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
-  {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
-  {0x11,0x06,W|R|O,"Circ unrecovered error"},
-  {0x11,0x07,W|O,"Data resynchronization error"},
+  {0x09,0x04,D|T|W|R|O|B,"Head select fault"},
+  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Error log overflow"},
+  {0x0B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning"},
+  {0x0B,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - specified temperature exceeded"},
+  {0x0B,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Warning - enclosure degraded"},
+  {0x0C,0x00,T|R|S,"Write error"},
+  {0x0C,0x01,K,"Write error - recovered with auto reallocation"},
+  {0x0C,0x02,D|W|O|B|K,"Write error - auto reallocation failed"},
+  {0x0C,0x03,D|W|O|B|K,"Write error - recommend reassignment"},
+  {0x0C,0x04,D|T|W|O|B,"Compression check miscompare error"},
+  {0x0C,0x05,D|T|W|O|B,"Data expansion occurred during compression"},
+  {0x0C,0x06,D|T|W|O|B,"Block not compressible"},
+  {0x0C,0x07,R,"Write error - recovery needed"},
+  {0x0C,0x08,R,"Write error - recovery failed"},
+  {0x0C,0x09,R,"Write error - loss of streaming"},
+  {0x0C,0x0A,R,"Write error - padding blocks added"},
+  {0x10,0x00,D|W|O|B|K,"Id CRC or ECC error"},
+  {0x11,0x00,D|T|W|R|S|O|B|K,"Unrecovered read error"},
+  {0x11,0x01,D|T|W|R|S|O|B|K,"Read retries exhausted"},
+  {0x11,0x02,D|T|W|R|S|O|B|K,"Error too long to correct"},
+  {0x11,0x03,D|T|W|S|O|B|K,"Multiple read errors"},
+  {0x11,0x04,D|W|O|B|K,"Unrecovered read error - auto reallocate failed"},
+  {0x11,0x05,W|R|O|B,"L-EC uncorrectable error"},
+  {0x11,0x06,W|R|O|B,"CIRC unrecovered error"},
+  {0x11,0x07,W|O|B,"Data re-synchronization error"},
   {0x11,0x08,T,"Incomplete block read"},
   {0x11,0x09,T,"No gap found"},
-  {0x11,0x0A,D|T|O,"Miscorrected error"},
-  {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
-  {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
-  {0x12,0x00,D|W|O,"Address mark not found for id field"},
-  {0x13,0x00,D|W|O,"Address mark not found for data field"},
-  {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
-  {0x14,0x01,D|T|W|R|O,"Record not found"},
+  {0x11,0x0A,D|T|O|B|K,"Miscorrected error"},
+  {0x11,0x0B,D|W|O|B|K,"Unrecovered read error - recommend reassignment"},
+  {0x11,0x0C,D|W|O|B|K,"Unrecovered read error - recommend rewrite the data"},
+  {0x11,0x0D,D|T|W|R|O|B,"De-compression CRC error"},
+  {0x11,0x0E,D|T|W|R|O|B,"Cannot decompress using declared algorithm"},
+  {0x11,0x0F,R,"Error reading UPC/EAN number"},
+  {0x11,0x10,R,"Error reading ISRC number"},
+  {0x11,0x11,R,"Read error - loss of streaming"},
+  {0x12,0x00,D|W|O|B|K,"Address mark not found for id field"},
+  {0x13,0x00,D|W|O|B|K,"Address mark not found for data field"},
+  {0x14,0x00,D|T|L|W|R|S|O|B|K,"Recorded entity not found"},
+  {0x14,0x01,D|T|W|R|O|B|K,"Record not found"},
   {0x14,0x02,T,"Filemark or setmark not found"},
   {0x14,0x03,T,"End-of-data not found"},
   {0x14,0x04,T,"Block sequence error"},
-  {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
-  {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
-  {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
-  {0x16,0x00,D|W|O,"Data synchronization mark error"},
-  {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
-  {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
-  {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
-  {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
-  {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
-  {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
-  {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
-  {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
-  {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
-  {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
-  {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
-  {0x18,0x03,R,"Recovered data with circ"},
-  {0x18,0x04,R,"Recovered data with lec"},
-  {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
-  {0x19,0x00,D|O,"Defect list error"},
-  {0x19,0x01,D|O,"Defect list not available"},
-  {0x19,0x02,D|O,"Defect list error in primary list"},
-  {0x19,0x03,D|O,"Defect list error in grown list"},
-  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
-  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
-  {0x1C,0x00,D|O,"Defect list not found"},
-  {0x1C,0x01,D|O,"Primary defect list not found"},
-  {0x1C,0x02,D|O,"Grown defect list not found"},
-  {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
-  {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
-  {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
-  {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
-  {0x21,0x01,M,"Invalid element address"},
-  {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
-  {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
-  {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
-  {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
-  {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
-  {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
-  {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
-  {0x27,0x00,D|T|W|O,"Write protected"},
-  {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
-  {0x28,0x01,M,"Import or export element accessed"},
-  {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
-  {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
-  {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
-  {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
-  {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
-  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+  {0x14,0x05,D|T|W|O|B|K,"Record not found - recommend reassignment"},
+  {0x14,0x06,D|T|W|O|B|K,"Record not found - data auto-reallocated"},
+  {0x15,0x00,D|T|L|W|R|S|O|M|B|K,"Random positioning error"},
+  {0x15,0x01,D|T|L|W|R|S|O|M|B|K,"Mechanical positioning error"},
+  {0x15,0x02,D|T|W|R|O|B|K,"Positioning error detected by read of medium"},
+  {0x16,0x00,D|W|O|B|K,"Data synchronization mark error"},
+  {0x16,0x01,D|W|O|B|K,"Data sync error - data rewritten"},
+  {0x16,0x02,D|W|O|B|K,"Data sync error - recommend rewrite"},
+  {0x16,0x03,D|W|O|B|K,"Data sync error - data auto-reallocated"},
+  {0x16,0x04,D|W|O|B|K,"Data sync error - recommend reassignment"},
+  {0x17,0x00,D|T|W|R|S|O|B|K,"Recovered data with no error correction applied"},
+  {0x17,0x01,D|T|W|R|S|O|B|K,"Recovered data with retries"},
+  {0x17,0x02,D|T|W|R|O|B|K,"Recovered data with positive head offset"},
+  {0x17,0x03,D|T|W|R|O|B|K,"Recovered data with negative head offset"},
+  {0x17,0x04,W|R|O|B,"Recovered data with retries and/or circ applied"},
+  {0x17,0x05,D|W|R|O|B|K,"Recovered data using previous sector id"},
+  {0x17,0x06,D|W|O|B|K,"Recovered data without ecc - data auto-reallocated"},
+  {0x17,0x07,D|W|R|O|B|K,"Recovered data without ecc - recommend reassignment"},
+  {0x17,0x08,D|W|R|O|B|K,"Recovered data without ecc - recommend rewrite"},
+  {0x17,0x09,D|W|R|O|B|K,"Recovered data without ecc - data rewritten"},
+  {0x18,0x00,D|T|W|R|O|B|K,"Recovered data with error correction applied"},
+  {0x18,0x01,D|W|R|O|B|K,"Recovered data with error corr. & retries applied"},
+  {0x18,0x02,D|W|R|O|B|K,"Recovered data - data auto-reallocated"},
+  {0x18,0x03,R,"Recovered data with CIRC"},
+  {0x18,0x04,R,"Recovered data with L-EC"},
+  {0x18,0x05,D|W|R|O|B|K,"Recovered data - recommend reassignment"},
+  {0x18,0x06,D|W|R|O|B|K,"Recovered data - recommend rewrite"},
+  {0x18,0x07,D|W|O|B|K,"Recovered data with ecc - data rewritten"},
+  {0x19,0x00,D|O|K,"Defect list error"},
+  {0x19,0x01,D|O|K,"Defect list not available"},
+  {0x19,0x02,D|O|K,"Defect list error in primary list"},
+  {0x19,0x03,D|O|K,"Defect list error in grown list"},
+  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter list length error"},
+  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Synchronous data transfer error"},
+  {0x1C,0x00,D|O|B|K,"Defect list not found"},
+  {0x1C,0x01,D|O|B|K,"Primary defect list not found"},
+  {0x1C,0x02,D|O|B|K,"Grown defect list not found"},
+  {0x1D,0x00,D|T|W|R|O|B|K,"Miscompare during verify operation"},
+  {0x1E,0x00,D|W|O|B|K,"Recovered id with ecc correction"},
+  {0x1F,0x00,D|O|K,"Partial defect list transfer"},
+  {0x20,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid command operation code"},
+  {0x21,0x00,D|T|W|R|O|M|B|K,"Logical block address out of range"},
+  {0x21,0x01,D|T|W|R|O|M|B|K,"Invalid element address"},
+  {0x22,0x00,D,"Illegal function (use 20 00,24 00,or 26 00)"},
+  {0x24,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in cdb"},
+  {0x24,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"CDB decryption error"},
+  {0x25,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit not supported"},
+  {0x26,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid field in parameter list"},
+  {0x26,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter not supported"},
+  {0x26,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Parameter value invalid"},
+  {0x26,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Threshold parameters not supported"},
+  {0x26,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid release of persistent reservation"},
+  {0x26,0x05,D|T|L|P|W|R|S|O|M|C|A|B|K,"Data decryption error"},
+  {0x26,0x06,D|T|L|P|W|R|S|O|C|K,"Too many target descriptors"},
+  {0x26,0x07,D|T|L|P|W|R|S|O|C|K,"Unsupported target descriptor type code"},
+  {0x26,0x08,D|T|L|P|W|R|S|O|C|K,"Too many segment descriptors"},
+  {0x26,0x09,D|T|L|P|W|R|S|O|C|K,"Unsupported segment descriptor type code"},
+  {0x26,0x0A,D|T|L|P|W|R|S|O|C|K,"Unexpected inexact segment"},
+  {0x26,0x0B,D|T|L|P|W|R|S|O|C|K,"Inline data length exceeded"},
+  {0x26,0x0C,D|T|L|P|W|R|S|O|C|K,"Invalid operation for copy source or destination"},
+  {0x26,0x0D,D|T|L|P|W|R|S|O|C|K,"Copy segment granularity violation"},
+  {0x27,0x00,D|T|W|R|O|B|K,"Write protected"},
+  {0x27,0x01,D|T|W|R|O|B|K,"Hardware write protected"},
+  {0x27,0x02,D|T|W|R|O|B|K,"Logical unit software write protected"},
+  {0x27,0x03,T|R,"Associated write protect"},
+  {0x27,0x04,T|R,"Persistent write protect"},
+  {0x27,0x05,T|R,"Permanent write protect"},
+  {0x28,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Not ready to ready change,medium may have changed"},
+  {0x28,0x01,D|T|W|R|O|M|B,"Import or export element accessed"},
+  {0x29,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on,reset,or bus device reset occurred"},
+  {0x29,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Power on occurred"},
+  {0x29,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi bus reset occurred"},
+  {0x29,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Bus device reset function occurred"},
+  {0x29,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Device internal reset"},
+  {0x29,0x05,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to single-ended"},
+  {0x29,0x06,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Transceiver mode changed to lvd"},
+  {0x2A,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Parameters changed"},
+  {0x2A,0x01,D|T|L|W|R|S|O|M|C|A|E|B|K,"Mode parameters changed"},
+  {0x2A,0x02,D|T|L|W|R|S|O|M|C|A|E|K,"Log parameters changed"},
+  {0x2A,0x03,D|T|L|P|W|R|S|O|M|C|A|E|K,"Reservations preempted"},
+  {0x2A,0x04,D|T|L|P|W|R|S|O|M|C|A|E,"Reservations released"},
+  {0x2A,0x05,D|T|L|P|W|R|S|O|M|C|A|E,"Registrations preempted"},
+  {0x2B,0x00,D|T|L|P|W|R|S|O|C|K,"Copy cannot execute since host cannot disconnect"},
+  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command sequence error"},
   {0x2C,0x01,S,"Too many windows specified"},
   {0x2C,0x02,S,"Invalid combination of windows specified"},
+  {0x2C,0x03,R,"Current program area is not empty"},
+  {0x2C,0x04,R,"Current program area is empty"},
+  {0x2C,0x05,B,"Illegal power condition request"},
   {0x2D,0x00,T,"Overwrite error on update in place"},
-  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
-  {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
-  {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
-  {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
-  {0x30,0x03,D|T,"Cleaning cartridge installed"},
-  {0x31,0x00,D|T|W|O,"Medium format corrupted"},
-  {0x31,0x01,D|L|O,"Format command failed"},
-  {0x32,0x00,D|W|O,"No defect spare location available"},
-  {0x32,0x01,D|W|O,"Defect list update failure"},
+  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Commands cleared by another initiator"},
+  {0x30,0x00,D|T|W|R|O|M|B|K,"Incompatible medium installed"},
+  {0x30,0x01,D|T|W|R|O|B|K,"Cannot read medium - unknown format"},
+  {0x30,0x02,D|T|W|R|O|B|K,"Cannot read medium - incompatible format"},
+  {0x30,0x03,D|T|R|K,"Cleaning cartridge installed"},
+  {0x30,0x04,D|T|W|R|O|B|K,"Cannot write medium - unknown format"},
+  {0x30,0x05,D|T|W|R|O|B|K,"Cannot write medium - incompatible format"},
+  {0x30,0x06,D|T|W|R|O|B,"Cannot format medium - incompatible medium"},
+  {0x30,0x07,D|T|L|W|R|S|O|M|A|E|B|K,"Cleaning failure"},
+  {0x30,0x08,R,"Cannot write - application code mismatch"},
+  {0x30,0x09,R,"Current session not fixated for append"},
+  {0x31,0x00,D|T|W|R|O|B|K,"Medium format corrupted"},
+  {0x31,0x01,D|L|R|O|B,"Format command failed"},
+  {0x32,0x00,D|W|O|B|K,"No defect spare location available"},
+  {0x32,0x01,D|W|O|B|K,"Defect list update failure"},
   {0x33,0x00,T,"Tape length error"},
-  {0x36,0x00,L,"Ribbon, ink, or toner failure"},
-  {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
-  {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
-  {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+  {0x34,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure failure"},
+  {0x35,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services failure"},
+  {0x35,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Unsupported enclosure function"},
+  {0x35,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services unavailable"},
+  {0x35,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer failure"},
+  {0x35,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Enclosure services transfer refused"},
+  {0x36,0x00,L,"Ribbon,ink,or toner failure"},
+  {0x37,0x00,D|T|L|W|R|S|O|M|C|A|E|B|K,"Rounded parameter"},
+  {0x38,0x00,B,"Event status notification"},
+  {0x38,0x02,B,"Esn - power management class event"},
+  {0x38,0x04,B,"Esn - media class event"},
+  {0x38,0x06,B,"Esn - device busy class event"},
+  {0x39,0x00,D|T|L|W|R|S|O|M|C|A|E|K,"Saving parameters not supported"},
+  {0x3A,0x00,D|T|L|W|R|S|O|M|B|K,"Medium not present"},
+  {0x3A,0x01,D|T|W|R|O|M|B|K,"Medium not present - tray closed"},
+  {0x3A,0x02,D|T|W|R|O|M|B|K,"Medium not present - tray open"},
+  {0x3A,0x03,D|T|W|R|O|M|B,"Medium not present - loadable"},
+  {0x3A,0x04,D|T|W|R|O|M|B,"Medium not present - medium auxiliary memory accessible"},
   {0x3B,0x00,T|L,"Sequential positioning error"},
   {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
   {0x3B,0x02,T,"Tape position error at end-of-medium"},
@@ -329,57 +415,244 @@ static struct error_info additional[] =
   {0x3B,0x09,S,"Read past end of medium"},
   {0x3B,0x0A,S,"Read past beginning of medium"},
   {0x3B,0x0B,S,"Position past end of medium"},
-  {0x3B,0x0C,S,"Position past beginning of medium"},
-  {0x3B,0x0D,M,"Medium destination element full"},
-  {0x3B,0x0E,M,"Medium source element empty"},
-  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
-  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
-  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
-  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
-  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
-  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
-  {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
-  {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
-  {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
-  {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
-  {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
-  {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
-  {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
-  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
-  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
-  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
-  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+  {0x3B,0x0C,T|S,"Position past beginning of medium"},
+  {0x3B,0x0D,D|T|W|R|O|M|B|K,"Medium destination element full"},
+  {0x3B,0x0E,D|T|W|R|O|M|B|K,"Medium source element empty"},
+  {0x3B,0x0F,R,"End of medium reached"},
+  {0x3B,0x11,D|T|W|R|O|M|B|K,"Medium magazine not accessible"},
+  {0x3B,0x12,D|T|W|R|O|M|B|K,"Medium magazine removed"},
+  {0x3B,0x13,D|T|W|R|O|M|B|K,"Medium magazine inserted"},
+  {0x3B,0x14,D|T|W|R|O|M|B|K,"Medium magazine locked"},
+  {0x3B,0x15,D|T|W|R|O|M|B|K,"Medium magazine unlocked"},
+  {0x3B,0x16,R,"Mechanical positioning or changer error"},
+  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|K,"Invalid bits in identify message"},
+  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit has not self-configured yet"},
+  {0x3E,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failure"},
+  {0x3E,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Timeout on logical unit"},
+  {0x3E,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-test"},
+  {0x3E,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit unable to update self-test log"},
+  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Target operating conditions have changed"},
+  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Microcode has been changed"},
+  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C|B|K,"Changed operating definition"},
+  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Inquiry data has changed"},
+  {0x3F,0x04,D|T|W|R|O|M|C|A|E|B|K,"Component device attached"},
+  {0x3F,0x05,D|T|W|R|O|M|C|A|E|B|K,"Device identifier changed"},
+  {0x3F,0x06,D|T|W|R|O|M|C|A|E|B,"Redundancy group created or modified"},
+  {0x3F,0x07,D|T|W|R|O|M|C|A|E|B,"Redundancy group deleted"},
+  {0x3F,0x08,D|T|W|R|O|M|C|A|E|B,"Spare created or modified"},
+  {0x3F,0x09,D|T|W|R|O|M|C|A|E|B,"Spare deleted"},
+  {0x3F,0x0A,D|T|W|R|O|M|C|A|E|B|K,"Volume set created or modified"},
+  {0x3F,0x0B,D|T|W|R|O|M|C|A|E|B|K,"Volume set deleted"},
+  {0x3F,0x0C,D|T|W|R|O|M|C|A|E|B|K,"Volume set deassigned"},
+  {0x3F,0x0D,D|T|W|R|O|M|C|A|E|B|K,"Volume set reassigned"},
+  {0x3F,0x0E,D|T|L|P|W|R|S|O|M|C|A|E,"Reported luns data has changed"},
+  {0x3F,0x10,D|T|W|R|O|M|B,"Medium loadable"},
+  {0x3F,0x11,D|T|W|R|O|M|B,"Medium auxiliary memory accessible"},
+  {0x40,0x00,D,"Ram failure (should use 40 nn)"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x40,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Diagnostic failure on component nn (80h-ffh)"},
+  {0x41,0x00,D,"Data path failure (should use 40 nn)"},
+  {0x42,0x00,D,"Power-on or self-test failure (should use 40 nn)"},
+  {0x43,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Message error"},
+  {0x44,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Internal target failure"},
+  {0x45,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Select or reselect failure"},
+  {0x46,0x00,D|T|L|P|W|R|S|O|M|C|B|K,"Unsuccessful soft reset"},
+  {0x47,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error"},
+  {0x47,0x01,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase CRC error detected"},
+  {0x47,0x02,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Scsi parity error detected during st data phase"},
+  {0x47,0x03,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Information unit CRC error detected"},
+  {0x47,0x04,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Asynchronous information protection error detected"},
+  {0x48,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Initiator detected error message received"},
+  {0x49,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Invalid message error"},
+  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Command phase error"},
+  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Data phase error"},
+  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Logical unit failed self-configuration"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x4D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Tagged overlapped commands (nn = queue tag)"},
+  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Overlapped commands attempted"},
   {0x50,0x00,T,"Write append error"},
   {0x50,0x01,T,"Write append position error"},
   {0x50,0x02,T,"Position error related to timing"},
-  {0x51,0x00,T|O,"Erase failure"},
+  {0x51,0x00,T|R|O,"Erase failure"},
   {0x52,0x00,T,"Cartridge fault"},
-  {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+  {0x53,0x00,D|T|L|W|R|S|O|M|B|K,"Media load or eject failed"},
   {0x53,0x01,T,"Unload tape failure"},
-  {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+  {0x53,0x02,D|T|W|R|O|M|B|K,"Medium removal prevented"},
   {0x54,0x00,P,"Scsi to host system interface failure"},
   {0x55,0x00,P,"System resource failure"},
+  {0x55,0x01,D|O|B|K,"System buffer full"},
+  {0x55,0x02,D|T|L|P|W|R|S|O|M|A|E|K,"Insufficient reservation resources"},
+  {0x55,0x03,D|T|L|P|W|R|S|O|M|C|A|E,"Insufficient resources"},
+  {0x55,0x04,D|T|L|P|W|R|S|O|M|A|E,"Insufficient registration resources"},
   {0x57,0x00,R,"Unable to recover table-of-contents"},
   {0x58,0x00,O,"Generation does not exist"},
   {0x59,0x00,O,"Updated block read"},
-  {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
-  {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
-  {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
-  {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
-  {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
-  {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
-  {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
-  {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+  {0x5A,0x00,D|T|L|P|W|R|S|O|M|B|K,"Operator request or state change input"},
+  {0x5A,0x01,D|T|W|R|O|M|B|K,"Operator medium removal request"},
+  {0x5A,0x02,D|T|W|R|O|A|B|K,"Operator selected write protect"},
+  {0x5A,0x03,D|T|W|R|O|A|B|K,"Operator selected write permit"},
+  {0x5B,0x00,D|T|L|P|W|R|S|O|M|K,"Log exception"},
+  {0x5B,0x01,D|T|L|P|W|R|S|O|M|K,"Threshold condition met"},
+  {0x5B,0x02,D|T|L|P|W|R|S|O|M|K,"Log counter at maximum"},
+  {0x5B,0x03,D|T|L|P|W|R|S|O|M|K,"Log list codes exhausted"},
   {0x5C,0x00,D|O,"Rpl status change"},
   {0x5C,0x01,D|O,"Spindles synchronized"},
   {0x5C,0x02,D|O,"Spindles not synchronized"},
+  {0x5D,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded"},
+  {0x5D,0x01,R|B,"Media failure prediction threshold exceeded"},
+  {0x5D,0x02,R,"Logical unit failure prediction threshold exceeded"},
+  {0x5D,0x10,D|B,"Hardware impending failure general hard drive failure"},
+  {0x5D,0x11,D|B,"Hardware impending failure drive error rate too high"},
+  {0x5D,0x12,D|B,"Hardware impending failure data error rate too high"},
+  {0x5D,0x13,D|B,"Hardware impending failure seek error rate too high"},
+  {0x5D,0x14,D|B,"Hardware impending failure too many block reassigns"},
+  {0x5D,0x15,D|B,"Hardware impending failure access times too high"},
+  {0x5D,0x16,D|B,"Hardware impending failure start unit times too high"},
+  {0x5D,0x17,D|B,"Hardware impending failure channel parametrics"},
+  {0x5D,0x18,D|B,"Hardware impending failure controller detected"},
+  {0x5D,0x19,D|B,"Hardware impending failure throughput performance"},
+  {0x5D,0x1A,D|B,"Hardware impending failure seek time performance"},
+  {0x5D,0x1B,D|B,"Hardware impending failure spin-up retry count"},
+  {0x5D,0x1C,D|B,"Hardware impending failure drive calibration retry count"},
+  {0x5D,0x20,D|B,"Controller impending failure general hard drive failure"},
+  {0x5D,0x21,D|B,"Controller impending failure drive error rate too high"},
+  {0x5D,0x22,D|B,"Controller impending failure data error rate too high"},
+  {0x5D,0x23,D|B,"Controller impending failure seek error rate too high"},
+  {0x5D,0x24,D|B,"Controller impending failure too many block reassigns"},
+  {0x5D,0x25,D|B,"Controller impending failure access times too high"},
+  {0x5D,0x26,D|B,"Controller impending failure start unit times too high"},
+  {0x5D,0x27,D|B,"Controller impending failure channel parametrics"},
+  {0x5D,0x28,D|B,"Controller impending failure controller detected"},
+  {0x5D,0x29,D|B,"Controller impending failure throughput performance"},
+  {0x5D,0x2A,D|B,"Controller impending failure seek time performance"},
+  {0x5D,0x2B,D|B,"Controller impending failure spin-up retry count"},
+  {0x5D,0x2C,D|B,"Controller impending failure drive calibration retry count"},
+  {0x5D,0x30,D|B,"Data channel impending failure general hard drive failure"},
+  {0x5D,0x31,D|B,"Data channel impending failure drive error rate too high"},
+  {0x5D,0x32,D|B,"Data channel impending failure data error rate too high"},
+  {0x5D,0x33,D|B,"Data channel impending failure seek error rate too high"},
+  {0x5D,0x34,D|B,"Data channel impending failure too many block reassigns"},
+  {0x5D,0x35,D|B,"Data channel impending failure access times too high"},
+  {0x5D,0x36,D|B,"Data channel impending failure start unit times too high"},
+  {0x5D,0x37,D|B,"Data channel impending failure channel parametrics"},
+  {0x5D,0x38,D|B,"Data channel impending failure controller detected"},
+  {0x5D,0x39,D|B,"Data channel impending failure throughput performance"},
+  {0x5D,0x3A,D|B,"Data channel impending failure seek time performance"},
+  {0x5D,0x3B,D|B,"Data channel impending failure spin-up retry count"},
+  {0x5D,0x3C,D|B,"Data channel impending failure drive calibration retry count"},
+  {0x5D,0x40,D|B,"Servo impending failure general hard drive failure"},
+  {0x5D,0x41,D|B,"Servo impending failure drive error rate too high"},
+  {0x5D,0x42,D|B,"Servo impending failure data error rate too high"},
+  {0x5D,0x43,D|B,"Servo impending failure seek error rate too high"},
+  {0x5D,0x44,D|B,"Servo impending failure too many block reassigns"},
+  {0x5D,0x45,D|B,"Servo impending failure access times too high"},
+  {0x5D,0x46,D|B,"Servo impending failure start unit times too high"},
+  {0x5D,0x47,D|B,"Servo impending failure channel parametrics"},
+  {0x5D,0x48,D|B,"Servo impending failure controller detected"},
+  {0x5D,0x49,D|B,"Servo impending failure throughput performance"},
+  {0x5D,0x4A,D|B,"Servo impending failure seek time performance"},
+  {0x5D,0x4B,D|B,"Servo impending failure spin-up retry count"},
+  {0x5D,0x4C,D|B,"Servo impending failure drive calibration retry count"},
+  {0x5D,0x50,D|B,"Spindle impending failure general hard drive failure"},
+  {0x5D,0x51,D|B,"Spindle impending failure drive error rate too high"},
+  {0x5D,0x52,D|B,"Spindle impending failure data error rate too high"},
+  {0x5D,0x53,D|B,"Spindle impending failure seek error rate too high"},
+  {0x5D,0x54,D|B,"Spindle impending failure too many block reassigns"},
+  {0x5D,0x55,D|B,"Spindle impending failure access times too high"},
+  {0x5D,0x56,D|B,"Spindle impending failure start unit times too high"},
+  {0x5D,0x57,D|B,"Spindle impending failure channel parametrics"},
+  {0x5D,0x58,D|B,"Spindle impending failure controller detected"},
+  {0x5D,0x59,D|B,"Spindle impending failure throughput performance"},
+  {0x5D,0x5A,D|B,"Spindle impending failure seek time performance"},
+  {0x5D,0x5B,D|B,"Spindle impending failure spin-up retry count"},
+  {0x5D,0x5C,D|B,"Spindle impending failure drive calibration retry count"},
+  {0x5D,0x60,D|B,"Firmware impending failure general hard drive failure"},
+  {0x5D,0x61,D|B,"Firmware impending failure drive error rate too high"},
+  {0x5D,0x62,D|B,"Firmware impending failure data error rate too high"},
+  {0x5D,0x63,D|B,"Firmware impending failure seek error rate too high"},
+  {0x5D,0x64,D|B,"Firmware impending failure too many block reassigns"},
+  {0x5D,0x65,D|B,"Firmware impending failure access times too high"},
+  {0x5D,0x66,D|B,"Firmware impending failure start unit times too high"},
+  {0x5D,0x67,D|B,"Firmware impending failure channel parametrics"},
+  {0x5D,0x68,D|B,"Firmware impending failure controller detected"},
+  {0x5D,0x69,D|B,"Firmware impending failure throughput performance"},
+  {0x5D,0x6A,D|B,"Firmware impending failure seek time performance"},
+  {0x5D,0x6B,D|B,"Firmware impending failure spin-up retry count"},
+  {0x5D,0x6C,D|B,"Firmware impending failure drive calibration retry count"},
+  {0x5D,0xFF,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Failure prediction threshold exceeded (false)"},
+  {0x5E,0x00,D|T|L|P|W|R|S|O|C|A|K,"Low power condition on"},
+  {0x5E,0x01,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by timer"},
+  {0x5E,0x02,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by timer"},
+  {0x5E,0x03,D|T|L|P|W|R|S|O|C|A|K,"Idle condition activated by command"},
+  {0x5E,0x04,D|T|L|P|W|R|S|O|C|A|K,"Standby condition activated by command"},
+  {0x5E,0x41,B,"Power state change to active"},
+  {0x5E,0x42,B,"Power state change to idle"},
+  {0x5E,0x43,B,"Power state change to standby"},
+  {0x5E,0x45,B,"Power state change to sleep"},
+  {0x5E,0x47,B|K,"Power state change to device control"},
   {0x60,0x00,S,"Lamp failure"},
   {0x61,0x00,S,"Video acquisition error"},
   {0x61,0x01,S,"Unable to acquire video"},
   {0x61,0x02,S,"Out of focus"},
   {0x62,0x00,S,"Scan head positioning error"},
   {0x63,0x00,R,"End of user area encountered on this track"},
+  {0x63,0x01,R,"Packet does not fit in available space"},
   {0x64,0x00,R,"Illegal mode for this track"},
+  {0x64,0x01,R,"Invalid packet size"},
+  {0x65,0x00,D|T|L|P|W|R|S|O|M|C|A|E|B|K,"Voltage fault"},
+  {0x66,0x00,S,"Automatic document feeder cover up"},
+  {0x66,0x01,S,"Automatic document feeder lift up"},
+  {0x66,0x02,S,"Document jam in automatic document feeder"},
+  {0x66,0x03,S,"Document miss feed automatic in document feeder"},
+  {0x67,0x00,A,"Configuration failure"},
+  {0x67,0x01,A,"Configuration of incapable logical units failed"},
+  {0x67,0x02,A,"Add logical unit failed"},
+  {0x67,0x03,A,"Modification of logical unit failed"},
+  {0x67,0x04,A,"Exchange of logical unit failed"},
+  {0x67,0x05,A,"Remove of logical unit failed"},
+  {0x67,0x06,A,"Attachment of logical unit failed"},
+  {0x67,0x07,A,"Creation of logical unit failed"},
+  {0x67,0x08,A,"Assign failure occurred"},
+  {0x67,0x09,A,"Multiply assigned logical unit"},
+  {0x68,0x00,A,"Logical unit not configured"},
+  {0x69,0x00,A,"Data loss on logical unit"},
+  {0x69,0x01,A,"Multiple logical unit failures"},
+  {0x69,0x02,A,"Parity/data mismatch"},
+  {0x6A,0x00,A,"Informational,refer to log"},
+  {0x6B,0x00,A,"State change has occurred"},
+  {0x6B,0x01,A,"Redundancy level got better"},
+  {0x6B,0x02,A,"Redundancy level got worse"},
+  {0x6C,0x00,A,"Rebuild failure occurred"},
+  {0x6D,0x00,A,"Recalculate failure occurred"},
+  {0x6E,0x00,A,"Command to logical unit failed"},
+  {0x6F,0x00,R,"Copy protection key exchange failure - authentication failure"},
+  {0x6F,0x01,R,"Copy protection key exchange failure - key not present"},
+  {0x6F,0x02,R,"Copy protection key exchange failure - key not established"},
+  {0x6F,0x03,R,"Read of scrambled sector without authentication"},
+  {0x6F,0x04,R,"Media region code is mismatched to logical unit region"},
+  {0x6F,0x05,R,"Drive region must be permanent/region reset count error"},
+  /*
+   * FIXME(eric) - need a way to represent wildcards here.
+   */
+  {0x70,0x00,T,"Decompression exception short algorithm id of nn"},
+  {0x71,0x00,T,"Decompression exception long algorithm id"},
+  {0x72,0x00,R,"Session fixation error"},
+  {0x72,0x01,R,"Session fixation error writing lead-in"},
+  {0x72,0x02,R,"Session fixation error writing lead-out"},
+  {0x72,0x03,R,"Session fixation error - incomplete track in session"},
+  {0x72,0x04,R,"Empty or partially written reserved track"},
+  {0x72,0x05,R,"No more track reservations allowed"},
+  {0x73,0x00,R,"Cd control error"},
+  {0x73,0x01,R,"Power calibration area almost full"},
+  {0x73,0x02,R,"Power calibration area is full"},
+  {0x73,0x03,R,"Power calibration area error"},
+  {0x73,0x04,R,"Program memory area update failure"},
+  {0x73,0x05,R,"Program memory area is full"},
+  {0x73,0x06,R,"RMA/PMA is full"},
   {0, 0, 0, NULL}
 };
 #endif
index 8768db48c0b01a9faa3e9e09f3a11266c11d3086..7961483e171b3d07a112bc970735f0784d66be0a 100644 (file)
@@ -69,7 +69,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
 
     Scsi_Device *scd, *SDev;
     struct Scsi_Host *HBA_ptr;
-    Scsi_Cmnd  * scmd;
+    Scsi_Request  * scmd;
     char cmnd[MAX_COMMAND_SIZE];
     static u8 buff[512];
     static u8 buff2[512];
@@ -153,7 +153,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
 
     } else {
         SDev = scsi_get_host_dev(HBA_ptr);
-       scmd  = scsi_allocate_device(SDev, 1, FALSE);
+       scmd  = scsi_allocate_request(SDev);
 
        cmnd[0] = LOG_SENSE;
        cmnd[1] = 0;
@@ -166,13 +166,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
        cmnd[8] = 0x66;
        cmnd[9] = 0;
 
-       scmd->cmd_len = 10;
-       scmd->sc_data_direction = SCSI_DATA_READ;
+       scmd->sr_cmd_len = 10;
+       scmd->sr_data_direction = SCSI_DATA_READ;
        
        /*
         * Do the command and wait for it to finish.
         */     
-       scsi_wait_cmd (scmd, cmnd, buff + 0x144, 0x66,  
+       scsi_wait_req (scmd, cmnd, buff + 0x144, 0x66,  
                       1 * HZ, 1);
 
        size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
@@ -291,13 +291,13 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
            cmnd[8] = 0x44;
            cmnd[9] = 0;
            
-           scmd->cmd_len = 10;
-           scmd->sc_data_direction = SCSI_DATA_READ;
+           scmd->sr_cmd_len = 10;
+           scmd->sr_data_direction = SCSI_DATA_READ;
 
            /*
             * Do the command and wait for it to finish.
             */ 
-           scsi_wait_cmd (scmd, cmnd, buff2, 0x144,
+           scsi_wait_req (scmd, cmnd, buff2, 0x144,
                           1 * HZ, 1);
 
            swap_statistics(buff2);
@@ -333,7 +333,7 @@ int eata_proc_info(char *buffer, char **start, off_t offset, int length,
            pos = begin + len;
        }
 
-       scsi_release_command(scmd);
+       scsi_release_request(scmd);
        scsi_free_host_dev(SDev);
     }
     
index f1b82a5a17bcc07c91db1acec98711bb62815246..685925e7defdf5c9ee6fc10d63a53df374d45607 100644 (file)
@@ -469,10 +469,10 @@ extern void scsi_deregister_blocked_host(struct Scsi_Host * SHpnt);
  * Prototypes for functions/data in scsi_scan.c
  */
 extern void scan_scsis(struct Scsi_Host *shpnt,
-                      unchar hardcoded,
-                      unchar hchannel,
-                      unchar hid,
-                       unchar hlun);
+                      uint hardcoded,
+                      uint hchannel,
+                      uint hid,
+                       uint hlun);
 
 extern void scsi_mark_host_reset(struct Scsi_Host *Host);
 
@@ -485,12 +485,12 @@ struct Scsi_Device_Template
     const char * tag;
     struct module * module;      /* Used for loadable modules */
     unsigned char scsi_type;
-    unsigned char major;
-    unsigned char min_major;      /* Minimum major in range. */ 
-    unsigned char max_major;      /* Maximum major in range. */
-    unsigned char nr_dev;        /* Number currently attached */
-    unsigned char dev_noticed;   /* Number of devices detected. */
-    unsigned char dev_max;       /* Current size of arrays */
+    unsigned int major;
+    unsigned int min_major;      /* Minimum major in range. */ 
+    unsigned int max_major;      /* Maximum major in range. */
+    unsigned int nr_dev;         /* Number currently attached */
+    unsigned int dev_noticed;    /* Number of devices detected. */
+    unsigned int dev_max;        /* Current size of arrays */
     unsigned blk:1;              /* 0 if character device */
     int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
     int (*init)(void);           /* Sizes arrays based upon number of devices
@@ -534,20 +534,18 @@ extern void scsi_unregister_module(int, void *);
  * Note: These things are all evil and all need to go away.  My plan is to
  * tackle the character devices first, as there aren't any locking implications
  * in the block device layer.   The block devices will require more work.
+ *
+ * The generics driver has been updated to resize as required.  So as the tape
+ * driver. Two down, two more to go.
  */
 #ifndef CONFIG_SD_EXTRA_DEVS
 #define CONFIG_SD_EXTRA_DEVS 2
 #endif
-#ifndef CONFIG_ST_EXTRA_DEVS
-#define CONFIG_ST_EXTRA_DEVS 2
-#endif
 #ifndef CONFIG_SR_EXTRA_DEVS
 #define CONFIG_SR_EXTRA_DEVS 2
 #endif
 #define SD_EXTRA_DEVS CONFIG_SD_EXTRA_DEVS
-#define ST_EXTRA_DEVS CONFIG_ST_EXTRA_DEVS
 #define SR_EXTRA_DEVS CONFIG_SR_EXTRA_DEVS
-#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
 
 #endif
 /*
index a6e2c14d935302545ec356c7067ac8299e376fb4..21cb989ca8f8c7dd86409014c7e64cc869711d40 100644 (file)
@@ -218,23 +218,6 @@ static void scsi_wait_done(Scsi_Cmnd * SCpnt)
        }
 }
 
-void scsi_wait_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
-                 void *buffer, unsigned bufflen, 
-                 int timeout, int retries)
-{
-       DECLARE_MUTEX_LOCKED(sem);
-       
-        if (buffer != NULL && SCpnt->sc_data_direction == SCSI_DATA_NONE)
-                BUG();
-       SCpnt->request.sem = &sem;
-       SCpnt->request.rq_status = RQ_SCSI_BUSY;
-       scsi_do_cmd (SCpnt, (void *) cmnd,
-               buffer, bufflen, scsi_wait_done, timeout, retries);
-       down (&sem);
-       SCpnt->request.sem = NULL;
-}
-
-
 /*
  * This lock protects the freelist for all devices on the system.
  * We could make this finer grained by having a single lock per
@@ -2499,7 +2482,6 @@ static void scsi_dump_status(int level)
                       atomic_read(&shpnt->host_active),
                       shpnt->host_blocked,
                       shpnt->host_self_blocked);
-
        }
 
        printk("\n\n");
index 7a073f86efb3f3d4c0420cdabf7b7a526d2ab73c..677d2141084278132513de036aa85c5a35b71644 100644 (file)
@@ -497,9 +497,6 @@ extern void scsi_do_cmd(Scsi_Cmnd *, const void *cmnd,
                        void *buffer, unsigned bufflen,
                        void (*done) (struct scsi_cmnd *),
                        int timeout, int retries);
-extern void scsi_wait_cmd(Scsi_Cmnd *, const void *cmnd,
-                         void *buffer, unsigned bufflen,
-                         int timeout, int retries);
 extern int scsi_dev_init(void);
 
 /*
@@ -571,7 +568,7 @@ struct scsi_device {
        Scsi_Cmnd *device_queue;        /* queue of SCSI Command structures */
 
 /* public: */
-       unsigned char id, lun, channel;
+       unsigned int id, lun, channel;
 
        unsigned int manufacturer;      /* Manufacturer of device, for using 
                                         * vendor-specific cmd's */
@@ -731,9 +728,9 @@ struct scsi_cmnd {
 
 /* public: */
 
-       unsigned char target;
-       unsigned char lun;
-       unsigned char channel;
+       unsigned int target;
+       unsigned int lun;
+       unsigned int channel;
        unsigned char cmd_len;
        unsigned char old_cmd_len;
        unsigned char sc_data_direction;
index 9f36d08c536d89918cb49fa0a4dde68a058b85c1..4bed377edbb9da2671bd1ad76cd9206aca53956d 100644 (file)
@@ -549,24 +549,24 @@ static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
        static unsigned char cmd[6] =
        {TEST_UNIT_READY, 0, 0, 0, 0, 0};
 
-        Scsi_Cmnd     * scp;
+        Scsi_Request  * scp;
         Scsi_Device   * sdev;
         
         printk("Allocating host dev\n");
         sdev = scsi_get_host_dev(shpnt);
         printk("Got %p. Allocating command block\n", sdev);
-        scp  = scsi_allocate_device(sdev, 1, FALSE);
+        scp  = scsi_allocate_request(sdev);
         printk("Got %p\n", scp);
 
-        scp->cmd_len = 6;
-        scp->use_sg = 0;
+        scp->sr_cmd_len = 6;
+        scp->sr_use_sg = 0;
         
         printk("Sending command\n");
-        scsi_wait_cmd (scp, (void *) cmd, (void *) NULL,
+        scsi_wait_req (scp, (void *) cmd, (void *) NULL,
                        0, 100, 3);
         
         printk("Releasing command\n");
-        scsi_release_command(scp);
+        scsi_release_request(scp);
         printk("Freeing device\n");
         scsi_free_host_dev(sdev);
 }
index abdef85ef8b20c11f90a5e3a32991573703111e4..a211980a5a6fec113b64311e2f5b2004f26667eb 100644 (file)
@@ -94,24 +94,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
                                  int timeout, int retries)
 {
        int result;
-       Scsi_Cmnd *SCpnt;
+       Scsi_Request *SRpnt;
        Scsi_Device *SDpnt;
 
 
        SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0]));
-       SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
-        if( SCpnt == NULL )
-        {
-                return -EINTR;
-        }
+       SRpnt = scsi_allocate_request(dev);
 
-       SCpnt->sc_data_direction = SCSI_DATA_NONE;
-        scsi_wait_cmd(SCpnt, cmd, NULL, 0, timeout, retries);
+       SRpnt->sr_data_direction = SCSI_DATA_NONE;
+        scsi_wait_req(SRpnt, cmd, NULL, 0, timeout, retries);
 
-       SCSI_LOG_IOCTL(2, printk("Ioctl returned  0x%x\n", SCpnt->result));
+       SCSI_LOG_IOCTL(2, printk("Ioctl returned  0x%x\n", SRpnt->sr_result));
 
-       if (driver_byte(SCpnt->result) != 0)
-               switch (SCpnt->sense_buffer[2] & 0xf) {
+       if (driver_byte(SRpnt->sr_result) != 0)
+               switch (SRpnt->sr_sense_buffer[2] & 0xf) {
                case ILLEGAL_REQUEST:
                        if (cmd[0] == ALLOW_MEDIUM_REMOVAL)
                                dev->lockable = 0;
@@ -126,7 +122,7 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
                case UNIT_ATTENTION:
                        if (dev->removable) {
                                dev->changed = 1;
-                               SCpnt->result = 0;      /* This is no longer considered an error */
+                               SRpnt->sr_result = 0;   /* This is no longer considered an error */
                                /* gag this error, VFS will log it anyway /axboe */
                                /* printk(KERN_INFO "Disc change detected.\n"); */
                                break;
@@ -136,20 +132,20 @@ static int ioctl_internal_command(Scsi_Device * dev, char *cmd,
                               dev->host->host_no,
                               dev->id,
                               dev->lun,
-                              SCpnt->result);
+                              SRpnt->sr_result);
                        printk("\tSense class %x, sense error %x, extended sense %x\n",
-                              sense_class(SCpnt->sense_buffer[0]),
-                              sense_error(SCpnt->sense_buffer[0]),
-                              SCpnt->sense_buffer[2] & 0xf);
+                              sense_class(SRpnt->sr_sense_buffer[0]),
+                              sense_error(SRpnt->sr_sense_buffer[0]),
+                              SRpnt->sr_sense_buffer[2] & 0xf);
 
                };
 
-       result = SCpnt->result;
+       result = SRpnt->sr_result;
 
        SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n"));
-       SDpnt = SCpnt->device;
-       scsi_release_command(SCpnt);
-       SCpnt = NULL;
+       SDpnt = SRpnt->sr_device;
+       scsi_release_request(SRpnt);
+       SRpnt = NULL;
 
        return result;
 }
@@ -192,7 +188,7 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
        char *buf;
        unsigned char cmd[MAX_COMMAND_SIZE];
        char *cmd_in;
-       Scsi_Cmnd *SCpnt;
+       Scsi_Request *SRpnt;
        Scsi_Device *SDpnt;
        unsigned char opcode;
        int inlen, outlen, cmdlen;
@@ -235,9 +231,9 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
                        return -ENOMEM;
                memset(buf, 0, buf_needed);
                if( inlen == 0 ) {
-                       data_direction = SCSI_DATA_WRITE;
-               } else if (outlen == 0 ) {
                        data_direction = SCSI_DATA_READ;
+               } else if (outlen == 0 ) {
+                       data_direction = SCSI_DATA_WRITE;
                } else {
                        /*
                         * Can this ever happen?
@@ -297,38 +293,38 @@ int scsi_ioctl_send_command(Scsi_Device * dev, Scsi_Ioctl_Command * sic)
 #ifndef DEBUG_NO_CMD
 
 
-       SCpnt = scsi_allocate_device(dev, TRUE, TRUE);
-        if( SCpnt == NULL )
+       SRpnt = scsi_allocate_request(dev);
+        if( SRpnt == NULL )
         {
                 return -EINTR;
         }
 
-       SCpnt->sc_data_direction = data_direction;
-        scsi_wait_cmd(SCpnt, cmd, buf, needed, timeout, retries);
+       SRpnt->sr_data_direction = data_direction;
+        scsi_wait_req(SRpnt, cmd, buf, needed, timeout, retries);
 
        /* 
         * If there was an error condition, pass the info back to the user. 
         */
-       if (SCpnt->result) {
-               int sb_len = sizeof(SCpnt->sense_buffer);
+       if (SRpnt->sr_result) {
+               int sb_len = sizeof(SRpnt->sr_sense_buffer);
 
                sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len;
                result = verify_area(VERIFY_WRITE, cmd_in, sb_len);
                if (result)
                        return result;
-               copy_to_user(cmd_in, SCpnt->sense_buffer, sb_len);
+               copy_to_user(cmd_in, SRpnt->sr_sense_buffer, sb_len);
        } else {
                result = verify_area(VERIFY_WRITE, cmd_in, outlen);
                if (result)
                        return result;
                copy_to_user(cmd_in, buf, outlen);
        }
-       result = SCpnt->result;
+       result = SRpnt->sr_result;
 
 
-       SDpnt = SCpnt->device;
-       scsi_release_command(SCpnt);
-       SCpnt = NULL;
+       SDpnt = SRpnt->sr_device;
+       scsi_release_request(SRpnt);
+       SRpnt = NULL;
 
        if (buf)
                scsi_free(buf, buf_needed);
index 8c7b448e11e1cc8d44966ebe4b0b113f4ba30ffa..74ac6d2452056178357e1ed237068be1c529a885 100644 (file)
@@ -912,6 +912,9 @@ void scsi_request_fn(request_queue_t * q)
                         * be in an interrupt handler.  Only do this
                         * from user space, since we do not want to
                         * sleep from an interrupt.
+                        *
+                        * FIXME(eric) - have the error handler thread do
+                        * this work.
                         */
                        SDpnt->was_reset = 0;
                        if (SDpnt->removable && !in_interrupt()) {
@@ -953,6 +956,9 @@ void scsi_request_fn(request_queue_t * q)
                        if( SRpnt->sr_magic == SCSI_REQ_MAGIC ) {
                                SCpnt = scsi_allocate_device(SRpnt->sr_device, 
                                                             FALSE, FALSE);
+                               if( !SCpnt ) {
+                                       break;
+                               }
                                scsi_init_cmd_from_req(SCpnt, SRpnt);
                        }
 
index 4060872bf2e50a6ea1804086351cbb18970ba6f8..a43f2988c8cb3ab5592a34c55de6a07bc118ac9c 100644 (file)
@@ -252,12 +252,12 @@ static int get_device_flags(unsigned char *response_data)
  *  devices to the disk driver.
  */
 void scan_scsis(struct Scsi_Host *shpnt,
-                      unchar hardcoded,
-                      unchar hchannel,
-                      unchar hid,
-                      unchar hlun)
+                      uint hardcoded,
+                      uint hchannel,
+                      uint hid,
+                      uint hlun)
 {
-       int channel;
+       uint channel;
        int dev;
        int lun;
        int max_dev_lun;
@@ -299,8 +299,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
        SDpnt->host = shpnt;
        SDpnt->online = TRUE;
 
-       scsi_build_commandblocks(SDpnt);
-
        initialize_merge_fn(SDpnt);
 
         /*
@@ -405,7 +403,7 @@ void scan_scsis(struct Scsi_Host *shpnt,
 
       leave:
 
-       {                       /* Unchain SCpnt from host_queue */
+       {                       /* Unchain SRpnt from host_queue */
                Scsi_Device *prev, *next;
                Scsi_Device *dqptr;
 
@@ -423,8 +421,6 @@ void scan_scsis(struct Scsi_Host *shpnt,
                }
        }
 
-       scsi_release_commandblocks(SDpnt);
-
        /* Last device block does not exist.  Free memory. */
        if (SDpnt != NULL)
                kfree((char *) SDpnt);
@@ -460,7 +456,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
        unsigned char scsi_cmd[MAX_COMMAND_SIZE];
        struct Scsi_Device_Template *sdtpnt;
        Scsi_Device *SDtail, *SDpnt = *SDpnt2;
-       Scsi_Cmnd * SCpnt;
+       Scsi_Request * SRpnt;
        int bflags, type = -1;
        static int ghost_channel=-1, ghost_dev=-1;
        int org_lun = lun;
@@ -472,6 +468,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
        SDpnt->channel = channel;
        SDpnt->online = TRUE;
 
+       scsi_build_commandblocks(SDpnt);
  
        if ((channel == ghost_channel) && (dev == ghost_dev) && (lun == 1)) {
                SDpnt->lun = 0;
@@ -496,37 +493,32 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
        scsi_cmd[1] = lun << 5;
        scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
 
-       SCpnt = scsi_allocate_device(SDpnt, 0, 0);
+       SRpnt = scsi_allocate_request(SDpnt);
 
-       SCpnt->host = SDpnt->host;
-       SCpnt->device = SDpnt;
-       SCpnt->target = SDpnt->id;
-       SCpnt->lun = SDpnt->lun;
-       SCpnt->channel = SDpnt->channel;
-       SCpnt->sc_data_direction = SCSI_DATA_NONE;
+       SRpnt->sr_data_direction = SCSI_DATA_NONE;
 
-       scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+       scsi_wait_req (SRpnt, (void *) scsi_cmd,
                  (void *) NULL,
                  0, SCSI_TIMEOUT + 4 * HZ, 5);
 
        SCSI_LOG_SCAN_BUS(3, printk("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
-                                   dev, lun, SCpnt->result));
-       SCSI_LOG_SCAN_BUS(3, print_driverbyte(SCpnt->result));
-       SCSI_LOG_SCAN_BUS(3, print_hostbyte(SCpnt->result));
+                                   dev, lun, SRpnt->sr_result));
+       SCSI_LOG_SCAN_BUS(3, print_driverbyte(SRpnt->sr_result));
+       SCSI_LOG_SCAN_BUS(3, print_hostbyte(SRpnt->sr_result));
        SCSI_LOG_SCAN_BUS(3, printk("\n"));
 
-       if (SCpnt->result) {
-               if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||
-                    (status_byte(SCpnt->result) & CHECK_CONDITION)) &&
-                   ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
-                       if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
-                           ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
-                           ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
-                               scsi_release_command(SCpnt);
+       if (SRpnt->sr_result) {
+               if (((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) ||
+                    (status_byte(SRpnt->sr_result) & CHECK_CONDITION)) &&
+                   ((SRpnt->sr_sense_buffer[0] & 0x70) >> 4) == 7) {
+                       if (((SRpnt->sr_sense_buffer[2] & 0xf) != NOT_READY) &&
+                           ((SRpnt->sr_sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+                           ((SRpnt->sr_sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0)) {
+                               scsi_release_request(SRpnt);
                                return 1;
                        }
                } else {
-                       scsi_release_command(SCpnt);
+                       scsi_release_request(SRpnt);
                        return 0;
                }
        }
@@ -540,18 +532,18 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
        scsi_cmd[3] = 0;
        scsi_cmd[4] = 255;
        scsi_cmd[5] = 0;
-       SCpnt->cmd_len = 0;
-       SCpnt->sc_data_direction = SCSI_DATA_READ;
+       SRpnt->sr_cmd_len = 0;
+       SRpnt->sr_data_direction = SCSI_DATA_READ;
 
-       scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+       scsi_wait_req (SRpnt, (void *) scsi_cmd,
                  (void *) scsi_result,
                  256, SCSI_TIMEOUT, 3);
 
        SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n",
-               SCpnt->result ? "failed" : "successful", SCpnt->result));
+               SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result));
 
-       if (SCpnt->result) {
-               scsi_release_command(SCpnt);
+       if (SRpnt->sr_result) {
+               scsi_release_request(SRpnt);
                return 0;       /* assume no peripheral if any sort of error */
        }
 
@@ -560,7 +552,7 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
         * are supported here or not.
         */
        if ((scsi_result[0] >> 5) == 3) {
-               scsi_release_command(SCpnt);
+               scsi_release_request(SRpnt);
                return 0;       /* assume no peripheral if any sort of error */
        }
 
@@ -705,15 +697,15 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
                scsi_cmd[3] = 0;
                scsi_cmd[4] = 0x2a;
                scsi_cmd[5] = 0;
-               SCpnt->cmd_len = 0;
-               SCpnt->sc_data_direction = SCSI_DATA_READ;
-               scsi_wait_cmd (SCpnt, (void *) scsi_cmd,
+               SRpnt->sr_cmd_len = 0;
+               SRpnt->sr_data_direction = SCSI_DATA_READ;
+               scsi_wait_req (SRpnt, (void *) scsi_cmd,
                        (void *) scsi_result, 0x2a,
                        SCSI_TIMEOUT, 3);
        }
 
-       scsi_release_command(SCpnt);
-       SCpnt = NULL;
+       scsi_release_request(SRpnt);
+       SRpnt = NULL;
 
        scsi_release_commandblocks(SDpnt);
 
@@ -734,8 +726,6 @@ static int scan_scsis_single(int channel, int dev, int lun, int *max_dev_lun,
        SDpnt->host = shpnt;
        SDpnt->online = TRUE;
 
-       scsi_build_commandblocks(SDpnt);
-
        /*
         * Register the queue for the device.  All I/O requests will come
         * in through here.  We also need to register a pointer to
index f6e8939a6bd93603f255e965053b5b8d8ea2a2a1..94820f5db8d357591ead816a54dc7f6be3a80464 100644 (file)
@@ -43,7 +43,6 @@ EXPORT_SYMBOL(scsicam_bios_param);
 EXPORT_SYMBOL(scsi_partsize);
 EXPORT_SYMBOL(scsi_allocate_device);
 EXPORT_SYMBOL(scsi_do_cmd);
-EXPORT_SYMBOL(scsi_wait_cmd);
 EXPORT_SYMBOL(scsi_command_size);
 EXPORT_SYMBOL(scsi_ioctl);
 EXPORT_SYMBOL(print_command);
index bde5626a260bca27f1e72f22d05cde7039b305ec..584a84905dcc2c090f8d7ad8bebf7d98cbf8eedc 100644 (file)
@@ -662,7 +662,7 @@ static int sd_init_onedisk(int i)
        unsigned long spintime_value = 0;
        int the_result, retries, spintime;
        int sector_size;
-       Scsi_Cmnd *SCpnt;
+       Scsi_Request *SRpnt;
 
        /*
         * Get the name of the disk, in case we need to log it somewhere.
@@ -681,7 +681,7 @@ static int sd_init_onedisk(int i)
         * just after a scsi bus reset.
         */
 
-       SCpnt = scsi_allocate_device(rscsi_disks[i].device, 1, FALSE);
+       SRpnt = scsi_allocate_request(rscsi_disks[i].device);
 
        buffer = (unsigned char *) scsi_malloc(512);
 
@@ -696,18 +696,18 @@ static int sd_init_onedisk(int i)
                        cmd[0] = TEST_UNIT_READY;
                        cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
                        memset((void *) &cmd[2], 0, 8);
-                       SCpnt->cmd_len = 0;
-                       SCpnt->sense_buffer[0] = 0;
-                       SCpnt->sense_buffer[2] = 0;
-                       SCpnt->sc_data_direction = SCSI_DATA_READ;
+                       SRpnt->sr_cmd_len = 0;
+                       SRpnt->sr_sense_buffer[0] = 0;
+                       SRpnt->sr_sense_buffer[2] = 0;
+                       SRpnt->sr_data_direction = SCSI_DATA_READ;
 
-                       scsi_wait_cmd (SCpnt, (void *) cmd, (void *) buffer,
+                       scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer,
                                0/*512*/, SD_TIMEOUT, MAX_RETRIES);
 
-                       the_result = SCpnt->result;
+                       the_result = SRpnt->sr_result;
                        retries++;
                        if (the_result == 0
-                           || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+                           || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION)
                                break;
                }
 
@@ -718,8 +718,8 @@ static int sd_init_onedisk(int i)
                 */
                if( the_result != 0
                    && ((driver_byte(the_result) & DRIVER_SENSE) != 0)
-                   && SCpnt->sense_buffer[2] == UNIT_ATTENTION
-                   && SCpnt->sense_buffer[12] == 0x3A ) {
+                   && SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION
+                   && SRpnt->sr_sense_buffer[12] == 0x3A ) {
                        rscsi_disks[i].capacity = 0x1fffff;
                        sector_size = 512;
                        rscsi_disks[i].device->changed = 1;
@@ -730,7 +730,7 @@ static int sd_init_onedisk(int i)
                /* Look for non-removable devices that return NOT_READY.
                 * Issue command to spin up drive for these cases. */
                if (the_result && !rscsi_disks[i].device->removable &&
-                   SCpnt->sense_buffer[2] == NOT_READY) {
+                   SRpnt->sr_sense_buffer[2] == NOT_READY) {
                        unsigned long time1;
                        if (!spintime) {
                                printk("%s: Spinning up disk...", nbuff);
@@ -739,12 +739,12 @@ static int sd_init_onedisk(int i)
                                cmd[1] |= 1;    /* Return immediately */
                                memset((void *) &cmd[2], 0, 8);
                                cmd[4] = 1;     /* Start spin cycle */
-                               SCpnt->cmd_len = 0;
-                               SCpnt->sense_buffer[0] = 0;
-                               SCpnt->sense_buffer[2] = 0;
+                               SRpnt->sr_cmd_len = 0;
+                               SRpnt->sr_sense_buffer[0] = 0;
+                               SRpnt->sr_sense_buffer[2] = 0;
 
-                               SCpnt->sc_data_direction = SCSI_DATA_READ;
-                               scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+                               SRpnt->sr_data_direction = SCSI_DATA_READ;
+                               scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
                                            0/*512*/, SD_TIMEOUT, MAX_RETRIES);
                        }
                        spintime = 1;
@@ -770,15 +770,15 @@ static int sd_init_onedisk(int i)
                cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
                memset((void *) &cmd[2], 0, 8);
                memset((void *) buffer, 0, 8);
-               SCpnt->cmd_len = 0;
-               SCpnt->sense_buffer[0] = 0;
-               SCpnt->sense_buffer[2] = 0;
+               SRpnt->sr_cmd_len = 0;
+               SRpnt->sr_sense_buffer[0] = 0;
+               SRpnt->sr_sense_buffer[2] = 0;
 
-               SCpnt->sc_data_direction = SCSI_DATA_READ;
-               scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+               SRpnt->sr_data_direction = SCSI_DATA_READ;
+               scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
                            8, SD_TIMEOUT, MAX_RETRIES);
 
-               the_result = SCpnt->result;
+               the_result = SRpnt->sr_result;
                retries--;
 
        } while (the_result && retries);
@@ -808,7 +808,7 @@ static int sd_init_onedisk(int i)
                    );
                if (driver_byte(the_result) & DRIVER_SENSE)
                        printk("%s : extended sense code = %1x \n",
-                              nbuff, SCpnt->sense_buffer[2] & 0xf);
+                              nbuff, SRpnt->sr_sense_buffer[2] & 0xf);
                else
                        printk("%s : sense not available. \n", nbuff);
 
@@ -820,7 +820,7 @@ static int sd_init_onedisk(int i)
                /* Set dirty bit for removable devices if not ready - sometimes drives
                 * will not report this properly. */
                if (rscsi_disks[i].device->removable &&
-                   SCpnt->sense_buffer[2] == NOT_READY)
+                   SRpnt->sr_sense_buffer[2] == NOT_READY)
                        rscsi_disks[i].device->changed = 1;
 
        } else {
@@ -921,16 +921,16 @@ static int sd_init_onedisk(int i)
                cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
                cmd[2] = 1;     /* page code 1 ?? */
                cmd[4] = 12;
-               SCpnt->cmd_len = 0;
-               SCpnt->sense_buffer[0] = 0;
-               SCpnt->sense_buffer[2] = 0;
+               SRpnt->sr_cmd_len = 0;
+               SRpnt->sr_sense_buffer[0] = 0;
+               SRpnt->sr_sense_buffer[2] = 0;
 
                /* same code as READCAPA !! */
-               SCpnt->sc_data_direction = SCSI_DATA_READ;
-               scsi_wait_cmd(SCpnt, (void *) cmd, (void *) buffer,
+               SRpnt->sr_data_direction = SCSI_DATA_READ;
+               scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
                            512, SD_TIMEOUT, MAX_RETRIES);
 
-               the_result = SCpnt->result;
+               the_result = SRpnt->sr_result;
 
                if (the_result) {
                        printk("%s: test WP failed, assume Write Protected\n", nbuff);
@@ -942,12 +942,12 @@ static int sd_init_onedisk(int i)
                }
 
        }                       /* check for write protect */
-       SCpnt->device->ten = 1;
-       SCpnt->device->remap = 1;
-       SCpnt->device->sector_size = sector_size;
+       SRpnt->sr_device->ten = 1;
+       SRpnt->sr_device->remap = 1;
+       SRpnt->sr_device->sector_size = sector_size;
        /* Wake up a process waiting for device */
-       scsi_release_command(SCpnt);
-       SCpnt = NULL;
+       scsi_release_request(SRpnt);
+       SRpnt = NULL;
 
        scsi_free(buffer, 512);
        return i;
index 781cde85af84b4c3c778fe723964f475bac11862..dc6712247482f7cba9f27e2144d6a8660f4db1ea 100644 (file)
@@ -486,7 +486,7 @@ void get_sectorsize(int i)
 
                SRpnt->sr_data_direction = SCSI_DATA_READ;
                scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
-                             512, SR_TIMEOUT, MAX_RETRIES);
+                             8, SR_TIMEOUT, MAX_RETRIES);
 
                the_result = SRpnt->sr_result;
                retries--;
@@ -663,7 +663,7 @@ static int sr_packet(struct cdrom_device_info *cdi, struct cdrom_generic_command
 
        /* do the locking and issue the command */
        SRpnt->sr_request.rq_dev = cdi->dev;
-       /* scsi_wait_cmd sets the command length */
+       /* scsi_wait_req sets the command length */
        SRpnt->sr_cmd_len = 0;
 
        SRpnt->sr_data_direction = cgc->data_direction;
index 9d61157ef8a613373792137aa469abf20aca8a8d..217bdb605575efaf81d291701a1cec3811cd04f7 100644 (file)
@@ -217,6 +217,16 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
 
 static struct sound_unit *chains[16];
 
+/**
+ *     register_sound_special
+ *     @fops: File operations for the driver
+ *     @unit: Unit number to allocate
+ *
+ *     Allocate a special sound device by minor number from the sound
+ *     subsystem. The allocated number is returned on succes. On failure
+ *     a negative error code is returned.
+ */
 int register_sound_special(struct file_operations *fops, int unit)
 {
        char *name;
@@ -277,6 +287,16 @@ int register_sound_special(struct file_operations *fops, int unit)
  
 EXPORT_SYMBOL(register_sound_special);
 
+/**
+ *     register_sound_mixer
+ *     @fops: File operations for the driver
+ *     @dev: Unit number to allocate
+ *
+ *     Allocate a mixer device. Unit is the number of the mixer requested.
+ *     Pass -1 to request the next free mixer unit. On success the allocated
+ *     number is returned, on failure a negative error code is returned.
+ */
+
 int register_sound_mixer(struct file_operations *fops, int dev)
 {
        return sound_insert_unit(&chains[0], fops, dev, 0, 128,
@@ -285,6 +305,16 @@ int register_sound_mixer(struct file_operations *fops, int dev)
 
 EXPORT_SYMBOL(register_sound_mixer);
 
+/**
+ *     register_sound_midi
+ *     @fops: File operations for the driver
+ *     @dev: Unit number to allocate
+ *
+ *     Allocate a midi device. Unit is the number of the midi device requested.
+ *     Pass -1 to request the next free midi unit. On success the allocated
+ *     number is returned, on failure a negative error code is returned.
+ */
+
 int register_sound_midi(struct file_operations *fops, int dev)
 {
        return sound_insert_unit(&chains[2], fops, dev, 2, 130,
@@ -298,6 +328,19 @@ EXPORT_SYMBOL(register_sound_midi);
  *     in open - see below.
  */
  
+/**
+ *     register_sound_dsp
+ *     @fops: File operations for the driver
+ *     @dev: Unit number to allocate
+ *
+ *     Allocate a DSP device. Unit is the number of the DSP requested.
+ *     Pass -1 to request the next free DSP unit. On success the allocated
+ *     number is returned, on failure a negative error code is returned.
+ *
+ *     This function allocates both the audio and dsp device entries together
+ *     and will always allocate them as a matching pair - eg dsp3/audio3
+ */
+
 int register_sound_dsp(struct file_operations *fops, int dev)
 {
        return sound_insert_unit(&chains[3], fops, dev, 3, 131,
@@ -306,6 +349,17 @@ int register_sound_dsp(struct file_operations *fops, int dev)
 
 EXPORT_SYMBOL(register_sound_dsp);
 
+/**
+ *     register_sound_synth
+ *     @fops: File operations for the driver
+ *     @dev: Unit number to allocate
+ *
+ *     Allocate a synth device. Unit is the number of the synth device requested.
+ *     Pass -1 to request the next free synth unit. On success the allocated
+ *     number is returned, on failure a negative error code is returned.
+ */
+
+
 int register_sound_synth(struct file_operations *fops, int dev)
 {
        return sound_insert_unit(&chains[9], fops, dev, 9, 137,
@@ -314,6 +368,15 @@ int register_sound_synth(struct file_operations *fops, int dev)
 
 EXPORT_SYMBOL(register_sound_synth);
 
+/**
+ *     unregister_sound_special
+ *     @unit: Unit number to allocate
+ *
+ *     Release a sound device that was allocated with register_sound_special.
+ *     The unit passed is the return value from the register function.
+ */
+
+
 void unregister_sound_special(int unit)
 {
        sound_remove_unit(&chains[unit&15], unit);
@@ -321,6 +384,14 @@ void unregister_sound_special(int unit)
  
 EXPORT_SYMBOL(unregister_sound_special);
 
+/**
+ *     unregister_sound_mixer
+ *     @unit: Unit number to allocate
+ *
+ *     Release a sound device that was allocated with register_sound_mixer.
+ *     The unit passed is the return value from the register function.
+ */
+
 void unregister_sound_mixer(int unit)
 {
        sound_remove_unit(&chains[0], unit);
@@ -328,6 +399,14 @@ void unregister_sound_mixer(int unit)
 
 EXPORT_SYMBOL(unregister_sound_mixer);
 
+/**
+ *     unregister_sound_midi
+ *     @unit: Unit number to allocate
+ *
+ *     Release a sound device that was allocated with register_sound_midi.
+ *     The unit passed is the return value from the register function.
+ */
+
 void unregister_sound_midi(int unit)
 {
        return sound_remove_unit(&chains[2], unit);
@@ -335,13 +414,32 @@ void unregister_sound_midi(int unit)
 
 EXPORT_SYMBOL(unregister_sound_midi);
 
+/**
+ *     unregister_sound_dsp
+ *     @unit: Unit number to allocate
+ *
+ *     Release a sound device that was allocated with register_sound_dsp.
+ *     The unit passed is the return value from the register function.
+ *
+ *     Both of the allocated units are released together automatically.
+ */
+
 void unregister_sound_dsp(int unit)
 {
        return sound_remove_unit(&chains[3], unit);
 }
 
+
 EXPORT_SYMBOL(unregister_sound_dsp);
 
+/**
+ *     unregister_sound_synth
+ *     @unit: Unit number to allocate
+ *
+ *     Release a sound device that was allocated with register_sound_synth.
+ *     The unit passed is the return value from the register function.
+ */
+
 void unregister_sound_synth(int unit)
 {
        return sound_remove_unit(&chains[9], unit);
index c446e98e01c4fe4a64b0eb107e74fa0ac4be1adb..393e6a780d26ff79cc5626ff357659c154f3afdf 100644 (file)
@@ -47,6 +47,24 @@ static int do_mod_firmware_load(const char *fn, char **fp)
        return (int) l;
 }
 
+/**
+ *     mod_firmware_load - load sound driver firmware
+ *     @fn: filename
+ *     @fp: return for the buffer.
+ *
+ *     Load the firmware for a sound module (up to 128K) into a buffer.
+ *     The buffer is returned in *fp. It is allocated with vmalloc so is
+ *     virtually linear and not DMAable. The caller should free it with
+ *     vfree when finished.
+ *
+ *     The length of the buffer is returned on a successful load, the
+ *     value zero on a failure.
+ *
+ *     Caution: This API is not recommended. Firmware should be loaded via
+ *     an ioctl call and a setup application. This function may disappear
+ *     in future.
+ */
 int mod_firmware_load(const char *fn, char **fp)
 {
        int r;
index ea90ed81ca67bc70bb23d8f73336a3fc2e29c1f4..f3b008f0c27f76b5ab5cf2c86c5cc131915b8eff 100644 (file)
@@ -838,8 +838,8 @@ void conf_printf(char *name, struct address_info *hw_config)
                        printk(",%d", hw_config->dma2);
        }
        printk("\n");
-}
 #endif
+}
 
 void conf_printf2(char *name, int base, int irq, int dma, int dma2)
 {
@@ -858,8 +858,8 @@ void conf_printf2(char *name, int base, int irq, int dma, int dma2)
                          printk(",%d", dma2);
        }
        printk("\n");
-}
 #endif
+}
 
 /*
  *     Module and lock management
index a210b51061c9e72b0b451e492681bf20b24699ee..fbb7562dcac336d670400393feed662da058dc18 100644 (file)
@@ -9,9 +9,6 @@ if [ ! "$CONFIG_USB" = "n" ]; then
 
 comment 'USB Controllers'
    dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
-   if [ "$CONFIG_USB_UHCI" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
-      bool '    USB-UHCI High Bandwidth (EXPERIMENTAL)' CONFIG_USB_UHCI_HIGH_BANDWIDTH
-   fi
    if [ "$CONFIG_USB_UHCI" != "y" ]; then
       dep_tristate '  UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB
       if [ "$CONFIG_USB_UHCI_ALT" != "n" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then
index f5fc141404cbd8dfdc0983961c91b00491c32161..8b81e06c0c3ff42cd793f4a7331f44369b0d8011 100644 (file)
@@ -12,6 +12,9 @@
  device, and I couldn't figure out their meaning.  My suspicion
  is that they don't have any:-)
 
+ You might find some interesting stuff about this module at
+ http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr
+
  Copyright (c) 2000 Markus Demleitner
 
  This program is free software; you can redistribute it and/or modify
 
  History:
 
- Version 0.3: 
-       Brad Hards <bradh@dynamite.com.au>: Trivial fix for usb.h, more GPL
-       
+ Version 0.21:
+       Markus Demleitner <msdemlei@tucana.harvard.edu>:
+       Minor cleanup, warnings if something goes wrong, lame attempt
+       to adhere to Documentation/CodingStyle
+
  Version 0.2: 
        Brad Hards <bradh@dynamite.com.au>: Fixes to make it work as non-module
-       Markus Demleitner <msdemlei@tucana.harvard.edu>: Copyright clarification
+       Markus: Copyright clarification
 
- Version 0.01: Markus Demleitner <msdemlei@tucana.harvard.edu>
-       initial release
+ Version 0.01: Markus: initial release
 
 */
 
 
 #include <linux/kernel.h>
+
+#if CONFIG_MODVERSIONS==1
+#define MODVERSIONS
+#include <linux/modversions.h>
+#endif
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/malloc.h>
@@ -100,22 +110,25 @@ static struct usb_driver usb_dsbr100_driver = {
        minor: 0
 };
 
+
 static int dsbr100_start(usb_dsbr100 *radio)
 {
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300);
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300);
+       if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 ||
+           usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
+               return -1;
        return (radio->transfer_buffer)[0];
 }
 
 
 static int dsbr100_stop(usb_dsbr100 *radio)
 {
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300);
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300);
+       if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 ||
+           usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
+               return -1;
        return (radio->transfer_buffer)[0];
 }
 
@@ -123,21 +136,27 @@ static int dsbr100_stop(usb_dsbr100 *radio)
 static int dsbr100_setfreq(usb_dsbr100 *radio, int freq)
 {
        freq = (freq*80)/16+856;
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, radio->transfer_buffer, 8, 300);
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300);
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-                       0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300);
+       if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, 
+               radio->transfer_buffer, 8, 300)<0
+        || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 ||
+           usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) {
+               radio->stereo = -1;
+               return -1;
+       }
        radio->stereo = ! ((radio->transfer_buffer)[0]&0x01);
        return (radio->transfer_buffer)[0];
 }
 
 static void dsbr100_getstat(usb_dsbr100 *radio)
 {
-       usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
-               0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300);
-       radio->stereo = ! (radio->transfer_buffer[0]&0x01);
+       if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0),
+               0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0)
+               radio->stereo = -1;
+       else
+               radio->stereo = ! (radio->transfer_buffer[0]&0x01);
 }
 
 
@@ -177,8 +196,7 @@ static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
 
        switch(cmd)
        {
-               case VIDIOCGCAP:
-               {
+               case VIDIOCGCAP: {
                        struct video_capability v;
                        v.type=VID_TYPE_TUNER;
                        v.channels=1;
@@ -193,8 +211,7 @@ static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
                                return -EFAULT;
                        return 0;
                }
-               case VIDIOCGTUNER:
-               {
+               case VIDIOCGTUNER: {
                        struct video_tuner v;
                        dsbr100_getstat(radio);
                        if(copy_from_user(&v, arg,sizeof(v))!=0) 
@@ -212,8 +229,7 @@ static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
                                return -EFAULT;
                        return 0;
                }
-               case VIDIOCSTUNER:
-               {
+               case VIDIOCSTUNER: {
                        struct video_tuner v;
                        if(copy_from_user(&v, arg, sizeof(v)))
                                return -EFAULT;
@@ -222,17 +238,23 @@ static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
                        /* Only 1 tuner so no setting needed ! */
                        return 0;
                }
-               case VIDIOCGFREQ:
-                       if(copy_to_user(arg, &(radio->curfreq), sizeof(radio->curfreq)))
+               case VIDIOCGFREQ: 
+                       if (radio->curfreq==-1)
+                               return -EINVAL;
+                       if(copy_to_user(arg, &(radio->curfreq), 
+                               sizeof(radio->curfreq)))
                                return -EFAULT;
                        return 0;
+
                case VIDIOCSFREQ:
-                       if(copy_from_user(&(radio->curfreq), arg,sizeof(radio->curfreq)))
+                       if(copy_from_user(&(radio->curfreq), arg,
+                               sizeof(radio->curfreq)))
                                return -EFAULT;
-                       dsbr100_setfreq(radio, radio->curfreq);
+                       if (dsbr100_setfreq(radio, radio->curfreq)==-1)
+                               warn("set frequency failed");
                        return 0;
-               case VIDIOCGAUDIO:
-               {       
+
+               case VIDIOCGAUDIO: {
                        struct video_audio v;
                        memset(&v,0, sizeof(v));
                        v.flags|=VIDEO_AUDIO_MUTABLE;
@@ -244,18 +266,20 @@ static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd,
                                return -EFAULT;
                        return 0;                       
                }
-               case VIDIOCSAUDIO:
-               {
+               case VIDIOCSAUDIO: {
                        struct video_audio v;
                        if(copy_from_user(&v, arg, sizeof(v))) 
                                return -EFAULT; 
                        if(v.audio) 
                                return -EINVAL;
 
-                       if(v.flags&VIDEO_AUDIO_MUTE) 
-                               dsbr100_stop(radio);
+                       if(v.flags&VIDEO_AUDIO_MUTE) {
+                               if (dsbr100_stop(radio)==-1)
+                                       warn("radio did not respond properly");
+                       }
                        else
-                               dsbr100_start(radio);                                                           
+                               if (dsbr100_start(radio)==-1)
+                                       warn("radio did not respond properly");
                        return 0;
                }
                default:
@@ -268,13 +292,19 @@ static int usb_dsbr100_open(struct video_device *dev, int flags)
 {
        usb_dsbr100 *radio=dev->priv;
 
-       if (! radio)
-               return -EINVAL;
+       if (! radio) {
+               warn("radio not initialised");
+               return -EAGAIN;
+       }
        if(users)
+       {
+               warn("radio in use");
                return -EBUSY;
+       }
        users++;
        MOD_INC_USE_COUNT;
-       dsbr100_start(radio);
+       if (dsbr100_start(radio)<0)
+               warn("radio did not start up properly");
        dsbr100_setfreq(radio,radio->curfreq);
        return 0;
 }
@@ -295,6 +325,7 @@ int __init dsbr100_init(void)
        usb_dsbr100_radio.priv = NULL;
        usb_register(&usb_dsbr100_driver);
        if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) {     
+               warn("couldn't register video device");
                return -EINVAL;
        }
        return 0;
@@ -302,11 +333,21 @@ int __init dsbr100_init(void)
 
 int __init init_module(void)
 {
-        return dsbr100_init();
+       return dsbr100_init();
 }
 
 void cleanup_module(void)
 {
+       usb_dsbr100 *radio=usb_dsbr100_radio.priv;
+
+       if (radio)
+               dsbr100_stop(radio);
        video_unregister_device(&usb_dsbr100_radio);
        usb_deregister(&usb_dsbr100_driver);
 }
+
+/*
+vi: ts=8
+Sigh.  Of course, I am one of the ts=2 heretics, but Linus' wish is
+my command.
+*/
index d3761bf52083d51bb246f9afab2711121cf3ca13..2d5de6f73b22a2cbfac660b26c66f5988287904a 100644 (file)
 /*
-**
 **     Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller
 **
-**     Copyleft (L) 1999 Petko Manolov - Petkan (petkan@spct.net)
-**     
+**     Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net)
+**
 **     Distribute under GPL version 2 or later.
 */
 
-
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/malloc.h>
 #include <linux/init.h>
 #include <linux/delay.h>
-#include <linux/usb.h>
-
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
+#include <linux/usb.h>
 
-#if LINUX_VERSION_CODE<0x2032d || !defined(__KERNEL__) || !defined(__OPTIMIZE__)
-#error You can not compile this driver on this kernel with this C options!
-#endif
-
-
-#define        ADMTEK_VENDOR_ID        0x07a6
-#define        ADMTEK_HPNA_PEGASUS     0x0986
-
-#define        HPNA_MTU                1500
-#define MAX_MTU                        1536
 
-#define        TX_TIMEOUT              (HZ*5)
-#define        SOMETHING               (jiffies + TX_TIMEOUT)
+static const char *version = __FILE__ ": v0.3.3 2000/03/13 Written by Petko Manolov (petkan@spct.net)\n";
 
 
-static const char version[] = "pegasus.c: v0.2.27 2000/02/29 Written by Petko Manolov (petkan@spct.net)\n";
+#define        ADMTEK_VENDOR_ID                0x07a6
+#define        ADMTEK_DEVICE_ID_PEGASUS        0x0986
 
+#define        PEGASUS_MTU             1500
+#define PEGASUS_MAX_MTU                1536
+#define        PEGASUS_TX_TIMEOUT      (HZ*5)
+#define        ALIGN(x)                x __attribute__((aligned(16)))
 
-typedef struct usb_hpna
-{
-       struct usb_device       *usb_dev;
-       struct net_device       *net_dev;
-       int                     present;
-       int                     active;
-       void                    *irq_handler;
-       struct list_head        list;
+struct pegasus {
+       struct usb_device       *usb;
+       struct net_device       *net;
        struct net_device_stats stats;
-       spinlock_t              hpna_lock;
-       struct timer_list       timer;
-
-       unsigned int            rx_pipe;
-       unsigned char *         rx_buff;
-       urb_t                   rx_urb;
-
-       unsigned int            tx_pipe;
-       unsigned char *         tx_buff;
-       urb_t                   tx_urb;
-       struct sk_buff *        tx_skbuff;
-
-       __u8                    intr_ival;
-       unsigned int            intr_pipe;
-       unsigned char           intr_buff[8];
-       urb_t                   intr_urb;
-} usb_hpna_t;
-
-
-usb_hpna_t     usb_dev_hpna;
-static int     loopback = 0;
-int            multicast_filter_limit = 32;
-static LIST_HEAD(hpna_list);
+       spinlock_t              pegasus_lock;
+       struct urb              rx_urb, tx_urb, intr_urb;
+       unsigned char           ALIGN(rx_buff[PEGASUS_MAX_MTU]); 
+       unsigned char           ALIGN(tx_buff[PEGASUS_MAX_MTU]); 
+       unsigned char           ALIGN(intr_buff[8]);
+};
 
+static int loopback = 0;
+static int multicast_filter_limit = 32;
 
 MODULE_AUTHOR("Petko Manolov <petkan@spct.net>");
-MODULE_DESCRIPTION("ADMtek \"Pegasus\" USB Ethernet driver");
+MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver");
 MODULE_PARM(loopback, "i");
 
 
-
-/*** vendor specific commands ***/
-static __inline__ int  hpna_get_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
-{
-       return  usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0,
-                               indx, data, size, HZ);
-}
-
-
-static __inline__ int  hpna_set_register( struct usb_device *dev, __u16 indx, __u8 value )
-{
-       __u8    data = value;
-       return  usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40,
-                               data, indx, &data, 1, HZ);
-}
+#define pegasus_get_registers(dev, indx, size, data)\
+       usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ);
+#define pegasus_set_registers(dev, indx, size, data)\
+       usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ);
+#define pegasus_set_register(dev, indx, value) \
+       { __u8  data = value;                   \
+       usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, data, indx, &data, 1, HZ);}
 
 
-static __inline__ int  hpna_set_registers( struct usb_device *dev, __u16 indx, __u16 size, void *data )
+static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata)
 {
-       return  usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0,
-                               indx, data, size, HZ);
-}
-
-
-static int read_phy_word( struct usb_device *dev, __u8 index, __u16 *regdata )
-{
-       int     i;
-       __u8    data[4];
-
-       data[0] = 1;
-       data[1] = 0;
-       data[2] = 0;
-       data[3] = 0x40 + index;
-       hpna_set_registers( dev, 0x25, 4, data );
-       for ( i=0; i<100; i++ ) {
-               hpna_get_registers( dev, 0x25, 4, data );
-               if ( data[3] & 0x80 ) {
-                       *regdata = *(__u16 *)(data+1);
-                       return  0;
+       int i;
+       __u8 data[4] = { 1, 0, 0, 0x40 + index };
+
+       pegasus_set_registers(dev, 0x25, 4, data);
+       for (i = 0; i < 100; i++) {
+               pegasus_get_registers(dev, 0x26, 3, data);
+               if (data[2] & 0x80) {
+                       *regdata = *(__u16 *)(data);
+                       return 0;
                }
                udelay(100);
        }
+
        warn("read_phy_word() failed");
-       return  1;
+       return 1;
 }
 
-
-static int write_phy_word( struct usb_device *dev, __u8 index, __u16 regdata )
+static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata)
 {
-       int     i;
-       __u8    data[4];
-
-       data[0] = 1;
-       data[1] = regdata;
-       data[2] = regdata >> 8;
-       data[3] = 0x20 + index;
-       hpna_set_registers( dev, 0x25, 4, data );
-       for ( i=0; i<100; i++ ) {
-               hpna_get_registers( dev, 0x28, 1, data );
-               if ( data[0] & 0x80 ) {
-                       return  0;
-               }
+       int i;
+       __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index };
+
+       pegasus_set_registers(dev, 0x25, 4, data);
+       for (i = 0; i < 100; i++) {
+               pegasus_get_registers(dev, 0x28, 1, data);
+               if (data[0] & 0x80)
+                       return 0;
                udelay(100);
        }
+
        warn("write_phy_word() failed");
-       return  1;
+       return 1;
 }
 
-
-int read_srom_word( struct usb_device *dev, __u8 index, __u16 *retdata)
+static int pegasus_read_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata)
 {
-       int     i;
-       __u8    data[4];
-
-       data[0] = index;
-       data[1] = data[2] = 0;
-       data[3] = 0x02;
-       hpna_set_registers(dev, 0x20, 4, data);
-       for ( i=0; i<100; i++ ) {
-               hpna_get_registers(dev, 0x23, 1, data);
-               if ( data[0] & 4 ) {
-                       hpna_get_registers(dev, 0x21, 2, data);
+       int i;
+       __u8 data[4] = { index, 0, 0, 0x02 };
+
+       pegasus_set_registers(dev, 0x20, 4, data);
+       for (i = 0; i < 100; i++) {
+               pegasus_get_registers(dev, 0x23, 1, data);
+               if (data[0] & 4) {
+                       pegasus_get_registers(dev, 0x21, 2, data);
                        *retdata = *(__u16 *)data;
-                       return  0;              
+                       return 0;
                }
        }
+
        warn("read_srom_word() failed");
-       return  1;
+       return 1;
 }
-/*** end ***/
-
-
 
-
-int get_node_id( struct usb_device *dev, __u8 *id )
+static int pegasus_get_node_id(struct usb_device *dev, __u8 *id)
 {
-       int     i;
-
-       for ( i=0; i<3; i++ ) {
-               if ( read_srom_word(dev, i, (__u16 *)&id[i*2] ) )
-                       return  1;
-       }
-       return  0;
+       int i;
+       for (i = 0; i < 3; i++)
+               if (pegasus_read_srom_word(dev, i, (__u16 *)&id[i * 2]))
+                       return 1;
+       return 0;
 }
 
-
-static int reset_mac( struct usb_device *dev )
+static int pegasus_reset_mac(struct usb_device *dev)
 {
-       __u8    data = 0x8;
-       int     i;
-       
-       hpna_set_register( dev, 1, 0x08 );
-       for ( i=0; i<100; i++ ) {
-               hpna_get_registers( dev, 1, 1, &data);
-               if ( !(data & 0x08) ) {
-                       if ( loopback & 1 )
-                               return  0;
-                       else if ( loopback & 2 ) {
-                               write_phy_word( dev, 0, 0x4000 );
-                               /*return        0;*/
-                       }
-                       hpna_set_register( dev, 0x7e, 0x24 );
-                       hpna_set_register( dev, 0x7e, 0x27 );
-                       return  0;
+       __u8 data = 0x8;
+       int i;
+
+       pegasus_set_register(dev, 1, data);
+       for (i = 0; i < 100; i++) {
+               pegasus_get_registers(dev, 1, 1, &data);
+               if (~data & 0x08) {
+                       if (loopback & 1) 
+                               return 0;
+                       if (loopback & 2) 
+                               pegasus_write_phy_word(dev, 0, 0x4000);
+                       pegasus_set_register(dev, 0x7e, 0x24);
+                       pegasus_set_register(dev, 0x7e, 0x27);
+                       return 0;
                }
        }
+
        return 1;
 }
 
-
-int start_net( struct net_device *dev, struct usb_device *usb_dev )
+static int pegasus_start_net(struct net_device *dev, struct usb_device *usb)
 {
-       __u16   partmedia, temp;
-       __u8    node_id[6];
-       __u8    data[4];
-       
-       if ( get_node_id(usb_dev, node_id) )
-               return  1;
-       hpna_set_registers(usb_dev, 0x10, 6, node_id);
+       __u16 partmedia, temp;
+       __u8 node_id[6];
+       __u8 data[4];
+
+       if (pegasus_get_node_id(usb, node_id)) 
+               return 1;
+
+       pegasus_set_registers(usb, 0x10, 6, node_id);
        memcpy(dev->dev_addr, node_id, 6);
-       if ( read_phy_word(usb_dev, 1, &temp) )
-               return  2;
-       if ( !(temp & 4) ) {
-               if ( loopback )
-                       goto ok;
+       if (pegasus_read_phy_word(usb, 1, &temp)) 
+               return 2;
+
+       if ((~temp & 4) && !loopback) {
                err("link NOT established - %x", temp);
-               return  3;
-       }
-ok:
-       if ( read_phy_word(usb_dev, 5, &partmedia) )
-               return  4;
-       temp = partmedia;
-       partmedia &= 0x1f;
-       if ( partmedia != 1 ) {
-               err("party FAIL %x", temp);
-               return  5;
+               return 3;
        }
-       partmedia = temp;
-       if ( partmedia & 0x100 )
-               data[1] = 0x30;
-       else {
-               if ( partmedia & 0x80 )
-                       data[1] = 0x10;
-               else 
-                       data[1] = 0;
+
+       if (pegasus_read_phy_word(usb, 5, &partmedia))
+               return 4;
+
+       if ((partmedia & 0x1f) != 1) {
+               err("party FAIL %x", partmedia);
+               return 5;
        }
-       
-       data[0] = 0xc9;
-       data[2] = (loopback & 1) ? 0x08 : 0x00; 
-       
-       hpna_set_registers(usb_dev, 0, 3, data);
-       
-       return  0;
-}
 
+       data[0] = 0xc9;
+       data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0);
+       data[2] = (loopback & 1) ? 0x08 : 0x00;
 
-static void hpna_read_irq( purb_t urb )
-{
-       struct net_device *net_dev = urb->context;
-       usb_hpna_t *hpna = net_dev->priv;
-       int     count = urb->actual_length, res;
-       int     rx_status = *(int *)(hpna->rx_buff + count - 4);
+       pegasus_set_registers(usb, 0, 3, data);
 
+       return 0;
+}
 
-       if ( urb->status ) {
-               info( "%s: RX status %d\n", net_dev->name, urb->status );
+static void pegasus_read_bulk(struct urb *urb)
+{
+       struct pegasus *pegasus = urb->context;
+       struct net_device *net = pegasus->net;
+       int count = urb->actual_length, res;
+       int rx_status = *(int *)(pegasus->rx_buff + count - 4);
+       struct sk_buff  *skb;
+       __u16 pkt_len;
+
+       if (urb->status) {
+               info("%s: RX status %d", net->name, urb->status);
                goto goon;
        }
 
-       if ( !count )
+       if (!count)
                goto goon;
-/*     if ( rx_status & 0x00010000 )
+#if 0
+       if (rx_status & 0x00010000)
+               goto goon;
+#endif
+       if (rx_status & 0x000e0000) {
+
+               dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000);
+               pegasus->stats.rx_errors++;
+               if(rx_status & 0x060000) pegasus->stats.rx_length_errors++;
+               if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++;
+               if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++;
+
                goto goon;
-*/     
-       if ( rx_status & 0x000e0000 ) {
-               dbg("%s: error receiving packet %x",
-                       net_dev->name, rx_status & 0xe0000);
-               hpna->stats.rx_errors++;
-               if(rx_status & 0x060000) hpna->stats.rx_length_errors++;
-               if(rx_status & 0x080000) hpna->stats.rx_crc_errors++;
-               if(rx_status & 0x100000) hpna->stats.rx_frame_errors++;
-       } else {
-               struct sk_buff  *skb;
-               __u16           pkt_len = (rx_status & 0xfff) - 8;
-
-
-               if((skb = dev_alloc_skb(pkt_len+2)) != NULL ) {
-                       skb->dev = net_dev;
-                       skb_reserve(skb, 2);
-                       eth_copy_and_sum(skb, hpna->rx_buff, pkt_len, 0);
-                       skb_put(skb, pkt_len);
-               } else
-                       goto    goon;
-               skb->protocol = eth_type_trans(skb, net_dev);
-               netif_rx(skb);
-               hpna->stats.rx_packets++;
-               hpna->stats.rx_bytes += pkt_len;
        }
+
+       pkt_len = (rx_status & 0xfff) - 8;
+
+       if(!(skb = dev_alloc_skb(pkt_len+2)))
+               goto goon;
+
+       skb->dev = net;
+       skb_reserve(skb, 2);
+       eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0);
+       skb_put(skb, pkt_len);
+
+       skb->protocol = eth_type_trans(skb, net);
+       netif_rx(skb);
+       pegasus->stats.rx_packets++;
+       pegasus->stats.rx_bytes += pkt_len;
+
 goon:
-       if ( (res = usb_submit_urb( &hpna->rx_urb )) )
-               warn("failed rx_urb %d", res);
+       if ((res = usb_submit_urb(&pegasus->rx_urb)))
+               warn("(prb)failed rx_urb %d", res);
 }
 
-
-static void hpna_irq( urb_t *urb)
+static void pegasus_irq(urb_t *urb)
 {
-       if( urb->status ) {
+       if(urb->status) {
                __u8    *d = urb->transfer_buffer;
                printk("txst0 %x, txst1 %x, rxst %x, rxlst0 %x, rxlst1 %x, wakest %x",
-                       d[0], d[1], d[2], d[3], d[4], d[5] );
+                       d[0], d[1], d[2], d[3], d[4], d[5]);
        }
 }
 
-
-static void hpna_write_irq( purb_t urb )
+static void pegasus_write_bulk(struct urb *urb)
 {
-       struct net_device *net_dev = urb->context;
-       usb_hpna_t *hpna = net_dev->priv;
+       struct pegasus *pegasus = urb->context;
 
+       spin_lock(&pegasus->pegasus_lock);
 
-       spin_lock( &hpna->hpna_lock );
-       
-       if ( urb->status ) 
-               info("%s: TX status %d\n", net_dev->name, urb->status);
-       netif_wake_queue( net_dev );
+       if (urb->status)
+               info("%s: TX status %d", pegasus->net->name, urb->status);
+       netif_wake_queue(pegasus->net);
 
-       spin_unlock( &hpna->hpna_lock );
+       spin_unlock(&pegasus->pegasus_lock);
 }
 
-
-static void tx_timeout( struct net_device *dev )
+static void pegasus_tx_timeout(struct net_device *net)
 {
-       usb_hpna_t      *hpna = dev->priv;
+       struct pegasus *pegasus = net->priv;
 
-       warn( "%s: Tx timed out. Reseting...", dev->name );
-       hpna->stats.tx_errors++;
-       dev->trans_start = jiffies;
-       netif_wake_queue( dev );
-}
+       warn("%s: Tx timed out. Reseting...", net->name);
+       pegasus->stats.tx_errors++;
+       net->trans_start = jiffies;
 
+       netif_wake_queue(net);
+}
 
-static int hpna_start_xmit( struct sk_buff *skb, struct net_device *net_dev )
+static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net)
 {
-       usb_hpna_t      *hpna = (usb_hpna_t *)net_dev->priv;
-       int             count = skb->len+2 % 64 ? skb->len+2 : skb->len+3;
-       int             res;
+       struct pegasus  *pegasus = net->priv;
+       int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3;
+       int res;
 
-       spin_lock( &hpna->hpna_lock );
+       spin_lock(&pegasus->pegasus_lock);
 
-       netif_stop_queue( net_dev );
-       ((__u16 *)hpna->tx_buff)[0] = skb->len;
-       memcpy(hpna->tx_buff+2, skb->data, skb->len);
-       (&hpna->tx_urb)->transfer_buffer_length = count;
-       if ( (res = usb_submit_urb( &hpna->tx_urb )) ) {
+       netif_stop_queue(net);
+
+       ((__u16 *)pegasus->tx_buff)[0] = skb->len;
+       memcpy(pegasus->tx_buff+2, skb->data, skb->len);
+       (&pegasus->tx_urb)->transfer_buffer_length = count;
+
+       if ((res = usb_submit_urb(&pegasus->tx_urb))) {
                warn("failed tx_urb %d", res);
-               hpna->stats.tx_errors++;
-               netif_start_queue( net_dev );
+               pegasus->stats.tx_errors++;
+               netif_start_queue(net);
        } else {
-               hpna->stats.tx_packets++;
-               hpna->stats.tx_bytes += skb->len;
-               net_dev->trans_start = jiffies;
+               pegasus->stats.tx_packets++;
+               pegasus->stats.tx_bytes += skb->len;
+               net->trans_start = jiffies;
        }
-       dev_kfree_skb( skb );
-       spin_unlock( &hpna->hpna_lock );
-       return  0;
-}
 
+       dev_kfree_skb(skb);
 
-static struct net_device_stats *hpna_netdev_stats( struct net_device *dev )
-{
-       return  &((usb_hpna_t *)dev->priv)->stats;
+       spin_unlock(&pegasus->pegasus_lock);
+
+       return 0;
 }
 
-static int hpna_open( struct net_device *net_dev )
+static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev)
 {
-       usb_hpna_t *hpna = (usb_hpna_t *)net_dev->priv;
-       int     res;
+       return &((struct pegasus *)dev->priv)->stats;
+}
 
-       if ( hpna->active )
-               return  -EBUSY;
-       else
-               hpna->active = 1;
+static int pegasus_open(struct net_device *net)
+{
+       struct pegasus *pegasus = (struct pegasus *)net->priv;
+       int res;
 
-       if ( start_net(net_dev, hpna->usb_dev) ) {
-               err("can't start_net()");
-               return  -EIO;
+       if ((res = pegasus_start_net(net, pegasus->usb))) {
+               err("can't start_net() - %d", res);
+               return -EIO;
        }
 
-       if ( (res = usb_submit_urb( &hpna->rx_urb )) )
-               warn("failed rx_urb %d", res);
+       if ((res = usb_submit_urb(&pegasus->rx_urb)))
+               warn("(open)failed rx_urb %d", res);
 
-/*     usb_submit_urb( &hpna->intr_urb );*/
-       netif_start_queue( net_dev );
+/*     usb_submit_urb(&pegasus->intr_urb);*/
+       netif_start_queue(net);
 
        MOD_INC_USE_COUNT;
 
        return 0;
 }
 
-
-static int hpna_close( struct net_device *net_dev )
+static int pegasus_close(struct net_device *net)
 {
-       usb_hpna_t      *hpna = net_dev->priv;
-
-
-       netif_stop_queue( net_dev );
+       struct pegasus  *pegasus = net->priv;
 
-       usb_unlink_urb( &hpna->rx_urb );
-       usb_unlink_urb( &hpna->tx_urb );
-/*     usb_unlink_urb( hpna->intr_urb );*/
+       netif_stop_queue(net);
 
-       hpna->active = 0;
+       usb_unlink_urb(&pegasus->rx_urb);
+       usb_unlink_urb(&pegasus->tx_urb);
+/*     usb_unlink_urb(&pegasus->intr_urb); */
 
        MOD_DEC_USE_COUNT;
 
        return 0;
 }
 
-
-static int hpna_ioctl( struct net_device *dev, struct ifreq *rq, int cmd )
+static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
 {
-       __u16   *data = (__u16 *)&rq->ifr_data;
-       usb_hpna_t      *hpna = dev->priv;
+       __u16 *data = (__u16 *)&rq->ifr_data;
+       struct pegasus  *pegasus = net->priv;
 
-       switch( cmd ) {
-               case SIOCDEVPRIVATE: 
+       switch(cmd) {
+               case SIOCDEVPRIVATE:
                        data[0] = 1;
                case SIOCDEVPRIVATE+1:
-                       read_phy_word(hpna->usb_dev, data[1] & 0x1f, &data[3]);
-                       return  0;
+                       pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]);
+                       return 0;
                case SIOCDEVPRIVATE+2:
-                       if ( !capable(CAP_NET_ADMIN) )
-                               return  -EPERM;
-                       write_phy_word(hpna->usb_dev, data[1] & 0x1f, data[2]);
-                       return  0;
+                       if (!capable(CAP_NET_ADMIN))
+                               return -EPERM;
+                       pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]);
+                       return 0;
                default:
-                       return  -EOPNOTSUPP;
+                       return -EOPNOTSUPP;
        }
 }
 
-
-static void set_rx_mode( struct net_device *net_dev )
+static void pegasus_set_rx_mode(struct net_device *net)
 {
-       usb_hpna_t      *hpna=net_dev->priv;
+       struct pegasus *pegasus = net->priv;
 
-       netif_stop_queue( net_dev );
-       
-       if ( net_dev->flags & IFF_PROMISC ) {
-               info("%s: Promiscuous mode enabled", net_dev->name);
-               hpna_set_register( hpna->usb_dev, 2, 0x04 );
-       } else if ((net_dev->mc_count > multicast_filter_limit) ||
-                       (net_dev->flags & IFF_ALLMULTI)) {
-               hpna_set_register(hpna->usb_dev, 0, 0xfa);
-               hpna_set_register(hpna->usb_dev, 2, 0);
+       netif_stop_queue(net);
+
+       if (net->flags & IFF_PROMISC) {
+               info("%s: Promiscuous mode enabled", net->name);
+               pegasus_set_register(pegasus->usb, 2, 0x04);
+       } else if ((net->mc_count > multicast_filter_limit) ||
+                       (net->flags & IFF_ALLMULTI)) {
+               pegasus_set_register(pegasus->usb, 0, 0xfa);
+               pegasus_set_register(pegasus->usb, 2, 0);
        } else {
-               dbg("%s: set Rx mode", net_dev->name);
+               dbg("%s: set Rx mode", net->name);
        }
 
-       netif_wake_queue( net_dev );
+       netif_wake_queue(net);
 }
 
-
-static void * usb_hpna_probe( struct usb_device *dev, unsigned int ifnum )
+static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum)
 {
-       struct net_device               *net_dev;
-       usb_hpna_t                      *hpna = &usb_dev_hpna;
-
+       struct net_device *net;
+       struct pegasus *pegasus;
 
-
-       if ( dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
-            dev->descriptor.idProduct != ADMTEK_HPNA_PEGASUS ) {
-               return  NULL;
+       if (dev->descriptor.idVendor != ADMTEK_VENDOR_ID ||
+               dev->descriptor.idProduct != ADMTEK_DEVICE_ID_PEGASUS) {
+               return NULL;
        }
 
-       printk("USB HPNA Pegasus found\n");
-
-       if ( usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
+       if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) {
                err("usb_set_configuration() failed");
                return NULL;
        }
 
-       hpna->usb_dev = dev;
-
-       hpna->rx_pipe = usb_rcvbulkpipe(hpna->usb_dev, 1);
-       hpna->tx_pipe = usb_sndbulkpipe(hpna->usb_dev, 2);
-       hpna->intr_pipe = usb_rcvintpipe(hpna->usb_dev, 0);
-
-       if ( reset_mac(dev) ) {
-               err("can't reset MAC");
+       if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) {
+               err("out of memory allocating device structure");
+               return NULL;
        }
+       memset(pegasus, 0, sizeof(struct pegasus));
 
-       hpna->present = 1;
-
-       if(!(hpna->rx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
-               err("not enough mem for out buff");
-               return  NULL;
-       }
-       if(!(hpna->tx_buff=kmalloc(MAX_MTU, GFP_KERNEL))) {
-               kfree_s(hpna->rx_buff, MAX_MTU);
-               err("not enough mem for out buff");
-               return  NULL;
+       if (pegasus_reset_mac(dev)) {
+               err("can't reset MAC");
+               kfree(pegasus);
+               return NULL;
        }
-
-       net_dev = init_etherdev( 0, 0 );
-       hpna->net_dev = net_dev;
-       net_dev->priv = hpna;
-       net_dev->open = hpna_open;
-       net_dev->stop = hpna_close;
-       net_dev->watchdog_timeo = TX_TIMEOUT;
-       net_dev->tx_timeout = tx_timeout;
-       net_dev->do_ioctl = hpna_ioctl; 
-       net_dev->hard_start_xmit = hpna_start_xmit;
-       net_dev->set_multicast_list = set_rx_mode;
-       net_dev->get_stats = hpna_netdev_stats; 
-       net_dev->mtu = HPNA_MTU;
-       hpna->hpna_lock = SPIN_LOCK_UNLOCKED;
-       
-       FILL_BULK_URB( &hpna->rx_urb, hpna->usb_dev, hpna->rx_pipe, 
-                       hpna->rx_buff, MAX_MTU, hpna_read_irq, net_dev );
-       FILL_BULK_URB( &hpna->tx_urb, hpna->usb_dev, hpna->tx_pipe, 
-                       hpna->tx_buff, MAX_MTU, hpna_write_irq, net_dev );
-       FILL_INT_URB( &hpna->intr_urb, hpna->usb_dev, hpna->intr_pipe,
-                       hpna->intr_buff, 8, hpna_irq, net_dev, 250 );
        
-/*     list_add( &hpna->list, &hpna_list );*/
-       
-       return  net_dev; 
+       net = init_etherdev(0, 0);
+       net->priv = pegasus;
+       net->open = pegasus_open;
+       net->stop = pegasus_close;
+       net->watchdog_timeo = PEGASUS_TX_TIMEOUT;
+       net->tx_timeout = pegasus_tx_timeout;
+       net->do_ioctl = pegasus_ioctl;
+       net->hard_start_xmit = pegasus_start_xmit;
+       net->set_multicast_list = pegasus_set_rx_mode;
+       net->get_stats = pegasus_netdev_stats;
+       net->mtu = PEGASUS_MTU;
+
+       pegasus->usb = dev;
+       pegasus->net = net;
+       pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED;
+
+       FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1),
+                       pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, 
+                       pegasus);
+       FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2),
+                       pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk,
+                       pegasus);
+       FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 0),
+                       pegasus->intr_buff, 8, pegasus_irq, pegasus, 250);
+
+
+       printk(KERN_INFO "%s: ADMtek AN986 Pegasus usb device\n", net->name);
+
+       return pegasus;
 }
 
-
-static void usb_hpna_disconnect( struct usb_device *dev, void *ptr )
+static void pegasus_disconnect(struct usb_device *dev, void *ptr)
 {
-       struct net_device       *net_dev = ptr;
-       struct usb_hpna         *hpna = net_dev->priv;
+       struct pegasus *pegasus = ptr;
 
+       if (!pegasus) {
+               warn("unregistering non-existant device");
+               return;
+       }
 
-       if ( net_dev->flags & IFF_UP )
-               dev_close(net_dev);
-       
-       unregister_netdev( net_dev );
+       if (pegasus->net->flags & IFF_UP)
+               dev_close(pegasus->net);
 
-       if ( !hpna ) /* should never happen */
-               return;
-               
-       usb_unlink_urb( &hpna->rx_urb );
-       usb_unlink_urb( &hpna->tx_urb );
-/*     usb_unlink_urb( &hpna->intr_urb );*/
-       kfree_s(hpna->rx_buff, MAX_MTU);
-       kfree_s(hpna->tx_buff, MAX_MTU);
+       unregister_netdev(pegasus->net);
 
-       hpna->usb_dev = NULL;
-       hpna->present = 0;
+       usb_unlink_urb(&pegasus->rx_urb);
+       usb_unlink_urb(&pegasus->tx_urb);
+/*     usb_unlink_urb(&pegasus->intr_urb);*/
 
-       printk("USB HPNA disconnected\n");
+       kfree(pegasus);
 }
 
-
-static struct usb_driver usb_hpna_driver = {
-       "ADMtek \"Pegasus\" USB Ethernet",
-       usb_hpna_probe,
-       usb_hpna_disconnect,
-       {NULL, NULL}
+static struct usb_driver pegasus_driver = {
+       name:           "pegasus",
+       probe:          pegasus_probe,
+       disconnect:     pegasus_disconnect,
 };
 
-
-
-static int  __init start_hpna( void )
+int __init pegasus_init(void)
 {
        printk( version );
-       return  usb_register( &usb_hpna_driver );
+       return usb_register(&pegasus_driver);
 }
 
-
-static void __exit stop_hpna( void )
+void __exit pegasus_exit(void)
 {
-       usb_deregister( &usb_hpna_driver );
+       usb_deregister(&pegasus_driver);
 }
 
-
-module_init( start_hpna );
-module_exit( stop_hpna );
+module_init(pegasus_init);
+module_exit(pegasus_exit);
index c3b4ebc2e42dd236f073457abe70fed24470497d..480c6252df273fc6132119625bfc55135c6ceb43 100644 (file)
@@ -236,7 +236,7 @@ static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td)
 static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth)
 {
        struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
-       struct uhci_td *td, *prevtd = NULL;
+       struct uhci_td *td, *prevtd;
 
        if (!urbp)
                return;
@@ -617,6 +617,8 @@ static int uhci_submit_control(urb_t *urb)
        return -EINPROGRESS;
 }
 
+static int usb_control_retrigger_status(urb_t *urb);
+
 static int uhci_result_control(urb_t *urb)
 {
        struct urb_priv *urbp = urb->hcpriv;
@@ -630,6 +632,9 @@ static int uhci_result_control(urb_t *urb)
        if (!td)
                return -EINVAL;
 
+       if (urbp->short_control_packet)
+               goto status_phase;
+
        /* The first TD is the SETUP phase, check the status, but skip */
        /*  the count */
        status = uhci_status_bits(td->status);
@@ -653,10 +658,9 @@ static int uhci_result_control(urb_t *urb)
 
                /* If SPD is set then we received a short packet */
                /*  There will be no status phase at the end */
-               /* FIXME: Re-setup the queue to run the STATUS phase? */
                if ((td->status & TD_CTRL_SPD) &&
                    (uhci_actual_length(td->status) < uhci_expected_length(td->info)))
-                       return 0;
+                       return usb_control_retrigger_status(urb);
 
                if (status)
                        goto td_error;
@@ -664,12 +668,13 @@ static int uhci_result_control(urb_t *urb)
                td = td->list.next;
        }
 
+status_phase:
        /* Control status phase */
        status = uhci_status_bits(td->status);
 
        /* APC BackUPS Pro kludge */
-       /* It tries to send all of the descriptor instead of */
-       /*  the amount we requested */
+       /* It tries to send all of the descriptor instead of the amount */
+       /*  we requested */
        if (td->status & TD_CTRL_IOC &&
            status & TD_CTRL_ACTIVE &&
            status & TD_CTRL_NAK)
@@ -700,6 +705,47 @@ td_error:
        return uhci_map_status(status, uhci_packetout(td->info));
 }
 
+static int usb_control_retrigger_status(urb_t *urb)
+{
+       struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv;
+       struct uhci *uhci = urb->dev->bus->hcpriv;
+       struct uhci_td *td, *nexttd;
+
+       urbp->short_control_packet = 1;
+
+       /* Delete all of the TD's except for the status TD at the end */
+       td = urbp->list.begin;
+       while (td && td->list.next) {
+               nexttd = td->list.next;
+
+               uhci_remove_td_from_urb(urb, td);
+
+               uhci_remove_td(uhci, td);
+
+               uhci_free_td(td);
+               
+               td = nexttd;
+       }
+
+       /* Create a new QH to avoid pointer overwriting problems */
+       uhci_remove_qh(uhci, urbp->qh);
+
+       urbp->qh = uhci_alloc_qh(urb->dev);
+       if (!urbp->qh)
+               return -ENOMEM;
+
+       /* One TD, who cares about Breadth first? */
+       uhci_insert_tds_in_qh(urbp->qh, urb, 0);
+
+       /* Low speed or small transfers gets a different queue and treatment */
+       if (urb->pipe & TD_CTRL_LS)
+               uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh);
+       else
+               uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh);
+
+       return -EINPROGRESS;
+}
+
 /*
  * Interrupt transfers
  */
index 62d4e772e195c061453e79e5c68da418c7bc7d14..9f4c45e9633574832731e0976efaa0b37626ac68 100644 (file)
@@ -338,7 +338,11 @@ struct uhci {
 struct urb_priv {
        struct uhci_qh *qh;             /* QH for this URB */
 
-       int fsbr;
+       int fsbr;                       /* Did this URB turn on FSBR? */
+
+       char short_control_packet;      /* If we get a short packet during */
+                                       /*  a control transfer, retrigger */
+                                       /*  the status phase */
 
        unsigned long inserttime;       /* In jiffies */
 
index 0e2ab20a4a6c060be556ee607ab57ac899d79718..956e80a0abc6353199e6232981fa99436b1a00a7 100644 (file)
@@ -6,9 +6,9 @@
  * Further reference:
  *     This driver is based on the 'USB Mass Storage Class' document. This
  *     describes in detail the protocol used to communicate with such
- *      devices.  Clearly, the designers had SCSI commands in mind when they
- *      created this document.  The commands are all similar to commands
- *      in the SCSI-II specification.
+ *      devices.  Clearly, the designers had SCSI and ATAPI commands in mind 
+ *      when they created this document.  The commands are all very similar 
+ *      to commands in the SCSI-II and ATAPI specifications.
  *
  *     It is important to note that in a number of cases this class exhibits
  *     class-specific exemptions from the USB specification. Notably the
@@ -65,8 +65,6 @@ unsigned char us_direction[256/8] = {
 
 static int my_host_number;
 
-int usb_stor_debug = 1;
-
 struct us_data;
 
 typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -74,7 +72,7 @@ typedef int (*trans_reset)(struct us_data*);
 typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
 
 struct us_data {
-       struct us_data  *next;                   /* next device */
+       struct us_data          *next;           /* next device */
        struct usb_device       *pusb_dev;       /* this usb_device */
        unsigned int            flags;           /* from filter initially */
        __u8                    ifnum;           /* interface number */
@@ -93,15 +91,17 @@ struct us_data {
        int                     host_number;     /* to find us */
        int                     host_no;         /* allocated by scsi */
        Scsi_Cmnd               *srb;            /* current srb */
+       Scsi_Cmnd               *queue_srb;      /* the single queue slot */
        int                     action;          /* what to do */
-       wait_queue_head_t       waitq;           /* thread waits */
-       wait_queue_head_t       ip_waitq;        /* for CBI interrupts */
+       struct semaphore        ip_waitq;        /* for CBI interrupts */
        __u16                   ip_data;         /* interrupt data */
        int                     ip_wanted;       /* needed */
        int                     pid;             /* control thread */
        struct semaphore        *notify;         /* wait for thread to begin */
        void                    *irq_handle;     /* for USB int requests */
        unsigned int            irqpipe;         /* pipe for release_irq */
+       struct semaphore        sleeper;         /* to sleep on */
+       struct semaphore        queue_exclusion; /* to protect data structs */
 };
 
 /*
@@ -129,117 +129,100 @@ static struct usb_driver storage_driver = {
  * Data transfer routines
  ***********************************************************************/
 
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory.  Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
  *
- * FIXME: is the above true?  Or will the URB status show ETIMEDOUT after
- * retrying several times allready?  Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit.  Thus we don't have to worry about it for individual
+ * packets.
  */
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe, 
+                           char *buf, int length)
 {
-       int max_size;
-       int this_xfer;
        int result;
        int partial;
-       int maxtry;
-
-       /* determine the maximum packet size for these transfers */
-       max_size = usb_maxpacket(us->pusb_dev, 
-                                pipe, usb_pipeout(pipe)) * 16;
-
-       /* while we have data left to transfer */
-       while (length) {
-
-               /* calculate how long this will be -- maximum or a remainder */
-               this_xfer = length > max_size ? max_size : length;
-               length -= this_xfer;
-
-               /* FIXME: this number is totally outrageous.  We need to pick
-                * a better (smaller) number).
-                */
-
-               /* setup the retry counter */
-               maxtry = 100;
-
-               /* set up the transfer loop */
-               do {
-                       /* transfer the data */
-                       US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n", 
-                                 (unsigned int)buf, this_xfer, 101 - maxtry);
-                       result = usb_bulk_msg(us->pusb_dev, pipe, buf,
-                                             this_xfer, &partial, HZ*5);
-                       US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
-                                 result, partial, this_xfer);
-
-                       /* if we stall, we need to clear it before we go on */
-                       if (result == -EPIPE) {
-                               US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
-                               usb_clear_halt(us->pusb_dev, pipe);
-                       }
-
-                       /* update to show what data was transferred */
-                       this_xfer -= partial;
-                       buf += partial;
-
-                       /* NAK - we retry a few times */
-                       if (result == -ETIMEDOUT) {
 
-                               US_DEBUGP("us_one_transfer: device NAKed\n");
-
-                               /* if our try counter reaches 0, bail out */
-                               if (!maxtry--)
-                                       return -ETIMEDOUT;
+       /* transfer the data */
+       US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
+       result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, HZ*5);
+       US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+                 result, partial, length);
+       
+       /* if we stall, we need to clear it before we go on */
+       if (result == -EPIPE) {
+               US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+               usb_clear_halt(us->pusb_dev, pipe);
+       }
 
-                               /* just continue the while loop */
-                               continue;
-                       }
-      
-                       /* other errors (besides NAK) -- we just bail out*/
-                       if (result != 0) {
-                               US_DEBUGP("us_one_transfer: device returned error %d\n", result);
-                               return result;
-                       }
+       /* did we send all the data? */
+       if (partial == length) {
+               return US_BULK_TRANSFER_GOOD;
+       }
 
-                       /* continue until this transfer is done */
-               } while ( this_xfer );
+       /* uh oh... we have an error code, so something went wrong. */
+       if (result) {
+               /* NAK - that means we've retried a few times allready */
+               if (result == -ETIMEDOUT) {
+                       US_DEBUGP("us_bulk_transfer: device NAKed\n");
+               }
+               return US_BULK_TRANSFER_FAILED;
        }
 
-       /* if we get here, we're done and successful */
-       return 0;
+       /* no error code, so we must have transferred some data, 
+        * just not all of it */
+       return US_BULK_TRANSFER_SHORT;
 }
 
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * function simply determines if we're going to use scatter-gather or not,
+ * and acts appropriately.  For now, it also re-interprets the error codes.
+ */
+static void us_transfer(Scsi_Cmnd *srb, int dir_in)
 {
-       struct us_data *us = (struct us_data *)srb->host_scribble;
+       struct us_data *us;
        int i;
        int result = -1;
-       unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
-               usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+       unsigned int pipe;
+       struct scatterlist *sg;
 
-       /* FIXME: stop transferring data at us_transfer_length(), not 
-        * bufflen */
+       /* calculate the appropriate pipe information */
+       us = (struct us_data*) srb->host_scribble;
+       if (dir_in)
+               pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+       else
+               pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+
+       /* are we scatter-gathering? */
        if (srb->use_sg) {
-               struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
 
+               /* loop over all the scatter gather structures and 
+                * make the appropriate requests for each, until done
+                */
+               sg = (struct scatterlist *) srb->request_buffer;
                for (i = 0; i < srb->use_sg; i++) {
-                       result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+                       result = us_bulk_transfer(us, pipe, sg[i].address, 
+                                                 sg[i].length);
                        if (result)
                                break;
                }
        }
        else
-               result = us_one_transfer(us, pipe, srb->request_buffer, 
-                                        us_transfer_length(srb));
+               /* no scatter-gather, just make the request */
+               result = us_bulk_transfer(us, pipe, srb->request_buffer, 
+                                         srb->request_bufflen);
 
-       if (result < 0)
-               US_DEBUGP("us_transfer returning error %d\n", result);
-       return result;
+       /* return the result in the data structure itself */
+       srb->result = result;
 }
 
 /* calculate the length of the data transfer (not the command) for any
@@ -265,6 +248,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
        case MODE_SENSE:
                return srb->cmnd[4];
 
+       case READ_CAPACITY:
+               return 8;
+
        case LOG_SENSE:
        case MODE_SENSE_10:
                return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -274,8 +260,9 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
        }
 
        if (srb->use_sg) {
-               struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+               struct scatterlist *sg;
 
+               sg = (struct scatterlist *) srb->request_buffer;
                for (i = 0; i < srb->use_sg; i++) {
                        total += sg[i].length;
                }
@@ -289,12 +276,148 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
  * Protocol routines
  ***********************************************************************/
 
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+       int old_cmnd = 0;
+       int result;
+  
+       /* Fix some commands -- this is a form of mode translation
+        * ATAPI devices only accept 12 byte long commands 
+        *
+        * NOTE: This only works because a Scsi_Cmnd struct field contains
+        * a unsigned char cmnd[12], so we know we have storage available
+        */
+
+       /* set command length to 12 bytes */
+       srb->cmd_len = 12;
+
+       /* determine the correct (or minimum) data length for these commands */
+       switch (us->srb->cmnd[0]) {
+
+               /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+       case MODE_SENSE:
+       case MODE_SELECT:
+               /* save the command so we can tell what it was */
+               old_cmnd = srb->cmnd[0];
+
+               srb->cmnd[11] = 0;
+               srb->cmnd[10] = 0;
+               srb->cmnd[9] = 0;
+               srb->cmnd[8] = srb->cmnd[4];
+               srb->cmnd[7] = 0;
+               srb->cmnd[6] = 0;
+               srb->cmnd[5] = 0;
+               srb->cmnd[4] = 0;
+               srb->cmnd[3] = 0;
+               srb->cmnd[2] = srb->cmnd[2];
+               srb->cmnd[1] = srb->cmnd[1];
+               srb->cmnd[0] = srb->cmnd[0] | 0x40;
+               break;
+
+               /* change READ_6/WRITE_6 to READ_10/WRITE_10, which 
+                * are ATAPI commands */
+       case WRITE_6:
+       case READ_6:
+               srb->cmnd[11] = 0;
+               srb->cmnd[10] = 0;
+               srb->cmnd[9] = 0;
+               srb->cmnd[8] = srb->cmnd[4];
+               srb->cmnd[7] = 0;
+               srb->cmnd[6] = 0;
+               srb->cmnd[5] = srb->cmnd[3];
+               srb->cmnd[4] = srb->cmnd[2];
+               srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+               srb->cmnd[2] = 0;
+               srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+               srb->cmnd[0] = srb->cmnd[0] | 0x20;
+               break;
+       } /* end switch on cmnd[0] */
+  
+       /* send the command to the transport layer */
+       result = us->transport(srb, us);
+
+       /* If we got a short transfer, but it was for a command that
+        * can have short transfers, we're actually okay
+        */
+       if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+           ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+            (us->srb->cmnd[0] == INQUIRY) ||
+            (us->srb->cmnd[0] == MODE_SENSE) ||
+            (us->srb->cmnd[0] == LOG_SENSE) ||
+            (us->srb->cmnd[0] == MODE_SENSE_10))) {
+               us->srb->result = DID_OK;
+       }
+
+       /*
+        * If we have an error, we're going to do a 
+        * REQUEST_SENSE automatically
+        */
+       if (result != USB_STOR_TRANSPORT_GOOD) {
+               int temp_result;
+               void* old_request_buffer;
+               int old_sg;
+
+               US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+               us->srb->cmnd[0] = REQUEST_SENSE;
+               us->srb->cmnd[1] = 0;
+               us->srb->cmnd[2] = 0;
+               us->srb->cmnd[3] = 0;
+               us->srb->cmnd[4] = 18;
+               us->srb->cmnd[5] = 0;
+    
+               /* set the buffer length for transfer */
+               old_request_buffer = us->srb->request_buffer;
+               old_sg = us->srb->use_sg;
+               us->srb->request_bufflen = 18;
+               us->srb->request_buffer = us->srb->sense_buffer;
+
+               /* FIXME: what if this command fails? */
+               temp_result = us->transport(us->srb, us);
+               US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+               US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+                         us->srb->sense_buffer[2] & 0xf,
+                         us->srb->sense_buffer[12], 
+                         us->srb->sense_buffer[13]);
+
+               /* set the result so the higher layers expect this data */
+               us->srb->result = CHECK_CONDITION;
+
+               /* we're done here */
+               us->srb->request_buffer = old_request_buffer;
+               us->srb->use_sg = old_sg;
+               return;
+       }
+  
+       /* Fix the MODE_SENSE data if we translated the command
+        */
+       if (old_cmnd == MODE_SENSE) {
+               unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+               /* FIXME: we need to compress the entire data structure here
+                */
+               dta[0] = dta[1];        /* data len */
+               dta[1] = dta[2];        /* med type */
+               dta[2] = dta[3];        /* dev-spec prm */
+               dta[3] = dta[7];        /* block desc len */
+               printk (KERN_DEBUG USB_STORAGE
+                       "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+                       dta[0], dta[1], dta[2], dta[3]);
+       }
+
+       /* Fix-up the return data from an INQUIRY command to show 
+        * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+        */
+       if (us->srb->cmnd[0] == INQUIRY) {
+               ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+       }
+}
+
 
 static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
        int old_cmnd = 0;
+       int result;
   
        /* fix some commands -- this is a form of mode translation
         * UFI devices only accept 12 byte long commands 
@@ -372,23 +495,31 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
        } /* end switch on cmnd[0] */
   
        /* send the command to the transport layer */
-       us->srb->result = us->transport(srb, us);
+       result = us->transport(srb, us);
 
-       /* if we have an error, we're going to do a 
-        * REQUEST_SENSE automatically */
+       /* If we got a short transfer, but it was for a command that
+        * can have short transfers, we're actually okay
+        */
+       if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+           ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+            (us->srb->cmnd[0] == INQUIRY) ||
+            (us->srb->cmnd[0] == MODE_SENSE) ||
+            (us->srb->cmnd[0] == LOG_SENSE) ||
+            (us->srb->cmnd[0] == MODE_SENSE_10))) {
+               us->srb->result = DID_OK;
+       }
 
-       /* FIXME: we should only do this for device 
-        * errors, not system errors */
-       if (us->srb->result) {
+       /*
+        * If we have an error, we're going to do a 
+        * REQUEST_SENSE automatically
+        */
+       if (result != USB_STOR_TRANSPORT_GOOD) {
                int temp_result;
-               int count;
                void* old_request_buffer;
+               int old_sg;
 
                US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-               /* set the result so the higher layers expect this data */
-               us->srb->result = CHECK_CONDITION;
-
                us->srb->cmnd[0] = REQUEST_SENSE;
                us->srb->cmnd[1] = 0;
                us->srb->cmnd[2] = 0;
@@ -398,49 +529,34 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
     
                /* set the buffer length for transfer */
                old_request_buffer = us->srb->request_buffer;
+               old_sg = us->srb->use_sg;
                us->srb->request_bufflen = 18;
-               us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+               us->srb->request_buffer = us->srb->sense_buffer;
 
                /* FIXME: what if this command fails? */
                temp_result = us->transport(us->srb, us);
                US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-               /* copy the data from the request buffer to the sense buffer */
-               for(count = 0; count < 18; count++)
-                       us->srb->sense_buffer[count] = 
-                               ((unsigned char *)(us->srb->request_buffer))[count];
-
                US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
                          us->srb->sense_buffer[2] & 0xf,
-                         us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+                         us->srb->sense_buffer[12], 
+                         us->srb->sense_buffer[13]);
+
+               /* set the result so the higher layers expect this data */
+               us->srb->result = CHECK_CONDITION;
 
                /* we're done here */
-               kfree(us->srb->request_buffer);
                us->srb->request_buffer = old_request_buffer;
+               us->srb->use_sg = old_sg;
                return;
        }
   
-       /* FIXME: if we need to send more data, or recieve data, we should
-        * do it here.  Then, we can do status handling here also.
-        *
-        * This includes MODE_SENSE from above
+       /* Fix the MODE_SENSE data here if we had to translate the command
         */
        if (old_cmnd == MODE_SENSE) {
                unsigned char *dta = (unsigned char *)us->srb->request_buffer;
 
-               /* calculate the new length */
-               int length = (dta[0] << 8) + dta[1] + 2;
-
-               /* copy the available data length into the structure */
-               us->srb->cmnd[7] = length >> 8;
-               us->srb->cmnd[8] = length & 0xFF;
-
-               /* send the command to the transport layer */
-               us->srb->result = us->transport(srb, us);
-
-               /* FIXME: this assumes that the 2nd attempt is always
-                * successful convert MODE_SENSE_10 return data format 
-                * to MODE_SENSE_6 format */
+               /* FIXME: we need to compress the entire data structure here
+                */
                dta[0] = dta[1];        /* data len */
                dta[1] = dta[2];        /* med type */
                dta[2] = dta[3];        /* dev-spec prm */
@@ -450,126 +566,18 @@ static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
                        dta[0], dta[1], dta[2], dta[3]);
        }
 
-       /* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
-        * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry 
-        */
-
-       /* FIXME: here is where we need to fix-up the return data from 
-        * an INQUIRY command to show ANSI SCSI rev 2
-        */
-
-       /* FIXME: The rest of this is bogus.  usb_control_msg() will only
-        * return an error if we've really honked things up.  If it just
-        * needs a START_STOP, then we'll get some data back via 
-        * REQUEST_SENSE --  either way, this belongs at a higher level
+       /* Fix-up the return data from an INQUIRY command to show 
+        * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
         */
-
-#if 0
-       /* For UFI, if this is the first time we've sent this TEST_UNIT_READY 
-        * command, we can try again
-        */
-       if (!done_start && (us->subclass == US_SC_UFI)
-           && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-    
-               /* as per spec try a start command, wait and retry */
-               wait_ms(100);
-    
-               done_start++;
-               memset(cmd, 0, sizeof(cmd));
-               cmd[0] = START_STOP;
-               cmd[4] = 1;             /* start */
-    
-               result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-                                        US_CBI_ADSC, 
-                                        USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-                                        0, us->ifnum,
-                                        cmd, 12, HZ*5);
-               US_DEBUGP("Next usb_control_msg returns %d\n", result);
-    
-                               /* allow another retry */
-               retry++;
-               continue;
+       if (us->srb->cmnd[0] == INQUIRY) {
+               ((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
        }
-#endif
 }
 
 static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
-       unsigned int savelen = us->srb->request_bufflen;
-       unsigned int saveallocation = 0;
-
-#if 0
-       /* force attention on first command */
-       if (!us->attention_done) {
-               if (us->srb->cmnd[0] == REQUEST_SENSE) {
-                       US_DEBUGP("forcing unit attention\n");
-                       us->attention_done = 1;
-
-                       if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
-                               unsigned char *p = (unsigned char *)us->srb->request_buffer;
-       
-                               if ((p[2] & 0x0f) != UNIT_ATTENTION) {
-                                       p[2] = UNIT_ATTENTION;
-                                       p[12] = 0x29;   /* power on, reset or bus-reset */
-                                       p[13] = 0;
-                               } /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
-                       } /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
-               }
-       } /* if (!us->attention_done) */
-#endif
-
-       /* If the command has a variable-length payload, then we do them
-        * in two steps -- first we do the minimum, then we recalculate
-        * then length, and re-issue the command 
-        *
-        * we use savelen to remember how much buffer we really have
-        * we use savealloction to remember how much was really requested
-        */
+       unsigned int result = 0;
 
-       /* FIXME: remove savelen based on mods to us_transfer_length() */
-       switch (us->srb->cmnd[0]) {
-       case REQUEST_SENSE:
-               if (us->srb->request_bufflen > 18)
-                       us->srb->request_bufflen = 18;
-               else
-                       break;
-               saveallocation = us->srb->cmnd[4];
-               us->srb->cmnd[4] = 18;
-               break;
-    
-       case INQUIRY:
-               if (us->srb->request_bufflen > 36)
-                       us->srb->request_bufflen = 36;
-               else
-                       break;
-               saveallocation = us->srb->cmnd[4];
-               us->srb->cmnd[4] = 36;
-               break;
-    
-       case MODE_SENSE:
-               if (us->srb->request_bufflen > 4)
-                       us->srb->request_bufflen = 4;
-               else
-                       break;
-               saveallocation = us->srb->cmnd[4];
-               us->srb->cmnd[4] = 4;
-               break;
-    
-       case LOG_SENSE:
-       case MODE_SENSE_10:
-               if (us->srb->request_bufflen > 8)
-                       us->srb->request_bufflen = 8;
-               else
-                       break;
-               saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
-               us->srb->cmnd[7] = 0;
-               us->srb->cmnd[8] = 8;
-               break;
-    
-       default:
-               break;
-       } /* end switch on cmnd[0] */
-  
        /* This code supports devices which do not support {READ|WRITE}_6
         * Apparently, neither Windows or MacOS will use these commands,
         * so some devices do not support them
@@ -631,25 +639,33 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
                        US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
                        US_DEBUG(us_show_command(us->srb));
                }
-       } /* end if (us->flags & US_FL_MODE_XLATE) */
+       } /* if (us->flags & US_FL_MODE_XLATE) */
   
        /* send the command to the transport layer */
-       us->srb->result = us->transport(us->srb, us);
+       result = us->transport(us->srb, us);
+
+       /* If we got a short transfer, but it was for a command that
+        * can have short transfers, we're actually okay
+        */
+       if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+           ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+            (us->srb->cmnd[0] == INQUIRY) ||
+            (us->srb->cmnd[0] == MODE_SENSE) ||
+            (us->srb->cmnd[0] == LOG_SENSE) ||
+            (us->srb->cmnd[0] == MODE_SENSE_10))) {
+               us->srb->result = DID_OK;
+       }
 
        /* if we have an error, we're going to do a REQUEST_SENSE 
         * automatically */
-       /* FIXME: we should only do this for device errors, not 
-        * system errors */
-       if (us->srb->result) {
+       if (result != USB_STOR_TRANSPORT_GOOD) {
                int temp_result;
-               int count;
+               int old_sg;
                void* old_request_buffer;
 
                US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-               /* set the result so the higher layers expect this data */
-               us->srb->result = CHECK_CONDITION;
-
+               /* set up the REQUEST_SENSE command and parameters */
                us->srb->cmnd[0] = REQUEST_SENSE;
                us->srb->cmnd[1] = 0;
                us->srb->cmnd[2] = 0;
@@ -659,115 +675,32 @@ static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
     
                /* set the buffer length for transfer */
                old_request_buffer = us->srb->request_buffer;
+               old_sg = us->srb->use_sg;
                us->srb->request_bufflen = 18;
-               us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+               us->srb->request_buffer = us->srb->sense_buffer;
 
                /* FIXME: what if this command fails? */
                temp_result = us->transport(us->srb, us);
                US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-               /* copy the data from the request buffer to the sense buffer */
-               for(count = 0; count < 18; count++)
-                       us->srb->sense_buffer[count] = 
-                               ((unsigned char *)(us->srb->request_buffer))[count];
-
                US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
                          us->srb->sense_buffer[2] & 0xf,
-                         us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+                         us->srb->sense_buffer[12], 
+                         us->srb->sense_buffer[13]);
+
+               /* set the result so the higher layers expect this data */
+               us->srb->result = CHECK_CONDITION;
 
                /* we're done here */
-               kfree(us->srb->request_buffer);
+               us->srb->use_sg = old_sg;
                us->srb->request_buffer = old_request_buffer;
                return;
        }
 
-       if (savelen != us->srb->request_bufflen) {
-               unsigned char *p = (unsigned char *)us->srb->request_buffer;
-               unsigned int length = 0;
-    
-               /* set correct length and retry */
-               switch (us->srb->cmnd[0]) {
-
-                       /* FIXME: we should try to get all the sense data */
-               case REQUEST_SENSE:
-                       /* simply return 18 bytes */
-                       p[7] = 10;
-                       length = us->srb->request_bufflen;
-                       break;
-      
-               case INQUIRY:
-                       length = p[4] + 5 > savelen ? savelen : p[4] + 5;
-                       us->srb->cmnd[4] = length;
-                       break;
-      
-               case MODE_SENSE:
-                       US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
-                       length = p[0] + 1 > savelen ? savelen : p[0] + 1;
-                       us->srb->cmnd[4] = length;
-                       break;
-      
-               case LOG_SENSE:
-                       length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
-                       us->srb->cmnd[7] = length >> 8;
-                       us->srb->cmnd[8] = length;
-                       break;
-      
-               case MODE_SENSE_10:
-                       US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
-                                 (p[0] << 8) + p[1]);
-                       length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
-                       us->srb->cmnd[7] = length >> 8;
-                       us->srb->cmnd[8] = length;
-                       break;
-               } /* end switch on cmnd[0] */
-    
-               US_DEBUGP("Old/New length = %d/%d\n",
-                         savelen, length);
-    
-               /* issue the new command */
-               /* FIXME: this assumes that the second attempt is 
-                * always successful */
-               if (us->srb->request_bufflen != length) {
-                       US_DEBUGP("redoing cmd with len=%d\n", length);
-                       us->srb->request_bufflen = length;
-                       us->srb->result = us->transport(us->srb, us);
-               }
-    
-               /* reset back to original values */
-               us->srb->request_bufflen = savelen;
-
-               /* fix data as necessary */
-               switch (us->srb->cmnd[0]) {
-               case INQUIRY:
-                       if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) { 
-                               US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
-                               ((unsigned char*)us->srb->request_buffer)[2] |= 2;
-                       }
-                       /* FALL THROUGH */
-               case REQUEST_SENSE:
-               case MODE_SENSE:
-                       if (us->srb->use_sg == 0 && length > 0) {
-                               int i;
-                               printk(KERN_DEBUG "Data is");
-                               for (i = 0; i < 32 && i < length; ++i)
-                                       printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
-                               if (i < length)
-                                       printk(" ...");
-                               printk("\n");
-                       }
-
-                       /* FIXME: is this really necessary? */
-                       us->srb->cmnd[4] = saveallocation;
-                       break;
-      
-               case LOG_SENSE:
-               case MODE_SENSE_10:
-                       /* FIXME: is this really necessary? */
-                       us->srb->cmnd[7] = saveallocation >> 8;
-                       us->srb->cmnd[8] = saveallocation;
-                       break;
-               } /* end switch on cmnd[0] */
-       } /* if good command */
+       /* fix the results of an INQUIRY */
+       if (us->srb->cmnd[0] == INQUIRY) {
+               US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+               ((unsigned char*)us->srb->request_buffer)[2] |= 2;
+       }
 }
 
 /***********************************************************************
@@ -789,7 +722,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
        /* was this a wanted interrupt? */
        if (us->ip_wanted) {
                us->ip_wanted = 0;
-               wake_up(&us->ip_waitq);
+               up(&(us->ip_waitq));
        } else {
                US_DEBUGP("ERROR: Unwanted interrupt received!\n");
        }
@@ -801,9 +734,7 @@ static int CBI_irq(int state, void *buffer, int len, void *dev_id)
        return 0;
 }
 
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
  */
 static int CB_reset(struct us_data *us)
 {
@@ -816,41 +747,39 @@ static int CB_reset(struct us_data *us)
        cmd[0] = SEND_DIAGNOSTIC;
        cmd[1] = 4;
        result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-                                US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                                US_CBI_ADSC, 
+                                USB_TYPE_CLASS | USB_RECIP_INTERFACE,
                                 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
 
        /* long wait for reset */
        schedule_timeout(HZ*6);
 
        US_DEBUGP("CB_reset: clearing endpoint halt\n");
-       usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-       usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+       usb_clear_halt(us->pusb_dev, 
+                      usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+       usb_clear_halt(us->pusb_dev, 
+                      usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
 
        US_DEBUGP("CB_reset done\n");
        return 0;
 }
 
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
  */
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
        int result;
 
        US_DEBUGP("CBI gets a command:\n");
        US_DEBUG(us_show_command(srb));
 
-       /* FIXME: we aren't setting the ip_wanted indicator early enough, which
-        * causes some commands to never complete.  This hangs the driver.
-        */
-
+       /* COMMAND STAGE */
        /* let's send the command via the control pipe */
-       result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-                                US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-                                0, us->ifnum,
-                                srb->cmnd, srb->cmd_len, HZ*5);
+       result = usb_control_msg(us->pusb_dev, 
+                                usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
+                                USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+                                us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
 
        /* check the return code for the command */
        if (result < 0) {
@@ -858,131 +787,160 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
 
                /* a stall is a fatal condition from the device */
                if (result == -EPIPE) {
-                       US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-      
+                       US_DEBUGP("-- Stall on control pipe. Clearing\n");
                        US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
                                  usb_clear_halt(us->pusb_dev, 
-                                                usb_sndctrlpipe(us->pusb_dev, 0)));
+                                                usb_sndctrlpipe(us->pusb_dev,
+                                                                0)));
                        return USB_STOR_TRANSPORT_ERROR;
                }
 
-               /* FIXME: we need to handle NAKs here */
+                               /* FIXME: we need to handle NAKs here */
                return USB_STOR_TRANSPORT_ERROR;
        }
 
+       /* Set up for status notification */
+       us->ip_wanted = 1;
+
+       /* DATA STAGE */
        /* transfer the data payload for this command, if one exists*/
        if (us_transfer_length(srb)) {
-               result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
-               US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
-
-               /* FIXME: what do the return codes from us_transfer mean? */
-               if ((result < 0) && 
-                   (result != USB_ST_DATAUNDERRUN) && 
-                   (result != USB_ST_STALL)) {
-                       return DID_ERROR << 16;
-               }
-       } /* if (us_transfer_length(srb)) */
+               us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+               US_DEBUGP("CBI data stage result is 0x%x\n", result);
+       }
 
-       /* get status and return it */
-       return pop_CB_status(srb);
+       /* STATUS STAGE */
+
+       /* go to sleep until we get this interrup */
+       /* FIXME: this should be changed to use a timeout */
+       down(&(us->ip_waitq));
+       
+       /* FIXME: currently this code is unreachable, but the idea is
+        * necessary.  See above comment.
+        */
+       if (us->ip_wanted) {
+               US_DEBUGP("Did not get interrupt on CBI\n");
+               us->ip_wanted = 0;
+               return USB_STOR_TRANSPORT_ERROR;
+       }
+       
+       US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
+       
+       /* UFI gives us ASC and ASCQ, like a request sense */
+       /* FIXME: is this right?  Do REQUEST_SENSE and INQUIRY need special
+        * case handling?
+        */
+       if (us->subclass == US_SC_UFI) {
+               if (srb->cmnd[0] == REQUEST_SENSE ||
+                   srb->cmnd[0] == INQUIRY)
+                       return USB_STOR_TRANSPORT_GOOD;
+               else
+                       if (us->ip_data)
+                               return USB_STOR_TRANSPORT_FAILED;
+                       else
+                               return USB_STOR_TRANSPORT_GOOD;
+       }
+       
+       /* otherwise, we interpret the data normally */
+       switch (us->ip_data) {
+       case 0x0001: 
+               return USB_STOR_TRANSPORT_GOOD;
+       case 0x0002: 
+               return USB_STOR_TRANSPORT_FAILED;
+       default: 
+               return USB_STOR_TRANSPORT_ERROR;
+       }
+
+       US_DEBUGP("CBI_transport() reached end of function\n");
+       return USB_STOR_TRANSPORT_ERROR;
 }
 
 /*
- * Control/Bulk status handler
+ * Control/Bulk transport
  */
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
-       struct us_data *us = (struct us_data *)srb->host_scribble;
-       int result = 0;
+       int result;
        __u8 status[2];
-       int retry = 5;
 
-       US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
-       switch (us->protocol) {
-       case US_PR_CB:
-               /* get from control */
-
-               while (retry--) {
-                       result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
-                                                USB_REQ_GET_STATUS, USB_DIR_IN |
-                                                USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-                                                0, us->ifnum, status, sizeof(status), HZ*5);
-                       if (result != USB_ST_TIMEOUT)
-                               break;
-               }
-               if (result) {
-                       US_DEBUGP("Bad AP status request %d\n", result);
-                       return DID_ABORT << 16;
-               }
-               US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
-               if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
-                   ( (status[0] & ~3) || status[1]))
-                       return (DID_OK << 16) | 2;
-               else
-                       return USB_STOR_TRANSPORT_GOOD;
-               break;
+       US_DEBUGP("CBC gets a command:\n");
+       US_DEBUG(us_show_command(srb));
 
-               /* FIXME: this should be in a separate function */
-       case US_PR_CBI:
-               /* get from interrupt pipe */
+       /* COMMAND STAGE */
+       /* let's send the command via the control pipe */
+       result = usb_control_msg(us->pusb_dev, 
+                                usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
+                                USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+                                us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
 
-               /* add interrupt transfer, marked for removal */
-               us->ip_wanted = 1;
+       /* check the return code for the command */
+       if (result < 0) {
+               US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
 
-               /* go to sleep until we get this interrup */
-               /* FIXME: this should be changed to use a timeout */
-               sleep_on(&us->ip_waitq);
-    
-               if (us->ip_wanted) {
-                       US_DEBUGP("Did not get interrupt on CBI\n");
-                       us->ip_wanted = 0;
+               /* a stall is a fatal condition from the device */
+               if (result == -EPIPE) {
+                       US_DEBUGP("-- Stall on control pipe. Clearing\n");
+                       US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+                                 usb_clear_halt(us->pusb_dev, 
+                                                usb_sndctrlpipe(us->pusb_dev,
+                                                                0)));
                        return USB_STOR_TRANSPORT_ERROR;
                }
-    
-               US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
 
-               /* UFI gives us ASC and ASCQ, like a request sense */
-               /* FIXME: is this right?  do REQUEST_SENSE and INQUIRY need special
-                * case handling?
-                */
-               if (us->subclass == US_SC_UFI) {
-                       if (srb->cmnd[0] == REQUEST_SENSE ||
-                           srb->cmnd[0] == INQUIRY)
-                               return USB_STOR_TRANSPORT_GOOD;
-                       else
-                               if (us->ip_data)
-                                       return USB_STOR_TRANSPORT_FAILED;
-                               else
-                                       return USB_STOR_TRANSPORT_GOOD;
-               }
+                               /* FIXME: we need to handle NAKs here */
+               return USB_STOR_TRANSPORT_ERROR;
+       }
 
-               /* otherwise, we interpret the data normally */
-               switch (us->ip_data) {
-               case 0x0001: 
-                       return USB_STOR_TRANSPORT_GOOD;
-               case 0x0002: 
-                       return USB_STOR_TRANSPORT_FAILED;
-               default: 
-                       return USB_STOR_TRANSPORT_ERROR;
-               }
+       /* DATA STAGE */
+       /* transfer the data payload for this command, if one exists*/
+       if (us_transfer_length(srb)) {
+               us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+               US_DEBUGP("CBC data stage result is 0x%x\n", result);
        }
-       US_DEBUGP("pop_CB_status, reached end of function\n");
+       
+       
+       /* STATUS STAGE */
+       /* FIXME: this is wrong */
+       result = usb_control_msg(us->pusb_dev, 
+                                usb_rcvctrlpipe(us->pusb_dev,0),
+                                USB_REQ_GET_STATUS, USB_DIR_IN |
+                                USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+                                0, us->ifnum, status, sizeof(status), HZ*5);
+
+       if (result < 0) {
+               US_DEBUGP("CBC Status stage returns %d\n", result);
+               return USB_STOR_TRANSPORT_ERROR;
+       }
+
+       US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+       if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+           ( (status[0] & ~3) || status[1]))
+               return USB_STOR_TRANSPORT_FAILED;
+       else
+               return USB_STOR_TRANSPORT_GOOD;
+       
+       US_DEBUGP("CB_transport() reached end of function\n");
        return USB_STOR_TRANSPORT_ERROR;
 }
 
+/* FIXME: Does this work? */
 static int Bulk_reset(struct us_data *us)
 {
        int result;
 
-       result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-                                US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-                                US_BULK_RESET_HARD, us->ifnum,
-                                NULL, 0, HZ*5);
-       if (result)
+       result = usb_control_msg(us->pusb_dev, 
+                                usb_sndctrlpipe(us->pusb_dev,0), 
+                                US_BULK_RESET, 
+                                USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                                US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+       if (result < 0)
                US_DEBUGP("Bulk hard reset failed %d\n", result);
-       usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-       usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+       usb_clear_halt(us->pusb_dev, 
+                      usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+       usb_clear_halt(us->pusb_dev, 
+                      usb_sndbulkpipe(us->pusb_dev, us->ep_out));
 
        /* long wait for reset */
        schedule_timeout(HZ*6);
@@ -991,8 +949,7 @@ static int Bulk_reset(struct us_data *us)
 }
 
 /*
- * The bulk only protocol handler.
- *     Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
  */
 static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
@@ -1001,7 +958,7 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        int result;
        int pipe;
        int partial;
-
+       
        /* set up the command wrapper */
        bcb.Signature = US_BULK_CB_SIGN;
        bcb.DataTransferLength = us_transfer_length(srb);
@@ -1009,14 +966,14 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        bcb.Tag = srb->serial_number;
        bcb.Lun = 0;
        bcb.Length = srb->cmd_len;
-
+       
        /* construct the pipe handle */
        pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+       
        /* copy the command payload */
        memset(bcb.CDB, 0, sizeof(bcb.CDB));
        memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+       
        /* send it to out endpoint */
        US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
                  bcb.Signature, bcb.Tag, bcb.DataTransferLength,
@@ -1024,94 +981,83 @@ static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
                              US_BULK_CB_WRAP_LEN, &partial, HZ*5);
        US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+       
        /* if we stall, we need to clear it before we go on */
        if (result == -EPIPE) {
                US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
                usb_clear_halt(us->pusb_dev, pipe);
        }
-  
+       
        /* if the command transfered well, then we go to the data stage */
-       /* FIXME: Regardless of the status of the data stage, we go on to the
-        * status stage.  Note that this implies that if a command is
-        * partially successful, we rely on the device reporting an error
-        * the CSW. The spec says that the device may just decide to short us.
-        */
        if (result == 0) {
                /* send/receive data payload, if there is any */
                if (bcb.DataTransferLength) {
-                       result = us_transfer(srb, bcb.Flags);
-                       US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
-                       if ((result < 0) && (result != USB_ST_DATAUNDERRUN) 
-                           && (result != USB_ST_STALL)) {
-                               US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-                               return DID_ABORT << 16;
-                       }
-#endif
+                       us_transfer(srb, bcb.Flags);
+                       US_DEBUGP("Bulk data transfer result 0x%x\n", 
+                                 srb->result);
                }
        }
-
+       
        /* See flow chart on pg 15 of the Bulk Only Transport spec for
         * an explanation of how this code works.
         */
-
+       
        /* construct the pipe handle */
        pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+       
        /* get CSW for device status */
        result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
                              US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+       
        /* did the attempt to read the CSW fail? */
        if (result == -EPIPE) {
                US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
                usb_clear_halt(us->pusb_dev, pipe);
-
+               
                /* get the status again */
                result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
                                      US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-   
+               
                /* if it fails again, we need a reset and return an error*/
                if (result == -EPIPE) {
                        Bulk_reset(us);
-                       return (DID_ABORT << 16);
+                       return USB_STOR_TRANSPORT_ERROR;
                }
        }
-
+       
        /* if we still have a failure at this point, we're in trouble */
        if (result) {
-               US_DEBUGP("Bulk status result = 0x%x\n", result);
-               return DID_ABORT << 16;
+               US_DEBUGP("Bulk status result = %d\n", result);
+               return USB_STOR_TRANSPORT_ERROR;
        }
-
+       
        /* check bulk status */
        US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
                  bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
        if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
            bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
                US_DEBUGP("Bulk logical error\n");
-               return DID_ABORT << 16;
+               return USB_STOR_TRANSPORT_ERROR;
        }
-
+       
        /* based on the status code, we report good or bad */
        switch (bcs.Status) {
        case US_BULK_STAT_OK:
-               /* if there is residue, we really didn't finish the command */
-               if (bcs.Residue)
-                       return DID_ERROR << 16;
-               else
-                       return DID_OK << 16;
+               /* command good -- note that we could be short on data */
+               return USB_STOR_TRANSPORT_GOOD;
 
        case US_BULK_STAT_FAIL:
-               return DID_ERROR << 16;
-
+               /* command failed */
+               return USB_STOR_TRANSPORT_FAILED;
+               
        case US_BULK_STAT_PHASE:
+               /* phase error */
                Bulk_reset(us);
-               return DID_ERROR << 16;
+               return USB_STOR_TRANSPORT_ERROR;
        }
-
-       return DID_OK << 16;        /* check sense required */
+       
+       /* we should never get here, but if we do, we're in trouble */
+       return USB_STOR_TRANSPORT_ERROR;
 }
 
 /***********************************************************************
@@ -1163,14 +1109,20 @@ static int us_release(struct Scsi_Host *psh)
                usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
                us->irq_handle = NULL;
        }
-       if (us->pusb_dev)
-               usb_deregister(&storage_driver);
+
+       /* FIXME: release the interface claim here? */
+       //      if (us->pusb_dev)
+       //              usb_deregister(&storage_driver);
 
        /* FIXME - leaves hanging host template copy */
        /* (because scsi layer uses it after removal !!!) */
-       while (prev->next != us)
-               prev = prev->next;
-       prev->next = us->next;
+       if (us_list == us)
+               us_list = us->next;
+       else {
+               while (prev->next != us)
+                       prev = prev->next;
+               prev->next = us->next;
+       }
        return 0;
 }
 
@@ -1188,17 +1140,19 @@ static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
        struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
        US_DEBUGP("Command wakeup\n");
-       if (us->srb) {
-               /* busy */
-       }
        srb->host_scribble = (unsigned char *)us;
-       us->srb = srb;
+
+       /* get exclusive access to the structures we want */
+       down(&(us->queue_exclusion));
+
+       /* enqueue the command */
+       us->queue_srb = srb;
        srb->scsi_done = done;
        us->action = US_ACT_COMMAND;
 
        /* wake up the process task */
-
-       wake_up_interruptible(&us->waitq);
+       up(&(us->queue_exclusion));
+       up(&(us->sleeper));
 
        return 0;
 }
@@ -1209,6 +1163,7 @@ static int us_abort( Scsi_Cmnd *srb )
        return 0;
 }
 
+/* FIXME: this doesn't do anything right now */
 static int us_bus_reset( Scsi_Cmnd *srb )
 {
        //  struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -1340,11 +1295,11 @@ static Scsi_Host_Template my_host_template = {
        NULL,                       /* select_queue_depths */
        1,                          /* can_queue */
        -1,                         /* this_id */
-       SG_ALL,             /* sg_tablesize */
+       SG_ALL,                     /* sg_tablesize */
        1,                          /* cmd_per_lun */
        0,                          /* present */
-       FALSE,              /* unchecked_isa_dma */
-       FALSE,              /* use_clustering */
+       FALSE,                      /* unchecked_isa_dma */
+       TRUE,                       /* use_clustering */
        TRUE,                       /* use_new_eh_code */
        TRUE                        /* emulated */
 };
@@ -1391,10 +1346,18 @@ static int usb_stor_control_thread(void * __us)
                siginfo_t info;
                int unsigned long signr;
 
-               interruptible_sleep_on(&us->waitq);
+               US_DEBUGP("*** thread sleeping.\n");
+               down(&(us->sleeper));
+               down(&(us->queue_exclusion));
+               US_DEBUGP("*** thread awakened.\n");
 
+                               /* take the command off the queue */
                action = us->action;
                us->action = 0;
+               us->srb = us-> queue_srb;
+       
+               /* release the queue lock as fast as possible */
+               up(&(us->queue_exclusion));
 
                /* FIXME: we need to examine placment of break; and 
                 * scsi_done() calls */
@@ -1460,29 +1423,20 @@ static int usb_stor_control_thread(void * __us)
                        break;
 
                } /* end switch on action */
-    
+
+               /* FIXME: we ignore TERM and KILL... is this right? */
                if (signal_pending(current)) {
                        /* sending SIGUSR1 makes us print out some info */
                        spin_lock_irq(&current->sigmask_lock);
                        signr = dequeue_signal(&current->blocked, &info);
                        spin_unlock_irq(&current->sigmask_lock);
-
-                       if (signr == SIGUSR2) {
-                               usb_stor_debug = !usb_stor_debug;
-                               printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
-                       } else {
-                               break;      /* exit the loop on any other signal */
-                       }
-               }
-       }
+               } /* if (singal_pending(current)) */
+       } /* for (;;) */
   
        //  MOD_DEC_USE_COUNT;
 
        printk("usb_stor_control_thread exiting\n");
 
-       /* FIXME: this is a hack to allow for debugging */
-       // scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
        return 0;
 }      
 
@@ -1498,7 +1452,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
        unsigned int flags = 0;
        GUID(guid);                  /* Global Unique Identifier */
        struct us_data *prev;
-       Scsi_Host_Template *htmplt;
        int protocol = 0;
        int subclass = 0;
        struct usb_interface_descriptor *altsetting = 
@@ -1565,14 +1518,18 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                        return NULL;
                }
                memset(ss, 0, sizeof(struct us_data));
+
+               /* Initialize the mutexes only when the struct is new */
+               init_MUTEX_LOCKED(&(ss->sleeper));
+               init_MUTEX(&(ss->queue_exclusion));
        }
 
-       /* Initialize the us_data structure with some useful info */
+       /* establish the connection to the new device */
        interface = altsetting;
        ss->flags = flags;
        ss->ifnum = ifnum;
-       ss->pusb_dev = dev;
        ss->attention_done = 0;
+       ss->pusb_dev = dev;
 
        /* If the device has subclass and protocol, then use that.  Otherwise, 
         * take data from the specific interface.
@@ -1596,7 +1553,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
 
        case US_PR_CBI:
                US_DEBUGPX("Control/Bulk/Interrupt\n");
-               ss->transport = CB_transport;
+               ss->transport = CBI_transport;
                ss->transport_reset = CB_reset;
                break;
 
@@ -1620,7 +1577,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
         */
        for (i = 0; i < interface->bNumEndpoints; i++) {
                /* is it an BULK endpoint? */
-               if ((interface->endpoint[i].bmAttributes &  USB_ENDPOINT_XFERTYPE_MASK)
+               if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                    == USB_ENDPOINT_XFER_BULK) {
                        if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
                                ss->ep_in = interface->endpoint[i].bEndpointAddress &
@@ -1646,7 +1603,6 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
            (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
                US_DEBUGP("Problems with device\n");
                if (ss->host) {
-                       scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
                        kfree(ss->htmplt->name);
                        kfree(ss->htmplt);
                }
@@ -1667,11 +1623,13 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                US_DEBUGP("Protocol: ");
                switch (ss->subclass) {
                case US_SC_RBC:
-                       US_DEBUGPX("Reduced Block Commands\n");
+                       US_DEBUGPX("Reduced Block Commands (RBC)\n");
+                       ss->proto_handler = transparent_scsi_command;
                        break;
 
                case US_SC_8020:
-                       US_DEBUGPX("8020\n");
+                       US_DEBUGPX("8020i\n");
+                       ss->proto_handler = ATAPI_command;
                        break;
 
                case US_SC_QIC:
@@ -1679,7 +1637,8 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                        break;
 
                case US_SC_8070:
-                       US_DEBUGPX("8070\n");
+                       US_DEBUGPX("8070i\n");
+                       ss->proto_handler = ATAPI_command;
                        break;
 
                case US_SC_SCSI:
@@ -1697,22 +1656,9 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                        break;
                }
 
-               /* We only handle certain protocols.  Currently, these are
-                *the only ones that devices use.
-                */
-               if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
-                       US_DEBUGP("Sorry, we do not support that protocol yet.\n");
-                       US_DEBUGP("If you have a device which uses one of the unsupported\n");
-                       US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-     
-                       kfree(ss);
-                       return NULL;
-               }
-
                /* Allocate memory for the SCSI Host Template */
-               if ((htmplt = (Scsi_Host_Template *)
-                    kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+               if ((ss->htmplt = (Scsi_Host_Template *)
+                    kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
                        printk(KERN_WARNING USB_STORAGE "Out of memory\n");
 
                        kfree(ss);
@@ -1720,7 +1666,7 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                }
 
                /* Initialize the host template based on the default one */
-               memcpy(htmplt, &my_host_template, sizeof(my_host_template));
+               memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
 
                /* Grab the next host number */
                ss->host_number = my_host_number++;
@@ -1729,32 +1675,34 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                 * can pass the ss pointer to the host controler thread
                 * in us_detect
                 */
-               (struct us_data *)htmplt->proc_dir = ss; 
+               (struct us_data *)ss->htmplt->proc_dir = ss; 
 
                /* shuttle E-USB */     
                if (dev->descriptor.idVendor == 0x04e6 &&
                    dev->descriptor.idProduct == 0x0001) {
                        __u8 qstat[2];
                        int result;
-           
-                       result = usb_control_msg(ss->pusb_dev, usb_rcvctrlpipe(dev,0),
+                       
+                       result = usb_control_msg(ss->pusb_dev, 
+                                                usb_rcvctrlpipe(dev,0),
                                                 1, 0xC0,
                                                 0, ss->ifnum,
                                                 qstat, 2, HZ*5);
                        US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
-                       init_waitqueue_head(&ss->ip_waitq);
+                       init_MUTEX_LOCKED(&(ss->ip_waitq));
                        ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
-                       result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
-                                                255, (void *)ss, &ss->irq_handle);
-                       if (result)
+                       result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
+                                                CBI_irq, 255, (void *)ss, 
+                                                &ss->irq_handle);
+                       if (result < 0)
                                return NULL;
-
-                       interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
-               } else if (ss->protocol == US_PR_CBI)
-               {
+                       /* FIXME: what is this?? */
+                       down(&(ss->ip_waitq));
+               } else if (ss->protocol == US_PR_CBI) {
                        int result; 
-
-                       init_waitqueue_head(&ss->ip_waitq);
+                       
+                       /* set up so we'll wait for notification */
+                       init_MUTEX_LOCKED(&(ss->ip_waitq));
 
                        /* set up the IRQ pipe and handler */
                        /* FIXME: This needs to get the period from the device */
@@ -1768,18 +1716,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                }
     
 
-               /* start up our thread */
+                               /* start up our thread */
                {
                        DECLARE_MUTEX_LOCKED(sem);
 
-                       init_waitqueue_head(&ss->waitq);
-
                        ss->notify = &sem;
                        ss->pid = kernel_thread(usb_stor_control_thread, ss,
                                                CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
                        if (ss->pid < 0) {
                                printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
-                               kfree(htmplt);
+                               kfree(ss->htmplt);
 
                                kfree(ss);
                                return NULL;
@@ -1790,17 +1736,16 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
                }
 
                /* now register - our detect function will be called */
-               scsi_register_module(MODULE_SCSI_HA, htmplt);
+               ss->htmplt->module = &__this_module;
+               scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
 
                /* put us in the list */
-               prev = (struct us_data *)&us_list;
-               while (prev->next)
-                       prev = prev->next;
-               prev->next = ss;
+               ss->next = us_list;
+                us_list = ss;
        }
 
-       printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
-       printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+       printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
+       printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
 
        return ss;
 }
@@ -1814,7 +1759,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
                return;
 
        ss->pusb_dev = NULL;
-       //  MOD_DEC_USE_COUNT;
 }
 
 
@@ -1824,8 +1768,6 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
 
 int __init usb_stor_init(void)
 {
-       //  MOD_INC_USE_COUNT;
-
        if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
                printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
                printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", 
@@ -1844,6 +1786,14 @@ int __init usb_stor_init(void)
 
 void __exit usb_stor_exit(void)
 {
+       static struct us_data *ptr;
+
+       // FIXME: this needs to be put back to free _all_ the hosts
+       //      for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+       //              scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
+       printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
+       scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+
        usb_deregister(&storage_driver) ;
 }
 
index 80a03f3c91faf6fd53f884e5cf35cf87a6d5fbc0..06e6d958b319e7738464ba98adc6a469cf694f9c 100644 (file)
@@ -9,13 +9,11 @@
 
 #define USB_STORAGE "usb-storage: "
 
-extern int usb_stor_debug;
-
 #ifdef CONFIG_USB_STORAGE_DEBUG
 void us_show_command(Scsi_Cmnd *srb);
-#define US_DEBUGP(x...) { if(usb_stor_debug) printk( KERN_DEBUG USB_STORAGE ## x ); }
-#define US_DEBUGPX(x...) { if(usb_stor_debug) printk( ## x ); }
-#define US_DEBUG(x)  { if(usb_stor_debug) x; }
+#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x )
+#define US_DEBUGPX(x...) printk( ## x )
+#define US_DEBUG(x) 
 #else
 #define US_DEBUGP(x...)
 #define US_DEBUGPX(x...)
@@ -82,16 +80,23 @@ struct bulk_cs_wrap {
 #define US_BULK_RESET_SOFT     1
 #define US_BULK_RESET_HARD     0
 
+/*
+ * us_bulk_transfer() return codes
+ */
+#define US_BULK_TRANSFER_GOOD   0
+#define US_BULK_TRANSFER_SHORT  1
+#define US_BULK_TRANSFER_FAILED 2
+
 /*
  * Transport return codes
  */
 
-#define USB_STOR_TRANSPORT_GOOD    0    /* Transport good, command good    */
-#define USB_STOR_TRANSPORT_FAILED  1    /* Transport good, command failed  */
-#define USB_STOR_TRANSPORT_ERROR   2    /* Transport bad (i.e. device dead */
+#define USB_STOR_TRANSPORT_GOOD    0   /* Transport good, command good     */
+#define USB_STOR_TRANSPORT_FAILED  1   /* Transport good, command failed   */
+#define USB_STOR_TRANSPORT_ERROR   2   /* Transport bad (i.e. device dead) */
 
 /*
- * CBI style
+ * CBI accept device specific command
  */
 
 #define US_CBI_ADSC            0
@@ -128,3 +133,4 @@ static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *seri
 #define US_FL_FIXED_COMMAND   0x00000002 /* expand commands to fixed size */
 #define US_FL_MODE_XLATE      0x00000004 /* translate _6 to _10 comands for
                                            Win/MacOS compatibility */
+
index 73d16937a2d2bcf8bde1491bb6c7e3034125ecb2..4eedc418360ecf642d12e4955d71a40cb6a27136 100644 (file)
@@ -1,41 +1,32 @@
 #ifdef DEBUG
-
 static void uhci_show_qh (puhci_desc_t qh)
 {
        if (qh->type != QH_TYPE) {
                dbg("qh has not QH_TYPE");
                return;
        }
-       dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh));
+       dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh));
 
        if (qh->hw.qh.head & UHCI_PTR_TERM)
-               dbg("Head Terminate");
-       else {
-               if (qh->hw.qh.head & UHCI_PTR_QH)
-                       dbg("Head points to QH");
-               else
-                       dbg("Head points to TD");
-
-               dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS);
-       }
+               dbg("    Head Terminate");
+       else 
+               dbg("    Head: %s @ %08X",
+                   (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"),
+                   qh->hw.qh.head & ~UHCI_PTR_BITS);
+
        if (qh->hw.qh.element & UHCI_PTR_TERM)
-               dbg("Element Terminate");
-       else {
-
-               if (qh->hw.qh.element & UHCI_PTR_QH)
-                       dbg("Element points to QH");
-               else
-                       dbg("Element points to TD");
-               dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS);
-       }
+               dbg("    Element Terminate");
+       else 
+               dbg("    Element: %s @ %08X",
+                   (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"),
+                   qh->hw.qh.element & ~UHCI_PTR_BITS);
 }
 #endif
 
 static void uhci_show_td (puhci_desc_t td)
 {
        char *spid;
-       warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td));
-
+       
        switch (td->hw.td.info & 0xff) {
        case USB_PID_SETUP:
                spid = "SETUP";
@@ -51,16 +42,16 @@ static void uhci_show_td (puhci_desc_t td)
                break;
        }
 
-       warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)",
+       warn("  TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x",
+            td, virt_to_bus (td),
             td->hw.td.info >> 21,
             ((td->hw.td.info >> 19) & 1),
             (td->hw.td.info >> 15) & 15,
             (td->hw.td.info >> 8) & 127,
-            (td->hw.td.info & 0xff),
             spid,
             td->hw.td.buffer);
 
-       warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
+       warn("    Len=%02x e%d %s%s%s%s%s%s%s%s%s%s",
             td->hw.td.status & 0x7ff,
             ((td->hw.td.status >> 27) & 3),
             (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "",
@@ -74,50 +65,41 @@ static void uhci_show_td (puhci_desc_t td)
             (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "",
             (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : ""
                );
-#if 1
+
        if (td->hw.td.link & UHCI_PTR_TERM)
-               warn("Link Terminate");
-       else {
-               if (td->hw.td.link & UHCI_PTR_QH)
-                       warn("%s, link points to QH @ %08x",
-                            (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
-                            td->hw.td.link & ~UHCI_PTR_BITS);
-               else
-                       warn("%s, link points to TD @ %08x",
-                            (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"),
-                            td->hw.td.link & ~UHCI_PTR_BITS);
-       }
-#endif
+               warn("   TD Link Terminate");
+       else 
+               warn("    Link points to %s @ %08x, %s",
+                    (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"),
+                    td->hw.td.link & ~UHCI_PTR_BITS,
+                    (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first"));
 }
 #ifdef DEBUG
 static void uhci_show_td_queue (puhci_desc_t td)
 {
-       dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
+       //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td));
        while (1) {
                uhci_show_td (td);
                if (td->hw.td.link & UHCI_PTR_TERM)
                        break;
-               //if(!(td->hw.td.link&UHCI_PTR_DEPTH))
-               //      break;
                if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS))
                        td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS);
                else {
                        dbg("td points to itself!");
                        break;
                }
-//              schedule();
        }
 }
 
 static void uhci_show_queue (puhci_desc_t qh)
 {
+       uhci_desc_t *start_qh=qh;
+
        dbg("uhci_show_queue %p:", qh);
        while (1) {
                uhci_show_qh (qh);
 
-               if (qh->hw.qh.element & UHCI_PTR_QH)
-                       dbg("Warning: qh->element points to qh!");
-               else if (!(qh->hw.qh.element & UHCI_PTR_TERM))
+               if (!(qh->hw.qh.element & UHCI_PTR_TERM))
                        uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS));
 
                if (qh->hw.qh.head & UHCI_PTR_TERM)
@@ -129,7 +111,12 @@ static void uhci_show_queue (puhci_desc_t qh)
                        dbg("qh points to itself!");
                        break;
                }
-       }
+               
+               if (qh==start_qh) { // avoid loop
+                       dbg("Loop detect");
+                       break;
+               }
+       }               
 }
 
 static void uhci_show_sc (int port, unsigned short status)
index aed79f849fc765135449822e4db04c41251d60fe..85a5cd476d5d9cfe7d69997365320081322f32ca 100644 (file)
@@ -12,7 +12,7 @@
  * (C) Copyright 1999 Johannes Erdfelt
  * (C) Copyright 1999 Randy Dunlap
  *
- * $Id: usb-uhci.c,v 1.197 2000/02/15 17:44:22 acher Exp $
+ * $Id: usb-uhci.c,v 1.222 2000/03/13 21:18:02 fliegl Exp $
  */
 
 #include <linux/config.h>
@@ -28,9 +28,9 @@
 #include <linux/unistd.h>
 #include <linux/interrupt.h>   /* for in_interrupt() */
 #include <linux/init.h>
-/* This enables debug printks */
-#define DEBUG
-#include <linux/usb.h>
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
+#include <linux/pm.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
 /* This enables more detailed sanity checks in submit_iso */
 //#define ISO_SANITY_CHECK
 
+/* This enables debug printks */
+#define DEBUG
+
 /* This enables all symbols to be exported, to ease debugging oopses */
 //#define DEBUG_SYMBOLS
 
 /* This enables an extra UHCI slab for memory debugging */
 #define DEBUG_SLAB
 
+#include <linux/usb.h>
 #include "usb-uhci.h"
 #include "usb-uhci-debug.h"
 
 #undef DEBUG
 #undef dbg
 #define dbg(format, arg...) do {} while (0)
-
-#include <linux/pm.h>
-
+#define DEBUG_SYMBOLS
 #ifdef DEBUG_SYMBOLS
        #define _static
        #ifndef EXPORT_SYMTAB
-       #define EXPORT_SYMTAB
+               #define EXPORT_SYMTAB
        #endif
 #else
        #define _static static
 #endif
 
+#define queue_dbg dbg //err
+#define async_dbg dbg //err
+
 #ifdef DEBUG_SLAB
        static kmem_cache_t *uhci_desc_kmem;
        static kmem_cache_t *urb_priv_kmem;
 #endif
 
+#define SLAB_FLAG     (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL)
+
+#define CONFIG_USB_UHCI_HIGH_BANDWIDTH 
 #define USE_CTRL_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
 #define USE_BULK_DEPTH_FIRST 0  // 0: Breadth first, 1: Depth first
 
-#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
-#define USE_RECLAMATION_LOOP
-#else
-//#define USE_RECLAMATION_LOOP
-#endif
-
-// stop bandwidth reclamation after (roughly) 50ms (depends also on
-// hub polling interval)
+// stop bandwidth reclamation after (roughly) 50ms
 #define IDLE_TIMEOUT  (HZ/20)
 
 _static int rh_submit_urb (urb_t *urb);
 _static int rh_unlink_urb (urb_t *urb);
 _static int delete_qh (uhci_t *s, uhci_desc_t *qh);
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode);
+_static int process_interrupt (uhci_t *s, urb_t *urb);
+_static int process_iso (uhci_t *s, urb_t *urb, int force);
 
 static uhci_t *devs = NULL;
 
@@ -105,58 +110,47 @@ void clean_descs(uhci_t *s, int force)
                qh = list_entry (q, uhci_desc_t, horizontal);
                if ((qh->last_used!=now) || force)
                        delete_qh(s,qh);
+
                q=qh->horizontal.prev;
        }
 }
 /*-------------------------------------------------------------------*/
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 _static void enable_desc_loop(uhci_t *s, urb_t *urb)
 {
        int flags;
-       
-       dbg("enable_desc_loop: enter");
-       
+
        spin_lock_irqsave (&s->qh_lock, flags);
-       s->chain_end->hw.qh.head=virt_to_bus(s->control_chain)|UHCI_PTR_QH;
+       s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; 
+       mb();
        s->loop_usage++;
        ((urb_priv_t*)urb->hcpriv)->use_loop=1;
        spin_unlock_irqrestore (&s->qh_lock, flags);
-       
-       dbg("enable_desc_loop: finished");
 }
 /*-------------------------------------------------------------------*/
 _static void disable_desc_loop(uhci_t *s, urb_t *urb)
 {
        int flags;
-       
-       dbg("disable_desc_loop: enter\n");
-       
+
        spin_lock_irqsave (&s->qh_lock, flags);
 
        if (((urb_priv_t*)urb->hcpriv)->use_loop) {
                s->loop_usage--;
 
-               if (!s->loop_usage)
-                       s->chain_end->hw.qh.head=UHCI_PTR_TERM;
-
+               if (!s->loop_usage) {
+                       s->chain_end->hw.qh.head|=UHCI_PTR_TERM;
+                       mb();
+               }
                ((urb_priv_t*)urb->hcpriv)->use_loop=0;
        }
        spin_unlock_irqrestore (&s->qh_lock, flags);
-       
-       dbg("disable_desc_loop: finished");
-
 }
 #endif
 /*-------------------------------------------------------------------*/
-_static void queue_urb (uhci_t *s, urb_t *urb)
+_static void queue_urb_unlocked (uhci_t *s, urb_t *urb)
 {
-       unsigned long flags=0;
        struct list_head *p=&urb->urb_list;
-       
-
-       spin_lock_irqsave (&s->urb_list_lock, flags);
-
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
        {
                int type;
                type=usb_pipetype (urb->pipe);
@@ -166,15 +160,21 @@ _static void queue_urb (uhci_t *s, urb_t *urb)
        }
 #endif
        ((urb_priv_t*)urb->hcpriv)->started=jiffies;
-       list_add_tail (p, &s->urb_list);
-       
-       spin_unlock_irqrestore (&s->urb_list_lock, flags);
+       list_add (p, &s->urb_list);
 }
+/*-------------------------------------------------------------------*/
+_static void queue_urb (uhci_t *s, urb_t *urb)
+{
+       unsigned long flags=0;
 
+       spin_lock_irqsave (&s->urb_list_lock, flags);
+       queue_urb_unlocked(s,urb);
+       spin_unlock_irqrestore (&s->urb_list_lock, flags);
+}
 /*-------------------------------------------------------------------*/
 _static void dequeue_urb (uhci_t *s, urb_t *urb)
 {
-#ifdef USE_RECLAMATION_LOOP
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
        int type;
 
        type=usb_pipetype (urb->pipe);
@@ -189,9 +189,9 @@ _static void dequeue_urb (uhci_t *s, urb_t *urb)
 _static int alloc_td (uhci_desc_t ** new, int flags)
 {
 #ifdef DEBUG_SLAB
-       *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+       *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
 #else
-       *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+       *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif
        if (!*new)
                return -ENOMEM;
@@ -205,6 +205,19 @@ _static int alloc_td (uhci_desc_t ** new, int flags)
        return 0;
 }
 /*-------------------------------------------------------------------*/
+// append a qh to td.link physically, the SW linkage is not affected
+_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int  flags)
+{
+       unsigned long xxx;
+       
+       spin_lock_irqsave (&s->td_lock, xxx);
+
+       td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH;
+       
+       mb();
+       spin_unlock_irqrestore (&s->td_lock, xxx);
+}
+/*-------------------------------------------------------------------*/
 /* insert td at last position in td-list of qh (vertical) */
 _static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags)
 {
@@ -271,10 +284,9 @@ _static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink)
                if (prev->type == TD_TYPE)
                        prev->hw.td.link = element->hw.td.link;
                else
-                       prev->hw.qh.element = element->hw.td.link;      
+                       prev->hw.qh.element = element->hw.td.link;
        }
 
-       element->hw.td.link=UHCI_PTR_TERM;
        mb ();
 
        if (dir == 0)
@@ -302,9 +314,9 @@ _static int delete_desc (uhci_desc_t *element)
 _static int alloc_qh (uhci_desc_t ** new)
 {
 #ifdef DEBUG_SLAB
-       *new= kmem_cache_alloc(uhci_desc_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+       *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG);
 #else
-       *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+       *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG);
 #endif 
        if (!*new)
                return -ENOMEM;
@@ -350,9 +362,10 @@ _static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order)
        mb ();
        
        spin_unlock_irqrestore (&s->qh_lock, flags);
-       
+
        return 0;
 }
+
 /*-------------------------------------------------------------------*/
 _static int unlink_qh (uhci_t *s, uhci_desc_t *element)
 {
@@ -406,6 +419,14 @@ _static void clean_td_chain (uhci_desc_t *td)
        
        delete_desc (td);
 }
+
+/*-------------------------------------------------------------------*/
+_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
+{
+       td->hw.td.status = status;
+       td->hw.td.info = info;
+       td->hw.td.buffer = buffer;
+}
 /*-------------------------------------------------------------------*/
 // Removes ALL qhs in chain (paranoia!)
 _static void cleanup_skel (uhci_t *s)
@@ -441,19 +462,20 @@ _static void cleanup_skel (uhci_t *s)
                qh = s->control_chain;
                while ((p = qh->horizontal.next) != &qh->horizontal) {
                        qh1 = list_entry (p, uhci_desc_t, horizontal);
-                       dbg("delete_qh @ %p",qh1);
                        delete_qh (s, qh1);
                }
-                       dbg("delete_qh last @ %p",qh);
+
                delete_qh (s, qh);
        }
        else {
+               if (s->ls_control_chain)
+                       delete_desc (s->ls_control_chain);
                if (s->control_chain)
-                       kfree (s->control_chain);
+                        delete_desc(s->control_chain);
                if (s->bulk_chain)
-                       kfree (s->bulk_chain);
+                       delete_desc (s->bulk_chain);
                if (s->chain_end)
-                       kfree (s->chain_end);
+                       delete_desc (s->chain_end);
        }
        dbg("cleanup_skel finished");   
 }
@@ -480,6 +502,7 @@ _static int init_skel (uhci_t *s)
        if (!s->iso_td)
                goto init_skel_cleanup;
 
+       s->ls_control_chain = NULL;
        s->control_chain = NULL;
        s->bulk_chain = NULL;
        s->chain_end = NULL;
@@ -499,26 +522,46 @@ _static int init_skel (uhci_t *s)
        
        if (ret)
                goto init_skel_cleanup;
-       
+                               
        s->chain_end = qh;
 
+       ret = alloc_td (&td, 0);
+
+       if (ret)
+               goto init_skel_cleanup;
+       
+       fill_td (td, TD_CTRL_IOC, 0, 0); // generate 1ms interrupt
+       insert_td (s, qh, td, 0);
+
        dbg("allocating qh: bulk_chain");
        ret = alloc_qh (&qh);
-       
        if (ret)
                goto init_skel_cleanup;
        
        insert_qh (s, s->chain_end, qh, 0);
        s->bulk_chain = qh;
+
        dbg("allocating qh: control_chain");
        ret = alloc_qh (&qh);
-       
        if (ret)
                goto init_skel_cleanup;
        
        insert_qh (s, s->bulk_chain, qh, 0);
        s->control_chain = qh;
 
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+       // disabled reclamation loop
+       s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM;
+#endif
+
+       dbg("allocating qh: ls_control_chain");
+       ret = alloc_qh (&qh);
+       if (ret)
+               goto init_skel_cleanup;
+       
+       insert_qh (s, s->control_chain, qh, 0);
+       s->ls_control_chain = qh;
+
        for (n = 0; n < 8; n++)
                s->int_chain[n] = 0;
 
@@ -532,7 +575,7 @@ _static int init_skel (uhci_t *s)
                        goto init_skel_cleanup;
                s->int_chain[n] = td;
                if (n == 0) {
-                       s->int_chain[0]->hw.td.link = virt_to_bus (s->control_chain) | UHCI_PTR_QH;
+                       s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH;
                }
                else {
                        s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]);
@@ -547,34 +590,22 @@ _static int init_skel (uhci_t *s)
                dbg("framelist[%i]=%x",n,s->framelist[n]);
                if ((n&127)==127) 
                        ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]);
-               else {
-                       for (o = 1, m = 2; m <= 128; o++, m += m) {
-                               // n&(m-1) = n%m
-                               if ((n & (m - 1)) == ((m - 1) / 2)) {
+               else 
+                       for (o = 1, m = 2; m <= 128; o++, m += m)
+                               if ((n & (m - 1)) == ((m - 1) / 2))
                                        ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]);
-                               }
-                       }
-               }
        }
 
        mb();
        //uhci_show_queue(s->control_chain);   
        dbg("init_skel exit");
-       return 0;               // OK
+       return 0;
 
       init_skel_cleanup:
        cleanup_skel (s);
        return -ENOMEM;
 }
 
-/*-------------------------------------------------------------------*/
-_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer)
-{
-       td->hw.td.status = status;
-       td->hw.td.info = info;
-       td->hw.td.buffer = buffer;
-}
-
 /*-------------------------------------------------------------------*/
 //                         LOW LEVEL STUFF
 //          assembles QHs und TDs for control, bulk and iso
@@ -586,10 +617,15 @@ _static int uhci_submit_control_urb (urb_t *urb)
        urb_priv_t *urb_priv = urb->hcpriv;
        unsigned long destination, status;
        int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe));
-       unsigned long len, bytesrequested;
+       unsigned long len;
        char *data;
        int depth_first=USE_CTRL_DEPTH_FIRST;  // UHCI descriptor chasing method
 
+       if (!maxsze) {
+               err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe);
+               return -EINVAL;
+       }
+
        dbg("uhci_submit_control start");
        alloc_qh (&qh);         // alloc qh for this request
 
@@ -615,26 +651,21 @@ _static int uhci_submit_control_urb (urb_t *urb)
 
        insert_td (s, qh, td, 0);       // queue 'setup stage'-td in qh
 #if 0
-       dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
-               urb->setup_packet[0], urb->setup_packet[1], urb->setup_packet[2], urb->setup_packet[3],
-               urb->setup_packet[4], urb->setup_packet[5], urb->setup_packet[6], urb->setup_packet[7]);
+       {
+               char *sp=urb->setup_packet;
+               dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe,
+                   sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]);
+       }
        //uhci_show_td(td);
 #endif
 
-       /*  Build the DATA TD's */
        len = urb->transfer_buffer_length;
-       bytesrequested = len;
        data = urb->transfer_buffer;
 
        /* If direction is "send", change the frame from SETUP (0x2D)
           to OUT (0xE1). Else change it from SETUP to IN (0x69). */
 
-       destination &= ~UHCI_PID;
-       
-       if (usb_pipeout (urb->pipe))
-               destination |= USB_PID_OUT;
-       else
-               destination |= USB_PID_IN;      
+       destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN);
 
        while (len > 0) {
                int pktsze = len;
@@ -664,7 +695,7 @@ _static int uhci_submit_control_urb (urb_t *urb)
 
        destination &= ~UHCI_PID;
 
-       if (usb_pipeout (urb->pipe) || (bytesrequested == 0))
+       if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0))
                destination |= USB_PID_IN;
        else
                destination |= USB_PID_OUT;
@@ -690,32 +721,34 @@ _static int uhci_submit_control_urb (urb_t *urb)
        urb->status = -EINPROGRESS;
        queue_urb (s, urb);     // queue before inserting in desc chain
 
-       qh->hw.qh.element&=~UHCI_PTR_TERM;
+       qh->hw.qh.element &= ~UHCI_PTR_TERM;
 
        //uhci_show_queue(qh);
        /* Start it up... put low speed first */
        if (urb->pipe & TD_CTRL_LS)
-               insert_qh (s, s->control_chain, qh, 1); // insert after control chain
+               insert_qh (s, s->control_chain, qh, 0);
        else
-               insert_qh (s, s->bulk_chain, qh, 0);    // insert before bulk chain
-       //uhci_show_queue(qh);
+               insert_qh (s, s->bulk_chain, qh, 0);
 
        dbg("uhci_submit_control end");
        return 0;
 }
 /*-------------------------------------------------------------------*/
-_static int uhci_submit_bulk_urb (urb_t *urb)
+// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh)
+// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock!
+
+_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb)
 {
        uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
        urb_priv_t *urb_priv = urb->hcpriv;
-       uhci_desc_t *qh, *td;
+       uhci_desc_t *qh, *td, *nqh, *bqh;
        unsigned long destination, status;
        char *data;
        unsigned int pipe = urb->pipe;
        int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe));
        int info, len;
        int depth_first=USE_BULK_DEPTH_FIRST;  // UHCI descriptor chasing method
-
+       urb_priv_t *upriv, *bpriv;
 
        if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)))
                return -EPIPE;
@@ -727,12 +760,55 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
        
        if (!maxsze)
                return -EMSGSIZE;
-       /* FIXME: should tell the client that the endpoint is invalid, i.e. not in the descriptor */
+       
+       queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i",
+                 urb,bulk_urb,urb->pipe,urb->transfer_buffer_length);
 
-       alloc_qh (&qh);         // get qh for this request
+       upriv=(urb_priv_t*)urb->hcpriv;
 
-       if (!qh)
-               return -ENOMEM;
+       if (!bulk_urb) {
+               alloc_qh (&qh);         // get qh for this request
+               
+               if (!qh)
+                       return -ENOMEM;
+
+               if (urb->transfer_flags & USB_QUEUE_BULK) {
+                       alloc_qh(&nqh); // placeholder for clean unlink
+                       if (!nqh) {
+                               delete_desc (qh);
+                               return -ENOMEM;
+                       }
+                       upriv->next_qh = nqh;
+                       queue_dbg("new next qh %p",nqh);
+               }
+       }
+       else { 
+               bpriv = (urb_priv_t*)bulk_urb->hcpriv;
+               qh = bpriv->bottom_qh;  // re-use bottom qh and next qh
+               nqh = bpriv->next_qh;
+               upriv->next_qh=nqh;
+               bpriv->next_queued_urb=urb;
+               upriv->prev_queued_urb=bulk_urb;
+       }
+
+       queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh);
+
+       if (urb->transfer_flags & USB_QUEUE_BULK) {
+               alloc_qh (&bqh); // "bottom" QH,
+               
+               if (!bqh) {
+                       if (!bulk_urb) { 
+                               delete_desc(qh);
+                               delete_desc(nqh);
+                       }
+                       return -ENOMEM;
+               }
+               bqh->hw.qh.element = UHCI_PTR_TERM;
+               bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH;
+               upriv->bottom_qh = bqh;
+               queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh);
+       }
+       
 
        /* The "pipe" thing contains the destination in bits 8--18. */
        destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe);
@@ -744,7 +820,6 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
        /* Build the TDs for the bulk request */
        len = urb->transfer_buffer_length;
        data = urb->transfer_buffer;
-       dbg("uhci_submit_bulk_urb: pipe %x, len %d", pipe, len);
        
        do {                                    // TBD: Really allow zero-length packets?
                int pktsze = len;
@@ -770,54 +845,149 @@ _static int uhci_submit_bulk_urb (urb_t *urb)
 
                if (!len)
                        td->hw.td.status |= TD_CTRL_IOC;        // last one generates INT
-               //dbg("insert td %p, len %i",td,pktsze);
 
                insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first);
-               
-               /* Alternate Data0/1 (start with Data0) */
                usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
+
        } while (len > 0);
 
        list_add (&qh->desc_list, &urb_priv->desc_list);
 
+       if (urb->transfer_flags & USB_QUEUE_BULK) {
+               qh->hw.qh.element&=~UHCI_PTR_TERM;
+               append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first);
+       }
+
        urb->status = -EINPROGRESS;
-       queue_urb (s, urb);
+       queue_urb_unlocked (s, urb);
        
-       qh->hw.qh.element&=~UHCI_PTR_TERM;
+       qh->hw.qh.element  &= ~UHCI_PTR_TERM;
 
-       insert_qh (s, s->chain_end, qh, 0);     // insert before end marker
+       if (!bulk_urb) { 
+               if (urb->transfer_flags & USB_QUEUE_BULK) {
+                       spin_lock (&s->td_lock);                        // both QHs in one go
+                       insert_qh (s, s->chain_end, qh, 0);     // Main QH
+                       insert_qh (s, s->chain_end, nqh, 0);    // Helper QH
+                       spin_unlock (&s->td_lock);
+               }
+               else
+                       insert_qh (s, s->chain_end, qh, 0);
+       }
+       
        //uhci_show_queue(s->bulk_chain);
-
-       dbg("uhci_submit_bulk_urb: exit");
+       //dbg("uhci_submit_bulk_urb: exit\n");
        return 0;
 }
-
 /*-------------------------------------------------------------------*/
-// unlinks an urb by dequeuing its qh, waits some frames and forgets it
-// Problem: unlinking in interrupt requires waiting for one frame (udelay)
-// to allow the whole structures to be safely removed
-_static int uhci_unlink_urb (urb_t *urb)
+_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv)
 {
-       uhci_t *s;
-       uhci_desc_t *qh;
+       struct list_head *p;
        uhci_desc_t *td;
-       urb_priv_t *urb_priv;
-       unsigned long flags=0;
 
+       for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
+                               td = list_entry (p, uhci_desc_t, desc_list);
+                               unlink_td (s, td, 1);
+       }
+}
+/*-------------------------------------------------------------------*/
+_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv)
+{
        struct list_head *p;
+       uhci_desc_t *td;
 
-       if (!urb || !urb->dev)          // you never know...
-               return -EINVAL;
+       while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
+                               td = list_entry (p, uhci_desc_t, desc_list);
+                               list_del (p);
+                               delete_desc (td);
+       }
+}
+/*-------------------------------------------------------------------*/
+// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink
+// looks a bit complicated because of all the bulk queueing goodies
 
-       s = (uhci_t*) urb->dev->bus->hcpriv;    // get pointer to uhci struct
+_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode)
+{
+       uhci_desc_t *bqh, *nqh, *prevqh;
+       int now;
+       urb_priv_t *priv=(urb_priv_t*)urb->hcpriv;
 
-       if (usb_pipedevice (urb->pipe) == s->rh.devnum)
-               return rh_unlink_urb (urb);
+       now=UHCI_GET_CURRENT_FRAME(s);
 
-       if (!urb->hcpriv)               // you never know...
-               return -EINVAL;
+       dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode);
+       bqh=priv->bottom_qh;    
 
-       //dbg("unlink_urb called %p",urb);
+       if (!priv->next_queued_urb)  { // no more appended bulk queues
+
+               if (mode != 2) 
+                       unlink_qh (s, qh);
+       
+               if (priv->prev_queued_urb) {
+                       urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+
+                       ppriv->bottom_qh = priv->bottom_qh;
+                       ppriv->next_queued_urb = NULL;
+               }
+               else if (bqh) {  // queue dead
+                       nqh=priv->next_qh;
+                       
+                       if (mode != 2)
+                               unlink_qh(s, nqh);
+
+                       if (mode) {
+                               nqh->last_used = bqh->last_used = now;
+                               list_add_tail (&nqh->horizontal, &s->free_desc);
+                               list_add_tail (&bqh->horizontal, &s->free_desc);
+                       }                       
+               }
+       }
+       else { // there are queued urbs following
+               urb_t *nurb;
+               unsigned long flags;
+               
+                       nurb=priv->next_queued_urb;
+               spin_lock_irqsave (&s->qh_lock, flags);
+               
+               if (!priv->prev_queued_urb) { // top
+                       if (mode !=2) {
+                               prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal);
+                               prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+                               queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh);  
+                       
+                               list_del (&qh->horizontal);
+                               list_add (&bqh->horizontal, &prevqh->horizontal);
+                       }       
+               }
+               else {          //intermediate
+                       urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv;
+                       uhci_desc_t * bnqh;
+                       
+                       bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list);
+                       ppriv->bottom_qh=bnqh;
+                       ppriv->next_queued_urb=nurb;
+                       
+                       if (mode!=2) {
+                               prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list);
+                               prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH;
+                               queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh);   
+                       }
+               }
+               mb();
+               spin_unlock_irqrestore (&s->qh_lock, flags);
+               ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb;     
+       }
+
+       if (mode) {
+               qh->last_used = now;
+               list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+       }
+}
+/*-------------------------------------------------------------------*/
+// unlinks an urb by dequeuing its qh, waits some frames and forgets it
+_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb)
+{
+       uhci_desc_t *qh;
+       urb_priv_t *urb_priv;
+       unsigned long flags=0;
 
        spin_lock_irqsave (&s->urb_list_lock, flags);
 
@@ -833,32 +1003,22 @@ _static int uhci_unlink_urb (urb_t *urb)
                switch (usb_pipetype (urb->pipe)) {
                case PIPE_ISOCHRONOUS:
                case PIPE_INTERRUPT:
-                       for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) {
-                               td = list_entry (p, uhci_desc_t, desc_list);
-                               unlink_td (s, td, 1);
-                       }
-                       // wait at least 1 Frame
-                       uhci_wait_ms(1);        
-                       while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) {
-                               td = list_entry (p, uhci_desc_t, desc_list);
-                               list_del (p);
-                               delete_desc (td);
-                       }
+                       uhci_clean_iso_step1(s, urb_priv);
+                       uhci_wait_ms(1);
+                       uhci_clean_iso_step2(s, urb_priv);
                        break;
 
                case PIPE_BULK:
                case PIPE_CONTROL:
                        qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
-
-                       unlink_qh (s, qh);      // remove this qh from qh-list
-                       qh->last_used=UHCI_GET_CURRENT_FRAME(s);
-                       list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
-                       // wait at least 1 Frame
+                       spin_lock_irqsave (&s->urb_list_lock, flags);
+                       uhci_clean_transfer(s, urb, qh, 1);
+                       spin_unlock_irqrestore (&s->urb_list_lock, flags);
                        uhci_wait_ms(1);
                }
                
 #ifdef DEBUG_SLAB
-               kmem_cache_free(urb_priv_kmem, urb->hcpriv);
+               kmem_cache_free (urb_priv_kmem, urb->hcpriv);
 #else
                kfree (urb->hcpriv);
 #endif
@@ -874,6 +1034,147 @@ _static int uhci_unlink_urb (urb_t *urb)
        return 0;
 }
 /*-------------------------------------------------------------------*/
+// async unlink_urb completion/cleanup work
+// has to be protected by urb_list_lock!
+// features: if set in transfer_flags, the resulting status of the killed
+// transaction is not overwritten
+
+_static void uhci_cleanup_unlink(uhci_t *s, int force)
+{
+       struct list_head *q;
+       urb_t *urb;
+       struct usb_device *dev;
+       int pipe,now;
+       urb_priv_t *urb_priv;
+
+       q=s->urb_unlinked.next;
+       now=UHCI_GET_CURRENT_FRAME(s);
+
+       while (q != &s->urb_unlinked) {
+
+               urb = list_entry (q, urb_t, urb_list);
+
+               urb_priv = (urb_priv_t*)urb->hcpriv;
+               q = urb->urb_list.next;
+               
+               if (force ||
+                   ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) {
+                       async_dbg("async cleanup %p",urb);
+                       switch (usb_pipetype (urb->pipe)) { // process descriptors
+                       case PIPE_CONTROL:
+                               process_transfer (s, urb, 2);
+                               break;
+                       case PIPE_BULK:
+                               if (!s->avoid_bulk.counter)
+                                       process_transfer (s, urb, 2); // don't unlink (already done)
+                               else
+                                       continue;
+                               break;
+                       case PIPE_ISOCHRONOUS:
+                               process_iso (s, urb, 1); // force, don't unlink
+                               break;
+                       case PIPE_INTERRUPT:
+                               process_interrupt (s, urb);
+                               break;
+                       }
+
+                       if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+                               urb->status = -ECONNRESET; // mark as asynchronously killed
+
+                       pipe = urb->pipe;               // completion may destroy all...
+                       dev = urb->dev;
+                       urb_priv = urb->hcpriv;
+
+                       if (urb->complete) {
+                               spin_unlock(&s->urb_list_lock);
+                               urb->complete ((struct urb *) urb);
+                               spin_lock(&s->urb_list_lock);
+                       }
+
+                       if (!(urb->transfer_flags & USB_TIMEOUT_KILLED))
+                               urb->status = -ENOENT;  // now the urb is really dead
+       
+                       usb_dec_dev_use (dev);
+#ifdef DEBUG_SLAB
+                       kmem_cache_free (urb_priv_kmem, urb_priv);
+#else
+                       kfree (urb_priv);
+#endif
+                       switch (usb_pipetype (pipe)) {
+                       case PIPE_ISOCHRONOUS:
+                       case PIPE_INTERRUPT:
+                               uhci_clean_iso_step2(s, urb_priv);
+                               break;
+                       }
+                       list_del (&urb->urb_list);
+               }
+       }
+}
+
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb)
+{
+       uhci_desc_t *qh;
+       urb_priv_t *urb_priv;
+       
+       async_dbg("unlink_urb_async called %p",urb);
+
+       if (urb->status == -EINPROGRESS) {
+               ((urb_priv_t*)urb->hcpriv)->started = ~0;
+               dequeue_urb (s, urb);
+               list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb
+
+               s->unlink_urb_done = 1;
+               
+               urb->status = -ECONNABORTED;    // mark urb as "waiting to be killed"   
+               urb_priv = (urb_priv_t*)urb->hcpriv;
+
+               switch (usb_pipetype (urb->pipe)) {
+               case PIPE_ISOCHRONOUS:
+               case PIPE_INTERRUPT:
+                       uhci_clean_iso_step1 (s, urb_priv);
+                       break;
+
+               case PIPE_BULK:
+               case PIPE_CONTROL:
+                       qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list);
+                       uhci_clean_transfer (s, urb, qh, 0);
+                       break;
+               }
+               ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s);
+       }               
+
+       return -EINPROGRESS;
+}
+/*-------------------------------------------------------------------*/
+_static int uhci_unlink_urb (urb_t *urb)
+{
+       uhci_t *s;
+       unsigned long flags=0;
+       dbg("uhci_unlink_urb called for %p",urb);
+       if (!urb || !urb->dev)          // you never know...
+               return -EINVAL;
+       
+       s = (uhci_t*) urb->dev->bus->hcpriv;
+
+       if (usb_pipedevice (urb->pipe) == s->rh.devnum)
+               return rh_unlink_urb (urb);
+
+       if (!urb->hcpriv)
+               return -EINVAL;
+
+       if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+               int ret;
+
+               spin_lock_irqsave (&s->urb_list_lock, flags);
+               ret = uhci_unlink_urb_async(s, urb);
+               spin_unlock_irqrestore (&s->urb_list_lock, flags);
+               return ret;
+       }
+       else
+               return uhci_unlink_urb_sync(s, urb);
+}
+/*-------------------------------------------------------------------*/
 // In case of ASAP iso transfer, search the URB-list for already queued URBs
 // for this EP and calculate the earliest start frame for the new
 // URB (easy seamless URB continuation!)
@@ -886,9 +1187,9 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
        unsigned long flags;
        
        spin_lock_irqsave (&s->urb_list_lock, flags);
-       p=s->urb_list.next;
+       p=s->urb_list.prev;
 
-       for (; p != &s->urb_list; p = p->next) {
+       for (; p != &s->urb_list; p = p->prev) {
                u = list_entry (p, urb_t, urb_list);
                // look for pending URBs with identical pipe handle
                // works only because iso doesn't toggle the data bit!
@@ -906,8 +1207,7 @@ _static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end)
        
        spin_unlock_irqrestore(&s->urb_list_lock, flags);
        
-       return ret;     // no previous urb found
-
+       return ret;
 }
 /*-------------------------------------------------------------------*/
 // adjust start_frame according to scheduling constraints (ASAP etc)
@@ -940,35 +1240,7 @@ _static int iso_find_start (urb_t *urb)
                                info("iso_find_start: gap in seamless isochronous scheduling");
                                dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x",
                                        now, urb->start_frame, urb->number_of_packets, urb->pipe);
-// The following code is only for debugging purposes...
-#if 0
-                               {
-                                       uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv;
-                                       struct list_head *p;
-                                       urb_t *u;
-                                       int a = -1, b = -1;
-                                       unsigned long flags;
-
-                                       spin_lock_irqsave (&s->urb_list_lock, flags);
-                                       p=s->urb_list.next;
-
-                                       for (; p != &s->urb_list; p = p->next) {
-                                               u = list_entry (p, urb_t, urb_list);
-                                               if (urb->dev != u->dev)
-                                                       continue;
-                                               dbg("urb: pipe 0x%08x status %d start_frame %u number_of_packets %u",
-                                                       u->pipe, u->status, u->start_frame, u->number_of_packets);
-                                               if (!usb_pipeisoc (u->pipe))
-                                                       continue;
-                                               if (a == -1)
-                                                       a = u->start_frame;
-                                               b = (u->start_frame + u->number_of_packets - 1) & 1023;
-                                       }
-                                       spin_unlock_irqrestore(&s->urb_list_lock, flags);
-                               }
-#endif
                                urb->start_frame = (now + 5) & 1023;    // 5ms setup should be enough //FIXME!
-                               //return -EAGAIN; //FIXME
                        }
                }
        }
@@ -996,7 +1268,7 @@ _static int iso_find_start (urb_t *urb)
 /*-------------------------------------------------------------------*/
 // submits USB interrupt (ie. polling ;-) 
 // ASAP-flag set implicitely
-// if period==0, the the transfer is only done once (usb_scsi need this...)
+// if period==0, the the transfer is only done once
 
 _static int uhci_submit_int_urb (urb_t *urb)
 {
@@ -1005,12 +1277,9 @@ _static int uhci_submit_int_urb (urb_t *urb)
        int nint, n, ret;
        uhci_desc_t *td;
        int status, destination;
-       int now;
        int info;
        unsigned int pipe = urb->pipe;
 
-       //dbg("SUBMIT INT");
-
        if (urb->interval < 0 || urb->interval >= 256)
                return -EINVAL;
 
@@ -1029,8 +1298,7 @@ _static int uhci_submit_int_urb (urb_t *urb)
 
        dbg("Rounded interval to %i, chain  %i", urb->interval, nint);
 
-       now = UHCI_GET_CURRENT_FRAME (s) & 1023;
-       urb->start_frame = now; // remember start frame, just in case...
+       urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023;   // remember start frame, just in case...
 
        urb->number_of_packets = 1;
 
@@ -1062,13 +1330,6 @@ _static int uhci_submit_int_urb (urb_t *urb)
 
        usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe));
 
-#if 0
-       td = tdm[urb->number_of_packets];
-       fill_td (td, TD_CTRL_IOC, 0, 0);
-       insert_td_horizontal (s, s->iso_td[(urb->start_frame + (urb->number_of_packets) * urb->interval + 1) & 1023], td);
-       list_add_tail (&td->desc_list, &urb_priv->desc_list);
-#endif
-
        return 0;
 }
 /*-------------------------------------------------------------------*/
@@ -1086,11 +1347,11 @@ _static int uhci_submit_iso_urb (urb_t *urb)
        __save_flags(flags);
        __cli();                      // Disable IRQs to schedule all ISO-TDs in time
        ret = iso_find_start (urb);     // adjusts urb->start_frame for later use
-
+       
        if (ret)
                goto err;
 
-       tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+       tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG);
 
        if (!tdm) {
                ret = -ENOMEM;
@@ -1105,14 +1366,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
                        tdm[n] = 0;
                        continue;
                }
-               #ifdef ISO_SANITY_CHECK
+               
                if(urb->iso_frame_desc[n].length > maxsze) {
+#ifdef ISO_SANITY_CHECK
                        err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze);
                        tdm[n] = 0;
                        ret=-EINVAL;
                        goto inval;
+#endif
                }
-               #endif
+               
                ret = alloc_td (&td, UHCI_PTR_DEPTH);
        inval:
                if (ret) {
@@ -1120,7 +1383,7 @@ _static int uhci_submit_iso_urb (urb_t *urb)
 
                        for (i = 0; i < n; n++)
                                if (tdm[i])
-                                       kfree (tdm[i]);
+                                        delete_desc(tdm[i]);
                        kfree (tdm);
                        goto err;
                }
@@ -1165,15 +1428,16 @@ _static int uhci_submit_iso_urb (urb_t *urb)
 
 }
 /*-------------------------------------------------------------------*/
-_static int search_dev_ep (uhci_t *s, urb_t *urb)
+// returns: 0 (no transfer queued), urb* (this urb already queued)
+_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb)
 {
-       unsigned long flags;
        struct list_head *p;
        urb_t *tmp;
        unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0);
 
        dbg("search_dev_ep:");
-       spin_lock_irqsave (&s->urb_list_lock, flags);
+
        p=s->urb_list.next;
 
        for (; p != &s->urb_list; p = p->next) {
@@ -1181,13 +1445,12 @@ _static int search_dev_ep (uhci_t *s, urb_t *urb)
                dbg("urb: %p", tmp);
                // we can accept this urb if it is not queued at this time 
                // or if non-iso transfer requests should be scheduled for the same device and pipe
-               if ((!usb_pipeisoc(urb->pipe) && tmp->dev == urb->dev && !((tmp->pipe ^ urb->pipe) & mask)) ||
+               if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) ||
                    (urb == tmp)) {
-                       spin_unlock_irqrestore (&s->urb_list_lock, flags);
-                       return 1;       // found another urb already queued for processing
+                       return tmp;     // found another urb already queued for processing
                }
        }
-       spin_unlock_irqrestore (&s->urb_list_lock, flags);
+
        return 0;
 }
 /*-------------------------------------------------------------------*/
@@ -1196,60 +1459,93 @@ _static int uhci_submit_urb (urb_t *urb)
        uhci_t *s;
        urb_priv_t *urb_priv;
        int ret = 0;
-
+       unsigned long flags;
+       urb_t *bulk_urb=NULL;
+               
        if (!urb->dev || !urb->dev->bus)
                return -ENODEV;
 
        s = (uhci_t*) urb->dev->bus->hcpriv;
        //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe));
-
+       
+       if (!s->running)
+               return -ENODEV;
+               
        if (usb_pipedevice (urb->pipe) == s->rh.devnum)
                return rh_submit_urb (urb);     /* virtual root hub */
 
        usb_inc_dev_use (urb->dev);
 
-       if (search_dev_ep (s, urb)) {
-               usb_dec_dev_use (urb->dev);
-               return -ENXIO;  // urb already queued
+       spin_lock_irqsave (&s->urb_list_lock, flags);
+
+       bulk_urb = search_dev_ep (s, urb);
 
+       if (bulk_urb) {
+
+               queue_dbg("found bulk urb %p\n",bulk_urb);
+
+               if ((usb_pipetype (urb->pipe) != PIPE_BULK) ||
+                   ((usb_pipetype (urb->pipe) == PIPE_BULK) &&
+                    (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) {
+                       spin_unlock_irqrestore (&s->urb_list_lock, flags);
+                       usb_dec_dev_use (urb->dev);
+                       err("ENXIO1 %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb);
+                       return -ENXIO;  // urb already queued
+               }
        }
 
 #ifdef DEBUG_SLAB
-       urb_priv = kmem_cache_alloc(urb_priv_kmem, in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL);
+       urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG);
 #else
-       urb_priv = kmalloc (sizeof (urb_priv_t), in_interrupt ()? GFP_ATOMIC : GFP_KERNEL);
+       urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG);
 #endif
        if (!urb_priv) {
                usb_dec_dev_use (urb->dev);
+               spin_unlock_irqrestore (&s->urb_list_lock, flags);
                return -ENOMEM;
        }
 
        urb->hcpriv = urb_priv;
        INIT_LIST_HEAD (&urb_priv->desc_list);
-       urb_priv->short_control_packet=0;
+       urb_priv->short_control_packet = 0;
        dbg("submit_urb: scheduling %p", urb);
-
-       switch (usb_pipetype (urb->pipe)) {
-       case PIPE_ISOCHRONOUS:
-               ret = uhci_submit_iso_urb (urb);
-               break;
-       case PIPE_INTERRUPT:
-               ret = uhci_submit_int_urb (urb);
-               break;
-       case PIPE_CONTROL:
-               //dump_urb (urb);
-               ret = uhci_submit_control_urb (urb);
-               break;
-       case PIPE_BULK:
-               ret = uhci_submit_bulk_urb (urb);
-               break;
-       default:
-               ret = -EINVAL;
+       urb_priv->next_queued_urb = NULL;
+       urb_priv->prev_queued_urb = NULL;
+       urb_priv->bottom_qh = NULL;
+       urb_priv->next_qh = NULL;
+       
+       if (usb_pipetype (urb->pipe) == PIPE_BULK) {
+       
+               if (bulk_urb) {
+                       while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb)  // find last queued bulk
+                               bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb;
+                       
+                       ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb;
+               }
+               atomic_inc (&s->avoid_bulk);
+               ret = uhci_submit_bulk_urb (urb, bulk_urb);
+               atomic_dec (&s->avoid_bulk);
+               spin_unlock_irqrestore (&s->urb_list_lock, flags);
+       }
+       else {
+               spin_unlock_irqrestore (&s->urb_list_lock, flags);
+               switch (usb_pipetype (urb->pipe)) {
+               case PIPE_ISOCHRONOUS:
+                       ret = uhci_submit_iso_urb (urb);
+                       break;
+               case PIPE_INTERRUPT:
+                       ret = uhci_submit_int_urb (urb);
+                       break;
+               case PIPE_CONTROL:
+                       ret = uhci_submit_control_urb (urb);
+                       break;
+               default:
+                       ret = -EINVAL;
+               }
        }
 
        dbg("submit_urb: scheduled with ret: %d", ret);
        
-       
        if (ret != 0) {
                usb_dec_dev_use (urb->dev);
 #ifdef DEBUG_SLAB
@@ -1262,41 +1558,43 @@ _static int uhci_submit_urb (urb_t *urb)
 
        return 0;
 }
-#ifdef USE_RECLAMATION_LOOP
-// Removes bandwidth reclamation if URB idles too long
-void check_idling_urbs(uhci_t *s)
+
+// Checks for URB timeout and removes bandwidth reclamation 
+// if URB idles too long
+_static void uhci_check_timeouts(uhci_t *s)
 {
        struct list_head *p,*p2;
        urb_t *urb;
        int type;       
 
-       //dbg("check_idling_urbs: enter i:%d",in_interrupt());
-       
-       spin_lock (&s->urb_list_lock);
        p = s->urb_list.prev;   
 
        while (p != &s->urb_list) {
+               urb_priv_t *hcpriv;
+
                p2 = p;
                p = p->prev;
-               urb=list_entry (p2, urb_t, urb_list);
-               type=usb_pipetype (urb->pipe);
-
-#if 0
-               err("URB timers: %li now: %li %i\n",
-                       ((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT, jiffies,
-                       type);
-#endif                  
-               if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
-                   (((urb_priv_t*)urb->hcpriv)->use_loop) &&
-                   ((((urb_priv_t*)urb->hcpriv)->started +IDLE_TIMEOUT) < jiffies))
-                       disable_desc_loop(s,urb);
+               urb = list_entry (p2, urb_t, urb_list);
+               type = usb_pipetype (urb->pipe);
+
+               hcpriv = (urb_priv_t*)urb->hcpriv;
+                               
+               if ( urb->timeout && 
+                       ((hcpriv->started + urb->timeout) < jiffies)) {
+                       urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
+                       async_dbg("uhci_check_timeout: timeout for %p",urb);
+                       uhci_unlink_urb_async(s, urb);
+               }
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+               else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
+                    (hcpriv->use_loop) &&
+                    ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+                       disable_desc_loop(s, urb);
+#endif
 
        }
-       spin_unlock (&s->urb_list_lock);
-       
-       //dbg("check_idling_urbs: finished");
 }
-#endif
+
 /*-------------------------------------------------------------------
  Virtual Root Hub
  -------------------------------------------------------------------*/
@@ -1396,7 +1694,6 @@ _static int rh_send_irq (urb_t *urb)
                dbg("Root-Hub INT complete: port1: %x port2: %x data: %x",
                     inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data);
                urb->complete (urb);
-
        }
        return 0;
 }
@@ -1411,10 +1708,6 @@ _static void rh_int_timer_do (unsigned long ptr)
        urb_t *urb = (urb_t*) ptr;
        uhci_t *uhci = urb->dev->bus->hcpriv;
 
-#ifdef USE_RECLAMATION_LOOP
-       check_idling_urbs(uhci);
-#endif
-
        if (uhci->rh.send) {
                len = rh_send_irq (urb);
                if (len > 0) {
@@ -1427,7 +1720,9 @@ _static void rh_int_timer_do (unsigned long ptr)
 }
 
 /*-------------------------------------------------------------------------*/
-/* Root Hub INTs are polled by this timer */
+/* Root Hub INTs are polled by this timer, polling interval 20ms */
+/* This time is also used for URB-timeout checking */
+
 _static int rh_init_int_timer (urb_t *urb)
 {
        uhci_t *uhci = urb->dev->bus->hcpriv;
@@ -1436,7 +1731,7 @@ _static int rh_init_int_timer (urb_t *urb)
        init_timer (&uhci->rh.rh_int_timer);
        uhci->rh.rh_int_timer.function = rh_int_timer_do;
        uhci->rh.rh_int_timer.data = (unsigned long) urb;
-       uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000;
+       uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000;
        add_timer (&uhci->rh.rh_int_timer);
 
        return 0;
@@ -1640,7 +1935,6 @@ _static int rh_submit_urb (urb_t *urb)
                stat = -EPIPE;
        }
 
-
        dbg("Root-Hub stat port1: %x port2: %x",
             inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2));
 
@@ -1716,13 +2010,19 @@ _static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_
        p = s->urb_list.prev;   
        while (p != &s->urb_list) {
                p2 = p;
-               p = p->prev;
+               p = p->prev ;
                urb = list_entry (p2, urb_t, urb_list);
-               dbg("urb: %p", urb);
+               dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev);
+               
+               //urb->transfer_flags |=USB_ASYNC_UNLINK; 
+                       
                if (remove_all || (usb_dev == urb->dev)) {
+                       spin_unlock_irqrestore (&s->urb_list_lock, flags);
                        warn("forced removing of queued URB %p due to disconnect",urb);
                        uhci_unlink_urb(urb);
-                       urb->dev = NULL; // avoid further processing of this URB
+                       urb->dev = NULL; // avoid further processing of this UR
+                       spin_lock_irqsave (&s->urb_list_lock, flags);
+                       p = s->urb_list.prev;   
                }
        }
        spin_unlock_irqrestore (&s->urb_list_lock, flags);
@@ -1732,13 +2032,11 @@ _static int uhci_free_dev (struct usb_device *usb_dev)
 {
        uhci_t *s;
        
-       dbg("uhci_free_dev");   
 
        if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv)
                return -EINVAL;
        
-       s=(uhci_t*) usb_dev->bus->hcpriv;
-       
+       s=(uhci_t*) usb_dev->bus->hcpriv;       
        uhci_unlink_urbs(s, usb_dev, 0);
 
        return 0;
@@ -1769,12 +2067,10 @@ struct usb_operations uhci_device_operations =
  * have announced. This leads to a queue abort due to the short packet,
  * the status stage is not executed. If this happens, the status stage
  * is manually re-executed.
- * FIXME: Stall-condition may override 'nearly' successful CTRL-IN-transfer
- * when the transfered length fits exactly in maxsze-packets. A bit
- * more intelligence is needed to detect this and finish without error.
+ * mode: 0: QHs already unlinked
  */
 
-_static int process_transfer (uhci_t *s, urb_t *urb)
+_static int process_transfer (uhci_t *s, urb_t *urb, int mode)
 {
        int ret = 0;
        urb_priv_t *urb_priv = urb->hcpriv;
@@ -1784,25 +2080,21 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
        uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
        uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical);
        int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));      // save initial data_toggle
-
-
-       // extracted and remapped info from TD
-       int maxlength;
+       int maxlength;  // extracted and remapped info from TD
        int actual_length;
        int status = 0;
 
-       dbg("process_transfer: urb contains bulk/control request");
-
+       //dbg("process_transfer: urb contains bulk/control request");
 
        /* if the status phase has been retriggered and the
           queue is empty or the last status-TD is inactive, the retriggered
           status stage is completed
         */
-#if 1
+
        if (urb_priv->short_control_packet && 
                ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) 
                goto transfer_finished;
-#endif
+
        urb->actual_length=0;
 
        for (; p != &qh->vertical; p = p->next) {
@@ -1810,22 +2102,20 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
 
                if (desc->hw.td.status & TD_CTRL_ACTIVE)        // do not process active TDs
                        return ret;
-
-               // extract transfer parameters from TD
-               actual_length = (desc->hw.td.status + 1) & 0x7ff;
+       
+               actual_length = (desc->hw.td.status + 1) & 0x7ff;               // extract transfer parameters from TD
                maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff;
                status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe));
 
-               // see if EP is stalled
-               if (status == -EPIPE) {
+               if (status == -EPIPE) {                 // see if EP is stalled
                        // set up stalled condition
                        usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
                }
 
-               // if any error occured stop processing of further TDs
-               if (status != 0) {
+               if (status != 0) {              // if any error occured stop processing of further TDs
                        // only set ret if status returned an error
-                       uhci_show_td (desc);
+                       if (status != -EPIPE) 
+                               uhci_show_td (desc);
                        ret = status;
                        urb->error_count++;
                        break;
@@ -1833,11 +2123,6 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
                else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP)
                        urb->actual_length += actual_length;
 
-#if 0
-               //      if (i++==0)
-                       uhci_show_td (desc);    // show first TD of each transfer
-#endif
-
                // got less data than requested
                if ( (actual_length < maxlength)) {
                        if (urb->transfer_flags & USB_DISABLE_SPD) {
@@ -1852,8 +2137,8 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
                        
                                        qh->hw.qh.element = virt_to_bus (last_desc);  // re-trigger status stage
                                        dbg("short packet during control transfer, retrigger status stage @ %p",last_desc);
-                                       uhci_show_td (desc);
-                                       uhci_show_td (last_desc);
+                                       //uhci_show_td (desc);
+                                       //uhci_show_td (last_desc);
                                        urb_priv->short_control_packet=1;
                                        return 0;
                                }
@@ -1864,34 +2149,24 @@ _static int process_transfer (uhci_t *s, urb_t *urb)
                }
 
                data_toggle = uhci_toggle (desc->hw.td.info);
-               //dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      
+               queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle);      
 
        }
+
        usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle);
-       transfer_finished:
 
-       unlink_qh (s, qh);
-       //delete_qh (s, qh);
-       qh->last_used=UHCI_GET_CURRENT_FRAME(s);
-       list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion
+ transfer_finished:
+       
+       uhci_clean_transfer(s, urb, qh, (mode==0?2:1));
 
        urb->status = status;
 
-#ifdef USE_RECLAMATION_LOOP    
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH  
        disable_desc_loop(s,urb);
 #endif 
 
-       dbg("process_transfer: urb %p, wanted len %d, len %d status %x err %d",
+       queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d",
                urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count);
-       //dbg("process_transfer: exit");
-#if 0
-       if (urb->actual_length){
-               char *uu;
-               uu=urb->transfer_buffer;
-               dbg("%x %x %x %x %x %x %x %x",
-                       *uu,*(uu+1),*(uu+2),*(uu+3),*(uu+4),*(uu+5),*(uu+6),*(uu+7));
-       }
-#endif
        return ret;
 }
 
@@ -1934,15 +2209,13 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
 
                // if any error occured: ignore this td, and continue
                if (status != 0) {
-                       uhci_show_td (desc);
+                       //uhci_show_td (desc);
                        urb->error_count++;
                        goto recycle;
                }
                else
                        urb->actual_length = actual_length;
 
-               // FIXME: SPD?
-
        recycle:
                if (urb->complete) {
                        //dbg("process_interrupt: calling completion, status %i",status);
@@ -1962,6 +2235,7 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
 
                        desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE);
                        if (status==0) {
+                               ((urb_priv_t*)urb->hcpriv)->started=jiffies;
                                desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe),
                                      usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE);
                                usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
@@ -1981,8 +2255,8 @@ _static int process_interrupt (uhci_t *s, urb_t *urb)
        return ret;
 }
 
-
-_static int process_iso (uhci_t *s, urb_t *urb)
+// mode: 1: force processing, don't unlink tds (already unlinked)
+_static int process_iso (uhci_t *s, urb_t *urb, int mode)
 {
        int i;
        int ret = 0;
@@ -1991,16 +2265,18 @@ _static int process_iso (uhci_t *s, urb_t *urb)
        uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list);
 
        dbg("urb contains iso request");
-       if (desc->hw.td.status & TD_CTRL_ACTIVE)
+       if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode)
                return -EXDEV;  // last TD not finished
 
        urb->error_count = 0;
        urb->actual_length = 0;
        urb->status = 0;
+       dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s),
+           urb->number_of_packets,mode,desc->hw.td.status);
 
        for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) {
                desc = list_entry (p, uhci_desc_t, desc_list);
-
+               
                //uhci_show_td(desc);
                if (desc->hw.td.status & TD_CTRL_ACTIVE) {
                        // means we have completed the last TD, but not the TDs before
@@ -2013,7 +2289,8 @@ _static int process_iso (uhci_t *s, urb_t *urb)
                        goto err;
                }
 
-               unlink_td (s, desc, 1);
+               if (!mode)
+                       unlink_td (s, desc, 1);
 
                if (urb->number_of_packets <= i) {
                        dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i);
@@ -2038,13 +2315,14 @@ _static int process_iso (uhci_t *s, urb_t *urb)
                        urb->error_count++;
                        urb->status = urb->iso_frame_desc[i].status;
                }
-               dbg("process_iso: len:%d status:%x",
-                    urb->iso_frame_desc[i].length, urb->iso_frame_desc[i].status);
+               dbg("process_iso: %i: len:%d %08x status:%x",
+                    i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status);
 
                delete_desc (desc);
                list_del (p);
        }
-       dbg("process_iso: exit %i (%d)", i, ret);
+       
+       dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length);
        return ret;
 }
 
@@ -2056,15 +2334,20 @@ _static int process_urb (uhci_t *s, struct list_head *p)
 
 
        urb=list_entry (p, urb_t, urb_list);
-       dbg("found queued urb: %p", urb);
+       //dbg("process_urb: found queued urb: %p", urb);
 
        switch (usb_pipetype (urb->pipe)) {
        case PIPE_CONTROL:
+               ret = process_transfer (s, urb, 1);
+               break;
        case PIPE_BULK:
-               ret = process_transfer (s, urb);
+               if (!s->avoid_bulk.counter)
+                       ret = process_transfer (s, urb, 1);
+               else
+                       return 0;
                break;
        case PIPE_ISOCHRONOUS:
-               ret = process_iso (s, urb);
+               ret = process_iso (s, urb, 0);
                break;
        case PIPE_INTERRUPT:
                ret = process_interrupt (s, urb);
@@ -2158,23 +2441,18 @@ _static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs)
        if (status != 1) {
                warn("interrupt, status %x, frame# %i", status, 
                     UHCI_GET_CURRENT_FRAME(s));
-               //uhci_show_queue(s->control_chain);
+
                // remove host controller halted state
                if ((status&0x20) && (s->running)) {
-                       // more to be done - check TDs for invalid entries
-                       // but TDs are only invalid if somewhere else is a (memory ?) problem
                        outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD);
                }
                //uhci_show_status (s);
        }
-       //beep(1000);           
        /*
-        * the following is very subtle and was blatantly wrong before
         * traverse the list in *reverse* direction, because new entries
         * may be added at the end.
         * also, because process_urb may unlink the current urb,
         * we need to advance the list before
-        * - Thomas Sailer
         */
 
        spin_lock (&s->urb_list_lock);
@@ -2186,18 +2464,23 @@ restart:
                p2 = p;
                p = p->prev;
                process_urb (s, p2);
-               if(s->unlink_urb_done)
-               {
+               if (s->unlink_urb_done) {
                        s->unlink_urb_done=0;
                        goto restart;
                }
        }
-       spin_unlock (&s->urb_list_lock);
-       clean_descs(s,0);       
+       if ((s->frame_counter & 63) == 0)
+               uhci_check_timeouts(s);
 
+       clean_descs(s,0);
+       uhci_cleanup_unlink(s, 0);
+       
+       spin_unlock (&s->urb_list_lock);
+       
+       s->frame_counter++;
        outw (status, io_addr + USBSTS);
 
-       dbg("done");
+       //dbg("uhci_interrupt: done");
 }
 
 _static void reset_hc (uhci_t *s)
@@ -2249,15 +2532,19 @@ _static void __exit uhci_cleanup_dev(uhci_t *s)
 {
        struct usb_device *root_hub = s->bus->root_hub;
 
+       s->running = 0;             // Don't allow submit_urb
+
        if (root_hub)
                usb_disconnect (&root_hub);
 
-       uhci_unlink_urbs(s, 0, 1);  // Forced unlink of remaining URBs
+       reset_hc (s);
+       wait_ms (1);
 
+       uhci_unlink_urbs (s, 0, 1);  // Forced unlink of remaining URBs
+       uhci_cleanup_unlink (s, 1);  // force cleanup of async killed URBs
+       
        usb_deregister_bus (s->bus);
 
-       s->running = 0;
-       reset_hc (s);
        release_region (s->io_addr, s->io_size);
        free_irq (s->irq, s);
        usb_free_bus (s->bus);
@@ -2285,6 +2572,7 @@ _static int __init uhci_start_usb (uhci_t *s)
        return 0;
 }
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
 _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
 {
        uhci_t *s = (uhci_t*) dev->data;
@@ -2301,6 +2589,7 @@ _static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data)
        }
        return 0;
 }
+#endif
 
 _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size)
 {
@@ -2315,14 +2604,17 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
        memset (s, 0, sizeof (uhci_t));
        INIT_LIST_HEAD (&s->free_desc);
        INIT_LIST_HEAD (&s->urb_list);
+       INIT_LIST_HEAD (&s->urb_unlinked);
        spin_lock_init (&s->urb_list_lock);
        spin_lock_init (&s->qh_lock);
        spin_lock_init (&s->td_lock);
+       atomic_set(&s->avoid_bulk, 0);
        s->irq = -1;
        s->io_addr = io_addr;
        s->io_size = io_size;
        s->next = devs; //chain new uhci device into global list        
-
+       s->frame_counter = 0;
+       
        bus = usb_alloc_bus (&uhci_device_operations);
        if (!bus) {
                kfree (s);
@@ -2389,11 +2681,11 @@ _static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_add
 
        //chain new uhci device into global list
        devs = s;
-
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
        pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event);
        if (pmdev)
                pmdev->data = s;
-
+#endif
        return 0;
 }
 
@@ -2407,7 +2699,7 @@ _static int __init start_uhci (struct pci_dev *dev)
                unsigned int io_addr = dev->resource[i].start;
                unsigned int io_size =
                dev->resource[i].end - dev->resource[i].start + 1;
-               if (!(dev->resource[i].flags & IORESOURCE_IO))
+               if (!(dev->resource[i].flags & 1))
                        continue;
 #else
                unsigned int io_addr = dev->base_address[i];
@@ -2422,6 +2714,10 @@ _static int __init start_uhci (struct pci_dev *dev)
                        break;
                /* disable legacy emulation */
                pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT);
+               if(dev->vendor==0x8086) {
+                       info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER);
+                       pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER);
+               }
                return alloc_uhci(dev, dev->irq, io_addr, io_size);
        }
        return -1;
@@ -2452,6 +2748,9 @@ int __init uhci_init (void)
 #endif 
        info(VERSTR);
 
+#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
+       info("High bandwidth mode enabled");    
+#endif
        for (;;) {
                dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev);
                if (!dev)
@@ -2506,7 +2805,9 @@ int init_module (void)
 
 void cleanup_module (void)
 {
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44)
        pm_unregister_all (handle_pm_event);
+#endif
        uhci_cleanup ();
 }
 
index e74e23bd81221356aa72c754ae092101708d4b05..93173f2c26cdf038b7cf5ca3e9b8f57b2e3636cf 100644 (file)
@@ -2,10 +2,11 @@
 #define __LINUX_UHCI_H
 
 /*
-   $Id: usb-uhci.h,v 1.41 2000/02/13 21:37:38 acher Exp $
+   $Id: usb-uhci.h,v 1.50 2000/03/13 21:18:04 fliegl Exp $
  */
 #define MODNAME "usb-uhci"
-#define VERSTR "version v1.184 time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.50 $ time " __TIME__ " " __DATE__
+#define UHCI_LATENCY_TIMER 0
 
 static __inline__ void uhci_wait_ms(unsigned int ms)
 {
@@ -154,9 +155,13 @@ typedef struct {
 
 typedef struct {
        struct list_head desc_list;     // list pointer to all corresponding TDs/QHs associated with this request
-       int short_control_packet;
        unsigned long started;
-       int use_loop;
+       urb_t *next_queued_urb;         // next queued urb for this EP
+       urb_t *prev_queued_urb;
+       uhci_desc_t *bottom_qh;
+       uhci_desc_t *next_qh;           // next helper QH
+       char use_loop;
+       char short_control_packet;
 } urb_priv_t, *purb_priv_t;
 
 struct virt_root_hub {
@@ -186,12 +191,14 @@ typedef struct uhci {
        spinlock_t urb_list_lock;       // lock to keep consistency 
 
        int unlink_urb_done;
+       atomic_t avoid_bulk;
        
        struct usb_bus *bus;    // our bus
 
        __u32 *framelist;
        uhci_desc_t **iso_td;
        uhci_desc_t *int_chain[8];
+       uhci_desc_t *ls_control_chain;
        uhci_desc_t *control_chain;
        uhci_desc_t *bulk_chain;
        uhci_desc_t *chain_end;
@@ -200,6 +207,9 @@ typedef struct uhci {
        spinlock_t td_lock;
        struct virt_root_hub rh;        //private data of the virtual root hub
        int loop_usage;            // URBs using bandwidth reclamation
+
+       struct list_head urb_unlinked;  // list of all unlinked  urbs
+       int frame_counter;
 } uhci_t, *puhci_t;
 
 
index 12643937b7472ffadff9382e371bacece078541c..7b1452bf62b641dcfca6d41a8d8debcb564758fe 100644 (file)
 /* DAC_CNTL bit constants */                       
 #define DAC_8BIT_EN                            0x00000100
 #define DAC_MASK                               0xFF000000
+#define DAC_BLANKING                           0x00000004
+#define DAC_RANGE_CNTL                         0x00000003
 
 /* GEN_RESET_CNTL bit constants */
 #define SOFT_RESET_GUI                         0x00000001
index d7d4116c0a6be5ac0082a24b02edfc454d562cd2..fcc0f8c5cc86cf53bfaaa5de812a545c2fbe8aa4 100644 (file)
 #include <linux/ioport.h>
 #include <asm/io.h>
 
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
+#include <video/macmodes.h>
 #ifdef CONFIG_NVRAM
 #include <linux/nvram.h>
 #endif
-#include <video/macmodes.h>
 #endif
 
 #ifdef CONFIG_FB_COMPAT_XPMAC
@@ -69,7 +69,7 @@
 
 #ifdef CONFIG_MTRR
 #include <asm/mtrr.h>
-#endif /* CONFIG_MTRR */
+#endif
 
 #include "aty128.h"
 
@@ -158,13 +158,18 @@ struct aty128_meminfo {
     u8 LoopLatency;
     u8 DspOn;
     u8 Rloop;
+    const char *name;
 };
 
 /* various memory configurations */
-const struct aty128_meminfo sdr_128   = { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16 };
-const struct aty128_meminfo sdr_64    = { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17 };
-const struct aty128_meminfo sdr_sgram = { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16 };
-const struct aty128_meminfo ddr_sgram = { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16 };
+const struct aty128_meminfo sdr_128   =
+    { 4, 4, 3, 3, 1, 3, 1, 16, 30, 16, "128-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_64    =
+    { 4, 8, 3, 3, 1, 3, 1, 17, 46, 17, "64-bit SDR SGRAM (1:1)" };
+const struct aty128_meminfo sdr_sgram =
+    { 4, 4, 1, 2, 1, 2, 1, 16, 24, 16, "64-bit SDR SGRAM (2:1)" };
+const struct aty128_meminfo ddr_sgram =
+    { 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
 
 static int currcon = 0;
 
@@ -176,20 +181,23 @@ static unsigned int initdepth __initdata = 8;
 #ifndef MODULE
 static const char *mode_option __initdata = NULL;
 #endif
+#ifndef CONFIG_PPC
+static void *bios_seg = NULL;
+#endif
 
 #ifdef CONFIG_PPC
 #ifdef CONFIG_NVRAM_NOT_DEFINED
+static int default_vmode __initdata = VMODE_640_480_60;
+static int default_cmode __initdata = CMODE_8;
+#else
 static int default_vmode __initdata = VMODE_NVRAM;
 static int default_cmode __initdata = CMODE_NVRAM;
-#else
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_8;
 #endif
 #endif
 
 #ifdef CONFIG_MTRR
 static int mtrr = 1;
-#endif /* CONFIG_MTRR */
+#endif
 
 /* PLL constants */
 struct aty128_constants {
@@ -236,17 +244,13 @@ struct aty128fb_par {
 struct fb_info_aty128 {
     struct fb_info fb_info;
     struct fb_info_aty128 *next;
-    struct aty128_constants constants;
-    unsigned long regbase_phys;         /* mmio                */
-    unsigned long frame_buffer_phys;    /* framebuffer memory  */
+    struct aty128_constants constants;  /* PLL and others      */
+    unsigned long regbase_phys;         /* physical mmio       */
+    void *regbase;                      /* remapped mmio       */
+    unsigned long frame_buffer_phys;    /* physical fb memory  */
     unsigned long frame_buffer;         /* remaped framebuffer */
-    void *regbase;
     const struct aty128_meminfo *mem;   /* onboard mem info    */
     u32 vram_size;                      /* onboard video ram   */
-#ifndef CONFIG_PPC
-    void *bios_seg;                     /* video BIOS segment  */
-#endif
-    unsigned short card_revision;      /* video card revision */
     struct aty128fb_par default_par, current_par;
     struct display disp;
     struct display_switch dispsw;       /* for cursor and font */
@@ -268,7 +272,7 @@ struct fb_info_aty128 {
 #endif
 #ifdef CONFIG_MTRR
     struct { int vram; int vram_valid; } mtrr;
-#endif /* CONFIG_MTRR */
+#endif
 };
 
 static struct fb_info_aty128 *board_list = NULL;
@@ -321,12 +325,18 @@ static int aty128_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
 static int aty128_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
                                u_int transp, struct fb_info *info);
 static void do_install_cmap(int con, struct fb_info *info);
+static int aty128_encode_var(struct fb_var_screeninfo *var,
+                             const struct aty128fb_par *par,
+                             const struct fb_info_aty128 *info);
+static int aty128_decode_var(struct fb_var_screeninfo *var,
+                             struct aty128fb_par *par,
+                             const struct fb_info_aty128 *info);
 static int aty128_pci_register(struct pci_dev *pdev,
                                const struct aty128_chip_info *aci);
 static struct fb_info_aty128 *aty128_board_list_add(struct fb_info_aty128
                                *board_list, struct fb_info_aty128 *new_node);
-#ifndef CONFIG_PPC
 static int aty128find_ROM(struct fb_info_aty128 *info);
+#ifndef CONFIG_PPC
 static void aty128_get_pllinfo(struct fb_info_aty128 *info);
 #endif
 static void aty128_timings(struct fb_info_aty128 *info);
@@ -513,7 +523,7 @@ aty_pll_writeupdate(const struct fb_info_aty128 *info)
 
 
 /* write to the scratch register to test r/w functionality */
-static u32
+static int __init
 register_test(const struct fb_info_aty128 *info)
 {
     u32 val, flag = 0;
@@ -720,6 +730,9 @@ aty128_set_crtc(const struct aty128_crtc *crtc,
     aty_st_le32(CRTC_PITCH, crtc->pitch);
     aty_st_le32(CRTC_OFFSET, crtc->offset);
     aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
+
+    /* Disable ATOMIC updating.  Is this the right place? */
+    aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
 }
 
 
@@ -818,7 +831,7 @@ aty128_var_to_crtc(const struct fb_var_screeninfo *var,
     
     c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-    crtc->gen_cntl = 0x03000000L | c_sync | (depth << 8);
+    crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
 
     crtc->h_total = h_total | (h_disp << 16);
     crtc->v_total = v_total | (v_disp << 16);
@@ -896,7 +909,7 @@ aty128_bpp_to_var(int pix_width, struct fb_var_screeninfo *var)
        break;
     default:
         printk(KERN_ERR "Invalid pixel width\n");
-        return -1;
+        return -EINVAL;
     }
 
     return 0;
@@ -913,31 +926,31 @@ aty128_crtc_to_var(const struct aty128_crtc *crtc,
     u32 pix_width;
 
     /* fun with masking */
-    h_total = crtc->h_total & 0x1ff;
-    h_disp = (crtc->h_total>>16) & 0xff;
+    h_total     = crtc->h_total & 0x1ff;
+    h_disp      = (crtc->h_total>>16) & 0xff;
     h_sync_strt = (crtc->h_sync_strt_wid>>3) & 0x1ff;
-    h_sync_dly = crtc->h_sync_strt_wid & 0x7;
-    h_sync_wid = (crtc->h_sync_strt_wid>>16) & 0x3f;
-    h_sync_pol = (crtc->h_sync_strt_wid>>23) & 0x1;
-    v_total = crtc->v_total & 0x7ff;
-    v_disp = (crtc->v_total>>16) & 0x7ff;
+    h_sync_dly  = crtc->h_sync_strt_wid & 0x7;
+    h_sync_wid  = (crtc->h_sync_strt_wid>>16) & 0x3f;
+    h_sync_pol  = (crtc->h_sync_strt_wid>>23) & 0x1;
+    v_total     = crtc->v_total & 0x7ff;
+    v_disp      = (crtc->v_total>>16) & 0x7ff;
     v_sync_strt = crtc->v_sync_strt_wid & 0x7ff;
-    v_sync_wid = (crtc->v_sync_strt_wid>>16) & 0x1f;
-    v_sync_pol = (crtc->v_sync_strt_wid>>23) & 0x1;
-    c_sync = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
-    pix_width = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
-
-    xres = (h_disp+1)*8;
-    yres = v_disp+1;
-    left = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
+    v_sync_wid  = (crtc->v_sync_strt_wid>>16) & 0x1f;
+    v_sync_pol  = (crtc->v_sync_strt_wid>>23) & 0x1;
+    c_sync      = crtc->gen_cntl & CRTC_CSYNC_EN ? 1 : 0;
+    pix_width   = crtc->gen_cntl & CRTC_PIX_WIDTH_MASK;
+
+    xres  = (h_disp+1) << 3;
+    yres  = v_disp+1;
+    left  = (h_total-h_sync_strt-h_sync_wid)*8-h_sync_dly;
     right = (h_sync_strt-h_disp)*8+h_sync_dly;
     hslen = h_sync_wid*8;
     upper = v_total-v_sync_strt-v_sync_wid;
     lower = v_sync_strt-v_disp;
     vslen = v_sync_wid;
-    sync = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
-           (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
-           (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
+    sync  = (h_sync_pol ? 0 : FB_SYNC_HOR_HIGH_ACT) |
+            (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
+            (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
 
     aty128_bpp_to_var(pix_width, var);
 
@@ -1007,7 +1020,7 @@ aty128_var_to_pll(u32 period_in_ps, struct aty128_pll *pll,
                        const struct fb_info_aty128 *info)
 {
     const struct aty128_constants c = info->constants;
-    unsigned char post_dividers [] = {1,2,4,8,3,6,12};
+    unsigned char post_dividers[] = {1,2,4,8,3,6,12};
     u32 output_freq;
     u32 vclk;        /* in .01 MHz */
     int i;
@@ -1110,6 +1123,7 @@ aty128_ddafifo(struct aty128_ddafifo *dsp,
     n <<= (11 - p);
     x = round_div(n, d);
     roff = x * (fifo_depth - 4);
+
     if ((ron + m->Rloop) >= roff) {
        printk(KERN_ERR "aty128fb: Mode out of range!\n");
        return -EINVAL;
@@ -1134,7 +1148,14 @@ aty128_set_par(struct aty128fb_par *par,
                        struct fb_info_aty128 *info)
 { 
     u32 config;
-    
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* enable this when macmodes gets updated */
+    struct vc_mode disp_info;
+#endif
+    struct fb_var_screeninfo var;
+    int cmode, vmode;
+#endif
+
     info->current_par = *par;
     
     if (info->blitter_may_be_busy)
@@ -1174,6 +1195,46 @@ aty128_set_par(struct aty128fb_par *par,
 
     if (par->accel_flags & FB_ACCELF_TEXT)
         aty128_init_engine(par, info);
+
+#ifdef CONFIG_FB_COMPAT_XPMAC
+#if 0 /* use this when macmodes gets updated */
+    if (!console_fb_info || console_fb_info == &info->fb_info) {
+       disp_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+       disp_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+       disp_info.depth = par->crtc.bpp;
+       disp_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+        aty128_encode_var(&var, par, info);
+       if (mac_var_to_vmode(&var, &vmode, &cmode))
+           disp_info.mode = 0;
+       else
+           disp_info.mode = vmode;
+       strcpy(disp_info.name, aty128fb_name);
+       disp_info.fb_address = info->frame_buffer_phys;
+       disp_info.cmap_adr_address = 0;
+       disp_info.cmap_data_address = 0;
+       disp_info.disp_reg_address = info->regbase_phys;
+        register_compat_xpmac(disp_info);
+    }
+#else
+    if (!console_fb_info || console_fb_info == &info->fb_info) {
+       display_info.width = ((par->crtc.v_total >> 16) & 0x7ff)+1;
+       display_info.height = (((par->crtc.h_total >> 16) & 0xff)+1) << 3;
+       display_info.depth = par->crtc.bpp;
+       display_info.pitch = par->crtc.vxres*par->crtc.bpp >> 3;
+        aty128_encode_var(&var, par, info);
+       if (mac_var_to_vmode(&var, &vmode, &cmode))
+           display_info.mode = 0;
+       else
+           display_info.mode = vmode;
+       strcpy(display_info.name, aty128fb_name);
+       display_info.fb_address = info->frame_buffer_phys;
+       display_info.cmap_adr_address = 0;
+       display_info.cmap_data_address = 0;
+       display_info.disp_reg_address = info->regbase_phys;
+        register_compat_xpmac(display_info);
+    }
+#endif
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 }
 
 
@@ -1293,16 +1354,23 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
         var->xres_virtual = var->xres;
     if (var->yres > var->yres_virtual)
         var->yres_virtual = var->yres;
-    if (var->bits_per_pixel <= 8)
-        var->bits_per_pixel = 8;
-    else if (var->bits_per_pixel <= 16)
-        var->bits_per_pixel = 16;
-    else if (var->bits_per_pixel <= 24)
-        var->bits_per_pixel = 24;
-    else if (var->bits_per_pixel <= 32)
-        var->bits_per_pixel = 32;
-    else
-        return -EINVAL;
+
+    switch (var->bits_per_pixel) {
+        case 0 ... 8:
+            var->bits_per_pixel = 8;
+            break;
+        case 9 ... 16:
+            var->bits_per_pixel = 16;
+            break;
+        case 17 ... 24:
+            var->bits_per_pixel = 24;
+            break;
+        case 25 ... 32:
+            var->bits_per_pixel = 32;
+            break;
+        default:
+            return -EINVAL;
+    }
 
     if ((err = aty128_decode_var(var, &par, info)))
        return err;
@@ -1357,26 +1425,6 @@ aty128fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *fb)
        do_install_cmap(con, &info->fb_info);
     } 
 
-#ifdef CONFIG_FB_COMPAT_XPMAC
-    if (!console_fb_info || console_fb_info == &info->fb_info) {
-       int vmode, cmode;
-
-       display_info.width = var->xres;
-       display_info.height = var->yres;
-       display_info.depth = var->bits_per_pixel;
-       display_info.pitch = (var->xres_virtual)*(var->bits_per_pixel)/8;
-       if (mac_var_to_vmode(var, &vmode, &cmode))
-           display_info.mode = 0;
-       else
-           display_info.mode = vmode;
-       strcpy(info->fb_info.modename, aty128fb_name);
-       display_info.fb_address = info->frame_buffer_phys;
-       display_info.cmap_adr_address = 0;
-       display_info.cmap_data_address = 0;
-       display_info.disp_reg_address = info->regbase_phys;
-    }
-#endif
-
     return 0;
 }
 
@@ -1435,14 +1483,14 @@ aty128_encode_fix(struct fb_fix_screeninfo *fix,
     fix->smem_len = (u32)info->vram_size;
     fix->mmio_len = 0x1fff;
 
-    fix->type = FB_TYPE_PACKED_PIXELS;
-    fix->type_aux = 0;
+    fix->type        = FB_TYPE_PACKED_PIXELS;
+    fix->type_aux    = 0;
     fix->line_length = par->crtc.vxres*par->crtc.bpp/8;
-    fix->visual = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
-                                     : FB_VISUAL_DIRECTCOLOR;
+    fix->visual      = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+                                          : FB_VISUAL_DIRECTCOLOR;
     fix->ywrapstep = 0;
-    fix->xpanstep = 8;
-    fix->ypanstep = 1;
+    fix->xpanstep  = 8;
+    fix->ypanstep  = 1;
 
     fix->accel = FB_ACCEL_ATI_RAGE128;
 
@@ -1540,6 +1588,7 @@ aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
        disp = &fb_display[con];
     else
        disp = info->disp;
+
     if (!disp->cmap.len) {      /* no colormap allocated? */
         int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
        if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
@@ -1614,7 +1663,7 @@ aty128fb_setup(char *options)
             mtrr = 0;
         }
 #endif /* CONFIG_MTRR */
-#if defined(CONFIG_PPC)
+#ifdef CONFIG_PPC
         /* vmode and cmode depreciated */
        else if (!strncmp(this_opt, "vmode:", 6)) {
             unsigned int vmode = simple_strtoul(this_opt+6, NULL, 0);
@@ -1660,27 +1709,22 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
     const struct aty128_chip_info *aci = &aty128_pci_probe_list[0];
     char *video_card = "Rage128";
 
-    if (!register_test(info)) {
-       printk(KERN_ERR "aty128fb: Can't write to video registers\n");
-       return 0;
-    }
-
     if (!info->vram_size)      /* may have already been probed */
        info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
 
+    /* Get the chip revision */
     chip_rev = (aty_ld_le32(CONFIG_CNTL) >> 16) & 0x1F;
 
     /* put a name with the face */
     while (aci->name && info->pdev->device != aci->device) { aci++; }
     video_card = (char *)aci->name;
 
-    printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] [card rev %x] ",
-                      video_card, chip_rev, info->card_revision);
+    printk(KERN_INFO "aty128fb: %s [chip rev 0x%x] ", video_card, chip_rev);
 
     if (info->vram_size % (1024 * 1024) == 0)
-       printk("%dM\n", info->vram_size / (1024*1024));
+       printk("%dM %s\n", info->vram_size / (1024*1024), info->mem->name);
     else
-       printk("%dk\n", info->vram_size / 1024);
+       printk("%dk %s\n", info->vram_size / 1024, info->mem->name);
 
     /* fill in info */
     strcpy(info->fb_info.modename, aty128fb_name);
@@ -1697,32 +1741,34 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
     var = default_var;
 #else
     memset(&var, 0, sizeof(var));
+#ifdef CONFIG_FB_COMPAT_XPMAC       /* CONFIG_PPC implied */
+    if (_machine == _MACH_Pmac) {
+        if (mode_option) {
+            if (!mac_find_mode(&var, &info->fb_info, mode_option, 8))
+                var = default_var;
+        } else {
 #ifdef CONFIG_NVRAM
-    if (default_vmode == VMODE_NVRAM) {
-        default_vmode = nvram_read_byte(NV_VMODE);
-        if (default_vmode <= 0 || default_vmode > VMODE_MAX)
-            default_vmode = VMODE_CHOOSE;
-    }
-#endif
-#ifdef CONFIG_PPC
-    if (default_vmode == VMODE_CHOOSE) {
-        var = default_var;
-#endif /* CONFIG_PPC */
+            if (default_vmode == VMODE_NVRAM)
+                default_vmode = nvram_read_byte(NV_VMODE);
 
-       if (!fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
-                         &defaultmode, initdepth))
-           var = default_var;
-
-#ifdef CONFIG_PPC
-#ifdef CONFIG_NVRAM
-    if (default_cmode == CMODE_NVRAM)
-        default_cmode = nvram_read_byte(NV_CMODE);
+            if (default_cmode == CMODE_NVRAM)
+                default_cmode = nvram_read_byte(NV_CMODE);
 #endif
-    } else if (_machine == _MACH_Pmac)
-       if (mac_vmode_to_var(default_vmode, default_cmode, &var))
-           var = default_var;
+            if (default_vmode <= 0 || default_vmode > VMODE_MAX)
+                default_vmode = VMODE_640_480_60;
 
-#endif
+            if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
+                default_cmode = CMODE_8;
+
+            if (mac_vmode_to_var(default_vmode, default_cmode, &var))
+                var = default_var;
+        }
+    }
+#else
+    if (fb_find_mode(&var, &info->fb_info, mode_option, NULL, 0,
+                      &defaultmode, initdepth) == 0)
+        var = default_var;
+#endif /* CONFIG_FB_COMPAT_XPMAC */
 #endif /* MODULE */
 
     if (noaccel)
@@ -1743,8 +1789,8 @@ aty128_init(struct fb_info_aty128 *info, const char *name)
         info->palette[j].blue = default_blu[k];
     }
 
-    dac = aty_ld_le32(DAC_CNTL) & 15;  /* preserve lower three bits */
-    dac |= DAC_8BIT_EN;                        /* set 8 bit dac             */
+    dac = aty_ld_le32(DAC_CNTL);
+    dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL | DAC_BLANKING);
     dac |= DAC_MASK;                   /* set DAC mask              */
     aty_st_le32(DAC_CNTL, dac);
 
@@ -1845,40 +1891,51 @@ aty128_pci_register(struct pci_dev *pdev,
     }
     memset(info, 0, sizeof(struct fb_info_aty128));
 
+    /* Copy PCI device info into info->pdev */
     info->pdev = pdev;
 
+    /* Virtualize mmio region */
     info->regbase_phys = reg_addr;
     info->regbase = ioremap(reg_addr, 0x1FFF);
 
     if (!info->regbase)
         goto err_out;
 
-    pci_read_config_word(pdev, 0x08, &tmp);
-    info->card_revision = tmp;
-
     info->vram_size = aty_ld_le32(CONFIG_MEMSIZE) & 0x03FFFFFF;
 
+    pci_read_config_word(pdev, PCI_COMMAND, &tmp);
+    if (!(tmp & PCI_COMMAND_MEMORY)) {
+        tmp |= PCI_COMMAND_MEMORY;
+        pci_write_config_word(pdev, PCI_COMMAND, tmp);
+    }
+
+    /* Virtualize the framebuffer */
     info->frame_buffer_phys = fb_addr;
     info->frame_buffer = (unsigned long)ioremap(fb_addr, info->vram_size);
 
     if (!info->frame_buffer)
         goto err_out;
 
-    pci_read_config_word(pdev, PCI_COMMAND, &tmp);
-    if (!(tmp & PCI_COMMAND_MEMORY)) {
-        tmp |= PCI_COMMAND_MEMORY;
-        pci_write_config_word(pdev, PCI_COMMAND, tmp);
+    /* If we can't test scratch registers, something is seriously wrong */
+    if (!register_test(info)) {
+        printk(KERN_ERR "aty128fb: Can't write to video register!\n");
+        goto err_out;
     }
 
-#if defined(CONFIG_PPC)
-    aty128_timings(info);
-#else
     if (!aty128find_ROM(info)) {
-        printk(KERN_INFO "Rage128 BIOS not located.  Guessing...\n");
+        printk(KERN_INFO "aty128fb: Rage128 BIOS not located. Guessing...\n");
         aty128_timings(info);
     }
-    else
+#ifndef CONFIG_PPC
+ else
         aty128_get_pllinfo(info);
+
+        /* free up to-be unused resources. bios_seg is mapped by
+         * aty128find_ROM() and used by aty128_get_pllinfo()
+         *
+         * TODO: make more elegant. doesn't need to be global */
+        if (bios_seg)
+            iounmap(bios_seg);
 #endif
 #ifdef CONFIG_MTRR
     if (mtrr) {
@@ -1910,27 +1967,26 @@ unmap_out:
 #endif /* CONFIG_PCI */
 
 
-#ifndef CONFIG_PPC
+/* PPC cannot read video ROM, so we fail by default */
 static int __init
 aty128find_ROM(struct fb_info_aty128 *info)
 {
-    u32 segstart;
+    int  flag = 0;
+#ifndef CONFIG_PPC
+    u32  segstart;
     char *rom_base;
-    char *rom_base1;
     char *rom;
-    int stage;
-    int i;
+    int  stage;
+    int  i;
     char aty_rom_sig[] = "761295520";   /* ATI ROM Signature      */
     char R128_sig[] = "R128";           /* Rage128 ROM identifier */
-    int flag = 0;
 
     for (segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
         stage = 1;
 
         rom_base = (char *) ioremap(segstart, 0x1000);
-        rom_base1 = (char *) (rom_base+1);
 
-        if ((*rom_base == 0x55) && (((*rom_base1) & 0xff) == 0xaa))
+        if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
             stage = 2;
 
         if (stage != 2) {
@@ -1967,16 +2023,18 @@ aty128find_ROM(struct fb_info_aty128 *info)
             continue;
         }
 
+        bios_seg = rom_base;
         printk(KERN_INFO "aty128fb: Rage128 BIOS located at segment %4.4X\n",
-                         (u32)rom_base);
-        info->bios_seg = rom_base;
+                         (unsigned int)rom_base);
         flag = 1;
         break;
     }
+#endif /* !CONFIG_PPC */
     return (flag);
 }
 
 
+#ifndef CONFIG_PPC
 static void __init
 aty128_get_pllinfo(struct fb_info_aty128 *info)
 {   
@@ -1985,16 +2043,16 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
     u16 bios_header_offset, pll_info_offset;
     PLL_BLOCK pll;
 
-    bios_header = info->bios_seg + 0x48L;
-    header_ptr = bios_header;
+    bios_header = bios_seg + 0x48L;
+    header_ptr  = bios_header;
 
     bios_header_offset = readw(header_ptr);
-    bios_header = info->bios_seg + bios_header_offset;
+    bios_header = bios_seg + bios_header_offset;
     bios_header += 0x30;
 
     header_ptr = bios_header;
     pll_info_offset = readw(header_ptr);
-    header_ptr = info->bios_seg + pll_info_offset;
+    header_ptr = bios_seg + pll_info_offset;
 
     memcpy_fromio(&pll, header_ptr, 50);
 
@@ -2032,10 +2090,6 @@ aty128_get_pllinfo(struct fb_info_aty128 *info)
         info->mem = &sdr_sgram;
     }       
 
-    /* free up to-be unused resources */
-    if (info->bios_seg)
-        iounmap(info->bios_seg);
-
     return;
 }           
 #endif /* ! CONFIG_PPC */
@@ -2056,7 +2110,7 @@ aty128_timings(struct fb_info_aty128 *info)
         info->constants.ppll_max = 25000;    /* 23000 on some cards? */
 
 #if 1
-    /* XXX TODO. Calculuate properly. Fix OF's pll ideas. */
+    /* XXX TODO. Calculuate properly. */
     if (!info->constants.ref_divider)
         info->constants.ref_divider = 0x3b;
     aty_st_pll(PPLL_REF_DIV, info->constants.ref_divider);
@@ -2473,10 +2527,9 @@ static struct display_switch fbcon_aty128_32 = {
 };
 #endif
 
-#if defined(MODULE)
-MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>, Anthony Tong "
-              "<atong@uiuc.edu>");
-MODULE_DESCRIPTION("FBDev driver for ATI Rage128 cards");
+#ifdef MODULE
+MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
+MODULE_DESCRIPTION("FBDev driver for ATI Rage128 / Pro cards");
 
 int __init
 init_module(void)
index fc7e508abf97a80a31934242a607e04270a61f21..a63b4fd2c4628c6ba5d2b5055142559166f152bd 100644 (file)
@@ -424,10 +424,13 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
        int fbidx = GET_FB_IDX(file->f_dentry->d_inode->i_rdev);
        struct fb_info *info = registered_fb[fbidx];
        struct fb_ops *fb = info->fbops;
+       unsigned long off;
+#if !defined(__sparc__) || defined(__sparc_v9__)
        struct fb_fix_screeninfo fix;
        struct fb_var_screeninfo var;
-       unsigned long start, off;
+       unsigned long start;
        u32 len;
+#endif
 
        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
                return -EINVAL;
index 307037153dee1e892c2dc46f423187cf11e70e9b..e79fdcac8c9c65bca7a70a64947bd9b43f7da69a 100644 (file)
@@ -35,8 +35,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 
-#include <linux/config.h>
-
 #define DLINFO_ITEMS 13
 
 #include <linux/elf.h>
index 1c3b857f3cb77991de5d6fcf3601238a90e23778..f6ff5e420ea999b973e3a042e2b51694a6aebc71 100644 (file)
@@ -706,7 +706,7 @@ static int __init init_ncp_fs(void)
        ncp_malloced = 0;
        ncp_current_malloced = 0;
 #endif
-       return init_ncp_fs();
+       return register_filesystem(&ncp_fs_type);
 }
 
 static void __exit exit_ncp_fs(void)
index 5199aceaac33b479075141145b887633826d88b9..27b4be8cb341c0082dbf7e9fb20ddbf2dc3867e6 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: inode.c,v 1.8 2000/03/12 03:55:19 davem Exp $
+/* $Id: inode.c,v 1.9 2000/03/13 21:59:43 davem Exp $
  * openpromfs.c: /proc/openprom handling routines
  *
  * Copyright (C) 1996-1999 Jakub Jelinek  (jakub@redhat.com)
index 51f4775558e27dbc79b4407a7de9f60a1f337c2e..c5ee7cbb2fcdf8cbdf928ae43beaaaf0be7e7102 100644 (file)
@@ -9,47 +9,10 @@
 #ifndef _ASM_AXP_PARPORT_H
 #define _ASM_AXP_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-       int count = 0, i = 0;
-
-       if (io && *io) {
-               /* Only probe the ports we were given. */
-               user_specified = 1;
-               do {
-                       if (!*io_hi) *io_hi = 0x400 + *io;
-                       if (parport_pc_probe_port(*(io++), *(io_hi++),
-                                                 *(irq++), *(dma++), NULL))
-                               count++;
-               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
-       } else {
-               if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-                       count++;
-       }
-
-        return count;
+       return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_AXP_PARPORT_H) */
index 748b5eefca41b8f5d38a1d6890c0822db4cd04be..bab49ad3a4e2bf07bdc9c52fcc569eca36636d27 100644 (file)
@@ -9,57 +9,10 @@
 #ifndef __ASMARM_PARPORT_H
 #define __ASMARM_PARPORT_H
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-       int count = 0, i = 0;
-
-#ifndef MODULE
-       detect_and_report_winbond();
-       detect_and_report_smsc();
-
-       count += parport_pc_init_superio ();
-#endif
-
-       if (io && *io) {
-               /* Only probe the ports we were given. */
-               user_specified = 1;
-               do {
-                       if (!*io_hi) *io_hi = 0x400 + *io;
-                       if (parport_pc_probe_port(*(io++), *(io_hi++),
-                                                 *(irq++), *(dma++), NULL))
-                               count++;
-               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
-       } else {
-               /* Probe all the likely ports. */
-               if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-                       count++;
-       }
-
-        return count;
+       return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASMARM_PARPORT_H) */
index c5bd25f7360e7e436d930ff47b06af225dde44ac..f953c436ce511648e0d8b7b1d951bd62155b2962 100644 (file)
 #define MCL_CURRENT    1               /* lock all current mappings */
 #define MCL_FUTURE     2               /* lock all future mappings */
 
+#define MADV_NORMAL    0x0             /* default page-in behavior */
+#define MADV_RANDOM    0x1             /* page-in minimum required */
+#define MADV_SEQUENTIAL        0x2             /* read-ahead aggressively */
+#define MADV_WILLNEED  0x3             /* pre-fault pages */
+#define MADV_DONTNEED  0x4             /* discard these pages */
+
 /* compatibility flags */
 #define MAP_ANON       MAP_ANONYMOUS
 #define MAP_FILE       0
index 0fe20dbf1589c12e1bff6b0059b90adf1f5b76dc..fa0e321e498e61e06efbfefb85972f073a6e1941 100644 (file)
@@ -9,35 +9,10 @@
 #ifndef _ASM_I386_PARPORT_H
 #define _ASM_I386_PARPORT_H 1
 
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-static int user_specified __devinitdata = 0;
-int __devinit
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-       int count = 0, i = 0;
-
-       if (io && *io) {
-               /* Only probe the ports we were given. */
-               user_specified = 1;
-               do {
-                       if (!*io_hi) *io_hi = 0x400 + *io;
-                       if (parport_pc_probe_port(*(io++), *(io_hi++),
-                                                 *(irq++), *(dma++), NULL))
-                               count++;
-               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
-       } else {
-               if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-                       count++;
-       }
-
-        return count;
+       return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_I386_PARPORT_H) */
index 239257b2177672d8bc8b9773dabaf08d3890924b..36f4e317651dda76c471bd2f250685af9628cace 100644 (file)
 #define __NR_setfsgid32                216
 #define __NR_pivot_root                217
 #define __NR_mincore           218
+#define __NR_madvise           219
+#define __NR_madvise1          219     /* delete when C lib stub is removed */
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
index de1714e8b77167d84378126238c8d3074cb31ec4..f303ce56951a494853f5d706594fe93843d5e9c0 100644 (file)
@@ -9,47 +9,10 @@
 #ifndef _ASM_PARPORT_H
 #define _ASM_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-       int count = 0, i = 0;
-
-       if (io && *io) {
-               /* Only probe the ports we were given. */
-               user_specified = 1;
-               do {
-                       if (!*io_hi) *io_hi = 0x400 + *io;
-                       if (parport_pc_probe_port(*(io++), *(io_hi++),
-                                                 *(irq++), *(dma++), NULL))
-                               count++;
-               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
-       } else {
-               if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-                       count++;
-       }
-
-        return count;
+       return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_PARPORT_H) */
index de1714e8b77167d84378126238c8d3074cb31ec4..f303ce56951a494853f5d706594fe93843d5e9c0 100644 (file)
@@ -9,47 +9,10 @@
 #ifndef _ASM_PARPORT_H
 #define _ASM_PARPORT_H 1
 
-#include <linux/config.h>
-
-/* Maximum number of ports to support.  It is useless to set this greater
-   than PARPORT_MAX (in <linux/parport.h>).  */
-#define PARPORT_PC_MAX_PORTS  8
-
-/* If parport_cs (PCMCIA) is managing ports for us, we'll need the
- * probing routines forever; otherwise we can lose them at boot time. */
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_initdata
-#define __maybe_init
-#else
-#define __maybe_initdata __initdata
-#define __maybe_init __init
-#endif
-
-static int user_specified __maybe_initdata = 0;
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int __devinit parport_pc_find_isa_ports (int autoirq, int autodma);
+static int __devinit parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
-       int count = 0, i = 0;
-
-       if (io && *io) {
-               /* Only probe the ports we were given. */
-               user_specified = 1;
-               do {
-                       if (!*io_hi) *io_hi = 0x400 + *io;
-                       if (parport_pc_probe_port(*(io++), *(io_hi++),
-                                                 *(irq++), *(dma++), NULL))
-                               count++;
-               } while (*io && (++i < PARPORT_PC_MAX_PORTS));
-       } else {
-               if (parport_pc_probe_port(0x3bc, 0x7bc, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x378, 0x778, irq[0], dma[0], NULL))
-                       count++;
-               if (parport_pc_probe_port(0x278, 0x678, irq[0], dma[0], NULL))
-                       count++;
-       }
-
-        return count;
+       return parport_pc_find_isa_ports (autoirq, autodma);
 }
 
 #endif /* !(_ASM_PARPORT_H) */
index 259c755d5a0ad60d703139f62c504b2e60a57ae9..dac004a6b03e79d9856c73d236266f76646bc648 100644 (file)
 #include <asm/ebus.h>
 #include <asm/ns87303.h>
 
-#ifdef CONFIG_PARPORT_PC_PCMCIA
-#define __maybe_init
-#define __maybe_initdata
-#else
-#define __maybe_init __init
-#define __maybe_initdata __initdata
-#endif
-
 #define PARPORT_PC_MAX_PORTS   PARPORT_MAX
 
 static struct linux_ebus_dma *sparc_ebus_dmas[PARPORT_PC_MAX_PORTS];
@@ -108,12 +100,7 @@ get_dma_residue(unsigned int dmanr)
        return res;
 }
 
-static int __maybe_init parport_pc_init_pci(int irq, int dma);
-
-static int user_specified __initdata = 0;
-
-int __init
-parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
+static int parport_pc_find_nonpci_ports (int autoirq, int autodma)
 {
        struct linux_ebus *ebus;
        struct linux_ebus_device *edev;
@@ -155,7 +142,6 @@ parport_pc_init(int *io, int *io_hi, int *irq, int *dma)
                }
        }
 
-       count += parport_pc_init_pci(PARPORT_IRQ_AUTO, PARPORT_DMA_NONE);
        return count;
 }
 
index bf8e95f11df996ae38c409f2a1850cfa72c40855..5cdc60ca9739260663d06f90d9888fe2171c779a 100644 (file)
@@ -60,6 +60,7 @@ struct vm_area_struct {
        struct vm_operations_struct * vm_ops;
        unsigned long vm_pgoff;         /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */
        struct file * vm_file;
+       unsigned long vm_raend;
        void * vm_private_data;         /* was vm_pte (shared mem) */
 };
 
@@ -83,10 +84,19 @@ struct vm_area_struct {
 
 #define VM_EXECUTABLE  0x00001000
 #define VM_LOCKED      0x00002000
-#define VM_IO           0x00004000  /* Memory mapped I/O or similar */
+#define VM_IO           0x00004000     /* Memory mapped I/O or similar */
+
+#define VM_SEQ_READ    0x00008000      /* App will access data sequentially */
+#define VM_RAND_READ   0x00010000      /* App will not benefit from clustered reads */
 
 #define VM_STACK_FLAGS 0x00000177
 
+#define VM_READHINTMASK                        (VM_SEQ_READ | VM_RAND_READ)
+#define VM_ClearReadHint(v)            (v)->vm_flags &= ~VM_READHINTMASK
+#define VM_NormalReadHint(v)           (!((v)->vm_flags & VM_READHINTMASK))
+#define VM_SequentialReadHint(v)       ((v)->vm_flags & VM_SEQ_READ)
+#define VM_RandomReadHint(v)           ((v)->vm_flags & VM_RAND_READ)
+
 /*
  * mapping from the currently active vm_flags protection bits (the
  * low four bits) to a page protection mask..
@@ -105,7 +115,6 @@ struct vm_operations_struct {
        void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
        void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
        int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
-       void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
        struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
        struct page * (*wppage)(struct vm_area_struct * area, unsigned long address, struct page * page);
        int (*swapout)(struct page *, struct file *);
index 17a64200ea22f5510aa66e5e1cfac55a545f531d..82d3e62f45fcd896cb6a71bad883dd0cf3a57504 100644 (file)
@@ -15,8 +15,8 @@
 #define MAX_ORDER 10
 
 typedef struct free_area_struct {
-       struct list_head free_list;
-       unsigned int map;
+       struct list_head        free_list;
+       unsigned int            *map;
 } free_area_t;
 
 struct pglist_data;
@@ -25,30 +25,31 @@ typedef struct zone_struct {
        /*
         * Commonly accessed fields:
         */
-       spinlock_t lock;
-       unsigned long offset;
-       unsigned long free_pages;
-       char low_on_memory;
-       char zone_wake_kswapd;
-       unsigned long pages_min, pages_low, pages_high;
+       spinlock_t              lock;
+       unsigned long           offset;
+       unsigned long           free_pages;
+       char                    low_on_memory;
+       char                    zone_wake_kswapd;
+       unsigned long           pages_min, pages_low, pages_high;
+       struct list_head        lru_cache;
 
        /*
         * free areas of different sizes
         */
-       free_area_t free_area[MAX_ORDER];
+       free_area_t             free_area[MAX_ORDER];
 
        /*
         * rarely used fields:
         */
-       char name;
-       unsigned long size;
+       char                    *name;
+       unsigned long           size;
        /*
         * Discontig memory support fields.
         */
-       struct pglist_data *zone_pgdat;
-       unsigned long zone_start_paddr;
-       unsigned long zone_start_mapnr;
-       struct page zone_mem_map;
+       struct pglist_data      *zone_pgdat;
+       unsigned long           zone_start_paddr;
+       unsigned long           zone_start_mapnr;
+       struct page             *zone_mem_map;
 } zone_t;
 
 #define ZONE_DMA               0
index 2e7e27e252fb47d5ab7c88f5d19adc7603299aeb..d79fd68ef5b60afa0bef206712af31ffee8a52fb 100644 (file)
@@ -67,7 +67,6 @@ FASTCALL(unsigned int nr_free_pages(void));
 FASTCALL(unsigned int nr_free_buffer_pages(void));
 FASTCALL(unsigned int nr_free_highpages(void));
 extern int nr_lru_pages;
-extern struct list_head lru_cache;
 extern atomic_t nr_async_pages;
 extern struct address_space swapper_space;
 extern atomic_t page_cache_size;
@@ -167,7 +166,7 @@ extern spinlock_t pagemap_lru_lock;
 #define        lru_cache_add(page)                     \
 do {                                           \
        spin_lock(&pagemap_lru_lock);           \
-       list_add(&(page)->lru, &lru_cache);     \
+       list_add(&(page)->lru, &page->zone->lru_cache); \
        nr_lru_pages++;                         \
        spin_unlock(&pagemap_lru_lock);         \
 } while (0)
index 69f0189aa4a6de21a98113fe51d71ff027023332..8326fb791b4ef0cfd749be18a6d84d185898dc15 100644 (file)
@@ -364,16 +364,20 @@ struct usb_driver {
  */
 typedef int (*usb_device_irq)(int, void *, int, void *);
 
-/* --------------------------------------------------------------------------* 
- * New USB Structures                                                        *
- * --------------------------------------------------------------------------*/
+/* -------------------------------------------------------------------------------------
+ * New USB Structures                                                                   *
+ * -------------------------------------------------------------------------------------*/
 
-#define USB_DISABLE_SPD                1
-#define USB_ISO_ASAP           2
-#define USB_URB_EARLY_COMPLETE 4
-#define USB_ASYNC_UNLINK       8
 
-typedef struct {
+#define USB_DISABLE_SPD         0x0001
+#define USB_ISO_ASAP            0x0002
+#define USB_URB_EARLY_COMPLETE  0x0004
+#define USB_ASYNC_UNLINK        0x0008
+#define USB_QUEUE_BULK          0x0010
+#define USB_TIMEOUT_KILLED     0x1000  // only set by HCD!
+
+typedef struct
+{
        unsigned int offset;
        unsigned int length;            // expected length
        unsigned int actual_length;
@@ -402,6 +406,7 @@ typedef struct urb
        int number_of_packets;          // number of packets in this request (iso/irq only)
        int interval;                   // polling interval (irq only)
        int error_count;                // number of errors in this transfer (iso only)
+       int timeout;                    // timeout (in jiffies)
        //
        void *context;                  // context for completion routine
        usb_complete_t complete;        // pointer to completion routine
@@ -445,6 +450,31 @@ typedef struct urb
        (a)->start_frame=-1;\
     } while (0)
 
+#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
+    do {\
+       spin_lock_init(&(a)->lock);\
+       (a)->dev=aa;\
+       (a)->pipe=b;\
+       (a)->setup_packet=c;\
+       (a)->transfer_buffer=d;\
+       (a)->transfer_buffer_length=e;\
+       (a)->complete=f;\
+       (a)->context=g;\
+       (a)->timeout=h;\
+    } while (0)
+
+#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \
+    do {\
+       spin_lock_init(&(a)->lock);\
+       (a)->dev=aa;\
+       (a)->pipe=b;\
+       (a)->transfer_buffer=c;\
+       (a)->transfer_buffer_length=d;\
+       (a)->complete=e;\
+       (a)->context=f;\
+       (a)->timeout=g;\
+    } while (0)
+    
 purb_t usb_alloc_urb(int iso_packets);
 void usb_free_urb (purb_t purb);
 int usb_submit_urb(purb_t purb);
index 2b432a786a5fe15ba114ffbd94052efb6e77ef68..cb7e2c33a3400a7381249f9f869f09ebc9b1efe3 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -1129,11 +1129,13 @@ asmlinkage long sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
        if (IS_ERR (name))
                return PTR_ERR (name);
 
+       lock_kernel();
        file = filp_open (name, O_RDWR, 0);
        putname (name);
-       if (IS_ERR (file))
+       if (IS_ERR (file)) {
+               unlock_kernel();
                goto bad_file;
-       lock_kernel();
+       }
        *raddr = do_mmap (file, addr, file->f_dentry->d_inode->i_size,
                          (shmflg & SHM_RDONLY ? PROT_READ :
                           PROT_READ | PROT_WRITE), flags, 0);
index 7c834b8934f6b55bf7e649c4482dd313e1bdb81c..c6f22872de4e33ecb00d2e81b3091fb8fd1314e1 100644 (file)
@@ -55,8 +55,8 @@ static void release(struct task_struct * p)
                 * was given away by the parent in the first place.)
                 */
                current->counter += p->counter;
-               if (current->counter > current->priority*2)
-                       current->counter = current->priority*2;
+               if (current->counter >= current->priority*2)
+                       current->counter = current->priority*2-1;
                free_task_struct(p);
        } else {
                printk("task releasing itself\n");
index da4de2c9bbf690ab6428e2d2911204a2a8b48a94..2fbb08e32fc685103768de933482bbafff9b855f 100644 (file)
@@ -647,8 +647,11 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
        p->run_list.next = NULL;
        p->run_list.prev = NULL;
 
-       if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT))
-               p->p_pptr = p->p_opptr = current;
+       if ((clone_flags & CLONE_VFORK) || !(clone_flags & CLONE_PARENT)) {
+               p->p_opptr = current;
+               if (!(current->flags & PF_PTRACED))
+                       p->p_pptr = current;
+       }
        p->p_cptr = NULL;
        init_waitqueue_head(&p->wait_chldexit);
        p->vfork_sem = NULL;
index b2f60d65f416324feceff6e8f91c94baf9efa195..369d9b954dfdbf795442ce9f7accb87033ff6141 100644 (file)
@@ -28,9 +28,17 @@ int pm_active = 0;
 static spinlock_t pm_devs_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(pm_devs);
 
-/*
- * Register a device with power management
+/**
+ *     pm_register - register a device with power management
+ *     @type: The device type 
+ *     @id: Device ID
+ *     @callback: Callback function
+ *
+ *     Add a device to the list of devices that wish to be notified about
+ *     power management events. A pm_dev structure is returnd on success,
+ *     on failure the return is NULL
  */
 struct pm_dev *pm_register(pm_dev_t type,
                           unsigned long id,
                           pm_callback callback)
@@ -51,9 +59,14 @@ struct pm_dev *pm_register(pm_dev_t type,
        return dev;
 }
 
-/*
- * Unregister a device with power management
+/**
+ *     pm_unregister -  unregister a device with power management
+ *     @dev: device to unregister
+ *
+ *     Remove a device from the power management notification lists. The
+ *     dev passed must be a handle previously returned by pm_register.
  */
 void pm_unregister(struct pm_dev *dev)
 {
        if (dev) {
@@ -67,9 +80,16 @@ void pm_unregister(struct pm_dev *dev)
        }
 }
 
-/*
- * Unregister all devices with matching callback
+/**
+ *     pm_unregister_all - unregister all devices with matching callback
+ *     @callback: callback function pointer
+ *
+ *     Unregister every device that would call the callback passed. This
+ *     is primarily meant as a helper function for loadable modules. It
+ *     enables a module to give up all its managed devices without keeping
+ *     its own private list.
  */
 void pm_unregister_all(pm_callback callback)
 {
        struct list_head *entry;
@@ -86,9 +106,21 @@ void pm_unregister_all(pm_callback callback)
        }
 }
 
-/*
- * Send request to a single device
+/**
+ *     pm_send - send request to a single device
+ *     @dev: device to send to
+ *     @rqst: power management request
+ *     @data: data for the callback
+ *
+ *     Issue a power management request to a given device. The 
+ *     PM_SUSPEND and PM_RESUME events are handled specially. The
+ *     data field must hold the intented next state. No call is made
+ *     if the state matches.
+ *
+ *     BUGS: what stops two power management requests occuring in parallel
+ *     and conflicting.
  */
 int pm_send(struct pm_dev *dev, pm_request_t rqst, void *data)
 {
        int status = 0;
@@ -138,9 +170,26 @@ static void pm_undo_all(struct pm_dev *last)
        }
 }
 
-/*
- * Send a request to all devices
+/**
+ *     pm_send - send request to all managed device
+ *     @rqst: power management request
+ *     @data: data for the callback
+ *
+ *     Issue a power management request to a all devices. The 
+ *     PM_SUSPEND events are handled specially. Any device is 
+ *     permitted to fail a suspend by returning a non zero (error)
+ *     value from its callback function. If any device vetoes a 
+ *     suspend request then all other devices that have suspended 
+ *     during the processing of this request are restored to their
+ *     previous state.
+ *
+ *     Zero is returned on success. If a suspend fails then the status
+ *     from the device that vetoes the suspend is returned.
+ *
+ *     BUGS: what stops two power management requests occuring in parallel
+ *     and conflicting.
  */
 int pm_send_all(pm_request_t rqst, void *data)
 {
        struct list_head *entry = pm_devs.next;
@@ -162,9 +211,19 @@ int pm_send_all(pm_request_t rqst, void *data)
        return 0;
 }
 
-/*
- * Find a device
+/**
+ *     pm_find  - find a device
+ *     @type: type of device
+ *     @from: Where to start looking
+ *
+ *     Scan the power management list for devices of a specific type. The
+ *     return value for a matching device may be passed to further calls
+ *     to this function to find further matches. A NULL indicates the end
+ *     of the list. 
+ *
+ *     To search from the beginning pass NULL as the from value.
  */
 struct pm_dev *pm_find(pm_dev_t type, struct pm_dev *from)
 {
        struct list_head *entry = from ? from->entry.next:pm_devs.next;
index 4fa56405bc990ac0ffee7c6007d1e4322a3dab5d..3fb7d011cb7eb2f691b350dce5b86acf1f0e7a2a 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <asm/pgalloc.h>
 #include <asm/uaccess.h>
+#include <asm/mman.h>
 
 #include <linux/highmem.h>
 
@@ -220,15 +221,18 @@ int shrink_mmap(int priority, int gfp_mask, zone_t *zone)
        struct list_head * page_lru, * dispose;
        struct page * page;
 
+       if (!zone)
+               BUG();
+
        count = nr_lru_pages / (priority+1);
 
        spin_lock(&pagemap_lru_lock);
 
-       while (count > 0 && (page_lru = lru_cache.prev) != &lru_cache) {
+       while (count > 0 && (page_lru = zone->lru_cache.prev) != &zone->lru_cache) {
                page = list_entry(page_lru, struct page, lru);
                list_del(page_lru);
 
-               dispose = &lru_cache;
+               dispose = &zone->lru_cache;
                if (test_and_clear_bit(PG_referenced, &page->flags))
                        /* Roll the page at the top of the lru list,
                         * we could also be more aggressive putting
@@ -355,8 +359,8 @@ made_buffer_progress:
        nr_lru_pages--;
 
 out:
-       list_splice(&young, &lru_cache);
-       list_splice(&old, lru_cache.prev);
+       list_splice(&young, &zone->lru_cache);
+       list_splice(&old, zone->lru_cache.prev);
 
        spin_unlock(&pagemap_lru_lock);
 
@@ -1293,6 +1297,61 @@ out:
        return retval;
 }
 
+/*
+ * Read-ahead and flush behind for MADV_SEQUENTIAL areas.  Since we are
+ * sure this is sequential access, we don't need a flexible read-ahead
+ * window size -- we can always use a large fixed size window.
+ */
+static void nopage_sequential_readahead(struct vm_area_struct * vma,
+       unsigned long pgoff, unsigned long filesize)
+{
+       unsigned long ra_window;
+
+       ra_window = get_max_readahead(vma->vm_file->f_dentry->d_inode);
+       ra_window = CLUSTER_OFFSET(ra_window + CLUSTER_PAGES - 1);
+
+       /* vm_raend is zero if we haven't read ahead in this area yet.  */
+       if (vma->vm_raend == 0)
+               vma->vm_raend = vma->vm_pgoff + ra_window;
+
+       /*
+        * If we've just faulted the page half-way through our window,
+        * then schedule reads for the next window, and release the
+        * pages in the previous window.
+        */
+       if ((pgoff + (ra_window >> 1)) == vma->vm_raend) {
+               unsigned long start = vma->vm_pgoff + vma->vm_raend;
+               unsigned long end = start + ra_window;
+
+               if (end > ((vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff))
+                       end = (vma->vm_end >> PAGE_SHIFT) + vma->vm_pgoff;
+               if (start > end)
+                       return;
+
+               while ((start < end) && (start < filesize)) {
+                       if (read_cluster_nonblocking(vma->vm_file,
+                                                       start, filesize) < 0)
+                               break;
+                       start += CLUSTER_PAGES;
+               }
+               run_task_queue(&tq_disk);
+
+               /* if we're far enough past the beginning of this area,
+                  recycle pages that are in the previous window. */
+               if (vma->vm_raend > (vma->vm_pgoff + ra_window + ra_window)) {
+                       unsigned long window = ra_window << PAGE_SHIFT;
+
+                       end = vma->vm_start + (vma->vm_raend << PAGE_SHIFT);
+                       end -= window + window;
+                       filemap_sync(vma, end - window, window, MS_INVALIDATE);
+               }
+
+               vma->vm_raend += ra_window;
+       }
+
+       return;
+}
+
 /*
  * filemap_nopage() is invoked via the vma operations vector for a
  * mapped memory region to read in file data during a page fault.
@@ -1339,6 +1398,12 @@ retry_find:
                goto page_not_uptodate;
 
 success:
+       /*
+        * Try read-ahead for sequential areas.
+        */
+       if (VM_SequentialReadHint(area))
+               nopage_sequential_readahead(area, pgoff, size);
+
        /*
         * Found the page and have a reference on it, need to check sharing
         * and possibly copy it over to another page..
@@ -1355,7 +1420,7 @@ success:
                page_cache_release(page);
                return new_page;
        }
-               
+
        flush_page_to_ram(old_page);
        return old_page;
 
@@ -1367,7 +1432,7 @@ no_cached_page:
         * Otherwise, we're off the end of a privately mapped file,
         * so we need to map a zero page.
         */
-       if (pgoff < size)
+       if ((pgoff < size) && !VM_RandomReadHint(area))
                error = read_cluster_nonblocking(file, pgoff, size);
        else
                error = page_cache_read(file, pgoff);
@@ -1646,7 +1711,6 @@ int generic_file_mmap(struct file * file, struct vm_area_struct * vma)
        return 0;
 }
 
-
 /*
  * The msync() system call.
  */
@@ -1727,6 +1791,351 @@ out:
        return error;
 }
 
+static inline void setup_read_behavior(struct vm_area_struct * vma,
+       int behavior)
+{
+       VM_ClearReadHint(vma);
+       switch(behavior) {
+               case MADV_SEQUENTIAL:
+                       vma->vm_flags |= VM_SEQ_READ;
+                       break;
+               case MADV_RANDOM:
+                       vma->vm_flags |= VM_RAND_READ;
+                       break;
+               default:
+                       break;
+       }
+       return;
+}
+
+static long madvise_fixup_start(struct vm_area_struct * vma,
+       unsigned long end, int behavior)
+{
+       struct vm_area_struct * n;
+
+       n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (!n)
+               return -EAGAIN;
+       *n = *vma;
+       n->vm_end = end;
+       setup_read_behavior(n, behavior);
+       n->vm_raend = 0;
+       get_file(n->vm_file);
+       if (n->vm_ops && n->vm_ops->open)
+               n->vm_ops->open(n);
+       vmlist_modify_lock(vma->vm_mm);
+       vma->vm_pgoff += (end - vma->vm_start) >> PAGE_SHIFT;
+       vma->vm_start = end;
+       insert_vm_struct(current->mm, n);
+       vmlist_modify_unlock(vma->vm_mm);
+       return 0;
+}
+
+static long madvise_fixup_end(struct vm_area_struct * vma,
+       unsigned long start, int behavior)
+{
+       struct vm_area_struct * n;
+
+       n = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (!n)
+               return -EAGAIN;
+       *n = *vma;
+       n->vm_start = start;
+       n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
+       setup_read_behavior(n, behavior);
+       n->vm_raend = 0;
+       get_file(n->vm_file);
+       if (n->vm_ops && n->vm_ops->open)
+               n->vm_ops->open(n);
+       vmlist_modify_lock(vma->vm_mm);
+       vma->vm_end = start;
+       insert_vm_struct(current->mm, n);
+       vmlist_modify_unlock(vma->vm_mm);
+       return 0;
+}
+
+static long madvise_fixup_middle(struct vm_area_struct * vma,
+       unsigned long start, unsigned long end, int behavior)
+{
+       struct vm_area_struct * left, * right;
+
+       left = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (!left)
+               return -EAGAIN;
+       right = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
+       if (!right) {
+               kmem_cache_free(vm_area_cachep, left);
+               return -EAGAIN;
+       }
+       *left = *vma;
+       *right = *vma;
+       left->vm_end = start;
+       right->vm_start = end;
+       right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+       left->vm_raend = 0;
+       right->vm_raend = 0;
+       atomic_add(2, &vma->vm_file->f_count);
+
+       if (vma->vm_ops && vma->vm_ops->open) {
+               vma->vm_ops->open(left);
+               vma->vm_ops->open(right);
+       }
+       vmlist_modify_lock(vma->vm_mm);
+       vma->vm_pgoff += (start - vma->vm_start) >> PAGE_SHIFT;
+       vma->vm_start = start;
+       vma->vm_end = end;
+       setup_read_behavior(vma, behavior);
+       vma->vm_raend = 0;
+       insert_vm_struct(current->mm, left);
+       insert_vm_struct(current->mm, right);
+       vmlist_modify_unlock(vma->vm_mm);
+       return 0;
+}
+
+/*
+ * We can potentially split a vm area into separate
+ * areas, each area with its own behavior.
+ */
+static long madvise_behavior(struct vm_area_struct * vma,
+       unsigned long start, unsigned long end, int behavior)
+{
+       int error = 0;
+
+       /* This caps the number of vma's this process can own */
+       if (vma->vm_mm->map_count > MAX_MAP_COUNT)
+               return -ENOMEM;
+
+       if (start == vma->vm_start) {
+               if (end == vma->vm_end) {
+                       setup_read_behavior(vma, behavior);
+                       vma->vm_raend = 0;
+               } else
+                       error = madvise_fixup_start(vma, end, behavior);
+       } else {
+               if (end == vma->vm_end)
+                       error = madvise_fixup_end(vma, start, behavior);
+               else
+                       error = madvise_fixup_middle(vma, start, end, behavior);
+       }
+
+       return error;
+}
+
+/*
+ * Schedule all required I/O operations, then run the disk queue
+ * to make sure they are started.  Do not wait for completion.
+ */
+static long madvise_willneed(struct vm_area_struct * vma,
+       unsigned long start, unsigned long end)
+{
+       long error = -EBADF;
+       struct file * file;
+       unsigned long size, rlim_rss;
+
+       /* Doesn't work if there's no mapped file. */
+       if (!vma->vm_file)
+               return error;
+       file = vma->vm_file;
+       size = (file->f_dentry->d_inode->i_size + PAGE_CACHE_SIZE - 1) >>
+                                                       PAGE_CACHE_SHIFT;
+
+       start = ((start - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+       if (end > vma->vm_end)
+               end = vma->vm_end;
+       end = ((end - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+
+       /* Make sure this doesn't exceed the process's max rss. */
+       error = -EIO;
+       rlim_rss = current->rlim ?  current->rlim[RLIMIT_RSS].rlim_cur :
+                               LONG_MAX; /* default: see resource.h */
+       if ((vma->vm_mm->rss + (end - start)) > rlim_rss)
+               return error;
+
+       /* round to cluster boundaries if this isn't a "random" area. */
+       if (!VM_RandomReadHint(vma)) {
+               start = CLUSTER_OFFSET(start);
+               end = CLUSTER_OFFSET(end + CLUSTER_PAGES - 1);
+
+               while ((start < end) && (start < size)) {
+                       error = read_cluster_nonblocking(file, start, size);
+                       start += CLUSTER_PAGES;
+                       if (error < 0)
+                               break;
+               }
+       } else {
+               while ((start < end) && (start < size)) {
+                       error = page_cache_read(file, start);
+                       start++;
+                       if (error < 0)
+                               break;
+               }
+       }
+
+       /* Don't wait for someone else to push these requests. */
+       run_task_queue(&tq_disk);
+
+       return error;
+}
+
+/*
+ * Application no longer needs these pages.  If the pages are dirty,
+ * it's OK to just throw them away.  The app will be more careful about
+ * data it wants to keep.  Be sure to free swap resources too.  The
+ * zap_page_range call sets things up for shrink_mmap to actually free
+ * these pages later if no one else has touched them in the meantime,
+ * although we could add these pages to a global reuse list for
+ * shrink_mmap to pick up before reclaiming other pages.
+ *
+ * NB: This interface discards data rather than pushes it out to swap,
+ * as some implementations do.  This has performance implications for
+ * applications like large transactional databases which want to discard
+ * pages in anonymous maps after committing to backing store the data
+ * that was kept in them.  There is no reason to write this data out to
+ * the swap area if the application is discarding it.
+ *
+ * An interface that causes the system to free clean pages and flush
+ * dirty pages is already available as msync(MS_INVALIDATE).
+ */
+static long madvise_dontneed(struct vm_area_struct * vma,
+       unsigned long start, unsigned long end)
+{
+       if (vma->vm_flags & VM_LOCKED)
+               return -EINVAL;
+
+       lock_kernel();  /* is this really necessary? */
+
+       flush_cache_range(vma->vm_mm, start, end);
+       zap_page_range(vma->vm_mm, start, end - start);
+       flush_tlb_range(vma->vm_mm, start, end);
+
+       unlock_kernel();
+       return 0;
+}
+
+static long madvise_vma(struct vm_area_struct * vma, unsigned long start,
+       unsigned long end, int behavior)
+{
+       long error = -EBADF;
+
+       switch (behavior) {
+       case MADV_NORMAL:
+       case MADV_SEQUENTIAL:
+       case MADV_RANDOM:
+               error = madvise_behavior(vma, start, end, behavior);
+               break;
+
+       case MADV_WILLNEED:
+               error = madvise_willneed(vma, start, end);
+               break;
+
+       case MADV_DONTNEED:
+               error = madvise_dontneed(vma, start, end);
+               break;
+
+       default:
+               error = -EINVAL;
+               break;
+       }
+               
+       return error;
+}
+
+/*
+ * The madvise(2) system call.
+ *
+ * Applications can use madvise() to advise the kernel how it should
+ * handle paging I/O in this VM area.  The idea is to help the kernel
+ * use appropriate read-ahead and caching techniques.  The information
+ * provided is advisory only, and can be safely disregarded by the
+ * kernel without affecting the correct operation of the application.
+ *
+ * behavior values:
+ *  MADV_NORMAL - the default behavior is to read clusters.  This
+ *             results in some read-ahead and read-behind.
+ *  MADV_RANDOM - the system should read the minimum amount of data
+ *             on any access, since it is unlikely that the appli-
+ *             cation will need more than what it asks for.
+ *  MADV_SEQUENTIAL - pages in the given range will probably be accessed
+ *             once, so they can be aggressively read ahead, and
+ *             can be freed soon after they are accessed.
+ *  MADV_WILLNEED - the application is notifying the system to read
+ *             some pages ahead.
+ *  MADV_DONTNEED - the application is finished with the given range,
+ *             so the kernel can free resources associated with it.
+ *
+ * return values:
+ *  zero    - success
+ *  -EINVAL - start + len < 0, start is not page-aligned,
+ *             "behavior" is not a valid value, or application
+ *             is attempting to release locked or shared pages.
+ *  -ENOMEM - addresses in the specified range are not currently
+ *             mapped, or are outside the AS of the process.
+ *  -EIO    - an I/O error occurred while paging in data.
+ *  -EBADF  - map exists, but area maps something that isn't a file.
+ *  -EAGAIN - a kernel resource was temporarily unavailable.
+ */
+asmlinkage long sys_madvise(unsigned long start, size_t len, int behavior)
+{
+       unsigned long end;
+       struct vm_area_struct * vma;
+       int unmapped_error = 0;
+       int error = -EINVAL;
+
+       down(&current->mm->mmap_sem);
+
+       if (start & ~PAGE_MASK)
+               goto out;
+       len = (len + ~PAGE_MASK) & PAGE_MASK;
+       end = start + len;
+       if (end < start)
+               goto out;
+
+       error = 0;
+       if (end == start)
+               goto out;
+
+       /*
+        * If the interval [start,end) covers some unmapped address
+        * ranges, just ignore them, but return -ENOMEM at the end.
+        */
+       vma = find_vma(current->mm, start);
+       for (;;) {
+               /* Still start < end. */
+               error = -ENOMEM;
+               if (!vma)
+                       goto out;
+
+               /* Here start < vma->vm_end. */
+               if (start < vma->vm_start) {
+                       unmapped_error = -ENOMEM;
+                       start = vma->vm_start;
+               }
+
+               /* Here vma->vm_start <= start < vma->vm_end. */
+               if (end <= vma->vm_end) {
+                       if (start < end) {
+                               error = madvise_vma(vma, start, end,
+                                                       behavior);
+                               if (error)
+                                       goto out;
+                       }
+                       error = unmapped_error;
+                       goto out;
+               }
+
+               /* Here vma->vm_start <= start < vma->vm_end < end. */
+               error = madvise_vma(vma, start, vma->vm_end, behavior);
+               if (error)
+                       goto out;
+               start = vma->vm_end;
+               vma = vma->vm_next;
+       }
+
+out:
+       up(&current->mm->mmap_sem);
+       return error;
+}
+
 /*
  * Later we can get more picky about what "in core" means precisely.
  * For now, simply check to see if the page is in the page cache,
index c3e40db54399b20b979bda0a743f3a93bdb3e186..a3d10ff99dc50ae438ec087fc7c9420ca61a70f4 100644 (file)
@@ -31,6 +31,7 @@ static inline int mlock_fixup_start(struct vm_area_struct * vma,
        *n = *vma;
        n->vm_end = end;
        n->vm_flags = newflags;
+       n->vm_raend = 0;
        if (n->vm_file)
                get_file(n->vm_file);
        if (n->vm_ops && n->vm_ops->open)
@@ -55,6 +56,7 @@ static inline int mlock_fixup_end(struct vm_area_struct * vma,
        n->vm_start = start;
        n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
        n->vm_flags = newflags;
+       n->vm_raend = 0;
        if (n->vm_file)
                get_file(n->vm_file);
        if (n->vm_ops && n->vm_ops->open)
@@ -85,6 +87,8 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma,
        right->vm_start = end;
        right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
        vma->vm_flags = newflags;
+       left->vm_raend = 0;
+       right->vm_raend = 0;
        if (vma->vm_file)
                atomic_add(2, &vma->vm_file->f_count);
 
@@ -97,6 +101,7 @@ static inline int mlock_fixup_middle(struct vm_area_struct * vma,
        vma->vm_start = start;
        vma->vm_end = end;
        vma->vm_flags = newflags;
+       vma->vm_raend = 0;
        insert_vm_struct(current->mm, left);
        insert_vm_struct(current->mm, right);
        vmlist_modify_unlock(vma->vm_mm);
index b650babc8ded090b2ca193042e799f81933066ec..7bc2cf91083973ace4f997d78274781e1077b765 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -249,6 +249,9 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, unsigned lon
        vma->vm_flags = vm_flags(prot,flags) | mm->def_flags;
 
        if (file) {
+               VM_ClearReadHint(vma);
+               vma->vm_raend = 0;
+
                if (file->f_mode & 1)
                        vma->vm_flags |= VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;
                if (flags & MAP_SHARED) {
@@ -549,6 +552,7 @@ static struct vm_area_struct * unmap_fixup(struct vm_area_struct *area,
                mpnt->vm_end = area->vm_end;
                mpnt->vm_page_prot = area->vm_page_prot;
                mpnt->vm_flags = area->vm_flags;
+               mpnt->vm_raend = 0;
                mpnt->vm_ops = area->vm_ops;
                mpnt->vm_pgoff = area->vm_pgoff + ((end - area->vm_start) >> PAGE_SHIFT);
                mpnt->vm_file = area->vm_file;
index 70f1d8e2c932022804d365c68ba01bf2b217f07b..53fc53acb211dea5a4fe058e6c06bb8cab9413d4 100644 (file)
@@ -105,6 +105,7 @@ static inline int mprotect_fixup_start(struct vm_area_struct * vma,
        *n = *vma;
        n->vm_end = end;
        n->vm_flags = newflags;
+       n->vm_raend = 0;
        n->vm_page_prot = prot;
        if (n->vm_file)
                get_file(n->vm_file);
@@ -131,6 +132,7 @@ static inline int mprotect_fixup_end(struct vm_area_struct * vma,
        n->vm_start = start;
        n->vm_pgoff += (n->vm_start - vma->vm_start) >> PAGE_SHIFT;
        n->vm_flags = newflags;
+       n->vm_raend = 0;
        n->vm_page_prot = prot;
        if (n->vm_file)
                get_file(n->vm_file);
@@ -162,6 +164,8 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
        left->vm_end = start;
        right->vm_start = end;
        right->vm_pgoff += (right->vm_start - left->vm_start) >> PAGE_SHIFT;
+       left->vm_raend = 0;
+       right->vm_raend = 0;
        if (vma->vm_file)
                atomic_add(2,&vma->vm_file->f_count);
        if (vma->vm_ops && vma->vm_ops->open) {
@@ -173,6 +177,7 @@ static inline int mprotect_fixup_middle(struct vm_area_struct * vma,
        vma->vm_start = start;
        vma->vm_end = end;
        vma->vm_flags = newflags;
+       vma->vm_raend = 0;
        vma->vm_page_prot = prot;
        insert_vm_struct(current->mm, left);
        insert_vm_struct(current->mm, right);
index 5721fc5d51f66dcfb10497bb9e919448260428bf..d8d18cf623d15f094284a40b217b673021e0ae6d 100644 (file)
@@ -135,8 +135,8 @@ static inline unsigned long move_vma(struct vm_area_struct * vma,
                        *new_vma = *vma;
                        new_vma->vm_start = new_addr;
                        new_vma->vm_end = new_addr+new_len;
-                       new_vma->vm_pgoff = vma->vm_pgoff;
                        new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
+                       new_vma->vm_raend = 0;
                        if (new_vma->vm_file)
                                get_file(new_vma->vm_file);
                        if (new_vma->vm_ops && new_vma->vm_ops->open)
index 02ba33bd5c816b8ea4922fbdfed0cadd81352628..0204cf1412d819829e7571d6946fcc74a7902431 100644 (file)
@@ -26,7 +26,6 @@
 
 int nr_swap_pages = 0;
 int nr_lru_pages;
-LIST_HEAD(lru_cache);
 pg_data_t *pgdat_list = (pg_data_t *)0;
 
 static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" };
@@ -59,6 +58,19 @@ static int zone_balance_max[MAX_NR_ZONES] = { 255 , 255, 255, };
  */
 #define BAD_RANGE(zone,x) (((zone) != (x)->zone) || (((x)-mem_map) < (zone)->offset) || (((x)-mem_map) >= (zone)->offset+(zone)->size))
 
+static inline unsigned long classfree(zone_t *zone)
+{
+       unsigned long free = 0;
+       zone_t *z = zone->zone_pgdat->node_zones;
+
+       while (z != zone) {
+               free += z->free_pages;
+               z++;
+       }
+       free += zone->free_pages;
+       return(free);
+}
+
 /*
  * Buddy system. Hairy. You really aren't expected to understand this
  *
@@ -135,6 +147,9 @@ void __free_pages_ok (struct page *page, unsigned long order)
        memlist_add_head(&(base + page_idx)->list, &area->free_list);
 
        spin_unlock_irqrestore(&zone->lock, flags);
+
+       if (classfree(zone) > zone->pages_high)
+               zone->zone_wake_kswapd = 0;
 }
 
 #define MARK_USED(index, order, area) \
@@ -201,19 +216,6 @@ static inline struct page * rmqueue (zone_t *zone, unsigned long order)
        return NULL;
 }
 
-static inline unsigned long classfree(zone_t *zone)
-{
-       unsigned long free = 0;
-       zone_t *z = zone->zone_pgdat->node_zones;
-
-       while (z != zone) {
-               free += z->free_pages;
-               z++;
-       }
-       free += zone->free_pages;
-       return(free);
-}
-
 static inline int zone_balance_memory (zone_t *zone, int gfp_mask)
 {
        int freed;
@@ -263,21 +265,12 @@ struct page * __alloc_pages (zonelist_t *zonelist, unsigned long order)
                {
                        unsigned long free = classfree(z);
 
-                       if (free > z->pages_high)
-                       {
-                               if (z->low_on_memory)
-                                       z->low_on_memory = 0;
-                               z->zone_wake_kswapd = 0;
-                       }
-                       else
+                       if (free <= z->pages_high)
                        {
                                extern wait_queue_head_t kswapd_wait;
 
-                               if (free <= z->pages_low) {
-                                       z->zone_wake_kswapd = 1;
-                                       wake_up_interruptible(&kswapd_wait);
-                               } else
-                                       z->zone_wake_kswapd = 0;
+                               z->zone_wake_kswapd = 1;
+                               wake_up_interruptible(&kswapd_wait);
 
                                if (free <= z->pages_min)
                                        z->low_on_memory = 1;
@@ -585,6 +578,7 @@ void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
                        unsigned long bitmap_size;
 
                        memlist_init(&zone->free_area[i].free_list);
+                       memlist_init(&zone->lru_cache);
                        mask += mask;
                        size = (size + ~mask) & mask;
                        bitmap_size = size >> i;
index 603b9a2e0f4e6ca1c82293be19243e3fec43ba1e..d3dfb8db686fb33debfbc3e7a71dd966e179bca1 100644 (file)
@@ -504,8 +504,7 @@ int kswapd(void *unused)
                        while (pgdat) {
                                for (i = 0; i < MAX_NR_ZONES; i++) {
                                        zone = pgdat->node_zones + i;
-                                       if ((!zone->size) || 
-                                                       (!zone->zone_wake_kswapd))
+                                       if ((!zone->size) || (!zone->zone_wake_kswapd))
                                                continue;
                                        do_try_to_free_pages(GFP_KSWAPD, zone);
                                }
index 80fc635f27f4b26d4aebfea65d635b4bd7c0e0b3..9a1b18270cd68c3faee18a4af15a2a37080c6657 100644 (file)
@@ -1378,11 +1378,12 @@ static int atalk_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 
                if (n < 0)
                        return n;
-       } else
+       } else {
                sk->protinfo.af_at.src_port = addr->sat_port;
 
-       if (atalk_find_or_insert_socket(sk, addr) != NULL)
-               return -EADDRINUSE;
+               if (atalk_find_or_insert_socket(sk, addr) != NULL)
+                       return -EADDRINUSE;
+       }
 
        sk->zapped = 0;
 
index 6944a4d850d3bf4dccb31b3da34e29981b660b85..a22098207a090d97439f7a3d5e4d5bb8af6889cd 100644 (file)
@@ -51,12 +51,13 @@ static struct sock *econet_sklist;
    how you count) it makes sense to use a simple lookup table. */
 static struct net_device *net2dev_map[256];
 
+#define EC_PORT_IP     0xd2
+
 #ifdef CONFIG_ECONET_AUNUDP
 static spinlock_t aun_queue_lock;
 static struct socket *udpsock;
 #define AUN_PORT       0x8000
 
-#define EC_PORT_IP     0xd2
 
 struct aunhdr
 {
@@ -750,6 +751,28 @@ struct sock *ec_listening_socket(unsigned char port, unsigned char
        return NULL;
 }
 
+/*
+ *     Queue a received packet for a socket.
+ */
+
+static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
+                          unsigned char stn, unsigned char net,
+                          unsigned char cb, unsigned char port)
+{
+       struct ec_cb *eb = (struct ec_cb *)&skb->cb;
+       struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
+
+       memset(sec, 0, sizeof(struct sockaddr_ec));
+       sec->sec_family = AF_ECONET;
+       sec->type = ECTYPE_PACKET_RECEIVED;
+       sec->port = port;
+       sec->cb = cb;
+       sec->addr.net = net;
+       sec->addr.station = stn;
+
+       return sock_queue_rcv_skb(sk, skb);
+}
+
 #ifdef CONFIG_ECONET_AUNUDP
 
 /*
@@ -792,27 +815,6 @@ static void aun_send_response(__u32 addr, unsigned long seq, int code, int cb)
        set_fs(oldfs);
 }
 
-/*
- *     Queue a received packet for a socket.
- */
-
-static int ec_queue_packet(struct sock *sk, struct sk_buff *skb,
-                          unsigned char stn, unsigned char net,
-                          unsigned char cb, unsigned char port)
-{
-       struct ec_cb *eb = (struct ec_cb *)&skb->cb;
-       struct sockaddr_ec *sec = (struct sockaddr_ec *)&eb->sec;
-
-       memset(sec, 0, sizeof(struct sockaddr_ec));
-       sec->sec_family = AF_ECONET;
-       sec->type = ECTYPE_PACKET_RECEIVED;
-       sec->port = port;
-       sec->cb = cb;
-       sec->addr.net = net;
-       sec->addr.station = stn;
-
-       return sock_queue_rcv_skb(sk, skb);
-}
 
 /*
  *     Handle incoming AUN packets.  Work out if anybody wants them,
@@ -1132,9 +1134,9 @@ void __exit econet_proto_exit(void)
 int __init econet_proto_init(struct net_proto *pro)
 {
        extern void econet_sysctl_register(void);
-       spin_lock_init(&aun_queue_lock);
        sock_register(&econet_family_ops);
 #ifdef CONFIG_ECONET_AUNUDP
+       spin_lock_init(&aun_queue_lock);
        aun_udp_initialise();
 #endif
 #ifdef CONFIG_ECONET_NATIVE
index 3a9d20e9ccd7e7edc1e8063c5c1f9033931639ca..799ec94760fc08ab2b123ddbd195e81236393989 100644 (file)
@@ -5,7 +5,7 @@
  *
  *             PACKET - implements raw packet sockets.
  *
- * Version:    $Id: af_packet.c,v 1.32 2000/02/21 16:25:55 davem Exp $
+ * Version:    $Id: af_packet.c,v 1.33 2000/03/13 22:11:50 davem Exp $
  *
  * Authors:    Ross Biro, <bir7@leland.Stanford.Edu>
  *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -1439,6 +1439,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg
                case SIOCGIFBR:
                case SIOCSIFBR:
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
+#ifdef CONFIG_INET
 #ifdef CONFIG_KMOD
                        if (br_ioctl_hook == NULL)
                                request_module("bridge");
@@ -1446,6 +1447,7 @@ static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg
                        if (br_ioctl_hook != NULL)
                                return br_ioctl_hook(arg);
 #endif
+#endif                         
 
                        return -ENOPKG;
                        
index f0a5a5135870bdeb0b0c9e24f8559b696ff37857..8d5fefef3abe3b1aa315141ae4c769ce251f2112 100644 (file)
@@ -1,3 +1,3 @@
 #!/bin/sh
-X=`$TOPDIR/scripts/gen-all-syms "$1"`
+X=`$TOPDIR/scripts/gen-all-syms "$*"`
 $TOPDIR/scripts/docproc $X