From d24028e95f2875c2009d99fb4748e91250ff1f28 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:30:33 -0500 Subject: [PATCH] Import 2.3.42pre1 --- CREDITS | 35 + Documentation/Configure.help | 144 +- MAINTAINERS | 41 + Makefile | 2 +- arch/i386/config.in | 2 +- arch/m68k/amiga/amiints.c | 6 +- arch/m68k/atari/debug.c | 4 - arch/m68k/bvme6000/config.c | 1 + arch/m68k/kernel/signal.c | 2 + arch/m68k/kernel/traps.c | 23 - arch/m68k/mac/debug.c | 4 - arch/m68k/mvme147/config.c | 1 + arch/m68k/mvme16x/config.c | 1 + drivers/block/swim3.c | 40 - drivers/block/swim_iop.c | 38 - drivers/char/agp/agpgart_be.c | 81 +- drivers/char/agp/agpgart_fe.c | 3 +- drivers/char/generic_serial.c | 9 +- drivers/char/generic_serial.h | 3 + drivers/char/scc.h | 614 ++++ drivers/char/serial167.c | 100 +- drivers/char/tty_io.c | 21 +- drivers/char/vme_scc.c | 1136 ++++++++ drivers/i2c/Config.in | 22 +- drivers/i2c/i2c-core.c | 612 ++-- drivers/i2c/i2c-dev.c | 605 ++-- drivers/i2c/i2c-elektor.c | 213 +- drivers/i2c/i2c-pcf8584.h | 38 +- drivers/ieee1394/ieee1394_syms.c | 1 + drivers/ieee1394/ieee1394_transactions.c | 24 + drivers/ieee1394/ieee1394_transactions.h | 3 + drivers/ieee1394/ohci1394.c | 461 ++- drivers/ieee1394/ohci1394.h | 26 +- drivers/ieee1394/raw1394.c | 30 +- drivers/net/Makefile | 4 +- drivers/net/Space.c | 21 +- drivers/net/cs89x0.h | 6 + drivers/net/daynaport.c | 451 ++- drivers/net/irda/Config.in | 10 +- drivers/net/irda/Makefile | 12 +- drivers/net/irda/airport.c | 358 --- drivers/net/irda/irport.c | 61 +- drivers/net/irda/irtty.c | 10 +- drivers/net/irda/{nsc_fir.c => nsc-ircc.c} | 1017 ++++--- drivers/net/irda/smc-ircc.c | 698 +++-- drivers/net/irda/toshoboe.c | 46 +- drivers/net/irda/w83977af_ir.c | 51 +- drivers/net/mac89x0.c | 678 +++++ drivers/net/macmace.c | 825 ++++++ drivers/net/macsonic.c | 835 +++--- drivers/net/sun3lance.c | 21 +- drivers/scsi/sd.c | 55 +- drivers/sound/Config.in | 2 +- drivers/sound/Makefile | 4 +- drivers/sound/ac97_codec.c | 441 +++ drivers/sound/ac97_codec.h | 157 + drivers/sound/trident.c | 3055 ++++++++------------ drivers/sound/trident.h | 266 +- drivers/usb/Config.in | 1 + drivers/usb/Makefile | 2 + drivers/usb/acm.c | 36 +- drivers/usb/ov511.c | 2 +- drivers/usb/printer.c | 19 +- drivers/usb/scanner.c | 1 - drivers/usb/uhci-debug.h | 399 +-- drivers/usb/uhci.c | 2215 ++++++++++++++ drivers/usb/uhci.h | 425 +++ drivers/usb/usb-core.c | 3 + drivers/usb/usb-serial.c | 8 +- drivers/usb/usb-uhci-debug.h | 195 ++ drivers/usb/usb-uhci.c | 2 +- drivers/usb/usb.c | 47 +- drivers/usb/usb.h | 1 + drivers/video/dn_accel.h | 9 + drivers/video/dn_cfb4.c | 547 ++++ drivers/video/dn_cfb8.c | 595 ++++ drivers/video/dnfb.c | 396 ++- fs/autofs4/inode.c | 8 +- fs/nfsd/export.c | 1 + fs/nfsd/lockd.c | 2 +- fs/nfsd/nfsproc.c | 4 - fs/nfsd/vfs.c | 55 +- fs/select.c | 15 +- include/asm-m68k/entry.h | 36 +- include/asm-m68k/init.h | 16 - include/asm-m68k/kgdb.h | 83 - include/asm-m68k/semaphore.h | 35 +- include/linux/agp_backend.h | 1 + include/linux/agpgart.h | 8 + include/linux/auto_fs.h | 8 +- include/linux/i2c-dev.h | 127 +- include/linux/i2c-elektor.h | 10 +- include/linux/nfsd/export.h | 3 +- include/linux/nfsd/nfsd.h | 5 +- include/linux/pci.h | 6 +- include/linux/serial.h | 15 + include/linux/serialP.h | 10 +- include/net/irda/ircomm_tty.h | 4 +- include/net/irda/irda.h | 4 +- include/net/irda/irda_device.h | 28 +- include/net/irda/irmod.h | 4 +- include/net/irda/irport.h | 8 +- include/net/irda/irtty.h | 11 +- include/net/irda/{nsc_fir.h => nsc-ircc.h} | 79 +- include/net/irda/smc-ircc.h | 237 +- include/net/irda/toshoboe.h | 28 +- include/net/irda/w83977af_ir.h | 12 +- include/net/irda/wrapper.h | 6 +- ipc/shm.c | 9 + net/ax25/af_ax25.c | 2 +- net/irda/irda_device.c | 10 +- net/irda/wrapper.c | 30 +- net/netrom/af_netrom.c | 2 +- net/rose/af_rose.c | 2 +- 114 files changed, 13560 insertions(+), 5697 deletions(-) create mode 100644 drivers/char/scc.h create mode 100644 drivers/char/vme_scc.c delete mode 100644 drivers/net/irda/airport.c rename drivers/net/irda/{nsc_fir.c => nsc-ircc.c} (62%) create mode 100644 drivers/net/mac89x0.c create mode 100644 drivers/net/macmace.c create mode 100644 drivers/sound/ac97_codec.c create mode 100644 drivers/sound/ac97_codec.h create mode 100644 drivers/usb/uhci.c create mode 100644 drivers/usb/uhci.h create mode 100644 drivers/usb/usb-uhci-debug.h create mode 100644 drivers/video/dn_accel.h create mode 100644 drivers/video/dn_cfb4.c create mode 100644 drivers/video/dn_cfb8.c delete mode 100644 include/asm-m68k/kgdb.h rename include/net/irda/{nsc_fir.h => nsc-ircc.h} (79%) diff --git a/CREDITS b/CREDITS index dd5ea002b751..cc4e3b84bd3e 100644 --- a/CREDITS +++ b/CREDITS @@ -287,6 +287,13 @@ N: Erik Inge Bols E: knan@mo.himolde.no D: Misc kernel hacks +N: Andreas E. Bombe +E: andreas.bombe@munich.netsurf.de +W: http://home.pages.de/~andreas.bombe/ +P: 1024/04880A44 72E5 7031 4414 2EB6 F6B4 4CBD 1181 7032 0488 0A44 +D: IEEE 1394 subsystem rewrite and maintainer +D: Texas Instruments PCILynx IEEE 1394 driver + N: Zoltán Böszörményi E: zboszor@mail.externet.hu D: MTRR emulation with Cyrix style ARR registers, Athlon MTRR support @@ -1306,6 +1313,14 @@ S: Post Office Box 371 S: North Little Rock, Arkansas 72115 S: USA +N: Stephan Linz +E: linz@mazet.de +E: Stephan.Linz@gmx.de +W: http://www.crosswinds.net/~tuxer +D: PCILynx patch to work with 1394a PHY and without local RAM +S: (ask for current address) +S: Germany + N: Siegfried "Frieder" Loeffler (dg1sek) E: floeff@tunix.mathematik.uni-stuttgart.de, fl@LF.net W: http://www.mathematik.uni-stuttgart.de/~floeff @@ -1702,6 +1717,13 @@ S: Demonstratsii 8-382 S: Tula 300000 S: Russia +N: Gordon Peters +E: GordPeters@smarttech.com +D: Isochronous receive for IEEE 1394 driver (OHCI module). +D: Bugfixes for the aforementioned. +S: Calgary, Alberta +S: Canada + N: Johnnie Peters E: jpeters@phx.mcd.mot.com D: Motorola PowerPC changes for PReP @@ -1736,6 +1758,12 @@ S: M"ullerstr. 69 S: 13349 Berlin S: Germany +N: Emanuel Pirker +E: epirker@edu.uni-klu.ac.at +D: AIC5800 IEEE 1394, RAW I/O on 1394 +D: Starter of Linux1394 effort +S: ask per mail for current address + N: Ken Pizzini E: ken@halcyon.com D: CDROM driver "sonycd535" (Sony CDU-535/531) @@ -1852,6 +1880,13 @@ S: 21 Rue Carnot S: 95170 Deuil La Barre S: France +N: Sebastien Rougeaux +E: Sebastien.Rougeaux@syseng.anu.edu.au +D: IEEE 1394 OHCI module +S: Research School of Information Science and Engineering +S: The Australian National University, ACT 0200 +S: Australia + N: Alessandro Rubini E: rubini@ipvvis.unipv.it D: the gpm mouse server and kernel support for it diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 3c55bd0b224f..7e8b661cf547 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -7504,14 +7504,16 @@ CONFIG_IBMOL Linux Token Ring Project site for the latest information at http://www.linuxtr.net -Generic TMS380 Token Ring ISA/PCI adapter support +Generic TMS380 Token Ring ISA/PCI/MCA/EISA adapter support CONFIG_TMS380TR This driver provides generic support for token ring adapters based on the Texas Instruments TMS380 series chipsets. This includes the SysKonnect TR4/16(+) ISA (SK-4190), SysKonnect TR4/16(+) PCI (SK-4590), SysKonnect TR4/16 PCI (SK-4591), - Compaq 4/16 PCI, Thomas-Conrad TC4048 4/16 PCI, and Intel - TokenExpress 4/16 and PRO ISA adapters. + Compaq 4/16 PCI, Thomas-Conrad TC4048 4/16 PCI, and several + Madge adapters. If selected, you will be asked to select + which cards to support below. If you're using modules, each + class of card will be supported by a seperate module. If you have such an adapter and would like to use it, say Y or M and read the Token-Ring mini-HOWTO, available from @@ -7520,9 +7522,28 @@ CONFIG_TMS380TR Also read the file linux/Documentation/networking/tms380tr.txt or check http://www.auk.cx/tms380tr/ +Generic TMS380 PCI support +CONFIG_TMSPCI + This tms380 module supports generic TMS380-based PCI cards. + + These cards are known to work: + - Compaq 4/16 TR PCI + - SysKonnect TR4/16 PCI (SK-4590/SK-4591) + - Thomas-Conrad TC4048 PCI 4/16 + - 3Com Token Link Velocity + +Madge Smart 16/4 PCI Mk2 support +CONFIG_ABYSS + This tms380 module supports the Madge Smart 16/4 PCI Mk2 cards (51-02). + +Madge Smart 16/4 Ringode MicroChannel +CONFIG_MADGEMC + This tms380 module supports the Madge Smart 16/4 MC16 and MC32 + MicroChannel adapters + SMC ISA TokenRing adapter support CONFIG_SMCTR - This is support for the ISA SMC Token Ring cards, specifically + This is support for the ISA and MCA SMC Token Ring cards, specifically SMC TokenCard Elite (8115T) and SMC TokenCard Elite/A (8115T/A) adapters. If you have such an adapter and would like to use it, say Y or M and @@ -7928,6 +7949,25 @@ CONFIG_USB_UHCI The module will be called usb-uhci.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. +UHCI (intel PIIX4, VIA, ...) alternate (JE) support? +CONFIG_USB_UHCI_ALT + This is an alternate driver for UHCI support. It has been commonly + been referred to as the "JE driver". + + The Universal Host Controller Interface is a standard by Intel for + accessing the USB hardware in the PC (which is also called the USB + host controller). If your USB host controller conforms to this + standard, say Y. All recent boards with Intel PCI chipsets (like + intel 430TX, 440FX, 440LX, 440BX, i810, i820) conform to this standard. + Also all VIA PCI chipsets (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo + Pro II or Apollo Pro 133). + If unsure, say Y. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called uhci.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support? CONFIG_USB_OHCI The Open Host Controller Interface is a standard by @@ -8735,6 +8775,30 @@ CONFIG_AUTOFS_FS If you are not a part of a fairly large, distributed network, you probably do not need an automounter, and can say N here. + If you want to use the newer version of autofs with more features, + say N here and select automounter v4. + +Kernel automounter v4 support +CONFIG_AUTOFS4_FS + The automounter is a tool to automatically mount remote filesystems + on demand. This implementation is partially kernel-based to reduce + overhead in the already-mounted case; this is unlike the BSD + automounter (amd), which is a pure user space daemon. + + To use the automounter you need the user-space tools from + ftp://ftp.kernel.org/pub/linux/daemons/autofs/testing-v4 ; you also + want to answer Y to "NFS filesystem support", below. + + If you want to compile this as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. The module will be + called autofs4.o. You will need to add "alias autofs autofs4" to + your modules configuration file. + + If you are not a part of a fairly large, distributed network or don't + have a laptop which needs to dynamically reconfigure to the local + network, you probably do not need an automounter, and can say N here. + EFS filesystem support (read only) (EXPERIMENTAL) CONFIG_EFS_FS EFS is an older filesystem used for non-ISO9660 CDROMs and hard disk @@ -10865,20 +10929,6 @@ CONFIG_SOUND_SB You can say M here to compile this driver as a module; the module is called sb.o. -Generic OPL2/OPL3 FM synthesizer support -CONFIG_SOUND_ADLIB - Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). - Answering Y is usually a safe and recommended choice, however some - cards may have software (TSR) FM emulation. Enabling FM support with - these cards may cause trouble (I don't currently know of any such - cards, however). - - Please read the file Documentation/sound/OPL3 if your card has an - OPL3 chip. - - If unsure, say Y. - - #Loopback MIDI device support #CONFIG_SOUND_VMIDI ### @@ -11154,8 +11204,16 @@ CONFIG_SOUND_MIDI FM synthesizer (YM3812/OPL-3) support CONFIG_SOUND_YM3812 - Answer Y here, unless you know you will not need the option. + Answer Y if your card has a FM chip made by Yamaha (OPL2/OPL3/OPL4). + Answering Y is usually a safe and recommended choice, however some + cards may have software (TSR) FM emulation. Enabling FM support with + these cards may cause trouble (I don't currently know of any such + cards, however). + Please read the file Documentation/sound/OPL3 if your card has an + OPL3 chip. + If unsure, say Y. + Sun Audio support CONFIG_SUN_AUDIO This is support for the sound cards on Sun workstations. The code @@ -11323,6 +11381,20 @@ CONFIG_SOUND_SONICVIBES differs slightly from OSS/Free, so PLEASE READ Documentation/sound/sonicvibes. +Trident 4DWave DX/NX or SiS 7018 PCI Audio Core +CONFIG_SOUND_TRIDENT + Say Y or M if you have a PCI sound card utilizing the Trident + 4DWave-DX/NX chipset or your mother board chipset has SiS 7018 + built-in. The SiS 7018 PCI Audio Core is embedded in SiS960 + Super South Bridge and SiS540/630 Single Chipset. + + Use lspci -n to find out if your sound card or chipset uses + Trident 4DWave or SiS 7018. PCI ID 1023:2000 or 1023:2001 stands + for Trident 4Dwave. PCI ID 1039:7018 stands for SiS7018. + + This driver differs slightly from OSS/Free, so PLEASE READ the + comments at of driver/sound/trident.c + Rockwell WaveArtist CONFIG_SOUND_WAVEARTIST Say Y here to include support for the Rockwell WaveArtist sound @@ -13191,8 +13263,7 @@ CONFIG_IRCOMM and makes it possible to use all existing applications that understands TTY's with an infrared link. Thus you should be able to use application like PPP, minicom and others. Enabling this option - will create two modules called ircomm and ircomm_tty. For more - information go to http://www.pluto.dti.ne.jp/~thiguchi/irda/ + will create two modules called ircomm and ircomm-tty. IrTTY IrDA Device Driver CONFIG_IRTTY_SIR @@ -13242,11 +13313,10 @@ CONFIG_WINBOND_FIR NSC PC87108 IrDA Device Driver CONFIG_NSC_FIR - Say Y here if you want to build support for the NSC PC87108 IrDA - chipset. If you want to compile it as a module, say M here and - read Documentation/modules.txt. This drivers currently only supports - the ACTiSYS IR2000B ISA card and supports SIR, MIR and FIR (4Mbps) - speeds. + Say Y here if you want to build support for the NSC PC87108 and + PC87338 IrDA chipsets. If you want to compile it as a module, say M + here and read Documentation/modules.txt. This driver supports SIR, + MIR and FIR (4Mbps) speeds. Toshiba Type-O IR Port Device Driver CONFIG_TOSHIBA_FIR @@ -13262,7 +13332,7 @@ CONFIG_ESI_DONGLE and read Documentation/modules.txt. The ESI dongle attaches to the normal 9-pin serial port connector, and can currently only be used by IrTTY. To activate support for ESI dongles you will have to - insert "irattach -d esi" in the /etc/irda/drivers script. + start irattach like this: "irattach -d esi". ACTiSYS IR-220L and IR220L+ dongle CONFIG_ACTISYS_DONGLE @@ -13271,8 +13341,8 @@ CONFIG_ACTISYS_DONGLE say M here and read Documentation/modules.txt. The ACTiSYS dongles attaches to the normal 9-pin serial port connector, and can currently only be used by IrTTY. To activate support for ACTiSYS - dongles you will have to insert "irattach -d actisys" or - "irattach -d actisys_plus" in the/etc/irda/drivers script. + dongles you will have to start irattach like this: + "irattach -d actisys" or "irattach -d actisys+". Tekram IrMate 210B dongle CONFIG_TEKRAM_DONGLE @@ -13281,7 +13351,7 @@ CONFIG_TEKRAM_DONGLE and read Documentation/modules.txt. The Tekram dongle attaches to the normal 9-pin serial port connector, and can currently only be used by IrTTY. To activate support for Tekram dongles you will have - to insert "irattach -d tekram" in the /etc/irda/drivers script. + to start irattach like this: "irattach -d tekram". Greenwich GIrBIL dongle CONFIG_GIRBIL_DONGLE @@ -13299,17 +13369,7 @@ CONFIG_LITELINK_DONGLE Documentation/modules.txt. The Parallax dongle attaches to the normal 9-pin serial port connector, and can currently only be used by IrTTY. To activate support for Parallax dongles you will have to - insert "irattach -d litelink" in the /etc/irda/drivers script. - -Adaptec Airport 1000 and 2000 dongle -CONFIG_AIRPORT_DONGLE - Say Y here if you want to build support for the Adaptec Airport 1000 - and 2000 dongles. If you want to compile it as a module, say M here - and read Documentation/modules.txt. The module will be called - airport.o. The Airport dongle attaches to the normal 9-pin serial - port connector, and can currently only be used by IrTTY. To activate - support for Airport dongles you will have to insert "irattach -d - airport" in the /etc/irda/drivers script. + start irattach like this "irattach -d litelink". VME (Motorola and BVM) support CONFIG_VME @@ -13427,6 +13487,7 @@ CONFIG_KHTTPD The kHTTPd is experimental. Be careful when using it on a production machine. Also note that kHTTPd doesn't support virtual servers yet. +# I2C support CONFIG_I2C I2C (pronounce: I-square-C) is a slow bus protocol developed by @@ -13479,7 +13540,6 @@ CONFIG_I2C_CHARDEV files, usually found in the /dev directory on your system. They make it possible to have user-space programs use the I2C bus. -# # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, # Intel, IRQ, Linux, MSDOS, NetWare, NetWinder, NFS, diff --git a/MAINTAINERS b/MAINTAINERS index f053d2aae604..96b0c60b9a47 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -456,6 +456,41 @@ M: Gadi Oxman L: linux-kernel@vger.rutgers.edu S: Maintained +IEEE 1394 SUBSYSTEM +P: Andreas Bombe +M: andreas.bombe@munich.netsurf.de +L: linux1394-devel@eclipt.uni-klu.ac.at +W: http://eclipt.uni-klu.ac.at/ieee1394 +S: Maintained + +IEEE 1394 AIC5800 DRIVER +P: Emanuel Pirker +M: epirker@edu.uni-klu.ac.at +L: linux1394-devel@eclipt.uni-klu.ac.at +W: http://eclipt.uni-klu.ac.at/ieee1394 +S: Maintained + +IEEE 1394 OHCI DRIVER +P: Sebastien Rougeaux +M: sebastien.rougeaux@anu.edu.au +L: linux1394-devel@eclipt.uni-klu.ac.at +W: http://eclipt.uni-klu.ac.at/ieee1394 +S: Maintained + +IEEE 1394 PCILYNX DRIVER +P: Andreas Bombe +M: andreas.bombe@munich.netsurf.de +L: linux1394-devel@eclipt.uni-klu.ac.at +W: http://eclipt.uni-klu.ac.at/ieee1394 +S: Maintained + +IEEE 1394 RAW I/O DRIVER +P: Andreas Bombe +M: andreas.bombe@munich.netsurf.de +L: linux1394-devel@eclipt.uni-klu.ac.at +W: http://eclipt.uni-klu.ac.at/ieee1394 +S: Maintained + INTEL APIC/IOAPIC, LOWLEVEL X86 SMP SUPPORT P: Ingo Molnar M: mingo@redhat.com @@ -936,6 +971,12 @@ L: linux-net@vger.rutgers.edu L: linux-tr@linuxtr.net S: Maintained +TRIDENT 4DWAVE/SIS 7018 PCI AUDIO CORE +P: Ollie Lho +M: ollie@sis.com.tw +L: linux-kernel@vger.rutgers.edu +S: Supported + TMS380 TOKEN-RING NETWORK DRIVER P: Adam Fritzler M: mid@auk.cx diff --git a/Makefile b/Makefile index 077111e640be..7fbd8969e67a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 3 -SUBLEVEL = 41 +SUBLEVEL = 42 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff --git a/arch/i386/config.in b/arch/i386/config.in index 0dda4d49f779..2377c24b705d 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -22,7 +22,7 @@ choice 'Processor family' \ 586/K5/5x86/6x86 CONFIG_M586 \ Pentium/TSC CONFIG_M586TSC \ PPro/6x86MX CONFIG_M686 \ - K6/II/II CONFIG_MK6 \ + K6/II/III CONFIG_MK6 \ Athlon CONFIG_MK7" PPro # # Define implied options from the CPU selection here diff --git a/arch/m68k/amiga/amiints.c b/arch/m68k/amiga/amiints.c index 209813dfb947..099119178641 100644 --- a/arch/m68k/amiga/amiints.c +++ b/arch/m68k/amiga/amiints.c @@ -364,7 +364,7 @@ void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) intena = ami_intena_vals[irq]; custom.intreq = intena; - /* serve first fast handlers - there can only be one of these */ + /* serve fast handler if present - there can only be one of these */ node = ami_irq_list[irq]; /* @@ -392,7 +392,11 @@ void amiga_do_irq_list(int irq, struct pt_regs *fp, struct irq_server *server) */ custom.intena = intena; save_flags(flags); +#if 0 /* def CPU_M68060_ONLY */ sti(); +#else + restore_flags((flags & ~0x0700) | (fp->sr & 0x0700)); +#endif slow_nodes = node; for (;;) { diff --git a/arch/m68k/atari/debug.c b/arch/m68k/atari/debug.c index e14c97e428d1..e30cb4e55788 100644 --- a/arch/m68k/atari/debug.c +++ b/arch/m68k/atari/debug.c @@ -311,10 +311,6 @@ void atari_init_midi_port( int cflag ) void __init atari_debug_init(void) { -#ifdef CONFIG_KGDB - /* the m68k_debug_device is used by the GDB stub, do nothing here */ - return; -#endif if (!strcmp( m68k_debug_device, "ser" )) { /* defaults to ser2 for a Falcon and ser1 otherwise */ strcpy( m68k_debug_device, MACH_IS_FALCON ? "ser2" : "ser1" ); diff --git a/arch/m68k/bvme6000/config.c b/arch/m68k/bvme6000/config.c index bbe808d3f598..b1b245ec2074 100644 --- a/arch/m68k/bvme6000/config.c +++ b/arch/m68k/bvme6000/config.c @@ -132,6 +132,7 @@ void __init config_bvme6000(void) bvme6000_set_vectors(); #endif + mach_max_dma_address = 0xffffffff; mach_sched_init = bvme6000_sched_init; mach_keyb_init = bvme6000_keyb_init; mach_kbdrate = bvme6000_kbdrate; diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 555b39612a0e..d795c71ad5e5 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -1048,6 +1049,7 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) info.si_code = SI_USER; info.si_pid = current->p_pptr->pid; info.si_uid = current->p_pptr->uid; + info.si_uid16 = high2lowuid(current->p_pptr->uid); } /* If the (new) signal is now blocked, requeue it. */ diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index db298c9979b6..5fd9dc0a7a8a 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c @@ -37,9 +37,6 @@ #include #include #include -#ifdef CONFIG_KGDB -#include -#endif /* assembler routines */ asmlinkage void system_call(void); @@ -766,11 +763,6 @@ int kstack_depth_to_print = 48; static void dump_stack(struct frame *fp) { -#ifdef CONFIG_KGDB - /* This will never return to here, if kgdb has been initialized. And if - * it returns from there, then to where the error happened... */ - enter_kgdb( &fp->ptregs ); -#else unsigned long *stack, *endstack, addr, module_start, module_end; extern char _start, _etext; int i; @@ -868,15 +860,10 @@ static void dump_stack(struct frame *fp) for (i = 0; i < 10; i++) printk("%04x ", 0xffff & ((short *) fp->ptregs.pc)[i]); printk ("\n"); -#endif } void bad_super_trap (struct frame *fp) { -#ifdef CONFIG_KGDB - /* Save the register dump if we'll enter kgdb anyways */ - if (!kgdb_initialized) { -#endif console_verbose(); if (fp->ptregs.vector < 4*sizeof(vec_names)/sizeof(vec_names[0])) printk ("*** %s *** FORMAT=%X\n", @@ -906,9 +893,6 @@ void bad_super_trap (struct frame *fp) fp->ptregs.pc); } printk ("Current process id is %d\n", current->pid); -#ifdef CONFIG_KGDB - } -#endif die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); } @@ -1037,10 +1021,6 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr) if (!(fp->sr & PS_S)) return; -#ifdef CONFIG_KGDB - /* Save the register dump if we'll enter kgdb anyways */ - if (!kgdb_initialized) { -#endif console_verbose(); printk("%s: %08x\n",str,nr); printk("PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n", @@ -1052,9 +1032,6 @@ void die_if_kernel (char *str, struct pt_regs *fp, int nr) printk("Process %s (pid: %d, stackpage=%08lx)\n", current->comm, current->pid, PAGE_SIZE+(unsigned long)current); -#ifdef CONFIG_KGDB - } -#endif dump_stack((struct frame *)fp); do_exit(SIGSEGV); } diff --git a/arch/m68k/mac/debug.c b/arch/m68k/mac/debug.c index 98cb2a695dc6..fa3910b5e227 100644 --- a/arch/m68k/mac/debug.c +++ b/arch/m68k/mac/debug.c @@ -397,10 +397,6 @@ void mac_init_sccb_port( int cflag ) void __init mac_debug_init(void) { -#ifdef CONFIG_KGDB - /* the m68k_debug_device is used by the GDB stub, do nothing here */ - return; -#endif #ifdef DEBUG_SERIAL if ( !strcmp( m68k_debug_device, "ser" ) || !strcmp( m68k_debug_device, "ser1" )) { diff --git a/arch/m68k/mvme147/config.c b/arch/m68k/mvme147/config.c index c27a182798ca..d48a0201bf3c 100644 --- a/arch/m68k/mvme147/config.c +++ b/arch/m68k/mvme147/config.c @@ -101,6 +101,7 @@ static int mvme147_get_hardware_list(char *buffer) void __init config_mvme147(void) { + mach_max_dma_address = 0x01000000; mach_sched_init = mvme147_sched_init; mach_keyb_init = mvme147_keyb_init; mach_kbdrate = mvme147_kbdrate; diff --git a/arch/m68k/mvme16x/config.c b/arch/m68k/mvme16x/config.c index 3c93562f9d52..83a0d5297f8f 100644 --- a/arch/m68k/mvme16x/config.c +++ b/arch/m68k/mvme16x/config.c @@ -145,6 +145,7 @@ void __init config_mvme16x(void) p_bdid p = &mvme_bdid; char id[40]; + mach_max_dma_address = 0xffffffff; mach_sched_init = mvme16x_sched_init; mach_keyb_init = mvme16x_keyb_init; mach_kbdrate = mvme16x_kbdrate; diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 3190c10b9fc5..911bafe23855 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -241,10 +241,6 @@ static int floppy_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param); static int floppy_open(struct inode *inode, struct file *filp); static int floppy_release(struct inode *inode, struct file *filp); -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos); -static ssize_t floppy_write(struct file *filp, const char *buf, - size_t count, loff_t *ppos); static int floppy_check_change(kdev_t dev); static int floppy_revalidate(kdev_t dev); static int swim3_add_device(struct device_node *swims); @@ -988,42 +984,6 @@ static int floppy_revalidate(kdev_t dev) return ret; } -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - return block_read(filp, buf, count, ppos); -} - -static ssize_t floppy_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - check_disk_change(inode->i_rdev); - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - if (fs->write_prot < 0) - fs->write_prot = swim3_readbit(fs, WRITE_PROT); - if (fs->write_prot) - return -EROFS; - return block_write(filp, buf, count, ppos); -} - static void floppy_off(unsigned int nr) { } diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c index 6a74f9a0bdc9..467cda26d8b4 100644 --- a/drivers/block/swim_iop.c +++ b/drivers/block/swim_iop.c @@ -102,10 +102,6 @@ static void swimiop_receive(struct iop_msg *, struct pt_regs *); static void swimiop_status_update(int, struct swim_drvstatus *); static int swimiop_eject(struct floppy_state *fs); -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos); -static ssize_t floppy_write(struct file *filp, const char *buf, - size_t count, loff_t *ppos); static int floppy_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param); static int floppy_open(struct inode *inode, struct file *filp); @@ -338,40 +334,6 @@ static int swimiop_eject(struct floppy_state *fs) return cmd->error; } -static ssize_t floppy_read(struct file *filp, char *buf, - size_t count, loff_t *ppos) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - return block_read(filp, buf, count, ppos); -} - -static ssize_t floppy_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - struct inode * inode = filp->f_dentry->d_inode; - struct floppy_state *fs; - int devnum = MINOR(inode->i_rdev); - - if (devnum >= floppy_count) - return -ENODEV; - check_disk_change(inode->i_rdev); - fs = &floppy_states[devnum]; - if (fs->ejected) - return -ENXIO; - if (fs->write_prot) - return -EROFS; - return block_write(filp, buf, count, ppos); -} - static struct floppy_struct floppy_type = { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,NULL }; /* 7 1.44MB 3.5" */ diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c index 27413489ade9..a83928a023d2 100644 --- a/drivers/char/agp/agpgart_be.c +++ b/drivers/char/agp/agpgart_be.c @@ -232,7 +232,6 @@ void agp_free_memory(agp_memory * curr) } if (curr->type != 0) { agp_bridge.free_by_type(curr); - MOD_DEC_USE_COUNT; return; } if (curr->page_count != 0) { @@ -260,15 +259,23 @@ agp_memory *agp_allocate_memory(size_t page_count, u32 type) agp_bridge.max_memory_agp) { return NULL; } + if (type != 0) { new = agp_bridge.alloc_by_type(page_count, type); return new; } + /* We always increase the module count, since free auto-decrements + * it + */ + + MOD_INC_USE_COUNT; + scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; new = agp_create_memory(scratch_pages); if (new == NULL) { + MOD_DEC_USE_COUNT; return NULL; } for (i = 0; i < page_count; i++) { @@ -286,7 +293,6 @@ agp_memory *agp_allocate_memory(size_t page_count, u32 type) new->page_count++; } - MOD_INC_USE_COUNT; return new; } @@ -781,11 +787,13 @@ static aper_size_info_fixed intel_i810_sizes[] = }; #define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 static gatt_mask intel_i810_masks[] = { {I810_PTE_VALID, 0}, - {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY} + {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY}, + {I810_PTE_VALID, 0} }; static struct _intel_i810_private { @@ -896,7 +904,7 @@ static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, if ((type == AGP_DCACHE_MEMORY) && (mem->type == AGP_DCACHE_MEMORY)) { /* special insert */ - + CACHE_FLUSH(); for (i = pg_start; i < (pg_start + mem->page_count); i++) { OUTREG32(intel_i810_private.registers, @@ -904,20 +912,24 @@ static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, (i * 4096) | I810_PTE_LOCAL | I810_PTE_VALID); } - + CACHE_FLUSH(); agp_bridge.tlb_flush(mem); return 0; } + if((type == AGP_PHYS_MEMORY) && + (mem->type == AGP_PHYS_MEMORY)) { + goto insert; + } return -EINVAL; } - if (mem->is_flushed == FALSE) { - CACHE_FLUSH(); - mem->is_flushed = TRUE; - } + +insert: + CACHE_FLUSH(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { OUTREG32(intel_i810_private.registers, I810_PTE_BASE + (j * 4), mem->memory[i]); } + CACHE_FLUSH(); agp_bridge.tlb_flush(mem); return 0; @@ -955,15 +967,55 @@ static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) new->page_count = pg_count; new->num_scratch_pages = 0; vfree(new->memory); + MOD_INC_USE_COUNT; return new; } + if(type == AGP_PHYS_MEMORY) { + /* The I810 requires a physical address to program + * it's mouse pointer into hardware. However the + * Xserver still writes to it through the agp + * aperture + */ + if (pg_count != 1) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + MOD_INC_USE_COUNT; + new->memory[0] = agp_alloc_page(); + + if (new->memory[0] == 0) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[0] = + agp_bridge.mask_memory( + virt_to_phys((void *) new->memory[0]), + type); + new->page_count = 1; + new->num_scratch_pages = 1; + new->type = AGP_PHYS_MEMORY; + new->physical = virt_to_phys((void *) new->memory[0]); + return new; + } + return NULL; } static void intel_i810_free_by_type(agp_memory * curr) { agp_free_key(curr->key); + if(curr->type == AGP_PHYS_MEMORY) { + agp_destroy_page((unsigned long) + phys_to_virt(curr->memory[0])); + vfree(curr->memory); + } kfree(curr); + MOD_DEC_USE_COUNT; } static unsigned long intel_i810_mask_memory(unsigned long addr, int type) @@ -1916,7 +1968,7 @@ static struct agp_max_table maxes_table[9] = static int agp_find_max(void) { - long memory, t, index, result; + long memory, index, result; memory = virt_to_phys(high_memory) >> 20; index = 1; @@ -1926,16 +1978,15 @@ static int agp_find_max(void) index++; } - t = (memory - maxes_table[index - 1].mem) / - (maxes_table[index].mem - maxes_table[index - 1].mem); - result = maxes_table[index - 1].agp + - (t * (maxes_table[index].agp - maxes_table[index - 1].agp)); + ( (memory - maxes_table[index - 1].mem) * + (maxes_table[index].agp - maxes_table[index - 1].agp)) / + (maxes_table[index].mem - maxes_table[index - 1].mem); printk(KERN_INFO "agpgart: Maximum main memory to use " "for agp memory: %ldM\n", result); result = result << (20 - PAGE_SHIFT); - return result; + return result; } #define AGPGART_VERSION_MAJOR 0 diff --git a/drivers/char/agp/agpgart_fe.c b/drivers/char/agp/agpgart_fe.c index e098b4bab3eb..ef915259edcc 100644 --- a/drivers/char/agp/agpgart_fe.c +++ b/drivers/char/agp/agpgart_fe.c @@ -298,7 +298,7 @@ static agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type) agp_memory *memory; memory = agp_allocate_memory(pg_count, type); - + printk("memory : %p\n", memory); if (memory == NULL) { return NULL; } @@ -911,6 +911,7 @@ static int agpioc_allocate_wrap(agp_file_private * priv, unsigned long arg) return -ENOMEM; } alloc.key = memory->key; + alloc.physical = memory->physical; if (copy_to_user((void *) arg, &alloc, sizeof(agp_allocate))) { agp_free_memory_wrap(memory); diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index f4ecc75a5ea3..95a6c4370d7d 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -103,10 +103,6 @@ static inline int copy_from_user(void *to,const void *from, int c) #include "generic_serial.h" -#ifndef MODULE -extern void my_hd (unsigned char *ptr, int n); -#endif - static char * tmp_buf; static DECLARE_MUTEX(tmp_buf_sem); @@ -119,8 +115,8 @@ int gs_debug = 0; #define gs_dprintk(f, str...) /* nothing */ #endif -#define func_enter() gs_dprintk (SX_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n") -#define func_exit() gs_dprintk (SX_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n") +#define func_enter() gs_dprintk (GS_DEBUG_FLOW, "gs: enter " __FUNCTION__ "\n") +#define func_exit() gs_dprintk (GS_DEBUG_FLOW, "gs: exit " __FUNCTION__ "\n") @@ -856,7 +852,6 @@ void gs_set_termios (struct tty_struct * tty, if (gs_debug & GS_DEBUG_TERMIOS) { gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp); - my_hd ((unsigned char *)tiosp, sizeof (struct termios)); } #if 0 diff --git a/drivers/char/generic_serial.h b/drivers/char/generic_serial.h index ca321af9b0bf..2e44bee95129 100644 --- a/drivers/char/generic_serial.h +++ b/drivers/char/generic_serial.h @@ -12,6 +12,8 @@ #ifndef GENERIC_SERIAL_H #define GENERIC_SERIAL_H +#define RS_EVENT_WRITE_WAKEUP 0 + struct real_driver { void (*disable_tx_interrupts) (void *); void (*enable_tx_interrupts) (void *); @@ -75,6 +77,7 @@ struct gs_port { #define GS_DEBUG_TERMIOS 0x00000004 #define GS_DEBUG_STUFF 0x00000008 #define GS_DEBUG_CLOSE 0x00000010 +#define GS_DEBUG_FLOW 0x00000020 void gs_put_char(struct tty_struct *tty, unsigned char ch); diff --git a/drivers/char/scc.h b/drivers/char/scc.h new file mode 100644 index 000000000000..a6a583f44bb6 --- /dev/null +++ b/drivers/char/scc.h @@ -0,0 +1,614 @@ +/* + * atari_SCC.h: Definitions for the Am8530 Serial Communications Controller + * + * Copyright 1994 Roman Hodek + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + + +#ifndef _SCC_H +#define _SCC_H + +#include +#include + +/* Special configuration ioctls for the Atari SCC5380 Serial + * Communications Controller + */ + +/* ioctl command codes */ + +#define TIOCGATSCC 0x54c0 /* get SCC configuration */ +#define TIOCSATSCC 0x54c1 /* set SCC configuration */ +#define TIOCDATSCC 0x54c2 /* reset configuration to defaults */ + +/* Clock sources */ + +#define CLK_RTxC 0 +#define CLK_TRxC 1 +#define CLK_PCLK 2 + +/* baud_bases for the common clocks in the Atari. These are the real + * frequencies divided by 16. + */ + +#define SCC_BAUD_BASE_TIMC 19200 /* 0.3072 MHz from TT-MFP, Timer C */ +#define SCC_BAUD_BASE_BCLK 153600 /* 2.4576 MHz */ +#define SCC_BAUD_BASE_PCLK4 229500 /* 3.6720 MHz */ +#define SCC_BAUD_BASE_PCLK 503374 /* 8.0539763 MHz */ +#define SCC_BAUD_BASE_NONE 0 /* for not connected or unused + * clock sources */ + +/* The SCC clock configuration structure */ + +struct scc_clock_config { + unsigned RTxC_base; /* base_baud of RTxC */ + unsigned TRxC_base; /* base_baud of TRxC */ + unsigned PCLK_base; /* base_baud of PCLK, both channels! */ + struct { + unsigned clksrc; /* CLK_RTxC, CLK_TRxC or CLK_PCLK */ + unsigned divisor; /* divisor for base baud, valid values: + * see below */ + } baud_table[17]; /* For 50, 75, 110, 135, 150, 200, 300, + * 600, 1200, 1800, 2400, 4800, 9600, + * 19200, 38400, 57600 and 115200 bps. + * The last two could be replaced by + * other rates > 38400 if they're not + * possible. + */ +}; + +/* The following divisors are valid: + * + * - CLK_RTxC: 1 or even (1, 2 and 4 are the direct modes, > 4 use + * the BRG) + * + * - CLK_TRxC: 1, 2 or 4 (no BRG, only direct modes possible) + * + * - CLK_PCLK: >= 4 and even (no direct modes, only BRG) + * + */ + +struct scc_port { + struct gs_port gs; + volatile unsigned char *ctrlp; + volatile unsigned char *datap; + int x_char; /* xon/xoff character */ + int c_dcd; + int channel; + struct scc_port *port_a; /* Reference to port A and B */ + struct scc_port *port_b; /* structs for reg access */ +}; + +#define SCC_MAGIC 0x52696368 + +/***********************************************************************/ +/* */ +/* Register Names */ +/* */ +/***********************************************************************/ + +/* The SCC documentation gives no explicit names to the registers, + * they're just called WR0..15 and RR0..15. To make the source code + * better readable and make the transparent write reg read access (see + * below) possible, I christen them here with self-invented names. + * Note that (real) read registers are assigned numbers 16..31. WR7' + * has number 33. + */ + +#define COMMAND_REG 0 /* wo */ +#define INT_AND_DMA_REG 1 /* wo */ +#define INT_VECTOR_REG 2 /* rw, common to both channels */ +#define RX_CTRL_REG 3 /* rw */ +#define AUX1_CTRL_REG 4 /* rw */ +#define TX_CTRL_REG 5 /* rw */ +#define SYNC_ADR_REG 6 /* wo */ +#define SYNC_CHAR_REG 7 /* wo */ +#define SDLC_OPTION_REG 33 /* wo */ +#define TX_DATA_REG 8 /* wo */ +#define MASTER_INT_CTRL 9 /* wo, common to both channels */ +#define AUX2_CTRL_REG 10 /* rw */ +#define CLK_CTRL_REG 11 /* wo */ +#define TIMER_LOW_REG 12 /* rw */ +#define TIMER_HIGH_REG 13 /* rw */ +#define DPLL_CTRL_REG 14 /* wo */ +#define INT_CTRL_REG 15 /* rw */ + +#define STATUS_REG 16 /* ro */ +#define SPCOND_STATUS_REG 17 /* wo */ +/* RR2 is WR2 for Channel A, Channel B gives vector + current status: */ +#define CURR_VECTOR_REG 18 /* Ch. B only, Ch. A for rw */ +#define INT_PENDING_REG 19 /* Channel A only! */ +/* RR4 is WR4, if b6(MR7') == 1 */ +/* RR5 is WR5, if b6(MR7') == 1 */ +#define FS_FIFO_LOW_REG 22 /* ro */ +#define FS_FIFO_HIGH_REG 23 /* ro */ +#define RX_DATA_REG 24 /* ro */ +/* RR9 is WR3, if b6(MR7') == 1 */ +#define DPLL_STATUS_REG 26 /* ro */ +/* RR11 is WR10, if b6(MR7') == 1 */ +/* RR12 is WR12 */ +/* RR13 is WR13 */ +/* RR14 not present */ +/* RR15 is WR15 */ + + +/***********************************************************************/ +/* */ +/* Register Values */ +/* */ +/***********************************************************************/ + + +/* WR0: COMMAND_REG "CR" */ + +#define CR_RX_CRC_RESET 0x40 +#define CR_TX_CRC_RESET 0x80 +#define CR_TX_UNDERRUN_RESET 0xc0 + +#define CR_EXTSTAT_RESET 0x10 +#define CR_SEND_ABORT 0x18 +#define CR_ENAB_INT_NEXT_RX 0x20 +#define CR_TX_PENDING_RESET 0x28 +#define CR_ERROR_RESET 0x30 +#define CR_HIGHEST_IUS_RESET 0x38 + + +/* WR1: INT_AND_DMA_REG "IDR" */ + +#define IDR_EXTSTAT_INT_ENAB 0x01 +#define IDR_TX_INT_ENAB 0x02 +#define IDR_PARERR_AS_SPCOND 0x04 + +#define IDR_RX_INT_DISAB 0x00 +#define IDR_RX_INT_FIRST 0x08 +#define IDR_RX_INT_ALL 0x10 +#define IDR_RX_INT_SPCOND 0x18 +#define IDR_RX_INT_MASK 0x18 + +#define IDR_WAITREQ_RX 0x20 +#define IDR_WAITREQ_IS_REQ 0x40 +#define IDR_WAITREQ_ENAB 0x80 + + +/* WR3: RX_CTRL_REG "RCR" */ + +#define RCR_RX_ENAB 0x01 +#define RCR_DISCARD_SYNC_CHARS 0x02 +#define RCR_ADDR_SEARCH 0x04 +#define RCR_CRC_ENAB 0x08 +#define RCR_SEARCH_MODE 0x10 +#define RCR_AUTO_ENAB_MODE 0x20 + +#define RCR_CHSIZE_MASK 0xc0 +#define RCR_CHSIZE_5 0x00 +#define RCR_CHSIZE_6 0x40 +#define RCR_CHSIZE_7 0x80 +#define RCR_CHSIZE_8 0xc0 + + +/* WR4: AUX1_CTRL_REG "A1CR" */ + +#define A1CR_PARITY_MASK 0x03 +#define A1CR_PARITY_NONE 0x00 +#define A1CR_PARITY_ODD 0x01 +#define A1CR_PARITY_EVEN 0x03 + +#define A1CR_MODE_MASK 0x0c +#define A1CR_MODE_SYNCR 0x00 +#define A1CR_MODE_ASYNC_1 0x04 +#define A1CR_MODE_ASYNC_15 0x08 +#define A1CR_MODE_ASYNC_2 0x0c + +#define A1CR_SYNCR_MODE_MASK 0x30 +#define A1CR_SYNCR_MONOSYNC 0x00 +#define A1CR_SYNCR_BISYNC 0x10 +#define A1CR_SYNCR_SDLC 0x20 +#define A1CR_SYNCR_EXTCSYNC 0x30 + +#define A1CR_CLKMODE_MASK 0xc0 +#define A1CR_CLKMODE_x1 0x00 +#define A1CR_CLKMODE_x16 0x40 +#define A1CR_CLKMODE_x32 0x80 +#define A1CR_CLKMODE_x64 0xc0 + + +/* WR5: TX_CTRL_REG "TCR" */ + +#define TCR_TX_CRC_ENAB 0x01 +#define TCR_RTS 0x02 +#define TCR_USE_CRC_CCITT 0x00 +#define TCR_USE_CRC_16 0x04 +#define TCR_TX_ENAB 0x08 +#define TCR_SEND_BREAK 0x10 + +#define TCR_CHSIZE_MASK 0x60 +#define TCR_CHSIZE_5 0x00 +#define TCR_CHSIZE_6 0x20 +#define TCR_CHSIZE_7 0x40 +#define TCR_CHSIZE_8 0x60 + +#define TCR_DTR 0x80 + + +/* WR7': SLDC_OPTION_REG "SOR" */ + +#define SOR_AUTO_TX_ENAB 0x01 +#define SOR_AUTO_EOM_RESET 0x02 +#define SOR_AUTO_RTS_MODE 0x04 +#define SOR_NRZI_DISAB_HIGH 0x08 +#define SOR_ALT_DTRREQ_TIMING 0x10 +#define SOR_READ_CRC_CHARS 0x20 +#define SOR_EXTENDED_REG_ACCESS 0x40 + + +/* WR9: MASTER_INT_CTRL "MIC" */ + +#define MIC_VEC_INCL_STAT 0x01 +#define MIC_NO_VECTOR 0x02 +#define MIC_DISAB_LOWER_CHAIN 0x04 +#define MIC_MASTER_INT_ENAB 0x08 +#define MIC_STATUS_HIGH 0x10 +#define MIC_IGN_INTACK 0x20 + +#define MIC_NO_RESET 0x00 +#define MIC_CH_A_RESET 0x40 +#define MIC_CH_B_RESET 0x80 +#define MIC_HARD_RESET 0xc0 + + +/* WR10: AUX2_CTRL_REG "A2CR" */ + +#define A2CR_SYNC_6 0x01 +#define A2CR_LOOP_MODE 0x02 +#define A2CR_ABORT_ON_UNDERRUN 0x04 +#define A2CR_MARK_IDLE 0x08 +#define A2CR_GO_ACTIVE_ON_POLL 0x10 + +#define A2CR_CODING_MASK 0x60 +#define A2CR_CODING_NRZ 0x00 +#define A2CR_CODING_NRZI 0x20 +#define A2CR_CODING_FM1 0x40 +#define A2CR_CODING_FM0 0x60 + +#define A2CR_PRESET_CRC_1 0x80 + + +/* WR11: CLK_CTRL_REG "CCR" */ + +#define CCR_TRxCOUT_MASK 0x03 +#define CCR_TRxCOUT_XTAL 0x00 +#define CCR_TRxCOUT_TXCLK 0x01 +#define CCR_TRxCOUT_BRG 0x02 +#define CCR_TRxCOUT_DPLL 0x03 + +#define CCR_TRxC_OUTPUT 0x04 + +#define CCR_TXCLK_MASK 0x18 +#define CCR_TXCLK_RTxC 0x00 +#define CCR_TXCLK_TRxC 0x08 +#define CCR_TXCLK_BRG 0x10 +#define CCR_TXCLK_DPLL 0x18 + +#define CCR_RXCLK_MASK 0x60 +#define CCR_RXCLK_RTxC 0x00 +#define CCR_RXCLK_TRxC 0x20 +#define CCR_RXCLK_BRG 0x40 +#define CCR_RXCLK_DPLL 0x60 + +#define CCR_RTxC_XTAL 0x80 + + +/* WR14: DPLL_CTRL_REG "DCR" */ + +#define DCR_BRG_ENAB 0x01 +#define DCR_BRG_USE_PCLK 0x02 +#define DCR_DTRREQ_IS_REQ 0x04 +#define DCR_AUTO_ECHO 0x08 +#define DCR_LOCAL_LOOPBACK 0x10 + +#define DCR_DPLL_EDGE_SEARCH 0x20 +#define DCR_DPLL_ERR_RESET 0x40 +#define DCR_DPLL_DISAB 0x60 +#define DCR_DPLL_CLK_BRG 0x80 +#define DCR_DPLL_CLK_RTxC 0xa0 +#define DCR_DPLL_FM 0xc0 +#define DCR_DPLL_NRZI 0xe0 + + +/* WR15: INT_CTRL_REG "ICR" */ + +#define ICR_OPTIONREG_SELECT 0x01 +#define ICR_ENAB_BRG_ZERO_INT 0x02 +#define ICR_USE_FS_FIFO 0x04 +#define ICR_ENAB_DCD_INT 0x08 +#define ICR_ENAB_SYNC_INT 0x10 +#define ICR_ENAB_CTS_INT 0x20 +#define ICR_ENAB_UNDERRUN_INT 0x40 +#define ICR_ENAB_BREAK_INT 0x80 + + +/* RR0: STATUS_REG "SR" */ + +#define SR_CHAR_AVAIL 0x01 +#define SR_BRG_ZERO 0x02 +#define SR_TX_BUF_EMPTY 0x04 +#define SR_DCD 0x08 +#define SR_SYNC_ABORT 0x10 +#define SR_CTS 0x20 +#define SR_TX_UNDERRUN 0x40 +#define SR_BREAK 0x80 + + +/* RR1: SPCOND_STATUS_REG "SCSR" */ + +#define SCSR_ALL_SENT 0x01 +#define SCSR_RESIDUAL_MASK 0x0e +#define SCSR_PARITY_ERR 0x10 +#define SCSR_RX_OVERRUN 0x20 +#define SCSR_CRC_FRAME_ERR 0x40 +#define SCSR_END_OF_FRAME 0x80 + + +/* RR3: INT_PENDING_REG "IPR" */ + +#define IPR_B_EXTSTAT 0x01 +#define IPR_B_TX 0x02 +#define IPR_B_RX 0x04 +#define IPR_A_EXTSTAT 0x08 +#define IPR_A_TX 0x10 +#define IPR_A_RX 0x20 + + +/* RR7: FS_FIFO_HIGH_REG "FFHR" */ + +#define FFHR_CNT_MASK 0x3f +#define FFHR_IS_FROM_FIFO 0x40 +#define FFHR_FIFO_OVERRUN 0x80 + + +/* RR10: DPLL_STATUS_REG "DSR" */ + +#define DSR_ON_LOOP 0x02 +#define DSR_ON_LOOP_SENDING 0x10 +#define DSR_TWO_CLK_MISSING 0x40 +#define DSR_ONE_CLK_MISSING 0x80 + +/***********************************************************************/ +/* */ +/* Register Access */ +/* */ +/***********************************************************************/ + + +/* The SCC needs 3.5 PCLK cycles recovery time between to register + * accesses. PCLK runs with 8 MHz on an Atari, so this delay is 3.5 * + * 125 ns = 437.5 ns. This is too short for udelay(). + * 10/16/95: A tstb mfp.par_dt_reg takes 600ns (sure?) and thus should be + * quite right + */ + +#define scc_reg_delay() \ + do { \ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) \ + __asm__ __volatile__ ( " nop; nop"); \ + else if (MACH_IS_ATARI) \ + __asm__ __volatile__ ( "tstb %0" : : "g" (*_scc_del) : "cc" );\ + } while (0) + +extern unsigned char scc_shadow[2][16]; + +/* The following functions should relax the somehow complicated + * register access of the SCC. _SCCwrite() stores all written values + * (except for WR0 and WR8) in shadow registers for later recall. This + * removes the burden of remembering written values as needed. The + * extra work of storing the value doesn't count, since a delay is + * needed after a SCC access anyway. Additionally, _SCCwrite() manages + * writes to WR0 and WR8 differently, because these can be accessed + * directly with less overhead. Another special case are WR7 and WR7'. + * _SCCwrite automatically checks what of this registers is selected + * and changes b0 of WR15 if needed. + * + * _SCCread() for standard read registers is straightforward, except + * for RR2 (split into two "virtual" registers: one for the value + * written to WR2 (from the shadow) and one for the vector including + * status from RR2, Ch. B) and RR3. The latter must be read from + * Channel A, because it reads as all zeros on Ch. B. RR0 and RR8 can + * be accessed directly as before. + * + * The two inline function contain complicated switch statements. But + * I rely on regno and final_delay being constants, so gcc can reduce + * the whole stuff to just some assembler statements. + * + * _SCCwrite and _SCCread aren't intended to be used directly under + * normal circumstances. The macros SCCread[_ND] and SCCwrite[_ND] are + * for that purpose. They assume that a local variable 'port' is + * declared and pointing to the port's scc_struct entry. The + * variants with "_NB" appended should be used if no other SCC + * accesses follow immediatly (within 0.5 usecs). They just skip the + * final delay nops. + * + * Please note that accesses to SCC registers should only take place + * when interrupts are turned off (at least if SCC interrupts are + * enabled). Otherwise, an interrupt could interfere with the + * two-stage accessing process. + * + */ + + +static __inline__ void _SCCwrite( + struct scc_port *port, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, + unsigned char val, int final_delay ) +{ + switch( regno ) { + + case COMMAND_REG: + /* WR0 can be written directly without pointing */ + *port->ctrlp = val; + break; + + case SYNC_CHAR_REG: + /* For WR7, first set b0 of WR15 to 0, if needed */ + if (shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT) { + *port->ctrlp = 15; + shadow[INT_CTRL_REG] &= ~ICR_OPTIONREG_SELECT; + scc_reg_delay(); + *port->ctrlp = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + goto normal_case; + + case SDLC_OPTION_REG: + /* For WR7', first set b0 of WR15 to 1, if needed */ + if (!(shadow[INT_CTRL_REG] & ICR_OPTIONREG_SELECT)) { + *port->ctrlp = 15; + shadow[INT_CTRL_REG] |= ICR_OPTIONREG_SELECT; + scc_reg_delay(); + *port->ctrlp = shadow[INT_CTRL_REG]; + scc_reg_delay(); + } + *port->ctrlp = 7; + shadow[8] = val; /* WR7' shadowed at WR8 */ + scc_reg_delay(); + *port->ctrlp = val; + break; + + case TX_DATA_REG: /* WR8 */ + /* TX_DATA_REG can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + *port->ctrlp = regno; + scc_reg_delay(); + *port->ctrlp = val; + } + else + *port->datap = val; + break; + + case MASTER_INT_CTRL: + *port->ctrlp = regno; + val &= 0x3f; /* bits 6..7 are the reset commands */ + scc_shadow[0][regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + case DPLL_CTRL_REG: + *port->ctrlp = regno; + val &= 0x1f; /* bits 5..7 are the DPLL commands */ + shadow[regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + case 1 ... 6: + case 10 ... 13: + case 15: + normal_case: + *port->ctrlp = regno; + shadow[regno] = val; + scc_reg_delay(); + *port->ctrlp = val; + break; + + default: + printk( "Bad SCC write access to WR%d\n", regno ); + break; + + } + + if (final_delay) + scc_reg_delay(); +} + + +static __inline__ unsigned char _SCCread( + struct scc_port *port, + unsigned char *shadow, + volatile unsigned char *_scc_del, + int regno, int final_delay ) +{ + unsigned char rv; + + switch( regno ) { + + /* --- real read registers --- */ + case STATUS_REG: + rv = *port->ctrlp; + break; + + case INT_PENDING_REG: + /* RR3: read only from Channel A! */ + port = port->port_a; + goto normal_case; + + case RX_DATA_REG: + /* RR8 can be accessed directly on some h/w */ + if (MACH_IS_MVME16x || MACH_IS_BVME6000 || MACH_IS_MVME147) + { + *port->ctrlp = 8; + scc_reg_delay(); + rv = *port->ctrlp; + } + else + rv = *port->datap; + break; + + case CURR_VECTOR_REG: + /* RR2 (vector including status) from Ch. B */ + port = port->port_b; + goto normal_case; + + /* --- reading write registers: access the shadow --- */ + case 1 ... 7: + case 10 ... 15: + return shadow[regno]; /* no final delay! */ + + /* WR7' is special, because it is shadowed at the place of WR8 */ + case SDLC_OPTION_REG: + return shadow[8]; /* no final delay! */ + + /* WR9 is special too, because it is common for both channels */ + case MASTER_INT_CTRL: + return scc_shadow[0][9]; /* no final delay! */ + + default: + printk( "Bad SCC read access to %cR%d\n", (regno & 16) ? 'R' : 'W', + regno & ~16 ); + break; + + case SPCOND_STATUS_REG: + case FS_FIFO_LOW_REG: + case FS_FIFO_HIGH_REG: + case DPLL_STATUS_REG: + normal_case: + *port->ctrlp = regno & 0x0f; + scc_reg_delay(); + rv = *port->ctrlp; + break; + + } + + if (final_delay) + scc_reg_delay(); + return rv; +} + +#define SCC_ACCESS_INIT(port) \ + unsigned char *_scc_shadow = &scc_shadow[port->channel][0] + +#define SCCwrite(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),1) +#define SCCwrite_NB(reg,val) _SCCwrite(port,_scc_shadow,scc_del,(reg),(val),0) +#define SCCread(reg) _SCCread(port,_scc_shadow,scc_del,(reg),1) +#define SCCread_NB(reg) _SCCread(port,_scc_shadow,scc_del,(reg),0) + +#define SCCmod(reg,and,or) SCCwrite((reg),(SCCread(reg)&(and))|(or)) + +#endif /* _SCC_H */ diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 5142c84956e2..885b236fd172 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ #include #include #include +#include +#include #include #include @@ -144,7 +147,7 @@ static struct termios *serial_termios_locked[NR_PORTS]; * memory if large numbers of serial ports are open. */ static unsigned char *tmp_buf = 0; -static struct semaphore tmp_buf_sem = MUTEX; +DECLARE_MUTEX(tmp_buf_sem); /* * This is used to look up the divisor speeds and the timeouts @@ -2501,8 +2504,8 @@ scrn[1] = '\0'; info->tqueue.data = info; info->callout_termios =cy_callout_driver.init_termios; info->normal_termios = cy_serial_driver.init_termios; - info->open_wait = 0; - info->close_wait = 0; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); /* info->session */ /* info->pgrp */ /*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/ @@ -2728,7 +2731,7 @@ void console_setup(char *str, int *ints) * that serial167_init() doesn't leave the chip non-functional. */ -void serial167_write(struct console *co, const char *str, unsigned count) +void serial167_console_write(struct console *co, const char *str, unsigned count) { volatile unsigned char *base_addr = (u_char *)BASE_ADDR; unsigned long flags; @@ -2788,6 +2791,95 @@ void serial167_write(struct console *co, const char *str, unsigned count) restore_flags(flags); } +/* This is a hack; if there are multiple chars waiting in the chip we + * discard all but the last one, and return that. The cd2401 is not really + * designed to be driven in polled mode. + */ + +int serial167_console_wait_key(struct console *co) +{ + volatile unsigned char *base_addr = (u_char *)BASE_ADDR; + unsigned long flags; + volatile u_char sink; + u_char ier; + int port; + int keypress = 0; + + save_flags(flags); cli(); + + /* Ensure receiver is enabled! */ + + port = 0; + base_addr[CyCAR] = (u_char)port; + while (base_addr[CyCCR]) + ; + base_addr[CyCCR] = CyENB_RCVR; + ier = base_addr[CyIER]; + base_addr[CyIER] = CyRxData; + + while (!keypress) { + if (pcc2chip[PccSCCRICR] & 0x20) + { + /* We have an Rx int. Acknowledge it */ + sink = pcc2chip[PccRPIACKR]; + if ((base_addr[CyLICR] >> 2) == port) { + int cnt = base_addr[CyRFOC]; + while (cnt-- > 0) + { + keypress = base_addr[CyRDR]; + } + base_addr[CyREOIR] = 0; + } + else + base_addr[CyREOIR] = CyNOTRANS; + } + } + + base_addr[CyIER] = ier; + + restore_flags(flags); + + return keypress; +} + + +static kdev_t serial167_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + + +static int __init serial167_console_setup(struct console *co, char *options) +{ + return 0; +} + + +static struct console sercons = { + "ttyS", + serial167_console_write, + NULL, + serial167_console_device, + serial167_console_wait_key, + NULL, + serial167_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + + +void __init serial167_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME166 || + vme_brdtype == VME_TYPE_MVME167 || + vme_brdtype == VME_TYPE_MVME177) { + mvme167_serial_console_setup(0); + register_console(&sercons); + } +} + #ifdef CONFIG_REMOTE_DEBUG void putDebugChar (int c) { diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index c726f745ce0c..b4194bb9b952 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -128,6 +128,14 @@ static int tty_fasync(int fd, struct file * filp, int on); #ifdef CONFIG_SX extern int sx_init (void); #endif +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) +extern int vme_scc_init (void); +extern long vme_scc_console_init(void); +#endif +#ifdef CONFIG_SERIAL167 +extern int serial167_init(void); +extern long serial167_console_init(void); +#endif #ifdef CONFIG_8xx extern console_8xx_init(void); extern int rs_8xx_init(void); @@ -2090,9 +2098,15 @@ void __init console_init(void) #ifdef CONFIG_SERIAL_CONSOLE #ifdef CONFIG_8xx console_8xx_init(); -#else +#elif defined(CONFIG_SERIAL) serial_console_init(); #endif /* CONFIG_8xx */ +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) + vme_scc_console_init(); +#endif +#if defined(CONFIG_SERIAL167) + serial167_console_init(); +#endif #endif } @@ -2176,6 +2190,9 @@ void __init tty_init(void) #ifdef CONFIG_SERIAL rs_init(); #endif +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_BVME6000_SCC) || defined(CONFIG_MVME147_SCC) + vme_scc_init(); +#endif #ifdef CONFIG_COMPUTONE ip2_init(); #endif @@ -2185,7 +2202,7 @@ void __init tty_init(void) #ifdef CONFIG_ROCKETPORT rp_init(); #endif -#ifdef CONFIG_MVME16x +#ifdef CONFIG_SERIAL167 serial167_init(); #endif #ifdef CONFIG_CYCLADES diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c new file mode 100644 index 000000000000..15232f8321de --- /dev/null +++ b/drivers/char/vme_scc.c @@ -0,0 +1,1136 @@ +/* + * drivers/char/vme_scc.c: MVME147, MVME162, BVME6000 SCC serial ports + * implementation. + * Copyright 1999 Richard Hirst + * + * Based on atari_SCC.c which was + * Copyright 1994-95 Roman Hodek + * Partially based on PC-Linux serial.c by Linus Torvalds and Theodore Ts'o + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MVME147_SCC +#include +#endif +#ifdef CONFIG_MVME162_SCC +#include +#endif +#ifdef CONFIG_BVME6000_SCC +#include +#endif + +#include "generic_serial.h" +#include "scc.h" + + +#define CHANNEL_A 0 +#define CHANNEL_B 1 + +#define SCC_MINOR_BASE 64 + +/* Shadows for all SCC write registers */ +static unsigned char scc_shadow[2][16]; + +/* Location to access for SCC register access delay */ +static volatile unsigned char *scc_del = NULL; + +/* To keep track of STATUS_REG state for detection of Ext/Status int source */ +static unsigned char scc_last_status_reg[2]; + +/***************************** Prototypes *****************************/ + +/* Function prototypes */ +static void scc_disable_tx_interrupts(void * ptr); +static void scc_enable_tx_interrupts(void * ptr); +static void scc_disable_rx_interrupts(void * ptr); +static void scc_enable_rx_interrupts(void * ptr); +static int scc_get_CD(void * ptr); +static void scc_shutdown_port(void * ptr); +static void scc_set_real_termios(void *ptr); +static void scc_hungup(void *ptr); +static void scc_close(void *ptr); +static int scc_chars_in_buffer(void * ptr); +static int scc_open(struct tty_struct * tty, struct file * filp); +static int scc_ioctl(struct tty_struct * tty, struct file * filp, + unsigned int cmd, unsigned long arg); +static void scc_throttle(struct tty_struct *tty); +static void scc_unthrottle(struct tty_struct *tty); +static void scc_tx_int(int irq, void *data, struct pt_regs *fp); +static void scc_rx_int(int irq, void *data, struct pt_regs *fp); +static void scc_stat_int(int irq, void *data, struct pt_regs *fp); +static void scc_spcond_int(int irq, void *data, struct pt_regs *fp); +static void scc_setsignals(struct scc_port *port, int dtr, int rts); +static void scc_break_ctl(struct tty_struct *tty, int break_state); + +static struct tty_driver scc_driver, scc_callout_driver; + +static struct tty_struct *scc_table[2] = { NULL, }; +static struct termios * scc_termios[2]; +static struct termios * scc_termios_locked[2]; +struct scc_port scc_ports[2]; + +int scc_refcount; +int scc_initialized = 0; + +/*--------------------------------------------------------------------------- + * Interface from generic_serial.c back here + *--------------------------------------------------------------------------*/ + +static struct real_driver scc_real_driver = { + scc_disable_tx_interrupts, + scc_enable_tx_interrupts, + scc_disable_rx_interrupts, + scc_enable_rx_interrupts, + scc_get_CD, + scc_shutdown_port, + scc_set_real_termios, + scc_chars_in_buffer, + scc_close, + scc_hungup, + NULL +}; + + +/*---------------------------------------------------------------------------- + * vme_scc_init() and support functions + *---------------------------------------------------------------------------*/ + +static int scc_init_drivers(void) +{ + int error; + + memset(&scc_driver, 0, sizeof(scc_driver)); + scc_driver.magic = TTY_DRIVER_MAGIC; + scc_driver.driver_name = "scc"; + scc_driver.name = "ttyS"; + scc_driver.major = TTY_MAJOR; + scc_driver.minor_start = SCC_MINOR_BASE; + scc_driver.num = 2; + scc_driver.type = TTY_DRIVER_TYPE_SERIAL; + scc_driver.subtype = SERIAL_TYPE_NORMAL; + scc_driver.init_termios = tty_std_termios; + scc_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + scc_driver.flags = TTY_DRIVER_REAL_RAW; + scc_driver.refcount = &scc_refcount; + scc_driver.table = scc_table; + scc_driver.termios = scc_termios; + scc_driver.termios_locked = scc_termios_locked; + + scc_driver.open = scc_open; + scc_driver.close = gs_close; + scc_driver.write = gs_write; + scc_driver.put_char = gs_put_char; + scc_driver.flush_chars = gs_flush_chars; + scc_driver.write_room = gs_write_room; + scc_driver.chars_in_buffer = gs_chars_in_buffer; + scc_driver.flush_buffer = gs_flush_buffer; + scc_driver.ioctl = scc_ioctl; + scc_driver.throttle = scc_throttle; + scc_driver.unthrottle = scc_unthrottle; + scc_driver.set_termios = gs_set_termios; + scc_driver.stop = gs_stop; + scc_driver.start = gs_start; + scc_driver.hangup = gs_hangup; + scc_driver.break_ctl = scc_break_ctl; + + scc_callout_driver = scc_driver; + scc_callout_driver.name = "cua"; + scc_callout_driver.major = TTYAUX_MAJOR; + scc_callout_driver.subtype = SERIAL_TYPE_CALLOUT; + + if ((error = tty_register_driver(&scc_driver))) { + printk(KERN_ERR "scc: Couldn't register scc driver, error = %d\n", + error); + return 1; + } + if ((error = tty_register_driver(&scc_callout_driver))) { + tty_unregister_driver(&scc_driver); + printk(KERN_ERR "scc: Couldn't register scc callout driver, error = %d\n", + error); + return 1; + } + + return 0; +} + + +/* ports[] array is indexed by line no (i.e. [0] for ttyS0, [1] for ttyS1). + */ + +static void scc_init_portstructs(void) +{ + struct scc_port *port; + int i; + + for (i = 0; i < 2; i++) { + port = scc_ports + i; + port->gs.callout_termios = tty_std_termios; + port->gs.normal_termios = tty_std_termios; + port->gs.magic = SCC_MAGIC; + port->gs.close_delay = HZ/2; + port->gs.closing_wait = 30 * HZ; + port->gs.rd = &scc_real_driver; +#ifdef NEW_WRITE_LOCKING + port->gs.port_write_sem = MUTEX; +#endif + init_waitqueue_head(&port->gs.open_wait); + init_waitqueue_head(&port->gs.close_wait); + } +} + + +#ifdef CONFIG_MVME147_SCC +static int mvme147_scc_init(void) +{ + struct scc_port *port; + + printk("SCC: MVME147 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)M147_SCC_A_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME147_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(MVME147_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(MVME147_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(MVME147_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME147_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)M147_SCC_B_ADDR; + port->datap = port->ctrlp + 1; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME147_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(MVME147_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(MVME147_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(MVME147_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the PCC chip */ + m147_pcc->serial_cntrl=PCC_LEVEL_SERIAL|PCC_INT_ENAB; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +#ifdef CONFIG_MVME162_SCC +static int mvme162_scc_init(void) +{ + struct scc_port *port; + + if (!(mvme16x_config & MVME16x_CONFIG_GOT_SCCA)) + return (-ENODEV); + + printk("SCC: MVME162 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)MVME_SCC_A_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME162_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(MVME162_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(MVME162_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(MVME162_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, MVME162_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)MVME_SCC_B_ADDR; + port->datap = port->ctrlp + 2; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(MVME162_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(MVME162_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(MVME162_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(MVME162_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Ensure interrupts are enabled in the MC2 chip */ + *(volatile char *)0xfff4201d = 0x14; + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +#ifdef CONFIG_BVME6000_SCC +static int bvme6000_scc_init(void) +{ + struct scc_port *port; + + printk("SCC: BVME6000 Serial Driver\n"); + /* Init channel A */ + port = &scc_ports[0]; + port->channel = CHANNEL_A; + port->ctrlp = (volatile unsigned char *)BVME_SCC_A_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(BVME_IRQ_SCCA_TX, scc_tx_int, SA_INTERRUPT, + "SCC-A TX", port); + request_irq(BVME_IRQ_SCCA_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-A status", port); + request_irq(BVME_IRQ_SCCA_RX, scc_rx_int, SA_INTERRUPT, + "SCC-A RX", port); + request_irq(BVME_IRQ_SCCA_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-A special cond", port); + { + SCC_ACCESS_INIT(port); + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + /* Set the interrupt vector */ + SCCwrite(INT_VECTOR_REG, BVME_IRQ_SCC_BASE); + /* Interrupt parameters: vector includes status, status low */ + SCCwrite(MASTER_INT_CTRL, MIC_VEC_INCL_STAT); + SCCmod(MASTER_INT_CTRL, 0xff, MIC_MASTER_INT_ENAB); + } + + /* Init channel B */ + port = &scc_ports[1]; + port->channel = CHANNEL_B; + port->ctrlp = (volatile unsigned char *)BVME_SCC_B_ADDR; + port->datap = port->ctrlp + 4; + port->port_a = &scc_ports[0]; + port->port_b = &scc_ports[1]; + request_irq(BVME_IRQ_SCCB_TX, scc_tx_int, SA_INTERRUPT, + "SCC-B TX", port); + request_irq(BVME_IRQ_SCCB_STAT, scc_stat_int, SA_INTERRUPT, + "SCC-B status", port); + request_irq(BVME_IRQ_SCCB_RX, scc_rx_int, SA_INTERRUPT, + "SCC-B RX", port); + request_irq(BVME_IRQ_SCCB_SPCOND, scc_spcond_int, SA_INTERRUPT, + "SCC-B special cond", port); + + { + SCC_ACCESS_INIT(port); /* Either channel will do */ + + /* disable interrupts for this channel */ + SCCwrite(INT_AND_DMA_REG, 0); + } + + /* Initialise the tty driver structures and register */ + scc_init_portstructs(); + scc_init_drivers(); + + return 0; +} +#endif + + +int vme_scc_init(void) +{ + int res = -ENODEV; + static int called = 0; + + if (called) + return res; + called = 1; +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + res = mvme147_scc_init(); +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + res = mvme162_scc_init(); +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + res = bvme6000_scc_init(); +#endif + return res; +} + + +/*--------------------------------------------------------------------------- + * Interrupt handlers + *--------------------------------------------------------------------------*/ + +static void scc_rx_int(int irq, void *data, struct pt_regs *fp) +{ + unsigned char ch; + struct scc_port *port = data; + struct tty_struct *tty = port->gs.tty; + SCC_ACCESS_INIT(port); + + ch = SCCread_NB(RX_DATA_REG); + if (!tty) { + printk ("scc_rx_int with NULL tty!\n"); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = 0; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* Check if another character is already ready; in that case, the + * spcond_int() function must be used, because this character may have an + * error condition that isn't signalled by the interrupt vector used! + */ + if (SCCread(INT_PENDING_REG) & + (port->channel == CHANNEL_A ? IPR_A_RX : IPR_B_RX)) { + scc_spcond_int (irq, data, fp); + return; + } + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); +} + + +static void scc_spcond_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + struct tty_struct *tty = port->gs.tty; + unsigned char stat, ch, err; + int int_pending_mask = port->channel == CHANNEL_A ? + IPR_A_RX : IPR_B_RX; + SCC_ACCESS_INIT(port); + + if (!tty) { + printk ("scc_spcond_int with NULL tty!\n"); + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + do { + stat = SCCread(SPCOND_STATUS_REG); + ch = SCCread_NB(RX_DATA_REG); + + if (stat & SCSR_RX_OVERRUN) + err = TTY_OVERRUN; + else if (stat & SCSR_PARITY_ERR) + err = TTY_PARITY; + else if (stat & SCSR_CRC_FRAME_ERR) + err = TTY_FRAME; + else + err = 0; + + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + *tty->flip.char_buf_ptr = ch; + *tty->flip.flag_buf_ptr = err; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + /* ++TeSche: *All* errors have to be cleared manually, + * else the condition persists for the next chars + */ + if (err) + SCCwrite(COMMAND_REG, CR_ERROR_RESET); + + } while(SCCread(INT_PENDING_REG) & int_pending_mask); + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + + tty_flip_buffer_push(tty); +} + + +static void scc_tx_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + SCC_ACCESS_INIT(port); + + if (!port->gs.tty) { + printk ("scc_tx_int with NULL tty!\n"); + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); + return; + } + while ((SCCread_NB(STATUS_REG) & SR_TX_BUF_EMPTY)) { + if (port->x_char) { + SCCwrite(TX_DATA_REG, port->x_char); + port->x_char = 0; + } + else if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || + port->gs.tty->hw_stopped) + break; + else { + SCCwrite(TX_DATA_REG, port->gs.xmit_buf[port->gs.xmit_tail++]); + port->gs.xmit_tail = port->gs.xmit_tail & (SERIAL_XMIT_SIZE-1); + if (--port->gs.xmit_cnt <= 0) + break; + } + } + if ((port->gs.xmit_cnt <= 0) || port->gs.tty->stopped || + port->gs.tty->hw_stopped) { + /* disable tx interrupts */ + SCCmod (INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + SCCwrite(COMMAND_REG, CR_TX_PENDING_RESET); /* disable tx_int on next tx underrun? */ + port->gs.flags &= ~GS_TX_INTEN; + } + if (port->gs.tty && port->gs.xmit_cnt <= port->gs.wakeup_chars) { + if ((port->gs.tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + port->gs.tty->ldisc.write_wakeup) + (port->gs.tty->ldisc.write_wakeup)(port->gs.tty); + wake_up_interruptible(&port->gs.tty->write_wait); + } + + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); +} + + +static void scc_stat_int(int irq, void *data, struct pt_regs *fp) +{ + struct scc_port *port = data; + unsigned channel = port->channel; + unsigned char last_sr, sr, changed; + SCC_ACCESS_INIT(port); + + last_sr = scc_last_status_reg[channel]; + sr = scc_last_status_reg[channel] = SCCread_NB(STATUS_REG); + changed = last_sr ^ sr; + + if (changed & SR_DCD) { + port->c_dcd = !!(sr & SR_DCD); + if (!(port->gs.flags & ASYNC_CHECK_CD)) + ; /* Don't report DCD changes */ + else if (port->c_dcd) { + if (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || + ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) { + /* Are we blocking in open?*/ + wake_up_interruptible(&port->gs.open_wait); + } + } + else { + if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) && + (port->gs.flags & ASYNC_CALLOUT_NOHUP))) { + if (port->gs.tty) + tty_hangup (port->gs.tty); + } + } + } + SCCwrite(COMMAND_REG, CR_EXTSTAT_RESET); + SCCwrite_NB(COMMAND_REG, CR_HIGHEST_IUS_RESET); +} + + +/*--------------------------------------------------------------------------- + * generic_serial.c callback funtions + *--------------------------------------------------------------------------*/ + +static void scc_disable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, ~IDR_TX_INT_ENAB, 0); + port->gs.flags &= ~GS_TX_INTEN; + restore_flags(flags); +} + + +static void scc_enable_tx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, 0xff, IDR_TX_INT_ENAB); + /* restart the transmitter */ + scc_tx_int (0, port, 0); + restore_flags(flags); +} + + +static void scc_disable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, + ~(IDR_RX_INT_MASK|IDR_PARERR_AS_SPCOND|IDR_EXTSTAT_INT_ENAB), 0); + restore_flags(flags); +} + + +static void scc_enable_rx_interrupts(void *ptr) +{ + struct scc_port *port = ptr; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(INT_AND_DMA_REG, 0xff, + IDR_EXTSTAT_INT_ENAB|IDR_PARERR_AS_SPCOND|IDR_RX_INT_ALL); + restore_flags(flags); +} + + +static int scc_get_CD(void *ptr) +{ + struct scc_port *port = ptr; + unsigned channel = port->channel; + + return !!(scc_last_status_reg[channel] & SR_DCD); +} + + +static void scc_shutdown_port(void *ptr) +{ + struct scc_port *port = ptr; + + port->gs.flags &= ~ GS_ACTIVE; + if (port->gs.tty && port->gs.tty->termios->c_cflag & HUPCL) { + scc_setsignals (port, 0, 0); + } +} + + +static void scc_set_real_termios (void *ptr) +{ + /* the SCC has char sizes 5,7,6,8 in that order! */ + static int chsize_map[4] = { 0, 2, 1, 3 }; + unsigned cflag, baud, chsize, channel, brgval = 0; + unsigned long flags; + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + if (!port->gs.tty || !port->gs.tty->termios) return; + + channel = port->channel; + + if (channel == CHANNEL_A) + return; /* Settings controlled by boot PROM */ + + cflag = port->gs.tty->termios->c_cflag; + baud = port->gs.baud; + chsize = (cflag & CSIZE) >> 4; + + if (baud == 0) { + /* speed == 0 -> drop DTR */ + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_DTR, 0); + restore_flags(flags); + return; + } + else if ((MACH_IS_MVME16x && (baud < 50 || baud > 38400)) || + (MACH_IS_MVME147 && (baud < 50 || baud > 19200)) || + (MACH_IS_BVME6000 &&(baud < 50 || baud > 76800))) { + printk("SCC: Bad speed requested, %d\n", baud); + return; + } + + if (cflag & CLOCAL) + port->gs.flags &= ~ASYNC_CHECK_CD; + else + port->gs.flags |= ASYNC_CHECK_CD; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + brgval = (M147_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; + else +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + brgval = (MVME_SCC_PCLK + baud/2) / (16 * 2 * baud) - 2; + else +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + brgval = (BVME_SCC_RTxC + baud/2) / (16 * 2 * baud) - 2; +#endif + /* Now we have all parameters and can go to set them: */ + save_flags(flags); + cli(); + + /* receiver's character size and auto-enables */ + SCCmod(RX_CTRL_REG, ~(RCR_CHSIZE_MASK|RCR_AUTO_ENAB_MODE), + (chsize_map[chsize] << 6) | + ((cflag & CRTSCTS) ? RCR_AUTO_ENAB_MODE : 0)); + /* parity and stop bits (both, Tx and Rx), clock mode never changes */ + SCCmod (AUX1_CTRL_REG, + ~(A1CR_PARITY_MASK | A1CR_MODE_MASK), + ((cflag & PARENB + ? (cflag & PARODD ? A1CR_PARITY_ODD : A1CR_PARITY_EVEN) + : A1CR_PARITY_NONE) + | (cflag & CSTOPB ? A1CR_MODE_ASYNC_2 : A1CR_MODE_ASYNC_1))); + /* sender's character size, set DTR for valid baud rate */ + SCCmod(TX_CTRL_REG, ~TCR_CHSIZE_MASK, chsize_map[chsize] << 5 | TCR_DTR); + /* clock sources never change */ + /* disable BRG before changing the value */ + SCCmod(DPLL_CTRL_REG, ~DCR_BRG_ENAB, 0); + /* BRG value */ + SCCwrite(TIMER_LOW_REG, brgval & 0xff); + SCCwrite(TIMER_HIGH_REG, (brgval >> 8) & 0xff); + /* BRG enable, and clock source never changes */ + SCCmod(DPLL_CTRL_REG, 0xff, DCR_BRG_ENAB); + + restore_flags(flags); +} + + +static int scc_chars_in_buffer (void *ptr) +{ + struct scc_port *port = ptr; + SCC_ACCESS_INIT(port); + + return (SCCread (SPCOND_STATUS_REG) & SCSR_ALL_SENT) ? 0 : 1; +} + + +static void scc_hungup(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); + MOD_DEC_USE_COUNT; +} + + +static void scc_close(void *ptr) +{ + scc_disable_tx_interrupts(ptr); + scc_disable_rx_interrupts(ptr); +} + + +/*--------------------------------------------------------------------------- + * Internal support functions + *--------------------------------------------------------------------------*/ + +static void scc_setsignals(struct scc_port *port, int dtr, int rts) +{ + unsigned long flags; + unsigned char t; + SCC_ACCESS_INIT(port); + + save_flags(flags); + t = SCCread(TX_CTRL_REG); + if (dtr >= 0) t = dtr? (t | TCR_DTR): (t & ~TCR_DTR); + if (rts >= 0) t = rts? (t | TCR_RTS): (t & ~TCR_RTS); + SCCwrite(TX_CTRL_REG, t); + restore_flags(flags); +} + + +static void scc_send_xchar(struct tty_struct *tty, char ch) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + + port->x_char = ch; + if (ch) + scc_enable_tx_interrupts(port); +} + + +/*--------------------------------------------------------------------------- + * Driver entrypoints referenced from above + *--------------------------------------------------------------------------*/ + +static int scc_open (struct tty_struct * tty, struct file * filp) +{ + int line = MINOR(tty->device) - SCC_MINOR_BASE; + int retval; + struct scc_port *port = &scc_ports[line]; + int i, channel = port->channel; + unsigned long flags; + SCC_ACCESS_INIT(port); +#if defined(CONFIG_MVME162_SCC) || defined(CONFIG_MVME147_SCC) + static const struct { + unsigned reg, val; + } mvme_init_tab[] = { + /* Values for MVME162 and MVME147 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB | DCR_BRG_USE_PCLK }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif +#if defined(CONFIG_BVME6000_SCC) + static const struct { + unsigned reg, val; + } bvme_init_tab[] = { + /* Values for BVME6000 */ + /* no parity, 1 stop bit, async, 1:16 */ + { AUX1_CTRL_REG, A1CR_PARITY_NONE|A1CR_MODE_ASYNC_1|A1CR_CLKMODE_x16 }, + /* parity error is special cond, ints disabled, no DMA */ + { INT_AND_DMA_REG, IDR_PARERR_AS_SPCOND | IDR_RX_INT_DISAB }, + /* Rx 8 bits/char, no auto enable, Rx off */ + { RX_CTRL_REG, RCR_CHSIZE_8 }, + /* DTR off, Tx 8 bits/char, RTS off, Tx off */ + { TX_CTRL_REG, TCR_CHSIZE_8 }, + /* special features off */ + { AUX2_CTRL_REG, 0 }, + { CLK_CTRL_REG, CCR_RTxC_XTAL | CCR_RXCLK_BRG | CCR_TXCLK_BRG }, + { DPLL_CTRL_REG, DCR_BRG_ENAB }, + /* Start Rx */ + { RX_CTRL_REG, RCR_RX_ENAB | RCR_CHSIZE_8 }, + /* Start Tx */ + { TX_CTRL_REG, TCR_TX_ENAB | TCR_RTS | TCR_DTR | TCR_CHSIZE_8 }, + /* Ext/Stat ints: DCD only */ + { INT_CTRL_REG, ICR_ENAB_DCD_INT }, + /* Reset Ext/Stat ints */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + /* ...again */ + { COMMAND_REG, CR_EXTSTAT_RESET }, + }; +#endif + if (!(port->gs.flags & ASYNC_INITIALIZED)) { + save_flags(flags); + cli(); +#if defined(CONFIG_MVME147_SCC) || defined(CONFIG_MVME162_SCC) + if (MACH_IS_MVME147 || MACH_IS_MVME16x) { + for (i=0; ic_dcd = 0; /* Prevent initial 1->0 interrupt */ + scc_setsignals (port, 1,1); + restore_flags(flags); + } + + tty->driver_data = port; + port->gs.tty = tty; + port->gs.count++; + retval = gs_init_port(&port->gs); + if (retval) { + port->gs.count--; + return retval; + } + port->gs.flags |= GS_ACTIVE; + if (port->gs.count == 1) { + MOD_INC_USE_COUNT; + } + retval = block_til_ready(port, filp); + + if (retval) { + MOD_DEC_USE_COUNT; + port->gs.count--; + return retval; + } + + if ((port->gs.count == 1) && (port->gs.flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = port->gs.normal_termios; + else + *tty->termios = port->gs.callout_termios; + scc_set_real_termios (port); + } + + port->gs.session = current->session; + port->gs.pgrp = current->pgrp; + port->c_dcd = scc_get_CD (port); + + scc_enable_rx_interrupts(port); + + return 0; +} + + +static void scc_throttle (struct tty_struct * tty) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_RTS, 0); + restore_flags(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, STOP_CHAR(tty)); +} + + +static void scc_unthrottle (struct tty_struct * tty) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + if (tty->termios->c_cflag & CRTSCTS) { + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, 0xff, TCR_RTS); + restore_flags(flags); + } + if (I_IXOFF(tty)) + scc_send_xchar(tty, START_CHAR(tty)); +} + + +static int scc_ioctl(struct tty_struct *tty, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + + +static void scc_break_ctl(struct tty_struct *tty, int break_state) +{ + struct scc_port *port = (struct scc_port *)tty->driver_data; + unsigned long flags; + SCC_ACCESS_INIT(port); + + save_flags(flags); + cli(); + SCCmod(TX_CTRL_REG, ~TCR_SEND_BREAK, + break_state ? TCR_SEND_BREAK : 0); + restore_flags(flags); +} + + +/*--------------------------------------------------------------------------- + * Serial console stuff... + *--------------------------------------------------------------------------*/ + +#define scc_delay() do { __asm__ __volatile__ (" nop; nop"); } while (0) + +static void scc_ch_write (char ch) +{ + volatile char *p = NULL; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + p = (volatile char *)M147_SCC_A_ADDR; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + p = (volatile char *)MVME_SCC_A_ADDR; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + p = (volatile char *)BVME_SCC_A_ADDR; +#endif + + do { + scc_delay(); + } + while (!(*p & 4)); + scc_delay(); + *p = 8; + scc_delay(); + *p = ch; +} + + +static void scc_console_write (struct console *co, const char *str, unsigned count) +{ + unsigned long flags; + + save_flags(flags); + cli(); + + while (count--) + { + if (*str == '\n') + scc_ch_write ('\r'); + scc_ch_write (*str++); + } + restore_flags(flags); +} + + +static int scc_console_wait_key(struct console *co) +{ + unsigned long flags; + volatile char *p = NULL; + int c; + +#ifdef CONFIG_MVME147_SCC + if (MACH_IS_MVME147) + p = (volatile char *)M147_SCC_A_ADDR; +#endif +#ifdef CONFIG_MVME162_SCC + if (MACH_IS_MVME16x) + p = (volatile char *)MVME_SCC_A_ADDR; +#endif +#ifdef CONFIG_BVME6000_SCC + if (MACH_IS_BVME6000) + p = (volatile char *)BVME_SCC_A_ADDR; +#endif + + save_flags(flags); + cli(); + + /* wait for rx buf filled */ + while ((*p & 0x01) == 0) + ; + + *p = 8; + scc_delay(); + c = *p; + restore_flags(flags); + return c; +} + + +static kdev_t scc_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, SCC_MINOR_BASE + c->index); +} + + +static int __init scc_console_setup(struct console *co, char *options) +{ + return 0; +} + + +static struct console sercons = { + "ttyS", + scc_console_write, + NULL, + scc_console_device, + scc_console_wait_key, + NULL, + scc_console_setup, + CON_PRINTBUFFER, + -1, + 0, + NULL +}; + + +void __init vme_scc_console_init(void) +{ + if (vme_brdtype == VME_TYPE_MVME147 || + vme_brdtype == VME_TYPE_MVME162 || + vme_brdtype == VME_TYPE_MVME172 || + vme_brdtype == VME_TYPE_BVME4000 || + vme_brdtype == VME_TYPE_BVME6000) + register_console(&sercons); +} + diff --git a/drivers/i2c/Config.in b/drivers/i2c/Config.in index ba533aac0a5e..9050f3528707 100644 --- a/drivers/i2c/Config.in +++ b/drivers/i2c/Config.in @@ -8,22 +8,22 @@ tristate 'I2C support' CONFIG_I2C if [ "$CONFIG_I2C" != "n" ]; then - dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C - if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then - dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT - dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT - dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT - fi + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi - dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C - if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then - dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF - fi + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here - dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C fi endmenu diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 3d88b6c39012..e74337f5bd77 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -20,7 +20,7 @@ /* With some changes from Kyösti Mälkki . All SMBus-related things are written by Frodo Looijaard */ -/* $Id: i2c-core.c,v 1.44 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-core.c,v 1.48 2000/01/24 21:41:19 frodo Exp $ */ #include #include @@ -33,37 +33,14 @@ /* ----- compatibility stuff ----------------------------------------------- */ -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif - #include -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,53) #include -#else -#define __init -#endif #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) #define init_MUTEX(s) do { *(s) = MUTEX; } while(0) #endif -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs -#else #include -#endif /* ----- global defines ---------------------------------------------------- */ @@ -83,8 +60,8 @@ /* ----- global variables -------------------------------------------------- */ /**** lock for writing to global variables: the adapter & driver list */ -struct semaphore adap_lock; -struct semaphore driver_lock; +struct semaphore adap_lock; +struct semaphore driver_lock; /**** adapter list */ static struct i2c_adapter *adapters[I2C_ADAP_MAX]; @@ -104,73 +81,29 @@ static void i2c_dummy_client(struct i2c_client *client); *---------------------------------------------------- */ -/* Note that quite some things changed within the 2.1 kernel series. - Some things below are somewhat difficult to read because of this. */ - #ifdef CONFIG_PROC_FS static int i2cproc_init(void); static int i2cproc_cleanup(void); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ - (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) static void monitor_bus_i2c(struct inode *inode, int fill); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) - static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos); static int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof , void *private); -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - -static int i2cproc_bus_read(struct inode * inode, struct file * file, - char * buf, int count); -static int read_bus_i2c(char *buf, char **start, off_t offset, int len, - int unused); - -static struct proc_dir_entry proc_bus_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "bus", - /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, - /* nlink */ 2, /* Corrected by proc_register[_dynamic] */ - /* uid */ 0, - /* gid */ 0, - /* size */ 0, -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,0,36)) - /* ops */ &proc_dir_inode_operations, -#endif - }; - -static struct proc_dir_entry proc_bus_i2c_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "i2c", - /* mode */ S_IRUGO | S_IFREG, - /* nlink */ 1, - /* uid */ 0, - /* gid */ 0, - /* size */ 0, - /* ops */ NULL, - /* get_info */ &read_bus_i2c - }; - -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ - /* To implement the dynamic /proc/bus/i2c-? files, we need our own implementation of the read hook */ static struct file_operations i2cproc_operations = { - NULL, - i2cproc_bus_read, + NULL, + i2cproc_bus_read, }; static struct inode_operations i2cproc_inode_operations = { - &i2cproc_operations + &i2cproc_operations }; static int i2cproc_initialized = 0; @@ -183,7 +116,7 @@ static int i2cproc_initialized = 0; #endif /* CONFIG_PROC_FS */ -/* --------------------------------------------------- +/* --------------------------------------------------- * registering functions * --------------------------------------------------- */ @@ -222,55 +155,21 @@ int i2c_add_adapter(struct i2c_adapter *adap) if (i2cproc_initialized) { char name[8]; struct proc_dir_entry *proc_entry; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) - int res; -#endif sprintf(name,"i2c-%d", i); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) proc_entry = create_proc_entry(name,0,proc_bus); if (! proc_entry) { printk("i2c-core.o: Could not create /proc/bus/%s\n", - name); + name); return -ENOENT; - } + } proc_entry->ops = &i2cproc_inode_operations; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) proc_entry->owner = THIS_MODULE; -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) +#else proc_entry->fill_inode = &monitor_bus_i2c; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - adap->proc_entry = NULL; - if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+ - strlen(name)+1, GFP_KERNEL))) { - printk("i2c-core.o: Out of memory!\n"); - return -ENOMEM; - } - memset(proc_entry,0,sizeof(struct proc_dir_entry)); - proc_entry->namelen = strlen(name); - proc_entry->name = (char *) (proc_entry + 1); - proc_entry->mode = S_IRUGO | S_IFREG; - proc_entry->nlink = 1; - proc_entry->ops = &i2cproc_inode_operations; - - /* Nasty stuff to keep GCC satisfied */ - { - char *procname; - (const char *) procname = proc_entry->name; - strcpy (procname,name); - } - - if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) { - printk("i2c-core.o: Could not create %s.\n",name); - kfree(proc_entry); - return res; - } - - adap->proc_entry = proc_entry; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ - adap->inode = proc_entry->low_ino; } @@ -283,7 +182,8 @@ int i2c_add_adapter(struct i2c_adapter *adap) drivers[j]->attach_adapter(adap); DRV_UNLOCK(); - DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n",adap->name,i)); + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n", + adap->name,i)); return 0; } @@ -306,23 +206,9 @@ int i2c_del_adapter(struct i2c_adapter *adap) i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ #ifdef CONFIG_PROC_FS if (i2cproc_initialized) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) char name[8]; sprintf(name,"i2c-%d", i); remove_proc_entry(name,proc_bus); -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - int res; - if (adapters[i]->proc_entry) { - if ((res = proc_unregister(&proc_bus_dir, - adapters[i]->proc_entry->low_ino))) { - printk("i2c-core.o: Deregistration of /proc " - "entry failed\n"); - ADAP_UNLOCK(); - return res; - } - kfree(adapters[i]->proc_entry); - } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ } #endif /* def CONFIG_PROC_FS */ @@ -344,7 +230,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) ADAP_UNLOCK(); DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); - return 0; + return 0; } @@ -384,7 +270,8 @@ int i2c_add_driver(struct i2c_driver *driver) driver->attach_adapter(adapters[i]); for (j=0; jclients[j]) - driver->detach_client(adapters[i]->clients[j]); + driver->detach_client( + adapters[i]->clients[j]); } } ADAP_UNLOCK(); @@ -418,7 +305,7 @@ int i2c_del_driver(struct i2c_driver *driver) } /* Have a look at each adapter, if clients of this driver are still * attached. If so, detach them to be able to kill the driver - * afterwards. + * afterwards. */ DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); /* removing clients does not depend on the notify flag, else @@ -453,11 +340,11 @@ int i2c_del_driver(struct i2c_driver *driver) int i2c_check_addr (struct i2c_adapter *adapter, int addr) { - int i; - for (i = 0; i < I2C_CLIENT_MAX ; i++) - if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) - return -EBUSY; - return 0; + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; } int i2c_attach_client(struct i2c_client *client) @@ -465,8 +352,8 @@ int i2c_attach_client(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; int i; - if (i2c_check_addr(client->adapter,client->addr)) - return -EBUSY; + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; for (i = 0; i < I2C_CLIENT_MAX; i++) if (NULL == adapter->clients[i]) @@ -513,7 +400,7 @@ int i2c_detach_client(struct i2c_client *client) i2c_dummy_client(client); DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); - return 0; + return 0; } void i2c_inc_use_client(struct i2c_client *client) @@ -543,26 +430,21 @@ void i2c_dec_use_client(struct i2c_client *client) #ifdef CONFIG_PROC_FS -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) && \ - (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) /* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible if some process still uses it or some file in it */ void monitor_bus_i2c(struct inode *inode, int fill) { - if (fill) - MOD_INC_USE_COUNT; - else - MOD_DEC_USE_COUNT; + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,37)) */ /* This function generates the output for /proc/bus/i2c */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, void *private) -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ -int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ { int i; int nr = 0; @@ -571,7 +453,7 @@ int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) if (adapters[i]) { nr += sprintf(buf+nr, "i2c-%d\t", i); if (adapters[i]->algo->smbus_xfer) { - if (adapters[i]->algo->master_xfer) + if (adapters[i]->algo->master_xfer) nr += sprintf(buf+nr,"smbus/i2c"); else nr += sprintf(buf+nr,"smbus "); @@ -587,62 +469,55 @@ int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) } /* This function generates the output for /proc/bus/i2c-? */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, loff_t *ppos) { - struct inode * inode = file->f_dentry->d_inode; -#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) -int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf, - int count) -{ -#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) - char *kbuf; - struct i2c_client *client; - int i,j,len=0; - - if (count < 0) - return -EINVAL; - if (count > 4000) - count = 4000; - for (i = 0; i < I2C_ADAP_MAX; i++) - if (adapters[i]->inode == inode->i_ino) { - /* We need a bit of slack in the kernel buffer; this makes the - sprintf safe. */ - if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) - return -ENOMEM; - for (j = 0; j < I2C_CLIENT_MAX; j++) - if ((client = adapters[i]->clients[j])) - /* Filter out dummy clients */ - if (client->driver->id != I2C_DRIVERID_I2CDEV) - len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", - client->addr, - client->name,client->driver->name); - if (file->f_pos+len > count) - len = count - file->f_pos; - len = len - file->f_pos; - if (len < 0) - len = 0; - copy_to_user (buf,kbuf+file->f_pos,len); - file->f_pos += len; - kfree(kbuf); - return len; - } - return -ENOENT; + struct inode * inode = file->f_dentry->d_inode; + char *kbuf; + struct i2c_client *client; + int i,j,len=0; + + if (count < 0) + return -EINVAL; + if (count > 4000) + count = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + for (j = 0; j < I2C_CLIENT_MAX; j++) + if ((client = adapters[i]->clients[j])) + /* Filter out dummy clients */ + if (client->driver->id != I2C_DRIVERID_I2CDEV) + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name,client->driver->name); + if (file->f_pos+len > count) + len = count - file->f_pos; + len = len - file->f_pos; + if (len < 0) + len = 0; + if (copy_to_user (buf,kbuf+file->f_pos, + len)) { + kfree(kbuf); + return -EFAULT; + } + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; } int i2cproc_init(void) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) struct proc_dir_entry *proc_bus_i2c; -#else - int res; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ i2cproc_initialized = 0; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) if (! proc_bus) { printk("i2c-core.o: /proc/bus/ does not exist"); i2cproc_cleanup(); @@ -657,29 +532,10 @@ int i2cproc_init(void) proc_bus_i2c->read_proc = &read_bus_i2c; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) proc_bus_i2c->owner = THIS_MODULE; -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) +#else proc_bus_i2c->fill_inode = &monitor_bus_i2c; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) */ i2cproc_initialized += 2; -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module - introduced it, or we are fucked. And 2.0.35 and earlier does not - export proc_dir_inode_operations, so we grab it from proc_net, - which also uses it. Not nice. */ - proc_bus_dir.ops = proc_net.ops; - if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { - printk("i2c-core.o: Could not create /proc/bus/"); - i2cproc_cleanup(); - return res; - } - i2cproc_initialized ++; - if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { - printk("i2c-core.o: Could not create /proc/bus/i2c\n"); - i2cproc_cleanup(); - return res; - } - i2cproc_initialized ++; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ return 0; } @@ -687,27 +543,8 @@ int i2cproc_cleanup(void) { if (i2cproc_initialized >= 1) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) remove_proc_entry("i2c",proc_bus); i2cproc_initialized -= 2; -#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */ - int res; - if (i2cproc_initialized >= 2) { - if ((res = proc_unregister(&proc_bus_dir, - proc_bus_i2c_dir.low_ino))) { - printk("i2c-core.o: could not delete " - "/proc/bus/i2c, module not removed."); - return res; - } - i2cproc_initialized --; - } - if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { - printk("i2c-core.o: could not delete /proc/bus/, " - "module not removed."); - return res; - } - i2cproc_initialized --; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */ } return 0; } @@ -715,7 +552,7 @@ int i2cproc_cleanup(void) #endif /* def CONFIG_PROC_FS */ -/* --------------------------------------------------- +/* --------------------------------------------------- * dummy driver notification * --------------------------------------------------- */ @@ -724,16 +561,16 @@ static void i2c_dummy_adapter(struct i2c_adapter *adap) { int i; for (i=0; iflags & I2C_DF_DUMMY)) - drivers[i]->attach_adapter(adap); + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); } static void i2c_dummy_client(struct i2c_client *client) { int i; for (i=0; iflags & I2C_DF_DUMMY)) - drivers[i]->detach_client(client); + if (drivers[i] && (drivers[i]->flags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); } @@ -747,7 +584,8 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) int ret; if (adap->algo->master_xfer) { - DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n",adap->name,num)); + DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n", + adap->name,num)); I2C_LOCK(adap); ret = adap->algo->master_xfer(adap,msgs,num); @@ -756,7 +594,7 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) return ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - adap->id); + adap->id); return -ENOSYS; } } @@ -786,7 +624,7 @@ int i2c_master_send(struct i2c_client *client,const char *buf ,int count) return (ret == 1 )? count : ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - client->adapter->id); + client->adapter->id); return -ENOSYS; } } @@ -819,7 +657,7 @@ int i2c_master_recv(struct i2c_client *client, char *buf ,int count) return (ret == 1 )? count : ret; } else { printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", - client->adapter->id); + client->adapter->id); return -ENOSYS; } } @@ -855,128 +693,124 @@ int i2c_probe(struct i2c_adapter *adapter, struct i2c_client_address_data *address_data, i2c_client_found_addr_proc *found_proc) { - int addr,i,found,err; - int adap_id = i2c_adapter_id(adapter); - - /* Forget it if we can't probe using SMBUS_QUICK */ - if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) - return -1; - - for (addr = 0x00; - addr <= 0x7f; - addr++) { - - /* Skip if already in use */ - if (i2c_check_addr(adapter,addr)) - continue; - - /* If it is in one of the force entries, we don't do any detection - at all */ - found = 0; - - for (i = 0; - !found && (address_data->force[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->force[i]) || - (address_data->force[i] == ANY_I2C_BUS)) && - (addr == address_data->force[i+1])) { - DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", - adap_id,addr)); - if ((err = found_proc(adapter,addr,0,0))) - return err; - found = 1; - } - } - if (found) - continue; - - /* If this address is in one of the ignores, we can forget about it - right now */ - for (i = 0; - !found && (address_data->ignore[i] != I2C_CLIENT_END); - i += 2) { - if (((adap_id == address_data->ignore[i]) || - ((address_data->ignore[i] == ANY_I2C_BUS))) && - (addr == address_data->ignore[i+1])) { - DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " - "addr %04x\n", adap_id ,addr)); - found = 1; - } - } - for (i = 0; - !found && (address_data->ignore_range[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->ignore_range[i]) || - ((address_data->ignore_range[i]==ANY_I2C_BUS))) && - (addr >= address_data->ignore_range[i+1]) && - (addr <= address_data->ignore_range[i+2])) { - DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - found = 1; - } - } - if (found) - continue; - - /* Now, we will do a detection, but only if it is in the normal or - probe entries */ - for (i = 0; - !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); - i += 1) { - if (addr == address_data->normal_i2c[i]) { - found = 1; - DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " - "addr %02x", adap_id,addr)); - } - } - - for (i = 0; - !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); - i += 2) { - if ((addr >= address_data->normal_i2c_range[i]) && - (addr <= address_data->normal_i2c_range[i+1])) { - found = 1; - DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - - for (i = 0; - !found && (address_data->probe[i] != I2C_CLIENT_END); - i += 2) { - if (((adap_id == address_data->probe[i]) || - ((address_data->probe[i] == ANY_I2C_BUS))) && - (addr == address_data->probe[i+1])) { - found = 1; - DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - for (i = 0; - !found && (address_data->probe_range[i] != I2C_CLIENT_END); - i += 3) { - if (((adap_id == address_data->probe_range[i]) || - (address_data->probe_range[i] == ANY_I2C_BUS)) && - (addr >= address_data->probe_range[i+1]) && - (addr <= address_data->probe_range[i+2])) { - found = 1; - DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " - "addr %04x\n", adap_id,addr)); - } - } - if (!found) - continue; - - /* OK, so we really should examine this address. First check - whether there is some client here at all! */ - if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) - if ((err = found_proc(adapter,addr,0,-1))) - return err; - } - return 0; + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; addr <= 0x7f; addr++) { + + /* Skip if already in use */ + if (i2c_check_addr(adapter,addr)) + continue; + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about + it right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; } -/* +++ frodo +/* * return id number for a specific adapter */ int i2c_adapter_id(struct i2c_adapter *adap) @@ -993,14 +827,14 @@ int i2c_adapter_id(struct i2c_adapter *adap) extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) { return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - value,0,I2C_SMBUS_QUICK,NULL); + value,0,I2C_SMBUS_QUICK,NULL); } extern s32 i2c_smbus_read_byte(struct i2c_client * client) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) return -1; else return 0x0FF & data.byte; @@ -1009,26 +843,26 @@ extern s32 i2c_smbus_read_byte(struct i2c_client * client) extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) { return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); } extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) return -1; else return 0x0FF & data.byte; } -extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, - u8 command, u8 value) +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, u8 command, + u8 value) { union i2c_smbus_data data; data.byte = value; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, + I2C_SMBUS_WRITE,command, I2C_SMBUS_BYTE_DATA,&data); } @@ -1036,7 +870,7 @@ extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) { union i2c_smbus_data data; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) return -1; else return 0x0FFFF & data.word; @@ -1048,8 +882,8 @@ extern s32 i2c_smbus_write_word_data(struct i2c_client * client, union i2c_smbus_data data; data.word = value; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, - I2C_SMBUS_WORD_DATA,&data); + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); } extern s32 i2c_smbus_process_call(struct i2c_client * client, @@ -1058,8 +892,8 @@ extern s32 i2c_smbus_process_call(struct i2c_client * client, union i2c_smbus_data data; data.word = value; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, - I2C_SMBUS_PROC_CALL, &data)) + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) return -1; else return 0x0FFFF & data.word; @@ -1072,7 +906,7 @@ extern s32 i2c_smbus_read_block_data(struct i2c_client * client, union i2c_smbus_data data; int i; if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_READ,command, + I2C_SMBUS_READ,command, I2C_SMBUS_BLOCK_DATA,&data)) return -1; else { @@ -1093,7 +927,7 @@ extern s32 i2c_smbus_write_block_data(struct i2c_client * client, data.block[i] = values[i-1]; data.block[0] = length; return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,command, + I2C_SMBUS_WRITE,command, I2C_SMBUS_BLOCK_DATA,&data); } @@ -1177,7 +1011,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, size); return -1; } - + if (i2c_transfer(adapter, msg, num) < 0) return -1; @@ -1193,8 +1027,8 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, case I2C_SMBUS_PROC_CALL: data->word = msgbuf1[0] | (msgbuf1[1] << 8); break; - } - return 0; + } + return 0; } @@ -1203,7 +1037,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, union i2c_smbus_data * data) { s32 res; - flags = flags & I2C_M_TEN; + flags = flags & I2C_M_TEN; if (adapter->algo->smbus_xfer) { I2C_LOCK(adapter); res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, @@ -1211,7 +1045,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, I2C_UNLOCK(adapter); } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, - command,size,data); + command,size,data); return res; } @@ -1220,22 +1054,22 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, backward compatibility. */ u32 i2c_get_functionality (struct i2c_adapter *adap) { - if (adap->algo->functionality) - return adap->algo->functionality(adap); - else - return 0xffffffff; + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; } int i2c_check_functionality (struct i2c_adapter *adap, u32 func) { - u32 adap_func = i2c_get_functionality (adap); - return (func & adap_func) == func; + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; } static int __init i2c_init(void) { - printk("i2c-core.o: i2c core module\n"); + printk("i2c-core.o: i2c core module\n"); memset(adapters,0,sizeof(adapters)); memset(drivers,0,sizeof(drivers)); adap_count=0; diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 088d730fce22..c2c51bef43b0 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -23,7 +23,7 @@ But I have used so much of his original code and ideas that it seems only fair to recognize him as co-author -- Frodo */ -/* $Id: i2c-dev.c,v 1.18 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-dev.c,v 1.25 2000/01/26 14:14:20 frodo Exp $ */ #include #include @@ -34,34 +34,8 @@ /* If you want debugging uncomment: */ /* #define DEBUG */ -#ifndef KERNEL_VERSION -#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,51) #include -#else -#define __init -#endif - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4)) -#define copy_from_user memcpy_fromfs -#define copy_to_user memcpy_tofs -#define get_user_data(to,from) ((to) = get_user(from),0) -#else #include -#define get_user_data(to,from) get_user(to,from) -#endif - -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif #include #include @@ -73,45 +47,18 @@ extern int cleanup_module(void); /* struct file_operations changed too often in the 2.1 series for nice code */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) -static long long i2cdev_lseek (struct file *file, long long offset, int origin); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long long i2cdev_llseek (struct inode *inode, struct file *file, - long long offset, int origin); -#else -static int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, - int origin); -#endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset); static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, loff_t *offset); -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_read (struct inode *inode, struct file *file, char *buf, - unsigned long count); -static long i2cdev_write (struct inode *inode, struct file *file, - const char *buf, unsigned long offset); -#else -static int i2cdev_read(struct inode *inode, struct file *file, char *buf, - int count); -static int i2cdev_write(struct inode *inode, struct file *file, - const char *buf, int count); -#endif static int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int i2cdev_open (struct inode *inode, struct file *file); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) static int i2cdev_release (struct inode *inode, struct file *file); -#else -static void i2cdev_release (struct inode *inode, struct file *file); -#endif - static int i2cdev_attach_adapter(struct i2c_adapter *adap); static int i2cdev_detach_client(struct i2c_client *client); @@ -127,413 +74,349 @@ extern static int i2cdev_cleanup(void); static struct file_operations i2cdev_fops = { - i2cdev_lseek, - i2cdev_read, - i2cdev_write, - NULL, /* i2cdev_readdir */ - NULL, /* i2cdev_select */ - i2cdev_ioctl, - NULL, /* i2cdev_mmap */ - i2cdev_open, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) - NULL, /* i2cdev_flush */ -#endif - i2cdev_release, + i2cdev_lseek, + i2cdev_read, + i2cdev_write, + NULL, /* i2cdev_readdir */ + NULL, /* i2cdev_select */ + i2cdev_ioctl, + NULL, /* i2cdev_mmap */ + i2cdev_open, + NULL, /* i2cdev_flush */ + i2cdev_release, }; #define I2CDEV_ADAPS_MAX I2C_ADAP_MAX static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; static struct i2c_driver i2cdev_driver = { - /* name */ "i2c-dev dummy driver", - /* id */ I2C_DRIVERID_I2CDEV, - /* flags */ I2C_DF_DUMMY, - /* attach_adapter */ i2cdev_attach_adapter, - /* detach_client */ i2cdev_detach_client, - /* command */ i2cdev_command, - /* inc_use */ NULL, - /* dec_use */ NULL, + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, }; static struct i2c_client i2cdev_client_template = { - /* name */ "I2C /dev entry", - /* id */ 1, - /* flags */ 0, - /* addr */ -1, - /* adapter */ NULL, - /* driver */ &i2cdev_driver, - /* data */ NULL + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL }; static int i2cdev_initialized; /* Note that the lseek function is called llseek in 2.1 kernels. But things are complicated enough as is. */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) -long long i2cdev_lseek (struct file *file, long long offset, int origin) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -long long i2cdev_llseek (struct inode *inode, struct file *file, - long long offset, int origin) -#else -int i2cdev_lseek (struct inode *inode, struct file *file, off_t offset, - int origin) -#endif { #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,56)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ - printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", - MINOR(inode->i_rdev),(long) offset,origin); + struct inode *inode = file->f_dentry->d_inode; + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); #endif /* DEBUG */ - return -ESPIPE; + return -ESPIPE; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, loff_t *offset) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_read (struct inode *inode, struct file *file, char *buf, - unsigned long count) -#else -static int i2cdev_read(struct inode *inode, struct file *file, char *buf, - int count) -#endif { - char *tmp; - int ret; + char *tmp; + int ret; #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + struct inode *inode = file->f_dentry->d_inode; #endif /* DEBUG */ - struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_client *client = (struct i2c_client *)file->private_data; - /* copy user space data to kernel space. */ - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) - return -ENOMEM; + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; #ifdef DEBUG - printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev),count); + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev), + count); #endif - ret = i2c_master_recv(client,tmp,count); - copy_to_user(buf,tmp,count); - kfree(tmp); - return ret; + ret = i2c_master_recv(client,tmp,count); + if (! ret) + ret = copy_to_user(buf,tmp,count)?-EFAULT:0; + kfree(tmp); + return ret; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, loff_t *offset) -#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) -static long i2cdev_write (struct inode *inode, struct file *file, - const char *buf, unsigned long offset) -#else -static int i2cdev_write(struct inode *inode, struct file *file, - const char *buf, int count) -#endif { - int ret; - char *tmp; - struct i2c_client *client = (struct i2c_client *)file->private_data; + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; #ifdef DEBUG -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) - struct inode *inode = file->f_dentry->d_inode; -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,70)) */ + struct inode *inode = file->f_dentry->d_inode; #endif /* DEBUG */ - /* copy user space data to kernel space. */ - tmp = kmalloc(count,GFP_KERNEL); - if (tmp==NULL) - return -ENOMEM; - copy_from_user(tmp,buf,count); + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + if (copy_from_user(tmp,buf,count)) { + kfree(tmp); + return -EFAULT; + } #ifdef DEBUG - printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev),count); + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev), + count); #endif - ret = i2c_master_send(client,tmp,count); - kfree(tmp); - return ret; + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; } int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct i2c_client *client = (struct i2c_client *)file->private_data; - struct i2c_smbus_ioctl_data data_arg; - union i2c_smbus_data temp; - int ver,datasize,res; - unsigned long funcs; + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + int datasize,res; + unsigned long funcs; #ifdef DEBUG - printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", - MINOR(inode->i_rdev),cmd, arg); + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); #endif /* DEBUG */ - switch ( cmd ) { - case I2C_SLAVE: - case I2C_SLAVE_FORCE: - if ((arg > 0x3ff) || (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) - return -EINVAL; - if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) - return -EBUSY; - client->addr = arg; - return 0; - case I2C_TENBIT: - if (arg) - client->flags |= I2C_M_TEN; - else - client->flags &= ~I2C_M_TEN; - return 0; - case I2C_FUNCS: - if (! arg) { -#ifdef DEBUG - printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); -#endif - return -EINVAL; - } - if (verify_area(VERIFY_WRITE,(unsigned long *) arg, - sizeof(unsigned long))) { + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return (copy_to_user((unsigned long *)arg,&funcs, + sizeof(unsigned long)))?-EFAULT:0; + case I2C_SMBUS: + copy_from_user_ret(&data_arg, + (struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data), + -EFAULT); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { #ifdef DEBUG - printk("i2c-dev.o: invalid argument pointer (%ld) " - "in IOCTL I2C_SMBUS.\n", arg); + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); #endif - return -EINVAL; - } - - funcs = i2c_get_functionality(client->adapter); - copy_to_user((unsigned long *)arg,&funcs,sizeof(unsigned long)); - return 0; - case I2C_SMBUS: - if (! arg) { + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { #ifdef DEBUG - printk("i2c-dev.o: NULL argument pointer in ioctl I2C_SMBUS.\n"); + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); #endif - return -EINVAL; - } - if (verify_area(VERIFY_READ,(struct i2c_smbus_ioctl_data *) arg, - sizeof(struct i2c_smbus_ioctl_data))) { + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + data_arg.read_write, + data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { #ifdef DEBUG - printk("i2c-dev.o: invalid argument pointer (%ld) " - "in IOCTL I2C_SMBUS.\n", arg); + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); #endif - return -EINVAL; - } - copy_from_user(&data_arg,(struct i2c_smbus_ioctl_data *) arg, - sizeof(struct i2c_smbus_ioctl_data)); - if ((data_arg.size != I2C_SMBUS_BYTE) && - (data_arg.size != I2C_SMBUS_QUICK) && - (data_arg.size != I2C_SMBUS_BYTE_DATA) && - (data_arg.size != I2C_SMBUS_WORD_DATA) && - (data_arg.size != I2C_SMBUS_PROC_CALL) && - (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { -#ifdef DEBUG - printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.size); -#endif - return -EINVAL; - } - /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, - so the check is valid if size==I2C_SMBUS_QUICK too. */ - if ((data_arg.read_write != I2C_SMBUS_READ) && - (data_arg.read_write != I2C_SMBUS_WRITE)) { -#ifdef DEBUG - printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", - data_arg.read_write); -#endif - return -EINVAL; - } - - /* Note that command values are always valid! */ - - if ((data_arg.size == I2C_SMBUS_QUICK) || - ((data_arg.size == I2C_SMBUS_BYTE) && - (data_arg.read_write == I2C_SMBUS_WRITE))) - /* These are special: we do not use data */ - return i2c_smbus_xfer(client->adapter, client->addr, client->flags, - data_arg.read_write, data_arg.command, - data_arg.size, NULL); - - if (data_arg.data == NULL) { -#ifdef DEBUG - printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); -#endif - return -EINVAL; - } - - /* This seems unlogical but it is not: if the user wants to read a - value, we must write that value to user memory! */ - ver = ((data_arg.read_write == I2C_SMBUS_WRITE) && - (data_arg.size != I2C_SMBUS_PROC_CALL))?VERIFY_READ:VERIFY_WRITE; - - if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || (data_arg.size == I2C_SMBUS_BYTE)) - datasize = sizeof(data_arg.data->byte); - else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || - (data_arg.size == I2C_SMBUS_PROC_CALL)) - datasize = sizeof(data_arg.data->word); - else /* size == I2C_SMBUS_BLOCK_DATA */ - datasize = sizeof(data_arg.data->block); - - if (verify_area(ver,data_arg.data,datasize)) { -#ifdef DEBUG - printk("i2c-dev.o: invalid pointer data (%p) in ioctl I2C_SMBUS.\n", - data_arg.data); -#endif - return -EINVAL; - } - - if ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_WRITE)) - copy_from_user(&temp,data_arg.data,datasize); - res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, - data_arg.read_write, - data_arg.command,data_arg.size,&temp); - if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || - (data_arg.read_write == I2C_SMBUS_READ))) - copy_to_user(data_arg.data,&temp,datasize); - return res; - - default: - return i2c_control(client,cmd,arg); - } - return 0; + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user_ret(&temp,data_arg.data,datasize, + -EFAULT); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user_ret(data_arg.data,&temp,datasize,-EFAULT); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; } int i2cdev_open (struct inode *inode, struct file *file) { - unsigned int minor = MINOR(inode->i_rdev); - struct i2c_client *client; + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; - if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { #ifdef DEBUG - printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n",minor); + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n", + minor); #endif - return -ENODEV; - } + return -ENODEV; + } - /* Note that we here allocate a client for later use, but we will *not* - register this client! Yes, this is safe. No, it is not very clean. */ - if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) - return -ENOMEM; - memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); - client->adapter = i2cdev_adaps[minor]; - file->private_data = client; + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; - i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); - MOD_INC_USE_COUNT; + if (i2cdev_adaps[minor]->inc_use) + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; #ifdef DEBUG - printk("i2c-dev.o: opened i2c-%d\n",minor); + printk("i2c-dev.o: opened i2c-%d\n",minor); #endif - return 0; + return 0; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) static int i2cdev_release (struct inode *inode, struct file *file) -#else -static void i2cdev_release (struct inode *inode, struct file *file) -#endif { - unsigned int minor = MINOR(inode->i_rdev); - kfree(file->private_data); - file->private_data=NULL; + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; #ifdef DEBUG - printk("i2c-dev.o: Closed: i2c-%d\n", minor); -#endif - MOD_DEC_USE_COUNT; - i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,31)) - return 0; + printk("i2c-dev.o: Closed: i2c-%d\n", minor); #endif + MOD_DEC_USE_COUNT; + if (i2cdev_adaps[minor]->dec_use) + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); + return 0; } int i2cdev_attach_adapter(struct i2c_adapter *adap) { - int i; - - if ((i = i2c_adapter_id(adap)) < 0) { - printk("i2c-dev.o: Unknown adapter ?!?\n"); - return -ENODEV; - } - if (i >= I2CDEV_ADAPS_MAX) { - printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); - return -ENODEV; - } - - if (! i2cdev_adaps[i]) { - i2cdev_adaps[i] = adap; - printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); - } else { - i2cdev_adaps[i] = NULL; + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; #ifdef DEBUG - printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); #endif - } + } - return 0; + return 0; } int i2cdev_detach_client(struct i2c_client *client) { - return 0; + return 0; } static int i2cdev_command(struct i2c_client *client, unsigned int cmd, void *arg) { - return -1; + return -1; } int __init i2c_dev_init(void) { - int res; - - printk("i2c-dev.o: i2c /dev entries driver module\n"); - - i2cdev_initialized = 0; - if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { - printk("i2c-dev.o: unable to get major %d for i2c bus\n",I2C_MAJOR); - return -EIO; - } - i2cdev_initialized ++; - - if ((res = i2c_add_driver(&i2cdev_driver))) { - printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); - i2cdev_cleanup(); - return res; - } - i2cdev_initialized ++; - return 0; + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n", + I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; } int i2cdev_cleanup(void) { - int res; - - if (i2cdev_initialized >= 2) { - if ((res = i2c_del_driver(&i2cdev_driver))) { - printk("i2c-dev.o: Driver deregistration failed, " - "module not removed.\n"); - return res; - } - i2cdev_initialized ++; - } - - if (i2cdev_initialized >= 1) { - if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { - printk("i2c-dev.o: unable to release major %d for i2c bus\n",I2C_MAJOR); - return res; - } - i2cdev_initialized --; - } - return 0; + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n", + I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; } EXPORT_NO_SYMBOLS; @@ -545,12 +428,12 @@ MODULE_DESCRIPTION("I2C /dev entries driver"); int init_module(void) { - return i2c_dev_init(); + return i2c_dev_init(); } int cleanup_module(void) { - return i2cdev_cleanup(); + return i2cdev_cleanup(); } #endif /* def MODULE */ diff --git a/drivers/i2c/i2c-elektor.c b/drivers/i2c/i2c-elektor.c index fb965df0f713..ad7b9d41c700 100644 --- a/drivers/i2c/i2c-elektor.c +++ b/drivers/i2c/i2c-elektor.c @@ -22,7 +22,7 @@ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ -/* $Id: i2c-elektor.c,v 1.13 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-elektor.c,v 1.16 2000/01/24 02:06:33 mds Exp $ */ #include #include @@ -30,24 +30,10 @@ #include #include #include -#if LINUX_VERSION_CODE >= 0x020135 #include -#else -#define __init -#endif #include #include -/* 2.0.0 kernel compatibility */ -#if LINUX_VERSION_CODE < 0x020100 -#define MODULE_AUTHOR(noone) -#define MODULE_DESCRIPTION(none) -#define MODULE_PARM(no,param) -#define MODULE_PARM_DESC(no,description) -#define EXPORT_SYMBOL(noexport) -#define EXPORT_NO_SYMBOLS -#endif - #include #include #include @@ -87,56 +73,48 @@ static int pcf_pending; static void pcf_isa_setbyte(void *data, int ctl, int val) { - if (ctl) { - if (gpi.pi_irq > 0) { - DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val|I2C_PCF_ENI)); - outb(val | I2C_PCF_ENI, CTRL); - } else { - DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); - outb(val, CTRL); - } - } else { - DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); - outb(val, DATA); - } + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", + val|I2C_PCF_ENI)); + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write control 0x%x\n", val)); + outb(val, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write data 0x%x\n", val)); + outb(val, DATA); + } } static int pcf_isa_getbyte(void *data, int ctl) { - int val; - - if (ctl) { - val = inb(CTRL); - DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); - } else { - val = inb(DATA); - DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); - } - return (val); + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read control 0x%x\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read data 0x%x\n", val)); + } + return (val); } static int pcf_isa_getown(void *data) { - return (gpi.pi_own); + return (gpi.pi_own); } static int pcf_isa_getclock(void *data) { - return (gpi.pi_clock); + return (gpi.pi_clock); } -#if LINUX_VERSION_CODE < 0x02017f -static void schedule_timeout(int j) -{ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + j; - schedule(); -} -#endif - #if 0 static void pcf_isa_sleep(unsigned long timeout) { @@ -147,64 +125,53 @@ static void pcf_isa_sleep(unsigned long timeout) static void pcf_isa_waitforpin(void) { - int timeout = 2; - - if (gpi.pi_irq > 0) { - cli(); - if (pcf_pending == 0) { -#if LINUX_VERSION_CODE < 0x02017f - current->timeout = jiffies + timeout * HZ; - interruptible_sleep_on(&pcf_wait); -#else - interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); -#endif - } - else - pcf_pending = 0; - sti(); -#if LINUX_VERSION_CODE < 0x02017f - current->timeout = 0; -#endif - } - else { - udelay(100); - } + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); + } else + pcf_pending = 0; + sti(); + } else { + udelay(100); + } } static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { - - pcf_pending = 1; - wake_up_interruptible(&pcf_wait); + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); } static int pcf_isa_init(void) { - if (check_region(gpi.pi_base, 2) < 0 ) { - return -ENODEV; - } else { - request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); - } - if (gpi.pi_irq > 0) { - if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) < 0) { - printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); - gpi.pi_irq = 0; - } - else - enable_irq(gpi.pi_irq); - } - return 0; + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) + < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } else + enable_irq(gpi.pi_irq); + } + return 0; } static void pcf_isa_exit(void) { - if (gpi.pi_irq > 0) { - disable_irq(gpi.pi_irq); - free_irq(gpi.pi_irq, 0); - } - release_region(gpi.pi_base , 2); + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); } @@ -222,14 +189,14 @@ static int pcf_isa_unreg(struct i2c_client *client) static void pcf_isa_inc_use(struct i2c_adapter *adap) { #ifdef MODULE - MOD_INC_USE_COUNT; + MOD_INC_USE_COUNT; #endif } static void pcf_isa_dec_use(struct i2c_adapter *adap) { #ifdef MODULE - MOD_DEC_USE_COUNT; + MOD_DEC_USE_COUNT; #endif } @@ -262,41 +229,41 @@ static struct i2c_adapter pcf_isa_ops = { int __init i2c_pcfisa_init(void) { - struct i2c_pcf_isa *pisa = &gpi; + struct i2c_pcf_isa *pisa = &gpi; - printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); - if (base == 0) - pisa->pi_base = DEFAULT_BASE; - else - pisa->pi_base = base; + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; - if (irq == 0) - pisa->pi_irq = DEFAULT_IRQ; - else - pisa->pi_irq = irq; + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; - if (clock == 0) - pisa->pi_clock = DEFAULT_CLOCK; - else - pisa->pi_clock = clock; + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; - if (own == 0) - pisa->pi_own = DEFAULT_OWN; - else - pisa->pi_own = own; + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; - pcf_isa_data.data = (void *)pisa; + pcf_isa_data.data = (void *)pisa; #if (LINUX_VERSION_CODE >= 0x020301) - init_waitqueue_head(&pcf_wait); + init_waitqueue_head(&pcf_wait); #endif - if (pcf_isa_init() == 0) { - if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) - return -ENODEV; - } else { - return -ENODEV; - } - printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); - return 0; + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; } @@ -313,7 +280,7 @@ MODULE_PARM(own, "i"); int init_module(void) { - return i2c_pcfisa_init(); + return i2c_pcfisa_init(); } void cleanup_module(void) diff --git a/drivers/i2c/i2c-pcf8584.h b/drivers/i2c/i2c-pcf8584.h index 7dabbd9a91cb..9f0874012f91 100644 --- a/drivers/i2c/i2c-pcf8584.h +++ b/drivers/i2c/i2c-pcf8584.h @@ -21,20 +21,20 @@ /* With some changes from Frodo Looijaard */ -/* $Id: i2c-pcf8584.h,v 1.2 1999/12/21 23:45:58 frodo Exp $ */ +/* $Id: i2c-pcf8584.h,v 1.3 2000/01/18 23:54:07 frodo Exp $ */ #ifndef I2C_PCF8584_H #define I2C_PCF8584_H 1 /* ----- Control register bits ---------------------------------------- */ -#define I2C_PCF_PIN 0x80 -#define I2C_PCF_ESO 0x40 -#define I2C_PCF_ES1 0x20 -#define I2C_PCF_ES2 0x10 -#define I2C_PCF_ENI 0x08 -#define I2C_PCF_STA 0x04 -#define I2C_PCF_STO 0x02 -#define I2C_PCF_ACK 0x01 +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 #define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) #define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) @@ -45,7 +45,7 @@ /*#define I2C_PCF_PIN 0x80 as above*/ #define I2C_PCF_INI 0x40 /* 1 if not initialized */ -#define I2C_PCF_STS 0x20 +#define I2C_PCF_STS 0x20 #define I2C_PCF_BER 0x10 #define I2C_PCF_AD0 0x08 #define I2C_PCF_LRB 0x08 @@ -54,17 +54,17 @@ #define I2C_PCF_BB 0x01 /* ----- Chip clock frequencies --------------------------------------- */ -#define I2C_PCF_CLK3 0x00 -#define I2C_PCF_CLK443 0x10 -#define I2C_PCF_CLK6 0x14 -#define I2C_PCF_CLK8 0x18 -#define I2C_PCF_CLK12 0x1c +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c /* ----- transmission frequencies ------------------------------------- */ -#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ -#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ -#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ -#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ /* ----- Access to internal registers according to ES1,ES2 ------------ */ diff --git a/drivers/ieee1394/ieee1394_syms.c b/drivers/ieee1394/ieee1394_syms.c index e4d92fa0f3bd..ee4bde182d7e 100644 --- a/drivers/ieee1394/ieee1394_syms.c +++ b/drivers/ieee1394/ieee1394_syms.c @@ -39,6 +39,7 @@ EXPORT_SYMBOL(hpsb_make_readqpacket); EXPORT_SYMBOL(hpsb_make_readbpacket); EXPORT_SYMBOL(hpsb_make_writeqpacket); EXPORT_SYMBOL(hpsb_make_writebpacket); +EXPORT_SYMBOL(hpsb_make_lockpacket); EXPORT_SYMBOL(hpsb_read); EXPORT_SYMBOL(hpsb_write); EXPORT_SYMBOL(hpsb_lock); diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index c50bce62aa4d..e2eeffc682da 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -329,6 +329,30 @@ struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, return p; } +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode) +{ + struct hpsb_packet *p; + + p = alloc_hpsb_packet(8); + if (!p) return NULL; + + p->host = host; + p->tlabel = get_tlabel(host, node, 1); + p->node_id = node; + + switch (extcode) { + case EXTCODE_FETCH_ADD: + case EXTCODE_LITTLE_ADD: + fill_async_lock(p, addr, extcode, 4); + break; + default: + fill_async_lock(p, addr, extcode, 8); + break; + } + + return p; +} /* * FIXME - these functions should probably read from / write to user space to diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index 136d4f7d95bc..ebec602a9d1f 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -39,6 +39,9 @@ struct hpsb_packet *hpsb_make_writeqpacket(struct hpsb_host *host, struct hpsb_packet *hpsb_make_writebpacket(struct hpsb_host *host, nodeid_t node, u64 addr, size_t length); +struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node, + u64 addr, int extcode); + /* * hpsb_packet_success - Make sense of the ack and reply codes and diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index f39a8de869f3..87344ed9e644 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -1,5 +1,5 @@ /* - * ti_ohci1394.c - Texas Instruments Ohci1394 driver + * ohci1394.c - driver for OHCI 1394 boards * Copyright (C)1999,2000 Sebastien Rougeaux * Gord Peters * @@ -40,8 +40,6 @@ #include "ieee1394_core.h" #include "ohci1394.h" -#undef CONFIG_PROC_FS - /* print general (card independent) information */ #define PRINT_G(level, fmt, args...) \ printk(level "ohci1394: " fmt "\n" , ## args) @@ -68,7 +66,6 @@ static int init_driver(void); * IEEE-1394 functionality section * ***********************************/ - #if 0 /* not needed at this time */ static int get_phy_reg(struct ti_ohci *ohci, int addr) { @@ -324,31 +321,53 @@ static int ohci_initialize(struct hpsb_host *host) } /* Initialize AR dma */ - ohci->AR_resp_prg->control=0x283C << 16 | AR_RESP_BUF_SIZE; - ohci->AR_resp_prg->address=virt_to_bus(ohci->AR_resp_buf); - ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; - PRINT(KERN_INFO, ohci->id, "AR control: %x", - ohci->AR_resp_prg->control); - PRINT(KERN_INFO, ohci->id, "AR status: %x %d", - ohci->AR_resp_prg->status & 0xffff, - ohci->AR_resp_prg->status & 0xffff); - - /* Tell the controller where the AR program is */ - reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, - virt_to_bus(ohci->AR_resp_prg)|0x00000001); + /* make sure the context isn't running, dead, or active */ + if (!(reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) & 0x00008F00)) { -#if 1 - /* Accept phy packets into AR request context */ - reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); -#endif + /* initialize AR program */ + for (i= 0; i < AR_RESP_NUM_DESC; i++) { - /* Run AR context */ - reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000); + /* end of descriptor list? */ + if ((i + 1) < AR_RESP_NUM_DESC) { + ohci->AR_resp_prg[i]->control= + (0x283C << 16) | AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[i]->branchAddress= + (virt_to_bus(ohci->AR_resp_prg[i + 1]) + & 0xfffffff0) | 0x1; + } else { + ohci->AR_resp_prg[i]->control= + (0x283C << 16) | AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[i]->branchAddress= + (virt_to_bus(ohci->AR_resp_prg[0]) + & 0xfffffff0) | 0x1; + } + + ohci->AR_resp_prg[i]->address= + virt_to_bus(ohci->AR_resp_buf[i]); + ohci->AR_resp_prg[i]->status= AR_RESP_BUF_SIZE; + } + + /* Tell the controller where the first AR program is */ + reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, + virt_to_bus(ohci->AR_resp_prg[0]) | 0x1 ); + + /* Accept phy packets into AR request context */ + reg_write(ohci, OHCI1394_LinkControlSet, 0x00000400); + + /* Run AR context */ + reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x00008000); + } + + /* Specify AT retries */ + reg_write(ohci, OHCI1394_ATRetries, + OHCI1394_MAX_AT_REQ_RETRIES | + (OHCI1394_MAX_AT_RESP_RETRIES<<4) | + (OHCI1394_MAX_PHYS_RESP_RETRIES<<8)); #ifndef __BIG_ENDIAN - reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); -#else reg_write(ohci, OHCI1394_HCControlClear, 0x40000000); +#else + reg_write(ohci, OHCI1394_HCControlSet, 0x40000000); #endif /* Enable interrupts */ @@ -386,8 +405,9 @@ static void send_next_async(struct ti_ohci *ohci) int i=0; struct hpsb_packet *packet = ohci->async_queue; struct dma_cmd prg; +#if 0 quadlet_t *ptr = (quadlet_t *)ohci->AT_req_prg; - +#endif //HPSB_TRACE(); /* stop the channel program if it's still running */ @@ -435,14 +455,14 @@ static void send_next_async(struct ti_ohci *ohci) prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); memcpy(ohci->AT_req_prg + 1, packet->header, 16); - +#if 0 PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); PRINT(KERN_INFO, ohci->id, "header: %08x %08x %08x %08x", *(ptr+4), *(ptr+5), *(ptr+6), *(ptr+7)); - +#endif reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } @@ -455,11 +475,11 @@ static void send_next_async(struct ti_ohci *ohci) prg.branchAddress = 0; prg.status = 0; memcpy(ohci->AT_req_prg, &prg, 16); - +#if 0 PRINT(KERN_INFO, ohci->id, "dma_cmd: %08x %08x %08x %08x", *ptr, *(ptr+1), *(ptr+2), *(ptr+3)); - +#endif reg_write(ohci, OHCI1394_AsReqTrCommandPtr, virt_to_bus(ohci->AT_req_prg)|0x2); } @@ -638,6 +658,26 @@ static int ohci_devctl(struct hpsb_host *host, enum devctl_cmd cmd, int arg) * Global stuff (interrupt handler, init/shutdown code) * ********************************************************/ +static void stop_ar_resp_context(struct ti_ohci *ohci, char *msg) +{ + int i=0; + + /* stop the channel program if it's still running */ + reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000); + + /* Wait until it effectively stops */ + while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) + & 0x400) { + i++; + if (i>5000) { + PRINT(KERN_ERR, ohci->id, + "runaway loop in Dma Ar Resp. bailing out..."); + break; + } + } + PRINT(KERN_ERR, ohci->id, "%s\n async response receive dma stopped\n", msg); +} + static void ohci_irq_handler(int irq, void *dev_id, struct pt_regs *regs_are_unused) { @@ -656,15 +696,14 @@ static void ohci_irq_handler(int irq, void *dev_id, event,reg_read(ohci, OHCI1394_IntMaskSet)); */ if (event & OHCI1394_busReset) { +#if 0 PRINT(KERN_INFO, ohci->id, "bus reset interrupt"); +#endif if (!host->in_bus_reset) { hpsb_bus_reset(host); } ohci->NumBusResets++; } - if (event & OHCI1394_reqTxComplete) { - PRINT(KERN_INFO, ohci->id, "reqTxComplete int received"); - } if (event & OHCI1394_RQPkt) { PRINT(KERN_INFO, ohci->id, "RQPkt int received"); } @@ -673,58 +712,55 @@ static void ohci_irq_handler(int irq, void *dev_id, reg_read(ohci, OHCI1394_AsReqRcvContextControlSet)); } if (event & OHCI1394_RSPkt) { - int rcv_bytes; - int i=0; - - /* we calculate the number of received bytes from the - residual count field */ - rcv_bytes = AR_RESP_BUF_SIZE - - (ohci->AR_resp_prg->status & 0xFFFF); - - PRINT(KERN_INFO, ohci->id, "AR_status 0x%x %d, %d bytes read", - ohci->AR_resp_prg->status, - ohci->AR_resp_prg->status & 0xffff, - rcv_bytes); - - ohci->AR_resp_active = 0; - - if ((ohci->AR_resp_prg->status & 0x84000000) - && (ohci->AR_resp_prg->status & 0xFFFF) >= 8 ) { - hpsb_packet_received(host, ohci->AR_resp_buf, - rcv_bytes); - } else { - //HPSB_TRACE(); - PRINT(KERN_ERR, ohci->id, - "AR resp DMA program status value 0x%x is incorrect!", - ohci->AR_resp_prg->status); - } - - - /* --------------- FIXME --------------------------------- - this is a complete hack... we stop the dma prg - and start it again so as to reset the dma buffer address - Very slow, very bad design... to change ASAP */ - - /* stop the channel program if it's still running */ - reg_write(ohci, OHCI1394_AsRspRcvContextControlClear, 0x8000); - - /* Wait until it effectively stops */ - while (reg_read(ohci, OHCI1394_AsRspRcvContextControlSet) - & 0x400) { - i++; - if (i>5000) { - PRINT(KERN_ERR, ohci->id, - "runaway loop in DmaAT. bailing out..."); - break; + unsigned int idx,offset,rescount; + + spin_lock(&ohci->AR_resp_lock); + + idx = ohci->AR_resp_buf_th_ind; + offset = ohci->AR_resp_buf_th_offset; + + rescount = ohci->AR_resp_prg[idx]->status&0xffff; + ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE - rescount - offset; + offset = AR_RESP_BUF_SIZE - rescount; + + if (!rescount) { /* We cross a buffer boundary */ + idx = (idx+1) % AR_RESP_NUM_DESC; + +#if 0 + /* This bit of code does not work */ + /* Let's see how many bytes were written in the async response + receive buf since last interrupt. This is done by finding + the next active context (See OHCI Spec p91) */ + while (ohci->AR_resp_bytes_left <= AR_RESP_TOTAL_BUF_SIZE) { + if (ohci->AR_resp_prg[idx]->status&0x04000000) break; + idx = (idx+1) % AR_RESP_NUM_DESC; + PRINT(KERN_INFO,ohci->id,"crossing more than one buffer boundary !!!"); + ohci->AR_resp_bytes_left += AR_RESP_BUF_SIZE; } +#endif + /* ASSUMPTION: only one buffer boundary is crossed */ + rescount = ohci->AR_resp_prg[idx]->status&0xffff; + offset = AR_RESP_BUF_SIZE - rescount; + ohci->AR_resp_bytes_left += offset; } + if (offset==AR_RESP_BUF_SIZE) { + offset=0; + idx = (idx+1) % AR_RESP_NUM_DESC; + } + ohci->AR_resp_buf_th_ind = idx; + ohci->AR_resp_buf_th_offset = offset; - reg_write(ohci, OHCI1394_AsRspRcvCommandPtr, - virt_to_bus(ohci->AR_resp_prg)|0x00000001); - ohci->AR_resp_prg->status=AR_RESP_BUF_SIZE; - reg_write(ohci, OHCI1394_AsRspRcvContextControlSet, 0x8000); + /* is buffer processing too slow? (all buffers used) */ + if (ohci->AR_resp_bytes_left > AR_RESP_TOTAL_BUF_SIZE) { + stop_ar_resp_context(ohci,"async response receive processing too slow"); + spin_unlock(&ohci->AR_resp_lock); + return; + } + spin_unlock(&ohci->AR_resp_lock); - /* ---------------- end of FIXME --------------------------*/ + /* queue bottom half in immediate queue */ + queue_task(&ohci->AR_resp_pdl_task, &tq_immediate); + mark_bh(IMMEDIATE_BH); } if (event & OHCI1394_isochRx) { quadlet_t isoRecvIntEvent; @@ -826,8 +862,10 @@ static void ohci_irq_handler(int irq, void *dev_id, send_next_async(ohci); } spin_unlock(&ohci->async_queue_lock); +#if 0 PRINT(KERN_INFO,ohci->id, "packet sent with ack code %d",ack); +#endif hpsb_packet_sent(host, packet, ack); } else PRINT(KERN_INFO,ohci->id, @@ -839,6 +877,129 @@ static void ohci_irq_handler(int irq, void *dev_id, ohci->NumInterrupts++; } + +/* This is the bottom half that processes async response receive descriptor buffers. */ +static void ohci_ar_resp_proc_desc(void *data) +{ + quadlet_t *buf_ptr; + char *split_ptr; + unsigned int split_left; + struct ti_ohci *ohci= (struct ti_ohci*)data; + unsigned int packet_length; + unsigned int idx,offset,tcode; + unsigned long flags; + char msg[256]; + + spin_lock_irqsave(&ohci->AR_resp_lock, flags); + + idx = ohci->AR_resp_buf_bh_ind; + offset = ohci->AR_resp_buf_bh_offset; + + buf_ptr = ohci->AR_resp_buf[idx]; + buf_ptr += offset/4; + + while(ohci->AR_resp_bytes_left > 0) { + + /* check to see if a fatal error occurred */ + if ((ohci->AR_resp_prg[idx]->status >> 16) & 0x800) { + sprintf(msg,"fatal async response receive error -- status is %d", + ohci->AR_resp_prg[idx]->status & 0x1F); + stop_ar_resp_context(ohci, msg); + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); + return; + } + + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); + + /* Let's see what kind of packet is in there */ + tcode = (buf_ptr[0]>>4)&0xf; + if (tcode==2) /* no-data receive */ + packet_length=16; + else if (tcode==6) /* quadlet receive */ + packet_length=20; + else if (tcode==7) { /* block receive */ + /* Where is the data length ? */ + if (offset+12>=AR_RESP_BUF_SIZE) + packet_length=(ohci->AR_resp_buf[(idx+1)%AR_RESP_NUM_DESC] + [3-(AR_RESP_BUF_SIZE-offset)/4]>>16)+20; + else + packet_length=(buf_ptr[3]>>16)+20; + if (packet_length % 4) + packet_length += 4 - (packet_length % 4); + } + else /* something is wrong */ { + sprintf(msg,"unexpected packet tcode %d in async response receive buffer",tcode); + stop_ar_resp_context(ohci,msg); + return; + } + if ((offset+packet_length)>AR_RESP_BUF_SIZE) { + /* we have a split packet */ + if (packet_length>AR_RESP_SPLIT_PACKET_BUF_SIZE) { + sprintf(msg,"packet size %d bytes exceed split packet buffer size %d bytes", + packet_length,AR_RESP_SPLIT_PACKET_BUF_SIZE); + stop_ar_resp_context(ohci, msg); + return; + } + split_left = packet_length; + split_ptr = (char *)ohci->AR_resp_spb; + while (split_left>0) { + memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE-offset); + split_left -= AR_RESP_BUF_SIZE-offset; + split_ptr += AR_RESP_BUF_SIZE-offset; + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + offset=0; + while (split_left >= AR_RESP_BUF_SIZE) { + memcpy(split_ptr,buf_ptr,AR_RESP_BUF_SIZE); + split_ptr += AR_RESP_BUF_SIZE; + split_left -= AR_RESP_BUF_SIZE; + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + } + if (split_left>0) { + memcpy(split_ptr,buf_ptr,split_left); + offset = split_left; + split_left=0; + buf_ptr += split_left/4; + } + } +#if 0 + PRINT(KERN_INFO,ohci->id,"AR resp: received split packet tcode=%d length=%d", + tcode,packet_length); +#endif + hpsb_packet_received(ohci->host, ohci->AR_resp_spb, packet_length); + ohci->AR_resp_bytes_left -= packet_length; + } + else { +#if 0 + PRINT(KERN_INFO,ohci->id,"AR resp: received packet tcode=%d length=%d", + tcode,packet_length); +#endif + hpsb_packet_received(ohci->host, buf_ptr, packet_length); + offset += packet_length; + buf_ptr += packet_length/4; + ohci->AR_resp_bytes_left -= packet_length; + if (offset==AR_RESP_BUF_SIZE) { + ohci->AR_resp_prg[idx]->status = AR_RESP_BUF_SIZE; + idx = (idx+1) % AR_RESP_NUM_DESC; + buf_ptr = ohci->AR_resp_buf[idx]; + offset=0; + } + } + + } + + if (ohci->AR_resp_bytes_left<0) + stop_ar_resp_context(ohci, "Sync problem in AR resp dma buffer"); + + ohci->AR_resp_buf_bh_ind = idx; + ohci->AR_resp_buf_bh_offset = offset; + + spin_unlock_irqrestore(&ohci->AR_resp_lock, flags); +} + /* This is the bottom half that processes iso receive descriptor buffers. */ static void ohci_ir_proc_desc(void *data) { @@ -1023,23 +1184,61 @@ static int add_card(struct pci_dev *dev) FAIL("failed to allocate DMA buffer for self-id packets"); } - /* AR dma buffer allocation */ - ohci->AR_resp_buf = kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL); - if (ohci->AR_resp_buf != NULL) { - memset(ohci->AR_resp_buf, 0, AR_RESP_BUF_SIZE); - } else { - FAIL("failed to allocate AR response DMA buffer"); + /* AR dma buffer and program allocation */ + ohci->AR_resp_buf= + kmalloc(AR_RESP_NUM_DESC * sizeof(quadlet_t*), + GFP_KERNEL); + + if (ohci->AR_resp_buf == NULL) { + FAIL("failed to allocate AR response receive DMA buffer"); } - /* AR dma program allocation */ - ohci->AR_resp_prg = (struct dma_cmd *) kmalloc(AR_RESP_PRG_SIZE, - GFP_KERNEL); - if (ohci->AR_resp_prg != NULL) { - memset(ohci->AR_resp_prg, 0, AR_RESP_PRG_SIZE); - } else { - FAIL("failed to allocate AR response DMA program"); + ohci->AR_resp_prg= + kmalloc(AR_RESP_NUM_DESC * sizeof(struct dma_cmd*), + GFP_KERNEL); + + if (ohci->AR_resp_prg == NULL) { + FAIL("failed to allocate AR response receive DMA program"); + } + + ohci->AR_resp_spb= kmalloc(AR_RESP_SPLIT_PACKET_BUF_SIZE, GFP_KERNEL); + + if (ohci->AR_resp_spb == NULL) { + FAIL("failed to allocate AR response split packet buffer"); + } + + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + ohci->AR_resp_buf[i]= kmalloc(AR_RESP_BUF_SIZE, GFP_KERNEL); + + if (ohci->AR_resp_buf[i] != NULL) { + memset(ohci->AR_resp_buf[i], 0, AR_RESP_BUF_SIZE); + } else { + FAIL("failed to allocate AR response DMA buffer"); + } + + ohci->AR_resp_prg[i]= kmalloc(sizeof(struct dma_cmd), + GFP_KERNEL); + + if (ohci->AR_resp_prg[i] != NULL) { + memset(ohci->AR_resp_prg[i], 0, + sizeof(struct dma_cmd)); + } else { + FAIL("failed to allocate AR response DMA buffer"); + } + } + ohci->AR_resp_buf_th_ind = 0; + ohci->AR_resp_buf_th_offset = 0; + ohci->AR_resp_buf_bh_ind = 0; + ohci->AR_resp_buf_bh_offset = 0; + ohci->AR_resp_bytes_left = 0; + spin_lock_init(&ohci->AR_resp_lock); + + /* initialize AR response receive task */ + ohci->AR_resp_pdl_task.routine= ohci_ar_resp_proc_desc; + ohci->AR_resp_pdl_task.data= (void*)ohci; + /* AT dma program allocation */ ohci->AT_req_prg = (struct dma_cmd *) kmalloc(AT_REQ_PRG_SIZE, GFP_KERNEL); @@ -1129,8 +1328,12 @@ static int add_card(struct pci_dev *dev) p += sprintf(p,fmt,reg_read(ohci, reg0),\ reg_read(ohci, reg1),reg_read(ohci, reg2)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +static int ohci_get_status(char *buf) +#else int ohci_get_info(char *buf, char **start, off_t fpos, int length, int dummy) +#endif { struct ti_ohci *ohci=&cards[0]; struct hpsb_host *host=ohci->host; @@ -1165,15 +1368,23 @@ int ohci_get_info(char *buf, char **start, off_t fpos, host->is_irm ? "iso_res_mgr" : "", host->is_busmgr ? "bus_mgr" : ""); - p += sprintf(p,"\n### ohci data ###\n"); - p += sprintf(p,"AR_resp_buf : %p AR_resp_prg: %p\n", - ohci->AR_resp_buf, ohci->AR_resp_prg); - + p += sprintf(p,"\n---Iso Receive DMA---\n"); for (i= 0; i < IR_NUM_DESC; i++) { p += sprintf(p, "IR_recv_buf[%d] : %p IR_recv_prg[%d]: %p\n", i, ohci->IR_recv_buf[i], i, ohci->IR_recv_prg[i]); } + + p += sprintf(p,"\n---Async Reponse Receive DMA---\n"); + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + p += sprintf(p, "AR_resp_buf[%d] : %p AR_resp_prg[%d]: %p\n", + i, ohci->AR_resp_buf[i], i, ohci->AR_resp_prg[i]); + } + p += sprintf(p, "Current AR resp buf in irq handler: %d offset: %d\n", + ohci->AR_resp_buf_th_ind,ohci->AR_resp_buf_th_offset); + p += sprintf(p, "Current AR resp buf in bottom half: %d offset: %d\n", + ohci->AR_resp_buf_bh_ind,ohci->AR_resp_buf_bh_offset); + /* ----- Register Dump ----- */ p += sprintf(p,"\n### HC Register dump ###\n"); SR("Version : %08x GUID_ROM : %08x ATRetries : %08x\n", @@ -1242,12 +1453,27 @@ int ohci_get_info(char *buf, char **start, off_t fpos, phyreg&0x3f); #endif +#if 0 p += sprintf(p,"AR_resp_prg ctrl: %08x\n",ohci->AR_resp_prg->control); p += sprintf(p,"AR_resp_prg status: %08x\n",ohci->AR_resp_prg->status); +#endif return p - buf; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) +static int ohci1394_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = ohci_get_status(page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} +#else struct proc_dir_entry ohci_proc_entry = { 0, /* Inode number - dynamic */ @@ -1261,18 +1487,36 @@ struct proc_dir_entry ohci_proc_entry = ohci_get_info, /* The read function for this file */ NULL }; -#endif +#endif /* LINUX_VERSION_CODE */ +#endif /* CONFIG_PROC_FS */ static void remove_card(struct ti_ohci *ohci) { if (ohci->registers) iounmap(ohci->registers); - if (ohci->AR_resp_buf) - kfree(ohci->AR_resp_buf); - if (ohci->AR_resp_prg) - kfree(ohci->AR_resp_prg); + + /* Free AR response buffers and programs */ + if (ohci->AR_resp_buf) { + int i; + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + kfree(ohci->AR_resp_buf[i]); + } + kfree(ohci->AR_resp_buf); + } + if (ohci->AR_resp_prg) { + int i; + for (i= 0; i < AR_RESP_NUM_DESC; i++) { + kfree(ohci->AR_resp_prg[i]); + } + kfree(ohci->AR_resp_prg); + } + kfree(ohci->AR_resp_spb); + + /* Free AT request buffer and program */ if (ohci->AT_req_prg) kfree(ohci->AT_req_prg); + + /* Free Iso receive buffers and programs */ if (ohci->IR_recv_buf) { int i; for (i= 0; i < IR_NUM_DESC; i++) { @@ -1288,11 +1532,16 @@ static void remove_card(struct ti_ohci *ohci) kfree(ohci->IR_recv_prg); } kfree(ohci->IR_spb); + + /* Free self-id buffer */ if (ohci->self_id_buffer) kfree(ohci->self_id_buffer); + + /* Free config rom */ if (ohci->csr_config_rom) kfree(ohci->csr_config_rom); + /* Free the IRQ */ free_irq(ohci->dev->irq, ohci); ohci->state = 0; @@ -1327,10 +1576,14 @@ static int init_driver() } #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + create_proc_read_entry ("ohci1394", 0, NULL, ohci1394_read_proc, NULL); +#else if (proc_register(&proc_root, &ohci_proc_entry)) { PRINT_G(KERN_ERR, "unable to register proc file\n"); return -EIO; } +#endif #endif return 0; } @@ -1384,7 +1637,11 @@ void cleanup_module(void) { hpsb_unregister_lowlevel(get_ohci_template()); #ifdef CONFIG_PROC_FS +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + remove_proc_entry ("ohci1394", NULL); +#else proc_unregister(&proc_root, ohci_proc_entry.low_ino); +#endif #endif PRINT_G(KERN_INFO, "removed " OHCI1394_DRIVER_NAME " module\n"); } diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index c6d7f469a63a..35d8fa2afdb3 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -20,9 +20,15 @@ #define MAX_OHCI1394_CARDS 4 -#define AR_RESP_BUF_SIZE 4096 -#define AR_RESP_PRG_SIZE 256 -#define AT_REQ_PRG_SIZE 256 +#define OHCI1394_MAX_AT_REQ_RETRIES 1 +#define OHCI1394_MAX_AT_RESP_RETRIES 1 +#define OHCI1394_MAX_PHYS_RESP_RETRIES 4 + +#define AR_RESP_NUM_DESC 4 /* number of AR resp descriptors */ +#define AR_RESP_BUF_SIZE 4096 /* size of AR resp buffers */ +#define AR_RESP_SPLIT_PACKET_BUF_SIZE 256 /* split packet buffer */ +#define AR_RESP_TOTAL_BUF_SIZE (AR_RESP_BUF_SIZE * AR_RESP_NUM_DESC) +#define AT_REQ_PRG_SIZE 256 #define IR_RECV_BUF_SIZE 4096 /* 4096 bytes/buffer */ #define IR_SPLIT_PACKET_BUF_SIZE 8192 /* size of buffer for split packets */ @@ -49,8 +55,18 @@ struct ti_ohci { quadlet_t *csr_config_rom; /* buffer for csr config rom */ /* asynchronous receive */ - struct dma_cmd *AR_resp_prg; - quadlet_t *AR_resp_buf; + struct dma_cmd **AR_resp_prg; + quadlet_t **AR_resp_buf; + unsigned int AR_resp_buf_bh_ind; + unsigned int AR_resp_buf_bh_offset; + unsigned int AR_resp_buf_th_ind; + unsigned int AR_resp_buf_th_offset; + int AR_resp_bytes_left; + quadlet_t *AR_resp_spb; + spinlock_t AR_resp_lock; + + /* async receive task */ + struct tq_struct AR_resp_pdl_task; /* asynchronous transmit */ struct dma_cmd *AT_req_prg; diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index 596f952199e4..adc45896d43d 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -495,8 +495,8 @@ static int handle_local_request(struct file_info *fi, if (req->req.length == 8) { req->req.error = highlevel_lock(fi->host, req->data, - addr, req->data[0], - req->data[1], + addr, req->data[1], + req->data[0], req->req.misc); req->req.length = 4; } else { @@ -567,6 +567,32 @@ static int handle_remote_request(struct file_info *fi, break; case RAW1394_REQ_LOCK: + if ((req->req.misc != EXTCODE_FETCH_ADD) + && (req->req.misc != EXTCODE_LITTLE_ADD)) { + if (req->req.length != 4) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } else { + if (req->req.length != 8) { + req->req.error = RAW1394_ERROR_INVALID_ARG; + break; + } + } + + packet = hpsb_make_lockpacket(fi->host, node, addr, + req->req.misc); + if (!packet) return -ENOMEM; + + if (copy_from_user(packet->data, req->req.sendb, + req->req.length)) { + req->req.error = RAW1394_ERROR_MEMFAULT; + break; + } + + req->req.length = 4; + break; + case RAW1394_REQ_LOCK64: default: req->req.error = RAW1394_ERROR_STATE_ORDER; diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6df272d01388..d60ef92033f8 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -115,7 +115,7 @@ obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o obj-$(CONFIG_SEEQ8005) += seeq8005.o obj-$(CONFIG_ETHERTAP) += ethertap.o obj-$(CONFIG_NET_SB1000) += sb1000.o -obj-$(CONFIG_DAYNAPORT) += daynaport.o 8390.o +obj-$(CONFIG_MAC8390) += daynaport.o 8390.o obj-$(CONFIG_APNE) += apne.o 8390.o obj-$(CONFIG_PCMCIA_PCNET) += 8390.o obj-$(CONFIG_SHAPER) += shaper.o @@ -247,6 +247,8 @@ obj-$(CONFIG_IPDDP) += ipddp.o obj-$(CONFIG_RCPCI) += rcpci.o obj-$(CONFIG_MACE) += mace.o obj-$(CONFIG_MACSONIC) += macsonic.o +obj-$(CONFIG_MACMACE) += macmace.o +obj-$(CONFIG_MAC89x0) += mac89x0.o obj-$(CONFIG_BMAC) += bmac.o obj-$(CONFIG_NCR885E) += ncr885e.o obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o diff --git a/drivers/net/Space.c b/drivers/net/Space.c index e62ec4e21d22..e480562f9de7 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -105,7 +105,13 @@ extern int dec_lance_probe(struct net_device *); extern int mvme147lance_probe(struct net_device *dev); extern int tc515_probe(struct net_device *dev); extern int lance_probe(struct net_device *dev); -extern int mac_onboard_sonic_probe(struct net_device *dev); +extern int mace68k_probe(struct net_device *dev); +extern int macsonic_probe(struct net_device *dev); +extern int mac8390_probe(struct net_device *dev); +extern int mac89x0_probe(struct net_device *dev); + + /* Gigabit Ethernet adapters */ + extern int yellowfin_probe(struct net_device *dev); /* Detachable devices ("pocket adaptors") */ extern int atp_init(struct net_device *); @@ -355,8 +361,17 @@ struct devprobe m68k_probes[] __initdata = { #ifdef CONFIG_MVME147_NET /* MVME147 internal Ethernet */ {mvme147lance_probe, 0}, #endif -#ifdef CONFIG_MACSONIC /* Mac 68k Quadra builtin Ethernet */ - {mac_onboard_sonic_probe, 0}, +#ifdef CONFIG_MACMACE /* Mac 68k Quadra AV builtin Ethernet */ + {mace68k_probe, 0}, +#endif +#ifdef CONFIG_MACSONIC /* Mac SONIC-based Ethernet of all sorts */ + {macsonic_probe, 0}, +#endif +#ifdef CONFIG_MAC8390 /* NuBus NS8390-based cards */ + {mac8390_probe, 0}, +#endif +#ifdef CONFIG_MAC89x0 + {mac89x0_probe, 0}, #endif {NULL, 0}, }; diff --git a/drivers/net/cs89x0.h b/drivers/net/cs89x0.h index 42776088fd07..891ed33a2814 100644 --- a/drivers/net/cs89x0.h +++ b/drivers/net/cs89x0.h @@ -77,6 +77,12 @@ #define ADD_MASK 0x3000 /* Mask it use of the ADD_PORT register */ #define ADD_SIG 0x3000 /* Expected ID signature */ +/* On Macs, we only need use the ISA I/O stuff until we do MEMORY_ON */ +#ifdef CONFIG_MAC +#define LCSLOTBASE 0xfee00000 +#define MMIOBASE 0x40000 +#endif + #define CHIP_EISA_ID_SIG 0x630E /* Product ID Code for Crystal Chip (CS8900 spec 4.3) */ #ifdef IBMEIPKT diff --git a/drivers/net/daynaport.c b/drivers/net/daynaport.c index 02ebff8b6d67..724369bb8fcd 100644 --- a/drivers/net/daynaport.c +++ b/drivers/net/daynaport.c @@ -1,4 +1,4 @@ -/* mac_ns8390.c: A Macintosh 8390 based ethernet driver for linux. */ +/* daynaport.c: A Macintosh 8390 based ethernet driver for linux. */ /* Derived from code: @@ -15,14 +15,20 @@ The block output routines may be wrong for non Dayna cards - Reading MAC addresses -*/ + Fix this driver so that it will attempt to use the info + (i.e. iobase, iosize) given to it by the new and improved + NuBus code. + + Despite its misleading filename, this driver is not Dayna-specific + anymore. */ +/* Cabletron E6100 card support added by Tony Mantler (eek@escape.ca) April 1999 */ static const char *version = - "mac_ns8390.c:v0.01 7/5/97 Alan Cox (Alan.Cox@linux.org)\n"; + "daynaport.c: v0.02 1999-05-17 Alan Cox (Alan.Cox@linux.org) and others\n"; +static int version_printed = 0; #include - +#include #include #include #include @@ -31,20 +37,26 @@ static const char *version = #include #include #include +#include #include #include #include #include "8390.h" -int ns8390_probe1(struct net_device *dev, int word16, char *name, int id, int prom); +extern int console_loglevel; + +int ns8390_probe1(struct net_device *dev, int word16, char *name, int id, + int prom, struct nubus_dev *ndev); static int ns8390_open(struct net_device *dev); static void ns8390_no_reset(struct net_device *dev); static int ns8390_close_card(struct net_device *dev); +/* Interlan */ static void interlan_reset(struct net_device *dev); +/* Dayna */ static void dayna_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void dayna_block_input(struct net_device *dev, int count, @@ -52,6 +64,7 @@ static void dayna_block_input(struct net_device *dev, int count, static void dayna_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page); +/* Sane (32-bit chunk memory read/write) */ static void sane_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void sane_block_input(struct net_device *dev, int count, @@ -59,6 +72,7 @@ static void sane_block_input(struct net_device *dev, int count, static void sane_block_output(struct net_device *dev, int count, const unsigned char *buf, const int start_page); +/* Slow Sane (16-bit chunk memory read/write) */ static void slow_sane_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page); static void slow_sane_block_input(struct net_device *dev, int count, @@ -71,6 +85,10 @@ static void slow_sane_block_output(struct net_device *dev, int count, #define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */ #define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */ +#define CABLETRON_RX_START_PG 0x00 /* First page of RX buffer */ +#define CABLETRON_RX_STOP_PG 0x30 /* Last page +1 of RX ring */ +#define CABLETRON_TX_START_PG CABLETRON_RX_STOP_PG /* First page of TX buffer */ + #define DAYNA_MAC_BASE 0xf0007 #define DAYNA_8390_BASE 0x80000 /* 3 */ @@ -81,9 +99,14 @@ static void slow_sane_block_output(struct net_device *dev, int count, #define APPLE_8390_MEM 0xD0000 #define APPLE_MEMSIZE 8192 /* FIXME: need to dynamically check */ -#define KINETICS_8390_BASE 0x80003 -#define KINETICS_8390_MEM 0x00000 +#define KINETICS_MAC_BASE 0xf0004 /* first byte of each long */ +#define KINETICS_8390_BASE 0x80000 +#define KINETICS_8390_MEM 0x00000 /* first word of each long */ #define KINETICS_MEMSIZE 8192 /* FIXME: need to dynamically check */ +/*#define KINETICS_MEMSIZE (0x10000/2) * CSA: on the board I have, at least */ + +#define CABLETRON_8390_BASE 0x90000 +#define CABLETRON_8390_MEM 0x00000 static int test_8390(volatile char *ptr, int scale) { @@ -113,34 +136,59 @@ static int test_8390(volatile char *ptr, int scale) * Identify the species of NS8390 card/driver we need */ -#define NS8390_DAYNA 1 -#define NS8390_INTERLAN 2 -#define NS8390_KINETICS 3 -#define NS8390_APPLE 4 -#define NS8390_FARALLON 5 -#define NS8390_ASANTE 6 +enum mac8390_type { + NS8390_DAYNA, + NS8390_INTERLAN, + NS8390_KINETICS, + NS8390_APPLE, + NS8390_FARALLON, + NS8390_ASANTE, + NS8390_CABLETRON +}; -int ns8390_ident(struct nubus_type *nb) +int __init ns8390_ident(struct nubus_dev* ndev) { - /* It appears anything with a software type of 0 is an apple - compatible - even if the hardware matches others */ - - if(nb->DrSW==0x0001 || nb->DrSW==0x0109 || nb->DrSW==0x0000 || nb->DrSW==0x0100) - return NS8390_APPLE; - + /* This really needs to be tested and tested hard. */ + + /* Summary of what we know so far -- + * SW: 0x0104 -- asante, 16 bit, back4_offsets + * SW: 0x010b -- daynaport, 16 bit, fwrd4_offsets + * SW: 0x010c -- farallon, 16 bit, back4_offsets, no long word access + * SW: 0x011a -- focus, [no details yet] + * SW: ?????? -- interlan, 16 bit, back4_offsets, funny reset + * SW: ?????? -- kinetics, 8 bit, back4_offsets + * -- so i've this hypothesis going that says DrSW&1 says whether the + * map is forward or backwards -- and maybe DrSW&256 says what the + * register spacing is -- for all cards that report a DrSW in some + * range. + * This would allow the "apple compatible" driver to drive many + * seemingly different types of cards. More DrSW info is needed + * to investigate this properly. [CSA, 21-May-1999] + */ /* Dayna ex Kinetics board */ - if(nb->DrHW==0x0103) + if(ndev->dr_sw == NUBUS_DRSW_DAYNA) return NS8390_DAYNA; - - /* Asante board */ - if(nb->DrHW==0x0104) + if(ndev->dr_sw == NUBUS_DRSW_ASANTE) return NS8390_ASANTE; - if(nb->DrHW==0x0100) - return NS8390_INTERLAN; - if(nb->DrHW==0x0106) - return NS8390_KINETICS; - if(nb->DrSW==0x010C) + if(ndev->dr_sw == NUBUS_DRSW_FARALLON) /* farallon or sonic systems */ return NS8390_FARALLON; + if(ndev->dr_sw == NUBUS_DRSW_KINETICS) + return NS8390_KINETICS; + /* My ATI Engineering card with this combination crashes the */ + /* driver trying to xmit packets. Best not touch it for now. */ + /* - 1999-05-20 (funaho@jurai.org) */ + if(ndev->dr_sw == NUBUS_DRSW_FOCUS) + return -1; + + /* Check the HW on this one, because it shares the same DrSW as + the on-board SONIC chips */ + if(ndev->dr_hw == NUBUS_DRHW_CABLETRON) + return NS8390_CABLETRON; + /* does anyone have one of these? */ + if(ndev->dr_hw == NUBUS_DRHW_INTERLAN) + return NS8390_INTERLAN; + + /* FIXME: what do genuine Apple boards look like? */ return -1; } @@ -148,7 +196,7 @@ int ns8390_ident(struct nubus_type *nb) * Memory probe for 8390 cards */ -int apple_8390_mem_probe(volatile unsigned short *p) +int __init apple_8390_mem_probe(volatile unsigned short *p) { int i, j; /* @@ -192,61 +240,79 @@ int apple_8390_mem_probe(volatile unsigned short *p) /* * Probe for 8390 cards. * The ns8390_probe1() routine initializes the card and fills the - * station address field. On entry base_addr is set, irq is set - * (These come from the nubus probe code). dev->mem_start points + * station address field. + * + * The NuBus interface has changed! We now scan for these somewhat + * like how the PCI and Zorro drivers do. It's not clear whether + * this is actually better, but it makes things more consistent. + * + * dev->mem_start points * at the memory ring, dev->mem_end gives the end of it. */ -int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type *match) +int __init mac8390_probe(struct net_device *dev) { - struct net_device *dev; + static int slots = 0; volatile unsigned short *i; volatile unsigned char *p; int plen; int id; + static struct nubus_dev* ndev = NULL; + + /* Find the first card that hasn't already been seen */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) { + /* Have we seen it already? */ + if (slots & (1<board->slot)) + continue; + slots |= 1<board->slot; + + /* Is it one of ours? */ + if ((id = ns8390_ident(ndev)) != -1) + break; + } - if(match->category!=NUBUS_CAT_NETWORK || match->type!=1) - return -ENODEV; - /* Ok so it is an ethernet network device */ - if((id=ns8390_ident(match))==-1) - { - printk("Ethernet but type unknown %d\n",match->DrHW); + /* Hm. No more cards, then */ + if (ndev == NULL) return -ENODEV; + + dev = init_etherdev(dev, 0); + + if (!version_printed) { + printk(KERN_INFO "%s", version); + version_printed = 1; } - dev = init_etherdev(0, 0); - if(dev==NULL) - return -ENOMEM; /* * Dayna specific init */ if(id==NS8390_DAYNA) { - dev->base_addr=(int)(nubus_slot_addr(slot)+DAYNA_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+DAYNA_8390_MEM); - dev->mem_end=dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ + dev->base_addr = (int)(ndev->board->slot_addr+DAYNA_8390_BASE); + dev->mem_start = (int)(ndev->board->slot_addr+DAYNA_8390_MEM); + dev->mem_end = dev->mem_start+DAYNA_MEMSIZE; /* 8K it seems */ - printk("daynaport: testing board: "); - + printk(KERN_INFO "%s: daynaport. testing board: ", dev->name); + printk("memory - "); - - i=(void *)dev->mem_start; + + i = (void *)dev->mem_start; memset((void *)i,0xAA, DAYNA_MEMSIZE); while(i<(volatile unsigned short *)dev->mem_end) { if(*i!=0xAAAA) goto membad; - *i=0x5555; - if(*i!=0x5555) + *i=0x5678; /* make sure we catch byte smearing */ + if(*i!=0x5678) goto membad; i+=2; /* Skip a word */ } - + printk("controller - "); - + p=(void *)dev->base_addr; plen=0; - + while(plen<0x3FF00) { if(test_8390(p,0)==0) @@ -263,26 +329,71 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * if(plen==0x3FF00) goto membad; printk("OK\n"); - dev->irq=slot; - if(ns8390_probe1(dev, 0, "dayna", id, -1)==0) - return 0; + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "dayna", id, -1, ndev)==0) + return 0; + } + /* Cabletron */ + if (id==NS8390_CABLETRON) { + int memsize = 16<<10; /* fix this */ + + dev->base_addr=(int)(ndev->board->slot_addr+CABLETRON_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+CABLETRON_8390_MEM); + dev->mem_end=dev->mem_start+memsize; + dev->irq = SLOT2IRQ(ndev->board->slot); + + /* The base address is unreadable if 0x00 has been written to the command register */ + /* Reset the chip by writing E8390_NODMA+E8390_PAGE0+E8390_STOP just to be sure */ + i = (void *)dev->base_addr; + *i = 0x21; + + printk(KERN_INFO "%s: cabletron: testing board: ", dev->name); + printk("%dK memory - ", memsize>>10); + i=(void *)dev->mem_start; + while(i<(volatile unsigned short *)(dev->mem_start+memsize)) + { + *i=0xAAAA; + if(*i!=0xAAAA) + goto membad; + *i=0x5555; + if(*i!=0x5555) + goto membad; + i+=2; /* Skip a word */ + } + printk("OK\n"); + + if(ns8390_probe1(dev, 1, "cabletron", id, -1, ndev)==0) + return 0; } /* Apple, Farallon, Asante */ - if(id==NS8390_APPLE|| id==NS8390_FARALLON || id==NS8390_ASANTE) + if(id==NS8390_APPLE || id==NS8390_FARALLON || id==NS8390_ASANTE) { int memsize; - - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); - + + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); + memsize = apple_8390_mem_probe((void *)dev->mem_start); - + dev->mem_end=dev->mem_start+memsize; - dev->irq=slot; - printk("apple/clone: testing board: "); - + dev->irq = SLOT2IRQ(ndev->board->slot); + + switch(id) + { + case NS8390_FARALLON: + printk(KERN_INFO "%s: farallon: testing board: ", dev->name); + break; + case NS8390_ASANTE: + printk(KERN_INFO "%s: asante: testing board: ", dev->name); + break; + case NS8390_APPLE: + default: + printk(KERN_INFO "%s: apple/clone: testing board: ", dev->name); + break; + } + printk("%dK memory - ", memsize>>10); - + i=(void *)dev->mem_start; memset((void *)i,0xAA, memsize); while(i<(volatile unsigned short *)dev->mem_end) @@ -295,51 +406,75 @@ int ns8390_probe(struct nubus_device_specifier *d, int slot, struct nubus_type * i+=2; /* Skip a word */ } printk("OK\n"); - - if(id==NS8390_FARALLON) + + switch (id) { - if(ns8390_probe1(dev, 1, "farallon", id, -1)==0) + case NS8390_FARALLON: + if(ns8390_probe1(dev, 1, "farallon", id, -1, ndev)==0) return 0; - } - else - { - if(ns8390_probe1(dev, 1, "apple/clone", id, -1)==0) - return 0; + break; + case NS8390_ASANTE: + if(ns8390_probe1(dev, 1, "asante", id, -1, ndev)==0) + return 0; + break; + case NS8390_APPLE: + default: + if(ns8390_probe1(dev, 1, "apple/clone", id, -1, ndev)==0) + return 0; + break; } } /* Interlan */ if(id==NS8390_INTERLAN) { /* As apple and asante */ - dev->base_addr=(int)(nubus_slot_addr(slot)+APPLE_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+APPLE_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+APPLE_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+APPLE_8390_MEM); dev->mem_end=dev->mem_start+APPLE_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 1, "interlan", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 1, "interlan", id, -1, ndev)==0) return 0; } - /* Kinetics */ + /* Kinetics (Shiva Etherport) */ if(id==NS8390_KINETICS) { - dev->base_addr=(int)(nubus_slot_addr(slot)+KINETICS_8390_BASE); - dev->mem_start=(int)(nubus_slot_addr(slot)+KINETICS_8390_MEM); + dev->base_addr=(int)(ndev->board->slot_addr+KINETICS_8390_BASE); + dev->mem_start=(int)(ndev->board->slot_addr+KINETICS_8390_MEM); dev->mem_end=dev->mem_start+KINETICS_MEMSIZE; /* 8K it seems */ - dev->irq=slot; - if(ns8390_probe1(dev, 0, "kinetics", id, -1)==0) + dev->irq = SLOT2IRQ(ndev->board->slot); + if(ns8390_probe1(dev, 0, "kinetics", id, -1, ndev)==0) return 0; } - kfree(dev); + + /* We should hopefully not get here */ + printk(KERN_ERR "Probe unsucessful.\n"); return -ENODEV; -membad: - printk("failed.\n"); - kfree(dev); + + membad: + printk(KERN_ERR "failed at %p in %p - %p.\n", i, + (void *)dev->mem_start, (void *)dev->mem_end); return -ENODEV; } -int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type, int promoff) +int __init mac8390_ethernet_addr(struct nubus_dev* ndev, + unsigned char addr[6]) { - static unsigned version_printed = 0; + struct nubus_dir dir; + struct nubus_dirent ent; + + /* Get the functional resource for this device */ + if (nubus_get_func_dir(ndev, &dir) == -1) + return -1; + if (nubus_find_rsrc(&dir, NUBUS_RESID_MAC_ADDRESS, &ent) == -1) + return -1; + + nubus_get_rsrc_mem(addr, &ent, 6); + return 0; +} +int __init ns8390_probe1(struct net_device *dev, int word16, char *model_name, + int type, int promoff, struct nubus_dev *ndev) +{ static u32 fwrd4_offsets[16]={ 0, 4, 8, 12, 16, 20, 24, 28, @@ -352,25 +487,19 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type 28, 24, 20, 16, 12, 8, 4, 0 }; + static u32 fwrd2_offsets[16]={ + 0, 2, 4, 6, + 8, 10, 12, 14, + 16, 18, 20, 22, + 24, 26, 28, 30 + }; - unsigned char *prom=((unsigned char *)nubus_slot_addr(dev->irq))+promoff; + unsigned char *prom = (unsigned char*) ndev->board->slot_addr + promoff; - if (ei_debug && version_printed++ == 0) - printk(version); - - /* Snarf the interrupt now. There's no point in waiting since we cannot - share a slot! and the board will usually be enabled. */ - if (nubus_request_irq(dev->irq, dev, ei_interrupt)) - { - printk (" unable to get nubus IRQ %d.\n", dev->irq); - return EAGAIN; - } - /* Allocate dev->priv and fill in 8390 specific dev fields. */ if (ethdev_init(dev)) { - printk (" unable to get memory for dev->priv.\n"); - nubus_free_irq(dev->irq); + printk ("%s: unable to get memory for dev->priv.\n", dev->name); return -ENOMEM; } @@ -378,16 +507,25 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.name = model_name; ei_status.word16 = word16; - ei_status.tx_start_page = WD_START_PG; - ei_status.rx_start_page = WD_START_PG + TX_PAGES; - dev->rmem_start = dev->mem_start + TX_PAGES*256; - ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; - dev->rmem_end = dev->mem_end; + if (type==NS8390_CABLETRON) { + /* Cabletron card puts the RX buffer before the TX buffer */ + ei_status.tx_start_page = CABLETRON_TX_START_PG; + ei_status.rx_start_page = CABLETRON_RX_START_PG; + ei_status.stop_page = CABLETRON_RX_STOP_PG; + dev->rmem_start = dev->mem_start; + dev->rmem_end = dev->mem_start + CABLETRON_RX_STOP_PG*256; + } else { + ei_status.tx_start_page = WD_START_PG; + ei_status.rx_start_page = WD_START_PG + TX_PAGES; + ei_status.stop_page = (dev->mem_end - dev->mem_start)/256; + dev->rmem_start = dev->mem_start + TX_PAGES*256; + dev->rmem_end = dev->mem_end; + } if(promoff==-1) /* Use nubus resources ? */ { - if(nubus_ethernet_addr(dev->irq /* slot */, dev->dev_addr)) + if(mac8390_ethernet_addr(ndev, dev->dev_addr)) { printk("mac_ns8390: MAC address not in resources!\n"); return -ENODEV; @@ -400,7 +538,7 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type /* These should go in the end I hope */ if(type==NS8390_DAYNA) x=2; - if(type==NS8390_INTERLAN) + if(type==NS8390_INTERLAN || type==NS8390_KINETICS) x=4; while(i<6) { @@ -412,12 +550,24 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type } } - printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n", - model_name, dev->irq, dev->mem_start, dev->mem_end-1); + printk(KERN_INFO "%s: %s in slot %X (type %s)\n", + dev->name, ndev->board->name, ndev->board->slot, model_name); + printk(KERN_INFO "MAC "); + { + int i; + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + } + printk(" IRQ %d, shared memory at %#lx-%#lx.\n", + dev->irq, dev->mem_start, dev->mem_end-1); switch(type) { case NS8390_DAYNA: /* Dayna card */ + case NS8390_KINETICS: /* Kinetics -- 8 bit config, but 16 bit mem */ /* 16 bit, 4 word offsets */ ei_status.reset_8390 = &ns8390_no_reset; ei_status.block_input = &dayna_block_input; @@ -425,6 +575,15 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &dayna_get_8390_hdr; ei_status.reg_offset = fwrd4_offsets; break; + case NS8390_CABLETRON: /* Cabletron */ + /* 16 bit card, register map is short forward */ + ei_status.reset_8390 = &ns8390_no_reset; + /* Ctron card won't accept 32bit values read or written to it */ + ei_status.block_input = &slow_sane_block_input; + ei_status.block_output = &slow_sane_block_output; + ei_status.get_8390_hdr = &slow_sane_get_8390_hdr; + ei_status.reg_offset = fwrd2_offsets; + break; case NS8390_FARALLON: case NS8390_APPLE: /* Apple/Asante/Farallon */ /* 16 bit card, register map is reversed */ @@ -450,6 +609,8 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#if 0 /* i think this suffered code rot. my kinetics card has much + * different settings. -- CSA [22-May-1999] */ case NS8390_KINETICS: /* Kinetics */ /* 8bit card, map is forward */ ei_status.reset_8390 = &ns8390_no_reset; @@ -458,6 +619,7 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type ei_status.get_8390_hdr = &sane_get_8390_hdr; ei_status.reg_offset = back4_offsets; break; +#endif default: panic("Detected a card I can't drive - whoops\n"); } @@ -472,6 +634,19 @@ int ns8390_probe1(struct net_device *dev, int word16, char *model_name, int type static int ns8390_open(struct net_device *dev) { ei_open(dev); + + /* At least on my card (a Focus Enhancements PDS card) I start */ + /* getting interrupts right away, so the driver needs to be */ + /* completely initialized before enabling the interrupt. */ + /* - funaho@jurai.org (1999-05-17) */ + + /* Non-slow interrupt, works around issues with the SONIC driver */ + if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) + { + printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq); + return EAGAIN; + } + MOD_INC_USE_COUNT; return 0; } @@ -489,24 +664,19 @@ static int ns8390_close_card(struct net_device *dev) { if (ei_debug > 1) printk("%s: Shutting down ethercard.\n", dev->name); + free_irq(dev->irq, dev); ei_close(dev); MOD_DEC_USE_COUNT; return 0; } -struct nubus_device_specifier nubus_8390={ - ns8390_probe, - NULL -}; - - /* * Interlan Specific Code Starts Here */ static void interlan_reset(struct net_device *dev) { - unsigned char *target=nubus_slot_addr(dev->irq); + unsigned char *target=nubus_slot_addr(IRQ2SLOT(dev->irq)); if (ei_debug > 1) printk("Need to reset the NS8390 t=%lu...", jiffies); ei_status.txing = 0; @@ -531,16 +701,23 @@ static void interlan_reset(struct net_device *dev) The only complications are that the ring buffer wraps. */ -static void dayna_cpu_memcpy(struct net_device *dev, void *to, int from, int count) +static void dayna_memcpy_fromcard(struct net_device *dev, void *to, int from, int count) { volatile unsigned short *ptr; unsigned short *target=to; from<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+from); + /* + * Leading byte? + */ + if (from&2) { + *((char *)target)++ = *(((char *)ptr++)-1); + count--; + } while(count>=2) { *target++=*ptr++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -554,16 +731,24 @@ static void dayna_cpu_memcpy(struct net_device *dev, void *to, int from, int cou } } -static void cpu_dayna_memcpy(struct net_device *dev, int to, const void *from, int count) +static void dayna_memcpy_tocard(struct net_device *dev, int to, const void *from, int count) { volatile unsigned short *ptr; const unsigned short *src=from; to<<=1; /* word, skip overhead */ ptr=(unsigned short *)(dev->mem_start+to); + /* + * Leading byte? + */ + if (to&2) { /* avoid a byte write (stomps on other data) */ + ptr[-1] = (ptr[-1]&0xFF00)|*((unsigned char *)src)++; + ptr++; + count--; + } while(count>=2) { *ptr++=*src++; /* Copy and */ - ptr++; /* Cruft and */ + ptr++; /* skip cruft */ count-=2; } /* @@ -573,14 +758,15 @@ static void cpu_dayna_memcpy(struct net_device *dev, int to, const void *from, i { /* Big endian */ unsigned short v=*src; - *((char *)ptr)=v>>8; + /* card doesn't like byte writes */ + *ptr=(*ptr&0x00FF)|(v&0xFF00); } } static void dayna_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) { unsigned long hdr_start = (ring_page - WD_START_PG)<<8; - dayna_cpu_memcpy(dev, (void *)hdr, hdr_start, 4); + dayna_memcpy_fromcard(dev, (void *)hdr, hdr_start, 4); /* Register endianism - fix here rather than 8390.c */ hdr->count=(hdr->count&0xFF)<<8|(hdr->count>>8); } @@ -599,14 +785,14 @@ static void dayna_block_input(struct net_device *dev, int count, struct sk_buff { /* We must wrap the input move. */ int semi_count = dev->rmem_end - xfer_start; - dayna_cpu_memcpy(dev, skb->data, xfer_base, semi_count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, semi_count); count -= semi_count; - dayna_cpu_memcpy(dev, skb->data + semi_count, + dayna_memcpy_fromcard(dev, skb->data + semi_count, dev->rmem_start - dev->mem_start, count); } else { - dayna_cpu_memcpy(dev, skb->data, xfer_base, count); + dayna_memcpy_fromcard(dev, skb->data, xfer_base, count); } } @@ -615,7 +801,7 @@ static void dayna_block_output(struct net_device *dev, int count, const unsigned { long shmem = (start_page - WD_START_PG)<<8; - cpu_dayna_memcpy(dev, shmem, buf, count); + dayna_memcpy_tocard(dev, shmem, buf, count); } /* @@ -739,6 +925,7 @@ static void slow_sane_block_output(struct net_device *dev, int count, const unsi * Local variables: * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c daynaport.c" * version-control: t + * c-basic-offset: 4 * tab-width: 4 * kept-new-versions: 5 * End: diff --git a/drivers/net/irda/Config.in b/drivers/net/irda/Config.in index da1df253c9f3..da0b5f4beb02 100644 --- a/drivers/net/irda/Config.in +++ b/drivers/net/irda/Config.in @@ -6,10 +6,12 @@ dep_tristate 'IrTTY (uses Linux serial driver)' CONFIG_IRTTY_SIR $CONFIG_IRDA dep_tristate 'IrPORT (IrDA serial driver)' CONFIG_IRPORT_SIR $CONFIG_IRDA comment 'FIR device drivers' -dep_tristate 'NSC PC87108/PC97338' CONFIG_NSC_FIR $CONFIG_IRDA +dep_tristate 'NSC PC87108/PC87338' CONFIG_NSC_FIR $CONFIG_IRDA dep_tristate 'Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA -dep_tristate 'SMC IrCC' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA +if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then +dep_tristate 'SMC IrCC (Experimental)' CONFIG_SMC_IRCC_FIR $CONFIG_IRDA +fi comment 'Dongle support' bool 'Serial dongle support' CONFIG_DONGLE @@ -19,9 +21,7 @@ if [ "$CONFIG_DONGLE" != "n" ]; then dep_tristate ' Tekram IrMate 210B dongle' CONFIG_TEKRAM_DONGLE $CONFIG_IRDA dep_tristate ' Greenwich GIrBIL dongle' CONFIG_GIRBIL_DONGLE $CONFIG_IRDA dep_tristate ' Parallax LiteLink dongle' CONFIG_LITELINK_DONGLE $CONFIG_IRDA - dep_tristate ' Adaptec Airport 1000/2000 dongle' CONFIG_AIRPORT_DONGLE $CONFIG_IRDA - dep_tristate ' Old Belkin dongle' CONFIG_OLD_BELKIN_DONGLE $CONFIG_IRDA - + dep_tristate ' Old Belkin dongle' CONFIG_OLD_BELKIN_DONGLE $CONFIG_IRDA fi endmenu diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index bf5628ebd355..291a27f050eb 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -29,10 +29,10 @@ else endif ifeq ($(CONFIG_NSC_FIR),y) -L_OBJS += nsc_fir.o +L_OBJS += nsc-ircc.o else ifeq ($(CONFIG_NSC_FIR),m) - M_OBJS += nsc_fir.o + M_OBJS += nsc-ircc.o endif endif @@ -118,14 +118,6 @@ else endif endif -ifeq ($(CONFIG_AIRPORT_DONGLE),y) -L_OBJS += airport.o -else - ifeq ($(CONFIG_AIRPORT_DONGLE),m) - M_OBJS += airport.o - endif -endif - ifeq ($(CONFIG_OLD_BELKIN_DONGLE),y) L_OBJS += old_belkin.o else diff --git a/drivers/net/irda/airport.c b/drivers/net/irda/airport.c deleted file mode 100644 index b68378189b9d..000000000000 --- a/drivers/net/irda/airport.c +++ /dev/null @@ -1,358 +0,0 @@ -/********************************************************************* - * - * Filename: airport.c - * Version: 0.2 - * Description: Implementation for the Adaptec Airport 1000 and 2000 - * dongles - * Status: Experimental. - * Author: Fons Botman - * Created at: Wed May 19 23:14:34 CEST 1999 - * Based on: actisys.c by Dag Brattli - * - * Copyright (c) 1998-1999 Fons Botman, All Rights Reserved. - * - * 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. - * - * Neither Fons Botman nor anyone else admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -static int airport_reset_wrapper(struct irda_task *task); -static void airport_open(dongle_t *self, struct qos_info *qos); -static void airport_close(dongle_t *self); -static int airport_change_speed_wrapper(struct irda_task *task); - -static struct dongle_reg dongle = { - Q_NULL, - IRDA_AIRPORT_DONGLE, - airport_open, - airport_close, - airport_reset_wrapper, - airport_change_speed_wrapper, -}; - -int __init airport_init(void) -{ - int ret; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - ret = irda_device_register_dongle(&dongle); - if (ret < 0) - return ret; - return 0; -} - -void airport_cleanup(void) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - irda_device_unregister_dongle(&dongle); -} - -static void airport_open(dongle_t *self, struct qos_info *qos) -{ - qos->baud_rate.bits &= - IR_2400|IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; - /* May need 1ms */ - qos->min_turn_time.bits = 0x07; - - MOD_INC_USE_COUNT; -} - -static void airport_close(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - /* Power off dongle */ - self->set_dtr_rts(self->dev, FALSE, FALSE); - - MOD_DEC_USE_COUNT; -} - -static void airport_set_command_mode(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - self->set_dtr_rts(self->dev, FALSE, TRUE); -} - -static void airport_set_normal_mode(dongle_t *self) -{ - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - self->set_dtr_rts(self->dev, TRUE, TRUE); -} - -void airport_write_char(dongle_t *self, unsigned char c) -{ - int actual; - IRDA_DEBUG(2, __FUNCTION__ "(,0x%x)\n", c & 0xff); - actual = self->write(self->dev, &c, 1); - ASSERT(actual == 1, return;); -} - -#define JIFFIES_TO_MSECS(j) ((j)*1000/HZ) - -static int airport_waitfor_char(dongle_t *self, unsigned char c) -{ - __u8 buf[100]; - int i, found = FALSE; - int before; - int len; - - IRDA_DEBUG(2, __FUNCTION__ "(,0x%x)\n", c); - - /* Sleep approx. 10 ms */ - before = jiffies; - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - IRDA_DEBUG(4, __FUNCTION__ " waited %ldms\n", - JIFFIES_TO_MSECS(jiffies - before)); - - len = self->read(self->dev, buf, 100); - - for (i = 0; !found && i < len; i++ ) { - /* IRDA_DEBUG(6, __FUNCTION__ " 0x02x\n", idev->rx_buff.data[i]); */ - found = c == buf[i]; - } - - IRDA_DEBUG(2, __FUNCTION__ " returns %s\n", (found ? "true" : "false")); - return found; -} - -static int airport_check_command_mode(dongle_t *self) -{ - int i; - int found = FALSE; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - airport_set_command_mode(self); - - /* Loop until the time expires (200ms) or we get the magic char. */ - - for ( i = 0 ; i < 25 ; i++ ) { - airport_write_char(self, 0xff); - if (airport_waitfor_char(self, 0xc3)) { - found = TRUE; - break; - } - } - - if (found) { - IRDA_DEBUG(2, __FUNCTION__ " OK. (%d)\n", i); - } else { - IRDA_DEBUG(0, __FUNCTION__ " FAILED!\n"); - } - return found; -} - -static int airport_write_register(dongle_t *self, unsigned char reg) -{ - int ok = FALSE; - int i; - - IRDA_DEBUG(4, __FUNCTION__ "(,0x%x)\n", reg); - airport_check_command_mode(self); - - for ( i = 0 ; i < 6 ; i++ ) { - airport_write_char(self, reg); - if (!airport_waitfor_char(self, reg)) - continue; - - /* Now read it back */ - airport_write_char(self, (reg << 4) | 0x0f); - if (airport_waitfor_char(self, reg)) { - ok = TRUE; - break; - } - } - - airport_set_normal_mode(self); - if (ok) { - IRDA_DEBUG(4, __FUNCTION__ "(,0x%x) returns OK\n", reg); - } else { - IRDA_DEBUG(0, __FUNCTION__ "(,0x%x) returns False!\n", reg); - } - return ok; -} - - -/* - * Function airport_change_speed (self, speed) - * - * Change speed of the Airport type IrDA dongles. - */ -static void airport_change_speed(dongle_t *self, __u32 speed) -{ - __u32 current_baudrate; - int baudcode; - - IRDA_DEBUG(4, __FUNCTION__ "(,%d)\n", speed); - - ASSERT(self != NULL, return;); - - /* Find the correct baudrate code for the required baudrate */ - switch (speed) { - case 2400: baudcode = 0x10; break; - case 4800: baudcode = 0x20; break; - case 9600: baudcode = 0x30; break; - case 19200: baudcode = 0x40; break; - case 38400: baudcode = 0x50; break; - case 57600: baudcode = 0x60; break; - case 115200: baudcode = 0x70; break; - default: - IRDA_DEBUG(0, __FUNCTION__ " bad baud rate: %d\n", speed); - return; - } - - current_baudrate = self->speed; - IRDA_DEBUG(4, __FUNCTION__ " current baudrate: %d\n", current_baudrate); - - self->set_mode(self->dev, IRDA_RAW); - - /* Set the new speed in both registers */ - if (airport_write_register(self, baudcode)) { - if (airport_write_register(self, baudcode|0x01)) { - /* ok */ - } else { - IRDA_DEBUG(0, __FUNCTION__ - " Cannot set new speed in second register\n"); - } - } else { - IRDA_DEBUG(0, __FUNCTION__ - " Cannot set new speed in first register\n"); - } - - self->set_mode(self->dev, IRDA_IRLAP); - - /* How do I signal an error in these functions? */ - - IRDA_DEBUG(4, __FUNCTION__ " returning\n"); -} - -int airport_change_speed_wrapper(struct irda_task *task) -{ - dongle_t *self = (dongle_t *) task->instance; - __u32 speed = (__u32) task->param; - - irda_execute_as_process(self, (TODO_CALLBACK) airport_change_speed, - speed); - - irda_task_next_state(task, IRDA_TASK_DONE); - - return 0; -} - -/* - * Function airport_reset (self) - * - * Reset the Airport type dongle. Warning, this function must only be - * called with a process context! - * - */ -static void airport_reset(dongle_t *self) -{ - int ok; - - IRDA_DEBUG(2, __FUNCTION__ "()\n"); - ASSERT(self != NULL, return;); - - self->set_mode(self->dev, IRDA_RAW); - - airport_set_normal_mode(self); - - /* Sleep 2000 ms */ - IRDA_DEBUG(2, __FUNCTION__ " waiting for powerup\n"); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(2000)); - IRDA_DEBUG(2, __FUNCTION__ " finished waiting for powerup\n"); - - /* set dongle speed to 9600 */ - ok = TRUE; - - if (ok) - ok = airport_write_register(self, 0x30); - if (!ok) - MESSAGE(__FUNCTION__ "() dongle not connected?\n"); - if (ok) - ok = airport_write_register(self, 0x31); - - if (ok) - ok = airport_write_register(self, 0x02); - if (ok) - ok = airport_write_register(self, 0x03); - - if (ok) { - ok = airport_check_command_mode(self); - - if (ok) { - airport_write_char(self, 0x04); - ok = airport_waitfor_char(self, 0x04); - } - airport_set_normal_mode(self); - } - - self->set_mode(self->dev, IRDA_IRLAP); - - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(MSECS_TO_JIFFIES(20)); - IRDA_DEBUG(4, __FUNCTION__ " waited 20ms\n"); - - self->speed = 9600; - if (!ok) - MESSAGE(__FUNCTION__ "() failed.\n"); - IRDA_DEBUG(2, __FUNCTION__ " returning.\n"); -} - -int airport_reset_wrapper(struct irda_task *task) -{ - dongle_t *self = (dongle_t *) task->instance; - - irda_execute_as_process(self, (TODO_CALLBACK) airport_reset, 0); - - irda_task_next_state(task, IRDA_TASK_DONE); - - return 0; -} - -#ifdef MODULE - -MODULE_AUTHOR("Fons Botman "); -MODULE_DESCRIPTION("Adaptec Airport 1000 and 2000 dongle driver"); - -/* - * Function init_module (void) - * - * Initialize Airport module - * - */ -int init_module(void) -{ - return airport_init(); -} - -/* - * Function cleanup_module (void) - * - * Cleanup Airport module - * - */ -void cleanup_module(void) -{ - airport_cleanup(); -} - -#endif diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c index 352dddecc0b0..d8832292a9c3 100644 --- a/drivers/net/irda/irport.c +++ b/drivers/net/irda/irport.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli * Created at: Sun Aug 3 13:49:59 1997 - * Modified at: Wed Jan 5 13:59:38 2000 + * Modified at: Fri Jan 28 20:22:38 2000 * Modified by: Dag Brattli * Sources: serial.c by Linus Torvalds * @@ -161,20 +161,19 @@ irport_open(int i, unsigned int iobase, unsigned int irq) self->index = i; /* Initialize IO */ - self->io.iobase = iobase; + self->io.sir_base = iobase; + self->io.sir_ext = IO_EXTENT; self->io.irq = irq; - self->io.io_ext = IO_EXTENT; self->io.fifo_size = 16; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.sir_base, self->io.sir_ext); if (ret < 0) { IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); - /* irport_cleanup(self->self); */ + self->io.sir_base); return NULL; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.sir_base, self->io.sir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -218,7 +217,6 @@ irport_open(int i, unsigned int iobase, unsigned int irq) ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return NULL; } - self->netdev = dev; /* May be overridden by piggyback drivers */ @@ -268,19 +266,19 @@ int irport_close(struct irport_cb *self) /* Release the IO-port that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.sir_base); + release_region(self->io.sir_base, self->io.sir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); if (self->rx_buff.head) kfree(self->rx_buff.head); - + /* Remove ourselves */ dev_self[self->index] = NULL; kfree(self); - + return 0; } @@ -289,7 +287,7 @@ void irport_start(struct irport_cb *self) unsigned long flags; int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; spin_lock_irqsave(&self->lock, flags); @@ -310,7 +308,7 @@ void irport_stop(struct irport_cb *self) unsigned long flags; int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; spin_lock_irqsave(&self->lock, flags); @@ -355,7 +353,7 @@ void irport_change_speed(void *priv, __u32 speed) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -431,7 +429,7 @@ int __irport_change_speed(struct irda_task *task) break; case IRDA_TASK_CHILD_INIT: /* Go to default speed */ - irport_change_speed(self, 9600); + self->change_speed(self->priv, 9600); /* Change speed of dongle */ if (irda_task_execute(self->dongle, @@ -454,7 +452,7 @@ int __irport_change_speed(struct irda_task *task) break; case IRDA_TASK_CHILD_DONE: /* Finally we are ready to change the speed */ - irport_change_speed(self, speed); + self->change_speed(self->priv, speed); irda_task_next_state(task, IRDA_TASK_DONE); break; @@ -484,7 +482,7 @@ static void irport_write_wakeup(struct irport_cb *self) IRDA_DEBUG(4, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Finished with frame? */ if (self->tx_buff.len > 0) { @@ -598,7 +596,6 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct irport_cb *self; unsigned long flags; - int actual = 0; int iobase; __u32 speed; @@ -607,7 +604,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) self = (struct irport_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) { @@ -617,7 +614,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) WARNING("%s: transmit timed out\n", dev->name); irport_start(self); - irport_change_speed(self, self->io.speed ); + self->change_speed(self->priv, self->io.speed); dev->trans_start = jiffies; } @@ -635,8 +632,7 @@ int irport_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); - self->tx_buff.data += actual; - self->tx_buff.len -= actual; + self->stats.tx_bytes += self->tx_buff.len; /* Turn on transmit finished interrupt. Will fire immediately! */ outb(UART_IER_THRI, iobase+UART_IER); @@ -661,7 +657,7 @@ static void irport_receive(struct irport_cb *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* * Receive all characters in Rx FIFO, unwrap and unstuff them. @@ -702,15 +698,16 @@ void irport_interrupt(int irq, void *dev_id, struct pt_regs *regs) dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.sir_base; iir = inb(iobase+UART_IIR) & UART_IIR_ID; while (iir) { /* Clear interrupt */ lsr = inb(iobase+UART_LSR); - IRDA_DEBUG(4, __FUNCTION__ "(), iir=%02x, lsr=%02x, iobase=%#x\n", - iir, lsr, iobase); + IRDA_DEBUG(4, __FUNCTION__ + "(), iir=%02x, lsr=%02x, iobase=%#x\n", + iir, lsr, iobase); switch (iir) { case UART_IIR_RLSI: @@ -765,7 +762,7 @@ int irport_net_open(struct net_device *dev) ASSERT(dev != NULL, return -1;); self = (struct irport_cb *) dev->priv; - iobase = self->io.iobase; + iobase = self->io.sir_base; if (request_irq(self->io.irq, self->interrupt, 0, dev->name, (void *) dev)) @@ -809,7 +806,7 @@ int irport_net_close(struct net_device *dev) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Stop device */ dev->tbusy = 1; @@ -840,7 +837,7 @@ void irport_wait_until_sent(struct irport_cb *self) { int iobase; - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Wait until Tx FIFO is empty */ while (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { @@ -875,7 +872,7 @@ static int irport_set_dtr_rts(struct net_device *dev, int dtr, int rts) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; if (dtr) dtr = UART_MCR_DTR; @@ -895,7 +892,7 @@ static int irport_raw_write(struct net_device *dev, __u8 *buf, int len) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.sir_base; /* Tx FIFO should be empty! */ if (!(inb(iobase+UART_LSR) & UART_LSR_THRE)) { diff --git a/drivers/net/irda/irtty.c b/drivers/net/irda/irtty.c index 2ec4eba14b03..370a9c7fede1 100644 --- a/drivers/net/irda/irtty.c +++ b/drivers/net/irda/irtty.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Dag Brattli * Created at: Tue Dec 9 21:18:38 1997 - * Modified at: Wed Jan 5 14:00:13 2000 + * Modified at: Fri Jan 14 21:02:27 2000 * Modified by: Dag Brattli * Sources: slip.c by Laurence Culhane, * Fred N. van Kempen, @@ -233,6 +233,8 @@ static int irtty_open(struct tty_struct *tty) ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } + /* dev_alloc doesn't clear the struct */ + memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*)); dev->priv = (void *) self; self->netdev = dev; @@ -654,6 +656,7 @@ static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); dev->trans_start = jiffies; + self->stats.tx_bytes += self->tx_buff.len; if (self->tty->driver.write) actual = self->tty->driver.write(self->tty, 0, @@ -663,9 +666,6 @@ static int irtty_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->tx_buff.data += actual; self->tx_buff.len -= actual; - self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; - dev_kfree_skb(skb); return 0; @@ -709,6 +709,8 @@ static void irtty_write_wakeup(struct tty_struct *tty) self->tx_buff.data += actual; self->tx_buff.len -= actual; + + self->stats.tx_packets++; } else { /* * Now serial buffer is almost free & we can start diff --git a/drivers/net/irda/nsc_fir.c b/drivers/net/irda/nsc-ircc.c similarity index 62% rename from drivers/net/irda/nsc_fir.c rename to drivers/net/irda/nsc-ircc.c index affd1d8fa2c1..550d4176f527 100644 --- a/drivers/net/irda/nsc_fir.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1,12 +1,12 @@ /********************************************************************* * - * Filename: nsc_fir.c + * Filename: nsc-ircc.c * Version: 1.0 * Description: Driver for the NSC PC'108 and PC'338 IrDA chipsets * Status: Stable. * Author: Dag Brattli * Created at: Sat Nov 7 21:43:15 1998 - * Modified at: Wed Jan 5 13:59:21 2000 + * Modified at: Fri Jan 28 12:10:10 2000 * Modified by: Dag Brattli * * Copyright (c) 1998-2000 Dag Brattli @@ -42,7 +42,7 @@ ********************************************************************/ #include -#include + #include #include #include @@ -67,29 +67,38 @@ #include #include -#include +#include #define CHIP_IO_EXTENT 8 #define BROKEN_DONGLE_ID -/* - * Define if you have multiple NSC IrDA controllers in your machine. Not - * enabled by default since some single chips detects at multiple addresses - */ -#undef CONFIG_NSC_FIR_MULTIPLE - -static char *driver_name = "nsc_fir"; +static char *driver_name = "nsc-ircc"; /* Module parameters */ static int qos_mtt_bits = 0x07; /* 1 ms or more */ static int dongle_id = 0; -static unsigned int io[] = { 0x2f8, 0x2f8, 0x2f8, 0x2f8, 0x2f8 }; -static unsigned int io2[] = { 0x150, 0x398, 0xea, 0x15c, 0x2e }; -static unsigned int irq[] = { 3, 3, 3, 3, 3 }; -static unsigned int dma[] = { 0, 0, 0, 0, 3 }; +/* Use BIOS settions by default, but user may supply module parameters */ +static unsigned int io[] = { ~0, ~0, ~0, ~0 }; +static unsigned int irq[] = { 0, 0, 0, 0, 0 }; +static unsigned int dma[] = { 0, 0, 0, 0, 0 }; + +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info); +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info); + +/* These are the known NSC chips */ +static nsc_chip_t chips[] = { + { "PC87108", { 0x150, 0x398, 0xea }, 0x05, 0x10, 0xf0, + nsc_ircc_probe_108, nsc_ircc_init_108 }, + { "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf0, + nsc_ircc_probe_338, nsc_ircc_init_338 }, + { NULL } +}; -static struct nsc_fir_cb *dev_self[] = { NULL, NULL, NULL, NULL, NULL }; +/* Max 4 instances for now */ +static struct nsc_ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL }; static char *dongle_types[] = { "Differential serial interface", @@ -111,143 +120,182 @@ static char *dongle_types[] = { }; /* Some prototypes */ -static int nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, - unsigned int irq, unsigned int dma); +static int nsc_ircc_open(int i, chipio_t *info); #ifdef MODULE -static int nsc_fir_close(struct nsc_fir_cb *self); +static int nsc_ircc_close(struct nsc_ircc_cb *self); #endif /* MODULE */ -static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma); -static void nsc_fir_pio_receive(struct nsc_fir_cb *self); -static int nsc_fir_dma_receive(struct nsc_fir_cb *self); -static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase); -static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); -static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); -static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size); -static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase); -static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 baud); -static void nsc_fir_interrupt(int irq, void *dev_id, struct pt_regs *regs); -static int nsc_fir_is_receiving(struct nsc_fir_cb *self); -static int nsc_fir_read_dongle_id (int iobase); -static void nsc_fir_init_dongle_interface (int iobase, int dongle_id); - -static int nsc_fir_net_init(struct net_device *dev); -static int nsc_fir_net_open(struct net_device *dev); -static int nsc_fir_net_close(struct net_device *dev); -static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -static struct net_device_stats *nsc_fir_net_get_stats(struct net_device *dev); +static int nsc_ircc_setup(chipio_t *info); +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self); +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase); +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev); +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size); +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase); +static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 baud); +static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self); +static int nsc_ircc_read_dongle_id (int iobase); +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id); + +static int nsc_ircc_net_init(struct net_device *dev); +static int nsc_ircc_net_open(struct net_device *dev); +static int nsc_ircc_net_close(struct net_device *dev); +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev); #ifdef CONFIG_APM -static int nsc_fir_apmproc(apm_event_t event); +static int nsc_ircc_apmproc(apm_event_t event); #endif /* CONFIG_APM */ + /* - * Function nsc_fir_init () + * Function nsc_ircc_init () * * Initialize chip. Just try to find out how many chips we are dealing with * and where they are */ -int __init nsc_fir_init(void) +int __init nsc_ircc_init(void) { + chipio_t info; + nsc_chip_t *chip; int ret = -ENODEV; - int ioaddr; - int i; + int cfg_base; + int cfg, id; + int reg; + int i = 0; + + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++,i++) { + IRDA_DEBUG(2, __FUNCTION__"(), Probing for %s ...\n", + chip->name); + + /* Try all config registers for this chip */ + for (cfg=0; cfg<3; cfg++) { + cfg_base = chip->cfg[cfg]; + if (!cfg_base) + continue; + + memset(&info, 0, sizeof(chipio_t)); + info.cfg_base = cfg_base; + info.fir_base = io[i]; + info.dma = dma[i]; + info.irq = irq[i]; + + /* Read index register */ + reg = inb(cfg_base); + if (reg == 0xff) { + IRDA_DEBUG(2, __FUNCTION__ + "() no chip at 0x%03x\n", cfg_base); + continue; + } + + /* Read chip identification register */ + outb(chip->cid_index, cfg_base); + id = inb(cfg_base+1); + if ((id & chip->cid_mask) == chip->cid_value) { + IRDA_DEBUG(2, __FUNCTION__ + "() Found %s chip, revision=%d\n", + chip->name, id & ~chip->cid_mask); + /* + * If the user supplies the base address, then + * we init the chip, if not we probe the values + * set by the BIOS + */ + if (io[i] < 2000) { + chip->init(chip, &info); + } else + chip->probe(chip, &info); + if (nsc_ircc_open(i, &info) == 0) + ret = 0; + i++; + } else { + IRDA_DEBUG(2, __FUNCTION__ + "(), Wrong chip id=0x%02x\n", id); + } + } + + } #ifdef CONFIG_APM - apm_register_callback(nsc_fir_apmproc); + /* Make sure at least one chip was found before enabling APM */ + if (ret == 0) + apm_register_callback(nsc_ircc_apmproc); #endif /* CONFIG_APM */ - for (i=0; (io[i] < 2000) && (i < 5); i++) { - ioaddr = io[i]; - if (check_region(ioaddr, CHIP_IO_EXTENT) < 0) - continue; - if (nsc_fir_open(i, io[i], io2[i], irq[i], dma[i]) == 0) - { -#ifdef CONFIG_NSC_FIR_MULTIPLE - ret = 0; -#else - return 0; -#endif - } - } - return ret; } /* - * Function nsc_fir_cleanup () + * Function nsc_ircc_cleanup () * * Close all configured chips * */ #ifdef MODULE -static void nsc_fir_cleanup(void) +static void nsc_ircc_cleanup(void) { int i; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - #ifdef CONFIG_APM - apm_unregister_callback(nsc_fir_apmproc); + apm_unregister_callback(nsc_ircc_apmproc); #endif /* CONFIG_APM */ - - for (i=0; i < 5; i++) { + for (i=0; i < 4; i++) { if (dev_self[i]) - nsc_fir_close(dev_self[i]); + nsc_ircc_close(dev_self[i]); } } #endif /* MODULE */ /* - * Function nsc_fir_open (iobase, irq) + * Function nsc_ircc_open (iobase, irq) * * Open driver instance * */ -static int -nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, - unsigned int irq, unsigned int dma) +static int nsc_ircc_open(int i, chipio_t *info) { struct net_device *dev; - struct nsc_fir_cb *self; - int dongle_id; + struct nsc_ircc_cb *self; int ret; int err; IRDA_DEBUG(2, __FUNCTION__ "()\n"); - if ((dongle_id = nsc_fir_probe(iobase, board_addr, irq, dma)) == -1) + if ((nsc_ircc_setup(info)) == -1) return -1; - /* - * Allocate new instance of the driver - */ - self = kmalloc(sizeof(struct nsc_fir_cb), GFP_KERNEL); + /* Allocate new instance of the driver */ + self = kmalloc(sizeof(struct nsc_ircc_cb), GFP_KERNEL); if (self == NULL) { ERROR(__FUNCTION__ "(), can't allocate memory for " "control block!\n"); return -ENOMEM; } - memset(self, 0, sizeof(struct nsc_fir_cb)); + memset(self, 0, sizeof(struct nsc_ircc_cb)); spin_lock_init(&self->lock); /* Need to store self somewhere */ dev_self[i] = self; + self->index = i; /* Initialize IO */ - self->io.iobase = iobase; - self->io.irq = irq; - self->io.io_ext = CHIP_IO_EXTENT; - self->io.dma = dma; + self->io.cfg_base = info->cfg_base; + self->io.fir_base = info->fir_base; + self->io.irq = info->irq; + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.dma = info->dma; self->io.fifo_size = 32; - - /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + + /* Reserve the ioports that we need */ + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { - IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); - /* nsc_fir_cleanup(self->self); */ + WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n", + self->io.fir_base); + dev_self[i] = NULL; + kfree(self); return -ENODEV; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -268,13 +316,16 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, /* Allocate memory if needed */ self->rx_buff.head = (__u8 *) kmalloc(self->rx_buff.truesize, GFP_KERNEL|GFP_DMA); - if (self->rx_buff.head == NULL) + if (self->rx_buff.head == NULL) { + kfree(self); return -ENOMEM; + } memset(self->rx_buff.head, 0, self->rx_buff.truesize); self->tx_buff.head = (__u8 *) kmalloc(self->tx_buff.truesize, GFP_KERNEL|GFP_DMA); if (self->tx_buff.head == NULL) { + kfree(self); kfree(self->rx_buff.head); return -ENOMEM; } @@ -298,12 +349,12 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, self->netdev = dev; /* Override the network functions we need to use */ - dev->init = nsc_fir_net_init; - dev->hard_start_xmit = nsc_fir_hard_xmit_sir; - dev->open = nsc_fir_net_open; - dev->stop = nsc_fir_net_close; - dev->do_ioctl = nsc_fir_net_ioctl; - dev->get_stats = nsc_fir_net_get_stats; + dev->init = nsc_ircc_net_init; + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; + dev->open = nsc_ircc_net_open; + dev->stop = nsc_ircc_net_close; + dev->do_ioctl = nsc_ircc_net_ioctl; + dev->get_stats = nsc_ircc_net_get_stats; rtnl_lock(); err = register_netdevice(dev); @@ -312,23 +363,33 @@ nsc_fir_open(int i, unsigned int iobase, unsigned int board_addr, ERROR(__FUNCTION__ "(), register_netdev() failed!\n"); return -1; } - MESSAGE("IrDA: Registered device %s\n", dev->name); + + /* Check if user has supplied the dongle id or not */ + if (!dongle_id) { + dongle_id = nsc_ircc_read_dongle_id(self->io.fir_base); + + MESSAGE("%s, Found dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } else { + MESSAGE("%s, Using dongle: %s\n", driver_name, + dongle_types[dongle_id]); + } self->io.dongle_id = dongle_id; - nsc_fir_init_dongle_interface(iobase, dongle_id); + nsc_ircc_init_dongle_interface(self->io.fir_base, dongle_id); return 0; } #ifdef MODULE /* - * Function nsc_fir_close (self) + * Function nsc_ircc_close (self) * * Close driver instance * */ -static int nsc_fir_close(struct nsc_fir_cb *self) +static int nsc_ircc_close(struct nsc_ircc_cb *self) { int iobase; @@ -336,7 +397,7 @@ static int nsc_fir_close(struct nsc_fir_cb *self) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Remove netdevice */ if (self->netdev) { @@ -347,8 +408,8 @@ static int nsc_fir_close(struct nsc_fir_cb *self) /* Release the PORT that this driver is using */ IRDA_DEBUG(4, __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -356,37 +417,39 @@ static int nsc_fir_close(struct nsc_fir_cb *self) if (self->rx_buff.head) kfree(self->rx_buff.head); + dev_self[self->index] = NULL; kfree(self); - + return 0; } #endif /* MODULE */ /* - * Function nsc_fir_init_807 (iobase, board_addr, irq, dma) + * Function nsc_ircc_init_108 (iobase, cfg_base, irq, dma) * * Initialize the NSC '108 chip * */ -static void nsc_fir_init_807(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_init_108(nsc_chip_t *chip, chipio_t *info) { + int cfg_base = info->cfg_base; __u8 temp=0; - outb(2, board_addr); /* Mode Control Register (MCTL) */ - outb(0x00, board_addr+1); /* Disable device */ + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x00, cfg_base+1); /* Disable device */ /* Base Address and Interrupt Control Register (BAIC) */ - outb(0, board_addr); - switch (iobase) { - case 0x3e8: outb(0x14, board_addr+1); break; - case 0x2e8: outb(0x15, board_addr+1); break; - case 0x3f8: outb(0x16, board_addr+1); break; - case 0x2f8: outb(0x17, board_addr+1); break; + outb(0, cfg_base); + switch (info->fir_base) { + case 0x3e8: outb(0x14, cfg_base+1); break; + case 0x2e8: outb(0x15, cfg_base+1); break; + case 0x3f8: outb(0x16, cfg_base+1); break; + case 0x2f8: outb(0x17, cfg_base+1); break; default: ERROR(__FUNCTION__ "(), invalid base_address"); } /* Control Signal Routing Register (CSRT) */ - switch (irq) { + switch (info->irq) { case 3: temp = 0x01; break; case 4: temp = 0x02; break; case 5: temp = 0x03; break; @@ -396,90 +459,242 @@ static void nsc_fir_init_807(int iobase, int board_addr, int irq, int dma) case 15: temp = 0x07; break; default: ERROR(__FUNCTION__ "(), invalid irq"); } - outb(1, board_addr); + outb(1, cfg_base); - switch (dma) { - case 0: outb(0x08+temp, board_addr+1); break; - case 1: outb(0x10+temp, board_addr+1); break; - case 3: outb(0x18+temp, board_addr+1); break; + switch (info->dma) { + case 0: outb(0x08+temp, cfg_base+1); break; + case 1: outb(0x10+temp, cfg_base+1); break; + case 3: outb(0x18+temp, cfg_base+1); break; default: ERROR(__FUNCTION__ "(), invalid dma"); } - outb(2, board_addr); /* Mode Control Register (MCTL) */ - outb(0x03, board_addr+1); /* Enable device */ + outb(2, cfg_base); /* Mode Control Register (MCTL) */ + outb(0x03, cfg_base+1); /* Enable device */ + + return 0; } /* - * Function nsc_fir_init_338 (iobase, board_addr, irq, dma) + * Function nsc_ircc_probe_108 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_108(nsc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + int reg; + + /* Read address and interrupt control register (BAIC) */ + outb(CFG_BAIC, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x03) { + case 0: + info->fir_base = 0x3e8; + break; + case 1: + info->fir_base = 0x2e8; + break; + case 2: + info->fir_base = 0x3f8; + break; + case 3: + info->fir_base = 0x2f8; + break; + } + info->sir_base = info->fir_base; + IRDA_DEBUG(2, __FUNCTION__ "(), probing fir_base=0x%03x\n", + info->fir_base); + + /* Read control signals routing register (CSRT) */ + outb(CFG_CSRT, cfg_base); + reg = inb(cfg_base+1); + + switch (reg & 0x07) { + case 0: + info->irq = -1; + break; + case 1: + info->irq = 3; + break; + case 2: + info->irq = 4; + break; + case 3: + info->irq = 5; + break; + case 4: + info->irq = 7; + break; + case 5: + info->irq = 9; + break; + case 6: + info->irq = 11; + break; + case 7: + info->irq = 15; + break; + } + IRDA_DEBUG(2, __FUNCTION__ "(), probing irq=%d\n", info->irq); + + /* Currently we only read Rx DMA but it will also be used for Tx */ + switch ((reg >> 3) & 0x03) { + case 0: + info->dma = -1; + break; + case 1: + info->dma = 0; + break; + case 2: + info->dma = 1; + break; + case 3: + info->dma = 3; + break; + } + IRDA_DEBUG(2, __FUNCTION__ "(), probing dma=%d\n", info->dma); + + /* Read mode control register (MCTL) */ + outb(CFG_MCTL, cfg_base); + reg = inb(cfg_base+1); + + info->enabled = reg & 0x01; + info->suspended = !((reg >> 1) & 0x01); + + return 0; +} + +/* + * Function nsc_ircc_init_338 (chip, info) * * Initialize the NSC '338 chip. Remember that the 87338 needs two * consecutive writes to the data registers while CPU interrupts are * disabled. The 97338 does not require this, but shouldn't be any * harm if we do it anyway. */ -static void nsc_fir_init_338(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_init_338(nsc_chip_t *chip, chipio_t *info) { /* No init yet */ + + return 0; } -static int nsc_fir_find_chip(int board_addr) +/* + * Function nsc_ircc_probe_338 (chip, info) + * + * + * + */ +static int nsc_ircc_probe_338(nsc_chip_t *chip, chipio_t *info) { - __u8 index, id; + int cfg_base = info->cfg_base; + int reg, com = 0; + int pnp; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); + /* Read funtion enable register (FER) */ + outb(CFG_FER, cfg_base); + reg = inb(cfg_base+1); - /* Read index register */ - index = inb(board_addr); - if (index == 0xff) { - IRDA_DEBUG(0, __FUNCTION__ "(), no chip at 0x%03x\n", - board_addr); - return -1; - } + info->enabled = (reg >> 2) & 0x01; - /* Read chip identification register (SID) for the PC97338 */ - outb(8, board_addr); - id = inb(board_addr+1); - if ((id & 0xf0) == PC97338) { - MESSAGE("%s, Found NSC PC97338 chip, revision=%d\n", - driver_name, id & 0x0f); - return PC97338; - } - - /* Read device identification (DID) for the PC87108 */ - outb(5, board_addr); - id = inb(board_addr+1); - if ((id & 0xf0) == PC87108) { - MESSAGE("%s, Found NSC PC87108 chip, revision=%d\n", - driver_name, id & 0x0f); - return PC87108; + /* Check if we are in Legacy or PnP mode */ + outb(CFG_PNP0, cfg_base); + reg = inb(cfg_base+1); + + pnp = (reg >> 4) & 0x01; + if (pnp) { + IRDA_DEBUG(2, "(), Chip is in PnP mode\n"); + outb(0x46, cfg_base); + reg = (inb(cfg_base+1) & 0xfe) << 2; + + outb(0x47, cfg_base); + reg |= ((inb(cfg_base+1) & 0xfc) << 8); + + info->fir_base = reg; + } else { + /* Read function address register (FAR) */ + outb(CFG_FAR, cfg_base); + reg = inb(cfg_base+1); + + switch ((reg >> 4) & 0x03) { + case 0: + info->fir_base = 0x3f8; + break; + case 1: + info->fir_base = 0x2f8; + break; + case 2: + com = 3; + break; + case 3: + com = 4; + break; + } + + if (com) { + switch ((reg >> 6) & 0x03) { + case 0: + if (com == 3) + info->fir_base = 0x3e8; + else + info->fir_base = 0x2e8; + break; + case 1: + if (com == 3) + info->fir_base = 0x338; + else + info->fir_base = 0x238; + break; + case 2: + if (com == 3) + info->fir_base = 0x2e8; + else + info->fir_base = 0x2e0; + break; + case 3: + if (com == 3) + info->fir_base = 0x220; + else + info->fir_base = 0x228; + break; + } + } } + info->sir_base = info->fir_base; + + /* Read PnP register 1 (PNP1) */ + outb(CFG_PNP1, cfg_base); + reg = inb(cfg_base+1); + + info->irq = reg >> 4; + + /* Read PnP register 3 (PNP3) */ + outb(CFG_PNP3, cfg_base); + reg = inb(cfg_base+1); + + info->dma = (reg & 0x07) - 1; + + /* Read power and test register (PTR) */ + outb(CFG_PTR, cfg_base); + reg = inb(cfg_base+1); - return -1; + info->suspended = reg & 0x01; + + return 0; } /* - * Function nsc_fir_probe (iobase, board_addr, irq, dma) + * Function nsc_ircc_setup (info) * * Returns non-negative on success. * */ -static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) +static int nsc_ircc_setup(chipio_t *info) { int version; - __u8 chip; - - chip = nsc_fir_find_chip(board_addr); - switch (chip) { - case PC87108: - nsc_fir_init_807(iobase, board_addr, irq, dma); - break; - case PC97338: - nsc_fir_init_338(iobase, board_addr, irq, dma); - break; - default: - /* Found no chip */ - return -1; - } + int iobase = info->fir_base; /* Read the Module ID */ switch_bank(iobase, BANK3); @@ -490,23 +705,13 @@ static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) ERROR("%s, Wrong chip version %02x\n", driver_name, version); return -1; } - MESSAGE("%s, Found chip at base=0x%04x\n", driver_name, board_addr); + MESSAGE("%s, Found chip at base=0x%03x\n", driver_name, + info->cfg_base); /* Switch to advanced mode */ switch_bank(iobase, BANK2); outb(ECR1_EXT_SL, iobase+ECR1); switch_bank(iobase, BANK0); - - /* Check if user has supplied the dongle id or not */ - if (!dongle_id) { - dongle_id = nsc_fir_read_dongle_id(iobase); - - MESSAGE("%s, Found dongle: %s\n", driver_name, - dongle_types[dongle_id]); - } else { - MESSAGE("%s, Using dongle: %s\n", driver_name, - dongle_types[dongle_id]); - } /* Set FIFO threshold to TX17, RX16, reset and enable FIFO's */ switch_bank(iobase, BANK0); @@ -533,24 +738,21 @@ static int nsc_fir_probe(int iobase, int board_addr, int irq, int dma) switch_bank(iobase, BANK0); outb(IER_RXHDL_IE, iobase+IER); - return dongle_id; + return 0; } /* - * Function nsc_fir_read_dongle_id (void) + * Function nsc_ircc_read_dongle_id (void) * * Try to read dongle indentification. This procedure needs to be executed * once after power-on/reset. It also needs to be used whenever you suspect * that the user may have plugged/unplugged the IrDA Dongle. - * */ -static int nsc_fir_read_dongle_id (int iobase) +static int nsc_ircc_read_dongle_id (int iobase) { int dongle_id; __u8 bank; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - bank = inb(iobase+BSR); /* Select Bank 7 */ @@ -568,8 +770,7 @@ static int nsc_fir_read_dongle_id (int iobase) #ifdef BROKEN_DONGLE_ID if (dongle_id == 0x0a) dongle_id = 0x09; -#endif - +#endif /* Go back to bank 0 before returning */ switch_bank(iobase, BANK0); @@ -579,14 +780,14 @@ static int nsc_fir_read_dongle_id (int iobase) } /* - * Function nsc_fir_init_dongle_interface (iobase, dongle_id) + * Function nsc_ircc_init_dongle_interface (iobase, dongle_id) * * This function initializes the dongle for the transceiver that is * used. This procedure needs to be executed once after * power-on/reset. It also needs to be used whenever you suspect that * the dongle is changed. */ -static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) +static void nsc_ircc_init_dongle_interface (int iobase, int dongle_id) { int bank; @@ -625,11 +826,11 @@ static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) dongle_types[dongle_id]); break; case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - IRDA_DEBUG(0, __FUNCTION__ "(), %s not supported yet\n", - dongle_types[dongle_id]); + IRDA_DEBUG(0, __FUNCTION__ "(), %s\n", + dongle_types[dongle_id]); break; case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - outb_p(0x28, iobase+7); /* Set irsl[0-2] as output */ + outb(0x28, iobase+7); /* Set irsl[0-2] as output */ break; case 0x0A: /* same as */ case 0x0B: /* Reserved */ @@ -668,18 +869,16 @@ static void nsc_fir_init_dongle_interface (int iobase, int dongle_id) } /* set_up_dongle_interface */ /* - * Function nsc_fir_change_dongle_speed (iobase, speed, dongle_id) + * Function nsc_ircc_change_dongle_speed (iobase, speed, dongle_id) * * Change speed of the attach dongle * */ -static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) +static void nsc_ircc_change_dongle_speed(int iobase, int speed, int dongle_id) { unsigned long flags; __u8 bank; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - /* Save current bank */ bank = inb(iobase+BSR); @@ -714,11 +913,14 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) dongle_types[dongle_id]); break; case 0x08: /* HP HSDL-2300, HP HSDL-3600/HSDL-3610 */ - IRDA_DEBUG(0, __FUNCTION__ "(), %s not supported yet\n", + IRDA_DEBUG(0, __FUNCTION__ "(), %s\n", dongle_types[dongle_id]); + outb(0x00, iobase+4); + if (speed > 115200) + outb(0x01, iobase+4); + break; case 0x09: /* IBM31T1100 or Temic TFDS6000/TFDS6500 */ - switch_bank(iobase, BANK7); - outb_p(0x01, iobase+4); + outb(0x01, iobase+4); if (speed == 4000000) { save_flags(flags); @@ -727,7 +929,7 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) outb(0x80, iobase+4); restore_flags(flags); } else - outb_p(0x00, iobase+4); + outb(0x00, iobase+4); break; case 0x0A: /* same as */ case 0x0B: /* Reserved */ @@ -754,12 +956,12 @@ static void nsc_fir_change_dongle_speed(int iobase, int speed, int dongle_id) } /* - * Function nsc_fir_change_speed (self, baud) + * Function nsc_ircc_change_speed (self, baud) * * Change the speed of the device * */ -static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) +static void nsc_ircc_change_speed(struct nsc_ircc_cb *self, __u32 speed) { struct net_device *dev = self->netdev; __u8 mcr = MCR_SIR; @@ -770,7 +972,7 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -821,7 +1023,7 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) outb(mcr | MCR_TX_DFR, iobase+MCR); /* Give some hits to the transceiver */ - nsc_fir_change_dongle_speed(iobase, speed, self->io.dongle_id); + nsc_ircc_change_dongle_speed(iobase, speed, self->io.dongle_id); /* Set FIFO threshold to TX17, RX16 */ switch_bank(iobase, BANK0); @@ -844,12 +1046,12 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) switch_bank(iobase, BANK0); if (speed > 115200) { /* Install FIR xmit handler */ - dev->hard_start_xmit = nsc_fir_hard_xmit_fir; + dev->hard_start_xmit = nsc_ircc_hard_xmit_fir; outb(IER_SFIF_IE, iobase+IER); - nsc_fir_dma_receive(self); + nsc_ircc_dma_receive(self); } else { /* Install SIR xmit handler */ - dev->hard_start_xmit = nsc_fir_hard_xmit_sir; + dev->hard_start_xmit = nsc_ircc_hard_xmit_sir; outb(IER_RXHDL_IE, iobase+IER); } @@ -858,24 +1060,24 @@ static void nsc_fir_change_speed(struct nsc_fir_cb *self, __u32 speed) } /* - * Function nsc_fir_hard_xmit (skb, dev) + * Function nsc_ircc_hard_xmit (skb, dev) * * Transmit the frame! * */ -static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) +static int nsc_ircc_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int iobase; __u32 speed; __u8 bank; - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) @@ -894,6 +1096,8 @@ static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, self->tx_buff.truesize); + + self->stats.tx_bytes += self->tx_buff.len; /* Add interrupt on tx low level (will fire immediately) */ switch_bank(iobase, BANK0); @@ -909,20 +1113,17 @@ static int nsc_fir_hard_xmit_sir(struct sk_buff *skb, struct net_device *dev) return 0; } -static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) +static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int iobase; __u32 speed; __u8 bank; int mtt, diff; - self = (struct nsc_fir_cb *) dev->priv; - - ASSERT(self != NULL, return 0;); - - iobase = self->io.iobase; + self = (struct nsc_ircc_cb *) dev->priv; + iobase = self->io.fir_base; /* Lock transmit buffer */ if (irda_lock((void *) &dev->tbusy) == FALSE) @@ -942,6 +1143,8 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) self->tx_fifo.queue[self->tx_fifo.free].len = skb->len; self->tx_fifo.tail += skb->len; + self->stats.tx_bytes += skb->len; + memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, skb->len); @@ -950,10 +1153,11 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) /* Start transmit only if there is currently no transmit going on */ if (self->tx_fifo.len == 1) { + /* Check if we must wait the min turn time or not */ mtt = irda_get_mtt(skb); if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); + get_fast_time(&self->now); diff = self->now.tv_usec - self->stamp.tv_usec; if (diff < 0) diff += 1000000; @@ -963,9 +1167,15 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) */ if (mtt > diff) { mtt -= diff; + + /* + * Use timer if delay larger than 125 us, and + * use udelay for smaller values which should + * be acceptable + */ if (mtt > 125) { /* Adjust for timer resolution */ - mtt = mtt / 125 + 1; + mtt = mtt / 125; /* Setup timer */ switch_bank(iobase, BANK4); @@ -985,34 +1195,35 @@ static int nsc_fir_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) } else udelay(mtt); } - } - + } /* Enable DMA interrupt */ switch_bank(iobase, BANK0); outb(IER_DMA_IE, iobase+IER); - nsc_fir_dma_xmit(self, iobase); + + /* Transmit frame */ + nsc_ircc_dma_xmit(self, iobase); } + out: /* Not busy transmitting anymore if window is not full */ - if (self->tx_fifo.len < MAX_WINDOW) + if (self->tx_fifo.free < MAX_TX_WINDOW) dev->tbusy = 0; - out: + /* Restore bank register */ outb(bank, iobase+BSR); spin_unlock_irqrestore(&self->lock, flags); - dev_kfree_skb(skb); return 0; } /* - * Function nsc_fir_dma_xmit (self, iobase) + * Function nsc_ircc_dma_xmit (self, iobase) * * Transmit data using DMA * */ -static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase) +static void nsc_ircc_dma_xmit(struct nsc_ircc_cb *self, int iobase) { int bsr; @@ -1043,13 +1254,13 @@ static void nsc_fir_dma_xmit(struct nsc_fir_cb *self, int iobase) } /* - * Function nsc_fir_pio_xmit (self, iobase) + * Function nsc_ircc_pio_xmit (self, iobase) * * Transmit data using PIO. Returns the number of bytes that actually * got transfered * */ -static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) +static int nsc_ircc_pio_write(int iobase, __u8 *buf, int len, int fifo_size) { int actual = 0; __u8 bank; @@ -1064,6 +1275,7 @@ static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) IRDA_DEBUG(4, __FUNCTION__ "(), warning, FIFO not empty yet!\n"); + /* FIFO may still be filled to the Tx interrupt threshold */ fifo_size -= 17; } @@ -1083,13 +1295,13 @@ static int nsc_fir_pio_write(int iobase, __u8 *buf, int len, int fifo_size) } /* - * Function nsc_fir_dma_xmit_complete (self) + * Function nsc_ircc_dma_xmit_complete (self) * * The transfer of a frame in finished. This function will only be called * by the interrupt handler * */ -static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) +static int nsc_ircc_dma_xmit_complete(struct nsc_ircc_cb *self) { int iobase; __u8 bank; @@ -1097,7 +1309,7 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) IRDA_DEBUG(2, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current bank */ bank = inb(iobase+BSR); @@ -1115,11 +1327,11 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) outb(ASCR_TXUR, iobase+ASCR); } else { self->stats.tx_packets++; - self->stats.tx_bytes += self->tx_buff.len; } - + + /* Check if we need to change the speed */ if (self->new_speed) { - nsc_fir_change_speed(self, self->new_speed); + nsc_ircc_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -1129,17 +1341,24 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) /* Any frames to be sent back-to-back? */ if (self->tx_fifo.len) { - nsc_fir_dma_xmit(self, iobase); + nsc_ircc_dma_xmit(self, iobase); /* Not finished yet! */ ret = FALSE; + } else { + /* Reset Tx FIFO info */ + self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; + self->tx_fifo.tail = self->tx_buff.head; } - /* Not busy transmitting anymore */ - self->netdev->tbusy = 0; + /* Make sure we have room for more frames */ + if (self->tx_fifo.free < MAX_TX_WINDOW) { + /* Not busy transmitting anymore */ + self->netdev->tbusy = 0; - /* Tell the network layer, that we can accept more frames */ - mark_bh(NET_BH); + /* Tell the network layer, that we can accept more frames */ + mark_bh(NET_BH); + } /* Restore bank */ outb(bank, iobase+BSR); @@ -1148,20 +1367,18 @@ static int nsc_fir_dma_xmit_complete(struct nsc_fir_cb *self) } /* - * Function nsc_fir_dma_receive (self) + * Function nsc_ircc_dma_receive (self) * * Get ready for receiving a frame. The device will initiate a DMA * if it starts to receive a frame. * */ -static int nsc_fir_dma_receive(struct nsc_fir_cb *self) +static int nsc_ircc_dma_receive(struct nsc_ircc_cb *self) { int iobase; __u8 bsr; - ASSERT(self != NULL, return -1;); - - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Reset Tx FIFO info */ self->tx_fifo.len = self->tx_fifo.ptr = self->tx_fifo.free = 0; @@ -1184,7 +1401,9 @@ static int nsc_fir_dma_receive(struct nsc_fir_cb *self) /* Reset Rx FIFO. This will also flush the ST_FIFO */ switch_bank(iobase, BANK0); outb(FCR_RXSR|FCR_FIFO_EN, iobase+FCR); - self->st_fifo.len = self->st_fifo.tail = self->st_fifo.head = 0; + + self->st_fifo.len = self->st_fifo.pending_bytes = 0; + self->st_fifo.tail = self->st_fifo.head = 0; setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, DMA_RX_MODE); @@ -1200,18 +1419,18 @@ static int nsc_fir_dma_receive(struct nsc_fir_cb *self) } /* - * Function nsc_fir_dma_receive_complete (self) + * Function nsc_ircc_dma_receive_complete (self) * * Finished with receiving frames * * */ -static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) +static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) { - struct sk_buff *skb; struct st_fifo *st_fifo; - __u8 bank; + struct sk_buff *skb; __u8 status; + __u8 bank; int len; st_fifo = &self->st_fifo; @@ -1219,22 +1438,27 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) /* Save current bank */ bank = inb(iobase+BSR); - /* Read status FIFO */ + /* Read all entries in status FIFO */ switch_bank(iobase, BANK5); while ((status = inb(iobase+FRM_ST)) & FRM_ST_VLD) { - st_fifo->entries[st_fifo->tail].status = status; + /* We must empty the status FIFO no matter what */ + len = inb(iobase+RFLFL) | ((inb(iobase+RFLFH) & 0x1f) << 8); - st_fifo->entries[st_fifo->tail].len = inb(iobase+RFLFL); - st_fifo->entries[st_fifo->tail].len |= inb(iobase+RFLFH) << 8; - + if (st_fifo->tail >= MAX_RX_WINDOW) + continue; + + st_fifo->entries[st_fifo->tail].status = status; + st_fifo->entries[st_fifo->tail].len = len; + st_fifo->pending_bytes += len; st_fifo->tail++; st_fifo->len++; } /* Try to process all entries in status FIFO */ - while (st_fifo->len) { + while (st_fifo->len > 0) { /* Get first entry */ status = st_fifo->entries[st_fifo->head].status; len = st_fifo->entries[st_fifo->head].len; + st_fifo->pending_bytes -= len; st_fifo->head++; st_fifo->len--; @@ -1265,36 +1489,46 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) if (status & FRM_ST_OVR2) self->stats.rx_fifo_errors++; } else { - /* Check if we have transfered all data to memory */ - switch_bank(iobase, BANK0); - if (inb(iobase+LSR) & LSR_RXDA) { - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - - /* Restore bank register */ - outb(bank, iobase+BSR); + /* + * First we must make sure that the frame we + * want to deliver is all in main memory. If we + * cannot tell, then we check if the Rx FIFO is + * empty. If not then we will have to take a nap + * and try again later. + */ + if (st_fifo->pending_bytes < self->io.fifo_size) { + switch_bank(iobase, BANK0); + if (inb(iobase+LSR) & LSR_RXDA) { + /* Put this entry back in fifo */ + st_fifo->head--; + st_fifo->len++; + st_fifo->pending_bytes += len; + st_fifo->entries[st_fifo->head].status = status; + st_fifo->entries[st_fifo->head].len = len; - return FALSE; /* I'll be back! */ + /* Restore bank register */ + outb(bank, iobase+BSR); + + return FALSE; /* I'll be back! */ + } } /* - * Remember when we received this frame, so we can + * Remember the time we received this frame, so we can * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + get_fast_time(&self->stamp); skb = dev_alloc_skb(len+1); if (skb == NULL) { WARNING(__FUNCTION__ "(), memory squeeze, " "dropping frame.\n"); - + self->stats.rx_dropped++; + /* Restore bank register */ outb(bank, iobase+BSR); - + return FALSE; } @@ -1312,6 +1546,7 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) /* Move to next frame */ self->rx_buff.data += len; + self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; @@ -1327,21 +1562,17 @@ static int nsc_fir_dma_receive_complete(struct nsc_fir_cb *self, int iobase) } /* - * Function nsc_fir_pio_receive (self) + * Function nsc_ircc_pio_receive (self) * * Receive all data in receiver FIFO * */ -static void nsc_fir_pio_receive(struct nsc_fir_cb *self) +static void nsc_ircc_pio_receive(struct nsc_ircc_cb *self) { - __u8 byte = 0x00; + __u8 byte; int iobase; - IRDA_DEBUG(4, __FUNCTION__ "()\n"); - - ASSERT(self != NULL, return;); - - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Receive all characters in Rx FIFO */ do { @@ -1352,20 +1583,19 @@ static void nsc_fir_pio_receive(struct nsc_fir_cb *self) } /* - * Function nsc_fir_sir_interrupt (self, eir) + * Function nsc_ircc_sir_interrupt (self, eir) * * Handle SIR interrupt * */ -static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) +static void nsc_ircc_sir_interrupt(struct nsc_ircc_cb *self, int eir) { int actual; - __u8 new_ier = 0; /* Check if transmit FIFO is low on data */ if (eir & EIR_TXLDL_EV) { /* Write data left in transmit buffer */ - actual = nsc_fir_pio_write(self->io.iobase, + actual = nsc_ircc_pio_write(self->io.fir_base, self->tx_buff.data, self->tx_buff.len, self->io.fifo_size); @@ -1376,14 +1606,14 @@ static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) /* Check if finished */ if (self->tx_buff.len > 0) - new_ier |= IER_TXLDL_IE; + self->ier = IER_TXLDL_IE; else { self->netdev->tbusy = 0; /* Unlock */ self->stats.tx_packets++; mark_bh(NET_BH); - new_ier |= IER_TXEMP_IE; + self->ier = IER_TXEMP_IE; } } @@ -1392,56 +1622,62 @@ static __u8 nsc_fir_sir_interrupt(struct nsc_fir_cb *self, int eir) /* Check if we need to change the speed? */ if (self->new_speed) { IRDA_DEBUG(2, __FUNCTION__ "(), Changing speed!\n"); - nsc_fir_change_speed(self, self->new_speed); + nsc_ircc_change_speed(self, self->new_speed); self->new_speed = 0; - } + /* Check if we are going to FIR */ + if (self->io.speed > 115200) { + /* Should wait for status FIFO interrupt */ + self->ier = IER_SFIF_IE; + + /* No need to do anymore SIR stuff */ + return; + } + } /* Turn around and get ready to receive some data */ self->io.direction = IO_RECV; - new_ier |= IER_RXHDL_IE; + self->ier = IER_RXHDL_IE; } /* Rx FIFO threshold or timeout */ if (eir & EIR_RXHDL_EV) { - nsc_fir_pio_receive(self); + nsc_ircc_pio_receive(self); /* Keep receiving */ - new_ier |= IER_RXHDL_IE; + self->ier = IER_RXHDL_IE; } - return new_ier; } /* - * Function nsc_fir_fir_interrupt (self, eir) + * Function nsc_ircc_fir_interrupt (self, eir) * * Handle MIR/FIR interrupt * */ -static __u8 nsc_fir_fir_interrupt(struct nsc_fir_cb *self, int iobase, int eir) +static void nsc_ircc_fir_interrupt(struct nsc_ircc_cb *self, int iobase, + int eir) { - __u8 new_ier = 0; __u8 bank; bank = inb(iobase+BSR); - /* Status event, or end of frame detected in FIFO */ - if (eir & (EIR_SFIF_EV|EIR_LS_EV)) { - if (nsc_fir_dma_receive_complete(self, iobase)) { - + /* Status FIFO event*/ + if (eir & EIR_SFIF_EV) { + if (nsc_ircc_dma_receive_complete(self, iobase)) { /* Wait for next status FIFO interrupt */ - new_ier |= IER_SFIF_IE; + self->ier = IER_SFIF_IE; } else { - /* DMA not finished yet */ - - /* Set timer value, resolution 125 us */ + /* + * DMA not finished yet, so try again later, set + * timer value, resolution 125 us + */ switch_bank(iobase, BANK4); - outb(0x0f, iobase+TMRL); /* 125 us * 15 */ + outb(0x02, iobase+TMRL); /* 2 * 125 us */ outb(0x00, iobase+TMRH); /* Start timer */ outb(IRCR1_TMR_EN, iobase+IRCR1); - - new_ier |= IER_TMR_IE; + self->ier = IER_TMR_IE | IER_SFIF_IE; } } else if (eir & EIR_TMR_EV) { /* Timer finished */ /* Disable timer */ @@ -1452,101 +1688,102 @@ static __u8 nsc_fir_fir_interrupt(struct nsc_fir_cb *self, int iobase, int eir) switch_bank(iobase, BANK0); outb(ASCR_CTE, iobase+ASCR); - /* Check if this is a TX timer interrupt */ + /* Check if this is a Tx timer interrupt */ if (self->io.direction == IO_XMIT) { - nsc_fir_dma_xmit(self, iobase); + nsc_ircc_dma_xmit(self, iobase); /* Interrupt on DMA */ - new_ier |= IER_DMA_IE; + self->ier = IER_DMA_IE; } else { /* Check if DMA has now finished */ - nsc_fir_dma_receive_complete(self, iobase); + nsc_ircc_dma_receive_complete(self, iobase); - new_ier |= IER_SFIF_IE; + self->ier = IER_SFIF_IE; } - } else if (eir & EIR_DMA_EV) { /* Finished with transmission */ - if (nsc_fir_dma_xmit_complete(self)) { + } else if (eir & EIR_DMA_EV) { + /* Finished with all transmissions? */ + if (nsc_ircc_dma_xmit_complete(self)) { /* Check if there are more frames to be transmitted */ if (irda_device_txqueue_empty(self->netdev)) { /* Prepare for receive */ - nsc_fir_dma_receive(self); + nsc_ircc_dma_receive(self); - new_ier = IER_LS_IE|IER_SFIF_IE; + self->ier = IER_SFIF_IE; } } else { /* Not finished yet, so interrupt on DMA again */ - new_ier |= IER_DMA_IE; + self->ier = IER_DMA_IE; } } outb(bank, iobase+BSR); - - return new_ier; } /* - * Function nsc_fir_interrupt (irq, dev_id, regs) + * Function nsc_ircc_interrupt (irq, dev_id, regs) * * An interrupt from the chip has arrived. Time to do some work * */ -static void nsc_fir_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static void nsc_ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct net_device *dev = (struct net_device *) dev_id; - struct nsc_fir_cb *self; - __u8 bsr, eir, ier; + struct nsc_ircc_cb *self; + __u8 bsr, eir; int iobase; if (!dev) { - printk(KERN_WARNING "%s: irq %d for unknown device.\n", - driver_name, irq); + WARNING("%s: irq %d for unknown device.\n", driver_name, irq); return; } - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; spin_lock(&self->lock); dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.fir_base; bsr = inb(iobase+BSR); /* Save current bank */ switch_bank(iobase, BANK0); - ier = inb(iobase+IER); - eir = inb(iobase+EIR) & ier; /* Mask out the interesting ones */ + self->ier = inb(iobase+IER); + eir = inb(iobase+EIR) & self->ier; /* Mask out the interesting ones */ outb(0, iobase+IER); /* Disable interrupts */ if (eir) { /* Dispatch interrupt handler for the current speed */ if (self->io.speed > 115200) - ier = nsc_fir_fir_interrupt(self, iobase, eir); + nsc_ircc_fir_interrupt(self, iobase, eir); else - ier = nsc_fir_sir_interrupt(self, eir); + nsc_ircc_sir_interrupt(self, eir); } - - outb(ier, iobase+IER); /* Restore interrupts */ - outb(bsr, iobase+BSR); /* Restore bank register */ + + outb(self->ier, iobase+IER); /* Restore interrupts */ + outb(bsr, iobase+BSR); /* Restore bank register */ dev->interrupt = 0; spin_unlock(&self->lock); } /* - * Function nsc_fir_is_receiving (self) + * Function nsc_ircc_is_receiving (self) * * Return TRUE is we are currently receiving a frame * */ -static int nsc_fir_is_receiving(struct nsc_fir_cb *self) +static int nsc_ircc_is_receiving(struct nsc_ircc_cb *self) { + unsigned long flags; int status = FALSE; int iobase; __u8 bank; ASSERT(self != NULL, return FALSE;); + spin_lock_irqsave(&self->lock, flags); + if (self->io.speed > 115200) { - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Check if rx FIFO is not empty */ bank = inb(iobase+BSR); @@ -1559,16 +1796,18 @@ static int nsc_fir_is_receiving(struct nsc_fir_cb *self) } else status = (self->rx_buff.state != OUTSIDE_FRAME); + spin_unlock_irqrestore(&self->lock, flags); + return status; } /* - * Function nsc_fir_net_init (dev) + * Function nsc_ircc_net_init (dev) * * Initialize network device * */ -static int nsc_fir_net_init(struct net_device *dev) +static int nsc_ircc_net_init(struct net_device *dev) { IRDA_DEBUG(4, __FUNCTION__ "()\n"); @@ -1581,29 +1820,29 @@ static int nsc_fir_net_init(struct net_device *dev) } /* - * Function nsc_fir_net_open (dev) + * Function nsc_ircc_net_open (dev) * * Start the device * */ -static int nsc_fir_net_open(struct net_device *dev) +static int nsc_ircc_net_open(struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; int iobase; __u8 bank; IRDA_DEBUG(4, __FUNCTION__ "()\n"); ASSERT(dev != NULL, return -1;); - self = (struct nsc_fir_cb *) dev->priv; + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; - - if (request_irq(self->io.irq, nsc_fir_interrupt, 0, dev->name, - (void *) dev)) - { + iobase = self->io.fir_base; + + if (request_irq(self->io.irq, nsc_ircc_interrupt, 0, dev->name, dev)) { + WARNING("%s, unable to allocate irq=%d\n", driver_name, + self->io.irq); return -EAGAIN; } /* @@ -1611,6 +1850,8 @@ static int nsc_fir_net_open(struct net_device *dev) * failure. */ if (request_dma(self->io.dma, dev->name)) { + WARNING("%s, unable to allocate dma=%d\n", driver_name, + self->io.dma); free_irq(self->io.irq, self); return -EAGAIN; } @@ -1642,22 +1883,22 @@ static int nsc_fir_net_open(struct net_device *dev) } /* - * Function nsc_fir_net_close (dev) + * Function nsc_ircc_net_close (dev) * * Stop the device * */ -static int nsc_fir_net_close(struct net_device *dev) +static int nsc_ircc_net_close(struct net_device *dev) { - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; int iobase; __u8 bank; IRDA_DEBUG(4, __FUNCTION__ "()\n"); ASSERT(dev != NULL, return -1;); - self = (struct nsc_fir_cb *) dev->priv; - + + self = (struct nsc_ircc_cb *) dev->priv; ASSERT(self != NULL, return 0;); /* Stop device */ @@ -1669,7 +1910,7 @@ static int nsc_fir_net_close(struct net_device *dev) irlap_close(self->irlap); self->irlap = NULL; - iobase = self->io.iobase; + iobase = self->io.fir_base; disable_dma(self->io.dma); @@ -1692,15 +1933,15 @@ static int nsc_fir_net_close(struct net_device *dev) } /* - * Function nsc_fir_net_ioctl (dev, rq, cmd) + * Function nsc_ircc_net_ioctl (dev, rq, cmd) * * Process IOCTL commands for this device * */ -static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +static int nsc_ircc_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct if_irda_req *irq = (struct if_irda_req *) rq; - struct nsc_fir_cb *self; + struct nsc_ircc_cb *self; unsigned long flags; int ret = 0; @@ -1718,13 +1959,13 @@ static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) switch (cmd) { case SIOCSBANDWIDTH: /* Set bandwidth */ - nsc_fir_change_speed(self, irq->ifr_baudrate); + nsc_ircc_change_speed(self, irq->ifr_baudrate); break; case SIOCSMEDIABUSY: /* Set media busy */ irda_device_set_media_busy(self->netdev, TRUE); break; case SIOCGRECEIVING: /* Check if we are receiving right now */ - irq->ifr_receiving = nsc_fir_is_receiving(self); + irq->ifr_receiving = nsc_ircc_is_receiving(self); break; default: ret = -EOPNOTSUPP; @@ -1735,54 +1976,60 @@ static int nsc_fir_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) return ret; } -static struct net_device_stats *nsc_fir_net_get_stats(struct net_device *dev) +static struct net_device_stats *nsc_ircc_net_get_stats(struct net_device *dev) { - struct nsc_fir_cb *self = (struct nsc_fir_cb *) dev->priv; + struct nsc_ircc_cb *self = (struct nsc_ircc_cb *) dev->priv; return &self->stats; } #ifdef CONFIG_APM -static void nsc_fir_suspend(struct nsc_fir_cb *self) +static void nsc_ircc_suspend(struct nsc_ircc_cb *self) { - int i = 10; - MESSAGE("%s, Suspending\n", driver_name); - if (self->suspend) + if (self->io.suspended) return; - self->suspend = 1; -} + nsc_ircc_net_close(self->netdev); + self->io.suspended = 1; +} -static void nsc_fir_wakeup(struct nsc_fir_cb *self) +static void nsc_ircc_wakeup(struct nsc_ircc_cb *self) { struct net_device *dev = self->netdev; - unsigned long flags; + int iobase; - if (!self->suspend) + if (!self->io.suspended) return; - save_flags(flags); - cli(); + iobase = self->io.fir_base; - restore_flags(flags); + /* Switch to advanced mode */ + switch_bank(iobase, BANK2); + outb(ECR1_EXT_SL, iobase+ECR1); + switch_bank(iobase, BANK0); + + nsc_ircc_net_open(self->netdev); + MESSAGE("%s, Waking up\n", driver_name); + + self->io.suspended = 0; } -static int nsc_fir_apmproc(apm_event_t event) +static int nsc_ircc_apmproc(apm_event_t event) { - static int down = 0; /* Filter out double events */ + static int down = 0; /* Filter out double events */ int i; switch (event) { case APM_SYS_SUSPEND: case APM_USER_SUSPEND: if (!down) { - for (i = 0; i < 4; i++) { + for (i=0; i<4; i++) { if (dev_self[i]) - nsc_fir_suspend(dev_self[i]); + nsc_ircc_suspend(dev_self[i]); } } down = 1; @@ -1790,9 +2037,9 @@ static int nsc_fir_apmproc(apm_event_t event) case APM_NORMAL_RESUME: case APM_CRITICAL_RESUME: if (down) { - for (i = 0; i < 4; i++) { + for (i=0; i<4; i++) { if (dev_self[i]) - nsc_fir_wakeup(dev_self[i]); + nsc_ircc_wakeup(dev_self[i]); } } down = 0; @@ -1804,22 +2051,22 @@ static int nsc_fir_apmproc(apm_event_t event) #ifdef MODULE MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("NSC FIR IrDA Device Driver"); +MODULE_DESCRIPTION("NSC IrDA Device Driver"); MODULE_PARM(qos_mtt_bits, "i"); -MODULE_PARM(io, "1-4i"); -MODULE_PARM(io2, "1-4i"); +MODULE_PARM(io, "1-4i"); MODULE_PARM(irq, "1-4i"); +MODULE_PARM(dma, "1-4i"); MODULE_PARM(dongle_id, "i"); int init_module(void) { - return nsc_fir_init(); + return nsc_ircc_init(); } void cleanup_module(void) { - nsc_fir_cleanup(); + nsc_ircc_cleanup(); } #endif /* MODULE */ diff --git a/drivers/net/irda/smc-ircc.c b/drivers/net/irda/smc-ircc.c index 1d857c2b064c..030e14c7f918 100644 --- a/drivers/net/irda/smc-ircc.c +++ b/drivers/net/irda/smc-ircc.c @@ -1,12 +1,12 @@ /********************************************************************* * * Filename: smc-ircc.c - * Version: 0.3 + * Version: 0.4 * Description: Driver for the SMC Infrared Communications Controller * Status: Experimental. * Author: Thomas Davis (tadavis@jps.net) * Created at: - * Modified at: Wed Jan 5 12:38:06 2000 + * Modified at: Fri Jan 21 09:41:08 2000 * Modified by: Dag Brattli * * Copyright (c) 1999-2000 Dag Brattli @@ -28,7 +28,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * - * SIO's: SMC FDC37N869, FDC37C669 + * SIO's: SMC FDC37N869, FDC37C669, FDC37N958 * Applicable Models : Fujitsu Lifebook 635t, Sony PCG-505TX * ********************************************************************/ @@ -44,11 +44,16 @@ #include #include #include +#include #include #include #include +#ifdef CONFIG_APM +#include +#endif + #include #include #include @@ -62,10 +67,10 @@ static char *driver_name = "smc-ircc"; #define CHIP_IO_EXTENT 8 -static unsigned int io[] = { 0x2e8, 0x140, 0x118, 0x240 }; -static unsigned int io2[] = { 0x2f8, 0x3e8, 0x2e8, 0x3e8 }; +static unsigned int io[] = { ~0, ~0 }; +static unsigned int io2[] = { 0, 0 }; -static struct ircc_cb *dev_self[] = { NULL, NULL, NULL, NULL}; +static struct ircc_cb *dev_self[] = { NULL, NULL}; /* Some prototypes */ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); @@ -73,17 +78,30 @@ static int ircc_open(int i, unsigned int iobase, unsigned int board_addr); static int ircc_close(struct ircc_cb *self); #endif /* MODULE */ static int ircc_probe(int iobase, int board_addr); -static int ircc_probe_smc(int *ioaddr, int *ioaddr2); -static int ircc_dma_receive(struct ircc_cb *self); -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase); +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info); +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info); +static int ircc_dma_receive(struct ircc_cb *self, int iobase); +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase); static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev); -static void ircc_dma_xmit(struct ircc_cb *self, int iobase); +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs); static void ircc_change_speed(void *priv, __u32 speed); static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs); static int ircc_is_receiving(struct ircc_cb *self); static int ircc_net_open(struct net_device *dev); static int ircc_net_close(struct net_device *dev); +#ifdef CONFIG_APM +static int ircc_apmproc(apm_event_t event); +#endif /* CONFIG_APM */ + +/* These are the currently known SMC chipsets */ +static smc_chip_t chips[] = +{ + { "FDC37C669", 0x55, 0x55, 0x0d, 0x04, ircc_probe_69 }, + { "FDC37N869", 0x55, 0x00, 0x0d, 0x29, ircc_probe_69 }, + { "FDC37N958", 0x55, 0x55, 0x20, 0x09, ircc_probe_58 }, + { NULL } +}; static int ircc_irq=255; static int ircc_dma=255; @@ -102,26 +120,38 @@ static inline void register_bank(int iobase, int bank) */ int __init ircc_init(void) { - int ioaddr, ioaddr2; + static int smcreg[] = { 0x3f0, 0x370 }; + smc_chip_t *chip; + chipio_t info; + int ret = -ENODEV; int i; IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; (io[i] < 2000) && (i < 4); i++) { - int ioaddr = io[i]; - if (check_region(ioaddr, CHIP_IO_EXTENT)) - continue; - if (ircc_open(i, io[i], io2[i]) == 0) - return 0; - } - /* last chance saloon, see what the controller says */ - if (ircc_probe_smc(&ioaddr, &ioaddr2) == 0) { - if (check_region(ioaddr, CHIP_IO_EXTENT) == 0) - if (ircc_open(0, ioaddr, ioaddr2) == 0) - return 0; + /* Probe for all the NSC chipsets we know about */ + for (chip=chips; chip->name ; chip++,i++) { + for (i=0; i<2; i++) { + info.cfg_base = smcreg[i]; + + /* + * First we check if the user has supplied any + * parameters which we should use instead of probed + * values + */ + if (io[i] < 2000) { + info.fir_base = io[i]; + info.sir_base = io2[i]; + } else if (chip->probe(chip, &info) < 0) + continue; + if (check_region(info.fir_base, CHIP_IO_EXTENT) < 0) + continue; + if (check_region(info.sir_base, CHIP_IO_EXTENT) < 0) + continue; + if (ircc_open(i, info.fir_base, info.sir_base) == 0) + ret = 0; + } } - - return -ENODEV; + return ret; } /* @@ -137,7 +167,7 @@ static void ircc_cleanup(void) IRDA_DEBUG(0, __FUNCTION__ "\n"); - for (i=0; i < 4; i++) { + for (i=0; i < 2; i++) { if (dev_self[i]) ircc_close(dev_self[i]); } @@ -150,7 +180,7 @@ static void ircc_cleanup(void) * Open driver instance * */ -static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) +static int ircc_open(int i, unsigned int fir_base, unsigned int sir_base) { struct ircc_cb *self; struct irport_cb *irport; @@ -159,9 +189,9 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) IRDA_DEBUG(0, __FUNCTION__ "\n"); - if ((config = ircc_probe(iobase, iobase2)) == -1) { + if ((config = ircc_probe(fir_base, sir_base)) == -1) { IRDA_DEBUG(0, __FUNCTION__ - "(), addr 0x%04x - no device found!\n", iobase); + "(), addr 0x%04x - no device found!\n", fir_base); return -1; } @@ -180,7 +210,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Need to store self somewhere */ dev_self[i] = self; - irport = irport_open(0, iobase2, config >> 4 & 0x0f); + irport = irport_open(i, sir_base, config >> 4 & 0x0f); if (!irport) return -ENODEV; @@ -190,34 +220,32 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->priv = self; /* Initialize IO */ - self->io.iobase = iobase; - self->io.iobase2 = iobase2; /* Used by irport */ + self->io.fir_base = fir_base; + self->io.sir_base = sir_base; /* Used by irport */ self->io.irq = config >> 4 & 0x0f; if (ircc_irq < 255) { MESSAGE("%s, Overriding IRQ - chip says %d, using %d\n", driver_name, self->io.irq, ircc_irq); self->io.irq = ircc_irq; } - self->io.io_ext = CHIP_IO_EXTENT; - self->io.io_ext2 = 8; /* Used by irport */ + self->io.fir_ext = CHIP_IO_EXTENT; + self->io.sir_ext = 8; /* Used by irport */ self->io.dma = config & 0x0f; if (ircc_dma < 255) { MESSAGE("%s, Overriding DMA - chip says %d, using %d\n", driver_name, self->io.dma, ircc_dma); self->io.dma = ircc_dma; } - self->io.fifo_size = 16; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { - IRDA_DEBUG(0, __FUNCTION__ ": can't get iobase of 0x%03x\n", - self->io.iobase); - /* ircc_cleanup(self->self); */ + IRDA_DEBUG(0, __FUNCTION__ ": can't get fir_base of 0x%03x\n", + self->io.fir_base); + kfree(self); return -ENODEV; } - - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&irport->qos); @@ -229,7 +257,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) irport->qos.min_turn_time.bits = 0x07; irda_qos_bits_to_value(&irport->qos); - irport->flags = IFF_FIR|IFF_SIR|IFF_DMA|IFF_PIO; + irport->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA|IFF_PIO; /* Max DMA buffer size needed = (data_size + 6) * (window_size) + 6; */ self->rx_buff.truesize = 4000; @@ -256,6 +284,7 @@ static int ircc_open(int i, unsigned int iobase, unsigned int iobase2) /* Override the speed change function, since we must control it now */ irport->change_speed = &ircc_change_speed; + irport->interrupt = &ircc_interrupt; self->netdev->open = &ircc_net_open; self->netdev->stop = &ircc_net_close; @@ -279,23 +308,26 @@ static int ircc_close(struct ircc_cb *self) ASSERT(self != NULL, return -1;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_close(self->irport); + /* Stop interrupts */ register_bank(iobase, 0); outb(0, iobase+IRCC_IER); outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + outb(0x00, iobase+IRCC_MASTER); +#if 0 + /* Reset to SIR mode */ register_bank(iobase, 1); - outb(IRCC_CFGA_IRDA_SIR_A|IRCC_CFGA_TX_POLARITY, iobase+IRCC_SCE_CFGA); outb(IRCC_CFGB_IR, iobase+IRCC_SCE_CFGB); - +#endif /* Release the PORT that this driver is using */ - IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", self->io.iobase); + IRDA_DEBUG(0, __FUNCTION__ "(), releasing 0x%03x\n", + self->io.fir_base); - release_region(self->io.iobase, self->io.io_ext); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -310,58 +342,106 @@ static int ircc_close(struct ircc_cb *self) #endif /* MODULE */ /* - * Function ircc_probe_smc (ioaddr, ioaddr2) + * Function ircc_probe_69 (chip, info) * - * Probe the SMC Chip for an IrDA port + * Probes for the SMC FDC37C669 and FDC37N869 * */ -static int ircc_probe_smc(int *ioaddr, int *ioaddr2) +static int ircc_probe_69(smc_chip_t *chip, chipio_t *info) { - static int smcreg[] = { 0x3f0, 0x370 }; + int cfg_base = info->cfg_base; __u8 devid, mode; - __u8 conf_reg; - int ret = -1; + int ret = -ENODEV; int fir_io; - int i; IRDA_DEBUG(0, __FUNCTION__ "()\n"); - for (i = 0; i < 2 && ret == -1; i++) { - conf_reg = smcreg[i]; - - /* Enter configuration */ - outb(0x55, conf_reg); - outb(0x55, conf_reg); - - outb(0x0d, conf_reg); - devid = inb(conf_reg+1); - IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + outb(0x0c, cfg_base); + mode = inb(cfg_base+1); + mode = (mode & 0x38) >> 3; - /* Check for expected device ID; are there others? */ - if (devid == 0x29) { - outb(0x0c, conf_reg); - mode = inb(conf_reg+1); - mode = (mode & 0x38) >> 3; - - /* Value for IR port */ - if (mode && mode < 4) { - /* SIR iobase */ - outb(0x25, conf_reg); - *ioaddr2 = inb(conf_reg+1) << 2; - - /* FIR iobase */ - outb(0x2b, conf_reg); - fir_io = inb(conf_reg+1) << 3; - if (fir_io) { - ret = 0; - *ioaddr = fir_io; - } + /* Value for IR port */ + if (mode && mode < 4) { + /* SIR iobase */ + outb(0x25, cfg_base); + info->sir_base = inb(cfg_base+1) << 2; + + /* FIR iobase */ + outb(0x2b, cfg_base); + fir_io = inb(cfg_base+1) << 3; + if (fir_io) { + ret = 0; + info->fir_base = fir_io; } } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); + + return ret; +} + +/* + * Function ircc_probe_58 (chip, info) + * + * Probes for the SMC FDC37N958 + * + */ +static int ircc_probe_58(smc_chip_t *chip, chipio_t *info) +{ + int cfg_base = info->cfg_base; + __u8 devid; + int ret = -ENODEV; + int fir_io; + + IRDA_DEBUG(0, __FUNCTION__ "()\n"); + + /* Enter configuration */ + outb(chip->entr1, cfg_base); + outb(chip->entr2, cfg_base); + + outb(chip->cid_index, cfg_base); + devid = inb(cfg_base+1); + IRDA_DEBUG(0, __FUNCTION__ "(), devid=0x%02x\n",devid); + + /* Check for expected device ID; are there others? */ + if (devid == chip->cid_value) { + /* Select logical device (UART2) */ + outb(0x07, cfg_base); + outb(0x05, cfg_base + 1); + + /* SIR iobase */ + outb(0x60, cfg_base); + info->sir_base = inb(cfg_base + 1) << 8; + outb(0x61, cfg_base); + info->sir_base |= inb(cfg_base + 1); + + /* Read FIR base */ + outb(0x62, cfg_base); + fir_io = inb(cfg_base + 1) << 8; + outb(0x63, cfg_base); + fir_io |= inb(cfg_base + 1); + outb(0x2b, cfg_base); + if (fir_io) { + ret = 0; + info->fir_base = fir_io; + } + } + + /* Exit configuration */ + outb(0xaa, cfg_base); - /* Exit configuration */ - outb(0xaa, conf_reg); - } return ret; } @@ -371,34 +451,32 @@ static int ircc_probe_smc(int *ioaddr, int *ioaddr2) * Returns non-negative on success. * */ -static int ircc_probe(int iobase, int iobase2) +static int ircc_probe(int fir_base, int sir_base) { - int version = 1; int low, high, chip, config, dma, irq; - - IRDA_DEBUG(0, __FUNCTION__ "\n"); + int iobase = fir_base; + int version = 1; - /* Power on device */ - outb(inb(iobase+IRCC_MASTER) & ~IRCC_MASTER_POWERDOWN, - iobase+IRCC_MASTER); + IRDA_DEBUG(0, __FUNCTION__ "\n"); register_bank(iobase, 3); - high = inb(iobase+IRCC_ID_HIGH); - low = inb(iobase+IRCC_ID_LOW); - chip = inb(iobase+IRCC_CHIP_ID); + high = inb(iobase+IRCC_ID_HIGH); + low = inb(iobase+IRCC_ID_LOW); + chip = inb(iobase+IRCC_CHIP_ID); version = inb(iobase+IRCC_VERSION); - config = inb(iobase+IRCC_INTERFACE); - irq = config >> 4 & 0x0f; - dma = config & 0x0f; + config = inb(iobase+IRCC_INTERFACE); + irq = config >> 4 & 0x0f; + dma = config & 0x0f; if (high == 0x10 && low == 0xb8 && (chip == 0xf1 || chip == 0xf2)) { - IRDA_DEBUG(0, "SMC IrDA Controller found; IrCC version %d.%d, " - "port 0x%04x, dma %d, interrupt %d\n", - chip & 0x0f, version, iobase, dma, irq); + MESSAGE("SMC IrDA Controller found; IrCC version %d.%d, " + "port 0x%03x, dma=%d, irq=%d\n", + chip & 0x0f, version, iobase, dma, irq); } else - return -1; + return -ENODEV; - outb(0, iobase+IRCC_MASTER); + /* Power on device */ + outb(0x00, iobase+IRCC_MASTER); return config; } @@ -411,7 +489,7 @@ static int ircc_probe(int iobase, int iobase2) */ static void ircc_change_speed(void *priv, __u32 speed) { - int iobase, ir_mode, select, fast; + int iobase, ir_mode, ctrl, fast; struct ircc_cb *self = (struct ircc_cb *) priv; struct net_device *dev; @@ -420,49 +498,39 @@ static void ircc_change_speed(void *priv, __u32 speed) ASSERT(self != NULL, return;); dev = self->netdev; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; + outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); + switch (speed) { case 9600: case 19200: case 38400: case 57600: - case 115200: - IRDA_DEBUG(0, __FUNCTION__ - "(), using irport to change speed to %d\n", speed); - - register_bank(iobase, 0); - outb(0, iobase+IRCC_IER); - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); - - dev->hard_start_xmit = &irport_hard_xmit; - - /* We must give the interrupt back to irport */ - self->irport->interrupt = irport_interrupt; - - irport_start(self->irport); - irport_change_speed(self->irport, speed); - return; + case 115200: + ir_mode = IRCC_CFGA_IRDA_SIR_A; + ctrl = 0; + fast = 0; break; case 576000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = 0; + ctrl = IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 576000\n"); break; case 1152000: ir_mode = IRCC_CFGA_IRDA_HDLC; - select = IRCC_1152; + ctrl = IRCC_1152 | IRCC_CRC; fast = 0; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 1152000\n"); break; case 4000000: ir_mode = IRCC_CFGA_IRDA_4PPM; - select = 0; + ctrl = IRCC_CRC; fast = IRCC_LCR_A_FAST; IRDA_DEBUG(0, __FUNCTION__ "(), handling baud of 4000000\n"); break; @@ -471,39 +539,55 @@ static void ircc_change_speed(void *priv, __u32 speed) speed); return; } - - outb(IRCC_MASTER_RESET, iobase+IRCC_MASTER); - + register_bank(iobase, 0); outb(0, iobase+IRCC_IER); - - irport_stop(self->irport); + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + + /* Make special FIR init if necessary */ + if (speed > 115200) { + irport_stop(self->irport); - /* Install FIR transmit handler */ - dev->hard_start_xmit = &ircc_hard_xmit; + /* Install FIR transmit handler */ + dev->hard_start_xmit = &ircc_hard_xmit; - /* Need to steal the interrupt as well */ - self->irport->interrupt = &ircc_interrupt; + /* + * Don't know why we have to do this, but FIR interrupts + * stops working if we remove it. + */ + /* outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); */ + /* Be ready for incomming frames */ + ircc_dma_receive(self, iobase); + } else { + /* Install SIR transmit handler */ + dev->hard_start_xmit = &irport_hard_xmit; + irport_start(self->irport); + + IRDA_DEBUG(0, __FUNCTION__ + "(), using irport to change speed to %d\n", speed); + irport_change_speed(self->irport, speed); + } dev->tbusy = 0; - + register_bank(iobase, 1); outb(((inb(iobase+IRCC_SCE_CFGA) & 0x87) | ir_mode), iobase+IRCC_SCE_CFGA); - - outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_IR), - iobase+IRCC_SCE_CFGB); +#ifdef SMC_669 /* Uses pin 88/89 for Rx/Tx */ + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_COM), + iobase+IRCC_SCE_CFGB); +#else + outb(((inb(iobase+IRCC_SCE_CFGB) & 0x3f) | IRCC_CFGB_MUX_IR), + iobase+IRCC_SCE_CFGB); +#endif (void) inb(iobase+IRCC_FIFO_THRESHOLD); outb(64, iobase+IRCC_FIFO_THRESHOLD); - + register_bank(iobase, 4); - - outb((inb(iobase+IRCC_CONTROL) & 0x30) | select | IRCC_CRC, - iobase+IRCC_CONTROL); - + outb((inb(iobase+IRCC_CONTROL) & 0x30) | ctrl, iobase+IRCC_CONTROL); + register_bank(iobase, 0); - outb(fast, iobase+IRCC_LCR_A); } @@ -517,21 +601,20 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) { struct irport_cb *irport; struct ircc_cb *self; + unsigned long flags; + __u32 speed; int iobase; int mtt; - __u32 speed; irport = (struct irport_cb *) dev->priv; self = (struct ircc_cb *) irport->priv; - ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; - IRDA_DEBUG(2, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, - (int) skb->len); + spin_lock_irqsave(&self->lock, flags); - /* Check if we need to change the speed */ + /* Check if we need to change the speed after this frame */ if ((speed = irda_get_speed(skb)) != self->io.speed) self->new_speed = speed; @@ -541,19 +624,28 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) memcpy(self->tx_buff.head, skb->data, skb->len); - /* Make sure that the length is a multiple of 16 bits */ - if (skb->len & 0x01) - skb->len++; - self->tx_buff.len = skb->len; self->tx_buff.data = self->tx_buff.head; mtt = irda_get_mtt(skb); - if (mtt) - udelay(mtt); - - ircc_dma_xmit(self, iobase); + if (mtt) { + int bofs; + + /* + * Compute who many BOFS (STA or PA's) we need to waste the + * min turn time given the speed of the link. + */ + bofs = mtt * (self->io.speed / 1000) / 8000; + if (bofs > 4095) + bofs = 4095; + + ircc_dma_xmit(self, iobase, bofs); + } else { + /* Transmit frame */ + ircc_dma_xmit(self, iobase, 0); + } + spin_unlock_irqrestore(&self->lock, flags); dev_kfree_skb(skb); return 0; @@ -565,44 +657,49 @@ static int ircc_hard_xmit(struct sk_buff *skb, struct net_device *dev) * Transmit data using DMA * */ -static void ircc_dma_xmit(struct ircc_cb *self, int iobase) +static void ircc_dma_xmit(struct ircc_cb *self, int iobase, int bofs) { - IRDA_DEBUG(2, __FUNCTION__ "\n"); + __u8 ctrl; - ASSERT(self != NULL, return;); - - iobase = self->io.iobase; + IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 1); + outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + iobase+IRCC_SCE_CFGB); - setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, - DMA_TX_MODE); - self->io.direction = IO_XMIT; - outb(0x08, self->io.iobase2+4); - + /* Set BOF additional count for generating the min turn time */ register_bank(iobase, 4); - outb((inb(iobase+IRCC_CONTROL) & 0xf0), iobase+IRCC_CONTROL); - - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); -#if 1 - outb(self->tx_buff.len >> 8, iobase+IRCC_BRICKWALL_TX_CNT_HI); + outb(bofs & 0xff, iobase+IRCC_BOF_COUNT_LO); + ctrl = inb(iobase+IRCC_CONTROL) & 0xf0; + outb(ctrl | ((bofs >> 8) & 0x0f), iobase+IRCC_BOF_COUNT_HI); + + /* Set max Tx frame size */ + outb(self->tx_buff.len >> 8, iobase+IRCC_TX_SIZE_HI); outb(self->tx_buff.len & 0xff, iobase+IRCC_TX_SIZE_LO); -#else - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); -#endif + /* Setup DMA controller (must be done after enabling chip DMA) */ + setup_dma(self->io.dma, self->tx_buff.data, self->tx_buff.len, + DMA_TX_MODE); + + outb(UART_MCR_OUT2, self->io.sir_base + UART_MCR); + /* Enable burst mode chip Tx DMA */ register_bank(iobase, 1); - outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE, - iobase+IRCC_SCE_CFGB); + outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | + IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); + /* Enable interrupt */ + outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); register_bank(iobase, 0); - outb(IRCC_IER_ACTIVE_FRAME | IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); + /* Enable transmit */ + outb(IRCC_LCR_B_SCE_TRANSMIT|IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); } /* @@ -612,39 +709,36 @@ static void ircc_dma_xmit(struct ircc_cb *self, int iobase) * by the interrupt handler * */ -static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) +static void ircc_dma_xmit_complete(struct ircc_cb *self, int iobase) { - int iobase, d; - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return;); - - register_bank(self->io.iobase, 1); - - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - d = get_dma_residue(self->io.dma); - - IRDA_DEBUG(0, __FUNCTION__ - ": dma residue = %d, len=%d, sent=%d\n", - d, self->tx_buff.len, self->tx_buff.len - d); - - iobase = self->io.iobase; +#if 0 + /* Disable Tx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(self->io.fir_base, 1); + outb(inb(self->io.fir_base+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + self->io.fir_base+IRCC_SCE_CFGB); /* Check for underrrun! */ - if (underrun) { + register_bank(iobase, 0); + if (inb(iobase+IRCC_LSR) & IRCC_LSR_UNDERRUN) { self->irport->stats.tx_errors++; - self->irport->stats.tx_fifo_errors++; + self->irport->stats.tx_fifo_errors++; + + /* Reset error condition */ + register_bank(iobase, 0); + outb(IRCC_MASTER_ERROR_RESET, iobase+IRCC_MASTER); + outb(0x00, iobase+IRCC_MASTER); } else { self->irport->stats.tx_packets++; self->irport->stats.tx_bytes += self->tx_buff.len; } + /* Check if it's time to change the speed */ if (self->new_speed) { - ircc_change_speed(self, self->new_speed); - + ircc_change_speed(self, self->new_speed); self->new_speed = 0; } @@ -662,39 +756,31 @@ static void ircc_dma_xmit_complete(struct ircc_cb *self, int underrun) * if it starts to receive a frame. * */ -static int ircc_dma_receive(struct ircc_cb *self) -{ - int iobase; - - IRDA_DEBUG(2, __FUNCTION__ "\n"); - - ASSERT(self != NULL, return -1;); - - iobase= self->io.iobase; +static int ircc_dma_receive(struct ircc_cb *self, int iobase) +{ + /* Turn off chip DMA */ + //register_bank(iobase, 1); + //outb(inb(iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, + // iobase+IRCC_SCE_CFGB); setup_dma(self->io.dma, self->rx_buff.data, self->rx_buff.truesize, - DMA_RX_MODE); - - /* driver->media_busy = FALSE; */ + DMA_RX_MODE); + /* Set max Rx frame size */ + register_bank(iobase, 4); + outb((2050 >> 8) & 0x0f, iobase+IRCC_RX_SIZE_HI); + outb(2050 & 0xff, iobase+IRCC_RX_SIZE_LO); + self->io.direction = IO_RECV; self->rx_buff.data = self->rx_buff.head; -#if 0 - self->rx_buff.offset = 0; -#endif - - register_bank(iobase, 4); - outb(inb(iobase+IRCC_CONTROL) & 0xf0, iobase+IRCC_CONTROL); - outb(2, iobase+IRCC_BOF_COUNT_LO); - outb(0, iobase+IRCC_BRICKWALL_CNT_LO); - outb(0, iobase+IRCC_BRICKWALL_TX_CNT_HI); - outb(0, iobase+IRCC_TX_SIZE_LO); - outb(0, iobase+IRCC_RX_SIZE_HI); - outb(0, iobase+IRCC_RX_SIZE_LO); + /* Setup DMA controller */ + + /* Enable receiver */ register_bank(iobase, 0); outb(IRCC_LCR_B_SCE_RECEIVE | IRCC_LCR_B_SIP_ENABLE, iobase+IRCC_LCR_B); + /* Enable burst mode chip Rx DMA */ register_bank(iobase, 1); outb(inb(iobase+IRCC_SCE_CFGB) | IRCC_CFGB_DMA_ENABLE | IRCC_CFGB_DMA_BURST, iobase+IRCC_SCE_CFGB); @@ -709,45 +795,54 @@ static int ircc_dma_receive(struct ircc_cb *self) * * */ -static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) +static void ircc_dma_receive_complete(struct ircc_cb *self, int iobase) { + unsigned long flags; struct sk_buff *skb; int len, msgcnt; IRDA_DEBUG(2, __FUNCTION__ "\n"); +#if 0 + /* Disable Rx */ + register_bank(iobase, 0); + outb(0x00, iobase+IRCC_LCR_B); +#endif + register_bank(iobase, 0); + msgcnt = inb(iobase+IRCC_LCR_B) & 0x08; - msgcnt = inb(self->io.iobase+IRCC_LCR_B) & 0x08; - - IRDA_DEBUG(0, __FUNCTION__ ": dma count = %d\n", + IRDA_DEBUG(2, __FUNCTION__ ": dma count = %d\n", get_dma_residue(self->io.dma)); - len = self->rx_buff.truesize - get_dma_residue(self->io.dma) - 4; - - IRDA_DEBUG(0, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); + len = self->rx_buff.truesize - get_dma_residue(self->io.dma); + + /* Remove CRC */ + if (self->io.speed < 4000000) + len -= 2; + else + len -= 4; + + if ((len < 2) && (len > 2050)) { + WARNING(__FUNCTION__ "(), bogus len=%d\n", len); + return; + } + IRDA_DEBUG(2, __FUNCTION__ ": msgcnt = %d, len=%d\n", msgcnt, len); skb = dev_alloc_skb(len+1); if (!skb) { WARNING(__FUNCTION__ "(), memory squeeze, dropping frame.\n"); - return FALSE; - } - + return; + } /* Make sure IP header gets aligned */ skb_reserve(skb, 1); - skb_put(skb, len); - memcpy(skb->data, self->rx_buff.data, len); + memcpy(skb_put(skb, len), self->rx_buff.data, len); self->irport->stats.rx_packets++; + self->irport->stats.rx_bytes += len; skb->dev = self->netdev; skb->mac.raw = skb->data; skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); - - register_bank(self->io.iobase, 1); - outb(inb(self->io.iobase+IRCC_SCE_CFGB) & ~IRCC_CFGB_DMA_ENABLE, - self->io.iobase+IRCC_SCE_CFGB); - - return TRUE; } /* @@ -758,58 +853,54 @@ static int ircc_dma_receive_complete(struct ircc_cb *self, int iobase) */ static void ircc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { - int iobase, iir; struct net_device *dev = (struct net_device *) dev_id; + struct irport_cb *irport; struct ircc_cb *self; + int iobase, iir; if (dev == NULL) { printk(KERN_WARNING "%s: irq %d for unknown device.\n", driver_name, irq); return; } - - self = (struct ircc_cb *) dev->priv; + irport = (struct irport_cb *) dev->priv; + ASSERT(irport != NULL, return;); + self = (struct ircc_cb *) irport->priv; + ASSERT(self != NULL, return;); - iobase = self->io.iobase; + /* Check if we should use the SIR interrupt handler */ + if (self->io.speed < 576000) { + irport_interrupt(irq, dev_id, regs); + return; + } + iobase = self->io.fir_base; + spin_lock(&self->lock); dev->interrupt = 1; - outb(0, iobase+IRCC_MASTER); - register_bank(iobase, 0); iir = inb(iobase+IRCC_IIR); /* Disable interrupts */ outb(0, iobase+IRCC_IER); - IRDA_DEBUG(0, __FUNCTION__ "(), iir = 0x%02x\n", iir); + IRDA_DEBUG(2, __FUNCTION__ "(), iir = 0x%02x\n", iir); if (iir & IRCC_IIR_EOM) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_EOM\n"); - if (self->io.direction == IO_RECV) ircc_dma_receive_complete(self, iobase); else ircc_dma_xmit_complete(self, iobase); - ircc_dma_receive(self); - } - if (iir & IRCC_IIR_ACTIVE_FRAME) { - IRDA_DEBUG(0, __FUNCTION__ "(), IRCC_IIR_ACTIVE_FRAME\n"); - self->rx_buff.state = INSIDE_FRAME; -#if 0 - ircc_dma_receive(self); -#endif - } - if (iir & IRCC_IIR_RAW_MODE) { - IRDA_DEBUG(0, __FUNCTION__ "(), IIR RAW mode interrupt.\n"); + ircc_dma_receive(self, iobase); } + /* Enable interrupts again */ register_bank(iobase, 0); outb(IRCC_IER_ACTIVE_FRAME|IRCC_IER_EOM, iobase+IRCC_IER); - outb(IRCC_MASTER_INT_EN, iobase+IRCC_MASTER); dev->interrupt = 0; + spin_unlock(&self->lock); } /* @@ -855,7 +946,7 @@ static int ircc_net_open(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_open(dev); /* irport allocates the irq */ @@ -894,7 +985,7 @@ static int ircc_net_close(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; irport_net_close(dev); @@ -907,6 +998,69 @@ static int ircc_net_close(struct net_device *dev) return 0; } +#ifdef CONFIG_APM +static void ircc_suspend(struct ircc_cb *self) +{ + int i = 10; + + MESSAGE("%s, Suspending\n", driver_name); + + if (self->io.suspended) + return; + + ircc_net_close(self->netdev); + + self->io.suspended = 1; +} + +static void ircc_wakeup(struct ircc_cb *self) +{ + struct net_device *dev = self->netdev; + unsigned long flags; + + if (!self->io.suspended) + return; + + save_flags(flags); + cli(); + + ircc_net_open(self->netdev); + + restore_flags(flags); + MESSAGE("%s, Waking up\n", driver_name); +} + +static int ircc_apmproc(apm_event_t event) +{ + static int down = 0; /* Filter out double events */ + int i; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (!down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_suspend(dev_self[i]); + } + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (down) { + for (i=0; i<4; i++) { + if (dev_self[i]) + ircc_wakeup(dev_self[i]); + } + } + down = 0; + break; + } + return 0; +} +#endif /* CONFIG_APM */ + #ifdef MODULE MODULE_AUTHOR("Thomas Davis "); MODULE_DESCRIPTION("SMC IrCC controller driver"); diff --git a/drivers/net/irda/toshoboe.c b/drivers/net/irda/toshoboe.c index cbf06af5f004..a7b97af373f5 100644 --- a/drivers/net/irda/toshoboe.c +++ b/drivers/net/irda/toshoboe.c @@ -32,37 +32,6 @@ static char *rcsid = "$Id: toshoboe.c,v 1.91 1999/06/29 14:21:06 root Exp $"; -/* - * $Log: toshoboe.c,v $ - * Revision 1.9 1999/06/29 14:21:06 root - * *** empty log message *** - * - * Revision 1.8 1999/06/29 14:15:08 root - * *** empty log message *** - * - * Revision 1.7 1999/06/29 13:46:42 root - * *** empty log message *** - * - * Revision 1.6 1999/06/29 12:31:03 root - * *** empty log message *** - * - * Revision 1.5 1999/05/12 12:24:39 root - * *** empty log message *** - * - * Revision 1.4 1999/05/12 11:55:08 root - * *** empty log message *** - * - * Revision 1.3 1999/05/09 01:33:12 root - * *** empty log message *** - * - * Revision 1.2 1999/05/09 01:30:38 root - * *** empty log message *** - * - * Revision 1.1 1999/05/09 01:25:04 root - * Initial revision - * - */ - /* Define this to have only one frame in the XMIT or RECV queue */ /* Toshiba's drivers do this, but it disables back to back tansfers */ /* I think that the chip may have some problems certainly, I have */ @@ -682,7 +651,7 @@ toshoboe_close (struct toshoboe_cb *self) toshoboe_disablebm (self); } - release_region (self->io.iobase, self->io.io_ext); + release_region (self->io.sir_base, self->io.sir_ext); for (i = 0; i < TX_SLOTS; ++i) @@ -754,17 +723,17 @@ toshoboe_open (struct pci_dev *pci_dev) self->pdev = pci_dev; self->base = pci_dev->resource[0].start; - self->io.iobase = self->base; + self->io.sir_base = self->base; self->io.irq = pci_dev->irq; - self->io.io_ext = CHIP_IO_EXTENT; + self->io.sir_ext = CHIP_IO_EXTENT; self->io.speed = 9600; /* Lock the port that we need */ - i = check_region (self->io.iobase, self->io.io_ext); + i = check_region (self->io.sir_base, self->io.sir_ext); if (i < 0) { IRDA_DEBUG (0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); + self->io.sir_base); dev_self[i] = NULL; kfree (self); @@ -864,13 +833,12 @@ toshoboe_open (struct pci_dev *pci_dev) } - request_region (self->io.iobase, self->io.io_ext, driver_name); + request_region (self->io.sir_base, self->io.sir_ext, driver_name); if (!(dev = dev_alloc("irda%d", &err))) { ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } - dev->priv = (void *) self; self->netdev = dev; @@ -1025,7 +993,7 @@ int __init toshoboe_init (void) if (pci_dev) { printk (KERN_WARNING "ToshOboe: Found 701 chip at 0x%0lx irq %d\n", - pci_dev->resource[0].start, + pci_dev->resource[0].start, pci_dev->irq); if (!toshoboe_open (pci_dev)) diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 1bc2d9fa91f0..7f781719c45d 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -6,7 +6,7 @@ * Status: Experimental. * Author: Paul VanderSpek * Created at: Wed Nov 4 11:46:16 1998 - * Modified at: Wed Jan 5 15:11:21 2000 + * Modified at: Fri Jan 28 12:10:59 2000 * Modified by: Dag Brattli * * Copyright (c) 1998-2000 Dag Brattli @@ -183,21 +183,21 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, dev_self[i] = self; /* Initialize IO */ - self->io.iobase = iobase; + self->io.fir_base = iobase; self->io.irq = irq; - self->io.io_ext = CHIP_IO_EXTENT; + self->io.fir_ext = CHIP_IO_EXTENT; self->io.dma = dma; self->io.fifo_size = 32; /* Lock the port that we need */ - ret = check_region(self->io.iobase, self->io.io_ext); + ret = check_region(self->io.fir_base, self->io.fir_ext); if (ret < 0) { IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n", - self->io.iobase); + self->io.fir_base); /* w83977af_cleanup( self); */ return -ENODEV; } - request_region(self->io.iobase, self->io.io_ext, driver_name); + request_region(self->io.fir_base, self->io.fir_ext, driver_name); /* Initialize QoS for this device */ irda_init_max_qos_capabilies(&self->qos); @@ -243,9 +243,6 @@ int w83977af_open(int i, unsigned int iobase, unsigned int irq, ERROR(__FUNCTION__ "(), dev_alloc() failed!\n"); return -ENOMEM; } - /* dev_alloc doesn't clear the struct, so lets do a little hack */ - memset(((__u8*)dev)+sizeof(char*),0,sizeof(struct net_device)-sizeof(char*)); - dev->priv = (void *) self; self->netdev = dev; @@ -282,7 +279,7 @@ static int w83977af_close(struct w83977af_ir *self) IRDA_DEBUG(0, __FUNCTION__ "()\n"); - iobase = self->io.iobase; + iobase = self->io.fir_base; #ifdef CONFIG_USE_W977_PNP /* enter PnP configuration mode */ @@ -301,14 +298,12 @@ static int w83977af_close(struct w83977af_ir *self) rtnl_lock(); unregister_netdevice(self->netdev); rtnl_unlock(); - /* Must free the old-style 2.2.x device */ - kfree(self->netdev); } /* Release the PORT that this driver is using */ IRDA_DEBUG(0 , __FUNCTION__ "(), Releasing Region %03x\n", - self->io.iobase); - release_region(self->io.iobase, self->io.io_ext); + self->io.fir_base); + release_region(self->io.fir_base, self->io.fir_ext); if (self->tx_buff.head) kfree(self->tx_buff.head); @@ -426,7 +421,7 @@ void w83977af_change_speed(struct w83977af_ir *self, __u32 speed) int iobase; __u8 set; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Update accounting for new speed */ self->io.speed = speed; @@ -510,7 +505,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) self = (struct w83977af_ir *) dev->priv; - iobase = self->io.iobase; + iobase = self->io.fir_base; IRDA_DEBUG(4, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len); @@ -692,7 +687,7 @@ void w83977af_dma_xmit_complete(struct w83977af_ir *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); @@ -748,7 +743,7 @@ int w83977af_dma_receive(struct w83977af_ir *self) IRDA_DEBUG(4, __FUNCTION__ "\n"); - iobase= self->io.iobase; + iobase= self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); @@ -822,12 +817,12 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self) st_fifo = &self->st_fifo; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current set */ set = inb(iobase+SSR); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Read status FIFO */ switch_bank(iobase, SET5); @@ -948,7 +943,7 @@ static void w83977af_pio_receive(struct w83977af_ir *self) ASSERT(self != NULL, return;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Receive all characters in Rx FIFO */ do { @@ -973,11 +968,11 @@ static __u8 w83977af_sir_interrupt(struct w83977af_ir *self, int isr) IRDA_DEBUG(4, __FUNCTION__ "(), isr=%#x\n", isr); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Transmit FIFO low on data */ if (isr & ISR_TXTH_I) { /* Write data left in transmit buffer */ - actual = w83977af_pio_write(self->io.iobase, + actual = w83977af_pio_write(self->io.fir_base, self->tx_buff.data, self->tx_buff.len, self->io.fifo_size); @@ -1042,7 +1037,7 @@ static __u8 w83977af_fir_interrupt(struct w83977af_ir *self, int isr) __u8 set; int iobase; - iobase = self->io.iobase; + iobase = self->io.fir_base; set = inb(iobase+SSR); /* End of frame detected in FIFO */ @@ -1131,7 +1126,7 @@ static void w83977af_interrupt(int irq, void *dev_id, struct pt_regs *regs) dev->interrupt = 1; - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Save current bank */ set = inb(iobase+SSR); @@ -1171,7 +1166,7 @@ static int w83977af_is_receiving(struct w83977af_ir *self) ASSERT(self != NULL, return FALSE;); if (self->io.speed > 115200) { - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Check if rx FIFO is not empty */ set = inb(iobase+SSR); @@ -1225,7 +1220,7 @@ static int w83977af_net_open(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; if (request_irq(self->io.irq, w83977af_interrupt, 0, dev->name, (void *) dev)) { @@ -1290,7 +1285,7 @@ static int w83977af_net_close(struct net_device *dev) ASSERT(self != NULL, return 0;); - iobase = self->io.iobase; + iobase = self->io.fir_base; /* Stop device */ dev->tbusy = 1; diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c new file mode 100644 index 000000000000..ce43ad7526f2 --- /dev/null +++ b/drivers/net/mac89x0.c @@ -0,0 +1,678 @@ +/* cs89x0.c: A Crystal Semiconductor CS89[02]0 driver for linux. */ +/* + Written 1996 by Russell Nelson, with reference to skeleton.c + written 1993-1994 by Donald Becker. + + This software may be used and distributed according to the terms + of the GNU Public License, incorporated herein by reference. + + The author may be reached at nelson@crynwr.com, Crynwr + Software, 11 Grant St., Potsdam, NY 13676 + + Changelog: + + Mike Cruse : mcruse@cti-ltd.com + : Changes for Linux 2.0 compatibility. + : Added dev_id parameter in net_interrupt(), + : request_irq() and free_irq(). Just NULL for now. + + Mike Cruse : Added MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT macros + : in net_open() and net_close() so kerneld would know + : that the module is in use and wouldn't eject the + : driver prematurely. + + Mike Cruse : Rewrote init_module() and cleanup_module using 8390.c + : as an example. Disabled autoprobing in init_module(), + : not a good thing to do to other devices while Linux + : is running from all accounts. + + Alan Cox : Removed 1.2 support, added 2.1 extra counters. + + David Huggins-Daines + + Split this off into mac89x0.c, and gutted it of all parts which are + not relevant to the existing CS8900 cards on the Macintosh + (i.e. basically the Daynaport CS and LC cards). To be precise: + + * Removed all the media-detection stuff, because these cards are + TP-only. + + * Lobotomized the ISA interrupt bogosity, because these cards use + a hardwired NuBus interrupt and a magic ISAIRQ value in the card. + + * Basically eliminated everything not relevant to getting the + cards minimally functioning on the Macintosh. + + I might add that these cards are badly designed even from the Mac + standpoint, in that Dayna, in their infinite wisdom, used NuBus slot + I/O space and NuBus interrupts for these cards, but neglected to + provide anything even remotely resembling a NuBus ROM. Therefore we + have to probe for them in a brain-damaged ISA-like fashion. +*/ + +static char *version = +"cs89x0.c:v1.02 11/26/96 Russell Nelson \n"; + +/* ======================= configure the driver here ======================= */ + +/* use 0 for production, 1 for verification, >2 for debug */ +#ifndef NET_DEBUG +#define NET_DEBUG 0 +#endif + +/* ======================= end of configuration ======================= */ + + +/* Always include 'config.h' first in case the user wants to turn on + or override something. */ +#ifdef MODULE +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + +#define PRINTK(x) printk x + +/* + Sources: + + Crynwr packet driver epktisa. + + Crystal Semiconductor data sheets. + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "cs89x0.h" + +static unsigned int net_debug = NET_DEBUG; + +/* Information that need to be kept for each board. */ +struct net_local { + struct net_device_stats stats; + int chip_type; /* one of: CS8900, CS8920, CS8920M */ + char chip_revision; /* revision letter of the chip ('A'...) */ + int send_cmd; /* the propercommand used to send a packet. */ + int rx_mode; + int curr_rx_cfg; + int send_underrun; /* keep track of how many underruns in a row we get */ + struct sk_buff *skb; +}; + +/* Index to functions, as function prototypes. */ + +extern int mac89x0_probe(struct net_device *dev); +extern void reset_chip(struct net_device *dev); +static int net_open(struct net_device *dev); +static int net_send_packet(struct sk_buff *skb, struct net_device *dev); +static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void set_multicast_list(struct net_device *dev); +static void net_rx(struct net_device *dev); +static int net_close(struct net_device *dev); +static struct net_device_stats *net_get_stats(struct net_device *dev); +static int set_mac_address(struct net_device *dev, void *addr); + + +/* Example routines you must write ;->. */ +#define tx_done(dev) 1 + +/* For reading/writing registers ISA-style */ +static int inline +readreg_io(struct net_device *dev, int portno) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + return swab16(readw(dev->base_addr + DATA_PORT)); +} + +static void inline +writereg_io(struct net_device *dev, int portno, int value) +{ + writew(swab16(portno), dev->base_addr + ADD_PORT); + writew(swab16(value), dev->base_addr + DATA_PORT); +} + +/* These are for reading/writing registers in shared memory */ +static int inline +readreg(struct net_device *dev, int portno) +{ + return swab16(readw(dev->mem_start + portno)); +} + +static void inline +writereg(struct net_device *dev, int portno, int value) +{ + writew(swab16(value), dev->mem_start + portno); +} + +/* Probe for the CS8900 card in slot E. We won't bother looking + anywhere else until we have a really good reason to do so. */ +int __init mac89x0_probe(struct net_device *dev) +{ + static int once_is_enough = 0; + struct net_local *lp; + static unsigned version_printed = 0; + int i, slot; + unsigned rev_type = 0; + unsigned long ioaddr; + unsigned short sig; + + if (once_is_enough) + return ENODEV; + once_is_enough = 1; + + /* We might have to parameterize this later */ + slot = 0xE; + /* Get out now if there's a real NuBus card in slot E */ + if (nubus_find_slot(slot, NULL) != NULL) + return ENODEV; + + /* The pseudo-ISA bits always live at offset 0x300 (gee, + wonder why...) */ + ioaddr = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + DEFAULTIOBASE); + { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*) ioaddr+4) + && hwreg_present((void*) ioaddr + DATA_PORT); + restore_flags(flags); + + if (!card_present) + return ENODEV; + } + + writew(0, ioaddr + ADD_PORT); + sig = readw(ioaddr + DATA_PORT); + if (sig != swab16(CHIP_EISA_ID_SIG)) + return ENODEV; + + /* Initialize the net_device structure. */ + if (dev->priv == NULL) { + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev->priv, 0, sizeof(struct net_local)); + } + lp = (struct net_local *)dev->priv; + + /* Fill in the 'dev' fields. */ + dev->base_addr = ioaddr; + dev->mem_start = (unsigned long) + nubus_slot_addr(slot) | (((slot&0xf) << 20) + MMIOBASE); + dev->mem_end = dev->mem_start + 0x1000; + + /* Turn on shared memory */ + writereg_io(dev, PP_BusCTL, MEMORY_ON); + + /* get the chip type */ + rev_type = readreg(dev, PRODUCT_ID_ADD); + lp->chip_type = rev_type &~ REVISON_BITS; + lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A'; + + /* Check the chip type and revision in order to set the correct send command + CS8920 revision C and CS8900 revision F can use the faster send. */ + lp->send_cmd = TX_AFTER_381; + if (lp->chip_type == CS8900 && lp->chip_revision >= 'F') + lp->send_cmd = TX_NOW; + if (lp->chip_type != CS8900 && lp->chip_revision >= 'C') + lp->send_cmd = TX_NOW; + + if (net_debug && version_printed++ == 0) + printk(version); + + printk(KERN_INFO "%s: cs89%c0%s rev %c found at %#8lx", + dev->name, + lp->chip_type==CS8900?'0':'2', + lp->chip_type==CS8920M?"M":"", + lp->chip_revision, + dev->base_addr); + + /* Try to read the MAC address */ + if ((readreg(dev, PP_SelfST) & (EEPROM_PRESENT | EEPROM_OK)) == 0) { + printk("\nmac89x0: No EEPROM, giving up now.\n"); + return ENODEV; + } else { + for (i = 0; i < ETH_ALEN; i += 2) { + /* Big-endian (why??!) */ + unsigned short s = readreg(dev, PP_IA + i); + dev->dev_addr[i] = s >> 8; + dev->dev_addr[i+1] = s & 0xff; + } + } + + dev->irq = SLOT2IRQ(slot); + printk(" IRQ %d ADDR ", dev->irq); + + /* print the ethernet address. */ + for (i = 0; i < ETH_ALEN; i++) + printk("%2.2x%s", dev->dev_addr[i], + ((i < ETH_ALEN-1) ? ":" : "")); + + dev->open = net_open; + dev->stop = net_close; + dev->hard_start_xmit = net_send_packet; + dev->get_stats = net_get_stats; + dev->set_multicast_list = &set_multicast_list; + dev->set_mac_address = &set_mac_address; + + /* Fill in the fields of the net_device structure with ethernet values. */ + ether_setup(dev); + + printk("\n"); + return 0; +} + +/* This is useful for something, but I don't know what yet. */ +void __init reset_chip(struct net_device *dev) +{ + int reset_start_time; + + writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET); + + /* wait 30 ms */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(30*HZ/1000); + + /* Wait until the chip is reset */ + reset_start_time = jiffies; + while( (readreg(dev, PP_SelfST) & INIT_DONE) == 0 && jiffies - reset_start_time < 2) + ; +} + +/* Open/initialize the board. This is called (in the current kernel) + sometime after booting when the 'ifconfig' program is run. + + This routine should set everything up anew at each open, even + registers that "should" only need to be set once at boot, so that + there is non-reboot way to recover if something goes wrong. + */ +static int +net_open(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + int i; + + /* Disable the interrupt for now */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) & ~ENABLE_IRQ); + + /* Grab the interrupt */ + if (request_irq(dev->irq, &net_interrupt, 0, "cs89x0", dev)) + return -EAGAIN; + + /* Set up the IRQ - Apparently magic */ + if (lp->chip_type == CS8900) + writereg(dev, PP_CS8900_ISAINT, 0); + else + writereg(dev, PP_CS8920_ISAINT, 0); + + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + /* Turn on both receive and transmit operations */ + writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON); + + /* Receive only error free packets addressed to this card */ + lp->rx_mode = 0; + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT); + + lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL; + + writereg(dev, PP_RxCFG, lp->curr_rx_cfg); + + writereg(dev, PP_TxCFG, TX_LOST_CRS_ENBL | TX_SQE_ERROR_ENBL | TX_OK_ENBL | + TX_LATE_COL_ENBL | TX_JBR_ENBL | TX_ANY_COL_ENBL | TX_16_COL_ENBL); + + writereg(dev, PP_BufCFG, READY_FOR_TX_ENBL | RX_MISS_COUNT_OVRFLOW_ENBL | + TX_COL_COUNT_OVRFLOW_ENBL | TX_UNDERRUN_ENBL); + + /* now that we've got our act together, enable everything */ + writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ); + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + MOD_INC_USE_COUNT; + return 0; +} + +static int +net_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + if (dev->tbusy) { + /* If we get here, some higher level has decided we are broken. + There should really be a "kick me" function call instead. */ + int tickssofar = jiffies - dev->trans_start; + if (tickssofar < 5) + return 1; + if (net_debug > 0) printk("%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ conflict" : "network cable problem"); + /* Try to restart the adaptor. */ + dev->tbusy=0; + dev->trans_start = jiffies; + } + + /* Block a timer-based transmit from overlapping. This could better be + done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) + printk("%s: Transmitter access conflict.\n", dev->name); + else { + struct net_local *lp = (struct net_local *)dev->priv; + unsigned long flags; + + if (net_debug > 3) + printk("%s: sent %d byte packet of type %x\n", + dev->name, skb->len, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + /* keep the upload from being interrupted, since we + ask the chip to start transmitting before the + whole packet has been completely uploaded. */ + save_flags(flags); + cli(); + + /* initiate a transmit sequence */ + writereg(dev, PP_TxCMD, lp->send_cmd); + writereg(dev, PP_TxLength, skb->len); + + /* Test to see if the chip has allocated memory for the packet */ + if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) { + /* Gasp! It hasn't. But that shouldn't happen since + we're waiting for TxOk, so return 1 and requeue this packet. */ + restore_flags(flags); + return 1; + } + + /* Write the contents of the packet */ + memcpy_toio(dev->mem_start + PP_TxFrame, skb->data, skb->len+1); + + restore_flags(flags); + dev->trans_start = jiffies; + } + dev_kfree_skb (skb); + + return 0; +} + +/* The typical workload of the driver: + Handle the network interface interrupts. */ +static void net_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = dev_id; + struct net_local *lp; + int ioaddr, status; + + if (dev == NULL) { + printk ("net_interrupt(): irq %d for unknown device.\n", irq); + return; + } + if (dev->interrupt) + printk("%s: Re-entering the interrupt handler.\n", dev->name); + dev->interrupt = 1; + + ioaddr = dev->base_addr; + lp = (struct net_local *)dev->priv; + + /* we MUST read all the events out of the ISQ, otherwise we'll never + get interrupted again. As a consequence, we can't have any limit + on the number of times we loop in the interrupt handler. The + hardware guarantees that eventually we'll run out of events. Of + course, if you're on a slow machine, and packets are arriving + faster than you can read them off, you're screwed. Hasta la + vista, baby! */ + while ((status = swab16(readw(dev->base_addr + ISQ_PORT)))) { + if (net_debug > 4)printk("%s: event=%04x\n", dev->name, status); + switch(status & ISQ_EVENT_MASK) { + case ISQ_RECEIVER_EVENT: + /* Got a packet(s). */ + net_rx(dev); + break; + case ISQ_TRANSMITTER_EVENT: + lp->stats.tx_packets++; + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + if ((status & TX_OK) == 0) lp->stats.tx_errors++; + if (status & TX_LOST_CRS) lp->stats.tx_carrier_errors++; + if (status & TX_SQE_ERROR) lp->stats.tx_heartbeat_errors++; + if (status & TX_LATE_COL) lp->stats.tx_window_errors++; + if (status & TX_16_COL) lp->stats.tx_aborted_errors++; + break; + case ISQ_BUFFER_EVENT: + if (status & READY_FOR_TX) { + /* we tried to transmit a packet earlier, + but inexplicably ran out of buffers. + That shouldn't happen since we only ever + load one packet. Shrug. Do the right + thing anyway. */ + dev->tbusy = 0; + mark_bh(NET_BH); /* Inform upper layers. */ + } + if (status & TX_UNDERRUN) { + if (net_debug > 0) printk("%s: transmit underrun\n", dev->name); + lp->send_underrun++; + if (lp->send_underrun == 3) lp->send_cmd = TX_AFTER_381; + else if (lp->send_underrun == 6) lp->send_cmd = TX_AFTER_ALL; + } + break; + case ISQ_RX_MISS_EVENT: + lp->stats.rx_missed_errors += (status >>6); + break; + case ISQ_TX_COL_EVENT: + lp->stats.collisions += (status >>6); + break; + } + } + dev->interrupt = 0; + return; +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +net_rx(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + struct sk_buff *skb; + int status, length; + + status = readreg(dev, PP_RxStatus); + if ((status & RX_OK) == 0) { + lp->stats.rx_errors++; + if (status & RX_RUNT) lp->stats.rx_length_errors++; + if (status & RX_EXTRA_DATA) lp->stats.rx_length_errors++; + if (status & RX_CRC_ERROR) if (!(status & (RX_EXTRA_DATA|RX_RUNT))) + /* per str 172 */ + lp->stats.rx_crc_errors++; + if (status & RX_DRIBBLE) lp->stats.rx_frame_errors++; + return; + } + + length = readreg(dev, PP_RxLength); + /* Malloc up new buffer. */ + skb = alloc_skb(length, GFP_ATOMIC); + if (skb == NULL) { + printk("%s: Memory squeeze, dropping packet.\n", dev->name); + lp->stats.rx_dropped++; + return; + } + skb->len = length; + skb->dev = dev; + + memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); + + if (net_debug > 3)printk("%s: received %d byte packet of type %x\n", + dev->name, length, + (skb->data[ETH_ALEN+ETH_ALEN] << 8) + | skb->data[ETH_ALEN+ETH_ALEN+1]); + + skb->protocol=eth_type_trans(skb,dev); + netif_rx(skb); + lp->stats.rx_packets++; + lp->stats.rx_bytes+=skb->len; + return; +} + +/* The inverse routine to net_open(). */ +static int +net_close(struct net_device *dev) +{ + + writereg(dev, PP_RxCFG, 0); + writereg(dev, PP_TxCFG, 0); + writereg(dev, PP_BufCFG, 0); + writereg(dev, PP_BusCTL, 0); + + dev->start = 0; + + free_irq(dev->irq, dev); + + /* Update the statistics here. */ + + MOD_DEC_USE_COUNT; + return 0; + +} + +/* Get the current statistics. This may be called with the card open or + closed. */ +static struct net_device_stats * +net_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + cli(); + /* Update the statistics from the device registers. */ + lp->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6); + lp->stats.collisions += (readreg(dev, PP_TxCol) >> 6); + sti(); + + return &lp->stats; +} + +static void set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + if(dev->flags&IFF_PROMISC) + { + lp->rx_mode = RX_ALL_ACCEPT; + } + else if((dev->flags&IFF_ALLMULTI)||dev->mc_list) + { + /* The multicast-accept list is initialized to accept-all, and we + rely on higher-level filtering for now. */ + lp->rx_mode = RX_MULTCAST_ACCEPT; + } + else + lp->rx_mode = 0; + + writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode); + + /* in promiscuous mode, we accept errored packets, so we have to enable interrupts on them also */ + writereg(dev, PP_RxCFG, lp->curr_rx_cfg | + (lp->rx_mode == RX_ALL_ACCEPT? (RX_CRC_ERROR_ENBL|RX_RUNT_ENBL|RX_EXTRA_DATA_ENBL) : 0)); +} + + +static int set_mac_address(struct net_device *dev, void *addr) +{ + int i; + if (dev->start) + return -EBUSY; + printk("%s: Setting MAC address to ", dev->name); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] = ((unsigned char *)addr)[i]); + printk(".\n"); + /* set the Ethernet address */ + for (i=0; i < ETH_ALEN/2; i++) + writereg(dev, PP_IA+i*2, dev->dev_addr[i*2] | (dev->dev_addr[i*2+1] << 8)); + + return 0; +} + +#ifdef MODULE + +static char namespace[16] = ""; +static struct net_device dev_cs89x0 = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; + +static int debug=0; + +MODULE_PARM(debug, "i"); + +EXPORT_NO_SYMBOLS; + +int +init_module(void) +{ + struct net_local *lp; + + net_debug = debug; + dev_cs89x0.name = namespace; + dev_cs89x0.init = mac89x0_probe; + dev_cs89x0.priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + memset(dev_cs89x0.priv, 0, sizeof(struct net_local)); + lp = (struct net_local *)dev_cs89x0.priv; + + if (register_netdev(&dev_cs89x0) != 0) { + printk(KERN_WARNING "mac89x0.c: No card found\n"); + return -ENXIO; + } + return 0; +} + +void +cleanup_module(void) +{ + +#endif +#ifdef MODULE + writew(0, dev_cs89x0.base_addr + ADD_PORT); +#endif +#ifdef MODULE + + if (dev_cs89x0.priv != NULL) { + /* Free up the private structure, or leak memory :-) */ + unregister_netdev(&dev_cs89x0); + kfree(dev_cs89x0.priv); + dev_cs89x0.priv = NULL; /* gets re-allocated by cs89x0_probe1 */ + } +} +#endif /* MODULE */ + +/* + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o mac89x0.o mac89x0.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * + */ diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c new file mode 100644 index 000000000000..a7c347a7da4b --- /dev/null +++ b/drivers/net/macmace.c @@ -0,0 +1,825 @@ +/* + * Driver for the Macintosh 68K onboard MACE controller with PSC + * driven DMA. The MACE driver code is derived from mace.c. The + * Mac68k theory of operation is courtesy of the MacBSD wizards. + * + * 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. + * + * Copyright (C) 1996 Paul Mackerras. + * Copyright (C) 1998 Alan Cox + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mace.h" + +#define N_RX_RING 8 +#define N_TX_RING 2 +#define MAX_TX_ACTIVE 1 +#define NCMDS_TX 1 /* dma commands per element in tx ring */ +#define RX_BUFLEN (ETH_FRAME_LEN + 8) +#define TX_TIMEOUT HZ /* 1 second */ + +/* Bits in transmit DMA status */ +#define TX_DMA_ERR 0x80 + +/* The MACE is simply wired down on a Mac68K box */ + +#define MACE_BASE (void *)(0x50F1C000) +#define MACE_PROM (void *)(0x50F08001) + +struct mace68k_data +{ + volatile struct mace *mace; + volatile unsigned char *tx_ring; + volatile unsigned char *rx_ring; + int dma_intr; + unsigned char maccc; + struct net_device_stats stats; + struct timer_list tx_timeout; + int timeout_active; + int rx_slot, rx_done; + int tx_slot, tx_count; +}; + +struct mace_frame +{ + u16 len; + u16 status; + u16 rntpc; + u16 rcvcc; + u32 pad1; + u32 pad2; + u8 data[1]; + /* And frame continues.. */ +}; + +#define PRIV_BYTES sizeof(struct mace68k_data) + +extern void psc_debug_dump(void); + +static int mace68k_open(struct net_device *dev); +static int mace68k_close(struct net_device *dev); +static int mace68k_xmit_start(struct sk_buff *skb, struct net_device *dev); +static struct net_device_stats *mace68k_stats(struct net_device *dev); +static void mace68k_set_multicast(struct net_device *dev); +static void mace68k_reset(struct net_device *dev); +static int mace68k_set_address(struct net_device *dev, void *addr); +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs); +static void mace68k_set_timeout(struct net_device *dev); +static void mace68k_tx_timeout(unsigned long data); + +/* + * PSC DMA engine control. As you'd expect on a macintosh its + * more like a lawnmower engine supplied without instructions + * + * The basic theory of operation appears to be as follows. + * + * There are two sets of receive DMA registers and two sets + * of transmit DMA registers. Instead of the more traditional + * "ring buffer" approach the Mac68K DMA engine expects you + * to be loading one chain while the other runs, and then + * to flip register set. Each entry in the chain is a fixed + * length. + */ + +/* + * Load a receive DMA channel with a base address and ring length + */ + +static void psc_load_rxdma_base(int set, void *base) +{ + psc_write_word(PSC_ENETRD_CMD + set, 0x0100); + psc_write_long(PSC_ENETRD_ADDR + set, (u32)base); + psc_write_long(PSC_ENETRD_LEN + set, N_RX_RING); + psc_write_word(PSC_ENETRD_CMD + set, 0x9800); +} + +/* + * Reset the receive DMA subsystem + */ + +static void mace68k_rxdma_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + /* + * Turn off receive + */ + + mcc&=~ENRCV; + mace->maccc=mcc; + + /* + * Program the DMA + */ + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x0, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_load_rxdma_base(0x10, (void *)virt_to_bus(mp->rx_ring)); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + mace->maccc=mcc|ENRCV; + +#if 0 + psc_write_word(PSC_ENETRD_CTL, 0x9800); + psc_write_word(PSC_ENETRD_CTL+0x10, 0x9800); +#endif +} + +/* + * Reset the transmit DMA subsystem + */ + +static void mace68k_txdma_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mace = mp->mace; + u8 mcc = mace->maccc; + + psc_write_word(PSC_ENETWR_CTL,0x8800); + + mace->maccc = mcc&~ENXMT; + psc_write_word(PSC_ENETWR_CTL,0x0400); + mace->maccc = mcc; +} + +/* + * Disable DMA + */ + +static void mace68k_dma_off(struct net_device *dev) +{ + psc_write_word(PSC_ENETRD_CTL, 0x8800); + psc_write_word(PSC_ENETRD_CTL, 0x1000); + psc_write_word(PSC_ENETRD_CMD, 0x1100); + psc_write_word(PSC_ENETRD_CMD+0x10, 0x1100); + + psc_write_word(PSC_ENETWR_CTL, 0x8800); + psc_write_word(PSC_ENETWR_CTL, 0x1000); + psc_write_word(PSC_ENETWR_CMD, 0x1100); + psc_write_word(PSC_ENETWR_CMD+0x10, 0x1100); +} + +/* Bit-reverse one byte of an ethernet hardware address. */ + +static int bitrev(int b) +{ + int d = 0, i; + + for (i = 0; i < 8; ++i, b >>= 1) + d = (d << 1) | (b & 1); + return d; +} + +/* + * Not really much of a probe. The hardware table tells us if this + * model of Macintrash has a MACE (AV macintoshes) + */ + +int mace68k_probe(struct net_device *unused) +{ + int j; + static int once=0; + struct mace68k_data *mp; + unsigned char *addr; + struct net_device *dev; + unsigned char checksum = 0; + + /* + * There can be only one... + */ + + if (once) return -ENODEV; + + once = 1; + + if (macintosh_config->ether_type != MAC_ETHER_MACE) return -ENODEV; + + printk("MACE ethernet should be present "); + + dev = init_etherdev(0, PRIV_BYTES); + if(dev==NULL) + { + printk("no free memory.\n"); + return -ENOMEM; + } + mp = (struct mace68k_data *) dev->priv; + dev->base_addr = (u32)MACE_BASE; + mp->mace = (volatile struct mace *) MACE_BASE; + + printk("at 0x%p", mp->mace); + + /* + * 16K RX ring and 4K TX ring should do nicely + */ + + mp->rx_ring=(void *)__get_free_pages(GFP_KERNEL, 2); + mp->tx_ring=(void *)__get_free_page(GFP_KERNEL); + + printk("."); + + if(mp->tx_ring==NULL || mp->rx_ring==NULL) + { + if(mp->tx_ring) + free_page((u32)mp->tx_ring); +// if(mp->rx_ring) +// __free_pages(mp->rx_ring,2); + printk("\nNo memory for ring buffers.\n"); + return -ENOMEM; + } + + /* We want the receive data to be uncached. We dont care about the + byte reading order */ + + printk("."); + kernel_set_cachemode((void *)mp->rx_ring, 16384, IOMAP_NOCACHE_NONSER); + + printk("."); + /* The transmit buffer needs to be write through */ + kernel_set_cachemode((void *)mp->tx_ring, 4096, IOMAP_WRITETHROUGH); + + printk(" Ok\n"); + dev->irq = IRQ_MAC_MACE; + printk(KERN_INFO "%s: MACE at", dev->name); + + /* + * The PROM contains 8 bytes which total 0xFF when XOR'd + * together. Due to the usual peculiar apple brain damage + * the bytes are spaced out in a strange boundary and the + * bits are reversed. + */ + + addr = (void *)MACE_PROM; + + for (j = 0; j < 6; ++j) + { + u8 v=bitrev(addr[j<<4]); + checksum^=v; + dev->dev_addr[j] = v; + printk("%c%.2x", (j ? ':' : ' '), dev->dev_addr[j]); + } + for (; j < 8; ++j) + { + checksum^=bitrev(addr[j<<4]); + } + + if(checksum!=0xFF) + { + printk(" (invalid checksum)\n"); + return -ENODEV; + } + printk("\n"); + + memset(&mp->stats, 0, sizeof(mp->stats)); + init_timer(&mp->tx_timeout); + mp->timeout_active = 0; + + dev->open = mace68k_open; + dev->stop = mace68k_close; + dev->hard_start_xmit = mace68k_xmit_start; + dev->get_stats = mace68k_stats; + dev->set_multicast_list = mace68k_set_multicast; + dev->set_mac_address = mace68k_set_address; + + ether_setup(dev); + + mp = (struct mace68k_data *) dev->priv; + mp->maccc = ENXMT | ENRCV; + mp->dma_intr = IRQ_MAC_MACE_DMA; + + psc_write_word(PSC_ENETWR_CTL, 0x9000); + psc_write_word(PSC_ENETRD_CTL, 0x9000); + psc_write_word(PSC_ENETWR_CTL, 0x0400); + psc_write_word(PSC_ENETRD_CTL, 0x0400); + + /* apple's driver doesn't seem to do this */ + /* except at driver shutdown time... */ +#if 0 + mace68k_dma_off(dev); +#endif + + return 0; +} + +/* + * Reset a MACE controller + */ + +static void mace68k_reset(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + + /* soft-reset the chip */ + i = 200; + while (--i) { + mb->biucc = SWRST; + if (mb->biucc & SWRST) { + udelay(10); + continue; + } + break; + } + if (!i) { + printk(KERN_ERR "mace: cannot reset chip!\n"); + return; + } + + mb->biucc = XMTSP_64; + mb->imr = 0xff; /* disable all intrs for now */ + i = mb->ir; + mb->maccc = 0; /* turn off tx, rx */ + mb->utr = RTRD; + mb->fifocc = RCVFW_64; + mb->xmtfc = AUTO_PAD_XMIT; /* auto-pad short frames */ + + /* load up the hardware address */ + + mb->iac = ADDRCHG | PHYADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i]; + + /* clear the multicast filter */ + mb->iac = ADDRCHG | LOGADDR; + + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = 0; + + mb->plscc = PORTSEL_GPSI + ENPLSIO; +} + +/* + * Load the address on a mace controller. + */ + +static int mace68k_set_address(struct net_device *dev, void *addr) +{ + unsigned char *p = addr; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i; + unsigned long flags; + + save_flags(flags); + cli(); + + /* load up the hardware address */ + mb->iac = ADDRCHG | PHYADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 6; ++i) + mb->padr = dev->dev_addr[i] = p[i]; + /* note: setting ADDRCHG clears ENRCV */ + mb->maccc = mp->maccc; + restore_flags(flags); + return 0; +} + +/* + * Open the Macintosh MACE. Most of this is playing with the DMA + * engine. The ethernet chip is quite friendly. + */ + +static int mace68k_open(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* reset the chip */ + mace68k_reset(dev); + + mp->rx_done = 0; + mace68k_rxdma_reset(dev); + + /* + * The interrupt is fixed and comes off the PSC. + */ + + if (request_irq(dev->irq, mace68k_interrupt, 0, "68K MACE", dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", dev->irq); + return -EAGAIN; + } + + /* + * Ditto the DMA interrupt. + */ + + if (request_irq(IRQ_MAC_MACE_DMA, mace68k_dma_intr, 0, "68K MACE DMA", + dev)) + { + printk(KERN_ERR "MACE: can't get irq %d\n", IRQ_MAC_MACE_DMA); + return -EAGAIN; + } + + /* Activate the Mac DMA engine */ + + mp->tx_slot = 0; /* Using register set 0 */ + mp->tx_count = 1; /* 1 Buffer ready for use */ + mace68k_txdma_reset(dev); + + /* turn it on! */ + mb->maccc = mp->maccc; + /* enable all interrupts except receive interrupts */ + mb->imr = RCVINT; + return 0; +} + +/* + * Shut down the mace and its interrupt channel + */ + +static int mace68k_close(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + + /* disable rx and tx */ + mb->maccc = 0; + mb->imr = 0xff; /* disable all intrs */ + + /* disable rx and tx dma */ + + mace68k_dma_off(dev); + + free_irq(dev->irq, dev); + free_irq(IRQ_MAC_MACE_DMA, dev); + return 0; +} + +static inline void mace68k_set_timeout(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + unsigned long flags; + + save_flags(flags); + cli(); + if (mp->timeout_active) + del_timer(&mp->tx_timeout); + mp->tx_timeout.expires = jiffies + TX_TIMEOUT; + mp->tx_timeout.function = mace68k_tx_timeout; + mp->tx_timeout.data = (unsigned long) dev; + add_timer(&mp->tx_timeout); + mp->timeout_active = 1; + restore_flags(flags); +} + +/* + * Transmit a frame + */ + +static int mace68k_xmit_start(struct sk_buff *skb, struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + /* + * This may need atomic types ??? + */ + + printk("mace68k_xmit_start: mp->tx_count = %d, dev->tbusy = %d, mp->tx_ring = %p (%p)\n", + mp->tx_count, dev->tbusy, + mp->tx_ring, virt_to_bus(mp->tx_ring)); + psc_debug_dump(); + + if(mp->tx_count == 0) + { + dev->tbusy=1; + mace68k_dma_intr(IRQ_MAC_MACE_DMA, dev, NULL); + return 1; + } + mp->tx_count--; + + /* + * FIXME: + * This is hackish. The memcpy probably isnt needed but + * the rules for alignment are not known. Ideally we'd like + * to just blast the skb directly to ethernet. We also don't + * use the ring properly - just a one frame buffer. That + * also requires cache pushes ;). + */ + memcpy((void *)mp->tx_ring, skb, skb->len); + psc_write_long(PSC_ENETWR_ADDR + mp->tx_slot, virt_to_bus(mp->tx_ring)); + psc_write_long(PSC_ENETWR_LEN + mp->tx_slot, skb->len); + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x9800); + mp->stats.tx_packets++; + mp->stats.tx_bytes+=skb->len; + dev_kfree_skb(skb); + return 0; +} + +static struct net_device_stats *mace68k_stats(struct net_device *dev) +{ + struct mace68k_data *p = (struct mace68k_data *) dev->priv; + return &p->stats; +} + +/* + * CRC polynomial - used in working out multicast filter bits. + */ +#define CRC_POLY 0xedb88320 + +static void mace68k_set_multicast(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + int i, j, k, b; + unsigned long crc; + + mp->maccc &= ~PROM; + if (dev->flags & IFF_PROMISC) + { + mp->maccc |= PROM; + } else + { + unsigned char multicast_filter[8]; + struct dev_mc_list *dmi = dev->mc_list; + + if (dev->flags & IFF_ALLMULTI) + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0xff; + } else + { + for (i = 0; i < 8; i++) + multicast_filter[i] = 0; + for (i = 0; i < dev->mc_count; i++) + { + crc = ~0; + for (j = 0; j < 6; ++j) + { + b = dmi->dmi_addr[j]; + for (k = 0; k < 8; ++k) + { + if ((crc ^ b) & 1) + crc = (crc >> 1) ^ CRC_POLY; + else + crc >>= 1; + b >>= 1; + } + } + j = crc >> 26; /* bit number in multicast_filter */ + multicast_filter[j >> 3] |= 1 << (j & 7); + dmi = dmi->next; + } + } +#if 0 + printk("Multicast filter :"); + for (i = 0; i < 8; i++) + printk("%02x ", multicast_filter[i]); + printk("\n"); +#endif + + mb->iac = ADDRCHG | LOGADDR; + while ((mb->iac & ADDRCHG) != 0); + + for (i = 0; i < 8; ++i) + mb->ladrf = multicast_filter[i]; + } + /* reset maccc */ + mb->maccc = mp->maccc; +} + +/* + * Miscellaneous interrupts are handled here. We may end up + * having to bash the chip on the head for bad errors + */ + +static void mace68k_handle_misc_intrs(struct mace68k_data *mp, int intr) +{ + volatile struct mace *mb = mp->mace; + static int mace68k_babbles, mace68k_jabbers; + + if (intr & MPCO) + mp->stats.rx_missed_errors += 256; + mp->stats.rx_missed_errors += mb->mpc; /* reading clears it */ + if (intr & RNTPCO) + mp->stats.rx_length_errors += 256; + mp->stats.rx_length_errors += mb->rntpc; /* reading clears it */ + if (intr & CERR) + ++mp->stats.tx_heartbeat_errors; + if (intr & BABBLE) + if (mace68k_babbles++ < 4) + printk(KERN_DEBUG "mace: babbling transmitter\n"); + if (intr & JABBER) + if (mace68k_jabbers++ < 4) + printk(KERN_DEBUG "mace: jabbering transceiver\n"); +} + +/* + * A transmit error has occured. (We kick the transmit side from + * the DMA completion) + */ + +static void mace68k_xmit_error(struct net_device *dev) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 xmtfs, xmtrc; + + xmtfs = mb->xmtfs; + xmtrc = mb->xmtrc; + + if(xmtfs & XMTSV) + { + if(xmtfs & UFLO) + { + printk("%s: DMA underrun.\n", dev->name); + mp->stats.tx_errors++; + mp->stats.tx_fifo_errors++; + mace68k_reset(dev); + } + if(xmtfs & RTRY) + mp->stats.collisions++; + } + mark_bh(NET_BH); +} + +/* + * A receive interrupt occured. + */ + +static void mace68k_recv_interrupt(struct net_device *dev) +{ +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Process the chip interrupt + */ + +static void mace68k_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + volatile struct mace *mb = mp->mace; + u8 ir; + + ir = mb->ir; + mace68k_handle_misc_intrs(mp, ir); + + if(ir&XMTINT) + mace68k_xmit_error(dev); + if(ir&RCVINT) + mace68k_recv_interrupt(dev); +} + +static void mace68k_tx_timeout(unsigned long data) +{ +// struct net_device *dev = (struct net_device *) data; +// struct mace68k_data *mp = (struct mace68k_data *) dev->priv; +// volatile struct mace *mb = mp->mace; +} + +/* + * Handle a newly arrived frame + */ + +static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf) +{ + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + struct sk_buff *skb; + + if(mf->status&RS_OFLO) + { + printk("%s: fifo overflow.\n", dev->name); + mp->stats.rx_errors++; + mp->stats.rx_fifo_errors++; + } + if(mf->status&(RS_CLSN|RS_FRAMERR|RS_FCSERR)) + mp->stats.rx_errors++; + + if(mf->status&RS_CLSN) + mp->stats.collisions++; + if(mf->status&RS_FRAMERR) + mp->stats.rx_frame_errors++; + if(mf->status&RS_FCSERR) + mp->stats.rx_crc_errors++; + + skb = dev_alloc_skb(mf->len+2); + if(skb==NULL) + { + mp->stats.rx_dropped++; + return; + } + skb_reserve(skb,2); + memcpy(skb_put(skb, mf->len), mf->data, mf->len); + + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + mp->stats.rx_packets++; + mp->stats.rx_bytes+=mf->len; +} + +/* + * The PSC has passed us a DMA interrupt event. + */ + +static void mace68k_dma_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct mace68k_data *mp = (struct mace68k_data *) dev->priv; + +#if 0 + u32 psc_status; + + /* It seems this must be allowed to stabilise ?? */ + + while((psc_status=psc_read_long(0x0804))!=psc_read_long(0x0804)); + + /* + * Was this an ethernet event ? + */ + + if(psc_status&0x60000000) + { +#endif + /* + * Process the read queue + */ + + u16 psc_status = psc_read_word(PSC_ENETRD_CTL); + + printk("mace68k_dma_intr: PSC_ENETRD_CTL = %04X\n", (uint) psc_status); + + if (psc_status & 0x2000) { + mace68k_rxdma_reset(dev); + mp->rx_done = 0; + } else if (psc_status & 0x100) { + int left; + + psc_write_word(PSC_ENETRD_CMD + mp->rx_slot, 0x1100); + left=psc_read_long(PSC_ENETRD_LEN + mp->rx_slot); + /* read packets */ + + while(mp->rx_done < left) + { + struct mace_frame *mf=((struct mace_frame *) + mp->rx_ring)+mp->rx_done++; + mace_dma_rx_frame(dev, mf); + } + + if(left == 0) /* Out of DMA room */ + { + psc_load_rxdma_base(mp->rx_slot, + (void *)virt_to_phys(mp->rx_ring)); + mp->rx_slot^=16; + mp->rx_done = 0; + } + else + { + psc_write_word(PSC_ENETRD_CMD+mp->rx_slot, + 0x9800); + } + + } + + /* + * Process the write queue + */ + + psc_status = psc_read_word(PSC_ENETWR_CTL); + printk("mace68k_dma_intr: PSC_ENETWR_CTL = %04X\n", (uint) psc_status); + + /* apple's driver seems to loop over this until neither */ + /* condition is true. - jmt */ + + if (psc_status & 0x2000) { + mace68k_txdma_reset(dev); + } else if (psc_status & 0x0100) { + psc_write_word(PSC_ENETWR_CMD + mp->tx_slot, 0x0100); + mp->tx_slot ^=16; + mp->tx_count++; + dev->tbusy = 0; + mark_bh(NET_BH); + } +#if 0 + } +#endif +} diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c index d14ccf3fb919..4ae889099f68 100644 --- a/drivers/net/macsonic.c +++ b/drivers/net/macsonic.c @@ -42,153 +42,246 @@ #include #include #include +#include #include #include +#include +#include #include #include #include #include - -#include +#include +#include #define SREGS_PAD(n) u16 n; #include "sonic.h" -extern int mac_onboard_sonic_probe(void); +static int sonic_debug = 0; +static int sonic_version_printed = 0; -static int setup_debug = -1; -static int setup_offset = -1; -static int setup_shift = -1; +extern int macsonic_probe(struct net_device* dev); +extern int mac_onboard_sonic_probe(struct net_device* dev); +extern int mac_nubus_sonic_probe(struct net_device* dev); -/* - * This seems to be the right default for the Q800 - */ +/* For onboard SONIC */ +#define ONBOARD_SONIC_REGISTERS 0x50F0A000 +#define ONBOARD_SONIC_PROM_BASE 0x50f08000 -static int reg_offset = 0; -static int reg_shift = 0; +enum macsonic_type { + MACSONIC_DUODOCK, + MACSONIC_APPLE, + MACSONIC_APPLE16, + MACSONIC_DAYNA, + MACSONIC_DAYNALINK +}; -/* - * Macros to access SONIC registers - */ - -#define MAC_SONIC_REGISTERS 0x50F0A000 -#define MAC_SONIC_PROM_BASE 0x50f08000 -#define MAC_SONIC_IRQ 9 /* Nubus 9 */ +/* For the built-in SONIC in the Duo Dock */ +#define DUODOCK_SONIC_REGISTERS 0xe10000 +#define DUODOCK_SONIC_PROM_BASE 0xe12000 -/* - * FIXME: We may need to invert the byte ordering. These should - * be ok for other aspects as they are uncached spaces. - * The original macros from jazzsonic.c works for me - * on my LC 630, YMMV /Andreas Ehliar - */ +/* For Apple-style NuBus SONIC */ +#define APPLE_SONIC_REGISTERS 0 +#define APPLE_SONIC_PROM_BASE 0x40000 -#if 0 -#define SONIC_READ(reg) \ - *((volatile unsigned int *)base_addr+((reg)<<2)+2) +/* Daynalink LC SONIC */ +#define DAYNALINK_PROM_BASE 0x400000 -#define SONIC_WRITE(reg,val) \ - *((volatile unsigned int *)base_addr+((reg)<<2)+2) = val -#else -#define SONIC_READ(reg) \ - *((volatile unsigned int *)base_addr+reg) +/* For Dayna-style NuBus SONIC (haven't seen one yet) */ +#define DAYNA_SONIC_REGISTERS 0x180000 +/* This is what OpenBSD says. However, this is definitely in NuBus + ROM space so we should be able to get it by walking the NuBus + resource directories */ +#define DAYNA_SONIC_MAC_ADDR 0xffe004 -#define SONIC_WRITE(reg,val) \ - *((volatile unsigned int *)base_addr+reg) = val -#endif +#define SONIC_READ_PROM(addr) readb(prom_addr+addr) + +int __init macsonic_probe(struct net_device* dev) +{ + int rv; + + /* This will catch fatal stuff like -ENOMEM as well as success */ + if ((rv = mac_onboard_sonic_probe(dev)) != -ENODEV) + return rv; + return mac_nubus_sonic_probe(dev); +} -#define SONIC_READ_PROM(addr) \ - *((volatile unsigned char *)prom_addr+addr) /* - * Function : mac_sonic_setup(char *str, int *ints) - * - * Purpose : booter command line initialization of the overrides array, - * - * Inputs : str - unused, ints - array of integer parameters with ints[0] - * equal to the number of ints. - * - * Currently unused in the new driver; need to add settable parameters to the - * detect function. - * + * For reversing the PROM address */ -void mac_sonic_setup(char *str, int *ints) { - /* Format of macsonic parameter is: - * macsonic=,, - * Negative values mean don't change. - */ - - /* Grmbl... the standard parameter parsing can't handle negative numbers - * :-( So let's do it ourselves! - */ +static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, + 1, 9, 5, 13, 3, 11, 7, 15}; - int i = ints[0]+1, fact; +static inline void bit_reverse_addr(unsigned char addr[6]) +{ + int i; - while( str && (isdigit(*str) || *str == '-') && i <= 10) { - if (*str == '-') - fact = -1, ++str; - else - fact = 1; - ints[i++] = simple_strtoul( str, NULL, 0 ) * fact; - if ((str = strchr( str, ',' )) != NULL) - ++str; - } - ints[0] = i-1; - - if (ints[0] < 1) { - printk( "mac_sonic_setup: no arguments!\n" ); - return; - } + for(i = 0; i < 6; i++) + addr[i] = ((nibbletab[addr[i] & 0xf] << 4) | + nibbletab[(addr[i] >> 4) &0xf]); +} + +int __init macsonic_init(struct net_device* dev) +{ + struct sonic_local* lp = (struct sonic_local *)dev->priv; + int i; - if (ints[0] >= 1) { - /* 0 <= n <= 2 */ - if (ints[1] >= 0 && ints[1] <= 8) - setup_debug = ints[1]; - else if (ints[1] > 16) - printk( "mac_sonic_setup: invalid debug level %d !\n", ints[1] ); + /* Allocate the entire chunk of memory for the descriptors. + Note that this cannot cross a 64K boundary. */ + for (i = 0; i < 20; i++) { + unsigned long desc_base, desc_top; + if ((lp->sonic_desc = + kmalloc(SIZEOF_SONIC_DESC + * SONIC_BUS_SCALE(lp->dma_bitmode), GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate descriptor buffers\n", dev->name); + } + desc_base = (unsigned long) lp->sonic_desc; + desc_top = desc_base + SIZEOF_SONIC_DESC * SONIC_BUS_SCALE(lp->dma_bitmode); + if ((desc_top & 0xffff) >= (desc_base & 0xffff)) + break; + /* Hmm. try again (FIXME: does this actually work?) */ + kfree(lp->sonic_desc); + printk(KERN_DEBUG + "%s: didn't get continguous chunk [%08lx - %08lx], trying again\n", + dev->name, desc_base, desc_top); } - if (ints[0] >= 2) { - /* 0 <= n <= 2 */ - if (ints[2] >= 0 && ints[2] <= 16) - setup_offset = ints[2]; - else if (ints[2] > 16) - printk( "mac_sonic_setup: invalid offset %d !\n", ints[2] ); + + if (lp->sonic_desc == NULL) { + printk(KERN_ERR "%s: tried 20 times to allocate descriptor buffers, giving up.\n", + dev->name); + return -ENOMEM; + } + + /* Now set up the pointers to point to the appropriate places */ + lp->cda = lp->sonic_desc; + lp->tda = lp->cda + (SIZEOF_SONIC_CDA * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rda = lp->tda + (SIZEOF_SONIC_TD * SONIC_NUM_TDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + lp->rra = lp->rda + (SIZEOF_SONIC_RD * SONIC_NUM_RDS + * SONIC_BUS_SCALE(lp->dma_bitmode)); + + /* FIXME, maybe we should use skbs */ + if ((lp->rba = (char *) + kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_DMA)) == NULL) { + printk(KERN_ERR "%s: couldn't allocate receive buffers\n", dev->name); + return -ENOMEM; } - if (ints[0] >= 3) { - /* 0 <= n <= 2 */ - if (ints[3] >= 0 && ints[3] <= 16) - setup_shift = ints[3]; - else if (ints[3] > 16) - printk( "mac_sonic_setup: invalid shift %d !\n", ints[3] ); + + { + int rs, ds; + + /* almost always 12*4096, but let's not take chances */ + rs = ((SONIC_NUM_RRS * SONIC_RBSIZE + 4095) / 4096) * 4096; + /* almost always under a page, but let's not take chances */ + ds = ((SIZEOF_SONIC_DESC + 4095) / 4096) * 4096; + kernel_set_cachemode(lp->rba, rs, IOMAP_NOCACHE_SER); + kernel_set_cachemode(lp->sonic_desc, ds, IOMAP_NOCACHE_SER); } + +#if 0 + flush_cache_all(); +#endif + + dev->open = sonic_open; + dev->stop = sonic_close; + dev->hard_start_xmit = sonic_send_packet; + dev->get_stats = sonic_get_stats; + dev->set_multicast_list = &sonic_multicast_list; + + /* + * clear tally counter + */ + sonic_write(dev, SONIC_CRCT, 0xffff); + sonic_write(dev, SONIC_FAET, 0xffff); + sonic_write(dev, SONIC_MPT, 0xffff); + + /* Fill in the fields of the device structure with ethernet values. */ + ether_setup(dev); + return 0; } -static int sonic_debug = 0; +int __init mac_onboard_sonic_ethernet_addr(struct net_device* dev) +{ + const int prom_addr = ONBOARD_SONIC_PROM_BASE; + int i; -/* - * For reversing the PROM address - */ + /* On NuBus boards we can sometimes look in the ROM resources. + No such luck for comm-slot/onboard. */ + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); -static unsigned char nibbletab[] = {0, 8, 4, 12, 2, 10, 6, 14, - 1, 9, 5, 13, 3, 11, 7, 15}; + /* Most of the time, the address is bit-reversed. The NetBSD + source has a rather long and detailed historical account of + why this is so. */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + bit_reverse_addr(dev->dev_addr); + else + return 0; + + /* If we still have what seems to be a bogus address, we'll + look in the CAM. The top entry should be ours. */ + /* Danger! This only works if MacOS has already initialized + the card... */ + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + unsigned short val; + + printk(KERN_INFO "macsonic: PROM seems to be wrong, trying CAM entry 15\n"); + + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_CEP, 15); + + val = sonic_read(dev, SONIC_CAP2); + dev->dev_addr[5] = val >> 8; + dev->dev_addr[4] = val & 0xff; + val = sonic_read(dev, SONIC_CAP1); + dev->dev_addr[3] = val >> 8; + dev->dev_addr[2] = val & 0xff; + val = sonic_read(dev, SONIC_CAP0); + dev->dev_addr[1] = val >> 8; + dev->dev_addr[0] = val & 0xff; + + printk(KERN_INFO "HW Address from CAM 15: "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } + printk("\n"); + } else return 0; -int __init mac_onboard_sonic_probe(void) + if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && + memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && + memcmp(dev->dev_addr, "\x00\x05\x02", 3)) + { + /* + * Still nonsense ... messed up someplace! + */ + printk(KERN_ERR "macsonic: ERROR (INVALID MAC)\n"); + return -EIO; + } else return 0; +} + +int __init mac_onboard_sonic_probe(struct net_device* dev) { - struct net_device *dev; - unsigned int silicon_revision; - unsigned int val; - struct sonic_local *lp; + /* Bwahahaha */ + static int once_is_more_than_enough = 0; + struct sonic_local* lp; int i; - int base_addr = MAC_SONIC_REGISTERS; - int prom_addr = MAC_SONIC_PROM_BASE; - static int one=0; - if (!MACH_IS_MAC) + if (once_is_more_than_enough) return -ENODEV; + once_is_more_than_enough = 1; - if(++one!=1) /* Only one is allowed */ + if (!MACH_IS_MAC) return -ENODEV; printk(KERN_INFO "Checking for internal Macintosh ethernet (SONIC).. "); @@ -198,288 +291,332 @@ int __init mac_onboard_sonic_probe(void) printk("none.\n"); return -ENODEV; } - - printk("yes\n"); - - if (setup_debug >= 0) - sonic_debug = setup_debug; - /* - * This may depend on the actual Mac model ... works for me. - */ - reg_offset = - (setup_offset >= 0) ? setup_offset : 0; - reg_shift = - (setup_shift >= 0) ? setup_shift : 0; + /* Bogus probing, on the models which may or may not have + Ethernet (BTW, the Ethernet *is* always at the same + address, and nothing else lives there, at least if Apple's + documentation is to be believed) */ + if (macintosh_config->ident == MAC_MODEL_Q630 || + macintosh_config->ident == MAC_MODEL_P588 || + macintosh_config->ident == MAC_MODEL_C610) { + unsigned long flags; + int card_present; + + save_flags(flags); + cli(); + card_present = hwreg_present((void*)ONBOARD_SONIC_REGISTERS); + restore_flags(flags); + + if (!card_present) { + printk("none.\n"); + return -ENODEV; + } + } - /* - * get the Silicon Revision ID. If this is one of the known - * one assume that we found a SONIC ethernet controller at - * the expected location. - * (This is not implemented in the Macintosh driver yet; need - * to collect values from various sources. Mine is 0x4 ...) - */ + printk("yes\n"); - silicon_revision = SONIC_READ(SONIC_SR); - if (sonic_debug > 1) - printk("SONIC Silicon Revision = 0x%04x\n", silicon_revision); + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } - /* - * We need to allocate sonic_local later on, making sure it's - * aligned on a 64k boundary. So, no space for dev->priv allocated - * here ... - */ - dev = init_etherdev(0,0); - - if(dev==NULL) + if (dev == NULL) return -ENOMEM; - printk("%s: %s found at 0x%08x, ", - dev->name, "SONIC ethernet", base_addr); + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ - if (sonic_debug > 1) - printk("using offset %d shift %d,", reg_offset, reg_shift); + dev->base_addr = ONBOARD_SONIC_REGISTERS; + if (via_alt_mapping) + dev->irq = IRQ_AUTO_3; + else + dev->irq = IRQ_NUBUS_9; - /* Fill in the 'dev' fields. */ - dev->base_addr = base_addr; - dev->irq = MAC_SONIC_IRQ; + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: onboard / comm-slot SONIC at 0x%08lx\n", + dev->name, dev->base_addr); + + /* Now do a song and dance routine in an attempt to determine + the bus width */ + + /* The PowerBook's SONIC is 16 bit always. */ + if (macintosh_config->ident == MAC_MODEL_PB520) { + lp->reg_offset = 0; + lp->dma_bitmode = 0; + } else { + /* Some of the comm-slot cards are 16 bit. But some + of them are not. The 32-bit cards use offset 2 and + pad with zeroes or sometimes ones (I think...) + Therefore, if we try offset 0 and get a silicon + revision of 0, we assume 16 bit. */ + int sr; + + /* Technically this is not necessary since we zeroed + it above */ + lp->reg_offset = 0; + lp->dma_bitmode = 0; + sr = sonic_read(dev, SONIC_SR); + if (sr == 0 || sr == 0xffff) { + lp->reg_offset = 2; + /* 83932 is 0x0004, 83934 is 0x0100 or 0x0101 */ + sr = sonic_read(dev, SONIC_SR); + lp->dma_bitmode = 1; + + } + printk(KERN_INFO + "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sr, lp->dma_bitmode?32:16, lp->reg_offset); + } - /* - * Put the sonic into software reset, then - * retrieve and print the ethernet address. - */ - SONIC_WRITE(SONIC_CMD, SONIC_CR_RST); + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, SONIC_DCR_BMS | + SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | + (lp->dma_bitmode ? SONIC_DCR_DW : 0)); + /* This *must* be written back to in order to restore the + extended programmable output bits */ + sonic_write(dev, SONIC_DCR2, 0); - /* - * We can't trust MacOS to initialise things it seems. - */ + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); - if (sonic_debug > 1) - printk("SONIC_DCR was %X\n",SONIC_READ(SONIC_DCR)); - - SONIC_WRITE(SONIC_DCR, - SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_EXBUS | SONIC_DCR_DW); + /* Now look for the MAC address. */ + if (mac_onboard_sonic_ethernet_addr(dev) != 0) + return -ENODEV; - /* - * We don't want floating spare IRQ's around, not on - * level triggered systems! - * Strange though - writing to the ISR only clears currently - * pending IRQs, but doesn't disable them... Does this make - * a difference?? Seems it does ... - */ -#if 1 - SONIC_WRITE(SONIC_ISR,0x7fff); - SONIC_WRITE(SONIC_IMR,0); -#else - SONIC_WRITE(SONIC_ISR, SONIC_IMR_DEFAULT); -#endif - - /* This is how it is done in jazzsonic.c - * It doesn't seem to work here though. - */ - if (sonic_debug > 2) { - printk("Retreiving CAM entry 0. This should be the HW address.\n"); - - SONIC_WRITE(SONIC_CEP, 0); - for (i = 0; i < 3; i++) - { - val = SONIC_READ(SONIC_CAP0 - i); - dev->dev_addr[i * 2] = val; - dev->dev_addr[i * 2 + 1] = val >> 8; - } + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { + printk("%2.2x", dev->dev_addr[i]); + if (i < 5) + printk(":"); + } - printk("HW Address from CAM 0: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); + printk(" IRQ %d\n", dev->irq); - printk("Retreiving CAM entry 15. Another candidate...\n"); + /* Shared init code */ + return macsonic_init(dev); +} - /* - * MacOS seems to use CAM entry 15 ... - */ - SONIC_WRITE(SONIC_CEP, 15); - for (i = 0; i < 3; i++) - { - val = SONIC_READ(SONIC_CAP0 - i); - dev->dev_addr[i * 2] = val; - dev->dev_addr[i * 2 + 1] = val >> 8; - } +int __init mac_nubus_sonic_ethernet_addr(struct net_device* dev, + unsigned long prom_addr, + int id) +{ + int i; + for(i = 0; i < 6; i++) + dev->dev_addr[i] = SONIC_READ_PROM(i); + /* For now we are going to assume that they're all bit-reversed */ + bit_reverse_addr(dev->dev_addr); - printk("HW Address from CAM 15: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); - } + return 0; +} - /* - * if we can read the PROM, we're safe :-) - */ - if (sonic_debug > 1) - printk("Retreiving HW address from the PROM: "); - - for(i=0;i<6;i++){ - dev->dev_addr[i]=SONIC_READ_PROM(i); - } - if (sonic_debug > 1) { - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); +int __init macsonic_ident(struct nubus_dev* ndev) +{ + if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC && + ndev->dr_sw == NUBUS_DRSW_SONIC_LC) + return MACSONIC_DAYNALINK; + if (ndev->dr_hw == NUBUS_DRHW_SONIC && + ndev->dr_sw == NUBUS_DRSW_APPLE) { + /* There has to be a better way to do this... */ + if (strstr(ndev->board->name, "DuoDock")) + return MACSONIC_DUODOCK; + else + return MACSONIC_APPLE; } - /* - * If its not one of these we have - * screwed up on this Mac model - */ + return -1; +} - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) +int __init mac_nubus_sonic_probe(struct net_device* dev) +{ + static int slots = 0; + struct nubus_dev* ndev = NULL; + struct sonic_local* lp; + unsigned long base_addr, prom_addr; + u16 sonic_dcr; + int id; + int i; + int reg_offset, dma_bitmode; + + /* Find the first SONIC that hasn't been initialized already */ + while ((ndev = nubus_find_type(NUBUS_CAT_NETWORK, + NUBUS_TYPE_ETHERNET, ndev)) != NULL) { - /* - * Try bit reversed - */ - for(i=0;i<6;i++){ - val = SONIC_READ_PROM(i); - dev->dev_addr[i]=(nibbletab[val & 0xf] << 4) | - nibbletab[(val >> 4) &0xf]; - } - if (sonic_debug > 1) { - printk("Trying bit reversed: "); - for (i = 0; i < 6; i++) - { - printk("%2.2x", dev->dev_addr[i]); - if (i < 5) - printk(":"); - } - printk("\n"); - } - if (memcmp(dev->dev_addr, "\x08\x00\x07", 3) && - memcmp(dev->dev_addr, "\x00\xA0\x40", 3) && - memcmp(dev->dev_addr, "\x00\x05\x02", 3)) - { - /* - * Still nonsense ... messed up someplace! - */ - printk("ERROR (INVALID MAC)\n"); - return -EIO; - } + /* Have we seen it already? */ + if (slots & (1<board->slot)) + continue; + slots |= 1<board->slot; + + /* Is it one of ours? */ + if ((id = macsonic_ident(ndev)) != -1) + break; } - printk(" MAC "); - for (i = 0; i < 6; i++) - { + if (ndev == NULL) + return -ENODEV; + + switch (id) { + case MACSONIC_DUODOCK: + base_addr = ndev->board->slot_addr + DUODOCK_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DUODOCK_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS | SONIC_DCR_RFT0 | SONIC_DCR_RFT1 + | SONIC_DCR_TFT0; + reg_offset = 2; + dma_bitmode = 1; + break; + case MACSONIC_APPLE: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_BMS | SONIC_DCR_RFT1 | SONIC_DCR_TFT0; + reg_offset = 0; + dma_bitmode = 1; + break; + case MACSONIC_APPLE16: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + APPLE_SONIC_PROM_BASE; + sonic_dcr = SONIC_DCR_EXBUS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNALINK: + base_addr = ndev->board->slot_addr + APPLE_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNALINK_PROM_BASE; + sonic_dcr = SONIC_DCR_RFT1 | SONIC_DCR_TFT0 + | SONIC_DCR_PO1 | SONIC_DCR_BMS; + reg_offset = 0; + dma_bitmode = 0; + break; + case MACSONIC_DAYNA: + base_addr = ndev->board->slot_addr + DAYNA_SONIC_REGISTERS; + prom_addr = ndev->board->slot_addr + DAYNA_SONIC_MAC_ADDR; + sonic_dcr = SONIC_DCR_BMS + | SONIC_DCR_RFT1 | SONIC_DCR_TFT0 | SONIC_DCR_PO1; + reg_offset = 0; + dma_bitmode = 0; + break; + default: + printk(KERN_ERR "macsonic: WTF, id is %d\n", id); + return -ENODEV; + } + + if (dev) { + dev = init_etherdev(dev, sizeof(struct sonic_local)); + /* methinks this will always be true but better safe than sorry */ + if (dev->priv == NULL) + dev->priv = kmalloc(sizeof(struct sonic_local), GFP_KERNEL); + } else { + dev = init_etherdev(NULL, sizeof(struct sonic_local)); + } + + if (dev == NULL) + return -ENOMEM; + + lp = (struct sonic_local*) dev->priv; + memset(lp, 0, sizeof(struct sonic_local)); + /* Danger! My arms are flailing wildly! You *must* set this + before using sonic_read() */ + lp->reg_offset = reg_offset; + lp->dma_bitmode = dma_bitmode; + dev->base_addr = base_addr; + dev->irq = SLOT2IRQ(ndev->board->slot); + + if (!sonic_version_printed) { + printk(KERN_INFO "%s", version); + sonic_version_printed = 1; + } + printk(KERN_INFO "%s: %s in slot %X\n", + dev->name, ndev->board->name, ndev->board->slot); + printk(KERN_INFO "%s: revision 0x%04x, using %d bit DMA and register offset %d\n", + dev->name, sonic_read(dev, SONIC_SR), dma_bitmode?32:16, reg_offset); + + /* Software reset, then initialize control registers. */ + sonic_write(dev, SONIC_CMD, SONIC_CR_RST); + sonic_write(dev, SONIC_DCR, sonic_dcr + | (dma_bitmode ? SONIC_DCR_DW : 0)); + + /* Clear *and* disable interrupts to be on the safe side */ + sonic_write(dev, SONIC_ISR,0x7fff); + sonic_write(dev, SONIC_IMR,0); + + /* Now look for the MAC address. */ + if (mac_nubus_sonic_ethernet_addr(dev, prom_addr, id) != 0) + return -ENODEV; + + printk(KERN_INFO "MAC "); + for (i = 0; i < 6; i++) { printk("%2.2x", dev->dev_addr[i]); if (i < 5) printk(":"); } + printk(" IRQ %d\n", dev->irq); - printk(" IRQ %d\n", MAC_SONIC_IRQ); - - /* Initialize the device structure. */ - if (dev->priv == NULL) - { - if (sonic_debug > 2) { - printk("Allocating memory for dev->priv aka lp\n"); - printk("Memory to allocate: %d\n",sizeof(*lp)); - } - /* - * the memory be located in the same 64kb segment - */ - lp = NULL; - i = 0; - do - { - lp = (struct sonic_local *) kmalloc(sizeof(*lp), GFP_KERNEL); - if ((unsigned long) lp >> 16 != ((unsigned long) lp + sizeof(*lp)) >> 16) - { - /* FIXME, free the memory later */ - kfree(lp); - lp = NULL; - } - } - while (lp == NULL && i++ < 20); + /* Shared init code */ + return macsonic_init(dev); +} - if (lp == NULL) - { - printk("%s: couldn't allocate memory for descriptors\n", - dev->name); - return -ENOMEM; - } +#ifdef MODULE +static char namespace[16] = ""; +static struct net_device dev_macsonic = { + NULL, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0, NULL, NULL }; - if (sonic_debug > 2) { - printk("Memory allocated after %d tries\n",i); - } +MODULE_PARM(sonic_debug, "i"); - /* XXX sonic_local has the TDA, RRA, RDA, don't cache */ - kernel_set_cachemode((u32)lp, 8192, IOMAP_NOCACHE_SER); - memset(lp, 0, sizeof(struct sonic_local)); +EXPORT_NO_SYMBOLS; - lp->cda_laddr = (u32)lp; - if (sonic_debug > 2) { - printk("memory allocated for sonic at 0x%x\n",lp); - } - lp->tda_laddr = lp->cda_laddr + sizeof(lp->cda); - lp->rra_laddr = lp->tda_laddr + sizeof(lp->tda); - lp->rda_laddr = lp->rra_laddr + sizeof(lp->rra); - - /* allocate receive buffer area */ - /* FIXME, maybe we should use skbs */ - if ((lp->rba = (char *) kmalloc(SONIC_NUM_RRS * SONIC_RBSIZE, GFP_KERNEL)) == NULL) - { - printk("%s: couldn't allocate receive buffers\n", dev->name); - return -ENOMEM; - } - /* XXX RBA written by Sonic, not cached either */ - kernel_set_cachemode((u32)lp->rba, 6*8192, IOMAP_NOCACHE_SER); - lp->rba_laddr = (u32)lp->rba; - flush_cache_all(); - dev->priv = (struct sonic_local *) lp; - } - lp = (struct sonic_local *) dev->priv; - dev->open = sonic_open; - dev->stop = sonic_close; - dev->hard_start_xmit = sonic_send_packet; - dev->get_stats = sonic_get_stats; - dev->set_multicast_list = &sonic_multicast_list; +int +init_module(void) +{ + dev_macsonic.name = namespace; + dev_macsonic.init = macsonic_probe; - /* Fill in the fields of the device structure with ethernet values. */ - ether_setup(dev); + if (register_netdev(&dev_macsonic) != 0) { + printk(KERN_WARNING "macsonic.c: No card found\n"); + return -ENXIO; + } return 0; } -/* - * SONIC uses a nubus IRQ - */ +void +cleanup_module(void) +{ + if (dev_macsonic.priv != NULL) { + unregister_netdev(&dev_macsonic); + kfree(dev_macsonic.priv); + dev_macsonic.priv = NULL; + } +} +#endif /* MODULE */ -#define sonic_request_irq(irq, vec, flags, name, dev) \ - nubus_request_irq(irq, dev, vec) -#define sonic_free_irq(irq,id) nubus_free_irq(irq) -/* - * No funnies on memory mapping. - */ +#define vdma_alloc(foo, bar) ((u32)foo) +#define vdma_free(baz) +#define sonic_chiptomem(bat) (bat) +#define PHYSADDR(quux) (quux) -#define sonic_chiptomem(x) (x) +#include "sonic.c" /* - * No VDMA on a Macintosh. So we need request no such facility. + * Local variables: + * compile-command: "m68k-linux-gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -ffixed-a2 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -c -o macsonic.o macsonic.c" + * version-control: t + * kept-new-versions: 5 + * c-indent-level: 8 + * tab-width: 8 + * End: + * */ - -#define vdma_alloc(x,y) ((u32)(x)) -#define vdma_free(x) -#define PHYSADDR(x) (x) - -#include "sonic.c" diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index 4fff08b7fcd2..006e36f4bbe2 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -21,7 +21,7 @@ */ -static char *version = "sun3lance.c: v1.0 12/12/96 Sam Creasey (sammy@users.qual.net)\n"; +static char *version = "sun3lance.c: v1.1 11/17/1999 Sam Creasey (sammy@oh.verio.com)\n"; #include @@ -263,6 +263,8 @@ static int __init lance_probe( struct net_device *dev) int i; static int did_version = 0; int found = 0; + volatile unsigned short *ioaddr_probe; + unsigned short tmp1, tmp2; /* LANCE_OBIO can be found within the IO pmeg with some effort */ for(ioaddr = 0xfe00000; ioaddr < (0xfe00000 + @@ -282,6 +284,23 @@ static int __init lance_probe( struct net_device *dev) if(!found) return 0; + /* test to see if there's really a lance here */ + /* (CSRO_INIT shouldn't be readable) */ + + ioaddr_probe = (volatile unsigned short *)ioaddr; + tmp1 = ioaddr_probe[0]; + tmp2 = ioaddr_probe[1]; + + ioaddr_probe[1] = CSR0; + ioaddr_probe[0] = CSR0_INIT | CSR0_STOP; + + if(ioaddr_probe[0] != CSR0_STOP) { + ioaddr_probe[0] = tmp1; + ioaddr_probe[1] = tmp2; + + return 0; + } + init_etherdev( dev, sizeof(struct lance_private) ); if (!dev->priv) dev->priv = kmalloc( sizeof(struct lance_private), GFP_KERNEL ); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0790a197804a..ccbacf12737f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -373,6 +373,7 @@ static int sd_init_command(Scsi_Cmnd * SCpnt) static int sd_open(struct inode *inode, struct file *filp) { int target; + Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); SCSI_LOG_HLQUEUE(1, printk("target=%d, max=%d\n", target, sd_template.dev_max)); @@ -412,13 +413,14 @@ static int sd_open(struct inode *inode, struct file *filp) if ((rscsi_disks[target].write_prot) && (filp->f_mode & 2)) return -EROFS; } + SDev = rscsi_disks[target].device; /* * It is possible that the disk changing stuff resulted in the device * being taken offline. If this is the case, report this to the user, * and don't pretend that * the open actually succeeded. */ - if (!rscsi_disks[target].device->online) { + if (!SDev->online) { return -ENXIO; } /* @@ -428,13 +430,14 @@ static int sd_open(struct inode *inode, struct file *filp) if (sd_sizes[SD_PARTITION(inode->i_rdev)] == 0) return -ENXIO; - if (rscsi_disks[target].device->removable) - if (!rscsi_disks[target].device->access_count) - sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0); + if (SDev->removable) + if (!SDev->access_count) + if (scsi_block_when_processing_errors(SDev)) + scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, NULL); - rscsi_disks[target].device->access_count++; - if (rscsi_disks[target].device->host->hostt->module) - __MOD_INC_USE_COUNT(rscsi_disks[target].device->host->hostt->module); + SDev->access_count++; + if (SDev->host->hostt->module) + __MOD_INC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) __MOD_INC_USE_COUNT(sd_template.module); return 0; @@ -443,17 +446,20 @@ static int sd_open(struct inode *inode, struct file *filp) static int sd_release(struct inode *inode, struct file *file) { int target; + Scsi_Device * SDev; target = DEVICE_NR(inode->i_rdev); + SDev = rscsi_disks[target].device; - rscsi_disks[target].device->access_count--; + SDev->access_count--; - if (rscsi_disks[target].device->removable) { - if (!rscsi_disks[target].device->access_count) - sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0); + if (SDev->removable) { + if (!SDev->access_count) + if (scsi_block_when_processing_errors(SDev)) + scsi_ioctl(SDev, SCSI_IOCTL_DOORUNLOCK, NULL); } - if (rscsi_disks[target].device->host->hostt->module) - __MOD_DEC_USE_COUNT(rscsi_disks[target].device->host->hostt->module); + if (SDev->host->hostt->module) + __MOD_DEC_USE_COUNT(SDev->host->hostt->module); if (sd_template.module) __MOD_DEC_USE_COUNT(sd_template.module); return 0; @@ -580,17 +586,17 @@ static int check_scsidisk_media_change(kdev_t full_dev) { int retval; int target; - struct inode inode; int flag = 0; + Scsi_Device * SDev; target = DEVICE_NR(full_dev); + SDev = rscsi_disks[target].device; - if (target >= sd_template.dev_max || - !rscsi_disks[target].device) { + if (target >= sd_template.dev_max || !SDev) { printk("SCSI disk request error: invalid device.\n"); return 0; } - if (!rscsi_disks[target].device->removable) + if (!SDev->removable) return 0; /* @@ -599,13 +605,12 @@ static int check_scsidisk_media_change(kdev_t full_dev) * can deal with it then. It is only because of unrecoverable errors * that we would ever take a device offline in the first place. */ - if (rscsi_disks[target].device->online == FALSE) { + if (SDev->online == FALSE) { rscsi_disks[target].ready = 0; - rscsi_disks[target].device->changed = 1; + SDev->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ } - inode.i_rdev = full_dev; /* This is all we really need here */ /* Using Start/Stop enables differentiation between drive with * no cartridge loaded - NOT READY, drive with changed cartridge - @@ -613,7 +618,9 @@ static int check_scsidisk_media_change(kdev_t full_dev) * This also handles drives that auto spin down. eg iomega jaz 1GB * as this will spin up the drive. */ - retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_START_UNIT, 0); + retval = -ENODEV; + if (scsi_block_when_processing_errors(SDev)) + retval = scsi_ioctl(SDev, SCSI_IOCTL_START_UNIT, NULL); if (retval) { /* Unable to test, unit probably not ready. * This usually means there is no disc in the @@ -622,7 +629,7 @@ static int check_scsidisk_media_change(kdev_t full_dev) * again. */ rscsi_disks[target].ready = 0; - rscsi_disks[target].device->changed = 1; + SDev->changed = 1; return 1; /* This will force a flush, if called from * check_disk_change */ } @@ -634,9 +641,9 @@ static int check_scsidisk_media_change(kdev_t full_dev) rscsi_disks[target].ready = 1; /* FLOPTICAL */ - retval = rscsi_disks[target].device->changed; + retval = SDev->changed; if (!flag) - rscsi_disks[target].device->changed = 0; + SDev->changed = 0; return retval; } diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index 4859685c63f5..c218ca69dc39 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -26,7 +26,7 @@ if [ "$CONFIG_VISWS" = "y" ]; then dep_tristate ' SGI Visual Workstation Sound' CONFIG_SOUND_VWSND $CONFIG_SOUND fi -dep_tristate ' Trident 4DWave-DX/NX' CONFIG_SOUND_TRIDENT $CONFIG_SOUND +dep_tristate ' Trident 4DWave DX/NX or SiS 7018 PCI Audio Core' CONFIG_SOUND_TRIDENT $CONFIG_SOUND dep_tristate ' Support for Turtle Beach MultiSound Classic, Tahiti, Monterey' CONFIG_SOUND_MSNDCLAS $CONFIG_SOUND if [ "$CONFIG_SOUND_MSNDCLAS" = "y" -o "$CONFIG_SOUND_MSNDCLAS" = "m" ]; then diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 50ba920b93c9..17f4045319e1 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -25,7 +25,7 @@ endif export-objs := ad1848.o audio_syms.o midi_syms.o mpu401.o \ msnd.o opl3.o sb_card.o sequencer_syms.o \ sound_core.o sound_syms.o uart401.o ad1816.o \ - nm256_audio.o ac97.o + nm256_audio.o ac97.o ac97_codec.o @@ -84,7 +84,7 @@ obj-$(CONFIG_SOUND_ES1370) += es1370.o obj-$(CONFIG_SOUND_ES1371) += es1371.o obj-$(CONFIG_SOUND_ESSSOLO1) += esssolo1.o obj-$(CONFIG_SOUND_MAESTRO) += maestro.o -obj-$(CONFIG_SOUND_TRIDENT) += trident.o +obj-$(CONFIG_SOUND_TRIDENT) += trident.o ac97_codec.o # Declare multi-part drivers. diff --git a/drivers/sound/ac97_codec.c b/drivers/sound/ac97_codec.c new file mode 100644 index 000000000000..ab31e2f15214 --- /dev/null +++ b/drivers/sound/ac97_codec.c @@ -0,0 +1,441 @@ +/* + * ac97_codec.c: Generic AC97 mixer module + * + * Derived from ac97 mixer in maestro and trident driver. + * + * Copyright 2000 Silicon Integrated System Corporation + * + * 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 distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History + * Jan 14 2000 Ollie Lho + * Isloated from trident.c to support multiple ac97 codec + */ +#include +#include +#include +#include + +#include "ac97_codec.h" + +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel); +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right); +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ); +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask); +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg); + +static struct { + unsigned int id; + char *name; + int (*init) (struct ac97_codec *codec); +} snd_ac97_codec_ids[] = { + {0x414B4D00, "Asahi Kasei AK4540" , NULL}, + {0x41445340, "Analog Devices AD1881" , NULL}, + {0x43525900, "Cirrus Logic CS4297" , NULL}, + {0x43525913, "Cirrus Logic CS4297A" , NULL}, + {0x43525931, "Cirrus Logic CS4299" , NULL}, + {0x4e534331, "National Semiconductor LM4549", NULL}, + {0x83847600, "SigmaTel STAC????" , NULL}, + {0x83847604, "SigmaTel STAC9701/3/4/5", NULL}, + {0x83847605, "SigmaTel STAC9704" , NULL}, + {0x83847608, "SigmaTel STAC9708" , NULL}, + {0x83847609, "SigmaTel STAC9721/23" , NULL}, + {0x00000000, NULL, NULL} +}; + + +/* this table has default mixer values for all OSS mixers. */ +static struct mixer_defaults { + int mixer; + unsigned int value; +} mixer_defaults[SOUND_MIXER_NRDEVICES] = { + /* all values 0 -> 100 in bytes */ + {SOUND_MIXER_VOLUME, 0x3232}, + {SOUND_MIXER_BASS, 0x3232}, + {SOUND_MIXER_TREBLE, 0x3232}, + {SOUND_MIXER_PCM, 0x3232}, + {SOUND_MIXER_SPEAKER, 0x3232}, + {SOUND_MIXER_LINE, 0x3232}, + {SOUND_MIXER_MIC, 0x3232}, + {SOUND_MIXER_CD, 0x3232}, + {SOUND_MIXER_ALTPCM, 0x3232}, + {SOUND_MIXER_IGAIN, 0x3232}, + {SOUND_MIXER_LINE1, 0x3232}, + {SOUND_MIXER_PHONEIN, 0x3232}, + {SOUND_MIXER_PHONEOUT, 0x3232}, + {SOUND_MIXER_VIDEO, 0x3232}, + {-1,0} +}; + +/* table to scale scale from OSS mixer value to AC97 mixer register value */ +static struct ac97_mixer_hw { + unsigned char offset; + int scale; +} ac97_hw[SOUND_MIXER_NRDEVICES]= { + [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,63}, + [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 15}, + [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 15}, + [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 31}, + [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 15}, + [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 31}, + [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 31}, + [SOUND_MIXER_CD] = {AC97_CD_VOL, 31}, + [SOUND_MIXER_ALTPCM] = {AC97_HEADPHONE_VOL, 63}, + [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 31}, + [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 31}, + [SOUND_MIXER_PHONEIN] = {AC97_PHONE_VOL, 15}, + [SOUND_MIXER_PHONEOUT] = {AC97_MASTER_VOL_MONO, 63}, + [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 31}, +}; + +/* the following tables allow us to go from OSS <-> ac97 quickly. */ +enum ac97_recsettings { + AC97_REC_MIC=0, + AC97_REC_CD, + AC97_REC_VIDEO, + AC97_REC_AUX, + AC97_REC_LINE, + AC97_REC_STEREO, /* combination of all enabled outputs.. */ + AC97_REC_MONO, /*.. or the mono equivalent */ + AC97_REC_PHONE +}; + +static unsigned int ac97_rm2oss[] = { + [AC97_REC_MIC] = SOUND_MIXER_MIC, + [AC97_REC_CD] = SOUND_MIXER_CD, + [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, + [AC97_REC_AUX] = SOUND_MIXER_LINE1, + [AC97_REC_LINE] = SOUND_MIXER_LINE, + [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN +}; + +/* indexed by bit position */ +static unsigned int ac97_oss_rm[] = { + [SOUND_MIXER_MIC] = AC97_REC_MIC, + [SOUND_MIXER_CD] = AC97_REC_CD, + [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, + [SOUND_MIXER_LINE1] = AC97_REC_AUX, + [SOUND_MIXER_LINE] = AC97_REC_LINE, + [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE +}; + +/* reads the given OSS mixer from the ac97 the caller must have insured that the ac97 knows + about that given mixer, and should be holding a spinlock for the card */ +static int ac97_read_mixer(struct ac97_codec *codec, int oss_channel) +{ + u16 val; + int ret = 0; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + + val = codec->codec_read(codec , mh->offset); + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* nice stereo mixers .. */ + int left,right; + + left = (val >> 8) & 0x7f; + right = val & 0x7f; + + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * 100) / mh->scale; + left = (left * 100) / mh->scale; + } else { + right = 100 - ((right * 100) / mh->scale); + left = 100 - ((left * 100) / mh->scale); + } + + ret = left | (right << 8); + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_MIC) { + ret = 100 - (((val & 0x1f) * 100) / mh->scale); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); + } else if (oss_channel == SOUND_MIXER_TREBLE) { + ret = 100 - (((val & 0xe) * 100) / mh->scale); + } + +#ifdef DEBUG + printk("ac97_codec: read OSS mixer %2d (ac97 register 0x%02x), " + "0x%04x -> 0x%04x\n", oss_channel, mh->offset, val, ret); +#endif + + return ret; +} + +/* write the OSS encoded volume to the given OSS encoded mixer, again caller's job to + make sure all is well in arg land, call with spinlock held */ +static void ac97_write_mixer(struct ac97_codec *codec, int oss_channel, + unsigned int left, unsigned int right) +{ + u16 val = 0; + struct ac97_mixer_hw *mh = &ac97_hw[oss_channel]; + +#ifdef DEBUG + printk("ac97_codec: wrote OSS mixer %2d (%s ac97 register 0x%02x), " + "left vol:%2d, right vol:%2d:", + oss_channel, codec->id ? "Secondary" : "Primary", + mh->offset, left, right); +#endif + + if (AC97_STEREO_MASK & (1 << oss_channel)) { + /* stereo mixers */ + if (oss_channel == SOUND_MIXER_IGAIN) { + right = (right * mh->scale) / 100; + left = (left * mh->scale) / 100; + } else { + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + } + val = (left << 8) | right; + } else if (oss_channel == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (oss_channel == SOUND_MIXER_MIC) { + val = codec->codec_read(codec , mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets us avoid the 0xf 'bypass'.. */ + } else if (oss_channel == SOUND_MIXER_BASS) { + val = codec->codec_read(codec , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (oss_channel == SOUND_MIXER_TREBLE) { + val = codec->codec_read(codec , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + +#ifdef DEBUG + printk(" 0x%04x", val); +#endif + codec->codec_write(codec, mh->offset, val); + +#ifdef DEBUG + val = codec->codec_read(codec, mh->offset); + printk(" -> 0x%04x\n", val); +#endif +} + +/* a thin wrapper for write_mixer */ +static void ac97_set_mixer(struct ac97_codec *codec, unsigned int oss_mixer, unsigned int val ) +{ + unsigned int left,right; + + /* cleanse input a little */ + right = ((val >> 8) & 0xff) ; + left = (val & 0xff) ; + + if (right > 100) right = 100; + if (left > 100) left = 100; + + codec->mixer_state[oss_mixer] = (right << 8) | left; + codec->write_mixer(codec, oss_mixer, left, right); +} + +/* read or write the recmask, the ac97 can really have left and right recording + inputs independantly set, but OSS doesn't seem to want us to express that to + the user. the caller guarantees that we have a supported bit set, and they + must be holding the card's spinlock */ +static int ac97_recmask_io(struct ac97_codec *codec, int rw, int mask) +{ + unsigned int val; + + if (rw) { + /* read it from the card */ + val = codec->codec_read(codec, 0x1a) & 0x7; + return ac97_rm2oss[val]; + } + + /* else, write the first set in the mask as the + output */ + + val = ffs(mask); + val = ac97_oss_rm[val-1]; + val |= val << 8; /* set both channels */ + +#ifdef DEBUG + printk("ac97_codec: setting ac97 recmask to 0x%x\n", val); +#endif + + codec->codec_write(codec, 0x1a, val); + + return 0; +}; + +static int ac97_mixer_ioctl(struct ac97_codec *codec, unsigned int cmd, unsigned long arg) +{ + int i, val = 0; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + info.modify_counter = codec->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, codec->name, sizeof(info.id)); + strncpy(info.name, codec->name, sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* give them the current record source */ + if (!codec->recmask_io) { + val = 0; + } else { + val = codec->recmask_io(codec, 1, 0); + } + break; + + case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ + val = codec->supported_mixers; + break; + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + val = codec->record_sources; + break; + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + val = codec->stereo_mixers; + break; + + case SOUND_MIXER_CAPS: + val = SOUND_CAP_EXCL_INPUT; + break; + + default: /* read a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + /* do we ever want to touch the hardware? */ + /* val = codec->read_mixer(card,i); */ + val = codec->mixer_state[i]; + break; + } + return put_user(val,(int *)arg); + } + + if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) { + codec->modcnt++; + get_user_ret(val, (int *)arg, -EFAULT); + + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + if (!codec->recmask_io) return -EINVAL; + if (!(val &= codec->record_sources)) return -EINVAL; + + codec->recmask_io(codec, 0, val); + + return 0; + default: /* write a specific mixer */ + i = _IOC_NR(cmd); + + if (!supported_mixer(codec, i)) + return -EINVAL; + + ac97_set_mixer(codec, i, val); + + return 0; + } + } + return -EINVAL; +} + +int ac97_probe_codec(struct ac97_codec *codec) +{ + u16 id1, id2, cap; + int i; + + /* probing AC97 codec, AC97 2.0 says that bit 15 of register 0x00 (reset) should + be read zero. Probing of AC97 in this way is not reliable, it is not even SAFE !! */ + codec->codec_write(codec, AC97_RESET, 0L); + if ((cap = codec->codec_read(codec, AC97_RESET)) & 0x8000) + return 0; + + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + for (i = 0; i < sizeof (snd_ac97_codec_ids); i++) { + if (snd_ac97_codec_ids[i].id == ((id1 << 16) | id2)) { + codec->name = snd_ac97_codec_ids[i].name; + codec->codec_init = snd_ac97_codec_ids[i].init; + break; + } + } + if (codec->name == NULL) + codec->name = "Unknown"; + printk(KERN_INFO "ac97_codec: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n", + id1, id2, codec->name); + printk(KERN_INFO "ac97_codec: capability: 0x%04x\n", cap); + + /* mixer masks */ + codec->supported_mixers = AC97_SUPPORTED_MASK; + codec->stereo_mixers = AC97_STEREO_MASK; + codec->record_sources = AC97_RECORD_MASK; + if (!(cap & 0x04)) + codec->supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + + /* generic OSS to AC97 wrapper */ + codec->read_mixer = ac97_read_mixer; + codec->write_mixer = ac97_write_mixer; + codec->recmask_io = ac97_recmask_io; + codec->mixer_ioctl = ac97_mixer_ioctl; + + /* initialize volume level */ + codec->codec_write(codec, AC97_MASTER_VOL_STEREO, 0L); + codec->codec_write(codec, AC97_PCMOUT_VOL, 0L); + + /* codec specific initialization for 4-6 channel output */ + if (codec->id != 0 && codec->codec_init != NULL) { + codec->codec_init(codec); + } + + /* initilize mixer channel volumes */ + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + struct mixer_defaults *md = &mixer_defaults[i]; + if (md->mixer == -1) + break; + if (!supported_mixer(codec, md->mixer)) + continue; + ac97_set_mixer(codec, md->mixer, md->value); + } + + return 1; +} + +static int sigmatel_init(struct ac97_codec * codec) +{ + codec->codec_write(codec, AC97_SURROUND_MASTER, 0L); + /* initialize SigmaTel STAC9721/23 */ + codec->codec_write(codec, 0x74, 0x01); + return 1; +} + +EXPORT_SYMBOL(ac97_probe_codec); diff --git a/drivers/sound/ac97_codec.h b/drivers/sound/ac97_codec.h new file mode 100644 index 000000000000..36d2b052a659 --- /dev/null +++ b/drivers/sound/ac97_codec.h @@ -0,0 +1,157 @@ +#ifndef _AC97_CODEC_H_ +#define _AC97_CODEC_H_ + +#include "sound_config.h" +#include "sound_calls.h" + +/* AC97 1.0 */ +#define AC97_RESET 0x0000 // +#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out +#define AC97_HEADPHONE_VOL 0x0004 // +#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output +#define AC97_MASTER_TONE 0x0008 // +#define AC97_PCBEEP_VOL 0x000a // none +#define AC97_PHONE_VOL 0x000c // TAD Input (mono) +#define AC97_MIC_VOL 0x000e // MIC Input (mono) +#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) +#define AC97_CD_VOL 0x0012 // CD Input (stereo) +#define AC97_VIDEO_VOL 0x0014 // none +#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) +#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) +#define AC97_RECORD_SELECT 0x001a // +#define AC97_RECORD_GAIN 0x001c +#define AC97_RECORD_GAIN_MIC 0x001e +#define AC97_GENERAL_PURPOSE 0x0020 +#define AC97_3D_CONTROL 0x0022 +#define AC97_MODEM_RATE 0x0024 +#define AC97_POWER_CONTROL 0x0026 + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved */ + +/* range 0x3c-0x58 - MODEM */ + +/* registers 0x005a - 0x007a are vendor reserved */ + +#define AC97_VENDOR_ID1 0x007c +#define AC97_VENDOR_ID2 0x007e + +/* volume control bit defines */ +#define AC97_MUTE 0x8000 +#define AC97_MICBOOST 0x0040 +#define AC97_LEFTVOL 0x3f00 +#define AC97_RIGHTVOL 0x003f + +/* record mux defines */ +#define AC97_RECMUX_MIC 0x0000 +#define AC97_RECMUX_CD 0x0101 +#define AC97_RECMUX_VIDEO 0x0202 /* not used */ +#define AC97_RECMUX_AUX 0x0303 +#define AC97_RECMUX_LINE 0x0404 +#define AC97_RECMUX_STEREO_MIX 0x0505 +#define AC97_RECMUX_MONO_MIX 0x0606 +#define AC97_RECMUX_PHONE 0x0707 + + +/* general purpose register bit defines */ +#define AC97_GP_LPBK 0x0080 /* Loopback mode */ +#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ +#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ +#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ +#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ +#define AC97_GP_LD 0x1000 /* Loudness 1=on */ +#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ +#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ +#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ + + +/* powerdown control and status bit defines */ + +/* status */ +#define AC97_PWR_MDM 0x0010 /* Modem section ready */ +#define AC97_PWR_REF 0x0008 /* Vref nominal */ +#define AC97_PWR_ANL 0x0004 /* Analog section ready */ +#define AC97_PWR_DAC 0x0002 /* DAC section ready */ +#define AC97_PWR_ADC 0x0001 /* ADC section ready */ + +/* control */ +#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ +#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ +#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ +#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ +#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ +#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ +#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ +#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ + +/* useful power states */ +#define AC97_PWR_D0 0x0000 /* everything on */ +#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 +#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ + +/* Total number of defined registers. */ +#define AC97_REG_CNT 64 + + +/* OSS interface to the ac97s.. */ +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\ + SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MIXER_ALTPCM|SOUND_MASK_IGAIN|\ + SOUND_MASK_LINE1|SOUND_MASK_VIDEO) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|\ + SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\ + SOUND_MIXER_PHONEIN|SOUND_MIXER_PHONEOUT) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +#define supported_mixer(CODEC,FOO) ( CODEC->supported_mixers & (1< - * adapt to 2.3.x new __setup/__initcall + * adapt to 2.3.x new __setup/__init call * v0.04 Dec 31 1999 Ollie Lho - * Multiple Open, useing Middle Loop Interrupt to smooth playback + * Multiple Open, using Middle Loop Interrupt to smooth playback * v0.03 Dec 24 1999 Ollie Lho * mem leak in prog_dmabuf and dealloc_dmabuf removed * v0.02 Dec 15 1999 Ollie Lho * SiS 7018 support added, playback O.K. * v0.01 Alan Cox et. al. * Initial Release in kernel 2.3.30, does not work + * + * ToDo + * Clean up of low level channel register access code. (done) + * Fix the bug on dma buffer management in update_ptr, read/write, drain_dac (done) + * Dual AC97 codecs support (done partially, need channel binding to test) + * Recording support + * Mmap support + * "Channel Binding" ioctl extension */ #include @@ -66,34 +86,40 @@ #endif #include "trident.h" -#include "ac97.h" +#include "ac97_codec.h" #undef DEBUG -#define DRIVER_VERSION "0.05" - -#define TRIDENT_FMT_STEREO 0x01 -#define TRIDENT_FMT_16BIT 0x02 -#define TRIDENT_FMT_MASK 0x03 -#define TRIDENT_DAC_SHIFT 0 -#define TRIDENT_ADC_SHIFT 4 - -#define TRIDENT_ENABLE_PE 1 -#define TRIDENT_ENABLE_RE 2 -#define DAC_RUNNING 1 -#define ADC_RUNNING 2 +#define DRIVER_VERSION "0.11" +/* magic numbers to protect our data structures */ #define TRIDENT_CARD_MAGIC 0x5072696E /* "Prin" */ #define TRIDENT_STATE_MAGIC 0x63657373 /* "cess" */ -/* number of instances of opening /dev/dsp, can your CPU handle this ? */ -#define NR_DSPS 32 +/* The first 32 channels are called Bank A. They are (should be) reserved + for MIDI synthesizer. But since that is not supported yet, we can (ab)use + them to play PCM samples */ +#undef ABUSE_BANK_A + +/* maxinum number of instances of opening /dev/dspN, can your CPU handle this ? + NOTE: If /dev/dsp is opened O_RDWR (i.e. full duplex) it will consume 2 HW + channels */ +#ifdef ABUSE_BANK_A +#define NR_HW_CH 64 +#else +#define NR_HW_CH 32 +#endif + +/* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only + have 2 SDATA_IN lines (currently) */ +#define NR_AC97 2 +/* minor number of /dev/dspW */ #define SND_DEV_DSP16 5 static const unsigned sample_size[] = { 1, 2, 2, 4 }; static const unsigned sample_shift[] = { 0, 1, 1, 2 }; -static const char *sample_format[] = {"8 bits Mono", "8 bits Stereo", "16 bits Mono", "16 bits Stereo"}; + static const char invalid_magic[] = KERN_CRIT "trident: invalid magic value in %s\n"; struct pci_audio_info { @@ -108,52 +134,12 @@ static struct pci_audio_info pci_audio_devices[] = { {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7018, "SiS 7018 PCI Audio"} }; -static struct { - unsigned int id; - char *name; -} snd_ac97_codec_ids[] = { - {0x414B4D00, "Asahi Kasei AK4540" }, - {0x41445340, "Analog Devices AD1881" }, - {0x43525900, "Cirrus Logic CS4297" }, - {0x43525913, "Cirrus Logic CS4297A" }, - {0x43525931, "Cirrus Logic CS4299" }, - {0x4e534331, "National Semiconductor LM4549"}, - {0x83847600, "SigmaTel STAC????" }, - {0x83847604, "SigmaTel STAC9701/3/4/5"}, - {0x83847605, "SigmaTel STAC9704" }, - {0x83847608, "SigmaTel STAC9708" }, - {0x83847609, "SigmaTel STAC9721/23" }, - {0x00000000, NULL} -}; - -typedef struct tChannelControl -{ - // register data - unsigned int * lpChStart; - unsigned int * lpChStop; - unsigned int * lpChAint; - unsigned int * lpChAinten; - - // register addresses - unsigned int * lpAChStart; - unsigned int * lpAChStop; - unsigned int * lpAChAint; - unsigned int * lpAChAinten; - - unsigned int data[16]; - -} CHANNELCONTROL; - /* "software" or virtual channel, an instance of opened /dev/dsp */ struct trident_state { unsigned int magic; struct trident_card *card; /* Card info */ - /* wave stuff */ - unsigned int rateadc, ratedac; - unsigned char fmt, enable; - - /* single opne lock mechanism, should be removed */ + /* single open lock mechanism, only used for recording */ struct semaphore open_sem; wait_queue_head_t open_wait; @@ -164,26 +150,33 @@ struct trident_state { int virt; struct dmabuf { + /* wave sample stuff */ + unsigned int rate; + unsigned char fmt, enable; + + /* hardware channel */ + struct trident_channel *channel; + + /* OSS buffer manangemeent stuff */ void *rawbuf; unsigned buforder; unsigned numfrag; unsigned fragshift; - /* hardware channel number */ - int chan; + /* our buffer acts like a circular ring */ + unsigned hwptr; /* where dma last started, update by update_ptr */ + unsigned swptr; /* where driver last clear/filled, updated by read/write */ + int count; /* bytes to be comsumed by dma machine */ + unsigned total_bytes; /* total bytes dmaed by hardware */ - /* XXX zab - swptr only in here so that it can be referenced by - clear_advance, as far as I can tell :( */ - unsigned hwptr, swptr; - unsigned total_bytes; - int count; - unsigned error; /* over/underrun */ - wait_queue_head_t wait; + 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; @@ -192,44 +185,45 @@ struct trident_state { int ossmaxfrags; unsigned subdivision; } dma_dac, dma_adc; - - u8 bDMAStart; - }; /* hardware channels */ struct trident_channel { - int chan; /* channel number */ - u32 lba; - u32 eso; + int num; /* channel number */ + u32 lba; /* reg 0xe4 */ + u32 eso; /* reg 0xe8 */ u32 delta; - u16 attribute; - + u16 attribute; /* reg 0xec */ + u16 fm_vol; + u32 control; /* reg 0xf0 */ }; -struct trident_pcm_bank { - /* registers to control bank operations */ +struct trident_pcm_bank_address { u32 start; u32 stop; u32 aint; u32 aint_en; +}; +static struct trident_pcm_bank_address bank_a_addrs = +{ + T4D_START_A, + T4D_STOP_A, + T4D_AINT_A, + T4D_AINTEN_A +}; +static struct trident_pcm_bank_address bank_b_addrs = +{ + T4D_START_B, + T4D_STOP_B, + T4D_AINT_B, + T4D_AINTEN_B +}; +struct trident_pcm_bank { + /* register addresses to control bank operations */ + struct trident_pcm_bank_address *addresses; /* each bank has 32 channels */ u32 bitmap; /* channel allocation bitmap */ - //struct trident_channel channels[32]; -}; - -struct trident_mixer { - int modcnt; - int supported_mixers; - int stereo_mixers; - int record_sources; - - /* the caller must guarantee arg sanity before calling these */ - /* int (*read_mixer)(struct trident_card *card, int index);*/ - void (*write_mixer)(struct trident_card *card,int mixer, unsigned int left, - unsigned int right); - int (*recmask_io)(struct trident_card *card,int rw,int mask); - unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + struct trident_channel channels[32]; }; struct trident_card { @@ -239,7 +233,7 @@ struct trident_card { struct trident_card *next; /* The trident has a certain amount of cross channel interaction - so we use a single per card lock */ + so we use a single per card lock */ spinlock_t lock; /* PCI device stuff */ @@ -249,286 +243,225 @@ struct trident_card { /* soundcore stuff */ int dev_audio; - int dev_mixer; - struct trident_mixer mix; - struct trident_state *channels[NR_DSPS]; + /* structures for abstraction of hardware facilities, codecs, banks and channels*/ + struct ac97_codec *ac97_codec[NR_AC97]; + struct trident_pcm_bank banks[NR_BANKS]; + struct trident_state *states[NR_HW_CH]; /* hardware resources */ unsigned long iobase; u32 irq; - - /* hardware channel allocation bitmap */ - u32 bitmap[2]; - - /* ugly stupid thing, remove ASAP */ - CHANNELCONTROL ChRegs; - int ChanDwordCount; }; static struct trident_card *devs = NULL; -/* - * Trident support library routines - */ - -/*--------------------------------------------------------------------------- - void ResetAinten( struct trident_state *trident, int ChannelNum) - - Description: This routine will disable interrupts and ack any - existing interrupts for specified channel. - - Parameters: trident - pointer to target device class for 4DWave. - ChannelNum - channel number - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static void ResetAinten(struct trident_card * trident, int ChannelNum) -{ - unsigned int dwMask; - unsigned int x = ChannelNum >> 5; - unsigned int ChanDwordCount = trident->ChanDwordCount; - - IReadAinten(&trident->ChRegs); - dwMask = 1 << (ChannelNum & 0x1f); - trident->ChRegs.lpChAinten[x] &= ~dwMask; - IWriteAinten(&trident->ChRegs); - // Ack the channel in case the interrupt was set before we disable it. - outl(dwMask, TRID_REG(trident, trident->ChRegs.lpAChAint[x])); -} +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val); +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg); + +static int trident_open_mixdev(struct inode *inode, struct file *file); +static int trident_release_mixdev(struct inode *inode, struct file *file); +static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg); +static loff_t trident_llseek(struct file *file, loff_t offset, int origin); -/*--------------------------------------------------------------------------- - void EnableEndInterrupts( struct trident_card *trident) - - Description: This routine will enable end of loop interrupts. - End of loop interrupts will occur when a running - channel reaches ESO. - - Parameters: trident - pointer to target device class for 4DWave. - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static int trident_enable_end_interrupts(struct trident_card * trident) +static int trident_enable_loop_interrupts(struct trident_card * card) { u32 global_control; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); - switch (trident->pci_id) + switch (card->pci_id) { case PCI_DEVICE_ID_SI_7018: - global_control |= (ENDLP_IE | BANK_B_EN); + global_control |= (ENDLP_IE | MIDLP_IE| BANK_B_EN); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - global_control |= ENDLP_IE; + global_control |= (ENDLP_IE | MIDLP_IE); break; default: return FALSE; } - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); #ifdef DEBUG - printk("trident: Enable End Interrupts, globctl = 0x%08X\n", global_control); + printk("trident: Enable Loop Interrupts, globctl = 0x%08X\n", + global_control); #endif return (TRUE); } -static int trident_enable_middle_interrupts(struct trident_card * trident) +static int trident_disable_loop_interrupts(struct trident_card * card) { u32 global_control; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - global_control |= (MIDLP_IE | BANK_B_EN); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - default: - global_control |= MIDLP_IE; - break; - } - - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + global_control = inl(TRID_REG(card, T4D_LFO_GC_CIR)); + global_control &= ~(ENDLP_IE | MIDLP_IE); + outl(global_control, TRID_REG(card, T4D_LFO_GC_CIR)); #ifdef DEBUG - printk("trident: Enable Middle Interrupts, globctl = 0x%08X\n", global_control); + printk("trident: Disabled Loop Interrupts, globctl = 0x%08X\n", + global_control); #endif return (TRUE); } -/*--------------------------------------------------------------------------- - void DisableEndInterrupts( struct trident_card *trident) - - Description: This routine will disable end of loop interrupts. - End of loop interrupts will occur when a running - channel reaches ESO. - - Parameters: - trident - pointer to target device class for 4DWave. - - returns: TRUE if everything went ok, else FALSE. - - ---------------------------------------------------------------------------*/ - -static int trident_disable_end_interrupts(struct trident_card * trident) + +static void trident_enable_voice_irq(struct trident_card * card, unsigned int channel) { - u32 global_control; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - global_control &= ~ENDLP_IE; - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + reg = inl(TRID_REG(card, addr)); + reg |= mask; + outl(reg, TRID_REG(card, addr)); #ifdef DEBUG - printk("trident: Disabled End Interrupts, globctl = 0x%08X\n", global_control); + reg = inl(TRID_REG(card, T4D_AINTEN_B)); + printk("trident: enabled IRQ on channel %d, AINTEN_B = 0x%08x\n", + channel, reg); #endif - return (TRUE); } -static int trident_disable_middle_interrupts(struct trident_card * trident) +static void trident_disable_voice_irq(struct trident_card * card, unsigned int channel) { - u32 global_control; - - global_control = inl(TRID_REG(trident, T4D_LFO_GC_CIR)); - global_control &= ~MIDLP_IE; - outl(global_control, TRID_REG(trident, T4D_LFO_GC_CIR)); + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint_en; + + reg = inl(TRID_REG(card, addr)); + reg &= ~mask; + outl(reg, TRID_REG(card, addr)); + + /* Ack the channel in case the interrupt was set before we disable it. */ + outl(mask, TRID_REG(card, bank->addresses->aint)); #ifdef DEBUG - printk("trident: Disabled Middle Interrupts, globctl = 0x%08X\n", global_control); + reg = inl(TRID_REG(card, T4D_AINTEN_B)); + printk("trident: disabled IRQ on channel %d, AINTEN_B = 0x%08x\n", + channel, reg); #endif - return (TRUE); } -/*--------------------------------------------------------------------------- - void trident_enable_voice_irq( unsigned int HwChannel ) - - Description: Enable an interrupt channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_enable_voice_irq(struct trident_card * trident, unsigned int channel) + +static void trident_start_voice(struct trident_card * card, unsigned int channel) { - unsigned int bank, mask, ChanDwordCount; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->start; + +#ifdef DEBUG u32 reg; +#endif - bank = channel >> 5; - mask = 1 << (channel & 0x1f); + outl(mask, TRID_REG(card, addr)); + +#ifdef DEBUG + reg = inl(TRID_REG(card, T4D_START_B)); + printk("trident: start voice on channel %d, START_B = 0x%08x\n", + channel, reg); +#endif +} + +static void trident_stop_voice(struct trident_card * card, unsigned int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 addr = bank->addresses->stop; - ChanDwordCount = trident->ChanDwordCount; +#ifdef DEBUG + u32 reg; +#endif - IReadAinten(&trident->ChRegs); - trident->ChRegs.lpChAinten[bank] |= mask; - IWriteAinten(&trident->ChRegs); + outl(mask, TRID_REG(card, addr)); #ifdef DEBUG - reg = inl(TRID_REG(trident, T4D_AINTEN_B)); - printk("trident: enabled IRQ on channel %d\n", channel); + reg = inl(TRID_REG(card, T4D_STOP_B)); + printk("trident: stop voice on channel %d, STOP_B = 0x%08x\n", + channel, reg); #endif } -/*--------------------------------------------------------------------------- - void trident_disable_voice_irq( unsigned int HwChannel ) - - Description: Disable an interrupt channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_disable_voice_irq(struct trident_card * trident, unsigned int channel) +static int trident_check_channel_interrupt(struct trident_card * card, int channel) { - unsigned int bank, mask, ChanDwordCount; - u32 reg; + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; - bank = channel >> 5; - mask = 1 << (channel & 0x1f); + reg = inl(TRID_REG(card, addr)); + +#ifdef DEBUG + if (reg & mask) + printk("trident: channel %d has interrupt, AINT_B = 0x%08x\n", + channel, reg); +#endif + return (reg & mask) ? TRUE : FALSE; +} + +static void trident_ack_channel_interrupt(struct trident_card * card, int channel) +{ + unsigned int mask = 1 << (channel & 0x1f); + struct trident_pcm_bank *bank = &card->banks[channel >> 5]; + u32 reg, addr = bank->addresses->aint; - ChanDwordCount = trident->ChanDwordCount; - IReadAinten(&trident->ChRegs); - trident->ChRegs.lpChAinten[bank] &= ~mask; - IWriteAinten(&trident->ChRegs); + reg = inl(TRID_REG(card, addr)); + reg &= mask; + outl(reg, TRID_REG(card, addr)); #ifdef DEBUG - reg = inl(TRID_REG(trident, T4D_AINTEN_B)); - printk("trident: disabled IRQ on channel %d\n", channel); + reg = inl(TRID_REG(card, T4D_AINT_B)); + printk("trident: Ack channel %d interrupt, AINT_B = 0x%08x\n", + channel, reg); #endif } -/*--------------------------------------------------------------------------- - unsigned int AllocateChannelPCM( void ) - - Description: Allocate hardware channel by reverse order (63-0). - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: hardware channel - 0-63 or -1 when no channel is available - - ---------------------------------------------------------------------------*/ - -static int trident_alloc_pcm_channel(struct trident_card *trident) +static struct trident_channel * trident_alloc_pcm_channel(struct trident_card *card) { + struct trident_pcm_bank *bank; int idx; - if (trident->bitmap[BANK_B] == ~0UL) { + bank = &card->banks[BANK_B]; + if (bank->bitmap == ~0UL) { /* no more free channels avaliable */ printk(KERN_ERR "trident: no more channels available on Bank B.\n"); - return -1; +#ifdef ABUSE_BANK_A + goto bank_a; +#endif + return NULL; } for (idx = 31; idx >= 0; idx--) { - if (!(trident->bitmap[BANK_B] & (1 << idx))) { - trident->bitmap[BANK_B] |= 1 << idx; - return idx + 32; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + bank->bitmap |= 1 << idx; + channel->num = idx + 32; + return channel; } } #ifdef ABUSE_BANK_A /* channels in Bank A should be reserved for synthesizer not for normal use (channels in Bank A can't record) */ - if (trident->bitmap[BANK_A] == ~0UL) { + bank_a: + bank = &card->banks[BANK_A]; + if (bank->bitmap == ~0UL) { /* no more free channels avaliable */ - printk(KERN_ERR "trident: no channels available on Bank A.\n"); - return -1; + printk(KERN_ERR "trident: no more channels available on Bank A.\n"); + return NULL; } for (idx = 31; idx >= 0; idx--) { - if (!(trident->bitmap[BANK_A] & (1 << idx))) { - trident->bitmap[BANK_A] |= 1 << idx; - return idx; + if (!(bank->bitmap & (1 << idx))) { + struct trident_channel *channel = &bank->channels[idx]; + banks->bitmap |= 1 << idx; + channel->num = idx; + return channels; } } #endif - - return -1; + return NULL; } -/*--------------------------------------------------------------------------- - void FreeChannelPCM( int channel ) - - Description: Free hardware channel. - - Parameters : trident - pointer to target device class for 4DWave. - channel - hardware channel number 0-63 - - Return Value: none - - ---------------------------------------------------------------------------*/ - -static void trident_free_pcm_channel(struct trident_card *trident, int channel) +static void trident_free_pcm_channel(struct trident_card *card, int channel) { int bank; @@ -543,236 +476,74 @@ static void trident_free_pcm_channel(struct trident_card *trident, int channel) bank = channel >> 5; channel = channel & 0x1f; - if (trident->bitmap[bank] & (1 << (channel))) { - trident->bitmap[bank] &= ~(1 << (channel)); + if (card->banks[bank].bitmap & (1 << (channel))) { + card->banks[bank].bitmap &= ~(1 << (channel)); } } -/*--------------------------------------------------------------------------- - void trident_start_voice( ULONG HwChannel ) - - Description: Start a channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_start_voice(struct trident_card * trident, unsigned int channel) +/* called with spin lock held */ +static int trident_load_channel_registers(struct trident_card *card, u32 *data, unsigned int channel) { - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStart[bank])); -#ifdef DEBUG - printk("trident: start voice on channel %d\n", channel); -#endif -} - -/*--------------------------------------------------------------------------- - void trident_stop_voice( ULONG HwChannel ) - - Description: Stop a channel, any channel 0 thru n. - This routine automatically handles the fact that there are - more than 32 channels available. - - Parameters : HwChannel - Channel number 0 thru n. - trident - pointer to target device class for 4DWave. - - Return Value: None. - - ---------------------------------------------------------------------------*/ -void trident_stop_voice(struct trident_card * trident, unsigned int channel) -{ - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - outl(mask, TRID_REG(trident, trident->ChRegs.lpAChStop[bank])); -#ifdef DEBUG - printk("trident: stop voice on channel %d\n", channel); -#endif -} - -/*--------------------------------------------------------------------------- - int DidChannelInterrupt( ) - - Description: Check if interrupt channel occurred. - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: TRUE if interrupt occurred, else FALSE. - - ---------------------------------------------------------------------------*/ -static int trident_check_channel_interrupt(struct trident_card * trident, int channel) -{ - unsigned int ChanDwordCount = NUM_BANKS; - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - ReadAint(&trident->ChRegs); - -#ifdef DEBUG - if (trident->ChRegs.lpChAint[bank] & mask) - printk("trident: channel %d has interrupt\n", channel); -#endif - return (trident->ChRegs.lpChAint[bank] & mask) ? TRUE : FALSE; -} - -/*--------------------------------------------------------------------------- - void AckChannelInterrupt( ) - - Description: Acknowledge the interrupt bit for channel intrs. - - Parameters : trident - pointer to target device class for 4DWave. - - Return Value: None - - ---------------------------------------------------------------------------*/ -static void trident_ack_channel_interrupt(struct trident_card * trident, int channel) -{ - unsigned int ChanDwordCount = NUM_BANKS; - unsigned int bank = channel >> 5; - unsigned int mask = 1 << (channel & 0x1f); - - ReadAint(&trident->ChRegs); - trident->ChRegs.lpChAint[bank] &= mask; - IWriteAint(&trident->ChRegs); -} - -/*--------------------------------------------------------------------------- - int trident_load_hw_delta( unsigned int HwChannel, unsigned int Delta ) - - Description: This routine writes Delta to the hardware. - - Parameters: Delta - data to write (2 Bytes only) - HwChannel - Hardware channel to write to. - trident - pointer to target device class for 4DWave. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -static int trident_load_hw_delta (struct trident_card * trident, unsigned int channel, - unsigned short delta) -{ - /* select a channel for output */ - outb(channel, TRID_REG(trident, T4D_LFO_GC_CIR)); + int i; - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - outw((u16) delta, TRID_REG(trident, CH_DX_ESO_DELTA)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - outb(delta & 0xff, TRID_REG(trident, CH_NX_DELTA_CSO + 3)); - outb((delta >> 8)& 0xff, TRID_REG(trident, CH_NX_DELTA_ESO + 3)); - break; - default: + if (channel > 63) return FALSE; - } - return TRUE; -} - -/*--------------------------------------------------------------------------- - int LoadVirtualChannel( ULONG *Data, ULONG HwChannel) - - Description: This routine writes all required channel registers to hardware. - - Parameters: *Data - a pointer to the data to write (5 ULONGS always). - HwChannel - Hardware channel to write to. - trident - pointer to target device class for 4DWave. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -static int LoadVirtualChannel(struct trident_card * trident, unsigned int *Data, unsigned int HwChannel) -{ - unsigned int ChanData[CHANNEL_REGS]; - unsigned int ULONGSToDo = CHANNEL_REGS; - unsigned int i; - unsigned int Address = CHANNEL_START; - /* Copy the data first... Hack... Before mucking with Volume! */ - memcpy((unsigned char *) ChanData, (unsigned char *) Data, ULONGSToDo * 4); - - outb((unsigned char) HwChannel, TRID_REG(trident, T4D_LFO_GC_CIR)); + /* select hardware channel to write */ + outb(channel, TRID_REG(card, T4D_LFO_GC_CIR)); + /* output the channel registers */ + for (i = 0; i < CHANNEL_REGS; i++) { + outl(data[i], TRID_REG(card, CHANNEL_START + 4*i)); + } - for (i = 0; i < ULONGSToDo; i++, Address += 4) - outl(ChanData[i], TRID_REG(trident, Address)); -#ifdef DEBUG - printk("(trident) load virtual channel %d\n", HwChannel); -#endif return TRUE; } -/*--------------------------------------------------------------------------- - trident_write_voice_regs - - Description: This routine will write the 5 hardware channel registers - to hardware. - - Paramters: trident - pointer to target device class for 4DWave. - Channel - Real or Virtual channel number. - Each register field. - - Returns: TRUE if all goes well, else FALSE. - - ---------------------------------------------------------------------------*/ -int trident_write_voice_regs(struct trident_card * trident, - unsigned int Channel, - unsigned int LBA, - unsigned int CSO, - unsigned int ESO, - unsigned int DELTA, - unsigned int ALPHA_FMS, - unsigned int FMC_RVOL_CVOL, - unsigned int GVSEL, - unsigned int PAN, - unsigned int VOL, - unsigned int CTRL, - unsigned int EC) +/* called with spin lock held */ +static int trident_write_voice_regs(struct trident_state *state, unsigned int rec) { - unsigned int ChanData[CHANNEL_REGS + 1], FmcRvolCvol; + unsigned int data[CHANNEL_REGS + 1]; + struct trident_channel *channel; - ChanData[1] = LBA; - ChanData[4] = (GVSEL << 31) | - ((PAN & 0x0000007f) << 24) | - ((VOL & 0x000000ff) << 16) | - ((CTRL & 0x0000000f) << 12) | - (EC & 0x00000fff); + if (rec) + channel = state->dma_adc.channel; + else + channel = state->dma_dac.channel; - FmcRvolCvol = FMC_RVOL_CVOL & 0x0000ffff; + data[1] = channel->lba; + data[4] = channel->control; - switch (trident->pci_id) + switch (state->card->pci_id) { case PCI_DEVICE_ID_SI_7018: + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = (channel->attribute << 16) | (channel->fm_vol & 0xffff); + break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - ChanData[0] = (CSO << 16) | (ALPHA_FMS & 0x0000ffff); - ChanData[2] = (ESO << 16) | (DELTA & 0x0000ffff); - ChanData[3] = FmcRvolCvol; + data[0] = 0; /* Current Sample Offset */ + data[2] = (channel->eso << 16) | (channel->delta & 0xffff); + data[3] = channel->fm_vol & 0xffff; break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - ChanData[0] = (DELTA << 24) | (CSO & 0x00ffffff); - ChanData[2] = ((DELTA << 16) & 0xff000000) | (ESO & 0x00ffffff); - ChanData[3] = (ALPHA_FMS << 16) | FmcRvolCvol; + data[0] = (channel->delta << 24); + data[2] = ((channel->delta << 24) & 0xff000000) | (channel->eso & 0x00ffffff); + data[3] = channel->fm_vol & 0xffff; break; + default: + return FALSE; } - LoadVirtualChannel(trident, ChanData, Channel); - - return TRUE; + return trident_load_channel_registers(state->card, data, channel->num); } static int compute_rate(u32 rate) { int delta; - // We special case 44100 and 8000 since rounding with the equation - // does not give us an accurate enough value. For 11025 and 22050 - // the equation gives us the best answer. All other frequencies will - // also use the equation. JDW + /* We special case 44100 and 8000 since rounding with the equation + does not give us an accurate enough value. For 11025 and 22050 + the equation gives us the best answer. All other frequencies will + also use the equation. JDW */ if (rate == 44100) delta = 0xeb3; else if (rate == 8000) @@ -784,838 +555,267 @@ static int compute_rate(u32 rate) return delta; } -/*--------------------------------------------------------------------------- - trident_set_dac_rate - - Description: This routine will set the sample rate for playback. - - Paramters: trident - pointer to target device class for 4DWave. - rate - desired sample rate - set - actually write hardware if set is true. - - Returns: The rate allowed by device. - - ---------------------------------------------------------------------------*/ - -static unsigned int trident_set_dac_rate(struct trident_state * trident, - unsigned int rate, int set) -{ - u16 delta; +/* set playback sample rate */ +static unsigned int trident_set_dac_rate(struct trident_state * state, unsigned int rate) +{ + struct dmabuf *dmabuf = &state->dma_dac; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - delta = compute_rate(rate); - trident->ratedac = rate; + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate(rate); - if (set) - trident_load_hw_delta(trident->card, trident->dma_dac.chan, - delta); -#ifdef DEBUG - printk("trident: called trident_set_dac_rate : rate = %d, " - "set = %d, delta = 0x%04x\n", rate, set, delta); + trident_write_voice_regs(state, 0); + +#ifdef DEBUG + printk("trident: called trident_set_dac_rate : rate = %d\n", rate); #endif return rate; } -/*--------------------------------------------------------------------------- - trident_set_adc_rate - - Description: This routine will set the sample rate for capture. - - Paramters: trident - pointer to target device class for 4DWave. - rate - desired sample rate - set - actually write hardware if set is true. - - Returns: The rate allowed by device. - - ---------------------------------------------------------------------------*/ - -static unsigned int trident_set_adc_rate(struct trident_state * trident, - unsigned int rate, int set) +/* set recording sample rate */ +static unsigned int trident_set_adc_rate(struct trident_state * state, unsigned int rate) { - u16 delta; - + struct dmabuf *dmabuf = &state->dma_adc; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - delta = compute_rate(rate); - trident->ratedac = rate; - -#if 0 /* It seems that 4D-Wave can not use wave tables channels for recording */ - if (set) - trident_load_hw_delta(trident->card, trident->dma_adc.chan, - delta); -#endif -#ifdef DEBUG - printk("trident: called trident_set_adc_rate : rate = %d, " - "set = %d, delta = 0x%04x\n", rate, set, delta); -#endif - return rate; -} - -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; -} - -/* Write AC97 mixer registers */ -static void trident_ac97_set(struct trident_card *trident, u8 cmd, u16 val) -{ - unsigned int address, mask, busy; - unsigned short count = 0xffff; - u32 data; - - data = ((u32) val) << 16; - - switch (trident->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_WRITE; - mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; - busy = SI_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR0_AC97_W; - mask = busy = DX_AC97_BUSY_WRITE; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - address = NX_ACR1_AC97_W; - mask = busy = NX_AC97_BUSY_WRITE; - break; - } + dmabuf->rate = rate; + dmabuf->channel->delta = compute_rate(rate); - do { - if ((inw(TRID_REG(trident, address)) & busy) == 0) - break; - } while (count--); - - data |= (mask | (cmd & AC97_REG_ADDR)); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); - return; - } - outl(data, TRID_REG(trident, address)); -} - -/* Read AC97 codec registers */ -static u16 trident_ac97_get(struct trident_card *trident, u8 cmd) -{ - unsigned int address, mask, busy; - unsigned short count = 0xffff; - u32 data; - - switch (trident->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - address = SI_AC97_READ; - mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; - busy = SI_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - address = DX_ACR1_AC97_R; - mask = busy = DX_AC97_BUSY_READ; - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - address = NX_ACR2_AC97_R_PRIMARY; - mask = NX_AC97_BUSY_READ; - busy = 0x0c00; - break; - } - - data = (mask | (cmd & AC97_REG_ADDR)); - outl(data, TRID_REG(trident, address)); - - do { - data = inl(TRID_REG(trident, address)); - if ((data & busy) == 0) - break; - } while (count--); - - if (count == 0) { - printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); - data = 0; - } - return ((u16) (data >> 16)); -} - -/* OSS interface to the ac97s.. */ - -#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|\ - SOUND_MASK_PCM|SOUND_MASK_LINE|SOUND_MASK_CD|\ - SOUND_MASK_VIDEO|SOUND_MASK_LINE1|SOUND_MASK_IGAIN) - -#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ - SOUND_MASK_BASS|SOUND_MASK_TREBLE|SOUND_MASK_MIC|\ - SOUND_MASK_SPEAKER) - -#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ - SOUND_MASK_CD| SOUND_MASK_VIDEO| SOUND_MASK_LINE1| SOUND_MASK_LINE|\ - SOUND_MASK_PHONEIN) - -#define supported_mixer(CARD,FOO) ( CARD->mix.supported_mixers & (1< 100 in bytes */ - {SOUND_MIXER_VOLUME, 0x3232}, - {SOUND_MIXER_BASS, 0x3232}, - {SOUND_MIXER_TREBLE, 0x3232}, - {SOUND_MIXER_SPEAKER, 0x3232}, - {SOUND_MIXER_MIC, 0x3232}, - {SOUND_MIXER_LINE, 0x3232}, - {SOUND_MIXER_CD, 0x3232}, - {SOUND_MIXER_VIDEO, 0x3232}, - {SOUND_MIXER_LINE1, 0x3232}, - {SOUND_MIXER_PCM, 0x3232}, - {SOUND_MIXER_IGAIN, 0x3232}, - {-1,0} -}; - -static struct ac97_mixer_hw { - unsigned char offset; - int scale; -} ac97_hw[SOUND_MIXER_NRDEVICES]= { - [SOUND_MIXER_VOLUME] = {AC97_MASTER_VOL_STEREO,63}, - [SOUND_MIXER_BASS] = {AC97_MASTER_TONE, 15}, - [SOUND_MIXER_TREBLE] = {AC97_MASTER_TONE, 15}, - [SOUND_MIXER_SPEAKER] = {AC97_PCBEEP_VOL, 15}, - [SOUND_MIXER_MIC] = {AC97_MIC_VOL, 31}, - [SOUND_MIXER_LINE] = {AC97_LINEIN_VOL, 31}, - [SOUND_MIXER_CD] = {AC97_CD_VOL, 31}, - [SOUND_MIXER_VIDEO] = {AC97_VIDEO_VOL, 31}, - [SOUND_MIXER_LINE1] = {AC97_AUX_VOL, 31}, - [SOUND_MIXER_PCM] = {AC97_PCMOUT_VOL, 31}, - [SOUND_MIXER_IGAIN] = {AC97_RECORD_GAIN, 31} -}; - -#if 0 /* *shrug* removed simply because we never used it. - feel free to implement again if needed */ - -/* reads the given OSS mixer from the ac97 - the caller must have insured that the ac97 knows - about that given mixer, and should be holding a - spinlock for the card */ -static int ac97_read_mixer(struct trident_card *card, int mixer) -{ - u16 val; - int ret = 0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; - - val = trident_ac97_get(card , mh->offset); - - if (AC97_STEREO_MASK & (1<> 8) & 0x7f; - right = val & 0x7f; - - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * 100) / mh->scale; - left = (left * 100) / mh->scale; - else { - right = 100 - ((right * 100) / mh->scale); - left = 100 - ((left * 100) / mh->scale); - } - - ret = left | (right << 8); - } else if (mixer == SOUND_MIXER_SPEAKER) { - ret = 100 - ((((val & 0x1e)>>1) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_MIC) { - ret = 100 - (((val & 0x1f) * 100) / mh->scale); - /* the low bit is optional in the tone sliders and masking - it lets us avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - ret = 100 - ((((val >> 8) & 0xe) * 100) / mh->scale); - } else if (mixer == SOUND_MIXER_TREBLE) { - ret = 100 - (((val & 0xe) * 100) / mh->scale); - } + trident_write_voice_regs(state, 1); #ifdef DEBUG - printk("trident: read OSS mixer %2d (ac97 register 0x%02x), " - "0x%04x -> 0x%04x\n", mixer, mh->offset, val, ret); + printk("trident: called trident_set_adc_rate : rate = %d\n", rate); #endif - - return ret; + return rate; } -#endif -/* write the OSS encoded volume to the given OSS encoded mixer, - again caller's job to make sure all is well in arg land, - call with spinlock held */ -static void ac97_write_mixer(struct trident_card *card, int mixer, - unsigned int left, unsigned int right) +/* prepare channel attributes for playback */ +static void trident_play_setup(struct trident_state *state) { - u16 val = 0; - struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + struct dmabuf *dmabuf = &state->dma_dac; + struct trident_channel *channel = dmabuf->channel; -#ifdef DEBUG - printk("trident: wrote OSS mixer %2d (ac97 register 0x%02x), " - "left vol:%2d, right vol:%2d:", - mixer, mh->offset, left, right); -#endif + channel->lba = virt_to_bus(dmabuf->rawbuf); + channel->delta = compute_rate(dmabuf->rate); - if (AC97_STEREO_MASK & (1 << mixer)) { - /* stereo mixers */ - if (mixer == SOUND_MIXER_IGAIN) { - right = (right * mh->scale) / 100; - left = (left * mh->scale) / 100; - } else { - right = ((100 - right) * mh->scale) / 100; - left = ((100 - left) * mh->scale) / 100; - } + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; - val = (left << 8) | right; - } else if (mixer == SOUND_MIXER_SPEAKER) { - val = (((100 - left) * mh->scale) / 100) << 1; - } else if (mixer == SOUND_MIXER_MIC) { - val = trident_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 us avoid the 0xf 'bypass'.. */ - } else if (mixer == SOUND_MIXER_BASS) { - val = trident_ac97_get(card , mh->offset) & ~0x0f00; - val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; - } else if (mixer == SOUND_MIXER_TREBLE) { - val = trident_ac97_get(card , mh->offset) & ~0x000f; - val |= (((100 - left) * mh->scale) / 100) & 0x000e; + if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* FIXME: channel attributes are configured by ioctls, but it is not implemented + so just set to ZERO for the moment */ + channel->attribute = 0; + } else { + channel->attribute = 0; } -#ifdef DEBUG - printk(" 0x%04x", val); -#endif - trident_ac97_set(card, mh->offset, val); - -#ifdef DEBUG - val = trident_ac97_get(card, mh->offset); - printk(" -> 0x%04x\n", val); -#endif -} - -/* the following tables allow us to go from - OSS <-> ac97 quickly. */ - -enum ac97_recsettings { - AC97_REC_MIC=0, - AC97_REC_CD, - AC97_REC_VIDEO, - AC97_REC_AUX, - AC97_REC_LINE, - AC97_REC_STEREO, /* combination of all enabled outputs.. */ - AC97_REC_MONO, /*.. or the mono equivalent */ - AC97_REC_PHONE -}; - -static unsigned int ac97_rm2oss[] = { - [AC97_REC_MIC] = SOUND_MIXER_MIC, - [AC97_REC_CD] = SOUND_MIXER_CD, - [AC97_REC_VIDEO] = SOUND_MIXER_VIDEO, - [AC97_REC_AUX] = SOUND_MIXER_LINE1, - [AC97_REC_LINE] = SOUND_MIXER_LINE, - [AC97_REC_PHONE] = SOUND_MIXER_PHONEIN -}; - -/* indexed by bit position */ -static unsigned int ac97_oss_rm[] = { - [SOUND_MIXER_MIC] = AC97_REC_MIC, - [SOUND_MIXER_CD] = AC97_REC_CD, - [SOUND_MIXER_VIDEO] = AC97_REC_VIDEO, - [SOUND_MIXER_LINE1] = AC97_REC_AUX, - [SOUND_MIXER_LINE] = AC97_REC_LINE, - [SOUND_MIXER_PHONEIN] = AC97_REC_PHONE -}; + channel->fm_vol = 0x0; -/* read or write the recmask - the ac97 can really have left and right recording - inputs independantly set, but OSS doesn't seem to - want us to express that to the user. - the caller guarantees that we have a supported bit set, - and they must be holding the card's spinlock */ -static int ac97_recmask_io(struct trident_card *card, int rw, int mask) -{ - unsigned int val; - - if (rw) { - /* read it from the card */ - val = trident_ac97_get(card, 0x1a) & 0x7; - return ac97_rm2oss[val]; - } - - /* else, write the first set in the mask as the - output */ - - val = ffs(mask); - val = ac97_oss_rm[val-1]; - val |= val << 8; /* set both channels */ -#ifdef DEBUG - printk("trident: setting ac97 recmask to 0x%x\n", val); -#endif - trident_ac97_set(card, 0x1a, val); - - return 0; -}; - -/* AC97 codec initialisation. */ -static u16 trident_ac97_init(struct trident_card *trident) -{ - u16 id1, id2; - char *ac97_name = NULL; - int i; - - /* initialize controller side of AC link */ - switch (trident->pci_id) - { - case PCI_DEVICE_ID_SI_7018: - /* disable AC97 GPIO interrupt */ - outl(0x00, TRID_REG(trident, SI_AC97_GPIO)); - /* stop AC97 cold reset process */ - outl(0x00014000, TRID_REG(trident, SI_SERIAL_INTF_CTRL)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - /* playback on */ - outl(0x02, TRID_REG(trident, DX_ACR2_AC97_COM_STAT)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ - outl(0x02, TRID_REG(trident, NX_ACR0_AC97_COM_STAT)); - break; - } - - /* get some information about our AC97 codec */ - id1 = trident_ac97_get(trident, AC97_VENDOR_ID1); - id2 = trident_ac97_get(trident, AC97_VENDOR_ID2); - for (i = 0; i < sizeof (snd_ac97_codec_ids); i++) { - if (snd_ac97_codec_ids[i].id == ((id1 << 16) | id2)) { - ac97_name = snd_ac97_codec_ids[i].name; - break; - } - } - if (ac97_name == NULL) - ac97_name = "Unknown"; - printk(KERN_INFO "trident: ac97 vendor id1: 0x%04x, id2: 0x%04x (%s)\n", - id1, id2, ac97_name); - - /* initialize volume level */ - trident_ac97_set(trident, AC97_RESET, 0L); - trident_ac97_set(trident, AC97_MASTER_VOL_STEREO, 0L); - trident_ac97_set(trident, AC97_PCMOUT_VOL, 0L); - - /* set appropriate masks and function pointers */ - trident->mix.supported_mixers = AC97_SUPPORTED_MASK; - trident->mix.stereo_mixers = AC97_STEREO_MASK; - trident->mix.record_sources = AC97_RECORD_MASK; - /* FIXME: trident->mix.read_mixer = ac97_read_mixer; */ - trident->mix.write_mixer = ac97_write_mixer; - trident->mix.recmask_io = ac97_recmask_io; - - return 0; -} - -/* this function only update fmt field in trident_state, the hardware channel attribute - will be update in trident_play(rec)_setup() which will be called every time a new - sample is played(recorded) */ -static void set_fmt(struct trident_state *s, unsigned char mask, unsigned char data) -{ - s->fmt = (s->fmt & mask) | data; -} - -/* the mode passed should be already shifted and masked */ -/* trident_play_setup: initialize channel for play back, mode specify the format of samples to - be played. - default values: -*/ - -static void trident_play_setup(struct trident_state *trident, int mode, u32 rate, - void *buffer, int size) -{ - unsigned int LBA; - unsigned int Delta; - unsigned int ESO; - unsigned int CTRL; - unsigned int FMC_RVOL_CVOL; - unsigned int GVSEL; - unsigned int PAN; - unsigned int VOL; - unsigned int EC; - - /* set Loop Begin Address */ - LBA = virt_to_bus(buffer); - Delta = compute_rate(rate); - - /* set ESO */ - ESO = size; - if (mode & TRIDENT_FMT_16BIT) - ESO /= 2; - if (mode & TRIDENT_FMT_STEREO) - ESO /= 2; - ESO = ESO - 1; - - /* loop mode enable */ - CTRL = 0x00000001; - if (mode & TRIDENT_FMT_16BIT) { + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { /* 16-bits */ - CTRL |= 0x00000008; + channel->control |= CHANNEL_16BITS; /* signed */ - CTRL |= 0x00000002; + channel->control |= CHANNEL_SIGNED; } - if (mode & TRIDENT_FMT_STEREO) + if (dmabuf->fmt & TRIDENT_FMT_STEREO) /* stereo */ - CTRL |= 0x00000004; - - /* FIXME: some difference between 4D and 7018 in FMC_RVOL_CVOL */ - /* right vol: mute, ledt vol: mute */ - FMC_RVOL_CVOL = 0x0000ffff; - GVSEL = 1; - PAN = 0; - VOL = 0; - EC = 0; - - trident_write_voice_regs(trident->card, - trident->dma_dac.chan, - LBA, - 0, /* cso */ - ESO, - Delta, - 0, /* alpha */ - FMC_RVOL_CVOL, - GVSEL, - PAN, - VOL, - CTRL, - EC); - + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_play_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state, 0); } -/* - * Native record driver - */ -/* FIXME: Not exammed yet */ -/* again, passed mode is alrady shifted/masked */ - -static void trident_rec_setup(struct trident_state *trident, int mode, u32 rate, - void *buffer, int size) +/* prepare channel attributes for recording */ +static void trident_rec_setup(struct trident_state *state) { - unsigned int LBA; - unsigned int Delta; - unsigned int ESO; - unsigned int CTRL; - unsigned int FMC_RVOL_CVOL; - unsigned int GVSEL; - unsigned int PAN; - unsigned int VOL; - unsigned int EC; - unsigned char bValue; - unsigned short wValue; - unsigned int dwValue; - unsigned short wRecCODECSamples; - unsigned int dwChanFlags; - struct trident_card *card = trident->card; - -#ifdef DEBUG - printk("trident: trident_rec_setup called\n"); -#endif + u16 w; + struct trident_card *card = state->card; + struct dmabuf *dmabuf = &state->dma_adc; + struct trident_channel *channel = dmabuf->channel; - // Enable AC-97 ADC (capture), disable capture interrupt + /* Enable AC-97 ADC (capture) */ switch (card->pci_id) { case PCI_DEVICE_ID_SI_7018: /* for 7018, the ac97 is always in playback/record (duplex) mode */ break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - bValue = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); - outb(bValue | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + w = inb(TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + outb(w | 0x48, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - wValue = inw(TRID_REG(card, T4D_MISCINT)); - outw(wValue | 0x1000, TRID_REG(card, T4D_MISCINT)); + w = inw(TRID_REG(card, T4D_MISCINT)); + outw(w | 0x1000, TRID_REG(card, T4D_MISCINT)); break; + default: + return; } - // Initilize the channel and set channel Mode - outb(0, TRID_REG(card, LEGACY_DMAR15)); + channel->lba = virt_to_bus(dmabuf->rawbuf); + channel->delta = compute_rate(dmabuf->rate); - // Set DMA channel operation mode register - bValue = inb(TRID_REG(card, LEGACY_DMAR11)) & 0x03; - outb(bValue | 0x54, TRID_REG(card, LEGACY_DMAR11)); + channel->eso = dmabuf->dmasize >> sample_shift[dmabuf->fmt]; + channel->eso -= 1; - // Set channel buffer Address - LBA = virt_to_bus(buffer); - outl(LBA, TRID_REG(card, LEGACY_DMAR0)); - - /* set ESO */ - ESO = size; - - dwValue = inl(TRID_REG(card, LEGACY_DMAR4)) & 0xff000000; - dwValue |= (ESO - 1) & 0x0000ffff; - outl(dwValue, TRID_REG(card, LEGACY_DMAR4)); - - // Set channel sample rate , 4.12 format - dwValue = (((unsigned int) 48000L << 12) / (unsigned long) (rate)); - outw((unsigned short) dwValue, TRID_REG(card, T4D_SBDELTA_DELTA_R)); - - // Set channel interrupt blk length - if (mode & TRIDENT_FMT_16BIT) { - wRecCODECSamples = (unsigned short) ((ESO >> 1) - 1); - dwChanFlags = 0xffffb000; + if (state->card->pci_id == PCI_DEVICE_ID_SI_7018) { + /* FIXME: channel attributes are configured by ioctls, but it is not implemented + so just set to ZERO for the moment */ + channel->attribute = 0; } else { - wRecCODECSamples = (unsigned short) (ESO - 1); - dwChanFlags = 0xffff1000; + channel->attribute = 0; } - dwValue = ((unsigned int) wRecCODECSamples) << 16; - dwValue |= (unsigned int) (wRecCODECSamples) & 0x0000ffff; - outl(dwValue, TRID_REG(card, T4D_SBBL_SBCL)); - - // Right now, set format and start to run capturing, - // continuous run loop enable. - trident->bDMAStart = 0x19; // 0001 1001b - - if (mode & TRIDENT_FMT_16BIT) - trident->bDMAStart |= 0xa0; - if (mode & TRIDENT_FMT_STEREO) - trident->bDMAStart |= 0x40; - - // Prepare capture intr channel - - Delta = ((((unsigned int) rate) << 12) / ((unsigned long) (48000L))); - - /* set Loop Back Address */ - LBA = virt_to_bus(buffer); - - /* set ESO */ - ESO = size; - if (mode & TRIDENT_FMT_16BIT) - ESO /= 2; - if (mode & TRIDENT_FMT_STEREO) - ESO /= 2; - - ESO = ESO - 1; - //snd_printk("trid: ESO = %d\n", ESO); - - /* set ctrl mode - CTRL default: 8-bit (unsigned) mono, loop mode enabled - */ - CTRL = 0x00000001; - if (mode & TRIDENT_FMT_16BIT) - CTRL |= 0x00000008; // 16-bit data - /* XXX DO UNSIGNED XXX */ - //if (!(mode & SND_PCM1_MODE_U)) - // CTRL |= 0x00000002; // signed data - if (mode& TRIDENT_FMT_STEREO) - CTRL |= 0x00000004; // stereo data - - FMC_RVOL_CVOL = 0x0000ffff; - GVSEL = 1; - PAN = 0xff; - VOL = 0xff; - EC = 0; - - trident_write_voice_regs(card, - trident->dma_adc.chan, - LBA, - 0, /* cso */ - ESO, - Delta, - 0, /* alpha */ - FMC_RVOL_CVOL, - GVSEL, - PAN, - VOL, - CTRL, - EC); - + channel->fm_vol = 0x0; + + channel->control = CHANNEL_LOOP; + if (dmabuf->fmt & TRIDENT_FMT_16BIT) { + /* 16-bits */ + channel->control |= CHANNEL_16BITS; + /* signed */ + channel->control |= CHANNEL_SIGNED; + } + if (dmabuf->fmt & TRIDENT_FMT_STEREO) + /* stereo */ + channel->control |= CHANNEL_STEREO; +#ifdef DEBUG + printk("trident: trident_rec_setup, LBA = 0x%08x, " + "Delat = 0x%08x, ESO = 0x%08x, Control = 0x%08x\n", + channel->lba, channel->delta, channel->eso, channel->control); +#endif + trident_write_voice_regs(state, 1); } -/* get current playback pointer */ -__inline__ unsigned int get_dmaa(struct trident_state *trident) +/* get current playback/recording dma buffer pointer (byte offset from LBA), + called with spinlock held! */ +extern __inline__ unsigned trident_get_dma_addr(struct trident_state *state, unsigned rec) { + struct dmabuf *dmabuf; u32 cso; - u32 eso; -#if 0 - /* FIXME: does this mean that FULL duplex is not supported ? */ - if (!(trident->enable & ADC_RUNNING)) + + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + + if (!dmabuf->enable) return 0; -#endif - outb(trident->dma_dac.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR)); - switch (trident->card->pci_id) + outb(dmabuf->channel->num, TRID_REG(state->card, T4D_LFO_GC_CIR)); + + switch (state->card->pci_id) { case PCI_DEVICE_ID_SI_7018: case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: /* 16 bits ESO, CSO for 7018 and DX */ - cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2)); - eso = inw(TRID_REG(trident->card, CH_DX_ESO_DELTA + 2)); + cso = inw(TRID_REG(state->card, CH_DX_CSO_ALPHA_FMS + 2)); break; case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: /* 24 bits ESO, CSO for NX */ - cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff; - eso = inl(TRID_REG(trident->card, CH_NX_DELTA_ESO)) & 0x00ffffff; + cso = inl(TRID_REG(state->card, CH_NX_DELTA_CSO)) & 0x00ffffff; break; default: return 0; } #ifdef DEBUG - printk("trident: get_dmaa: chip reported channel: %d, cso = %d, eso = %d\n", - trident->dma_dac.chan, cso, eso); + printk("trident: trident_get_dma_addr: chip reported channel: %d, cso = %d\n", + dmabuf->channel->num, cso); #endif /* ESO and CSO are in units of Samples, convert to byte offset */ - if (cso > eso) - cso = eso; - if (trident->fmt & TRIDENT_FMT_16BIT) - cso *= 2; - if (trident->fmt & TRIDENT_FMT_STEREO) - cso *= 2; - return cso; + cso <<= sample_shift[dmabuf->fmt]; + + return (cso % dmabuf->dmasize); } -/* get current recording pointer */ -extern __inline__ unsigned get_dmac(struct trident_state *trident) +/* Stop recording (lock held) */ +extern __inline__ void __stop_adc(struct trident_state *state) { - u32 cso; -#if 0 - /* FIXME: does this mean that FULL duplex is not supported ? */ - if (!(trident->enable & DAC_RUNNING)) - return 0; -#endif - outb(trident->dma_adc.chan, TRID_REG(trident->card, T4D_LFO_GC_CIR)); + struct dmabuf *dmabuf = &state->dma_adc; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; - switch (trident->card->pci_id) - { - default: - case PCI_DEVICE_ID_SI_7018: - case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: - /* 16 bits ESO, CSO for 7018 and DX */ - cso = inw(TRID_REG(trident->card, CH_DX_CSO_ALPHA_FMS + 2)); - break; - case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: - /* 24 bits ESO, CSO for NX */ - cso = inl(TRID_REG(trident->card, CH_NX_DELTA_CSO)) & 0x00ffffff; - break; - } - -#ifdef DEBUG - printk("(trident) get_dmac: chip reported cso = %d\n", cso); -#endif - /* ESO and CSO are in units of Samples, convert to byte offset */ - if (trident->fmt & TRIDENT_FMT_16BIT) - cso *= 2; - if (trident->fmt & TRIDENT_FMT_STEREO) - cso *= 2; - return cso; + dmabuf->enable &= ~DMA_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); } -/* Stop recording (lock held) */ -extern inline void __stop_adc(struct trident_state *s) +static void stop_adc(struct trident_state *state) { - struct trident_card *trident = s->card; -#ifdef DEBUG - printk("(trident) stopping ADC\n"); -#endif - s->enable &= ~ADC_RUNNING; - trident_disable_voice_irq(trident, s->dma_adc.chan); - outb(0x00, TRID_REG(trident, T4D_SBCTRL_SBE2R_SBDD)); - trident_disable_voice_irq(trident, s->dma_adc.chan); - trident_stop_voice(trident, s->dma_adc.chan); - ResetAinten(trident, s->dma_adc.chan); -} + struct trident_card *card = state->card; + unsigned long flags; -extern inline void stop_adc(struct trident_state *s) + spin_lock_irqsave(&card->lock, flags); + __stop_adc(state); + spin_unlock_irqrestore(&card->lock, flags); +} + +static void start_adc(struct trident_state *state) { + struct dmabuf *dmabuf = &state->dma_adc; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; unsigned long flags; - struct trident_card *trident = s->card; - spin_lock_irqsave(&trident->lock, flags); - __stop_adc(s); - spin_unlock_irqrestore(&trident->lock, flags); -} + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || + dmabuf->count < (signed)(dmabuf->dmasize - 2*dmabuf->fragsize)) + && dmabuf->ready) { + dmabuf->enable |= DMA_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); + } + spin_unlock_irqrestore(&card->lock, flags); +} /* stop playback (lock held) */ -extern inline void __stop_dac(struct trident_state *state) +extern __inline__ void __stop_dac(struct trident_state *state) { - struct trident_card *trident = state->card; - trident_stop_voice(trident, state->dma_dac.chan); - trident_disable_voice_irq(trident, state->dma_dac.chan); - state->enable &= ~DAC_RUNNING; -} + struct dmabuf *dmabuf = &state->dma_dac; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; + + dmabuf->enable &= ~DMA_RUNNING; + trident_stop_voice(card, chan_num); + trident_disable_voice_irq(card, chan_num); +} -extern inline void stop_dac(struct trident_state *state) +static void stop_dac(struct trident_state *state) { - struct trident_card *trident = state->card; + struct trident_card *card = state->card; unsigned long flags; - spin_lock_irqsave(&trident->lock, flags); + spin_lock_irqsave(&card->lock, flags); __stop_dac(state); - spin_unlock_irqrestore(&trident->lock, flags); + spin_unlock_irqrestore(&card->lock, flags); } static void start_dac(struct trident_state *state) { - unsigned long flags; - struct trident_card *trident = state->card; - - spin_lock_irqsave(&state->card->lock, flags); - if ((state->dma_dac.mapped || state->dma_dac.count > 0) && state->dma_dac.ready) { - state->enable |= DAC_RUNNING; - trident_enable_voice_irq(trident, state->dma_dac.chan); - trident_start_voice(trident, state->dma_dac.chan); - } - spin_unlock_irqrestore(&state->card->lock, flags); -} - -static void start_adc(struct trident_state *s) -{ + struct dmabuf *dmabuf = &state->dma_dac; + unsigned int chan_num = dmabuf->channel->num; + struct trident_card *card = state->card; unsigned long flags; - spin_lock_irqsave(&s->card->lock, flags); - if ((s->dma_adc.mapped || s->dma_adc.count < (signed)(s->dma_adc.dmasize - 2*s->dma_adc.fragsize)) - && s->dma_adc.ready) { - s->enable |= ADC_RUNNING; - trident_enable_voice_irq(s->card, s->dma_adc.chan); - outb(s->bDMAStart, TRID_REG(s->card, T4D_SBCTRL_SBE2R_SBDD)); - trident_start_voice(s->card, s->dma_adc.chan); -#ifdef DEBUG - printk("(trident) starting ADC\n"); -#endif + spin_lock_irqsave(&card->lock, flags); + if ((dmabuf->mapped || dmabuf->count > 0) && dmabuf->ready) { + dmabuf->enable |= DMA_RUNNING; + trident_enable_voice_irq(card, chan_num); + trident_start_voice(card, chan_num); } - spin_unlock_irqrestore(&s->card->lock, flags); -} + spin_unlock_irqrestore(&card->lock, flags); +} #define DMABUF_DEFAULTORDER (15-PAGE_SHIFT) #define DMABUF_MINORDER 1 @@ -1623,10 +823,16 @@ static void start_adc(struct trident_state *s) /* allocate DMA buffer, playback and recording buffer should be allocated seperately */ static int alloc_dmabuf(struct trident_state *state, unsigned rec) { + struct dmabuf *dmabuf; void *rawbuf; int order; unsigned long map, mapend; + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + /* alloc as big a chunk as we can, FIXME: is this necessary ?? */ for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) if ((rawbuf = (void *)__get_free_pages(GFP_KERNEL, order))) @@ -1634,7 +840,7 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) if (!rawbuf) return -ENOMEM; #ifdef DEBUG - printk("trident: allocated %ld (%d) bytes at %p\n", + printk("trident: allocated %ld (order = %d) bytes at %p\n", PAGE_SIZE << order, order, rawbuf); #endif @@ -1648,17 +854,10 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) return -ENOMEM; } - if (rec) { - state->dma_adc.ready = state->dma_adc.mapped = 0; - state->dma_adc.rawbuf = rawbuf; - state->dma_adc.buforder = order; - } - else { - state->dma_dac.ready = state->dma_dac.mapped = 0; - state->dma_dac.rawbuf = rawbuf; - state->dma_dac.buforder = order; - } - + 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++) @@ -1668,180 +867,237 @@ static int alloc_dmabuf(struct trident_state *state, unsigned rec) } /* free DMA buffer */ -static void dealloc_dmabuf(struct dmabuf *db) +static void dealloc_dmabuf(struct dmabuf *dmabuf) { unsigned long map, mapend; - if (db->rawbuf) { + if (dmabuf->rawbuf) { /* undo marking the pages as reserved */ - mapend = MAP_NR(db->rawbuf + (PAGE_SIZE << db->buforder) - 1); - for (map = MAP_NR(db->rawbuf); map <= mapend; map++) + 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); - free_pages((unsigned long)db->rawbuf, db->buforder); + free_pages((unsigned long)dmabuf->rawbuf, dmabuf->buforder); } - db->rawbuf = NULL; - db->mapped = db->ready = 0; + dmabuf->rawbuf = NULL; + dmabuf->mapped = dmabuf->ready = 0; } static int prog_dmabuf(struct trident_state *state, unsigned rec) { - struct dmabuf *db = rec ? &state->dma_adc : &state->dma_dac; - unsigned rate = rec ? state->rateadc : state->ratedac; + struct dmabuf *dmabuf; unsigned bytepersec; - unsigned bufs; - unsigned char fmt; + unsigned bufsize; unsigned long flags; int ret; + if (rec) + dmabuf = &state->dma_adc; + else + dmabuf = &state->dma_dac; + spin_lock_irqsave(&state->card->lock, flags); - fmt = state->fmt; - if (rec) { - state->enable &= ~TRIDENT_ENABLE_RE; - fmt >>= TRIDENT_ADC_SHIFT; - } else { - state->enable &= ~TRIDENT_ENABLE_PE; - fmt >>= TRIDENT_DAC_SHIFT; - } + dmabuf->hwptr = dmabuf->swptr = dmabuf->total_bytes = 0; + dmabuf->count = dmabuf->error = dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); - fmt &= TRIDENT_FMT_MASK; - - db->hwptr = db->swptr = db->total_bytes = 0; - db->count = db->error = db->endcleared = 0; - /* allocate DMA buffer if not allocated yet */ - if (!db->rawbuf) + if (!dmabuf->rawbuf) if ((ret = alloc_dmabuf(state, rec))) return ret; - bytepersec = rate << sample_shift[fmt]; - bufs = PAGE_SIZE << db->buforder; - if (db->ossfragshift) { - if ((1000 << db->ossfragshift) < bytepersec) - db->fragshift = ld2(bytepersec/1000); + /* 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 - db->fragshift = db->ossfragshift; + dmabuf->fragshift = dmabuf->ossfragshift; } else { /* lets hand out reasonable big ass buffers by default */ - db->fragshift = (db->buforder + PAGE_SHIFT -2); + dmabuf->fragshift = (dmabuf->buforder + PAGE_SHIFT -2); } - db->numfrag = bufs >> db->fragshift; - while (db->numfrag < 4 && db->fragshift > 3) { - db->fragshift--; - db->numfrag = bufs >> db->fragshift; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; + while (dmabuf->numfrag < 4 && dmabuf->fragshift > 3) { + dmabuf->fragshift--; + dmabuf->numfrag = bufsize >> dmabuf->fragshift; } - db->fragsize = 1 << db->fragshift; - if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) - db->numfrag = db->ossmaxfrags; - db->fragsamples = db->fragsize >> sample_shift[fmt]; - db->dmasize = db->numfrag << db->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(db->rawbuf, (fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, db->dmasize); + memset(dmabuf->rawbuf, (dmabuf->fmt & TRIDENT_FMT_16BIT) ? 0 : 0x80, + dmabuf->dmasize); spin_lock_irqsave(&state->card->lock, flags); if (rec) { - trident_rec_setup(state, fmt, state->rateadc, - db->rawbuf, db->numfrag << db->fragshift); + trident_rec_setup(state); } else { - trident_play_setup(state, fmt, state->ratedac, - db->rawbuf, db->numfrag << db->fragshift); + trident_play_setup(state); } spin_unlock_irqrestore(&state->card->lock, flags); /* set the ready flag for the dma buffer */ - db->ready = 1; + dmabuf->ready = 1; #ifdef DEBUG printk("trident: prog_dmabuf, sample rate = %d, format = %d, numfrag = %d, " "fragsize = %d dmasize = %d\n", - rate, fmt, db->numfrag, db->fragsize, db->dmasize); + dmabuf->rate, dmabuf->fmt, dmabuf->numfrag, + dmabuf->fragsize, dmabuf->dmasize); #endif return 0; } -extern __inline__ void clear_advance(struct trident_state *s) +/* 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 trident_clear_tail(struct trident_state *state) +{ + struct dmabuf *dmabuf = &state->dma_dac; + unsigned swptr; + unsigned char silence = (dmabuf->fmt & TRIDENT_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 trident_state *state, int nonblock) { - unsigned char c = ((s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_16BIT) ? 0 : 0x80; - unsigned char *buf = s->dma_dac.rawbuf; - unsigned bsize = s->dma_dac.dmasize; - unsigned bptr = s->dma_dac.swptr; - unsigned len = s->dma_dac.fragsize; - - if (bptr + len > bsize) { - unsigned x = bsize - bptr; - memset(buf + bptr, c, x); - /* account for wrapping? */ - bptr = 0; - len -= x; + DECLARE_WAITQUEUE(wait, current); + struct dmabuf *dmabuf = &state->dma_dac; + 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 "trident: drain_dac, dma timeout?\n"); + break; + } } - memset(buf + bptr, c, len); + remove_wait_queue(&dmabuf->wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + + return 0; } /* call with spinlock held! */ -static void trident_update_ptr(struct trident_state *s) +static void trident_update_ptr(struct trident_state *state) { + struct dmabuf *dmabuf; unsigned hwptr; int diff; /* update ADC pointer */ - if (s->dma_adc.ready) { - hwptr = get_dmac(s) % s->dma_adc.dmasize; - diff = (s->dma_adc.dmasize + hwptr - s->dma_adc.hwptr) % - s->dma_adc.dmasize; - s->dma_adc.hwptr = hwptr; - s->dma_adc.total_bytes += diff; - s->dma_adc.count += diff; - if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) - wake_up(&s->dma_adc.wait); - if (!s->dma_adc.mapped) { - if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { - s->enable &= ~TRIDENT_ENABLE_RE; - __stop_adc(s); - s->dma_adc.error++; + if (state->dma_adc.ready) { + dmabuf = &state->dma_adc; + hwptr = trident_get_dma_addr(state, 1); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + dmabuf->count += diff; + + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + if (!dmabuf->mapped) { + if (dmabuf->count > (signed)(dmabuf->dmasize - ((3 * dmabuf->fragsize) >> 1))) { + __stop_adc(state); + dmabuf->error++; } } } /* update DAC pointer */ - if (s->dma_dac.ready) { - hwptr = get_dmaa(s) % s->dma_dac.dmasize; - diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % - s->dma_dac.dmasize; - s->dma_dac.hwptr = hwptr; - s->dma_dac.total_bytes += diff; - if (s->dma_dac.mapped) { - s->dma_dac.count += diff; - if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) - wake_up(&s->dma_dac.wait); - } + if (state->dma_dac.ready) { + dmabuf = &state->dma_dac; + hwptr = trident_get_dma_addr(state, 0); + diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize; + + dmabuf->hwptr = hwptr; + dmabuf->total_bytes += diff; + + if (dmabuf->mapped) { + dmabuf->count += diff; + if (dmabuf->count >= (signed)dmabuf->fragsize) + wake_up(&dmabuf->wait); + } else { - s->dma_dac.count -= diff; - if (s->dma_dac.count <= 0) { - s->enable &= ~TRIDENT_ENABLE_PE; - /* Lock already held */ - __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.error++; - } - else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && - !s->dma_dac.endcleared) { - clear_advance(s); - s->dma_dac.endcleared = 1; + 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); + dmabuf->error++; } - - if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) - wake_up(&s->dma_dac.wait); + /* 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); } } } -/* - * Trident interrupt handlers. - */ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct trident_state *state; @@ -1858,19 +1114,17 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) if (event & ADDRESS_IRQ) { /* Update the pointers for all channels we are running. */ - /* FIXME: improve interrupt latency !!! */ - for (i = 0; i < NR_DSPS; i++) { - state = card->channels[i]; + /* 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 != NULL) + if ((state = card->states[i]) != NULL) { trident_update_ptr(state); - else { - /* Spurious ? */ - printk("trident: spurious channel irq %d.\n", + } else { + printk("trident: spurious channel irq %d.\n", 63 - i); trident_stop_voice(card, i); - trident_disable_voice_irq(card, i); + trident_disable_voice_irq(card, i); } } } @@ -1886,254 +1140,29 @@ static void trident_interrupt(int irq, void *dev_id, struct pt_regs *regs) spin_unlock(&card->lock); } -static void set_mixer(struct trident_card *card,unsigned int mixer, unsigned int val ) -{ - unsigned int left,right; - - /* cleanse input a little */ - right = ((val >> 8) & 0xff) ; - left = (val & 0xff) ; - - if (right > 100) right = 100; - if (left > 100) left = 100; - - card->mix.mixer_state[mixer] = (right << 8) | left; - card->mix.write_mixer(card, mixer, left, right); -} - -static int mixer_ioctl(struct trident_card *card, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - int i, val = 0; - - VALIDATE_CARD(card); - if (cmd == SOUND_MIXER_INFO) { - mixer_info info; - strncpy(info.id, card->pci_info->name, sizeof(info.id)); - strncpy(info.name, card->pci_info->name, sizeof(info.name)); - info.modify_counter = card->mix.modcnt; - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - if (cmd == SOUND_OLD_MIXER_INFO) { - _old_mixer_info info; - strncpy(info.id, card->pci_info->name, sizeof(info.id)); - strncpy(info.name, card->pci_info->name, sizeof(info.name)); - if (copy_to_user((void *)arg, &info, sizeof(info))) - return -EFAULT; - return 0; - } - - if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) - return -EINVAL; - - if (cmd == OSS_GETVERSION) - return put_user(SOUND_VERSION, (int *)arg); - - if (_IOC_DIR(cmd) == _IOC_READ) { - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* give them the current record source */ - if (!card->mix.recmask_io) { - val = 0; - } else { - spin_lock_irqsave(&card->lock, flags); - val = card->mix.recmask_io(card,1,0); - spin_unlock_irqrestore(&card->lock, flags); - } - break; - - case SOUND_MIXER_DEVMASK: /* give them the supported mixers */ - val = card->mix.supported_mixers; - break; - - case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ - val = card->mix.record_sources; - break; - - case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ - val = card->mix.stereo_mixers; - break; - - case SOUND_MIXER_CAPS: - val = SOUND_CAP_EXCL_INPUT; - break; - - default: /* read a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(card,i)) - return -EINVAL; - - /* do we ever want to touch the hardware? */ - /* spin_lock_irqsave(&s->lock, flags); - val = card->mix.read_mixer(card,i); - spin_unlock_irqrestore(&s->lock, flags);*/ - - val = card->mix.mixer_state[i]; - /* printk("returned 0x%x for mixer %d\n",val,i);*/ - break; - } - return put_user(val,(int *)arg); - } - - if (_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) { - card->mix.modcnt++; - get_user_ret(val, (int *)arg, -EFAULT); - - switch (_IOC_NR(cmd)) { - case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ - if (!card->mix.recmask_io) return -EINVAL; - if (!(val &= card->mix.record_sources)) return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - card->mix.recmask_io(card, 0, val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - default: /* write a specific mixer */ - i = _IOC_NR(cmd); - - if (!supported_mixer(card, i)) - return -EINVAL; - - spin_lock_irqsave(&card->lock, flags); - set_mixer(card, i, val); - spin_unlock_irqrestore(&card->lock, flags); - - return 0; - } - } - return -EINVAL; -} - -static int trident_open_mixdev(struct inode *inode, struct file *file) -{ - int minor = MINOR(inode->i_rdev); - struct trident_card *card = devs; - - while (card && card->dev_mixer != minor) - card = card->next; - if (!card) - return -ENODEV; - - file->private_data = card; - - //FIXME put back in - //MOD_INC_USE_COUNT; - return 0; -} - -static int trident_release_mixdev(struct inode *inode, struct file *file) -{ - struct trident_card *card = (struct trident_card *)file->private_data; - - VALIDATE_CARD(card); - - //FIXME put back in - //MOD_DEC_USE_COUNT; - return 0; -} - -static int trident_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct trident_card *card = (struct trident_card *)file->private_data; - - VALIDATE_CARD(card); - return mixer_ioctl(card, cmd, arg); -} - static loff_t trident_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } -static /*const*/ struct file_operations trident_mixer_fops = { - &trident_llseek, - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - &trident_ioctl_mixdev, - NULL, /* mmap */ - &trident_open_mixdev, - NULL, /* flush */ - &trident_release_mixdev, - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* lock */ -}; - -/* drain the DAC buffer - FIXME: This function will block (forever ??) when using - XMMS Qsound plugin and direct cat sample.wav > /dev/dsp - This behavior is when drain_dac is called by trident_release. */ -static int drain_dac(struct trident_state *s, int nonblock) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - int count; - signed long tmo; - - if (s->dma_dac.mapped || !s->dma_dac.ready) - return 0; - - current->state = TASK_INTERRUPTIBLE; - add_wait_queue(&s->dma_dac.wait, &wait); - for (;;) { - spin_lock_irqsave(&s->card->lock, flags); - count = s->dma_dac.count; - spin_unlock_irqrestore(&s->card->lock, flags); - - if (count <= 0) - break; - - if (signal_pending(current)) - break; - - if (nonblock) { - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - return -EBUSY; - } - - tmo = (count * HZ) / s->ratedac; - tmo >>= sample_shift[(s->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK]; - - /* XXX this is just broken. someone is waking us up alot, - or schedule_timeout is broken. - or something. who cares. - zach */ - if (!schedule_timeout(tmo ? tmo : 1) && tmo) - printk(KERN_ERR "trident: drain_dac, " - "dma timed out? jiffies = %ld\n", - jiffies); - } - remove_wait_queue(&s->dma_dac.wait, &wait); - current->state = TASK_RUNNING; - if (signal_pending(current)) - return -ERESTARTSYS; - - return 0; -} - /* in this loop, dma_adc.count signifies the amount of data thats waiting to be copied to the user's buffer. it is filled by the interrupt handler and drained by this loop. */ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dma_dac; ssize_t ret; unsigned long flags; unsigned swptr; int cnt; - + VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; - if (state->dma_adc.mapped) + if (dmabuf->mapped) return -ENXIO; - if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1))) + if (!dmabuf->ready && (ret = prog_dmabuf(state, 1))) return ret; if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; @@ -2141,12 +1170,10 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); - /* remember, all these things are expressed in bytes to be - sent to the user.. hence the evil / 2 down below */ - swptr = state->dma_adc.swptr; - cnt = state->dma_adc.dmasize - swptr; - if (state->dma_adc.count < cnt) - cnt = state->dma_adc.count; + swptr = dmabuf->swptr; + cnt = dmabuf->dmasize - swptr; + if (dmabuf->count < cnt) + cnt = dmabuf->count; spin_unlock_irqrestore(&state->card->lock, flags); if (cnt > count) @@ -2158,20 +1185,17 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ ret = ret ? ret : -EAGAIN; return ret; } - if (!interruptible_sleep_on_timeout(&state->dma_adc.wait, HZ)) { - printk(KERN_DEBUG "(trident) read: chip lockup? " + if (!interruptible_sleep_on_timeout(&dmabuf->wait, HZ)) { + printk(KERN_ERR + "(trident) read: chip lockup? " "dmasz %u fragsz %u count %i hwptr %u swptr %u\n", - state->dma_adc.dmasize, - state->dma_adc.fragsize, - state->dma_adc.count, - state->dma_adc.hwptr, - state->dma_adc.swptr); + dmabuf->dmasize, dmabuf->fragsize, dmabuf->count, + dmabuf->hwptr, dmabuf->swptr); stop_adc(state); - spin_lock_irqsave(&state->card->lock, flags); - state->dma_adc.count = 0; - state->dma_adc.hwptr = 0; - state->dma_adc.swptr = 0; + dmabuf->count = 0; + dmabuf->hwptr = 0; + dmabuf->swptr = 0; spin_unlock_irqrestore(&state->card->lock, flags); } if (signal_pending(current)) { @@ -2180,17 +1204,19 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ } continue; } - - if (copy_to_user(buffer, state->dma_adc.rawbuf + swptr, cnt)) { + + if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) { ret = ret ? ret : -EFAULT; return ret; } - swptr = (swptr + cnt) % state->dma_adc.dmasize; + swptr = (swptr + cnt) % dmabuf->dmasize; + spin_lock_irqsave(&state->card->lock, flags); - state->dma_adc.swptr = swptr; - state->dma_adc.count -= cnt; + dmabuf->swptr = swptr; + dmabuf->count -= cnt; spin_unlock_irqrestore(&state->card->lock, flags); + count -= cnt; buffer += cnt; ret += cnt; @@ -2203,11 +1229,11 @@ static ssize_t trident_read(struct file *file, char *buffer, size_t count, loff_ static ssize_t trident_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) { struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf = &state->dma_dac; ssize_t ret; unsigned long flags; unsigned swptr; int cnt; - int mode = (state->fmt >> TRIDENT_DAC_SHIFT) & TRIDENT_FMT_MASK; #ifdef DEBUG printk("trident: trident_write called, count = %d\n", count); @@ -2216,9 +1242,9 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count VALIDATE_STATE(state); if (ppos != &file->f_pos) return -ESPIPE; - if (state->dma_dac.mapped) + if (dmabuf->mapped) return -ENXIO; - if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0))) + if (!dmabuf->ready && (ret = prog_dmabuf(state, 0))) return ret; if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; @@ -2226,41 +1252,47 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count while (count > 0) { spin_lock_irqsave(&state->card->lock, flags); - if (state->dma_dac.count < 0) { - state->dma_dac.count = 0; - state->dma_dac.swptr = state->dma_dac.hwptr; + 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 = state->dma_dac.swptr; - cnt = state->dma_dac.dmasize - swptr; - if (state->dma_dac.count + cnt > state->dma_dac.dmasize) - cnt = state->dma_dac.dmasize - state->dma_dac.count; + 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) { - /* buffer is full, wait for it to be played */ + 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; } - if (!interruptible_sleep_on_timeout(&state->dma_dac.wait, HZ)) { - printk(KERN_ERR - "trident: write: chip lockup? " - "dmasz %u fragsz %u count %i " - "hwptr %u swptr %u\n", - state->dma_dac.dmasize, - state->dma_dac.fragsize, - state->dma_dac.count, - state->dma_dac.hwptr, - state->dma_dac.swptr); - stop_dac(state); - spin_lock_irqsave(&state->card->lock, flags); - state->dma_dac.count = 0; - state->dma_dac.hwptr = 0; - state->dma_dac.swptr = 0; - spin_unlock_irqrestore(&state->card->lock, flags); + /* 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 "trident: 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; @@ -2268,18 +1300,17 @@ static ssize_t trident_write(struct file *file, const char *buffer, size_t count } continue; } - if (copy_from_user(state->dma_dac.rawbuf + swptr, buffer, cnt)) { - if (!ret) - ret = -EFAULT; + if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; return ret; } - swptr = (swptr + cnt) % state->dma_dac.dmasize; + swptr = (swptr + cnt) % dmabuf->dmasize; spin_lock_irqsave(&state->card->lock, flags); - state->dma_dac.swptr = swptr; - state->dma_dac.count += cnt; - state->dma_dac.endcleared = 0; + dmabuf->swptr = swptr; + dmabuf->count += cnt; + dmabuf->endcleared = 0; spin_unlock_irqrestore(&state->card->lock, flags); count -= cnt; @@ -2304,7 +1335,6 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa spin_lock_irqsave(&s->card->lock, flags); trident_update_ptr(s); - if (file->f_mode & FMODE_READ) { if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) mask |= POLLIN | POLLRDNORM; @@ -2319,56 +1349,56 @@ static unsigned int trident_poll(struct file *file, struct poll_table_struct *wa } } spin_unlock_irqrestore(&s->card->lock, flags); + return mask; } -/* this needs to be fixed to deal with the dual apus/buffers */ -#if 0 static int trident_mmap(struct file *file, struct vm_area_struct *vma) { - struct trident_state *s = (struct trident_state *)file->private_data; - struct dmabuf *db; + struct trident_state *state = (struct trident_state *)file->private_data; + struct dmabuf *dmabuf; int ret; unsigned long size; - VALIDATE_STATE(s); + VALIDATE_STATE(state); if (vma->vm_flags & VM_WRITE) { - if ((ret = prog_dmabuf(s, 0)) != 0) + if ((ret = prog_dmabuf(state, 0)) != 0) return ret; - db = &s->dma_dac; + dmabuf = &state->dma_dac; } else if (vma->vm_flags & VM_READ) { - if ((ret = prog_dmabuf(s, 1)) != 0) + if ((ret = prog_dmabuf(state, 1)) != 0) return ret; - db = &s->dma_adc; + dmabuf = &state->dma_adc; } else return -EINVAL; - if (vma->vm_offset != 0) + + if (vma->vm_pgoff != 0) return -EINVAL; size = vma->vm_end - vma->vm_start; - if (size > (PAGE_SIZE << db->buforder)) + if (size > (PAGE_SIZE << dmabuf->buforder)) return -EINVAL; - if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf), + size, vma->vm_page_prot)) return -EAGAIN; - db->mapped = 1; + dmabuf->mapped = 1; + return 0; } -#endif static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - struct trident_state *s = (struct trident_state *)file->private_data; + struct trident_state *state = (struct trident_state *)file->private_data; unsigned long flags; audio_buf_info abinfo; count_info cinfo; int val, mapped, ret; - unsigned char fmtm, fmtd; - VALIDATE_STATE(s); - mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || - ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + VALIDATE_STATE(state); + mapped = ((file->f_mode & FMODE_WRITE) && state->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && state->dma_adc.mapped); #ifdef DEBUG - printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",_IOC_NR(cmd), - arg ? *(int *)arg : 0); + printk("trident: trident_ioctl, command = %2d, arg = 0x%08x\n", + _IOC_NR(cmd), arg ? *(int *)arg : 0); #endif switch (cmd) @@ -2378,189 +1408,218 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm case SNDCTL_DSP_RESET: if (file->f_mode & FMODE_WRITE) { - stop_dac(s); + stop_dac(state); synchronize_irq(); - s->dma_dac.swptr = s->dma_dac.hwptr = 0; - s->dma_dac.count = s->dma_dac.total_bytes = 0; + state->dma_dac.ready = 0; + state->dma_dac.swptr = state->dma_dac.hwptr = 0; + state->dma_dac.count = state->dma_dac.total_bytes = 0; } if (file->f_mode & FMODE_READ) { - stop_adc(s); + stop_adc(state); synchronize_irq(); - s->dma_adc.swptr = s->dma_adc.hwptr = 0; - s->dma_adc.count = s->dma_adc.total_bytes = 0; + state->dma_adc.ready = 0; + state->dma_adc.swptr = state->dma_adc.hwptr = 0; + state->dma_adc.count = state->dma_adc.total_bytes = 0; } return 0; case SNDCTL_DSP_SYNC: if (file->f_mode & FMODE_WRITE) - return drain_dac(s, file->f_flags & O_NONBLOCK); + return drain_dac(state, file->f_flags & O_NONBLOCK); return 0; - case SNDCTL_DSP_SPEED: + case SNDCTL_DSP_SPEED: /* set smaple rate */ get_user_ret(val, (int *)arg, -EFAULT); if (val >= 0) { - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - trident_set_adc_rate(s, val, 1); - } if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; - trident_set_dac_rate(s, val, 1); + stop_dac(state); + state->dma_dac.ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_dac_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); + } + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; + spin_lock_irqsave(&state->card->lock, flags); + trident_set_adc_rate(state, val); + spin_unlock_irqrestore(&state->card->lock, flags); } } - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, + return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate : + state->dma_dac.rate, (int *)arg); - case SNDCTL_DSP_STEREO: + case SNDCTL_DSP_STEREO: /* set stereo or mono channel */ get_user_ret(val, (int *)arg, -EFAULT); - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO; } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO; } - set_fmt(s, fmtm, fmtd); return 0; case SNDCTL_DSP_GETBLKSIZE: if (file->f_mode & FMODE_WRITE) { - if ((val = prog_dmabuf(s, 0))) + if ((val = prog_dmabuf(state, 0))) return val; - return put_user(s->dma_dac.fragsize, (int *)arg); + return put_user(state->dma_dac.fragsize, (int *)arg); } - if ((val = prog_dmabuf(s, 1))) - return val; - return put_user(s->dma_adc.fragsize, (int *)arg); + if (file->f_mode & FMODE_READ) { + if ((val = prog_dmabuf(state, 1))) + return val; + return put_user(state->dma_adc.fragsize, (int *)arg); + } + + case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/ + return put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg); - case SNDCTL_DSP_GETFMTS: /* Returns a mask */ - return put_user(AFMT_S8|AFMT_S16_LE, (int *)arg); - case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + case SNDCTL_DSP_SETFMT: /* Select sample format */ get_user_ret(val, (int *)arg, -EFAULT); if (val != AFMT_QUERY) { - fmtd = 0; - fmtm = ~0; - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; - /* fixed at 16bit for now */ - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; -#if 0 + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val == AFMT_S16_LE) - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_16BIT; else - fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT); -#endif + state->dma_dac.fmt &= ~TRIDENT_FMT_16BIT; } - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val == AFMT_S16_LE) - fmtd |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_16BIT; else - fmtm &= ~(TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_16BIT; } - set_fmt(s, fmtm, fmtd); } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? - AFMT_S16_LE : AFMT_S8, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); case SNDCTL_DSP_CHANNELS: get_user_ret(val, (int *)arg, -EFAULT); if (val != 0) { - fmtd = 0; - fmtm = ~0; - - if (file->f_mode & FMODE_READ) { - stop_adc(s); - s->dma_adc.ready = 0; + if (file->f_mode & FMODE_WRITE) { + stop_dac(state); + state->dma_dac.ready = 0; if (val >= 2) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_STEREO; } - - if (file->f_mode & FMODE_WRITE) { - stop_dac(s); - s->dma_dac.ready = 0; + if (file->f_mode & FMODE_READ) { + stop_adc(state); + state->dma_adc.ready = 0; if (val >= 2) - fmtd |= TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT; + state->dma_adc.fmt |= TRIDENT_FMT_STEREO; else - fmtm &= ~(TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT); + state->dma_adc.fmt &= ~TRIDENT_FMT_STEREO; } - set_fmt(s, fmtm, fmtd); } - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); case SNDCTL_DSP_POST: + /* FIXME: the same as RESET ?? */ return 0; case SNDCTL_DSP_SUBDIVIDE: - if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || - (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + if ((file->f_mode & FMODE_READ && state->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && state->dma_dac.subdivision)) return -EINVAL; get_user_ret(val, (int *)arg, -EFAULT); if (val != 1 && val != 2 && val != 4) return -EINVAL; if (file->f_mode & FMODE_READ) - s->dma_adc.subdivision = val; + state->dma_adc.subdivision = val; if (file->f_mode & FMODE_WRITE) - s->dma_dac.subdivision = val; + state->dma_dac.subdivision = val; return 0; case SNDCTL_DSP_SETFRAGMENT: get_user_ret(val, (int *)arg, -EFAULT); if (file->f_mode & FMODE_READ) { - s->dma_adc.ossfragshift = val & 0xffff; - s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_adc.ossfragshift < 4) - s->dma_adc.ossfragshift = 4; - if (s->dma_adc.ossfragshift > 15) - s->dma_adc.ossfragshift = 15; - if (s->dma_adc.ossmaxfrags < 4) - s->dma_adc.ossmaxfrags = 4; + state->dma_adc.ossfragshift = val & 0xffff; + state->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (state->dma_adc.ossfragshift < 4) + state->dma_adc.ossfragshift = 4; + if (state->dma_adc.ossfragshift > 15) + state->dma_adc.ossfragshift = 15; + if (state->dma_adc.ossmaxfrags < 4) + state->dma_adc.ossmaxfrags = 4; } if (file->f_mode & FMODE_WRITE) { - s->dma_dac.ossfragshift = val & 0xffff; - s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; - if (s->dma_dac.ossfragshift < 4) - s->dma_dac.ossfragshift = 4; - if (s->dma_dac.ossfragshift > 15) - s->dma_dac.ossfragshift = 15; - if (s->dma_dac.ossmaxfrags < 4) - s->dma_dac.ossmaxfrags = 4; + state->dma_dac.ossfragshift = val & 0xffff; + state->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (state->dma_dac.ossfragshift < 4) + state->dma_dac.ossfragshift = 4; + if (state->dma_dac.ossfragshift > 15) + state->dma_dac.ossfragshift = 15; + if (state->dma_dac.ossmaxfrags < 4) + state->dma_dac.ossmaxfrags = 4; } return 0; - case SNDCTL_DSP_GETCAPS: - return put_user(0/* DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP */, - (int *)arg); - - case SNDCTL_DSP_SETDUPLEX: - /* XXX fix */ + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!state->dma_dac.enable && (val = prog_dmabuf(state, 0)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = state->dma_dac.fragsize; + abinfo.bytes = state->dma_dac.dmasize - state->dma_dac.count; + abinfo.fragstotal = state->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> state->dma_dac.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 (!state->dma_adc.enable && (val = prog_dmabuf(state, 1)) != 0) + return val; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + abinfo.fragsize = state->dma_adc.fragsize; + abinfo.bytes = state->dma_adc.count; + abinfo.fragstotal = state->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> state->dma_adc.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_DUPLEX|*/DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP, + (int *)arg); + case SNDCTL_DSP_GETTRIGGER: val = 0; - if (file->f_mode & FMODE_READ && s->enable & TRIDENT_ENABLE_RE) + if (file->f_mode & FMODE_READ && state->dma_adc.enable) val |= PCM_ENABLE_INPUT; - if (file->f_mode & FMODE_WRITE && s->enable & TRIDENT_ENABLE_PE) + if (file->f_mode & FMODE_WRITE && state->dma_dac.enable) val |= PCM_ENABLE_OUTPUT; return put_user(val, (int *)arg); @@ -2568,106 +1627,86 @@ static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cm get_user_ret(val, (int *)arg, -EFAULT); if (file->f_mode & FMODE_READ) { if (val & PCM_ENABLE_INPUT) { - if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + if (!state->dma_adc.ready && (ret = prog_dmabuf(state, 1))) return ret; - start_adc(s); + start_adc(state); } else - stop_adc(s); + stop_adc(state); } if (file->f_mode & FMODE_WRITE) { if (val & PCM_ENABLE_OUTPUT) { - if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + if (!state->dma_dac.ready && (ret = prog_dmabuf(state, 0))) return ret; - start_dac(s); + start_dac(state); } else - stop_dac(s); + stop_dac(state); } return 0; - case SNDCTL_DSP_GETOSPACE: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - if (!(s->enable & TRIDENT_ENABLE_PE) && (val = prog_dmabuf(s, 0)) != 0) - return val; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - abinfo.fragsize = s->dma_dac.fragsize; - abinfo.bytes = s->dma_dac.dmasize - s->dma_dac.count; - abinfo.fragstotal = s->dma_dac.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; - spin_unlock_irqrestore(&s->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 (!(s->enable & TRIDENT_ENABLE_RE) && (val = prog_dmabuf(s, 1)) != 0) - return val; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - abinfo.fragsize = s->dma_adc.fragsize; - abinfo.bytes = s->dma_adc.count; - abinfo.fragstotal = s->dma_adc.numfrag; - abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; - spin_unlock_irqrestore(&s->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_GETODELAY: - if (!(file->f_mode & FMODE_WRITE)) - return -EINVAL; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - val = s->dma_dac.count; - spin_unlock_irqrestore(&s->card->lock, flags); - return put_user(val, (int *)arg); - case SNDCTL_DSP_GETIPTR: if (!(file->f_mode & FMODE_READ)) return -EINVAL; - spin_lock_irqsave(&s->card->lock, flags); - trident_update_ptr(s); - cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; - cinfo.ptr = s->dma_adc.hwptr; - if (s->dma_adc.mapped) - s->dma_adc.count &= s->dma_adc.fragsize-1; - spin_unlock_irqrestore(&s->card->lock, flags); + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = state->dma_adc.total_bytes; + cinfo.blocks = state->dma_adc.count >> state->dma_adc.fragshift; + cinfo.ptr = state->dma_adc.hwptr; + if (state->dma_adc.mapped) + state->dma_adc.count &= state->dma_adc.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(&s->card->lock, flags); - trident_update_ptr(s); - cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; - cinfo.ptr = s->dma_dac.hwptr; - if (s->dma_dac.mapped) - s->dma_dac.count &= s->dma_dac.fragsize-1; - spin_unlock_irqrestore(&s->card->lock, flags); + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + cinfo.bytes = state->dma_dac.total_bytes; + cinfo.blocks = state->dma_dac.count >> state->dma_dac.fragshift; + cinfo.ptr = state->dma_dac.hwptr; + if (state->dma_dac.mapped) + state->dma_dac.count &= state->dma_dac.fragsize-1; + spin_unlock_irqrestore(&state->card->lock, flags); return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&state->card->lock, flags); + trident_update_ptr(state); + val = state->dma_dac.count; + spin_unlock_irqrestore(&state->card->lock, flags); + return put_user(val, (int *)arg); + case SOUND_PCM_READ_RATE: - return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + return put_user((file->f_mode & FMODE_READ) ? state->dma_adc.rate : + state->dma_dac.rate, (int *)arg); case SOUND_PCM_READ_CHANNELS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_STEREO << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_STEREO << TRIDENT_DAC_SHIFT))) ? 2 : 1, (int *)arg); + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_STEREO) ? 2 : 1, + (int *)arg); case SOUND_PCM_READ_BITS: - return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? - (TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT) : - (TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT))) ? 16 : 8, (int *)arg); - - case SOUND_PCM_WRITE_FILTER: + if (file->f_mode & FMODE_WRITE) + return put_user((state->dma_dac.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (int *)arg); + else + return put_user((state->dma_adc.fmt & TRIDENT_FMT_16BIT) ? + AFMT_S16_LE : AFMT_U8, (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; @@ -2681,13 +1720,12 @@ static int trident_open(struct inode *inode, struct file *file) int minor = MINOR(inode->i_rdev); struct trident_card *card = devs; struct trident_state *state = NULL; - unsigned char fmtm = ~0, fmts = 0; /* find an avaiable virtual channel (instance of /dev/dsp) */ while (card != NULL) { - for (i = 0; i < NR_DSPS; i++) { - if (card->channels[i] == NULL) { - state = card->channels[i] = (struct trident_state *) + for (i = 0; i < NR_HW_CH; i++) { + if (card->states[i] == NULL) { + state = card->states[i] = (struct trident_state *) kmalloc(sizeof(struct trident_state), GFP_KERNEL); if (state == NULL) return -ENOMEM; @@ -2704,18 +1742,18 @@ static int trident_open(struct inode *inode, struct file *file) found_virt: /* found a free virtual channel, allocate hardware channels */ if (file->f_mode & FMODE_READ) - if ((state->dma_adc.chan = trident_alloc_pcm_channel(card)) == -1) { - kfree (card->channels[i]); - card->channels[i] = NULL;; + if ((state->dma_adc.channel = trident_alloc_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL;; return -ENODEV; } if (file->f_mode & FMODE_WRITE) - if ((state->dma_dac.chan = trident_alloc_pcm_channel(card)) == -1) { - kfree (card->channels[i]); - card->channels[i] = NULL; + if ((state->dma_dac.channel = trident_alloc_pcm_channel(card)) == NULL) { + kfree (card->states[i]); + card->states[i] = NULL; if (file->f_mode & FMODE_READ) /* free previously allocated hardware channel */ - trident_free_pcm_channel(card, state->dma_adc.chan); + trident_free_pcm_channel(card, state->dma_adc.channel->num); return -ENODEV; } @@ -2732,30 +1770,27 @@ static int trident_open(struct inode *inode, struct file *file) /* set default sample format, Refer to OSS Programmer's Guide */ if (file->f_mode & FMODE_READ) { - /* fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT); - if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= TRIDENT_FMT_16BIT << TRIDENT_ADC_SHIFT; */ - fmtm = (TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT) << TRIDENT_ADC_SHIFT; + /* FIXME: Trident 4d can only record in singed 16-bits stereo, 48kHz sample */ + state->dma_adc.fmt = TRIDENT_FMT_STEREO|TRIDENT_FMT_16BIT; state->dma_adc.ossfragshift = 0; state->dma_adc.ossmaxfrags = 0; state->dma_adc.subdivision = 0; - trident_set_adc_rate(state, 8000, 0); + trident_set_adc_rate(state, 48000); } /* according to OSS document, /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) { - fmtm &= ~((TRIDENT_FMT_STEREO | TRIDENT_FMT_16BIT) << TRIDENT_DAC_SHIFT); + state->dma_dac.fmt &= ~TRIDENT_FMT_MASK; if ((minor & 0xf) == SND_DEV_DSP16) - fmts |= TRIDENT_FMT_16BIT << TRIDENT_DAC_SHIFT; + state->dma_dac.fmt |= TRIDENT_FMT_16BIT; state->dma_dac.ossfragshift = 0; state->dma_dac.ossmaxfrags = 0; state->dma_dac.subdivision = 0; - trident_set_dac_rate(state, 8000, 1); + trident_set_dac_rate(state, 8000); } - set_fmt(state, fmtm, fmts); - state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); up(&state->open_sem); //FIXME put back in @@ -2768,8 +1803,12 @@ static int trident_release(struct inode *inode, struct file *file) struct trident_state *state = (struct trident_state *)file->private_data; VALIDATE_STATE(state); - if (file->f_mode & FMODE_WRITE) + + + if (file->f_mode & FMODE_WRITE) { + trident_clear_tail(state); drain_dac(state, file->f_flags & O_NONBLOCK); + } /* stop DMA state machine and free DMA buffers/channels */ down(&state->open_sem); @@ -2777,16 +1816,16 @@ static int trident_release(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) { stop_dac(state); dealloc_dmabuf(&state->dma_dac); - trident_free_pcm_channel(state->card, state->dma_dac.chan); + trident_free_pcm_channel(state->card, state->dma_dac.channel->num); } if (file->f_mode & FMODE_READ) { stop_adc(state); dealloc_dmabuf(&state->dma_adc); - trident_free_pcm_channel(state->card, state->dma_adc.chan); + trident_free_pcm_channel(state->card, state->dma_adc.channel->num); } - kfree(state->card->channels[state->virt]); - state->card->channels[state->virt] = NULL; + 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 */ @@ -2796,6 +1835,7 @@ static int trident_release(struct inode *inode, struct file *file) //MOD_DEC_USE_COUNT; return 0; } + static /*const*/ struct file_operations trident_audio_fops = { &trident_llseek, &trident_read, @@ -2803,7 +1843,7 @@ static /*const*/ struct file_operations trident_audio_fops = { NULL, /* readdir */ &trident_poll, &trident_ioctl, - NULL, /* XXX &trident_mmap, */ + &trident_mmap, &trident_open, NULL, /* flush */ &trident_release, @@ -2812,15 +1852,238 @@ static /*const*/ struct file_operations trident_audio_fops = { NULL, /* lock */ }; -/* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered - untill open time */ -static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info) +/* trident specific AC97 functions */ +/* Write AC97 mixer registers */ +static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + data = ((u32) val) << 16; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_WRITE; + mask = SI_AC97_BUSY_WRITE | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR0_AC97_W; + mask = busy = DX_AC97_BUSY_WRITE; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + address = NX_ACR1_AC97_W; + mask = NX_AC97_BUSY_WRITE; + if (codec->id) + mask |= NX_AC97_WRITE_SECONDARY; + busy = NX_AC97_BUSY_WRITE; + break; + } + + spin_lock_irqsave(&card->lock, flags); + do { + if ((inw(TRID_REG(card, address)) & busy) == 0) + break; + } while (count--); + + + data |= (mask | (reg & AC97_REG_ADDR)); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC write timed out.\n"); + spin_unlock_irqrestore(&card->lock, flags); + return; + } + + outl(data, TRID_REG(card, address)); + spin_unlock_irqrestore(&card->lock, flags); +} + +/* Read AC97 codec registers */ +static u16 trident_ac97_get(struct ac97_codec *codec, u8 reg) +{ + struct trident_card *card = (struct trident_card *)codec->private_data; + unsigned int address, mask, busy; + unsigned short count = 0xffff; + unsigned long flags; + u32 data; + + switch (card->pci_id) + { + default: + case PCI_DEVICE_ID_SI_7018: + address = SI_AC97_READ; + mask = SI_AC97_BUSY_READ | SI_AC97_AUDIO_BUSY; + if (codec->id) + mask |= SI_AC97_SECONDARY; + busy = SI_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_DX: + address = DX_ACR1_AC97_R; + mask = busy = DX_AC97_BUSY_READ; + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + if (codec->id) + address = NX_ACR3_AC97_R_SECONDARY; + else + address = NX_ACR2_AC97_R_PRIMARY; + mask = NX_AC97_BUSY_READ; + busy = 0x0c00; + break; + } + + data = (mask | (reg & AC97_REG_ADDR)); + + spin_lock_irqsave(&card->lock, flags); + outl(data, TRID_REG(card, address)); + do { + data = inl(TRID_REG(card, address)); + if ((data & busy) == 0) + break; + } while (count--); + spin_unlock_irqrestore(&card->lock, flags); + + if (count == 0) { + printk(KERN_ERR "trident: AC97 CODEC read timed out.\n"); + data = 0; + } + return ((u16) (data >> 16)); +} + +/* OSS /dev/mixer file operation methods */ +static int trident_open_mixdev(struct inode *inode, struct file *file) { int i; + int minor = MINOR(inode->i_rdev); + struct trident_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]; + + //FIXME put back in + //MOD_INC_USE_COUNT; + return 0; +} + +static int trident_release_mixdev(struct inode *inode, struct file *file) +{ + //struct ac97_codec *codec = (struct ac97_codec *)file->private_data; + + //FIXME put back in + //MOD_DEC_USE_COUNT; + return 0; +} + +static int trident_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 trident_mixer_fops = { + &trident_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &trident_ioctl_mixdev, + NULL, /* mmap */ + &trident_open_mixdev, + NULL, /* flush */ + &trident_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* lock */ +}; + +/* AC97 codec initialisation. */ +static int __init trident_ac97_init(struct trident_card *card) +{ + int num_ac97 = 0; + int ready_2nd = 0; + struct ac97_codec *codec; + + /* initialize controller side of AC link, and find out if secondary codes + really exist */ + switch (card->pci_id) + { + case PCI_DEVICE_ID_SI_7018: + /* disable AC97 GPIO interrupt */ + outl(0x00, TRID_REG(card, SI_AC97_GPIO)); + /* stop AC97 cold reset process */ + 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_TRIDENT_4DWAVE_DX: + /* playback on */ + outl(DX_AC97_PLAYBACK, TRID_REG(card, DX_ACR2_AC97_COM_STAT)); + break; + case PCI_DEVICE_ID_TRIDENT_4DWAVE_NX: + /* enable AC97 Output Slot 3,4 (PCM Left/Right Playback) */ + outl(NX_AC97_PCM_OUTPUT, TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd = inl(TRID_REG(card, NX_ACR0_AC97_COM_STAT)); + ready_2nd &= NX_AC97_SECONDARY_READY; + break; + } + + for (num_ac97 = 0; num_ac97 < NR_AC97; num_ac97++) { + if ((codec = kmalloc(sizeof(struct ac97_codec), GFP_KERNEL)) == NULL) + return -1; + 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; + /* controller specific low level AC97 access function */ + codec->codec_read = trident_ac97_get; + codec->codec_write = trident_ac97_set; + + if (ac97_probe_codec(codec) == 0) + break; + + if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { + printk(KERN_ERR "trident: couldn't register mixer!\n"); + kfree(codec); + break; + } + + 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 calles by open/read/write/ioctl/mmap) */ +static int __init trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_info) +{ u16 w; unsigned long iobase; struct trident_card *card; - u32 ChanDwordCount; iobase = pcidev->resource[0].start; if (check_region(iobase, 256)) { @@ -2847,30 +2110,12 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in card->irq = pcidev->irq; card->next = devs; card->magic = TRIDENT_CARD_MAGIC; + card->banks[BANK_A].addresses = &bank_a_addrs; + card->banks[BANK_A].bitmap = 0UL; + card->banks[BANK_B].addresses = &bank_b_addrs; + card->banks[BANK_B].bitmap = 0UL; spin_lock_init(&card->lock); - devs = card; - - /* ungly stupid thing, remove ASAP */ - ChanDwordCount = card->ChanDwordCount = 2; - card->ChRegs.lpChStart = card->ChRegs.data; - card->ChRegs.lpChStop = card->ChRegs.lpChStart + ChanDwordCount; - card->ChRegs.lpChAint = card->ChRegs.lpChStop + ChanDwordCount; - card->ChRegs.lpChAinten = card->ChRegs.lpChAint + ChanDwordCount; - card->ChRegs.lpAChStart = card->ChRegs.lpChAinten + ChanDwordCount; - card->ChRegs.lpAChStop = card->ChRegs.lpAChStart + ChanDwordCount; - card->ChRegs.lpAChAint = card->ChRegs.lpAChStop + ChanDwordCount; - card->ChRegs.lpAChAinten = card->ChRegs.lpAChAint + ChanDwordCount; - // Assign Bank A addresses. - card->ChRegs.lpAChStart[0] = T4D_START_A; - card->ChRegs.lpAChStop[0] = T4D_STOP_A; - card->ChRegs.lpAChAint[0] = T4D_AINT_A; - card->ChRegs.lpAChAinten[0] = T4D_AINTEN_A; - /* Assign Bank B addresses */ - card->ChRegs.lpAChStart[1] = T4D_START_B; - card->ChRegs.lpAChStop[1] = T4D_STOP_B; - card->ChRegs.lpAChAint[1] = T4D_AINT_B; - card->ChRegs.lpAChAinten[1] = T4D_AINTEN_B; - + devs = card; printk(KERN_INFO "trident: %s found at IO 0x%04lx, IRQ %d\n", card->pci_info->name, card->iobase, card->irq); @@ -2883,11 +2128,6 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in kfree(card); return 0; } - - /* initilize AC97 codec */ - trident_ac97_init(card); - outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); - /* 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"); @@ -2896,29 +2136,18 @@ static int trident_install(struct pci_dev *pcidev, struct pci_audio_info *pci_in kfree(card); return 0; } - /* register /dev/mixer */ - if ((card->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) { - printk(KERN_ERR "trident: couldn't register mixer!\n"); + /* initilize AC97 codec and register /dev/mixer */ + if (trident_ac97_init(card) <= 0) { unregister_sound_dsp(card->dev_audio); release_region(iobase, 256); free_irq(card->irq, card); kfree(card); return 0; - } else { - /* initilize mixer channels */ - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - struct mixer_defaults *md = &mixer_defaults[i]; - if (md->mixer == -1) - break; - if (!supported_mixer(card, md->mixer)) - continue; - set_mixer(card, md->mixer, md->value); - } } + outl(0x00, TRID_REG(card, T4D_MUSICVOL_WAVEVOL)); /* Enable Address Engine Interrupts */ - trident_enable_end_interrupts(card); - trident_enable_middle_interrupts(card); + trident_enable_loop_interrupts(card); return 1; } @@ -2952,23 +2181,23 @@ static int __init init_trident(void) MODULE_AUTHOR("Alan Cox, Aaron Holtzman, Ollie Lho"); MODULE_DESCRIPTION("Trident 4DWave/SiS 7018 PCI Audio Driver"); -#ifdef DEBUG -MODULE_PARM(debug,"i"); -#endif - static void __exit cleanup_trident(void) { while (devs != NULL) { + int i; /* Kill interrupts, and SP/DIF */ - trident_disable_end_interrupts(devs); - trident_enable_middle_interrupts(devs); + trident_disable_loop_interrupts(devs); /* free hardware resources */ free_irq(devs->irq, devs); release_region(devs->iobase, 256); /* unregister audio devices */ - unregister_sound_mixer(devs->dev_mixer); + for (i = 0; i < NR_AC97; i++) + if (devs->ac97_codec[i] != NULL) { + unregister_sound_mixer(devs->ac97_codec[i]->dev_mixer); + kfree (devs->ac97_codec[i]); + } unregister_sound_dsp(devs->dev_audio); kfree(devs); @@ -2978,5 +2207,3 @@ static void __exit cleanup_trident(void) module_init(init_trident); module_exit(cleanup_trident); - - diff --git a/drivers/sound/trident.h b/drivers/sound/trident.h index a22e60650d47..4504cb30390a 100644 --- a/drivers/sound/trident.h +++ b/drivers/sound/trident.h @@ -23,6 +23,7 @@ * */ +/* PCI vendor and device ID */ #ifndef PCI_VENDOR_ID_TRIDENT #define PCI_VENDOR_ID_TRIDENT 0x1023 #endif @@ -43,118 +44,101 @@ #define PCI_DEVICE_ID_SI_7018 0x7018 #endif -/* - * Direct registers - */ - #ifndef FALSE -#define FALSE 0 -#define TRUE 1 +#define FALSE 0 +#define TRUE 1 #endif -#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) - #define CHANNEL_REGS 5 #define CHANNEL_START 0xe0 // The first bytes of the contiguous register space. -#define BANK_A 0 -#define BANK_B 1 -#define NUM_BANKS 2 - -#define ID_4DWAVE_DX 0x2000 -#define ID_4DWAVE_NX 0x2001 -#define ID_SI_7018 0x7018 - -// Register definitions - -// Global registers - -// T2 legacy dma control registers. -#define LEGACY_DMAR0 0x00 // ADR0 -#define LEGACY_DMAR4 0x04 // CNT0 -#define LEGACY_DMAR11 0x0b // MOD -#define LEGACY_DMAR15 0x0f // MMR - -#define T4D_START_A 0x80 -#define T4D_STOP_A 0x84 -#define T4D_DLY_A 0x88 -#define T4D_SIGN_CSO_A 0x8c -#define T4D_CSPF_A 0x90 -#define T4D_CEBC_A 0x94 -#define T4D_AINT_A 0x98 -#define T4D_EINT_A 0x9c -#define T4D_LFO_GC_CIR 0xa0 -#define T4D_AINTEN_A 0xa4 -#define T4D_MUSICVOL_WAVEVOL 0xa8 -#define T4D_SBDELTA_DELTA_R 0xac -#define T4D_MISCINT 0xb0 -#define T4D_START_B 0xb4 -#define T4D_STOP_B 0xb8 -#define T4D_CSPF_B 0xbc -#define T4D_SBBL_SBCL 0xc0 -#define T4D_SBCTRL_SBE2R_SBDD 0xc4 -#define T4D_STIMER 0xc8 -#define T4D_LFO_B_I2S_DELTA 0xcc -#define T4D_AINT_B 0xd8 -#define T4D_AINTEN_B 0xdc - -// MPU-401 UART -#define T4D_MPU401_BASE 0x20 -#define T4D_MPUR0 0x20 -#define T4D_MPUR1 0x21 -#define T4D_MPUR2 0x22 -#define T4D_MPUR3 0x23 - -// S/PDIF Registers -#define NX_SPCTRL_SPCSO 0x24 -#define NX_SPLBA 0x28 -#define NX_SPESO 0x2c -#define NX_SPCSTATUS 0x64 - -// Channel Registers - -#define CH_DX_CSO_ALPHA_FMS 0xe0 -#define CH_DX_ESO_DELTA 0xe8 -#define CH_DX_FMC_RVOL_CVOL 0xec - -#define CH_NX_DELTA_CSO 0xe0 -#define CH_NX_DELTA_ESO 0xe8 -#define CH_NX_ALPHA_FMS_FMC_RVOL_CVOL 0xec - -#define CH_LBA 0xe4 -#define CH_GVSEL_PAN_VOL_CTRL_EC 0xf0 - -// AC-97 Registers - -#define DX_ACR0_AC97_W 0x40 -#define DX_ACR1_AC97_R 0x44 -#define DX_ACR2_AC97_COM_STAT 0x48 - -#define NX_ACR0_AC97_COM_STAT 0x40 -#define NX_ACR1_AC97_W 0x44 -#define NX_ACR2_AC97_R_PRIMARY 0x48 -#define NX_ACR3_AC97_R_SECONDARY 0x4c - -#define SI_AC97_WRITE 0x40 -#define SI_AC97_READ 0x44 -#define SI_SERIAL_INTF_CTRL 0x48 -#define SI_AC97_GPIO 0x4c +#define BANK_A 0 +#define BANK_B 1 +#define NR_BANKS 2 + +#define TRIDENT_FMT_STEREO 0x01 +#define TRIDENT_FMT_16BIT 0x02 +#define TRIDENT_FMT_MASK 0x03 + +#define DMA_ENABLE 0x01 +#define DMA_RUNNING 0x02 + +/* Register Addresses */ + +/* operational registers common to DX, NX, 7018 */ +enum trident_op_registers { + T4D_START_A = 0x80, T4D_STOP_A = 0x84, + T4D_DLY_A = 0x88, T4D_SIGN_CSO_A = 0x8c, + T4D_CSPF_A = 0x90, T4D_CEBC_A = 0x94, + T4D_AINT_A = 0x98, T4D_EINT_A = 0x9c, + T4D_LFO_GC_CIR = 0xa0, T4D_AINTEN_A = 0xa4, + T4D_MUSICVOL_WAVEVOL = 0xa8, T4D_SBDELTA_DELTA_R = 0xac, + T4D_MISCINT = 0xb0, T4D_START_B = 0xb4, + T4D_STOP_B = 0xb8, T4D_CSPF_B = 0xbc, + T4D_SBBL_SBCL = 0xc0, T4D_SBCTRL_SBE2R_SBDD = 0xc4, + T4D_STIMER = 0xc8, T4D_LFO_B_I2S_DELTA = 0xcc, + T4D_AINT_B = 0xd8, T4D_AINTEN_B = 0xdc +}; -#define AC97_SIGMATEL_DAC2INVERT 0x6E -#define AC97_SIGMATEL_BIAS1 0x70 -#define AC97_SIGMATEL_BIAS2 0x72 -#define AC97_SIGMATEL_CIC1 0x76 -#define AC97_SIGMATEL_CIC2 0x78 +/* S/PDIF Operational Registers for 4D-NX */ +enum nx_spdif_registers { + NX_SPCTRL_SPCSO = 0x24, NX_SPLBA = 0x28, + NX_SPESO = 0x2c, NX_SPCSTATUS = 0x64 +}; + +/* OP registers to access each hardware channel */ +enum channel_registers { + CH_DX_CSO_ALPHA_FMS = 0xe0, CH_DX_ESO_DELTA = 0xe8, + CH_DX_FMC_RVOL_CVOL = 0xec, + CH_NX_DELTA_CSO = 0xe0, CH_NX_DELTA_ESO = 0xe8, + CH_NX_ALPHA_FMS_FMC_RVOL_CVOL = 0xec, + CH_LBA = 0xe4, + CH_GVSEL_PAN_VOL_CTRL_EC = 0xf0 +}; + +/* registers to read/write/control AC97 codec */ +enum dx_ac97_registers { + DX_ACR0_AC97_W = 0x40, DX_ACR1_AC97_R = 0x44, + DX_ACR2_AC97_COM_STAT = 0x48 +}; -#define SI_AC97_BUSY_WRITE 0x8000 -#define SI_AC97_AUDIO_BUSY 0x4000 -#define DX_AC97_BUSY_WRITE 0x8000 -#define NX_AC97_BUSY_WRITE 0x0800 -#define SI_AC97_BUSY_READ 0x8000 -#define DX_AC97_BUSY_READ 0x8000 -#define NX_AC97_BUSY_READ 0x0800 +enum nx_ac97_registers { + NX_ACR0_AC97_COM_STAT = 0x40, NX_ACR1_AC97_W = 0x44, + NX_ACR2_AC97_R_PRIMARY = 0x48, NX_ACR3_AC97_R_SECONDARY = 0x4c +}; + +enum si_ac97_registers { + SI_AC97_WRITE = 0x40, SI_AC97_READ = 0x44, + SI_SERIAL_INTF_CTRL = 0x48, SI_AC97_GPIO = 0x4c +}; + +/* Bit mask for operational registers */ #define AC97_REG_ADDR 0x000000ff +enum sis7018_ac97_bits { + SI_AC97_BUSY_WRITE = 0x8000, SI_AC97_BUSY_READ = 0x8000, + SI_AC97_AUDIO_BUSY = 0x4000, SI_AC97_MODEM_BUSY = 0x2000, + SI_AC97_SECONDARY = 0x0080 +}; + +enum trident_dx_ac97_bits { + DX_AC97_BUSY_WRITE = 0x8000, DX_AC97_BUSY_READ = 0x8000, + DX_AC97_READY = 0x0010, DX_AC97_RECORD = 0x0008, + DX_AC97_PLAYBACK = 0x0002 +}; + +enum trident_nx_ac97_bits { + /* ACR1-3 */ + NX_AC97_BUSY_WRITE = 0x0800, NX_AC97_BUSY_READ = 0x0800, + NX_AC97_WRITE_SECONDARY = 0x0100, + /* ACR0 */ + NX_AC97_SECONDARY_READY = 0x0040, NX_AC97_SECONDARY_RECORD = 0x0020, + NX_AC97_SURROUND_OUTPUT = 0x0010, + NX_AC97_PRIMARY_READY = 0x0008, NX_AC97_PRIMARY_RECORD = 0x0004, + NX_AC97_PCM_OUTPUT = 0x0002, + NX_AC97_WARM_RESET = 0x0001 +}; + enum serial_intf_ctrl_bits { WARM_REST = 0x00000001, COLD_RESET = 0x00000002, I2S_CLOCK = 0x00000004, PCM_SEC_AC97= 0x00000008, @@ -162,6 +146,16 @@ enum serial_intf_ctrl_bits { I2S_OUTPUT_EN = 0x00000040, I2S_INPUT_EN = 0x00000080, PCMIN = 0x00000100, LINE1IN = 0x00000200, MICIN = 0x00000400, LINE2IN = 0x00000800, + HEAD_SET_IN = 0x00001000, GPIOIN = 0x00002000, + /* 7018 spec says id = 01 but the demo board routed to 10 + SECONDARY_ID= 0x00008000, */ + SECONDARY_ID= 0x00004000, + PCMOUT = 0x00010000, SURROUT = 0x00020000, + CENTEROUT = 0x00040000, LFEOUT = 0x00080000, + LINE1OUT = 0x00100000, LINE2OUT = 0x00200000, + GPIOOUT = 0x00400000, + SI_AC97_PRIMARY_READY = 0x01000000, + SI_AC97_SECONDARY_READY = 0x02000000, }; enum global_control_bits { @@ -173,6 +167,18 @@ enum global_control_bits { EDROP_IE = 0x00008000, BANK_B_EN = 0x00010000 }; +enum channel_control_bits { + CHANNEL_LOOP = 0x00001000, CHANNEL_SIGNED = 0x00002000, + CHANNEL_STEREO = 0x00004000, CHANNEL_16BITS = 0x00008000, +}; + +enum channel_attribute { + MODEM_LINE1, MODEM_LINE2, PCM_LR, HSET, + I2SLR, CENTER_LFE, SURR_LR, SPDIF_LR, + CHANNEL_PB = 0x00000000, CHANNEL_SPC_PB = 0x40000000, + CHANNEL_REC = 0x80000000, CHANNEL_REC_PB = 0xc0000000 +}; + enum miscint_bits { PB_UNDERRUN_IRO = 0x00000001, REC_OVERRUN_IRQ = 0x00000002, SB_IRQ = 0x00000004, MPU401_IRQ = 0x00000008, @@ -184,31 +190,13 @@ enum miscint_bits { ST_IRQ_EN = 0x00800000, ACGPIO_IRQ = 0x01000000 }; -#define IWriteAinten( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - outl((x)->lpChAinten[i], TRID_REG(trident, (x)->lpAChAinten[i]));} - -#define IReadAinten( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - (x)->lpChAinten[i] = inl(TRID_REG(trident, (x)->lpAChAinten[i]));} - -#define ReadAint( x ) \ - IReadAint( x ) - -#define WriteAint( x ) \ - IWriteAint( x ) - -#define IWriteAint( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - outl((x)->lpChAint[i], TRID_REG(trident, (x)->lpAChAint[i]));} +#define AC97_SIGMATEL_DAC2INVERT 0x6E +#define AC97_SIGMATEL_BIAS1 0x70 +#define AC97_SIGMATEL_BIAS2 0x72 +#define AC97_SIGMATEL_CIC1 0x76 +#define AC97_SIGMATEL_CIC2 0x78 -#define IReadAint( x ) \ - {int i; \ - for( i= 0; i < ChanDwordCount; i++) \ - (x)->lpChAint[i] = inl(TRID_REG(trident, (x)->lpAChAint[i]));} +#define TRID_REG( trident, x ) ( (trident) -> iobase + (x) ) #define VALIDATE_MAGIC(FOO,MAG) \ ({ \ @@ -221,5 +209,31 @@ enum miscint_bits { #define VALIDATE_STATE(a) VALIDATE_MAGIC(a,TRIDENT_STATE_MAGIC) #define VALIDATE_CARD(a) VALIDATE_MAGIC(a,TRIDENT_CARD_MAGIC) + +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; +} + #endif /* __TRID4DWAVE_H */ diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index aa8c6adb242e..bb812ea90cb8 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -9,6 +9,7 @@ if [ ! "$CONFIG_USB" = "n" ]; then comment 'USB Controllers' dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB comment 'Miscellaneous USB options' diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index ee2cee37aa80..c43a41a31bfb 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -45,6 +45,7 @@ obj- := obj-$(CONFIG_USB) += usbcore.o obj-$(CONFIG_USB_UHCI) += usb-uhci.o +obj-$(CONFIG_USB_UHCI_ALT) += uhci.o obj-$(CONFIG_USB_OHCI) += usb-ohci.o obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o @@ -107,3 +108,4 @@ usbcore.o: $(usbcore-objs) usb-storage.o: $(usb-storage-objs) $(LD) -r -o $@ $(usb-storage-objs) + diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c index 154d5cd221ba..1459f1d103fd 100644 --- a/drivers/usb/acm.c +++ b/drivers/usb/acm.c @@ -1,5 +1,5 @@ /* - * acm.c Version 0.14 + * acm.c Version 0.15 * * Copyright (c) 1999 Armin Fuerst * Copyright (c) 1999 Pavel Machek @@ -17,6 +17,7 @@ * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced * v0.13 - added termios, added hangup * v0.14 - sized down struct acm + * v0.15 - fixed flow control again - characters could be lost */ /* @@ -137,6 +138,7 @@ struct acm { unsigned int writesize; /* max packet size for the output bulk endpoint */ unsigned int used; /* someone has this acm's device open */ unsigned int minor; /* acm minor number */ + unsigned char throttle; /* throttled by tty layer */ unsigned char clocal; /* termios CLOCAL */ }; @@ -210,8 +212,6 @@ static void acm_ctrl_irq(struct urb *urb) dr->request, dr->index, dr->length, data[0], data[1]); return; } - - return; } static void acm_read_bulk(struct urb *urb) @@ -219,24 +219,25 @@ static void acm_read_bulk(struct urb *urb) struct acm *acm = urb->context; struct tty_struct *tty = acm->tty; unsigned char *data = urb->transfer_buffer; - int i; + int i = 0; if (!ACM_READY(acm)) return; - if (!urb->status) { - - for (i = 0; i < urb->actual_length; i++) + if (!urb->status & !acm->throttle) { + for (i = 0; i < urb->actual_length && !acm->throttle; i++) tty_insert_flip_char(tty, data[i], 0); - tty_flip_buffer_push(tty); - } else dbg("nonzero read bulk status received: %d", urb->status); - if (usb_submit_urb(urb)) - dbg("failed resubmitting read urb"); - - return; + if (!acm->throttle) { + urb->actual_length = 0; + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); + } else { + memmove(data, data + i, urb->actual_length - i); + urb->actual_length -= i; + } } static void acm_write_bulk(struct urb *urb) @@ -253,8 +254,6 @@ static void acm_write_bulk(struct urb *urb) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); - - return; } /* @@ -347,15 +346,16 @@ static void acm_tty_throttle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; - usb_unlink_urb(&acm->readurb); + acm->throttle = 1; } static void acm_tty_unthrottle(struct tty_struct *tty) { struct acm *acm = tty->driver_data; if (!ACM_READY(acm)) return; - if (usb_submit_urb(&acm->readurb)) - dbg("usb_submit_urb(read bulk) in unthrottle() failed"); + acm->throttle = 0; + if (acm->readurb.status != -EINPROGRESS) + acm_read_bulk(&acm->readurb); } static void acm_tty_break_ctl(struct tty_struct *tty, int state) diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c index 6ec72d5023a0..6dacc46b4075 100644 --- a/drivers/usb/ov511.c +++ b/drivers/usb/ov511.c @@ -931,7 +931,7 @@ static int ov511_init_isoc(struct usb_ov511 *ov511) err("ov511_init_isoc: usb_run_isoc(0) ret %d", err); err = usb_submit_urb(ov511->sbuf[1].urb); if (err) - err("ov511_init_isoc: usb_run_isoc(1) ret %d\n", err); + err("ov511_init_isoc: usb_run_isoc(1) ret %d", err); ov511->streaming = 1; diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c index 370ed6dd643a..a00a9f0379c1 100644 --- a/drivers/usb/printer.c +++ b/drivers/usb/printer.c @@ -125,15 +125,15 @@ static int usblp_check_status(struct usblp *usblp) if (status & LP_PERRORP) { if (status & LP_POUTPA) { - printk(KERN_INFO "usblp%d: out of paper", usblp->minor); + info("usblp%d: out of paper", usblp->minor); return -ENOSPC; } if (~status & LP_PSELECD) { - printk(KERN_INFO "usblp%d: off-line", usblp->minor); + info("usblp%d: off-line", usblp->minor); return -EIO; } if (~status & LP_PERRORP) { - printk(KERN_INFO "usblp%d: on fire", usblp->minor); + info("usblp%d: on fire", usblp->minor); return -EIO; } } @@ -229,18 +229,19 @@ static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, if (usblp->writeurb.status == -EINPROGRESS) { usb_unlink_urb(&usblp->writeurb); - printk(KERN_ERR "usblp%d: timed out\n", usblp->minor); + err("usblp%d: timed out", usblp->minor); return -EIO; } if (!usblp->dev) return -ENODEV; - if (!usblp->writeurb.status) + if (!usblp->writeurb.status) { writecount += usblp->writeurb.transfer_buffer_length; - else { + usblp->writeurb.transfer_buffer_length = 0; + } else { if (!(retval = usblp_check_status(usblp))) { - printk(KERN_ERR "usblp%d: error %d writing to printer\n", + err("usblp%d: error %d writing to printer", usblp->minor, usblp->writeurb.status); return -EIO; } @@ -287,7 +288,7 @@ static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t return -ENODEV; if (usblp->readurb.status) { - printk(KERN_ERR "usblp%d: error %d reading from printer\n", + err("usblp%d: error %d reading from printer", usblp->minor, usblp->readurb.status); usb_submit_urb(&usblp->readurb); return -EIO; @@ -389,7 +390,7 @@ static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); } - printk(KERN_INFO "usblp%d: USB %sdirectional printer dev %d if %d alt %d\n", + info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); return usblp_table[minor] = usblp; diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c index ec45a6fb9751..e707d23e88fa 100644 --- a/drivers/usb/scanner.c +++ b/drivers/usb/scanner.c @@ -331,7 +331,6 @@ probe_scanner(struct usb_device *dev, unsigned int ifnum) int ep_cnt; - char *ident; char valid_device = 0; char have_bulk_in, have_bulk_out, have_intr; diff --git a/drivers/usb/uhci-debug.h b/drivers/usb/uhci-debug.h index 73d16937a2d2..9d715d4d8d50 100644 --- a/drivers/usb/uhci-debug.h +++ b/drivers/usb/uhci-debug.h @@ -1,195 +1,260 @@ -#ifdef DEBUG +/* + * UHCI-specific debugging code. Invaluable when something + * goes wrong, but don't get in my face. + * + * Kernel visible pointers are surrounded in []'s and bus + * visible pointers are surrounded in ()'s + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + */ -static void uhci_show_qh (puhci_desc_t qh) +#include +#include + +#include "uhci.h" + +void uhci_show_td(struct uhci_td * td) { - if (qh->type != QH_TYPE) { - dbg("qh has not QH_TYPE"); - return; - } - dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh)); + char *spid; - if (qh->hw.qh.head & UHCI_PTR_TERM) - dbg("Head Terminate"); - else { - if (qh->hw.qh.head & UHCI_PTR_QH) - dbg("Head points to QH"); - else - dbg("Head points to TD"); + printk("%08x ", td->link); + printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", + ((td->status >> 27) & 3), + (td->status & TD_CTRL_SPD) ? "SPD " : "", + (td->status & TD_CTRL_LS) ? "LS " : "", + (td->status & TD_CTRL_IOC) ? "IOC " : "", + (td->status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->status & TD_CTRL_NAK) ? "NAK " : "", + (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", + td->status & 0x7ff); - dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS); - } - if (qh->hw.qh.element & UHCI_PTR_TERM) - dbg("Element Terminate"); - else { - - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Element points to QH"); - else - dbg("Element points to TD"); - dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS); + switch (td->info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = "OUT"; + break; + case USB_PID_IN: + spid = "IN"; + break; + default: + spid = "?"; + break; } + + printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", + td->info >> 21, + ((td->info >> 19) & 1), + (td->info >> 15) & 15, + (td->info >> 8) & 127, + (td->info & 0xff), + spid); + printk("(buf=%08x)\n", td->buffer); } -#endif -static void uhci_show_td (puhci_desc_t td) +static void uhci_show_sc(int port, unsigned short status) { - char *spid; - warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); - - switch (td->hw.td.info & 0xff) { - case USB_PID_SETUP: - spid = "SETUP"; - break; - case USB_PID_OUT: - spid = " OUT "; - break; - case USB_PID_IN: - spid = " IN "; - break; - default: - spid = " ? "; - break; - } + printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} - warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)", - td->hw.td.info >> 21, - ((td->hw.td.info >> 19) & 1), - (td->hw.td.info >> 15) & 15, - (td->hw.td.info >> 8) & 127, - (td->hw.td.info & 0xff), - spid, - td->hw.td.buffer); - - warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", - td->hw.td.status & 0x7ff, - ((td->hw.td.status >> 27) & 3), - (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", - (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", - (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", - (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", - (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", - (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", - (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", - (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", - (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", - (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" - ); -#if 1 - if (td->hw.td.link & UHCI_PTR_TERM) - warn("Link Terminate"); - else { - if (td->hw.td.link & UHCI_PTR_QH) - warn("%s, link points to QH @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - else - warn("%s, link points to TD @ %08x", - (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), - td->hw.td.link & ~UHCI_PTR_BITS); - } -#endif +void uhci_show_status(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw(io_addr + 0); + usbstat = inw(io_addr + 2); + usbint = inw(io_addr + 4); + usbfrnum = inw(io_addr + 6); + flbaseadd = inl(io_addr + 8); + sof = inb(io_addr + 12); + portsc1 = inw(io_addr + 16); + portsc2 = inw(io_addr + 18); + + printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + printk(" usbstat = %04x %s%s%s%s%s%s\n", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + printk(" usbint = %04x\n", usbint); + printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, + 0xfff & (4*(unsigned int)usbfrnum)); + printk(" flbaseadd = %08x\n", flbaseadd); + printk(" sof = %02x\n", sof); + uhci_show_sc(1, portsc1); + uhci_show_sc(2, portsc2); } -#ifdef DEBUG -static void uhci_show_td_queue (puhci_desc_t td) + +#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x)) + +struct uhci_td *uhci_link_to_td(unsigned int link) { - dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); - while (1) { - uhci_show_td (td); - if (td->hw.td.link & UHCI_PTR_TERM) - break; - //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) - // break; - if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) - td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); - else { - dbg("td points to itself!"); - break; - } -// schedule(); - } + if (link & UHCI_PTR_TERM) + return NULL; + + return bus_to_virt(link & ~UHCI_PTR_BITS); } -static void uhci_show_queue (puhci_desc_t qh) +void uhci_show_queue(struct uhci_qh *qh) { - dbg("uhci_show_queue %p:", qh); - while (1) { - uhci_show_qh (qh); + struct uhci_td *td, *first; + int i = 0, count = 1000; - if (qh->hw.qh.element & UHCI_PTR_QH) - dbg("Warning: qh->element points to qh!"); - else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) - uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + if (qh->element & UHCI_PTR_QH) + printk(" Element points to QH (bug?)\n"); - if (qh->hw.qh.head & UHCI_PTR_TERM) - break; + if (qh->element & UHCI_PTR_DEPTH) + printk(" Depth traverse\n"); - if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) - qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); - else { - dbg("qh points to itself!"); + if (qh->element & UHCI_PTR_TERM) + printk(" Terminate\n"); + + if (!(qh->element & ~UHCI_PTR_BITS)) { + printk(" td 0: [NULL]\n"); + return; + } + + first = uhci_link_to_td(qh->element); + + /* Make sure it doesn't runaway */ + for (td = first; td && count > 0; + td = uhci_link_to_td(td->link), --count) { + printk(" td %d: [%p]\n", i++, td); + printk(" "); + uhci_show_td(td); + + if (td == uhci_link_to_td(td->link)) { + printk(KERN_ERR "td links to itself!\n"); break; } } } -static void uhci_show_sc (int port, unsigned short status) +static int uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td) { - dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", - port, - status, - (status & USBPORTSC_SUSP) ? "PortSuspend " : "", - (status & USBPORTSC_PR) ? "PortReset " : "", - (status & USBPORTSC_LSDA) ? "LowSpeed " : "", - (status & USBPORTSC_RD) ? "ResumeDetect " : "", - (status & USBPORTSC_PEC) ? "EnableChange " : "", - (status & USBPORTSC_PE) ? "PortEnabled " : "", - (status & USBPORTSC_CSC) ? "ConnectChange " : "", - (status & USBPORTSC_CCS) ? "PortConnected " : ""); + int j; + + for (j = 0; j < UHCI_NUM_SKELTD; j++) + if (td == uhci->skeltd + j) + return 1; + + return 0; } -void uhci_show_status (puhci_t s) +static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) { - unsigned int io_addr = s->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; + int j; + + for (j = 0; j < UHCI_NUM_SKELQH; j++) + if (qh == uhci->skelqh + j) + return 1; - usbcmd = inw (io_addr + 0); - usbstat = inw (io_addr + 2); - usbint = inw (io_addr + 4); - usbfrnum = inw (io_addr + 6); - flbaseadd = inl (io_addr + 8); - sof = inb (io_addr + 12); - portsc1 = inw (io_addr + 16); - portsc2 = inw (io_addr + 18); - - dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", - usbcmd, - (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", - (usbcmd & USBCMD_CF) ? "CF " : "", - (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", - (usbcmd & USBCMD_FGR) ? "FGR " : "", - (usbcmd & USBCMD_EGSM) ? "EGSM " : "", - (usbcmd & USBCMD_GRESET) ? "GRESET " : "", - (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", - (usbcmd & USBCMD_RS) ? "RS " : ""); - - dbg(" usbstat = %04x %s%s%s%s%s%s", - usbstat, - (usbstat & USBSTS_HCH) ? "HCHalted " : "", - (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", - (usbstat & USBSTS_HSE) ? "HostSystemError " : "", - (usbstat & USBSTS_RD) ? "ResumeDetect " : "", - (usbstat & USBSTS_ERROR) ? "USBError " : "", - (usbstat & USBSTS_USBINT) ? "USBINT " : ""); - - dbg(" usbint = %04x", usbint); - dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, - 0xfff & (4 * (unsigned int) usbfrnum)); - dbg(" flbaseadd = %08x", flbaseadd); - dbg(" sof = %02x", sof); - uhci_show_sc (1, portsc1); - uhci_show_sc (2, portsc2); + return 0; } -#endif + +static const char *td_names[] = {"interrupt1", "interrupt2", "interrupt4", + "interrupt8", "interrupt16", "interrupt32", + "interrupt64", "interrupt128", "interrupt256" }; +static const char *qh_names[] = { "control", "bulk" }; + +void uhci_show_queues(struct uhci *uhci) +{ + int i, isqh; + struct uhci_qh *qh; + struct uhci_td *td; + + for (i = 0; i < UHCI_NUMFRAMES; ++i) { + int shown = 0; + + td = uhci_link_to_td(uhci->fl->frame[i]); + if (td) + isqh = uhci->fl->frame[i] & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + if (!shown) { + printk(" Frame %d\n", i); + shown = 1; + } + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELTD; ++i) { + printk(" %s: [%p] (%08x)\n", td_names[i], + &uhci->skeltd[i], + uhci->skeltd[i].link); + + td = uhci_link_to_td(uhci->skeltd[i].link); + if (td) + isqh = uhci->skeltd[i].link & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELQH; ++i) { + printk(" %s: [%p] (%08x) (%08x)\n", qh_names[i], + &uhci->skelqh[i], + uhci->skelqh[i].link, uhci->skelqh[i].element); + + qh = uhci_link_to_qh(uhci->skelqh[i].link); + for (; qh; qh = uhci_link_to_qh(qh->link)) { + if (uhci_is_skeleton_qh(uhci, qh)) + break; + + printk(" [%p] (%08x) (%08x)\n", + qh, qh->link, qh->element); + + uhci_show_queue(qh); + } + } +} + diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c new file mode 100644 index 000000000000..c1d7386220ab --- /dev/null +++ b/drivers/usb/uhci.c @@ -0,0 +1,2215 @@ +/* + * Universal Host Controller Interface driver for USB. + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@sventech.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * + * Intel documents this fairly well, and as far as I know there + * are no royalties or anything like that, but even so there are + * people who decided that they want to do the same thing in a + * completely different way. + * + * WARNING! The USB documentation is downright evil. Most of it + * is just crap, written by a committee. You're better off ignoring + * most of it, the important stuff is: + * - the low-level protocol (fairly simple but lots of small details) + * - working around the horridness of the rest + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define DEBUG +#include "usb.h" + +#include "uhci.h" +#include "uhci-debug.h" + +#ifdef CONFIG_APM +#include +static int handle_apm_event(apm_event_t event); +#endif + +static int debug = 1; +MODULE_PARM(debug, "i"); + +static kmem_cache_t *uhci_td_cachep; +static kmem_cache_t *uhci_qh_cachep; + +static LIST_HEAD(uhci_list); + +static int rh_submit_urb(urb_t *urb); +static int rh_unlink_urb(urb_t *urb); +static int uhci_get_current_frame_number(struct usb_device *usb_dev); + +#define min(a,b) (((a)<(b))?(a):(b)) + +/* + * Only the USB core should call uhci_alloc_dev and uhci_free_dev + */ +static int uhci_alloc_dev(struct usb_device *usb_dev) +{ + struct uhci_device *dev; + + /* Allocate the UHCI device private data */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -1; + + /* Initialize "dev" */ + memset(dev, 0, sizeof(*dev)); + + usb_dev->hcpriv = dev; + dev->usb = usb_dev; + atomic_set(&dev->refcnt, 1); + + if (usb_dev->parent) + dev->uhci = usb_to_uhci(usb_dev->parent)->uhci; + + return 0; +} + +static int uhci_free_dev(struct usb_device *usb_dev) +{ + struct uhci_device *dev = usb_to_uhci(usb_dev); + + if (atomic_dec_and_test(&dev->refcnt)) + kfree(dev); + + return 0; +} + +static void uhci_inc_dev_use(struct uhci_device *dev) +{ + atomic_inc(&dev->refcnt); +} + +static void uhci_dec_dev_use(struct uhci_device *dev) +{ + uhci_free_dev(dev->usb); +} + +/* + * UHCI interrupt list operations.. + */ +static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + nested_lock(&uhci->irqlist_lock, flags); + list_add(&td->irq_list, &uhci->interrupt_list); + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void uhci_remove_irq_list(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + nested_lock(&uhci->irqlist_lock, flags); + if (td->irq_list.next != &td->irq_list) { + list_del(&td->irq_list); + INIT_LIST_HEAD(&td->irq_list); + } + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + list_add(&urb->urb_list, &uhci->urb_list); + spin_unlock_irqrestore(&uhci->urblist_lock, flags); +} + +static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + if (urb->urb_list.next != &urb->urb_list) { + list_del(&urb->urb_list); + INIT_LIST_HEAD(&urb->urb_list); + } + spin_unlock_irqrestore(&uhci->urblist_lock, flags); +} + +/* + * We insert Isochronous transfers directly into the frame list at the + * beginning + * The layout looks as follows: + * frame list pointer -> iso td's (if any) -> + * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh + */ + +static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) +{ + unsigned long flags; + struct uhci_td *nexttd; + + framenum %= UHCI_NUMFRAMES; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + td->frameptr = &uhci->fl->frame[framenum]; + td->link = uhci->fl->frame[framenum]; + if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { + nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); + td->nexttd = nexttd; + nexttd->prevtd = td; + nexttd->frameptr = NULL; + } + uhci->fl->frame[framenum] = virt_to_bus(td); + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (td->frameptr) { + *(td->frameptr) = td->link; + if (td->nexttd) { + td->nexttd->frameptr = td->frameptr; + td->nexttd->prevtd = NULL; + td->nexttd = NULL; + } + td->frameptr = NULL; + } else { + if (td->prevtd) { + td->prevtd->nexttd = td->nexttd; + td->prevtd->link = td->link; + } + if (td->nexttd) + td->nexttd->prevtd = td->prevtd; + td->prevtd = td->nexttd = NULL; + } + td->link = UHCI_PTR_TERM; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + + /* Fix the linked list pointers */ + td->nexttd = skeltd->nexttd; + td->prevtd = skeltd; + if (skeltd->nexttd) + skeltd->nexttd->prevtd = td; + skeltd->nexttd = td; + + td->link = skeltd->link; + skeltd->link = virt_to_bus(td); + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +/* + * Inserts a td into qh list at the top. + */ +static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *begin) +{ + struct uhci_td *td, *prevtd; + + if (!begin) /* Nothing to do */ + return; + + /* Grab the first TD and add it to the QH */ + td = begin; + qh->element = virt_to_bus(td) | UHCI_PTR_DEPTH; + + /* Go through the rest of the TD's, link them together */ + prevtd = td; + td = td->next; + while (td) { + prevtd->link = virt_to_bus(td) | UHCI_PTR_DEPTH; + + prevtd = td; + td = td->next; + } + + prevtd->link = UHCI_PTR_TERM; +} + +static struct uhci_td *uhci_td_alloc(struct uhci_device *dev) +{ + struct uhci_td *td; + + td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!td) + return NULL; + + td->link = UHCI_PTR_TERM; + td->buffer = 0; + + td->frameptr = NULL; + td->nexttd = td->prevtd = NULL; + td->next = NULL; + td->dev = dev; + INIT_LIST_HEAD(&td->irq_list); + INIT_LIST_HEAD(&td->list); + + uhci_inc_dev_use(dev); + + return td; +} + +static void uhci_td_free(struct uhci_td *td) +{ + kmem_cache_free(uhci_td_cachep, td); + + if (td->dev) + uhci_dec_dev_use(td->dev); +} + +static void uhci_schedule_delete_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->freelist_lock, flags); + list_add(&td->list, &uhci->td_free_list); + if (td->dev) { + uhci_dec_dev_use(td->dev); + td->dev = NULL; + } + spin_unlock_irqrestore(&uhci->freelist_lock, flags); +} + +static struct uhci_qh *uhci_qh_alloc(struct uhci_device *dev) +{ + struct uhci_qh *qh; + + qh = kmem_cache_alloc(uhci_qh_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!qh) + return NULL; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + qh->dev = dev; + qh->prevqh = qh->nextqh = NULL; + + INIT_LIST_HEAD(&qh->list); + + uhci_inc_dev_use(dev); + + return qh; +} + +static void uhci_qh_free(struct uhci_qh *qh) +{ + kmem_cache_free(uhci_qh_cachep, qh); + + if (qh->dev) + uhci_dec_dev_use(qh->dev); +} + +static void uhci_schedule_delete_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->freelist_lock, flags); + list_add(&qh->list, &uhci->qh_free_list); + if (qh->dev) { + uhci_dec_dev_use(qh->dev); + qh->dev = NULL; + } + spin_unlock_irqrestore(&uhci->freelist_lock, flags); +} + +static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + + /* Fix the linked list pointers */ + qh->nextqh = skelqh->nextqh; + qh->prevqh = skelqh; + if (skelqh->nextqh) + skelqh->nextqh->prevqh = qh; + skelqh->nextqh = qh; + + qh->link = skelqh->link; + skelqh->link = virt_to_bus(qh) | UHCI_PTR_QH; + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (qh->prevqh) { + qh->prevqh->nextqh = qh->nextqh; + qh->prevqh->link = qh->link; + } + if (qh->nextqh) + qh->nextqh->prevqh = qh->prevqh; + qh->prevqh = qh->nextqh = NULL; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void inline uhci_fill_td(struct uhci_td *td, __u32 status, + __u32 info, __u32 buffer) +{ + td->status = status; + td->info = info; + td->buffer = buffer; +} + +static void uhci_add_td_to_urb(urb_t *urb, struct uhci_td *td) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + td->urb = urb; + + if (urbp->end) + urbp->end->next = td; + + urbp->end = td; + + if (!urbp->begin) + urbp->begin = td; +} + +/* + * Map status to standard result codes + * + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] + * is True for output TDs and False for input TDs. + */ +static int uhci_map_status(int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EPIPE; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; + + return -EINVAL; +} + +/* + * Control transfers + */ +static int uhci_submit_control(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; + struct urb_priv *urbp; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + /* + * Build the TD for the control request + */ + td = uhci_td_alloc(dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | (7 << 21), + virt_to_bus(urb->setup_packet)); + + /* + * If direction is "send", change the frame from SETUP (0x2D) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). + */ + destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + /* Alternate Data0/1 (start with Data1) */ + destination ^= 1 << TD_TOKEN_TOGGLE; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), + virt_to_bus(data)); + + data += pktsze; + len -= pktsze; + + td = uhci_td_alloc(dev); + if (!td) + /* FIXME: Free all of the previously allocated td's */ + return -ENOMEM; + } + + /* + * Build the final TD for control status + * + * It's IN if the pipe is an output pipe or we're not expecting + * data back. + */ + destination &= ~TD_PID; + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + + status &= ~TD_CTRL_SPD; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status | TD_CTRL_IOC, + destination | (UHCI_NULL_DATA_SIZE << 21), 0); + + uhci_add_irq_list(uhci, td); + + qh = uhci_qh_alloc(dev); + if (!qh) { + /* FIXME: Free all of the TD's */ + return -ENOMEM; + } + uhci_insert_tds_in_qh(qh, urbp->begin); + + uhci_insert_qh(uhci, &uhci->skel_control_qh, qh); + urbp->qh = qh; + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_control(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + uhci_remove_qh(uhci, urbp->qh); + uhci_schedule_delete_qh(uhci, urbp->qh); + + /* Go through the rest of the TD's, deleting them, then scheduling */ + /* their deletion */ + td = urbp->begin; + while (td) { + struct uhci_td *next = td->next; + + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + + uhci_schedule_delete_td(uhci, td); + + td = next; + } + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_control(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret; + + td = urbp->begin; + if (!td) /* Nothing to do */ + return -EINVAL; + + /* The first TD is the SETUP phase, check the status, but skip */ + /* the count */ + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + td = td->next; + while (td && td->next) { + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + /* If SPD is set then we received a short packet */ + /* There will be no status phase at the end */ + if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) + goto td_success; + + if (status) + goto td_error; + + td = td->next; + } + + /* Control status phase */ + status = uhci_status_bits(td->status); + + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of */ + /* the amount we requested */ + if (td->status & TD_CTRL_IOC && + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK) + goto td_success; + + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + +td_success: + uhci_unlink_control(urb); + + return 0; + +td_error: + /* Some debugging code */ + if (debug) { + dbg("uhci_result_control() failed with status %x", + status); + + /* Print the chain for debugging purposes */ + uhci_show_queue(urbp->qh); + } + + if (status & TD_CTRL_STALLED) { + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + uhci_unlink_control(urb); + + return -EPIPE; + } + + ret = uhci_map_status(status, uhci_packetout(td->info)); + + uhci_unlink_control(urb); + + return ret; +} + +/* + * Interrupt transfers + */ +static int uhci_submit_interrupt(urb_t *urb) +{ + struct uhci_td *td; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + struct urb_priv *urbp; + + if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_SPD | + TD_CTRL_IOC; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + td = uhci_td_alloc(dev); + if (!td) + return -ENOMEM; + + destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + destination |= ((urb->transfer_buffer_length - 1) << 21); + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination, + virt_to_bus(urb->transfer_buffer)); + + uhci_add_irq_list(uhci, td); + + uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td); + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + td = urbp->begin; + uhci_remove_td(uhci, td); + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + uhci_schedule_delete_td(uhci, td); + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + int status; + + if (!urbp) + return -EINVAL; + + td = urbp->begin; + if (!td) + return -EINVAL; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + return uhci_map_status(status, uhci_packetout(td->info)); + + urb->actual_length += uhci_actual_length(td->status); + + return 0; +} + +static void uhci_reset_interrupt(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + + if (urb->interval) { + td = urbp->begin; + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + td->info &= ~(1 << TD_TOKEN_TOGGLE); + td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + + urb->status = -EINPROGRESS; + } else + uhci_unlink_interrupt(urb); +} + +/* + * Bulk transfers + */ +static int uhci_submit_bulk(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; + struct urb_priv *urbp; + + if (len < 0) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + /* + * Build the DATA TD's + */ + while (len > 0) { + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), + virt_to_bus(data)); + + data += pktsze; + len -= maxsze; + + if (len <= 0) { + td->status |= TD_CTRL_IOC; + uhci_add_irq_list(uhci, td); + } + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } + + qh = uhci_qh_alloc(dev); + if (!qh) { + /* FIXME: Free all of the TD's */ + return -ENOMEM; + } + uhci_insert_tds_in_qh(qh, urbp->begin); + + uhci_insert_qh(dev->uhci, &dev->uhci->skel_bulk_qh, qh); + urbp->qh = qh; + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +/* We can use the control unlink since they're identical */ +#define uhci_unlink_bulk uhci_unlink_control + +static int uhci_result_bulk(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + for (td = urbp->begin; td; td = td->next) { + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + /* If SPD is set then we received a short packet */ + if (td->status & TD_CTRL_SPD && (uhci_actual_length(td->status) < uhci_expected_length(td->info))) { + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info) ^ 1); + + goto td_success; + } + + if (status) + goto td_error; + } + +td_success: + uhci_unlink_bulk(urb); + + return 0; + +td_error: + /* Some debugging code */ + if (debug) { + dbg("uhci_result_bulk() failed with status %x", + status); + + /* Print the chain for debugging purposes */ + uhci_show_queue(urbp->qh); + } + + if (status & TD_CTRL_STALLED) { + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + return -EPIPE; + } + + return uhci_map_status(status, uhci_packetout(td->info)); +} + +/* + * Isochronous transfers + */ +static int isochronous_find_limits(urb_t *urb, unsigned int *start, unsigned int *end) +{ + urb_t *u, *last_urb = NULL; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head *tmp, *head = &uhci->urb_list; + unsigned long flags; + + spin_lock_irqsave(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + u = list_entry(tmp, urb_t, urb_list); + + /* look for pending URB's with identical pipe handle */ + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && + (u->status == -EINPROGRESS) && (u != urb)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + tmp = tmp->next; + } + spin_unlock_irqrestore(&uhci->urblist_lock, flags); + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + return 0; + } else + return -1; // no previous urb found + +} + +static int isochronous_find_start(urb_t *urb) +{ + int limits; + unsigned int start = 0, end = 0; + + if (urb->number_of_packets > 900) /* 900? Why? */ + return -EFBIG; + + limits = isochronous_find_limits(urb, &start, &end); + + if (urb->transfer_flags & USB_ISO_ASAP) { + if (limits) { + int curframe; + + curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; + urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; + } else + urb->start_frame = end; + } else { + urb->start_frame %= UHCI_NUMFRAMES; + /* FIXME: Sanity check */ + } + + return 0; +} + +static int uhci_submit_isochronous(urb_t *urb) +{ + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + struct urb_priv *urbp; + int i, ret, framenum; + int status, destination; + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + ret = isochronous_find_start(urb); + if (ret) + return ret; + + urbp = kmalloc(sizeof(*urbp), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urbp) + return -ENOMEM; + + urbp->begin = urbp->end = NULL; + + urb->hcpriv = urbp; + + framenum = urb->start_frame; + for (i = 0; i < urb->number_of_packets; i++, framenum++) { + if (!urb->iso_frame_desc[i].length) + continue; + + td = uhci_td_alloc(dev); + if (!td) { + /* FIXME: Free the TD's */ + return -ENOMEM; + } + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), + virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset)); + + if (i + 1 >= urb->number_of_packets) { + td->status |= TD_CTRL_IOC; + uhci_add_irq_list(uhci, td); + } + + uhci_insert_td_frame_list(uhci, td, framenum); + } + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int uhci_unlink_isochronous(urb_t *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + struct uhci_device *dev = usb_to_uhci(urb->dev); + struct uhci *uhci = dev->uhci; + + if (!urbp) + return -EINVAL; + + /* Go through the rest of the TD's, deleting them, then scheduling */ + /* their deletion */ + td = urbp->begin; + while (td) { + struct uhci_td *next = td->next; + + uhci_remove_td(uhci, td); + + if (td->status & TD_CTRL_IOC) + uhci_remove_irq_list(uhci, td); + uhci_schedule_delete_td(uhci, td); + + td = next; + } + + kfree(urbp); + urb->hcpriv = NULL; + + uhci_remove_urb_list(uhci, urb); + + return 0; +} + +static int uhci_result_isochronous(urb_t *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + int status; + int i, ret = 0; + + td = urbp->end; + if (!td) /* Nothing to do */ + return -EINVAL; + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + /* Assume no errors, we'll overwrite this if not */ + urb->status = 0; + + urb->actual_length = 0; + for (i = 0, td = urbp->begin; td; i++, td = td->next) { + int actlength; + + actlength = uhci_actual_length(td->status); + urb->iso_frame_desc[i].actual_length = actlength; + urb->actual_length += actlength; + + status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); + urb->iso_frame_desc[i].status = status; + if (status != 0) { + urb->error_count++; + ret = status; + } + } + + uhci_unlink_isochronous(urb); + + return status; +} + +static int uhci_submit_urb(urb_t *urb) +{ + int ret = -EINVAL; + struct uhci *uhci; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_submit_urb(urb); /* Virtual root hub */ + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_submit_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_submit_isochronous(urb); + break; + } + + urb->status = ret; + if (ret == -EINPROGRESS) { + usb_inc_dev_use(urb->dev); + + return 0; + } + + return ret; +} + +/* + * Return the result of a transfer + */ +static void uhci_transfer_result(urb_t *urb) +{ + urb_t *turb; + int proceed = 0, is_ring = 0; + int ret = -EINVAL; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_result_control(urb); + break; + case PIPE_INTERRUPT: + /* Interrupts are an exception */ + urb->status = uhci_result_interrupt(urb); + if (urb->status != -EINPROGRESS) { + urb->complete(urb); + uhci_reset_interrupt(urb); + } + return; + case PIPE_BULK: + ret = uhci_result_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_result_isochronous(urb); + break; + } + + urb->status = ret; + if (urb->status == -EINPROGRESS) + return; + + if (urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS) { + proceed = 1; + break; + } + + turb = turb->next; + } while (turb && turb != urb && turb != urb->next); + + if (turb == urb || turb == urb->next) + is_ring = 1; + } + + if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + urb->complete(urb); + if (!proceed && is_ring) + uhci_submit_urb(urb); + } + + if (proceed && urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS && + uhci_submit_urb(turb) != 0) + + turb = turb->next; + } while (turb && turb != urb->next); + + if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) + urb->complete(urb); + } + + usb_dec_dev_use(urb->dev); +} + +static int uhci_unlink_urb(urb_t *urb) +{ + struct uhci *uhci; + int ret = 0; + + if (!urb) + return -EINVAL; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_unlink_urb(urb); + + if (urb->status == -EINPROGRESS) { + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_unlink_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_unlink_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_unlink_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_unlink_isochronous(urb); + break; + } + + if (urb->complete) + urb->complete(urb); + + if (in_interrupt()) { /* wait at least 1 frame */ + int errorcount = 10; + + if (errorcount--) + dbg("uhci_unlink_urb called from interrupt for urb %p", urb); + udelay(1000); + } else + schedule_timeout(1+1*HZ/1000); + + usb_dec_dev_use(urb->dev); + } + + urb->status = -ENOENT; + + return ret; +} + +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +static int uhci_get_current_frame_number(struct usb_device *usb_dev) +{ + struct uhci_device *dev = (struct uhci_device *)usb_dev->hcpriv; + + return inw(dev->uhci->io_addr + USBFRNUM); +} + +struct usb_operations uhci_device_operations = { + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; + +/* ------------------------------------------------------------------- + Virtual Root Hub + ------------------------------------------------------------------- */ + +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x00, /* __u8 iProduct; */ + 0x00, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, + Bit 5 Remote-wakeup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +static int rh_send_irq(urb_t *urb) +{ + int i, len = 1; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; + + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } + + *(__u16 *) urb->transfer_buffer = cpu_to_le16(data); + urb->actual_length = len; + urb->status = USB_ST_NOERROR; + + if ((data > 0) && (uhci->rh.send != 0)) { +#ifdef DEBUG /* JE */ +static int foo=5; +if (foo--) +#endif + dbg("root-hub INT complete: port1: %x port2: %x data: %x", + inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); + urb->complete(urb); + } + + return USB_ST_NOERROR; +} + +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ +static int rh_init_int_timer(urb_t *urb); + +static void rh_int_timer_do(unsigned long ptr) +{ + int len; + urb_t *urb = (urb_t *)ptr; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (uhci->rh.send) { + len = rh_send_irq(urb); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete(urb); + } + } + + rh_init_int_timer(urb); +} + +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer */ +static int rh_init_int_timer(urb_t *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + uhci->rh.interval = urb->interval; + init_timer(&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long)urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer(&uhci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break + +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) + + +/*-------------------------------------------------------------------------*/ +/************************* + ** Root Hub Control Pipe + *************************/ + +static int rh_submit_urb(urb_t *urb) +{ + struct usb_device *usb_dev = urb->dev; + struct uhci *uhci = (struct uhci *)usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest *cmd = (devrequest *)urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = USB_ST_NOERROR; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype(pipe) == PIPE_INTERRUPT) { + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer(urb); + + return USB_ST_NOERROR; + } + + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *)data = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)data = cpu_to_le32(0); + OK(4); /* hub power */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *)data = cpu_to_le16(status); + *(__u16 *)(data + 2) = cpu_to_le16(cstatus); + OK(4); + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case RH_ENDPOINT_STALL: + OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_OVER_CURRENT: + OK(0); /* hub power over current */ + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_ENABLE: + CLR_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + case RH_PORT_SUSPEND: + CLR_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power */ + case RH_C_PORT_CONNECTION: + SET_RH_PORTSTAT(USBPORTSC_CSC); + OK(0); + case RH_C_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PEC); + OK(0); + case RH_C_PORT_SUSPEND: + /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK(0); + case RH_C_PORT_OVER_CURRENT: + OK(0); /* port power over current */ + case RH_C_PORT_RESET: + uhci->rh.c_p_r[wIndex - 1] = 0; + OK(0); + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_SUSPEND: + SET_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_RESET: + SET_RH_PORTSTAT(USBPORTSC_PR); + wait_ms(10); + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT(USBPORTSC_PR); + udelay(10); + SET_RH_PORTSTAT(USBPORTSC_PE); + wait_ms(10); + SET_RH_PORTSTAT(0xa); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power ** */ + case RH_PORT_ENABLE: + SET_RH_PORTSTAT (USBPORTSC_PE); + OK(0); + } + break; + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK(0); + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case 0x01: /* device descriptor */ + len = min(leni, min(sizeof(root_hub_dev_des), wLength)); + memcpy(data, root_hub_dev_des, len); + OK(len); + case 0x02: /* configuration descriptor */ + len = min(leni, min(sizeof(root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK(len); + case 0x03: /* string descriptors */ + stat = -EPIPE; + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min(leni, min(sizeof(root_hub_hub_des), wLength)); + memcpy(data, root_hub_hub_des, len); + OK(len); + case RH_GET_CONFIGURATION: + *(__u8 *)data = 0x01; + OK(1); + case RH_SET_CONFIGURATION: + OK(0); + default: + stat = -EPIPE; + } + + urb->actual_length = len; + urb->status = stat; + if (urb->complete) + urb->complete(urb); + + return USB_ST_NOERROR; +} +/*-------------------------------------------------------------------------*/ + +static int rh_unlink_urb(urb_t *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + uhci->rh.send = 0; + del_timer(&uhci->rh.rh_int_timer); + + return 0; +} +/*-------------------------------------------------------------------*/ + +/* + * This is just incredibly fragile. The timings must be just + * right, and they aren't really documented very well. + * + * Note the short delay between disabling reset and enabling + * the port.. + */ +static void uhci_reset_port(unsigned int port) +{ + unsigned short status; + + status = inw(port); + outw(status | USBPORTSC_PR, port); /* reset port */ + wait_ms(10); + outw(status & ~USBPORTSC_PR, port); + udelay(50); + + status = inw(port); + outw(status | USBPORTSC_PE, port); /* enable port */ + wait_ms(10); + + status = inw(port); + if (!(status & USBPORTSC_PE)) { + outw(status | USBPORTSC_PE, port); /* one more try at enabling port */ + wait_ms(50); + } + +} + +void uhci_free_pending(struct uhci *uhci) +{ + struct list_head *tmp, *head; + + /* Free all of the pending QH's and TD's */ + head = &uhci->td_free_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + list_del(&td->list); + INIT_LIST_HEAD(&td->list); + + uhci_td_free(td); + } + + head = &uhci->qh_free_list; + tmp = head->next; + while (tmp != head) { + struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, list); + + tmp = tmp->next; + + list_del(&qh->list); + INIT_LIST_HEAD(&qh->list); + + uhci_qh_free(qh); + } +} + +static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) +{ + struct uhci *uhci = __uhci; + unsigned int io_addr = uhci->io_addr; + unsigned short status; + unsigned long flags; + struct list_head *tmp, *head; + urb_t *urb; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + status = inw(io_addr + USBSTS); + if (!status) /* shared interrupt, not mine */ + return; + outw(status, io_addr + USBSTS); + + if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) { + if (status & USBSTS_RD) + printk(KERN_INFO "uhci: resume detected, not implemented\n"); + if (status & USBSTS_HSE) + printk(KERN_ERR "uhci: host system error, PCI problems?\n"); + if (status & USBSTS_HCPE) + printk(KERN_ERR "uhci: host controller process error. something bad happened\n"); + if (status & USBSTS_HCH) { + printk(KERN_ERR "uhci: host controller halted. very bad\n"); + /* FIXME: Reset the controller, fix the offending TD */ + } + } + + /* Free all of the pending QH's and TD's */ + spin_lock(&uhci->freelist_lock); + uhci_free_pending(uhci); + spin_unlock(&uhci->freelist_lock); + + /* Walk the list of pending TD's to see which ones completed.. */ + nested_lock(&uhci->irqlist_lock, flags); + head = &uhci->interrupt_list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list); + + urb = td->urb; + + tmp = tmp->next; + + /* Checks the status and does all of the magic necessary */ + uhci_transfer_result(urb); + } + nested_unlock(&uhci->irqlist_lock, flags); +} + +static void reset_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + + /* Global reset for 50ms */ + outw(USBCMD_GRESET, io_addr + USBCMD); + wait_ms(50); + outw(0, io_addr + USBCMD); + wait_ms(10); +} + +static void start_hc(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + int timeout = 1000; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw(USBCMD_HCRESET, io_addr + USBCMD); + while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); + break; + } + } + + /* Turn on all interrupts */ + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + io_addr + USBINTR); + + /* Start at frame 0 */ + outw(0, io_addr + USBFRNUM); + outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); + + /* Run and mark it configured with a 64-byte max packet */ + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); +} + +/* + * Allocate a frame list, and then setup the skeleton + * + * The hardware doesn't really know any difference + * in the queues, but the order does matter for the + * protocols higher up. The order is: + * + * - any isochronous events handled before any + * of the queues. We don't do that here, because + * we'll create the actual TD entries on demand. + * - The first queue is the "interrupt queue". + * - The second queue is the "control queue". + * - The third queue is "bulk data". + */ +static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size) +{ + int i, port; + struct uhci *uhci; + struct usb_bus *bus; + + uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); + if (!uhci) + return NULL; + + memset(uhci, 0, sizeof(*uhci)); + + uhci->irq = -1; + uhci->io_addr = io_addr; + uhci->io_size = io_size; + INIT_LIST_HEAD(&uhci->interrupt_list); + INIT_LIST_HEAD(&uhci->urb_list); + + INIT_LIST_HEAD(&uhci->td_free_list); + INIT_LIST_HEAD(&uhci->qh_free_list); + + spin_lock_init(&uhci->urblist_lock); + spin_lock_init(&uhci->framelist_lock); + spin_lock_init(&uhci->freelist_lock); + + nested_init(&uhci->irqlist_lock); + + /* We need exactly one page (per UHCI specs), how convenient */ + /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ + uhci->fl = (void *)__get_free_page(GFP_KERNEL); + if (!uhci->fl) + goto au_free_uhci; + + bus = usb_alloc_bus(&uhci_device_operations); + if (!bus) + goto au_free_fl; + + uhci->bus = bus; + bus->hcpriv = uhci; + + /* Initialize the root hub */ + + /* UHCI specs says devices must have 2 ports, but goes on to say */ + /* they may have more but give no way to determine how many they */ + /* have. However, according to the UHCI spec, Bit 7 is always set */ + /* to 1. So we try to use this to our advantage */ + for (port = 0; port < (io_size - 0x10) / 2; port++) { + unsigned int portstatus; + + portstatus = inw(io_addr + 0x10 + (port * 2)); + if (!(portstatus & 0x0080)) + break; + } + if (debug) + info("detected %d ports", port); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (port < 2 || port > 8) { + info("port count misdetected? forcing to 2 ports"); + port = 2; + } + + uhci->rh.numports = port; + + /* + * 9 Interrupt queues; link int2 to int1, int4 to int2, etc + * then link int1 to control and control to bulk + */ + for (i = 1; i < 9; i++) { + struct uhci_td *td = &uhci->skeltd[i]; + + uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + td->link = virt_to_bus(&uhci->skeltd[i - 1]); + } + + + uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_control_qh) | UHCI_PTR_QH; + + uhci->skel_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH; + uhci->skel_control_qh.element = UHCI_PTR_TERM; + + uhci->skel_bulk_qh.link = UHCI_PTR_TERM; + uhci->skel_bulk_qh.element = UHCI_PTR_TERM; + + /* + * Fill the frame list: make all entries point to + * the proper interrupt queue. + * + * This is probably silly, but it's a simple way to + * scatter the interrupt queues in a way that gives + * us a reasonable dynamic range for irq latencies. + */ + for (i = 0; i < 1024; i++) { + struct uhci_td *irq = &uhci->skel_int2_td; + + if (i & 1) { + irq++; + if (i & 2) { + irq++; + if (i & 4) { + irq++; + if (i & 8) { + irq++; + if (i & 16) { + irq++; + if (i & 32) { + irq++; + if (i & 64) + irq++; + } + } + } + } + } + } + + /* Only place we don't use the frame list routines */ + uhci->fl->frame[i] = virt_to_bus(irq); + } + + return uhci; + +/* + * error exits: + */ +au_free_fl: + free_page((unsigned long)uhci->fl); +au_free_uhci: + kfree(uhci); + + return NULL; +} + +/* + * De-allocate all resources.. + */ +static void release_uhci(struct uhci *uhci) +{ + if (uhci->irq >= 0) { + free_irq(uhci->irq, uhci); + uhci->irq = -1; + } + + if (uhci->fl) { + free_page((unsigned long)uhci->fl); + uhci->fl = NULL; + } + + usb_free_bus(uhci->bus); + kfree(uhci); +} + +int uhci_start_root_hub(struct uhci *uhci) +{ + struct usb_device *usb_dev; + + usb_dev = usb_alloc_dev(NULL, uhci->bus); + if (!usb_dev) + return -1; + + usb_to_uhci(usb_dev)->uhci = uhci; + + uhci->bus->root_hub = usb_dev; + usb_connect(usb_dev); + + if (usb_new_device(usb_dev) != 0) { + usb_free_dev(usb_dev); + + return -1; + } + + return 0; +} + +/* + * If we've successfully found a UHCI, now is the time to increment the + * module usage count, and return success.. + */ +static int setup_uhci(int irq, unsigned int io_addr, unsigned int io_size) +{ + int retval; + struct uhci *uhci; + + uhci = alloc_uhci(io_addr, io_size); + if (!uhci) + return -ENOMEM; + + INIT_LIST_HEAD(&uhci->uhci_list); + list_add(&uhci->uhci_list, &uhci_list); + + request_region(uhci->io_addr, io_size, "usb-uhci"); + + reset_hc(uhci); + + usb_register_bus(uhci->bus); + start_hc(uhci); + + retval = -EBUSY; + if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) { + uhci->irq = irq; + + if (!uhci_start_root_hub(uhci)) + return 0; + } + + /* Couldn't allocate IRQ if we got here */ + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + release_uhci(uhci); + + return retval; +} + +static int found_uhci(struct pci_dev *dev) +{ + int i; + + /* Search for the IO base address.. */ + for (i = 0; i < 6; i++) { + unsigned int io_addr = dev->resource[i].start; + unsigned int io_size = + dev->resource[i].end - dev->resource[i].start + 1; + + /* IO address? */ + if (!(dev->resource[i].flags & 1)) + continue; + + /* Is it already in use? */ + if (check_region(io_addr, io_size)) + break; + + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + pci_enable_device(dev); + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + continue; + } + + return setup_uhci(dev->irq, io_addr, io_size); + } + + return -1; +} + +#ifdef CONFIG_APM +static int handle_apm_event(apm_event_t event) +{ + static int down = 0; + + switch (event) { + case APM_SYS_SUSPEND: + case APM_USER_SUSPEND: + if (down) { + dbg("received extra suspend event"); + break; + } + down = 1; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + if (!down) { + dbg("received bogus resume event"); + break; + } + down = 0; + break; + } + return 0; +} +#endif + +int uhci_init(void) +{ + int retval; + struct pci_dev *dev; + u8 type; + + retval = -ENOMEM; + + /* We throw all of the TD's and QH's into a kmem cache */ + /* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */ + /* does this for us */ + uhci_td_cachep = kmem_cache_create("uhci_td", + sizeof(struct uhci_td), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_td_cachep) + goto td_failed; + + uhci_qh_cachep = kmem_cache_create("uhci_qh", + sizeof(struct uhci_qh), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_qh_cachep) + goto qh_failed; + + retval = -ENODEV; + dev = NULL; + for (;;) { + dev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, dev); + if (!dev) + break; + + /* Is it the UHCI programming interface? */ + pci_read_config_byte(dev, PCI_CLASS_PROG, &type); + if (type != 0) + continue; + + /* Ok set it up */ + retval = found_uhci(dev); + } + + /* We only want to return an error code if ther was an error */ + /* and we didn't find a UHCI controller */ + if (retval && uhci_list.next == &uhci_list) + goto init_failed; + +#ifdef CONFIG_APM + apm_register_callback(&handle_apm_event); +#endif + + return 0; + +init_failed: + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + +qh_failed: + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); + +td_failed: + return retval; +} + +void uhci_cleanup(void) +{ + struct list_head *next, *tmp, *head = &uhci_list; + int i; + unsigned long flags; + + tmp = head->next; + while (tmp != head) { + struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list); + + next = tmp->next; + + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + if (uhci->bus->root_hub) + usb_disconnect(&uhci->bus->root_hub); + + usb_deregister_bus(uhci->bus); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + + /* Free any outstanding TD's and QH's */ + spin_lock_irqsave(&uhci->freelist_lock, flags); + uhci_free_pending(uhci); + spin_unlock_irqrestore(&uhci->freelist_lock, flags); + + release_uhci(uhci); + + tmp = next; + } + + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); +} + +#ifdef MODULE +int init_module(void) +{ + return uhci_init(); +} + +void cleanup_module(void) +{ +#ifdef CONFIG_APM + apm_unregister_callback(&handle_apm_event); +#endif + uhci_cleanup(); +} +#endif //MODULE + diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h new file mode 100644 index 000000000000..ab48c2b2ede2 --- /dev/null +++ b/drivers/usb/uhci.h @@ -0,0 +1,425 @@ +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H + +#include + +#include "usb.h" + +/* + * This nested spinlock code is courtesy of Davide Libenzi + */ +struct s_nested_lock { + spinlock_t lock; + volatile short int pid; + short int count; +}; + +#define nested_init(snl) \ + spin_lock_init(&(snl)->lock); \ + (snl)->pid = 0; \ + (snl)->count = 0; + +#define nested_lock(snl, flags) \ + if ((snl)->pid == current->pid) { \ + (snl)->count++; \ + flags = 0; \ + } else { \ + spin_lock_irqsave(&(snl)->lock, flags); \ + (snl)->count++; \ + (snl)->pid = current->pid; \ + } + +#define nested_unlock(snl, flags) \ + if (!--(snl)->count) { \ + (snl)->pid = 0; \ + spin_unlock_irqrestore(&(snl)->lock, flags); \ + } + +/* + * Universal Host Controller Interface data structures and defines + */ + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + +struct uhci_td; + +struct uhci_qh { + /* Hardware fields */ + __u32 link; /* Next queue */ + __u32 element; /* Queue element pointer */ + + /* Software fields */ + struct uhci_qh *prevqh, *nextqh; /* Previous and next TD in queue */ + + struct uhci_device *dev; /* The owning device */ + + struct list_head list; +} __attribute__((aligned(16))); + +struct uhci_framelist { + __u32 frame[UHCI_NUMFRAMES]; +} __attribute__((aligned(4096))); + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ + +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 +#define TD_PID 0xFF + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* + * The documentation says "4 words for hardware, 4 words for software". + * + * That's silly, the hardware doesn't care. The hardware only cares that + * the hardware words are 16-byte aligned, and we can have any amount of + * sw space after the TD entry as far as I can tell. + * + * But let's just go with the documentation, at least for 32-bit machines. + * On 64-bit machines we probably want to take advantage of the fact that + * hw doesn't really care about the size of the sw-only area. + * + * Alas, not anymore, we have more than 4 words for software, woops + */ +struct uhci_td { + /* Hardware fields */ + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; + + /* Software fields */ + unsigned int *frameptr; /* Frame list pointer */ + struct uhci_td *prevtd, *nexttd; /* Previous and next TD in queue */ + + struct uhci_device *dev; + struct urb *urb; /* URB this TD belongs to */ + struct uhci_td *next; /* List of chained TD's for an URB */ + + struct list_head irq_list; /* Active interrupt list.. */ + struct list_head list; +} __attribute__((aligned(16))); + +/* + * Note the alignment requirements of the entries + * + * Each UHCI device has pre-allocated QH and TD entries. + * You can use more than the pre-allocated ones, but I + * don't see you usually needing to. + */ +struct uhci; + +struct uhci_device { + struct usb_device *usb; + + atomic_t refcnt; + + struct uhci *uhci; /* HC this device is connected to */ +}; + +#define uhci_to_usb(uhci) ((uhci)->usb) +#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv) + +/* + * There are various standard queues. We set up several different + * queues for each of the three basic queue types: interrupt, + * control, and bulk. + * + * - There are various different interrupt latencies: ranging from + * every other USB frame (2 ms apart) to every 256 USB frames (ie + * 256 ms apart). Make your choice according to how obnoxious you + * want to be on the wire, vs how critical latency is for you. + * - The control list is done every frame. + * - There are 4 bulk lists, so that up to four devices can have a + * bulk list of their own and when run concurrently all four lists + * will be be serviced. + * + * This is a bit misleading, there are various interrupt latencies, but they + * vary a bit, interrupt2 isn't exactly 2ms, it can vary up to 4ms since the + * other queues can "override" it. interrupt4 can vary up to 8ms, etc. Minor + * problem + * + * In the case of the root hub, these QH's are just head's of qh's. Don't + * be scared, it kinda makes sense. Look at this wonderful picture care of + * Linus: + * + * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... + * iso-QH iso-QH irq-QH irq-QH QH QH + * | | | | | | + * End dev1-iso-TD1 End dev1-irq-TD1 ... ... + * | + * dev1-iso-TD2 + * | + * .... + * + * This may vary a bit (the UHCI docs don't explicitly say you can put iso + * transfers in QH's and all of their pictures don't have that either) but + * other than that, that is what we're doing now + * + * And now we don't put Iso transfers in QH's, so we don't waste one on it + * --jerdfelt + * + * To keep with Linus' nomenclature, this is called the QH skeleton. These + * labels (below) are only signficant to the root hub's QH's + */ + +#define UHCI_NUM_SKELTD 9 +#define skel_int1_td skeltd[0] +#define skel_int2_td skeltd[1] +#define skel_int4_td skeltd[2] +#define skel_int8_td skeltd[3] +#define skel_int16_td skeltd[4] +#define skel_int32_td skeltd[5] +#define skel_int64_td skeltd[6] +#define skel_int128_td skeltd[7] +#define skel_int256_td skeltd[8] + +#define UHCI_NUM_SKELQH 2 +#define skel_control_qh skelqh[0] +#define skel_bulk_qh skelqh[1] + +/* + * Search tree for determining where fits in the + * skelqh[] skeleton. + * + * An interrupt request should be placed into the slowest skelqh[] + * which meets the interval/period/frequency requirement. + * An interrupt request is allowed to be faster than but not slower. + * + * For a given , this function returns the appropriate/matching + * skelqh[] index value. + * + * NOTE: For UHCI, we don't really need int256_qh since the maximum interval + * is 255 ms. However, we do need an int1_qh since 1 is a valid interval + * and we should meet that frequency when requested to do so. + * This will require some change(s) to the UHCI skeleton. + */ +static inline int __interval_to_skel(int interval) +{ + if (interval < 16) { + if (interval < 4) { + if (interval < 2) + return 0; /* int1 for 0-1 ms */ + return 1; /* int2 for 2-3 ms */ + } + if (interval < 8) + return 2; /* int4 for 4-7 ms */ + return 3; /* int8 for 8-15 ms */ + } + if (interval < 64) { + if (interval < 32) + return 4; /* int16 for 16-31 ms */ + return 5; /* int32 for 32-63 ms */ + } + if (interval < 128) + return 6; /* int64 for 64-127 ms */ + return 7; /* int128 for 128-255 ms (Max.) */ +} + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; + +/* + * This describes the full uhci information. + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. + */ +struct uhci { + int irq; + unsigned int io_addr; + unsigned int io_size; + + struct list_head uhci_list; + + struct usb_bus *bus; + + struct uhci_td skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */ + struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + + struct uhci_framelist *fl; /* Frame list */ + + struct s_nested_lock irqlist_lock; + struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */ + + spinlock_t urblist_lock; + struct list_head urb_list; + + spinlock_t framelist_lock; + + spinlock_t freelist_lock; + struct list_head td_free_list; + struct list_head qh_free_list; + + struct virt_root_hub rh; /* private data of the virtual root hub */ +}; + +struct urb_priv { + struct uhci_qh *qh; /* QH for this URB */ + struct uhci_td *begin; + struct uhci_td *end; +}; + +/* ------------------------------------------------------------------------- + Virtual Root HUB + ------------------------------------------------------------------------- */ +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +/* needed for the debugging code */ +struct uhci_td *uhci_link_to_td(unsigned int element); + +/* Debugging code */ +void uhci_show_td(struct uhci_td *td); +void uhci_show_status(struct uhci *uhci); +void uhci_show_queue(struct uhci_qh *qh); +void uhci_show_queues(struct uhci *uhci); + +#endif + diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c index 68927a086661..9e7557e8fac1 100644 --- a/drivers/usb/usb-core.c +++ b/drivers/usb/usb-core.c @@ -132,6 +132,9 @@ int usb_init(void) #ifdef CONFIG_USB_UHCI uhci_init(); #endif +#ifdef CONFIG_USB_UHCI_ALT + uhci_init(); +#endif #ifdef CONFIG_USB_OHCI ohci_hcd_init(); #endif diff --git a/drivers/usb/usb-serial.c b/drivers/usb/usb-serial.c index 9f37fcc9ed1f..f30057d9cd5b 100644 --- a/drivers/usb/usb-serial.c +++ b/drivers/usb/usb-serial.c @@ -996,7 +996,7 @@ static int whiteheat_writememory (struct usb_serial *serial, int address, unsign // dbg("whiteheat_writememory %x, %d", address, length); if (!transfer_buffer) { - err("whiteheat_writememory: kmalloc(%d) failed.\n", length); + err("whiteheat_writememory: kmalloc(%d) failed.", length); return -ENOMEM; } memcpy (transfer_buffer, data, length); @@ -1078,7 +1078,7 @@ static int whiteheat_startup (struct usb_serial *serial) response = whiteheat_writememory (serial, record->address, (unsigned char *)record->data, record->data_size, 0xa0); if (response < 0) { - err("whiteheat_writememory failed for second firmware step (%d %04X %p %d)\n", + err("whiteheat_writememory failed for second firmware step (%d %04X %p %d)", response, record->address, record->data, record->data_size); break; } @@ -1127,7 +1127,7 @@ static void visor_serial_close(struct tty_struct *tty, struct file * filp) dbg("visor_serial_close port %d", port); if (!transfer_buffer) { - err("visor_serial_close: kmalloc(%d) failed.\n", 0x12); + err("visor_serial_close: kmalloc(%d) failed.", 0x12); } else { /* send a shutdown message to the device */ usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, @@ -1175,7 +1175,7 @@ static int visor_startup (struct usb_serial *serial) unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL); if (!transfer_buffer) { - err("visor_startup: kmalloc(%d) failed.\n", 256); + err("visor_startup: kmalloc(%d) failed.", 256); return -ENOMEM; } diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h new file mode 100644 index 000000000000..73d16937a2d2 --- /dev/null +++ b/drivers/usb/usb-uhci-debug.h @@ -0,0 +1,195 @@ +#ifdef DEBUG + +static void uhci_show_qh (puhci_desc_t qh) +{ + if (qh->type != QH_TYPE) { + dbg("qh has not QH_TYPE"); + return; + } + dbg("uhci_show_qh %p (%08lX):", qh, virt_to_bus (qh)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + dbg("Head Terminate"); + else { + if (qh->hw.qh.head & UHCI_PTR_QH) + dbg("Head points to QH"); + else + dbg("Head points to TD"); + + dbg("head: %08X", qh->hw.qh.head & ~UHCI_PTR_BITS); + } + if (qh->hw.qh.element & UHCI_PTR_TERM) + dbg("Element Terminate"); + else { + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg("Element points to QH"); + else + dbg("Element points to TD"); + dbg("element: %08X", qh->hw.qh.element & ~UHCI_PTR_BITS); + } +} +#endif + +static void uhci_show_td (puhci_desc_t td) +{ + char *spid; + warn("uhci_show_td %p (%08lX) ", td, virt_to_bus (td)); + + switch (td->hw.td.info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = " OUT "; + break; + case USB_PID_IN: + spid = " IN "; + break; + default: + spid = " ? "; + break; + } + + warn("MaxLen=%02x DT%d EndPt=%x Dev=%x, PID=%x(%s) (buf=%08x)", + td->hw.td.info >> 21, + ((td->hw.td.info >> 19) & 1), + (td->hw.td.info >> 15) & 15, + (td->hw.td.info >> 8) & 127, + (td->hw.td.info & 0xff), + spid, + td->hw.td.buffer); + + warn("Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", + td->hw.td.status & 0x7ff, + ((td->hw.td.status >> 27) & 3), + (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", + (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", + (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", + (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", + (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" + ); +#if 1 + if (td->hw.td.link & UHCI_PTR_TERM) + warn("Link Terminate"); + else { + if (td->hw.td.link & UHCI_PTR_QH) + warn("%s, link points to QH @ %08x", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + else + warn("%s, link points to TD @ %08x", + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : " Breadth first"), + td->hw.td.link & ~UHCI_PTR_BITS); + } +#endif +} +#ifdef DEBUG +static void uhci_show_td_queue (puhci_desc_t td) +{ + dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); + while (1) { + uhci_show_td (td); + if (td->hw.td.link & UHCI_PTR_TERM) + break; + //if(!(td->hw.td.link&UHCI_PTR_DEPTH)) + // break; + if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) + td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); + else { + dbg("td points to itself!"); + break; + } +// schedule(); + } +} + +static void uhci_show_queue (puhci_desc_t qh) +{ + dbg("uhci_show_queue %p:", qh); + while (1) { + uhci_show_qh (qh); + + if (qh->hw.qh.element & UHCI_PTR_QH) + dbg("Warning: qh->element points to qh!"); + else if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + break; + + if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) + qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); + else { + dbg("qh points to itself!"); + break; + } + } +} + +static void uhci_show_sc (int port, unsigned short status) +{ + dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} + +void uhci_show_status (puhci_t s) +{ + unsigned int io_addr = s->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw (io_addr + 0); + usbstat = inw (io_addr + 2); + usbint = inw (io_addr + 4); + usbfrnum = inw (io_addr + 6); + flbaseadd = inl (io_addr + 8); + sof = inb (io_addr + 12); + portsc1 = inw (io_addr + 16); + portsc2 = inw (io_addr + 18); + + dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + dbg(" usbstat = %04x %s%s%s%s%s%s", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + dbg(" usbint = %04x", usbint); + dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, + 0xfff & (4 * (unsigned int) usbfrnum)); + dbg(" flbaseadd = %08x", flbaseadd); + dbg(" sof = %02x", sof); + uhci_show_sc (1, portsc1); + uhci_show_sc (2, portsc2); +} +#endif diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c index 3fe469699aa6..a85dfab5af22 100644 --- a/drivers/usb/usb-uhci.c +++ b/drivers/usb/usb-uhci.c @@ -43,7 +43,7 @@ #include "usb.h" #include "usb-uhci.h" -#include "uhci-debug.h" +#include "usb-uhci-debug.h" #ifdef CONFIG_APM #include diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index 213f69036167..32d6e519e0f3 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1595,17 +1595,34 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) tbuf = kmalloc(256, GFP_KERNEL); if (!tbuf) return -ENOMEM; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_get_string(dev, 0, 0, tbuf, 4); + if (err < 0) { + err("error getting string descriptor 0 (error=%d)", err); + goto errout; + } else if (tbuf[0] < 4) { + err("string descriptor 0 too short"); + err = -EINVAL; + goto errout; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3]<< 8); + /* always use the first langid listed */ + info("USB device number %d default language ID 0x%x", + dev->devnum, dev->string_langid); + } + } + /* - * is this two step process necessary? can't we just - * ask for a maximum length string and then take the length - * that was returned? + * Just ask for a maximum length string and then take the length + * that was returned. */ - err = usb_get_string(dev, dev->string_langid, index, tbuf, 4); - if (err < 0) - goto errout; - err = usb_get_string(dev, dev->string_langid, index, tbuf, tbuf[0]); + err = usb_get_string(dev, dev->string_langid, index, tbuf, 255); if (err < 0) goto errout; + info("actual string desc. length = %d", err); size--; /* leave room for trailing NULL char in output buffer */ for (idx = 0, u = 2; u < err; u += 2) { @@ -1633,7 +1650,6 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) */ int usb_new_device(struct usb_device *dev) { - unsigned char *buf; int addr, err; int tmp; @@ -1709,21 +1725,6 @@ int usb_new_device(struct usb_device *dev) err("failed to set default configuration"); return -1; } - /* get langid for strings */ - buf = kmalloc(256, GFP_KERNEL); - if (!buf) { - err("out of memory\n"); - } else { - err = usb_get_string(dev, 0, 0, buf, 4); - if (err < 0) { - err("error getting string descriptor 0 (error=%d)\n", err); - } else if (buf[0] < 4) { - err("string descriptpr 0 too short\n"); - } else - dev->string_langid = buf[2] | (buf[3]<< 8); - kfree(buf); - info("USB device number %d default language ID 0x%x", dev->devnum, dev->string_langid); - } if (dev->descriptor.iManufacturer) usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h index ec2754b1fc7a..a7b027bf2ff1 100644 --- a/drivers/usb/usb.h +++ b/drivers/usb/usb.h @@ -509,6 +509,7 @@ struct usb_device { struct usb_device_descriptor descriptor;/* Descriptor */ struct usb_config_descriptor *config; /* All of the configs */ + int have_langid; /* whether string_langid is valid yet */ int string_langid; /* language ID for strings */ void *hcpriv; /* Host Controller private data */ diff --git a/drivers/video/dn_accel.h b/drivers/video/dn_accel.h new file mode 100644 index 000000000000..b39be932d1ec --- /dev/null +++ b/drivers/video/dn_accel.h @@ -0,0 +1,9 @@ +#ifndef _DN_ACCEL_H_ +#define _DN_ACCEL_H_ + +#include + +void dn_bitblt(struct display *p,int x_src,int y_src, int x_dest, int y_dest, + int x_count, int y_count); + +#endif diff --git a/drivers/video/dn_cfb4.c b/drivers/video/dn_cfb4.c new file mode 100644 index 000000000000..61bc68014f76 --- /dev/null +++ b/drivers/video/dn_cfb4.c @@ -0,0 +1,547 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include