From 59778e08da1521489686ec3c78972d4bff9414bb Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:33:54 -0500 Subject: [PATCH] Import 2.3.99pre6-1 --- CREDITS | 6 +- Documentation/Changes | 34 +- Documentation/Configure.help | 16 +- Documentation/fb/tgafb.txt | 66 +- Documentation/sound/ALS | 10 +- Documentation/sound/AWE32 | 17 +- Documentation/sound/README.awe | 9 +- Documentation/sound/README.modules | 6 + Documentation/sound/Soundblaster | 13 +- MAINTAINERS | 2 +- Makefile | 2 +- REPORTING-BUGS | 6 +- arch/i386/config.in | 2 +- arch/i386/kernel/apic.c | 271 ++-- arch/i386/kernel/i386_ksyms.c | 7 +- arch/i386/kernel/i8259.c | 2 +- arch/i386/kernel/io_apic.c | 242 ++-- arch/i386/kernel/irq.c | 8 +- arch/i386/kernel/mca.c | 12 +- arch/i386/kernel/mpparse.c | 239 ++-- arch/i386/kernel/mtrr.c | 10 +- arch/i386/kernel/setup.c | 4 + arch/i386/kernel/smp.c | 114 +- arch/i386/kernel/smpboot.c | 135 +- arch/i386/kernel/visws_apic.c | 2 +- arch/ia64/kernel/irq.c | 4 +- arch/ppc/kernel/irq.c | 4 +- arch/sparc/kernel/Makefile | 2 +- arch/sparc/kernel/cpu.c | 2 +- arch/sparc/kernel/ptrace.c | 12 +- arch/sparc/kernel/signal.c | 2 +- arch/sparc/kernel/sys_sunos.c | 27 +- arch/sparc/kernel/systbls.S | 2 +- arch/sparc/lib/Makefile | 2 +- arch/sparc/mm/Makefile | 2 +- arch/sparc/mm/sun4c.c | 2 +- arch/sparc64/defconfig | 36 +- arch/sparc64/kernel/Makefile | 2 +- arch/sparc64/kernel/binfmt_aout32.c | 2 +- arch/sparc64/kernel/pci_sabre.c | 6 +- arch/sparc64/kernel/ptrace.c | 10 +- arch/sparc64/kernel/signal.c | 2 +- arch/sparc64/kernel/signal32.c | 2 +- arch/sparc64/kernel/sys_sparc32.c | 66 +- arch/sparc64/kernel/sys_sunos32.c | 16 +- arch/sparc64/lib/Makefile | 2 +- arch/sparc64/mm/Makefile | 2 +- arch/sparc64/prom/Makefile | 2 +- arch/sparc64/solaris/fs.c | 4 +- arch/sparc64/solaris/misc.c | 4 +- drivers/block/Config.in | 8 +- drivers/block/floppy.c | 2 +- drivers/block/genhd.c | 10 +- drivers/block/ll_rw_blk.c | 3 + drivers/block/lvm.c | 2 +- drivers/block/md.c | 293 +---- drivers/char/Config.in | 6 +- drivers/char/misc.c | 6 +- drivers/char/pc_keyb.c | 10 + drivers/char/planb.c | 332 ++--- drivers/char/planb.h | 15 +- drivers/char/random.c | 7 +- drivers/char/serial.c | 2 +- drivers/char/videodev.c | 12 +- drivers/i2o/Config.in | 4 +- drivers/i2o/README | 19 +- drivers/i2o/README.ioctl | 2 +- drivers/i2o/i2o_block.c | 1218 ++++++++++++----- drivers/i2o/i2o_config.c | 15 +- drivers/i2o/i2o_core.c | 1609 ++++++++++++++++------- drivers/i2o/i2o_lan.c | 1482 +++++++++++++-------- drivers/i2o/i2o_lan.h | 58 +- drivers/i2o/i2o_pci.c | 27 +- drivers/i2o/i2o_proc.c | 227 ++-- drivers/i2o/i2o_scsi.c | 31 +- drivers/isdn/avmb1/b1dma.c | 2 +- drivers/isdn/avmb1/c4.c | 2 +- drivers/net/8390.c | 12 +- drivers/net/appletalk/ltpc.c | 4 +- drivers/net/arcnet/arcnet.c | 2 +- drivers/net/arcnet/com90io.c | 2 +- drivers/net/eepro.c | 2 + drivers/net/hamradio/baycom_epp.c | 2 +- drivers/net/hp100.c | 12 +- drivers/net/pcmcia/ray_cs.c | 7 +- drivers/net/pcmcia/xircom_tulip_cb.c | 2 +- drivers/net/plip.c | 8 +- drivers/net/tulip/tulip.h | 49 + drivers/net/via-rhine.c | 24 +- drivers/net/wan/comx.c | 55 +- drivers/net/wan/comx.h | 2 +- drivers/net/wan/sdladrv.c | 2 +- drivers/net/wan/syncppp.c | 2 +- drivers/net/wan/z85230.c | 6 +- drivers/parport/ChangeLog | 4 + drivers/parport/daisy.c | 114 +- drivers/parport/ieee1284.c | 149 ++- drivers/parport/parport_pc.c | 4 + drivers/pci/pci.c | 1 + drivers/scsi/53c7,8xx.c | 2 +- drivers/scsi/scsi.c | 5 + drivers/scsi/scsi.h | 2 + drivers/scsi/scsi_error.c | 9 +- drivers/scsi/scsi_lib.c | 1 + drivers/scsi/scsi_obsolete.c | 3 + drivers/scsi/sym53c8xx_comm.h | 2 +- drivers/sound/Config.in | 1 + drivers/sound/Makefile | 1 + drivers/sound/ad1848.c | 110 +- drivers/sound/i810_audio.c | 1794 ++++++++++++++++++++++++++ drivers/sound/maestro.c | 696 ++++++---- drivers/sound/sb_card.c | 701 +++++----- drivers/sound/sb_common.c | 3 +- drivers/sound/sound_core.c | 23 +- drivers/sound/soundcard.c | 2 +- drivers/sound/trident.c | 363 +++++- drivers/sound/trident.h | 42 + drivers/video/mdacon.c | 38 +- drivers/video/riva/fbdev.c | 5 +- drivers/video/tgafb.c | 243 ++-- drivers/video/tgafb.h | 33 +- fs/affs/bitmap.c | 29 +- fs/affs/file.c | 273 ++-- fs/affs/inode.c | 7 +- fs/bad_inode.c | 6 +- fs/binfmt_aout.c | 5 +- fs/block_dev.c | 4 +- fs/cramfs/inflate/adler32.c | 2 +- fs/cramfs/inflate/infblock.c | 36 +- fs/cramfs/inflate/infblock.h | 10 +- fs/cramfs/inflate/infcodes.c | 16 +- fs/cramfs/inflate/infcodes.h | 6 +- fs/cramfs/inflate/inffast.c | 14 +- fs/cramfs/inflate/inffast.h | 2 +- fs/cramfs/inflate/inflate.c | 34 +- fs/cramfs/inflate/inftrees.c | 8 +- fs/cramfs/inflate/inftrees.h | 6 +- fs/cramfs/inflate/infutil.c | 4 +- fs/cramfs/inflate/infutil.h | 8 +- fs/cramfs/inflate/uncompr.c | 8 +- fs/cramfs/inflate/zlib.h | 24 +- fs/cramfs/uncompress.c | 14 +- fs/dcache.c | 24 +- fs/fat/file.c | 8 +- fs/inode.c | 16 +- fs/partitions/Config.in | 3 + fs/partitions/Makefile | 5 + fs/partitions/check.c | 22 +- fs/partitions/ibm.c | 122 ++ fs/partitions/ibm.h | 1 + fs/super.c | 20 +- fs/umsdos/inode.c | 22 +- include/asm-i386/apic.h | 33 +- include/asm-i386/apicdef.h | 33 +- include/asm-i386/bugs.h | 29 +- include/asm-i386/mca_dma.h | 12 +- include/asm-i386/mpspec.h | 10 +- include/asm-i386/string.h | 30 + include/asm-sparc/namei.h | 4 +- include/asm-sparc64/namei.h | 4 +- include/asm-sparc64/ttable.h | 4 +- include/linux/dcache.h | 10 +- include/linux/fs.h | 7 +- include/linux/i2o-dev.h | 397 ++++++ include/linux/i2o.h | 512 ++------ include/linux/msdos_fs.h | 1 - include/linux/parport.h | 33 +- include/linux/personality.h | 1 + include/linux/raid/md_u.h | 6 - include/linux/skbuff.h | 62 +- include/linux/string.h | 1 + include/linux/usb.h | 5 - include/linux/videodev.h | 1 + include/net/sock.h | 1 - include/net/tcp.h | 4 +- include/video/fbcon.h | 257 +++- kernel/ksyms.c | 15 +- kernel/pm.c | 24 +- mm/slab.c | 6 +- net/appletalk/ddp.c | 2 +- net/core/dev.c | 67 +- net/core/skbuff.c | 32 +- net/core/sock.c | 26 +- net/decnet/af_decnet.c | 2 +- net/ipv4/icmp.c | 11 +- net/ipv4/ipconfig.c | 2 +- net/ipv4/tcp.c | 15 +- net/ipv4/tcp_input.c | 13 +- net/ipv4/tcp_output.c | 23 +- net/ipv4/tcp_timer.c | 87 +- net/sunrpc/sunrpc_syms.c | 1 + net/unix/af_unix.c | 5 +- scripts/docproc.c | 73 +- scripts/kernel-doc | 169 ++- 194 files changed, 9549 insertions(+), 4640 deletions(-) create mode 100644 drivers/sound/i810_audio.c create mode 100644 fs/partitions/ibm.c create mode 100644 fs/partitions/ibm.h create mode 100644 include/linux/i2o-dev.h diff --git a/CREDITS b/CREDITS index bbab863c581a..d7b04cbde9db 100644 --- a/CREDITS +++ b/CREDITS @@ -268,10 +268,10 @@ E: bir7@leland.Stanford.Edu D: Original author of the Linux networking code N: Anton Blanchard -E: anton@progsoc.uts.edu.au -W: http://www.progsoc.uts.edu.au/~anton/ +E: anton@linuxcare.com +W: http://linuxcare.com.au/anton/ P: 1024/8462A731 4C 55 86 34 44 59 A7 99 2B 97 88 4A 88 9A 0D 97 -D: sun4 port +D: sun4 port, Sparc hacker S: 47 Robert Street S: Marrickville NSW 2204 S: Australia diff --git a/Documentation/Changes b/Documentation/Changes index 73511d74ac20..ce2c77a9be09 100644 --- a/Documentation/Changes +++ b/Documentation/Changes @@ -425,12 +425,6 @@ Psmisc fuser, which comes with psmisc, reads /proc/*/fd/* to do its job. Upgrade psmisc if 2.2 changes to /proc broke the version you're using. -Tunelp -====== - - A new version of tunelp is available which will allow you to enable -"trustirq" mode, improving printing while using IRQ-driven lp ports. - PCI utils ========= @@ -525,6 +519,17 @@ Since 2.3.47 the kernel contains the Logical Volume Manager aka LVM. To use it, you need to install the LVM tools. More information can be found at the home page of the LVM project at http://linux.msede.com/lvm/. +Inline Documentation +==================== +Many of the functions available for modules to use are now documented +with specially-formatted comments near their definitions. These +comments can be combined with the SGML templates in the +Documentation/DocBook directory to make DocBook files, which can then +be combined with DocBook stylesheets to make PostScript documents, +HTML pages, PDF files, and so on. In order to convert from DocBook +format to a format of your choice, you'll need to install jade, as +well as some stylesheets. + Where to get the files ********************** @@ -775,12 +780,6 @@ The 0.1.2 release: http://linux.powertweak.com/files/powertweak-0.1.2.tgz ftp://atrey.karlin.mff.cuni.cz/pub/linux/pci/powertweak/powertweak-0.1.2.tgz -Tunelp -====== - -The 0-2.1.131 release: -ftp://e-mind.com/pub/linux/tunelp/tunelp-0-2.1.131.tar.gz - Xosview ======= @@ -818,6 +817,17 @@ Logical Volume Manager The 0.7 release: ftp://linux.msede.com/lvm/v0.7/lvm_0.7.tar.gz +Jade +==== + +The 1.2.1 release: +ftp://ftp.jclark.com/pub/jade/jade-1.2.1.tar.gz + +DSSSL Stylesheets for the DocBook DTD +===================================== + +http://nwalsh.com/docbook/dsssl/ + Other Info ========== diff --git a/Documentation/Configure.help b/Documentation/Configure.help index eb5e4efecff8..f6fe91f36493 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -7216,7 +7216,7 @@ CONFIG_NET_SCHED Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "QoS support", "Packet classifier API" and to some classifiers below. Documentation - and software is at http://icawwww1.ipfl.ch/linux/diffserv/ . + and software is at http://icawww1.epfl.ch/linux-diffserv/ . If you say Y here and to "/proc file system" below, you will be able to read status information about packet schedulers from the file @@ -7346,7 +7346,7 @@ CONFIG_NET_QOS Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router if you also say Y to "Packet classifier API" and to some classifiers below. Documentation and software is at - http://icawwww1.ipfl.ch/linux/diffserv/ . + http://icawww1.epfl.ch/linux-diffserv/ . Note that the answer to this question won't directly affect the kernel: saying N will just cause this configure script to skip all @@ -7369,7 +7369,7 @@ CONFIG_NET_CLS This will enable you to use Differentiated Services (diffserv) and Resource Reservation Protocol (RSVP) on your Linux router. Documentation and software is at - http://icawwww1.ipfl.ch/linux/diffserv/ . + http://icawww1.epfl.ch/linux-diffserv/ . ### Add #tristate ' TC index classifier' CONFIG_NET_CLS_TCINDEX @@ -12854,12 +12854,12 @@ CONFIG_SOUND_SB Please read the file Documentation/sound/Soundblaster. You should also say Y here for cards based on the Avance Logic - ALS-007 chip (read Documentation/sound/ALS) and for cards based - on ESS chips (read Documentation/sound/ESS1868 and + ALS-007 and ALS-1X0 chips (read Documentation/sound/ALS) and for cards + based on ESS chips (read Documentation/sound/ESS1868 and Documentation/sound/ESS). If you have an SB AWE 32 or SB AWE 64, say - Y here and also to "Additional lowlevel drivers" and to "SB32/AWE - support" below and read Documentation/sound/INSTALL.awe. If you have - an IBM Mwave card, say Y here and read Documentation/sound/mwave. + Y here and also to "AWE32 synth" below and read + Documentation/sound/INSTALL.awe. If you have an IBM Mwave card, say + Y here and read Documentation/sound/mwave. If you compile the driver into the kernel and don't want to use isapnp, you have to add "sb=,,," to the kernel diff --git a/Documentation/fb/tgafb.txt b/Documentation/fb/tgafb.txt index 36fe07821175..1fe34cf5284a 100644 --- a/Documentation/fb/tgafb.txt +++ b/Documentation/fb/tgafb.txt @@ -1,25 +1,35 @@ -[Also cloned from vesafb.txt, thanks to Gerd] +$Id: tgafb.txt,v 1.1.2.2 2000/04/04 06:50:18 mato Exp $ What is tgafb? =============== This is a driver for DECChip 21030 based graphics framebuffers, a.k.a. TGA -cards, specifically the following models +cards, which are usually found in older Digital Alpha systems. The +following models are supported: -ZLxP-E1 (8bpp, 4 MB VRAM) +ZLxP-E1 (8bpp, 2 MB VRAM) ZLxP-E2 (32bpp, 8 MB VRAM) ZLxP-E3 (32bpp, 16 MB VRAM, Zbuffer) -This version, tgafb-1.12, is almost a complete rewrite of the code written -by Geert Uytterhoeven, which was based on the original TGA console code -written by Jay Estabrook. +This version is an almost complete rewrite of the code written by Geert +Uytterhoeven, which was based on the original TGA console code written by +Jay Estabrook. -Major new features: +Major new features since Linux 2.0.x: - * Support for multiple resolutions, including setting the resolution at - boot time, allowing the use of a fixed-frequency monitor. - * Complete code rewrite to follow Geert's skeletonfb spec which will allow - future implementation of hardware acceleration and other features. + * Support for multiple resolutions + * Support for fixed-frequency and other oddball monitors + (by allowing the video mode to be set at boot time) + +User-visible changes since Linux 2.2.x: + + * Sync-on-green is now handled properly + * More useful information is printed on bootup + (this helps if people run into problems) + +This driver does not (yet) support the TGA2 family of framebuffers, so the +PowerStorm 3D30/4D20 (also known as PBXGB) cards are not supported. These +can however be used with the standard VGA Text Console driver. Configuration @@ -32,19 +42,27 @@ Accepted options: font:X - default font to use. All fonts are supported, including the SUN12x22 font which is very nice at high resolutions. -mode:X - default video mode. See drivers/video/tgafb.c for a list. - -X11 -=== - -XF68_FBDev should work just fine, but I haven't tested it. Running -the XF86_TGA server (reasonably recent versions of which support all TGA -cards) works fine for me. - -One minor problem with XF86_TGA is when running tgafb in resolutions higher -than 640x480, on switching VCs from tgafb to X, the entire screen is not -re-drawn and must be manually refreshed. This is an X server problem, not a -tgafb problem. + +mode:X - default video mode. The following video modes are supported: + 640x480-60, 800x600-56, 640x480-72, 800x600-60, 800x600-72, + 1024x768-60, 1152x864-60, 1024x768-70, 1024x768-76, + 1152x864-70, 1280x1024-61, 1024x768-85, 1280x1024-70, + 1152x864-84, 1280x1024-76, 1280x1024-85 + + +Known Issues +============ + +The XFree86 FBDev server has been reported not to work, since tgafb doesn't do +mmap(). Running the standard XF86_TGA server from XFree86 3.3.x works fine for +me, however this server does not do acceleration, which make certain operations +quite slow. Support for acceleration is being progressively integrated in +XFree86 4.x. + +When running tgafb in resolutions higher than 640x480, on switching VCs from +tgafb to XF86_TGA 3.3.x, the entire screen is not re-drawn and must be manually +refreshed. This is an X server problem, not a tgafb problem, and is fixed in +XFree86 4.0. Enjoy! diff --git a/Documentation/sound/ALS b/Documentation/sound/ALS index db98daf30ff7..c5e55d5b76be 100644 --- a/Documentation/sound/ALS +++ b/Documentation/sound/ALS @@ -13,9 +13,12 @@ To use an ALS sound card under Linux, enable the following options in the sound configuration section of the kernel config: - 100% Sound Blaster compatibles (SB16/32/64, ESS, Jazz16) support - FM synthesizer (YM3812/OPL-3) support -Since the ALS-007/100/200 is a PnP card, the sound driver probably should be -compiled as a module, with the isapnptools used to wake up the sound card. -Set the "I/O base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit - +Since the ALS-007/100/200 are PnP cards, ISAPnP support should probably be +compiled in. + +Alternatively, if you decide not to use kernel level ISAPnP, you can use the +user mode isapnptools to wake up the sound card, as in 2.2.X. Set the "I/O +base for SB", "Sound Blaster IRQ" and "Sound Blaster DMA" (8 bit - either 0, 1 or 3) to the values used in your particular installation (they should match the values used to configure the card using isapnp). The ALS-007 does NOT implement 16 bit DMA, so the "Sound Blaster 16 bit DMA" @@ -41,3 +44,4 @@ jwoithe@physics.adelaide.edu.au 30 March 1998 Modified 2000-02-26 by Dave Forrest, drf5n@virginia.edu to add ALS100/ALS200 +Modified 2000-04-10 by Paul Laufer, pelaufer@csupomona.edu to add ISAPnP info. diff --git a/Documentation/sound/AWE32 b/Documentation/sound/AWE32 index a0f18d640a93..8cf3966ed7e3 100644 --- a/Documentation/sound/AWE32 +++ b/Documentation/sound/AWE32 @@ -9,9 +9,10 @@ important, because the driver works only with real Creative cards. 2) If your card is NOT "Plug-n-Play" then go to 5th step now. In the other case proceed to step 3. -3) You should obtain isapnptools. I looked through other PnP packages -for Linux, but all they are either in deep unstable beta/alpha releases or -they are much worse than isapnptools. In my case isapnptools were included in +3) You should compile in kernel ISAPnP support or you should obtain isapnptools. +If you choose kernel level ISAPnP skip to step 5. I looked through other PnP +packages for Linux, but all they are either in deep unstable beta/alpha releases +or they are much worse than isapnptools. In my case isapnptools were included in a Linux distribution (Red Hat 5.x). If you also already have them then go to step 4. @@ -67,11 +68,7 @@ support" as (module). In "make (x,menu)config" select in "Sound": select "OSS sound modules" as (module) - -In "Additional low level sound drivers": -"Additional low level sound drivers", "AWE32 synth" as (module). -Select "Additional low level sound drivers" as [y] (or [*] (yes)) (If it is not -available as [y], select it as (module)) +select "AWE32 Synth" as (module) Now recompile the kernel (make dep; make (b)zImage, b(z)lilo, etc...; make modules; make modules_install), update your boot loader (if required) and @@ -87,9 +84,7 @@ http://members.xoom.com/yar/synthgm.sbk.gz. Copy it to /usr and gunzip it there. alias midi awe_wave post-install awe_wave /usr/bin/sfxload /usr/synthfm.sbk -options sb io=0x220 irq=5 dma=1 dma16=5 mpu_io=0x330 -(on io=0xaaa irq=b.... you should use your own settings) That will enable the Sound Blaster and AWE wave synthesis. To play midi files you should get one of these programs: @@ -107,4 +102,4 @@ welcome. Yaroslav Rosomakho (alons55@dialup.ptt.ru) http://www.yar.opennet.ru -Last Updated: 3Jan99 +Last Updated: 10Apr2000 diff --git a/Documentation/sound/README.awe b/Documentation/sound/README.awe index b0dc82427518..80054cd8fcde 100644 --- a/Documentation/sound/README.awe +++ b/Documentation/sound/README.awe @@ -23,11 +23,10 @@ not frequent now. * NOTE TO LINUX USERS -To enable this driver on linux-2.[01].x kernels, you need turn on both -"lowlevel drivers support" and "AWE32 synth support" options in sound -menu when configure your linux kernel and modules. The precise -installation procedure is described in the AWE64-Mini-HOWTO and -linux-kernel/Documetation/sound/AWE32. +To enable this driver on linux-2.[01].x kernels, you need turn on +"AWE32 synth" options in sound menu when configure your linux kernel +and modules. The precise installation procedure is described in the +AWE64-Mini-HOWTO and linux-kernel/Documetation/sound/AWE32. If you're using PnP cards, the card must be initialized before loading the sound driver. There're several options to do this: diff --git a/Documentation/sound/README.modules b/Documentation/sound/README.modules index 9cc30a3ac837..39f7d954a8a9 100644 --- a/Documentation/sound/README.modules +++ b/Documentation/sound/README.modules @@ -33,6 +33,12 @@ post-install sb /sbin/modprobe "-k" "adlib_card" options sb io=0x220 irq=7 dma=1 dma16=5 mpu_io=0x330 options adlib_card io=0x388 # FM synthesizer + Alternatively, if you have compiled in kernel level ISAPnP support: + +alias char-major-14 sb +post-install sb /sbin/modprobe "-k" "adlib_card" +options adlib_card io=0x388 + The effect of this is that the sound driver and all necessary bits and pieces autoload on demand, assuming you use kerneld (a sound choice) and autoclean when not in use. Also, options for the device drivers are diff --git a/Documentation/sound/Soundblaster b/Documentation/sound/Soundblaster index 160763f53c42..515c4ec77c83 100644 --- a/Documentation/sound/Soundblaster +++ b/Documentation/sound/Soundblaster @@ -13,12 +13,19 @@ dma 8-bit DMA channel for the Sound Blaster (0,1,3) dma16 16-bit DMA channel for SB16 and equivalent cards (5,6,7) mpu_io I/O for MPU chip if present (0x300,0x330) -mad16=1 Set when loading this as part of the MAD16 setup only -trix=1 Set when loading this as part of the Audiotrix setup only -pas2=1 Set when loading this as part of the Pas2 setup only sm_games=1 Set if you have a Logitech soundman games acer=1 Set this to detect cards in some ACER notebooks mwave_bug=1 Set if you are trying to use this driver with mwave (see on) +type Use this to specify a specific card type + +The following arguments are taken if ISAPnP support is compiled in + +isapnp=0 Set this to disable ISAPnP detection (use io=0xXXX etc. above) +multiple=1 Set to enable detection of multiple Soundblaster cards. +reverse=1 Reverses the order of the search in the PnP table. +uart401=1 Set to enable detection of mpu devices on some clones. +isapnpjump Jumps to a specific slot in the driver's PnP table. Use the + source, Luke. You may well want to load the opl3 driver for synth music on most SB and clone SB devices diff --git a/MAINTAINERS b/MAINTAINERS index 04b7177cb227..ed1b09e33b80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -969,7 +969,7 @@ M: ecd@skynet.be P: Jakub Jelinek M: jj@sunsite.ms.mff.cuni.cz P: Anton Blanchard -M: anton@progsoc.uts.edu.au +M: anton@linuxcare.com L: sparclinux@vger.rutgers.edu L: ultralinux@vger.rutgers.edu W: http://ultra.linux.cz diff --git a/Makefile b/Makefile index 8a22852fe25d..542715cb1961 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 3 SUBLEVEL = 99 -EXTRAVERSION = -pre5 +EXTRAVERSION = -pre6 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION) diff --git a/REPORTING-BUGS b/REPORTING-BUGS index b2f1f7c35500..8654763802c5 100644 --- a/REPORTING-BUGS +++ b/REPORTING-BUGS @@ -46,8 +46,10 @@ summary from [1.]>" for easy identification by the developers [7.1.] Software (add the output of the ver_linux script here) [7.2.] Processor information (from /proc/cpuinfo): [7.3.] Module information (from /proc/modules): -[7.4.] SCSI information (from /proc/scsi/scsi) -[7.5.] Other information that might be relevant to the problem +[7.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem) +[7.5.] PCI information ('lspci -vvv' as root) +[7.6.] SCSI information (from /proc/scsi/scsi) +[7.7.] Other information that might be relevant to the problem (please look in /proc and include all information that you think to be relevant): [X.] Other notes, patches, fixes, workarounds: diff --git a/arch/i386/config.in b/arch/i386/config.in index 664b77800ce8..f652df3331df 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -52,7 +52,6 @@ fi if [ "$CONFIG_MK6" = "y" ]; then define_bool CONFIG_X86_ALIGNMENT_16 y define_bool CONFIG_X86_TSC y - define_bool CONFIG_X86_USE_3DNOW y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y fi if [ "$CONFIG_M686" = "y" ]; then @@ -67,6 +66,7 @@ if [ "$CONFIG_MK7" = "y" ]; then define_bool CONFIG_X86_USE_3DNOW y define_bool CONFIG_X86_PGE y define_bool CONFIG_X86_USE_PPRO_CHECKSUM y + define_int CONFIG_X86_L1_CACHE_BYTES 64 fi tristate '/dev/cpu/microcode - Intel P6 CPU microcode support' CONFIG_MICROCODE diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index a5f72548ff4a..d4c35fdf0ae0 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c @@ -4,7 +4,9 @@ * (c) 1999, 2000 Ingo Molnar * * Fixes - * Maciej W. Rozycki : Bits for genuine 82489DX timers + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore for + * testing these extensively */ #include @@ -44,32 +46,96 @@ int get_maxlvt(void) return maxlvt; } -void disable_local_APIC (void) +static void clear_local_APIC(void) { - unsigned long value; - int maxlvt; + int maxlvt; + unsigned long v; + + maxlvt = get_maxlvt(); /* - * Disable APIC + * Careful: we have to set masks only first to deassert + * any level-triggered sources. */ - value = apic_read(APIC_SPIV); - value &= ~(1<<8); - apic_write(APIC_SPIV,value); + v = apic_read(APIC_LVTT); + apic_write_around(APIC_LVTT, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT0); + apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED); + v = apic_read(APIC_LVT1); + apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED); + if (maxlvt >= 3) { + v = apic_read(APIC_LVTERR); + apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED); + } + if (maxlvt >= 4) { + v = apic_read(APIC_LVTPC); + apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED); + } /* * Clean APIC state for other OSs: */ - value = apic_read(APIC_SPIV); - value &= ~(1<<8); - apic_write(APIC_SPIV,value); - maxlvt = get_maxlvt(); - apic_write_around(APIC_LVTT, 0x00010000); - apic_write_around(APIC_LVT0, 0x00010000); - apic_write_around(APIC_LVT1, 0x00010000); + apic_write_around(APIC_LVTT, APIC_LVT_MASKED); + apic_write_around(APIC_LVT0, APIC_LVT_MASKED); + apic_write_around(APIC_LVT1, APIC_LVT_MASKED); if (maxlvt >= 3) - apic_write_around(APIC_LVTERR, 0x00010000); + apic_write_around(APIC_LVTERR, APIC_LVT_MASKED); if (maxlvt >= 4) - apic_write_around(APIC_LVTPC, 0x00010000); + apic_write_around(APIC_LVTPC, APIC_LVT_MASKED); +} + +void __init connect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Do not trust the local APIC being empty at bootup. + */ + clear_local_APIC(); + /* + * PIC mode, enable symmetric IO mode in the IMCR, + * i.e. connect BSP's local APIC to INT and NMI lines. + */ + printk("leaving PIC mode, enabling symmetric IO mode.\n"); + outb(0x70, 0x22); + outb(0x01, 0x23); + } +} + +void disconnect_bsp_APIC(void) +{ + if (pic_mode) { + /* + * Put the board back into PIC mode (has an effect + * only on certain older boards). Note that APIC + * interrupts, including IPIs, won't work beyond + * this point! The only exception are INIT IPIs. + */ + printk("disabling symmetric IO mode, entering PIC mode.\n"); + outb(0x70, 0x22); + outb(0x00, 0x23); + } +} + +void disable_local_APIC(void) +{ + unsigned long value; + + clear_local_APIC(); + + /* + * Disable APIC (implies clearing of registers + * for 82489DX!). + */ + value = apic_read(APIC_SPIV); + value &= ~(1<<8); + apic_write_around(APIC_SPIV, value); +} + +void __init sync_Arb_IDs(void) +{ + Dprintk("Synchronizing Arb IDs.\n"); + apic_write_around(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG + | APIC_DM_INIT); } extern void __error_in_apic_c (void); @@ -78,6 +144,9 @@ void __init setup_local_APIC (void) { unsigned long value, ver, maxlvt; + value = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(value); + if ((SPURIOUS_APIC_VECTOR & 0x0f) != 0x0f) __error_in_apic_c(); @@ -87,11 +156,12 @@ void __init setup_local_APIC (void) if (!test_bit(GET_APIC_ID(apic_read(APIC_ID)), &phys_cpu_present_map)) BUG(); - value = apic_read(APIC_SPIV); + value = apic_read(APIC_SPIV); + value &= ~APIC_VECTOR_MASK; /* * Enable APIC */ - value |= (1<<8); + value |= (1<<8); /* * Some unknown Intel IO/APIC (or APIC) errata is biting us with @@ -108,7 +178,7 @@ void __init setup_local_APIC (void) */ #if 0 /* Enable focus processor (bit==0) */ - value &= ~(1<<9); + value &= ~(1<<9); #else /* Disable focus processor (bit==1) */ value |= (1<<9); @@ -117,7 +187,7 @@ void __init setup_local_APIC (void) * Set spurious IRQ vector */ value |= SPURIOUS_APIC_VECTOR; - apic_write(APIC_SPIV,value); + apic_write_around(APIC_SPIV, value); /* * Set up LVT0, LVT1: @@ -126,48 +196,44 @@ void __init setup_local_APIC (void) * strictly necessery in pure symmetric-IO mode, but sometimes * we delegate interrupts to the 8259A. */ - if (!smp_processor_id()) { - value = 0x00000700; + /* + * TODO: set up through-local-APIC from through-I/O-APIC? --macro + */ + value = apic_read(APIC_LVT0) & APIC_LVT_MASKED; + if (!smp_processor_id() && (pic_mode || !value)) { + value = APIC_DM_EXTINT; printk("enabled ExtINT on CPU#%d\n", smp_processor_id()); } else { - value = 0x00010700; + value = APIC_DM_EXTINT | APIC_LVT_MASKED; printk("masked ExtINT on CPU#%d\n", smp_processor_id()); } - apic_write_around(APIC_LVT0,value); + apic_write_around(APIC_LVT0, value); /* * only the BP should see the LINT1 NMI signal, obviously. */ if (!smp_processor_id()) - value = 0x00000400; // unmask NMI + value = APIC_DM_NMI; else - value = 0x00010400; // mask NMI - apic_write_around(APIC_LVT1,value); + value = APIC_DM_NMI | APIC_LVT_MASKED; + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + value |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT1, value); - value = apic_read(APIC_LVR); - ver = GET_APIC_VERSION(value); if (APIC_INTEGRATED(ver)) { /* !82489DX */ maxlvt = get_maxlvt(); - /* - * Due to the Pentium erratum 3AP. - */ - if (maxlvt > 3) { - apic_readaround(APIC_SPIV); // not strictly necessery + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); - } value = apic_read(APIC_ESR); printk("ESR value before enabling vector: %08lx\n", value); - value = apic_read(APIC_LVTERR); value = ERROR_APIC_VECTOR; // enables sending errors - apic_write(APIC_LVTERR,value); + apic_write_around(APIC_LVTERR, value); /* * spec says clear errors after enabling vector. */ - if (maxlvt != 3) { - apic_readaround(APIC_SPIV); + if (maxlvt > 3) apic_write(APIC_ESR, 0); - } value = apic_read(APIC_ESR); printk("ESR value after enabling vector: %08lx\n", value); } else @@ -177,22 +243,23 @@ void __init setup_local_APIC (void) * Set Task Priority to 'accept all'. We never change this * later on. */ - value = apic_read(APIC_TASKPRI); - value &= ~APIC_TPRI_MASK; - apic_write(APIC_TASKPRI,value); + value = apic_read(APIC_TASKPRI); + value &= ~APIC_TPRI_MASK; + apic_write_around(APIC_TASKPRI, value); /* * Set up the logical destination ID and put the * APIC into flat delivery mode. */ - value = apic_read(APIC_LDR); + value = apic_read(APIC_LDR); value &= ~APIC_LDR_MASK; value |= (1<<(smp_processor_id()+24)); - apic_write(APIC_LDR,value); + apic_write_around(APIC_LDR, value); - value = apic_read(APIC_DFR); - value |= SET_APIC_DFR(0xf); - apic_write(APIC_DFR, value); + /* + * Must be "all ones" explicitly for 82489DX. + */ + apic_write_around(APIC_DFR, 0xffffffff); } void __init init_apic_mappings(void) @@ -214,6 +281,13 @@ void __init init_apic_mappings(void) set_fixmap_nocache(FIX_APIC_BASE, apic_phys); Dprintk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys); + /* + * Fetch the APIC ID of the BSP in case we have a + * default configuration (or the MP table is broken). + */ + if (boot_cpu_id == -1U) + boot_cpu_id = GET_APIC_ID(apic_read(APIC_ID)); + #ifdef CONFIG_X86_IO_APIC { unsigned long ioapic_phys, idx = FIX_IO_APIC_BASE_0; @@ -285,7 +359,7 @@ void __init wait_8254_wraparound(void) * chipset timer can cause. */ - } while (delta<300); + } while (delta < 300); } /* @@ -305,21 +379,19 @@ void __setup_APIC_LVTT(unsigned int clocks) { unsigned int lvtt1_value, tmp_value; - tmp_value = apic_read(APIC_LVTT); lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) | APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; - apic_write(APIC_LVTT, lvtt1_value); + apic_write_around(APIC_LVTT, lvtt1_value); /* * Divide PICLK by 16 */ tmp_value = apic_read(APIC_TDCR); - apic_write(APIC_TDCR, (tmp_value + apic_write_around(APIC_TDCR, (tmp_value & ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE)) | APIC_TDR_DIV_16); - tmp_value = apic_read(APIC_TMICT); - apic_write(APIC_TMICT, clocks/APIC_DIVISOR); + apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR); } void setup_APIC_timer(void * data) @@ -353,6 +425,12 @@ void setup_APIC_timer(void * data) t0 = apic_read(APIC_TMCCT)*APIC_DIVISOR; do { + /* + * It looks like the 82489DX cannot handle + * consecutive reads of the TMCCT register well; + * this dummy read prevents it from a lockup. + */ + apic_read(APIC_SPIV); t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR; delta = (int)(t0 - t1 - slice*(smp_processor_id()+1)); } while (delta < 0); @@ -490,6 +568,41 @@ int setup_profiling_timer(unsigned int multiplier) #undef APIC_DIVISOR +#ifdef CONFIG_SMP +static inline void handle_smp_time (int user, int cpu) +{ + int system = !user; + struct task_struct * p = current; + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + irq_enter(cpu, 0); + update_one_process(p, 1, user, system, cpu); + if (p->pid) { + p->counter -= 1; + if (p->counter <= 0) { + p->counter = 0; + p->need_resched = 1; + } + if (p->priority < DEF_PRIORITY) { + kstat.cpu_nice += user; + kstat.per_cpu_nice[cpu] += user; + } else { + kstat.cpu_user += user; + kstat.per_cpu_user[cpu] += user; + } + kstat.cpu_system += system; + kstat.per_cpu_system[cpu] += system; + + } + irq_exit(cpu, 0); +} +#endif + /* * Local timer interrupt handler. It does both profiling and * process statistics/rescheduling. @@ -502,7 +615,6 @@ int setup_profiling_timer(unsigned int multiplier) inline void smp_local_timer_interrupt(struct pt_regs * regs) { - int user = (user_mode(regs) != 0); int cpu = smp_processor_id(); /* @@ -511,13 +623,8 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs) * updated with atomic operations). This is especially * useful with a profiling multiplier != 1 */ - if (!user) - x86_do_profile(regs->eip); if (--prof_counter[cpu] <= 0) { - int system = 1 - user; - struct task_struct * p = current; - /* * The multiplier may have changed since the last time we got * to this point as a result of the user writing to @@ -532,33 +639,9 @@ inline void smp_local_timer_interrupt(struct pt_regs * regs) prof_old_multiplier[cpu] = prof_counter[cpu]; } - /* - * After doing the above, we need to make like - * a normal interrupt - otherwise timer interrupts - * ignore the global interrupt lock, which is the - * WrongThing (tm) to do. - */ - - irq_enter(cpu, 0); - update_one_process(p, 1, user, system, cpu); - if (p->pid) { - p->counter -= 1; - if (p->counter <= 0) { - p->counter = 0; - p->need_resched = 1; - } - if (p->priority < DEF_PRIORITY) { - kstat.cpu_nice += user; - kstat.per_cpu_nice[cpu] += user; - } else { - kstat.cpu_user += user; - kstat.per_cpu_user[cpu] += user; - } - kstat.cpu_system += system; - kstat.per_cpu_system[cpu] += system; - - } - irq_exit(cpu, 0); +#ifdef CONFIG_SMP + handle_smp_time(user_mode(regs), cpu); +#endif } /* @@ -603,7 +686,17 @@ void smp_apic_timer_interrupt(struct pt_regs * regs) */ asmlinkage void smp_spurious_interrupt(void) { - ack_APIC_irq(); + unsigned long v; + + /* + * Check if this really is a spurious interrupt and ACK it + * if it is a vectored one. Just in case... + * Spurious interrupts should not be ACKed. + */ + v = apic_read(APIC_ISR + ((SPURIOUS_APIC_VECTOR & ~0x1f) >> 1)); + if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f))) + ack_APIC_irq(); + /* see sw-dev-man vol 3, chapter 7.4.13.5 */ printk("spurious APIC interrupt on CPU#%d, should never happen.\n", smp_processor_id()); diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 5327f24a46e5..ce406fb38dc1 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -27,6 +27,11 @@ extern void dump_thread(struct pt_regs *, struct user *); extern int dump_fpu(elf_fpregset_t *); extern spinlock_t rtc_lock; +#if defined(CONFIG_APM) +extern void machine_real_restart(unsigned char *, int); +EXPORT_SYMBOL(machine_real_restart); +#endif + #ifdef CONFIG_SMP extern void FASTCALL( __write_lock_failed(rwlock_t *rw)); extern void FASTCALL( __read_lock_failed(rwlock_t *rw)); @@ -68,7 +73,6 @@ EXPORT_SYMBOL_NOVERS(__down_write_failed); EXPORT_SYMBOL_NOVERS(__down_read_failed); EXPORT_SYMBOL_NOVERS(__rwsem_wake); /* Networking helper routines. */ -EXPORT_SYMBOL(csum_partial_copy); EXPORT_SYMBOL(csum_partial_copy_generic); /* Delay loops */ EXPORT_SYMBOL(__udelay); @@ -84,7 +88,6 @@ EXPORT_SYMBOL_NOVERS(__put_user_4); EXPORT_SYMBOL(strtok); EXPORT_SYMBOL(strpbrk); -EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(strncpy_from_user); EXPORT_SYMBOL(__strncpy_from_user); diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index 61571ab596e4..334a75f88aca 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -415,7 +415,7 @@ void __init init_ISA_irqs (void) for (i = 0; i < NR_IRQS; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = 0; - irq_desc[i].depth = 0; + irq_desc[i].depth = 1; if (i < 16) { /* diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index 1d44456698e6..dfee6e4d55f9 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -13,7 +13,9 @@ * and Ingo Molnar * * Fixes - * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Maciej W. Rozycki : Bits for genuine 82489DX APICs; + * thanks to Eric Gilmore for + * testing these extensively */ #include @@ -46,9 +48,6 @@ struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; /* MP IRQ source entries */ int mp_irq_entries = 0; -/* non-0 if default (table-less) MP configuration */ -int mpc_default_type = 0; - /* * Rough estimation of how many shared IRQs there are, can * be changed anytime. @@ -166,7 +165,7 @@ static void clear_IO_APIC (void) #define MAX_PIRQS 8 int pirq_entries [MAX_PIRQS]; -int pirqs_enabled; +int pirqs_enabled = 0; int skip_ioapic_setup = 0; static int __init ioapic_setup(char *str) @@ -235,7 +234,8 @@ static int __init find_timer_pin(int type) int lbus = mp_irqs[i].mpc_srcbus; if ((mp_bus_id_to_type[lbus] == MP_BUS_ISA || - mp_bus_id_to_type[lbus] == MP_BUS_EISA) && + mp_bus_id_to_type[lbus] == MP_BUS_EISA || + mp_bus_id_to_type[lbus] == MP_BUS_MCA) && (mp_irqs[i].mpc_irqtype == type) && (mp_irqs[i].mpc_srcbusirq == 0x00)) @@ -260,13 +260,15 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pci_pin) if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic) break; - if ((apic || IO_APIC_IRQ(mp_irqs[i].mpc_dstirq)) && - (mp_bus_id_to_type[lbus] == MP_BUS_PCI) && + if ((mp_bus_id_to_type[lbus] == MP_BUS_PCI) && !mp_irqs[i].mpc_irqtype && (bus == mp_bus_id_to_pci_bus[mp_irqs[i].mpc_srcbus]) && (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) { int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq); + if (!(apic || IO_APIC_IRQ(irq))) + continue; + if (pci_pin == (mp_irqs[i].mpc_srcbusirq & 3)) return irq; /* @@ -298,15 +300,27 @@ static int __init EISA_ELCR(unsigned int irq) * EISA conforming in the MP table, that means its trigger type must * be read in from the ELCR */ -#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_dstirq)) +#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq)) #define default_EISA_polarity(idx) (0) -/* ISA interrupts are always polarity zero edge triggered, even when - * listed as conforming in the MP table. */ +/* ISA interrupts are always polarity zero edge triggered, + * when listed as conforming in the MP table. */ #define default_ISA_trigger(idx) (0) #define default_ISA_polarity(idx) (0) +/* PCI interrupts are always polarity one level triggered, + * when listed as conforming in the MP table. */ + +#define default_PCI_trigger(idx) (1) +#define default_PCI_polarity(idx) (1) + +/* MCA interrupts are always polarity zero level triggered, + * when listed as conforming in the MP table. */ + +#define default_MCA_trigger(idx) (1) +#define default_MCA_polarity(idx) (0) + static int __init MPBIOS_polarity(int idx) { int bus = mp_irqs[idx].mpc_srcbus; @@ -326,14 +340,19 @@ static int __init MPBIOS_polarity(int idx) polarity = default_ISA_polarity(idx); break; } - case MP_BUS_EISA: + case MP_BUS_EISA: /* EISA pin */ { polarity = default_EISA_polarity(idx); break; } case MP_BUS_PCI: /* PCI pin */ { - polarity = 1; + polarity = default_PCI_polarity(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + polarity = default_MCA_polarity(idx); break; } default: @@ -385,19 +404,24 @@ static int __init MPBIOS_trigger(int idx) { switch (mp_bus_id_to_type[bus]) { - case MP_BUS_ISA: + case MP_BUS_ISA: /* ISA pin */ { trigger = default_ISA_trigger(idx); break; } - case MP_BUS_EISA: + case MP_BUS_EISA: /* EISA pin */ { trigger = default_EISA_trigger(idx); break; } - case MP_BUS_PCI: /* PCI pin, level */ + case MP_BUS_PCI: /* PCI pin */ { - trigger = 1; + trigger = default_PCI_trigger(idx); + break; + } + case MP_BUS_MCA: /* MCA pin */ + { + trigger = default_MCA_trigger(idx); break; } default: @@ -460,6 +484,7 @@ static int __init pin_2_irq(int idx, int apic, int pin) { case MP_BUS_ISA: /* ISA pin */ case MP_BUS_EISA: + case MP_BUS_MCA: { irq = mp_irqs[idx].mpc_srcbusirq; break; @@ -624,8 +649,8 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) disable_8259A_irq(0); - apic_readaround(APIC_LVT0); - apic_write(APIC_LVT0, 0x00010700); // mask LVT0 + /* mask LVT0 */ + apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); init_8259A(1); @@ -650,8 +675,8 @@ void __init setup_ExtINT_IRQ0_pin(unsigned int pin, int vector) /* * Add it to the IO-APIC irq-routing table: */ - io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); io_apic_write(0, 0x11+2*pin, *(((int *)&entry)+1)); + io_apic_write(0, 0x10+2*pin, *(((int *)&entry)+0)); enable_8259A_irq(0); } @@ -725,8 +750,8 @@ void __init print_IO_APIC(void) printk(KERN_DEBUG ".... IRQ redirection table:\n"); - printk(KERN_DEBUG " NR Log Phy "); - printk(KERN_DEBUG "Mask Trig IRR Pol Stat Dest Deli Vect: \n"); + printk(KERN_DEBUG " NR Log Phy Mask Trig IRR Pol" + " Stat Dest Deli Vect: \n"); for (i = 0; i <= reg_01.entries; i++) { struct IO_APIC_route_entry entry; @@ -831,13 +856,8 @@ void /*__init*/ print_local_APIC(void * dummy) print_APIC_bitfield(APIC_IRR); if (APIC_INTEGRATED(ver)) { /* !82489DX */ - /* - * Due to the Pentium erratum 3AP. - */ - if (maxlvt > 3) { - apic_readaround(APIC_SPIV); // not strictly necessery + if (maxlvt > 3) /* Due to the Pentium erratum 3AP. */ apic_write(APIC_ESR, 0); - } v = apic_read(APIC_ESR); printk(KERN_DEBUG "... APIC ESR: %08x\n", v); } @@ -879,6 +899,32 @@ void print_all_local_APICs (void) print_local_APIC(NULL); } +void /*__init*/ print_PIC(void) +{ + unsigned int v, flags; + + printk(KERN_DEBUG "\nprinting PIC contents\n"); + + v = inb(0xa1) << 8 | inb(0x21); + printk(KERN_DEBUG "... PIC IMR: %04x\n", v); + + v = inb(0xa0) << 8 | inb(0x20); + printk(KERN_DEBUG "... PIC IRR: %04x\n", v); + + __save_flags(flags); + __cli(); + outb(0x0b,0xa0); + outb(0x0b,0x20); + v = inb(0xa0) << 8 | inb(0x20); + outb(0x0a,0xa0); + outb(0x0a,0x20); + __restore_flags(flags); + printk(KERN_DEBUG "... PIC ISR: %04x\n", v); + + v = inb(0x4d1) << 8 | inb(0x4d0); + printk(KERN_DEBUG "... PIC ELCR: %04x\n", v); +} + static void __init enable_IO_APIC(void) { struct IO_APIC_reg_01 reg_01; @@ -890,16 +936,7 @@ static void __init enable_IO_APIC(void) } if (!pirqs_enabled) for (i = 0; i < MAX_PIRQS; i++) - pirq_entries[i] =- 1; - - if (pic_mode) { - /* - * PIC mode, enable symmetric IO mode in the IMCR. - */ - printk("leaving PIC mode, enabling symmetric IO mode.\n"); - outb(0x70, 0x22); - outb(0x01, 0x23); - } + pirq_entries[i] = -1; /* * The number of IO-APIC IRQ registers (== #pins): @@ -925,15 +962,7 @@ void disable_IO_APIC(void) */ clear_IO_APIC(); - /* - * Put it back into PIC mode (has an effect only on - * certain older boards) - */ - if (pic_mode) { - printk("disabling symmetric IO mode, entering PIC mode.\n"); - outb_p(0x70, 0x22); - outb_p(0x00, 0x23); - } + disconnect_bsp_APIC(); } /* @@ -986,48 +1015,6 @@ static void __init setup_ioapic_ids_from_mpc (void) } } -static void __init construct_default_ISA_mptable(void) -{ - int i, pos = 0; - const int bus_type = (mpc_default_type == 2 || mpc_default_type == 3 || - mpc_default_type == 6) ? MP_BUS_EISA : MP_BUS_ISA; - - for (i = 0; i < 16; i++) { - if (!IO_APIC_IRQ(i)) - continue; - - mp_irqs[pos].mpc_irqtype = mp_INT; - mp_irqs[pos].mpc_irqflag = 0; /* default */ - mp_irqs[pos].mpc_srcbus = 0; - mp_irqs[pos].mpc_srcbusirq = i; - mp_irqs[pos].mpc_dstapic = 0; - mp_irqs[pos].mpc_dstirq = i; - pos++; - } - mp_irq_entries = pos; - mp_bus_id_to_type[0] = bus_type; - - /* - * MP specification 1.4 defines some extra rules for default - * configurations, fix them up here: - */ - switch (mpc_default_type) - { - case 2: - /* - * IRQ0 is not connected: - */ - mp_irqs[0].mpc_irqtype = mp_ExtINT; - break; - default: - /* - * pin 2 is IRQ0: - */ - mp_irqs[0].mpc_dstirq = 2; - } - -} - /* * There is a nasty bug in some older SMP boards, their mptable lies * about the timer IRQ. We do the following to work around the situation: @@ -1041,9 +1028,17 @@ static int __init timer_irq_works(void) unsigned int t1 = jiffies; sti(); - mdelay(40); + /* Let ten ticks pass... */ + mdelay((10 * 1000) / HZ); - if (jiffies-t1>1) + /* + * Expect a few ticks at least, to be sure some possible + * glue logic does not lock up after one or two first + * ticks in a non-ExtINT mode. Also the local APIC + * might have cached one ExtINT interrupt. Finally, at + * least one tick may be lost due to delays. + */ + if (jiffies - t1 > 4) return 1; return 0; @@ -1257,8 +1252,14 @@ static struct hw_interrupt_type lapic_irq_type = { static void enable_NMI_through_LVT0 (void * dummy) { - apic_readaround(APIC_LVT0); - apic_write(APIC_LVT0, 0x00000400); // unmask and set to NMI + unsigned int v, ver; + + ver = apic_read(APIC_LVR); + ver = GET_APIC_VERSION(ver); + v = APIC_DM_NMI; /* unmask and set to NMI */ + if (!APIC_INTEGRATED(ver)) /* 82489DX */ + v |= APIC_LVT_LEVEL_TRIGGER; + apic_write_around(APIC_LVT0, v); } static void setup_nmi (void) @@ -1303,24 +1304,23 @@ static inline void check_timer(void) printk(KERN_INFO "..TIMER: vector=%d pin1=%d pin2=%d\n", vector, pin1, pin2); - /* - * Ok, does IRQ0 through the IOAPIC work? - */ - if (timer_irq_works()) { - if (nmi_watchdog) { - disable_8259A_irq(0); - init_8259A(1); - setup_nmi(); - enable_8259A_irq(0); - if (nmi_irq_works()) - return; - } else - return; - } - if (pin1 != -1) { - printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); + /* + * Ok, does IRQ0 through the IOAPIC work? + */ + unmask_IO_APIC_irq(0); + if (timer_irq_works()) { + if (nmi_watchdog) { + disable_8259A_irq(0); + init_8259A(1); + setup_nmi(); + enable_8259A_irq(0); + nmi_irq_works(); + } + return; + } clear_IO_APIC_pin(0, pin1); + printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); } printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); @@ -1334,10 +1334,9 @@ static inline void check_timer(void) printk("works.\n"); if (nmi_watchdog) { setup_nmi(); - if (nmi_irq_works()) - return; - } else - return; + nmi_irq_works(); + } + return; } /* * Cleanup, just in case ... @@ -1355,9 +1354,8 @@ static inline void check_timer(void) disable_8259A_irq(0); irq_desc[0].handler = &lapic_irq_type; - init_8259A(1); // AEOI mode - apic_readaround(APIC_LVT0); - apic_write(APIC_LVT0, 0x00000000 | vector); // Fixed mode + init_8259A(1); /* AEOI mode */ + apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ enable_8259A_irq(0); if (timer_irq_works()) { @@ -1391,21 +1389,12 @@ void __init setup_IO_APIC(void) io_apic_irqs = ~PIC_IRQS; printk("ENABLING IO-APIC IRQs\n"); - /* - * If there are no explicit MP IRQ entries, it's either one of the - * default configuration types or we are broken. In both cases it's - * fine to set up most of the low 16 IO-APIC pins to ISA defaults. - */ - if (!mp_irq_entries) { - printk("no explicit IRQ entries, using default mptable\n"); - construct_default_ISA_mptable(); - } - /* * Set up the IO-APIC IRQ routing table by parsing the MP-BIOS * mptable: */ setup_ioapic_ids_from_mpc(); + sync_Arb_IDs(); setup_IO_APIC_irqs(); init_IO_APIC_traps(); check_timer(); @@ -1421,6 +1410,7 @@ void IO_APIC_init_uniprocessor (void) { if (!smp_found_config) return; + connect_bsp_APIC(); setup_local_APIC(); setup_IO_APIC(); setup_APIC_clocks(); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index a96540d6e78d..79155a3262b4 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -462,8 +462,8 @@ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * * @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. + * 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. */ @@ -1127,7 +1127,7 @@ static void register_irq_proc (unsigned int irq) irq_dir[irq] = proc_mkdir(name, root_irq_dir); /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]); + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); entry->nlink = 1; entry->data = (void *)(long)irq; @@ -1148,7 +1148,7 @@ void init_irq_proc (void) root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir); + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; diff --git a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c index f0f0f9f371cb..6c6053bf7525 100644 --- a/arch/i386/kernel/mca.c +++ b/arch/i386/kernel/mca.c @@ -366,12 +366,12 @@ void mca_handle_nmi(void) /** * mca_find_adapter - scan for adapters * @id: MCA identification to search for - * @start: Starting slot + * @start: starting slot * * Search the MCA configuration for adapters matching the 16bit * ID given. The first time it should be called with start as zero * and then further calls made passing the return value of the - * previous call until MCA_NOTFOUND is returned. + * previous call until %MCA_NOTFOUND is returned. * * Disabled adapters are not reported. */ @@ -411,12 +411,12 @@ EXPORT_SYMBOL(mca_find_adapter); /** * mca_find_unused_adapter - scan for unused adapters * @id: MCA identification to search for - * @start: Starting slot + * @start: starting slot * * Search the MCA configuration for adapters matching the 16bit * ID given. The first time it should be called with start as zero * and then further calls made passing the return value of the - * previous call until MCA_NOTFOUND is returned. + * previous call until %MCA_NOTFOUND is returned. * * Adapters that have been claimed by drivers and those that * are disabled are not reported. This function thus allows a driver @@ -647,10 +647,10 @@ EXPORT_SYMBOL(mca_set_adapter_name); * function is called with the buffer, slot, and device pointer (or * some equally informative context information, or nothing, if you * prefer), and is expected to put useful information into the - * buffer. The adapter name, id, and POS registers get printed + * buffer. The adapter name, ID, and POS registers get printed * before this is called though, so don't do it again. * - * This should be called with a NULL procfn when a module + * This should be called with a %NULL @procfn when a module * unregisters, thus preventing kernel crashes and other such * nastiness. */ diff --git a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c index 030b31647e6b..5df83a10a2fa 100644 --- a/arch/i386/kernel/mpparse.c +++ b/arch/i386/kernel/mpparse.c @@ -9,7 +9,7 @@ * Erich Boleyn : MP v1.4 and additional changes. * Alan Cox : Added EBDA scanning * Ingo Molnar : various cleanups and rewrites - * Maciej W. Rozycki : Bits for genuine 82489DX APICs + * Maciej W. Rozycki : Bits for default MP configurations */ #include @@ -34,7 +34,7 @@ int smp_found_config = 0; * Various Linux-internal data structures created from the * MP-table. */ -int apic_version [NR_CPUS]; +int apic_version [MAX_APICS]; int mp_bus_id_to_type [MAX_MP_BUSSES] = { -1, }; int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { -1, }; int mp_current_pci_id = 0; @@ -42,9 +42,9 @@ int pic_mode; unsigned long mp_lapic_addr = 0; /* Processor that is doing the boot up */ -unsigned int boot_cpu_id = 0; +unsigned int boot_cpu_id = -1U; /* Internal processor count */ -static unsigned int num_processors = 1; +static unsigned int num_processors = 0; /* Bitmask of physically existing CPUs */ unsigned long phys_cpu_present_map = 0; @@ -132,13 +132,12 @@ static void __init MP_processor_info (struct mpc_config_processor *m) if (m->mpc_cpuflag & CPU_BOOTPROCESSOR) { Dprintk(" Bootup CPU\n"); boot_cpu_id = m->mpc_apicid; - } else - /* Boot CPU already counted */ - num_processors++; + } + num_processors++; - if (m->mpc_apicid > NR_CPUS) { - printk("Processor #%d unused. (Max %d processors).\n", - m->mpc_apicid, NR_CPUS); + if (m->mpc_apicid > MAX_APICS) { + printk("Processor #%d INVALID. (Max ID: %d).\n", + m->mpc_apicid, MAX_APICS); return; } ver = m->mpc_apicver; @@ -164,18 +163,18 @@ static void __init MP_bus_info (struct mpc_config_bus *m) if (strncmp(str, "ISA", 3) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; - } else { - if (strncmp(str, "EISA", 4) == 0) { + } else if (strncmp(str, "EISA", 4) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; - } else { - if (strncmp(str, "PCI", 3) == 0) { + } else if (strncmp(str, "PCI", 3) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; mp_current_pci_id++; + } else if (strncmp(str, "MCA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA; } else { printk("Unknown bustype %s\n", str); panic("cannot handle bus - mail to linux-smp@vger.rutgers.edu"); - } } } + } } static void __init MP_ioapic_info (struct mpc_config_ioapic *m) @@ -197,12 +196,22 @@ static void __init MP_ioapic_info (struct mpc_config_ioapic *m) static void __init MP_intsrc_info (struct mpc_config_intsrc *m) { mp_irqs [mp_irq_entries] = *m; + Dprintk("Int: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC INT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, + m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); if (++mp_irq_entries == MAX_IRQ_SOURCES) panic("Max # of irq sources exceeded!!\n"); } static void __init MP_lintsrc_info (struct mpc_config_lintsrc *m) { + Dprintk("Lint: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC LINT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) &3, m->mpc_srcbusid, + m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint); /* * Well it seems all SMP boards in existence * use ExtINT/LVT1 == LINT0 and @@ -316,6 +325,122 @@ static int __init smp_read_mpc(struct mp_config_table *mpc) return num_processors; } +static void __init construct_default_ioirq_mptable(int mpc_default_type) +{ + struct mpc_config_intsrc intsrc; + int i; + + intsrc.mpc_type = MP_INTSRC; + intsrc.mpc_irqflag = 0; /* conforming */ + intsrc.mpc_srcbus = 0; + intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid; + + intsrc.mpc_irqtype = mp_INT; + for (i = 0; i < 16; i++) { + switch (mpc_default_type) { + case 2: + if (i == 0 || i == 13) + continue; /* IRQ0 & IRQ13 not connected */ + /* fall through */ + default: + if (i == 2) + continue; /* IRQ2 is never connected */ + } + + intsrc.mpc_srcbusirq = i; + intsrc.mpc_dstirq = i ? i : 2; /* IRQ0 to INTIN2 */ + MP_intsrc_info(&intsrc); + } + + intsrc.mpc_irqtype = mp_ExtINT; + intsrc.mpc_srcbusirq = 0; + intsrc.mpc_dstirq = 0; /* 8259A to INTIN0 */ + MP_intsrc_info(&intsrc); +} + +static inline void __init construct_default_ISA_mptable(int mpc_default_type) +{ + struct mpc_config_processor processor; + struct mpc_config_bus bus; + struct mpc_config_ioapic ioapic; + struct mpc_config_lintsrc lintsrc; + int linttypes[2] = { mp_ExtINT, mp_NMI }; + int i; + + /* + * local APIC has default address + */ + mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + + /* + * 2 CPUs, numbered 0 & 1. + */ + processor.mpc_type = MP_PROCESSOR; + /* Either an integrated APIC or a discrete 82489DX. */ + processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + processor.mpc_cpuflag = CPU_ENABLED; + processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) | + (boot_cpu_data.x86_model << 4) | + boot_cpu_data.x86_mask; + processor.mpc_featureflag = boot_cpu_data.x86_capability; + processor.mpc_reserved[0] = 0; + processor.mpc_reserved[1] = 0; + for (i = 0; i < 2; i++) { + processor.mpc_apicid = i; + MP_processor_info(&processor); + } + + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + switch (mpc_default_type) { + default: + printk("???\nUnknown standard configuration %d\n", + mpc_default_type); + /* fall through */ + case 1: + case 5: + memcpy(bus.mpc_bustype, "ISA ", 6); + break; + case 2: + case 6: + case 3: + memcpy(bus.mpc_bustype, "EISA ", 6); + break; + case 4: + case 7: + memcpy(bus.mpc_bustype, "MCA ", 6); + } + MP_bus_info(&bus); + if (mpc_default_type > 4) { + bus.mpc_busid = 1; + memcpy(bus.mpc_bustype, "PCI ", 6); + MP_bus_info(&bus); + } + + ioapic.mpc_type = MP_IOAPIC; + ioapic.mpc_apicid = 2; + ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01; + ioapic.mpc_flags = MPC_APIC_USABLE; + ioapic.mpc_apicaddr = 0xFEC00000; + MP_ioapic_info(&ioapic); + + /* + * We set up most of the low 16 IO-APIC pins according to MPS rules. + */ + construct_default_ioirq_mptable(mpc_default_type); + + lintsrc.mpc_type = MP_LINTSRC; + lintsrc.mpc_irqflag = 0; /* conforming */ + lintsrc.mpc_srcbusid = 0; + lintsrc.mpc_srcbusirq = 0; + lintsrc.mpc_destapic = MP_APIC_ALL; + for (i = 0; i < 2; i++) { + lintsrc.mpc_irqtype = linttypes[i]; + lintsrc.mpc_destapiclint = i; + MP_lintsrc_info(&lintsrc); + } +} + static struct intel_mp_floating *mpf_found; /* @@ -332,83 +457,43 @@ void __init get_smp_config (void) printk(" Virtual Wire compatibility mode.\n"); pic_mode = 0; } - /* - * default CPU id - if it's different in the mptable - * then we change it before first using it. - */ - boot_cpu_id = 0; + /* * Now see if we need to read further. */ if (mpf->mpf_feature1 != 0) { + printk("Default MP configuration #%d\n", mpf->mpf_feature1); + construct_default_ISA_mptable(mpf->mpf_feature1); - /* - * local APIC has default address - */ - mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; + } else if (mpf->mpf_physptr) { /* - * 2 CPUs, numbered 0 & 1. + * Read the physical hardware table. Anything here will + * override the defaults. */ - phys_cpu_present_map = 3; - num_processors = 2; + smp_read_mpc((void *)mpf->mpf_physptr); - nr_ioapics = 1; - mp_ioapics[0].mpc_apicaddr = 0xFEC00000; - mp_ioapics[0].mpc_apicid = 2; /* - * Save the default type number, we - * need it later to set the IO-APIC - * up properly: + * If there are no explicit MP IRQ entries, then we are + * broken. We set up most of the low 16 IO-APIC pins to + * ISA defaults and hope it will work. */ - mpc_default_type = mpf->mpf_feature1; + if (!mp_irq_entries) { + struct mpc_config_bus bus; - printk("Bus #0 is "); - } + printk("BIOS bug, no explicit IRQ entries, using default mptable. (tell your hw vendor)\n"); - switch (mpf->mpf_feature1) { - case 1: - case 5: - printk("ISA\n"); - break; - case 2: - printk("EISA with no IRQ0 and no IRQ13 DMA chaining\n"); - break; - case 6: - case 3: - printk("EISA\n"); - break; - case 4: - case 7: - printk("MCA\n"); - break; - case 0: - if (!mpf->mpf_physptr) - BUG(); - break; - default: - printk("???\nUnknown standard configuration %d\n", - mpf->mpf_feature1); - return; - } - if (mpf->mpf_feature1 > 4) { - printk("Bus #1 is PCI\n"); + bus.mpc_type = MP_BUS; + bus.mpc_busid = 0; + memcpy(bus.mpc_bustype, "ISA ", 6); + MP_bus_info(&bus); - /* - * Set local APIC version to the integrated form. - * It's initialized to zero otherwise, representing - * a discrete 82489DX. - */ - apic_version[0] = 0x10; - apic_version[1] = 0x10; - } - /* - * Read the physical hardware table. Anything here will override the - * defaults. - */ - if (mpf->mpf_physptr) - smp_read_mpc((void *)mpf->mpf_physptr); + construct_default_ioirq_mptable(0); + } + + } else + BUG(); printk("Processors: %d\n", num_processors); /* diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index f5a035cc712e..1c8b1e0a6f38 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -1110,7 +1110,7 @@ static int (*get_free_region) (unsigned long base, * * 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 + * MTRR is added. The details and hardware specifics of each processor's * 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. @@ -1125,13 +1125,13 @@ static int (*get_free_region) (unsigned long base, * * The available types are * - * MTRR_TYPE_UNCACHEABLE - No caching + * %MTRR_TYPE_UNCACHEABLE - No caching * - * MTRR_TYPE_WRITEBACK - Write data back in bursts whenever + * %MTRR_TYPE_WRITEBACK - Write data back in bursts whenever * - * MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts * - * MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * %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. diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 71407c0eae91..60bb4177b371 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -495,6 +495,10 @@ static inline void parse_mem_cmdline (char ** cmdline_p) if (!memcmp(from+4, "nopentium", 9)) { from += 9+4; boot_cpu_data.x86_capability &= ~X86_FEATURE_PSE; + } else if (!memcmp(from+4, "exactmap", 8)) { + from += 8+4; + e820.nr_map = 0; + usermem = 1; } else { /* If the user specifies memory size, we * blow away any automatically generated diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 12f193c714d8..94ba5c49f70d 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -111,108 +111,26 @@ struct tlb_state cpu_tlbstate[NR_CPUS] = {[0 ... NR_CPUS-1] = { &init_mm, 0 }}; * We use 'broadcast', CPU->CPU IPIs and self-IPIs too. */ -static unsigned int cached_APIC_ICR; -static unsigned int cached_APIC_ICR2; - -/* - * Caches reserved bits, APIC reads are (mildly) expensive - * and force otherwise unnecessary CPU synchronization. - * - * (We could cache other APIC registers too, but these are the - * main ones used in RL.) - */ -#define slow_ICR (apic_read(APIC_ICR) & ~0xFDFFF) -#define slow_ICR2 (apic_read(APIC_ICR2) & 0x00FFFFFF) - -void cache_APIC_registers (void) -{ - cached_APIC_ICR = slow_ICR; - cached_APIC_ICR2 = slow_ICR2; - mb(); -} - -static inline unsigned int __get_ICR (void) -{ -#if FORCE_READ_AROUND_WRITE - /* - * Wait for the APIC to become ready - this should never occur. It's - * a debugging check really. - */ - int count = 0; - unsigned int cfg; - - while (count < 1000) - { - cfg = slow_ICR; - if (!(cfg&(1<<12))) - return cfg; - printk("CPU #%d: ICR still busy [%08x]\n", - smp_processor_id(), cfg); - irq_err_count++; - count++; - udelay(10); - } - printk("CPU #%d: previous IPI still not cleared after 10mS\n", - smp_processor_id()); - return cfg; -#else - return cached_APIC_ICR; -#endif -} - -static inline unsigned int __get_ICR2 (void) -{ -#if FORCE_READ_AROUND_WRITE - return slow_ICR2; -#else - return cached_APIC_ICR2; -#endif -} - -#define LOGICAL_DELIVERY 1 - static inline int __prepare_ICR (unsigned int shortcut, int vector) { - unsigned int cfg; - - cfg = __get_ICR(); - cfg |= APIC_DEST_DM_FIXED|shortcut|vector -#if LOGICAL_DELIVERY - |APIC_DEST_LOGICAL -#endif - ; - - return cfg; + return APIC_DM_FIXED | shortcut | vector | APIC_DEST_LOGICAL; } static inline int __prepare_ICR2 (unsigned int mask) { - unsigned int cfg; - - cfg = __get_ICR2(); -#if LOGICAL_DELIVERY - cfg |= SET_APIC_DEST_FIELD(mask); -#else - cfg |= SET_APIC_DEST_FIELD(mask); -#endif - - return cfg; + return SET_APIC_DEST_FIELD(mask); } static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) { + /* + * Subtle. In the case of the 'never do double writes' workaround + * we have to lock out interrupts to be safe. As we don't care + * of the value read we use an atomic rmw access to avoid costly + * cli/sti. Otherwise we use an even cheaper single atomic write + * to the APIC. + */ unsigned int cfg; -/* - * Subtle. In the case of the 'never do double writes' workaround we - * have to lock out interrupts to be safe. Otherwise it's just one - * single atomic write to the APIC, no need for cli/sti. - */ -#if FORCE_READ_AROUND_WRITE - unsigned long flags; - - __save_flags(flags); - __cli(); -#endif /* * No need to touch the target chip field @@ -222,10 +140,7 @@ static inline void __send_IPI_shortcut(unsigned int shortcut, int vector) /* * Send the IPI. The write to APIC_ICR fires this off. */ - apic_write(APIC_ICR, cfg); -#if FORCE_READ_AROUND_WRITE - __restore_flags(flags); -#endif + apic_write_around(APIC_ICR, cfg); } static inline void send_IPI_allbutself(int vector) @@ -252,19 +167,16 @@ void send_IPI_self(int vector) static inline void send_IPI_mask(int mask, int vector) { unsigned long cfg; -#if FORCE_READ_AROUND_WRITE unsigned long flags; __save_flags(flags); __cli(); -#endif /* * prepare target chip field */ - cfg = __prepare_ICR2(mask); - apic_write(APIC_ICR2, cfg); + apic_write_around(APIC_ICR2, cfg); /* * program the ICR @@ -274,10 +186,8 @@ static inline void send_IPI_mask(int mask, int vector) /* * Send the IPI. The write to APIC_ICR fires this off. */ - apic_write(APIC_ICR, cfg); -#if FORCE_READ_AROUND_WRITE + apic_write_around(APIC_ICR, cfg); __restore_flags(flags); -#endif } /* diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 50b743a99c43..ae84ff2b5749 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -28,6 +28,7 @@ * from Jose Renau * Ingo Molnar : various cleanups and rewrites * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. + * Maciej W. Rozycki : Bits for genuine 82489DX APICs */ #include @@ -489,11 +490,43 @@ static int __init fork_by_hand(void) return do_fork(CLONE_VM|CLONE_PID, 0, ®s); } +#if APIC_DEBUG +static inline void inquire_remote_apic(int apicid) +{ + int i, regs[] = { APIC_ID >> 4, APIC_LVR >> 4, APIC_SPIV >> 4 }; + char *names[] = { "ID", "VERSION", "SPIV" }; + int timeout, status; + + printk("Inquiring remote APIC #%d...\n", apicid); + + for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) { + printk("... APIC #%d %s: ", apicid, names[i]); + + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR, APIC_DM_REMRD | regs[i]); + + timeout = 0; + do { + udelay(100); + status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK; + } while (status == APIC_ICR_RR_INPROG && timeout++ < 1000); + + switch (status) { + case APIC_ICR_RR_VALID: + status = apic_read(APIC_RRR); + printk("%08x\n", status); + break; + default: + printk("failed\n"); + } + } +} +#endif + static void __init do_boot_cpu (int apicid) { - unsigned long cfg; struct task_struct *idle; - unsigned long send_status, accept_status; + unsigned long send_status, accept_status, boot_status, maxlvt; int timeout, num_starts, j, cpu; unsigned long start_eip; @@ -527,7 +560,7 @@ static void __init do_boot_cpu (int apicid) start_eip = setup_trampoline(); /* So we see what's up */ - printk("Booting processor %d eip %lx\n", cpu, start_eip); + printk("Booting processor %d/%d eip %lx\n", cpu, apicid, start_eip); stack_start.esp = (void *) (1024 + PAGE_SIZE + (char *)idle); /* @@ -549,16 +582,17 @@ static void __init do_boot_cpu (int apicid) * Be paranoid about clearing APIC errors. */ if (APIC_INTEGRATED(apic_version[apicid])) { - apic_readaround(APIC_SPIV); + apic_read_around(APIC_SPIV); apic_write(APIC_ESR, 0); - accept_status = (apic_read(APIC_ESR) & 0xEF); + apic_read(APIC_ESR); } /* * Status is now clean */ - send_status = 0; + send_status = 0; accept_status = 0; + boot_status = 0; /* * Starting actual IPI sequence... @@ -567,37 +601,41 @@ static void __init do_boot_cpu (int apicid) Dprintk("Asserting INIT.\n"); /* - * Turn INIT on - */ - cfg = apic_read(APIC_ICR2); - cfg &= 0x00FFFFFF; - - /* - * Target chip + * Turn INIT on target chip */ - apic_write(APIC_ICR2, cfg | SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); /* * Send IPI */ - cfg = apic_read(APIC_ICR); - cfg &= ~0xCDFFF; - cfg |= (APIC_DEST_LEVELTRIG | APIC_DEST_ASSERT | APIC_DEST_DM_INIT); - apic_write(APIC_ICR, cfg); + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT + | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); + + mdelay(10); - udelay(200); Dprintk("Deasserting INIT.\n"); /* Target chip */ - cfg = apic_read(APIC_ICR2); - cfg &= 0x00FFFFFF; - apic_write(APIC_ICR2, cfg|SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); /* Send IPI */ - cfg = apic_read(APIC_ICR); - cfg &= ~0xCDFFF; - cfg |= (APIC_DEST_LEVELTRIG | APIC_DEST_DM_INIT); - apic_write(APIC_ICR, cfg); + apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); + + Dprintk("Waiting for send to finish...\n"); + timeout = 0; + do { + Dprintk("+"); + udelay(100); + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; + } while (send_status && (timeout++ < 1000)); /* * Should we send STARTUP IPIs ? @@ -616,9 +654,11 @@ static void __init do_boot_cpu (int apicid) */ Dprintk("#startup loops: %d.\n", num_starts); + maxlvt = get_maxlvt(); + for (j = 1; j <= num_starts; j++) { Dprintk("Sending STARTUP #%d.\n",j); - apic_readaround(APIC_SPIV); + apic_read_around(APIC_SPIV); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); Dprintk("After apic_write.\n"); @@ -628,17 +668,12 @@ static void __init do_boot_cpu (int apicid) */ /* Target chip */ - cfg = apic_read(APIC_ICR2); - cfg &= 0x00FFFFFF; - apic_write(APIC_ICR2, cfg | SET_APIC_DEST_FIELD(apicid)); + apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); /* Boot on the stack */ - cfg = apic_read(APIC_ICR); - cfg &= ~0xCDFFF; - cfg |= (APIC_DEST_DM_STARTUP | (start_eip >> 12)); - /* Kick the second */ - apic_write(APIC_ICR, cfg); + apic_write_around(APIC_ICR, APIC_DM_STARTUP + | (start_eip >> 12)); Dprintk("Startup point 1.\n"); @@ -647,13 +682,20 @@ static void __init do_boot_cpu (int apicid) do { Dprintk("+"); udelay(100); - send_status = apic_read(APIC_ICR) & 0x1000; + send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); /* * Give the other CPU some time to accept the IPI. */ udelay(200); + /* + * Due to the Pentium erratum 3AP. + */ + if (maxlvt > 3) { + apic_read_around(APIC_SPIV); + apic_write(APIC_ESR, 0); + } accept_status = (apic_read(APIC_ESR) & 0xEF); if (send_status || accept_status) break; @@ -676,7 +718,7 @@ static void __init do_boot_cpu (int apicid) /* * Wait 5s total for a response */ - for (timeout = 0; timeout < 1000000000; timeout++) { + for (timeout = 0; timeout < 50000; timeout++) { if (test_bit(cpu, &cpu_callin_map)) break; /* It has booted */ udelay(100); @@ -687,15 +729,22 @@ static void __init do_boot_cpu (int apicid) Dprintk("OK.\n"); printk("CPU%d: ", cpu); print_cpu_info(&cpu_data[cpu]); + Dprintk("CPU has booted.\n"); } else { + boot_status = 1; if (*((volatile unsigned char *)phys_to_virt(8192)) - == 0xA5) /* trampoline code not run */ + == 0xA5) + /* trampoline started but...? */ printk("Stuck ??\n"); else - printk("CPU booted but not responding.\n"); + /* trampoline code not run */ + printk("Not responding.\n"); +#if APIC_DEBUG + inquire_remote_apic(apicid); +#endif } - Dprintk("CPU has booted.\n"); - } else { + } + if (send_status || accept_status || boot_status) { x86_cpu_to_apicid[cpu] = -1; x86_apicid_to_cpu[apicid] = -1; cpucount--; @@ -858,6 +907,7 @@ void __init smp_boot_cpus(void) Dprintk("Getting LVT1: %x\n", reg); } + connect_bsp_APIC(); setup_local_APIC(); if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_id) @@ -877,7 +927,7 @@ void __init smp_boot_cpus(void) if (!(phys_cpu_present_map & (1 << apicid))) continue; - if ((max_cpus >= 0) && (max_cpus < cpucount+1)) + if ((max_cpus >= 0) && (max_cpus <= cpucount+1)) continue; do_boot_cpu(apicid); @@ -934,7 +984,6 @@ void __init smp_boot_cpus(void) printk(KERN_WARNING "WARNING: SMP operation may be unreliable with B stepping processors.\n"); Dprintk("Boot done.\n"); - cache_APIC_registers(); #ifndef CONFIG_VISWS /* * Here we can be sure that there is an IO-APIC in the system. Let's diff --git a/arch/i386/kernel/visws_apic.c b/arch/i386/kernel/visws_apic.c index be80cd628f09..288f83e8f7c1 100644 --- a/arch/i386/kernel/visws_apic.c +++ b/arch/i386/kernel/visws_apic.c @@ -376,7 +376,7 @@ void init_VISWS_APIC_irqs(void) for (i = 0; i < 16; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = 0; - irq_desc[i].depth = 0; + irq_desc[i].depth = 1; /* * Cobalt IRQs are mapped to standard ISA diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index 0ddfe3f05209..6d9e87e2990f 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -1019,7 +1019,7 @@ static void register_irq_proc (unsigned int irq) irq_dir[irq] = proc_mkdir(name, root_irq_dir); /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]); + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); entry->nlink = 1; entry->data = (void *)(long)irq; @@ -1040,7 +1040,7 @@ void init_irq_proc (void) root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir); + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index e4b2790326bd..bc44db97655a 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -733,7 +733,7 @@ static void register_irq_proc (unsigned int irq) irq_dir[irq] = proc_mkdir(name, root_irq_dir); /* create /proc/irq/1234/smp_affinity */ - entry = create_proc_entry("smp_affinity", 0700, irq_dir[irq]); + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); entry->nlink = 1; entry->data = (void *)irq; @@ -754,7 +754,7 @@ void init_irq_proc (void) root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ - entry = create_proc_entry("prof_cpu_mask", 0700, root_irq_dir); + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index bd9181933e42..7c8998eaa11b 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.52 1999/12/21 04:02:17 davem Exp $ +# $Id: Makefile,v 1.53 2000/03/31 04:06:19 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc/kernel/cpu.c b/arch/sparc/kernel/cpu.c index 4a9c6cdcbd32..0efc28f657f4 100644 --- a/arch/sparc/kernel/cpu.c +++ b/arch/sparc/kernel/cpu.c @@ -92,7 +92,7 @@ struct cpu_iu_info linux_sparc_chips[] = { /* Someone please write the code to support this beast! ;) */ { 2, 0, "Bipolar Integrated Technology - B5010"}, { 3, 0, "LSI Logic Corporation - unknown-type"}, - { 4, 0, "Texas Instruments, Inc. - SuperSparc 50"}, + { 4, 0, "Texas Instruments, Inc. - SuperSparc-(II)"}, /* SparcClassic -- borned STP1010TAB-50*/ { 4, 1, "Texas Instruments, Inc. - MicroSparc"}, { 4, 2, "Texas Instruments, Inc. - MicroSparc II"}, diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index d05153f7ac4e..742d7db97a6b 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -60,7 +60,7 @@ pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr) static void pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr) { - if (current->personality & PER_BSD) + if (current->personality == PER_SUNOS) pt_succ_return (regs, val); else pt_succ_return_linux (regs, val, addr); @@ -155,7 +155,7 @@ static inline void read_sunos_user(struct pt_regs *regs, unsigned long offset, pt_error_return(regs, EIO); return; } - if (current->personality & PER_BSD) + if (current->personality == PER_SUNOS) pt_succ_return (regs, v); else pt_succ_return_linux (regs, v, addr); @@ -310,8 +310,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } - if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) - || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) + || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { unsigned long flags; if(child == current) { @@ -349,9 +349,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_succ_return(regs, 0); goto out; } - if (!(child->flags & PF_PTRACED) - && ((current->personality & PER_BSD) && (request != PTRACE_SUNATTACH)) - && (!(current->personality & PER_BSD) && (request != PTRACE_ATTACH))) { + if (!(child->flags & PF_PTRACED)) { pt_error_return(regs, ESRCH); goto out; } diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 484ffac82d32..0af0b63d7677 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.101 2000/01/21 11:38:38 jj Exp $ +/* $Id: signal.c,v 1.102 2000/04/08 02:11:36 davem Exp $ * linux/arch/sparc/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 36670ab93b58..675ac5890d4e 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.118 2000/03/26 11:28:56 davem Exp $ +/* $Id: sys_sunos.c,v 1.120 2000/04/08 08:32:14 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -50,6 +50,7 @@ #include #include #include +#include #include /* for sunos_select */ @@ -69,7 +70,6 @@ asmlinkage unsigned long sunos_mmap(unsigned long addr, unsigned long len, down(¤t->mm->mmap_sem); lock_kernel(); - current->personality |= PER_BSD; if(flags & MAP_NORESERVE) { static int cnt; if (cnt++ < 10) @@ -582,7 +582,6 @@ asmlinkage int sunos_select(int width, fd_set *inp, fd_set *outp, fd_set *exp, s /* SunOS binaries expect that select won't change the tvp contents */ lock_kernel(); - current->personality |= STICKY_TIMEOUTS; ret = sys_select (width, inp, outp, exp, tvp); if (ret == -EINTR && tvp) { time_t sec, usec; @@ -712,7 +711,7 @@ static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) * address to create a socket and bind it to a reserved * port on this system */ - if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount)) + if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount))) return -EFAULT; server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -803,14 +802,14 @@ sunos_mount(char *type, char *dir, int flags, void *data) dev_fname = getname(data); } else if(strcmp(type_page, "nfs") == 0) { ret = sunos_nfs_mount (dir_page, flags, data); - goto out2 + goto out2; } else if(strcmp(type_page, "ufs") == 0) { printk("Warning: UFS filesystem mounts unsupported.\n"); ret = -ENODEV; - goto out2 + goto out2; } else if(strcmp(type_page, "proc")) { ret = -ENODEV; - goto out2 + goto out2; } ret = PTR_ERR(dev_fname); if (IS_ERR(dev_fname)) @@ -1054,18 +1053,6 @@ asmlinkage int sunos_shmsys(int op, unsigned long arg1, unsigned long arg2, return rval; } -asmlinkage int sunos_open(const char *filename, int flags, int mode) -{ - int ret; - - lock_kernel(); - current->personality |= PER_BSD; - ret = sys_open (filename, flags, mode); - unlock_kernel(); - return ret; -} - - #define SUNOS_EWOULDBLOCK 35 /* see the sunos man page read(2v) for an explanation @@ -1200,8 +1187,6 @@ sunos_sigaction(int sig, const struct old_sigaction *act, struct k_sigaction new_ka, old_ka; int ret; - current->personality |= PER_BSD; - if(act) { old_sigset_t mask; diff --git a/arch/sparc/kernel/systbls.S b/arch/sparc/kernel/systbls.S index 431f03daf2d5..de3e68560612 100644 --- a/arch/sparc/kernel/systbls.S +++ b/arch/sparc/kernel/systbls.S @@ -79,7 +79,7 @@ sys_call_table: .globl sunos_sys_table sunos_sys_table: /*0*/ .long sunos_indir, sys_exit, sys_fork - .long sunos_read, sunos_write, sunos_open + .long sunos_read, sunos_write, sys_open .long sys_close, sunos_wait4, sys_creat .long sys_link, sys_unlink, sunos_execv .long sys_chdir, sunos_nosys, sys_mknod diff --git a/arch/sparc/lib/Makefile b/arch/sparc/lib/Makefile index f5e316534fe7..d7b3c7554691 100644 --- a/arch/sparc/lib/Makefile +++ b/arch/sparc/lib/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.33 2000/03/16 00:52:07 anton Exp $ +# $Id: Makefile,v 1.34 2000/03/31 04:06:20 davem Exp $ # Makefile for Sparc library files.. # diff --git a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile index 440e4fc61167..82bfaf6bf287 100644 --- a/arch/sparc/mm/Makefile +++ b/arch/sparc/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.36 2000/01/29 01:09:05 anton Exp $ +# $Id: Makefile,v 1.37 2000/03/31 04:06:22 davem Exp $ # Makefile for the linux Sparc-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 3c8b079c3255..24fc68c5a585 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -1,4 +1,4 @@ -/* $Id: sun4c.c,v 1.190 2000/02/14 04:52:34 jj Exp $ +/* $Id: sun4c.c,v 1.191 2000/04/08 02:11:41 davem Exp $ * sun4c.c: Doing in software what should be done in hardware. * * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) diff --git a/arch/sparc64/defconfig b/arch/sparc64/defconfig index 2f9febcbe57c..3ceeb634b255 100644 --- a/arch/sparc64/defconfig +++ b/arch/sparc64/defconfig @@ -370,6 +370,7 @@ CONFIG_VIDEO_DEV=y CONFIG_AUTOFS_FS=m CONFIG_AUTOFS4_FS=m # CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set CONFIG_AFFS_FS=m # CONFIG_HFS_FS is not set CONFIG_BFS_FS=m @@ -379,47 +380,52 @@ CONFIG_MSDOS_FS=m CONFIG_VFAT_FS=m CONFIG_EFS_FS=m CONFIG_CRAMFS=m +CONFIG_RAMFS=m CONFIG_ISO9660_FS=m -# CONFIG_JOLIET is not set +CONFIG_JOLIET=y CONFIG_MINIX_FS=m # CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set CONFIG_HPFS_FS=m CONFIG_PROC_FS=y # CONFIG_DEVFS_FS is not set # CONFIG_DEVFS_DEBUG is not set CONFIG_DEVPTS_FS=y # CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set CONFIG_ROMFS_FS=m CONFIG_EXT2_FS=y CONFIG_SYSV_FS=m -# CONFIG_SYSV_FS_WRITE is not set +CONFIG_SYSV_FS_WRITE=y CONFIG_UDF_FS=m -# CONFIG_UDF_RW is not set +CONFIG_UDF_RW=y CONFIG_UFS_FS=m -# CONFIG_UFS_FS_WRITE is not set +CONFIG_UFS_FS_WRITE=y # # Network File Systems # CONFIG_CODA_FS=m CONFIG_NFS_FS=y +CONFIG_NFS_V3=y # CONFIG_ROOT_NFS is not set CONFIG_NFSD=m -# CONFIG_NFSD_V3 is not set +CONFIG_NFSD_V3=y CONFIG_SUNRPC=y CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y CONFIG_SMB_FS=m CONFIG_NCP_FS=m -# CONFIG_NCPFS_PACKET_SIGNING is not set -# CONFIG_NCPFS_IOCTL_LOCKING is not set -# CONFIG_NCPFS_STRONG is not set -# CONFIG_NCPFS_NFS_NS is not set -# CONFIG_NCPFS_OS2_NS is not set -# CONFIG_NCPFS_SMALLDOS is not set -# CONFIG_NCPFS_MOUNT_SUBDIR is not set -# CONFIG_NCPFS_NDS_DOMAINS is not set -# CONFIG_NCPFS_NLS is not set -# CONFIG_NCPFS_EXTRAS is not set +CONFIG_NCPFS_PACKET_SIGNING=y +CONFIG_NCPFS_IOCTL_LOCKING=y +CONFIG_NCPFS_STRONG=y +CONFIG_NCPFS_NFS_NS=y +CONFIG_NCPFS_OS2_NS=y +CONFIG_NCPFS_SMALLDOS=y +CONFIG_NCPFS_MOUNT_SUBDIR=y +CONFIG_NCPFS_NDS_DOMAINS=y +CONFIG_NCPFS_NLS=y +CONFIG_NCPFS_EXTRAS=y # # Partition Types diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile index 5e37c94b4e77..5c0d38d0d074 100644 --- a/arch/sparc64/kernel/Makefile +++ b/arch/sparc64/kernel/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.52 2000/03/19 07:00:29 ecd Exp $ +# $Id: Makefile,v 1.53 2000/03/31 04:06:22 davem Exp $ # Makefile for the linux kernel. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index c72f7272faa2..b927f499ad4b 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -230,7 +230,7 @@ static int load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs) return retval; /* OK, This is the point of no return */ - current->personality = PER_LINUX; + current->personality = PER_SUNOS; current->mm->end_code = ex.a_text + (current->mm->start_code = N_TXTADDR(ex)); diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index a557721799ca..65f7450cb675 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,4 +1,4 @@ -/* $Id: pci_sabre.c,v 1.16 2000/03/25 05:18:12 davem Exp $ +/* $Id: pci_sabre.c,v 1.17 2000/03/31 04:06:59 davem Exp $ * pci_sabre.c: Sabre specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -1086,7 +1086,7 @@ static void __init sabre_scan_bus(struct pci_controller_info *p) sabre_bus = pci_scan_bus(p->pci_first_busno, p->pci_ops, &p->pbm_A); - +#if 0 { unsigned int devfn; u8 *addr; @@ -1096,7 +1096,7 @@ static void __init sabre_scan_bus(struct pci_controller_info *p) devfn, PCI_LATENCY_TIMER); pci_config_write8(addr, 32); } - +#endif apb_init(p, sabre_bus); walk = &sabre_bus->children; diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index c582be060925..f061c417f875 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -68,7 +68,7 @@ pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr) static void pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr) { - if (current->personality & PER_BSD) + if (current->personality == PER_SUNOS) pt_succ_return (regs, val); else pt_succ_return_linux (regs, val, addr); @@ -164,8 +164,8 @@ asmlinkage void do_ptrace(struct pt_regs *regs) goto out; } - if (((current->personality & PER_BSD) && (request == PTRACE_SUNATTACH)) - || (!(current->personality & PER_BSD) && (request == PTRACE_ATTACH))) { + if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH) + || (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) { unsigned long flags; if(child == current) { @@ -203,9 +203,7 @@ asmlinkage void do_ptrace(struct pt_regs *regs) pt_succ_return(regs, 0); goto out; } - if (!(child->flags & PF_PTRACED) - && ((current->personality & PER_BSD) && (request != PTRACE_SUNATTACH)) - && (!(current->personality & PER_BSD) && (request != PTRACE_ATTACH))) { + if (!(child->flags & PF_PTRACED)) { pt_error_return(regs, ESRCH); goto out; } diff --git a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c index e7a50c150a92..efd6a64d131c 100644 --- a/arch/sparc64/kernel/signal.c +++ b/arch/sparc64/kernel/signal.c @@ -1,4 +1,4 @@ -/* $Id: signal.c,v 1.48 1999/12/15 22:24:52 davem Exp $ +/* $Id: signal.c,v 1.49 2000/04/08 02:11:46 davem Exp $ * arch/sparc64/kernel/signal.c * * Copyright (C) 1991, 1992 Linus Torvalds diff --git a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c index 423c5f6481ec..273643db6bf1 100644 --- a/arch/sparc64/kernel/signal32.c +++ b/arch/sparc64/kernel/signal32.c @@ -1,4 +1,4 @@ -/* $Id: signal32.c,v 1.60 2000/02/25 06:02:37 jj Exp $ +/* $Id: signal32.c,v 1.61 2000/04/08 02:11:46 davem Exp $ * arch/sparc64/kernel/signal32.c * * Copyright (C) 1991, 1992 Linus Torvalds diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index b1eb160ada71..06258d9b27b8 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.142 2000/03/24 04:17:38 davem Exp $ +/* $Id: sys_sparc32.c,v 1.144 2000/04/08 02:11:47 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -3542,6 +3542,18 @@ struct nfsctl_fhparm32 { s32 gf32_version; }; +struct nfsctl_fdparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_version; +}; + +struct nfsctl_fsparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_maxlen; +}; + struct nfsctl_arg32 { s32 ca32_version; /* safeguard */ union { @@ -3550,15 +3562,17 @@ struct nfsctl_arg32 { struct nfsctl_export32 u32_export; struct nfsctl_uidmap32 u32_umap; struct nfsctl_fhparm32 u32_getfh; - u32 u32_debug; + struct nfsctl_fdparm32 u32_getfd; + struct nfsctl_fsparm32 u32_getfs; } u; #define ca32_svc u.u32_svc #define ca32_client u.u32_client #define ca32_export u.u32_export #define ca32_umap u.u32_umap #define ca32_getfh u.u32_getfh +#define ca32_getfd u.u32_getfd +#define ca32_getfs u.u32_getfs #define ca32_authd u.u32_authd -#define ca32_debug u.u32_debug }; union nfsctl_res32 { @@ -3689,6 +3703,38 @@ static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32 return err; } +static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfd.gd_addr, + &arg32->ca32_getfd.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfd.gd_path, + &arg32->ca32_getfd.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfd.gd_version, + &arg32->ca32_getfd.gd32_version); + return err; +} + +static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfs.gd_addr, + &arg32->ca32_getfs.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfs.gd_path, + &arg32->ca32_getfs.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfs.gd_maxlen, + &arg32->ca32_getfs.gd32_maxlen); + return err; +} + /* This really doesn't need translations, we are only passing * back a union which contains opaque nfs file handle data. */ @@ -3727,6 +3773,7 @@ int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsct err = nfs_clnt32_trans(karg, arg32); break; case NFSCTL_EXPORT: + case NFSCTL_UNEXPORT: err = nfs_exp32_trans(karg, arg32); break; /* This one is unimplemented, be we're ready for it. */ @@ -3736,6 +3783,12 @@ int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsct case NFSCTL_GETFH: err = nfs_getfh32_trans(karg, arg32); break; + case NFSCTL_GETFD: + err = nfs_getfd32_trans(karg, arg32); + break; + case NFSCTL_GETFS: + err = nfs_getfs32_trans(karg, arg32); + break; default: err = -EINVAL; break; @@ -3747,7 +3800,12 @@ int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsct err = sys_nfsservctl(cmd, karg, kres); set_fs(oldfs); - if(!err && cmd == NFSCTL_GETFH) + if (err) + goto done; + + if((cmd == NFSCTL_GETFH) || + (cmd == NFSCTL_GETFD) || + (cmd == NFSCTL_GETFS)) err = nfs_getfh32_res_trans(kres, res32); done: diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 291b174ac8db..7b44226da1a8 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.43 2000/03/26 11:28:53 davem Exp $ +/* $Id: sys_sunos32.c,v 1.44 2000/04/08 02:11:50 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -45,6 +45,7 @@ #include #include #include +#include #include /* for sunos_select */ @@ -69,7 +70,6 @@ asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 of down(¤t->mm->mmap_sem); lock_kernel(); - current->personality |= PER_BSD; if(flags & MAP_NORESERVE) { static int cnt; if (cnt++ < 10) @@ -549,7 +549,6 @@ asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp_x) /* SunOS binaries expect that select won't change the tvp contents */ lock_kernel(); - current->personality |= STICKY_TIMEOUTS; ret = sys32_select (width, inp, outp, exp, tvp_x); if (ret == -EINTR && tvp_x) { struct timeval32 *tvp = (struct timeval32 *)A(tvp_x); @@ -685,7 +684,7 @@ static int sunos_nfs_mount(char *dir_name, int linux_flags, void *data) * address to create a socket and bind it to a reserved * port on this system */ - if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount)) + if (copy_from_user(&sunos_mount, data, sizeof(sunos_mount))) return -EFAULT; server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -776,14 +775,14 @@ sunos_mount(char *type, char *dir, int flags, void *data) dev_fname = getname(data); } else if(strcmp(type_page, "nfs") == 0) { ret = sunos_nfs_mount (dir_page, flags, data); - goto out2 + goto out2; } else if(strcmp(type_page, "ufs") == 0) { printk("Warning: UFS filesystem mounts unsupported.\n"); ret = -ENODEV; - goto out2 + goto out2; } else if(strcmp(type_page, "proc")) { ret = -ENODEV; - goto out2 + goto out2; } ret = PTR_ERR(dev_fname); if (IS_ERR(dev_fname)) @@ -1214,7 +1213,6 @@ asmlinkage int sunos_open(u32 fname, int flags, int mode) { const char *filename = (const char *)(long)fname; - current->personality |= PER_BSD; return sparc32_open(filename, flags, mode); } @@ -1350,8 +1348,6 @@ asmlinkage int sunos_sigaction (int sig, u32 act, u32 oact) struct k_sigaction new_ka, old_ka; int ret; - current->personality |= PER_BSD; - if (act) { old_sigset_t32 mask; diff --git a/arch/sparc64/lib/Makefile b/arch/sparc64/lib/Makefile index e70e28e5297b..548ef0ac4448 100644 --- a/arch/sparc64/lib/Makefile +++ b/arch/sparc64/lib/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.21 2000/03/27 10:38:41 davem Exp $ +# $Id: Makefile,v 1.22 2000/03/31 04:06:23 davem Exp $ # Makefile for Sparc library files.. # diff --git a/arch/sparc64/mm/Makefile b/arch/sparc64/mm/Makefile index 039195944beb..36b871af5cbd 100644 --- a/arch/sparc64/mm/Makefile +++ b/arch/sparc64/mm/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.6 2000/01/31 01:30:49 davem Exp $ +# $Id: Makefile,v 1.7 2000/03/31 04:06:24 davem Exp $ # Makefile for the linux Sparc64-specific parts of the memory manager. # # Note! Dependencies are done automagically by 'make dep', which also diff --git a/arch/sparc64/prom/Makefile b/arch/sparc64/prom/Makefile index 88ca5251b5b0..04471ab8510c 100644 --- a/arch/sparc64/prom/Makefile +++ b/arch/sparc64/prom/Makefile @@ -1,4 +1,4 @@ -# $Id: Makefile,v 1.5 1999/12/21 04:02:26 davem Exp $ +# $Id: Makefile,v 1.6 2000/03/31 04:06:25 davem Exp $ # Makefile for the Sun Boot PROM interface library under # Linux. # diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index 2c17d664d2ff..57e50cd8d021 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -1,4 +1,4 @@ -/* $Id: fs.c,v 1.17 2000/03/10 04:43:30 davem Exp $ +/* $Id: fs.c,v 1.18 2000/04/08 02:11:54 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -506,7 +506,7 @@ asmlinkage int solaris_fstatvfs(unsigned int fd, u32 buf) unlock_kernel(); fput(file); } -out: + return error; } diff --git a/arch/sparc64/solaris/misc.c b/arch/sparc64/solaris/misc.c index c9341b0fd83d..09b12cbb7b7d 100644 --- a/arch/sparc64/solaris/misc.c +++ b/arch/sparc64/solaris/misc.c @@ -1,4 +1,4 @@ -/* $Id: misc.c,v 1.23 2000/03/13 21:57:34 davem Exp $ +/* $Id: misc.c,v 1.24 2000/04/08 02:11:55 davem Exp $ * misc.c: Miscelaneous syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -54,7 +54,7 @@ static u32 do_solaris_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u64 o unsigned long retval, ret_type; lock_kernel(); - current->personality |= PER_SVR4; + current->personality = PER_SVR4; if (flags & MAP_NORESERVE) { static int cnt = 0; diff --git a/drivers/block/Config.in b/drivers/block/Config.in index 146cbce88ebc..4abd42a5426e 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -41,14 +41,16 @@ comment 'Additional Block Devices' tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET +tristate 'Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM N +if [ "$CONFIG_BLK_DEV_LVM" != "n" ]; then + bool ' LVM information in proc filesystem' CONFIG_LVM_PROC_FS Y +fi + bool 'Multiple devices driver support' CONFIG_BLK_DEV_MD dep_tristate ' Linear (append) mode' CONFIG_MD_LINEAR $CONFIG_BLK_DEV_MD dep_tristate ' RAID-0 (striping) mode' CONFIG_MD_STRIPED $CONFIG_BLK_DEV_MD #dep_tristate ' RAID-1 (mirroring) mode' CONFIG_MD_MIRRORING $CONFIG_BLK_DEV_MD #dep_tristate ' RAID-4/RAID-5 mode' CONFIG_MD_RAID5 $CONFIG_BLK_DEV_MD -if [ "$CONFIG_MD_LINEAR" = "y" -o "$CONFIG_MD_STRIPED" = "y" ]; then - bool ' Boot support (linear, striped)' CONFIG_MD_BOOT -fi tristate 'RAM disk support' CONFIG_BLK_DEV_RAM dep_bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 07f727910cb3..85cf628aa6de 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4076,7 +4076,7 @@ static int __init floppy_setup(char *str) } else DPRINT("botched floppy option\n"); DPRINT("Read linux/drivers/block/README.fd\n"); - return 1; + return 0; } static int have_no_fdc= -EIO; diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index 3f3237e8730e..a9bf5815733f 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -36,17 +36,13 @@ void __init device_init(void) { #ifdef CONFIG_PARPORT parport_init(); -#endif - /* - * I2O must come before block and char as the I2O layer may - * in future claim devices that block/char most not touch. - */ -#ifdef CONFIG_I2O - i2o_init(); #endif chr_dev_init(); blk_dev_init(); sti(); +#ifdef CONFIG_I2O + i2o_init(); +#endif #ifdef CONFIG_BLK_DEV_DAC960 DAC960_Initialize(); #endif diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 9383d36d3bc9..7b1818d15e6a 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -1077,6 +1077,9 @@ int __init blk_dev_init(void) #ifdef CONFIG_DASD dasd_init(); #endif +#ifdef CONFIG_BLK_DEV_LVM + lvm_init(); +#endif return 0; }; diff --git a/drivers/block/lvm.c b/drivers/block/lvm.c index 0c590db73714..df9bba37be90 100644 --- a/drivers/block/lvm.c +++ b/drivers/block/lvm.c @@ -287,7 +287,7 @@ static int lvm_reset_spindown = 0; static char pv_name[NAME_LEN]; /* static char rootvg[NAME_LEN] = { 0, }; */ static uint lv_open = 0; -static const char *const lvm_name = LVM_NAME; +const char *const lvm_name = LVM_NAME; static int lock = 0; static int loadtime = 0; static uint vg_count = 0; diff --git a/drivers/block/md.c b/drivers/block/md.c index 48796b4025f4..d9c1ab9c0b6b 100644 --- a/drivers/block/md.c +++ b/drivers/block/md.c @@ -46,10 +46,6 @@ extern unsigned long io_events[MAX_BLKDEV]; #include -#ifdef CONFIG_MD_BOOT -extern kdev_t name_to_kdev_t(char *line) md__init; -#endif - #define DEBUG 0 #if DEBUG # define dprintk(x...) printk(x) @@ -1805,136 +1801,6 @@ out: #undef OUT -/* support old ioctls/init - cold add only */ -int do_md_add(mddev_t *mddev, kdev_t dev) -{ - int err; - mdk_rdev_t *rdev; - - if (mddev->sb || mddev->pers) - return -EBUSY; - err = md_import_device(dev, 0); - if (err) return err; - rdev = find_rdev_all(dev); - if (!rdev) { - MD_BUG(); - return -EINVAL; - } - rdev->old_dev = dev; - rdev->desc_nr = mddev->nb_dev; - bind_rdev_to_array(rdev, mddev); - return 0; -} - -#define SET_SB(x,v) mddev->sb->x = v -#define SET_RSB(x,y) mddev->sb->disks[nr].x = y -static void autorun_array (mddev_t *mddev); -int do_md_start(mddev_t *mddev, int info) -{ - int pers = (info & 0xFF0000UL)>>16; -// int fault= (info & 0x00FF00UL)>>8; - int factor=(info & 0x0000FFUL); - - struct md_list_head *tmp; - mdk_rdev_t *rdev, *rdev0=NULL; - int err = 0; - - if (mddev->sb) { - printk("array md%d already has superbloc!!\n", - mdidx(mddev)); - return -EBUSY; - } - if (pers==1 || pers==2) { - /* non-persistant super block */ - int devs = mddev->nb_dev; - if (alloc_array_sb(mddev)) - return -ENOMEM; - mddev->sb->major_version = MD_MAJOR_VERSION; - mddev->sb->minor_version = MD_MINOR_VERSION; - mddev->sb->patch_version = MD_PATCHLEVEL_VERSION; - mddev->sb->ctime = CURRENT_TIME; - - SET_SB(level,pers_to_level(pers)); - SET_SB(size,0); - SET_SB(nr_disks, devs); - SET_SB(raid_disks, devs); - SET_SB(md_minor,mdidx(mddev)); - SET_SB(not_persistent, 1); - - - SET_SB(state, 1<sb->md_magic = MD_SB_MAGIC; - - /* - * Generate a 128 bit UUID - */ - get_random_bytes(&mddev->sb->set_uuid0, 4); - get_random_bytes(&mddev->sb->set_uuid1, 4); - get_random_bytes(&mddev->sb->set_uuid2, 4); - get_random_bytes(&mddev->sb->set_uuid3, 4); - - /* add each disc */ - ITERATE_RDEV(mddev,rdev,tmp) { - int nr, size; - nr = rdev->desc_nr; - SET_RSB(number,nr); - SET_RSB(major,MAJOR(rdev->dev)); - SET_RSB(minor,MINOR(rdev->dev)); - SET_RSB(raid_disk,nr); - SET_RSB(state,6); /* ACTIVE|SYNC */ - size = calc_dev_size(rdev->dev, mddev, 0); - rdev->sb_offset = calc_dev_sboffset(rdev->dev, mddev, 0); - - if (!mddev->sb->size || (mddev->sb->size > size)) - mddev->sb->size = size; - } - sync_sbs(mddev); - err = do_md_run(mddev); - if (err) - do_md_stop(mddev, 0); - } else { - /* persistant super block - ignore the info and read the superblocks */ - ITERATE_RDEV(mddev,rdev,tmp) { - if ((err = read_disk_sb(rdev))) { - printk("md: could not read %s's sb, not importing!\n", - partition_name(rdev->dev)); - break; - } - if ((err = check_disk_sb(rdev))) { - printk("md: %s has invalid sb, not importing!\n", - partition_name(rdev->dev)); - break; - } - rdev->desc_nr = rdev->sb->this_disk.number; - if (!rdev0) rdev0=rdev; - if (!uuid_equal(rdev0, rdev)) { - printk("%s has different UUID to %s .. dropping\n", - partition_name(rdev->dev), - partition_name(rdev0->dev)); - err = -EINVAL; - break; - } - if (!sb_equal(rdev0->sb, rdev->sb)) { - printk("%s has same UUID as %s, but superblocks differ ...\n", partition_name(rdev->dev), partition_name(rdev0->dev)); - err = -EINVAL; - break; - } - } - if (!err) - autorun_array(mddev); - } - return err; -} -#undef SET_SB -#undef SET_RSB /* * We have to safely support old arrays too. */ @@ -2703,59 +2569,6 @@ static int md_ioctl (struct inode *inode, struct file *file, } default: } - /* handle "old style" ioctls */ - switch (cmd) - { - case START_MD: - if (!mddev) - return -ENODEV; - err = lock_mddev(mddev); - if (err) { - printk("ioctl lock interrupted, reason %d, cmd %d\n",err, cmd); - goto abort; - } - err = do_md_start(mddev, (int) arg); - if (err) { - printk("couldn't mdstart\n"); - goto abort_unlock; - } - goto done_unlock; - case STOP_MD: - if (!mddev) - return -ENODEV; - err = lock_mddev(mddev); - if (err) { - printk("ioctl lock interrupted, reason %d, cmd %d\n",err, cmd); - goto abort_unlock; - } - err = do_md_stop(mddev, 0); - if (err) { - printk("couldn't mdstop\n"); - goto abort_unlock; - } - goto done_unlock; - case REGISTER_DEV: - /* add this device to an unstarted array, - * create the array if needed */ - if (!mddev) - mddev = alloc_mddev(dev); - if (!mddev) { - err = -ENOMEM; - goto abort; - } - err = lock_mddev(mddev); - if (err) { - printk("ioctl, reason %d, cmd %d\n", err, cmd); - goto abort; - } - err = do_md_add(mddev, to_kdev_t((dev_t) arg)); - if (err) { - printk("do_md_add failed %d\n", err); - goto abort_unlock; - } - goto done_unlock; - } - switch (cmd) { case SET_ARRAY_INFO: @@ -3369,94 +3182,11 @@ void md__init raid_setup(char *str, int *ints) return; } -#ifdef CONFIG_MD_BOOT -#define MAX_MD_BOOT_DEVS 16 -struct { - unsigned long set; - int pers[MAX_MD_BOOT_DEVS]; - kdev_t devices[MAX_MD_BOOT_DEVS][MAX_REAL]; -} md_setup_args md__initdata = { - 0,{0},{{0}} -}; - -/* - * Parse the command-line parameters given our kernel, but do not - * actually try to invoke the MD device now; that is handled by - * md_setup_drive after the low-level disk drivers have initialised. - * - * 27/11/1999: Fixed to work correctly with the 2.3 kernel (which - * assigns the task of parsing integer arguments to the - * invoked program now). Added ability to initialise all - * the MD devices (by specifying multiple "md=" lines) - * instead of just one. -- KTK - */ -static int __init md_setup(char *str) -{ - int minor, level, factor, fault, i; - kdev_t device; - char *devnames, *pername; - - if(get_option(&str, &minor) != 2 || /* MD Number */ - get_option(&str, &level) != 2 || /* RAID Personality */ - get_option(&str, &factor) != 2 || /* Chunk Size */ - get_option(&str, &fault) != 2) { - printk("md: Too few arguments supplied to md=.\n"); - return 0; - } else if (minor >= MAX_MD_BOOT_DEVS) { - printk ("md: Minor device number too high.\n"); - return 0; - } else if (md_setup_args.set & (1 << minor)) { - printk ("md: Warning - md=%d,... has been specified twice;\n" - " will discard the first definition.\n", minor); - } - switch(level) { -#ifdef CONFIG_MD_LINEAR - case -1: - level = LINEAR<<16; - pername = "linear"; - break; -#endif -#ifdef CONFIG_MD_STRIPED - case 0: - level = STRIPED<<16; - pername = "striped"; - break; -#endif - default: - printk ("md: The kernel has not been configured for raid%d" - " support!\n", level); - return 0; - } - devnames = str; - for (i = 0; str; i++) { - if ((device = name_to_kdev_t(str))) { - md_setup_args.devices[minor][i] = device; - } else { - printk ("md: Unknown device name, %s.\n", str); - return 0; - } - if ((str = strchr(str, ',')) != NULL) - str++; - } - if (!i) { - printk ("md: No devices specified for md%d?\n", minor); - return 0; - } - - printk ("md: Will configure md%d (%s) from %s, below.\n", - minor, pername, devnames); - md_setup_args.devices[minor][i] = (kdev_t) 0; - md_setup_args.pers[minor] = level | factor | (fault << 8); - md_setup_args.set |= (1 << minor); - return 1; -} -#endif - static void md_geninit (void) { int i; - for(i = 0; i < MAX_MD_BOOT_DEVS; i++) { + for(i = 0; i < MAX_MD_DEVS; i++) { md_blocksizes[i] = 1024; md_size[i] = 0; md_maxreadahead[i] = MD_READAHEAD; @@ -3526,27 +3256,6 @@ int md__init md_init (void) return (0); } -#ifdef CONFIG_MD_BOOT -void __init md_setup_drive(void) -{ - int minor, i; - kdev_t dev; - mddev_t*mddev; - - for (minor = 0; minor < MAX_MD_BOOT_DEVS; minor++) { - if ((md_setup_args.set & (1 << minor)) == 0) - continue; - printk("md: Loading md%d.\n", minor); - mddev = alloc_mddev(MKDEV(MD_MAJOR,minor)); - for (i = 0; (dev = md_setup_args.devices[minor][i]); i++) - do_md_add (mddev, dev); - do_md_start (mddev, md_setup_args.pers[minor]); - } -} - -__setup("md=", md_setup); -#endif - MD_EXPORT_SYMBOL(md_size); MD_EXPORT_SYMBOL(register_md_personality); MD_EXPORT_SYMBOL(unregister_md_personality); diff --git a/drivers/char/Config.in b/drivers/char/Config.in index d6e0a55bd278..89f51af944de 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -210,13 +210,13 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then dep_tristate ' QuickCam Colour Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi fi - dep_tristate 'CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV + dep_tristate ' CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then if [ "CONFIG_PARPORT_1284" != "n" ]; then - dep_tristate 'CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT + dep_tristate ' CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT fi if [ "$CONFIG_USB" != "n" ]; then - dep_tristate 'CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB + dep_tristate ' CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB fi fi dep_tristate ' SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV $CONFIG_I2C diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 7cb29f93629a..7e4686e405de 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -138,12 +138,12 @@ static struct file_operations misc_fops = { * @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 + * 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 + * destroyed until it has been unregistered. * * A zero is returned on success and a negative errno code for * failure. @@ -195,7 +195,7 @@ int misc_register(struct miscdevice * misc) * @misc: device to unregister * * Unregister a miscellaneous device that was previously - * successfully registered with misc_register. Success + * successfully registered with misc_register()F. Success * is indicated by a zero return, a negative errno code * indicates an error. */ diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 5641e47e7150..beb2f5284d73 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -59,6 +59,9 @@ unsigned char pckbd_sysrq_xlate[128] = static void kbd_write_command_w(int data); static void kbd_write_output_w(int data); +#ifdef CONFIG_PSMOUSE +static void aux_write_ack(int val); +#endif spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; static unsigned char handle_kbd_event(void); @@ -76,6 +79,8 @@ static volatile unsigned char resend = 0; static int __init psaux_init(void); +#define AUX_RECONNECT 170 /* scancode when ps2 device is plugged (back) in */ + static struct aux_queue *queue; /* Mouse data buffer. */ static int aux_count = 0; /* used when we send commands to the mouse that expect an ACK. */ @@ -396,6 +401,11 @@ static inline void handle_mouse_event(unsigned char scancode) } mouse_reply_expected = 0; } + else if(scancode == AUX_RECONNECT){ + queue->head = queue->tail = 0; /* Flush input queue */ + aux_write_ack(AUX_ENABLE_DEV); /* ping the mouse :) */ + return; + } add_mouse_randomness(scancode); if (aux_count) { diff --git a/drivers/char/planb.c b/drivers/char/planb.c index 524ab7be57ee..1c483485134a 100644 --- a/drivers/char/planb.c +++ b/drivers/char/planb.c @@ -8,9 +8,7 @@ Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) - Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) - (Some codes are stolen from proposed v4l2 videodev.c - of Bill Dirks ) + Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -94,11 +92,7 @@ static void saa_write_reg(unsigned char, unsigned char); static unsigned char saa_status(int, struct planb *); static void saa_set(unsigned char, unsigned char, struct planb *); static void saa_init_regs(struct planb *); -static void * rvmalloc(unsigned long); -static void rvfree(void *, unsigned long); -static unsigned long vmalloc_to_bus(void *); -static unsigned long vmalloc_to_phys(void *); -static int fbuffer_alloc(struct planb *); +static int grabbuf_alloc(struct planb *); static int vgrab(struct planb *, struct video_mmap *); static void add_clip(struct planb *, struct video_clip *); static void fill_cmd_buff(struct planb *); @@ -125,82 +119,36 @@ static inline int overlay_is_active(struct planb *); /* Memory management functions */ /*******************************/ -static void * rvmalloc(unsigned long size) +static int grabbuf_alloc(struct planb *pb) { - void *mem, *memptr; - unsigned long page; - - mem=vmalloc(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, leave no junk */ - memptr = mem; - while (size > 0) - { - page = vmalloc_to_phys(memptr); - mem_map_reserve(MAP_NR(phys_to_virt(page))); - memptr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} + int i, npage; -static void rvfree(void * mem, unsigned long size) -{ - void *memptr; - unsigned long page; - - if (mem) - { - memptr = mem; - while (size > 0) - { - page = vmalloc_to_phys(memptr); - mem_map_unreserve(MAP_NR(phys_to_virt(page))); - memptr += PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); + npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1) +#ifndef PLANB_GSCANLINE + + MAX_LNUM +#endif /* PLANB_GSCANLINE */ + ); + if ((pb->rawbuf = (unsigned char**) kmalloc (npage + * sizeof(unsigned long), GFP_KERNEL)) == 0) + return -ENOMEM; + for (i = 0; i < npage; i++) { + pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL + |GFP_DMA, 0); + if (!pb->rawbuf[i]) + break; + set_bit(PG_reserved, &mem_map[MAP_NR(pb->rawbuf[i])].flags); } -} - -/* Useful for using vmalloc()ed memory as DMA target */ -static unsigned long vmalloc_to_bus(void *virt) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - unsigned long a = (unsigned long)virt; - - if (pgd_none(*(pgd = pgd_offset(current->mm, a))) || - pmd_none(*(pmd = pmd_offset(pgd, a))) || - pte_none(*(pte = pte_offset(pmd, a)))) - return 0; - return virt_to_bus((void *)pte_page(*pte)) - + (a & (PAGE_SIZE - 1)); -} - -static unsigned long vmalloc_to_phys(void *virt) { - return virt_to_phys(bus_to_virt(vmalloc_to_bus(virt))); -} - -/* - * Create the giant waste of buffer space we need for now - * until we get DMA to user space sorted out (probably 2.3.x) - * - * We only create this as and when someone uses mmap - */ - -static int fbuffer_alloc(struct planb *pb) -{ - if(!pb->fbuffer) - pb->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS - * PLANB_MAX_FBUF); - else - printk(KERN_ERR "PlanB: Double alloc of fbuffer!\n"); - if(!pb->fbuffer) + if (i-- < npage) { + printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n"); + for (; i > 0; i--) { + clear_bit(PG_reserved, + &mem_map[MAP_NR(pb->rawbuf[i])].flags); + free_pages((unsigned long)pb->rawbuf[i], 0); + } + kfree(pb->rawbuf); return -ENOBUFS; + } + pb->rawbuf_size = npage; return 0; } @@ -446,11 +394,8 @@ static int planb_prepare_open(struct planb *pb) + PLANB_DUMMY); pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS); - pb->fbuffer = (unsigned char *)rvmalloc(MAX_GBUFFERS * PLANB_MAX_FBUF); - if (!pb->fbuffer) { - kfree(pb->priv_space); - return -ENOMEM; - } + pb->rawbuf = NULL; + pb->rawbuf_size = 0; pb->grabbing = 0; for (i = 0; i < MAX_GBUFFERS; i++) { pb->frame_stat[i] = GBUFFER_UNUSED; @@ -461,31 +406,23 @@ static int planb_prepare_open(struct planb *pb) #ifndef PLANB_GSCANLINE pb->lsize[i] = 0; pb->lnum[i] = 0; - pb->l_fr_addr[i]=(unsigned char *)rvmalloc(PAGE_SIZE*MAX_LNUM); - if (!pb->l_fr_addr[i]) { - int j; - kfree(pb->priv_space); - rvfree((void *)pb->fbuffer, MAX_GBUFFERS - * PLANB_MAX_FBUF); - for(j = 0; j < i; j++) - rvfree((void *)pb->l_fr_addr[j], PAGE_SIZE - * MAX_LNUM); - return -ENOMEM; - } #endif /* PLANB_GSCANLINE */ } pb->gcount = 0; pb->suspend = 0; pb->last_fr = -999; pb->prev_last_fr = -999; - return 0; + + /* Reset DMA controllers */ + planb_dbdma_stop(&pb->planb_base->ch2); + planb_dbdma_stop(&pb->planb_base->ch1); + + return 0; } static void planb_prepare_close(struct planb *pb) { -#ifndef PLANB_GSCANLINE int i; -#endif /* make sure the dma's are idle */ planb_dbdma_stop(&pb->planb_base->ch2); @@ -496,16 +433,15 @@ static void planb_prepare_close(struct planb *pb) pb->priv_space = 0; pb->cmd_buff_inited = 0; } - if(pb->fbuffer) - rvfree((void *)pb->fbuffer, MAX_GBUFFERS*PLANB_MAX_FBUF); - pb->fbuffer = NULL; -#ifndef PLANB_GSCANLINE - for(i = 0; i < MAX_GBUFFERS; i++) { - if(pb->l_fr_addr[i]) - rvfree((void *)pb->l_fr_addr[i], PAGE_SIZE * MAX_LNUM); - pb->l_fr_addr[i] = NULL; + if(pb->rawbuf) { + for (i = 0; i < pb->rawbuf_size; i++) { + clear_bit(PG_reserved, + &mem_map[MAP_NR(pb->rawbuf[i])].flags); + free_pages((unsigned long)pb->rawbuf[i], 0); + } + kfree(pb->rawbuf); } -#endif /* PLANB_GSCANLINE */ + pb->rawbuf = NULL; } /*****************************/ @@ -940,12 +876,8 @@ static int palette2fmt[] = { 0, 0, }; -#define PLANB_PALETTE_MAX 15 -#define SWAP4(x) (((x>>24) & 0x000000ff) |\ - ((x>>8) & 0x0000ff00) |\ - ((x<<8) & 0x00ff0000) |\ - ((x<<24) & 0xff000000)) +#define PLANB_PALETTE_MAX 15 static inline int overlay_is_active(struct planb *pb) { @@ -962,9 +894,10 @@ static int vgrab(struct planb *pb, struct video_mmap *mp) unsigned int fr = mp->frame; unsigned int format; - if(pb->fbuffer==NULL) { - if(fbuffer_alloc(pb)) - return -ENOBUFS; + if(pb->rawbuf==NULL) { + int err; + if((err=grabbuf_alloc(pb))) + return err; } IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing, @@ -984,12 +917,10 @@ static int vgrab(struct planb *pb, struct video_mmap *mp) return -EINVAL; planb_lock(pb); - pb->gbuffer[fr] = (unsigned char *)(pb->fbuffer + PLANB_MAX_FBUF * fr); if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] || format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) { -#ifdef PLANB_GSCANLINE int i; -#else +#ifndef PLANB_GSCANLINE unsigned int osize = pb->gwidth[fr] * pb->gheight[fr] * pb->gfmt[fr]; unsigned int nsize = mp->width * mp->height * format; @@ -1001,10 +932,15 @@ static int vgrab(struct planb *pb, struct video_mmap *mp) #ifndef PLANB_GSCANLINE if(pb->gnorm_switch[fr]) nsize = 0; - if(nsize < osize) - memset((void *)(pb->gbuffer[fr] + nsize), 0, - osize - nsize); - memset((void *)pb->l_fr_addr[fr], 0, PAGE_SIZE * pb->lnum[fr]); + if (nsize < osize) { + for(i = pb->gbuf_idx[fr]; osize > 0; i++) { + memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); + osize -= PAGE_SIZE; + } + } + for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr] + + pb->lnum[fr]; i++) + memset((void *)pb->rawbuf[i], 0, PAGE_SIZE); #else /* XXX TODO */ /* @@ -1228,7 +1164,7 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) unsigned long base; #endif unsigned long jump; - unsigned char *vaddr; + int pagei; volatile struct dbdma_cmd *c1; volatile struct dbdma_cmd *jump_addr; @@ -1277,11 +1213,12 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) /* even field data: */ - vaddr = pb->gbuffer[fr]; + pagei = pb->gbuf_idx[fr]; #ifdef PLANB_GSCANLINE for (i = 0; i < nlines; i += stepsize) { tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - vmalloc_to_bus(vaddr + i * scanline), jump); + virt_to_bus(pb->rawbuf[pagei + + i * scanline / PAGE_SIZE]), jump); } #else i = 0; @@ -1289,7 +1226,7 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) do { int j; - base = vmalloc_to_bus((void*)vaddr); + base = virt_to_bus(pb->rawbuf[pagei]); nlpp = (PAGE_SIZE - leftover1) / count / stepsize; for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++) tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, @@ -1304,11 +1241,13 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base + count * nlpp * stepsize + leftover1, jump); } else { - pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count * nlpp - * stepsize + leftover1; + pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] + + count * nlpp * stepsize + leftover1; + pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; + pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0; tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE - * pb->lnum[fr]), jump); + virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] + + pb->lnum[fr]]), jump); if(++pb->lnum[fr] > MAX_LNUM) pb->lnum[fr]--; } @@ -1316,7 +1255,7 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) i += stepsize; } } - vaddr += PAGE_SIZE; + pagei++; } while(i < nlines); tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); c1 = jump_addr; @@ -1341,18 +1280,19 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) #ifdef PLANB_GSCANLINE for (i = 1; i < nlines; i += stepsize) { tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - vmalloc_to_bus(vaddr + i * scanline), jump); + virt_to_bus(pb->rawbuf[pagei + + i * scanline / PAGE_SIZE]), jump); } #else i = 1; leftover1 = 0; - vaddr = pb->gbuffer[fr]; + pagei = pb->gbuf_idx[fr]; if(nlines <= 1) goto skip; do { int j; - base = vmalloc_to_bus((void*)vaddr); + base = virt_to_bus(pb->rawbuf[pagei]); nlpp = (PAGE_SIZE - leftover1) / count / stepsize; if(leftover1 >= count) { tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count, @@ -1369,11 +1309,14 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) leftover1 = 0; else { if(lov0 > count) { - pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count - * (nlpp * stepsize + 1) + leftover1; + pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei] + + count * (nlpp * stepsize + 1) + leftover1; + pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1; + pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize + - lov0; tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, - vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE - * pb->lnum[fr]), jump); + virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr] + + pb->lnum[fr]]), jump); if(++pb->lnum[fr] > MAX_LNUM) pb->lnum[fr]--; i += stepsize; @@ -1381,7 +1324,7 @@ static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb) leftover1 = count * stepsize - lov0; } } - vaddr += PAGE_SIZE; + pagei++; } while(i < nlines); skip: tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump); @@ -1390,7 +1333,7 @@ skip: cmd_tab_data_end: tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat), - (fr << 2) | PLANB_FRM_IRQ | PLANB_GEN_IRQ); + (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ); /* stop it */ tab_cmd_dbdma(c1, DBDMA_STOP, 0); @@ -1406,13 +1349,15 @@ static void planb_irq(int irq, void *dev_id, struct pt_regs * regs) IDEBUG("PlanB: planb_irq()\n"); /* get/clear interrupt status bits */ + eieio(); stat = in_le32(&pb->planb_base->intr_stat); astat = stat & pb->intr_mask; - out_le32(&pb->planb_base->intr_stat, PLANB_IRQ_CMD_MASK + out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ & ~astat & stat & ~PLANB_GEN_IRQ); + IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat); if(astat & PLANB_FRM_IRQ) { - unsigned int fr = stat >> 2; + unsigned int fr = stat >> 9; #ifndef PLANB_GSCANLINE int i; #endif @@ -1425,9 +1370,16 @@ static void planb_irq(int irq, void *dev_id, struct pt_regs * regs) #ifndef PLANB_GSCANLINE IDEBUG("PlanB: %d * %d bytes are being copied over\n", pb->lnum[fr], pb->lsize[fr]); - for(i = 0; i < pb->lnum[fr]; i++) - memcpy(pb->l_to_addr[fr][i], pb->l_fr_addr[fr] - + PAGE_SIZE * i, pb->lsize[fr]); + for(i = 0; i < pb->lnum[fr]; i++) { + int first = pb->lsize[fr] - pb->l_to_next_size[fr][i]; + + memcpy(pb->l_to_addr[fr][i], + pb->rawbuf[pb->l_fr_addr_idx[fr] + i], + first); + memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]], + pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first, + pb->l_to_next_size[fr][i]); + } #endif pb->frame_stat[fr] = GBUFFER_DONE; pb->grabbing--; @@ -1848,6 +1800,8 @@ chk_grab: IDEBUG("PlanB: waiting for grab" " done (%d)\n", i); interruptible_sleep_on(&pb->capq); + if(signal_pending(current)) + return -EINTR; goto chk_grab; case GBUFFER_DONE: pb->frame_stat[i] = GBUFFER_UNUSED; @@ -2058,38 +2012,27 @@ unimplemented: return 0; } -/* - * This maps the vmalloced and reserved fbuffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - remap_page_range is kind of inefficient for page by page remapping. - * But e.g. pte_alloc() does not work in modules ... :-( - */ - static int planb_mmap(struct video_device *dev, const char *adr, unsigned long size) { - struct planb *pb=(struct planb *)dev; - unsigned long start=(unsigned long) adr; - unsigned long page; - void *pos; + int i; + struct planb *pb = (struct planb *)dev; + unsigned long start = (unsigned long)adr; - if (size>MAX_GBUFFERS*PLANB_MAX_FBUF) + if (size > MAX_GBUFFERS * PLANB_MAX_FBUF) return -EINVAL; - if (!pb->fbuffer) - { - if(fbuffer_alloc(pb)) - return -EINVAL; + if (!pb->rawbuf) { + int err; + if((err=grabbuf_alloc(pb))) + return err; } - pos = (void *)pb->fbuffer; - while (size > 0) - { - page = vmalloc_to_phys(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - size-=PAGE_SIZE; + for (i = 0; i < pb->rawbuf_size; i++) { + if (remap_page_range(start, virt_to_phys((void *)pb->rawbuf[i]), + PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + if (size <= PAGE_SIZE) + break; + size -= PAGE_SIZE; } return 0; } @@ -2125,6 +2068,7 @@ static int init_planb(struct planb *pb) { unsigned char saa_rev; int i, result; + unsigned long flags; memset ((void *) &pb->win, 0, sizeof (struct planb_window)); /* Simple sanity check */ @@ -2167,7 +2111,7 @@ static int init_planb(struct planb *pb) pb->tab_size = PLANB_MAXLINES + 40; pb->suspend = 0; pb->lock = 0; - pb->lockq = NULL; + init_waitqueue_head(&pb->lockq); pb->ch1_cmd = 0; pb->ch2_cmd = 0; pb->mask = 0; @@ -2175,7 +2119,7 @@ static int init_planb(struct planb *pb) pb->offset = 0; pb->user = 0; pb->overlay = 0; - pb->suspendq = NULL; + init_waitqueue_head(&pb->suspendq); pb->cmd_buff_inited = 0; pb->frame_buffer_phys = 0; @@ -2191,19 +2135,20 @@ static int init_planb(struct planb *pb) /* clear interrupt mask */ pb->intr_mask = PLANB_CLR_IRQ; + save_flags(flags); cli(); result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb); - if (result==-EINVAL) { - printk(KERN_ERR "PlanB: Bad irq number (%d) or handler\n", - (int)pb->irq); - return result; - } - if (result==-EBUSY) { - printk(KERN_ERR "PlanB: I don't know why, but IRQ %d busy\n", - (int)pb->irq); - return result; - } - if (result < 0) - return result; + if (result < 0) { + if (result==-EINVAL) + printk(KERN_ERR "PlanB: Bad irq number (%d) " + "or handler\n", (int)pb->irq); + else if (result==-EBUSY) + printk(KERN_ERR "PlanB: I don't know why, " + "but IRQ %d is busy\n", (int)pb->irq); + restore_flags(flags); + return result; + } + disable_irq(pb->irq); + restore_flags(flags); /* Now add the template and register the device unit. */ memcpy(&pb->video_dev,&planb_template,sizeof(planb_template)); @@ -2216,26 +2161,27 @@ static int init_planb(struct planb *pb) pb->picture.depth = pb->win.depth; pb->frame_stat=NULL; - pb->capq=NULL; + init_waitqueue_head(&pb->capq); for(i=0; igbuffer[i]=NULL; + pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE; pb->gwidth[i]=0; pb->gheight[i]=0; pb->gfmt[i]=0; pb->cap_cmd[i]=NULL; #ifndef PLANB_GSCANLINE - pb->l_fr_addr[i]=NULL; + pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF + / PAGE_SIZE + 1) + MAX_LNUM * i; pb->lsize[i] = 0; pb->lnum[i] = 0; #endif } - pb->fbuffer=NULL; + pb->rawbuf=NULL; pb->grabbing=0; - /* clear interrupts */ + /* enable interrupts */ out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ); - /* set interrupt mask */ pb->intr_mask = PLANB_FRM_IRQ; + enable_irq(pb->irq); if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER)<0) return -1; diff --git a/drivers/char/planb.h b/drivers/char/planb.h index e57bed49b943..98d5697c9374 100644 --- a/drivers/char/planb.h +++ b/drivers/char/planb.h @@ -8,7 +8,7 @@ Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de) - Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu) + Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu) This program is free software; you can redistribute it and/or modify @@ -69,6 +69,7 @@ /* for capture operations */ #define MAX_GBUFFERS 2 +/* note PLANB_MAX_FBUF must be divisible by PAGE_SIZE */ #ifdef PLANB_GSCANLINE #define PLANB_MAX_FBUF 0x240000 /* 576 * 1024 * 4 */ #define TAB_FACTOR (1) @@ -132,8 +133,7 @@ struct planb_registers { volatile unsigned int intr_stat; /* 0x104: irq status */ #define PLANB_CLR_IRQ 0x00 /* clear Plan B interrupt */ #define PLANB_GEN_IRQ 0x01 /* assert Plan B interrupt */ -#define PLANB_FRM_IRQ 0x02 /* end of frame */ -#define PLANB_IRQ_CMD_MASK 0x00000003U /* reserve 2 lsbs for command */ +#define PLANB_FRM_IRQ 0x0100 /* end of frame */ unsigned int pad3[1]; /* empty? */ volatile unsigned int reg5; /* 0x10c: ??? */ unsigned int pad4[60]; /* empty? */ @@ -199,8 +199,9 @@ struct planb { wait_queue_head_t capq; int last_fr; int prev_last_fr; - unsigned char *fbuffer; - unsigned char *gbuffer[MAX_GBUFFERS]; + unsigned char **rawbuf; + int rawbuf_size; + int gbuf_idx[MAX_GBUFFERS]; volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS]; volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS]; volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS]; @@ -218,8 +219,10 @@ struct planb { #else #define MAX_LNUM 431 /* change this if PLANB_MAXLINES or */ /* PLANB_MAXPIXELS changes */ - unsigned char *l_fr_addr[MAX_GBUFFERS]; + int l_fr_addr_idx[MAX_GBUFFERS]; unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM]; + int l_to_next_idx[MAX_GBUFFERS][MAX_LNUM]; + int l_to_next_size[MAX_GBUFFERS][MAX_LNUM]; int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS]; #endif }; diff --git a/drivers/char/random.c b/drivers/char/random.c index 97e771b26056..b983ae8252fc 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -763,7 +763,12 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) void add_keyboard_randomness(unsigned char scancode) { - add_timer_randomness(&keyboard_timer_state, scancode); + static unsigned char last_scancode = 0; + /* ignore autorepeat (multiple key down w/o key up) */ + if (scancode != last_scancode) { + last_scancode = scancode; + add_timer_randomness(&keyboard_timer_state, scancode); + } } void add_mouse_randomness(__u32 mouse_data) diff --git a/drivers/char/serial.c b/drivers/char/serial.c index a821b6dd58e2..03f12ad59094 100644 --- a/drivers/char/serial.c +++ b/drivers/char/serial.c @@ -4683,7 +4683,7 @@ int register_serial(struct serial_struct *req) * * 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. + * the port number returned by register_serial(). */ void unregister_serial(int line) diff --git a/drivers/char/videodev.c b/drivers/char/videodev.c index 60990d1db69c..244871791d84 100644 --- a/drivers/char/videodev.c +++ b/drivers/char/videodev.c @@ -221,26 +221,26 @@ extern struct file_operations video_fops; /** * video_register_device - register video4linux devices - * @vfd: Video device structure we want to register + * @vfd: video device structure we want to register * @type: type of device to register * FIXME: needs a semaphore on 2.3.x * * The registration code assigns minor numbers based on the type * requested. -ENFILE is returned in all the device slots for this * catetory are full. If not then the minor field is set and the - * driver initialize function is called (if non NULL). + * driver initialize function is called (if non %NULL). * * Zero is returned on success. * * Valid types are * - * VFL_TYPE_GRABBER - A frame grabber + * %VFL_TYPE_GRABBER - A frame grabber * - * VFL_TYPE_VTX - A teletext device + * %VFL_TYPE_VTX - A teletext device * - * VFL_TYPE_VBI - Vertical blank data (undecoded) + * %VFL_TYPE_VBI - Vertical blank data (undecoded) * - * VFL_TYPE_RADIO - A radio card + * %VFL_TYPE_RADIO - A radio card */ int video_register_device(struct video_device *vfd, int type) diff --git a/drivers/i2o/Config.in b/drivers/i2o/Config.in index 4cc286578b13..f324f074dc0c 100644 --- a/drivers/i2o/Config.in +++ b/drivers/i2o/Config.in @@ -3,7 +3,9 @@ comment 'I2O device support' tristate 'I2O support' CONFIG_I2O -dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O +if [ "$CONFIG_PCI" = "y" ]; then + dep_tristate ' I2O PCI support' CONFIG_I2O_PCI $CONFIG_I2O +fi dep_tristate ' I2O Block OSM' CONFIG_I2O_BLOCK $CONFIG_I2O if [ "$CONFIG_NET" = "y" ]; then dep_tristate ' I2O LAN OSM' CONFIG_I2O_LAN $CONFIG_I2O diff --git a/drivers/i2o/README b/drivers/i2o/README index 4efb923fb7b3..cebb79ac5d67 100644 --- a/drivers/i2o/README +++ b/drivers/i2o/README @@ -16,6 +16,7 @@ Steve Ralston, LSI Logic Corp. Debugging SCSI and Block OSM Deepak Saxena, Intel Corp. + Various core/block extensions /proc interface, bug fixes Ioctl interfaces for control Debugging LAN OSM @@ -64,24 +65,22 @@ ASUSTeK STATUS: o The core setup works within limits. -o The scsi layer seems to almost work. I'm still chasing down the hang - bug. -o The block OSM is fairly minimal but does seem to work. +o The scsi layer seems to almost work. + I'm still chasing down the hang bug. +o The block OSM is mostly functional o LAN OSM works with FDDI and Ethernet cards. TO DO: General: -o Support multiple IOP's and tell them about each other o Provide hidden address space if asked o Long term message flow control o PCI IOP's without interrupts are not supported yet o Push FAIL handling into the core o DDM control interfaces for module load etc -o Event handling +o Add I2O 2.0 support (Deffered to 2.5 kernel) Block: -o Real error handler o Multiple major numbers o Read ahead and cache handling stuff. Talk to Ingo and people o Power management @@ -90,9 +89,11 @@ o Finish Media changers SCSI: o Find the right way to associate drives/luns/busses -Lan: Batch mode sends - Performance tuning - Event handling +Lan: +o Performance tuning +o Test Fibre Channel code +o Fix lan_set_mc_list() Tape: o Anyone seen anything implementing this ? + (D.S: Will attempt to do so if spare cycles permit) diff --git a/drivers/i2o/README.ioctl b/drivers/i2o/README.ioctl index 3c1dd462ecfe..50537501ff32 100644 --- a/drivers/i2o/README.ioctl +++ b/drivers/i2o/README.ioctl @@ -20,7 +20,7 @@ on the specification, see http://www.i2osig.org This document and the I2O user space interface are currently maintained by Deepak Saxena. Please send all comments, errata, and bug fixes to -deepak@plexity.net +deepak@csociety.purdue.edu II. IOP Access diff --git a/drivers/i2o/i2o_block.c b/drivers/i2o/i2o_block.c index 8afad596f9ff..566e94b3b517 100644 --- a/drivers/i2o/i2o_block.c +++ b/drivers/i2o/i2o_block.c @@ -1,30 +1,35 @@ /* - * I2O block device driver. + * I2O Random Block Storage Class OSM * - * (C) Copyright 1999 Red Hat Software + * (C) Copyright 1999 Red Hat Software * - * Written by Alan Cox, Building Number Three Ltd + * Written by Alan Cox, Building Number Three Ltd * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * This program is 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 is a beta test release. Most of the good code was taken - * from the nbd driver by Pavel Machek, who in turn took some of it - * from loop.c. Isn't free software great for reusability 8) + * This is a beta test release. Most of the good code was taken + * from the nbd driver by Pavel Machek, who in turn took some of it + * from loop.c. Isn't free software great for reusability 8) + * + * Fixes/additions: + * Steve Ralston: + * Multiple device handling error fixes, + * Added a queue depth. + * Alan Cox: + * FC920 has an rmw bug. Dont or in the end marker. + * Removed queue walk, fixed for 64bitness. + * Deepak Saxena: + * Independent queues per IOP + * Support for dynamic device creation/deletion + * Code cleanup + * Support for larger I/Os through merge* functions + * (taken from DAC960 driver) * - * Fixes: - * Steve Ralston: Multiple device handling error fixes, - * Added a queue depth. - * Alan Cox: FC920 has an rmw bug. Dont or in the - * end marker. - * Removed queue walk, fixed for 64bitness. * To do: - * Multiple majors * Serial number scanning to find duplicates for FC multipathing - * Set the new max_sectors according to max message size - * Use scatter gather chains for bigger I/O sizes */ #include @@ -47,8 +52,11 @@ #include #include +#include #include #include +#include +#include #define MAJOR_NR I2O_MAJOR @@ -56,9 +64,44 @@ #define MAX_I2OB 16 -#define MAX_I2OB_DEPTH 32 +#define MAX_I2OB_DEPTH 128 #define MAX_I2OB_RETRIES 4 +//#define DRIVERDEBUG +#ifdef DRIVERDEBUG +#define DEBUG( s ) +#else +#define DEBUG( s ) printk( s ) +#endif + +/* + * Events that this OSM is interested in + */ +#define I2OB_EVENT_MASK I2O_EVT_IND_BSA_VOLUME_LOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD | \ + I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ | \ + I2O_EVT_IND_BSA_CAPACITY_CHANGE + + +/* + * I2O Block Error Codes - should be in a header file really... + */ +#define I2O_BSA_DSC_SUCCESS 0x0000 +#define I2O_BSA_DSC_MEDIA_ERROR 0x0001 +#define I2O_BSA_DSC_ACCESS_ERROR 0x0002 +#define I2O_BSA_DSC_DEVICE_FAILURE 0x0003 +#define I2O_BSA_DSC_DEVICE_NOT_READY 0x0004 +#define I2O_BSA_DSC_MEDIA_NOT_PRESENT 0x0005 +#define I2O_BSA_DSC_MEDIA_LOCKED 0x0006 +#define I2O_BSA_DSC_MEDIA_FAILURE 0x0007 +#define I2O_BSA_DSC_PROTOCOL_FAILURE 0x0008 +#define I2O_BSA_DSC_BUS_FAILURE 0x0009 +#define I2O_BSA_DSC_ACCESS_VIOLATION 0x000A +#define I2O_BSA_DSC_WRITE_PROTECTED 0x000B +#define I2O_BSA_DSC_DEVICE_RESET 0x000C +#define I2O_BSA_DSC_VOLUME_CHANGED 0x000D +#define I2O_BSA_DSC_TIMEOUT 0x000E + /* * Some of these can be made smaller later */ @@ -71,14 +114,20 @@ static u32 i2ob_max_sectors[MAX_I2OB<<4]; static int i2ob_context; +/* + * I2O Block device descriptor + */ struct i2ob_device { struct i2o_controller *controller; struct i2o_device *i2odev; + int unit; int tid; int flags; int refcnt; struct request *head, *tail; + request_queue_t *req_queue; + int max_segments; int done_flag; }; @@ -87,53 +136,81 @@ struct i2ob_device * We should cache align these to avoid ping-ponging lines on SMP * boxes under heavy I/O load... */ - struct i2ob_request { struct i2ob_request *next; struct request *req; int num; -}; +} __cacheline_aligned; +/* + * Per IOP requst queue information + * + * We have a separate requeust_queue_t per IOP so that a heavilly + * loaded I2O block device on an IOP does not starve block devices + * across all I2O controllers. + * + */ +struct i2ob_iop_queue +{ + atomic_t queue_depth; + struct i2ob_request request_queue[MAX_I2OB_DEPTH]; + struct i2ob_request *i2ob_qhead; + request_queue_t req_queue; +}; +static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS] = {NULL}; /* * Each I2O disk is one of these. */ static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; -static int i2ob_devices = 0; +static int i2ob_dev_count = 0; static struct hd_struct i2ob[MAX_I2OB<<4]; static struct gendisk i2ob_gendisk; /* Declared later */ -static atomic_t queue_depth; /* For flow control later on */ -static struct i2ob_request i2ob_queue[MAX_I2OB_DEPTH+1]; -static struct i2ob_request *i2ob_qhead; +/* + * Mutex and spin lock for event handling synchronization + * evt_msg contains the last event. + */ +DECLARE_MUTEX(i2ob_evt_sem); +static spinlock_t i2ob_evt_lock = SPIN_LOCK_UNLOCKED; +static unsigned int evt_msg[MSG_FRAME_SIZE>>2]; +DECLARE_WAIT_QUEUE_HEAD(i2ob_evt_wait); static struct timer_list i2ob_timer; static int i2ob_timer_started = 0; -#define DEBUG( s ) -/* #define DEBUG( s ) printk( s ) - */ - +static void i2o_block_reply(struct i2o_handler *, struct i2o_controller *, + struct i2o_message *); +static void i2ob_new_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_del_device(struct i2o_controller *, struct i2o_device *); +static void i2ob_reboot_event(void); static int i2ob_install_device(struct i2o_controller *, struct i2o_device *, int); static void i2ob_end_request(struct request *); -static void i2ob_request(request_queue_t * q); +static void i2ob_request(request_queue_t *); +static int i2ob_init_iop(unsigned int); +static request_queue_t* i2ob_get_queue(kdev_t); +static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); +static int do_i2ob_revalidate(kdev_t, int); +static int i2ob_evt(void *); + +static int evt_pid = 0; +static int evt_running = 0; /* - * Dump messages. + * I2O OSM registration structure...keeps getting bigger and bigger :) */ -static void i2ob_dump_msg(struct i2ob_device *dev,u32 *msg,int size) +static struct i2o_handler i2o_block_handler = { - int cnt; - - printk(KERN_INFO "\n\ni2o message:\n"); - for (cnt = 0; cntreq; struct buffer_head *bh = req->bh; int count = req->nr_sectors<<9; + char *last = NULL; + unsigned short size = 0; + // printk(KERN_INFO "i2ob_send called\n"); /* Map the message to a virtual address */ msg = c->mem_offset + m; /* - * Build the message based on the request. + * Build the message based on the request. */ __raw_writel(i2ob_context|(unit<<8), msg+8); __raw_writel(ireq->num, msg+12); @@ -184,18 +264,26 @@ static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, __raw_writel(1<<16, msg+16); while(bh!=NULL) { - /* - * Its best to do this in one not or it in - * later. mptr is in PCI space so fast to write - * sucky to read. - */ - if(bh->b_reqnext) - __raw_writel(0x10000000|(bh->b_size), mptr); + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } else - __raw_writel(0xD0000000|(bh->b_size), mptr); - - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr+=8; + { + if(bh->b_reqnext) + __raw_writel(0x10000000|(bh->b_size), mptr); + else + __raw_writel(0xD0000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + count -= bh->b_size; bh = bh->b_reqnext; } @@ -203,22 +291,41 @@ static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, else if(req->cmd == WRITE) { __raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4); - __raw_writel(1<<16, msg+16); + /* + * Allow replies to come back once data is cached in the controller + * This allows us to handle writes quickly thus giving more of the + * queue to reads. + */ + __raw_writel(0x00000010, msg+16); while(bh!=NULL) { - if(bh->b_reqnext) - __raw_writel(0x14000000|(bh->b_size), mptr); + if(bh->b_data == last) { + size += bh->b_size; + last += bh->b_size; + if(bh->b_reqnext) + __raw_writel(0x14000000|(size), mptr-8); + else + __raw_writel(0xD4000000|(size), mptr-8); + } else - __raw_writel(0xD4000000|(bh->b_size), mptr); + { + if(bh->b_reqnext) + __raw_writel(0x14000000|(bh->b_size), mptr); + else + __raw_writel(0xD4000000|(bh->b_size), mptr); + __raw_writel(virt_to_bus(bh->b_data), mptr+4); + mptr += 8; + size = bh->b_size; + last = bh->b_data + size; + } + count -= bh->b_size; - __raw_writel(virt_to_bus(bh->b_data), mptr+4); - mptr+=8; bh = bh->b_reqnext; } } __raw_writel(I2O_MESSAGE_SIZE(mptr-msg)>>2 | SGL_OFFSET_8, msg); - if(req->current_nr_sectors > 8) + if(req->current_nr_sectors > i2ob_max_sectors[unit]) printk("Gathered sectors %ld.\n", req->current_nr_sectors); @@ -228,7 +335,7 @@ static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, } i2o_post_message(c,m); - atomic_inc(&queue_depth); + atomic_inc(&i2ob_queues[c->unit]->queue_depth); return 0; } @@ -239,17 +346,18 @@ static int i2ob_send(u32 m, struct i2ob_device *dev, struct i2ob_request *ireq, * must hold the lock. */ -static inline void i2ob_unhook_request(struct i2ob_request *ireq) +static inline void i2ob_unhook_request(struct i2ob_request *ireq, + unsigned int iop) { - ireq->next = i2ob_qhead; - i2ob_qhead = ireq; + ireq->next = i2ob_queues[iop]->i2ob_qhead; + i2ob_queues[iop]->i2ob_qhead = ireq; } /* * Request completion handler */ -static void i2ob_end_request(struct request *req) +static inline void i2ob_end_request(struct request *req) { /* * Loop until all of the buffers that are linked @@ -257,19 +365,74 @@ static void i2ob_end_request(struct request *req) * unlocked. */ -// printk("ending request %p: ", req); - while (end_that_request_first( req, !req->errors, "i2o block" )) - { -// printk(" +\n"); - } + while (end_that_request_first( req, !req->errors, "i2o block" )); /* * It is now ok to complete the request. */ - -// printk("finishing "); end_that_request_last( req ); -// printk("done\n"); +} + +/* + * Request merging functions + */ +static inline int i2ob_new_segment(request_queue_t *q, struct request *req, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + if (req->nr_segments < max_segments) { + req->nr_segments++; + q->elevator.nr_segments++; + return 1; + } + return 0; +} + +static int i2ob_back_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (req->bhtail->b_data + req->bhtail->b_size == bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_front_merge(request_queue_t *q, struct request *req, + struct buffer_head *bh, int __max_segments) +{ + if (bh->b_data + bh->b_size == req->bh->b_data) + return 1; + return i2ob_new_segment(q, req, __max_segments); +} + +static int i2ob_merge_requests(request_queue_t *q, + struct request *req, + struct request *next, + int __max_segments) +{ + int max_segments = i2ob_dev[MINOR(req->rq_dev)].max_segments; + int total_segments = req->nr_segments + next->nr_segments; + int same_segment; + + if (__max_segments < max_segments) + max_segments = __max_segments; + + same_segment = 0; + if (req->bhtail->b_data + req->bhtail->b_size == next->bh->b_data) + { + total_segments--; + same_segment = 1; + } + + if (total_segments > max_segments) + return 0; + + q->elevator.nr_segments -= same_segment; + req->nr_segments = total_segments; + return 1; } @@ -280,136 +443,253 @@ static void i2ob_end_request(struct request *req) static void i2o_block_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *msg) { unsigned long flags; - struct i2ob_request *ireq; + struct i2ob_request *ireq = NULL; u8 st; u32 *m = (u32 *)msg; u8 unit = (m[2]>>8)&0xF0; /* low 4 bits are partition */ - + struct i2ob_device *dev = &i2ob_dev[(unit&0xF0)]; + + /* + * FAILed message + */ if(m[0] & (1<<13)) { - printk("IOP fail.\n"); - printk("From %d To %d Cmd %d.\n", - (m[1]>>12)&0xFFF, - m[1]&0xFFF, - m[1]>>24); - printk("Failure Code %d.\n", m[4]>>24); - if(m[4]&(1<<16)) - printk("Format error.\n"); - if(m[4]&(1<<17)) - printk("Path error.\n"); - if(m[4]&(1<<18)) - printk("Path State.\n"); - if(m[4]&(1<<18)) - printk("Congestion.\n"); - - m=(u32 *)bus_to_virt(m[7]); - printk("Failing message is %p.\n", m); - - /* We need to up the request failure count here and maybe - abort it */ - ireq=&i2ob_queue[m[3]]; + /* + * FAILed message from controller + * We increment the error count and abort it + * + * In theory this will never happen. The I2O block class + * speficiation states that block devices never return + * FAILs but instead use the REQ status field...but + * better be on the safe side since no one really follows + * the spec to the book :) + */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + /* Now flush the message by making it a NOP */ m[0]&=0x00FFFFFF; m[0]|=(I2O_CMD_UTIL_NOP)<<24; i2o_post_message(c,virt_to_bus(m)); - + + return; } - else + + if(msg->function == I2O_CMD_UTIL_EVT_REGISTER) + { + spin_lock(&i2ob_evt_lock); + memcpy(&evt_msg, m, msg->size); + spin_unlock(&i2ob_evt_lock); + wake_up_interruptible(&i2ob_evt_wait); + return; + } + + if(!dev->i2odev) { - if(m[2]&0x40000000) - { - int * ptr = (int *)m[3]; - if(m[4]>>24) - *ptr = -1; - else - *ptr = 1; - return; - } /* - * Lets see what is cooking. We stuffed the - * request in the context. + * This is HACK, but Intel Integrated RAID allows user + * to delete a volume that is claimed, locked, and in use + * by the OS. We have to check for a reply from a + * non-existent device and flag it as an error or the system + * goes kaput... */ + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + ireq->req->errors++; + printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); + spin_lock_irqsave(&io_request_lock, flags); + i2ob_unhook_request(ireq, c->unit); + i2ob_end_request(ireq->req); + spin_unlock_irqrestore(&io_request_lock, flags); + return; + } + + /* + * Lets see what is cooking. We stuffed the + * request in the context. + */ - ireq=&i2ob_queue[m[3]]; - st=m[4]>>24; - - if(st!=0) - { - printk(KERN_ERR "i2ob: error %08X\n", m[4]); - ireq->req->errors++; - if (ireq->req->errors < MAX_I2OB_RETRIES) - { - u32 retry_msg; - struct i2ob_device *dev; + ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; + st=m[4]>>24; - printk(KERN_ERR "i2ob: attempting retry %d for request %p\n",ireq->req->errors+1,ireq->req); - - /* - * Get a message for this retry. - */ - dev = &i2ob_dev[(unit&0xF0)]; - retry_msg = i2ob_get(dev); - - /* - * If we cannot get a message then - * forget the retry and fail the - * request. Note that since this is - * being called from the interrupt - * handler, a request has just been - * completed and there will most likely - * be space on the inbound message - * fifo so this won't happen often. - */ - if(retry_msg!=0xFFFFFFFF) - { - /* - * Decrement the queue depth since - * this request has completed and - * it will be incremented again when - * i2ob_send is called below. - */ - atomic_dec(&queue_depth); - - /* - * Send the request again. - */ - i2ob_send(retry_msg, dev,ireq,i2ob[unit].start_sect, (unit&0xF0)); - /* - * Don't fall through. - */ - return; - } - } - } - else - ireq->req->errors = 0; + if(st!=0) + { + char *bsa_errors[] = + { + "Success", + "Media Error", + "Failure communicating to device", + "Device Failure", + "Device is not ready", + "Media not present", + "Media is locked by another user", + "Media has failed", + "Failure communicating to device", + "Device bus failure", + "Device is locked by another user", + "Device is write protected", + "Device has reset", + "Volume has changed, waiting for acknowledgement" + }; + + printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, + bsa_errors[m[4]&0XFFFF]); + if(m[4]&0x00FF0000) + printk(" - DDM attempted %d retries", (m[4]>>16)&0x00FF ); + printk("\n"); + + ireq->req->errors++; } - + else + ireq->req->errors = 0; + /* * Dequeue the request. We use irqsave locks as one day we * may be running polled controllers from a BH... */ spin_lock_irqsave(&io_request_lock, flags); - i2ob_unhook_request(ireq); + i2ob_unhook_request(ireq, c->unit); i2ob_end_request(ireq->req); - + atomic_dec(&i2ob_queues[c->unit]->queue_depth); + /* * We may be able to do more I/O */ - - atomic_dec(&queue_depth); - i2ob_request(NULL); + i2ob_request(dev->req_queue); + spin_unlock_irqrestore(&io_request_lock, flags); } -static struct i2o_handler i2o_block_handler = +/* + * Event handler. Needs to be a separate thread b/c we may have + * to do things like scan a partition table, or query parameters + * which cannot be done from an interrupt or from a bottom half. + */ +static int i2ob_evt(void *dummy) { - i2o_block_reply, - "I2O Block OSM", - 0, - I2O_CLASS_RANDOM_BLOCK_STORAGE -}; + unsigned int evt; + unsigned int flags; + int unit; + int i; + + lock_kernel(); + exit_files(current); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oblock"); + evt_running = 1; + + while(1) + { + interruptible_sleep_on(&i2ob_evt_wait); + if(signal_pending(current)) { + evt_running = 0; + return 0; + } + + printk(KERN_INFO "Doing something in i2o_block event thread\n"); + + /* + * Keep another CPU/interrupt from overwriting the + * message while we're reading it + * + * We stuffed the unit in the TxContext and grab the event mask + * None of the BSA we care about events have EventData + */ + spin_lock_irqsave(&i2ob_evt_lock, flags); + unit = evt_msg[3]; + evt = evt_msg[4]; + spin_unlock_irqrestore(&i2ob_evt_lock, flags); + + switch(evt) + { + /* + * New volume loaded on same TID, so we just re-install. + * The TID/controller don't change as it is the same + * I2O device. It's just new media that we have to + * rescan. + */ + case I2O_EVT_IND_BSA_VOLUME_LOAD: + { + i2ob_install_device(i2ob_dev[unit].i2odev->controller, + i2ob_dev[unit].i2odev, unit); + break; + } + + /* + * No media, so set all parameters to 0 and set the media + * change flag. The I2O device is still valid, just doesn't + * have media, so we don't want to clear the controller or + * device pointer. + */ + case I2O_EVT_IND_BSA_VOLUME_UNLOAD: + { + for(i = unit; i <= unit+15; i++) + { + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + i2ob_media_change_flag[unit] = 1; + break; + } + + case I2O_EVT_IND_BSA_VOLUME_UNLOAD_REQ: + printk(KERN_WARNING "%s: Attempt to eject locked media\n", + i2ob_dev[unit].i2odev->dev_name); + break; + + /* + * The capacity has changed and we are going to be + * updating the max_sectors and other information + * about this disk. We try a revalidate first. If + * the block device is in use, we don't want to + * do that as there may be I/Os bound for the disk + * at the moment. In that case we read the size + * from the device and update the information ourselves + * and the user can later force a partition table + * update through an ioctl. + */ + case I2O_EVT_IND_BSA_CAPACITY_CHANGE: + { + u64 size; + + if(do_i2ob_revalidate(MKDEV(MAJOR_NR, unit),0) != -EBUSY) + continue; + + if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) + i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); + + spin_lock_irqsave(&io_request_lock, flags); + i2ob_sizes[unit] = (int)(size>>10); + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); + spin_unlock_irqrestore(&io_request_lock, flags); + break; + } + + /* + * An event we didn't ask for. Call the card manufacturer + * and tell them to fix their firmware :) + */ + default: + printk(KERN_INFO "%s: Received event we didn't register for\n" + KERN_INFO " Call I2O card manufacturer\n", + i2ob_dev[unit].i2odev->dev_name); + break; + } + }; + + return 0; +} /* * The timer handler will attempt to restart requests @@ -418,7 +698,7 @@ static struct i2o_handler i2o_block_handler = * had no more room in its inbound fifo. */ -static void i2ob_timer_handler(unsigned long dummy) +static void i2ob_timer_handler(unsigned long q) { unsigned long flags; @@ -437,7 +717,7 @@ static void i2ob_timer_handler(unsigned long dummy) /* * Restart any requests. */ - i2ob_request(NULL); + i2ob_request((request_queue_t*)q); /* * Free the lock. @@ -452,8 +732,7 @@ static void i2ob_timer_handler(unsigned long dummy) * on us. We must unlink CURRENT in this routine before we return, if * we use it. */ - -static void i2ob_request(request_queue_t * q) +static void i2ob_request(request_queue_t *q) { struct request *req; struct i2ob_request *ireq; @@ -461,26 +740,30 @@ static void i2ob_request(request_queue_t * q) struct i2ob_device *dev; u32 m; - while (!QUEUE_EMPTY) { + // printk(KERN_INFO "i2ob_request() called with queue %p\n", q); + + while (!list_empty(&q->queue_head)) { /* * On an IRQ completion if there is an inactive * request on the queue head it means it isnt yet * ready to dispatch. */ - if(CURRENT->rq_status == RQ_INACTIVE) + req = blkdev_entry_next_request(&q->queue_head); + + if(req->rq_status == RQ_INACTIVE) return; - /* - * Queue depths probably belong with some kind of - * generic IOP commit control. Certainly its not right - * its global! + unit = MINOR(req->rq_dev); + dev = &i2ob_dev[(unit&0xF0)]; + + /* + * Queue depths probably belong with some kind of + * generic IOP commit control. Certainly its not right + * its global! */ - if(atomic_read(&queue_depth)>=MAX_I2OB_DEPTH) + if(atomic_read(&i2ob_queues[dev->unit]->queue_depth)>=MAX_I2OB_DEPTH) break; - req = CURRENT; - unit = MINOR(req->rq_dev); - dev = &i2ob_dev[(unit&0xF0)]; /* Get a message */ m = i2ob_get(dev); @@ -506,6 +789,7 @@ static void i2ob_request(request_queue_t * q) * 500ms. */ i2ob_timer.expires = jiffies + (HZ >> 1); + i2ob_timer.data = (unsigned int)q; /* * Start it. @@ -514,68 +798,79 @@ static void i2ob_request(request_queue_t * q) add_timer(&i2ob_timer); } } + + /* + * Everything ok, so pull from kernel queue onto our queue + */ req->errors = 0; - blkdev_dequeue_request(req); + blkdev_dequeue_request(req); req->sem = NULL; - - ireq = i2ob_qhead; - i2ob_qhead = ireq->next; + + ireq = i2ob_queues[dev->unit]->i2ob_qhead; + i2ob_queues[dev->unit]->i2ob_qhead = ireq->next; ireq->req = req; i2ob_send(m, dev, ireq, i2ob[unit].start_sect, (unit&0xF0)); } } + /* * SCSI-CAM for ioctl geometry mapping * Duplicated with SCSI - this should be moved into somewhere common * perhaps genhd ? + * + * LBA -> CHS mapping table taken from: + * + * "Incorporating the I2O Architecture into BIOS for Intel Architecture + * Platforms" + * + * This is an I2O document that is only available to I2O members, + * not developers. + * + * From my understanding, this is how all the I2O cards do this + * + * Disk Size | Sectors | Heads | Cylinders + * ---------------+---------+-------+------------------- + * 1 < X <= 528M | 63 | 16 | X/(63 * 16 * 512) + * 528M < X <= 1G | 63 | 32 | X/(63 * 32 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * 1 < X <528M | 63 | 16 | X/(63 * 16 * 512) + * */ - +#define BLOCK_SIZE_528M 1081344 +#define BLOCK_SIZE_1G 2097152 +#define BLOCK_SIZE_21G 4403200 +#define BLOCK_SIZE_42G 8806400 +#define BLOCK_SIZE_84G 17612800 + static void i2o_block_biosparam( unsigned long capacity, unsigned short *cyls, unsigned char *hds, unsigned char *secs) { - unsigned long heads, sectors, cylinders, temp; - - cylinders = 1024L; /* Set number of cylinders to max */ - sectors = 62L; /* Maximize sectors per track */ - - temp = cylinders * sectors; /* Compute divisor for heads */ - heads = capacity / temp; /* Compute value for number of heads */ - if (capacity % temp) { /* If no remainder, done! */ - heads++; /* Else, increment number of heads */ - temp = cylinders * heads; /* Compute divisor for sectors */ - sectors = capacity / temp; /* Compute value for sectors per - track */ - if (capacity % temp) { /* If no remainder, done! */ - sectors++; /* Else, increment number of sectors */ - temp = heads * sectors; /* Compute divisor for cylinders */ - cylinders = capacity / temp;/* Compute number of cylinders */ - } - } - /* if something went wrong, then apparently we have to return - a geometry with more than 1024 cylinders */ - if (cylinders == 0 || heads > 255 || sectors > 63 || cylinders >1023) - { - unsigned long temp_cyl; - + unsigned long heads, sectors, cylinders; + + sectors = 63L; /* Maximize sectors per track */ + if(capacity <= BLOCK_SIZE_528M) + heads = 16; + else if(capacity <= BLOCK_SIZE_1G) + heads = 32; + else if(capacity <= BLOCK_SIZE_21G) heads = 64; - sectors = 32; - temp_cyl = capacity / (heads * sectors); - if (temp_cyl > 1024) - { - heads = 255; - sectors = 63; - } - cylinders = capacity / (heads * sectors); - } - *cyls = (unsigned int) cylinders; /* Stuff return values */ - *secs = (unsigned int) sectors; - *hds = (unsigned int) heads; -} + else if(capacity <= BLOCK_SIZE_42G) + heads = 128; + else + heads = 255; + + cylinders = capacity / (heads * sectors); + + *cyls = (unsigned short) cylinders; /* Stuff return values */ + *secs = (unsigned char) sectors; + *hds = (unsigned char) heads; +} + /* * Rescan the partition tables @@ -587,7 +882,7 @@ static int do_i2ob_revalidate(kdev_t dev, int maxu) int i; minor&=0xF0; - + i2ob_dev[minor].refcnt++; if(i2ob_dev[minor].refcnt>maxu+1) { @@ -685,6 +980,21 @@ static int i2ob_release(struct inode *inode, struct file *file) if (minor >= (MAX_I2OB<<4)) return -ENODEV; dev = &i2ob_dev[(minor&0xF0)]; + + /* + * This is to deail with the case of an application + * opening a device and then the device dissapears while + * it's in use, and then the application tries to release + * it. ex: Unmounting a deleted RAID volume at reboot. + * If we send messages, it will just cause FAILs since + * the TID no longer exists. + */ + if(!dev->i2odev) + return 0; + + /* Sync the device so we don't get errors */ + fsync_dev(inode->i_rdev); + if (dev->refcnt <= 0) printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt); dev->refcnt--; @@ -701,6 +1011,7 @@ static int i2ob_release(struct inode *inode, struct file *file) msg[3] = (u32)query_done; msg[4] = 60<<16; i2o_post_wait(dev->controller, msg, 20, 2); + /* * Unlock the media */ @@ -714,7 +1025,7 @@ static int i2ob_release(struct inode *inode, struct file *file) /* * Now unclaim the device. */ - if (i2o_release_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY)<0) + if (i2o_release_device(dev->i2odev, &i2o_block_handler)) printk(KERN_ERR "i2ob_release: controller rejected unclaim.\n"); } @@ -737,22 +1048,21 @@ static int i2ob_open(struct inode *inode, struct file *file) if (minor >= MAX_I2OB<<4) return -ENODEV; dev=&i2ob_dev[(minor&0xF0)]; - if(dev->i2odev == NULL) + + if(!dev->i2odev) return -ENODEV; - + if(dev->refcnt++==0) { u32 msg[6]; - int *query_done; - - if(i2o_claim_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY)<0) + if(i2o_claim_device(dev->i2odev, &i2o_block_handler)) { dev->refcnt--; + printk(KERN_INFO "I2O Block: Could not open device\n"); return -EBUSY; } - query_done = &dev->done_flag; /* * Mount the media if needed. Note that we don't use * the lock bit. Since we have to issue a lock if it @@ -761,18 +1071,15 @@ static int i2ob_open(struct inode *inode, struct file *file) */ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1] = I2O_CMD_BLOCK_MMOUNT<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; msg[4] = -1; msg[5] = 0; i2o_post_wait(dev->controller, msg, 24, 2); + /* * Lock the media */ msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1] = I2O_CMD_BLOCK_MLOCK<<24|HOST_TID<<12|dev->tid; - msg[2] = i2ob_context|0x40000000; - msg[3] = (u32)query_done; msg[4] = -1; i2o_post_wait(dev->controller, msg, 20, 2); } @@ -806,11 +1113,16 @@ static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, i struct i2ob_device *dev=&i2ob_dev[unit]; int i; + /* + * For logging purposes... + */ + printk(KERN_INFO "i2ob: Installing tid %d device at unit %d\n", + d->lct_data.tid, unit); + /* * Ask for the current media data. If that isn't supported * then we ask for the device capacity data */ - if(i2ob_query_device(dev, 0x0004, 1, &blocksize, 4) != 0 || i2ob_query_device(dev, 0x0004, 0, &size, 8) !=0 ) { @@ -822,23 +1134,35 @@ static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, i i2ob_query_device(dev, 0x0000, 6, &status, 4); i2ob_sizes[unit] = (int)(size>>10); i2ob_hardsizes[unit] = blocksize; + i2ob_gendisk.part[unit].nr_sects = size>>9; + i2ob[unit].nr_sects = (int)(size>>9); - limit=4096; /* 8 deep scatter gather */ + /* Set limit based on inbound frame size */ + limit = (d->controller->status_block->inbound_frame_size - 8)/2; + limit = limit<<9; - printk("Byte limit is %d.\n", limit); + /* + * Max number of Scatter-Gather Elements + */ + i2ob_dev[unit].max_segments = + (d->controller->status_block->inbound_frame_size - 8)/2; + + printk(KERN_INFO "Max Segments set to %d\n", + i2ob_dev[unit].max_segments); + printk(KERN_INFO "Byte limit is %d.\n", limit); for(i=unit;i<=unit+15;i++) - i2ob_max_sectors[i]=(limit>>9); - - i2ob[unit].nr_sects = (int)(size>>9); + { + i2ob_max_sectors[i]=MAX_SECTORS; + i2ob_dev[i].max_segments = + (d->controller->status_block->inbound_frame_size - 8)/2; + } i2ob_query_device(dev, 0x0000, 0, &type, 1); sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); - printk("%s: ", d->dev_name); - if(status&(1<<10)) - printk("RAID "); + printk(KERN_INFO "%s: ", d->dev_name); switch(type) { case 0: printk("Disk Storage");break; @@ -848,13 +1172,15 @@ static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, i default: printk("Type %d", type); } + if(status&(1<<10)) + printk("(RAID)"); if(((flags & (1<<3)) && !(status & (1<<3))) || ((flags & (1<<4)) && !(status & (1<<4)))) { - printk(" Not loaded.\n"); - return 0; + printk(KERN_INFO " Not loaded.\n"); + return 1; } - printk(" %dMb, %d byte sectors", + printk("- %dMb, %d byte sectors", (int)(size>>20), blocksize); if(status&(1<<0)) { @@ -867,12 +1193,91 @@ static int i2ob_install_device(struct i2o_controller *c, struct i2o_device *d, i printk(", %dKb cache", cachesize); } printk(".\n"); - printk("%s: Maximum sectors/read set to %d.\n", + printk(KERN_INFO "%s: Maximum sectors/read set to %d.\n", d->dev_name, i2ob_max_sectors[unit]); + + /* + * If this is the first I2O block device found on this IOP, + * we need to initialize all the queue data structures + * before any I/O can be performed. If it fails, this + * device is useless. + */ + if(!i2ob_queues[c->unit]) { + if(i2ob_init_iop(c->unit)) + return 1; + } + + /* + * This will save one level of lookup/indirection in critical + * code so that we can directly get the queue ptr from the + * device instead of having to go the IOP data structure. + */ + dev->req_queue = &i2ob_queues[c->unit]->req_queue; + grok_partitions(&i2ob_gendisk, unit>>4, 1<<4, (long)(size>>9)); + + /* + * Register for the events we're interested in and that the + * device actually supports. + */ + i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, + (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); + + return 0; +} + +/* + * Initialize IOP specific queue structures. This is called + * once for each IOP that has a block device sitting behind it. + */ +static int i2ob_init_iop(unsigned int unit) +{ + int i; + + i2ob_queues[unit] = (struct i2ob_iop_queue*) + kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC); + if(!i2ob_queues[unit]) + { + printk(KERN_WARNING + "Could not allocate request queue for I2O block device!\n"); + return -1; + } + + for(i = 0; i< MAX_I2OB_DEPTH; i++) + { + i2ob_queues[unit]->request_queue[i].next = + &i2ob_queues[unit]->request_queue[i+1]; + i2ob_queues[unit]->request_queue[i].num = i; + } + + /* Queue is MAX_I2OB + 1... */ + i2ob_queues[unit]->request_queue[i].next = NULL; + i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; + atomic_set(&i2ob_queues[unit]->queue_depth, 0); + + blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); + blk_queue_headactive(&i2ob_queues[unit]->req_queue, 0); + i2ob_queues[unit]->req_queue.back_merge_fn = i2ob_back_merge; + i2ob_queues[unit]->req_queue.front_merge_fn = i2ob_front_merge; + i2ob_queues[unit]->req_queue.merge_requests_fn = i2ob_merge_requests; + i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; + return 0; } +/* + * Get the request queue for the given device. + */ +static request_queue_t* i2ob_get_queue(kdev_t dev) +{ + int unit = MINOR(dev)&0xF0; + + return i2ob_dev[unit].req_queue; +} + +/* + * Probe the I2O subsytem for block class devices + */ static void i2ob_probe(void) { int i; @@ -889,12 +1294,20 @@ static void i2ob_probe(void) for(d=c->devices;d!=NULL;d=d->next) { - if(d->lct_data->class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) + if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE) continue; - if(d->lct_data->user_tid != 0xFFF) + if(d->lct_data.user_tid != 0xFFF) continue; + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_WARNING "i2o_block: Controller %d, TID %d\n", c->unit, + d->lct_data.tid); + printk(KERN_WARNING "\tDevice refused claim! Skipping installation\n"); + continue; + } + if(uniti2odev = d; dev->controller = c; - dev->tid = d->lct_data->tid; - - /* - * Insure the device can be claimed - * before installing it. - */ - if(i2o_claim_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY )==0) + dev->unit = c->unit; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,unit)) + printk(KERN_WARNING "Could not install I2O block device\n"); + else { - printk(KERN_INFO "Claimed Dev %p Tid %d Unit %d\n",dev,dev->tid,unit); - i2ob_install_device(c,d,unit); - unit+=16; - - /* - * Now that the device has been - * installed, unclaim it so that - * it can be claimed by either - * the block or scsi driver. - */ - if(i2o_release_device(dev->i2odev, &i2o_block_handler, I2O_CLAIM_PRIMARY)) - printk(KERN_INFO "Could not unclaim Dev %p Tid %d\n",dev,dev->tid); + unit+=16; + i2ob_dev_count++; + + /* We want to know when device goes away */ + i2o_device_notify_on(d, &i2o_block_handler); } - else - printk(KERN_INFO "TID %d not claimed\n",dev->tid); } else { if(!warned++) - printk("i2o_block: too many device, registering only %d.\n", unit>>4); + printk(KERN_WARNING "i2o_block: too many device, registering only %d.\n", unit>>4); } + i2o_release_device(d, &i2o_block_handler); } i2o_unlock_controller(c); } - i2ob_devices = unit; } /* - * Have we seen a media change ? + * New device notification handler. Called whenever a new + * I2O block storage device is added to the system. + * + * Should we spin lock around this to keep multiple devs from + * getting updated at the same time? + * */ +void i2ob_new_device(struct i2o_controller *c, struct i2o_device *d) +{ + struct i2ob_device *dev; + int unit = 0; + + printk(KERN_INFO "i2o_block: New device detected\n"); + printk(KERN_INFO " Controller %d Tid %d\n",c->unit, d->lct_data.tid); + + /* Check for available space */ + if(i2ob_dev_count>=MAX_I2OB<<4) + { + printk(KERN_ERR "i2o_block: No more devices allowed!\n"); + return; + } + for(unit = 0; unit < (MAX_I2OB<<4); unit += 16) + { + if(!i2ob_dev[unit].i2odev) + break; + } + + /* + * Creating a RAID 5 volume takes a little while and the UTIL_CLAIM + * will fail if we don't give the card enough time to do it's magic, + * so we just sleep for a little while and let it do it's thing + */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(10*HZ); + + if(i2o_claim_device(d, &i2o_block_handler)) + { + printk(KERN_INFO + "i2o_block: Unable to claim device. Installation aborted\n"); + return; + } + + dev = &i2ob_dev[unit]; + dev->i2odev = d; + dev->controller = c; + dev->tid = d->lct_data.tid; + + if(i2ob_install_device(c,d,unit)) + printk(KERN_ERR "i2o_block: Could not install new device\n"); + else + { + i2ob_dev_count++; + i2o_device_notify_on(d, &i2o_block_handler); + } + + i2o_release_device(d, &i2o_block_handler); + return; +} + +/* + * Deleted device notification handler. Called when a device we + * are talking to has been deleted by the user or some other + * mysterious fource outside the kernel. + */ +void i2ob_del_device(struct i2o_controller *c, struct i2o_device *d) +{ + int unit = 0; + int i = 0; + int flags; + + spin_lock_irqsave(&io_request_lock, flags); + + /* + * Need to do this...we somtimes get two events from the IRTOS + * in a row and that causes lots of problems. + */ + i2o_device_notify_off(d, &i2o_block_handler); + + printk(KERN_INFO "I2O Block Device Deleted\n"); + + for(unit = 0; unit < MAX_I2OB<<4; unit += 16) + { + if(i2ob_dev[unit].i2odev == d) + { + printk(KERN_INFO " /dev/%s: Controller %d Tid %d\n", + d->dev_name, c->unit, d->lct_data.tid); + break; + } + } + if(unit >= MAX_I2OB<<4) + { + printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); + return; + } + + /* + * This will force errors when i2ob_get_queue() is called + * by the kenrel. + */ + i2ob_dev[unit].req_queue = NULL; + for(i = unit; i <= unit+15; i++) + { + i2ob_dev[i].i2odev = NULL; + i2ob_sizes[i] = 0; + i2ob_hardsizes[i] = 0; + i2ob_max_sectors[i] = 0; + i2ob[i].nr_sects = 0; + i2ob_gendisk.part[i].nr_sects = 0; + } + spin_unlock_irqrestore(&io_request_lock, flags); + + /* + * Sync the device...this will force all outstanding I/Os + * to attempt to complete, thus causing error messages. + * We have to do this as the user could immediatelly create + * a new volume that gets assigned the same minor number. + * If there are still outstanding writes to the device, + * that could cause data corruption on the new volume! + * + * The truth is that deleting a volume that you are currently + * accessing will do _bad things_ to your system. This + * handler will keep it from crashing, but must probably + * you'll have to do a 'reboot' to get the system running + * properly. Deleting disks you are using is dumb. + * Umount them first and all will be good! + * + * It's not this driver's job to protect the system from + * dumb user mistakes :) + */ + if(i2ob_dev[unit].refcnt) + fsync_dev(MKDEV(MAJOR_NR,unit)); + + /* + * Decrease usage count for module + */ + while(i2ob_dev[unit].refcnt--) + MOD_DEC_USE_COUNT; + + i2ob_dev[unit].refcnt = 0; + + i2ob_dev[i].tid = 0; + + /* + * Do we need this? + * The media didn't really change...the device is just gone + */ + i2ob_media_change_flag[unit] = 1; + + i2ob_dev_count--; + + return; +} + +/* + * Have we seen a media change ? + */ static int i2ob_media_change(kdev_t dev) { int i=MINOR(dev); @@ -960,12 +1517,14 @@ static int i2ob_revalidate(kdev_t dev) return do_i2ob_revalidate(dev, 0); } -static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void *p) +/* + * Reboot notifier. This is called by i2o_core when the system + * shuts down. + */ +static void i2ob_reboot_event(void) { int i; - if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) - return NOTIFY_DONE; for(i=0;irefcnt!=0) { /* - * Flush the onboard cache on power down - * also unlock the media + * Flush the onboard cache */ u32 msg[5]; int *query_done = &dev->done_flag; @@ -984,6 +1542,7 @@ static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void msg[3] = (u32)query_done; msg[4] = 60<<16; i2o_post_wait(dev->controller, msg, 20, 2); + /* * Unlock the media */ @@ -995,25 +1554,18 @@ static int i2ob_reboot_event(struct notifier_block *n, unsigned long code, void i2o_post_wait(dev->controller, msg, 20, 2); } } - return NOTIFY_DONE; } -struct notifier_block i2ob_reboot_notifier = -{ - i2ob_reboot_event, - NULL, - 0 -}; - static struct block_device_operations i2ob_fops = { - open: i2ob_open, - release: i2ob_release, - ioctl: i2ob_ioctl, - check_media_change: i2ob_media_change, - revalidate: i2ob_revalidate, + open: i2ob_open, + release: i2ob_release, + ioctl: i2ob_ioctl, + check_media_change: i2ob_media_change, + revalidate: i2ob_revalidate, }; - + + static struct gendisk i2ob_gendisk = { MAJOR_NR, @@ -1027,6 +1579,7 @@ static struct gendisk i2ob_gendisk = NULL }; + /* * And here should be modules and kernel interface * (Just smiley confuses emacs :-) @@ -1040,19 +1593,20 @@ int i2o_block_init(void) { int i; - printk(KERN_INFO "I2O Block Storage OSM v0.07. (C) 1999 Red Hat Software.\n"); + printk(KERN_INFO "I2O Block Storage OSM v0.9\n"); + printk(KERN_INFO " (c) Copyright 1999, 2000 Red Hat Software.\n"); /* * Register the block device interfaces */ if (register_blkdev(MAJOR_NR, "i2o_block", &i2ob_fops)) { - printk("Unable to get major number %d for i2o_block\n", + printk(KERN_ERR "Unable to get major number %d for i2o_block\n", MAJOR_NR); return -EIO; } #ifdef MODULE - printk("i2o_block: registered device at major %d\n", MAJOR_NR); + printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR); #endif /* @@ -1063,6 +1617,7 @@ int i2o_block_init(void) hardsect_size[MAJOR_NR] = i2ob_hardsizes; blk_size[MAJOR_NR] = i2ob_sizes; max_sectors[MAJOR_NR] = i2ob_max_sectors; + blk_dev[MAJOR_NR].queue = i2ob_get_queue; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0); @@ -1082,17 +1637,11 @@ int i2o_block_init(void) /* * Set up the queue */ - - for(i = 0; i< MAX_I2OB_DEPTH; i++) + for(i = 0; i < MAX_I2O_CONTROLLERS; i++) { - i2ob_queue[i].next = &i2ob_queue[i+1]; - i2ob_queue[i].num = i; + i2ob_queues[i] = NULL; } - - /* Queue is MAX_I2OB + 1... */ - i2ob_queue[i].next = NULL; - i2ob_qhead = &i2ob_queue[0]; - + /* * Timers */ @@ -1115,15 +1664,26 @@ int i2o_block_init(void) i2ob_context = i2o_block_handler.context; /* - * Finally see what is actually plugged in to our controllers + * Initialize event handling thread */ + init_MUTEX_LOCKED(&i2ob_evt_sem); + evt_pid = kernel_thread(i2ob_evt, NULL, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR + "i2o_block: Could not initialize event thread. Aborting\n"); + i2o_remove_handler(&i2o_block_handler); + return 0; + } + /* + * Finally see what is actually plugged in to our controllers + */ for (i = 0; i < MAX_I2OB; i++) register_disk(&i2ob_gendisk, MKDEV(MAJOR_NR,i<<4), 1<<4, &i2ob_fops, 0); i2ob_probe(); - register_reboot_notifier(&i2ob_reboot_notifier); return 0; } @@ -1136,8 +1696,21 @@ MODULE_DESCRIPTION("I2O Block Device OSM"); void cleanup_module(void) { struct gendisk **gdp; + int i; - unregister_reboot_notifier(&i2ob_reboot_notifier); + /* + * Unregister for updates from any devices..otherwise we still + * get them and the core jumps to random memory :O + */ + if(i2ob_dev_count) { + struct i2o_device *d; + for(i = 0; i < MAX_I2OB; i++) + if((d=i2ob_dev[i<<4].i2odev)) { + i2o_device_notify_off(d, &i2o_block_handler); + i2o_event_register(d->controller, d->lct_data.tid, + i2ob_context, i<<4, 0); + } + } /* * Flush the OSM @@ -1151,6 +1724,21 @@ void cleanup_module(void) if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) printk("i2o_block: cleanup_module failed\n"); + if(evt_running) { + i = kill_proc(evt_pid, SIGTERM, 1); + if(!i) { + int count = 5 * 100; + while(evt_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk(KERN_ERR "Giving up on i2oblock thread...\n"); + } + } + + /* * Why isnt register/unregister gendisk in the kernel ??? */ diff --git a/drivers/i2o/i2o_config.c b/drivers/i2o/i2o_config.c index c89fe3b97f4b..c2b72b21c2df 100644 --- a/drivers/i2o/i2o_config.c +++ b/drivers/i2o/i2o_config.c @@ -166,6 +166,9 @@ static void i2o_cfg_reply(struct i2o_handler *h, struct i2o_controller *c, struc struct i2o_handler cfg_handler= { i2o_cfg_reply, + NULL, + NULL, + NULL, "Configuration", 0, 0xffffffff // All classes @@ -409,14 +412,14 @@ static int ioctl_parms(unsigned long arg, unsigned int type) return -ENOMEM; } - len = i2o_issue_params(i2o_cmd, c, kcmd.tid, - ops, kcmd.oplen, res, 65536); - i2o_unlock_controller(c); + len = i2o_issue_params(i2o_cmd, c, kcmd.tid, + ops, kcmd.oplen, res, 65536); + i2o_unlock_controller(c); kfree(ops); if (len < 0) { kfree(res); - return len; /* -DetailedStatus */ + return -EAGAIN; } put_user(len, kcmd.reslen); @@ -749,7 +752,7 @@ static int ioctl_evt_reg(unsigned long arg, struct file *fp) /* Device exists? */ for(d = iop->devices; d; d = d->next) - if(d->lct_data->tid == kdesc.tid) + if(d->lct_data.tid == kdesc.tid) break; if(!d) @@ -903,7 +906,7 @@ int __init i2o_config_init(void) #endif { printk(KERN_INFO "I2O configuration manager v 0.04.\n"); - printk(KERN_INFO " (C) Copyright 1999 Red Hat Software"); + printk(KERN_INFO " (C) Copyright 1999 Red Hat Software\n"); if((page_buf = kmalloc(4096, GFP_KERNEL))==NULL) { diff --git a/drivers/i2o/i2o_core.c b/drivers/i2o/i2o_core.c index 30aecaec5d9f..6d1092843dea 100644 --- a/drivers/i2o/i2o_core.c +++ b/drivers/i2o/i2o_core.c @@ -2,7 +2,7 @@ * Core I2O structure managment * * (C) Copyright 1999 Red Hat Software - * + * * Written by Alan Cox, Building Number Three Ltd * * This program is free software; you can redistribute it and/or @@ -12,12 +12,13 @@ * * A lot of the I2O message side code from this is taken from the * Red Creek RCPCI45 adapter driver by Red Creek Communications - * + * * Fixes by: * Philipp Rumpf * Juha Sievänen * Auvo Häkkinen * Deepak Saxena + * */ #include @@ -31,51 +32,73 @@ #include #include #include +#include #include #include +#include #include +#include +#include +#include +#include #include +#include #include "i2o_lan.h" // #define DRIVERDEBUG -// #define DEBUG_IRQ +#ifdef DRIVERDEBUG +#define dprintk(x) printk x +#else #define dprintk(x) +#endif -/* - * Size of the I2O module table - */ - -static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES]; -static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS]; -struct i2o_controller *i2o_controller_chain; +/* OSM table */ +static struct i2o_handler *i2o_handlers[MAX_I2O_MODULES] = {NULL}; + +/* Controller list */ +static struct i2o_controller *i2o_controllers[MAX_I2O_CONTROLLERS] = {NULL}; +struct i2o_controller *i2o_controller_chain = NULL; int i2o_num_controllers = 0; + +/* Initiator Context for Core message */ static int core_context = 0; -static int i2o_activate_controller(struct i2o_controller *iop); -static int i2o_online_controller(struct i2o_controller *c); -static int i2o_init_outbound_q(struct i2o_controller *c); +/* Initialization && shutdown functions */ +static void i2o_sys_init(void); +static void i2o_sys_shutdown(void); +static int i2o_clear_controller(struct i2o_controller *); +static int i2o_reboot_event(struct notifier_block *, unsigned long , void *); +static int i2o_online_controller(struct i2o_controller *); +static int i2o_init_outbound_q(struct i2o_controller *); +static int i2o_post_outbound_messages(struct i2o_controller *); +static int i2o_issue_claim(struct i2o_controller *, int, int, int, u32); + +/* Reply handler */ static void i2o_core_reply(struct i2o_handler *, struct i2o_controller *, struct i2o_message *); -static int i2o_add_management_user(struct i2o_device *, struct i2o_handler *); -static int i2o_remove_management_user(struct i2o_device *, struct i2o_handler *); -void i2o_dump_message(u32 *msg); - -static int i2o_issue_claim(struct i2o_controller *, int, int, int, u32); -static int i2o_reset_controller(struct i2o_controller *); +/* Various helper functions */ static int i2o_lct_get(struct i2o_controller *); +static int i2o_lct_notify(struct i2o_controller *); static int i2o_hrt_get(struct i2o_controller *); -static void i2o_sys_init(void); -static void i2o_sys_shutdown(void); - static int i2o_build_sys_table(void); static int i2o_systab_send(struct i2o_controller *c); +/* I2O core event handler */ +static int i2o_core_evt(void *); +static int evt_pid; +static int evt_running; + +/* Dynamic LCT update handler */ +static int i2o_dyn_lct(void *); + +void i2o_report_controller_unit(struct i2o_controller *, struct i2o_device *); + /* * I2O System Table. Contains information about * all the IOPs in the system. Used to inform IOPs @@ -126,65 +149,139 @@ static u32 post_wait_id = 0; // Unique ID for each post_wait static spinlock_t post_wait_lock = SPIN_LOCK_UNLOCKED; static void i2o_post_wait_complete(u32, int); -/* Message handler */ +/* OSM descriptor handler */ static struct i2o_handler i2o_core_handler = { (void *)i2o_core_reply, + NULL, + NULL, + NULL, "I2O core layer", + 0, 0 }; /* - * I2O configuration spinlock. This isnt a big deal for contention - * so we have one only + * Used when queing a reply to be handled later + */ +struct reply_info +{ + struct i2o_controller *iop; + u32 msg[MSG_FRAME_SIZE]; +}; +static struct reply_info evt_reply; +static struct reply_info events[I2O_EVT_Q_LEN]; +static int evt_in = 0; +static int evt_out = 0; +static int evt_q_len = 0; +#define MODINC(x,y) (x = x++ % y) + +/* + * I2O configuration spinlock. This isnt a big deal for contention + * so we have one only */ - static spinlock_t i2o_configuration_lock = SPIN_LOCK_UNLOCKED; +/* + * Event spinlock. Used to keep event queue sane and from + * handling multiple events simultaneously. + */ +static spinlock_t i2o_evt_lock = SPIN_LOCK_UNLOCKED; + +/* + * Semaphore used to syncrhonize event handling thread with + * interrupt handler. + */ +DECLARE_MUTEX(evt_sem); +DECLARE_WAIT_QUEUE_HEAD(evt_wait); + +static struct notifier_block i2o_reboot_notifier = +{ + i2o_reboot_event, + NULL, + 0 +}; + + /* * I2O Core reply handler - * - * Only messages this should see are i2o_post_wait() replies */ void i2o_core_reply(struct i2o_handler *h, struct i2o_controller *c, struct i2o_message *m) { u32 *msg=(u32 *)m; - int status; + u32 status; u32 context = msg[2]; #if 0 i2o_report_status(KERN_INFO, "i2o_core", msg); #endif - + if (msg[0] & (1<<13)) // Fail bit is set - { - printk(KERN_ERR "%s: Failed to process the msg:\n",c->name); - printk(KERN_ERR " Cmd = 0x%02X, InitiatorTid = %d, TargetTid =%d\n", - (msg[1] >> 24) & 0xFF, (msg[1] >> 12) & 0xFFF, msg[1] & - 0xFFF); - printk(KERN_ERR " FailureCode = 0x%02X\n Severity = 0x%02X\n" - "LowestVersion = 0x%02X\n HighestVersion = 0x%02X\n", - msg[4] >> 24, (msg[4] >> 16) & 0xFF, - (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); - printk(KERN_ERR " FailingHostUnit = 0x%04X\n FailingIOP = 0x%03X\n", - msg[5] >> 16, msg[5] & 0xFFF); - return; - } + { + printk(KERN_ERR "%s: Failed to process the msg:\n",c->name); + printk(KERN_ERR " Cmd = 0x%02X, InitiatorTid = %d, TargetTid =% d\n", + (msg[1] >> 24) & 0xFF, (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); + printk(KERN_ERR " FailureCode = 0x%02X\n Severity = 0x%02X\n" + "LowestVersion = 0x%02X\n HighestVersion = 0x%02X\n", + msg[4] >> 24, (msg[4] >> 16) & 0xFF, + (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); + printk(KERN_ERR " FailingHostUnit = 0x%04X\n FailingIOP = 0x%03X\n", + msg[5] >> 16, msg[5] & 0xFFF); + return; + } if(msg[2]&0x80000000) // Post wait message { if (msg[4] >> 24) { - i2o_report_status(KERN_WARNING, "i2o_core: post_wait reply", msg); + i2o_report_status(KERN_INFO, "i2o_core: post_wait reply", msg); status = -(msg[4] & 0xFFFF); } else status = I2O_POST_WAIT_OK; i2o_post_wait_complete(context, status); + return; + } + + if(m->function == I2O_CMD_UTIL_EVT_REGISTER) + { + memcpy(events[evt_in].msg, msg, MSG_FRAME_SIZE); + events[evt_in].iop = c; + + spin_lock(&i2o_evt_lock); + MODINC(evt_in, I2O_EVT_Q_LEN); + if(evt_q_len == I2O_EVT_Q_LEN) + MODINC(evt_out, I2O_EVT_Q_LEN); + else + evt_q_len++; + spin_unlock(&i2o_evt_lock); + + up(&evt_sem); + wake_up_interruptible(&evt_wait); + return; + } + + if(m->function == I2O_CMD_LCT_NOTIFY) + { + up(&c->lct_sem); + return; } + + /* + * If this happens, we want to dump the message to the syslog so + * it can be sent back to the card manufacturer by the end user + * to aid in debugging. + * + */ + printk(KERN_WARNING "%s: Unsolicited message reply sent to core!" + "Message dumped to syslog\n", + c->name); + i2o_dump_message(msg); + + return; } /* @@ -218,8 +315,9 @@ int i2o_remove_handler(struct i2o_handler *h) /* - * Each I2O controller has a chain of devices on it - these match - * the useful parts of the LCT of the board. + * Each I2O controller has a chain of devices on it. + * Each device has a pointer to it's LCT entry to be used + * for fun purposes. */ int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) @@ -245,19 +343,43 @@ int i2o_install_device(struct i2o_controller *c, struct i2o_device *d) int __i2o_delete_device(struct i2o_device *d) { struct i2o_device **p; + int i; p=&(d->controller->devices); /* * Hey we have a driver! + * Check to see if the driver wants us to notify it of + * device deletion. If it doesn't we assume that it + * is unsafe to delete a device with an owner and + * fail. */ - if(d->owner) - return -EBUSY; + { + if(d->owner->dev_del_notify) + { + dprintk((KERN_INFO "Device has owner, notifying\n")); + d->owner->dev_del_notify(d->controller, d); + if(d->owner) + { + printk(KERN_WARNING + "Driver \"%s\" did not release device!\n", d->owner->name); + return -EBUSY; + } + } + else + return -EBUSY; + } /* - * Seek, locate + * Tell any other users who are talking to this device + * that it's going away. We assume that everything works. */ + for(i=0; i < I2O_MAX_MANAGERS; i++) + { + if(d->managers[i] && d->managers[i]->dev_del_notify) + d->managers[i]->dev_del_notify(d->controller, d); + } while(*p!=NULL) { @@ -282,6 +404,10 @@ int i2o_delete_device(struct i2o_device *d) spin_lock(&i2o_configuration_lock); + /* + * Seek, locate + */ + ret = __i2o_delete_device(d); spin_unlock(&i2o_configuration_lock); @@ -302,12 +428,18 @@ int i2o_install_controller(struct i2o_controller *c) if(i2o_controllers[i]==NULL) { i2o_controllers[i]=c; + c->devices = NULL; c->next=i2o_controller_chain; i2o_controller_chain=c; c->unit = i; - + c->page_frame = NULL; + c->hrt = NULL; + c->lct = NULL; + c->dlct = (i2o_lct*)kmalloc(8192, GFP_KERNEL); + c->status_block = NULL; sprintf(c->name, "i2o/iop%d", i); i2o_num_controllers++; + init_MUTEX_LOCKED(&c->lct_sem); spin_unlock(&i2o_configuration_lock); return 0; } @@ -322,12 +454,21 @@ int i2o_delete_controller(struct i2o_controller *c) struct i2o_controller **p; int users; char name[16]; + int stat; + + dprintk((KERN_INFO "Deleting controller iop%d\n", c->unit)); + + /* + * Clear event registration as this can cause weird behavior + */ + if(c->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) + i2o_event_register(c, core_context, 0, 0, 0); spin_lock(&i2o_configuration_lock); if((users=atomic_read(&c->users))) { - printk(KERN_INFO "%s busy: %d users for controller.\n", c->name, users); - c->bus_disable(c); + dprintk((KERN_INFO "I2O: %d users for controller iop%d\n", users, + c->unit)); spin_unlock(&i2o_configuration_lock); return -EBUSY; } @@ -336,20 +477,40 @@ int i2o_delete_controller(struct i2o_controller *c) if(__i2o_delete_device(c->devices)<0) { /* Shouldnt happen */ - c->bus_disable(c); + c->bus_disable(c); spin_unlock(&i2o_configuration_lock); return -EBUSY; } } + /* + * If this is shutdown time, the thread's already been killed + */ + if(c->lct_running) { + stat = kill_proc(c->lct_pid, SIGTERM, 1); + if(!stat) { + int count = 10 * 100; + while(c->lct_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk(KERN_ERR + "%s: LCT thread still running!\n", + c->name); + } + } + p=&i2o_controller_chain; while(*p) { if(*p==c) { - /* Ask the IOP to switch into RESET state */ - i2o_reset_controller(c); + /* Ask the IOP to switch to HOLD state */ + if (i2o_clear_controller(c) < 0) + printk(KERN_ERR "Unable to clear iop%d\n", c->unit); /* Release IRQ */ c->destructor(c); @@ -365,14 +526,15 @@ int i2o_delete_controller(struct i2o_controller *c) kfree(c->lct); if(c->status_block) kfree(c->status_block); + if(c->dlct) + kfree(c->dlct); i2o_controllers[c->unit]=NULL; memcpy(name, c->name, strlen(c->name)+1); kfree(c); - i2o_num_controllers--; - dprintk((KERN_INFO "%s: Deleted from controller chain.\n", name)); - + + i2o_num_controllers--; return 0; } p=&((*p)->next); @@ -404,105 +566,86 @@ struct i2o_controller *i2o_find_controller(int n) /* - * Claim a device for use as either the primary user or just - * as a management/secondary user + * Claim a device for use by an OSM */ -int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h, u32 type) +int i2o_claim_device(struct i2o_device *d, struct i2o_handler *h) { - /* Device already has a primary user or too many managers */ - if((type == I2O_CLAIM_PRIMARY && d->owner) || - (d->num_managers == I2O_MAX_MANAGERS)) - { - return -EBUSY; - } - - if(i2o_issue_claim(d->controller,d->lct_data->tid, h->context, 1, type)) + spin_lock(&i2o_configuration_lock); + if(d->owner) { + printk(KERN_INFO "issue claim called, but dev as owner!"); + spin_unlock(&i2o_configuration_lock); return -EBUSY; } - spin_lock(&i2o_configuration_lock); - if(d->owner) + if(i2o_issue_claim(d->controller,d->lct_data.tid, h->context, 1, + I2O_CLAIM_PRIMARY)) { spin_unlock(&i2o_configuration_lock); return -EBUSY; } - atomic_inc(&d->controller->users); - - if(type == I2O_CLAIM_PRIMARY) - d->owner=h; - else - if (i2o_add_management_user(d, h)) - printk(KERN_WARNING "i2o: Too many managers for TID %d\n", - d->lct_data->tid); - + d->owner=h; spin_unlock(&i2o_configuration_lock); return 0; } -int i2o_release_device(struct i2o_device *d, struct i2o_handler *h, u32 type) +/* + * Release a device that the OS is using + */ +int i2o_release_device(struct i2o_device *d, struct i2o_handler *h) { int err = 0; spin_lock(&i2o_configuration_lock); - - /* Primary user */ - if(type == I2O_CLAIM_PRIMARY) + if(d->owner != h) { - if(d->owner != h) - err = -ENOENT; - else - { - if(i2o_issue_claim(d->controller, d->lct_data->tid, h->context, 0, - type)) - { - err = -ENXIO; - } - else - { - d->owner = NULL; - atomic_dec(&d->controller->users); - } - } - spin_unlock(&i2o_configuration_lock); - return err; - } + return -ENOENT; + } - /* Management or other user */ - if(i2o_remove_management_user(d, h)) - err = -ENOENT; - else + if(i2o_issue_claim(d->controller, d->lct_data.tid, h->context, 0, + I2O_CLAIM_PRIMARY)) { - atomic_dec(&d->controller->users); - - if(i2o_issue_claim(d->controller,d->lct_data->tid, h->context, 0, - type)) - err = -ENXIO; + err = -ENXIO; } + d->owner = NULL; + spin_unlock(&i2o_configuration_lock); return err; } -int i2o_add_management_user(struct i2o_device *d, struct i2o_handler *h) +/* + * Called by OSMs to let the core know that they want to be + * notified if the given device is deleted from the system. + */ +int i2o_device_notify_on(struct i2o_device *d, struct i2o_handler *h) { int i; if(d->num_managers == I2O_MAX_MANAGERS) - return 1; + return -ENOSPC; for(i = 0; i < I2O_MAX_MANAGERS; i++) + { if(!d->managers[i]) + { d->managers[i] = h; + break; + } + } d->num_managers++; return 0; } -int i2o_remove_management_user(struct i2o_device *d, struct i2o_handler *h) +/* + * Called by OSMs to let the core know that they no longer + * are interested in the fate of the given device. + */ +int i2o_device_notify_off(struct i2o_device *d, struct i2o_handler *h) { int i; @@ -511,6 +654,7 @@ int i2o_remove_management_user(struct i2o_device *d, struct i2o_handler *h) if(d->managers[i] == h) { d->managers[i] = NULL; + d->num_managers--; return 0; } } @@ -518,6 +662,305 @@ int i2o_remove_management_user(struct i2o_device *d, struct i2o_handler *h) return -ENOENT; } +/* + * Event registration API + */ +int i2o_event_register(struct i2o_controller *c, u32 tid, + u32 init_context, u32 tr_context, u32 evt_mask) +{ + u32 msg[5]; // Not performance critical, so we just + // i2o_post_this it instead of building it + // in IOP memory + + msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_UTIL_EVT_REGISTER<<24 | HOST_TID<<12 | tid; + msg[2] = (u32)init_context; + msg[3] = (u32)tr_context; + msg[4] = evt_mask; + + return i2o_post_this(c, msg, sizeof(msg)); +} + +/* + * Event ack API + * + * We just take a pointer to the original UTIL_EVENT_REGISTER reply + * message and change the function code since that's what spec + * describes an EventAck message looking like. + */ +int i2o_event_ack(struct i2o_controller *c, u32 *msg) +{ + struct i2o_message *m = (struct i2o_message *)msg; + + m->function = I2O_CMD_UTIL_EVT_ACK; + + return i2o_post_wait(c, msg, m->size * 4, 2); +} + +/* + * Core event handler. Runs as a separate thread and is woken + * up whenever there is an Executive class event. + */ +static int i2o_core_evt(void *foo) +{ + struct reply_info reply_data; + struct reply_info *reply = &reply_data; + u32 *msg = reply->msg; + struct i2o_controller *c = NULL; + int flags; + + lock_kernel(); + exit_files(current); + daemonize(); + unlock_kernel(); + + strcpy(current->comm, "i2oevtd"); + evt_running = 1; + + while(1) + { + down_interruptible(&evt_sem); + if(signal_pending(current)) + { + dprintk((KERN_INFO "I2O event thread dead\n")); + evt_running = 0; + return 0; + } + + /* + * Copy the data out of the queue so that we don't have to lock + * around the whole function and just around the qlen update + */ + spin_lock_irqsave(&i2o_evt_lock, flags); + memcpy(reply, &events[evt_out], sizeof(struct reply_info)); + MODINC(evt_out, I2O_EVT_Q_LEN); + evt_q_len--; + spin_unlock_irqrestore(&i2o_evt_lock, flags); + + c = reply->iop; + dprintk((KERN_INFO "I2O IRTOS EVENT: iop%d, event %#10x\n", c->unit, msg[4])); + + /* + * We do not attempt to delete/quiesce/etc. the controller if + * some sort of error indidication occurs. We may want to do + * so in the future, but for now we just let the user deal with + * it. One reason for this is that what to do with an error + * or when to send what ærror is not really agreed on, so + * we get errors that may not be fatal but just look like they + * are...so let the user deal with it. + */ + switch(msg[4]) + { + case I2O_EVT_IND_EXEC_RESOURCE_LIMITS: + printk(KERN_ERR "iop%d: Out of resources\n", c->unit); + break; + + case I2O_EVT_IND_EXEC_POWER_FAIL: + printk(KERN_ERR "iop%d: Power failure\n", c->unit); + break; + + case I2O_EVT_IND_EXEC_HW_FAIL: + { + char *fail[] = + { + "Unknown Error", + "Power Lost", + "Code Violation", + "Parity Error", + "Code Execution Exception", + "Watchdog Timer Expired" + }; + + if(msg[5] <= 6) + printk(KERN_ERR "%s: Hardware Failure: %s\n", + c->name, fail[msg[5]]); + else + printk(KERN_ERR "%s: Unknown Hardware Failure\n", c->name); + + break; + } + + /* + * New device created + * - Create a new i2o_device entry + * - Inform all interested drivers about this device's existence + */ + case I2O_EVT_IND_EXEC_NEW_LCT_ENTRY: + { + struct i2o_device *d = (struct i2o_device *) + kmalloc(sizeof(struct i2o_device), GFP_KERNEL); + int i; + + memcpy(&d->lct_data, &msg[5], sizeof(i2o_lct_entry)); + + d->next = NULL; + d->controller = c; + d->flags = 0; + + i2o_report_controller_unit(c, d); + i2o_install_device(c,d); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && + i2o_handlers[i]->new_dev_notify && + (i2o_handlers[i]->class&d->lct_data.class_id)) + i2o_handlers[i]->new_dev_notify(c,d); + } + + break; + } + + /* + * LCT entry for a device has been modified, so update it + * internally. + */ + case I2O_EVT_IND_EXEC_MODIFIED_LCT: + { + struct i2o_device *d; + i2o_lct_entry *new_lct = (i2o_lct_entry *)&msg[5]; + + for(d = c->devices; d; d = d->next) + { + if(d->lct_data.tid == new_lct->tid) + { + memcpy(&d->lct_data, new_lct, sizeof(i2o_lct_entry)); + break; + } + } + break; + } + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk(KERN_WARNING "%s requires user configuration\n", c->name); + break; + + case I2O_EVT_IND_GENERAL_WARNING: + printk(KERN_WARNING "%s: Warning notification received!" + "Check configuration for errors!\n", c->name); + break; + + default: + printk(KERN_WARNING "%s: Unknown event...check config\n", c->name); + break; + } + } + + return 0; +} + +/* + * Dynamic LCT update. This compares the LCT with the currently + * installed devices to check for device deletions..this needed b/c there + * is no DELETED_LCT_ENTRY EventIndicator for the Executive class so + * we can't just have the event handler do this...annoying + * + * This is a hole in the spec that will hopefully be fixed someday. + */ +static int i2o_dyn_lct(void *foo) +{ + struct i2o_controller *c = (struct i2o_controller *)foo; + struct i2o_device *d = NULL; + struct i2o_device *d1 = NULL; + int i = 0; + int found = 0; + int entries; + void *tmp; + char name[16]; + + lock_kernel(); + exit_files(current); + daemonize(); + unlock_kernel(); + + sprintf(name, "iop%d_lctd", c->unit); + strcpy(current->comm, name); + + c->lct_running = 1; + + while(1) + { + down_interruptible(&c->lct_sem); + if(signal_pending(current)) + { + dprintk((KERN_ERR "%s: LCT thread dead\n", c->name)); + c->lct_running = 0; + return 0; + } + + entries = c->dlct->table_size; + entries -= 3; + entries /= 9; + + dprintk((KERN_INFO "I2O: Dynamic LCT Update\n")); + dprintk((KERN_INFO "I2O: Dynamic LCT contains %d entries\n", entries)); + + if(!entries) + { + printk(KERN_INFO "iop%d: Empty LCT???\n", c->unit); + continue; + } + + /* + * Loop through all the devices on the IOP looking for their + * LCT data in the LCT. We assume that TIDs are not repeated. + * as that is the only way to really tell. It's been confirmed + * by the IRTOS vendor(s?) that TIDs are not reused until they + * wrap arround(4096), and I doubt a system will up long enough + * to create/delete that many devices. + */ + for(d = c->devices; d; ) + { + found = 0; + d1 = d->next; + + for(i = 0; i < entries; i++) + { + if(d->lct_data.tid == c->dlct->lct_entry[i].tid) + { + found = 1; + break; + } + } + if(!found) + { + dprintk((KERN_INFO "Deleted device!\n")); + i2o_delete_device(d); + } + d = d1; + } + + /* + * Tell LCT to renotify us next time there is a change + */ + i2o_lct_notify(c); + + /* + * Copy new LCT into public LCT + * + * Possible race if someone is reading LCT while we are copying + * over it. If this happens, we'll fix it then. but I doubt that + * the LCT will get updated often enough or will get read by + * a user often enough to worry. + */ + if(c->lct->table_size < c->dlct->table_size) + { + tmp = c->lct; + c->lct = kmalloc(c->dlct->table_size<<2, GFP_KERNEL); + if(!c->lct) + { + printk(KERN_ERR "%s: No memory for LCT!\n", c->name); + c->lct = tmp; + continue; + } + kfree(tmp); + } + memcpy(c->lct, c->dlct, c->dlct->table_size<<2); + } + + return 0; +} + /* * This is called by the bus specific driver layer when an interrupt * or poll of this card interface is desired. @@ -527,41 +970,46 @@ void i2o_run_queue(struct i2o_controller *c) { struct i2o_message *m; u32 mv; + u32 *msg; + int count = 0; -#ifdef DEBUG_IRQ - printk(KERN_INFO "%s: interrupt\n", c->name); -#endif - /* Sometimes we get here, but a message can't be read. Why? */ + /* + * Old 960 steppings had a bug in the I2O unit that caused + * the queue to appear empty when it wasn't. + */ if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) mv=I2O_REPLY_READ32(c); - while (mv!=0xFFFFFFFF) + while(mv!=0xFFFFFFFF) { struct i2o_handler *i; m=(struct i2o_message *)bus_to_virt(mv); + msg=(u32*)m; + + count++; + /* * Temporary Debugging */ if(m->function==0x15) - printk("UTFR!\n"); - -#ifdef DEBUG_IRQ - i2o_dump_message((u32*)m); -#endif + printk(KERN_ERR "%s: UTFR!\n", c->name); i=i2o_handlers[m->initiator_context&(MAX_I2O_MODULES-1)]; - if(i) + if(i && i->reply) i->reply(i,c,m); else { - printk("i2o: Spurious reply to handler %d\n", + printk(KERN_WARNING "I2O: Spurious reply to handler %d\n", m->initiator_context&(MAX_I2O_MODULES-1)); - i2o_dump_message((u32*)m); } i2o_flush_reply(c,mv); mb(); - mv=I2O_REPLY_READ32(c); - } + + /* That 960 bug again... */ + if((mv=I2O_REPLY_READ32(c))==0xFFFFFFFF) + mv=I2O_REPLY_READ32(c); + + } } @@ -643,7 +1091,7 @@ u32 i2o_wait_message(struct i2o_controller *c, char *why) { if((jiffies-time)>=5*HZ) { - dprintk((KERN_ERR "%s: Timeout waiting for message frame (%s).\n", + dprintk((KERN_ERR "%s: Timeout waiting for message frame to send %s.\n", c->name, why)); return 0xFFFFFFFF; } @@ -676,37 +1124,60 @@ u32 i2o_wait_reply(struct i2o_controller *c, char *why, int timeout) return m; } - /* * Dump the information block associated with a given unit (TID) */ -void i2o_report_controller_unit(struct i2o_controller *c, int unit) +void i2o_report_controller_unit(struct i2o_controller *c, struct i2o_device *d) { char buf[64]; + char str[22]; + int ret; + int unit = d->lct_data.tid; - if(i2o_query_scalar(c, unit, 0xF100, 3, buf, 16)>=0) + printk(KERN_INFO "Target ID %d.\n", unit); + + if((ret=i2o_query_scalar(c, unit, 0xF100, 3, buf, 16))>=0) { buf[16]=0; - printk(KERN_INFO " Vendor: %s", buf); + printk(KERN_INFO " Vendor: %s\n", buf); } - if(i2o_query_scalar(c, unit, 0xF100, 4, buf, 16)>=0) + if((ret=i2o_query_scalar(c, unit, 0xF100, 4, buf, 16))>=0) { + buf[16]=0; - printk(" Device: %s", buf); + printk(KERN_INFO " Device: %s\n", buf); } #if 0 if(i2o_query_scalar(c, unit, 0xF100, 5, buf, 16)>=0) { buf[16]=0; - printk("Description: %s", buf); + printk(KERN_INFO " Description: %s\n", buf); } #endif - if(i2o_query_scalar(c, unit, 0xF100, 6, buf, 8)>=0) + if((ret=i2o_query_scalar(c, unit, 0xF100, 6, buf, 8))>=0) { buf[8]=0; - printk(" Rev: %s\n", buf); + printk(KERN_INFO " Rev: %s\n", buf); } + + printk(KERN_INFO " Class: "); + sprintf(str, "%-21s", i2o_get_class_name(d->lct_data.class_id)); + printk("%s\n", str); + + printk(KERN_INFO " Subclass: 0x%04X\n", d->lct_data.sub_class); + printk(KERN_INFO " Flags: "); + + if(d->lct_data.device_flags&(1<<0)) + printk("C"); // ConfigDialog requested + if(d->lct_data.device_flags&(1<<1)) + printk("U"); // Multi-user capable + if(!(d->lct_data.device_flags&(1<<4))) + printk("P"); // Peer service enabled! + if(!(d->lct_data.device_flags&(1<<5))) + printk("M"); // Mgmt service enabled! + printk("\n"); + } @@ -723,20 +1194,21 @@ void i2o_report_controller_unit(struct i2o_controller *c, int unit) static int i2o_parse_hrt(struct i2o_controller *c) { #ifdef DRIVERDEBUG - u32 *rows=(u32 *)c->hrt; + u32 *rows=(u32*)c->hrt; u8 *p=(u8 *)c->hrt; u8 *d; int count; int length; int i; int state; - - if(p[3]!=0) { + + if(p[3]!=0) + { printk(KERN_ERR "%s: HRT table for controller is too new a version.\n", c->name); - return -1; + return -1; } - + count=p[0]|(p[1]<<8); length = p[2]; @@ -803,7 +1275,6 @@ static int i2o_parse_hrt(struct i2o_controller *c) printk("\n"); rows+=length; } - #endif return 0; } @@ -818,21 +1289,19 @@ static int i2o_parse_lct(struct i2o_controller *c) int i; int max; int tid; - u32 *p; struct i2o_device *d; - char str[22]; i2o_lct *lct = c->lct; if (lct == NULL) { - printk(KERN_ERR "%s: LCT is empty???\n",c->name); + printk(KERN_ERR "%s: LCT is empty???\n", c->name); return -1; } - - max = lct->table_size; + + max = lct->table_size; max -= 3; max /= 9; - - printk(KERN_INFO "%s: LCT has %d entries.\n", c->name,max); + + printk(KERN_INFO "%s: LCT has %d entries.\n", c->name, max); if(lct->iop_flags&(1<<0)) printk(KERN_WARNING "%s: Configuration dialog desired.\n", c->name); @@ -842,42 +1311,21 @@ static int i2o_parse_lct(struct i2o_controller *c) d = (struct i2o_device *)kmalloc(sizeof(struct i2o_device), GFP_KERNEL); if(d==NULL) { - printk(KERN_CRIT "i2o_core: Out of memory for I2O device data.\n"); + printk("i2o_core: Out of memory for I2O device data.\n"); return -ENOMEM; } d->controller = c; d->next = NULL; - d->lct_data = &lct->lct_entry[i]; + memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); d->flags = 0; - tid = d->lct_data->tid; + tid = d->lct_data.tid; - printk(KERN_INFO "Target ID %d.\n", tid); - - i2o_report_controller_unit(c, tid); + i2o_report_controller_unit(c, d); i2o_install_device(c, d); - - printk(KERN_INFO " Class: "); - - sprintf(str, "%-21s", i2o_get_class_name(d->lct_data->class_id)); - printk("%s", str); - - printk(" Subclass: 0x%04X Flags: ", - d->lct_data->sub_class); - - if(d->lct_data->device_flags&(1<<0)) - printk("C"); // ConfigDialog requested - if(d->lct_data->device_flags&(1<<1)) - printk("M"); // Multi-user capable - if(!(d->lct_data->device_flags&(1<<4))) - printk("P"); // Peer service enabled! - if(!(d->lct_data->device_flags&(1<<5))) - printk("m"); // Mgmt service enabled! - printk("\n"); - p+=9; } return 0; } @@ -892,48 +1340,55 @@ int i2o_quiesce_controller(struct i2o_controller *c) u32 msg[4]; int ret; + i2o_status_get(c); + /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ if ((c->status_block->iop_state != ADAPTER_STATE_READY) && - (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) + (c->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)) { return 0; } - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ - msg[3]=0; + msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; + msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; + msg[3] = 0; /* Long timeout needed for quiesce if lots of devices */ - if ((ret = i2o_post_wait(c, msg, sizeof(msg), 120))) - printk(KERN_INFO "%s: Unable to quiesce (status=%#10x).\n", + if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) + printk(KERN_INFO "%s: Unable to quiesce (status=%#10x).\n", c->name, ret); else dprintk((KERN_INFO "%s: Quiesced.\n", c->name)); i2o_status_get(c); // Reread the Status Block - return ret; + return ret; + } -/* +/* * Enable IOP. Allows the IOP to resume external operations. */ int i2o_enable_controller(struct i2o_controller *c) { u32 msg[4]; int ret; + + i2o_status_get(c); + /* Enable only allowed on READY state */ + if(c->status_block->iop_state != ADAPTER_STATE_READY) + return -EINVAL; + msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ - /* How long of a timeout do we need? */ + /* How long of a timeout do we need? */ if ((ret = i2o_post_wait(c, msg, sizeof(msg), 240))) - printk(KERN_ERR "%s: Could not enable (status=%#10x).\n", + printk(KERN_ERR "%s: Could not enable (status=%#10x).\n", c->name, ret); else dprintk((KERN_INFO "%s: Enabled.\n", c->name)); @@ -943,8 +1398,8 @@ int i2o_enable_controller(struct i2o_controller *c) return ret; } -/* - * Clear an IOP to HOLD state, ie. terminate external operations, clear all +/* + * Clear an IOP to HOLD state, ie. terminate external operations, clear all * input queues and prepare for a system restart. IOP's internal operation * continues normally and the outbound queue is alive. * IOP is not expected to rebuild its LCT. @@ -962,11 +1417,10 @@ int i2o_clear_controller(struct i2o_controller *c) msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1]=I2O_CMD_ADAPTER_CLEAR<<24|HOST_TID<<12|ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ msg[3]=0; if ((ret=i2o_post_wait(c, msg, sizeof(msg), 30))) - printk(KERN_INFO "%s: Unable to clear (status=%#10x).\n", + printk(KERN_INFO "%s: Unable to clear (status=%#10x).\n", c->name, ret); else dprintk((KERN_INFO "%s: Cleared.\n",c->name)); @@ -977,21 +1431,21 @@ int i2o_clear_controller(struct i2o_controller *c) for (iop = i2o_controller_chain; iop; iop = iop->next) if (iop != c) - i2o_enable_controller(iop); + i2o_enable_controller(iop); return ret; } -/* - * Reset the IOP into INIT state and wait until IOP gets into RESET state. - * Terminate all external operations, clear IOP's inbound and outbound - * queues, terminate all DDMs, and reload the IOP's operating environment +/* + * Reset the IOP into INIT state and wait until IOP gets into RESET state. + * Terminate all external operations, clear IOP's inbound and outbound + * queues, terminate all DDMs, and reload the IOP's operating environment * and all local DDMs. IOP rebuilds its LCT. */ static int i2o_reset_controller(struct i2o_controller *c) { - struct i2o_controller *iop; + struct i2o_controller *iop; u32 m; u8 *status; u32 *msg; @@ -1002,19 +1456,19 @@ static int i2o_reset_controller(struct i2o_controller *c) for (iop = i2o_controller_chain; iop; iop = iop->next) i2o_quiesce_controller(iop); + /* Get a message */ m=i2o_wait_message(c, "AdapterReset"); if(m==0xFFFFFFFF) return -ETIMEDOUT; msg=(u32 *)(c->mem_offset+m); - - status = kmalloc(4,GFP_KERNEL); - if (status==NULL) { - printk(KERN_ERR "%s: IOP reset failed - no free memory.\n", - c->name); + + status=(void *)kmalloc(4, GFP_KERNEL); + if(status==NULL) { + printk(KERN_ERR "IOP reset failed - no free memory.\n"); return -ENOMEM; } - memset(status,0,4); - + memset(status, 0, 4); + msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; msg[2]=core_context; @@ -1028,11 +1482,11 @@ static int i2o_reset_controller(struct i2o_controller *c) /* Wait for a reply */ time=jiffies; - while (status[0]==0) + while(status[0]==0) { - if((jiffies-time)>=5*HZ) + if((jiffies-time)>=20*HZ) { - printk(KERN_ERR "%s: IOP reset timeout.\n", c->name); + printk(KERN_ERR "IOP reset timeout.\n"); kfree(status); return -ETIMEDOUT; } @@ -1040,26 +1494,27 @@ static int i2o_reset_controller(struct i2o_controller *c) barrier(); } - if (status[0]==0x01) - { + if (status[0]==0x01) + { /* * Once the reset is sent, the IOP goes into the INIT state * which is indeterminate. We need to wait until the IOP * has rebooted before we can let the system talk to * it. We read the inbound Free_List until a message is - * available. If we can't read one in the given amount of + * available. If we can't read one in the given ammount of * time, we assume the IOP could not reboot properly. */ + dprintk((KERN_INFO "Reset succeeded...waiting for reboot\n")); + time = jiffies; m = I2O_POST_READ32(c); while(m == 0XFFFFFFFF) { if((jiffies-time) >= 30*HZ) { - printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", + printk(KERN_ERR "%s: Timeout waiting for IOP reset.\n", c->name); - kfree(status); return -ETIMEDOUT; } schedule(); @@ -1077,22 +1532,24 @@ static int i2o_reset_controller(struct i2o_controller *c) i2o_status_get(c); if (status[0] == 0x02 || c->status_block->iop_state != ADAPTER_STATE_RESET) { - printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); + printk(KERN_WARNING "%s: Reset rejected, trying to clear\n",c->name); i2o_clear_controller(c); - } /* Enable other IOPs */ for (iop = i2o_controller_chain; iop; iop = iop->next) if (iop != c) - i2o_enable_controller(iop); + i2o_enable_controller(iop); kfree(status); return 0; } +/* + * Get the status block for the IOP + */ int i2o_status_get(struct i2o_controller *c) { long time; @@ -1100,23 +1557,25 @@ int i2o_status_get(struct i2o_controller *c) u32 *msg; u8 *status_block; - if (c->status_block == NULL) { + if (c->status_block == NULL) + { c->status_block = (i2o_status_block *) - kmalloc(sizeof(i2o_status_block),GFP_KERNEL); + kmalloc(sizeof(i2o_status_block),GFP_KERNEL); if (c->status_block == NULL) { - printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", c->name); + printk(KERN_CRIT "%s: Get Status Block failed; Out of memory.\n", + c->name); return -ENOMEM; } } status_block = (u8*)c->status_block; memset(c->status_block,0,sizeof(i2o_status_block)); - + m=i2o_wait_message(c, "StatusGet"); if(m==0xFFFFFFFF) return -ETIMEDOUT; - + msg=(u32 *)(c->mem_offset+m); msg[0]=NINE_WORD_MSG_SIZE|SGL_OFFSET_0; @@ -1126,13 +1585,13 @@ int i2o_status_get(struct i2o_controller *c) msg[4]=0; msg[5]=0; msg[6]=virt_to_phys(c->status_block); - msg[7]=0; /* 64bit host FIXME */ + msg[7]=0; /* 64bit host FIXME */ msg[8]=sizeof(i2o_status_block); /* always 88 bytes */ i2o_post_message(c,m); /* Wait for a reply */ - + time=jiffies; while(status_block[87]!=0xFF) { @@ -1144,43 +1603,47 @@ int i2o_status_get(struct i2o_controller *c) schedule(); barrier(); } - + /* Ok the reply has arrived. Fill in the important stuff */ - c->inbound_size = c->status_block->inbound_frame_size *4; + c->inbound_size = (status_block[12]|(status_block[13]<<8))*4; #ifdef DRIVERDEBUG printk(KERN_INFO "%s: State = ", c->name); switch (c->status_block->iop_state) { - case 0x01: - printk("INIT\n"); + case 0x01: + printk("INIT\n"); break; - case 0x02: - printk("RESET\n"); + case 0x02: + printk("RESET\n"); break; - case 0x04: - printk("HOLD\n"); + case 0x04: + printk("HOLD\n"); break; - case 0x05: - printk("READY\n"); + case 0x05: + printk("READY\n"); break; - case 0x08: - printk("OPERATIONAL\n"); + case 0x08: + printk("OPERATIONAL\n"); break; - case 0x10: - printk("FAILED\n"); + case 0x10: + printk("FAILED\n"); break; - case 0x11: - printk("FAULTED\n"); + case 0x11: + printk("FAULTED\n"); break; - default: + default: printk("%x (unknown !!)\n",c->status_block->iop_state); - } -#endif +} +#endif return 0; } - +/* + * Get the Hardware Resource Table for the device. + * The HRT contains information about possible hidden devices + * but is mostly useless to us + */ int i2o_hrt_get(struct i2o_controller *c) { u32 msg[6]; @@ -1199,7 +1662,6 @@ int i2o_hrt_get(struct i2o_controller *c) msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ msg[3]= 0; msg[4]= (0xD0000000 | size); /* Simple transaction */ msg[5]= virt_to_phys(c->hrt); /* Dump it here */ @@ -1222,45 +1684,43 @@ int i2o_hrt_get(struct i2o_controller *c) return 0; } +/* + * Send the I2O System Table to the specified IOP + * + * The system table contains information about all the IOPs in the + * system. It is build and then sent to each IOP so that IOPs can + * establish connections between each other. + * + */ static int i2o_systab_send(struct i2o_controller *iop) { - u32 msg[12]; - u32 privmem[2]; - u32 privio[2]; - int ret; - - /* See i2o_status_block */ -#if 0 - iop->status->current_mem_base; - iop->status->current_mem_size; - iop->status->current_io_base; - iop->status->current_io_size; -#endif + u32 msg[12]; + u32 privmem[2]; + u32 privio[2]; + int ret; -/* FIXME */ - privmem[0]=iop->priv_mem; /* Private memory space base address */ - privmem[1]=iop->priv_mem_size; - privio[0]=iop->priv_io; /* Private I/O address */ - privio[1]=iop->priv_io_size; + privmem[0] = iop->status_block->current_mem_base; + privmem[1] = iop->status_block->current_mem_size; + privio[0] = iop->status_block->current_io_base; + privio[1] = iop->status_block->current_io_size; msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; - msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; - /* msg[2] filled in i2o_post_wait */ + msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; msg[3] = 0; - msg[4] = (0<<16) | ((iop->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ - msg[5] = 0; /* Segment 0 */ + msg[4] = (0<<16) | ((iop->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ + msg[5] = 0; /* Segment 0 */ - /* - * Provide three SGL-elements: - * System table (SysTab), Private memory space declaration and - * Private i/o space declaration - */ - msg[6] = 0x54000000 | sys_tbl_len; - msg[7] = virt_to_phys(sys_tbl); - msg[8] = 0x54000000 | 0; - msg[9] = virt_to_phys(privmem); - msg[10] = 0xD4000000 | 0; - msg[11] = virt_to_phys(privio); + /* + * Provide three SGL-elements: + * System table (SysTab), Private memory space declaration and + * Private i/o space declaration + */ + msg[6] = 0x54000000 | sys_tbl_len; + msg[7] = virt_to_phys(sys_tbl); + msg[8] = 0x54000000 | 0; + msg[9] = virt_to_phys(privmem); + msg[10] = 0xD4000000 | 0; + msg[11] = virt_to_phys(privio); if ((ret=i2o_post_wait(iop, msg, sizeof(msg), 120))) printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n", @@ -1281,10 +1741,11 @@ static void __init i2o_sys_init() printk(KERN_INFO "Activating I2O controllers\n"); printk(KERN_INFO "This may take a few minutes if there are many devices\n"); - + /* In INIT state, Activate IOPs */ - for (iop = i2o_controller_chain; iop; iop = niop) { + dprintk((KERN_INFO "Calling i2o_activate_controller for %s\n", + iop->name)); niop = iop->next; i2o_activate_controller(iop); } @@ -1299,6 +1760,7 @@ rebuild_sys_tab: * If build_sys_table fails, we kill everything and bail * as we can't init the IOPs w/o a system table */ + dprintk((KERN_INFO "calling i2o_build_sys_table\n")); if (i2o_build_sys_table() < 0) { i2o_sys_shutdown(); return; @@ -1307,11 +1769,30 @@ rebuild_sys_tab: /* If IOP don't get online, we need to rebuild the System table */ for (iop = i2o_controller_chain; iop; iop = niop) { niop = iop->next; + dprintk((KERN_INFO "Calling i2o_online_controller for %s\n", iop->name)); if (i2o_online_controller(iop) < 0) goto rebuild_sys_tab; } /* Active IOPs now in OPERATIONAL state */ + + /* + * Register for status updates from all IOPs + */ + for(iop = i2o_controller_chain; iop; iop=iop->next) { + + /* Create a kernel thread to deal with dynamic LCT updates */ + iop->lct_pid = kernel_thread(i2o_dyn_lct, iop, CLONE_SIGHAND); + + /* Update change ind on DLCT */ + iop->dlct->change_ind = iop->lct->change_ind; + + /* Start dynamic LCT updates */ + i2o_lct_notify(iop); + + /* Register for all events from IRTOS */ + i2o_event_register(iop, core_context, 0, 0, 0xFFFFFFFF); + } } /* @@ -1339,10 +1820,10 @@ int i2o_activate_controller(struct i2o_controller *iop) /* In READY state, Get status */ if (i2o_status_get(iop) < 0) { - printk("Unable to obtain status of IOP, attempting a reset.\n"); + printk(KERN_INFO "Unable to obtain status of IOP, attempting a reset.\n"); i2o_reset_controller(iop); if (i2o_status_get(iop) < 0) { - printk("IOP not responding.\n"); + printk(KERN_ERR "%s: IOP not responding.\n", iop->name); i2o_delete_controller(iop); return -1; } @@ -1354,13 +1835,18 @@ int i2o_activate_controller(struct i2o_controller *iop) return -1; } -// if (iop->status_block->iop_state == ADAPTER_STATE_HOLD || if (iop->status_block->iop_state == ADAPTER_STATE_READY || iop->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || + iop->status_block->iop_state == ADAPTER_STATE_HOLD || iop->status_block->iop_state == ADAPTER_STATE_FAILED) { + u32 m[MSG_FRAME_SIZE]; dprintk((KERN_INFO "%s: already running...trying to reset\n", iop->name)); + + i2o_init_outbound_q(iop); + I2O_REPLY_WRITE32(iop,virt_to_phys(m)); + i2o_reset_controller(iop); if (i2o_status_get(iop) < 0 || @@ -1377,6 +1863,9 @@ int i2o_activate_controller(struct i2o_controller *iop) return -1; } + if (i2o_post_outbound_messages(iop)) + return -1; + /* In HOLD state */ if (i2o_hrt_get(iop) < 0) { @@ -1387,6 +1876,7 @@ int i2o_activate_controller(struct i2o_controller *iop) return 0; } + /* * Clear and (re)initialize IOP's outbound queue */ @@ -1396,8 +1886,8 @@ int i2o_init_outbound_q(struct i2o_controller *c) u32 m; u32 *msg; u32 time; - int i; + dprintk((KERN_INFO "%s: Initializing Outbound Queue\n", c->name)); m=i2o_wait_message(c, "OutboundInit"); if(m==0xFFFFFFFF) return -ETIMEDOUT; @@ -1410,66 +1900,72 @@ int i2o_init_outbound_q(struct i2o_controller *c) return -ENOMEM; } memset(status, 0, 4); - - msg[0]= EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6; + + + msg[0]= EIGHT_WORD_MSG_SIZE| TRL_OFFSET_6; msg[1]= I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID; msg[2]= core_context; - msg[3]= 0x0106; /* Transaction context */ - msg[4]= 4096; /* Host page frame size */ + msg[3]= 0x0106; /* Transaction context */ + msg[4]= 4096; /* Host page frame size */ /* Frame size is in words. Pick 128, its what everyone elses uses and - other sizes break some adapters. */ - msg[5]= (MSG_FRAME_SIZE>>2)<<16|0x80; /* Outbound msg frame size and Initcode */ - msg[6]= 0xD0000004; /* Simple SG LE, EOB */ + other sizes break some adapters. */ + msg[5]= MSG_FRAME_SIZE<<16|0x80; /* Outbound msg frame size and Initcode */ + msg[6]= 0xD0000004; /* Simple SG LE, EOB */ msg[7]= virt_to_bus(status); i2o_post_message(c,m); - barrier(); + barrier(); time=jiffies; while(status[0]<0x02) { - if((jiffies-time)>=5*HZ) + if((jiffies-time)>=30*HZ) { if(status[0]==0x00) printk(KERN_ERR "%s: Ignored queue initialize request.\n", c->name); - else + else printk(KERN_ERR "%s: Outbound queue initialize timeout.\n", c->name); kfree(status); return -ETIMEDOUT; - } + } schedule(); barrier(); - } + } if(status[0] != I2O_CMD_OUTBOUND_INIT_COMPLETE) { - printk(KERN_ERR "%s: Outbound queue initialize rejected (%d).\n", - c->name, status[0]); + printk(KERN_ERR "%s: IOP outbound initialise failed.\n", c->name); kfree(status); - return -EINVAL; + return -ETIMEDOUT; } + + return 0; +} + +int i2o_post_outbound_messages(struct i2o_controller *c) +{ + int i; + u32 m; /* Alloc space for IOP's outbound queue message frames */ c->page_frame = kmalloc(MSG_POOL_SIZE, GFP_KERNEL); if(c->page_frame==NULL) { printk(KERN_CRIT "%s: Outbound Q initialize failed; out of memory.\n", c->name); - kfree(status); return -ENOMEM; - } + } m=virt_to_phys(c->page_frame); - + /* Post frames */ for(i=0; i< NMBR_MSG_FRAMES; i++) { - I2O_REPLY_WRITE32(c,m); - mb(); + I2O_REPLY_WRITE32(c,m); + mb(); m += MSG_FRAME_SIZE; } - kfree(status); return 0; } @@ -1520,11 +2016,31 @@ int i2o_lct_get(struct i2o_controller *c) return 0; } +/* + * Like above, but used for async notification. The main + * difference is that we keep track of the CurrentChangeIndiicator + * so that we only get updates when it actually changes. + * + */ +int i2o_lct_notify(struct i2o_controller *c) +{ + u32 msg[8]; + + msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; + msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; + msg[2] = core_context; + msg[3] = 0xDEADBEEF; + msg[4] = 0xFFFFFFFF; /* All devices */ + msg[5] = c->dlct->change_ind+1; /* Next change */ + msg[6] = 0xD0000000|8192; + msg[7] = virt_to_bus(c->dlct); + return i2o_post_this(c, msg, sizeof(msg)); +} + /* * Bring a controller online into OPERATIONAL state. */ - int i2o_online_controller(struct i2o_controller *iop) { if (i2o_systab_send(iop) < 0) { @@ -1534,6 +2050,7 @@ int i2o_online_controller(struct i2o_controller *iop) /* In READY state */ + dprintk((KERN_INFO "Attempting to enable iop%d\n", iop->unit)); if (i2o_enable_controller(iop) < 0) { i2o_delete_controller(iop); return -1; @@ -1541,6 +2058,7 @@ int i2o_online_controller(struct i2o_controller *iop) /* In OPERATIONAL state */ + dprintk((KERN_INFO "Attempting to get/parse lct iop%d\n", iop->unit)); if (i2o_lct_get(iop) < 0){ i2o_delete_controller(iop); return -1; @@ -1549,9 +2067,18 @@ int i2o_online_controller(struct i2o_controller *iop) return 0; } +/* + * Build system table + * + * The system table contains information about all the IOPs in the + * system (duh) and is used by the Executives on the IOPs to establish + * peer2peer connections. We're not supporting peer2peer at the moment, + * but this will be needed down the road for things like lan2lan forwarding. + */ static int i2o_build_sys_table(void) { struct i2o_controller *iop = NULL; + struct i2o_controller *niop = NULL; int count = 0; sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs @@ -1563,7 +2090,7 @@ static int i2o_build_sys_table(void) sys_tbl = kmalloc(sys_tbl_len, GFP_KERNEL); if(!sys_tbl) { - printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); + printk(KERN_CRIT "SysTab Set failed. Out of memory.\n"); return -ENOMEM; } memset((void*)sys_tbl, 0, sys_tbl_len); @@ -1572,12 +2099,23 @@ static int i2o_build_sys_table(void) sys_tbl->version = I2OVERSION; /* TODO: Version 2.0 */ sys_tbl->change_ind = sys_tbl_ind++; - for(iop = i2o_controller_chain; iop; iop = iop->next) + for(iop = i2o_controller_chain; iop; iop = niop) { - // Get updated Status Block so we have the latest information - if (i2o_status_get(iop)) { + niop = iop->next; + + /* + * Get updated IOP state so we have the latest information + * + * We should delete the controller at this point if it + * doesn't respond since if it's not on the system table + * it is techninically not part of the I2O subsyßtem... + */ + if(i2o_status_get(iop)) { + printk(KERN_ERR "%s: Deleting b/c could not get status while" + "attempting to build system table", iop->name); + i2o_delete_controller(iop); sys_tbl->num_entries--; - continue; // try next one + continue; // try the next one } sys_tbl->iops[count].org_id = iop->status_block->org_id; @@ -1603,10 +2141,10 @@ static int i2o_build_sys_table(void) #ifdef DRIVERDEBUG { - u32 *table = (u32*)sys_tbl; + u32 *table; + table = (u32*)sys_tbl; for(count = 0; count < (sys_tbl_len >>2); count++) - printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", - count, table[count]); + printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", count, table[count]); } #endif @@ -1640,19 +2178,20 @@ int i2o_post_this(struct i2o_controller *c, u32 *data, int len) if(m==0xFFFFFFFF) { - printk(KERN_ERR "%s: Timeout waiting for message frame!\n", - c->name); + printk(KERN_ERR "i2o/iop%d: Timeout waiting for message frame!\n", + c->unit); return -ETIMEDOUT; } - msg = (u32 *)(c->mem_offset + m); - memcpy_toio(msg, data, len); + memcpy_toio(msg, data, len); i2o_post_message(c,m); return 0; } /* - * Post a message and wait for a response flag to be set. + * This core API allows an OSM to post a message and then be told whether + * or not the system received a successful reply. It is useful when + * the OSM does not want to know the exact 3 */ int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) { @@ -1673,19 +2212,32 @@ int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) spin_lock_irqsave(&post_wait_lock, flags); wait_data->next = post_wait_queue; post_wait_queue = wait_data; - wait_data->id = (++post_wait_id) & 0x7fff; + wait_data->id = (++post_wait_id) & 0x7fff; spin_unlock_irqrestore(&post_wait_lock, flags); wait_data->wq = &wq_i2o_post; wait_data->status = -EAGAIN; - msg[2]=0x80000000|(u32)core_context|((u32)wait_data->id<<16); - + msg[2] = 0x80000000|(u32)core_context|((u32)wait_data->id<<16); + if ((status = i2o_post_this(c, msg, len))==0) { interruptible_sleep_on_timeout(&wq_i2o_post, HZ * timeout); status = wait_data->status; - } + } + +#ifdef DRIVERDEBUG + if(status == -EAGAIN) + printk(KERN_INFO "POST WAIT TIMEOUT\n"); +#endif + /* + * Remove the entry from the queue. + * Since i2o_post_wait() may have been called again by + * a different thread while we were waiting for this + * instance to complete, we're not guaranteed that + * this entry is at the head of the queue anymore, so + * we need to search for it, find it, and delete it. + */ p2 = NULL; spin_lock_irqsave(&post_wait_lock, flags); for(p1 = post_wait_queue; p1; p2 = p1, p1 = p1->next) { @@ -1694,6 +2246,7 @@ int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) p2->next = p1->next; else post_wait_queue = p1->next; + break; } } @@ -1710,7 +2263,7 @@ int i2o_post_wait(struct i2o_controller *c, u32 *msg, int len, int timeout) */ static void i2o_post_wait_complete(u32 context, int status) { - struct i2o_post_wait_data *p1; + struct i2o_post_wait_data *p1 = NULL; /* * We need to search through the post_wait @@ -1728,60 +2281,21 @@ static void i2o_post_wait_complete(u32 context, int status) for(p1 = post_wait_queue; p1; p1 = p1->next) { if(p1->id == ((context >> 16) & 0x7fff)) { p1->status = status; - spin_unlock(&post_wait_lock); wake_up_interruptible(p1->wq); + spin_unlock(&post_wait_lock); return; } } spin_unlock(&post_wait_lock); - printk(KERN_DEBUG "i2o: i2o_post_wait reply after timeout!"); -} - -/* - * Send UTIL_EVENT messages - */ - -int i2o_event_register(struct i2o_controller *c, int tid, int context, - u32 evt_mask) -{ - u32 msg[5]; - - msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = I2O_CMD_UTIL_EVT_REGISTER << 24 | HOST_TID << 12 | tid; - msg[2] = context; - msg[3] = 0; - msg[4] = evt_mask; - - if (i2o_post_this(c, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; -} - -int i2o_event_ack(struct i2o_controller *c, int tid, int context, - u32 evt_indicator, void *evt_data, int evt_data_len) -{ - u32 msg[c->inbound_size]; - - msg[0] = I2O_MESSAGE_SIZE(5 + evt_data_len / 4) | SGL_OFFSET_5; - msg[1] = I2O_CMD_UTIL_EVT_ACK << 24 | HOST_TID << 12 | tid; - msg[2] = context; - msg[3] = 0; - msg[4] = evt_indicator; - memcpy(msg+5, evt_data, evt_data_len); - - if (i2o_post_this(c, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; - - return 0; + printk(KERN_DEBUG "i2o_post_wait reply after timeout!"); } /* * Issue UTIL_CLAIM or UTIL_RELEASE messages */ - -static int i2o_issue_claim(struct i2o_controller *c, int tid, int context, int onoff, u32 type) +static int i2o_issue_claim(struct i2o_controller *c, int tid, int context, + int onoff, u32 type) { u32 msg[5]; @@ -1791,53 +2305,73 @@ static int i2o_issue_claim(struct i2o_controller *c, int tid, int context, int o else msg[1] = I2O_CMD_UTIL_RELEASE << 24 | HOST_TID << 12 | tid; - /* msg[2] filled in i2o_post_wait */ msg[3] = 0; msg[4] = type; - - return i2o_post_wait(c, msg, sizeof(msg), 2); + + return i2o_post_wait(c, msg, sizeof(msg), 30); } /* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET * * This function can be used for all UtilParamsGet/Set operations. - * The OperationBlock is given in opblk-buffer, - * and results are returned in resblk-buffer. - * Note that the minimum sized resblk is 8 bytes and contains + * The OperationList is given in oplist-buffer, + * and results are returned in reslist-buffer. + * Note that the minimum sized reslist is 8 bytes and contains * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. */ int i2o_issue_params(int cmd, struct i2o_controller *iop, int tid, - void *opblk, int oplen, void *resblk, int reslen) + void *oplist, int oplen, void *reslist, int reslen) { u32 msg[9]; - u32 *res = (u32 *)resblk; + u8 *res = (u8 *)reslist; + u32 *res32 = (u32*)reslist; + u32 *restmp = (u32*)reslist; + int len = 0; + int i = 0; int wait_status; msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; msg[1] = cmd << 24 | HOST_TID << 12 | tid; - /* msg[2] filled in i2o_post_wait */ msg[3] = 0; msg[4] = 0; - msg[5] = 0x54000000 | oplen; /* OperationBlock */ - msg[6] = virt_to_bus(opblk); - msg[7] = 0xD0000000 | reslen; /* ResultBlock */ - msg[8] = virt_to_bus(resblk); + msg[5] = 0x54000000 | oplen; /* OperationList */ + msg[6] = virt_to_bus(oplist); + msg[7] = 0xD0000000 | reslen; /* ResultList */ + msg[8] = virt_to_bus(reslist); - if ((wait_status = i2o_post_wait(iop, msg, sizeof(msg), 20))) - return wait_status; /* -DetailedStatus */ + if((wait_status = i2o_post_wait(iop, msg, sizeof(msg), 10))) + return wait_status; /* -DetailedStatus */ - if (res[1]&0x00FF0000) /* BlockStatus != SUCCESS */ + /* + * Calculate number of bytes of Result LIST + * We need to loop through each Result BLOCK and grab the length + */ + restmp = res32 + 1; + len = 1; + for(i = 0; i < (res32[0]&0X0000FFFF); i++) { - printk(KERN_WARNING "%s: %s - Error:\n ErrorInfoSize = 0x%02x, " - "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", - iop->name, - (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" - : "PARAMS_GET", - res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF); - return -((res[1] >> 16) & 0xFF); /* -BlockStatus */ + if(restmp[0]&0x00FF0000) /* BlockStatus != SUCCESS */ + { + printk(KERN_WARNING "%s - Error:\n ErrorInfoSize = 0x%02x, " + "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", + (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" + : "PARAMS_GET", + res32[1]>>24, (res32[1]>>16)&0xFF, res32[1]&0xFFFF); + + /* + * If this is the only request,than we return an error + */ + if((res32[0]&0x0000FFFF) == 1) + return -((res[1] >> 16) & 0xFF); /* -BlockStatus */ + } + + len += restmp[0] & 0x0000FFFF; /* Length of res BLOCK */ + restmp += restmp[0] & 0x0000FFFF; /* Skip to next BLOCK */ } - return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */ + return (len << 2); + + // return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */ } /* @@ -1852,10 +2386,10 @@ int i2o_query_scalar(struct i2o_controller *iop, int tid, if (field == -1) /* whole group */ opblk[4] = -1; - + size = i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, iop, tid, opblk, sizeof(opblk), resblk, sizeof(resblk)); - + if (size < 0) return size; @@ -1955,7 +2489,6 @@ int i2o_query_table(int oper, struct i2o_controller *iop, int tid, int group, /* * Clear table group, i.e. delete all rows. */ - int i2o_clear_table(struct i2o_controller *iop, int tid, int group) { u16 opblk[] = { 1, 0, I2O_PARAMS_TABLE_CLEAR, group }; @@ -1973,7 +2506,6 @@ int i2o_clear_table(struct i2o_controller *iop, int tid, int group) * else just specific fields are given, rest use defaults * buf contains fieldindexes, rowcount, keyvalues */ - int i2o_row_add_table(struct i2o_controller *iop, int tid, int group, int fieldcount, void *buf, int buflen) { @@ -2005,7 +2537,6 @@ int i2o_row_add_table(struct i2o_controller *iop, int tid, /* * Delete rows from a table group. */ - int i2o_row_delete_table(struct i2o_controller *iop, int tid, int group, int keycount, void *keys, int keyslen) { @@ -2034,6 +2565,9 @@ int i2o_row_delete_table(struct i2o_controller *iop, int tid, return size; } +/* + * Used for error reporting/debugging purposes + */ void i2o_report_common_status(u8 req_status) { /* the following reply status strings are common to all classes */ @@ -2061,6 +2595,9 @@ void i2o_report_common_status(u8 req_status) return; } +/* + * Used for error reporting/debugging purposes + */ static void i2o_report_common_dsc(u16 detailed_status) { /* The following detailed statuscodes are valid @@ -2108,6 +2645,9 @@ static void i2o_report_common_dsc(u16 detailed_status) return; } +/* + * Used for error reporting/debugging purposes + */ static void i2o_report_lan_dsc(u16 detailed_status) { static char *LAN_DSC[] = { // Lan detailed status code strings @@ -2140,194 +2680,217 @@ static void i2o_report_lan_dsc(u16 detailed_status) return; } +/* + * Used for error reporting/debugging purposes + */ static void i2o_report_util_cmd(u8 cmd) { switch (cmd) { case I2O_CMD_UTIL_NOP: - printk("UTIL_NOP, "); + printk(KERN_INFO "UTIL_NOP, "); break; case I2O_CMD_UTIL_ABORT: - printk("UTIL_ABORT, "); + printk(KERN_INFO "UTIL_ABORT, "); break; case I2O_CMD_UTIL_CLAIM: - printk("UTIL_CLAIM, "); + printk(KERN_INFO "UTIL_CLAIM, "); break; case I2O_CMD_UTIL_RELEASE: - printk("UTIL_CLAIM_RELEASE, "); + printk(KERN_INFO "UTIL_CLAIM_RELEASE, "); break; case I2O_CMD_UTIL_CONFIG_DIALOG: - printk("UTIL_CONFIG_DIALOG, "); + printk(KERN_INFO "UTIL_CONFIG_DIALOG, "); break; case I2O_CMD_UTIL_DEVICE_RESERVE: - printk("UTIL_DEVICE_RESERVE, "); + printk(KERN_INFO "UTIL_DEVICE_RESERVE, "); break; case I2O_CMD_UTIL_DEVICE_RELEASE: - printk("UTIL_DEVICE_RELEASE, "); + printk(KERN_INFO "UTIL_DEVICE_RELEASE, "); break; case I2O_CMD_UTIL_EVT_ACK: - printk("UTIL_EVENT_ACKNOWLEDGE, "); + printk(KERN_INFO "UTIL_EVENT_ACKNOWLEDGE, "); break; case I2O_CMD_UTIL_EVT_REGISTER: - printk("UTIL_EVENT_REGISTER, "); + printk(KERN_INFO "UTIL_EVENT_REGISTER, "); break; case I2O_CMD_UTIL_LOCK: - printk("UTIL_LOCK, "); + printk(KERN_INFO "UTIL_LOCK, "); break; case I2O_CMD_UTIL_LOCK_RELEASE: - printk("UTIL_LOCK_RELEASE, "); + printk(KERN_INFO "UTIL_LOCK_RELEASE, "); break; case I2O_CMD_UTIL_PARAMS_GET: - printk("UTIL_PARAMS_GET, "); + printk(KERN_INFO "UTIL_PARAMS_GET, "); break; case I2O_CMD_UTIL_PARAMS_SET: - printk("UTIL_PARAMS_SET, "); + printk(KERN_INFO "UTIL_PARAMS_SET, "); break; case I2O_CMD_UTIL_REPLY_FAULT_NOTIFY: - printk("UTIL_REPLY_FAULT_NOTIFY, "); + printk(KERN_INFO "UTIL_REPLY_FAULT_NOTIFY, "); break; default: - printk("%0#2x, ",cmd); + printk(KERN_INFO "%0#2x, ",cmd); } return; } - +/* + * Used for error reporting/debugging purposes + */ static void i2o_report_exec_cmd(u8 cmd) { switch (cmd) { case I2O_CMD_ADAPTER_ASSIGN: - printk("EXEC_ADAPTER_ASSIGN, "); + printk(KERN_INFO "EXEC_ADAPTER_ASSIGN, "); break; case I2O_CMD_ADAPTER_READ: - printk("EXEC_ADAPTER_READ, "); + printk(KERN_INFO "EXEC_ADAPTER_READ, "); break; case I2O_CMD_ADAPTER_RELEASE: - printk("EXEC_ADAPTER_RELEASE, "); + printk(KERN_INFO "EXEC_ADAPTER_RELEASE, "); break; case I2O_CMD_BIOS_INFO_SET: - printk("EXEC_BIOS_INFO_SET, "); + printk(KERN_INFO "EXEC_BIOS_INFO_SET, "); break; case I2O_CMD_BOOT_DEVICE_SET: - printk("EXEC_BOOT_DEVICE_SET, "); + printk(KERN_INFO "EXEC_BOOT_DEVICE_SET, "); break; case I2O_CMD_CONFIG_VALIDATE: - printk("EXEC_CONFIG_VALIDATE, "); + printk(KERN_INFO "EXEC_CONFIG_VALIDATE, "); break; case I2O_CMD_CONN_SETUP: - printk("EXEC_CONN_SETUP, "); + printk(KERN_INFO "EXEC_CONN_SETUP, "); break; case I2O_CMD_DDM_DESTROY: - printk("EXEC_DDM_DESTROY, "); + printk(KERN_INFO "EXEC_DDM_DESTROY, "); break; case I2O_CMD_DDM_ENABLE: - printk("EXEC_DDM_ENABLE, "); + printk(KERN_INFO "EXEC_DDM_ENABLE, "); break; case I2O_CMD_DDM_QUIESCE: - printk("EXEC_DDM_QUIESCE, "); + printk(KERN_INFO "EXEC_DDM_QUIESCE, "); break; case I2O_CMD_DDM_RESET: - printk("EXEC_DDM_RESET, "); + printk(KERN_INFO "EXEC_DDM_RESET, "); break; case I2O_CMD_DDM_SUSPEND: - printk("EXEC_DDM_SUSPEND, "); + printk(KERN_INFO "EXEC_DDM_SUSPEND, "); break; case I2O_CMD_DEVICE_ASSIGN: - printk("EXEC_DEVICE_ASSIGN, "); + printk(KERN_INFO "EXEC_DEVICE_ASSIGN, "); break; case I2O_CMD_DEVICE_RELEASE: - printk("EXEC_DEVICE_RELEASE, "); + printk(KERN_INFO "EXEC_DEVICE_RELEASE, "); break; case I2O_CMD_HRT_GET: - printk("EXEC_HRT_GET, "); + printk(KERN_INFO "EXEC_HRT_GET, "); break; case I2O_CMD_ADAPTER_CLEAR: - printk("EXEC_IOP_CLEAR, "); + printk(KERN_INFO "EXEC_IOP_CLEAR, "); break; case I2O_CMD_ADAPTER_CONNECT: - printk("EXEC_IOP_CONNECT, "); + printk(KERN_INFO "EXEC_IOP_CONNECT, "); break; case I2O_CMD_ADAPTER_RESET: - printk("EXEC_IOP_RESET, "); + printk(KERN_INFO "EXEC_IOP_RESET, "); break; case I2O_CMD_LCT_NOTIFY: - printk("EXEC_LCT_NOTIFY, "); + printk(KERN_INFO "EXEC_LCT_NOTIFY, "); break; case I2O_CMD_OUTBOUND_INIT: - printk("EXEC_OUTBOUND_INIT, "); + printk(KERN_INFO "EXEC_OUTBOUND_INIT, "); break; case I2O_CMD_PATH_ENABLE: - printk("EXEC_PATH_ENABLE, "); + printk(KERN_INFO "EXEC_PATH_ENABLE, "); break; case I2O_CMD_PATH_QUIESCE: - printk("EXEC_PATH_QUIESCE, "); + printk(KERN_INFO "EXEC_PATH_QUIESCE, "); break; case I2O_CMD_PATH_RESET: - printk("EXEC_PATH_RESET, "); + printk(KERN_INFO "EXEC_PATH_RESET, "); break; case I2O_CMD_STATIC_MF_CREATE: - printk("EXEC_STATIC_MF_CREATE, "); + printk(KERN_INFO "EXEC_STATIC_MF_CREATE, "); break; case I2O_CMD_STATIC_MF_RELEASE: - printk("EXEC_STATIC_MF_RELEASE, "); + printk(KERN_INFO "EXEC_STATIC_MF_RELEASE, "); break; case I2O_CMD_STATUS_GET: - printk("EXEC_STATUS_GET, "); + printk(KERN_INFO "EXEC_STATUS_GET, "); break; case I2O_CMD_SW_DOWNLOAD: - printk("EXEC_SW_DOWNLOAD, "); + printk(KERN_INFO "EXEC_SW_DOWNLOAD, "); break; case I2O_CMD_SW_UPLOAD: - printk("EXEC_SW_UPLOAD, "); + printk(KERN_INFO "EXEC_SW_UPLOAD, "); break; case I2O_CMD_SW_REMOVE: - printk("EXEC_SW_REMOVE, "); + printk(KERN_INFO "EXEC_SW_REMOVE, "); break; case I2O_CMD_SYS_ENABLE: - printk("EXEC_SYS_ENABLE, "); + printk(KERN_INFO "EXEC_SYS_ENABLE, "); break; case I2O_CMD_SYS_MODIFY: - printk("EXEC_SYS_MODIFY, "); + printk(KERN_INFO "EXEC_SYS_MODIFY, "); break; case I2O_CMD_SYS_QUIESCE: - printk("EXEC_SYS_QUIESCE, "); + printk(KERN_INFO "EXEC_SYS_QUIESCE, "); break; case I2O_CMD_SYS_TAB_SET: - printk("EXEC_SYS_TAB_SET, "); + printk(KERN_INFO "EXEC_SYS_TAB_SET, "); break; default: - printk("%02x, ",cmd); + printk(KERN_INFO "%02x, ",cmd); } return; } +/* + * Used for error reporting/debugging purposes + */ static void i2o_report_lan_cmd(u8 cmd) { switch (cmd) { case LAN_PACKET_SEND: - printk("LAN_PACKET_SEND, "); + printk(KERN_INFO "LAN_PACKET_SEND, "); break; case LAN_SDU_SEND: - printk("LAN_SDU_SEND, "); + printk(KERN_INFO "LAN_SDU_SEND, "); break; case LAN_RECEIVE_POST: - printk("LAN_RECEIVE_POST, "); + printk(KERN_INFO "LAN_RECEIVE_POST, "); break; case LAN_RESET: - printk("LAN_RESET, "); + printk(KERN_INFO "LAN_RESET, "); break; case LAN_SUSPEND: - printk("LAN_SUSPEND, "); + printk(KERN_INFO "LAN_SUSPEND, "); break; default: - printk("%02x, ",cmd); + printk(KERN_INFO "%02x, ",cmd); } return; } -/* TODO: Add support for other classes */ +/* + * Used for error reporting/debugging purposes + * + * This will have to be rewritten someday. The code currently + * assumes that a certain range of commands is reserved for + * given class. This is not completely true. Exec and Util + * message have their numbers reserved, but the rest are + * available _for each device class to use as it wishes_ + * + * For example 0x37 is BsaCacheFlush for a block class device and + * LanSuspend for a LAN class device. + * + * The ideal way to do this would be to look at the TID and then + * find the LCT entry to determine what the class of the device is. + * + */ void i2o_report_status(const char *severity, const char *module, u32 *msg) { u8 cmd = (msg[1]>>24)&0xFF; @@ -2357,7 +2920,7 @@ void i2o_report_status(const char *severity, const char *module, u32 *msg) return; } - printk("%02x, %02x / %04x.\n", cmd, req_status, detailed_status); + printk(KERN_INFO "%02x, %02x / %04x.\n", cmd, req_status, detailed_status); return; } @@ -2366,7 +2929,6 @@ void i2o_dump_message(u32 *msg) { #ifdef DRIVERDEBUG int i; - printk(KERN_INFO "Dumping I2O message size %d @ %p\n", msg[0]>>16&0xffff, msg); for(i = 0; i < ((msg[0]>>16)&0xffff); i++) @@ -2374,53 +2936,101 @@ void i2o_dump_message(u32 *msg) #endif } -#ifdef MODULE +/* + * I2O reboot/shutdown notification. + * + * - Call each OSM's reboot notifier (if one exists) + * - Quiesce each IOP in the system + * + * Each IOP has to be quiesced before we can ensure that the system + * can be properly shutdown as a transaction that has already been + * acknowledged still needs to be placed in permanent store on the IOP. + * The SysQuiesce causes the IOP to force all HDMs to complete their + * transactions before returning, so only at that point is it safe + * + */ +static int i2o_reboot_event(struct notifier_block *n, unsigned long code, void +*p) +{ + int i = 0; + struct i2o_controller *c = NULL; -EXPORT_SYMBOL(i2o_install_handler); -EXPORT_SYMBOL(i2o_remove_handler); + if(code != SYS_RESTART && code != SYS_HALT && code != SYS_POWER_OFF) + return NOTIFY_DONE; -EXPORT_SYMBOL(i2o_install_controller); -EXPORT_SYMBOL(i2o_delete_controller); -EXPORT_SYMBOL(i2o_unlock_controller); -EXPORT_SYMBOL(i2o_find_controller); + printk(KERN_INFO "Shutting down I2O system.\n"); + printk(KERN_INFO + " This could take a few minutes if there are many devices attached\n"); + + for(i = 0; i < MAX_I2O_MODULES; i++) + { + if(i2o_handlers[i] && i2o_handlers[i]->reboot_notify) + i2o_handlers[i]->reboot_notify(); + } + + for(c = i2o_controller_chain; c; c = c->next) + { + if(i2o_quiesce_controller(c)) + { + printk(KERN_WARNING "i2o: Could not quiesce %s." " + Verify setup on next system power up.\n", c->name); + } + } + + return NOTIFY_DONE; +} + + +#ifdef MODULE + +EXPORT_SYMBOL(i2o_controller_chain); EXPORT_SYMBOL(i2o_num_controllers); +EXPORT_SYMBOL(i2o_find_controller); +EXPORT_SYMBOL(i2o_unlock_controller); +EXPORT_SYMBOL(i2o_status_get); -EXPORT_SYMBOL(i2o_event_register); -EXPORT_SYMBOL(i2o_event_ack); +EXPORT_SYMBOL(i2o_install_handler); +EXPORT_SYMBOL(i2o_remove_handler); EXPORT_SYMBOL(i2o_claim_device); EXPORT_SYMBOL(i2o_release_device); -EXPORT_SYMBOL(i2o_run_queue); -EXPORT_SYMBOL(i2o_activate_controller); -EXPORT_SYMBOL(i2o_get_class_name); -EXPORT_SYMBOL(i2o_status_get); +EXPORT_SYMBOL(i2o_device_notify_on); +EXPORT_SYMBOL(i2o_device_notify_off); + +EXPORT_SYMBOL(i2o_post_this); +EXPORT_SYMBOL(i2o_post_wait); EXPORT_SYMBOL(i2o_query_scalar); EXPORT_SYMBOL(i2o_set_scalar); EXPORT_SYMBOL(i2o_query_table); EXPORT_SYMBOL(i2o_clear_table); EXPORT_SYMBOL(i2o_row_add_table); - -EXPORT_SYMBOL(i2o_post_this); -EXPORT_SYMBOL(i2o_post_wait); +EXPORT_SYMBOL(i2o_row_delete_table); EXPORT_SYMBOL(i2o_issue_params); +EXPORT_SYMBOL(i2o_event_register); +EXPORT_SYMBOL(i2o_event_ack); + EXPORT_SYMBOL(i2o_report_status); +EXPORT_SYMBOL(i2o_dump_message); +EXPORT_SYMBOL(i2o_get_class_name); MODULE_AUTHOR("Red Hat Software"); MODULE_DESCRIPTION("I2O Core"); + int init_module(void) { - printk(KERN_INFO "I2O Core - (c) Copyright 1999 Red Hat Software.\n"); + printk(KERN_INFO "I2O Core - (C) Copyright 1999 Red Hat Software\n"); if (i2o_install_handler(&i2o_core_handler) < 0) { printk(KERN_ERR - "i2o: Unable to install core handler.\nI2O stack not loaded!"); + "i2o_core: Unable to install core handler.\nI2O stack not loaded!"); return 0; } core_context = i2o_core_handler.context; + /* * Attach core to I2O PCI transport (and others as they are developed) */ @@ -2429,22 +3039,60 @@ int init_module(void) printk(KERN_INFO "i2o: No PCI I2O controllers found\n"); #endif + /* + * Initialize event handling thread + */ + init_MUTEX_LOCKED(&evt_sem); + evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND); + if(evt_pid < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + else(KERN_INFO "event thread created as pid %d\n", evt_pid); + if(i2o_num_controllers) i2o_sys_init(); + register_reboot_notifier(&i2o_reboot_notifier); + return 0; } void cleanup_module(void) { + int stat; + + unregister_reboot_notifier(&i2o_reboot_notifier); + if(i2o_num_controllers) i2o_sys_shutdown(); + /* + * If this is shutdown time, the thread has already been killed + */ + if(evt_running) { + stat = kill_proc(evt_pid, SIGTERM, 1); + if(!stat) { + int count = 10 * 100; + while(evt_running && count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if(!count) + printk(KERN_ERR "i2o: Event thread still running!\n"); + } + } + #ifdef CONFIG_I2O_PCI_MODULE i2o_pci_core_detach(); #endif i2o_remove_handler(&i2o_core_handler); + + unregister_reboot_notifier(&i2o_reboot_notifier); } #else @@ -2468,6 +3116,21 @@ int __init i2o_init(void) core_context = i2o_core_handler.context; + /* + * Initialize event handling thread + * We may not find any controllers, but still want this as + * down the road we may have hot pluggable controllers that + * need to be dealt with. + */ + init_MUTEX_LOCKED(&evt_sem); + if((evt_pid = kernel_thread(i2o_core_evt, &evt_reply, CLONE_SIGHAND)) < 0) + { + printk(KERN_ERR "I2O: Could not create event handler kernel thread\n"); + i2o_remove_handler(&i2o_core_handler); + return 0; + } + + #ifdef CONFIG_I2O_PCI i2o_pci_init(); #endif @@ -2475,6 +3138,8 @@ int __init i2o_init(void) if(i2o_num_controllers) i2o_sys_init(); + register_reboot_notifier(&i2o_reboot_notifier); + i2o_config_init(); #ifdef CONFIG_I2O_BLOCK i2o_block_init(); diff --git a/drivers/i2o/i2o_lan.c b/drivers/i2o/i2o_lan.c index ddcefd1a6da1..2957424d6883 100644 --- a/drivers/i2o/i2o_lan.c +++ b/drivers/i2o/i2o_lan.c @@ -1,10 +1,10 @@ /* - * linux/drivers/i2o/i2o_lan.c + * drivers/i2o/i2o_lan.c * - * I2O LAN CLASS OSM January 7th 1999 + * I2O LAN CLASS OSM April 3rd 2000 * - * (C) Copyright 1999 University of Helsinki, - * Department of Computer Science + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science * * This code is still under development / test. * @@ -14,7 +14,8 @@ * 2 of the License, or (at your option) any later version. * * Authors: Auvo Häkkinen - * Juha Sievänen + * Fixes: Juha Sievänen + * Taneli Vähäkangas * Deepak Saxena * * Tested: in FDDI environment (using SysKonnect's DDM) @@ -31,10 +32,12 @@ #include #include #include +#include +#include + #include #include #include -#include #include #include #include @@ -52,46 +55,62 @@ #define dprintk(s, args...) #endif -/* Module params */ - -static u32 bucketpost = I2O_BUCKET_COUNT; -static u32 bucketthresh = I2O_BUCKET_THRESH; -static u32 rx_copybreak = 200; +/* The following module parameters are used as default values + * for per interface values located in the net_device private area. + * Private values are changed via /proc filesystem. + */ +static u32 max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; +static u32 bucket_thresh = I2O_LAN_BUCKET_THRESH; +static u32 rx_copybreak = I2O_LAN_RX_COPYBREAK; +static tx_batch_mode = I2O_LAN_TX_BATCH_MODE; +static i2o_event_mask = I2O_LAN_EVENT_MASK; #define MAX_LAN_CARDS 16 static struct net_device *i2o_landevs[MAX_LAN_CARDS+1]; -static int unit = -1; /* device unit number */ +static int unit = -1; /* device unit number */ -struct i2o_lan_local { - u8 unit; - struct i2o_device *i2o_dev; - struct fddi_statistics stats; /* see also struct net_device_stats */ - unsigned short (*type_trans)(struct sk_buff *, struct net_device *); - u32 bucket_count; /* nbr of buckets sent to DDM */ - u32 tx_count; /* packets in one TX message frame */ - u32 tx_max_out; /* DDM's Tx queue len */ - u32 tx_out; /* outstanding TXes */ - u32 sgl_max; /* max SGLs in one message frame */ - u32 m; /* IOP address of msg frame */ - - struct tq_struct i2o_batch_send_task; - struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ - int i2o_fbl_tail; - - spinlock_t lock; -}; +extern rwlock_t dev_mc_lock; -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, - struct i2o_message *m); -static void i2o_lan_event_reply(struct net_device *dev, u32 *msg); +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); +static void i2o_lan_send_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); static int i2o_lan_receive_post(struct net_device *dev); -static int i2o_lan_receive_post_reply(struct net_device *dev, u32 *msg); +static void i2o_lan_receive_post_reply(struct i2o_handler *h, struct i2o_controller *iop, struct i2o_message *m); static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg); +static int i2o_lan_reset(struct net_device *dev); +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg); + +/* Structures to register handlers for the incoming replies. */ + +static struct i2o_handler i2o_lan_send_handler = { + i2o_lan_send_post_reply, // For send replies + NULL, + NULL, + NULL, + "I2O Lan OSM send", + -1, + I2O_CLASS_LAN +}; +static int lan_send_context; + +static struct i2o_handler i2o_lan_receive_handler = { + i2o_lan_receive_post_reply, // For receive replies + NULL, + NULL, + NULL, + "I2O Lan OSM receive", + -1, + I2O_CLASS_LAN +}; +static int lan_receive_context; + static struct i2o_handler i2o_lan_handler = { - i2o_lan_reply, + i2o_lan_reply, // For other replies + NULL, + NULL, + NULL, "I2O Lan OSM", - 0, // context + -1, I2O_CLASS_LAN }; static int lan_context; @@ -100,245 +119,264 @@ static struct tq_struct i2o_post_buckets_task = { 0, 0, (void (*)(void *))i2o_lan_receive_post, (void *) 0 }; +/* Functions to handle message failures and transaction errors: +==============================================================*/ + /* - * i2o_lan_reply(): The only callback function to handle incoming messages. + * i2o_lan_handle_failure(): Fail bit has been set since IOP's message + * layer cannot deliver the request to the target, or the target cannot + * process the request. */ -static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, - struct i2o_message *m) +static void i2o_lan_handle_failure(struct net_device *dev, u32 *msg) { - u32 *msg = (u32 *)m; - u8 unit = (u8)(msg[2]>>16); // InitiatorContext - struct net_device *dev = i2o_landevs[unit]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; - if (msg[0] & (1<<13)) { // Fail bit is set - printk(KERN_ERR "%s: IOP failed to process the msg:\n",dev->name); - printk(KERN_ERR " Cmd = 0x%02X, InitiatorTid = %d, TargetTid = %d\n", - (msg[1] >> 24) & 0xFF, (msg[1] >> 12) & 0xFFF, msg[1] & 0xFFF); - printk(KERN_ERR " FailureCode = 0x%02X\n Severity = 0x%02X\n " - "LowestVersion = 0x%02X\n HighestVersion = 0x%02X\n", - msg[4] >> 24, (msg[4] >> 16) & 0xFF, - (msg[4] >> 8) & 0xFF, msg[4] & 0xFF); - printk(KERN_ERR " FailingHostUnit = 0x%04X\n FailingIOP = 0x%03X\n", - msg[5] >> 16, msg[5] & 0xFFF); - return; - } + u32 *preserved_msg = (u32*)(iop->mem_offset + msg[7]); + // FIXME on 64-bit host + u32 *sgl_elem = &preserved_msg[4]; + struct sk_buff *skb = NULL; + u8 le_flag; -#ifndef DRIVERDEBUG - if (msg[4] >> 24) /* ReqStatus != SUCCESS */ -#endif - i2o_report_status(KERN_INFO, dev->name, msg); - - switch (msg[1] >> 24) { - case LAN_RECEIVE_POST: - { - if (netif_running(dev)) { - if (!(msg[4]>>24)) { - i2o_lan_receive_post_reply(dev,msg); - break; - } +// To be added to i2o_core.c +// i2o_report_failure(KERN_INFO, iop, dev->name, msg); - // Something VERY wrong if this is happening - printk( KERN_WARNING "%s: rejected bucket post.\n", dev->name); - } + /* If PacketSend failed, free sk_buffs reserved by upper layers */ - // Shutting down, we are getting unused buckets back - i2o_lan_release_buckets(dev,msg); - - break; - } + if (msg[1] >> 24 == LAN_PACKET_SEND) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); - case LAN_PACKET_SEND: - case LAN_SDU_SEND: - { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_count = msg[3] & 0x000000FF; - - while (trl_count) { - // The HDM has handled the outgoing packet - dev_kfree_skb((struct sk_buff *)msg[4 + trl_count]); - dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n", - dev->name,trl_count); - priv->tx_out--; - trl_count--; - } + atomic_dec(&priv->tx_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ if (netif_queue_stopped(dev)) netif_wake_queue(dev); - - break; } - case LAN_RESET: /* default reply without payload */ - case LAN_SUSPEND: - break; + /* If ReceivePost failed, free sk_buffs we have reserved */ - case I2O_CMD_UTIL_EVT_REGISTER: - case I2O_CMD_UTIL_EVT_ACK: - i2o_lan_event_reply(dev, msg); - break; + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + do { + skb = (struct sk_buff *)(sgl_elem[1]); + dev_kfree_skb_irq(skb); - default: - printk(KERN_ERR "%s: No handler for the reply.\n", dev->name); - i2o_report_status(KERN_INFO, dev->name, msg); + atomic_dec(&priv->buckets_out); + + le_flag = *sgl_elem >> 31; + sgl_elem +=3; + } while (le_flag == 0); /* Last element flag not set */ + } + + /* Release the preserved msg frame by resubmitting it as a NOP */ + + preserved_msg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0; + preserved_msg[1] = I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0; + preserved_msg[2] = 0; + i2o_post_message(iop, msg[7]); +} +/* + * i2o_lan_handle_transaction_error(): IOP or DDM has rejected the request + * for general cause (format error, bad function code, insufficient resources, + * etc.). We get one transaction_error for each failed transaction. + */ +static void i2o_lan_handle_transaction_error(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct sk_buff *skb; + +// To be added to i2o_core.c +// i2o_report_transaction_error(KERN_INFO, dev->name, msg); + + /* If PacketSend was rejected, free sk_buff reserved by upper layers */ + + if (msg[1] >> 24 == LAN_PACKET_SEND) { + skb = (struct sk_buff *)(msg[3]); // TransactionContext + dev_kfree_skb_irq(skb); + atomic_dec(&priv->tx_out); + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); + } + + /* If ReceivePost was rejected, free sk_buff we have reserved */ + + if (msg[1] >> 24 == LAN_RECEIVE_POST) { + skb = (struct sk_buff *)(msg[3]); + dev_kfree_skb_irq(skb); + atomic_dec(&priv->buckets_out); } } /* - * i2o_lan_event_reply(): Handle events. + * i2o_lan_handle_status(): Common parts of handling a not succeeded request + * (status != SUCCESS). */ -static void i2o_lan_event_reply(struct net_device *dev, u32 *msg) +static int i2o_lan_handle_status(struct net_device *dev, u32 *msg) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - struct i2o_reply { - u8 version_offset; - u8 msg_flags; - u16 msg_size; - u32 tid:12; - u32 initiator:12; - u32 function:8; - u32 initiator_context; - u32 transaction_context; - u32 evt_indicator; - u32 evt_data[(iop->inbound_size - 20) / 4]; /* max */ - } *evt = (struct i2o_reply *)msg; - - int evt_data_len = (evt->msg_size - 5) * 4; /* real */ - - if (evt->function == I2O_CMD_UTIL_EVT_REGISTER) { - printk(KERN_INFO "%s: I2O event - ", dev->name); - - switch (evt->evt_indicator) { - case I2O_EVT_IND_STATE_CHANGE: - printk("State chance 0x%08X.\n", - evt->evt_data[0]); - break; - case I2O_EVT_IND_GENERAL_WARNING: - printk("General warning 0x%02X.\n", - evt->evt_data[0]); - break; - case I2O_EVT_IND_CONFIGURATION_FLAG: - printk("Configuration requested.\n"); - break; - case I2O_EVT_IND_LOCK_RELEASE: - printk("Lock released.\n"); - break; - case I2O_EVT_IND_CAPABILITY_CHANGE: - printk("Capability change 0x%02X.\n", - evt->evt_data[0]); - break; - case I2O_EVT_IND_DEVICE_RESET: - printk("Device reset.\n"); - break; - case I2O_EVT_IND_EVT_MASK_MODIFIED: - printk("Event mask modified, 0x%08X.\n", - evt->evt_data[0]); - break; - case I2O_EVT_IND_FIELD_MODIFIED: { - u16 *work16 = (u16 *)evt->evt_data; - printk("Group 0x%04X, field %d changed.\n", - work16[0], work16[1]); - break; - } - case I2O_EVT_IND_VENDOR_EVT: { - int i; - printk("Vendor event:\n"); - for (i = 0; i < evt_data_len / 4; i++) - printk(" 0x%08X\n", evt->evt_data[i]); - break; - } - case I2O_EVT_IND_DEVICE_STATE: - printk("Device state changed 0x%08X.\n", - evt->evt_data[0]); - break; - case I2O_LAN_EVT_LINK_DOWN: - printk("Link to the physical device is lost.\n"); - break; - case I2O_LAN_EVT_LINK_UP: - printk("Link to the physical device is (re)established.\n"); - break; - case I2O_LAN_EVT_MEDIA_CHANGE: - printk("Media change.\n"); - break; - default: - printk("Event Indicator = 0x%08X.\n", - evt->evt_indicator); - } - - /* - * EventAck necessary only for events that cause the device - * to syncronize with the user - * - *if (i2o_event_ack(iop, i2o_dev->lct_data->tid, - * priv->unit << 16 | lan_context, - * evt->evt_indicator, - * evt->evt_data, evt_data_len) < 0) - * printk("%s: Event Acknowledge timeout.\n", dev->name); - */ - } - - /* else evt->function == I2O_CMD_UTIL_EVT_ACK) */ - /* Do we need to do something here too? */ + /* Fail bit set? */ + + if (msg[0] & MSG_FAIL) { + i2o_lan_handle_failure(dev, msg); + return -1; + } + + /* Message rejected for general cause? */ + + if ((msg[4]>>24) == I2O_REPLY_STATUS_TRANSACTION_ERROR) { + i2o_lan_handle_transaction_error(dev, msg); + return -1; + } + + /* Else have to handle it in the callback function */ + + return 0; } +/* Callback functions called from the interrupt routine: +=======================================================*/ + /* - * i2o_lan_release_buckets(): Handle unused buckets. + * i2o_lan_send_post_reply(): Callback function to handle PostSend replies. */ -static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) +static void i2o_lan_send_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - u8 trl_count = (u8)(msg[3] & 0x000000FF); - u32 *pskb = &msg[6]; + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_count = msg[3] & 0x000000FF; - while (trl_count--) { - dprintk("%s: Releasing unused sk_buff %p.\n",dev->name, - (struct sk_buff*)(*pskb)); - dev_kfree_skb((struct sk_buff*)(*pskb)); - pskb++; - priv->bucket_count--; +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + /* Else we get pending transmit request(s) back */ } + + /* DDM has handled transmit request(s), free sk_buffs */ + + while (trl_count) { + dev_kfree_skb_irq((struct sk_buff *)msg[4 + trl_count]); + dprintk(KERN_INFO "%s: Request skb freed (trl_count=%d).\n", + dev->name, trl_count); + atomic_dec(&priv->tx_out); + trl_count--; + } + + /* If priv->tx_out had reached tx_max_out, the queue was stopped */ + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); } /* - * i2o_lan_receive_post_reply(): Process incoming packets. + * i2o_lan_receive_post_reply(): Callback function to process incoming packets. */ -static int i2o_lan_receive_post_reply(struct net_device *dev, u32 *msg) +static void i2o_lan_receive_post_reply(struct i2o_handler *h, + struct i2o_controller *iop, struct i2o_message *m) { + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_bucket_descriptor *bucket = (struct i2o_bucket_descriptor *)&msg[6]; struct i2o_packet_info *packet; u8 trl_count = msg[3] & 0x000000FF; struct sk_buff *skb, *old_skb; + unsigned long flags = 0; + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + /* Getting unused buckets back? */ + + if (msg[4] & I2O_LAN_DSC_CANCELED || + msg[4] & I2O_LAN_DSC_RECEIVE_ABORTED) { + i2o_lan_release_buckets(dev, msg); + return; + } + + /* Which DetailedStatusCodes need special treatment? */ + } + + /* Else we are receiving incoming post. */ while (trl_count--) { - skb = (struct sk_buff *)bucket->context; - packet = (struct i2o_packet_info *)bucket->packet_info; - priv->bucket_count--; + skb = (struct sk_buff *)bucket->context; + packet = (struct i2o_packet_info *)bucket->packet_info; + atomic_dec(&priv->buckets_out); +#if 0 +/* Is this enough? If we get erroneous bucket, we can't assume that skb could + * be reused, can we? + */ + + /* Should we optimise these ifs away from the fast path? -taneli */ + if (packet->flags & 0x0f) { + + if (packet->flags & 0x01) + printk(KERN_WARNING "%s: packet with errors.\n", dev->name); + if (packet->flags & 0x0c) + /* This actually means that the hw is b0rken, since we + have asked it to not send fragmented packets. */ + printk(KERN_DEBUG "%s: multi-bucket packets not supported!\n", dev->name); + bucket++; + if (skb) + dev_kfree_skb_irq(skb); + continue; + } - if (packet->len < rx_copybreak) { + if (packet->status & 0xff) { + /* Silently discard, unless debugging. */ + dprintk(KERN_DEBUG "%s: toasted packet received.\n", dev->name); + bucket++; + if (skb) + dev_kfree_skb_irq(skb); + continue; + } +#endif + if (packet->len < priv->rx_copybreak) { old_skb = skb; - skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); + skb = (struct sk_buff *)dev_alloc_skb(packet->len+2); if (skb == NULL) { - printk("%s: Can't allocate skb.\n", dev->name); - return -ENOMEM; - } - skb_reserve(skb,2); - memcpy(skb_put(skb,packet->len), old_skb->data, packet->len); - - if (priv->i2o_fbl_tail < I2O_BUCKET_COUNT) - priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; + printk(KERN_ERR "%s: Can't allocate skb.\n", dev->name); + return; + } + skb_reserve(skb, 2); + memcpy(skb_put(skb, packet->len), old_skb->data, packet->len); + + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail < I2O_LAN_MAX_BUCKETS_OUT) + priv->i2o_fbl[++priv->i2o_fbl_tail] = old_skb; else - dev_kfree_skb(old_skb); + dev_kfree_skb_irq(old_skb); + spin_unlock_irqrestore(&priv->fbl_lock, flags); } else - skb_put(skb,packet->len); - + skb_put(skb, packet->len); + skb->dev = dev; skb->protocol = priv->type_trans(skb, dev); netif_rx(skb); + dev->last_rx = jiffies; dprintk(KERN_INFO "%s: Incoming packet (%d bytes) delivered " - "to upper level.\n",dev->name,packet->len); + "to upper level.\n", dev->name, packet->len); bucket++; // to next Packet Descriptor Block } @@ -346,19 +384,193 @@ static int i2o_lan_receive_post_reply(struct net_device *dev, u32 *msg) #ifdef DRIVERDEBUG if (msg[5] == 0) printk(KERN_INFO "%s: DDM out of buckets (priv->count = %d)!\n", - dev->name, priv->bucket_count); + dev->name, atomic_read(&priv->buckets_out)); #endif - if (priv->bucket_count <= bucketpost - bucketthresh) { - i2o_post_buckets_task.data = (void *)dev; - queue_task(&i2o_post_buckets_task, &tq_immediate); - mark_bh(IMMEDIATE_BH); - /* Note: the task is queued only once */ + /* If DDM has already consumed bucket_tresh buckets, post new ones */ + + if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) { + i2o_post_buckets_task.data = (void *)dev; + queue_task(&i2o_post_buckets_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + + return; +} + +/* + * i2o_lan_reply(): Callback function to handle other incoming messages + * except SendPost and ReceivePost. + */ +static void i2o_lan_reply(struct i2o_handler *h, struct i2o_controller *iop, + struct i2o_message *m) +{ + u32 *msg = (u32 *)m; + u8 unit = (u8)(msg[2]>>16); // InitiatorContext + struct net_device *dev = i2o_landevs[unit]; + +#ifdef DRIVERDEBUG + i2o_report_status(KERN_INFO, dev->name, msg); +#endif + + if ((msg[4] >> 24) != I2O_REPLY_STATUS_SUCCESS) { + if (i2o_lan_handle_status(dev, msg)) + return; + + /* This should NOT be reached */ + } + + switch (msg[1] >> 24) { + case LAN_RESET: + case LAN_SUSPEND: + /* default reply without payload */ + break; + case I2O_CMD_UTIL_EVT_REGISTER: + case I2O_CMD_UTIL_EVT_ACK: + i2o_lan_handle_event(dev, msg); + break; + default: + printk(KERN_ERR "%s: No handler for the reply.\n", + dev->name); + i2o_report_status(KERN_INFO, dev->name, msg); } - - return 0; } +/* Functions used by the above callback functions: +=================================================*/ +/* + * i2o_lan_release_buckets(): Free unused buckets (sk_buffs). + */ +static void i2o_lan_release_buckets(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + u8 trl_elem_size = (u8)(msg[3]>>8 & 0x000000FF); + u8 trl_count = (u8)(msg[3] & 0x000000FF); + u32 *pskb = &msg[6]; + + while (trl_count--) { + dprintk(KERN_DEBUG "%s: Releasing unused sk_buff %p (trl_count=%d).\n", + dev->name, (struct sk_buff*)(*pskb),trl_count+1); + dev_kfree_skb_irq((struct sk_buff *)(*pskb)); + pskb += 1 + trl_elem_size; + atomic_dec(&priv->buckets_out); + } +} + +/* + * i2o_lan_event_reply(): Handle events. + */ +static void i2o_lan_handle_event(struct net_device *dev, u32 *msg) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + struct i2o_reply { + u8 version_offset; + u8 msg_flags; + u16 msg_size; + u32 tid:12; + u32 initiator:12; + u32 function:8; + u32 initiator_context; + u32 transaction_context; + u32 evt_indicator; + u32 data[(iop->inbound_size - 20) / 4]; /* max */ + } *evt = (struct i2o_reply *)msg; + int evt_data_len = (evt->msg_size - 5) * 4; /* real */ + + printk(KERN_INFO "%s: I2O event - ", dev->name); + + if (evt->function == I2O_CMD_UTIL_EVT_ACK) { + printk("Event acknowledgement reply.\n"); + return; + } + + /* Else evt->function == I2O_CMD_UTIL_EVT_REGISTER) */ + + switch (evt->evt_indicator) { + case I2O_EVT_IND_STATE_CHANGE: { + struct state_data { + u16 status; + u8 state; + u8 data; + } *evt_data = (struct state_data *)(evt->data[0]); + + printk("State chance 0x%08x.\n", evt->data[0]); + + /* If the DDM is in error state, recovery may be + * possible if status = Transmit or Receive Control + * Unit Inoperable. + */ + if (evt_data->state==0x05 && evt_data->status==0x0003) + i2o_lan_reset(dev); + break; + } + + case I2O_EVT_IND_GENERAL_WARNING: + printk("General warning 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_CONFIGURATION_FLAG: + printk("Configuration requested.\n"); + break; + + case I2O_EVT_IND_CAPABILITY_CHANGE: + printk("Capability change 0x%04x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_DEVICE_RESET: + /* Spec 2.0 p. 6-121: + * The event of _DEVICE_RESET should also be responded + */ + printk("Device reset.\n"); + if (i2o_event_ack(iop, msg) < 0) + printk("%s: Event Acknowledge timeout.\n", dev->name); + break; + + case I2O_EVT_IND_EVT_MASK_MODIFIED: + printk("Event mask modified, 0x%08x.\n", evt->data[0]); + break; + + case I2O_EVT_IND_FIELD_MODIFIED: { + u16 *work16 = (u16 *)evt->data; + printk("Group 0x%04x, field %d changed.\n", work16[0], + work16[1]); + break; + } + + case I2O_EVT_IND_VENDOR_EVT: { + int i; + printk("Vendor event:\n"); + for (i = 0; i < evt_data_len / 4; i++) + printk(" 0x%08x\n", evt->data[i]); + break; + } + + case I2O_EVT_IND_DEVICE_STATE: + printk("Device state changed 0x%08x.\n", evt->data[0]); + break; + + case I2O_LAN_EVT_LINK_DOWN: + printk("Link to the physical device is lost.\n"); + break; + + case I2O_LAN_EVT_LINK_UP: + printk("Link to the physical device is (re)established.\n"); + break; + + case I2O_LAN_EVT_MEDIA_CHANGE: + printk("Media change.\n"); + break; + + default: + printk("Event Indicator = 0x%08x.\n", evt->evt_indicator); + } + + /* Note: EventAck necessary only for events that cause the device to + * syncronize with the user. + */ +} /* * i2o_lan_receive_post(): Post buckets to receive packets. @@ -369,93 +581,108 @@ static int i2o_lan_receive_post(struct net_device *dev) struct i2o_device *i2o_dev = priv->i2o_dev; struct i2o_controller *iop = i2o_dev->controller; struct sk_buff *skb; - u32 m; u32 *msg; - u32 bucket_len = (dev->mtu + dev->hard_header_len); - u32 total = bucketpost - priv->bucket_count; - u32 bucket_count; - u32 *sgl_elem; + u32 m, *msg; + u32 bucket_len = (dev->mtu + dev->hard_header_len); + u32 total = priv->max_buckets_out - atomic_read(&priv->buckets_out); + u32 bucket_count; + u32 *sgl_elem; + unsigned long flags; + + /* Send (total/bucket_count) separate I2O requests */ + + while (total) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) + return -ETIMEDOUT; + msg = (u32 *)(iop->mem_offset + m); - while (total) { - m = I2O_POST_READ32(iop); - if (m == 0xFFFFFFFF) - return -ETIMEDOUT; - msg = (u32 *)(iop->mem_offset + m); + bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; + total -= bucket_count; + atomic_add(bucket_count, &priv->buckets_out); - bucket_count = (total >= priv->sgl_max) ? priv->sgl_max : total; - total -= bucket_count; - priv->bucket_count += bucket_count; + dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN DDM.\n", + dev->name, bucket_count, bucket_len); - dprintk(KERN_INFO "%s: Sending %d buckets (size %d) to LAN HDM.\n", - dev->name, bucket_count, bucket_len); + /* Fill in the header */ __raw_writel(I2O_MESSAGE_SIZE(4 + 3 * bucket_count) | SGL_OFFSET_4, msg); - __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data->tid, msg+1); - __raw_writel(priv->unit << 16 | lan_context, msg+2); + __raw_writel(LAN_RECEIVE_POST<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_receive_context, msg+2); __raw_writel(bucket_count, msg+3); - sgl_elem = &msg[4]; - - while (bucket_count--) { - if (priv->i2o_fbl_tail >= 0) - skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; - else { - skb = dev_alloc_skb(bucket_len + 2); - if (skb == NULL) - return -ENOMEM; - skb_reserve(skb, 2); - } - __raw_writel(0x51000000 | bucket_len, sgl_elem); - __raw_writel((u32)skb, sgl_elem+1); - __raw_writel(virt_to_bus(skb->data), sgl_elem+2); - sgl_elem += 3; - } - - /* set LE flag and post buckets */ + sgl_elem = &msg[4]; + + /* Fill in the payload - contains bucket_count SGL elements */ + + while (bucket_count--) { + spin_lock_irqsave(&priv->fbl_lock, flags); + if (priv->i2o_fbl_tail >= 0) + skb = priv->i2o_fbl[priv->i2o_fbl_tail--]; + else { + skb = dev_alloc_skb(bucket_len + 2); + if (skb == NULL) { + spin_unlock_irqrestore(&priv->fbl_lock, flags); + return -ENOMEM; + } + skb_reserve(skb, 2); + } + spin_unlock_irqrestore(&priv->fbl_lock, flags); + + __raw_writel(0x51000000 | bucket_len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + sgl_elem += 3; + } + + /* set LE flag and post */ __raw_writel(__raw_readl(sgl_elem-3) | 0x80000000, (sgl_elem-3)); - i2o_post_message(iop,m); - } + i2o_post_message(iop, m); + } - return 0; + return 0; } +/* Functions called from the network stack, and functions called by them: +========================================================================*/ + /* * i2o_lan_reset(): Reset the LAN adapter into the operational state and * restore it to full operation. */ static int i2o_lan_reset(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; + struct i2o_controller *iop = i2o_dev->controller; u32 msg[5]; dprintk(KERN_INFO "%s: LAN RESET MESSAGE.\n", dev->name); msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data->tid; + msg[1] = LAN_RESET<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; msg[2] = priv->unit << 16 | lan_context; // InitiatorContext msg[3] = 0; // TransactionContext - msg[4] = 1 << 16; // return posted buckets + msg[4] = 0; // keep posted buckets if (i2o_post_this(iop, msg, sizeof(msg)) < 0) - return -ETIMEDOUT; + return -ETIMEDOUT; return 0; } /* * i2o_lan_suspend(): Put LAN adapter into a safe, non-active state. - * Reply to any LAN class message with status error_no_data_transfer + * IOP replies to any LAN class message with status error_no_data_transfer * / suspended. */ static int i2o_lan_suspend(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; + struct i2o_controller *iop = i2o_dev->controller; u32 msg[5]; dprintk(KERN_INFO "%s: LAN SUSPEND MESSAGE.\n", dev->name); msg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0; - msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data->tid; + msg[1] = LAN_SUSPEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid; msg[2] = priv->unit << 16 | lan_context; // InitiatorContext msg[3] = 0; // TransactionContext msg[4] = 1 << 16; // return posted buckets @@ -471,75 +698,91 @@ static int i2o_lan_suspend(struct net_device *dev) */ static void i2o_set_batch_mode(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; u32 val; - /* set LAN_BATCH_CONTROL attributes */ + /* Set defaults LAN_BATCH_CONTROL attributes */ + /* May be changed via /proc or Configuration Utility */ - // enable batch mode, toggle automatically - val = 0x00000000; - if (i2o_set_scalar(iop, i2o_dev->lct_data->tid, 0x0003, 0, &val, sizeof(val)) <0) + val = 0x00000000; // enable batch mode, toggle automatically + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0003, 0, &val, sizeof(val)) <0) printk(KERN_WARNING "%s: Unable to enter I2O LAN batch mode.\n", - dev->name); + dev->name); + else + dprintk(KERN_INFO "%s: I2O LAN batch mode enabled.\n", dev->name); + + /* Set LAN_OPERATION attributes */ + +#ifdef DRIVERDEBUG +/* Added for testing: this will be removed */ + val = 0x00000003; // 1 = UserFlags + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 1, &val, sizeof(val)) < 0) + printk(KERN_WARNING "%s: Can't enable ErrorReporting & BadPacketHandling.\n", + dev->name); else - dprintk(KERN_INFO "%s: I2O LAN batch mode enabled.\n",dev->name); + dprintk(KERN_INFO "%s: ErrorReporting enabled, " + "BadPacketHandling enabled.\n", dev->name); +#endif /* DRIVERDEBUG */ /* * When PacketOrphanlimit is same as the maximum packet length, * the packets will never be split into two separate buckets */ - - /* set LAN_OPERATION attributes */ - - val = dev->mtu + dev->hard_header_len; // PacketOrphanLimit - if (i2o_set_scalar(iop, i2o_dev->lct_data->tid, 0x0004, 2, &val, sizeof(val)) < 0) + val = dev->mtu + dev->hard_header_len; // 2 = PacketOrphanLimit + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0004, 2, &val, sizeof(val)) < 0) printk(KERN_WARNING "%s: Unable to set PacketOrphanLimit.\n", - dev->name); + dev->name); else dprintk(KERN_INFO "%s: PacketOrphanLimit set to %d.\n", - dev->name,val); - - return; + dev->name, val); + + return; } +/* Functions called from the network stack: +==========================================*/ + /* * i2o_lan_open(): Open the device to send/receive packets via - * the network device. + * the network device. */ static int i2o_lan_open(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_device *i2o_dev = priv->i2o_dev; -#if 0 struct i2o_controller *iop = i2o_dev->controller; - u32 evt_mask = 0xFFC00007; // All generic events, all lan events -#endif - if (i2o_claim_device(i2o_dev, &i2o_lan_handler, I2O_CLAIM_PRIMARY)) { + + MOD_INC_USE_COUNT; + + if (i2o_claim_device(i2o_dev, &i2o_lan_handler)) { printk(KERN_WARNING "%s: Unable to claim the I2O LAN device.\n", dev->name); + MOD_DEC_USE_COUNT; return -EAGAIN; } - dprintk(KERN_INFO "%s: I2O LAN device claimed (tid=%d).\n", - dev->name, i2o_dev->lct_data->tid); -#if 0 - if (i2o_event_register(iop, i2o_dev->lct_data->tid, - priv->unit << 16 | lan_context, evt_mask) < 0) + dprintk(KERN_INFO "%s: I2O LAN device (tid=%d) claimed by LAN OSM.\n", + dev->name, i2o_dev->lct_data.tid); + + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, priv->i2o_event_mask) < 0) printk(KERN_WARNING "%s: Unable to set the event mask.\n", dev->name); -#endif + i2o_lan_reset(dev); - - priv->i2o_fbl = kmalloc(bucketpost * sizeof(struct sk_buff *),GFP_KERNEL); - if (priv->i2o_fbl == NULL) + + priv->i2o_fbl = kmalloc(priv->max_buckets_out * sizeof(struct sk_buff *), + GFP_KERNEL); + if (priv->i2o_fbl == NULL) { + MOD_DEC_USE_COUNT; return -ENOMEM; + } priv->i2o_fbl_tail = -1; - - netif_start_queue(dev); + priv->send_active = 0; i2o_set_batch_mode(dev); i2o_lan_receive_post(dev); - MOD_INC_USE_COUNT; + netif_start_queue(dev); return 0; } @@ -549,22 +792,24 @@ static int i2o_lan_open(struct net_device *dev) */ static int i2o_lan_close(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; -#if 0 + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; struct i2o_controller *iop = i2o_dev->controller; - if (i2o_event_register(iop, i2o_dev->lct_data->tid, - priv->unit << 16 | lan_context, 0) < 0) - printk(KERN_WARNING "%s: Unable to clear the event mask.\n", -#endif dev->name); - netif_stop_queue(dev); + i2o_lan_suspend(dev); - if (i2o_release_device(i2o_dev, &i2o_lan_handler, I2O_CLAIM_PRIMARY)) + if (i2o_event_register(iop, i2o_dev->lct_data.tid, + priv->unit << 16 | lan_context, 0, 0) < 0) + printk(KERN_WARNING "%s: Unable to clear the event mask.\n", + dev->name); + + if (i2o_release_device(i2o_dev, &i2o_lan_handler)) { printk(KERN_WARNING "%s: Unable to unclaim I2O LAN device " - "(tid=%d).\n", dev->name, i2o_dev->lct_data->tid); + "(tid=%d).\n", dev->name, i2o_dev->lct_data.tid); + return -EBUSY; + } while (priv->i2o_fbl_tail >= 0) dev_kfree_skb(priv->i2o_fbl[priv->i2o_fbl_tail--]); @@ -575,28 +820,115 @@ static int i2o_lan_close(struct net_device *dev) return 0; } -#if 0 /* - * i2o_lan_sdu_send(): Send a packet, MAC header added by the HDM. - * Must be supported by Fibre Channel, optional for Ethernet/802.3, - * Token Ring, FDDI + * i2o_lan_tx_timeout(): Tx timeout handler. */ -static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) -{ - return -EINVAL; +static void i2o_lan_tx_timeout(struct net_device *dev) +{ + if (!netif_queue_stopped(dev)) + netif_start_queue(dev); } -#endif +#define batching(x, cond) ( (x)->tx_batch_mode==1 || ((x)->tx_batch_mode==2 && (cond)) ) + +/* + * Batch send packets. Both i2o_lan_sdu_send and i2o_lan_packet_send + * use this. I'm still not pleased. If you come up with + * something better, please tell me. -taneli + */ static void i2o_lan_batch_send(struct net_device *dev) -{ +{ struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_controller *iop = priv->i2o_dev->controller; + spin_lock_irq(&priv->tx_lock); if (priv->tx_count != 0) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; + } + spin_unlock_irq(&priv->tx_lock); + + priv->send_active = 0; +} + +/* + * i2o_lan_sdu_send(): Send a packet, MAC header added by the DDM. + * Must be supported by Fibre Channel, optional for Ethernet/802.3, + * Token Ring, FDDI + */ + +/* + * This is a coarse first approximation. Needs testing. Any takers? -taneli + */ +static int i2o_lan_sdu_send(struct sk_buff *skb, struct net_device *dev) +{ + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; + u32 m, *msg; + u32 *sgl_elem; + + spin_lock_irq(&priv->tx_lock); + + priv->tx_count++; + atomic_inc(&priv->tx_out); + + if (priv->tx_count == 1) { + m = I2O_POST_READ32(iop); + if (m == 0xFFFFFFFF) { + spin_unlock_irq(&priv->tx_lock); + return 1; + } + msg = (u32 *)(iop->mem_offset + m); + priv->m = m; + + __raw_writel(NINE_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 3, msg+3); // TransmitControlWord + + __raw_writel(0xD7000000 | skb->len, msg+4); // MAC hdr included + __raw_writel((u32)skb, msg+5); // TransactionContext + __raw_writel(virt_to_bus(skb->data), msg+6); + __raw_writel((u32)skb->mac.raw, msg+7); + __raw_writel((u32)skb->mac.raw+4, msg+8); + if (batching(priv, !tickssofar) && !priv->send_active) { + priv->send_active = 1; + queue_task(&priv->i2o_batch_send_task, &tq_scheduler); + } + } else { /* Add new SGL element to the previous message frame */ + + msg = (u32 *)(iop->mem_offset + priv->m); + sgl_elem = &msg[priv->tx_count * 5 + 1]; + + __raw_writel(I2O_MESSAGE_SIZE((__raw_readl(msg)>>16) + 5) | 1<<12 | SGL_OFFSET_4, msg); + __raw_writel(__raw_readl(sgl_elem-5) & 0x7FFFFFFF, sgl_elem-5); /* clear LE flag */ + __raw_writel(0xD5000000 | skb->len, sgl_elem); + __raw_writel((u32)skb, sgl_elem+1); + __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + __raw_writel((u32)(skb->mac.raw), sgl_elem+3); + __raw_writel((u32)(skb->mac.raw)+1, sgl_elem+4); + } + + /* If tx not in batch mode or frame is full, send immediatelly */ + + if (!batching(priv, !tickssofar) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; i2o_post_message(iop, priv->m); - dprintk("%s: %d packets sent.\n", dev->name, priv->tx_count); + dprintk(KERN_DEBUG "%s: %d packets sent.\n", dev->name, priv->tx_count); priv->tx_count = 0; } + + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) + netif_stop_queue(dev); + + spin_unlock_irq(&priv->tx_lock); + return 0; } /* @@ -610,37 +942,37 @@ static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_device *i2o_dev = priv->i2o_dev; struct i2o_controller *iop = i2o_dev->controller; + int tickssofar = jiffies - dev->trans_start; u32 m, *msg; u32 *sgl_elem; + spin_lock_irq(&priv->tx_lock); + priv->tx_count++; - priv->tx_out++; + atomic_inc(&priv->tx_out); if (priv->tx_count == 1) { - dprintk("%s: New message frame\n", dev->name); - m = I2O_POST_READ32(iop); if (m == 0xFFFFFFFF) { - dev_kfree_skb(skb); - return -ETIMEDOUT; + spin_unlock_irq(&priv->tx_lock); + return 1; } msg = (u32 *)(iop->mem_offset + m); priv->m = m; __raw_writel(SEVEN_WORD_MSG_SIZE | 1<<12 | SGL_OFFSET_4, msg); - __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data->tid, msg+1); - __raw_writel(priv->unit << 16 | lan_context, msg+2); // InitiatorContext - __raw_writel(1 << 4, msg+3); // TransmitControlWord + __raw_writel(LAN_PACKET_SEND<<24 | HOST_TID<<12 | i2o_dev->lct_data.tid, msg+1); + __raw_writel(priv->unit << 16 | lan_send_context, msg+2); // InitiatorContext + __raw_writel(1 << 3, msg+3); // TransmitControlWord + __raw_writel(0xD5000000 | skb->len, msg+4); // MAC hdr included __raw_writel((u32)skb, msg+5); // TransactionContext __raw_writel(virt_to_bus(skb->data), msg+6); - - queue_task(&priv->i2o_batch_send_task, &tq_scheduler); - + if (batching(priv, !tickssofar) && !priv->send_active) { + priv->send_active = 1; + queue_task(&priv->i2o_batch_send_task, &tq_scheduler); + } } else { /* Add new SGL element to the previous message frame */ - - dprintk("%s: Adding packet %d to msg frame\n", - dev->name, priv->tx_count); msg = (u32 *)(iop->mem_offset + priv->m); sgl_elem = &msg[priv->tx_count * 3 + 1]; @@ -650,19 +982,23 @@ static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) __raw_writel(0xD5000000 | skb->len, sgl_elem); __raw_writel((u32)skb, sgl_elem+1); __raw_writel(virt_to_bus(skb->data), sgl_elem+2); + } - if (priv->tx_count == priv->sgl_max) { /* frame full, send now */ - i2o_post_message(iop, priv->m); - dprintk("%s: %d packets sent.\n", dev->name, priv->tx_count); - priv->tx_count = 0; - } + /* If tx not in batch mode or frame is full, send immediatelly */ + + if (!batching(priv, !tickssofar) || priv->tx_count == priv->sgl_max) { + dev->trans_start = jiffies; + i2o_post_message(iop, priv->m); + dprintk(KERN_DEBUG"%s: %d packets sent.\n", dev->name, priv->tx_count); + priv->tx_count = 0; } - - /* If HDMs TxMaxPktOut reached, stay busy (don't clean tbusy) */ - if (priv->tx_out >= priv->tx_max_out) + /* If DDMs TxMaxPktOut reached, stop queueing layer to send more */ + + if (atomic_read(&priv->tx_out) >= priv->tx_max_out) netif_stop_queue(dev); - + + spin_unlock_irq(&priv->tx_lock); return 0; } @@ -671,65 +1007,64 @@ static int i2o_lan_packet_send(struct sk_buff *skb, struct net_device *dev) */ static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; struct i2o_device *i2o_dev = priv->i2o_dev; struct i2o_controller *iop = i2o_dev->controller; u64 val64[16]; u64 supported_group[4] = { 0, 0, 0, 0 }; - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0100, -1, val64, - sizeof(val64)) < 0) - printk("%s: Unable to query LAN_HISTORICAL_STATS.\n",dev->name); + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0100, -1, val64, + sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_HISTORICAL_STATS.\n", dev->name); else { - dprintk("%s: LAN_HISTORICAL_STATS queried.\n",dev->name); - priv->stats.tx_packets = val64[0]; - priv->stats.tx_bytes = val64[1]; - priv->stats.rx_packets = val64[2]; - priv->stats.rx_bytes = val64[3]; - priv->stats.tx_errors = val64[4]; - priv->stats.rx_errors = val64[5]; + dprintk(KERN_DEBUG "%s: LAN_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.tx_packets = val64[0]; + priv->stats.tx_bytes = val64[1]; + priv->stats.rx_packets = val64[2]; + priv->stats.rx_bytes = val64[3]; + priv->stats.tx_errors = val64[4]; + priv->stats.rx_errors = val64[5]; priv->stats.rx_dropped = val64[6]; } - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0180, -1, - &supported_group, sizeof(supported_group)) < 0) - printk("%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n",dev->name); + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0180, -1, + &supported_group, sizeof(supported_group)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_OPTIONAL_HISTORICAL_STATS.\n", dev->name); if (supported_group[2]) { - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0183, -1, - val64, sizeof(val64)) < 0) - printk("%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n",dev->name); + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0183, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_RX_HISTORICAL_STATS.\n", dev->name); else { - dprintk("%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n",dev->name); - priv->stats.multicast = val64[4]; + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_RX_HISTORICAL_STATS queried.\n", dev->name); + priv->stats.multicast = val64[4]; priv->stats.rx_length_errors = val64[10]; priv->stats.rx_crc_errors = val64[0]; } } - if (i2o_dev->lct_data->sub_class == I2O_LAN_ETHERNET) { - u64 supported_stats = 0; - - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0200, -1, - val64, sizeof(val64)) < 0) - printk("%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n",dev->name); + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) { + u64 supported_stats = 0; + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0200, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_3_HISTORICAL_STATS.\n", dev->name); else { - dprintk("%s: LAN_802_3_HISTORICAL_STATS queried.\n",dev->name); + dprintk(KERN_DEBUG "%s: LAN_802_3_HISTORICAL_STATS queried.\n", dev->name); priv->stats.transmit_collision = val64[1] + val64[2]; - priv->stats.rx_frame_errors = val64[0]; + priv->stats.rx_frame_errors = val64[0]; priv->stats.tx_carrier_errors = val64[6]; } - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0280, -1, - &supported_stats, sizeof(supported_stats)) < 0) - printk("%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0280, -1, + &supported_stats, sizeof(supported_stats)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_SUPPORTED_802_3_HISTORICAL_STATS.\n", dev->name); - if (supported_stats != 0) { - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0281, -1, - val64, sizeof(val64)) < 0) - printk("%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n",dev->name); + if (supported_stats != 0) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0281, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_OPTIONAL_802_3_HISTORICAL_STATS.\n", dev->name); else { - dprintk("%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n",dev->name); + dprintk(KERN_DEBUG "%s: LAN_OPTIONAL_802_3_HISTORICAL_STATS queried.\n", dev->name); if (supported_stats & 0x1) priv->stats.rx_over_errors = val64[0]; if (supported_stats & 0x4) @@ -739,14 +1074,14 @@ static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) } #ifdef CONFIG_TR - if (i2o_dev->lct_data->sub_class == I2O_LAN_TR) { - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0300, -1, - val64, sizeof(val64)) < 0) - printk("%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n",dev->name); + if (i2o_dev->lct_data.sub_class == I2O_LAN_TR) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0300, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_802_5_HISTORICAL_STATS.\n", dev->name); else { struct tr_statistics *stats = - (struct tr_statistics *)&priv->stats; - dprintk("%s: LAN_802_5_HISTORICAL_STATS queried.\n",dev->name); + (struct tr_statistics *)&priv->stats; + dprintk(KERN_DEBUG "%s: LAN_802_5_HISTORICAL_STATS queried.\n", dev->name); stats->line_errors = val64[0]; stats->internal_errors = val64[7]; @@ -764,25 +1099,29 @@ static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) #endif #ifdef CONFIG_FDDI - if (i2o_dev->lct_data->sub_class == I2O_LAN_FDDI) { - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0400, -1, - val64, sizeof(val64)) < 0) - printk("%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n",dev->name); + if (i2o_dev->lct_data.sub_class == I2O_LAN_FDDI) { + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0400, -1, + val64, sizeof(val64)) < 0) + printk(KERN_INFO "%s: Unable to query LAN_FDDI_HISTORICAL_STATS.\n", dev->name); else { - dprintk("%s: LAN_FDDI_HISTORICAL_STATS queried.\n",dev->name); + dprintk(KERN_DEBUG "%s: LAN_FDDI_HISTORICAL_STATS queried.\n", dev->name); priv->stats.smt_cf_state = val64[0]; memcpy(priv->stats.mac_upstream_nbr, &val64[1], FDDI_K_ALEN); - memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); + memcpy(priv->stats.mac_downstream_nbr, &val64[2], FDDI_K_ALEN); priv->stats.mac_error_cts = val64[3]; priv->stats.mac_lost_cts = val64[4]; priv->stats.mac_rmt_state = val64[5]; memcpy(priv->stats.port_lct_fail_cts, &val64[6], 8); - memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); + memcpy(priv->stats.port_lem_reject_cts, &val64[7], 8); memcpy(priv->stats.port_lem_cts, &val64[8], 8); memcpy(priv->stats.port_pcm_state, &val64[9], 8); } /* FDDI optional stats not yet defined */ - } + } +#endif + +#ifdef CONFIG_NET_FC + /* Fibre Channel Statistics not yet defined in 1.53 nor 2.0 */ #endif return (struct net_device_stats *)&priv->stats; @@ -795,70 +1134,79 @@ static struct net_device_stats *i2o_lan_get_stats(struct net_device *dev) static void i2o_lan_set_mc_list(struct net_device *dev) { - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; - struct i2o_controller *iop = i2o_dev->controller; - u32 filter_mask; - u32 max_size_mc_table; - u32 mc_addr_group[64]; - - if (i2o_query_scalar(iop, i2o_dev->lct_data->tid, 0x0001, -1, - &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { - printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); - return; - } + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_controller *iop = i2o_dev->controller; + u32 filter_mask; + u32 max_size_mc_table; + u32 mc_addr_group[64]; - max_size_mc_table = mc_addr_group[8]; +// This isn't safe yet. Needs to be async. +return; - if (dev->flags & IFF_PROMISC) { - filter_mask = 0x00000002; - dprintk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); - } +// read_lock_bh(&dev_mc_lock); +// spin_lock(&dev->xmit_lock); +// dev->xmit_lock_owner = smp_processor_id(); - else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > max_size_mc_table) { - filter_mask = 0x00000004; - dprintk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); - } + if (i2o_query_scalar(iop, i2o_dev->lct_data.tid, 0x0001, -1, + &mc_addr_group, sizeof(mc_addr_group)) < 0 ) { + printk(KERN_WARNING "%s: Unable to query LAN_MAC_ADDRESS group.\n", dev->name); + return; + } - else if (dev->mc_count) { - struct dev_mc_list *mc; + max_size_mc_table = mc_addr_group[8]; + + if (dev->flags & IFF_PROMISC) { + filter_mask = 0x00000002; + printk(KERN_INFO "%s: Enabling promiscuous mode...\n", dev->name); + } else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count > max_size_mc_table) { + filter_mask = 0x00000004; + printk(KERN_INFO "%s: Enabling all multicast mode...\n", dev->name); + } else if (dev->mc_count) { + struct dev_mc_list *mc; u8 mc_table[2 + 8 * dev->mc_count]; // RowCount, Addresses u64 *work64 = (u64 *)(mc_table + 2); - filter_mask = 0x00000000; - dprintk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); + filter_mask = 0x00000000; + printk(KERN_INFO "%s: Enabling multicast mode...\n", dev->name); - /* Fill multicast addr table */ + /* Fill multicast addr table */ memset(mc_table, 0, sizeof(mc_table)); - memcpy(mc_table, &dev->mc_count, 2); - for (mc = dev->mc_list; mc ; mc = mc->next, work64++ ) - memcpy(work64, mc->dmi_addr, mc->dmi_addrlen); - - /* Clear old mc table, copy new table to */ + memcpy(mc_table, &dev->mc_count, 2); + for (mc = dev->mc_list; mc ; mc = mc->next, work64++ ) + memcpy(work64, mc->dmi_addr, mc->dmi_addrlen); - if (i2o_clear_table(iop, i2o_dev->lct_data->tid, 0x0002) < 0) - printk("%s: Unable to clear LAN_MULTICAST_MAC_ADDRESS table.\n",dev->name); + /* Clear old mc table, copy new table to */ - if ((i2o_row_add_table(iop, i2o_dev->lct_data->tid, 0x0002, -1, - mc_table, sizeof(mc_table))) < 0) - printk("%s: Unable to set LAN_MULTICAST_MAC_ADDRESS table.\n",dev->name); - } + if (i2o_clear_table(iop, i2o_dev->lct_data.tid, 0x0002) < 0) + printk(KERN_INFO "%s: Unable to clear LAN_MULTICAST_MAC_ADDRESS table.\n", dev->name); - else { - filter_mask = 0x00000300; // Broadcast, Multicast disabled - printk(KERN_INFO "%s: Enabling unicast mode...\n",dev->name); - } + if ((i2o_row_add_table(iop, i2o_dev->lct_data.tid, 0x0002, -1, + mc_table, sizeof(mc_table))) < 0) + printk(KERN_INFO "%s: Unable to set LAN_MULTICAST_MAC_ADDRESS table.\n", dev->name); + } else { + filter_mask = 0x00000300; // Broadcast, Multicast disabled + printk(KERN_INFO "%s: Enabling unicast mode...\n", dev->name); + } /* Finally copy new FilterMask to */ - if (i2o_set_scalar(iop, i2o_dev->lct_data->tid, 0x0001, 3, - &filter_mask, sizeof(filter_mask)) <0) - printk(KERN_WARNING "%s: Unable to set MAC FilterMask.\n",dev->name); + if (i2o_set_scalar(iop, i2o_dev->lct_data.tid, 0x0001, 3, + &filter_mask, sizeof(filter_mask)) <0) + printk(KERN_WARNING "%s: Unable to set MAC FilterMask.\n", dev->name); + + dev->xmit_lock_owner = -1; + spin_unlock(&dev->xmit_lock); +// read_unlock_bh(&dev_mc_lock); - return; + return; } +static struct tq_struct i2o_lan_set_mc_list_task = { + 0, 0, (void (*)(void *))i2o_lan_set_mc_list, (void *) 0 +}; + /* * i2o_lan_set_multicast_list(): * Queue routine i2o_lan_set_mc_list() to be called later. @@ -867,17 +1215,12 @@ static void i2o_lan_set_mc_list(struct net_device *dev) static void i2o_lan_set_multicast_list(struct net_device *dev) { - struct tq_struct *task; - - task = (struct tq_struct *)kmalloc(sizeof(struct tq_struct), GFP_KERNEL); - if (task == NULL) - return; - - task->next = NULL; - task->sync = 0; - task->routine = (void *)i2o_lan_set_mc_list; - task->data = (void *)dev; - queue_task(task, &tq_scheduler); + if (!in_interrupt()) { + i2o_lan_set_mc_list_task.data = (void *)dev; + queue_task(&i2o_lan_set_mc_list_task, &tq_scheduler); + } else { + i2o_lan_set_mc_list(dev); + } } /* @@ -885,13 +1228,24 @@ static void i2o_lan_set_multicast_list(struct net_device *dev) */ static int i2o_lan_change_mtu(struct net_device *dev, int new_mtu) { - if ((new_mtu < 68) || (new_mtu > 9000)) + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; + u32 max_pkt_size; + + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0000, 6, &max_pkt_size, 4) < 0) + return -EFAULT; + + if (new_mtu < 68 || max_pkt_size < new_mtu) return -EINVAL; - + dev->mtu = new_mtu; return 0; } +/* Functions to initialize I2O LAN OSM: +======================================*/ + /* * i2o_lan_register_device(): Register LAN class device to kernel. */ @@ -904,9 +1258,9 @@ struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) unsigned short (*type_trans)(struct sk_buff *, struct net_device *); void (*unregister_dev)(struct net_device *dev); - switch (i2o_dev->lct_data->sub_class) { + switch (i2o_dev->lct_data.sub_class) { case I2O_LAN_ETHERNET: - dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); + dev = init_etherdev(NULL, sizeof(struct i2o_lan_local)); if (dev == NULL) return NULL; type_trans = eth_type_trans; @@ -916,6 +1270,7 @@ struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) #ifdef CONFIG_ANYLAN case I2O_LAN_100VG: printk(KERN_ERR "i2o_lan: 100base VG not yet supported.\n"); + return NULL; break; #endif @@ -925,7 +1280,7 @@ struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) if (dev==NULL) return NULL; type_trans = tr_type_trans; - unregister_dev = unregister_trdev; + unregister_dev = unregister_trdev; break; #endif @@ -933,92 +1288,122 @@ struct net_device *i2o_lan_register_device(struct i2o_device *i2o_dev) case I2O_LAN_FDDI: { int size = sizeof(struct net_device) + sizeof(struct i2o_lan_local) - + sizeof("fddi%d "); + + sizeof("fddi%d "); - dev = (struct net_device *) kmalloc(size, GFP_KERNEL); - memset((char *)dev, 0, size); - dev->priv = (void *)(dev + 1); - dev->name = (char *)(dev + 1) + sizeof(struct i2o_lan_local); + dev = (struct net_device *) kmalloc(size, GFP_KERNEL); + if (dev == NULL) + return NULL; + memset((char *)dev, 0, size); + dev->priv = (void *)(dev + 1); + dev->name = (char *)(dev + 1) + sizeof(struct i2o_lan_local); - if (dev_alloc_name(dev,"fddi%d") < 0) { + if (dev_alloc_name(dev, "fddi%d") < 0) { printk(KERN_WARNING "i2o_lan: Too many FDDI devices.\n"); kfree(dev); return NULL; } type_trans = fddi_type_trans; unregister_dev = (void *)unregister_netdevice; - + fddi_setup(dev); register_netdev(dev); - } + } break; #endif -#ifdef CONFIG_FIBRE_CHANNEL +#ifdef CONFIG_NET_FC case I2O_LAN_FIBRE_CHANNEL: - printk(KERN_INFO "i2o_lan: Fibre Channel not yet supported.\n"); - break; + dev = init_fcdev(NULL, sizeof(struct i2o_lan_local)); + if (dev == NULL) + return NULL; + type_trans = NULL; +/* FIXME: Move fc_type_trans() from drivers/net/fc/iph5526.c to net/802/fc.c + * and export it in include/linux/fcdevice.h + * type_trans = fc_type_trans; + */ + unregister_dev = (void *)unregister_fcdev; + break; #endif case I2O_LAN_UNKNOWN: default: - printk(KERN_ERR "i2o_lan: LAN type 0x%08X not supported.\n", - i2o_dev->lct_data->sub_class); + printk(KERN_ERR "i2o_lan: LAN type 0x%04x not supported.\n", + i2o_dev->lct_data.sub_class); return NULL; } priv = (struct i2o_lan_local *)dev->priv; priv->i2o_dev = i2o_dev; priv->type_trans = type_trans; - priv->bucket_count = 0; priv->sgl_max = (i2o_dev->controller->inbound_size - 16) / 12; + atomic_set(&priv->buckets_out, 0); + + /* Set default values for user configurable parameters */ + /* Private values are changed via /proc file system */ + + priv->max_buckets_out = max_buckets_out; + priv->bucket_thresh = bucket_thresh; + priv->rx_copybreak = rx_copybreak; + priv->tx_batch_mode = tx_batch_mode; + priv->i2o_event_mask = i2o_event_mask; + + priv->tx_lock = SPIN_LOCK_UNLOCKED; + priv->fbl_lock = SPIN_LOCK_UNLOCKED; unit++; i2o_landevs[unit] = dev; priv->unit = unit; - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data->tid, + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, 0x0001, 0, &hw_addr, sizeof(hw_addr)) < 0) { - printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); + printk(KERN_ERR "%s: Unable to query hardware address.\n", dev->name); unit--; unregister_dev(dev); kfree(dev); - return NULL; + return NULL; } - dprintk("%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", - dev->name,hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], - hw_addr[4], hw_addr[5]); + dprintk(KERN_DEBUG "%s: hwaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + dev->name, hw_addr[0], hw_addr[1], hw_addr[2], hw_addr[3], + hw_addr[4], hw_addr[5]); dev->addr_len = 6; memcpy(dev->dev_addr, hw_addr, 6); - if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data->tid, - 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) - { - printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); - unit--; - unregister_dev(dev); - kfree(dev); - return NULL; + if (i2o_query_scalar(i2o_dev->controller, i2o_dev->lct_data.tid, + 0x0007, 2, &tx_max_out, sizeof(tx_max_out)) < 0) { + printk(KERN_ERR "%s: Unable to query max TX queue.\n", dev->name); + unit--; + unregister_dev(dev); + kfree(dev); + return NULL; } - dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); - priv->tx_max_out = tx_max_out; - priv->tx_out = 0; - priv->tx_count = 0; - priv->lock = SPIN_LOCK_UNLOCKED; + dprintk(KERN_INFO "%s: Max TX Outstanding = %d.\n", dev->name, tx_max_out); + priv->tx_max_out = tx_max_out; + atomic_set(&priv->tx_out, 0); + priv->tx_count = 0; priv->i2o_batch_send_task.next = NULL; priv->i2o_batch_send_task.sync = 0; priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send; priv->i2o_batch_send_task.data = (void *)dev; - dev->open = i2o_lan_open; - dev->stop = i2o_lan_close; - dev->hard_start_xmit = i2o_lan_packet_send; - dev->get_stats = i2o_lan_get_stats; + dev->open = i2o_lan_open; + dev->stop = i2o_lan_close; + dev->get_stats = i2o_lan_get_stats; dev->set_multicast_list = i2o_lan_set_multicast_list; - dev->change_mtu = i2o_lan_change_mtu; + dev->tx_timeout = i2o_lan_tx_timeout; + dev->watchdog_timeo = I2O_LAN_TX_TIMEOUT; + +#ifdef CONFIG_NET_FC + if (i2o_dev->lct_data.sub_class == I2O_LAN_FIBRE_CHANNEL) + dev->hard_start_xmit = i2o_lan_sdu_send; + else +#endif + dev->hard_start_xmit = i2o_lan_packet_send; + + if (i2o_dev->lct_data.sub_class == I2O_LAN_ETHERNET) + dev->change_mtu = i2o_lan_change_mtu; return dev; } @@ -1032,19 +1417,35 @@ int __init i2o_lan_init(void) struct net_device *dev; int i; - printk(KERN_INFO "Linux I2O LAN OSM (c) 1999 University of Helsinki.\n"); + printk(KERN_INFO "I2O LAN OSM (c) 1999 University of Helsinki.\n"); + + /* Module params used as global defaults for private values */ + + if (max_buckets_out > I2O_LAN_MAX_BUCKETS_OUT) + max_buckets_out = I2O_LAN_MAX_BUCKETS_OUT; + if (bucket_thresh > max_buckets_out) + bucket_thresh = max_buckets_out; + + /* Install handlers for incoming replies */ + + if (i2o_install_handler(&i2o_lan_send_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_send_context = i2o_lan_send_handler.context; - if (bucketpost > I2O_BUCKET_COUNT) - bucketpost = I2O_BUCKET_COUNT; - if (bucketthresh > bucketpost) - bucketthresh = bucketpost; + if (i2o_install_handler(&i2o_lan_receive_handler) < 0) { + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); + return -EINVAL; + } + lan_receive_context = i2o_lan_receive_handler.context; if (i2o_install_handler(&i2o_lan_handler) < 0) { printk(KERN_ERR "i2o_lan: Unable to register I2O LAN OSM.\n"); return -EINVAL; } lan_context = i2o_lan_handler.context; - + for(i=0; i <= MAX_LAN_CARDS; i++) i2o_landevs[i] = NULL; @@ -1052,15 +1453,16 @@ int __init i2o_lan_init(void) struct i2o_controller *iop = i2o_find_controller(i); struct i2o_device *i2o_dev; - if (iop==NULL) continue; + if (iop==NULL) + continue; for (i2o_dev=iop->devices;i2o_dev != NULL;i2o_dev=i2o_dev->next) { - if (i2o_dev->lct_data->class_id != I2O_CLASS_LAN) + if (i2o_dev->lct_data.class_id != I2O_CLASS_LAN) continue; /* Make sure device not already claimed by an ISM */ - if (i2o_dev->lct_data->user_tid != 0xFFF) + if (i2o_dev->lct_data.user_tid != 0xFFF) continue; if (unit == MAX_LAN_CARDS) { @@ -1071,14 +1473,16 @@ int __init i2o_lan_init(void) dev = i2o_lan_register_device(i2o_dev); if (dev == NULL) { - printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device.\n"); - continue; // try next one + printk(KERN_ERR "i2o_lan: Unable to register I2O LAN device 0x%04x.\n", + i2o_dev->lct_data.sub_class); + continue; } - printk(KERN_INFO "%s: I2O LAN device registered, tid = %d," - " subclass = 0x%08X, unit = %d.\n", - dev->name, i2o_dev->lct_data->tid, i2o_dev->lct_data->sub_class, - ((struct i2o_lan_local *)dev->priv)->unit); + printk(KERN_INFO "%s: I2O LAN device registered, " + "subclass = 0x%04x, unit = %d, tid = %d.\n", + dev->name, i2o_dev->lct_data.sub_class, + ((struct i2o_lan_local *)dev->priv)->unit, + i2o_dev->lct_data.tid); } i2o_unlock_controller(iop); @@ -1097,45 +1501,55 @@ void cleanup_module(void) for (i = 0; i <= unit; i++) { struct net_device *dev = i2o_landevs[i]; - struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; - struct i2o_device *i2o_dev = priv->i2o_dev; + struct i2o_lan_local *priv = (struct i2o_lan_local *)dev->priv; + struct i2o_device *i2o_dev = priv->i2o_dev; - switch (i2o_dev->lct_data->sub_class) { + switch (i2o_dev->lct_data.sub_class) { case I2O_LAN_ETHERNET: unregister_netdev(dev); - kfree(dev); break; #ifdef CONFIG_FDDI case I2O_LAN_FDDI: unregister_netdevice(dev); - kfree(dev); break; #endif #ifdef CONFIG_TR case I2O_LAN_TR: unregister_trdev(dev); - kfree(dev); + break; +#endif +#ifdef CONFIG_NET_FC + case I2O_LAN_FIBRE_CHANNEL: + unregister_fcdev(dev); break; #endif default: - printk(KERN_WARNING "i2o_lan: Spurious I2O LAN subclass 0x%08X.\n", - i2o_dev->lct_data->sub_class); + printk(KERN_WARNING "%s: Spurious I2O LAN subclass 0x%08x.\n", + dev->name, i2o_dev->lct_data.sub_class); } dprintk(KERN_INFO "%s: I2O LAN device unregistered.\n", dev->name); + kfree(dev); } i2o_remove_handler(&i2o_lan_handler); + i2o_remove_handler(&i2o_lan_send_handler); + i2o_remove_handler(&i2o_lan_receive_handler); } EXPORT_NO_SYMBOLS; -MODULE_AUTHOR("Univ of Helsinki, CS Department"); +MODULE_AUTHOR("University of Helsinki, Department of Computer Science"); MODULE_DESCRIPTION("I2O Lan OSM"); -MODULE_PARM(bucketpost, "i"); // Total number of buckets to post -MODULE_PARM(bucketthresh, "i"); // Bucket post threshold -MODULE_PARM(rx_copybreak, "i"); +MODULE_PARM(max_buckets_out, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(max_buckets_out, "Total number of buckets to post (1-)"); +MODULE_PARM(bucket_thresh, "1-" __MODULE_STRING(I2O_LAN_MAX_BUCKETS_OUT) "i"); +MODULE_PARM_DESC(bucket_thresh, "Bucket post threshold (1-)"); +MODULE_PARM(rx_copybreak, "1-" "i"); +MODULE_PARM_DESC(rx_copybreak, "Copy breakpoint for copy only small frames (1-)"); +MODULE_PARM(tx_batch_mode, "0-1" "i"); +MODULE_PARM_DESC(tx_batch_mode, "0=Use immediate mode send, 1=Use batch mode send"); #endif diff --git a/drivers/i2o/i2o_lan.h b/drivers/i2o/i2o_lan.h index d4e5aa44bd4d..72a87af2590a 100644 --- a/drivers/i2o/i2o_lan.h +++ b/drivers/i2o/i2o_lan.h @@ -1,24 +1,29 @@ /* - * i2o_lan.h LAN Class specific definitions + * i2o_lan.h I2O LAN Class definitions * - * I2O LAN CLASS OSM Prototyping, May 17th 1999 + * I2O LAN CLASS OSM April 3rd 2000 * - * (C) Copyright 1999 University of Helsinki, - * Department of Computer Science + * (C) Copyright 1999, 2000 University of Helsinki, + * Department of Computer Science * * This code is still under development / test. * - * Author: Auvo Häkkinen - * Juha Sievänen + * Author: Auvo Häkkinen + * Juha Sievänen + * Taneli Vähäkangas */ #ifndef _I2O_LAN_H #define _I2O_LAN_H -/* Tunable parameters first */ +/* Default values for tunable parameters first */ -#define I2O_BUCKET_COUNT 256 -#define I2O_BUCKET_THRESH 18 /* 9 buckets in one message */ +#define I2O_LAN_MAX_BUCKETS_OUT 256 +#define I2O_LAN_BUCKET_THRESH 18 /* 9 buckets in one message */ +#define I2O_LAN_RX_COPYBREAK 200 +#define I2O_LAN_TX_TIMEOUT (1*HZ) +#define I2O_LAN_TX_BATCH_MODE 1 /* 1=on, 0=off */ +#define I2O_LAN_EVENT_MASK 0 /* 0=None, 0xFFC00002=All */ /* LAN types */ #define I2O_LAN_ETHERNET 0x0030 @@ -114,5 +119,38 @@ struct i2o_bucket_descriptor { #define I2O_LAN_EVT_LINK_DOWN 0x01 #define I2O_LAN_EVT_LINK_UP 0x02 #define I2O_LAN_EVT_MEDIA_CHANGE 0x04 - + +#include +#include + +struct i2o_lan_local { + u8 unit; + struct i2o_device *i2o_dev; + struct fddi_statistics stats; /* see also struct net_device_stats */ + unsigned short (*type_trans)(struct sk_buff *, struct net_device *); + atomic_t buckets_out; /* nbr of unused buckets on DDM */ + atomic_t tx_out; /* outstanding TXes */ + u8 tx_count; /* packets in one TX message frame */ + u16 tx_max_out; /* DDM's Tx queue len */ + u8 sgl_max; /* max SGLs in one message frame */ + u32 m; /* IOP address of msg frame */ + + struct tq_struct i2o_batch_send_task; + int send_active; + struct sk_buff **i2o_fbl; /* Free bucket list (to reuse skbs) */ + int i2o_fbl_tail; + spinlock_t fbl_lock; + + spinlock_t tx_lock; + + /* LAN OSM configurable parameters are here: */ + + u16 max_buckets_out; /* max nbr of buckets to send to DDM */ + u16 bucket_thresh; /* send more when this many used */ + u16 rx_copybreak; + + u8 tx_batch_mode; /* Set when using batch mode sends */ + u32 i2o_event_mask; /* To turn on interesting event flags */ +}; + #endif /* _I2O_LAN_H */ diff --git a/drivers/i2o/i2o_pci.c b/drivers/i2o/i2o_pci.c index c57bd390d3b1..16b8aaedc2b8 100644 --- a/drivers/i2o/i2o_pci.c +++ b/drivers/i2o/i2o_pci.c @@ -53,8 +53,10 @@ static void i2o_pci_dispose(struct i2o_controller *c) iounmap(((u8 *)c->post_port)-0x40); #ifdef CONFIG_MTRR - if(c->bus.pci.mtrr_reg > 0) - mtrr_del(c->bus.pci.mtrr_reg, 0, 0); + if(c->bus.pci.mtrr_reg0 > 0) + mtrr_del(c->bus.pci.mtrr_reg0, 0, 0); + if(c->bus.pci.mtrr_reg1 > 0) + mtrr_del(c->bus.pci.mtrr_reg1, 0, 0); #endif } @@ -176,8 +178,22 @@ int __init i2o_pci_install(struct pci_dev *dev) * Enable Write Combining MTRR for IOP's memory region */ #ifdef CONFIG_MTRR - c->bus.pci.mtrr_reg = - mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); + c->bus.pci.mtrr_reg0 = + mtrr_add(c->mem_phys, size, MTRR_TYPE_WRCOMB, 1); +/* +* If it is an INTEL i960 I/O processor then set the first 64K to Uncacheable +* since the region contains the Messaging unit which shouldn't be cached. +*/ + c->bus.pci.mtrr_reg1 = -1; + if(dev->vendor == PCI_VENDOR_ID_INTEL) + { + printk(KERN_INFO "i2o_pci: MTRR workaround for Intel i960 processor\n"); + c->bus.pci.mtrr_reg1 = + mtrr_add(c->mem_phys, 65536, MTRR_TYPE_UNCACHABLE, 1); + if(c->bus.pci.mtrr_reg1< 0) + printk(KERN_INFO "i2o_pci: Error in setting MTRR_TYPE_UNCACHABLE\n"); + } + #endif I2O_IRQ_WRITE32(c,0xFFFFFFFF); @@ -230,7 +246,8 @@ int __init i2o_pci_scan(void) printk(KERN_INFO "i2o: Checking for PCI I2O controllers...\n"); - pci_for_each_dev(dev) { + pci_for_each_dev(dev) + { if((dev->class>>8)!=PCI_CLASS_INTELLIGENT_I2O) continue; if((dev->class&0xFF)>1) diff --git a/drivers/i2o/i2o_proc.c b/drivers/i2o/i2o_proc.c index ed89973eb288..1032729e85ee 100644 --- a/drivers/i2o/i2o_proc.c +++ b/drivers/i2o/i2o_proc.c @@ -29,7 +29,7 @@ /* * TODO List * - * - Add support for any version 2.0 spec changes once 2.0 IRTOS + * - Add support for any version 2.0 spec changes once 2.0 IRTOS is * is available to test with * - Clean up code to use official structure definitions */ @@ -64,6 +64,8 @@ typedef struct _i2o_proc_entry_t write_proc_t *write_proc; /* write func */ } i2o_proc_entry; +// #define DRIVERDEBUG + static int i2o_proc_read_lct(char *, char **, off_t, int, int *, void *); static int i2o_proc_read_hrt(char *, char **, off_t, int, int *, void *); static int i2o_proc_read_status(char *, char **, off_t, int, int *, void *); @@ -97,8 +99,12 @@ static int i2o_proc_add_controller(struct i2o_controller *, struct proc_dir_entry * ); static void i2o_proc_remove_controller(struct i2o_controller *, struct proc_dir_entry * ); +static void i2o_proc_add_device(struct i2o_device *, struct proc_dir_entry *); +static void i2o_proc_remove_device(struct i2o_device *); static int create_i2o_procfs(void); static int destroy_i2o_procfs(void); +static void i2o_proc_new_dev(struct i2o_controller *, struct i2o_device *); +static void i2o_proc_dev_del(struct i2o_controller *, struct i2o_device *); static int i2o_proc_read_lan_dev_info(char *, char **, off_t, int, int *, void *); @@ -129,6 +135,20 @@ static int i2o_proc_read_lan_fddi_stats(char *, char **, off_t, int, int *, static struct proc_dir_entry *i2o_proc_dir_root; +/* + * I2O OSM descriptor + */ +static struct i2o_handler i2o_proc_handler = +{ + NULL, + i2o_proc_new_dev, + i2o_proc_dev_del, + NULL, + "I2O procfs Layer", + 0, + 0xffffffff // All classes +}; + /* * IOP specific entries...write field just in case someone * ever wants one. @@ -240,6 +260,7 @@ static i2o_proc_entry lan_fddi_entries[] = {NULL, 0, NULL, NULL} }; + static char *chtostr(u8 *chars, int n) { char tmp[256]; @@ -412,6 +433,9 @@ int i2o_proc_read_lct(char *buf, char **start, off_t offset, int len, if(lct->boot_tid) len += sprintf(buf+len, "Boot Device @ ID %d\n", lct->boot_tid); + len += + sprintf(buf+len, "Current Change Indicator: %#10x\n", lct->change_ind); + for(i = 0; i < entries; i++) { len += sprintf(buf+len, "Entry %d\n", i); @@ -1024,7 +1048,7 @@ int i2o_proc_read_groups(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, 0xF000, -1, NULL, 0, + d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, &result, sizeof(result)); if (token < 0) { @@ -1088,7 +1112,7 @@ int i2o_proc_read_phys_device(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0xF001, -1, NULL, 0, &result, sizeof(result)); @@ -1137,7 +1161,7 @@ int i2o_proc_read_claimed(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0xF002, -1, NULL, 0, &result, sizeof(result)); @@ -1196,7 +1220,7 @@ int i2o_proc_read_users(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0xF003, -1, NULL, 0, &result, sizeof(result)); @@ -1255,7 +1279,7 @@ int i2o_proc_read_priv_msgs(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0xF000, -1, NULL, 0, &result, sizeof(result)); @@ -1310,7 +1334,7 @@ int i2o_proc_read_authorized_users(char *buf, char **start, off_t offset, int le len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0xF006, -1, NULL, 0, &result, sizeof(result)); @@ -1352,7 +1376,7 @@ int i2o_proc_read_dev_identity(char *buf, char **start, off_t offset, int len, len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0xF100, -1, &work32, sizeof(work32)); @@ -1421,7 +1445,7 @@ int i2o_proc_read_ddm_identity(char *buf, char **start, off_t offset, int len, len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0xF101, -1, &result, sizeof(result)); @@ -1464,7 +1488,7 @@ int i2o_proc_read_uinfo(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0xF102, -1, &result, sizeof(result)); @@ -1497,7 +1521,7 @@ int i2o_proc_read_sgl_limits(char *buf, char **start, off_t offset, int len, len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0xF103, -1, &work32, sizeof(work32)); @@ -1573,7 +1597,7 @@ int i2o_proc_read_sensors(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0xF200, -1, &result, sizeof(result)); @@ -2020,7 +2044,7 @@ int i2o_proc_read_lan_dev_info(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0000, -1, &work32, 56*4); if (token < 0) { len += i2o_report_query_status(buf+len, token, "0x0000 LAN Device Info"); @@ -2142,7 +2166,7 @@ int i2o_proc_read_lan_mac_addr(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0001, -1, &work32, 48*4); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x0001 LAN MAC Address"); @@ -2240,7 +2264,7 @@ int i2o_proc_read_lan_mcast_addr(char *buf, char **start, off_t offset, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, 0x0002, -1, + d->controller, d->lct_data.tid, 0x0002, -1, NULL, 0, &result, sizeof(result)); if (token < 0) { @@ -2275,7 +2299,7 @@ int i2o_proc_read_lan_batch_control(char *buf, char **start, off_t offset, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0003, -1, &work32, 9*4); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x0003 LAN Batch Control"); @@ -2316,7 +2340,7 @@ int i2o_proc_read_lan_operation(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0004, -1, &work32, 20); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x0004 LAN Operation"); @@ -2389,7 +2413,7 @@ int i2o_proc_read_lan_media_operation(char *buf, char **start, off_t offset, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0005, -1, &result, sizeof(result)); if (token < 0) { len += i2o_report_query_status(buf+len, token, "0x0005 LAN Media Operation"); @@ -2471,7 +2495,7 @@ int i2o_proc_read_lan_alt_addr(char *buf, char **start, off_t offset, int len, len = 0; token = i2o_query_table(I2O_PARAMS_TABLE_GET, - d->controller, d->lct_data->tid, + d->controller, d->lct_data.tid, 0x0006, -1, NULL, 0, &result, sizeof(result)); if (token < 0) { @@ -2506,7 +2530,7 @@ int i2o_proc_read_lan_tx_info(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0007, -1, &work32, 8*4); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x0007 LAN Transmit Info"); @@ -2558,7 +2582,7 @@ int i2o_proc_read_lan_rx_info(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0008, -1, &work32, 8*4); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x0008 LAN Receive Info"); @@ -2675,7 +2699,7 @@ int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0100, -1, &stats, sizeof(stats)); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x100 LAN Statistics"); @@ -2705,7 +2729,7 @@ int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, /* Optional statistics follows */ /* Get 0x0180 to see which optional groups/fields are supported */ - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0180, -1, &supp_groups, sizeof(supp_groups)); if (token < 0) { @@ -2716,7 +2740,7 @@ int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, if (supp_groups[1]) /* 0x0182 */ { - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0182, -1, &tx_stats, sizeof(tx_stats)); if (token < 0) { @@ -2749,7 +2773,7 @@ int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, if (supp_groups[2]) /* 0x0183 */ { - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0183, -1, &rx_stats, sizeof(rx_stats)); if (token < 0) { len += i2o_report_query_status(buf+len, token,"0x183 LAN Optional Rx Historical Stats"); @@ -2785,7 +2809,7 @@ int i2o_proc_read_lan_hist_stats(char *buf, char **start, off_t offset, int len, if (supp_groups[3]) /* 0x0184 */ { - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0184, -1, &chksum_stats, sizeof(chksum_stats)); if (token < 0) { @@ -2864,7 +2888,7 @@ int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0200, -1, &stats, sizeof(stats)); if (token < 0) { @@ -2893,7 +2917,7 @@ int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, /* Optional Ethernet statistics follows */ /* Get 0x0280 to see which optional fields are supported */ - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0280, -1, &supp_fields, sizeof(supp_fields)); if (token < 0) { @@ -2904,7 +2928,7 @@ int i2o_proc_read_lan_eth_stats(char *buf, char **start, off_t offset, if (supp_fields) /* 0x0281 */ { - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0281, -1, &stats, sizeof(stats)); if (token < 0) { @@ -2959,7 +2983,7 @@ int i2o_proc_read_lan_tr_stats(char *buf, char **start, off_t offset, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0300, -1, &work64, sizeof(work64)); if (token < 0) { @@ -3053,7 +3077,7 @@ int i2o_proc_read_lan_fddi_stats(char *buf, char **start, off_t offset, spin_lock(&i2o_proc_lock); len = 0; - token = i2o_query_scalar(d->controller, d->lct_data->tid, + token = i2o_query_scalar(d->controller, d->lct_data.tid, 0x0400, -1, &work64, sizeof(work64)); if (token < 0) { @@ -3135,78 +3159,117 @@ static int i2o_proc_add_controller(struct i2o_controller *pctrl, for(dev = pctrl->devices; dev; dev = dev->next) { - sprintf(buff, "%0#5x", dev->lct_data->tid); + sprintf(buff, "%0#5x", dev->lct_data.tid); dir1 = proc_mkdir(buff, dir); dev->proc_entry = dir1; if(!dir1) printk(KERN_INFO "i2o_proc: Could not allocate proc dir\n"); - - i2o_proc_create_entries(dev, generic_dev_entries, dir1); - switch(dev->lct_data->class_id) - { + i2o_proc_add_device(dev, dir1); + } + + return 0; +} + +void i2o_proc_new_dev(struct i2o_controller *c, struct i2o_device *d) +{ + char buff[10]; + +#ifdef DRIVERDEBUG + printk(KERN_INFO "Adding new device to /proc/i2o/iop%d\n", c->unit); +#endif + sprintf(buff, "%0#5x", d->lct_data.tid); + + d->proc_entry = proc_mkdir(buff, c->proc_entry); + + if(!d->proc_entry) + { + printk(KERN_WARNING "i2o: Could not allocate procdir!\n"); + return; + } + + i2o_proc_add_device(d, d->proc_entry); +} + +void i2o_proc_add_device(struct i2o_device *dev, struct proc_dir_entry *dir) +{ + i2o_proc_create_entries(dev, generic_dev_entries, dir); + + /* Inform core that we want updates about this device's status */ + i2o_device_notify_on(dev, &i2o_proc_handler); + switch(dev->lct_data.class_id) + { case I2O_CLASS_SCSI_PERIPHERAL: case I2O_CLASS_RANDOM_BLOCK_STORAGE: - i2o_proc_create_entries(dev, rbs_dev_entries, dir1); + i2o_proc_create_entries(dev, rbs_dev_entries, dir); break; case I2O_CLASS_LAN: - i2o_proc_create_entries(dev, lan_entries, dir1); - switch(dev->lct_data->sub_class) + i2o_proc_create_entries(dev, lan_entries, dir); + switch(dev->lct_data.sub_class) { - case I2O_LAN_ETHERNET: - i2o_proc_create_entries(dev, lan_eth_entries, - dir1); - break; - case I2O_LAN_FDDI: - i2o_proc_create_entries(dev, lan_fddi_entries, - dir1); - break; - case I2O_LAN_TR: - i2o_proc_create_entries(dev, lan_tr_entries, - dir1); - break; - default: - break; + case I2O_LAN_ETHERNET: + i2o_proc_create_entries(dev, lan_eth_entries, dir); + break; + case I2O_LAN_FDDI: + i2o_proc_create_entries(dev, lan_fddi_entries, dir); + break; + case I2O_LAN_TR: + i2o_proc_create_entries(dev, lan_tr_entries, dir); + break; + default: + break; } break; default: break; - } } - - return 0; } static void i2o_proc_remove_controller(struct i2o_controller *pctrl, struct proc_dir_entry *parent) { char buff[10]; - char dev_id[10]; - struct proc_dir_entry *de; struct i2o_device *dev; /* Remove unused device entries */ for(dev=pctrl->devices; dev; dev=dev->next) + i2o_proc_remove_device(dev); + + if(!pctrl->proc_entry->count) { - de=dev->proc_entry; - sprintf(dev_id, "%0#5x", dev->lct_data->tid); + sprintf(buff, "iop%d", pctrl->unit); - /* Would it be safe to remove _files_ even if they are in use? */ - if((de) && (!de->count)) - { - i2o_proc_remove_entries(generic_dev_entries, de); + i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); - switch(dev->lct_data->class_id) - { + remove_proc_entry(buff, parent); + pctrl->proc_entry = NULL; + } +} + +void i2o_proc_remove_device(struct i2o_device *dev) +{ + struct proc_dir_entry *de=dev->proc_entry; + char dev_id[10]; + + sprintf(dev_id, "%0#5x", dev->lct_data.tid); + + i2o_device_notify_off(dev, &i2o_proc_handler); + /* Would it be safe to remove _files_ even if they are in use? */ + if((de) && (!de->count)) + { + i2o_proc_remove_entries(generic_dev_entries, de); + switch(dev->lct_data.class_id) + { case I2O_CLASS_SCSI_PERIPHERAL: case I2O_CLASS_RANDOM_BLOCK_STORAGE: i2o_proc_remove_entries(rbs_dev_entries, de); break; case I2O_CLASS_LAN: + { i2o_proc_remove_entries(lan_entries, de); - switch(dev->lct_data->sub_class) + switch(dev->lct_data.sub_class) { case I2O_LAN_ETHERNET: i2o_proc_remove_entries(lan_eth_entries, de); @@ -3219,19 +3282,19 @@ static void i2o_proc_remove_controller(struct i2o_controller *pctrl, break; } } - remove_proc_entry(dev_id, parent); + remove_proc_entry(dev_id, dev->controller->proc_entry); } } +} + +void i2o_proc_dev_del(struct i2o_controller *c, struct i2o_device *d) +{ +#ifdef DRIVERDEBUG + printk(KERN_INFO, "Deleting device %d from iop%d\n", + d->lct_data.tid, c->unit); +#endif - if(!pctrl->proc_entry->count) - { - sprintf(buff, "iop%d", pctrl->unit); - - i2o_proc_remove_entries(generic_iop_entries, pctrl->proc_entry); - - remove_proc_entry(buff, parent); - pctrl->proc_entry = NULL; - } + i2o_proc_remove_device(d); } static int create_i2o_procfs(void) @@ -3278,13 +3341,19 @@ static int destroy_i2o_procfs(void) return 0; } - + #ifdef MODULE #define i2o_proc_init init_module #endif int __init i2o_proc_init(void) { + if (i2o_install_handler(&i2o_proc_handler) < 0) + { + printk(KERN_ERR "i2o_proc: Unable to install PROC handler.\n"); + return 0; + } + if(create_i2o_procfs()) return -EBUSY; @@ -3293,12 +3362,12 @@ int __init i2o_proc_init(void) #ifdef MODULE - MODULE_AUTHOR("Deepak Saxena"); MODULE_DESCRIPTION("I2O procfs Handler"); void cleanup_module(void) { destroy_i2o_procfs(); + i2o_remove_handler(&i2o_proc_handler); } #endif diff --git a/drivers/i2o/i2o_scsi.c b/drivers/i2o/i2o_scsi.c index f74b300e510c..3c147cf9fe80 100644 --- a/drivers/i2o/i2o_scsi.c +++ b/drivers/i2o/i2o_scsi.c @@ -288,6 +288,9 @@ static void i2o_scsi_reply(struct i2o_handler *h, struct i2o_controller *c, stru struct i2o_handler i2o_scsi_handler= { i2o_scsi_reply, + NULL, + NULL, + NULL, "I2O SCSI OSM", 0, I2O_CLASS_SCSI_PERIPHERAL @@ -297,12 +300,12 @@ static int i2o_find_lun(struct i2o_controller *c, struct i2o_device *d, int *tar { u8 reply[8]; - if(i2o_query_scalar(c, d->lct_data->tid, 0, 3, reply, 4)<0) + if(i2o_query_scalar(c, d->lct_data.tid, 0, 3, reply, 4)<0) return -1; *target=reply[0]; - if(i2o_query_scalar(c, d->lct_data->tid, 0, 4, reply, 8)<0) + if(i2o_query_scalar(c, d->lct_data.tid, 0, 4, reply, 8)<0) return -1; *lun=reply[1]; @@ -319,7 +322,7 @@ void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_H int target; h->controller=c; - h->bus_task=d->lct_data->tid; + h->bus_task=d->lct_data.tid; for(target=0;target<16;target++) for(lun=0;lun<8;lun++) @@ -328,33 +331,33 @@ void i2o_scsi_init(struct i2o_controller *c, struct i2o_device *d, struct Scsi_H for(unit=c->devices;unit!=NULL;unit=unit->next) { dprintk(("Class %03X, parent %d, want %d.\n", - unit->lct_data->class_id, unit->lct_data->parent_tid, d->lct_data->tid)); + unit->lct_data.class_id, unit->lct_data.parent_tid, d->lct_data.tid)); /* Only look at scsi and fc devices */ - if ( (unit->lct_data->class_id != I2O_CLASS_SCSI_PERIPHERAL) - && (unit->lct_data->class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) + if ( (unit->lct_data.class_id != I2O_CLASS_SCSI_PERIPHERAL) + && (unit->lct_data.class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL) ) continue; /* On our bus ? */ - dprintk(("Found a disk (%d).\n", unit->lct_data->tid)); - if ((unit->lct_data->parent_tid == d->lct_data->tid) - || (unit->lct_data->parent_tid == d->lct_data->parent_tid) + dprintk(("Found a disk (%d).\n", unit->lct_data.tid)); + if ((unit->lct_data.parent_tid == d->lct_data.tid) + || (unit->lct_data.parent_tid == d->lct_data.parent_tid) ) { u16 limit; dprintk(("Its ours.\n")); if(i2o_find_lun(c, unit, &target, &lun)==-1) { - printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data->tid); + printk(KERN_ERR "i2o_scsi: Unable to get lun for tid %d.\n", unit->lct_data.tid); continue; } dprintk(("Found disk %d %d.\n", target, lun)); - h->task[target][lun]=unit->lct_data->tid; + h->task[target][lun]=unit->lct_data.tid; h->tagclock[target][lun]=jiffies; /* Get the max fragments/request */ - i2o_query_scalar(c, d->lct_data->tid, 0xF103, 3, &limit, 2); + i2o_query_scalar(c, d->lct_data.tid, 0xF103, 3, &limit, 2); /* sanity */ if ( limit == 0 ) @@ -428,8 +431,8 @@ int i2o_scsi_detect(Scsi_Host_Template * tpnt) /* * bus_adapter, SCSI (obsolete), or FibreChannel busses only */ - if( (d->lct_data->class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter -// && (d->lct_data->class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT + if( (d->lct_data.class_id!=I2O_CLASS_BUS_ADAPTER_PORT) // bus_adapter +// && (d->lct_data.class_id!=I2O_CLASS_FIBRE_CHANNEL_PORT) // FC_PORT ) continue; diff --git a/drivers/isdn/avmb1/b1dma.c b/drivers/isdn/avmb1/b1dma.c index d61b883752a0..1785e1740212 100644 --- a/drivers/isdn/avmb1/b1dma.c +++ b/drivers/isdn/avmb1/b1dma.c @@ -40,7 +40,7 @@ static char *revision = "$Revision: 1.3 $"; MODULE_AUTHOR("Carsten Paeth "); -int suppress_pollack = 0; +static int suppress_pollack = 0; MODULE_PARM(suppress_pollack, "0-1i"); /* ------------------------------------------------------------- */ diff --git a/drivers/isdn/avmb1/c4.c b/drivers/isdn/avmb1/c4.c index e016bfde2789..8ac5a2d31f1d 100644 --- a/drivers/isdn/avmb1/c4.c +++ b/drivers/isdn/avmb1/c4.c @@ -71,7 +71,7 @@ static char *revision = "$Revision: 1.6 $"; /* ------------------------------------------------------------- */ -int suppress_pollack = 0; +static int suppress_pollack = 0; MODULE_AUTHOR("Carsten Paeth "); diff --git a/drivers/net/8390.c b/drivers/net/8390.c index 3097345c8a1d..416e9ce948de 100644 --- a/drivers/net/8390.c +++ b/drivers/net/8390.c @@ -181,7 +181,7 @@ int ei_open(struct net_device *dev) * ei_close - shut down network device * @dev: network device to close * - * Opposite of ei_open. Only used when "ifconfig down" is done. + * Opposite of ei_open(). Only used when "ifconfig down" is done. */ int ei_close(struct net_device *dev) { @@ -416,7 +416,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) * the 8390 via the card specific functions and fire them at the networking * stack. We also handle transmit completions and wake the transmit path if * neccessary. We also update the counters and do other housekeeping as - * needed + * needed. */ void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs) @@ -530,7 +530,7 @@ void ei_interrupt(int irq, void *dev_id, struct pt_regs * regs) * is a much better solution as it avoids kernel based Tx timeouts, and * an unnecessary card reset. * - * Called with lock held + * Called with lock held. */ static void ei_tx_err(struct net_device *dev) @@ -573,7 +573,7 @@ static void ei_tx_err(struct net_device *dev) * @dev: network device for which tx intr is handled * * We have finished a transmit: check for errors and then trigger the next - * packet to be sent. Called with lock held + * packet to be sent. Called with lock held. */ static void ei_tx_intr(struct net_device *dev) @@ -665,7 +665,7 @@ static void ei_tx_intr(struct net_device *dev) * @dev: network device with which receive will be run * * We have a good packet(s), get it/them out of the buffers. - * Called with lock held + * Called with lock held. */ static void ei_receive(struct net_device *dev) @@ -801,7 +801,7 @@ static void ei_receive(struct net_device *dev) * This includes causing "the NIC to defer indefinitely when it is stopped * on a busy network." Ugh. * Called with lock held. Don't call this with the interrupts off or your - * computer will hate you - it takes 10mS or so. + * computer will hate you - it takes 10ms or so. */ static void ei_rx_overrun(struct net_device *dev) diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index 2728d63c0bf8..da5afb2ad383 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -1253,17 +1253,15 @@ static int __init ltpc_setup(char *str) /* usage message */ printk (KERN_ERR "ltpc: usage: ltpc=auto|iobase[,irq[,dma]]\n"); + return 0; } - return 1; } else { io = ints[1]; if (ints[0] > 1) { irq = ints[2]; - return 1; } if (ints[0] > 2) { dma = ints[3]; - return 1; } /* ignore any other paramters */ } diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 1608b8bdd4c0..789123124363 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -107,7 +107,7 @@ static int go_tx(struct net_device *dev); void __init arcnet_init(void) { - static int arcnet_inited __initdata = 0; + static int arcnet_inited = 0; int count; if (arcnet_inited++) diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c index 79800883ff72..e7db72e15a35 100644 --- a/drivers/net/arcnet/com90io.c +++ b/drivers/net/arcnet/com90io.c @@ -426,7 +426,7 @@ static int __init com90io_setup(char *s) s = get_options(s, 4, ints); if (!ints[0]) - return 1; + return 0; dev = alloc_bootmem(sizeof(struct net_device) + 10); memset(dev, 0, sizeof(struct net_device) + 10); dev->name = (char *) (dev + 1); diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index c9e7beaca3d2..6549f10c76d7 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1614,6 +1614,8 @@ init_module(void) if (register_netdev(d) == 0) n_eepro++; + else + break; } return n_eepro ? 0 : -ENODEV; diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index e770baacf86d..8ffc4fd415b5 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -1509,7 +1509,7 @@ module_exit(cleanup_baycomepp); static int __init baycom_epp_setup(char *str) { - static unsigned __initdata nr_dev = 0; + static unsigned __initlocaldata nr_dev = 0; int ints[2]; if (nr_dev >= NR_PORTS) diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 85270f7079b0..a0cb270451ff 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1800,9 +1800,9 @@ static void hp100_clean_txring( struct net_device *dev ) donecount); #endif #ifdef LINUX_2_1 - dev_kfree_skb( lp->txrhead->skb ); + dev_kfree_skb_any( lp->txrhead->skb ); #else - dev_kfree_skb( lp->txrhead->skb, FREE_WRITE ); + dev_kfree_skb_any( lp->txrhead->skb, FREE_WRITE ); #endif lp->txrhead->skb=(void *)NULL; lp->txrhead=lp->txrhead->next; @@ -1960,9 +1960,9 @@ static int hp100_start_xmit( struct sk_buff *skb, struct net_device *dev ) hp100_ints_on(); #ifdef LINUX_2_1 - dev_kfree_skb( skb ); + dev_kfree_skb_any( skb ); #else - dev_kfree_skb( skb, FREE_WRITE ); + dev_kfree_skb_any( skb, FREE_WRITE ); #endif #ifdef HP100_DEBUG_TX @@ -2198,9 +2198,9 @@ static void hp100_rx_bm( struct net_device *dev ) #endif if(ptr->skb!=NULL) #ifdef LINUX_2_1 - dev_kfree_skb( ptr->skb ); + dev_kfree_skb_any( ptr->skb ); #else - dev_kfree_skb( ptr->skb, FREE_READ ); + dev_kfree_skb_any( ptr->skb, FREE_READ ); #endif lp->stats.rx_errors++; } diff --git a/drivers/net/pcmcia/ray_cs.c b/drivers/net/pcmcia/ray_cs.c index 3c088df36f8a..ba58883c2ceb 100644 --- a/drivers/net/pcmcia/ray_cs.c +++ b/drivers/net/pcmcia/ray_cs.c @@ -1494,16 +1494,19 @@ static int ray_open(struct net_device *dev) dev_link_t *link; ray_dev_t *local = (ray_dev_t *)dev->priv; + MOD_INC_USE_COUNT; + DEBUG(1, "ray_open('%s')\n", dev->name); for (link = dev_list; link; link = link->next) if (link->priv == dev) break; - if (!DEV_OK(link)) + if (!DEV_OK(link)) { + MOD_DEC_USE_COUNT; return -ENODEV; + } if (link->open == 0) local->num_multi = 0; link->open++; - MOD_INC_USE_COUNT; if (sniffer) netif_stop_queue(dev); else netif_start_queue(dev); diff --git a/drivers/net/pcmcia/xircom_tulip_cb.c b/drivers/net/pcmcia/xircom_tulip_cb.c index c1cb7629fbc3..380943f09e9a 100644 --- a/drivers/net/pcmcia/xircom_tulip_cb.c +++ b/drivers/net/pcmcia/xircom_tulip_cb.c @@ -3132,7 +3132,7 @@ static int __init tulip_init(void) return 0; } -static __exit void tulip_exit(void) +static void __exit tulip_exit(void) { pci_unregister_driver(&tulip_ops); } diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 6508c56c2618..877407590661 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -687,11 +687,12 @@ plip_receive_packet(struct net_device *dev, struct net_local *nl, return ERROR; } /* Malloc up new buffer. */ - rcv->skb = dev_alloc_skb(rcv->length.h); + rcv->skb = dev_alloc_skb(rcv->length.h + 2); if (rcv->skb == NULL) { printk(KERN_ERR "%s: Memory squeeze.\n", dev->name); return ERROR; } + skb_reserve(rcv->skb, 2); /* Align IP on 16 byte boundaries */ skb_put(rcv->skb,rcv->length.h); rcv->skb->dev = dev; rcv->state = PLIP_PK_DATA; @@ -989,7 +990,7 @@ plip_interrupt(int irq, void *dev_id, struct pt_regs * regs) switch (nl->connection) { case PLIP_CN_CLOSING: - netif_start_queue (dev); + netif_wake_queue (dev); case PLIP_CN_NONE: case PLIP_CN_SEND: dev->last_rx = jiffies; @@ -1035,7 +1036,7 @@ plip_tx_packet(struct sk_buff *skb, struct net_device *dev) if (skb->len > dev->mtu + dev->hard_header_len) { printk(KERN_WARNING "%s: packet too big, %d.\n", dev->name, (int)skb->len); netif_start_queue (dev); - return 0; + return 1; } if (net_debug > 2) @@ -1054,7 +1055,6 @@ plip_tx_packet(struct sk_buff *skb, struct net_device *dev) mark_bh(IMMEDIATE_BH); spin_unlock_irq(&nl->lock); - netif_start_queue (dev); return 0; } diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 22260591fffd..a0943758269b 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -19,6 +19,22 @@ #include #include + + +/* undefine, or define to various debugging levels (>4 == obscene levels) */ +#undef TULIP_DEBUG + + +#ifdef TULIP_DEBUG +/* note: prints function name for you */ +#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args) +#else +#define DPRINTK(fmt, args...) +#endif + + + + struct tulip_chip_table { char *chip_name; int io_size; @@ -148,6 +164,38 @@ enum t21041_csr13_bits { csr13_mask_10bt = (csr13_eng | csr13_cac | csr13_srl), }; +enum t21143_csr6_bits { + csr6_sc = (1<<31), + csr6_ra = (1<<30), + csr6_ign_dest_msb = (1<<26), + csr6_mbo = (1<<25), + csr6_scr = (1<<24), + csr6_pcs = (1<<23), + csr6_ttm = (1<<22), + csr6_sf = (1<<21), + csr6_hbd = (1<<19), + csr6_ps = (1<<18), + csr6_ca = (1<<17), + csr6_st = (1<<13), + csr6_fc = (1<<12), + csr6_om_int_loop = (1<<10), + csr6_om_ext_loop = (1<<11), + csr6_fd = (1<<9), + csr6_pm = (1<<7), + csr6_pr = (1<<6), + csr6_sb = (1<<5), + csr6_if = (1<<4), + csr6_pb = (1<<3), + csr6_ho = (1<<2), + csr6_sr = (1<<1), + csr6_hp = (1<<0), + + csr6_mask_capture = (csr6_sc | csr6_ca), + csr6_mask_defstate = (csr6_mask_capture | csr6_mbo), + csr6_mask_fullcap = (csr6_mask_defstate | csr6_hbd | + csr6_ps | (3<<14) | csr6_fd), +}; + /* Keep the ring sizes a power of two for efficiency. Making the Tx ring too large decreases the effectiveness of channel @@ -248,6 +296,7 @@ struct ring_info { dma_addr_t mapping; }; + struct tulip_private { const char *product_name; struct net_device *next_module; diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index a9988e77b597..83618f2fc597 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -32,10 +32,15 @@ - Urban Widmark: minor cleanups, merges from Becker 1.03a/1.04 versions LK1.1.3: - - Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c code) - update "Theory of Operation" with softnet/locking changes + - Urban Widmark: use PCI DMA interface (with thanks to the eepro100.c + code) update "Theory of Operation" with + softnet/locking changes - Dave Miller: PCI DMA and endian fixups - Jeff Garzik: MOD_xxx race fixes, updated PCI resource allocation + + LK1.1.4: + - Urban Widmark: fix gcc 2.95.2 problem and + remove writel's to fixed address 0x7c */ /* A few user-configurable values. These may be modified when a driver @@ -105,7 +110,7 @@ static const int multicast_filter_limit = 32; #include static const char *versionA __devinitdata = -"via-rhine.c:v1.03a-LK1.1.3 3/23/2000 Written by Donald Becker\n"; +"via-rhine.c:v1.03a-LK1.1.4 3/28/2000 Written by Donald Becker\n"; static const char *versionB __devinitdata = " http://cesdis.gsfc.nasa.gov/linux/drivers/via-rhine.html\n"; @@ -774,6 +779,7 @@ static void via_rhine_init_ring(struct net_device *dev) { struct netdev_private *np = (struct netdev_private *)dev->priv; int i; + dma_addr_t next = np->rx_ring_dma; np->cur_rx = np->cur_tx = 0; np->dirty_rx = np->dirty_tx = 0; @@ -784,8 +790,8 @@ static void via_rhine_init_ring(struct net_device *dev) for (i = 0; i < RX_RING_SIZE; i++) { np->rx_ring[i].rx_status = 0; np->rx_ring[i].desc_length = cpu_to_le32(np->rx_buf_sz); - np->rx_ring[i].next_desc = - cpu_to_le32(np->rx_ring_dma + sizeof(struct rx_desc)*(i+1)); + next += sizeof(struct rx_desc); + np->rx_ring[i].next_desc = cpu_to_le32(next); np->rx_skbuff[i] = 0; } /* Mark the last entry as wrapping the ring. */ @@ -806,13 +812,15 @@ static void via_rhine_init_ring(struct net_device *dev) np->rx_ring[i].addr = cpu_to_le32(np->rx_skbuff_dma[i]); np->rx_ring[i].rx_status = cpu_to_le32(DescOwn); } + np->dirty_rx = (unsigned int)(i - RX_RING_SIZE); + next = np->tx_ring_dma; for (i = 0; i < TX_RING_SIZE; i++) { np->tx_skbuff[i] = 0; np->tx_ring[i].tx_status = 0; np->tx_ring[i].desc_length = cpu_to_le32(0x00e08000); - np->tx_ring[i].next_desc = - cpu_to_le32(np->tx_ring_dma + sizeof(struct tx_desc)*(i+1)); + next += sizeof(struct tx_desc); + np->tx_ring[i].next_desc = cpu_to_le32(next); np->tx_buf[i] = kmalloc(PKT_BUF_SZ, GFP_KERNEL); } np->tx_ring[i-1].next_desc = cpu_to_le32(np->tx_ring_dma); @@ -1097,7 +1105,6 @@ static void via_rhine_error(struct net_device *dev, int intr_status) if (intr_status & IntrStatsMax) { np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); np->stats.rx_missed_errors += readw(ioaddr + RxMissed); - writel(0, RxMissed); } if (intr_status & IntrTxAbort) { /* Stats counted in Tx-done handler, just restart Tx. */ @@ -1129,7 +1136,6 @@ static struct net_device_stats *via_rhine_get_stats(struct net_device *dev) non-critical. */ np->stats.rx_crc_errors += readw(ioaddr + RxCRCErrs); np->stats.rx_missed_errors += readw(ioaddr + RxMissed); - writel(0, RxMissed); return &np->stats; } diff --git a/drivers/net/wan/comx.c b/drivers/net/wan/comx.c index 1c59075bedd9..65a810889108 100644 --- a/drivers/net/wan/comx.c +++ b/drivers/net/wan/comx.c @@ -91,8 +91,6 @@ static void comx_delete_dentry(struct dentry *dentry); static struct proc_dir_entry *create_comx_proc_entry(char *name, int mode, int size, struct proc_dir_entry *dir); -static void comx_fill_inode(struct inode *inode, int fill); - static struct dentry_operations comx_dentry_operations = { NULL, /* revalidate */ NULL, /* d_hash */ @@ -101,13 +99,7 @@ static struct dentry_operations comx_dentry_operations = { }; -struct proc_dir_entry comx_root_dir = { - 0, 4, "comx", - S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, 2, 0, 0, - 0, &comx_root_inode_ops, - NULL, comx_fill_inode, - NULL, &proc_root, NULL -}; +static struct proc_dir_entry * comx_root_dir; struct comx_debugflags_struct comx_debugflags[] = { { "comx_rx", DEBUG_COMX_RX }, @@ -121,14 +113,6 @@ struct comx_debugflags_struct comx_debugflags[] = { { NULL, 0 } }; -static void comx_fill_inode(struct inode *inode, int fill) -{ - if (fill) - MOD_INC_USE_COUNT; - else - MOD_DEC_USE_COUNT; -} - int comx_debug(struct net_device *dev, char *fmt, ...) { @@ -853,14 +837,13 @@ static int comx_mkdir(struct inode *dir, struct dentry *dentry, int mode) struct net_device *dev; struct comx_channel *ch; - if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + if (dir->i_ino != comx_root_dir->low_ino) return -ENOTDIR; if ((new_dir = create_proc_entry(dentry->d_name.name, mode | S_IFDIR, - &comx_root_dir)) == NULL) { + comx_root_dir)) == NULL) { return -EIO; } - new_dir->proc_iops = &proc_dir_inode_operations; // ez egy normalis /proc konyvtar new_dir->nlink = 2; new_dir->data = NULL; // ide jon majd a struct dev @@ -930,7 +913,7 @@ static int comx_rmdir(struct inode *dir, struct dentry *dentry) int ret; /* Egyelore miert ne ? */ - if (dir->i_ino != comx_root_dir.low_ino) return -ENOTDIR; + if (dir->i_ino != comx_root_dir->low_ino) return -ENOTDIR; if (dev->flags & IFF_UP) { printk(KERN_ERR "%s: down interface before removing it\n", dev->name); @@ -968,8 +951,7 @@ static int comx_rmdir(struct inode *dir, struct dentry *dentry) remove_proc_entry(FILENAME_STATUS, entry); remove_proc_entry(FILENAME_HARDWARE, entry); remove_proc_entry(FILENAME_PROTOCOL, entry); - remove_proc_entry(dentry->d_name.name, &comx_root_dir); -// proc_unregister(&comx_root_dir, dentry->d_inode->i_ino); + remove_proc_entry(dentry->d_name.name, comx_root_dir); MOD_DEC_USE_COUNT; return 0; @@ -1133,23 +1115,15 @@ int __init comx_init(void) { struct proc_dir_entry *new_file; - memcpy(&comx_root_inode_ops, &proc_dir_inode_operations, - sizeof(struct inode_operations)); comx_root_inode_ops.lookup = &comx_lookup; comx_root_inode_ops.mkdir = &comx_mkdir; comx_root_inode_ops.rmdir = &comx_rmdir; - memcpy(&comx_normal_inode_ops, &proc_net_inode_operations, - sizeof(struct inode_operations)); - comx_normal_inode_ops.default_file_ops = &comx_normal_file_ops; comx_normal_inode_ops.lookup = &comx_lookup; memcpy(&comx_debug_inode_ops, &comx_normal_inode_ops, sizeof(struct inode_operations)); - comx_debug_inode_ops.default_file_ops = &comx_debug_file_ops; - memcpy(&comx_normal_file_ops, proc_net_inode_operations.default_file_ops, - sizeof(struct file_operations)); comx_normal_file_ops.open = &comx_file_open; comx_normal_file_ops.release = &comx_file_release; @@ -1158,22 +1132,25 @@ int __init comx_init(void) comx_debug_file_ops.llseek = &comx_debug_lseek; comx_debug_file_ops.read = &comx_debug_read; - if (proc_register(&proc_root, &comx_root_dir) < 0) return -ENOMEM; - + comx_root_dir = create_proc_entry("comx", + S_IFDIR | S_IWUSR | S_IRUGO | S_IXUGO, &proc_root); + if (!comx_root_dir) + return -ENOMEM; + comx_root_dir->proc_iops = &comx_root_inode_ops; if ((new_file = create_proc_entry(FILENAME_HARDWARELIST, - S_IFREG | 0444, &comx_root_dir)) == NULL) { + S_IFREG | 0444, comx_root_dir)) == NULL) { return -ENOMEM; } - new_file->ops = &comx_normal_inode_ops; + new_file->proc_iops = &comx_normal_inode_ops; new_file->data = new_file; new_file->read_proc = &comx_root_read_proc; new_file->write_proc = NULL; new_file->nlink = 1; if ((new_file = create_proc_entry(FILENAME_PROTOCOLLIST, - S_IFREG | 0444, &comx_root_dir)) == NULL) { + S_IFREG | 0444, comx_root_dir)) == NULL) { return -ENOMEM; } @@ -1217,9 +1194,9 @@ int __init comx_init(void) #ifdef MODULE void cleanup_module(void) { - remove_proc_entry(FILENAME_HARDWARELIST, &comx_root_dir); - remove_proc_entry(FILENAME_PROTOCOLLIST, &comx_root_dir); - proc_unregister(&proc_root, comx_root_dir.low_ino); + remove_proc_entry(FILENAME_HARDWARELIST, comx_root_dir); + remove_proc_entry(FILENAME_PROTOCOLLIST, comx_root_dir); + remove_proc_entry(comx_root_dir->name, &proc_root); } #endif diff --git a/drivers/net/wan/comx.h b/drivers/net/wan/comx.h index e02849b90bf7..b343eb4ca0af 100644 --- a/drivers/net/wan/comx.h +++ b/drivers/net/wan/comx.h @@ -220,7 +220,7 @@ typedef u16 word; #define SEEK_END 2 #endif -extern struct proc_dir_entry comx_root_dir; +extern struct proc_dir_entry * comx_root_dir; extern int comx_register_hardware(struct comx_hardware *comx_hw); extern int comx_unregister_hardware(char *name); diff --git a/drivers/net/wan/sdladrv.c b/drivers/net/wan/sdladrv.c index 06e86de82a30..c2396ead31c0 100644 --- a/drivers/net/wan/sdladrv.c +++ b/drivers/net/wan/sdladrv.c @@ -308,7 +308,7 @@ static unsigned char s507_irqmask[] = #ifdef MODULE int init_module (void) #else -__initfunc(int wanpipe_init(void)) +int __init wanpipe_init(void) #endif { printk(KERN_INFO "%s v%u.%u %s\n", diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c index 5b850461660b..c57b59d4b9a4 100644 --- a/drivers/net/wan/syncppp.c +++ b/drivers/net/wan/syncppp.c @@ -193,7 +193,7 @@ static void sppp_clear_timeout(struct sppp *p) * * This can be called directly by cards that do not have * timing constraints but is normally called from the network layer - * after interrupt servicing to process frames queued via netif_rx. + * after interrupt servicing to process frames queued via netif_rx(). * * We process the options in the card. If the frame is destined for * the protocol stacks then it requeues the frame for the upper level diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 7b369c00439b..c45e6f7ae7dc 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -1313,12 +1313,12 @@ EXPORT_SYMBOL(z8530_shutdown); /** * z8530_channel_load - Load channel data * @c: Z8530 channel to configure - * @rtable: Table of register, value pairs + * @rtable: table of register, value pairs * FIXME: ioctl to allow user uploaded tables * * Load a Z8530 channel up from the system data. We use +16 to - * indicate the 'prime' registers. The value 255 terminates the - * table + * indicate the "prime" registers. The value 255 terminates the + * table. */ int z8530_channel_load(struct z8530_channel *c, u8 *rtable) diff --git a/drivers/parport/ChangeLog b/drivers/parport/ChangeLog index b2f122af0f3e..3d00ebbce4ae 100644 --- a/drivers/parport/ChangeLog +++ b/drivers/parport/ChangeLog @@ -1,3 +1,7 @@ +2000-03-29 Tim Waugh + + * parport_pc.c: Add support for another PCI card. + 2000-03-27 Tim Waugh * parport_pc.c (parport_pc_ecp_read_block_pio): Correct operation diff --git a/drivers/parport/daisy.c b/drivers/parport/daisy.c index 3b13ea71a704..ead3443b4f95 100644 --- a/drivers/parport/daisy.c +++ b/drivers/parport/daisy.c @@ -174,7 +174,26 @@ void parport_daisy_fini (struct parport *port) return; } -/* Find a device by canonical device number. */ +/** + * parport_open - find a device by canonical device number + * @devnum: canonical device number + * @name: name to associate with the device + * @pf: preemption callback + * @kf: kick callback + * @irqf: interrupt handler + * @flags: registration flags + * @handle: driver data + * + * This function is similar to parport_register_device(), except + * that it locates a device by its number rather than by the port + * it is attached to. See parport_find_device() and + * parport_find_class(). + * + * All parameters except for @devnum are the same as for + * parport_register_device(). The return value is the same as + * for parport_register_device(). + **/ + struct pardevice *parport_open (int devnum, const char *name, int (*pf) (void *), void (*kf) (void *), void (*irqf) (int, void *, struct pt_regs *), @@ -219,13 +238,32 @@ struct pardevice *parport_open (int devnum, const char *name, return dev; } -/* The converse of parport_open. */ +/** + * parport_close - close a device opened with parport_open() + * @dev: device to close + * + * This is to parport_open() as parport_unregister_device() is to + * parport_register_device(). + **/ + void parport_close (struct pardevice *dev) { parport_unregister_device (dev); } -/* Convert device coordinates into a canonical device number. */ +/** + * parport_device_num - convert device coordinates into a + * canonical device number + * @parport: parallel port number + * @mux: multiplexor port number (-1 for no multiplexor) + * @daisy: daisy chain address (-1 for no daisy chain address) + * + * This tries to locate a device on the given parallel port, + * multiplexor port and daisy chain address, and returns its + * device number or -NXIO if no device with those coordinates + * exists. + **/ + int parport_device_num (int parport, int mux, int daisy) { struct daisydev *dev = topology; @@ -240,7 +278,32 @@ int parport_device_num (int parport, int mux, int daisy) return dev->devnum; } -/* Convert a canonical device number into device coordinates. */ +/** + * parport_device_coords - convert a canonical device number into + * device coordinates + * @devnum: device number + * @parport: pointer to storage for parallel port number + * @mux: pointer to storage for multiplexor port number + * @daisy: pointer to storage for daisy chain address + * + * This function converts a device number into its coordinates in + * terms of which parallel port in the system it is attached to, + * which multiplexor port it is attached to if there is a + * multiplexor on that port, and which daisy chain address it has + * if it is in a daisy chain. + * + * The caller must allocate storage for @parport, @mux, and + * @daisy. + * + * If there is no device with the specified device number, -ENXIO + * is returned. Otherwise, the values pointed to by @parport, + * @mux, and @daisy are set to the coordinates of the device, + * with -1 for coordinates with no value. + * + * This function is not actually very useful, but this interface + * was suggested by IEEE 1284.3. + **/ + int parport_device_coords (int devnum, int *parport, int *mux, int *daisy) { struct daisydev *dev = topology; @@ -437,6 +500,28 @@ static int assign_addrs (struct parport *port) /* Find a device with a particular manufacturer and model string, starting from a given device number. Like the PCI equivalent, 'from' itself is skipped. */ + +/** + * parport_find_device - find a device with a specified + * manufacturer and model string + * @mfg: required manufacturer string + * @mdl: required model string + * @from: previous device number found in search, or %NULL for + * new search + * + * This walks through the list of parallel port devices looking + * for a device whose 'MFG' string matches @mfg and whose 'MDL' + * string matches @mdl in their IEEE 1284 Device ID. + * + * When a device is found matching those requirements, its device + * number is returned; if there is no matching device, a negative + * value is returned. + * + * A new search it initiated by passing %NULL as the @from + * argument. If @from is not %NULL, the search continues from + * that device. + **/ + int parport_find_device (const char *mfg, const char *mdl, int from) { struct daisydev *d = topology; /* sorted by devnum */ @@ -462,8 +547,25 @@ int parport_find_device (const char *mfg, const char *mdl, int from) return -1; } -/* Find a device in a particular class. Like the PCI equivalent, - 'from' itself is skipped. */ +/** + * parport_find_class - find a device in a specified class + * @cls: required class + * @from: previous device number found in search, or %NULL for + * new search + * + * This walks through the list of parallel port devices looking + * for a device whose 'CLS' string matches @cls in their IEEE + * 1284 Device ID. + * + * When a device is found matching those requirements, its device + * number is returned; if there is no matching device, a negative + * value is returned. + * + * A new search it initiated by passing %NULL as the @from + * argument. If @from is not %NULL, the search continues from + * that device. + **/ + int parport_find_class (parport_device_class cls, int from) { struct daisydev *d = topology; /* sorted by devnum */ diff --git a/drivers/parport/ieee1284.c b/drivers/parport/ieee1284.c index 6b9c9e47f2d8..f25c50abdeaf 100644 --- a/drivers/parport/ieee1284.c +++ b/drivers/parport/ieee1284.c @@ -42,11 +42,22 @@ static void timeout_waiting_on_port (unsigned long cookie) parport_ieee1284_wakeup (port_from_cookie[cookie % PARPORT_MAX]); } -/* Wait for a parport_ieee1284_wakeup. - * 0: success - * <0: error (exit as soon as possible) - * >0: timed out +/** + * parport_wait_event - wait for an event on a parallel port + * @port: port to wait on + * @timeout: time to wait (in jiffies) + * + * This function waits for up to @timeout jiffies for an + * interrupt to occur on a parallel port. If the port timeout is + * set to zero, it returns immediately. + * + * If an interrupt occurs before the timeout period elapses, this + * function returns one immediately. If it times out, it returns + * a value greater than zero. An error code less than zero + * indicates an error (most likely a pending signal), and the + * calling code should finish what it's doing as soon as it can. */ + int parport_wait_event (struct parport *port, signed long timeout) { int ret; @@ -72,13 +83,29 @@ int parport_wait_event (struct parport *port, signed long timeout) return ret; } -/* Wait for Status line(s) to change in 35 ms - see IEEE1284-1994 page 24 to - * 25 for this. After this time we can create a timeout because the - * peripheral doesn't conform to IEEE1284. We want to save CPU time: we are - * waiting a maximum time of 500 us busy (this is for speed). If there is - * not the right answer in this time, we call schedule and other processes - * are able to eat the time up to 40ms. - */ +/** + * parport_poll_peripheral - poll status lines + * @port: port to watch + * @mask: status lines to watch + * @result: desired values of chosen status lines + * @usec: timeout + * + * This function busy-waits until the masked status lines have + * the desired values, or until the timeout period elapses. The + * @mask and @result parameters are bitmasks, with the bits + * defined by the constants in parport.h: %PARPORT_STATUS_BUSY, + * and so on. + * + * This function does not call schedule(); instead it busy-waits + * using udelay(). It currently has a resolution of 5usec. + * + * If the status lines take on the desired values before the + * timeout period elapses, parport_poll_peripheral() returns zero + * immediately. A zero return value greater than zero indicates + * a timeout. An error code (less than zero) indicates an error, + * most likely a signal that arrived, and the caller should + * finish what it is doing as soon as possible. +*/ int parport_poll_peripheral(struct parport *port, unsigned char mask, @@ -102,6 +129,31 @@ int parport_poll_peripheral(struct parport *port, return 1; } +/** + * parport_wait_peripheral - wait for status lines to change in 35ms + * @port: port to watch + * @mask: status lines to watch + * @result: desired values of chosen status lines + * + * This function waits until the masked status lines have the + * desired values, or until 35ms have elapsed (see IEEE 1284-1994 + * page 24 to 25 for why this value in particular is hardcoded). + * The @mask and @result parameters are bitmasks, with the bits + * defined by the constants in parport.h: %PARPORT_STATUS_BUSY, + * and so on. + * + * The port is polled quickly to start off with, in anticipation + * of a fast response from the peripheral. This fast polling + * time is configurable (using /proc), and defaults to 500usec. + * If the timeout for this port (see parport_set_timeout()) is + * zero, the fast polling time is 35ms, and this function does + * not call schedule(). + * + * If the timeout for this port is non-zero, after the fast + * polling fails it uses parport_wait_event() to wait for up to + * 10ms, waking up if an interrupt occurs. + */ + int parport_wait_peripheral(struct parport *port, unsigned char mask, unsigned char result) @@ -255,12 +307,21 @@ static void parport_ieee1284_terminate (struct parport *port) } #endif /* IEEE1284 support */ -/* Negotiate an IEEE 1284 mode. - * return values are: - * 0 - handshake OK; IEEE1284 peripheral and mode available - * -1 - handshake failed; peripheral is not compliant (or none present) - * 1 - handshake OK; IEEE1284 peripheral present but mode not available +/** + * parport_negotiate - negotiate an IEEE 1284 mode + * @port: port to use + * @mode: mode to negotiate to + * + * Use this to negotiate to a particular IEEE 1284 transfer mode. + * The @mode parameter should be one of the constants in + * parport.h starting %IEEE1284_MODE_xxx. + * + * The return value is 0 if the peripheral has accepted the + * negotiation to the mode specified, -1 if the peripheral is not + * IEEE 1284 compliant (or not present), or 1 if the peripheral + * has rejected the negotiation. */ + int parport_negotiate (struct parport *port, int mode) { #ifndef CONFIG_PARPORT_1284 @@ -513,7 +574,24 @@ void parport_ieee1284_interrupt (int which, void *handle, struct pt_regs *regs) #endif /* IEEE1284 support */ } -/* Write a block of data. */ +/** + * parport_write - write a block of data to a parallel port + * @port: port to write to + * @buffer: data buffer (in kernel space) + * @len: number of bytes of data to transfer + * + * This will write up to @len bytes of @buffer to the port + * specified, using the IEEE 1284 transfer mode most recently + * negotiated to (using parport_negotiate()), as long as that + * mode supports forward transfers (host to peripheral). + * + * It is the caller's responsibility to ensure that the first + * @len bytes of @buffer are valid. + * + * This function returns the number of bytes transferred (if zero + * or positive), or else an error code. + */ + ssize_t parport_write (struct parport *port, const void *buffer, size_t len) { #ifndef CONFIG_PARPORT_1284 @@ -578,7 +656,24 @@ ssize_t parport_write (struct parport *port, const void *buffer, size_t len) #endif /* IEEE1284 support */ } -/* Read a block of data. */ +/** + * parport_read - read a block of data from a parallel port + * @port: port to read from + * @buffer: data buffer (in kernel space) + * @len: number of bytes of data to transfer + * + * This will read up to @len bytes of @buffer to the port + * specified, using the IEEE 1284 transfer mode most recently + * negotiated to (using parport_negotiate()), as long as that + * mode supports reverse transfers (peripheral to host). + * + * It is the caller's responsibility to ensure that the first + * @len bytes of @buffer are available to write to. + * + * This function returns the number of bytes transferred (if zero + * or positive), or else an error code. + */ + ssize_t parport_read (struct parport *port, void *buffer, size_t len) { #ifndef CONFIG_PARPORT_1284 @@ -637,7 +732,23 @@ ssize_t parport_read (struct parport *port, void *buffer, size_t len) #endif /* IEEE1284 support */ } -/* Set the amount of time we wait while nothing's happening. */ +/** + * parport_set_timeout - set the inactivity timeout for a device + * on a port + * @dev: device on a port + * @inactivity: inactivity timeout (in jiffies) + * + * This sets the inactivity timeout for a particular device on a + * port. This affects functions like parport_wait_peripheral(). + * The special value 0 means not to call schedule() while dealing + * with this device. + * + * The return value is the previous inactivity timeout. + * + * Any callers of parport_wait_event() for this device are woken + * up. + */ + long parport_set_timeout (struct pardevice *dev, long inactivity) { long int old = dev->timeout; diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 5b912d3ef0e3..b0460aad4c32 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -2254,6 +2254,7 @@ enum parport_pc_pci_cards { boca_ioppar, plx_9050, afavlab_tk9902, + timedia_1889, }; @@ -2291,6 +2292,7 @@ static struct parport_pc_pci { /* boca_ioppar */ { 1, { { 0, -1 }, } }, /* plx_9050 */ { 2, { { 4, -1 }, { 5, -1 }, } }, /* afavlab_tk9902 */ { 1, { { 0, 1 }, } }, + /* timedia_1889 */ { 1, { { 2, -1 }, } }, }; static struct pci_device_id parport_pc_pci_tbl[] __devinitdata = { @@ -2348,6 +2350,8 @@ static struct pci_device_id parport_pc_pci_tbl[] __devinitdata = { PCI_SUBVENDOR_ID_EXSYS, PCI_SUBDEVICE_ID_EXSYS_4014, 0,0, plx_9050 }, { PCI_VENDOR_ID_AFAVLAB, PCI_DEVICE_ID_AFAVLAB_TK9902, PCI_ANY_ID, PCI_ANY_ID, 0, 0, afavlab_tk9902 }, + { PCI_VENDOR_ID_TIMEDIA, PCI_DEVICE_ID_TIMEDIA_1889, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, timedia_1889 }, { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci,parport_pc_pci_tbl); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c08d2f38e2b1..ee901ab5d3cb 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1068,6 +1068,7 @@ EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_assign_resource); EXPORT_SYMBOL(pci_register_driver); EXPORT_SYMBOL(pci_unregister_driver); +EXPORT_SYMBOL(pci_dev_driver); EXPORT_SYMBOL(pci_match_device); EXPORT_SYMBOL(pci_find_parent_resource); diff --git a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c index 9c6754924303..30ad34200673 100644 --- a/drivers/scsi/53c7,8xx.c +++ b/drivers/scsi/53c7,8xx.c @@ -3145,7 +3145,7 @@ debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token, save_flags(flags); cli(); for (bp = (struct NCR53c7x0_break *) host->breakpoints; - bp; bp = (struct NCR53c7x0_break *) bp->next); { + bp; bp = (struct NCR53c7x0_break *) bp->next) { sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x", bp->addr, bp->old[0], bp->old[1]); len = strlen(buf); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 87236abbbf19..ca98256d5f10 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -478,6 +478,7 @@ Scsi_Cmnd *scsi_allocate_device(Scsi_Device * device, int wait, SCpnt->result = 0; SCpnt->underflow = 0; /* Do not flag underflow conditions */ + SCpnt->old_underflow = 0; SCpnt->resid = 0; SCpnt->state = SCSI_STATE_INITIALIZING; SCpnt->owner = SCSI_OWNER_HIGHLEVEL; @@ -906,6 +907,7 @@ void scsi_init_cmd_from_req(Scsi_Cmnd * SCpnt, Scsi_Request * SRpnt) SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; + SCpnt->old_underflow = SCpnt->underflow; /* Start the timer ticking. */ @@ -1011,6 +1013,7 @@ void scsi_do_cmd(Scsi_Cmnd * SCpnt, const void *cmnd, SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->old_cmd_len = SCpnt->cmd_len; SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; + SCpnt->old_underflow = SCpnt->underflow; /* Start the timer ticking. */ @@ -1271,6 +1274,7 @@ int scsi_retry_command(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; /* * Zero the sense information from the last time we tried @@ -1435,6 +1439,7 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt) SCpnt->old_use_sg = 0; SCpnt->old_cmd_len = 0; SCpnt->underflow = 0; + SCpnt->old_underflow = 0; SCpnt->transfersize = 0; SCpnt->resid = 0; SCpnt->serial_number = 0; diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 677d21410842..c9e6e70fa79b 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -756,6 +756,8 @@ struct scsi_cmnd { unsigned underflow; /* Return error if less than this amount is transfered */ + unsigned old_underflow; /* save underflow here when reusing the + * command for error handling */ unsigned transfersize; /* How much we are guaranteed to transfer with each SCSI transfer diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 7268bc15466f..6d1602d7ee7b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -409,6 +409,7 @@ STATIC int scsi_eh_retry_command(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; scsi_send_eh_cmnd(SCpnt, SCpnt->timeout_per_command); @@ -466,6 +467,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->sc_data_direction = SCSI_DATA_READ; + SCpnt->underflow = 0; scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); @@ -489,6 +491,7 @@ STATIC int scsi_request_sense(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; /* * Hey, we are done. Let's look to see what happened. @@ -533,9 +536,11 @@ STATIC int scsi_test_unit_ready(Scsi_Cmnd * SCpnt) SCpnt->request_bufflen = 256; SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); - scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + SCpnt->underflow = 0; SCpnt->sc_data_direction = SCSI_DATA_NONE; + scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT); + /* Last chance to have valid sense data */ if (!scsi_sense_valid(SCpnt)) memcpy((void *) SCpnt->sense_buffer, @@ -556,6 +561,7 @@ STATIC int scsi_test_unit_ready(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; /* * Hey, we are done. Let's look to see what happened. @@ -736,6 +742,7 @@ STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt) */ SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; *SClist = SCpnt; } diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 74ac6d245205..d4a1fd5fba47 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -232,6 +232,7 @@ int scsi_init_cmd_errh(Scsi_Cmnd * SCpnt) SCpnt->old_use_sg = SCpnt->use_sg; SCpnt->old_cmd_len = SCpnt->cmd_len; SCpnt->sc_old_data_direction = SCpnt->sc_data_direction; + SCpnt->old_underflow = SCpnt->underflow; memcpy((void *) SCpnt->data_cmnd, (const void *) SCpnt->cmnd, sizeof(SCpnt->cmnd)); SCpnt->buffer = SCpnt->request_buffer; diff --git a/drivers/scsi/scsi_obsolete.c b/drivers/scsi/scsi_obsolete.c index a957a4c017e3..0b25bd4e2e5e 100644 --- a/drivers/scsi/scsi_obsolete.c +++ b/drivers/scsi/scsi_obsolete.c @@ -377,6 +377,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; } switch (host_byte(result)) { case DID_OK: @@ -637,6 +638,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; SCpnt->result = 0; /* * Ugly, ugly. The newer interfaces all @@ -664,6 +666,7 @@ void scsi_old_done(Scsi_Cmnd * SCpnt) SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->sc_data_direction = SCpnt->sc_old_data_direction; + SCpnt->underflow = SCpnt->old_underflow; /* * The upper layers assume the lock isn't held. We mustn't * disappoint them. When the new error handling code is in diff --git a/drivers/scsi/sym53c8xx_comm.h b/drivers/scsi/sym53c8xx_comm.h index a9cc78a54bb3..7dd93ce42d58 100644 --- a/drivers/scsi/sym53c8xx_comm.h +++ b/drivers/scsi/sym53c8xx_comm.h @@ -2178,7 +2178,7 @@ static int __init sym53c8xx__setup(char *str) ++cur; } #endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */ - return 0; + return 1; } /*=================================================================== diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 947e0294eb03..d0072dcb1d43 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -97,6 +97,7 @@ if [ "$CONFIG_SOUND_OSS" = "y" -o "$CONFIG_SOUND_OSS" = "m" ]; then bool ' 16 bit sampling option of GUS (_NOT_ GUS MAX)' CONFIG_SOUND_GUS16 bool ' GUS MAX support' CONFIG_SOUND_GUSMAX fi + dep_tristate ' Intel ICH audio support' CONFIG_SOUND_ICH $CONFIG_SOUND_OSS dep_tristate ' Loopback MIDI device support' CONFIG_SOUND_VMIDI $CONFIG_SOUND_OSS dep_tristate ' MediaTrix AudioTrix Pro support' CONFIG_SOUND_TRIX $CONFIG_SOUND_OSS if [ "$CONFIG_SOUND_TRIX" = "y" ]; then diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 876daf485082..0ebdf61245bf 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SOUND_MSNDCLAS) += msnd.o msnd_classic.o obj-$(CONFIG_SOUND_MSNDPIN) += msnd.o msnd_pinnacle.o obj-$(CONFIG_SOUND_VWSND) += vwsnd.o obj-$(CONFIG_SOUND_NM256) += nm256_audio.o ac97.o +obj-$(CONFIG_SOUND_NM256) += i810_audio.o ac97.o obj-$(CONFIG_SOUND_SONICVIBES) += sonicvibes.o obj-$(CONFIG_SOUND_CMPCI) += cmpci.o obj-$(CONFIG_SOUND_ES1370) += es1370.o diff --git a/drivers/sound/ad1848.c b/drivers/sound/ad1848.c index bce04ef33a84..91485b6e0c4a 100644 --- a/drivers/sound/ad1848.c +++ b/drivers/sound/ad1848.c @@ -27,6 +27,7 @@ * Alan Cox : reformatted. Fixed SMP bugs. Moved to kernel alloc/free * of irqs. Use dev_id. * Christoph Hellwig : adapted to module_init/module_exit + * Aki Laukkanen : added power management support * * Status: * Tested. Believed fully functional. @@ -36,6 +37,7 @@ #include #include #include +#include #include "soundmodule.h" @@ -52,6 +54,7 @@ typedef struct int irq; int dma1, dma2; int dual_dma; /* 1, when two DMA channels allocated */ + int subtype; unsigned char MCE_bit; unsigned char saved_regs[32]; int debug_flag; @@ -87,6 +90,9 @@ typedef struct int irq_ok; mixer_ents *mix_devices; int mixer_output_port; + + /* Power management */ + struct pm_dev *pmdev; } ad1848_info; typedef struct ad1848_port_info @@ -100,7 +106,9 @@ typedef struct ad1848_port_info } ad1848_port_info; +static struct address_info cfg; static int nr_ad1848_devs = 0; + int deskpro_xl = 0; int deskpro_m = 0; int soundpro = 0; @@ -163,11 +171,11 @@ static void ad1848_halt(int dev); static void ad1848_halt_input(int dev); static void ad1848_halt_output(int dev); static void ad1848_trigger(int dev, int bits); +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data); #ifndef EXCLUDE_TIMERS static int ad1848_tmr_install(int dev); static void ad1848_tmr_reprogram(int dev); - #endif static int ad_read(ad1848_info * devc, int reg) @@ -1415,7 +1423,7 @@ static void ad1848_init_hw(ad1848_info * devc) if (devc->model == MD_IWAVE) ad_write(devc, 12, 0x6c); /* Select codec mode 3 */ - if (devc-> model != MD_1845_SSCAPE) + if (devc->model != MD_1845_SSCAPE) for (i = 16; i < 32; i++) ad_write(devc, i, init_values[i]); @@ -1849,7 +1857,6 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt * The actually used IRQ is ABS(irq). */ - int my_dev; char dev_name[100]; int e; @@ -1863,6 +1870,7 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt devc->timer_ticks = 0; devc->dma1 = dma_playback; devc->dma2 = dma_capture; + devc->subtype = cfg.card_subtype; devc->audio_flags = DMA_AUTOMODE; devc->playback_dev = devc->record_dev = 0; if (name != NULL) @@ -1915,6 +1923,10 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt nr_ad1848_devs++; + devc->pmdev = pm_register(PM_ISA_DEV, my_dev, ad1848_pm_callback); + if (devc->pmdev) + devc->pmdev->data = devc; + ad1848_init_hw(devc); if (irq > 0) @@ -1950,7 +1962,7 @@ int ad1848_init(char *name, int io_base, int irq, int dma_playback, int dma_capt devc->irq_ok = 1; } #else - devc->irq_ok=1; + devc->irq_ok = 1; #endif } else @@ -2076,6 +2088,9 @@ void ad1848_unload(int io_base, int irq, int dma_playback, int dma_capture, int if(mixer>=0) sound_unload_mixerdev(mixer); + if (devc->pmdev) + pm_unregister(devc->pmdev); + nr_ad1848_devs--; for ( ; i < nr_ad1848_devs ; i++) adev_info[i] = adev_info[i+1]; @@ -2507,7 +2522,8 @@ void attach_ms_sound(struct address_info *hw_config) hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, hw_config->irq, hw_config->dma, - hw_config->dma2, 0, hw_config->osp); + hw_config->dma2, 0, + hw_config->osp); request_region(hw_config->io_base, 4, "WSS config"); return; } @@ -2562,10 +2578,9 @@ void attach_ms_sound(struct address_info *hw_config) outb((bits | dma_bits[dma] | dma2_bit), config_port); /* Write IRQ+DMA setup */ - hw_config->slots[0] = ad1848_init("MSS audio codec", hw_config->io_base + 4, + hw_config->slots[0] = ad1848_init("MS Sound System", hw_config->io_base + 4, hw_config->irq, - dma, - dma2, 0, + dma, dma2, 0, hw_config->osp); request_region(hw_config->io_base, 4, "WSS config"); } @@ -2692,6 +2707,83 @@ static int ad1848_tmr_install(int dev) } #endif /* EXCLUDE_TIMERS */ +static int ad1848_suspend(ad1848_info *devc) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + ad_mute(devc); + + restore_flags(flags); + return 0; +} + +static int ad1848_resume(ad1848_info *devc) +{ + unsigned long flags; + int mixer_levels[32], i; + + save_flags(flags); + cli(); + + /* store old mixer levels */ + memcpy(mixer_levels, devc->levels, sizeof (mixer_levels)); + ad1848_init_hw(devc); + + /* restore mixer levels */ + for (i = 0; i < 32; i++) + ad1848_mixer_set(devc, devc->dev_no, mixer_levels[i]); + + if (!devc->subtype) { + static signed char interrupt_bits[12] = { -1, -1, -1, -1, -1, 0x00, -1, 0x08, -1, 0x10, 0x18, 0x20 }; + static char dma_bits[4] = { 1, 2, 0, 3 }; + + signed char bits; + char dma2_bit = 0; + + int config_port = devc->base + 0; + + bits = interrupt_bits[devc->irq]; + if (bits == -1) { + printk(KERN_ERR "MSS: Bad IRQ %d\n", devc->irq); + return -1; + } + + outb((bits | 0x40), config_port); + + if (devc->dma2 != -1 && devc->dma2 != devc->dma1) + if ( (devc->dma1 == 0 && devc->dma2 == 1) || + (devc->dma1 == 1 && devc->dma2 == 0) || + (devc->dma1 == 3 && devc->dma2 == 0)) + dma2_bit = 0x04; + + outb((bits | dma_bits[devc->dma1] | dma2_bit), config_port); + } + + restore_flags(flags); + return 0; +} + +static int ad1848_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + ad1848_info *devc = dev->data; + if (devc) { + DEB(printk("ad1848: pm event received: 0x%x\n", rqst)); + + switch (rqst) { + case PM_SUSPEND: + ad1848_suspend(devc); + break; + case PM_RESUME: + ad1848_resume(devc); + break; + } + } + return 0; +} + EXPORT_SYMBOL(ad1848_detect); EXPORT_SYMBOL(ad1848_init); @@ -2708,8 +2800,6 @@ static int __initdata dma = -1; static int __initdata dma2 = -1; static int __initdata type = 0; -static struct address_info cfg; - MODULE_PARM(io, "i"); /* I/O for a raw AD1848 card */ MODULE_PARM(irq, "i"); /* IRQ to use */ MODULE_PARM(dma, "i"); /* First DMA channel */ diff --git a/drivers/sound/i810_audio.c b/drivers/sound/i810_audio.c new file mode 100644 index 000000000000..673a0336ec8b --- /dev/null +++ b/drivers/sound/i810_audio.c @@ -0,0 +1,1794 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_DEVICE_ID_INTEL_82801 +#define PCI_DEVICE_ID_INTEL_82801 0x2415 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82901 +#define PCI_DEVICE_ID_INTEL_82901 0x2425 +#endif +#ifndef PCI_DEVICE_ID_INTEL_440MX +#define PCI_DEVICE_ID_INTEL_440MX 0x7195 +#endif + +#define ADC_RUNNING 1 +#define DAC_RUNNING 2 + +#define I810_FMT_16BIT 1 +#define I810_FMT_STEREO 2 +#define I810_FMT_MASK 3 + +/* the 810's array of pointers to data buffers */ + +struct sg_item { +#define BUSADDR_MASK 0xFFFFFFFE + u32 busaddr; +#define CON_IOC 0x80000000 /* interrupt on completion */ +#define CON_BUFPAD 0x40000000 /* pad underrun with last sample, else 0 */ +#define CON_BUFLEN_MASK 0x0000ffff /* buffer length in samples */ + u32 control; +}; + +/* an instance of the i810 channel */ +#define SG_LEN 32 +struct i810_channel +{ + /* these sg guys should probably be allocated + seperately as nocache. Must be 8 byte aligned */ + struct sg_item sg[SG_LEN]; /* 32*8 */ + u32 offset; /* 4 */ + u32 port; /* 4 */ + u32 used; + u32 num; +}; + +/* + * we have 3 seperate dma engines. pcm in, pcm out, and mic. + * each dma engine has controlling registers. These goofy + * names are from the datasheet, but make it easy to write + * code while leafing through it. + */ + +#define ENUM_ENGINE(PRE,DIG) \ +enum { \ + ##PRE##_BDBAR = 0x##DIG##0, /* Buffer Descriptor list Base Address */ \ + ##PRE##_CIV = 0x##DIG##4, /* Current Index Value */ \ + ##PRE##_LVI = 0x##DIG##5, /* Last Valid Index */ \ + ##PRE##_SR = 0x##DIG##6, /* Status Register */ \ + ##PRE##_PICB = 0x##DIG##8, /* Position In Current Buffer */ \ + ##PRE##_PIV = 0x##DIG##a, /* Prefetched Index Value */ \ + ##PRE##_CR = 0x##DIG##b /* Control Register */ \ +} + +ENUM_ENGINE(OFF,0); /* Offsets */ +ENUM_ENGINE(PI,0); /* PCM In */ +ENUM_ENGINE(PO,1); /* PCM Out */ +ENUM_ENGINE(MC,2); /* Mic In */ + +enum { + GLOB_CNT = 0x2c, /* Global Control */ + GLOB_STA = 0x30, /* Global Status */ + CAS = 0x34 /* Codec Write Semaphore Register */ +}; + +/* interrupts for a dma engine */ +#define DMA_INT_FIFO (1<<4) /* fifo under/over flow */ +#define DMA_INT_COMPLETE (1<<3) /* buffer read/write complete and ioc set */ +#define DMA_INT_LVI (1<<2) /* last valid done */ +#define DMA_INT_CELV (1<<1) /* last valid is current */ +#define DMA_INT_MASK (DMA_INT_FIFO|DMA_INT_COMPLETE|DMA_INT_LVI) + +/* interrupts for the whole chip */ +#define INT_SEC (1<<11) +#define INT_PRI (1<<10) +#define INT_MC (1<<7) +#define INT_PO (1<<6) +#define INT_PI (1<<5) +#define INT_MO (1<<2) +#define INT_NI (1<<1) +#define INT_GPI (1<<0) +#define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI) + + +#define DRIVER_VERSION "0.01" + +/* magic numbers to protect our data structures */ +#define I810_CARD_MAGIC 0x5072696E /* "Prin" */ +#define I810_STATE_MAGIC 0x63657373 /* "cess" */ +#define I810_DMA_MASK 0xffffffff /* DMA buffer mask for pci_alloc_consist */ +#define NR_HW_CH 3 + +/* maxinum number of AC97 codecs connected, AC97 2.0 defined 4 */ +#define NR_AC97 2 + +/* minor number of /dev/dspW */ +#define SND_DEV_DSP8 1 + +/* minor number of /dev/dspW */ +#define SND_DEV_DSP16 1 + +static const unsigned sample_size[] = { 1, 2, 2, 4 }; +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; + +enum { + ICH82801AA = 0, + ICH82901AB, + INTEL440MX +}; + +static char * card_names[] = { + "Intel ICH 82801AA", + "Intel ICH 82901AB", + "Intel 440MX" +}; + +static struct pci_device_id i810_pci_tbl [] __initdata = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82801AA}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82901, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ICH82901AB}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_440MX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTEL440MX}, + {0,} +}; + +MODULE_DEVICE_TABLE (pci, i810_pci_tbl); + +/* "software" or virtual channel, an instance of opened /dev/dsp */ +struct i810_state { + unsigned int magic; + struct i810_card *card; /* Card info */ + + /* single open lock mechanism, only used for recording */ + struct semaphore open_sem; + wait_queue_head_t open_wait; + + /* file mode */ + mode_t open_mode; + + /* virtual channel number */ + int virt; + + struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct i810_channel *channel; + + /* OSS buffer management stuff */ + void *rawbuf; + dma_addr_t dma_handle; + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, updated by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be comsumed or been generated by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ + + unsigned error; /* number of over/underruns */ + wait_queue_head_t wait; /* put process on wait queue when no more space in buffer */ + + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned update_flag; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + } dmabuf; +}; + + +struct i810_card { + struct i810_channel channel[3]; + unsigned int magic; + + /* We keep trident cards in a linked list */ + struct i810_card *next; + + /* The trident has a certain amount of cross channel interaction + so we use a single per card lock */ + spinlock_t lock; + + /* PCI device stuff */ + struct pci_dev * pci_dev; + u16 pci_id; + + /* soundcore stuff */ + int dev_audio; + + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct i810_state *states[NR_HW_CH]; + + u16 ac97_features; + + /* hardware resources */ + unsigned long iobase; + unsigned long ac97base; + u32 irq; + + /* Function support */ + struct i810_channel *(*alloc_pcm_channel)(struct i810_card *); + struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *); + void (*free_pcm_channel)(struct i810_card *, int chan); +}; + +static struct i810_card *devs = NULL; + +static int i810_open_mixdev(struct inode *inode, struct file *file); +static int i810_release_mixdev(struct inode *inode, struct file *file); +static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); +static loff_t i810_llseek(struct file *file, loff_t offset, int origin); + +extern __inline__ unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg); +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data); + +static struct i810_channel *i810_alloc_pcm_channel(struct i810_card *card) +{ + if(card->channel[1].used==1) + return NULL; + card->channel[1].used=1; + card->channel[1].offset = 0; + card->channel[1].port = 0x10; + card->channel[1].num=1; + return &card->channel[1]; +} + +static struct i810_channel *i810_alloc_rec_pcm_channel(struct i810_card *card) +{ + if(card->channel[0].used==1) + return NULL; + card->channel[0].used=1; + card->channel[0].offset = 0; + card->channel[0].port = 0x00; + card->channel[1].num=0; + return &card->channel[0]; +} + +static void i810_free_pcm_channel(struct i810_card *card, int channel) +{ + card->channel[channel].used=0; +} + +/* set playback sample rate */ +static unsigned int i810_set_dac_rate(struct i810_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u16 dacp, rp; + struct ac97_codec *codec=state->card->ac97_codec[0]; + + if(!(state->card->ac97_features&0x0001)) + return 48000; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + /* Power down the DAC */ + dacp=i810_ac97_get(codec, AC97_POWER_CONTROL); + i810_ac97_set(codec, AC97_POWER_CONTROL, dacp|0x0200); + + /* Load the rate and read the effective rate */ + i810_ac97_set(codec, AC97_PCM_FRONT_DAC_RATE, rate); + rp=i810_ac97_get(codec, AC97_PCM_FRONT_DAC_RATE); + + printk("DAC rate set to %d Returned %d\n", + rate, (int)rp); + + rate=rp; + + /* Power it back up */ + i810_ac97_set(codec, AC97_POWER_CONTROL, dacp); + + dmabuf->rate = rate; +#ifdef DEBUG + printk("i810_audio: called i810_set_dac_rate : rate = %d\n", rate); +#endif + + return rate; +} + +/* set recording sample rate */ +static unsigned int i810_set_adc_rate(struct i810_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dmabuf; + + if (rate > 48000) + rate = 48000; + if (rate < 48000) + rate = 48000; + + dmabuf->rate = rate; +#ifdef DEBUG + printk("i810_audio: called i810_set_adc_rate : rate = %d\n", rate); +#endif + return rate; +} + +/* prepare channel attributes for playback */ +static void i810_play_setup(struct i810_state *state) +{ +// struct dmabuf *dmabuf = &state->dmabuf; +// struct i810_channel *channel = dmabuf->channel; + /* Fixed format. .. */ + //if (dmabuf->fmt & I810_FMT_16BIT) + //if (dmabuf->fmt & I810_FMT_STEREO) +} + +/* prepare channel attributes for recording */ +static void i810_rec_setup(struct i810_state *state) +{ +// u16 w; +// struct i810_card *card = state->card; +// struct dmabuf *dmabuf = &state->dmabuf; +// struct i810_channel *channel = dmabuf->channel; + + /* Enable AC-97 ADC (capture) */ +// if (dmabuf->fmt & I810_FMT_16BIT) { +// if (dmabuf->fmt & I810_FMT_STEREO) +} + + +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ + +extern __inline__ unsigned i810_get_dma_addr(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + u32 offset; + struct i810_channel *c = dmabuf->channel; + + if (!dmabuf->enable) + return 0; + offset = inb(state->card->iobase+c->port+OFF_CIV); + offset++; + offset&=31; + /* Offset has to compensate for the fact we finished the segment + on the IRQ so we are at next_segment,0 */ +// printk("BANK%d ", offset); + offset *= (dmabuf->dmasize/SG_LEN); +// printk("DMASZ=%d", dmabuf->dmasize); +// offset += 1024-(4*inw(state->card->iobase+c->port+OFF_PICB)); +// printk("OFF%d ", offset); + return offset; +} + +static void resync_dma_ptrs(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_channel *c = dmabuf->channel; + int offset; + + offset = inb(state->card->iobase+c->port+OFF_CIV); + offset *= (dmabuf->dmasize/SG_LEN); + + dmabuf->hwptr=dmabuf->swptr = offset; +} + +/* Stop recording (lock held) */ +extern __inline__ void __stop_adc(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~ADC_RUNNING; + outb(0, card->iobase + PI_CR); +} + +static void stop_adc(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_adc(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count < (signed)dmabuf->dmasize) && dmabuf->ready) { + dmabuf->enable |= ADC_RUNNING; + outb((1<<4) | 1<<2 | 1, card->iobase + PI_CR); + } + spin_unlock_irqrestore(&card->lock, flags); +} + +/* stop playback (lock held) */ +extern __inline__ void __stop_dac(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + + dmabuf->enable &= ~DAC_RUNNING; + outb(0, card->iobase + PO_CR); +} + +static void stop_dac(struct i810_state *state) +{ + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + __stop_dac(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_dac(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct i810_card *card = state->card; + unsigned long flags; + + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { + if(!(dmabuf->enable&DAC_RUNNING)) + { + dmabuf->enable |= DAC_RUNNING; + outb((1<<4) | 1<<2 | 1, card->iobase + PO_CR); + } + } + spin_unlock_irqrestore(&card->lock, flags); +} + +#define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) +#define DMABUF_MINORDER 1 + +/* allocate DMA buffer, playback and recording buffer should be allocated seperately */ +static int alloc_dmabuf(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + void *rawbuf; + int order; + unsigned long map, mapend; + + /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) + if ((rawbuf = pci_alloc_consistent(state->card->pci_dev, + PAGE_SIZE << order, + &dmabuf->dma_handle))) + break; + if (!rawbuf) + return -ENOMEM; + +#ifdef DEBUG + printk("i810_audio: allocated %ld (order = %d) bytes at %p\n", + PAGE_SIZE << order, order, rawbuf); +#endif + + dmabuf->ready = dmabuf->mapped = 0; + dmabuf->rawbuf = rawbuf; + dmabuf->buforder = order; + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1); + for (map = MAP_NR(rawbuf); map <= mapend; map++) + set_bit(PG_reserved, &mem_map[map].flags); + + return 0; +} + +/* free DMA buffer */ +static void dealloc_dmabuf(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long map, mapend; + + if (dmabuf->rawbuf) { + /* undo marking the pages as reserved */ + mapend = MAP_NR(dmabuf->rawbuf + (PAGE_SIZE << dmabuf->buforder) - 1); + for (map = MAP_NR(dmabuf->rawbuf); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + pci_free_consistent(state->card->pci_dev, PAGE_SIZE << dmabuf->buforder, + dmabuf->rawbuf, dmabuf->dma_handle); + } + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; +} + +static int prog_dmabuf(struct i810_state *state, unsigned rec) +{ + struct dmabuf *dmabuf = &state->dmabuf; + struct sg_item *sg; + unsigned bytepersec; + unsigned bufsize; + unsigned long flags; + int ret; + unsigned fragsize; + int i; + + spin_lock_irqsave(&state->card->lock, flags); + resync_dma_ptrs(state); + dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* allocate DMA buffer if not allocated yet */ + if (!dmabuf->rawbuf) + if ((ret = alloc_dmabuf(state))) + return ret; + + /* FIXME: figure out all this OSS fragment stuff */ + bytepersec = dmabuf->rate << sample_shift[dmabuf->fmt]; + bufsize = PAGE_SIZE << dmabuf->buforder; + if (dmabuf->ossfragshift) { + if ((1000 << dmabuf->ossfragshift) < bytepersec) + dmabuf->fragshift = ld2(bytepersec/1000); + else + dmabuf->fragshift = dmabuf->ossfragshift; + } else { + /* lets hand out reasonable big ass buffers by default */ + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); + } + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { + dmabuf->fragshift--; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + } + dmabuf->fragsize = 1 << dmabuf->fragshift; + if (dmabuf->ossmaxfrags >= 4 && dmabuf->ossmaxfrags < dmabuf->numfrag) + dmabuf->numfrag = dmabuf->ossmaxfrags; + dmabuf->fragsamples = dmabuf->fragsize >> sample_shift[dmabuf->fmt]; + dmabuf->dmasize = dmabuf->numfrag << dmabuf->fragshift; + + memset(dmabuf->rawbuf, (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); + + /* + * Now set up the ring + */ + + sg=&dmabuf->channel->sg[0]; + fragsize = bufsize / SG_LEN; + + /* + * Load up 32 sg entries and take an interrupt at half + * way (we might want more interrupts later..) + */ + + for(i=0;i<32;i++) + { + sg->busaddr=virt_to_bus(dmabuf->rawbuf+fragsize*i); + sg->control=(fragsize>>1); + sg->control|=CON_IOC; + sg++; + } + spin_lock_irqsave(&state->card->lock, flags); + outl(virt_to_bus(&dmabuf->channel->sg[0]), state->card->iobase+dmabuf->channel->port+OFF_BDBAR); + outb(16, state->card->iobase+dmabuf->channel->port+OFF_LVI); + outb(0, state->card->iobase+dmabuf->channel->port+OFF_CIV); + if (rec) { + i810_rec_setup(state); + } else { + i810_play_setup(state); + } + spin_unlock_irqrestore(&state->card->lock, flags); + + /* set the ready flag for the dma buffer */ + dmabuf->ready = 1; + +#ifdef DEBUG + printk("i810_audio: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, " + "fragsize = %d dmasize = %d\n", + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); +#endif + + return 0; +} + +/* we are doing quantum mechanics here, the buffer can only be empty, half or full filled i.e. + |------------|------------| or |xxxxxxxxxxxx|------------| or |xxxxxxxxxxxx|xxxxxxxxxxxx| + but we almost always get this + |xxxxxx------|------------| or |xxxxxxxxxxxx|xxxxx-------| + so we have to clear the tail space to "silence" + |xxxxxx000000|------------| or |xxxxxxxxxxxx|xxxxxx000000| +*/ +static void i810_clear_tail(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned swptr; + unsigned char silence = (dmabuf->fmt & I810_FMT_16BIT) ? 0 : 0x80; + unsigned int len; + unsigned long flags; + + spin_lock_irqsave(&state->card->lock, flags); + swptr = dmabuf->swptr; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (swptr == 0 || swptr == dmabuf->dmasize / 2 || swptr == dmabuf->dmasize) + return; + + if (swptr < dmabuf->dmasize/2) + len = dmabuf->dmasize/2 - swptr; + else + len = dmabuf->dmasize - swptr; + + memset(dmabuf->rawbuf + swptr, silence, len); + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr += len; + dmabuf->count += len; + spin_unlock_irqrestore(&state->card->lock, flags); + + /* restart the dma machine in case it is halted */ + start_dac(state); +} + +static int drain_dac(struct i810_state *state, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned long tmo; + int count; + + if (dmabuf->mapped || !dmabuf->ready) + return 0; + + add_wait_queue(&dmabuf->wait, &wait); + for (;;) { + /* It seems that we have to set the current state to TASK_INTERRUPTIBLE + every time to make the process really go to sleep */ + current->state = TASK_INTERRUPTIBLE; + + spin_lock_irqsave(&state->card->lock, flags); + count = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (count <= 0) + break; + + if (signal_pending(current)) + break; + + if (nonblock) { + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + return -EBUSY; + } + + /* No matter how much data left in the buffer, we have to wait untill + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / dmabuf->rate; + tmo >>= sample_shift[dmabuf->fmt]; + if (!schedule_timeout(tmo ? tmo : 1) && tmo){ + printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n"); + break; + } + } + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; +} + +/* update buffer manangement pointers, especially, dmabuf->count and dmabuf->hwptr */ +static void i810_update_ptr(struct i810_state *state) +{ + struct dmabuf *dmabuf = &state->dmabuf; + unsigned hwptr, swptr; + int clear_cnt = 0; + int diff; + unsigned char silence; +// unsigned half_dmasize; + + /* update hardware pointer */ + hwptr = i810_get_dma_addr(state); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; +// printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff); + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + + /* error handling and process wake up for DAC */ + if (dmabuf->enable == ADC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count -= diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count += diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_adc(state); + dmabuf->error++; + } + else if (!dmabuf->endcleared) { + swptr = dmabuf->swptr; + silence = (dmabuf->fmt & I810_FMT_16BIT ? 0 : 0x80); + if (dmabuf->count < (signed) dmabuf->fragsize) + { + clear_cnt = dmabuf->fragsize; + if ((swptr + clear_cnt) > dmabuf->dmasize) + clear_cnt = dmabuf->dmasize - swptr; + memset (dmabuf->rawbuf + swptr, silence, clear_cnt); + dmabuf->endcleared = 1; + } + } + /* since dma machine only interrupts at ESO and ESO/2, we sure have at + least half of dma buffer free, so wake up the process unconditionally */ + wake_up(&dmabuf->wait); + } + } + /* error handling and process wake up for DAC */ + if (dmabuf->enable == DAC_RUNNING) { + if (dmabuf->mapped) { + dmabuf->count += diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { + dmabuf->count -= diff; + + if (dmabuf->count < 0 || dmabuf->count > dmabuf->dmasize) { + /* buffer underrun or buffer overrun, we have no way to recover + it here, just stop the machine and let the process force hwptr + and swptr to sync */ + __stop_dac(state); + printk("DMA overrun on send\n"); + dmabuf->error++; + } + /* since dma machine only interrupts at ESO and ESO/2, we sure have at + least half of dma buffer free, so wake up the process unconditionally */ + wake_up(&dmabuf->wait); + } + } +} + +static void i810_channel_interrupt(struct i810_card *card) +{ + int i; + +// printk("CHANNEL IRQ .. "); + for(i=0;istates[i]; + struct i810_channel *c; + unsigned long port = card->iobase; + u16 status; + + if(!state) + continue; + if(!state->dmabuf.ready) + continue; + c=state->dmabuf.channel; + + port+=c->port; + +// printk("PORT %lX (", port); + + status = inw(port + OFF_SR); + +// printk("ST%d ", status); + + if(status & DMA_INT_LVI) + { + /* Back to the start */ +// printk("LVI - STOP"); + outb((inb(port+OFF_CIV)-1)&31, port+OFF_LVI); + i810_update_ptr(state); + outb(0, port + OFF_CR); + } + if(status & DMA_INT_COMPLETE) + { + int x; + /* Kepe the card chasing its tail */ + outb(x=((inb(port+OFF_CIV)-1)&31), port+OFF_LVI); + i810_update_ptr(state); +// printk("COMP%d ",x); + } +// printk(")"); + outw(status & DMA_INT_MASK, port + OFF_SR); + } +// printk("\n"); +} + +static void i810_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct i810_card *card = (struct i810_card *)dev_id; + u32 status; + + spin_lock(&card->lock); + + status = inl(card->iobase + GLOB_STA); + if(!(status & INT_MASK)) + return; /* not for us */ + +// printk("Interrupt %X: ", status); + if(status & (INT_PO|INT_PI|INT_MC)) + i810_channel_interrupt(card); + + /* clear 'em */ + outl(status & INT_MASK, card->iobase + GLOB_STA); + spin_unlock(&card->lock); +} + +static loff_t i810_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be copied to + the user's buffer. it is filled by the dma machine and drained by this loop. */ +static ssize_t i810_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + +#ifdef DEBUG + printk("i810_audio: i810_read called, count = %d\n", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count > (signed) dmabuf->dmasize) { + /* buffer overrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr, make process flush the buffer */ + dmabuf->count = dmabuf->dmasize; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is empty, start the dma machine and wait for data to be + recorded */ + start_adc(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + return ret; + } + /* No matter how much space left in the buffer, we have to wait untill + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer overrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(KERN_ERR "i810_audio: recording schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer overrun, we delay the recovery untill next time the + while loop begin and we REALLY have space to record */ + } + if (signal_pending(current)) { + ret = ret ? ret : -ERESTARTSYS; + return ret; + } + continue; + } + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { + if (!ret) ret = -EFAULT; + return ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count -= cnt; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(state); + } + return ret; +} + +/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to + the soundcard. it is drained by the dma machine and filled by this loop. */ +static ssize_t i810_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + +#ifdef DEBUG + printk("i810_audio: i810_write called, count = %d\n", count); +#endif + + if (ppos != &file->f_pos) + return -ESPIPE; + if (dmabuf->mapped) + return -ENXIO; + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + ret = 0; + + while (count > 0) { + spin_lock_irqsave(&state->card->lock, flags); + if (dmabuf->count < 0) { + /* buffer underrun, we are recovering from sleep_on_timeout, + resync hwptr and swptr */ + dmabuf->count = 0; + dmabuf->swptr = dmabuf->hwptr; + } + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count + cnt > dmabuf->dmasize) + cnt = dmabuf->dmasize - dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + + if (cnt > count) + cnt = count; + if (cnt <= 0) { + unsigned long tmo; + /* buffer is full, start the dma machine and wait for data to be + played */ + start_dac(state); + if (file->f_flags & O_NONBLOCK) { + if (!ret) ret = -EAGAIN; + return ret; + } + /* No matter how much data left in the buffer, we have to wait untill + CSO == ESO/2 or CSO == ESO when address engine interrupts */ + tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2); + tmo >>= sample_shift[dmabuf->fmt]; + /* There are two situations when sleep_on_timeout returns, one is when + the interrupt is serviced correctly and the process is waked up by + ISR ON TIME. Another is when timeout is expired, which means that + either interrupt is NOT serviced correctly (pending interrupt) or it + is TOO LATE for the process to be scheduled to run (scheduler latency) + which results in a (potential) buffer underrun. And worse, there is + NOTHING we can do to prevent it. */ + if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) { +#ifdef DEBUG + printk(KERN_ERR "i810_audio: playback schedule timeout, " + "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); +#endif + /* a buffer underrun, we delay the recovery untill next time the + while loop begin and we REALLY have data to play */ + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + return ret; + } + continue; + } + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + return ret; + } + + swptr = (swptr + cnt) % dmabuf->dmasize; + + spin_lock_irqsave(&state->card->lock, flags); + dmabuf->swptr = swptr; + dmabuf->count += cnt; + dmabuf->endcleared = 0; + spin_unlock_irqrestore(&state->card->lock, flags); + + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(state); + } + return ret; +} + +static unsigned int i810_poll(struct file *file, struct poll_table_struct *wait) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &dmabuf->wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &dmabuf->wait, wait); + + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + if (file->f_mode & FMODE_READ) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (dmabuf->mapped) { + if (dmabuf->count >= (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&state->card->lock, flags); + + return mask; +} + +static int i810_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + int ret; + unsigned long size; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(state, 0)) != 0) + return ret; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(state, 1)) != 0) + return ret; + } else + return -EINVAL; + + if (vma->vm_pgoff != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << dmabuf->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) + return -EAGAIN; + dmabuf->mapped = 1; + + return 0; +} + +static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + + mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) || + ((file->f_mode & FMODE_READ) && dmabuf->mapped); +#ifdef DEBUG + printk("i810_audio: i810_ioctl, command = %2d, arg = 0x%08x\n", + _IOC_NR(cmd), arg ? *(int *)arg : 0); +#endif + + switch (cmd) + { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_RESET: + /* FIXME: spin_lock ? */ + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + synchronize_irq(); + dmabuf->ready = 0; + resync_dma_ptrs(state); + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + synchronize_irq(); + resync_dma_ptrs(state); + dmabuf->ready = 0; + dmabuf->swptr = dmabuf->hwptr = 0; + dmabuf->count = dmabuf->total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(state, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SPEED: /* set smaple rate */ + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + i810_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + } + return put_user(dmabuf->rate, (int *)arg); + + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ + get_user_ret(val, (int *)arg, -EFAULT); + if(val==0) + return -EINVAL; + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + dmabuf->fmt = I810_FMT_STEREO; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + dmabuf->fmt = I810_FMT_STEREO; + } + return 0; + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(state, 0))) + return val; + return put_user(dmabuf->fragsize, (int *)arg); + } + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + return put_user(dmabuf->fragsize, (int *)arg); + } + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Select sample format */ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + } + } + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dmabuf->ready = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dmabuf->ready = 0; + } + } + return put_user(2, (int *)arg); + + case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if (dmabuf->subdivision) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + dmabuf->subdivision = val; + return 0; + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + + dmabuf->ossfragshift = val & 0xffff; + dmabuf->ossmaxfrags = (val >> 16) & 0xffff; + if (dmabuf->ossfragshift < 4) + dmabuf->ossfragshift = 4; + if (dmabuf->ossfragshift > 15) + dmabuf->ossfragshift = 15; + if (dmabuf->ossmaxfrags < 4) + dmabuf->ossmaxfrags = 4; + + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!dmabuf->enable && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->dmasize - dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!dmabuf->enable && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + abinfo.fragsize = dmabuf->fragsize; + abinfo.bytes = dmabuf->count; + abinfo.fragstotal = dmabuf->numfrag; + abinfo.fragments = abinfo.bytes >> dmabuf->fragshift; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND, + (int *)arg); + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && dmabuf->enable) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && dmabuf->enable) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) + return ret; + start_adc(state); + } else + stop_adc(state); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) + return ret; + start_dac(state); + } else + stop_dac(state); + } + return 0; + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + cinfo.bytes = dmabuf->total_bytes; + cinfo.blocks = dmabuf->count >> dmabuf->fragshift; + cinfo.ptr = dmabuf->hwptr; + if (dmabuf->mapped) + dmabuf->count &= dmabuf->fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_SETDUPLEX: + return -EINVAL; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + i810_update_ptr(state); + val = dmabuf->count; + spin_unlock_irqrestore(&state->card->lock, flags); + return put_user(val, (int *)arg); + + case SOUND_PCM_READ_RATE: + return put_user(dmabuf->rate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((dmabuf->fmt & I810_FMT_STEREO) ? 2 : 1, + (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user(AFMT_S16_LE, (int *)arg); + + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_WRITE_FILTER: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -EINVAL; +} + +static int i810_open(struct inode *inode, struct file *file) +{ + int i = 0; + int minor = MINOR(inode->i_rdev); + struct i810_card *card = devs; + struct i810_state *state = NULL; + struct dmabuf *dmabuf = NULL; + + /* find an avaiable virtual channel (instance of /dev/dsp) */ + while (card != NULL) { + for (i = 0; i < NR_HW_CH; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct i810_state *) + kmalloc(sizeof(struct i810_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + memset(state, 0, sizeof(struct i810_state)); + dmabuf = &state->dmabuf; + goto found_virt; + } + } + card = card->next; + } + /* no more virtual channel avaiable */ + if (!state) + return -ENODEV; + + found_virt: + /* found a free virtual channel, allocate hardware channels */ + if(file->f_mode & FMODE_READ) + dmabuf->channel = card->alloc_rec_pcm_channel(card); + else + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { + kfree (card->states[i]); + card->states[i] = NULL;; + return -ENODEV; + } + + /* initialize the virtual channel */ + state->virt = i; + state->card = card; + state->magic = I810_STATE_MAGIC; + init_waitqueue_head(&dmabuf->wait); + init_MUTEX(&state->open_sem); + file->private_data = state; + + down(&state->open_sem); + + /* set default sample format. According to OSS Programmer's Guide /dev/dsp + should be default to unsigned 8-bits, mono, with sample rate 8kHz and + /dev/dspW will accept 16-bits sample */ + if (file->f_mode & FMODE_WRITE) { + dmabuf->fmt &= ~I810_FMT_MASK; + dmabuf->fmt |= I810_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + i810_set_dac_rate(state, 48000); + } + + if (file->f_mode & FMODE_READ) { + dmabuf->fmt &= ~I810_FMT_MASK; + dmabuf->fmt |= I810_FMT_16BIT; + dmabuf->ossfragshift = 0; + dmabuf->ossmaxfrags = 0; + dmabuf->subdivision = 0; + i810_set_adc_rate(state, 48000); + } + + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + up(&state->open_sem); + + MOD_INC_USE_COUNT; + return 0; +} + +static int i810_release(struct inode *inode, struct file *file) +{ + struct i810_state *state = (struct i810_state *)file->private_data; + struct dmabuf *dmabuf = &state->dmabuf; + + if (file->f_mode & FMODE_WRITE) { + i810_clear_tail(state); + drain_dac(state, file->f_flags & O_NONBLOCK); + } + + /* stop DMA state machine and free DMA buffers/channels */ + down(&state->open_sem); + + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + dealloc_dmabuf(state); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); + } + + kfree(state->card->states[state->virt]); + state->card->states[state->virt] = NULL; + state->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + + /* we're covered by the open_sem */ + up(&state->open_sem); + + MOD_DEC_USE_COUNT; + return 0; +} + +static /*const*/ struct file_operations i810_audio_fops = { + llseek: i810_llseek, + read: i810_read, + write: i810_write, + poll: i810_poll, + ioctl: i810_ioctl, + mmap: i810_mmap, + open: i810_open, + release: i810_release, +}; + +/* Write AC97 codec registers */ + +static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg) +{ + struct i810_card *card = dev->private_data; + int count = 100; + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + return inw(card->ac97base + (reg&0x7f)); +} + +static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data) +{ + struct i810_card *card = dev->private_data; + int count = 100; + + while(count-- && (inb(card->iobase + CAS) & 1)) + udelay(1); + outw(data, card->ac97base + (reg&0x7f)); +} + + +/* OSS /dev/mixer file operation methods */ + +static int i810_open_mixdev(struct inode *inode, struct file *file) +{ + int i; + int minor = MINOR(inode->i_rdev); + struct i810_card *card = devs; + + for (card = devs; card != NULL; card = card->next) + for (i = 0; i < NR_AC97; i++) + if (card->ac97_codec[i] != NULL && + card->ac97_codec[i]->dev_mixer == minor) + goto match; + + if (!card) + return -ENODEV; + + match: + file->private_data = card->ac97_codec[i]; + + MOD_INC_USE_COUNT; + return 0; +} + +static int i810_release_mixdev(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + return codec->mixer_ioctl(codec, cmd, arg); +} + +static /*const*/ struct file_operations i810_mixer_fops = { + llseek: i810_llseek, + ioctl: i810_ioctl_mixdev, + open: i810_open_mixdev, + release: i810_release_mixdev, +}; + +/* AC97 codec initialisation. */ +static int __init i810_ac97_init(struct i810_card *card) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + u16 eid; + + outl(0, card->iobase + GLOB_CNT); + udelay(500); + outl(1<<1, card->iobase + GLOB_CNT); + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(codec, 0, sizeof(struct ac97_codec)); + + /* initialize some basic codec information, other fields will be filled + in ac97_probe_codec */ + codec->private_data = card; + codec->id = num_ac97; + + codec->codec_read = i810_ac97_get; + codec->codec_write = i810_ac97_set; + + if (ac97_probe_codec(codec) == 0) + break; + + eid = i810_ac97_get(codec, AC97_EXTENDED_ID); + + if(eid==0xFFFFFF) + { + printk(KERN_WARNING "i810_audio: no codec attached ?\n"); + kfree(codec); + break; + } + + card->ac97_features = eid; + + if(!(eid&0x0001)) + printk(KERN_WARNING "i810_audio: only 48Khz playback available.\n"); + + if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) { + printk(KERN_ERR "i810_audio: couldn't register mixer!\n"); + kfree(codec); + break; + } + + /* Now check the codec for useful features to make up for + the dumbness of the 810 hardware engine */ + + card->ac97_codec[num_ac97] = codec; + + /* if there is no secondary codec at all, don't probe any more */ + if (!ready_2nd) + return num_ac97+1; + } + return num_ac97; +} + +/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered + untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */ + +static int __init i810_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) +{ + struct i810_card *card; + + if (!pci_dma_supported(pci_dev, I810_DMA_MASK)) { + printk(KERN_ERR "intel810: architecture does not support" + " 32bit PCI busmaster DMA\n"); + return -ENODEV; + } + + if ((card = kmalloc(sizeof(struct i810_card), GFP_KERNEL)) == NULL) { + printk(KERN_ERR "i810_audio: out of memory\n"); + return -ENOMEM; + } + memset(card, 0, sizeof(*card)); + + card->iobase = pci_dev->resource[1].start; + card->ac97base = pci_dev->resource[0].start; + card->pci_dev = pci_dev; + card->pci_id = pci_id->device; + card->irq = pci_dev->irq; + card->next = devs; + card->magic = I810_CARD_MAGIC; + spin_lock_init(&card->lock); + devs = card; + + pci_set_master(pci_dev); + pci_enable_device(pci_dev); + + printk(KERN_INFO "i810: %s found at IO 0x%04lx and 0x%04lx, IRQ %d\n", + card_names[pci_id->driver_data], card->iobase, card->ac97base, + card->irq); + + card->alloc_pcm_channel = i810_alloc_pcm_channel; + card->alloc_rec_pcm_channel = i810_alloc_rec_pcm_channel; + card->free_pcm_channel = i810_free_pcm_channel; + + /* claim our iospace and irq */ + request_region(card->iobase, 64, card_names[pci_id->driver_data]); + request_region(card->ac97base, 256, card_names[pci_id->driver_data]); + + if (request_irq(card->irq, &i810_interrupt, SA_SHIRQ, + card_names[pci_id->driver_data], card)) { + printk(KERN_ERR "i810_audio: unable to allocate irq %d\n", card->irq); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + kfree(card); + return -ENODEV; + } + /* register /dev/dsp */ + if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) { + printk(KERN_ERR "i810_audio: couldn't register DSP device!\n"); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + + + /* initialize AC97 codec and register /dev/mixer */ + if (i810_ac97_init(card) <= 0) { + unregister_sound_dsp(card->dev_audio); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + free_irq(card->irq, card); + kfree(card); + return -ENODEV; + } + pci_dev->driver_data = card; + pci_dev->dma_mask = I810_DMA_MASK; + +// printk("resetting codec?\n"); + outl(0, card->iobase + GLOB_CNT); + udelay(500); +// printk("bringing it back?\n"); + outl(1<<1, card->iobase + GLOB_CNT); + return 0; +} + +static void __exit i810_remove(struct pci_dev *pci_dev) +{ + int i; + struct i810_card *card = pci_dev->driver_data; + /* free hardware resources */ + free_irq(card->irq, devs); + release_region(card->iobase, 64); + release_region(card->ac97base, 256); + + /* unregister audio devices */ + for (i = 0; i < NR_AC97; i++) + if (devs->ac97_codec[i] != NULL) { + unregister_sound_mixer(card->ac97_codec[i]->dev_mixer); + kfree (card->ac97_codec[i]); + } + unregister_sound_dsp(card->dev_audio); + kfree(card); +} + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Intel 810 audio support"); + +#define I810_MODULE_NAME "intel810_audio" + +static struct pci_driver i810_pci_driver = { + name: I810_MODULE_NAME, + id_table: i810_pci_tbl, + probe: i810_probe, + remove: i810_remove, +}; + +static int __init i810_init_module (void) +{ + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + + printk(KERN_INFO "Intel 810 + AC97 Audio, version " + DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); + + if (!pci_register_driver(&i810_pci_driver)) { + pci_unregister_driver(&i810_pci_driver); + return -ENODEV; + } + return 0; +} + +static void __exit i810_cleanup_module (void) +{ + pci_unregister_driver(&i810_pci_driver); +} + +module_init(i810_init_module); +module_exit(i810_cleanup_module); diff --git a/drivers/sound/maestro.c b/drivers/sound/maestro.c index 8561df555f31..a526a99c0f9e 100644 --- a/drivers/sound/maestro.c +++ b/drivers/sound/maestro.c @@ -21,7 +21,7 @@ * Based heavily on SonicVibes.c: * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) * - * Heavily modified by Zach Brown based on lunch + * Heavily modified by Zach Brown based on lunch * with ESS engineers. Many thanks to Howard Kim for providing * contacts and hardware. Honorable mention goes to Eric * Brombaugh for all sorts of things. Best regards to the @@ -105,8 +105,28 @@ * being used now is quite dirty and assumes we're on a uni-processor * machine. Much of it will need to be cleaned up for SMP ACPI or * similar. + * + * We also pay attention to PCI power management now. The driver + * will power down units of the chip that it knows aren't needed. + * The WaveProcessor and company are only powered on when people + * have /dev/dsp*s open. On removal the driver will + * power down the maestro entirely. There could still be + * trouble with BIOSen that magically change power states + * themselves, but we'll see. * * History + * (still based on v0.14) Mar 29 2000 - Zach Brown + * move to 2.3 power management interface, which + * required hacking some suspend/resume/check paths + * make static compilation work + * v0.14 - Jan 28 2000 - Zach Brown + * add PCI power management through ACPI regs. + * we now shut down on machine reboot/halt + * leave scary PCI config items alone (isa stuff, mostly) + * enable 1921s, it seems only mine was broke. + * fix swapped left/right pcm dac. har har. + * up bob freq, increase buffers, fix pointers at underflow + * silly compilation problems * v0.13 - Nov 18 1999 - Zach Brown * fix nec Versas? man would that be cool. * v0.12 - Nov 12 1999 - Zach Brown @@ -162,13 +182,11 @@ * bob freq code, region sanity, jitter sync fix; all from Eric * * TODO - * some people get indir reg timeouts? * fix bob frequency * endianness * do smart things with ac97 2.0 bits. * docking and dual codecs and 978? * leave 54->61 open - * resolve 2.3/2.2 stuff * * it also would be fun to have a mode that would not use pci dma at all * but would copy into the wavecache on board memory and use that @@ -190,6 +208,7 @@ #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) #define SILLY_OFFSET(VMA) ((VMA)->vm_offset) + #else #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start) @@ -197,6 +216,7 @@ #define SILLY_MAKE_INIT(FUNC) __init FUNC #define SILLY_OFFSET(VMA) ((VMA)->vm_pgoff) + #endif #include @@ -213,15 +233,12 @@ #include #include #include +#include #include #include #include static int maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *d); -static int in_suspend=0; -wait_queue_head_t suspend_queue; -static void check_suspend(void); -#define CHECK_SUSPEND check_suspend(); #include "maestro.h" @@ -231,14 +248,18 @@ static void check_suspend(void); #ifdef M_DEBUG static int debug=0; -static int dsps_order=0; #define M_printk(args...) {if (debug) printk(args);} #else #define M_printk(x) #endif +/* we try to setup 2^(dsps_order) /dev/dsp devices */ +static int dsps_order=0; +/* wether or not we mess around with power management */ +static int use_pm=2; /* set to 1 for force */ + /* --------------------------------------------------------------------- */ -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.14" #ifndef PCI_VENDOR_ESS #define PCI_VENDOR_ESS 0x125D @@ -282,6 +303,46 @@ static int dsps_order=0; #define NR_APUS 64 #define NR_APU_REGS 16 +/* acpi states */ +enum { + ACPI_D0=0, + ACPI_D1, + ACPI_D2, + ACPI_D3 +}; + +/* bits in the acpi masks */ +#define ACPI_12MHZ ( 1 << 15) +#define ACPI_24MHZ ( 1 << 14) +#define ACPI_978 ( 1 << 13) +#define ACPI_SPDIF ( 1 << 12) +#define ACPI_GLUE ( 1 << 11) +#define ACPI__10 ( 1 << 10) /* reserved */ +#define ACPI_PCIINT ( 1 << 9) +#define ACPI_HV ( 1 << 8) /* hardware volume */ +#define ACPI_GPIO ( 1 << 7) +#define ACPI_ASSP ( 1 << 6) +#define ACPI_SB ( 1 << 5) /* sb emul */ +#define ACPI_FM ( 1 << 4) /* fm emul */ +#define ACPI_RB ( 1 << 3) /* ringbus / aclink */ +#define ACPI_MIDI ( 1 << 2) +#define ACPI_GP ( 1 << 1) /* game port */ +#define ACPI_WP ( 1 << 0) /* wave processor */ + +#define ACPI_ALL (0xffff) +#define ACPI_SLEEP (~(ACPI_SPDIF|ACPI_ASSP|ACPI_SB|ACPI_FM| \ + ACPI_MIDI|ACPI_GP|ACPI_WP)) +#define ACPI_NONE (ACPI__10) + +/* these masks indicate which units we care about at + which states */ +u16 acpi_state_mask[] = { + [ACPI_D0] = ACPI_ALL, + [ACPI_D1] = ACPI_SLEEP, + [ACPI_D2] = ACPI_SLEEP, + [ACPI_D3] = ACPI_NONE +}; + static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; @@ -303,6 +364,10 @@ static int clock_freq[]={ [TYPE_MAESTRO2E] = (50000000L / 1024L) }; +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf); + +static struct notifier_block maestro_nb = {maestro_notifier, NULL, 0}; + /* --------------------------------------------------------------------- */ struct ess_state { @@ -357,6 +422,7 @@ struct ess_state { /* pointer to each dsp?s piece of the apu->src buffer page */ void *mixbuf; + }; struct ess_card { @@ -383,6 +449,11 @@ struct ess_card { unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; } mix; + int power_regs; + + int in_suspend; + wait_queue_head_t suspend_queue; + struct ess_state channels[MAX_DSPS]; u16 maestro_map[NR_IDRS]; /* Register map */ /* we have to store this junk so that we can come back from a @@ -397,7 +468,7 @@ struct ess_card { int dmaorder; /* hardware resources */ - struct pci_dev pcidev; /* uck.. */ + struct pci_dev *pcidev; u32 iobase; u32 irq; @@ -434,6 +505,8 @@ ld2(unsigned int x) /* --------------------------------------------------------------------- */ +static void check_suspend(struct ess_card *card); + static struct ess_card *devs = NULL; /* --------------------------------------------------------------------- */ @@ -443,14 +516,15 @@ static struct ess_card *devs = NULL; * ESS Maestro AC97 codec programming interface. */ -static void maestro_ac97_set(int io, u8 cmd, u16 val) +static void maestro_ac97_set(struct ess_card *card, u8 cmd, u16 val) { + int io = card->iobase; int i; /* * Wait for the codec bus to be free */ - CHECK_SUSPEND; + check_suspend(card); for(i=0;i<10000;i++) { @@ -466,13 +540,14 @@ static void maestro_ac97_set(int io, u8 cmd, u16 val) mdelay(1); } -static u16 maestro_ac97_get(int io, u8 cmd) +static u16 maestro_ac97_get(struct ess_card *card, u8 cmd) { + int io = card->iobase; int sanity=10000; u16 data; int i; - CHECK_SUSPEND; + check_suspend(card); /* * Wait for the codec bus to be free */ @@ -564,7 +639,7 @@ static int ac97_read_mixer(struct ess_card *card, int mixer) int ret=0; struct ac97_mixer_hw *mh = &ac97_hw[mixer]; - val = maestro_ac97_get(card->iobase , mh->offset); + val = maestro_ac97_get(card, mh->offset); if(AC97_STEREO_MASK & (1< log */ -unsigned char lin2log[101] = +static unsigned char lin2log[101] = { 0, 0 , 15 , 23 , 30 , 34 , 38 , 42 , 45 , 47 , 50 , 52 , 53 , 55 , 57 , 58 , 60 , 61 , 62 , @@ -649,19 +724,19 @@ static void ac97_write_mixer(struct ess_card *card,int mixer, unsigned int left, } else if (mixer == SOUND_MIXER_SPEAKER) { val = (((100 - left) * mh->scale) / 100) << 1; } else if (mixer == SOUND_MIXER_MIC) { - val = maestro_ac97_get(card->iobase , mh->offset) & ~0x801f; + val = maestro_ac97_get(card, mh->offset) & ~0x801f; val |= (((100 - left) * mh->scale) / 100); /* the low bit is optional in the tone sliders and masking it lets is avoid the 0xf 'bypass'.. */ } else if (mixer == SOUND_MIXER_BASS) { - val = maestro_ac97_get(card->iobase , mh->offset) & ~0x0f00; + val = maestro_ac97_get(card , mh->offset) & ~0x0f00; val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; } else if (mixer == SOUND_MIXER_TREBLE) { - val = maestro_ac97_get(card->iobase , mh->offset) & ~0x000f; + val = maestro_ac97_get(card , mh->offset) & ~0x000f; val |= (((100 - left) * mh->scale) / 100) & 0x000e; } - maestro_ac97_set(card->iobase , mh->offset, val); + maestro_ac97_set(card , mh->offset, val); M_printk(" -> %x\n",val); } @@ -708,7 +783,7 @@ static unsigned int ac97_oss_rm[] = { static int ac97_recmask_io(struct ess_card *card, int read, int mask) { - unsigned int val = ac97_oss_mask[ maestro_ac97_get(card->iobase, 0x1a) & 0x7 ]; + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card, 0x1a) & 0x7 ]; if (read) return val; @@ -723,7 +798,7 @@ ac97_recmask_io(struct ess_card *card, int read, int mask) M_printk("maestro: setting ac97 recmask to 0x%x\n",val); - maestro_ac97_set(card->iobase,0x1a,val); + maestro_ac97_set(card,0x1a,val); return 0; }; @@ -736,7 +811,7 @@ ac97_recmask_io(struct ess_card *card, int read, int mask) * The PT101 setup is untested. */ -static u16 maestro_ac97_init(struct ess_card *card, int iobase) +static u16 maestro_ac97_init(struct ess_card *card) { u16 vend1, vend2, caps; @@ -747,13 +822,13 @@ static u16 maestro_ac97_init(struct ess_card *card, int iobase) card->mix.write_mixer = ac97_write_mixer; card->mix.recmask_io = ac97_recmask_io; - vend1 = maestro_ac97_get(iobase, 0x7c); - vend2 = maestro_ac97_get(iobase, 0x7e); + vend1 = maestro_ac97_get(card, 0x7c); + vend2 = maestro_ac97_get(card, 0x7e); - caps = maestro_ac97_get(iobase, 0x00); + caps = maestro_ac97_get(card, 0x00); printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", - vend1,vend2,caps,maestro_ac97_get(iobase,0x26) & 0xf); + vend1,vend2,caps,maestro_ac97_get(card,0x26) & 0xf); if (! (caps & 0x4) ) { /* no bass/treble nobs */ @@ -765,10 +840,14 @@ static u16 maestro_ac97_init(struct ess_card *card, int iobase) switch ((long)(vend1 << 16) | vend2) { case 0x545200ff: /* TriTech */ /* no idea what this does */ - maestro_ac97_set(iobase,0x2a,0x0001); - maestro_ac97_set(iobase,0x2c,0x0000); - maestro_ac97_set(iobase,0x2c,0xffff); + maestro_ac97_set(card,0x2a,0x0001); + maestro_ac97_set(card,0x2c,0x0000); + maestro_ac97_set(card,0x2c,0xffff); break; +#if 0 /* i thought the problems I was seeing were with + the 1921, but apparently they were with the pci board + it was on, so this code is commented out. + lets see if this holds true. */ case 0x83847609: /* ESS 1921 */ /* writing to 0xe (mic) or 0x1a (recmask) seems to hang this codec */ @@ -776,20 +855,21 @@ static u16 maestro_ac97_init(struct ess_card *card, int iobase) card->mix.record_sources = 0; card->mix.recmask_io = NULL; #if 0 /* don't ask. I have yet to see what these actually do. */ - maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + maestro_ac97_set(card,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ udelay(20); - maestro_ac97_set(iobase,0x78,0x3002); + maestro_ac97_set(card,0x78,0x3002); udelay(20); - maestro_ac97_set(iobase,0x78,0x3802); + maestro_ac97_set(card,0x78,0x3802); udelay(20); #endif break; +#endif default: break; } - maestro_ac97_set(iobase, 0x1E, 0x0404); + maestro_ac97_set(card, 0x1E, 0x0404); /* null misc stuff */ - maestro_ac97_set(iobase, 0x20, 0x0000); + maestro_ac97_set(card, 0x20, 0x0000); return 0; } @@ -923,7 +1003,7 @@ static void maestro_write(struct ess_state *s, u16 reg, u16 data) { unsigned long flags; - CHECK_SUSPEND; + check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); __maestro_write(s->card,reg,data); @@ -944,7 +1024,7 @@ static u16 maestro_read(struct ess_state *s, u16 reg) if(READABLE_MAP & (1<card); spin_lock_irqsave(&s->card->lock,flags); __maestro_read(s->card,reg); @@ -1003,7 +1083,7 @@ static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) { unsigned long flags; - CHECK_SUSPEND; + check_suspend(s->card); if(channel&ESS_CHAN_HARD) channel&=~ESS_CHAN_HARD; @@ -1032,7 +1112,7 @@ static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) unsigned long flags; u16 v; - CHECK_SUSPEND; + check_suspend(s->card); if(channel&ESS_CHAN_HARD) channel&=~ESS_CHAN_HARD; @@ -1060,7 +1140,7 @@ static void wave_set_register(struct ess_state *s, u16 reg, u16 value) { long ioaddr = s->card->iobase; unsigned long flags; - CHECK_SUSPEND; + check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); @@ -1075,7 +1155,7 @@ static u16 wave_get_register(struct ess_state *s, u16 reg) long ioaddr = s->card->iobase; unsigned long flags; u16 value; - CHECK_SUSPEND; + check_suspend(s->card); spin_lock_irqsave(&s->card->lock,flags); outw(reg, ioaddr+0x10); @@ -1198,7 +1278,7 @@ extern inline void stop_adc(struct ess_state *s) } /* stop output apus */ -extern inline void stop_dac(struct ess_state *s) +static void stop_dac(struct ess_state *s) { /* XXX have to lock around this? */ if (! (s->enable & DAC_RUNNING)) return; @@ -1304,11 +1384,12 @@ ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size /* XXX think about endianess when writing these registers */ M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); - /* Load the buffer into the wave engine */ + /* start of sample */ apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); apu_set_register(ess, channel, 5, pa&0xFFFF); + /* sample end */ apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); - /* setting loop == sample len */ + /* setting loop len == sample len */ apu_set_register(ess, channel, 7, size); /* clear effects/env.. */ @@ -1328,7 +1409,7 @@ ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size if(mode&ESS_FMT_STEREO) { /* set panning: left or right */ - apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0)); + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0 : 0x10)); ess->apu_mode[channel] += 0x10; } else apu_set_register(ess, channel, 10, 0x8F08); @@ -1539,7 +1620,7 @@ static void start_bob(struct ess_state *s) int divide; /* XXX make freq selector much smarter, see calc_bob_rate */ - int freq = 150; /* requested frequency - calculate what we want here. */ + int freq = 200; /* compute ideal interrupt frequency for buffer size & play rate */ /* first, find best prescaler value to match freq */ @@ -1647,6 +1728,7 @@ prog_dmabuf(struct ess_state *s, unsigned rec) db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + /* this algorithm is a little nuts.. where did /1000 come from? */ bytepersec = rate << sample_shift[fmt]; bufs = PAGE_SIZE << db->buforder; if (db->ossfragshift) { @@ -1675,13 +1757,11 @@ prog_dmabuf(struct ess_state *s, unsigned rec) memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); spin_lock_irqsave(&s->lock, flags); - if (rec) { - ess_rec_setup(s, fmt, s->rateadc, - db->rawbuf, db->numfrag << db->fragshift); - } else { - ess_play_setup(s, fmt, s->ratedac, - db->rawbuf, db->numfrag << db->fragshift); - } + if (rec) + ess_rec_setup(s, fmt, s->rateadc, db->rawbuf, db->dmasize); + else + ess_play_setup(s, fmt, s->ratedac, db->rawbuf, db->dmasize); + spin_unlock_irqrestore(&s->lock, flags); db->ready = 1; @@ -1720,8 +1800,7 @@ ess_update_ptr(struct ess_state *s) /* oh boy should this all be re-written. everything in the current code paths think that the various counters/pointers are expressed in bytes to the user but we have two apus doing stereo stuff so we fix it up here.. it propogates to all the various - counters from here. Notice that this means that mono recording is very very - broken right now. */ + counters from here. */ if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; } else { @@ -1751,7 +1830,7 @@ ess_update_ptr(struct ess_state *s) hwptr = get_dmaa(s) % s->dma_dac.dmasize; /* the apu only reports the length it has seen, not the length of the memory that has been used (the WP - knows that */ + knows that) */ if ( ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK) == (ESS_FMT_STEREO|ESS_FMT_16BIT)) hwptr<<=1; @@ -1768,14 +1847,15 @@ ess_update_ptr(struct ess_state *s) s->dma_dac.count -= diff; /* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ if (s->dma_dac.count <= 0) { + M_printk("underflow! diff: %d count: %d hw: %d sw: %d\n", diff, s->dma_dac.count, + hwptr, s->dma_dac.swptr); /* FILL ME wrindir(s, SV_CIENABLE, s->enable); */ /* XXX how on earth can calling this with the lock held work.. */ stop_dac(s); /* brute force everyone back in sync, sigh */ s->dma_dac.count = 0; - s->dma_dac.swptr = 0; - s->dma_dac.hwptr = 0; + s->dma_dac.swptr = hwptr; s->dma_dac.error++; } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { clear_advance(s); @@ -1783,6 +1863,8 @@ ess_update_ptr(struct ess_state *s) } if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { wake_up(&s->dma_dac.wait); +/* printk("waking up DAC count: %d sw: %d hw: %d\n",s->dma_dac.count, s->dma_dac.swptr, + hwptr);*/ } } } @@ -2023,10 +2105,10 @@ static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int } static /*const*/ struct file_operations ess_mixer_fops = { - llseek: ess_llseek, - ioctl: ess_ioctl_mixdev, - open: ess_open_mixdev, - release: ess_release_mixdev, + llseek: ess_llseek, + ioctl: ess_ioctl_mixdev, + open: ess_open_mixdev, + release: ess_release_mixdev, }; /* --------------------------------------------------------------------- */ @@ -2153,8 +2235,7 @@ ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) goto rec_return_free; } if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { - if(! in_suspend) - printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, s->dma_adc.hwptr, s->dma_adc.swptr); stop_adc(s); @@ -2253,8 +2334,7 @@ ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) goto return_free; } if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { - if(! in_suspend) - printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + if(! s->card->in_suspend) printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, s->dma_dac.hwptr, s->dma_dac.swptr); stop_dac(s); @@ -2277,6 +2357,7 @@ ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) if (!ret) ret = -EFAULT; goto return_free; } +/* printk("wrote %d bytes at sw: %d cnt: %d while hw: %d\n",cnt, swptr, s->dma_dac.count, s->dma_dac.hwptr);*/ swptr = (swptr + cnt) % s->dma_dac.dmasize; @@ -2613,6 +2694,7 @@ static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u case SNDCTL_DSP_SETFRAGMENT: get_user_ret(val, (int *)arg, -EFAULT); + M_printk("maestro: SETFRAGMENT: %0x\n",val); if (file->f_mode & FMODE_READ) { s->dma_adc.ossfragshift = val & 0xffff; s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; @@ -2678,6 +2760,36 @@ set_base_registers(struct ess_state *s,void *vaddr) wave_set_register(s, 0x01FF , packed_phys); } +/* + * this guy makes sure we're in the right power + * state for what we want to be doing + */ +static void maestro_power(struct ess_card *card, int tostate) +{ + u16 active_mask = acpi_state_mask[tostate]; + u8 state; + + if(!use_pm) return; + + pci_read_config_byte(card->pcidev, card->power_regs+0x4, &state); + state&=3; + + /* make sure we're in the right state */ + if(state != tostate) { + M_printk(KERN_WARNING "maestro: dev %02x:%02x.%x switching from D%d to D%d\n", + card->pcidev->bus->number, + PCI_SLOT(card->pcidev->devfn), + PCI_FUNC(card->pcidev->devfn), + state,tostate); + pci_write_config_byte(card->pcidev, card->power_regs+0x4, tostate); + } + + /* and make sure the units we care about are on + XXX we might want to do this before state flipping? */ + pci_write_config_word(card->pcidev, 0x54, ~ active_mask); + pci_write_config_word(card->pcidev, 0x56, ~ active_mask); +} + /* we allocate a large power of two for all our memory. this is cut up into (not to scale :): |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | @@ -2690,7 +2802,7 @@ allocate_buffers(struct ess_state *s) unsigned long mapend,map; /* alloc as big a chunk as we can */ - for (order = (dsps_order + (15-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + for (order = (dsps_order + (16-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) break; @@ -2709,12 +2821,6 @@ allocate_buffers(struct ess_state *s) s->card->dmapages = rawbuf; s->card->dmaorder = order; - /* play bufs are in the same first region as record bufs */ - set_base_registers(s,rawbuf); - - M_printk("maestro: writing %lx (%lx) to the wp\n",virt_to_bus(rawbuf), - ((virt_to_bus(rawbuf))&0xFFE00000)>>12); - for(i=0;icard->channels[i]; @@ -2733,7 +2839,7 @@ allocate_buffers(struct ess_state *s) happily scribble away.. */ ess->mixbuf = rawbuf + (512 * (i+1)); - M_printk("maestro: setup apu %d: %p %p %p\n",i,ess->dma_dac.rawbuf, + M_printk("maestro: setup apu %d: dac: %p adc: %p mix: %p\n",i,ess->dma_dac.rawbuf, ess->dma_adc.rawbuf, ess->mixbuf); } @@ -2819,6 +2925,20 @@ ess_open(struct inode *inode, struct file *file) return -ENOMEM; } + /* we're covered by the open_sem */ + if( ! s->card->dsps_open ) { + maestro_power(s->card,ACPI_D0); + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + /* ok, lets write WC base regs now that we've + powered up the chip */ + M_printk("maestro: writing 0x%lx (bus 0x%lx) to the wp\n",virt_to_bus(s->card->dmapages), + ((virt_to_bus(s->card->dmapages))&0xFFE00000)>>12); + set_base_registers(s,s->card->dmapages); + if (file->f_mode & FMODE_READ) { /* fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); @@ -2842,13 +2962,6 @@ ess_open(struct inode *inode, struct file *file) set_fmt(s, fmtm, fmts); s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); - /* we're covered by the open_sem */ - if( ! s->card->dsps_open ) { - start_bob(s); - } - s->card->dsps_open++; - M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); - up(&s->open_sem); MOD_INC_USE_COUNT; return 0; @@ -2874,8 +2987,10 @@ ess_release(struct inode *inode, struct file *file) /* we're covered by the open_sem */ M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); if( --s->card->dsps_open <= 0) { + s->card->dsps_open = 0; stop_bob(s); free_buffers(s); + maestro_power(s->card,ACPI_D2); } up(&s->open_sem); wake_up(&s->open_wait); @@ -2884,56 +2999,42 @@ ess_release(struct inode *inode, struct file *file) } static struct file_operations ess_audio_fops = { - llseek: ess_llseek, - read: ess_read, - write: ess_write, - poll: ess_poll, - ioctl: ess_ioctl, - mmap: ess_mmap, - open: ess_open, - release: ess_release, + llseek: ess_llseek, + read: ess_read, + write: ess_write, + poll: ess_poll, + ioctl: ess_ioctl, + mmap: ess_mmap, + open: ess_open, + release: ess_release, }; static int maestro_config(struct ess_card *card) { - struct pci_dev *pcidev = &card->pcidev; + struct pci_dev *pcidev = card->pcidev; struct ess_state *ess = &card->channels[0]; int apu,iobase = card->iobase; u16 w; u32 n; - /* - * Disable ACPI + /* We used to muck around with pci config space that + * we had no business messing with. We don't know enough + * about the machine to know which DMA mode is appropriate, + * etc. We were guessing wrong on some machines and making + * them unhappy. We now trust in the BIOS to do things right, + * which almost certainly means a new host of problems will + * arise with broken BIOS implementations. screw 'em. + * We're already intolerant of machines that don't assign + * IRQs. */ - - pci_write_config_dword(pcidev, 0x54, 0x00000000); - pci_write_config_dword(pcidev, 0x56, 0x00000000); - /* - * Use TDMA for now. TDMA works on all boards, so while its - * not the most efficient its the simplest. - */ + /* do config work at full power */ + maestro_power(card,ACPI_D0); pci_read_config_word(pcidev, 0x50, &w); - /* Clear DMA bits */ - w&=~(1<<10|1<<9|1<<8); - - /* TDMA on */ - w|= (1<<8); - - /* - * Some of these are undocumented bits - */ - - w&=~(1<<13)|(1<<14); /* PIC Snoop mode bits */ - w&=~(1<<11); /* Safeguard off */ - w|= (1<<7); /* Posted write */ - w|= (1<<6); /* ISA timing on */ - /* XXX huh? claims to be reserved.. */ - w&=~(1<<5); /* Don't swap left/right */ - w&=~(1<<1); /* Subtractive decode off */ + w&=~(1<<5); /* Don't swap left/right (undoc)*/ pci_write_config_word(pcidev, 0x50, w); @@ -2946,19 +3047,9 @@ maestro_config(struct ess_card *card) w&=~(1<<6); /* Debounce off */ w&=~(1<<5); /* GPIO 4:5 */ w|= (1<<4); /* Disconnect from the CHI. Enabling this made a dell 7500 work. */ - w&=~(1<<3); /* IDMA off (undocumented) */ w&=~(1<<2); /* MIDI fix off (undoc) */ w&=~(1<<1); /* reserved, always write 0 */ - w&=~(1<<0); /* IRQ to ISA off (undoc) */ pci_write_config_word(pcidev, 0x52, w); - - /* - * DDMA off - */ - - pci_read_config_word(pcidev, 0x60, &w); - w&=~(1<<0); - pci_write_config_word(pcidev, 0x60, w); /* * Legacy mode @@ -2971,8 +3062,6 @@ maestro_config(struct ess_card *card) pci_write_config_word(pcidev, 0x40, w); - /* stake our claim on the iospace */ - request_region(iobase, 256, card_names[card->card_type]); sound_reset(iobase); @@ -3140,6 +3229,39 @@ maestro_config(struct ess_card *card) } +/* this guy tries to find the pci power management + * register bank. this should really be in core + * code somewhere. 1 on success. */ +int +parse_power(struct ess_card *card, struct pci_dev *pcidev) +{ + u32 n; + u16 w; + u8 next; + int max = 64; /* an a 8bit guy pointing to 32bit guys + can only express so much. */ + + card->power_regs = 0; + + /* check to see if we have a capabilities list in + the config register */ + pci_read_config_word(pcidev, PCI_STATUS, &w); + if(! w & PCI_STATUS_CAP_LIST) return 0; + + /* walk the list, starting at the head. */ + pci_read_config_byte(pcidev,PCI_CAPABILITY_LIST,&next); + + while(next && max--) { + pci_read_config_dword(pcidev, next & ~3, &n); + if((n & 0xff) == PCI_CAP_ID_PM) { + card->power_regs = next; + break; + } + next = ((n>>8) & 0xff); + } + + return card->power_regs ? 1 : 0; +} static int maestro_install(struct pci_dev *pcidev, int card_type) @@ -3149,7 +3271,7 @@ maestro_install(struct pci_dev *pcidev, int card_type) int i; struct ess_card *card; struct ess_state *ess; - struct pm_dev *pmdev; + struct pm_dev *pmdev; int num = 0; /* don't pick up weird modem maestros */ @@ -3158,15 +3280,15 @@ maestro_install(struct pci_dev *pcidev, int card_type) iobase = SILLY_PCI_BASE_ADDRESS(pcidev); - if(check_region(iobase, 256)) + /* stake our claim on the iospace */ + if( request_region(iobase, 256, card_names[card_type]) == NULL ) { printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); return 0; } /* this was tripping up some machines */ - if(pcidev->irq == 0) - { + if(pcidev->irq == 0) { printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n"); } @@ -3181,13 +3303,16 @@ maestro_install(struct pci_dev *pcidev, int card_type) } memset(card, 0, sizeof(*card)); - memcpy(&card->pcidev,pcidev,sizeof(card->pcidev)); + card->pcidev = pcidev; - pmdev = pm_register(PM_PCI_DEV, - PM_PCI_ID(pcidev), - maestro_pm_callback); - if (pmdev) - pmdev->data = card; + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(pcidev), + maestro_pm_callback); + if (pmdev) + pmdev->data = card; + + if (register_reboot_notifier(&maestro_nb)) { + printk(KERN_WARNING "maestro: reboot notifier registration failed; may not reboot properly.\n"); + } card->iobase = iobase; card->card_type = card_type; @@ -3195,6 +3320,7 @@ maestro_install(struct pci_dev *pcidev, int card_type) card->next = devs; card->magic = ESS_CARD_MAGIC; spin_lock_init(&card->lock); + init_waitqueue_head(&card->suspend_queue); devs = card; /* init our groups of 6 apus */ @@ -3246,13 +3372,37 @@ maestro_install(struct pci_dev *pcidev, int card_type) pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + /* turn off power management unless: + * - the user explicitly asks for it + * or + * - we're not a 2e, lesser chipps seem to have problems. + * - we're not on our _very_ small whitelist. some implemenetations + * really dont' like the pm code, others require it. + * feel free to expand this as required. + */ +#define SUBSYSTEM_VENDOR(x) (x&0xffff) + if( (use_pm != 1) && + ((card_type != TYPE_MAESTRO2E) || (SUBSYSTEM_VENDOR(n) != 0x1028))) + use_pm = 0; + + if(!use_pm) + printk(KERN_INFO "maestro: not attempting power management.\n"); + else { + if(!parse_power(card,pcidev)) + printk(KERN_INFO "maestro: no PCI power managment interface found.\n"); + else { + pci_read_config_dword(pcidev, card->power_regs, &n); + printk(KERN_INFO "maestro: PCI power managment capability: 0x%x\n",n>>16); + } + } + maestro_config(card); - if(maestro_ac97_get(iobase, 0x00)==0x0080) { + if(maestro_ac97_get(card, 0x00)==0x0080) { printk(KERN_ERR "maestro: my goodness! you seem to have a pt101 codec, which is quite rare.\n" "\tyou should tell someone about this.\n"); } else { - maestro_ac97_init(card,iobase); + maestro_ac97_init(card); } if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { @@ -3273,9 +3423,12 @@ maestro_install(struct pci_dev *pcidev, int card_type) unregister_sound_dsp(s->dev_audio); } release_region(card->iobase, 256); + unregister_reboot_notifier(&maestro_nb); kfree(card); return 0; } + /* now go to sleep 'till something interesting happens */ + maestro_power(card,ACPI_D2); printk(KERN_INFO "maestro: %d channels configured.\n", num); return 1; @@ -3305,8 +3458,6 @@ int SILLY_MAKE_INIT(init_maestro(void)) printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); } - init_waitqueue_head(&suspend_queue); - /* * Find the ESS Maestro 2. */ @@ -3340,56 +3491,78 @@ int SILLY_MAKE_INIT(init_maestro(void)) return 0; } -/* --------------------------------------------------------------------- */ - -#ifdef MODULE -MODULE_AUTHOR("Zach Brown , Alan Cox "); -MODULE_DESCRIPTION("ESS Maestro Driver"); -#ifdef M_DEBUG -MODULE_PARM(debug,"i"); -#endif -MODULE_PARM(dsps_order,"i"); - -void cleanup_module(void) +static void nuke_maestros(void) { - struct ess_card *s; + struct ess_card *card; + /* we do these unconditionally, which is probably wrong */ pm_unregister_all(maestro_pm_callback); + unregister_reboot_notifier(&maestro_nb); - while ((s = devs)) { + while ((card = devs)) { int i; devs = devs->next; /* XXX maybe should force stop bob, but should be all stopped by _release by now */ - free_irq(s->irq, s); - unregister_sound_mixer(s->dev_mixer); + free_irq(card->irq, card); + unregister_sound_mixer(card->dev_mixer); for(i=0;ichannels[i]; + struct ess_state *ess = &card->channels[i]; if(ess->dev_audio != -1) unregister_sound_dsp(ess->dev_audio); } - release_region(s->iobase, 256); - kfree(s); + /* Goodbye, Mr. Bond. */ + maestro_power(card,ACPI_D3); + release_region(card->iobase, 256); + kfree(card); } + devs = NULL; +} + +static int maestro_notifier(struct notifier_block *nb, unsigned long event, void *buf) +{ + /* this notifier is called when the kernel is really shut down. */ + M_printk("maestro: shutting down\n"); + nuke_maestros(); + return NOTIFY_OK; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); +MODULE_PARM(use_pm,"i"); + +void cleanup_module(void) { M_printk("maestro: unloading\n"); + nuke_maestros(); } -#endif /* MODULE */ +#else /* MODULE */ +__initcall(init_maestro); +#endif + +/* --------------------------------------------------------------------- */ void -check_suspend(void) +check_suspend(struct ess_card *card) { DECLARE_WAITQUEUE(wait, current); - if(!in_suspend) return; + if(!card->in_suspend) return; - in_suspend++; - add_wait_queue(&suspend_queue, &wait); + card->in_suspend++; + add_wait_queue(&(card->suspend_queue), &wait); current->state = TASK_UNINTERRUPTIBLE; schedule(); - remove_wait_queue(&suspend_queue, &wait); + remove_wait_queue(&(card->suspend_queue), &wait); current->state = TASK_RUNNING; } @@ -3397,113 +3570,138 @@ static int maestro_suspend(struct ess_card *card) { unsigned long flags; - int i,j; + int i,j; - save_flags(flags); - cli(); - - M_printk("maestro: pm in dev %p\n",card); - - for(i=0;ichannels[i]; - - if(s->dev_audio == -1) - continue; - - M_printk("maestro: stopping apus for device %d\n",i); - stop_dac(s); - stop_adc(s); - for(j=0;j<6;j++) - card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); - - } + save_flags(flags); + cli(); /* over-kill */ - /* get rid of interrupts? */ - if( card->dsps_open > 0) - stop_bob(&card->channels[0]); + M_printk("maestro: apm in dev %p\n",card); - in_suspend=1; + /* we have to read from the apu regs, need + to power it up */ + maestro_power(card,ACPI_D0); - restore_flags(flags); + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; - /* we'll let the bios do the rest of the power down.. */ + M_printk("maestro: stopping apus for device %d\n",i); + stop_dac(s); + stop_adc(s); + for(j=0;j<6;j++) + card->apu_map[s->apu[j]][5]=apu_get_register(s,j,5); + + } + /* get rid of interrupts? */ + if( card->dsps_open > 0) + stop_bob(&card->channels[0]); + + card->in_suspend++; + + restore_flags(flags); + + /* we trust in the bios to power down the chip on suspend. + * XXX I'm also not sure that in_suspend will protect + * against all reg accesses from here on out. + */ return 0; } static int maestro_resume(struct ess_card *card) { unsigned long flags; - int i; + int i; save_flags(flags); - cli(); - in_suspend=0; - M_printk("maestro: resuming\n"); - - /* first lets just bring everything back. .*/ - - M_printk("maestro: pm in dev %p\n",card); - - maestro_config(card); - /* need to restore the base pointers.. */ - if(card->dmapages) - set_base_registers(&card->channels[0],card->dmapages); - - mixer_push_state(card); - - for(i=0;ichannels[i]; - int chan,reg; - - if(s->dev_audio == -1) - continue; - - for(chan = 0 ; chan < 6 ; chan++) { - wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); - for(reg = 1 ; reg < NR_APU_REGS ; reg++) - apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); - } - for(chan = 0 ; chan < 6 ; chan++) - apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); - } + cli(); /* over-kill */ + + card->in_suspend = 0; + + M_printk("maestro: resuming card at %p\n",card); + + /* restore all our config */ + maestro_config(card); + /* need to restore the base pointers.. */ + if(card->dmapages) + set_base_registers(&card->channels[0],card->dmapages); + + mixer_push_state(card); + + /* set each channels' apu control registers before + * restoring audio + */ + for(i=0;ichannels[i]; + int chan,reg; + + if(s->dev_audio == -1) + continue; + + for(chan = 0 ; chan < 6 ; chan++) { + wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); + for(reg = 1 ; reg < NR_APU_REGS ; reg++) + apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); + } + for(chan = 0 ; chan < 6 ; chan++) + apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); + } /* now we flip on the music */ - M_printk("maestro: pm in dev %p\n",card); - - for(i=0;ichannels[i]; - - /* these use the apu_mode, and can handle - spurious calls */ - start_dac(s); - start_adc(s); - } - if( card->dsps_open > 0) - start_bob(&card->channels[0]); + + if( card->dsps_open <= 0) { + /* this card's idle */ + maestro_power(card,ACPI_D2); + } else { + /* ok, we're actually playing things on + this card */ + maestro_power(card,ACPI_D0); + start_bob(&card->channels[0]); + for(i=0;ichannels[i]; + + /* these use the apu_mode, and can handle + spurious calls */ + start_dac(s); + start_adc(s); + } + } restore_flags(flags); - wake_up(&suspend_queue); + /* all right, we think things are ready, + wake up people who were using the device + when we suspended */ + wake_up(&(card->suspend_queue)); return 0; } int -maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) { - struct ess_card *card = (struct ess_card*) dev->data; - if (card) { - M_printk("maestro: pm event received: 0x%x\n", rqst); - - switch (rqst) { - case PM_SUSPEND: - maestro_suspend(card); - break; - case PM_RESUME: - maestro_resume(card); - break; - } - } +maestro_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + struct ess_card *card = (struct ess_card*) dev->data; + if ( ! card ) goto out; + + M_printk("maestro: pm event 0x%x received for card %p\n", rqst, card); + + switch (rqst) { + case PM_SUSPEND: + maestro_suspend(card); + break; + case PM_RESUME: + maestro_resume(card); + break; + /* + * we'd also like to find out about + * power level changes because some biosen + * do mean things to the maestro when they + * change their power state. + */ + } +out: return 0; } diff --git a/drivers/sound/sb_card.c b/drivers/sound/sb_card.c index 513e6aa24f51..a0a12fc81430 100644 --- a/drivers/sound/sb_card.c +++ b/drivers/sound/sb_card.c @@ -10,7 +10,6 @@ * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. * - * * 26-11-1999 Patched to compile without ISA PnP support in the * kernel - Daniel Stone (tamriel@ductape.net) * @@ -37,6 +36,10 @@ * 26-03-2000 Fixed acer, esstype and sm_games module options. * Alessandro Zummo * + * 27-03-2000 ISAPnP multiple card detection, cleanup, and reorg. + * Thanks to Gaël Quéri and Alessandro Zummo for testing and fixes. + * Paul E. Laufer + * */ #include @@ -51,7 +54,14 @@ #include "sb_mixer.h" #include "sb.h" -static int sbmpu = 0; +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE +#define SB_CARDS_MAX 4 +#else +#define SB_CARDS_MAX 1 +#endif + +static int sbmpu[SB_CARDS_MAX] = {0}; +static int sb_cards_num = 0; extern void *smw_free; @@ -75,7 +85,6 @@ static void __init attach_sb_card(struct address_info *hw_config) { if(!sb_dsp_init(hw_config)) hw_config->slots[0] = -1; - SOUND_LOCK; } static int __init probe_sb(struct address_info *hw_config) @@ -84,7 +93,7 @@ static int __init probe_sb(struct address_info *hw_config) if (hw_config->io_base == -1 || hw_config->dma == -1 || hw_config->irq == -1) { - printk(KERN_ERR "sb_card: I/O, IRQ, and DMA are mandatory\n"); + printk(KERN_ERR "sb: I/O, IRQ, and DMA are mandatory\n"); return -EINVAL; } @@ -149,14 +158,6 @@ iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", } #endif - /* This is useless since it is done by sb_dsp_detect - azummo */ - - if (check_region(hw_config->io_base, 16)) - { - printk(KERN_ERR "sb_card: I/O port 0x%x is already in use\n\n", hw_config->io_base); - return 0; - } - /* Setup extra module options */ sbmo.acer = acer; @@ -166,25 +167,31 @@ iobase=0x%x irq=%d lo_dma=%d hi_dma=%d\n", return sb_dsp_detect(hw_config, 0, 0, &sbmo); } -static void __exit unload_sb(struct address_info *hw_config) +static void __exit unload_sb(struct address_info *hw_config, int card) { if(hw_config->slots[0]!=-1) - sb_dsp_unload(hw_config, sbmpu); + sb_dsp_unload(hw_config, sbmpu[card]); } -static struct address_info cfg; -static struct address_info cfg_mpu; +static struct address_info cfg[SB_CARDS_MAX]; +static struct address_info cfg_mpu[SB_CARDS_MAX]; -struct pci_dev *sb_dev = NULL, - *mpu_dev = NULL; +struct pci_dev *sb_dev[SB_CARDS_MAX] = {NULL}, + *mpu_dev[SB_CARDS_MAX] = {NULL}; #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE static int isapnp = 1; static int isapnpjump = 0; -static int activated = 1; +static int multiple = 0; +static int reverse = 0; +static int uart401 = 0; + +static int audio_activated[SB_CARDS_MAX] = {0}; +static int mpu_activated[SB_CARDS_MAX] = {0}; #else static int isapnp = 0; +static int multiple = 1; #endif MODULE_DESCRIPTION("Soundblaster driver"); @@ -202,8 +209,14 @@ MODULE_PARM(acer, "i"); #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE MODULE_PARM(isapnp, "i"); MODULE_PARM(isapnpjump, "i"); +MODULE_PARM(multiple, "i"); +MODULE_PARM(reverse, "i"); +MODULE_PARM(uart401, "i"); MODULE_PARM_DESC(isapnp, "When set to 0, Plug & Play support will be disabled"); MODULE_PARM_DESC(isapnpjump, "Jumps to a specific slot in the driver's PnP table. Use the source, Luke."); +MODULE_PARM_DESC(multiple, "When set to 0, will not search for multiple cards"); +MODULE_PARM_DESC(reverse, "When set to 1, will reverse ISAPnP search order"); +MODULE_PARM_DESC(uart401, "When set to 1, will attempt to detect and enable the mpu on some clones"); #endif MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)"); @@ -218,6 +231,200 @@ MODULE_PARM_DESC(acer, "Set this to detect cards in some ACER notebooks"); #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE +/* Please add new entries at the end of the table */ +static struct { + char *name; + unsigned short card_vendor, card_device, audio_vendor, audio_function, mpu_vendor, mpu_function; + short dma, dma2, mpu_io, mpu_irq; /* see sb_init() */ +} sb_isapnp_list[] __initdata = { + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0024), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0026), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0027), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0029), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster 16", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x002b), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16S", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0051), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16C", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0070), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), + 0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16CL", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0080), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0, + 0,1,1,-1}, + {"Sound Blaster Vibra16X", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00F0), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0039), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0042), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0043), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0044), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0054), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 32", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009C), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009D), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64 Gold", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x009E), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0044), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C1), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C3), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C5), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00C7), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0, + 0,1,1,-1}, + {"Sound Blaster AWE 64", + ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x00E4), + ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), + 0,0, + 0,1,1,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), + 0,0, + 0,1,2,-1}, + {"ESS 1868", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1868), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), + 0,0, + 0,1,2,-1}, + {"ESS 1869 PnP AudioDrive", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x0003), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0, + 0,1,2,-1}, + {"ESS 1869", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1869), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), + 0,0, + 0,1,2,-1}, + {"ESS 1878", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1878), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), + 0,0, + 0,1,2,-1}, + {"ESS 1879", + ISAPNP_VENDOR('E','S','S'), ISAPNP_DEVICE(0x1879), + ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), + 0,0, + 0,1,2,-1}, + {"CMI 8330 SoundPRO", + ISAPNP_VENDOR('C','M','I'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), + 0,1,0,-1}, + {"Diamond DT0197H", + ISAPNP_VENDOR('R','W','B'), ISAPNP_DEVICE(0x1688), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS007", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0007), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + 0,-1,0,0}, + {"ALS100", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0001), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), + 1,0,0,0}, + {"ALS110", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0110), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x1001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x1001), + 1,0,0,0}, + {"ALS120", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0120), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + 1,0,0,0}, + {"ALS200", + ISAPNP_VENDOR('A','L','S'), ISAPNP_DEVICE(0x0200), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0020), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0020), + 1,0,0,0}, + {"RTL3000", + ISAPNP_VENDOR('R','T','L'), ISAPNP_DEVICE(0x3000), + ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x2001), + ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x2001), + 1,0,0,0}, + {0} +}; + /* That's useful. */ #define show_base(devname, resname, resptr) printk(KERN_INFO "sb: %s %s base located at %#lx\n", devname, resname, (resptr)->start) @@ -227,14 +434,10 @@ static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev int err; /* Device already active? Let's use it */ - if(dev->active) - { - activated = 0; return(dev); - } - if((err = dev->activate(dev)) < 0) - { + + if((err = dev->activate(dev)) < 0) { printk(KERN_ERR "sb: %s %s config failed (out of resources?)[%d]\n", devname, resname, err); dev->deactivate(dev); @@ -244,320 +447,129 @@ static struct pci_dev *activate_dev(char *devname, char *resname, struct pci_dev return(dev); } -/* Card's specific initialization functions - */ - -static struct pci_dev *sb_init_generic(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) +static struct pci_dev *sb_init(struct pci_bus *bus, struct address_info *hw_config, struct address_info *mpu_config, int slot, int card) { - if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) - { - sb_dev->prepare(sb_dev); - if((sb_dev = activate_dev("Soundblaster", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - mpu_config->io_base = sb_dev->resource[1].start; - } - } - return(sb_dev); -} - -static struct pci_dev *sb_init_ess(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - if((sb_dev = isapnp_find_dev(bus, card->vendor, card->device, NULL))) + /* Configure Audio device */ + if((sb_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].audio_vendor, sb_isapnp_list[slot].audio_function, NULL))) { - sb_dev->prepare(sb_dev); - - if((sb_dev = activate_dev("ESS", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - mpu_config->io_base = sb_dev->resource[2].start; + int ret; + ret = sb_dev[card]->prepare(sb_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway. Some other way to check this? */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: ISAPnP found device that could not be autoconfigured.\n"); + return(NULL); } - } - return(sb_dev); -} - -static struct pci_dev *sb_init_cmi(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - /* - * The CMI8330/C3D is a very 'stupid' chip... where did they get al those @@@ ? - * It's ISAPnP section is badly designed and has many flaws, i'll do my best - * to workaround them. I strongly suggest you to buy a real soundcard. - * The CMI8330 on my motherboard has also the bad habit to activate - * the rear channel of my amplifier instead of the front one. - */ - - /* @X@0001:Soundblaster. - */ - - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); + if(ret == -EBUSY) + audio_activated[card] = 1; - if((sb_dev = activate_dev("CMI8330", "sb", sb_dev))) + if((sb_dev[card] = activate_dev(sb_isapnp_list[slot].name, "sb", sb_dev[card]))) { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = sb_dev->dma_resource[1].start; - - show_base("CMI8330", "sb", &sb_dev->resource[0]); - } + hw_config->io_base = sb_dev[card]->resource[0].start; + hw_config->irq = sb_dev[card]->irq_resource[0].start; + hw_config->dma = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma].start; + if(sb_isapnp_list[slot].dma2 != -1) + hw_config->dma2 = sb_dev[card]->dma_resource[sb_isapnp_list[slot].dma2].start; + else + hw_config->dma2 = -1; + } else + return(NULL); + } else + return(NULL); - if(!sb_dev) return(NULL); + /* Cards with MPU as part of Audio device (CTL and ESS) */ + if(!sb_isapnp_list[slot].mpu_vendor) { + mpu_config->io_base = sb_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + return(sb_dev[card]); } - else - printk(KERN_ERR "sb: CMI8330 panic: sb base not found\n"); - /* @H@0001:mpu - */ - - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','H','@'), ISAPNP_FUNCTION(0x0001), NULL))) + /* Cards with separate MPU device (ALS, CMI, etc */ + if(!uart401) + return(sb_dev[card]); + if((mpu_dev[card] = isapnp_find_dev(bus, sb_isapnp_list[slot].mpu_vendor, sb_isapnp_list[slot].mpu_function, NULL))) { - mpu_dev->prepare(mpu_dev); - - /* This disables the interrupt on this resource. Do we need it ? - */ - - mpu_dev->irq_resource[0].flags = 0; - - if((mpu_dev = activate_dev("CMI8330", "mpu", mpu_dev))) - { - show_base("CMI8330", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; + int ret = mpu_dev[card]->prepare(mpu_dev[card]); + /* If device is active, assume configured with /proc/isapnp + * and use anyway */ + if(ret && ret != -EBUSY) { + printk(KERN_ERR "sb: MPU device could not be autoconfigured.\n"); + return(sb_dev[card]); } - } - else - printk(KERN_ERR "sb: CMI8330 panic: mpu not found\n"); - - printk(KERN_INFO "sb: CMI8330 mail reports to Alessandro Zummo \n"); - - return(sb_dev); -} - -static struct pci_dev *sb_init_diamond(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - /* - * Diamonds DT0197H - * very similar to the CMI8330 above - */ - - /* @@@0001:Soundblaster. - */ - - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); + if(ret == -EBUSY) + mpu_activated[card] = 1; - if((sb_dev = activate_dev("DT0197H", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[0].start; - hw_config->dma2 = -1; - - show_base("DT0197H", "sb", &sb_dev->resource[0]); - } - - if(!sb_dev) return(NULL); - } - else - printk(KERN_ERR "sb: DT0197H panic: sb base not found\n"); - - /* @X@0001:mpu - */ - - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - mpu_dev->prepare(mpu_dev); - - if((mpu_dev = activate_dev("DT0197H", "mpu", mpu_dev))) - { - show_base("DT0197H", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; - } - } - else - printk(KERN_ERR "sb: DT0197H panic: mpu not found\n"); - - printk(KERN_INFO "sb: DT0197H mail reports to Torsten Werner \n"); - - return(sb_dev); -} - -static struct pci_dev *sb_init_als(struct pci_bus *bus, struct pci_dev *card, struct address_info *hw_config, struct address_info *mpu_config) -{ - /* - * ALS100 - * very similar to both ones above above - */ - - /* @@@0001:Soundblaster. - */ - - if((sb_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','@','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - sb_dev->prepare(sb_dev); + /* Some mpus use audio device irq? Need to test... -PEL */ + if(sb_isapnp_list[slot].mpu_irq == -1) + mpu_dev[card]->irq_resource[0].flags = 0; - if((sb_dev = activate_dev("ALS100", "sb", sb_dev))) - { - hw_config->io_base = sb_dev->resource[0].start; - hw_config->irq = sb_dev->irq_resource[0].start; - hw_config->dma = sb_dev->dma_resource[1].start; - hw_config->dma2 = sb_dev->dma_resource[0].start; - - show_base("ALS100", "sb", &sb_dev->resource[0]); - } - - if(!sb_dev) return(NULL); - } - else - printk(KERN_ERR "sb: ALS100 panic: sb base not found\n"); - - /* @X@0001:mpu - */ - - if((mpu_dev = isapnp_find_dev(bus, - ISAPNP_VENDOR('@','X','@'), ISAPNP_FUNCTION(0x0001), NULL))) - { - mpu_dev->prepare(mpu_dev); - - if((mpu_dev = activate_dev("ALS100", "mpu", mpu_dev))) - { - show_base("ALS100", "mpu", &mpu_dev->resource[0]); - mpu_config->io_base = mpu_dev->resource[0].start; + if((mpu_dev[card] = activate_dev(sb_isapnp_list[slot].name, "mpu", mpu_dev[card]))) { + mpu_config->io_base = mpu_dev[card]->resource[sb_isapnp_list[slot].mpu_io].start; + if(sb_isapnp_list[slot].mpu_irq != -1) + mpu_config->irq = mpu_dev[card]->irq_resource[sb_isapnp_list[slot].mpu_irq].start; } } else - printk(KERN_ERR "sb: ALS100 panic: mpu not found\n"); - - printk(KERN_INFO "sb: ALS100 mail reports to Torsten Werner \n"); - - return(sb_dev); + printk(KERN_ERR "sb: %s panic: mpu not found\n", sb_isapnp_list[slot].name); + + return(sb_dev[card]); } -#define SBF_DEV 0x01 /* Please notice that cards without this flag are on the top in the list */ - - -static struct { unsigned short vendor, function, flags; struct pci_dev * (*initfunc)(struct pci_bus *, struct pci_dev *, struct address_info *, struct address_info *); char *name; } -sb_isapnp_list[] __initdata = { - {ISAPNP_VENDOR('C','M','I'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_cmi, "CMI 8330 SoundPRO" }, - {ISAPNP_VENDOR('R','W','B'), ISAPNP_FUNCTION(0x1688), 0, &sb_init_diamond, "Diamond DT0197H" }, - {ISAPNP_VENDOR('A','L','S'), ISAPNP_FUNCTION(0x0001), 0, &sb_init_als, "ALS 100" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0001), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0041), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0042), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0043), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0045), SBF_DEV, &sb_init_generic, "Sound Blaster 16" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x0968), SBF_DEV, &sb_init_ess, "ESS 1688" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1868), SBF_DEV, &sb_init_ess, "ESS 1868" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x8611), SBF_DEV, &sb_init_ess, "ESS 1868" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1869), SBF_DEV, &sb_init_ess, "ESS 1869" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1878), SBF_DEV, &sb_init_ess, "ESS 1878" }, - {ISAPNP_VENDOR('E','S','S'), ISAPNP_FUNCTION(0x1879), SBF_DEV, &sb_init_ess, "ESS 1879" }, - {0} -}; - -static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, struct pci_dev *card, int slot) +static int __init sb_isapnp_init(struct address_info *hw_config, struct address_info *mpu_config, struct pci_bus *bus, int slot, int card) { - struct pci_dev *idev = NULL; - - /* You missed the init func? That's bad. */ - if(sb_isapnp_list[slot].initfunc) - { - char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; - - printk(KERN_INFO "sb: %s detected\n", busname); + char *busname = bus->name[0] ? bus->name : sb_isapnp_list[slot].name; - /* Initialize this baby. */ + printk(KERN_INFO "sb: %s detected\n", busname); - if((idev = sb_isapnp_list[slot].initfunc(bus, card, hw_config, mpu_config))) - { - /* We got it. */ + /* Initialize this baby. */ - printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", - busname, - hw_config->io_base, hw_config->irq, hw_config->dma, - hw_config->dma2); - return 1; - } - else - printk(KERN_INFO "sb: Failed to initialize %s\n", busname); + if(sb_init(bus, hw_config, mpu_config, slot, card)) { + /* We got it. */ + + printk(KERN_NOTICE "sb: ISAPnP reports '%s' at i/o %#x, irq %d, dma %d, %d\n", + busname, + hw_config->io_base, hw_config->irq, hw_config->dma, + hw_config->dma2); + return 1; } else - printk(KERN_ERR "sb: Bad entry in sb_card.c PnP table\n"); + printk(KERN_INFO "sb: Failed to initialize %s\n", busname); return 0; } -/* Actually this routine will detect and configure only the first card with successful - initialization. isapnpjump could be used to jump to a specific entry. - Please always add entries at the end of the array. - Should this be fixed? - azummo -*/ - -int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config) +int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info *mpu_config, int card) { + static int first = 1; int i; /* Count entries in sb_isapnp_list */ - for (i = 0; sb_isapnp_list[i].vendor != 0; i++); + for (i = 0; sb_isapnp_list[i].card_vendor != 0; i++); + i--; /* Check and adjust isapnpjump */ - if( isapnpjump < 0 || isapnpjump > ( i - 1 ) ) - { - printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to 0.\n", i-1); - isapnpjump = 0; - } - - for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { - - if(!(sb_isapnp_list[i].flags & SBF_DEV)) - { - struct pci_bus *bus = NULL; - - while ((bus = isapnp_find_card( - sb_isapnp_list[i].vendor, - sb_isapnp_list[i].function, - bus))) { - - if(sb_isapnp_init(hw_config, mpu_config, bus, NULL, i)) - return 0; - } - } + if( isapnpjump < 0 || isapnpjump > i) { + isapnpjump = reverse ? i : 0; + printk(KERN_ERR "sb: Valid range for isapnpjump is 0-%d. Adjusted to %d.\n", i, isapnpjump); } - /* No cards found. I'll try now to search inside every card for a logical device - * that matches any entry marked with SBF_DEV in the table. - */ + if(!first || !reverse) + i = isapnpjump; + first = 0; + while(sb_isapnp_list[i].card_vendor != 0) { + static struct pci_bus *bus = NULL; - for (i = isapnpjump; sb_isapnp_list[i].vendor != 0; i++) { - - if(sb_isapnp_list[i].flags & SBF_DEV) - { - struct pci_dev *card = NULL; - - while ((card = isapnp_find_dev(NULL, - sb_isapnp_list[i].vendor, - sb_isapnp_list[i].function, - card))) { - - if(sb_isapnp_init(hw_config, mpu_config, card->bus, card, i)) - return 0; + while ((bus = isapnp_find_card( + sb_isapnp_list[i].card_vendor, + sb_isapnp_list[i].card_device, + bus))) { + + if(sb_isapnp_init(hw_config, mpu_config, bus, i, card)) { + isapnpjump = i; /* start next search from here */ + return 0; } } + i += reverse ? -1 : 1; } return -ENODEV; @@ -566,62 +578,77 @@ int __init sb_isapnp_probe(struct address_info *hw_config, struct address_info * static int __init init_sb(void) { + int card, max = multiple ? SB_CARDS_MAX : 1; + printk(KERN_INFO "Soundblaster audio driver Copyright (C) by Hannu Savolainen 1993-1996\n"); + + for(card = 0; card < max; card++, sb_cards_num++) { +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + /* Please remember that even with CONFIG_ISAPNP defined one should still be + able to disable PNP support for this single driver! */ + if(isapnp && (sb_isapnp_probe(&cfg[card], &cfg_mpu[card], card) < 0) ) { + if(!sb_cards_num) { + printk(KERN_NOTICE "sb: No ISAPnP cards found, trying standard ones...\n"); + isapnp = 0; + } else + break; + } +#endif - /* Please remember that even with CONFIG_ISAPNP defined one should still be - able to disable PNP support for this single driver! - */ + if(!isapnp) { + cfg[card].io_base = io; + cfg[card].irq = irq; + cfg[card].dma = dma; + cfg[card].dma2 = dma16; + } -#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE - if(isapnp && (sb_isapnp_probe(&cfg, &cfg_mpu) < 0) ) { - printk(KERN_NOTICE "sb_card: No ISAPnP cards found, trying standard ones...\n"); - isapnp = 0; - } -#endif + cfg[card].card_subtype = type; + + if (!probe_sb(&cfg[card])) + return -ENODEV; + attach_sb_card(&cfg[card]); - if( isapnp == 0 ) { - cfg.io_base = io; - cfg.irq = irq; - cfg.dma = dma; - cfg.dma2 = dma16; + if(cfg[card].slots[0]==-1) + return -ENODEV; + + if (!isapnp) + cfg_mpu[card].io_base = mpu_io; + if (probe_sbmpu(&cfg_mpu[card])) + sbmpu[card] = 1; + if (sbmpu[card]) + attach_sbmpu(&cfg_mpu[card]); } - cfg.card_subtype = type; + SOUND_LOCK; - if (!probe_sb(&cfg)) - return -ENODEV; - attach_sb_card(&cfg); + if(isapnp) + printk(KERN_NOTICE "sb: %d Soundblaster PnP card(s) found.\n", sb_cards_num); - if(cfg.slots[0]==-1) - return -ENODEV; - - if (isapnp == 0) - cfg_mpu.io_base = mpu_io; - if (probe_sbmpu(&cfg_mpu)) - sbmpu = 1; - if (sbmpu) - attach_sbmpu(&cfg_mpu); return 0; } static void __exit cleanup_sb(void) { + int i; + if (smw_free) { vfree(smw_free); smw_free = NULL; } - unload_sb(&cfg); - if (sbmpu) - unload_sbmpu(&cfg_mpu); - SOUND_LOCK_END; -#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE - if(activated) - { - if(sb_dev) sb_dev->deactivate(sb_dev); - if(mpu_dev) mpu_dev->deactivate(mpu_dev); - } + for(i = 0; i < sb_cards_num; i++) { + unload_sb(&cfg[i], i); + if (sbmpu[i]) + unload_sbmpu(&cfg_mpu[i]); + +#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE + if(!audio_activated[i] && sb_dev[i]) + sb_dev[i]->deactivate(sb_dev[i]); + if(!mpu_activated[i] && mpu_dev[i]) + mpu_dev[i]->deactivate(mpu_dev[i]); #endif + } + SOUND_LOCK_END; } module_init(init_sb); diff --git a/drivers/sound/sb_common.c b/drivers/sound/sb_common.c index ad98080491f4..ce5cd829ef0c 100644 --- a/drivers/sound/sb_common.c +++ b/drivers/sound/sb_common.c @@ -1258,7 +1258,8 @@ int probe_sbmpu(struct address_info *hw_config) return 0; } hw_config->name = "Sound Blaster 16"; - hw_config->irq = -devc->irq; + if (hw_config->irq < 3 || hw_config->irq == devc->irq) + hw_config->irq = -devc->irq; if (devc->minor > 12) /* What is Vibra's version??? */ sb16_set_mpu_port(devc, hw_config); break; diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index c0d6dd06a7ea..053b43160f4f 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -370,10 +370,11 @@ EXPORT_SYMBOL(register_sound_synth); /** * unregister_sound_special - unregister a special sound device - * @unit: Unit number to allocate + * @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. + * Release a sound device that was allocated with + * register_sound_special(). The unit passed is the return value from + * the register function. */ @@ -386,9 +387,9 @@ EXPORT_SYMBOL(unregister_sound_special); /** * unregister_sound_mixer - unregister a mixer - * @unit: Unit number to allocate + * @unit: unit number to allocate * - * Release a sound device that was allocated with register_sound_mixer. + * Release a sound device that was allocated with register_sound_mixer(). * The unit passed is the return value from the register function. */ @@ -401,9 +402,9 @@ EXPORT_SYMBOL(unregister_sound_mixer); /** * unregister_sound_midi - unregister a midi device - * @unit: Unit number to allocate + * @unit: unit number to allocate * - * Release a sound device that was allocated with register_sound_midi. + * Release a sound device that was allocated with register_sound_midi(). * The unit passed is the return value from the register function. */ @@ -416,9 +417,9 @@ EXPORT_SYMBOL(unregister_sound_midi); /** * unregister_sound_dsp - unregister a DSP device - * @unit: Unit number to allocate + * @unit: unit number to allocate * - * Release a sound device that was allocated with register_sound_dsp. + * 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. @@ -434,9 +435,9 @@ EXPORT_SYMBOL(unregister_sound_dsp); /** * unregister_sound_synth - unregister a synth device - * @unit: Unit number to allocate + * @unit: unit number to allocate * - * Release a sound device that was allocated with register_sound_synth. + * Release a sound device that was allocated with register_sound_synth(). * The unit passed is the return value from the register function. */ diff --git a/drivers/sound/soundcard.c b/drivers/sound/soundcard.c index f222592ea83a..9ed6305efac7 100644 --- a/drivers/sound/soundcard.c +++ b/drivers/sound/soundcard.c @@ -605,7 +605,7 @@ soundcard_init(void) static void destroy_special_devices(void) { - unregister_sound_special(6); + unregister_sound_special(1); unregister_sound_special(8); } diff --git a/drivers/sound/trident.c b/drivers/sound/trident.c index 38afc31c9824..3318e08cb9a1 100644 --- a/drivers/sound/trident.c +++ b/drivers/sound/trident.c @@ -29,6 +29,13 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * History + * v0.14.2 Mar 29 2000 Ching Ling Lee + * Add clear to silence advance in trident_update_ptr + * fix invalid data of the end of the sound + * v0.14.1 Mar 24 2000 Ching Ling Lee + * ALi 5451 support added, playback and recording O.K. + * ALi 5451 originally developed and structured based on sonicvibes, and + * suggested to merge into this file by Alan Cox. * v0.14 Mar 15 2000 Ollie Lho * 5.1 channel output support with channel binding. What's the Matrix ? * v0.13.1 Mar 10 2000 Ollie Lho @@ -74,6 +81,7 @@ * new pci device driver interface for 2.4 kernel (done) */ +#include #include #include #include @@ -127,13 +135,15 @@ static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in % enum { TRIDENT_4D_DX = 0, TRIDENT_4D_NX, - SIS_7018 + SIS_7018, + ALI_5451 }; static char * card_names[] = { "Trident 4DWave DX", "Trident 4DWave NX", - "SiS 7018 PCI Audio" + "SiS 7018 PCI Audio", + "ALi Audio Accelerator" }; static struct pci_device_id trident_pci_tbl [] __initdata = { @@ -143,6 +153,9 @@ static struct pci_device_id trident_pci_tbl [] __initdata = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, TRIDENT_4D_NX}, {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_7018}, + {PCI_VENDOR_ID_ALI, PCI_DEVICE_ID_ALI_5451, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALI_5451}, + {0,} }; MODULE_DEVICE_TABLE (pci, trident_pci_tbl); @@ -170,7 +183,7 @@ struct trident_state { /* hardware channel */ struct trident_channel *channel; - /* OSS buffer manangemeent stuff */ + /* OSS buffer management stuff */ void *rawbuf; dma_addr_t dma_handle; unsigned buforder; @@ -194,6 +207,8 @@ struct trident_state { /* OSS stuff */ unsigned mapped:1; unsigned ready:1; + unsigned endcleared:1; + unsigned update_flag; unsigned ossfragshift; int ossmaxfrags; unsigned subdivision; @@ -252,6 +267,7 @@ struct trident_card { /* PCI device stuff */ struct pci_dev * pci_dev; u16 pci_id; + u8 revision; /* soundcore stuff */ int dev_audio; @@ -264,6 +280,12 @@ struct trident_card { /* hardware resources */ unsigned long iobase; u32 irq; + + /* Function support */ + struct trident_channel *(*alloc_pcm_channel)(struct trident_card *); + struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *); + void (*free_pcm_channel)(struct trident_card *, int chan); + void (*address_interrupt)(struct trident_card *); }; /* table to map from CHANNELMASK to channel attribute for SiS 7018 */ @@ -281,6 +303,9 @@ static int attr2mask [] = { static struct trident_card *devs = NULL; +static void ali_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); +static u16 ali_ac97_get(struct ac97_codec *codec, u8 reg); + static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); @@ -301,6 +326,7 @@ static int trident_enable_loop_interrupts(struct trident_card * card) case PCI_DEVICE_ID_SI_7018: global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); break; + case PCI_DEVICE_ID_ALI_5451: case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: global_control |= (ENDLP_IE | MIDLP_IE); @@ -463,6 +489,46 @@ static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *c return NULL; } +static struct trident_channel *ali_alloc_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx; + + bank = &card->banks[BANK_A]; + + if (bank->bitmap == ~0UL) { + /* no more free channels avaliable */ + printk(KERN_ERR "trident: no more channels available on Bank B.\n"); + return NULL; + } + for (idx = 0; idx <= 31; idx++) { + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + } + return NULL; +} + +static struct trident_channel *ali_alloc_rec_pcm_channel(struct trident_card *card) +{ + struct trident_pcm_bank *bank; + int idx = ALI_PCM_IN_CHANNEL; + + bank = &card->banks[BANK_A]; + + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx; + return channel; + } + return NULL; +} + + static void trident_free_pcm_channel(struct trident_card *card, int channel) { int bank; @@ -478,7 +544,24 @@ static void trident_free_pcm_channel(struct trident_card *card, int channel) } } +static void ali_free_pcm_channel(struct trident_card *card, int channel) +{ + int bank; + + if (channel > 31) + return; + + bank = channel >> 5; + channel = channel & 0x1f; + + if (card->banks[bank].bitmap & (1 << (channel))) { + card->banks[bank].bitmap &= ~(1 << (channel)); + } +} + + /* called with spin lock held */ + static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) { int i; @@ -491,6 +574,9 @@ static int trident_load_channel_registers(struct trident_card *card, u32 *data, /* output the channel registers */ for (i = 0; i < CHANNEL_REGS; i++) { outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); + if (i == 2) + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) + i++; //skip i=3 } return TRUE; @@ -509,6 +595,11 @@ static int trident_write_voice_regs(struct trident_state *state) switch (state->card->pci_id) { + case PCI_DEVICE_ID_ALI_5451: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = 0; + break; case PCI_DEVICE_ID_SI_7018: data[0] = 0; /* Current Sample Offset */ data[2] = (channel->eso << 16) | (channel->delta & 0xffff); @@ -654,6 +745,7 @@ static void trident_rec_setup(struct trident_state *state) /* Enable AC-97 ADC (capture) */ switch (card->pci_id) { + case PCI_DEVICE_ID_ALI_5451: case PCI_DEVICE_ID_SI_7018: /* for 7018, the ac97 is always in playback/record (duplex) mode */ break; @@ -717,6 +809,7 @@ extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state) switch (state->card->pci_id) { + case PCI_DEVICE_ID_ALI_5451: case PCI_DEVICE_ID_SI_7018: case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: /* 16 bits ESO, CSO for 7018 and DX */ @@ -1029,8 +1122,11 @@ static int drain_dac(struct trident_state *state, int nonblock) static void trident_update_ptr(struct trident_state *state) { struct dmabuf *dmabuf = &state->dmabuf; - unsigned hwptr; + unsigned hwptr, swptr; + int clear_cnt = 0; int diff; + unsigned char silence; + unsigned half_dmasize; /* update hardware pointer */ hwptr = trident_get_dma_addr(state); @@ -1054,6 +1150,31 @@ static void trident_update_ptr(struct trident_state *state) __stop_adc(state); dmabuf->error++; } + else if (!dmabuf->endcleared) { + swptr = dmabuf->swptr; + silence = (dmabuf->fmt & TRIDENT_FMT_16BIT ? 0 : 0x80); + if (dmabuf->update_flag & ALI_ADDRESS_INT_UPDATE) { + /* We must clear end data of 1/2 dmabuf if needed. + According to 1/2 algorithm of Address Engine Interrupt, + check the validation of the data of half dmasize. */ + half_dmasize = dmabuf->dmasize / 2; + if ((diff = hwptr - half_dmasize) < 0 ) + diff = hwptr; + if ((dmabuf->count + diff) < half_dmasize) { + //there is invalid data in the end of half buffer + if ((clear_cnt = half_dmasize - swptr) < 0) + clear_cnt += half_dmasize; + memset (dmabuf->rawbuf + swptr, silence, clear_cnt); //clear the invalid data + dmabuf->endcleared = 1; + } + } else if (dmabuf->count < (signed) dmabuf->fragsize) { + clear_cnt = dmabuf->fragsize; + if ((swptr + clear_cnt) > dmabuf->dmasize) + clear_cnt = dmabuf->dmasize - swptr; + memset (dmabuf->rawbuf + swptr, silence, clear_cnt); + dmabuf->endcleared = 1; + } + } /* since dma machine only interrupts at ESO and ESO/2, we sure have at least half of dma buffer free, so wake up the process unconditionally */ wake_up(&dmabuf->wait); @@ -1080,13 +1201,54 @@ static void trident_update_ptr(struct trident_state *state) wake_up(&dmabuf->wait); } } + dmabuf->update_flag &= ~ALI_ADDRESS_INT_UPDATE; } -static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void trident_address_interrupt(struct trident_card *card) { + int i; struct trident_state *state; - struct trident_card *card = (struct trident_card *)dev_id; + + /* Update the pointers for all channels we are running. */ + /* FIXME: should read interrupt status only once */ + for (i = 0; i < NR_HW_CH; i++) { + if (trident_check_channel_interrupt(card, 63 - i)) { + trident_ack_channel_interrupt(card, 63 - i); + if ((state = card->states[i]) != NULL) { + trident_update_ptr(state); + } else { + printk("trident: spurious channel irq %d.\n", + 63 - i); + trident_stop_voice(card, 63 - i); + trident_disable_voice_irq(card, 63 - i); + } + } + } +} + +static void ali_address_interrupt(struct trident_card *card) +{ int i; + struct trident_state *state; + + for (i = 0; i < NR_HW_CH; i++) { + if (trident_check_channel_interrupt(card, i)) { + trident_ack_channel_interrupt(card, i); + if ((state = card->states[i]) != NULL) { + state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE; + trident_update_ptr(state); + } else { + printk("ali: spurious channel irq %d.\n", i); + trident_stop_voice(card, i); + trident_disable_voice_irq(card, i); + } + } + } +} + +static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct trident_card *card = (struct trident_card *)dev_id; u32 event; spin_lock(&card->lock); @@ -1097,21 +1259,7 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif if (event & ADDRESS_IRQ) { - /* Update the pointers for all channels we are running. */ - /* FIXME: should read interrupt status only once */ - for (i = 0; i < NR_HW_CH; i++) { - if (trident_check_channel_interrupt(card, 63 - i)) { - trident_ack_channel_interrupt(card, 63 - i); - if ((state = card->states[i]) != NULL) { - trident_update_ptr(state); - } else { - printk("trident: spurious channel irq %d.\n", - 63 - i); - trident_stop_voice(card, 63 - i); - trident_disable_voice_irq(card, 63 - i); - } - } - } + card->address_interrupt(card); } /* manually clear interrupt status, bad hardware design, blame T^2 */ @@ -1151,6 +1299,9 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ return -EFAULT; ret = 0; + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) + outl ( inl (TRID_REG (state->card, ALI_GLOBAL_CONTROL)) | ALI_PCM_IN_ENABLE, TRID_REG (state->card, ALI_GLOBAL_CONTROL)); + while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count > (signed) dmabuf->dmasize) { @@ -1250,6 +1401,10 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count return -EFAULT; ret = 0; + if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451) + if (dmabuf->channel->num == ALI_PCM_IN_CHANNEL) + outl ( inl (TRID_REG (state->card, ALI_GLOBAL_CONTROL)) & ALI_PCM_IN_DISABLE, TRID_REG (state->card, ALI_GLOBAL_CONTROL)); + while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); if (dmabuf->count < 0) { @@ -1312,6 +1467,7 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count spin_lock_irqsave(&state->card->lock, flags); dmabuf->swptr = swptr; dmabuf->count += cnt; + dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; @@ -1733,7 +1889,12 @@ static int trident_open(struct inode *inode, struct file *file) found_virt: /* found a free virtual channel, allocate hardware channels */ - if ((dmabuf->channel = trident_alloc_pcm_channel(card)) == NULL) { + if(file->f_mode & FMODE_READ) + dmabuf->channel = card->alloc_rec_pcm_channel(card); + else + dmabuf->channel = card->alloc_pcm_channel(card); + + if (dmabuf->channel == NULL) { kfree (card->states[i]); card->states[i] = NULL;; return -ENODEV; @@ -1767,7 +1928,12 @@ static int trident_open(struct inode *inode, struct file *file) } if (file->f_mode & FMODE_READ) { - /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample, + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + card->states[ALI_PCM_IN_CHANNEL] = state; + card->states[i] = NULL; + state->virt = ALI_PCM_IN_CHANNEL; + } + /* FIXME: Trident 4d can only record in signed 16-bits stereo, 48kHz sample, to be dealed with in trident_set_adc_rate() ?? */ dmabuf->fmt &= ~TRIDENT_FMT_MASK; if ((minor & 0x0f) == SND_DEV_DSP16) @@ -1809,12 +1975,12 @@ static int trident_release(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) { stop_dac(state); dealloc_dmabuf(state); - trident_free_pcm_channel(state->card, dmabuf->channel->num); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); } if (file->f_mode & FMODE_READ) { stop_adc(state); dealloc_dmabuf(state); - trident_free_pcm_channel(state->card, dmabuf->channel->num); + state->card->free_pcm_channel(state->card, dmabuf->channel->num); } kfree(state->card->states[state->virt]); @@ -1944,6 +2110,101 @@ static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) return ((u16) (data >> 16)); } +/* Write AC97 codec registers for ALi*/ +static void ali_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + address = ALI_AC97_WRITE; + mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= ALI_AC97_SECONDARY; + if (card->revision == 0x02) + mask |= ALI_AC97_WRITE_MIXER_REGISTER; + + spin_lock_irqsave(&card->lock, flags); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_WRITE) == 0) { + data |= (mask | (reg & AC97_REG_ADDR)); + + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + spin_unlock_irqrestore(&card->lock, flags); + return; + } + outl(data, TRID_REG(card, address)); //write! + spin_unlock_irqrestore(&card->lock, flags); + return; //success + } + inw(TRID_REG(card, address)); //wait a read cycle + } + + printk(KERN_ERR "ali: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; +} + +/* Read AC97 codec registers for ALi*/ +static u16 ali_ac97_get(struct ac97_codec *codec, u8 reg) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask; + unsigned int wCount1 = 0xffff; + unsigned int wCount2= 0xffff; + unsigned long chk1, chk2; + unsigned long flags; + u32 data; + + address = ALI_AC97_READ; + if (card->revision == 0x02) { + address = ALI_AC97_WRITE; + mask &= ALI_AC97_READ_MIXER_REGISTER; + } + mask = ALI_AC97_READ_ACTION | ALI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= ALI_AC97_SECONDARY; + + spin_lock_irqsave(&card->lock, flags); + data = (mask | (reg & AC97_REG_ADDR)); + while (wCount1--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + chk1 = inl(TRID_REG(card, ALI_STIMER)); + chk2 = inl(TRID_REG(card, ALI_STIMER)); + while (wCount2-- && (chk1 == chk2)) + chk2 = inl(TRID_REG(card, ALI_STIMER)); + if (wCount2 == 0) { + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return 0; + } + outl(data, TRID_REG(card, address)); //read! + wCount2 = 0xffff; + while (wCount2--) { + if ((inw(TRID_REG(card, address)) & ALI_AC97_BUSY_READ) == 0) { + data = inl(TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); + return ((u16) (data >> 16)); + } + } + } + inw(TRID_REG(card, address)); //wait a read cycle + } + spin_unlock_irqrestore(&card->lock, flags); + printk(KERN_ERR "ali: AC97 CODEC read timed out.\n"); + return 0; +} + /* OSS /dev/mixer file operation methods */ static int trident_open_mixdev(struct inode *inode, struct file *file) { @@ -1999,6 +2260,11 @@ static int __init trident_ac97_init(struct trident_card *card) really exist */ switch (card->pci_id) { + case PCI_DEVICE_ID_ALI_5451: + outl(PCMOUT|SECONDARY_ID, TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd = inl(TRID_REG(card, SI_SERIAL_INTF_CTRL)); + ready_2nd &= SI_AC97_SECONDARY_READY; + break; case PCI_DEVICE_ID_SI_7018: /* disable AC97 GPIO interrupt */ outl(0x00, TRID_REG(card, SI_AC97_GPIO)); @@ -2032,10 +2298,16 @@ static int __init trident_ac97_init(struct trident_card *card) in ac97_probe_codec */ codec->private_data = card; codec->id = num_ac97; - /* controller specific low level AC97 access function */ - codec->codec_read = trident_ac97_get; - codec->codec_write = trident_ac97_set; + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) { + codec->codec_read = ali_ac97_get; + codec->codec_write = ali_ac97_set; + } + else { + codec->codec_read = trident_ac97_get; + codec->codec_write = trident_ac97_set; + } + if (ac97_probe_codec(codec) == 0) break; @@ -2061,12 +2333,14 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device { unsigned long iobase; struct trident_card *card; + u8 revision; if (!pci_dma_supported(pci_dev, TRIDENT_DMA_MASK)) { printk(KERN_ERR "trident: architecture does not support" " 30bit PCI busmaster DMA\n"); return -ENODEV; } + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision); iobase = pci_dev->resource[0].start; if (check_region(iobase, 256)) { @@ -2084,6 +2358,7 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device card->iobase = iobase; card->pci_dev = pci_dev; card->pci_id = pci_id->device; + card->revision = revision; card->irq = pci_dev->irq; card->next = devs; card->magic = TRIDENT_CARD_MAGIC; @@ -2100,6 +2375,20 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", card_names[pci_id->driver_data], card->iobase, card->irq); + if(card->pci_id == PCI_DEVICE_ID_ALI_5451) + { + card->alloc_pcm_channel = ali_alloc_pcm_channel; + card->alloc_rec_pcm_channel = ali_alloc_rec_pcm_channel; + card->free_pcm_channel = ali_free_pcm_channel; + card->address_interrupt = ali_address_interrupt; + } + else + { + card->alloc_pcm_channel = trident_alloc_pcm_channel; + card->alloc_rec_pcm_channel = trident_alloc_pcm_channel; + card->free_pcm_channel = trident_free_pcm_channel; + card->address_interrupt = trident_address_interrupt; + } /* claim our iospace and irq */ request_region(card->iobase, 256, card_names[pci_id->driver_data]); if (request_irq(card->irq, &trident_interrupt, SA_SHIRQ, @@ -2111,7 +2400,7 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device } /* register /dev/dsp */ if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) { - printk(KERN_ERR "trident: coundn't register DSP device!\n"); + printk(KERN_ERR "trident: couldn't register DSP device!\n"); release_region(iobase, 256); free_irq(card->irq, card); kfree(card); @@ -2127,6 +2416,16 @@ static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device } outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); + if (card->pci_id == PCI_DEVICE_ID_ALI_5451) + { + /* edited by HMSEO for GT sound */ +#ifdef CONFIG_ALPHA_NAUTILUS + ac97_data = trident_ac97_get (card->ac97_codec[0], AC97_POWER_CONTROL); + trident_ac97_set (card->ac97_codec[0], AC97_POWER_CONTROL, ac97_data | ALI_EAPD_POWER_DOWN); +#endif + /* edited by HMSEO for GT sound*/ + } + pci_dev->driver_data = card; pci_dev->dma_mask = TRIDENT_DMA_MASK; @@ -2159,8 +2458,8 @@ static void __exit trident_remove(struct pci_dev *pci_dev) kfree(card); } -MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho"); -MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver"); +MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho, Ching Ling Lee"); +MODULE_DESCRIPTION("Trident 4DWave/SiS 7018/ALi 5451 PCI Audio Driver"); #define TRIDENT_MODULE_NAME "trident" @@ -2176,7 +2475,7 @@ static int __init trident_init_module (void) if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "Trident 4DWave/SiS 7018 PCI Audio, version " + printk(KERN_INFO "Trident 4DWave/SiS 7018/ALi 5451 PCI Audio, version " DRIVER_VERSION ", " __TIME__ " " __DATE__ "\n"); if (!pci_register_driver(&trident_pci_driver)) { diff --git a/drivers/sound/trident.h b/drivers/sound/trident.h index 847f85d873cf..dc8239b0efa9 100644 --- a/drivers/sound/trident.h +++ b/drivers/sound/trident.h @@ -32,6 +32,10 @@ #define PCI_VENDOR_ID_SI 0x0139 #endif +#ifndef PCI_VENDOR_ID_ALI +#define PCI_VENDOR_ID_ALI 0x10b9 +#endif + #ifndef PCI_DEVICE_ID_TRIDENT_4DWAVE_DX #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 #endif @@ -44,6 +48,10 @@ #define PCI_DEVICE_ID_SI_7018 0x7018 #endif +#ifndef PCI_DEVICE_ID_ALI_5451 +#define PCI_DEVICE_ID_ALI_5451 0x5451 +#endif + #ifndef FALSE #define FALSE 0 #define TRUE 1 @@ -81,6 +89,28 @@ enum trident_op_registers { T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc }; +enum ali_op_registers { + ALI_GLOBAL_CONTROL = 0xd4, + ALI_STIMER = 0xc8 +}; + +enum ali_global_control_bit { + ALI_PCM_IN_ENABLE = 0x80000000, + ALI_PCM_IN_DISABLE = 0x7fffffff +}; + +enum ali_pcm_in_channel_num { + ALI_PCM_IN_CHANNEL = 31 +}; + +enum ali_ac97_power_control_bit { + ALI_EAPD_POWER_DOWN = 0x8000 +}; + +enum ali_update_ptr_flags { + ALI_ADDRESS_INT_UPDATE = 0x01 +}; + /* S/PDIF Operational Registers for 4D-NX */ enum nx_spdif_registers { NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, @@ -113,9 +143,21 @@ enum si_ac97_registers { SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c }; +enum ali_ac97_registers { + ALI_AC97_WRITE = 0x40, ALI_AC97_READ = 0x44 +}; + /* Bit mask for operational registers */ #define AC97_REG_ADDR 0x000000ff +enum ali_ac97_bits { + ALI_AC97_BUSY_WRITE = 0x8000, ALI_AC97_BUSY_READ = 0x8000, + ALI_AC97_WRITE_ACTION = 0x8000, ALI_AC97_READ_ACTION = 0x8000, + ALI_AC97_AUDIO_BUSY = 0x4000, ALI_AC97_SECONDARY = 0x0080, + ALI_AC97_READ_MIXER_REGISTER = 0xfeff, + ALI_AC97_WRITE_MIXER_REGISTER = 0x0100 +}; + enum sis7018_ac97_bits { SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, diff --git a/drivers/video/mdacon.c b/drivers/video/mdacon.c index 0c45722febab..f249071f25f1 100644 --- a/drivers/video/mdacon.c +++ b/drivers/video/mdacon.c @@ -5,6 +5,8 @@ * * including portions (c) 1995-1998 Patrick Caulfield. * + * slight improvements (c) 2000 Edward Betts + * * This file is based on the VGA console driver (vgacon.c): * * Created 28 Sep 1997 by Geert Uytterhoeven @@ -240,13 +242,18 @@ static int __init mda_detect(void) /* Ok, there is definitely a card registering at the correct * memory location, so now we do an I/O port test. */ - - if (! test_mda_b(0x66, 0x0f)) { /* cursor low register */ + + /* Edward: These two mess `tests' mess up my cursor on bootup */ + + /* cursor low register */ + /* if (! test_mda_b(0x66, 0x0f)) { return 0; - } - if (! test_mda_b(0x99, 0x0f)) { /* cursor low register */ + } */ + + /* cursor low register */ + /* if (! test_mda_b(0x99, 0x0f)) { return 0; - } + } */ /* See if the card is a Hercules, by checking whether the vsync * bit of the status register is changing. This test lasts for @@ -339,6 +346,9 @@ static const char __init *mdacon_startup(void) mda_initialize(); } + /* cursor looks ugly during boot-up, so turn it off */ + mda_set_cursor(mda_vram_len - 1); + printk("mdacon: %s with %ldK of memory detected.\n", mda_type_name, mda_vram_len/1024); @@ -494,13 +504,21 @@ static int mdacon_set_palette(struct vc_data *c, unsigned char *table) static int mdacon_blank(struct vc_data *c, int blank) { - if (blank) { - outb_p(0x00, mda_mode_port); /* disable video */ + if (mda_type == TYPE_MDA) { + if (blank) + scr_memsetw((void *)mda_vram_base, + mda_convert_attr(c->vc_video_erase_char), + c->vc_screenbuf_size); + /* Tell console.c that it has to restore the screen itself */ + return 1; } else { - outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, mda_mode_port); + if (blank) + outb_p(0x00, mda_mode_port); /* disable video */ + else + outb_p(MDA_MODE_VIDEO_EN | MDA_MODE_BLINK_EN, + mda_mode_port); + return 0; } - - return 0; } static int mdacon_font_op(struct vc_data *c, struct console_font_op *op) diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index eef60b04e33e..4d214b025022 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -20,7 +20,7 @@ */ /* version number of this driver */ -#define RIVAFB_VERSION "0.7.0" +#define RIVAFB_VERSION "0.7.1" #include #include @@ -99,6 +99,7 @@ enum riva_chips { CH_RIVA_TNT2, CH_RIVA_UTNT2, /* UTNT2 */ CH_RIVA_VTNT2, /* VTNT2 */ + CH_RIVA_UVTNT2, /* VTNT2 */ CH_RIVA_ITNT2, /* ITNT2 */ }; @@ -113,6 +114,7 @@ static struct riva_chip_info { { "RIVA-TNT2", 5 }, { "RIVA-UTNT2", 5 }, { "RIVA-VTNT2", 5 }, + { "RIVA-UVTNT2", 5 }, { "RIVA-ITNT2", 5 }, }; @@ -123,6 +125,7 @@ static struct pci_device_id rivafb_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_TNT2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_UTNT2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_VTNT2 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_RIVA_ITNT2 }, { 0, }, /* terminate list */ }; diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index fcb6443ed66a..f39c0dfd453c 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -1,9 +1,9 @@ /* * linux/drivers/video/tgafb.c -- DEC 21030 TGA frame buffer device * - * Copyright (C) 1999 Martin Lucina, Tom Zerucha + * Copyright (C) 1999,2000 Martin Lucina, Tom Zerucha * - * $Id: tgafb.c,v 1.12 1999/07/01 13:39:23 mato Exp $ + * $Id: tgafb.c,v 1.12.2.3 2000/04/04 06:44:56 mato Exp $ * * This driver is partly based on the original TGA framebuffer device, which * was partly based on the original TGA console driver, which are @@ -42,6 +42,7 @@ #include #include #include +#include #include #include