From 886d9d744ac54ee9eddf96e0e4b129b591aa1996 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:32:54 -0500 Subject: [PATCH] Import 2.3.99pre1 --- CREDITS | 3 +- Documentation/Configure.help | 18 +- Documentation/DocBook/Makefile | 2 +- Documentation/DocBook/parport-multi.fig | 59 + Documentation/DocBook/parport-share.fig | 154 ++ Documentation/DocBook/parport-structure.fig | 60 + Documentation/DocBook/parportbook.sgml | 1747 +++++++++++++++ Documentation/DocBook/videobook.tmpl | 2133 ++++++++++--------- MAINTAINERS | 10 +- Makefile | 190 +- arch/i386/kernel/entry.S | 3 +- arch/i386/kernel/irq.c | 129 +- arch/i386/kernel/mtrr.c | 68 +- arch/mips/kernel/irixelf.c | 2 - arch/sparc/kernel/sys_sunos.c | 53 +- arch/sparc/kernel/systbls.S | 6 +- arch/sparc64/kernel/ioctl32.c | 6 +- arch/sparc64/kernel/sys_sparc32.c | 11 +- arch/sparc64/kernel/sys_sunos32.c | 52 +- arch/sparc64/kernel/systbls.S | 8 +- arch/sparc64/solaris/misc.c | 2 +- arch/sparc64/solaris/systbl.S | 4 +- drivers/char/drm/auth.c | 7 +- drivers/char/drm/bufs.c | 8 +- drivers/char/drm/context.c | 5 +- drivers/char/drm/dma.c | 5 +- drivers/char/drm/drawable.c | 5 +- drivers/char/drm/drm.h | 5 +- drivers/char/drm/drmP.h | 10 +- drivers/char/drm/fops.c | 18 +- drivers/char/drm/gamma_dma.c | 5 +- drivers/char/drm/gamma_drv.c | 9 +- drivers/char/drm/gamma_drv.h | 5 +- drivers/char/drm/init.c | 5 +- drivers/char/drm/ioctl.c | 5 +- drivers/char/drm/lists.c | 7 +- drivers/char/drm/lock.c | 5 +- drivers/char/drm/memory.c | 5 +- drivers/char/drm/proc.c | 21 +- drivers/char/drm/tdfx_context.c | 5 +- drivers/char/drm/tdfx_drv.c | 25 +- drivers/char/drm/tdfx_drv.h | 6 +- drivers/char/drm/vm.c | 5 +- drivers/char/generic_serial.c | 1 + drivers/char/misc.c | 26 + drivers/char/ppdev.c | 11 + drivers/char/serial.c | 28 +- drivers/ide/ide-floppy.c | 8 +- drivers/net/ppp_async.c | 1 - drivers/net/shaper.c | 11 + drivers/parport/ChangeLog | 21 + drivers/parport/Config.in | 3 + drivers/parport/parport_pc.c | 119 +- drivers/scsi/constants.c | 563 +++-- drivers/scsi/eata_dma_proc.c | 18 +- drivers/scsi/hosts.h | 28 +- drivers/scsi/scsi.c | 18 - drivers/scsi/scsi.h | 11 +- drivers/scsi/scsi_debug.c | 12 +- drivers/scsi/scsi_ioctl.c | 64 +- drivers/scsi/scsi_lib.c | 6 + drivers/scsi/scsi_scan.c | 80 +- drivers/scsi/scsi_syms.c | 1 - drivers/scsi/sd.c | 72 +- drivers/scsi/sr.c | 4 +- drivers/sound/sound_core.c | 98 + drivers/sound/sound_firmware.c | 18 + drivers/sound/soundcard.c | 4 +- drivers/usb/Config.in | 3 - drivers/usb/dsbr100.c | 129 +- drivers/usb/pegasus.c | 714 +++---- drivers/usb/uhci.c | 56 +- drivers/usb/uhci.h | 6 +- drivers/usb/usb-storage.c | 1124 +++++----- drivers/usb/usb-storage.h | 24 +- drivers/usb/usb-uhci-debug.h | 79 +- drivers/usb/usb-uhci.c | 975 ++++++--- drivers/usb/usb-uhci.h | 18 +- drivers/video/aty128.h | 2 + drivers/video/aty128fb.c | 351 +-- drivers/video/fbmem.c | 5 +- fs/binfmt_elf.c | 2 - fs/ncpfs/inode.c | 2 +- fs/openpromfs/inode.c | 2 +- include/asm-alpha/parport.h | 43 +- include/asm-arm/parport.h | 53 +- include/asm-i386/mman.h | 6 + include/asm-i386/parport.h | 31 +- include/asm-i386/unistd.h | 2 + include/asm-mips/parport.h | 43 +- include/asm-mips64/parport.h | 43 +- include/asm-sparc64/parport.h | 16 +- include/linux/mm.h | 13 +- include/linux/mmzone.h | 31 +- include/linux/swap.h | 3 +- include/linux/usb.h | 46 +- ipc/shm.c | 6 +- kernel/exit.c | 4 +- kernel/fork.c | 7 +- kernel/pm.c | 83 +- mm/filemap.c | 423 +++- mm/mlock.c | 5 + mm/mmap.c | 4 + mm/mprotect.c | 5 + mm/mremap.c | 2 +- mm/page_alloc.c | 46 +- mm/vmscan.c | 3 +- net/appletalk/ddp.c | 7 +- net/econet/af_econet.c | 48 +- net/packet/af_packet.c | 4 +- scripts/docgen | 2 +- 111 files changed, 6949 insertions(+), 3639 deletions(-) create mode 100644 Documentation/DocBook/parport-multi.fig create mode 100644 Documentation/DocBook/parport-share.fig create mode 100644 Documentation/DocBook/parport-structure.fig create mode 100644 Documentation/DocBook/parportbook.sgml diff --git a/CREDITS b/CREDITS index b1b002f1d86b..b98bae810985 100644 --- 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 diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 76e6b6fd8452..64bdd50f2ca2 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -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 diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile index 31b2193f2d86..6bc727e4391b 100644 --- a/Documentation/DocBook/Makefile +++ b/Documentation/DocBook/Makefile @@ -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 index 000000000000..e0517b36fe80 --- /dev/null +++ b/Documentation/DocBook/parport-multi.fig @@ -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 index 000000000000..fe4f37322bcf --- /dev/null +++ b/Documentation/DocBook/parport-share.fig @@ -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 index 000000000000..4299ce68717e --- /dev/null +++ b/Documentation/DocBook/parport-structure.fig @@ -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 index 000000000000..1644748adddc --- /dev/null +++ b/Documentation/DocBook/parportbook.sgml @@ -0,0 +1,1747 @@ + + + + + The Parallel Port Subsystem + + + + Tim + Waugh + +
+ twaugh@redhat.com +
+
+
+
+ + + 1999-2000 + Tim Waugh + + + + + 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. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + +Design goals + + +The problems + + + + + + +The first parallel port support for Linux came with the line +printer driver, lp. The printer driver is a +character special device, and (in Linux 2.0) had support for writing, +via write, and configuration and statistics +reporting via ioctl. + +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. + +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. + +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 check_region function to make sure +that the IO region associated with the parallel port was free, and +then it would call request_region to allocate it. +The Zip drive used the same mechanism. Whichever driver initialised +first would gain exclusive control of the parallel port. + +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. + +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. + +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). + + + +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: compatibility, +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 compatibility mode +(i.e. normal printer protocol) and reverse nibble mode. + + + + +The solutions + + + +The parport 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. + + + +There are two layers to the +parport 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. + + + +The sharing model provided by the parport +subsystem is one of exclusive access. A device driver, such as the +printer driver, must ask the parport layer for +access to the port, and can only use the port once access has been +granted. When it has finished a transaction, it can +tell the parport layer that it may release the +port for other device drivers to use. + + + +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. + + + + + + + +This technique relies on certain special signals +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. + + + +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. + + + + + + +Standard transfer modes + + + + +The standard transfer modes in use over the +parallel port are defined 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. + +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. + +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. + +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). + +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). + + + + +Structure + + + + + + + + + + + + +Sharing core + + + +At the core of the parport subsystem is the +sharing mechanism (see drivers/parport/share.c). +This module, parport, 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). + + + + +Parports and their overrides + + +The generic parport sharing code doesn't +directly handle the parallel port hardware. That is done instead by +low-level parport drivers. The +function of a low-level parport driver is to +detect parallel ports, register them with the sharing code, and +provide a list of access functions for each port. + +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 +forward direction (that is, the computer drives them), +but some ports allow switching to reverse mode (driven +by the peripheral). There is an access function for examining the +data lines once in reverse mode. + + + + +IEEE 1284 transfer modes + + +Stacked on top of the sharing mechanism, but still in the +parport 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 parport core they must +use the lowest common denominator 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. + +The low-level parport 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. + + + + + + + + +Pardevices and parport_drivers + +When a parallel port device driver (such as +lp) initialises it tells the sharing layer about +itself using parport_register_driver. The +information is put into a struct +parport_driver, which is put into a linked list. The +information in a struct parport_driver really +just amounts to some function pointers to callbacks in the parallel +port device driver. + +During its initialisation, a low-level port driver tells the +sharing layer about all the ports that it has found (using +parport_register_port), and the sharing layer +creates a struct parport for each of them. +Each struct parport contains (among other +things) a pointer to a struct +parport_operations, which is a list of function pointers +for the various operations that can be performed on a port. You can +think of a struct parport as a parallel port +object, if object-orientated programming +is your thing. The parport structures are +chained in a linked list, whose head is portlist +(in drivers/parport/share.c). + +Once the port has been registered, the low-level port driver +announces it. The parport_announce_port function +walks down the list of parallel port device drivers +(struct parport_drivers) calling the +attach function of each. + +Similarly, a low-level port driver can undo the effect of +registering a port with the +parport_unregister_port function, and device +drivers are notified using the detach +callback. + +Device drivers can undo the effect of registering themselves +with the parport_unregister_driver +function. + + + + + + +The IEEE 1284.3 API + +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. + +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 +parport code in Linux. + +At initialisation of the parallel port bus, 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. + +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. + +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! + +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. + + + + + + + + + + + + + + + +Each device in the system is numbered at initialisation (by +parport_daisy_init). You can convert between +this device number and its co-ordinates with +parport_device_num and +parport_device_coords. + + + int parport_device_num + int parport + int mux + int daisy + + + + int parport_device_coords + int devnum + int *parport + int *mux + int *daisy + + +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 -1 is +used to indicate these cases. + +Two functions are provided for finding devices based on their +IEEE 1284 Device ID: parport_find_device and +parport_find_class. + + + int parport_find_device + const char *mfg + const char *mdl + int from + + + + int parport_find_class + parport_device_class cls + int from + + +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 -1 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 +from+1, in other words). + + + + + + +Device driver's view + + + + + + + + + +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 parport interface to find +parallel ports, use them, and share them with other device +drivers. + +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. + +The interactions between the device driver and the +parport layer are as follows. First, the device +driver registers its existence with parport, 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 +parport 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. + +So, the first thing for the device driver to do is tell +parport that it wants to know what parallel ports +are on the system. To do this, it uses the +parport_register_device function: + + + + + + int parport_register_driver + struct parport_driver *driver + + +In other words, the device driver passes pointers to a couple of +functions to parport, and +parport calls attach for +each port that's detected (and detach for each +port that disappears -- yes, this can happen). + +The next thing that happens is that the device driver tells +parport that it thinks there's a device on the +port that it can drive. This typically will happen in the driver's +attach function, and is done with +parport_register_device: + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + void (*kf) + void * + void (*irq_func) + int, void *, struct pt_regs * + int flags + void *handle + + +The port comes from the parameter supplied +to the attach function when it is called, or +alternatively can be found from the list of detected parallel ports +directly with the (now deprecated) +parport_enumerate function. + +The next three parameters, pf, +kf, and irq_func, are +more function pointers. These callback functions get called under +various circumstances, and are always given the +handle as one of their parameters. + +The preemption callback, pf, 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 parport_release. If +pf 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. + +The kick callback, kf, is +called when the port can be claimed for exclusive access; that is, +parport_claim is guaranteed to succeed inside the +kick callback. If the driver wants to claim the port +it should do so; otherwise, it need not take any action. + +The irq_func 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 +request_irq; 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 irq_func function is called. + +None of the callback functions are allowed to block. + +The flags are for telling +parport any requirements or hints that are +useful. The only useful value here (other than +0, which is the usual value) is +PARPORT_DEV_EXCL. The point of that flag is to +request exclusive access at all times---once a driver has successfully +called parport_register_device 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). + +The PARPORT_DEV_EXCL 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! + +Devices can also be registered by device drivers based on their +device numbers (the same device numbers as in the previous +section). + +The parport_open function is similar to +parport_register_device, and +parport_close is the equivalent of +parport_unregister_device. The difference is +that parport_open takes a device number rather +than a pointer to a struct parport. + + + struct pardevice *parport_open + int devnum + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + void parport_close + struct pardevice *dev + + + + struct pardevice *parport_register_device + struct parport *port + const char *name + int (*pf) + void * + int (*kf) + void * + int (*irqf) + int, void *, struct pt_regs * + int flags + void *handle + + + + void parport_unregister_device + struct pardevice *dev + + +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: + + + + +Once your device driver has registered its device and been +handed a pointer to a struct pardevice, 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. + + + int parport_claim + struct pardevice *dev + + + + int parport_claim_or_block + struct pardevice *dev + + + + void parport_release + struct pardevice *dev + + +To claim access to the port, use +parport_claim or +parport_claim_or_block. The first of these will +not block, and so can be used from interrupt context. If +parport_claim 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. + +The other function, parport_claim_or_block, +will block if necessary to wait for the port to be free. If it slept, +it returns 1; if it succeeded without needing to +sleep it returns 0. If it fails it will return a +negative error code. + +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 parport_release 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. + +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. + +The parport_yield and +parport_yield_blocking 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 timeslice. The default timeslice is half a +second, but it can be adjusted via a /proc +entry. + + + int parport_yield + struct pardevice *dev + + + + int parport_yield_blocking + struct pardevice *dev + + +The first of these, parport_yield, will not +block but as a result may fail. The return value for +parport_yield is the same as for +parport_claim. The blocking version, +parport_yield_blocking, has the same return code +as parport_claim_or_block. + +Once the port has been claimed, the device driver can use the +functions in the struct parport_operations +pointer in the struct parport it has a +pointer to. For example: + + +ops->write_data (port, d); +]]> + +Some of these operations have shortcuts. For +instance, parport_write_data 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 port and +ops). + + + + +Port drivers + + + +To recap, then: + + + + + +The device driver registers itself with parport. + + + + + +A low-level driver finds a parallel port and registers it with +parport (these first two things can happen in +either order). This registration creates a struct +parport which is linked onto a list of known ports. + + + + + +parport calls the attach +function of each registered device driver, passing it the pointer to +the new struct parport. + + + + + +The device driver gets a handle from parport, for +use with +parport_claim/release. This +handle takes the form of a pointer to a struct +pardevice, representing a particular device on the +parallel port, and is acquired using +parport_register_device. + + + + + +The device driver claims the port using +parport_claim (or +function_claim_or_block). + + + + + +Then it goes ahead and uses the port. When finished it releases the +port. + + + + + +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 struct +parport_operations). + + + + + + +A more complete description of which operation is supposed to do +what is available in +Documentation/parport-lowlevel.txt. + + + + +The printer driver + + + + +The printer driver, lp is a character +special device driver and a parport client. As a +character special device driver it registers a struct +file_operations using +register_chrdev, with pointers filled in for +write, ioctl, +open and +release. As a client of +parport, it registers a struct +parport_driver using +parport_register_driver, so that +parport knows to call +lp_attach when a new parallel port is discovered +(and lp_detach when it goes away). + +The parallel port console functionality is also implemented in +lp.c, but that won't be covered here (it's quite +simple though). + +The initialisation of the driver is quite easy to understand +(see lp_init). The lp_table +is an array of structures that contain information about a specific +device (the struct pardevice associated with +it, for example). That array is initialised to sensible values first +of all. + +Next, the printer driver calls +register_chrdev passing it a pointer to +lp_fops, which contains function pointers for the +printer driver's implementation of open, +write, and so on. This part is the same as for +any character special device driver. + +After successfully registering itself as a character special +device driver, the printer driver registers itself as a +parport client using +parport_register_driver. It passes a pointer to +this structure: + + + + +The lp_detach function is not very +interesting (it does nothing); the interesting bit is +lp_attach. 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 auto, +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. + +For each port that the printer driver wants to use (see +lp_register), it calls +parport_register_device and stores the resulting +struct pardevice pointer in the +lp_table. If the user told it to do so, it then +resets the printer. + +The other interesting piece of the printer driver, from the +point of view of parport, is +lp_write. In this function, the user space +process has data that it wants printed, and the printer driver hands +it off to the parport code to deal with. + +The parport functions it uses that we have +not seen yet are parport_negotiate, +parport_set_timeout, and +parport_write. These functions are part of the +IEEE 1284 implementation. + +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. + +The particular transfer mode that the printer driver wants to +use is named in IEEE 1284 as compatibility mode, and +the function to request a particular mode is called +parport_negotiate. + + + int parport_negotiate + struct parport *port + int mode + + +The modes parameter is a symbolic +constant representing an IEEE 1284 mode; in this instance, it is +IEEE1284_MODE_COMPAT. (Compatibility mode is +slightly different to the other modes---rather than being specifically +requested, it is the default until another mode is selected.) + +Back to lp_write then. First, access to +the parallel port is secured with +parport_claim_or_block. 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 parport_negotiate. + +The main work is done in the write-loop. In particular, the +line that hands the data over to parport +reads: + + + + +The parport_write 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: + + + ssize_t parport_write + struct parport *port + const void *buf + size_t len + + + + ssize_t parport_read + struct parport *port + void *buf + size_t len + + +(parport_read does what it sounds like, but +only works for modes in which reverse transfer is possible. Of +course, parport_write only works in modes in +which forward transfer is possible, too.) + +The buf pointer should be to kernel space +memory, and obviously the len parameter +specifies the amount of data to transfer. + +In fact what parport_write does is call the +appropriate block transfer function from the struct +parport_operations: + + + + +The transfer code in parport will tolerate +a data transfer stall only for so long, and this timeout can be +specified with parport_set_timeout, which returns +the previous timeout: + + + long parport_set_timeout + struct pardevice *dev + long inactivity + + +This timeout is specific to the device, and is restored on +parport_claim. + + + + +User-level device drivers + + + +Introduction to ppdev + +The printer is accessible through /dev/lp0; +in the same way, the parallel port itself is accessible through +/dev/parport0. The difference is in the level of +control that you have over the wires in the parallel port +cable. + +With the printer driver, a user-space program (such as the +printer spooler) can send bytes in printer protocol. +Briefly, this means that for each byte, the eight data lines are set +up, then a strobe line tells the printer to look at the +data lines, and the printer sets an acknowledgement +line to say that it got the byte. The printer driver also allows the +user-space program to read bytes in nibble mode, 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). + +In contrast, the ppdev driver (accessed via +/dev/parport0) allows you to: + + + + + +examine status lines, + + + + + +set control lines, + + + + + +set/examine data lines (and control the direction of the data lines), + + + + + +wait for an interrupt (triggered by one of the status lines), + + + + + +find out how many new interrupts have occurred, + + + + + +set up a response to an interrupt, + + + + + +use IEEE 1284 negotiation (for telling peripheral which transfer mode, +to use) + + + + + +transfer data using a specified IEEE 1284 mode. + + + + + + + + +User-level or kernel-level driver? + +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. + +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. + + + + +Programming interface + +The ppdev interface is largely the same as +that of other character special devices, in that it supports +open, close, +read, write, and +ioctl. + + +Starting and stopping: <function>open</function> and +<function>close</function> + +The device node /dev/parport0 represents +any device that is connected to parport0, 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 /dev/parport0 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. + + + +Control: <function>ioctl</function> + +Most of the control is done, naturally enough, via the +ioctl call. Using ioctl, +the user-land driver can control both the ppdev +driver in the kernel and the physical parallel port itself. The +ioctl call takes as parameters a file descriptor +(the one returned from opening the device node), a command, and +optionally (a pointer to) some data. + + +PPCLAIM + + +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 ppdev driver and not the port, such as +PPSETMODE; they can be performed while access to +the port is not claimed. + + + +PPEXCL + + +Instructs the kernel driver to forbid any sharing of the port +with other drivers, i.e. it requests exclusivity. The +PPEXCL command is only valid when the port is not +already claimed for use, and it may mean that the next +PPCLAIM ioctl will fail: +some other driver may already have registered itself on that +port. + +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). + +Note that the PPEXCL +ioctl doesn't actually claim the port there and +then---action is deferred until the PPCLAIM +ioctl is performed. + + + +PPRELEASE + + +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). + + + +PPYIELD + + +Yields the port to another driver. This +ioctl 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. + +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 +PPEXCL). + + + +PPNEGOT + + +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. + +An IEEE 1284 compliant device will start out in compatibility +mode, and then the host can negotiate to another mode (such as +ECP). + +The ioctl parameter should be a pointer to +an int; values for this are in +parport.h and include: + + +IEEE1284_MODE_COMPAT +IEEE1284_MODE_NIBBLE +IEEE1284_MODE_BYTE +IEEE1284_MODE_EPP +IEEE1284_MODE_ECP + + +The PPNEGOT ioctl +actually does two things: it performs the on-the-wire negotiation, and +it sets the behaviour of subsequent +read/write calls so that +they use that mode (but see PPSETMODE). + + + +PPSETMODE + + +Sets which IEEE 1284 protocol to use for the +read and write calls. + +The ioctl parameter should be a pointer to +an int. + + + +PPGETTIME + + +Retrieves the time-out value. The read and +write calls will time out if the peripheral +doesn't respond quickly enough. The PPGETTIME +ioctl retrieves the length of time that the +peripheral is allowed to have before giving up. + +The ioctl parameter should be a pointer to +a struct timeval. + + + +PPSETTIME + + +Sets the time-out. The ioctl parameter +should be a pointer to a struct +timeval. + + + +PPWCONTROL + + +Sets the control lines. The ioctl +parameter is a pointer to an unsigned char, the bitwise +OR of the control line values in +parport.h. + + + +PPRCONTROL + + +Returns the last value written to the control register, in the +form of an unsigned char: each bit corresponds to a +control line (although some are unused). The +ioctl parameter should be a pointer to an +unsigned char. + +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. + +The control lines bits are defined in +parport.h: + + +PARPORT_CONTROL_STROBE +PARPORT_CONTROL_AUTOFD +PARPORT_CONTROL_SELECT +PARPORT_CONTROL_INIT + + + + +PPFCONTROL + + +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 +PPRCONTROL, make the change, and then use +PPWCONTROL. 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 +PPRCONTROL, ppdev must +remember the state of the control lines anyway. + +The PPFCONTROL ioctl +is for frobbing control lines, and is like +PPWCONTROL but acts on a restricted set of +control lines. The ioctl parameter is a pointer +to a struct ppdev_frob_struct: + + + + + +The mask and +val fields are bitwise ORs of control line +names (such as in PPWCONTROL). The operation +performed by PPFCONTROL is: + + + + + +In other words, the signals named in +mask are set to the values in +val. + + + +PPRSTATUS + + +Returns an unsigned char containing bits set for +each status line that is set (for instance, +PARPORT_STATUS_BUSY). The +ioctl parameter should be a pointer to an +unsigned char. + + + +PPDATADIR + + +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.) + +This is only needed in conjunction with +PPWDATA or PPRDATA. + +The ioctl parameter is a pointer to an +int. If the int is zero, the drivers are +turned on (forward direction); if non-zero, the drivers are turned off +(reverse direction). + + + +PPWDATA + + +Sets the data lines (if in forward mode). The +ioctl parameter is a pointer to an unsigned +char. + + + +PPRDATA + + +Reads the data lines (if in reverse mode). The +ioctl parameter is a pointer to an unsigned +char. + + + +PPCLRIRQ + + +Clears the interrupt count. The ppdev +driver keeps a count of interrupts as they are triggered. +PPCLRIRQ stores this count in an +int, a pointer to which is passed in as the +ioctl parameter. + +In addition, the interrupt count is reset to zero. + + + +PPWCTLONIRQ + + +Set a trigger response. Afterwards when an interrupt is +triggered, the interrupt handler will set the control lines as +requested. The ioctl parameter is a pointer to +an unsigned char, which is interpreted in the same way as +for PPWCONTROL. + +The reason for this ioctl is simply speed. +Without this ioctl, responding to an interrupt +would start in the interrupt handler, switch context to the user-land +driver via poll or select, +and then switch context back to the kernel in order to handle +PPWCONTROL. Doing the whole lot in the interrupt +handler is a lot faster. + + + + + + + + + + +Transferring data: <function>read</function> and +<function>write</function> + +Transferring data using read and +write is straightforward. The data is +transferring using the current IEEE 1284 mode (see the +PPSETMODE ioctl). For modes +which can only transfer data in one direction, only the appropriate +function will work, of course. + + + +Waiting for events: <function>poll</function> and +<function>select</function> + +The ppdev driver provides user-land device +drivers with the ability to wait for interrupts, and this is done +using poll (and select, +which is implemented in terms of poll). + +When a user-land device driver wants to wait for an interrupt, +it sleeps with poll. When the interrupt arrives, +ppdev wakes it up (with a read +event, although strictly speaking there is nothing to actually +read). + + + + + + +Examples + +Presented here are two demonstrations of how to write a simple +printer driver for ppdev. Firstly we will use +the write function, and after that we will drive +the control and data lines directly. + +The first thing to do is to actually open the device. + + + +Here name should be something along the lines +of "/dev/parport0". (If you don't have any +/dev/parport files, you can make them with +mknod; they are character special device nodes with +major 99.) + +In order to do anything with the port we need to claim access to +it. + + + +Our printer driver will copy its input (from +stdin) 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 compatibility mode. + + 0) { + int written = write_printer (fd, ptr, got); + + if (written < 0) { + perror ("write"); + close (fd); + return 1; + } + + ptr += written; + got -= written; + } + } +]]> + +The write_printer 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 +write_printer: + + + +We hand the data to the kernel-level driver (using +write) and it handles the printer +protocol. + +Now let's do it the hard way! In this particular example there +is no practical reason to do anything other than just call +write, 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. + +So, here is the alternative implementation of +write_printer (for brevity, error checking has +been omitted): + + + +To show a bit more of the ppdev interface, +here is a small piece of code that is intended to mimic the printer's +side of printer protocol. + + 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); + } +]]> + + + + +
\ No newline at end of file diff --git a/Documentation/DocBook/videobook.tmpl b/Documentation/DocBook/videobook.tmpl index e3678c04b22c..c152abf31347 100644 --- a/Documentation/DocBook/videobook.tmpl +++ b/Documentation/DocBook/videobook.tmpl @@ -56,88 +56,88 @@ Introduction - 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. - 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. - Radio Devices + Radio Devices - 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. - 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. Registering Radio Devices - 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. 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 */ }; - 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. - 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. - 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 + 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. - 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. - 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. - 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. @@ -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(&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(&my_radio, VFL_TYPE_RADIO)==-1) + return -EINVAL; + request_region(io, MY_IO_SIZE, "myradio"); + return 0; } - 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. - - - 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. - - - 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). - - - - We are most definitely a radio. - - - 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. + + + 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. + + + The types available are + + Device Types + + + + 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). + + + + +
+ + We are most definitely a radio. + + + 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.
Opening And Closing The Radio - 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. @@ -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; } - 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. static int radio_close(struct video_device *dev) { - users--; - MOD_DEC_USE_COUNT; + users--; + MOD_DEC_USE_COUNT; } - 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. - 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. The Ioctl Interface - 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. 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, &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, &v, sizeof(v))) + return -EFAULT; + return 0; + } - 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 - - - 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. - - - - 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. - - - 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 + + struct video_capability fields + + + + 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,minheight<>The smallest size the card is capable of capturing + images in. We set these to zero. Radios do not + capture pictures + + maxwidth,maxheight<>The largest image size the card is capable of + 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. + + + +
+ + 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. + + + 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. - case VIDIOCGTUNER: - { - struct video_tuner v; - if(copy_from_user(&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(&v, arg, sizeof(v))!=0) - return -EFAULT; - return 0; - } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&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(&v, arg, sizeof(v))!=0) + return -EFAULT; + return 0; + } - 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. - - - 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. - - - The video_tuner structure has the following fields - - - 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. - - - - 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. - - - 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. - - - 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. - - - 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. + + + 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. + + + The video_tuner structure has the following fields + + struct video_tuner fields + + + + int tunerThe 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 + Tuner capability flags + + + u16 modeThe current reception mode + + + u16 signalThe 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. + + + + +
+ + struct video_tuner flags + + + + VIDEO_TUNER_PALA PAL TV tuner + + VIDEO_TUNER_NTSCAn NTSC (US) TV tuner + + VIDEO_TUNER_SECAMA SECAM (French) 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_NORMThe tuner can set its format + + VIDEO_TUNER_STEREO_ONThe tuner is currently receiving a stereo signal + + + +
+ + struct video_tuner modes + + + + VIDEO_MODE_PAL<>PAL Format + + VIDEO_MODE_NTSC<>NTSC Format (USA) + + VIDEO_MODE_SECAM<>French Format + + VIDEO_MODE_AUTO<>A device that does not need to do + TV format switching + + + +
+ + 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. + + + 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. + + + 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. + + + 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. - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.tuner != 0) - return -EINVAL; - return 0; - } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner != 0) + return -EINVAL; + return 0; + } - 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. - 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. @@ -460,194 +501,226 @@ static unsigned long current_freq; - case VIDIOCGFREQ: - if(copy_to_user(arg, &current_freq, - sizeof(unsigned long)) - return -EFAULT; - return 0; + case VIDIOCGFREQ: + if(copy_to_user(arg, &current_freq, + sizeof(unsigned long)) + return -EFAULT; + return 0; - 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. - case VIDIOCSFREQ: - { - u32 freq; - if(copy_from_user(arg, &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, &freq, + sizeof(unsigned long))!=0) + return -EFAULT; + if(hardware_set_freq(freq)<0) + return -EINVAL; + current_freq = freq; + return 0; + } - 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. - 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. - There are two ioctls with audio we need to support + There are two ioctls with audio we need to support static int current_volume=0; - case VIDIOCGAUDIO: - { - struct video_audio v; - if(copy_from_user(&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. &v, sizeof(v))) - return -EFAULT; - return 0; - } + case VIDIOCGAUDIO: + { + struct video_audio v; + if(copy_from_user(&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. &v, sizeof(v))) + return -EFAULT; + return 0; + } - 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. - - - Then we fill in the video_audio structure. This has the following format - - - - 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. - - - - Having filled in the structure we copy it back to user space. - - - 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. + + + Then we fill in the video_audio structure. This has the following format + + struct video_audio fields + + + + 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 + + + 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 + + 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. + + + +
+ + struct video_audio flags + + + + 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 + + + +
+ + struct video_audio modes + + + + 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) + + + +
+ + Having filled in the structure we copy it back to user space. + + + 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. - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&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(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + current_volume = v/16384; + hardware_set_volume(current_volume); + return 0; + } - 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 - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - if(v.audio) - return -EINVAL; - current_volume = v/16384; - if(v.flags&VIDEO_AUDIO_MUTE) - hardware_set_volume(0); - else - hardware_set_volume(current_volume); - current_muted = v.flags&VIDEO_AUDIO_MUTE; - return 0; - } + case VIDIOCSAUDIO: + { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + current_volume = v/16384; + if(v.flags&VIDEO_AUDIO_MUTE) + hardware_set_volume(0); + else + hardware_set_volume(current_volume); + current_muted = v.flags & + VIDEO_AUDIO_MUTE; + return 0; + } - 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. - 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 - } - return -ENOIOCTLCMD; + } + return -ENOIOCTLCMD; } - 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.
Module Wrapper - 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. @@ -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(&my_radio); - release_region(io, MY_IO_SIZE); + video_unregister_device(&my_radio); + release_region(io, MY_IO_SIZE); } #endif - 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. - 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. - 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).
- Video Capture Devices + Video Capture Devices Video Capture Device Types - 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. - 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 . - 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. - 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. - 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. - 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. Registering Video Capture Devices - This time we need to add more functions for our camera device. + This time we need to add more functions for our camera device. 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 */ }; - 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. - - - 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. - - - 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. + + + We use the extra video capability flags that did not apply to the + radio interface. The video related flags are + + Capture Capabilities + + + +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. + + + 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(). @@ -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(&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(&my_camera, + VFL_TYPE_GRABBER)==-1) + return -EINVAL; + request_region(io, MY_IO_SIZE, "mycamera"); + return 0; } - 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.
@@ -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)<0) - return -EBUSY; - users++; - MOD_INC_USE_COUNT; - return 0; + if(users) + return -EBUSY; + if(request_irq(irq, camera_irq, 0, "camera", dev)<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; } - 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. Interrupt Handling - 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. - 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. @@ -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(&capture_wait); + capture_ready=1; + wake_up_interruptible(&capture_wait); } - 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. - 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. -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, &capture_wait, wait); - if(capture_read) - return POLLIN|POLLRDNORM; - return 0; + poll_wait(file, &capture_wait, wait); + if(capture_read) + return POLLIN|POLLRDNORM; + return 0; } - 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. @@ -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(&capture_wait, &wait); - - while(!capture_ready) - { - if(file->flags&O_NDELAY) - { - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; - return -EWOULDBLOCK; - } - if(signal_pending(current)) - { - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; - return -ERESTARTSYS; - } - schedule(); - current->state = TASK_INTERRUPTIBLE; - } - remove_wait_queue(&capture_wait, &wait); - current->state = TASK_RUNNING; + struct wait_queue wait = { current, NULL }; + u8 *ptr; + int len; + int i; + + add_wait_queue(&capture_wait, &wait); + + while(!capture_ready) + { + if(file->flags&O_NDELAY) + { + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; + return -EWOULDBLOCK; + } + if(signal_pending(current)) + { + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; + return -ERESTARTSYS; + } + schedule(); + current->state = TASK_INTERRUPTIBLE; + } + remove_wait_queue(&capture_wait, &wait); + current->state = TASK_RUNNING; - 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. - 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. - 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. - 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<len; i++) - { - put_user(inb(io+IMAGE_DATA), ptr); - ptr++; - } + for(i=0; i<len; i++) + { + put_user(inb(io+IMAGE_DATA), ptr); + ptr++; + } - hardware_restart_capture(); - - return i; + hardware_restart_capture(); + + return i; } - 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. - 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. Video Ioctl Handling - 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. @@ -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, &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, &v, sizeof(v))) + return -EFAULT; + return 0; + } - 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. - 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. - 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. - + - case VIDIOCGCHAN: - { - struct video_channel v; - if(copy_from_user(&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(&v, arg, sizeof(v))) - return -EFAULT; - return 0; - } + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&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(&v, arg, sizeof(v))) + return -EFAULT; + return 0; + } - 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. - - - 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. + + + 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 + + struct video_channel fields + + + + + 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<>Channel properties + + type<>Input type + + norm<>The current television encoding being used + if relevant for this channel. + + + + +
+ struct video_channel flags + + + + VIDEO_VC_TUNER<>Channel has a tuner. + + VIDEO_VC_AUDIO<>Channel has audio. + + + +
+ struct video_channel types + + + + VIDEO_TYPE_TV<>Television input. + + VIDEO_TYPE_CAMERA<>Fixed camera input. + + 0<>Type is unknown. + + + +
+ struct video_channel norms + + + + 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. - case VIDIOCSCHAN: - { - struct video_channel v; - if(copy_from_user(&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(&v, arg, sizeof(v))) + return -EFAULT; + if(v.channel != 0) + return -EINVAL; + if(v.norm != VIDEO_MODE_AUTO) + return -EINVAL; + return 0; + } - 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. - 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. - 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(&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(&v, arg, + sizeof(v))) + return -EFAULT; + return 0; + } - 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. - - - 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. - - - 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. + + + 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 + + Framebuffer Encodings + + + + 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 applications. + + + 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. - case VIDIOCSPICT: - { - struct video_picture v; - if(copy_from_user(&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(&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; + } - 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. - 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. - 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. static struct video_buffer capture_fb; - case VIDIOCGFBUF: - { - if(copy_to_user(arg, &capture_fb, sizeof(capture_fb))) - return -EFAULT; - return 0; - - } + case VIDIOCGFBUF: + { + if(copy_to_user(arg, &capture_fb, + sizeof(capture_fb))) + return -EFAULT; + return 0; + + } - 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. - 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(&v, arg, sizeof(v))) - return -EFAULT; - if(v.width!=320 && v.width!=640) - return -EINVAL; - if(v.height!=200 && v.height!=240 && v.height!=400 - && v.height !=480) - return -EINVAL; - memcpy(&capture_fb, &v, sizeof(v)); - hardware_set_fb(&v); - return 0; - } + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.width!=320 && v.width!=640) + return -EINVAL; + if(v.height!=200 && v.height!=240 + && v.height!=400 + && v.height !=480) + return -EINVAL; + memcpy(&capture_fb, &v, sizeof(v)); + hardware_set_fb(&v); + return 0; + } - 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. - 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. - 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. - 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. + + struct video_window fields + + + + 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 + + video_clip 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. - 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. - case VIDIOCGWIN: - { - if(copy_to_user(arg, &capture_win, sizeof(capture_win))) - return -EFAULT; - return 0; - } - - - case VIDIOCSWIN: - { - struct video_window v; - if(copy_from_user(&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(&capture_win, &v, sizeof(v)); - capture_w = v.width; - capture_h = v.height; - return 0; - } + case VIDIOCGWIN: + { + if(copy_to_user(arg, &capture_win, + sizeof(capture_win))) + return -EFAULT; + return 0; + } + + + case VIDIOCSWIN: + { + struct video_window v; + if(copy_from_user(&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(&capture_win, &v, sizeof(v)); + capture_w = v.width; + capture_h = v.height; + return 0; + } - 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. - 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. - 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; + } - 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. - default: - return -ENOIOCTLCMD; - } + default: + return -ENOIOCTLCMD; + } } - 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.
Other Functionality - 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.
@@ -1478,21 +1637,21 @@ static struct video_buffer capture_fb; Multiple Opens - 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. API Deficiences - 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. - + diff --git a/MAINTAINERS b/MAINTAINERS index 04bac916a64a..6762ce048d95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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 diff --git a/Makefile b/Makefile index 784688b9b945..c8bf2bcd3415 100644 --- 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 diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 1381fb84252a..56500e466105 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -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 diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 120c861e7997..d2ba2fd9c02f 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -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; diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index cc9c7eafe684..1d6203f658fa 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -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. The starting (base) address of the region. 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. The register. If this is less than 0 then <> and <> must diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index b6cc30fed8f8..df5f2a654990 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -35,8 +35,6 @@ #include #include -#include - #define DLINFO_ITEMS 12 #include diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 1d6f208f66fd..262f6afdd648 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -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. diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index 8746958d738b..42c072164f8d 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -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 diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index b8fb7cfbd679..d3a3814a841e 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -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) diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 462d4c12925d..d4ecb0f4fc66 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -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); +} diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 9673cdd36e9b..f7f5964e9012 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -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. diff --git a/arch/sparc64/kernel/systbls.S b/arch/sparc64/kernel/systbls.S index 1f7ab3fef35d..0a0edbf82912 100644 --- a/arch/sparc64/kernel/systbls.S +++ b/arch/sparc64/kernel/systbls.S @@ -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 diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index 9ac4027e4b9b..b77a27236dd5 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -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) diff --git a/arch/sparc64/solaris/systbl.S b/arch/sparc64/solaris/systbl.S index 17562bafd282..ca78499b1e5f 100644 --- a/arch/sparc64/solaris/systbl.S +++ b/arch/sparc64/solaris/systbl.S @@ -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 */ diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c index 865681956e32..ebf0671f4420 100644 --- a/drivers/char/drm/auth.c +++ b/drivers/char/drm/auth.c @@ -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 * */ diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c index a71d6dde98ac..1bb7bd6124a1 100644 --- a/drivers/char/drm/bufs.c +++ b/drivers/char/drm/bufs.c @@ -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 @@ -24,13 +23,12 @@ * 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 * */ #define __NO_VERSION__ -#include #include "drmP.h" #include "linux/un.h" diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c index d7f8bdf2bcc4..a8919d83d9b0 100644 --- a/drivers/char/drm/context.c +++ b/drivers/char/drm/context.c @@ -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 * */ diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c index ea08a859e376..0ec14ede5d5b 100644 --- a/drivers/char/drm/dma.c +++ b/drivers/char/drm/dma.c @@ -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 * */ diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c index c26953c1d8e2..19e5da3b7baf 100644 --- a/drivers/char/drm/drawable.c +++ b/drivers/char/drm/drawable.c @@ -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 * */ diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h index 320db51ebe03..fe0f8defec23 100644 --- a/drivers/char/drm/drm.h +++ b/drivers/char/drm/drm.h @@ -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 * * Acknowledgements: * Dec 1999, Richard Henderson , move to generic cmpxchg. diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h index fce2df7ec942..3a371c23d1d4 100644 --- a/drivers/char/drm/drmP.h +++ b/drivers/char/drm/drmP.h @@ -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 * */ @@ -50,6 +49,10 @@ #ifdef CONFIG_MTRR #include #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include +#include +#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 diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c index 24b17356bdd4..a823db35638e 100644 --- a/drivers/char/drm/fops.c +++ b/drivers/char/drm/fops.c @@ -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 + * Daryll Strauss * */ @@ -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; +} diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c index 3b1592180e0f..1f8c0a7de90e 100644 --- a/drivers/char/drm/gamma_dma.c +++ b/drivers/char/drm/gamma_dma.c @@ -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 * */ diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c index 028772f26af6..6df4440f37d0 100644 --- a/drivers/char/drm/gamma_drv.c +++ b/drivers/char/drm/gamma_drv.c @@ -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 @@ -24,12 +23,11 @@ * 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 * */ -#include #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 = { diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h index 15e77dca9ee6..a87655cb9753 100644 --- a/drivers/char/drm/gamma_drv.h +++ b/drivers/char/drm/gamma_drv.h @@ -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 * */ diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c index f416a99afe72..7a0115e86519 100644 --- a/drivers/char/drm/init.c +++ b/drivers/char/drm/init.c @@ -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 * */ diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c index 886ef661c59b..13bb606595b5 100644 --- a/drivers/char/drm/ioctl.c +++ b/drivers/char/drm/ioctl.c @@ -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 * */ diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c index b84561f2e519..212ed18edecb 100644 --- a/drivers/char/drm/lists.c +++ b/drivers/char/drm/lists.c @@ -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 * */ @@ -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"); diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c index e8c1eff1017c..2523eb21ad54 100644 --- a/drivers/char/drm/lock.c +++ b/drivers/char/drm/lock.c @@ -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 * */ diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c index af8d510b583c..a778a153911d 100644 --- a/drivers/char/drm/memory.c +++ b/drivers/char/drm/memory.c @@ -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 * */ diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c index 33a5b20e817a..4d5d1a964b4f 100644 --- a/drivers/char/drm/proc.c +++ b/drivers/char/drm/proc.c @@ -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 * */ @@ -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; diff --git a/drivers/char/drm/tdfx_context.c b/drivers/char/drm/tdfx_context.c index 0c3c541d056a..22bf59ff39fd 100644 --- a/drivers/char/drm/tdfx_context.c +++ b/drivers/char/drm/tdfx_context.c @@ -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 * */ diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c index f56e2af95bc9..82b2ac9a2588 100644 --- a/drivers/char/drm/tdfx_drv.c +++ b/drivers/char/drm/tdfx_drv.c @@ -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 @@ -23,17 +22,15 @@ * 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 + * Daryll Strauss * */ -#include #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; } diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h index bdff05ee19c3..4c0c3282b24d 100644 --- a/drivers/char/drm/tdfx_drv.h +++ b/drivers/char/drm/tdfx_drv.h @@ -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 + * Daryll Strauss * */ diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c index d649a6e75d80..b4c4c5bbf0a2 100644 --- a/drivers/char/drm/vm.c +++ b/drivers/char/drm/vm.c @@ -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 * */ diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 870a8b6ffc49..212525df759d 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -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) diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 6bc1c779015e..1337213e2c6b 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -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; diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 59853da1f16f..4336009fc1a2 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -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 " diff --git a/drivers/char/serial.c b/drivers/char/serial.c index 7e92c545be47..2d160acfd8ca 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -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; diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index bee6a4c6c9d7..58eb2411d82a 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -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; diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 15850a9f70ec..3cbf0e5e4946 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -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 */ diff --git a/drivers/net/shaper.c b/drivers/net/shaper.c index 89cbb0e9e94b..9190b5a87818 100644 --- a/drivers/net/shaper.c +++ b/drivers/net/shaper.c @@ -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; diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index 7429ec38bd2f..8d01df21e577 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,24 @@ +2000-03-13 + + * 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 * parport_pc.c (decode_winbond): Use correct 83877ATF chip ID. diff --git a/drivers/parport/Config.in b/drivers/parport/Config.in index d4222f353f17..1e486c6b3504 100644 --- a/drivers/parport/Config.in +++ b/drivers/parport/Config.in @@ -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 diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 86fc6f874088..7117c847ed9a 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -60,6 +60,8 @@ #include #include +#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) && (ibase) && (i 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) diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index 849ce76c9fac..5a74bc2df9a5 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -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 diff --git a/drivers/scsi/eata_dma_proc.c b/drivers/scsi/eata_dma_proc.c index 8768db48c0b0..7961483e171b 100644 --- a/drivers/scsi/eata_dma_proc.c +++ b/drivers/scsi/eata_dma_proc.c @@ -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); } diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index f1b82a5a17bc..685925e7defd 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -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 /* diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a6e2c14d9353..21cb989ca8f8 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -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"); diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 7a073f86efb3..677d21410842 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -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; diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 9f36d08c536d..4bed377edbb9 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -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); } diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index abdef85ef8b2..a211980a5a6f 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -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); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 8c7b448e11e1..74ac6d245205 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -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); } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 4060872bf2e5..a43f2988c8cb 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -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 diff --git a/drivers/scsi/scsi_syms.c b/drivers/scsi/scsi_syms.c index f6e8939a6bd9..94820f5db8d3 100644 --- a/drivers/scsi/scsi_syms.c +++ b/drivers/scsi/scsi_syms.c @@ -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); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index bde5626a260b..584a84905dcc 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -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; diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 781cde85af84..dc6712247482 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -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; diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index 9d61157ef8a6..217bdb605575 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -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); diff --git a/drivers/sound/sound_firmware.c b/drivers/sound/sound_firmware.c index c446e98e01c4..393e6a780d26 100644 --- a/drivers/sound/sound_firmware.c +++ b/drivers/sound/sound_firmware.c @@ -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; diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index ea90ed81ca67..f3b008f0c27f 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -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 diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index a210b51061c9..fbb7562dcac3 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -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 diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c index f5fc141404cb..8b81e06c0c3f 100644 --- a/drivers/usb/dsbr100.c +++ b/drivers/usb/dsbr100.c @@ -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 @@ -30,20 +33,27 @@ History: - Version 0.3: - Brad Hards : Trivial fix for usb.h, more GPL - + Version 0.21: + Markus Demleitner : + Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + Version 0.2: Brad Hards : Fixes to make it work as non-module - Markus Demleitner : Copyright clarification + Markus: Copyright clarification - Version 0.01: Markus Demleitner - initial release + Version 0.01: Markus: initial release */ #include + +#if CONFIG_MODVERSIONS==1 +#define MODVERSIONS +#include +#endif + #include #include #include @@ -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. +*/ diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c index d3761bf52083..2d5de6f73b22 100644 --- a/drivers/usb/pegasus.c +++ b/drivers/usb/pegasus.c @@ -1,578 +1,474 @@ /* -** ** 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 #include #include #include #include -#include - #include #include +#include -#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 "); -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); diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index c3b4ebc2e42d..480c6252df27 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -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 */ diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index 62d4e772e195..9f4c45e96335 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -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 */ diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c index 0e2ab20a4a6c..956e80a0abc6 100644 --- a/drivers/usb/usb-storage.c +++ b/drivers/usb/usb-storage.c @@ -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(¤t->sigmask_lock); signr = dequeue_signal(¤t->blocked, &info); spin_unlock_irq(¤t->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) ; } diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h index 80a03f3c91fa..06e6d958b319 100644 --- a/drivers/usb/usb-storage.h +++ b/drivers/usb/usb-storage.h @@ -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) 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 */ + diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h index 73d16937a2d2..4eedc418360e 100644 --- a/drivers/usb/usb-uhci-debug.h +++ b/drivers/usb/usb-uhci-debug.h @@ -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) diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index aed79f849fc7..85a5cd476d5d 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -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 @@ -28,9 +28,9 @@ #include #include /* for in_interrupt() */ #include -/* This enables debug printks */ -#define DEBUG -#include +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,44) +#include +#endif #include #include @@ -40,51 +40,56 @@ /* 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 #include "usb-uhci.h" #include "usb-uhci-debug.h" #undef DEBUG #undef dbg #define dbg(format, arg...) do {} while (0) - -#include - +#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 (); } diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h index e74e23bd8122..93173f2c26cd 100644 --- a/drivers/usb/usb-uhci.h +++ b/drivers/usb/usb-uhci.h @@ -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; diff --git a/drivers/video/aty128.h b/drivers/video/aty128.h index 12643937b747..7b1452bf62b6 100644 --- a/drivers/video/aty128.h +++ b/drivers/video/aty128.h @@ -264,6 +264,8 @@ /* 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 diff --git a/drivers/video/aty128fb.c b/drivers/video/aty128fb.c index d7d4116c0a6b..fcc0f8c5cc86 100644 --- a/drivers/video/aty128fb.c +++ b/drivers/video/aty128fb.c @@ -48,13 +48,13 @@ #include #include -#if defined(CONFIG_PPC) +#ifdef CONFIG_PPC #include #include +#include