From e95795508968d39808a73d1af2652514f5f051cb Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:20:10 -0500 Subject: [PATCH] Linux 2.2.14pre3 o ISDN ppp VJ fix (Henner Eisen) o ESS Maestro sound driver (Zach "Princess" Brown) o ESS 18xx driver update (Rolf Fokkens) o SiS 900 driver update (Ollie Lho) o Tulip 0.91g + ppc (Donald Becker) o Defragment option doc fix (Matthias Eckermann) o Gcc 2.95 fixes for hfmodem (Thomans Sailer) o Page cache hash size fix (Andrea Arcangeli) o Fix duplicated module installs (Alex Kanavin) o APM maintainer has moved (Stephen Rothwell) o Clean up x86 detection code (Dave Jones) o Fix MiroACI compile problems (Yasuhide OOMORI) o wait4() fixes (Andrea Arcangeli) o wait event race fixes (Andrea Arcangeli) o network backlog clear race fix (Andrea Arcangeli) o bdflush wakeup fix (Andrea Arcangeli) o grow inode overcommit fix (Andrea Arcangeli) o Fix a tcp syncookie handling bug (Alan Cox) --- CREDITS | 4 +- Documentation/Configure.help | 37 +- Documentation/kernel-parameters.txt | 4 +- Documentation/proc.txt | 15 + MAINTAINERS | 5 +- Makefile | 4 +- arch/i386/kernel/apm.c | 6 +- arch/i386/kernel/setup.c | 67 +- drivers/char/hfmodem/refclock.c | 7 +- drivers/isdn/isdn_ppp.c | 11 +- drivers/net/sis900.c | 832 +++---- drivers/net/sis900.h | 79 +- drivers/net/tulip.c | 109 +- drivers/sound/Config.in | 3 + drivers/sound/Makefile | 1 + drivers/sound/lowlevel/miroaci.h | 2 +- drivers/sound/maestro.c | 3582 +++++++++++++++++++++++++++ drivers/sound/maestro.h | 60 + drivers/sound/sb_ess.c | 40 +- drivers/sound/sound_core.c | 6 + fs/buffer.c | 3 +- fs/inode.c | 4 + include/linux/apm_bios.h | 2 +- include/linux/sched.h | 2 + kernel/exit.c | 9 +- net/core/dev.c | 34 +- net/ipv4/ip_forward.c | 2 +- net/ipv4/tcp_ipv4.c | 3 +- 28 files changed, 4231 insertions(+), 702 deletions(-) create mode 100644 drivers/sound/maestro.c create mode 100644 drivers/sound/maestro.h diff --git a/CREDITS b/CREDITS index cf41b2e647ef..f3f8fdfd66f0 100644 --- a/CREDITS +++ b/CREDITS @@ -1713,8 +1713,8 @@ S: 7000 Stuttgart 50 S: Germany N: Stephen Rothwell -E: Stephen.Rothwell@canb.auug.org.au -W: http://www.canb.auug.org.au/~sfr +E: sfr@linuxcare.com +W: http://linuxcare.com.au/sfr P: 1024/BD8C7805 CD A4 9D 01 10 6E 7E 3B 91 88 FA D9 C8 40 AA 02 D: Boot/setup/build work for setup > 2K D: Author, APM driver diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 90d47b5e26ae..89d23240a240 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -2532,6 +2532,8 @@ CONFIG_IP_MASQUERADE from a boot time script after the /proc filesystem has been mounted. + Enabling masquerading automagically enables ip_always_defrag too. + Details on how to set things up are contained in the IP Masquerade mini-HOWTO, available via FTP (user: anonymous) from ftp://metalab.unc.edu/pub/Linux/docs/HOWTO/mini; there's also some @@ -2643,25 +2645,6 @@ CONFIG_IP_MASQUERADE_MFW The module will be called ip_masq_markfw.o. If you want to compile it as a module, say M here and read Documentation/modules.txt. -IP: always defragment (required for masquerading) -CONFIG_IP_ALWAYS_DEFRAG - If you say Y here, then all incoming fragments (parts of IP packets - that arose when some host between origin and destination decided - that the packets were too large and cut them into pieces) will be - reassembled (defragmented) before being processed, even if they are - about to be forwarded. - - You must say Y here if you want to enable "IP: masquerading" or "IP: - transparent proxying". - - When using "IP: firewalling" support, you might also want to say Y - here, to have a more reliable firewall (otherwise second and further - fragments must be dealt with by the firewall, which can be tricky). - - Only say Y here if running either a firewall that is the sole link - to your network or a transparent proxy; never ever say Y here for a - normal router or host. - IP: aliasing support CONFIG_IP_ALIAS Sometimes it is useful to give several IP addresses to a single @@ -8884,9 +8867,15 @@ CONFIG_APM APM is a BIOS specification for saving power using several different techniques. This is mostly useful for battery powered laptops with APM compliant BIOSes. If you say Y here, the system time will be - reset after a USER RESUME operation, the /proc/apm device will - provide battery status information, and user-space programs will - receive notification of APM "events" (e.g., battery status change). + reset after a RESUME operation, the /proc/apm device will provide + battery status information, and user-space programs will receive + notification of APM "events" (e.g. battery status change). + + If you select "Y" here, you can disable actual use of the APM + BIOS by passing the "apm=off" option to the kernel at boot time. + + Note that the APM support is almost completely disabled for + machines with more than one CPU. Supporting software is available; for more information, read the Battery Powered Linux mini-HOWTO, available via FTP (user: @@ -8899,9 +8888,7 @@ CONFIG_APM This driver does not support the TI 4000M TravelMate and the ACER 486/DX4/75 because they don't have compliant BIOSes. Many "green" desktop machines also don't have compliant BIOSes, and this driver - will cause those machines to panic during the boot phase (typically, - these machines are using a data segment of 0040, which is reserved - for the Linux kernel). + may cause those machines to panic during the boot phase. If you are running Linux on a laptop, you may also want to read the Linux Laptop home page on the WWW at diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index d5ead50f2bd5..060f436b60fa 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -11,7 +11,7 @@ restrictions on the kernel for the said kernel parameter to be valid. The restrictions referred to are that the relevant option is valid if: APIC APIC support is enabled. - APM Automatic Power Management support is enabled. + APM Advanced Power Management support is enabled. AX25 Appropriate AX.25 support is enabled. CD Appropriate CD support is enabled. EIDE EIDE/ATAPI support is enabled. @@ -64,7 +64,7 @@ running once the system is up. AM53C974= [HW,SCSI] - apm= [APM] Automatic Power Management. + apm= [APM] Advanced Power Management. arcrimi= [HW,NET] diff --git a/Documentation/proc.txt b/Documentation/proc.txt index ef2246d02935..5f5c20f2fc9c 100644 --- a/Documentation/proc.txt +++ b/Documentation/proc.txt @@ -1041,6 +1041,21 @@ ip_masq_debug IP fragmentation settings +ip_always_defrag + Replaces the former Kernel-Configuration option: + CONFIG_IP_ALWAYS_DEFRAG + All incoming fragments (parts of IP packets + that arose when some host between origin and destination decided + that the packets were too large and cut them into pieces) will be + reassembled (defragmented) before being processed, even if they are + about to be forwarded. + + Only say Y here if running either a firewall that is the sole link + to your network or a transparent proxy; never ever say Y here for a + normal router or host. + + This is automagically enabled when enabling masquerading. + ipfrag_high_trash and ipfrag_low_trash Maximum memory used to reassemble IP fragments. When ipfrag_high_thresh bytes of memory is allocated for this purpose, diff --git a/MAINTAINERS b/MAINTAINERS index 58cec2df8007..e712ce505a25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -121,9 +121,10 @@ S: Maintained APM DRIVER P: Stephen Rothwell -M: Stephen.Rothwell@canb.auug.org.au +M: sfr@linuxcare.com L: linux-laptop@vger.rutgers.edu -S: Maintained +W: http://www.linuxcare.com.au/apm/ +S: Supported APPLETALK NETWORK LAYER P: Jay Schulist diff --git a/Makefile b/Makefile index a5cc3f5ce344..9a8ade403dbc 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 14 -EXTRAVERSION = pre1 +EXTRAVERSION = pre3 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -339,7 +339,7 @@ modules_install: if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_MODULES net; fi; \ \ - for f in *.o; do [ -r $$f ] && echo $$f; done > $$MODLIB/.allmods; \ + for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \ echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \ if [ -s $$MODLIB/.misc ]; then inst_mod $$MODLIB/.misc misc; fi; \ rm -f $$MODLIB/.misc $$MODLIB/.allmods; \ diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 6e7c3c4b8cdc..c805e16f7535 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -1,8 +1,8 @@ /* -*- linux-c -*- * APM BIOS driver for Linux - * Copyright 1994-1998 Stephen Rothwell - * (Stephen.Rothwell@canb.auug.org.au) - * Development of this driver was funded by NEC Australia P/L + * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) + * + * Initial development of this driver was funded by NEC Australia P/L * and NEC Corporation * * This program is free software; you can redistribute it and/or modify it diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 08795e0942d3..c3d9cb20c201 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -20,6 +20,9 @@ * * Added proper L2 cache detection for Coppermine * Dragan Stancevic , October 1999 + * + * Improved Intel cache detection. + * Dave Jones , October 1999 */ /* @@ -821,50 +824,48 @@ __initfunc(void identify_cpu(struct cpuinfo_x86 *c)) } } - for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { - if (c->cpuid_level > 1) { - /* supports eax=2 call */ - int edx, cache_size, dummy; + if (c->cpuid_level > 1) { + /* supports eax=2 call */ + int edx, dummy; - cpuid(2, &dummy, &dummy, &dummy, &edx); + cpuid(2, &dummy, &dummy, &dummy, &edx); - /* We need only the LSB */ - edx &= 0xff; + /* We need only the LSB */ + edx &= 0xff; - switch (edx) { - case 0x40: - cache_size = 0; - break; - - case 0x41: - cache_size = 128; - break; + switch (edx) { + case 0x40: + c->x86_cache_size = 0; + break; - case 0x42: - case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ - cache_size = 256; - break; + case 0x41: + c->x86_cache_size = 128; + break; - case 0x43: - cache_size = 512; - break; + case 0x42: + case 0x82: /*Detect 256-Kbyte cache on Coppermine*/ + c->x86_cache_size = 256; + break; - case 0x44: - cache_size = 1024; - break; + case 0x43: + c->x86_cache_size = 512; + break; - case 0x45: - cache_size = 2048; - break; + case 0x44: + c->x86_cache_size = 1024; + break; - default: - cache_size = 0; - break; - } + case 0x45: + c->x86_cache_size = 2048; + break; - c->x86_cache_size = cache_size; + default: + c->x86_cache_size = 0; + break; } + } + for (i = 0; i < sizeof(cpu_models)/sizeof(struct cpu_model_info); i++) { if (cpu_models[i].vendor == c->x86_vendor && cpu_models[i].x86 == c->x86) { if (c->x86_model <= 16) diff --git a/drivers/char/hfmodem/refclock.c b/drivers/char/hfmodem/refclock.c index 2ca829fbf779..e98cff8c1bb5 100644 --- a/drivers/char/hfmodem/refclock.c +++ b/drivers/char/hfmodem/refclock.c @@ -127,14 +127,13 @@ hfmodem_time_t hfmodem_refclock_current(struct hfmodem_state *dev, hfmodem_time_ #ifdef __i386__ if (rdtsc_ok) { - unsigned int tmp0, tmp1; - unsigned int tmp2, tmp3; + unsigned int tmp0, tmp1, tmp2, tmp3, tmp4; __asm__("rdtsc;\n\t" "subl %2,%%eax\n\t" "sbbl %3,%%edx\n\t" : "=&a" (tmp0), "=&d" (tmp1) - : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi) : "ax", "dx"); - __asm__("mull %1" : "=d" (tmp2) : "m" (scale_rdtsc), "a" (tmp0) : "ax"); + : "m" (dev->clk.starttime_lo), "m" (dev->clk.starttime_hi)); + __asm__("mull %2" : "=d" (tmp2), "=a" (tmp4) : "m" (scale_rdtsc), "1" (tmp0) : "ax"); __asm__("mull %1" : "=a" (tmp3) : "m" (scale_rdtsc), "a" (tmp1) : "dx"); curtime = tmp2 + tmp3; goto time_known; diff --git a/drivers/isdn/isdn_ppp.c b/drivers/isdn/isdn_ppp.c index 118905e5fbab..150468eaef70 100644 --- a/drivers/isdn/isdn_ppp.c +++ b/drivers/isdn/isdn_ppp.c @@ -1540,7 +1540,13 @@ isdn_ppp_xmit(struct sk_buff *skb, struct device *netdev) * sk_buff. old call to dev_alloc_skb only reserved * 16 bytes, now we are looking what the driver want. */ - hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; + hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER; + /* + * Note: hl might still be insufficient because the method + * above does not account for a possibible MPPP slave channel + * which had larger HL header space requirements than the + * master. + */ new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC); if (new_skb) { u_char *buf; @@ -2667,9 +2673,10 @@ static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, } /* Allow for at least 150 % expansion (for now) */ - skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32); + skb_out = dev_alloc_skb(skb_in->len + skb_in->len/2 + 32 + skb_headroom(skb_in)); if(!skb_out) return skb_in; + skb_reserve(skb_out, skb_headroom(skb_in)); ret = (compressor->compress)(stat,skb_in,skb_out,*proto); if(!ret) { diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index fa751547394a..b7fca44e1c07 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -18,7 +18,7 @@ preliminary Rev. 1.0 Jan. 18, 1998 http://www.sis.com.tw/support/databook.htm - Ollie Lho (ollie@sis.com.tw) + Rev 1.05.05 Oct. 29 1999 Ollie Lho (ollie@sis.com.tw) Single buffer Tx/Rx Chin-Shan Li (lcs@sis.com.tw) Added AMD Am79c901 HomePNA PHY support Rev 1.05 Aug. 7 1999 Jim Huang (cmhuang@sis.com.tw) Initial release */ @@ -35,6 +35,7 @@ #include #include #include + #include #include #include /* Processor type for cache alignment. */ @@ -45,7 +46,7 @@ #include "sis900.h" static const char *version = -"sis900.c:v1.05 8/07/99\n"; +"sis900.c:v1.05.05 10/29/99\n"; static int max_interrupt_work = 20; #define sis900_debug debug @@ -93,15 +94,11 @@ struct mii_phy { u16 status; }; -typedef struct _EuphLiteDesc { - u32 llink; - unsigned char* buf; - u32 physAddr; - /* Hardware sees the physical address of descriptor */ - u32 plink; +typedef struct _BufferDesc { + u32 link; u32 cmdsts; - u32 bufPhys; -} EuphLiteDesc; + u32 bufptr; +} BufferDesc; struct sis900_private { struct device *next_module; @@ -111,17 +108,17 @@ struct sis900_private { struct mac_chip_info * mac; struct mii_phy * mii; - struct timer_list timer; /* Media selection timer. */ - unsigned int cur_rx; /* Index into the Rx buffer of next Rx pkt. */ - unsigned int cur_tx, dirty_tx, tx_flag; + struct timer_list timer; /* Link status detection timer. */ + unsigned int cur_rx, dirty_rx; + unsigned int cur_tx, dirty_tx; - /* The saved address of a sent-in-place packet/buffer, for skfree(). */ - struct sk_buff* tx_skbuff[NUM_TX_DESC]; - EuphLiteDesc tx_buf[NUM_TX_DESC]; /* Tx bounce buffers */ - EuphLiteDesc rx_buf[NUM_RX_DESC]; - unsigned char *rx_bufs; - unsigned char *tx_bufs; /* Tx bounce buffer region. */ + /* The saved address of a sent/receive-in-place packet/buffer */ + struct sk_buff *tx_skbuff[NUM_TX_DESC]; + struct sk_buff *rx_skbuff[NUM_RX_DESC]; + BufferDesc tx_ring[NUM_TX_DESC]; + BufferDesc rx_ring[NUM_RX_DESC]; unsigned int tx_full; /* The Tx queue is full. */ + int MediaSpeed; /* user force speed */ int MediaDuplex; /* user force duplex */ int full_duplex; /* Full/Half-duplex. */ @@ -150,9 +147,11 @@ static u16 mdio_read(struct device *dev, int phy_id, int location); static void mdio_write(struct device *dev, int phy_id, int location, int val); static void sis900_timer(unsigned long data); static void sis900_tx_timeout(struct device *dev); -static void sis900_init_ring(struct device *dev); +static void sis900_init_tx_ring(struct device *dev); +static void sis900_init_rx_ring(struct device *dev); static int sis900_start_xmit(struct sk_buff *skb, struct device *dev); static int sis900_rx(struct device *dev); +static void sis900_finish_xmit (struct device *dev); static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs); static int sis900_close(struct device *dev); static int mii_ioctl(struct device *dev, struct ifreq *rq, int cmd); @@ -227,10 +226,11 @@ static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_ if (did_version++ == 0) printk(KERN_INFO "%s", version); - /* check to see if we have sane EEPROM */ + /* check to see if we have sane EEPROM */ signature = (u16) read_eeprom(ioaddr, EEPROMSignature); if (signature == 0xffff || signature == 0x0000) { - printk (KERN_INFO "Error EERPOM read %x\n", signature); + printk (KERN_INFO "%s: Error EERPOM read %x\n", + net_dev->name, signature); return NULL; } @@ -247,9 +247,10 @@ static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_ printk("%2.2x:", (u8)net_dev->dev_addr[i]); printk("%2.2x.\n", net_dev->dev_addr[i]); - if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) - /* FIXME: possible mem leak here */ - return NULL; + if ((net_dev->priv = kmalloc(sizeof(struct sis900_private), GFP_KERNEL)) == NULL) { + unregister_netdevice(net_dev); + return NULL; + } sis_priv = net_dev->priv; memset(sis_priv, 0, sizeof(struct sis900_private)); @@ -263,8 +264,9 @@ static struct device * sis900_mac_probe (struct mac_chip_info * mac, struct pci_ /* probe for mii transciver */ if (sis900_mii_probe(net_dev) == 0) { - /* FIXME: how to clean up this */ - release_region (ioaddr, mac->io_size); + unregister_netdev(net_dev); + kfree(sis_priv); + release_region(ioaddr, mac->io_size); return NULL; } @@ -289,7 +291,7 @@ static int sis900_mii_probe (struct device * net_dev) sis_priv->mii = NULL; - /* search for total of 32 possible mii phy address */ + /* search for total of 32 possible mii phy addresses */ for (phy_addr = 0; phy_addr < 32; phy_addr++) { u16 mii_status; u16 phy_id0, phy_id1; @@ -320,20 +322,23 @@ static int sis900_mii_probe (struct device * net_dev) mii_phy->next = sis_priv->mii; sis_priv->mii = mii_phy; } - /* the current mii is on our mii_info_table, quit searching (table) */ + /* the current mii is on our mii_info_table, + quit searching (table) */ break; } } if (sis_priv->mii == NULL) { - printk(KERN_INFO "%s: No MII transceivers found!\n", net_dev->name); + printk(KERN_INFO "%s: No MII transceivers found!\n", + net_dev->name); return 0; } /* FIXME: AMD stuff should be added */ /* auto negotiate FIXME: not completed */ elSetCapability(net_dev, sis_priv->mii->phy_addr, 1, 100); - sis_priv->mii->status = elAutoNegotiate(net_dev, sis_priv->mii->phy_addr, + sis_priv->mii->status = elAutoNegotiate(net_dev, + sis_priv->mii->phy_addr, &sis_priv->full_duplex, &sis_priv->speeds); @@ -396,7 +401,6 @@ static u16 read_eeprom(long ioaddr, int location) /* Read and write the MII management registers using software-generated serial MDIO protocol. Note that the command bits and data bits are send out seperately */ - #define mdio_delay() inl(mdio_addr) static void mdio_idle(long mdio_addr) @@ -493,8 +497,6 @@ sis900_open(struct device *dev) { struct sis900_private *sis_priv = (struct sis900_private *)dev->priv; long ioaddr = dev->base_addr; - int i = 0; - u32 status = TxRCMP | RxRCMP; /* Soft reset the chip. */ sis900_reset(dev); @@ -503,34 +505,13 @@ sis900_open(struct device *dev) return -EAGAIN; } - MOD_INC_USE_COUNT; - - if ((sis_priv->tx_bufs = kmalloc(TX_BUF_SIZE * NUM_TX_DESC, GFP_KERNEL)) == NULL) { - printk(KERN_ERR "%s: Can't allocate a %d byte TX Bufs.\n", - dev->name, TX_BUF_SIZE * NUM_TX_DESC); - return -ENOMEM; - } - if ((sis_priv->rx_bufs = kmalloc(RX_BUF_SIZE * NUM_RX_DESC, GFP_KERNEL)) == NULL) { - kfree (sis_priv->tx_buf); - printk(KERN_ERR "%s: Can't allocate a %d byte RX Bufs.\n", - dev->name, RX_BUF_SIZE * NUM_RX_DESC); - return -ENOMEM; - } + MOD_INC_USE_COUNT; + /* FIXME: should this be move to set_rx_mode() ? */ sis900_init_rxfilter(dev); - sis900_reset_tx_ring(dev); - sis900_reset_rx_ring(dev); - - if (sis900_debug > 4) - printk(KERN_INFO "%s: txdp:%8.8x\n", dev->name, inl(ioaddr + txdp)); - - /* Check that the chip has finished the reset. */ - while (status && (i++ < 30000)) { - status ^= (inl(isr + ioaddr) & status); - } - - outl(PESEL, ioaddr + cfg); + sis900_init_tx_ring(dev); + sis900_init_rx_ring(dev); /* FIXME: should be removed, and replaced by AutoNeogotiate stuff */ outl((RX_DMA_BURST << RxMXDMA_shift) | (RxDRNT_10 << RxDRNT_shift), @@ -538,15 +519,6 @@ sis900_open(struct device *dev) outl(TxATP | (TX_DMA_BURST << TxMXDMA_shift) | (TX_FILL_THRESH << TxFILLT_shift) | TxDRNT_10, ioaddr + txcfg); - if (sis_priv->LinkOn) { - printk(KERN_INFO "%s: Media Type %s%s-duplex.\n", - dev->name, - sis_priv->speeds == HW_SPEED_100_MBPS ? "100mbps " : "10mbps ", - sis_priv->full_duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half"); - } else { - printk(KERN_INFO "%s: Media Link Off\n", dev->name); - } - set_rx_mode(dev); dev->tbusy = 0; @@ -554,14 +526,14 @@ sis900_open(struct device *dev) dev->start = 1; /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr); outl(RxENA, ioaddr + cr); outl(IE, ioaddr + ier); /* Set the timer to switch to check for link beat and perhaps switch to an alternate media type. */ init_timer(&sis_priv->timer); - sis_priv->timer.expires = jiffies + 2*HZ; + sis_priv->timer.expires = jiffies + HZ; sis_priv->timer.data = (unsigned long)dev; sis_priv->timer.function = &sis900_timer; add_timer(&sis_priv->timer); @@ -582,6 +554,7 @@ sis900_init_rxfilter (struct device * net_dev) /* disable packet filtering before setting filter */ outl(rfcrSave & ~RFEN, rfcr); + /* load MAC addr to filter data register */ for (i = 0 ; i < 3 ; i++) { u32 w; @@ -589,12 +562,87 @@ sis900_init_rxfilter (struct device * net_dev) outl((i << RFADDR_shift), ioaddr + rfcr); outl(w, ioaddr + rfdr); - if (sis900_debug > 4) { + if (sis900_debug > 2) { printk(KERN_INFO "%s: Receive Filter Addrss[%d]=%x\n", net_dev->name, i, inl(ioaddr + rfdr)); } } - outl(rfcrSave, rfcr + ioaddr); + + /* enable packet filitering */ + outl(rfcrSave | RFEN, rfcr + ioaddr); +} + +/* Initialize the Tx ring. */ +static void +sis900_init_tx_ring(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + tp->tx_full = 0; + tp->dirty_tx = tp->cur_tx = 0; + + for (i = 0; i < NUM_TX_DESC; i++) { + tp->tx_skbuff[i] = NULL; + + tp->tx_ring[i].link = (u32) virt_to_bus(&tp->tx_ring[i+1]); + tp->tx_ring[i].cmdsts = 0; + tp->tx_ring[i].bufptr = 0; + } + tp->tx_ring[i-1].link = (u32) virt_to_bus(&tp->tx_ring[0]); + + /* load Transmit Descriptor Register */ + outl(virt_to_bus(&tp->tx_ring[0]), ioaddr + txdp); + if (sis900_debug > 2) + printk(KERN_INFO "%s: TX descriptor register loaded with: %8.8x\n", + dev->name, inl(ioaddr + txdp)); +} + +/* Initialize the Rx descriptor ring, pre-allocate recevie buffers */ +static void +sis900_init_rx_ring(struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + long ioaddr = dev->base_addr; + int i; + + tp->cur_rx = 0; + tp->dirty_rx = 0; + + /* init RX descriptor */ + for (i = 0; i < NUM_RX_DESC; i++) { + tp->rx_skbuff[i] = NULL; + + tp->rx_ring[i].link = (u32) virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_ring[i].cmdsts = 0; + tp->rx_ring[i].bufptr = 0; + } + tp->rx_ring[i-1].link = (u32) virt_to_bus(&tp->rx_ring[0]); + + /* allocate sock buffers */ + for (i = 0; i < NUM_RX_DESC; i++) { + struct sk_buff *skb; + + if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { + /* not enough memory for skbuff, this makes a "hole" + on the buffer ring, it is not clear how the + hardware will react to this kind of degenerated + buffer */ + break; + } + skb->dev = dev; + tp->rx_skbuff[i] = skb; + tp->rx_ring[i].cmdsts = RX_BUF_SIZE; + tp->rx_ring[i].bufptr = virt_to_bus(skb->tail); + } + tp->dirty_rx = (unsigned int) (i - NUM_RX_DESC); + + /* load Receive Descriptor Register */ + outl(virt_to_bus(&tp->rx_ring[0]), ioaddr + rxdp); + if (sis900_debug > 2) + printk(KERN_INFO "%s: RX descriptor register loaded with: %8.8x\n", + dev->name, inl(ioaddr + rxdp)); } static void sis900_timer(unsigned long data) @@ -604,6 +652,7 @@ static void sis900_timer(unsigned long data) int next_tick = 2*HZ; u16 status; + /* FIXME: Should we check transmission time out here ? */ /* FIXME: call auto negotiate routine to detect link status */ if (!tp->LinkOn) { status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); @@ -611,9 +660,12 @@ static void sis900_timer(unsigned long data) elPMDreadMode(dev, tp->mii->phy_addr, &tp->speeds, &tp->full_duplex); tp->LinkOn = TRUE; - printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", dev->name, - tp->speeds == HW_SPEED_100_MBPS ? "100mbps " : "10mbps ", - tp->full_duplex == FDX_CAPABLE_FULL_SELECTED ? "full" : "half"); + printk(KERN_INFO "%s: Media Link On %s%s-duplex \n", + dev->name, + tp->speeds == HW_SPEED_100_MBPS ? + "100mbps " : "10mbps ", + tp->full_duplex == FDX_CAPABLE_FULL_SELECTED ? + "full" : "half"); } } else { // previous link on status = mdio_read(dev, tp->mii->phy_addr, MII_STATUS); @@ -623,136 +675,31 @@ static void sis900_timer(unsigned long data) } } - if (next_tick) { - tp->timer.expires = jiffies + next_tick; - add_timer(&tp->timer); - } + tp->timer.expires = jiffies + next_tick; + add_timer(&tp->timer); } static void sis900_tx_timeout(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; long ioaddr = dev->base_addr; - int i; - if (sis900_debug > 0) + if (sis900_debug > 2) printk(KERN_INFO "%s: Transmit timeout, status %2.2x %4.4x \n", - dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); - + dev->name, inl(ioaddr + cr), inl(ioaddr + isr)); + /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); - /* Emit info to figure out what went wrong. */ - if (sis900_debug > 1) { - printk(KERN_INFO "%s:Tx queue start entry %d dirty entry %d.\n", - dev->name, tp->cur_tx, tp->dirty_tx); - for (i = 0; i < NUM_TX_DESC; i++) - printk(KERN_INFO "%s: Tx descriptor %d is %8.8x.%s\n", - dev->name, i, (unsigned int)&tp->tx_buf[i], - i == tp->dirty_tx % NUM_TX_DESC ? - " (queue head)" : ""); - } - - - tp->cur_rx = 0; - - { /* Save the unsent Tx packets. */ - struct sk_buff *saved_skb[NUM_TX_DESC], *skb; - int j; - for (j = 0; tp->cur_tx - tp->dirty_tx > 0 ; j++, tp->dirty_tx++) - saved_skb[j]=tp->tx_skbuff[tp->dirty_tx % NUM_TX_DESC]; - tp->dirty_tx = tp->cur_tx = 0; - - for (i = 0; i < j; i++) { - skb = tp->tx_skbuff[i] = saved_skb[i]; - /* Always alignment */ - memcpy((unsigned char*)(tp->tx_buf[i].buf), - skb->data, skb->len); - tp->tx_buf[i].cmdsts = OWN | skb->len; - } - outl(TxENA, ioaddr + cr); - tp->cur_tx = i; - while (i < NUM_TX_DESC) - tp->tx_skbuff[i++] = 0; - if (tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ - dev->tbusy = 0; - tp->tx_full = 0; - } else { - tp->tx_full = 1; - } - } - + tp->cur_rx = 0; dev->trans_start = jiffies; tp->stats.tx_errors++; - /* Enable all known interrupts by setting the interrupt mask. */ - outl((RxRCMP|RxOK|RxERR|RxORN|RxSOVR|TxOK|TxERR|TxURN), ioaddr + imr); - return; -} - -/* Reset (Initialize) the Tx rings, along with various 'dev' bits. */ -static void -sis900_reset_tx_ring(struct device *dev) -{ - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; - int i; - - tp->tx_full = 0; - tp->dirty_tx = tp->cur_tx = 0; - - /* Tx Buffer */ - for (i = 0; i < NUM_TX_DESC; i++) { - tp->tx_skbuff[i] = 0; - tp->tx_buf[i].buf = &tp->tx_bufs[i*TX_BUF_SIZE]; - tp->tx_buf[i].bufPhys = - virt_to_bus(&tp->tx_bufs[i*TX_BUF_SIZE]); - } - - /* Tx Descriptor */ - for (i = 0; i< NUM_TX_DESC; i++) { - tp->tx_buf[i].llink = (u32) - &(tp->tx_buf[((i+1) < NUM_TX_DESC) ? (i+1) : 0]); - tp->tx_buf[i].plink = (u32) - virt_to_bus(&(tp->tx_buf[((i+1) < NUM_TX_DESC) ? - (i+1) : 0].plink)); - tp->tx_buf[i].physAddr= - virt_to_bus(&(tp->tx_buf[i].plink)); - tp->tx_buf[i].cmdsts=0; - } - - outl((u32)tp->tx_buf[0].physAddr, ioaddr + txdp); -} -/* Reset (Initialize) the Rx rings, along with various 'dev' bits. */ -static void -sis900_reset_rx_ring(struct device *dev) -{ - struct sis900_private *tp = (struct sis900_private *)dev->priv; - long ioaddr = dev->base_addr; - int i; - - tp->cur_rx = 0; + /* FIXME: Should we restart the transmission thread here ?? */ - /* Rx Buffer */ - for (i = 0; i < NUM_RX_DESC; i++) { - tp->rx_buf[i].buf = &tp->rx_bufs[i*RX_BUF_SIZE]; - tp->rx_buf[i].bufPhys = - virt_to_bus(&tp->rx_bufs[i*RX_BUF_SIZE]); - } - - /* Rx Descriptor */ - for (i = 0; i< NUM_RX_DESC; i++) { - tp->rx_buf[i].llink = (u32) - &(tp->rx_buf[((i+1) < NUM_RX_DESC) ? (i+1) : 0]); - tp->rx_buf[i].plink = (u32) - virt_to_bus(&(tp->rx_buf[((i+1) < NUM_RX_DESC) ? - (i+1) : 0].plink)); - tp->rx_buf[i].physAddr= - virt_to_bus(&(tp->rx_buf[i].plink)); - tp->rx_buf[i].cmdsts=RX_BUF_SIZE; - } - - outl((u32)tp->rx_buf[0].physAddr, ioaddr + rxdp); + /* Enable all known interrupts by setting the interrupt mask. */ + outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxOK), ioaddr + imr); + return; } static int @@ -760,54 +707,39 @@ sis900_start_xmit(struct sk_buff *skb, struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; long ioaddr = dev->base_addr; - int entry; + unsigned int entry; - /* Block a timer-based transmit from overlapping. This could better be - done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ + /* test tbusy to see if we have timeout situation then set it */ if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { - if (jiffies - dev->trans_start < TX_TIMEOUT) - return 1; - sis900_tx_timeout(dev); + if (jiffies - dev->trans_start > TX_TIMEOUT) + sis900_tx_timeout(dev); return 1; } - /* Calculate the next Tx descriptor entry. ????? */ + /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % NUM_TX_DESC; - tp->tx_skbuff[entry] = skb; - if (sis900_debug > 5) { - int i; - printk(KERN_INFO "%s: SKB Tx Frame contents:(len=%d)", - dev->name,skb->len); - - for (i = 0; i < skb->len; i++) { - printk("%2.2x ", - (u8)skb->data[i]); - } - printk(".\n"); - } - - memcpy(tp->tx_buf[entry].buf, - skb->data, skb->len); - - tp->tx_buf[entry].cmdsts=(OWN | skb->len); - - //tp->tx_buf[entry].plink = 0; + /* set the transmit buffer descriptor and enable Transmit State Machine */ + tp->tx_ring[entry].bufptr = virt_to_bus(skb->data); + tp->tx_ring[entry].cmdsts = (OWN | skb->len); outl(TxENA, ioaddr + cr); - if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) {/* Typical path */ + + if (++tp->cur_tx - tp->dirty_tx < NUM_TX_DESC) { + /* Typical path, clear tbusy to indicate more + transmission is possible */ clear_bit(0, (void*)&dev->tbusy); } else { + /* no more transmit descriptor avaiable, tbusy remain set */ tp->tx_full = 1; } - /* Note: the chip doesn't have auto-pad! */ - dev->trans_start = jiffies; - if (sis900_debug > 4) - printk(KERN_INFO "%s: Queued Tx packet at " - "%p size %d to slot %d.\n", - dev->name, skb->data, (int)skb->len, entry); + + if (sis900_debug > 3) + printk(KERN_INFO "%s: Queued Tx packet at %p size %d " + "to slot %d.\n", + dev->name, skb->data, (int)skb->len, entry); return 0; } @@ -817,24 +749,22 @@ sis900_start_xmit(struct sk_buff *skb, struct device *dev) static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) { struct device *dev = (struct device *)dev_instance; - struct sis900_private *tp = (struct sis900_private *)dev->priv; int boguscnt = max_interrupt_work; - int status; - long ioaddr = dev->base_addr; + long ioaddr = dev->base_addr; + u32 status; #if defined(__i386__) /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ - if (test_and_set_bit(0, (void*)&dev->interrupt) -) { + if (test_and_set_bit(0, (void*)&dev->interrupt)) { printk(KERN_INFO "%s: SMP simultaneous entry of " - "an interrupt handler.\n", dev->name); + "an interrupt handler.\n", dev->name); dev->interrupt = 0; /* Avoid halting machine. */ return; } #else if (dev->interrupt) { - printk(KERN_INFO "%s: Re-entering the " - "interrupt handler.\n", dev->name); + printk(KERN_INFO "%s: Re-entering the interrupt handler.\n", + dev->name); return; } dev->interrupt = 1; @@ -842,158 +772,45 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) do { status = inl(ioaddr + isr); + + if (sis900_debug > 3) + printk(KERN_INFO "%s: entering interrupt, " + "original status = %#8.8x, " + "new status = %#8.8x.\n", + dev->name, status, inl(ioaddr + isr)); - if (sis900_debug > 4) - printk(KERN_INFO "%s: interrupt status=%#4.4x " - "new intstat=%#4.4x.\n", - dev->name, status, inl(ioaddr + isr)); - - if ((status & (TxURN|TxERR|TxOK | RxORN|RxERR|RxOK)) == 0) { + if ((status & (HIBERR|TxURN|TxERR|TxOK|RxORN|RxERR|RxOK)) == 0) /* nothing intresting happened */ break; - } - if (status & (RxOK|RxORN|RxERR)) /* Rx interrupt */ + /* why dow't we break after Tx/Rx case ?? keyword: full-duplex */ + if (status & (RxORN | RxERR | RxOK)) + /* Rx interrupt */ sis900_rx(dev); - if (status & (TxOK | TxERR)) { - unsigned int dirty_tx; - - if (sis900_debug > 5) { - printk(KERN_INFO "TxOK:tp->cur_tx:%d," - "tp->dirty_tx:%x\n", - tp->cur_tx, tp->dirty_tx); - } - for (dirty_tx = tp->dirty_tx; dirty_tx < tp->cur_tx; - dirty_tx++) - { - int i; - int entry = dirty_tx % NUM_TX_DESC; - int txstatus = tp->tx_buf[entry].cmdsts; - - if (sis900_debug > 4) { - printk(KERN_INFO "%s: Tx Frame contents:" - "(len=%d)", - dev->name, (txstatus & DSIZE)); - - for (i = 0; i < (txstatus & DSIZE) ; - i++) { - printk("%2.2x ", - (u8)(tp->tx_buf[entry].buf[i])); - } - printk(".\n"); - } - if ( ! (txstatus & (OK | UNDERRUN))) - { - if (sis900_debug > 1) - printk(KERN_INFO "Tx NOT (OK," - "UnderRun)\n"); - break; /* It still hasn't been Txed */ - } - - /* Note: TxCarrierLost is always asserted - at 100mbps. */ - if (txstatus & (OWCOLL | ABORT)) { - /* There was an major error, log it. */ - if (sis900_debug > 1) - printk(KERN_INFO "Tx Out of " - " Window,Abort\n"); -#ifndef final_version - if (sis900_debug > 1) - printk(KERN_INFO "%s: Transmit " - "error, Tx status %8.8x.\n", - dev->name, txstatus); -#endif - tp->stats.tx_errors++; - if (txstatus & ABORT) { - tp->stats.tx_aborted_errors++; - } - if (txstatus & NOCARRIER) - tp->stats.tx_carrier_errors++; - if (txstatus & OWCOLL) - tp->stats.tx_window_errors++; -#ifdef ETHER_STATS - if ((txstatus & COLCNT)==COLCNT) - tp->stats.collisions16++; -#endif - } else { -#ifdef ETHER_STATS - /* No count for tp->stats.tx_deferred */ -#endif - if (txstatus & UNDERRUN) { - if (sis900_debug > 2) - printk(KERN_INFO "Tx UnderRun\n"); - } - tp->stats.collisions += - (txstatus >> 16) & 0xF; -#if LINUX_VERSION_CODE > 0x20119 - tp->stats.tx_bytes += txstatus & DSIZE; -#endif - if (sis900_debug > 2) - printk(KERN_INFO "Tx Transmit OK\n"); - tp->stats.tx_packets++; - } - - /* Free the original skb. */ - if (sis900_debug > 2) - printk(KERN_INFO "Free original skb\n"); - dev_kfree_skb(tp->tx_skbuff[entry]); - tp->tx_skbuff[entry] = 0; - } // for dirty - -#ifndef final_version - if (tp->cur_tx - dirty_tx > NUM_TX_DESC) { - printk(KERN_INFO"%s: Out-of-sync dirty pointer," - " %d vs. %d, full=%d.\n", - dev->name, dirty_tx, - tp->cur_tx, tp->tx_full); - dirty_tx += NUM_TX_DESC; - } -#endif + if (status & (TxURN | TxERR | TxOK)) + /* Tx interrupt */ + sis900_finish_xmit(dev); - if (tp->tx_full && dirty_tx > tp->cur_tx-NUM_TX_DESC) { - /* The ring is no longer full, clear tbusy. */ - if (sis900_debug > 3) - printk(KERN_INFO "Tx Ring NO LONGER Full\n"); - tp->tx_full = 0; - dev->tbusy = 0; - mark_bh(NET_BH); - } - - tp->dirty_tx = dirty_tx; - if (sis900_debug > 2) - printk(KERN_INFO "TxOK,tp->cur_tx:%d,tp->dirty:%d\n", - tp->cur_tx, tp->dirty_tx); - } // if (TxOK | TxERR) - - /* Check uncommon events with one test. */ - if (status & (RxORN | TxERR | RxERR)) { - if (sis900_debug > 2) - printk(KERN_INFO "%s: Abnormal interrupt," - "status %8.8x.\n", dev->name, status); - - if (status == 0xffffffff) - break; - if (status & (RxORN | RxERR)) - tp->stats.rx_errors++; - - - if (status & RxORN) { - tp->stats.rx_over_errors++; - } - } + /* something strange happened !!! */ + if (status & HIBERR) { + printk(KERN_INFO "%s: Abnormal interrupt," + "status %#8.8x.\n", dev->name, status); + break; + } if (--boguscnt < 0) { printk(KERN_INFO "%s: Too much work at interrupt, " - "IntrStatus=0x%4.4x.\n", - dev->name, status); + "interrupt Status = %#8.8x.\n", + dev->name, status); break; } } while (1); - + if (sis900_debug > 3) - printk(KERN_INFO "%s: exiting interrupt, intr_status=%#4.4x.\n", - dev->name, inl(ioaddr + isr)); - + printk(KERN_INFO "%s: exiting interrupt, " + "interrupt status = 0x%#8.8x.\n", + dev->name, inl(ioaddr + isr)); + #if defined(__i386__) clear_bit(0, (void*)&dev->interrupt); #else @@ -1002,122 +819,160 @@ static void sis900_interrupt(int irq, void *dev_instance, struct pt_regs *regs) return; } +/* Process receive interrupt events, put buffer to higher layer and refill buffer pool + Note: This fucntion is called by interrupt handler, don't do "too much" work here */ static int sis900_rx(struct device *dev) { struct sis900_private *tp = (struct sis900_private *)dev->priv; long ioaddr = dev->base_addr; - u16 cur_rx = tp->cur_rx % NUM_RX_DESC; - int rx_status=tp->rx_buf[cur_rx].cmdsts; - - if (sis900_debug > 4) - printk(KERN_INFO "%s: sis900_rx, current %4.4x," - " rx status=%8.8x\n", - dev->name, cur_rx, - rx_status); + unsigned int entry = tp->cur_rx % NUM_RX_DESC; + u32 rx_status = tp->rx_ring[entry].cmdsts; + if (sis900_debug > 3) + printk(KERN_INFO "sis900_rx, cur_rx:%4.4d, dirty_rx:%4.4d " + "status:0x%8.8x\n", + tp->cur_rx, tp->dirty_rx,rx_status); + while (rx_status & OWN) { - int rx_size = rx_status & DSIZE; - rx_size -= CRC_SIZE; - - if (sis900_debug > 4) { - int i; - printk(KERN_INFO "%s: sis900_rx, rx status %8.8x," - " size %4.4x, cur %4.4x.\n", - dev->name, rx_status, rx_size, cur_rx); - printk(KERN_INFO "%s: Rx Frame contents:", dev->name); - - for (i = 0; i < rx_size; i++) { - printk("%2.2x ", - (u8)(tp->rx_buf[cur_rx].buf[i])); - } - - printk(".\n"); - } - if (rx_status & TOOLONG) { - if (sis900_debug > 1) - printk(KERN_INFO "%s: Oversized Ethernet frame," - " status %4.4x!\n", - dev->name, rx_status); - tp->stats.rx_length_errors++; - } else if (rx_status & (RXISERR | RUNT | CRCERR | FAERR)) { - if (sis900_debug > 1) - printk(KERN_INFO"%s: Ethernet frame had errors," - " status %4.4x.\n", - dev->name, rx_status); + unsigned int rx_size; + + rx_size = (rx_status & DSIZE) - CRC_SIZE; + + if (rx_status & (ABORT|OVERRUN|TOOLONG|RUNT|RXISERR|CRCERR|FAERR)) { + /* corrupted packet received */ + if (sis900_debug > 3) + printk(KERN_INFO "%s: Corrupted packet " + "received, buffer status = 0x%8.8x.\n", + dev->name, rx_status); tp->stats.rx_errors++; + if (rx_status & OVERRUN) + tp->stats.rx_over_errors++; + if (rx_status & (TOOLONG|RUNT)) + tp->stats.rx_length_errors++; if (rx_status & (RXISERR | FAERR)) tp->stats.rx_frame_errors++; - if (rx_status & (RUNT | TOOLONG)) - tp->stats.rx_length_errors++; - if (rx_status & CRCERR) tp->stats.rx_crc_errors++; + if (rx_status & CRCERR) + tp->stats.rx_crc_errors++; + /* reset buffer descriptor state */ + tp->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { - /* Malloc up new buffer, compatible with net-2e. */ - /* Omit the four octet CRC from the length. */ - struct sk_buff *skb; - - skb = dev_alloc_skb(rx_size + 2); - if (skb == NULL) { - printk(KERN_INFO "%s: Memory squeeze," - "deferring packet.\n", - dev->name); - /* We should check that some rx space is free. - If not, - free one and mark stats->rx_dropped++. */ - tp->stats.rx_dropped++; - tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; - break; - } - skb->dev = dev; - skb_reserve(skb, 2); /* 16 byte align the IP fields. */ - if (rx_size+CRC_SIZE > RX_BUF_SIZE) { - /* - int semi_count = RX_BUF_LEN - ring_offset - 4; - memcpy(skb_put(skb, semi_count), - &rx_bufs[ring_offset + 4], semi_count); - memcpy(skb_put(skb, rx_size-semi_count), - rx_bufs, rx_size - semi_count); - if (sis900_debug > 4) { - int i; - printk(KERN_DEBUG"%s: Frame wrap @%d", - dev->name, semi_count); - for (i = 0; i < 16; i++) - printk(" %2.2x", rx_bufs[i]); - printk(".\n"); - memset(rx_bufs, 0xcc, 16); - } - */ - } else { -#if 0 /* USE_IP_COPYSUM */ - eth_copy_and_sum(skb, - tp->rx_buf[cur_rx].buf, rx_size, 0); - skb_put(skb, rx_size); -#else - memcpy(skb_put(skb, rx_size), - tp->rx_buf[cur_rx].buf, rx_size); -#endif - } + struct sk_buff * skb; + + if (tp->rx_skbuff[entry] == NULL) { + printk(KERN_INFO "%s: NULL pointer " + "encountered in Rx ring, skipping\n", + dev->name); + break; + } + skb = tp->rx_skbuff[entry]; + tp->rx_skbuff[entry] = NULL; + /* reset buffer descriptor state */ + tp->rx_ring[entry].cmdsts = 0; + tp->rx_ring[entry].bufptr = 0; + + skb_put(skb, rx_size); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); -#if LINUX_VERSION_CODE > 0x20119 + + if (rx_status & MCAST) + tp->stats.multicast++; tp->stats.rx_bytes += rx_size; -#endif tp->stats.rx_packets++; } - tp->rx_buf[cur_rx].cmdsts = RX_BUF_SIZE; - - cur_rx = ((cur_rx+1) % NUM_RX_DESC); - rx_status = tp->rx_buf[cur_rx].cmdsts; + tp->cur_rx++; + entry = tp->cur_rx % NUM_RX_DESC; + rx_status = tp->rx_ring[entry].cmdsts; } // while - if (sis900_debug > 4) - printk(KERN_INFO "%s: Done sis900_rx(), current %4.4x " - "Cmd %2.2x.\n", - dev->name, cur_rx, - inb(ioaddr + cr)); - tp->cur_rx = cur_rx; - outl( RxENA , ioaddr + cr ); /* LCS */ + + /* refill the Rx buffer, what if the rate of refilling is slower than + consuming ?? */ + for (;tp->cur_rx - tp->dirty_rx > 0; tp->dirty_rx++) { + struct sk_buff *skb; + + entry = tp->dirty_rx % NUM_RX_DESC; + + if (tp->rx_skbuff[entry] == NULL) { + if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { + /* not enough memory for skbuff, this makes a "hole" + on the buffer ring, it is not clear how the + hardware will react to this kind of degenerated + buffer */ + printk(KERN_INFO "%s: Memory squeeze," + "deferring packet.\n", + dev->name); + tp->stats.rx_dropped++; + break; + } + skb->dev = dev; + tp->rx_skbuff[entry] = skb; + tp->rx_ring[entry].cmdsts = RX_BUF_SIZE; + tp->rx_ring[entry].bufptr = virt_to_bus(skb->tail); + } + } + /* re-enable the potentially idle receive state matchine */ + outl(RxENA , ioaddr + cr ); + return 0; } +/* finish up transmission of packets, check for error condition and free skbuff etc. + Note: This fucntion is called by interrupt handler, don't do "too much" work here */ +static void sis900_finish_xmit (struct device *dev) +{ + struct sis900_private *tp = (struct sis900_private *)dev->priv; + + for (; tp->dirty_tx < tp->cur_tx; tp->dirty_tx++) { + unsigned int entry; + u32 tx_status; + + entry = tp->dirty_tx % NUM_TX_DESC; + tx_status = tp->tx_ring[entry].cmdsts; + + if (tx_status & OWN) { + /* The packet is not transmited yet (owned by hardware) ! */ + break; + } + + if (tx_status & (ABORT | UNDERRUN | OWCOLL)) { + /* packet unsuccessfully transmited */ + if (sis900_debug > 3) + printk(KERN_INFO "%s: Transmit " + "error, Tx status %8.8x.\n", + dev->name, tx_status); + tp->stats.tx_errors++; + if (tx_status & UNDERRUN) + tp->stats.tx_fifo_errors++; + if (tx_status & ABORT) + tp->stats.tx_aborted_errors++; + if (tx_status & NOCARRIER) + tp->stats.tx_carrier_errors++; + if (tx_status & OWCOLL) + tp->stats.tx_window_errors++; + } else { + /* packet successfully transmited */ + if (sis900_debug > 3) + printk(KERN_INFO "Tx Transmit OK\n"); + tp->stats.collisions += (tx_status & COLCNT) >> 16; + tp->stats.tx_bytes += tx_status & DSIZE; + tp->stats.tx_packets++; + } + /* Free the original skb. */ + dev_kfree_skb(tp->tx_skbuff[entry]); + tp->tx_skbuff[entry] = NULL; + tp->tx_ring[entry].bufptr = 0; + tp->tx_ring[entry].cmdsts = 0; + } + + if (tp->tx_full && dev->tbusy && + tp->cur_tx - tp->dirty_tx < NUM_TX_DESC - 4) { + /* The ring is no longer full, clear tbusy, tx_full and schedule + more transmission by marking NET_BH */ + tp->tx_full = 0; + clear_bit(0, (void *)&dev->tbusy); + mark_bh(NET_BH); + } +} + static int sis900_close(struct device *dev) { @@ -1130,21 +985,26 @@ sis900_close(struct device *dev) /* Disable interrupts by clearing the interrupt mask. */ outl(0x0000, ioaddr + imr); + outl(0x0000, ioaddr + ier); - /* Stop the chip's Tx and Rx DMA processes. */ - outl(0x00, ioaddr + cr); + /* Stop the chip's Tx and Rx Status Machine */ + outl(RxDIS | TxDIS, ioaddr + cr); del_timer(&tp->timer); free_irq(dev->irq, dev); + /* Free Tx and RX skbuff */ + for (i = 0; i < NUM_RX_DESC; i++) { + if (tp->rx_skbuff[i] != NULL) + dev_kfree_skb(tp->rx_skbuff[i]); + tp->rx_skbuff[i] = 0; + } for (i = 0; i < NUM_TX_DESC; i++) { - if (tp->tx_skbuff[i]) + if (tp->tx_skbuff[i] != NULL) dev_kfree_skb(tp->tx_skbuff[i]); tp->tx_skbuff[i] = 0; } - kfree(tp->rx_bufs); - kfree(tp->tx_bufs); /* Green! Put the chip in low-power mode. */ @@ -1241,7 +1101,7 @@ static u16 elPMDreadMode(struct device *dev, *duplex = FDX_CAPABLE_HALF_SELECTED; status = mdio_read(dev, phy_id, MII_ANLPAR); - OurCap = mdio_read(dev, phy_id, MII_ANAR); + OurCap = mdio_read(dev, phy_id, MII_ANADV); if (sis900_debug > 1) { printk(KERN_INFO "Link Part Status %4X\n", status); printk(KERN_INFO "Our Status %4X\n", OurCap); @@ -1255,8 +1115,8 @@ static u16 elPMDreadMode(struct device *dev, if (sis900_debug > 1) { printk(KERN_INFO "The other end NOT support NWAY...\n"); } - while (( status = mdio_read(dev, phy_id, 18)) & 0x4000) ; - while (( status = mdio_read(dev, phy_id, 18)) & 0x0020) ; + while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x4000) ; + while (( status = mdio_read(dev, phy_id, MII_STSOUT)) & 0x0020) ; if (status & 0x80) *speed = HW_SPEED_100_MBPS; if (status & 0x40) @@ -1344,7 +1204,7 @@ static void elSetCapability(struct device *dev, int phy_id, } } - mdio_write(dev, phy_id, MII_ANAR, cap); + mdio_write(dev, phy_id, MII_ANADV, cap); } static void elSetMediaType(struct device *dev, int speed, int duplex) @@ -1412,9 +1272,7 @@ static void set_rx_mode(struct device *dev) outl((u32)(0x00000004+i) << 16, ioaddr + rfcr); outl(mc_filter[i], ioaddr + rfdr); } - /* We can safely update without stopping the chip. */ - //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS; - //rx_mode = ACCEPT_CAM_QUALIFIED | ACCEPT_ALL_BCASTS; + outl(RFEN | ((rx_mode & (ACCEPT_ALL_MCASTS | ACCEPT_ALL_BCASTS | ACCEPT_ALL_PHYS)) << RFAA_shift), ioaddr + rfcr); @@ -1443,8 +1301,8 @@ static void set_rx_mode(struct device *dev) if (sis900_debug > 2) { printk(KERN_INFO "After Set TxCfg=%8.8x\n",inl(ioaddr+txcfg)); printk(KERN_INFO "After Set RxCfg=%8.8x\n",inl(ioaddr+rxcfg)); - printk(KERN_INFO "Receive Filter Register:%8.8x\n", - inl(ioaddr + rfcr)); + printk(KERN_INFO "Receive Filter Register:%8.8x\n", + inl(ioaddr + rfcr)); } return; } @@ -1452,15 +1310,21 @@ static void set_rx_mode(struct device *dev) static void sis900_reset(struct device *dev) { long ioaddr = dev->base_addr; - + int i = 0; + u32 status = TxRCMP | RxRCMP; + outl(0, ioaddr + ier); outl(0, ioaddr + imr); outl(0, ioaddr + rfcr); outl(RxRESET | TxRESET | RESET, ioaddr + cr); - outl(PESEL, ioaddr + cfg); - set_rx_mode(dev); + /* Check that the chip has finished the reset. */ + while (status && (i++ < 1000)) { + status ^= (inl(isr + ioaddr) & status); + } + + outl(PESEL, ioaddr + cfg); } #ifdef MODULE diff --git a/drivers/net/sis900.h b/drivers/net/sis900.h index bb11533f957f..335d094e25a2 100644 --- a/drivers/net/sis900.h +++ b/drivers/net/sis900.h @@ -1,5 +1,5 @@ /* sis900.h Definitions for SiS ethernet controllers including 7014/7016 and 900 - * Copyrigth 1999 Silicon Integrated System Corporation + * Copyright 1999 Silicon Integrated System Corporation * References: * SiS 7016 Fast Ethernet PCI Bus 10/100 Mbps LAN Controller with OnNow Support, * preliminary Rev. 1.0 Jan. 14, 1998 @@ -15,7 +15,7 @@ #define SIS900_TOTAL_SIZE 0x100 /* Symbolic offsets to registers. */ -enum SIS900_registers { +enum sis900_registers { cr=0x0, //Command Register cfg=0x4, //Configuration Register mear=0x8, //EEPROM Access Register @@ -120,7 +120,7 @@ enum sis900_eeprom_address { /* The EEPROM commands include the alway-set leading bit. Refer to NM93Cxx datasheet */ enum sis900_eeprom_command { - EEread = 0x0180, EEwrite = 0x0140, EEerase = 0x01C0, + EEread = 0x0180, EEwrite = 0x0140, EEerase = 0x01C0, EEwriteEnable = 0x0130, EEwriteDisable = 0x0100, EEeraseAll = 0x0120, EEwriteAll = 0x0110, EEaddrMask = 0x013F, EEcmdShift = 16 @@ -134,48 +134,34 @@ enum sis900_eeprom_command { #define MIIcmdLen 16 #define MIIcmdShift 16 -/* Buffer Descriptor */ -#define OWN 0x80000000 -#define MORE 0x40000000 -#define INTR 0x20000000 -#define OK 0x08000000 -#define DSIZE 0x00000FFF - -#define SUPCRC 0x10000000 -#define ABORT 0x04000000 -#define UNDERRUN 0x02000000 -#define NOCARRIER 0x01000000 -#define DEFERD 0x00800000 -#define EXCDEFER 0x00400000 -#define OWCOLL 0x00200000 -#define EXCCOLL 0x00100000 -#define COLCNT 0x000F0000 - -#define INCCRC 0x10000000 -// ABORT 0x04000000 -#define OVERRUN 0x02000000 -#define DEST 0x01800000 -#define BCAST 0x01800000 -#define MCAST 0x01000000 -#define UNIMATCH 0x00800000 -#define TOOLONG 0x00400000 -#define RUNT 0x00200000 -#define RXISERR 0x00100000 -#define CRCERR 0x00080000 -#define FAERR 0x00040000 -#define LOOPBK 0x00020000 -#define RXCOL 0x00010000 - -#define RXSTS_shift 18 +/* Buffer Descriptor Status*/ +enum sis900_buffer_status { + OWN = 0x80000000, MORE = 0x40000000, INTR = 0x20000000, + SUPCRC = 0x10000000, INCCRC = 0x10000000, + OK = 0x08000000, DSIZE = 0x00000FFF +}; +/* Status for TX Buffers */ +enum sis900_tx_buffer_status { + ABORT = 0x04000000, UNDERRUN = 0x02000000, NOCARRIER = 0x01000000, + DEFERD = 0x00800000, EXCDEFER = 0x00400000, OWCOLL = 0x00200000, + EXCCOLL = 0x00100000, COLCNT = 0x000F0000 +}; + +enum sis900_rx_bufer_status { + OVERRUN = 0x02000000, DEST = 0x00800000, BCAST = 0x01800000, + MCAST = 0x01000000, UNIMATCH = 0x00800000, TOOLONG = 0x00400000, + RUNT = 0x00200000, RXISERR = 0x00100000, CRCERR = 0x00080000, + FAERR = 0x00040000, LOOPBK = 0x00020000, RXCOL = 0x00010000 +}; /* MII register offsets */ -#define MII_CONTROL 0x0000 -#define MII_STATUS 0x0001 -#define MII_PHY_ID0 0x0002 -#define MII_PHY_ID1 0x0003 -#define MII_ANAR 0x0004 -#define MII_ANLPAR 0x0005 -#define MII_ANER 0x0006 +enum mii_registers { + MII_CONTROL = 0x0000, MII_STATUS = 0x0001, MII_PHY_ID0 = 0x0002, + MII_PHY_ID1 = 0x0003, MII_ANADV = 0x0004, MII_ANLPAR = 0x0005, + MII_CONFIG1 = 0x0010, MII_CONFIG2 = 0x0011, MII_STSOUT = 0x0012, + MII_MASK = 0x0013 +}; + /* MII Control register bit definitions. */ #define MIICNTL_FDX 0x0100 #define MIICNTL_RST_AUTO 0x0200 @@ -229,11 +215,12 @@ enum sis900_eeprom_command { #define ACCEPT_ALL_PHYS 0x01 #define ACCEPT_ALL_MCASTS 0x02 #define ACCEPT_ALL_BCASTS 0x04 + #define ACCEPT_ALL_ERRORS 0x08 #define ACCEPT_CAM_QUALIFIED 0x10 + #define MAC_LOOPBACK 0x20 -//#define FDX_CAPABLE_FULL_SELECTED 4 #define CRC_SIZE 4 #define MAC_HEADER_SIZE 14 @@ -241,12 +228,12 @@ enum sis900_eeprom_command { #define RX_BUF_SIZE 1536 #define NUM_TX_DESC 16 /* Number of Tx descriptor registers. */ -#define NUM_RX_DESC 8 /* Number of Rx descriptor registers. */ +#define NUM_RX_DESC 16 /* Number of Rx descriptor registers. */ #define TRUE 1 #define FALSE 0 -/* PCI stuff, should be move to pci.h */ +/* PCI stuff, should be move to pic.h */ #define PCI_DEVICE_ID_SI_900 0x900 #define PCI_DEVICE_ID_SI_7016 0x7016 diff --git a/drivers/net/tulip.c b/drivers/net/tulip.c index c918d013d0d1..3b213fc510b5 100644 --- a/drivers/net/tulip.c +++ b/drivers/net/tulip.c @@ -18,7 +18,7 @@ */ #define SMP_CHECK -static const char version[] = "tulip.c:v0.91g 7/16/99 becker@cesdis.gsfc.nasa.gov\n"; +static const char version[] = "tulip.c:v0.91g-ppc 7/16/99 becker@cesdis.gsfc.nasa.gov\n"; /* A few user-configurable values. */ @@ -75,11 +75,7 @@ static int rx_copybreak = 100; #if defined(__alpha__) static int csr0 = 0x01A00000 | 0xE000; -#elif defined(__powerpc__) -static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) -static int csr0 = 0x01B00080 | 0x8000; -#elif defined(__i386__) +#elif defined(__i386__) || defined(__powerpc__) || defined(__sparc__) static int csr0 = 0x01A00000 | 0x8000; #else #warning Processor architecture undefined! @@ -183,6 +179,10 @@ static char kernel_version[] = UTS_RELEASE; #define netif_wake_queue(dev) mark_bh(NET_BH); #endif +/* Condensed operations for readability. */ +#define virt_to_le32desc(addr) cpu_to_le32(virt_to_bus(addr)) +#define le32desc_to_virt(addr) bus_to_virt(le32_to_cpu(addr)) + #define tulip_debug debug #ifdef TULIP_DEBUG static int tulip_debug = TULIP_DEBUG; @@ -1457,9 +1457,9 @@ tulip_open(struct device *dev) *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[1]; *setup_frm++ = eaddrs[2]; *setup_frm++ = eaddrs[2]; /* Put the setup frame on the Tx list. */ - tp->tx_ring[0].length = 0x08000000 | 192; - tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[0].status = DescOwned; + tp->tx_ring[0].length = cpu_to_le32(0x08000000 | 192); + tp->tx_ring[0].buffer1 = virt_to_le32desc(tp->setup_frame); + tp->tx_ring[0].status = cpu_to_le32(DescOwned); tp->cur_tx++; } @@ -1545,7 +1545,7 @@ media_picked: } else if (tp->chip_id == MX98715 || tp->chip_id == MX98725) { /* Provided by BOLO, Macronix - 12/10/1998. */ dev->if_port = 0; - tp->csr6 = 0x01880200; + tp->csr6 = 0x01a80200; outl(0x0f370000 | inw(ioaddr + 0x80), ioaddr + 0x80); outl(0x11000 | inw(ioaddr + 0xa0), ioaddr + 0xa0); } else if (tp->chip_id == DC21143 && @@ -1558,7 +1558,7 @@ media_picked: dev->if_port = 0; tp->csr6 = 0x00040000; } else if (tp->chip_id == AX88140) { - tp->csr6 = 0x00000100; + tp->csr6 = tp->mii_cnt ? 0x00040100 : 0x00000100; } else select_media(dev, 1); @@ -2487,14 +2487,13 @@ static void tulip_init_ring(struct device *dev) for (i = 0; i < RX_RING_SIZE; i++) { tp->rx_ring[i].status = 0x00000000; - tp->rx_ring[i].length = PKT_BUF_SZ; - tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]); + tp->rx_ring[i].length = cpu_to_le32(PKT_BUF_SZ); + tp->rx_ring[i].buffer2 = virt_to_le32desc(&tp->rx_ring[i+1]); tp->rx_skbuff[i] = NULL; } /* Mark the last entry as wrapping the ring. */ - tp->rx_ring[i-1].length = PKT_BUF_SZ | DESC_RING_WRAP; - tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]); - + tp->rx_ring[i-1].length = cpu_to_le32(PKT_BUF_SZ | DESC_RING_WRAP); + tp->rx_ring[i-1].buffer2 = virt_to_le32desc(&tp->rx_ring[0]); for (i = 0; i < RX_RING_SIZE; i++) { /* Note the receive buffer must be longword aligned. @@ -2505,8 +2504,8 @@ static void tulip_init_ring(struct device *dev) if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[i].status = DescOwned; /* Owned by Tulip chip */ - tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail); + tp->rx_ring[i].status = cpu_to_le32(DescOwned); /* Owned by Tulip chip */ + tp->rx_ring[i].buffer1 = virt_to_le32desc(skb->tail); } tp->dirty_rx = (unsigned int)(i - RX_RING_SIZE); @@ -2515,9 +2514,9 @@ static void tulip_init_ring(struct device *dev) for (i = 0; i < TX_RING_SIZE; i++) { tp->tx_skbuff[i] = 0; tp->tx_ring[i].status = 0x00000000; - tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]); + tp->tx_ring[i].buffer2 = virt_to_le32desc(&tp->tx_ring[i+1]); } - tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]); + tp->tx_ring[i-1].buffer2 = virt_to_le32desc(&tp->tx_ring[0]); } static int @@ -2536,14 +2535,14 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev) return 1; } - /* Caution: the write order is important here, set the base address - with the "ownership" bits last. */ + /* Caution: the write order is important here, set the field + with the ownership bits last. */ /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % TX_RING_SIZE; tp->tx_skbuff[entry] = skb; - tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); + tp->tx_ring[entry].buffer1 = virt_to_le32desc(skb->data); if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */ flag = 0x60000000; /* No interrupt */ @@ -2556,10 +2555,10 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev) flag = 0xe0000000; /* Tx-done intr. */ } if (entry == TX_RING_SIZE-1) - flag |= 0xe0000000 | DESC_RING_WRAP; + flag = 0xe0000000 | DESC_RING_WRAP; - tp->tx_ring[entry].length = skb->len | flag; - tp->tx_ring[entry].status = DescOwned; /* Pass ownership to the chip. */ + tp->tx_ring[entry].length = cpu_to_le32(skb->len | flag); + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); tp->cur_tx++; if ( ! tp->tx_full) clear_bit(0, (void*)&dev->tbusy); @@ -2617,10 +2616,10 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) for (dirty_tx = tp->dirty_tx; tp->cur_tx - dirty_tx > 0; dirty_tx++) { int entry = dirty_tx % TX_RING_SIZE; - int status = tp->tx_ring[entry].status; + int status = le32_to_cpu(tp->tx_ring[entry].status); if (status < 0) - break; /* It still hasn't been Txed */ + break; /* It still has not been Txed */ /* Check for Rx filter setup frames. */ if (tp->tx_skbuff[entry] == NULL) continue; @@ -2647,7 +2646,7 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) if (status & 0x0001) tp->stats.tx_deferred++; #endif #if LINUX_VERSION_CODE > 0x20127 - tp->stats.tx_bytes += tp->tx_ring[entry].length & 0x7ff; + tp->stats.tx_bytes += tp->tx_skbuff[entry]->len; #endif tp->stats.collisions += (status >> 3) & 15; tp->stats.tx_packets++; @@ -2724,13 +2723,14 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) /* Acknowledge all interrupt sources. */ outl(0x8001ffff, ioaddr + CSR5); /* Clear all interrupting sources, set timer to re-enable. */ - outl(((~csr5) & 0x0001ebef) | 0x0800, ioaddr + CSR7); + outl(((~csr5) & 0x0001ebef) | AbnormalIntr | TimerInt, + ioaddr + CSR7); outl(12, ioaddr + CSR11); break; } } while (1); - if (tulip_debug > 3) + if (tulip_debug > 4) printk(KERN_DEBUG "%s: exiting interrupt, csr5=%#4.4x.\n", dev->name, inl(ioaddr + CSR5)); @@ -2742,8 +2742,7 @@ static void tulip_interrupt(int irq, void *dev_instance, struct pt_regs *regs) return; } -static int -tulip_rx(struct device *dev) +static int tulip_rx(struct device *dev) { struct tulip_private *tp = (struct tulip_private *)dev->priv; int entry = tp->cur_rx % RX_RING_SIZE; @@ -2753,13 +2752,13 @@ tulip_rx(struct device *dev) if (tulip_debug > 4) printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, tp->rx_ring[entry].status); - /* If we own the next entry, it's a new packet. Send it up. */ - while (tp->rx_ring[entry].status >= 0) { - s32 status = tp->rx_ring[entry].status; + /* If we own the next entry, it is a new packet. Send it up. */ + while ( ! (tp->rx_ring[entry].status & cpu_to_le32(DescOwned))) { + s32 status = le32_to_cpu(tp->rx_ring[entry].status); if (tulip_debug > 5) - printk(KERN_DEBUG " In tulip_rx(), entry %d %8.8x.\n", entry, - tp->rx_ring[entry].status); + printk(KERN_DEBUG "%s: In tulip_rx(), entry %d %8.8x.\n", + dev->name, entry, status); if (--rx_work_limit < 0) break; if ((status & 0x38008300) != 0x0300) { @@ -2803,22 +2802,22 @@ tulip_rx(struct device *dev) skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ #if ! defined(__alpha__) - eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), - pkt_len, 0); + eth_copy_and_sum(skb, tp->rx_skbuff[entry]->tail, pkt_len, 0); skb_put(skb, pkt_len); #else - memcpy(skb_put(skb, pkt_len), - bus_to_virt(tp->rx_ring[entry].buffer1), pkt_len); + memcpy(skb_put(skb, pkt_len), tp->rx_skbuff[entry]->tail, + pkt_len); #endif work_done++; } else { /* Pass up the skb already on the Rx ring. */ char *temp = skb_put(skb = tp->rx_skbuff[entry], pkt_len); tp->rx_skbuff[entry] = NULL; #ifndef final_version - if (bus_to_virt(tp->rx_ring[entry].buffer1) != temp) + if (le32desc_to_virt(tp->rx_ring[entry].buffer1) != temp) printk(KERN_ERR "%s: Internal fault: The skbuff addresses " "do not match in tulip_rx: %p vs. %p / %p.\n", - dev->name, bus_to_virt(tp->rx_ring[entry].buffer1), + dev->name, + le32desc_to_virt(tp->rx_ring[entry].buffer1), skb->head, temp); #endif } @@ -2842,17 +2841,16 @@ tulip_rx(struct device *dev) if (skb == NULL) break; skb->dev = dev; /* Mark as being used by this device. */ - tp->rx_ring[entry].buffer1 = virt_to_bus(skb->tail); + tp->rx_ring[entry].buffer1 = virt_to_le32desc(skb->tail); work_done++; } - tp->rx_ring[entry].status = DescOwned; + tp->rx_ring[entry].status = cpu_to_le32(DescOwned); } return work_done; } -static int -tulip_close(struct device *dev) +static int tulip_close(struct device *dev) { long ioaddr = dev->base_addr; struct tulip_private *tp = (struct tulip_private *)dev->priv; @@ -2867,7 +2865,7 @@ tulip_close(struct device *dev) /* Disable interrupts by clearing the interrupt mask. */ outl(0x00000000, ioaddr + CSR7); - /* Stop the chip's Tx and Rx processes. */ + /* Stop the Tx and Rx processes. */ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6); /* 21040 -- Leave the card in 10baseT state. */ if (tp->chip_id == DC21040) @@ -2943,7 +2941,6 @@ static int private_ioctl(struct device *dev, struct ifreq *rq, int cmd) data[0] = 1; else return -ENODEV; - return 0; case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ if (data[0] == 32 && (tp->flags & HAS_NWAY143)) { int csr12 = inl(ioaddr + CSR12); @@ -3034,9 +3031,9 @@ static inline u32 ether_crc(int length, unsigned char *data) static void set_rx_mode(struct device *dev) { + struct tulip_private *tp = (struct tulip_private *)dev->priv; long ioaddr = dev->base_addr; int csr6 = inl(ioaddr + CSR6) & ~0x00D5; - struct tulip_private *tp = (struct tulip_private *)dev->priv; tp->csr6 &= ~0x00D5; if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ @@ -3126,9 +3123,9 @@ static void set_rx_mode(struct device *dev) /* Avoid a chip errata by prefixing a dummy entry. */ tp->tx_skbuff[entry] = 0; tp->tx_ring[entry].length = - (entry == TX_RING_SIZE-1) ? DESC_RING_WRAP : 0; + (entry == TX_RING_SIZE-1) ? cpu_to_le32(DESC_RING_WRAP) : 0; tp->tx_ring[entry].buffer1 = 0; - tp->tx_ring[entry].status = DescOwned; + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); entry = tp->cur_tx++ % TX_RING_SIZE; } @@ -3136,9 +3133,9 @@ static void set_rx_mode(struct device *dev) /* Put the setup frame on the Tx list. */ if (entry == TX_RING_SIZE-1) tx_flags |= DESC_RING_WRAP; /* Wrap ring. */ - tp->tx_ring[entry].length = tx_flags; - tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame); - tp->tx_ring[entry].status = DescOwned; + tp->tx_ring[entry].length = cpu_to_le32(tx_flags); + tp->tx_ring[entry].buffer1 = virt_to_le32desc(tp->setup_frame); + tp->tx_ring[entry].status = cpu_to_le32(DescOwned); if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) { set_bit(0, (void*)&dev->tbusy); tp->tx_full = 1; diff --git a/drivers/sound/Config.in b/drivers/sound/Config.in index d4bcd1ae2951..b4d8614951ba 100644 --- a/drivers/sound/Config.in +++ b/drivers/sound/Config.in @@ -28,6 +28,9 @@ if [ "$CONFIG_SOUND_ES1371" = "y" ]; then hex 'Gameport I/O 200,208,210,218' CONFIG_SOUND_ES1371_GAMEPORT 200 fi fi + +dep_tristate 'ESS Maestro' CONFIG_SOUND_MAESTRO $CONFIG_SOUND + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'ESS Solo1 (Experimental)' CONFIG_SOUND_ESSSOLO1 $CONFIG_SOUND fi diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index fe8ae5e59986..f53d2a02a6e5 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_SOUND_CMPCI) += cmpci.o 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_SONICVIBES) += sonicvibes.o # Declare multi-part drivers. diff --git a/drivers/sound/lowlevel/miroaci.h b/drivers/sound/lowlevel/miroaci.h index f3547adf2b64..a290f69a71c8 100644 --- a/drivers/sound/lowlevel/miroaci.h +++ b/drivers/sound/lowlevel/miroaci.h @@ -1,4 +1,4 @@ -#ifdef CONFIG_ACI_MIXER +#if defined(CONFIG_ACI_MIXER) || defined(CONFIG_ACI_MIXER_MODULE) extern int aci_implied_cmd(unsigned char opcode); extern int aci_write_cmd(unsigned char opcode, unsigned char parameter); extern int aci_write_cmd_d(unsigned char opcode, unsigned char parameter, unsigned char parameter2); diff --git a/drivers/sound/maestro.c b/drivers/sound/maestro.c new file mode 100644 index 000000000000..8741a20513b3 --- /dev/null +++ b/drivers/sound/maestro.c @@ -0,0 +1,3582 @@ +/***************************************************************************** + * + * ESS Maestro/Maestro-2/Maestro-2E driver for Linux 2.2.x + * + * 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. + * + * (c) Copyright 1999 Alan Cox + * + * Based heavily on SonicVibes.c: + * Copyright (C) 1998-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * Heavily modified by Zach Brown based on lunch + * with ESS engineers. Many thanks to Howard Kim for providing + * contacts and hardware. Honorable mention goes to Eric + * Brombaugh for all sorts of things. Best regards to the + * proprietors of Hack Central for fine lodging. + * + * Supported devices: + * /dev/dsp0-7 standard /dev/dsp device, (mostly) OSS compatible + * /dev/mixer standard /dev/mixer device, (mostly) OSS compatible + * + * Hardware Description + * + * A working Maestro setup contains the Maestro chip wired to a + * codec or 2. In the Maestro we have the APUs, the ASP, and the + * Wavecache. The APUs can be though of as virtual audio routing + * channels. They can take data from a number of sources and perform + * basic encodings of the data. The wavecache is a storehouse for + * PCM data. Typically it deals with PCI and interracts with the + * APUs. The ASP is a wacky DSP like device that ESS is loth + * to release docs on. Thankfully it isn't required on the Maestro + * until you start doing insane things like FM emulation and surround + * encoding. The codecs are almost always AC-97 compliant codecs, + * but it appears that early Maestros may have had PT101 (an ESS + * part?) wired to them. The only real difference in the Maestro + * families is external goop like docking capability, memory for + * the ASP, and initialization differences. + * + * Driver Operation + * + * We only drive the APU/Wavecache as typical DACs and drive the + * mixers in the codecs. There are 64 APUs. We assign 6 to each + * /dev/dsp? device. 2 channels for output, and 4 channels for + * input. + * + * For output we maintain a ring buffer of data that we are DMAing + * to the card. In mono operation this is nice and easy. When + * we receive data we tack it onto the ring buffer and make sure + * the APU assigned to it is playing over the data. When we fill + * the ring buffer we put the client to sleep until there is + * room again. Easy. + * + * However, this starts to stink when we use stereo. The APUs + * supposedly can decode LRLR packed stereo data, but it + * doesn't work. So we're forced to use dual mono APUs walking over + * mono encoded data. This requires us to split the input from + * the client and complicates the buffer maths tremendously. Ick. + * + * This also pollutes the recording paths as well. We have to use + * 2 L/R incoming APUs that are fixed at 16bit/48khz. We then pipe + * these through 2 rate converion apus that mix them down to the + * requested frequency and write them to memory through the wavecache. + * We also apparently need a 512byte region thats used as temp space + * between the incoming APUs and the rate converters. + * + * The wavecache makes our life even more fun. First off, it can + * only address the first 28 bits of PCI address space, making it + * useless on quite a few architectures. Secondly, its insane. + * It claims to fetch from 4 regions of PCI space, each 4 meg in length. + * But that doesn't really work. You can only use 1 region. So all our + * allocations have to be in 4meg of each other. Booo. Hiss. + * So we have a module parameter, dsps_order, that is the order of + * the number of dsps to provide. All their buffer space is allocated + * on open time. The sonicvibes OSS routines we inherited really want + * power of 2 buffers, so we have all those next to each other, then + * 512 byte regions for the recording wavecaches. This ends up + * wasting quite a bit of memory. The only fixes I can see would be + * getting a kernel allocator that could work in zones, or figuring out + * just how to coerce the WP into doing what we want. + * + * The indirection of the various registers means we have to spinlock + * nearly all register accesses. We have the main register indirection + * like the wave cache, maestro registers, etc. Then we have beasts + * like the APU interface that is indirect registers gotten at through + * the main maestro indirection. Ouch. We spinlock around the actual + * ports on a per card basis. This means spinlock activity at each IO + * operation, but the only IO operation clusters are in non critical + * paths and it makes the code far easier to follow. Interrupts are + * blocked while holding the locks because the int handler has to + * get at some of them :(. The mixer interface doesn't, however. + * We also have an OSS state lock that is thrown around in a few + * places. + * + * This driver has brute force APM suspend support. We catch suspend + * notifications and stop all work being done on the chip. Any people + * that try between this shutdown and the real suspend operation will + * be put to sleep. When we resume we restore our software state on + * the chip and wake up the people that were using it. The code thats + * being used now is quite dirty and assumes we're on a uni-processor + * machine. Much of it will need to be cleaned up for SMP ACPI or + * similar. + * + * History + * v0.10 - Oct 28 1999 - Zach Brown + * aha, so, sometimes the WP writes a status word to offset 0 + * from one of the PCMBARs. rearrange allocation accordingly.. + * Jeroen Hoogervorst submits 7500 fix out of nowhere. yay. :) + * v0.09 - Oct 23 1999 - Zach Brown + * added APM support. + * re-order something such that some 2Es now work. Magic! + * new codec reset routine. made some codecs come to life. + * fix clear_advance, sync some control with ESS. + * now write to all base regs to be paranoid. + * v0.08 - Oct 20 1999 - Zach Brown + * Fix initial buflen bug. I am so smart. also smp compiling.. + * I owe Eric yet another beer: fixed recmask, igain, + * muting, and adc sync consistency. Go Team. + * v0.07 - Oct 4 1999 - Zach Brown + * tweak adc/dac, formating, and stuff to allow full duplex + * allocate dsps memory at open() so we can fit in the wavecache window + * fix wavecache braindamage. again. no more scribbling? + * fix ess 1921 codec bug on some laptops. + * fix dumb pci scanning bug + * started 2.3 cleanup, redid spinlocks, little cleanups + * v0.06 - Sep 20 1999 - Zach Brown + * fix wavecache thinkos. limit to 1 /dev/dsp. + * eric is wearing his thinking toque this week. + * spotted apu mode bugs and gain ramping problem + * don't touch weird mixer regs, make recmask optional + * fixed igain inversion, defaults for mixers, clean up rec_start + * make mono recording work. + * report subsystem stuff, please send reports. + * littles: parallel out, amp now + * v0.05 - Sep 17 1999 - Zach Brown + * merged and fixed up Eric's initial recording code + * munged format handling to catch misuse, needs rewrite. + * revert ring bus init, fixup shared int, add pci busmaster setting + * fix mixer oss interface, fix mic mute and recmask + * mask off unsupported mixers, reset with all 1s, modularize defaults + * make sure bob is running while we need it + * got rid of device limit, initial minimal apm hooks + * pull out dead code/includes, only allow multimedia/audio maestros + * v0.04 - Sep 01 1999 - Zach Brown + * copied memory leak fix from sonicvibes driver + * different ac97 reset, play with 2.0 ac97, simplify ring bus setup + * bob freq code, region sanity, jitter sync fix; all from Eric + * + * TODO + * some people get indir reg timeouts? + * anyone have a pt101 codec? + * mmap(), but beware stereo encoding nastiness. + * actually post pci writes + * fix bob frequency + * do smart things with ac97 2.0 bits. + * ugh.. non aligned writes in the middle of a data stream.. ugh + * sort out 0x34->0x36 crap in init + * docking and dual codecs and 978? + * pcm_sync? + * actually use LRLR + * + * it also would be fun to have a mode that would not use pci dma at all + * but would copy into the wavecache on board memory and use that + * on architectures that don't like the maestro's pci dma ickiness. + */ + +/*****************************************************************************/ + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + + #ifdef MODULE + #include + #ifdef MODVERSIONS + #include + #endif + #endif + #define DECLARE_WAITQUEUE(QUEUE,INIT) struct wait_queue QUEUE = {INIT, NULL} + #define wait_queue_head_t struct wait_queue * + #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->base_address[0] & PCI_BASE_ADDRESS_IO_MASK) + #define SILLY_INIT_SEM(SEM) SEM=MUTEX; + #define init_waitqueue_head init_waitqueue + #define SILLY_MAKE_INIT(FUNC) __initfunc(FUNC) + +#else + + #define SILLY_PCI_BASE_ADDRESS(PCIDEV) (PCIDEV->resource[0].start) + #define SILLY_INIT_SEM(SEM) init_MUTEX(&SEM) + #define SILLY_MAKE_INIT(FUNC) __init FUNC + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_APM +#include +static int maestro_apm_callback(apm_event_t ae); +static int in_suspend=0; +wait_queue_head_t suspend_queue; +static void check_suspend(void); +#define CHECK_SUSPEND check_suspend(); +#else +#define CHECK_SUSPEND +#endif + +#include "maestro.h" + +/* --------------------------------------------------------------------- */ + +#define M_DEBUG 1 + +#ifdef M_DEBUG +static int debug=0; +static int dsps_order=0; +#define M_printk(args...) {if (debug) printk(args);} +#else +#define M_printk(x) +#endif + +/* --------------------------------------------------------------------- */ +#define DRIVER_VERSION "0.10" + +#ifndef PCI_VENDOR_ESS +#define PCI_VENDOR_ESS 0x125D +#define PCI_DEVICE_ID_ESS_ESS1968 0x1968 /* Maestro 2 */ +#define PCI_DEVICE_ID_ESS_ESS1978 0x1978 /* Maestro 2E */ + +#define PCI_VENDOR_ESS_OLD 0x1285 /* Platform Tech, + the people the maestro + was bought from */ +#define PCI_DEVICE_ID_ESS_ESS0100 0x0100 /* maestro 1 */ +#endif /* PCI_VENDOR_ESS */ + +#define ESS_CHAN_HARD 0x100 + + +/* changed so that I could actually find all the + references and fix them up. its a little more readable now. */ +#define ESS_FMT_STEREO 0x01 +#define ESS_FMT_16BIT 0x02 +#define ESS_FMT_MASK 0x03 +#define ESS_DAC_SHIFT 0 +#define ESS_ADC_SHIFT 4 + +#define ESS_STATE_MAGIC 0x125D1968 +#define ESS_CARD_MAGIC 0x19283746 + +#define DAC_RUNNING 1 +#define ADC_RUNNING 2 + +#define MAX_DSP_ORDER 3 +#define MAX_DSPS (1<<3) +#define NR_DSPS (1<src buffer page */ + void *mixbuf; +}; + +struct ess_card { + unsigned int magic; + + /* We keep maestro cards in a linked list */ + struct ess_card *next; + + int dev_mixer; + + int card_type; + + /* as most of this is static, + perhaps it should be a pointer to a global struct */ + struct mixer_goo { + int modcnt; + int supported_mixers; + int stereo_mixers; + int record_sources; + /* the caller must guarantee arg sanity before calling these */ +/* int (*read_mixer)(struct ess_card *card, int index);*/ + void (*write_mixer)(struct ess_card *card,int mixer, unsigned int left,unsigned int right); + int (*recmask_io)(struct ess_card *card,int rw,int mask); + unsigned int mixer_state[SOUND_MIXER_NRDEVICES]; + } mix; + + struct ess_state channels[MAX_DSPS]; + u16 maestro_map[NR_IDRS]; /* Register map */ +#ifdef CONFIG_APM + /* we have to store this junk so that we can come back from a + suspend */ + u16 apu_map[NR_APUS][NR_APU_REGS]; /* contents of apu regs */ +#endif + + /* this locks around the physical registers on the card */ + spinlock_t lock; + + /* memory for this card.. wavecache limited :(*/ + void *dmapages; + int dmaorder; + + /* hardware resources */ + struct pci_dev pcidev; /* uck.. */ + u32 iobase; + u32 irq; + + int bob_freq; + char dsps_open; +}; + +static unsigned +ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + + +/* --------------------------------------------------------------------- */ + +static struct ess_card *devs = NULL; + +/* --------------------------------------------------------------------- */ + + +/* + * ESS Maestro AC97 codec programming interface. + */ + +static void maestro_ac97_set(int io, u8 cmd, u16 val) +{ + int i; + /* + * Wait for the codec bus to be free + */ + + CHECK_SUSPEND; + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + /* + * Write the bus + */ + outw(val, io+ESS_AC97_DATA); + mdelay(1); + outb(cmd, io+ESS_AC97_INDEX); + mdelay(1); +} + +static u16 maestro_ac97_get(int io, u8 cmd) +{ + int sanity=10000; + u16 data; + int i; + + CHECK_SUSPEND; + /* + * Wait for the codec bus to be free + */ + + for(i=0;i<10000;i++) + { + if(!(inb(io+ESS_AC97_INDEX)&1)) + break; + } + + outb(cmd|0x80, io+ESS_AC97_INDEX); + mdelay(1); + + while(inb(io+ESS_AC97_INDEX)&1) + { + sanity--; + if(!sanity) + { + printk(KERN_ERR "maestro: ac97 codec timeout reading 0x%x.\n",cmd); + return 0; + } + } + data=inw(io+ESS_AC97_DATA); + mdelay(1); + return data; +} + +/* 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<iobase , 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 is 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); + } + + M_printk("read mixer %d (0x%x) %x -> %x\n",mixer,mh->offset,val,ret); + + return ret; +} +#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 ess_card *card,int mixer, unsigned int left, unsigned int right) +{ + u16 val=0; + struct ac97_mixer_hw *mh = &ac97_hw[mixer]; + + M_printk("wrote mixer %d (0x%x) %d,%d",mixer,mh->offset,left,right); + + if(AC97_STEREO_MASK & (1<scale) / 100; + left = (left * mh->scale) / 100; + if ((left == 0) && (right == 0)) + val |= 0x8000; + } else { + right = ((100 - right) * mh->scale) / 100; + left = ((100 - left) * mh->scale) / 100; + if((left == mh->scale) && (right == mh->scale)) + val |= 0x8000; + } + + val |= (left << 8) | right; + + } else if (mixer == SOUND_MIXER_SPEAKER) { + val = (((100 - left) * mh->scale) / 100) << 1; + } else if (mixer == SOUND_MIXER_MIC) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x801f; + val |= (((100 - left) * mh->scale) / 100); + /* the low bit is optional in the tone sliders and masking + it lets is avoid the 0xf 'bypass'.. */ + } else if (mixer == SOUND_MIXER_BASS) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x0f00; + val |= ((((100 - left) * mh->scale) / 100) << 8) & 0x0e00; + } else if (mixer == SOUND_MIXER_TREBLE) { + val = maestro_ac97_get(card->iobase , mh->offset) & ~0x000f; + val |= (((100 - left) * mh->scale) / 100) & 0x000e; + } + + maestro_ac97_set(card->iobase , mh->offset, val); + + M_printk(" -> %x\n",val); +} + +/* 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_oss_mask[] = { + [AC97_REC_MIC] = SOUND_MASK_MIC, + [AC97_REC_CD] = SOUND_MASK_CD, + [AC97_REC_VIDEO] = SOUND_MASK_VIDEO, + [AC97_REC_AUX] = SOUND_MASK_LINE1, + [AC97_REC_LINE] = SOUND_MASK_LINE, + [AC97_REC_PHONE] = SOUND_MASK_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 +}; + +/* 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 ess_card *card, int read, int mask) +{ + unsigned int val = ac97_oss_mask[ maestro_ac97_get(card->iobase, 0x1a) & 0x7 ]; + + if (read) return val; + + /* oss can have many inputs, maestro cant. try + to pick the 'new' one */ + + if (mask != val) mask &= ~val; + + val = ffs(mask) - 1; + val = ac97_oss_rm[val]; + val |= val << 8; /* set both channels */ + + M_printk("maestro: setting ac97 recmask to 0x%x\n",val); + + maestro_ac97_set(card->iobase,0x1a,val); + + return 0; +}; + +/* + * The Maestro can be wired to a standard AC97 compliant codec + * (see www.intel.com for the pdf's on this), or to a PT101 codec + * which appears to be the ES1918 (data sheet on the esstech.com.tw site) + * + * The PT101 setup is untested. + */ + +static u16 maestro_ac97_init(struct ess_card *card, int iobase) +{ + u16 vend1, vend2, caps; + + card->mix.supported_mixers = AC97_SUPPORTED_MASK; + card->mix.stereo_mixers = AC97_STEREO_MASK; + card->mix.record_sources = AC97_RECORD_MASK; +/* card->mix.read_mixer = ac97_read_mixer;*/ + card->mix.write_mixer = ac97_write_mixer; + card->mix.recmask_io = ac97_recmask_io; + + vend1 = maestro_ac97_get(iobase, 0x7c); + vend2 = maestro_ac97_get(iobase, 0x7e); + + caps = maestro_ac97_get(iobase, 0x00); + + printk(KERN_INFO "maestro: AC97 Codec detected: v: 0x%2x%2x caps: 0x%x pwr: 0x%x\n", + vend1,vend2,caps,maestro_ac97_get(iobase,0x26) & 0xf); + + if (! (caps & 0x4) ) { + /* no bass/treble nobs */ + card->mix.supported_mixers &= ~(SOUND_MASK_BASS|SOUND_MASK_TREBLE); + } + + /* XXX endianness, dork head. */ + /* vendor specifc bits.. */ + switch ((long)(vend1 << 16) | vend2) { + case 0x545200ff: /* TriTech */ + /* no idea what this does */ + maestro_ac97_set(iobase,0x2a,0x0001); + maestro_ac97_set(iobase,0x2c,0x0000); + maestro_ac97_set(iobase,0x2c,0xffff); + break; + case 0x83847609: /* ESS 1921 */ + /* writing to 0xe (mic) or 0x1a (recmask) seems + to hang this codec */ + card->mix.supported_mixers &= ~(SOUND_MASK_MIC); + card->mix.record_sources = 0; + card->mix.recmask_io = NULL; +#if 0 /* don't ask. I have yet to see what these actually do. */ + maestro_ac97_set(iobase,0x76,0xABBA); /* o/~ Take a chance on me o/~ */ + udelay(20); + maestro_ac97_set(iobase,0x78,0x3002); + udelay(20); + maestro_ac97_set(iobase,0x78,0x3802); + udelay(20); +#endif + break; + default: break; + } + + maestro_ac97_set(iobase, 0x1E, 0x0404); + /* null misc stuff */ + maestro_ac97_set(iobase, 0x20, 0x0000); + + return 0; +} + +static u16 maestro_pt101_init(struct ess_card *card,int iobase) +{ + printk(KERN_INFO "maestro: PT101 Codec detected, initializing but _not_ installing mixer device.\n"); + /* who knows.. */ + maestro_ac97_set(iobase, 0x2A, 0x0001); + maestro_ac97_set(iobase, 0x2C, 0x0000); + maestro_ac97_set(iobase, 0x2C, 0xFFFF); + maestro_ac97_set(iobase, 0x10, 0x9F1F); + maestro_ac97_set(iobase, 0x12, 0x0808); + maestro_ac97_set(iobase, 0x14, 0x9F1F); + maestro_ac97_set(iobase, 0x16, 0x9F1F); + maestro_ac97_set(iobase, 0x18, 0x0404); + maestro_ac97_set(iobase, 0x1A, 0x0000); + maestro_ac97_set(iobase, 0x1C, 0x0000); + maestro_ac97_set(iobase, 0x02, 0x0404); + maestro_ac97_set(iobase, 0x04, 0x0808); + maestro_ac97_set(iobase, 0x0C, 0x801F); + maestro_ac97_set(iobase, 0x0E, 0x801F); + return 0; +} + +/* this is very magic, and very slow.. */ +static void +maestro_ac97_reset(int ioaddr, struct pci_dev *pcidev) +{ + u16 save_68; + u16 w; + + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + + /* reset the first codec */ + outw(0x0000, ioaddr+0x36); + save_68 = inw(ioaddr+0x68); + pci_read_config_word(pcidev, 0x58, &w); /* something magical with gpio and bus arb. */ + if( w & 0x1) + save_68 |= 0x10; + outw(0xfffe, ioaddr + 0x64); /* tickly gpio 0.. */ + outw(0x0001, ioaddr + 0x68); + outw(0x0000, ioaddr + 0x60); + udelay(20); + outw(0x0001, ioaddr + 0x60); + mdelay(20); + + outw(save_68 | 0x1, ioaddr + 0x68); /* now restore .. */ + outw( (inw(ioaddr + 0x38) & 0xfffc)|0x1, ioaddr + 0x38); + outw( (inw(ioaddr + 0x3a) & 0xfffc)|0x1, ioaddr + 0x3a); + outw( (inw(ioaddr + 0x3c) & 0xfffc)|0x1, ioaddr + 0x3c); + + /* now the second codec */ + outw(0x0000, ioaddr+0x36); + outw(0xfff7, ioaddr + 0x64); + save_68 = inw(ioaddr+0x68); + outw(0x0009, ioaddr + 0x68); + outw(0x0001, ioaddr + 0x60); + udelay(20); + outw(0x0009, ioaddr + 0x60); + mdelay(500); /* .. ouch.. */ + outw( inw(ioaddr + 0x38) & 0xfffc, ioaddr + 0x38); + outw( inw(ioaddr + 0x3a) & 0xfffc, ioaddr + 0x3a); + outw( inw(ioaddr + 0x3c) & 0xfffc, ioaddr + 0x3c); + +#if 0 /* the loop here needs to be much better if we want it.. */ + M_printk("trying software reset\n"); + /* try and do a software reset */ + outb(0x80|0x7c, ioaddr + 0x30); + for (w=0; ; w++) { + if ((inw(ioaddr+ 0x30) & 1) == 0) { + if(inb(ioaddr + 0x32) !=0) break; + + outb(0x80|0x7d, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + outb(0x80|0x7f, ioaddr + 0x30); + if (((inw(ioaddr+ 0x30) & 1) == 0) && (inb(ioaddr + 0x32) !=0)) break; + } + + if( w > 10000) { + outb( inb(ioaddr + 0x37) | 0x08, ioaddr + 0x37); /* do a software reset */ + mdelay(500); /* oh my.. */ + outb( inb(ioaddr + 0x37) & ~0x08, ioaddr + 0x37); + udelay(1); + outw( 0x80, ioaddr+0x30); + for(w = 0 ; w < 10000; w++) { + if((inw(ioaddr + 0x30) & 1) ==0) break; + } + } + } +#endif +} +/* + * Indirect register access. Not all registers are readable so we + * need to keep register state ourselves + */ + +#define WRITEABLE_MAP 0xEFFFFF +#define READABLE_MAP 0x64003F + +/* + * The Maestro engineers were a little indirection happy. These indirected + * registers themselves include indirect registers at another layer + */ + +static void __maestro_write(struct ess_card *card, u16 reg, u16 data) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + outw(data, ioaddr+0x00); + if( reg >= NR_IDRS) printk("maestro: IDR %d out of bounds!\n",reg); + else card->maestro_map[reg]=data; + +} + +static void maestro_write(struct ess_state *s, u16 reg, u16 data) +{ + unsigned long flags; + + CHECK_SUSPEND; + spin_lock_irqsave(&s->card->lock,flags); + + __maestro_write(s->card,reg,data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 __maestro_read(struct ess_card *card, u16 reg) +{ + long ioaddr = card->iobase; + + outw(reg, ioaddr+0x02); + return card->maestro_map[reg]=inw(ioaddr+0x00); +} + +static u16 maestro_read(struct ess_state *s, u16 reg) +{ + if(READABLE_MAP & (1<card->lock,flags); + + __maestro_read(s->card,reg); + + spin_unlock_irqrestore(&s->card->lock,flags); + } + return s->card->maestro_map[reg]; +} + +/* + * These routines handle accessing the second level indirections to the + * wave ram. + */ + +/* + * The register names are the ones ESS uses (see 104T31.ZIP) + */ + +#define IDR0_DATA_PORT 0x00 +#define IDR1_CRAM_POINTER 0x01 +#define IDR2_CRAM_DATA 0x02 +#define IDR3_WAVE_DATA 0x03 +#define IDR4_WAVE_PTR_LOW 0x04 +#define IDR5_WAVE_PTR_HI 0x05 +#define IDR6_TIMER_CTRL 0x06 +#define IDR7_WAVE_ROMRAM 0x07 + +static void apu_index_set(struct ess_card *card, u16 index) +{ + int i; + __maestro_write(card, IDR1_CRAM_POINTER, index); + for(i=0;i<1000;i++) + if(__maestro_read(card, IDR1_CRAM_POINTER)==index) + return; + printk(KERN_WARNING "maestro: APU register select failed.\n"); +} + +static void apu_data_set(struct ess_card *card, u16 data) +{ + int i; + for(i=0;i<1000;i++) + { + if(__maestro_read(card, IDR0_DATA_PORT)==data) + return; + __maestro_write(card, IDR0_DATA_PORT, data); + } +} + +/* + * This is the public interface for APU manipulation. It handles the + * interlock to avoid two APU writes in parallel etc. Don't diddle + * directly with the stuff above. + */ + +static void apu_set_register(struct ess_state *s, u16 channel, u8 reg, u16 data) +{ + unsigned long flags; + + CHECK_SUSPEND; + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + { + if(channel>5) + printk("BAD CHANNEL %d.\n",channel); + else + channel = s->apu[channel]; +#ifdef CONFIG_APM + /* store based on real hardware apu/reg */ + s->card->apu_map[channel][reg]=data; +#endif + } + reg|=(channel<<4); + + /* hooray for double indirection!! */ + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + apu_data_set(s->card, data); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 apu_get_register(struct ess_state *s, u16 channel, u8 reg) +{ + unsigned long flags; + u16 v; + + CHECK_SUSPEND; + + if(channel&ESS_CHAN_HARD) + channel&=~ESS_CHAN_HARD; + else + channel = s->apu[channel]; + + reg|=(channel<<4); + + spin_lock_irqsave(&s->card->lock,flags); + + apu_index_set(s->card, reg); + v=__maestro_read(s->card, IDR0_DATA_PORT); + + spin_unlock_irqrestore(&s->card->lock,flags); + return v; +} + + +/* + * The wavecache buffers between the APUs and + * pci bus mastering + */ + +static void wave_set_register(struct ess_state *s, u16 reg, u16 value) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + CHECK_SUSPEND; + + spin_lock_irqsave(&s->card->lock,flags); + + outw(reg, ioaddr+0x10); + outw(value, ioaddr+0x12); + + spin_unlock_irqrestore(&s->card->lock,flags); +} + +static u16 wave_get_register(struct ess_state *s, u16 reg) +{ + long ioaddr = s->card->iobase; + unsigned long flags; + u16 value; + CHECK_SUSPEND; + + spin_lock_irqsave(&s->card->lock,flags); + outw(reg, ioaddr+0x10); + value=inw(ioaddr+0x12); + spin_unlock_irqrestore(&s->card->lock,flags); + + return value; +} + +static void sound_reset(int ioaddr) +{ + outw(0x2000, 0x18+ioaddr); + udelay(1); + outw(0x0000, 0x18+ioaddr); + udelay(1); +} + +/* sets the play formats of these apus, should be passed the already shifted format */ +static void set_apu_fmt(struct ess_state *s, int apu, int mode) +{ + if(mode&ESS_FMT_16BIT) { + s->apu_mode[apu] = 0x10; + s->apu_mode[apu+1] = 0x10; + } else { + s->apu_mode[apu] = 0x30; + s->apu_mode[apu+1] = 0x30; + } +} + +/* this only fixes the output apu mode to be later set by start_dac and + company. output apu modes are set in ess_rec_setup */ +static void set_fmt(struct ess_state *s, unsigned char mask, unsigned char data) +{ + s->fmt = (s->fmt & mask) | data; + set_apu_fmt(s, 0, (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK); +} + +static u16 compute_rate(u32 freq) +{ + if(freq==48000) + return 0xFFFF; + freq<<=16; + freq/=48000; + return freq; +} + +static void set_dac_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->ratedac = rate; + + if(!((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT)) + rate >>= 1; /* who knows */ + +/* M_printk("computing dac rate %d with mode %d\n",rate,s->fmt);*/ + + freq = compute_rate(rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 0, 2,(apu_get_register(s, 0, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 0, 3, freq>>8); + apu_set_register(s, 1, 2,(apu_get_register(s, 1, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 1, 3, freq>>8); +} + +static void set_adc_rate(struct ess_state *s, unsigned rate) +{ + u32 freq; + + if (rate > 48000) + rate = 48000; + if (rate < 4000) + rate = 4000; + + s->rateadc = rate; + + freq = compute_rate(rate); + + /* Load the frequency, turn on 6dB */ + apu_set_register(s, 2, 2,(apu_get_register(s, 2, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 2, 3, freq>>8); + apu_set_register(s, 3, 2,(apu_get_register(s, 3, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 3, 3, freq>>8); + + /* fix mixer rate at 48khz. and its _must_ be 0x10000. */ + freq = 0x10000; + + apu_set_register(s, 4, 2,(apu_get_register(s, 4, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 4, 3, freq>>8); + apu_set_register(s, 5, 2,(apu_get_register(s, 5, 2)&0x00FF)| + ( ((freq&0xFF)<<8)|0x10 )); + apu_set_register(s, 5, 3, freq>>8); +} + +/* Stop our host of recording apus */ +extern inline void stop_adc(struct ess_state *s) +{ + /* XXX lets hope we don't have to lock around this */ + if (! (s->enable & ADC_RUNNING)) return; + + s->enable &= ~ADC_RUNNING; + apu_set_register(s, 2, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 3, 0, apu_get_register(s, 3, 0)&0xFF0F); + apu_set_register(s, 4, 0, apu_get_register(s, 2, 0)&0xFF0F); + apu_set_register(s, 5, 0, apu_get_register(s, 3, 0)&0xFF0F); +} + +/* stop output apus */ +extern inline void stop_dac(struct ess_state *s) +{ + /* XXX have to lock around this? */ + if (! (s->enable & DAC_RUNNING)) return; + + s->enable &= ~DAC_RUNNING; + apu_set_register(s, 0, 0, apu_get_register(s, 0, 0)&0xFF0F); + apu_set_register(s, 1, 0, apu_get_register(s, 1, 0)&0xFF0F); +} + +static void start_dac(struct ess_state *s) +{ + /* XXX locks? */ + if ( (s->dma_dac.mapped || s->dma_dac.count > 0) && + s->dma_dac.ready && + (! (s->enable & DAC_RUNNING)) ) { + + s->enable |= DAC_RUNNING; + + apu_set_register(s, 0, 0, + (apu_get_register(s, 0, 0)&0xFF0F)|s->apu_mode[0]); + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) + apu_set_register(s, 1, 0, + (apu_get_register(s, 1, 0)&0xFF0F)|s->apu_mode[1]); + } +} + +static void start_adc(struct ess_state *s) +{ + /* XXX locks? */ + 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)) ) { + + s->enable |= ADC_RUNNING; + apu_set_register(s, 2, 0, + (apu_get_register(s, 2, 0)&0xFF0F)|s->apu_mode[2]); + apu_set_register(s, 4, 0, + (apu_get_register(s, 4, 0)&0xFF0F)|s->apu_mode[4]); + + if( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + apu_set_register(s, 3, 0, + (apu_get_register(s, 3, 0)&0xFF0F)|s->apu_mode[3]); + apu_set_register(s, 5, 0, + (apu_get_register(s, 5, 0)&0xFF0F)|s->apu_mode[5]); + } + + } +} + + +/* + * Native play back driver + */ + +/* the mode passed should be already shifted and masked */ +static void +ess_play_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + u32 pa; + u32 tmpval; + int high_apu = 0; + int channel; + + M_printk("mode=%d rate=%d buf=%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only play its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + high_apu++; + } + + for(channel=0; channel <= high_apu; channel++) + { + int i; + + if(!channel) + pa = virt_to_bus(buffer); + else + /* right channel plays its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + if(!(mode & 2)) tmpval |= 4; /* 8bit */ + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(!channel) ess->dma_dac.base = pa&0xFFFF; + + pa|=0x00400000; /* System RAM */ + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + +/* XXX think about endianess when writing these registers */ + M_printk("maestro: ess_play_setup: APU[%d] pa = 0x%x\n", ess->apu[channel], pa); + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+size)&0xFFFF); + /* setting loop == sample len */ + apu_set_register(ess, channel, 7, size); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x0000); + /* set amp now to 0xd0 (?), low byte is 'amplitude dest'? */ + apu_set_register(ess, channel, 9, 0xD000); + + /* clear routing stuff */ + apu_set_register(ess, channel, 11, 0x0000); + /* dma on, no envelopes, filter to all 1s) */ + apu_set_register(ess, channel, 0, 0x400F); + + if(mode&ESS_FMT_STEREO) + /* set panning: left or right */ + apu_set_register(ess, channel, 10, 0x8F00 | (channel ? 0x10 : 0)); + else + apu_set_register(ess, channel, 10, 0x8F08); + + if(mode&ESS_FMT_16BIT) + ess->apu_mode[channel]=0x10; + else + ess->apu_mode[channel]=0x30; + } + + /* clear WP interupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* go team! */ + set_dac_rate(ess,rate); + start_dac(ess); +} + +/* + * Native record driver + */ + +/* again, passed mode is alrady shifted/masked */ +static void +ess_rec_setup(struct ess_state *ess, int mode, u32 rate, void *buffer, int size) +{ + int apu_step = 2; + int channel; + + M_printk("maestro: ess_rec_setup: mode=%d rate=%d buf=0x%p len=%d.\n", + mode, rate, buffer, size); + + /* all maestro sizes are in 16bit words */ + size >>=1; + + /* we're given the full size of the buffer, but + in stereo each channel will only use its half */ + if(mode&ESS_FMT_STEREO) { + size >>=1; + apu_step = 1; + } + + /* APU assignments: 2 = mono/left SRC + 3 = right SRC + 4 = mono/left Input Mixer + 5 = right Input Mixer */ + for(channel=2;channel<6;channel+=apu_step) + { + int i; + int bsize, route; + u32 pa; + u32 tmpval; + + /* data seems to flow from the codec, through an apu into + the 'mixbuf' bit of page, then through the SRC apu + and out to the real 'buffer'. ok. sure. */ + + if(channel & 0x04) { + /* ok, we're an input mixer going from adc + through the mixbuf to the other apus */ + + if(!(channel & 0x01)) { + pa = virt_to_bus(ess->mixbuf); + } else { + pa = virt_to_bus(ess->mixbuf + (PAGE_SIZE >> 4)); + } + + /* we source from a 'magic' apu */ + bsize = PAGE_SIZE >> 5; /* half of this channels alloc, in words */ + route = 0x14 + (channel - 4); /* parallel in crap, see maestro reg 0xC [8-11] */ + ess->apu_mode[channel] = 0x90; /* Input Mixer */ + + } else { + /* we're a rate converter taking + input from the input apus and outputing it to + system memory */ + if(!(channel & 0x01)) { + pa = virt_to_bus(buffer); + } else { + /* right channel records its split half. + *2 accomodates for rampant shifting earlier */ + pa = virt_to_bus(buffer + size*2); + } + + ess->apu_mode[channel] = 0xB0; /* Sample Rate Converter */ + + bsize = size; + /* get input from inputing apu */ + route = channel + 2; + } + + M_printk("maestro: ess_rec_setup: getting pa 0x%x from %d\n",pa,channel); + + /* set the wavecache control reg */ + tmpval = (pa - 0x10) & 0xFFF8; + ess->apu_base[channel]=tmpval; + wave_set_register(ess, ess->apu[channel]<<3, tmpval); + + pa -= virt_to_bus(ess->card->dmapages); + pa>>=1; /* words */ + + /* base offset of dma calcs when reading the pointer + on this left one */ + if(channel==2) ess->dma_adc.base = pa&0xFFFF; + + pa|=0x00400000; /* bit 22 -> System RAM */ + + M_printk("maestro: ess_rec_setup: APU[%d] pa = 0x%x size = 0x%x route = 0x%x\n", + ess->apu[channel], pa, bsize, route); + + /* Begin loading the APU */ + for(i=0;i<15;i++) /* clear all PBRs */ + apu_set_register(ess, channel, i, 0x0000); + + apu_set_register(ess, channel, 0, 0x400F); + + /* need to enable subgroups.. and we should probably + have different groups for different /dev/dsps.. */ + apu_set_register(ess, channel, 2, 0x8); + + /* Load the buffer into the wave engine */ + apu_set_register(ess, channel, 4, ((pa>>16)&0xFF)<<8); + /* XXX reg is little endian.. */ + apu_set_register(ess, channel, 5, pa&0xFFFF); + apu_set_register(ess, channel, 6, (pa+bsize)&0xFFFF); + apu_set_register(ess, channel, 7, bsize); + + /* clear effects/env.. */ + apu_set_register(ess, channel, 8, 0x00F0); + + /* amplitude now? sure. why not. */ + apu_set_register(ess, channel, 9, 0x0000); + + /* set filter tune, radius, polar pan */ + apu_set_register(ess, channel, 10, 0x8F08); + + /* route input */ + apu_set_register(ess, channel, 11, route); + } + + /* clear WP interupts */ + outw(1, ess->card->iobase+0x04); + /* enable WP ints */ + outw(inw(ess->card->iobase+0x18)|4, ess->card->iobase+0x18); + + /* let 'er rip */ + set_adc_rate(ess,rate); + start_adc(ess); +} +/* --------------------------------------------------------------------- */ + +static void set_dmaa(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmaa??\n"); +} + +static void set_dmac(struct ess_state *s, unsigned int addr, unsigned int count) +{ + M_printk("set_dmac??\n"); +} + +/* Playback pointer */ +extern __inline__ unsigned get_dmaa(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,0,5); + +/* M_printk("dmaa: offset: %d, base: %d\n",offset,s->dma_dac.base); */ + + offset-=s->dma_dac.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* Record pointer */ +extern __inline__ unsigned get_dmac(struct ess_state *s) +{ + int offset; + + offset = apu_get_register(s,2,5); + +/* M_printk("dmac: offset: %d, base: %d\n",offset,s->dma_adc.base); */ + + /* The offset is an address not a position relative to base */ + offset-=s->dma_adc.base; + + return (offset&0xFFFE)<<1; /* hardware is in words */ +} + +/* + * Meet Bob, the timer... + */ + +static void ess_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +static void stop_bob(struct ess_state *s) +{ + /* Mask IDR 11,17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)&~1); + maestro_write(s, 0x17, maestro_read(s, 0x17)&~1); +} + +/* eventually we could be clever and limit bob ints + to the frequency at which our smallest duration + chunks may expire */ +#define ESS_SYSCLK 50000000 +static void start_bob(struct ess_state *s) +{ + int prescale; + int divide; + + /* XXX make freq selector much smarter, see calc_bob_rate */ + int freq = 150; /* requested frequency - calculate what we want here. */ + + /* compute ideal interrupt frequency for buffer size & play rate */ + /* first, find best prescaler value to match freq */ + for(prescale=5;prescale<12;prescale++) + if(freq > (ESS_SYSCLK>>(prescale+9))) + break; + + /* next, back off prescaler whilst getting divider into optimum range */ + divide=1; + while((prescale > 5) && (divide<32)) + { + prescale--; + divide <<=1; + } + divide>>=1; + + /* now fine-tune the divider for best match */ + for(;divide<31;divide++) + if(freq >= ((ESS_SYSCLK>>(prescale+9))/(divide+1))) + break; + + /* divide = 0 is illegal, but don't let prescale = 4! */ + if(divide == 0) + { + divide++; + if(prescale>5) + prescale--; + } + + maestro_write(s, 6, 0x9000 | (prescale<<5) | divide); /* set reg */ + + /* Now set IDR 11/17 */ + maestro_write(s, 0x11, maestro_read(s, 0x11)|1); + maestro_write(s, 0x17, maestro_read(s, 0x17)|1); +} +/* --------------------------------------------------------------------- */ + +/* this quickly calculates the frequency needed for bob + and sets it if its different than what bob is + currently running at. its called often so + needs to be fairly quick. */ +#define BOB_MIN 50 +#define BOB_MAX 400 +static void calc_bob_rate(struct ess_state *s) { +#if 0 /* this thing tries to set the frequency of bob such that + there are 2 interrupts / buffer walked by the dac/adc. That + is probably very wrong for people who actually care about + mid buffer positioning. it should be calculated as bytes/interrupt + and that needs to be decided :) so for now just use the static 150 + in start_bob.*/ + + unsigned int dac_rate=2,adc_rate=1,newrate; + static int israte=-1; + + if (s->dma_dac.fragsize == 0) dac_rate = BOB_MIN; + else { + dac_rate = (2 * s->ratedac * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_dac.fragsize) ; + } + + if (s->dma_adc.fragsize == 0) adc_rate = BOB_MIN; + else { + adc_rate = (2 * s->rateadc * sample_size[(s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK]) / + (s->dma_adc.fragsize) ; + } + + if(dac_rate > adc_rate) newrate = adc_rate; + else newrate=dac_rate; + + if(newrate > BOB_MAX) newrate = BOB_MAX; + else { + if(newrate < BOB_MIN) + newrate = BOB_MIN; + } + + if( israte != newrate) { + printk("dac: %d adc: %d rate: %d\n",dac_rate,adc_rate,israte); + israte=newrate; + } +#endif + +} + +static int +prog_dmabuf(struct ess_state *s, unsigned rec) +{ + struct dmabuf *db = rec ? &s->dma_adc : &s->dma_dac; + unsigned rate = rec ? s->rateadc : s->ratedac; + unsigned bytepersec; + unsigned bufs; + unsigned char fmt; + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + fmt = s->fmt; + if (rec) { + stop_adc(s); + fmt >>= ESS_ADC_SHIFT; + } else { + stop_dac(s); + fmt >>= ESS_DAC_SHIFT; + } + spin_unlock_irqrestore(&s->lock, flags); + fmt &= ESS_FMT_MASK; + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + bytepersec = rate << sample_shift[fmt]; + bufs = PAGE_SIZE << db->buforder; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->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; + + M_printk("maestro: setup oss: numfrag: %d fragsize: %d dmasize: %d\n",db->numfrag,db->fragsize,db->dmasize); + + memset(db->rawbuf, (fmt & ESS_FMT_16BIT) ? 0 : 0x80, db->dmasize); + + spin_lock_irqsave(&s->lock, flags); + if (rec) { + ess_rec_setup(s, fmt, s->rateadc, + db->rawbuf, db->numfrag << db->fragshift); + } else { + ess_play_setup(s, fmt, s->ratedac, + db->rawbuf, db->numfrag << db->fragshift); + } + spin_unlock_irqrestore(&s->lock, flags); + db->ready = 1; + + return 0; +} + +static __inline__ void +clear_advance(struct ess_state *s) +{ + unsigned char c = ((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_16BIT) ? 0 : 0x80; + unsigned char *buf = s->dma_dac.rawbuf; + unsigned bsize = s->dma_dac.dmasize; + /* swptr is always in bytes as read from an apu.. */ + unsigned bptr = s->dma_dac.swptr; + unsigned len = s->dma_dac.fragsize; + int i=1; + + if((s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_STEREO) { + i++; + bsize >>=1; + } + + for ( ;i; i-- , buf += bsize) { + + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(buf + bptr, c, x); + /* account for wrapping? */ + bptr = 0; + len -= x; + } + memset(buf + bptr, c, len); + } +} + +/* call with spinlock held! */ +static void +ess_update_ptr(struct ess_state *s) +{ + unsigned hwptr; + int diff; + + /* update ADC pointer */ + if (s->dma_adc.ready) { + /* oh boy should this all be re-written. everything in the current code paths think + that the various counters/pointers are expressed in bytes to the user but we have + two apus doing stereo stuff so we fix it up here.. it propogates to all the various + counters from here. Notice that this means that mono recording is very very + broken right now. */ + if ( s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + hwptr = (get_dmac(s)*2) % s->dma_adc.dmasize; + } else { + 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))) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + stop_adc(s); + /* brute force everyone back in sync, sigh */ + s->dma_adc.count = 0; + s->dma_adc.swptr = 0; + s->dma_adc.hwptr = 0; + s->dma_adc.error++; + } + } + } + /* update DAC pointer */ + if (s->dma_dac.ready) { + /* this is so gross. */ + hwptr = (/*s->dma_dac.dmasize -*/ get_dmaa(s)) % s->dma_dac.dmasize; + diff = (s->dma_dac.dmasize + hwptr - s->dma_dac.hwptr) % s->dma_dac.dmasize; +/* M_printk("updating dac: hwptr: %d diff: %d\n",hwptr,diff);*/ + 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); + } + } else { + s->dma_dac.count -= diff; +/* M_printk("maestro: ess_update_ptr: diff: %d, count: %d\n", diff, s->dma_dac.count); */ + if (s->dma_dac.count <= 0) { + /* FILL ME + wrindir(s, SV_CIENABLE, s->enable); */ + /* XXX how on earth can calling this with the lock held work.. */ + stop_dac(s); + /* brute force everyone back in sync, sigh */ + s->dma_dac.count = 0; + s->dma_dac.swptr = 0; + s->dma_dac.hwptr = 0; + s->dma_dac.error++; + } else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s); + s->dma_dac.endcleared = 1; + } + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + } + } + } +} + +static void +ess_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ess_state *s; + struct ess_card *c = (struct ess_card *)dev_id; + int i; + u32 event; + + if ( ! (event = inb(c->iobase+0x1A)) ) return; + + outw(inw(c->iobase+4)&1, c->iobase+4); + +/* M_printk("maestro int: %x\n",event);*/ + + if(event&(1<<6)) + { + /* XXX if we have a hw volume control int enable + all the ints? doesn't make sense.. */ + event = inw(c->iobase+0x18); + outb(0xFF, c->iobase+0x1A); + } + else + { + /* else ack 'em all, i imagine */ + outb(0xFF, c->iobase+0x1A); + } + + /* + * Update the pointers for all APU's we are running. + */ + for(i=0;ichannels[i]; + if(s->dev_audio == -1) + break; + spin_lock(&s->lock); + ess_update_ptr(s); + spin_unlock(&s->lock); + } +} + + +/* --------------------------------------------------------------------- */ + +static const char invalid_magic[] = KERN_CRIT "maestro: invalid magic value in %s\n"; + +#define VALIDATE_MAGIC(FOO,MAG) \ +({ \ + if (!(FOO) || (FOO)->magic != MAG) { \ + printk(invalid_magic,__FUNCTION__); \ + return -ENXIO; \ + } \ +}) + +#define VALIDATE_STATE(a) VALIDATE_MAGIC(a,ESS_STATE_MAGIC) +#define VALIDATE_CARD(a) VALIDATE_MAGIC(a,ESS_CARD_MAGIC) + +static void set_mixer(struct ess_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 void +mixer_push_state(struct ess_card *card) +{ + int i; + for(i = 0 ; i < SOUND_MIXER_NRDEVICES ; i++) { + if( ! supported_mixer(card,i)) continue; + + set_mixer(card,i,card->mix.mixer_state[i]); + } +} + +static int mixer_ioctl(struct ess_card *card, unsigned int cmd, unsigned long arg) +{ + int i, val=0; + + VALIDATE_CARD(card); + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, card_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],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_names[card->card_type], sizeof(info.id)); + strncpy(info.name,card_names[card->card_type],sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + 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(&card->lock); + val = card->mix.recmask_io(card,1,0); + spin_unlock(&card->lock); + } + 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(&card->lock); + val = card->mix.read_mixer(card,i); + spin_unlock(&card->lock);*/ + + val = card->mix.mixer_state[i]; +/* M_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)) + return -EINVAL; + + 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) return 0; + if(! (val &= card->mix.record_sources)) return -EINVAL; + + spin_lock(&card->lock); + card->mix.recmask_io(card,0,val); + spin_unlock(&card->lock); + return 0; + + default: + i = _IOC_NR(cmd); + + if ( ! supported_mixer(card,i)) + return -EINVAL; + + spin_lock(&card->lock); + set_mixer(card,i,val); + spin_unlock(&card->lock); + + return 0; + } +} + +/* --------------------------------------------------------------------- */ + +static loff_t ess_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int ess_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct ess_card *card = devs; + + while (card && card->dev_mixer != minor) + card = card->next; + if (!card) + return -ENODEV; + + file->private_data = card; + MOD_INC_USE_COUNT; + return 0; +} + +static int ess_release_mixdev(struct inode *inode, struct file *file) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + MOD_DEC_USE_COUNT; + return 0; +} + +static int ess_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_card *card = (struct ess_card *)file->private_data; + + VALIDATE_CARD(card); + + return mixer_ioctl(card, cmd, arg); +} + +static /*const*/ struct file_operations ess_mixer_fops = { + &ess_llseek, + NULL, /* read */ + NULL, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + &ess_ioctl_mixdev, + NULL, /* mmap */ + &ess_open_mixdev, + NULL, /* flush */ + &ess_release_mixdev, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +/* --------------------------------------------------------------------- */ + +static int drain_dac(struct ess_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 (;;) { + /* XXX uhm.. questionable locking*/ + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->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 >> ESS_DAC_SHIFT) & ESS_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) + M_printk(KERN_DEBUG "maestro: dma timed out?? %ld\n",jiffies); + } + remove_wait_queue(&s->dma_dac.wait, &wait); + current->state = TASK_RUNNING; + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ +/* Zach sez: "god this is gross.." */ +static int +comb_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize) +{ + /* No such thing as stereo recording, so we + use dual input mixers. which means we have to + combine mono to stereo buffer. yuck. + + but we don't have to be able to work a byte at a time..*/ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + +/* M_printk("comb_stereo writing %d to %p from %p and %p, offset: %d size: %d\n",count/2, tmp_buffer,left,right,offset,bufsize);*/ + + for(i=count/4; i ; i--) { + (*(so+2)) = *(right++); + (*(so+3)) = *(right++); + (*so) = *(left++); + (*(so+1)) = *(left++); + so+=4; + } + + 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 +ess_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + int cnt; + unsigned char *combbuf = NULL; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + if(!(combbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + /* remember, all these things are expressed in bytes to be + sent to the user.. hence the evil / 2 down below */ + swptr = s->dma_adc.swptr; + cnt = s->dma_adc.dmasize-swptr; + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + if ( cnt > 0 ) cnt &= ~3; + + if (cnt <= 0) { + start_adc(s); + if (file->f_flags & O_NONBLOCK) + { + ret = ret ? ret : -EAGAIN; + goto rec_return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_adc.wait, HZ)) { +#ifdef CONFIG_APM + if(! in_suspend) +#endif + printk(KERN_DEBUG "maestro: read: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_adc.dmasize, s->dma_adc.fragsize, s->dma_adc.count, + s->dma_adc.hwptr, s->dma_adc.swptr); + stop_adc(s); + spin_lock_irqsave(&s->lock, flags); + set_dmac(s, virt_to_bus(s->dma_adc.rawbuf), s->dma_adc.numfrag << s->dma_adc.fragshift); + /* program enhanced mode registers */ + /* FILL ME */ +/* wrindir(s, SV_CIDMACBASECOUNT1, (s->dma_adc.fragsamples-1) >> 8); + wrindir(s, SV_CIDMACBASECOUNT0, s->dma_adc.fragsamples-1); */ + s->dma_adc.count = s->dma_adc.hwptr = s->dma_adc.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) + { + ret = ret ? ret : -ERESTARTSYS; + goto rec_return_free; + } + continue; + } + + if(s->fmt & (ESS_FMT_STEREO << ESS_ADC_SHIFT)) { + /* swptr/2 so that we know the real offset in each apu's buffer */ + comb_stereo(s->dma_adc.rawbuf,combbuf,swptr/2,cnt,s->dma_adc.dmasize); + if (copy_to_user(buffer, combbuf, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } else { + if (copy_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt)) { + ret = ret ? ret : -EFAULT; + goto rec_return_free; + } + } + + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_adc(s); + } + +rec_return_free: + if(combbuf) kfree(combbuf); + return ret; +} + +/* god this is gross..*/ +/* again, the mode passed is shifted/masked */ +static int +split_stereo(unsigned char *real_buffer,unsigned char *tmp_buffer, int offset, + int count, int bufsize, int mode) +{ + /* oh, bother. stereo decoding APU's don't work in 16bit so we + use dual linear decoders. which means we have to hack up stereo + buffer's we're given. yuck. */ + + unsigned char *so,*left,*right; + int i; + + so = tmp_buffer; + left = real_buffer + offset; + right = real_buffer + bufsize/2 + offset; + + if(mode & ESS_FMT_16BIT) { + for(i=count/4; i ; i--) { + *(right++) = (*(so+2)); + *(right++) = (*(so+3)); + *(left++) = (*so); + *(left++) = (*(so+1)); + so+=4; + } + } else { + for(i=count/2; i ; i--) { + *(right++) = (*(so+1)); + *(left++) = (*so); + so+=2; + } + } + + return 0; +} + +static ssize_t +ess_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + ssize_t ret; + unsigned long flags; + unsigned swptr; + unsigned char *splitbuf = NULL; + int cnt; + int mode = (s->fmt >> ESS_DAC_SHIFT) & ESS_FMT_MASK; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + /* XXX be more clever than this.. */ + if (!(splitbuf = kmalloc(count,GFP_KERNEL))) + return -ENOMEM; + ret = 0; + + calc_bob_rate(s); + + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + + if(mode & ESS_FMT_STEREO) { + /* in stereo we have the 'dual' buffers.. */ + cnt = ((s->dma_dac.dmasize/2)-swptr)*2; + } else { + cnt = s->dma_dac.dmasize-swptr; + } + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt > count) + cnt = count; + + /* our goofball stereo splitter can only deal in mults of 4 */ + if (cnt > 0) + cnt &= ~3; + + if (cnt <= 0) { + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if(!ret) ret = -EAGAIN; + goto return_free; + } + if (!interruptible_sleep_on_timeout(&s->dma_dac.wait, HZ)) { +#ifdef CONFIG_APM + if(! in_suspend) +#endif + printk(KERN_DEBUG "maestro: write: chip lockup? dmasz %u fragsz %u count %i hwptr %u swptr %u\n", + s->dma_dac.dmasize, s->dma_dac.fragsize, s->dma_dac.count, + s->dma_dac.hwptr, s->dma_dac.swptr); + stop_dac(s); + spin_lock_irqsave(&s->lock, flags); + set_dmaa(s, virt_to_bus(s->dma_dac.rawbuf), s->dma_dac.numfrag << s->dma_dac.fragshift); + /* program enhanced mode registers */ +/* wrindir(s, SV_CIDMAABASECOUNT1, (s->dma_dac.fragsamples-1) >> 8); + wrindir(s, SV_CIDMAABASECOUNT0, s->dma_dac.fragsamples-1); */ + /* FILL ME */ + s->dma_dac.count = s->dma_dac.hwptr = s->dma_dac.swptr = 0; + spin_unlock_irqrestore(&s->lock, flags); + } + if (signal_pending(current)) { + if (!ret) ret = -ERESTARTSYS; + goto return_free; + } + continue; + } + if(mode & ESS_FMT_STEREO) { + if (copy_from_user(splitbuf, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } + split_stereo(s->dma_dac.rawbuf,splitbuf,swptr,cnt,s->dma_dac.dmasize, + mode); + } else { + if (copy_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt)) { + if (!ret) ret = -EFAULT; + goto return_free; + } + } + + if(mode & ESS_FMT_STEREO) { + /* again with the weird pointer magic */ + swptr = (swptr + (cnt/2)) % (s->dma_dac.dmasize/2); + } else { + swptr = (swptr + cnt) % s->dma_dac.dmasize; + } + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + start_dac(s); + } +return_free: + if (splitbuf) kfree(splitbuf); + return ret; +} + +static unsigned int ess_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + poll_wait(file, &s->dma_dac.wait, wait); + if (file->f_mode & FMODE_READ) + poll_wait(file, &s->dma_adc.wait, wait); + spin_lock_irqsave(&s->lock, flags); + ess_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +/* this needs to be fixed to deal with the dual apus/buffers */ +#if 0 +static int ess_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + struct dmabuf *db; + int ret; + unsigned long size; + + VALIDATE_STATE(s); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf(s, 1)) != 0) + return ret; + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf(s, 0)) != 0) + return ret; + db = &s->dma_adc; + } else + return -EINVAL; + if (vma->vm_offset != 0) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) + return -EINVAL; + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) + return -EAGAIN; + db->mapped = 1; + return 0; +} +#endif + +static int ess_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, mapped, ret; + unsigned char fmtm, fmtd; + +/* printk("maestro: ess_ioctl: cmd %d\n", cmd);*/ + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, file->f_flags & O_NONBLOCK); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + /* XXX fix */ + 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_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (file->f_mode & FMODE_READ) { + stop_adc(s); + s->dma_adc.ready = 0; + set_adc_rate(s, val); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + set_dac_rate(s, val); + } + } + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SNDCTL_DSP_STEREO: + 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 (val) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + return 0; + + 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 (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_ADC_SHIFT); + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val >= 2) + fmtd |= ESS_FMT_STEREO << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_STEREO << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (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*/ + 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 |= ESS_FMT_16BIT << ESS_ADC_SHIFT; +#if 0 + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_ADC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_ADC_SHIFT); +#endif + } + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + s->dma_dac.ready = 0; + if (val == AFMT_S16_LE) + fmtd |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + else + fmtm &= ~(ESS_FMT_16BIT << ESS_DAC_SHIFT); + } + set_fmt(s, fmtm, fmtd); + } + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? + (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? + AFMT_S16_LE : + AFMT_S8, + (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode & FMODE_READ) && (s->enable & ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->enable & DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf(s, 1))) + return ret; + start_adc(s); + } else + stop_adc(s); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf(s, 0))) + return ret; + start_dac(s); + } else + stop_dac(s); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(s->enable & DAC_RUNNING) && (val = prog_dmabuf(s, 0)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ess_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->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 & ADC_RUNNING) && (val = prog_dmabuf(s, 1)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ess_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->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->lock, flags); + ess_update_ptr(s); + val = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&s->lock, flags); + ess_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->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->lock, flags); + ess_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->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf(s, 0))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf(s, 1))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + 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; + } + 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; + } + 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)) + 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; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? s->rateadc : s->ratedac, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_STEREO << ESS_ADC_SHIFT) + : (ESS_FMT_STEREO << ESS_DAC_SHIFT))) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user((s->fmt & ((file->f_mode & FMODE_READ) ? (ESS_FMT_16BIT << ESS_ADC_SHIFT) + : (ESS_FMT_16BIT << ESS_DAC_SHIFT))) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +static void +set_base_registers(struct ess_state *s,void *vaddr) +{ + unsigned long packed_phys = virt_to_bus(vaddr)>>12; + wave_set_register(s, 0x01FC , packed_phys); + wave_set_register(s, 0x01FD , packed_phys); + wave_set_register(s, 0x01FE , packed_phys); + wave_set_register(s, 0x01FF , packed_phys); +} + +/* we allocate a large power of two for all our memory. + this is cut up into (not to scale :): + |silly fifo word | 512byte mixbuf per adc | dac/adc * channels | +*/ +static int +allocate_buffers(struct ess_state *s) +{ + void *rawbuf=NULL; + int order,i; + unsigned long mapend,map; + + /* alloc as big a chunk as we can */ + for (order = (dsps_order + (15-PAGE_SHIFT) + 1); order >= (dsps_order + 2 + 1); order--) + if((rawbuf = (void *)__get_free_pages(GFP_KERNEL|GFP_DMA, order))) + break; + + if (!rawbuf) + return 1; + + M_printk("maestro: allocated %ld (%d) bytes at %p\n",PAGE_SIZE<card->dmapages = rawbuf; + s->card->dmaorder = order; + + /* play bufs are in the same first region as record bufs */ + set_base_registers(s,rawbuf); + + M_printk("maestro: writing %lx (%lx) to the wp\n",virt_to_bus(rawbuf), + ((virt_to_bus(rawbuf))&0xFFE00000)>>12); + + for(i=0;icard->channels[i]; + + if(ess->dev_audio == -1) + continue; + + ess->dma_dac.ready = s->dma_dac.mapped = 0; + ess->dma_adc.ready = s->dma_adc.mapped = 0; + ess->dma_adc.buforder = ess->dma_dac.buforder = order - 1 - dsps_order - 1; + + /* offset dac and adc buffers starting half way through and then at each [da][ad]c's + order's intervals.. */ + ess->dma_dac.rawbuf = rawbuf + (PAGE_SIZE<<(order-1)) + (i * ( PAGE_SIZE << (ess->dma_dac.buforder + 1 ))); + ess->dma_adc.rawbuf = ess->dma_dac.rawbuf + ( PAGE_SIZE << ess->dma_dac.buforder); + /* offset mixbuf by a mixbuf so that the lame status fifo can + happily scribble away.. */ + ess->mixbuf = rawbuf + (512 * (i+1)); + + M_printk("maestro: setup apu %d: %p %p %p\n",i,ess->dma_dac.rawbuf, + ess->dma_adc.rawbuf, ess->mixbuf); + + } + + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + mapend = MAP_NR(rawbuf + (PAGE_SIZE << order) - 1); + for (map = MAP_NR(rawbuf); map <= mapend; map++) { + set_bit(PG_reserved, &mem_map[map].flags); + } + + return 0; +} +static void +free_buffers(struct ess_state *s) +{ + unsigned long map, mapend; + + s->dma_dac.rawbuf = s->dma_adc.rawbuf = NULL; + s->dma_dac.mapped = s->dma_adc.mapped = 0; + s->dma_dac.ready = s->dma_adc.ready = 0; + + M_printk("maestro: freeing %p\n",s->card->dmapages); + /* undo marking the pages as reserved */ + + mapend = MAP_NR(s->card->dmapages + (PAGE_SIZE << s->card->dmaorder) - 1); + for (map = MAP_NR(s->card->dmapages); map <= mapend; map++) + clear_bit(PG_reserved, &mem_map[map].flags); + + free_pages((unsigned long)s->card->dmapages,s->card->dmaorder); + s->card->dmapages = NULL; +} + +static int +ess_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct ess_card *c = devs; + struct ess_state *s = NULL, *sp; + int i; + unsigned char fmtm = ~0, fmts = 0; + + /* + * Scan the cards and find the channel. We only + * do this at open time so it is ok + */ + + while (c!=NULL) + { + for(i=0;ichannels[i]; + if(sp->dev_audio < 0) + continue; + if((sp->dev_audio ^ minor) & ~0xf) + continue; + s=sp; + } + c=c->next; + } + + if (!s) + return -ENODEV; + + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EWOULDBLOCK; + } + up(&s->open_sem); + interruptible_sleep_on(&s->open_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + + /* under semaphore.. */ + if ((s->card->dmapages==NULL) && allocate_buffers(s)) { + up(&s->open_sem); + return -ENOMEM; + } + + if (file->f_mode & FMODE_READ) { +/* + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; */ + + fmtm &= ~((ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT); + fmts = (ESS_FMT_STEREO|ESS_FMT_16BIT) << ESS_ADC_SHIFT; + + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + set_adc_rate(s, 8000); + } + if (file->f_mode & FMODE_WRITE) { + fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT); + if ((minor & 0xf) == SND_DEV_DSP16) + fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT; + + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + set_dac_rate(s, 8000); + } + set_fmt(s, fmtm, fmts); + s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + + /* we're covered by the open_sem */ + if( ! s->card->dsps_open ) { + start_bob(s); + } + s->card->dsps_open++; + M_printk("maestro: open, %d bobs now\n",s->card->dsps_open); + + up(&s->open_sem); + MOD_INC_USE_COUNT; + return 0; +} + +static int +ess_release(struct inode *inode, struct file *file) +{ + struct ess_state *s = (struct ess_state *)file->private_data; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + } + + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + /* we're covered by the open_sem */ + M_printk("maestro: %d dsps now alive\n",s->card->dsps_open-1); + if( --s->card->dsps_open <= 0) { + stop_bob(s); + free_buffers(s); + } + up(&s->open_sem); + wake_up(&s->open_wait); + MOD_DEC_USE_COUNT; + return 0; +} + +static struct file_operations ess_audio_fops = { + &ess_llseek, + &ess_read, + &ess_write, + NULL, /* readdir */ + &ess_poll, + &ess_ioctl, + NULL, /* XXX &ess_mmap, */ + &ess_open, + NULL, /* flush */ + &ess_release, + NULL, /* fsync */ + NULL, /* fasync */ + NULL, /* check_media_change */ + NULL, /* revalidate */ + NULL, /* lock */ +}; + +static int +maestro_config(struct ess_card *card) +{ + struct pci_dev *pcidev = &card->pcidev; + struct ess_state *ess = &card->channels[0]; + int apu,iobase = card->iobase; + u16 w; + u32 n; + + /* + * Disable ACPI + */ + + pci_write_config_dword(pcidev, 0x54, 0x00000000); + pci_write_config_dword(pcidev, 0x56, 0x00000000); + + /* + * Use TDMA for now. TDMA works on all boards, so while its + * not the most efficient its the simplest. + */ + + pci_read_config_word(pcidev, 0x50, &w); + + /* Clear DMA bits */ + w&=~(1<<10|1<<9|1<<8); + + /* TDMA on */ + w|= (1<<8); + + /* + * Some of these are undocumented bits + */ + + w&=~(1<<13)|(1<<14); /* PIC Snoop mode bits */ + w&=~(1<<11); /* Safeguard off */ + w|= (1<<7); /* Posted write */ + w|= (1<<6); /* ISA timing on */ + /* XXX huh? claims to be reserved.. */ + w&=~(1<<5); /* Don't swap left/right */ + w&=~(1<<1); /* Subtractive decode off */ + + pci_write_config_word(pcidev, 0x50, w); + + pci_read_config_word(pcidev, 0x52, &w); + w&=~(1<<15); /* Turn off internal clock multiplier */ + /* XXX how do we know which to use? */ + w&=~(1<<14); /* External clock */ + + w&=~(1<<7); /* HWV off */ + w&=~(1<<6); /* Debounce off */ + w&=~(1<<5); /* GPIO 4:5 */ + w|= (1<<4); /* Disconnect from the CHI. Enabling this in made a dell 7500 work. */ + w&=~(1<<3); /* IDMA off (undocumented) */ + w&=~(1<<2); /* MIDI fix off (undoc) */ + w&=~(1<<1); /* reserved, always write 0 */ + w&=~(1<<0); /* IRQ to ISA off (undoc) */ + pci_write_config_word(pcidev, 0x52, w); + + /* + * DDMA off + */ + + pci_read_config_word(pcidev, 0x60, &w); + w&=~(1<<0); + pci_write_config_word(pcidev, 0x60, w); + + /* + * Legacy mode + */ + + pci_read_config_word(pcidev, 0x40, &w); + w|=(1<<15); /* legacy decode off */ + w&=~(1<<14); /* Disable SIRQ */ + w&=~(0x1f); /* disable mpu irq/io, game port, fm, SB */ + + pci_write_config_word(pcidev, 0x40, w); + + /* stake our claim on the iospace */ + request_region(iobase, 256, card_names[card->card_type]); + + sound_reset(iobase); + + /* + * Ring Bus Setup + */ + + /* setup usual 0x34 stuff.. 0x36 may be chip specific */ + outw(0xC090, iobase+0x34); /* direct sound, stereo */ + udelay(20); + outw(0x3000, iobase+0x36); /* direct sound, stereo */ + udelay(20); + + + /* + * Reset the CODEC + */ + + maestro_ac97_reset(iobase,pcidev); + + /* + * Ring Bus Setup + */ + + n=inl(iobase+0x34); + n&=~0xF000; + n|=12<<12; /* Direct Sound, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x0F00; /* Modem off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F0; + n|=9<<4; /* DAC, Stereo */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F; /* ASSP off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<29); /* Enable ring bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n|=(1<<28); /* Enable serial bus */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x00F00000; /* MIC off */ + outl(n, iobase+0x34); + + n=inl(iobase+0x34); + n&=~0x000F0000; /* I2S off */ + outl(n, iobase+0x34); + + + w=inw(iobase+0x18); + w&=~(1<<7); /* ClkRun off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<6); /* Harpo off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<4); /* ASSP irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<3); /* ISDN irq off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<2); /* Direct Sound IRQ on */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w&=~(1<<1); /* MPU401 IRQ off */ + outw(w, iobase+0x18); + + w=inw(iobase+0x18); + w|=(1<<0); /* SB IRQ on */ + outw(w, iobase+0x18); + + /* it appears some maestros (dell 7500) only work if these are set, + regardless of wether we use the assp or not. */ + + outb(0, iobase+0xA4); + outb(3, iobase+0xA2); + outb(0, iobase+0xA6); + + for(apu=0;apu<16;apu++) + { + /* Write 0 into the buffer area 0x1E0->1EF */ + outw(0x01E0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + + /* + * The 1.10 test program seem to write 0 into the buffer area + * 0x1D0-0x1DF too. + */ + outw(0x01D0+apu, 0x10+iobase); + outw(0x0000, 0x12+iobase); + } + +#if 1 + wave_set_register(ess, IDR7_WAVE_ROMRAM, + (wave_get_register(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|0x100); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)&~0x200); + wave_set_register(ess, IDR7_WAVE_ROMRAM, + wave_get_register(ess, IDR7_WAVE_ROMRAM)|~0x400); +#else + maestro_write(ess, IDR7_WAVE_ROMRAM, + (maestro_read(ess, IDR7_WAVE_ROMRAM)&0xFF00)); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x100); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)&~0x200); + maestro_write(ess, IDR7_WAVE_ROMRAM, + maestro_read(ess, IDR7_WAVE_ROMRAM)|0x400); +#endif + + maestro_write(ess, IDR2_CRAM_DATA, 0x0000); + maestro_write(ess, 0x08, 0xB004); + /* Now back to the DirectSound stuff */ + maestro_write(ess, 0x09, 0x001B); + maestro_write(ess, 0x0A, 0x8000); + maestro_write(ess, 0x0B, 0x3F37); + maestro_write(ess, 0x0C, 0x0098); + + /* parallel out ?? */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0xF000)|0x8000); + /* parallel in, has something to do with recording :) */ + maestro_write(ess, 0x0C, + (maestro_read(ess, 0x0C)&~0x0F00)|0x0500); + + maestro_write(ess, 0x0D, 0x7632); + + /* Wave cache control on - test off, sg off, + enable, enable extra chans 1Mb */ + + outw(inw(0x14+iobase)|(1<<8),0x14+iobase); + outw(inw(0x14+iobase)&0xFE03,0x14+iobase); + outw((inw(0x14+iobase)&0xFFFC), 0x14+iobase); + outw(inw(0x14+iobase)|(1<<7),0x14+iobase); + + outw(0xA1A0, 0x14+iobase); /* 0300 ? */ + + /* Now clear the APU control ram */ + for(apu=0;apuclass >> 8) & 0xffff) != PCI_CLASS_MULTIMEDIA_AUDIO) + return 0; + + iobase = SILLY_PCI_BASE_ADDRESS(pcidev); + + if(check_region(iobase, 256)) + { + printk(KERN_WARNING "maestro: can't allocate 256 bytes I/O at 0x%4.4x\n", iobase); + return 0; + } + + /* this was tripping up some machines */ + if(pcidev->irq == 0) + { + printk(KERN_WARNING "maestro: pci subsystem reports irq 0, this might not be correct.\n"); + } + + /* just to be sure */ + pci_set_master(pcidev); + + card = kmalloc(sizeof(struct ess_card), GFP_KERNEL); + if(card == NULL) + { + printk(KERN_WARNING "maestro: out of memory\n"); + return 0; + } + + memset(card, 0, sizeof(*card)); + memcpy(&card->pcidev,pcidev,sizeof(card->pcidev)); + +#ifdef CONFIG_APM + if (apm_register_callback(maestro_apm_callback)) { + printk(KERN_WARNING "maestro: apm suspend might not work.\n"); + } +#endif + + card->iobase = iobase; + card->card_type = card_type; + card->irq = pcidev->irq; + card->next = devs; + card->magic = ESS_CARD_MAGIC; + spin_lock_init(&card->lock); + devs = card; + + /* init our groups of 6 apus */ + for(i=0;ichannels[i]; + + s->index = i; + + s->card = card; + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + spin_lock_init(&s->lock); + SILLY_INIT_SEM(s->open_sem); + s->magic = ESS_STATE_MAGIC; + + s->apu[0] = 6*i; + s->apu[1] = (6*i)+1; + s->apu[2] = (6*i)+2; + s->apu[3] = (6*i)+3; + s->apu[4] = (6*i)+4; + s->apu[5] = (6*i)+5; + + if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf) + printk("maestro: BOTCH!\n"); + /* register devices */ + if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0) + break; + } + + num = i; + + /* clear the rest if we ran out of slots to register */ + for(;ichannels[i]; + s->dev_audio = -1; + } + + ess = &card->channels[0]; + + /* + * Ok card ready. Begin setup proper + */ + + printk(KERN_INFO "maestro: Configuring %s found at IO 0x%04X IRQ %d\n", + card_names[card_type],iobase,card->irq); + pci_read_config_dword(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &n); + printk(KERN_INFO "maestro: subvendor id: 0x%08x\n",n); + + maestro_config(card); + + if(maestro_ac97_get(iobase, 0x00)==0x0080) { + maestro_pt101_init(card,iobase); + } else { + maestro_ac97_init(card,iobase); + } + + if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) { + printk("maestro: couldn't register mixer!\n"); + } else { + memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state)); + mixer_push_state(card); + } + + if(request_irq(card->irq, ess_interrupt, SA_SHIRQ, card_names[card_type], card)) + { + printk(KERN_ERR "maestro: unable to allocate irq %d,\n", card->irq); + unregister_sound_mixer(card->dev_mixer); + for(i=0;ichannels[i]; + if(s->dev_audio != -1) + unregister_sound_dsp(s->dev_audio); + } + release_region(card->iobase, 256); + kfree(card); + return 0; + } + + printk(KERN_INFO "maestro: %d channels configured.\n", num); + return 1; +} + +#ifdef MODULE +int init_module(void) +#else +int SILLY_MAKE_INIT(init_maestro(void)) +#endif +{ + struct pci_dev *pcidev = NULL; + int foundone = 0; + + if (!pci_present()) /* No PCI bus in this machine! */ + return -ENODEV; + printk(KERN_INFO "maestro: version " DRIVER_VERSION " time " __TIME__ " " __DATE__ "\n"); + + pcidev = NULL; + + if (dsps_order < 0) { + dsps_order = 1; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + else if (dsps_order > MAX_DSP_ORDER) { + dsps_order = MAX_DSP_ORDER; + printk(KERN_WARNING "maestro: clipping dsps_order to %d\n",dsps_order); + } + +#ifdef CONFIG_APM + init_waitqueue_head(&suspend_queue); +#endif + + /* + * Find the ESS Maestro 2. + */ + + while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1968, pcidev))!=NULL ) { + if (maestro_install(pcidev, TYPE_MAESTRO2)) + foundone=1; + } + + /* + * Find the ESS Maestro 2E + */ + + while( (pcidev = pci_find_device(PCI_VENDOR_ESS, PCI_DEVICE_ID_ESS_ESS1978, pcidev))!=NULL) { + if (maestro_install(pcidev, TYPE_MAESTRO2E)) + foundone=1; + } + + /* + * ESS Maestro 1 + */ + + while((pcidev = pci_find_device(PCI_VENDOR_ESS_OLD, PCI_DEVICE_ID_ESS_ESS0100, pcidev))!=NULL) { + if (maestro_install(pcidev, TYPE_MAESTRO)) + foundone=1; + } + if( ! foundone ) { + printk("maestro: no devices found.\n"); + return -ENODEV; + } + return 0; +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR("Zach Brown , Alan Cox "); +MODULE_DESCRIPTION("ESS Maestro Driver"); +#ifdef M_DEBUG +MODULE_PARM(debug,"i"); +#endif +MODULE_PARM(dsps_order,"i"); + +void cleanup_module(void) +{ + struct ess_card *s; + +#ifdef CONFIG_APM + apm_unregister_callback(maestro_apm_callback); +#endif + while ((s = devs)) { + int i; + devs = devs->next; + + /* XXX maybe should force stop bob, but should be all + stopped by _release by now */ + free_irq(s->irq, s); + unregister_sound_mixer(s->dev_mixer); + for(i=0;ichannels[i]; + if(ess->dev_audio != -1) + unregister_sound_dsp(ess->dev_audio); + } + release_region(s->iobase, 256); + kfree(s); + } + M_printk("maestro: unloading\n"); +} + +#endif /* MODULE */ +#ifdef CONFIG_APM + +void +check_suspend(void) +{ + DECLARE_WAITQUEUE(wait, current); + + if(!in_suspend) return; + + in_suspend++; + add_wait_queue(&suspend_queue, &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule(); + remove_wait_queue(&suspend_queue, &wait); + current->state = TASK_RUNNING; +} + +static int +maestro_apm_suspend(void) +{ + struct ess_card *card; + unsigned long flags; + + save_flags(flags); + cli(); + + for (card = devs; card ; card = card->next) { + int i,j; + + M_printk("maestro: apm in dev %p\n",card); + + for(i=0;ichannels[i]; + + if(s->dev_audio == -1) + continue; + + M_printk("maestro: stopping apus for device %d\n",i); + stop_dac(s); + stop_adc(s); + for(j=0;j<6;j++) + card->apu_map[s->apu[i]][5]=apu_get_register(s,i,5); + + } + + /* get rid of interrupts? */ + if( card->dsps_open > 0) + stop_bob(&card->channels[0]); + } + in_suspend=1; + + restore_flags(flags); + + /* we'll let the bios do the rest of the power down.. */ + + return 0; +} +static int +maestro_apm_resume(void) +{ + struct ess_card *card; + unsigned long flags; + + save_flags(flags); + cli(); + in_suspend=0; + M_printk("maestro: resuming\n"); + + /* first lets just bring everything back. .*/ + for (card = devs; card ; card = card->next) { + int i; + + M_printk("maestro: apm in dev %p\n",card); + + maestro_config(card); + /* need to restore the base pointers.. */ + if(card->dmapages) + set_base_registers(&card->channels[0],card->dmapages); + + mixer_push_state(card); + + for(i=0;ichannels[i]; + int chan,reg; + + if(s->dev_audio == -1) + continue; + + for(chan = 0 ; chan < 6 ; chan++) { + wave_set_register(s,s->apu[chan]<<3,s->apu_base[chan]); + for(reg = 1 ; reg < NR_APU_REGS ; reg++) + apu_set_register(s,chan,reg,s->card->apu_map[s->apu[chan]][reg]); + } + for(chan = 0 ; chan < 6 ; chan++) + apu_set_register(s,chan,0,s->card->apu_map[s->apu[chan]][0] & 0xFF0F); + } + } + + /* now we flip on the music */ + for (card = devs; card ; card = card->next) { + int i; + + M_printk("maestro: apm in dev %p\n",card); + + for(i=0;ichannels[i]; + + /* these use the apu_mode, and can handle + spurious calls */ + start_dac(s); + start_adc(s); + } + if( card->dsps_open > 0) + start_bob(&card->channels[0]); + } + + restore_flags(flags); + + wake_up(&suspend_queue); + + return 0; +} + +int +maestro_apm_callback(apm_event_t ae) { + + M_printk("maestro: apm event received: 0x%x\n",ae); + + switch(ae) { + case APM_SYS_SUSPEND: + case APM_CRITICAL_SUSPEND: + case APM_USER_SUSPEND: + maestro_apm_suspend();break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + case APM_STANDBY_RESUME: + maestro_apm_resume();break; + default: break; + } + + return 0; +} +#endif diff --git a/drivers/sound/maestro.h b/drivers/sound/maestro.h new file mode 100644 index 000000000000..023ec7f968f9 --- /dev/null +++ b/drivers/sound/maestro.h @@ -0,0 +1,60 @@ +/* + * Registers for the ESS PCI cards + */ + +/* + * Memory access + */ + +#define ESS_MEM_DATA 0x00 +#define ESS_MEM_INDEX 0x02 + +/* + * AC-97 Codec port. Delay 1uS after each write. This is used to + * talk AC-97 (see intel.com). Write data then register. + */ + +#define ESS_AC97_INDEX 0x30 /* byte wide */ +#define ESS_AC97_DATA 0x32 + +/* + * Reading is a bit different. You write register|0x80 to ubdex + * delay 1uS poll the low bit of index, when it clears read the + * data value. + */ + +/* + * Control port. Not yet fully understood + * The value 0xC090 gets loaded to it then 0x0000 and 0x2800 + * to the data port. Then after 4uS the value 0x300 is written + */ + +#define RING_BUS_CTRL_L 0x34 +#define RING_BUS_CTRL_H 0x36 + +/* + * This is also used during setup. The value 0x17 is written to it + */ + +#define ESS_SETUP_18 0x18 + +/* + * And this one gets 0x000b + */ + +#define ESS_SETUP_A2 0xA2 + +/* + * And this 0x0000 + */ + +#define ESS_SETUP_A4 0xA4 +#define ESS_SETUP_A6 0xA6 + +/* + * Stuff to do with Harpo - the wave stuff + */ + +#define ESS_WAVETABLE_SIZE 0x14 +#define ESS_WAVETABLE_2M 0xA180 + diff --git a/drivers/sound/sb_ess.c b/drivers/sound/sb_ess.c index ba45fd594c31..b6c9f0aeeb5d 100644 --- a/drivers/sound/sb_ess.c +++ b/drivers/sound/sb_ess.c @@ -10,29 +10,29 @@ * * History: * - * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per + * Rolf Fokkens (Dec 20 1998): ES188x recording level support on a per * fokkensr@vertis.nl input basis. - * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, + * (Dec 24 1998): Recognition of ES1788, ES1887, ES1888, * ES1868, ES1869 and ES1878. Could be used for * specific handling in the future. All except * ES1887 and ES1888 and ES688 are handled like * ES1688. - * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now + * (Dec 27 1998): RECLEV for all (?) ES1688+ chips. ES188x now * have the "Dec 20" support + RECLEV - * (Jan 2 1999): Preparation for Full Duplex. This means + * (Jan 2 1999): Preparation for Full Duplex. This means * Audio 2 is now used for playback when dma16 * is specified. The next step would be to use * Audio 1 and Audio 2 at the same time. - * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this + * (Jan 9 1999): Put all ESS stuff into sb_ess.[ch], this * includes both the ESS stuff that has been in * sb_*[ch] before I touched it and the ESS support * I added later - * (Jan 23 1999): Full Duplex seems to work. I wrote a small + * (Jan 23 1999): Full Duplex seems to work. I wrote a small * test proggy which works OK. Haven't found * any applications to test it though. So why did * I bother to create it anyway?? :) Just for * fun. - * (May 2 1999): I tried to be too smart by "introducing" + * (May 2 1999): I tried to be too smart by "introducing" * ess_calc_best_speed (). The idea was that two * dividers could be used to setup a samplerate, * ess_calc_best_speed () would choose the best. @@ -40,10 +40,12 @@ * recording problems for high samplerates. I * fixed this by removing ess_calc_best_speed () * and just doing what the documentation says. - * Andy Sloane (June 4 1999): Stole some code from ALSA to fix the playback + * Andy Sloane (Jun 4 1999): Stole some code from ALSA to fix the playback * andy@guildsoftware.com speed on ES1869, ES1879, ES1887, and ES1888. * 1879's were previously ignored by this driver; * added (untested) support for those. + * Cvetan Ivanov (Oct 27 1999): Fixed ess_dsp_init to call ess_set_dma_hw for + * zezo@inet.bg _ALL_ ESS models, not only ES1887 * * This files contains ESS chip specifics. It's based on the existing ESS * handling as it resided in sb_common.c, sb_mixer.c and sb_audio.c. This @@ -52,7 +54,7 @@ * - RECLEV support for ES1688 and later * - 6 bits playback level support chips later than ES1688 * - Recording level support on a per-device basis for ES1887 - * - Full-Duplex for ES1887 (under development) + * - Full-Duplex for ES1887 * * Full duplex is enabled by specifying dma16. While the normal dma must * be one of 0, 1 or 3, dma16 can be one of 0, 1, 3 or 5. DMA 5 is a 16 bit @@ -1304,6 +1306,13 @@ printk(KERN_INFO "ess_set_dma_hw: dma8=%d,dma16=%d,dup=%d\n" */ int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) { + /* + * Caller also checks this, but anyway + */ + if (devc->model != MDL_ESS) { + printk (KERN_INFO "ess_dsp_init for non ESS chip\n"); + return 1; + } /* * This for ES1887 to run Full Duplex. Actually ES1888 * is allowed to do so too. I have no idea yet if this @@ -1324,15 +1333,12 @@ int ess_dsp_init (sb_devc *devc, struct address_info *hw_config) if (devc->dma8 != devc->dma16 && devc->dma16 != -1) { devc->duplex = 1; } - - if (!ess_set_dma_hw (devc)) { - free_irq(devc->irq, devc); - return 0; - } - return 1; - } else { - return -1; } + if (!ess_set_dma_hw (devc)) { + free_irq(devc->irq, devc); + return 0; + } + return 1; } /**************************************************************************** diff --git a/drivers/sound/sound_core.c b/drivers/sound/sound_core.c index fdcf11dcf455..9bfd0d97778d 100644 --- a/drivers/sound/sound_core.c +++ b/drivers/sound/sound_core.c @@ -55,6 +55,9 @@ struct sound_unit #ifdef CONFIG_SOUND_SONICVIBES extern int init_sonicvibes(void); #endif +#ifdef CONFIG_SOUND_MAESTRO +extern int init_maestro(void); +#endif #ifdef CONFIG_SOUND_ES1370 extern int init_es1370(void); #endif @@ -410,6 +413,9 @@ int soundcore_init(void) #ifdef CONFIG_SOUND_ES1371 init_es1371(); #endif +#ifdef CONFIG_SOUND_MAESTRO + init_maestro(); +#endif #ifdef CONFIG_SOUND_MSNDCLAS msnd_classic_init(); #endif diff --git a/fs/buffer.c b/fs/buffer.c index e57f678f4d33..404e5705c1ac 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1512,7 +1512,7 @@ void __init buffer_init(unsigned long memory_size) fsync times (ext2) manageable, is the following */ memory_size >>= 20; - for (order = 5; (1UL << order) < memory_size; order++); + for (order = 0; (1UL << order) < memory_size; order++); /* try to allocate something until we get it or we're asking for something that is really too small */ @@ -1579,7 +1579,6 @@ void wakeup_bdflush(int wait) return; wake_up(&bdflush_wait); if (wait) { - run_task_queue(&tq_disk); sleep_on(&bdflush_done); } } diff --git a/fs/inode.c b/fs/inode.c index 66c1dd0e382f..c5071f119341 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -438,6 +438,10 @@ static struct inode * grow_inodes(void) inode = list_entry(tmp, struct inode, i_list); return inode; } + spin_unlock(&inode_lock); + printk(KERN_WARNING + "grow_inodes: inode-max limit reached\n"); + return NULL; } spin_unlock(&inode_lock); diff --git a/include/linux/apm_bios.h b/include/linux/apm_bios.h index 7dc036b25ffe..caf0b4b76430 100644 --- a/include/linux/apm_bios.h +++ b/include/linux/apm_bios.h @@ -3,7 +3,7 @@ /* * Include file for the interface to an APM BIOS - * Copyright 1994-1998 Stephen Rothwell (Stephen.Rothwell@canb.auug.org.au) + * Copyright 1994-1999 Stephen Rothwell (sfr@linuxcare.com) * * 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 diff --git a/include/linux/sched.h b/include/linux/sched.h index 4a5ffd58b555..a52c57d9bb30 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -737,6 +737,7 @@ do { \ add_wait_queue(&wq, &__wait); \ for (;;) { \ current->state = TASK_UNINTERRUPTIBLE; \ + mb(); \ if (condition) \ break; \ schedule(); \ @@ -760,6 +761,7 @@ do { \ add_wait_queue(&wq, &__wait); \ for (;;) { \ current->state = TASK_INTERRUPTIBLE; \ + mb(); \ if (condition) \ break; \ if (!signal_pending(current)) { \ diff --git a/kernel/exit.c b/kernel/exit.c index 8d5d72720d7c..faabb183a26e 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -444,6 +444,13 @@ asmlinkage int sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct add_wait_queue(¤t->wait_chldexit,&wait); repeat: flag = 0; + + /* The interruptible state must be set before looking at the + children. This because we want to catch any racy exit from + the children as do_exit() may run under us. The following + read_lock will enforce SMP ordering at the CPU level. */ + current->state = TASK_INTERRUPTIBLE; + read_lock(&tasklist_lock); for (p = current->p_cptr ; p ; p = p->p_osptr) { if (pid>0) { @@ -510,13 +517,13 @@ repeat: retval = -ERESTARTSYS; if (signal_pending(current)) goto end_wait4; - current->state=TASK_INTERRUPTIBLE; schedule(); goto repeat; } retval = -ECHILD; end_wait4: remove_wait_queue(¤t->wait_chldexit,&wait); + current->state = TASK_RUNNING; return retval; } diff --git a/net/core/dev.c b/net/core/dev.c index 963304594845..0b6268f43403 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -57,6 +57,8 @@ * A network device unload needs to purge * the backlog queue. * Paul Rusty Russel : SIOCSIFNAME + * Andrea Arcangeli : dev_clear_backlog() needs the + * skb_queue_lock held. */ #include @@ -711,7 +713,8 @@ static void netdev_wakeup(void) static void dev_clear_backlog(struct device *dev) { - struct sk_buff *prev, *curr; + struct sk_buff *curr; + unsigned long flags; /* * @@ -719,27 +722,24 @@ static void dev_clear_backlog(struct device *dev) * * We are competing here both with netif_rx() and net_bh(). * We don't want either of those to mess with skb ptrs - * while we work on them, thus cli()/sti(). - * - * It looks better to use net_bh trick, at least - * to be sure, that we keep interrupt latency really low. --ANK (980727) + * while we work on them, thus we must grab the + * skb_queue_lock. */ if (backlog.qlen) { - start_bh_atomic(); - curr = backlog.next; - while ( curr != (struct sk_buff *)(&backlog) ) { - unsigned long flags; - curr=curr->next; - if ( curr->prev->dev == dev ) { - prev = curr->prev; - spin_lock_irqsave(&skb_queue_lock, flags); - __skb_unlink(prev, &backlog); + repeat: + spin_lock_irqsave(&skb_queue_lock, flags); + for (curr = backlog.next; + curr != (struct sk_buff *)(&backlog); + curr = curr->next) + if (curr->dev == dev) + { + __skb_unlink(curr, &backlog); spin_unlock_irqrestore(&skb_queue_lock, flags); - kfree_skb(prev); + kfree_skb(curr); + goto repeat; } - } - end_bh_atomic(); + spin_unlock_irqrestore(&skb_queue_lock, flags); #ifdef CONFIG_NET_HW_FLOWCONTROL if (netdev_dropping) netdev_wakeup(); diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c index 076f07b06638..1042684f388a 100644 --- a/net/ipv4/ip_forward.c +++ b/net/ipv4/ip_forward.c @@ -176,7 +176,7 @@ int ip_forward(struct sk_buff *skb) (icmph->type==ICMP_TIME_EXCEEDED)) { #endif - maddr = rt->rt_src; + u32 maddr = rt->rt_src; fw_res = ip_fw_masq_icmp(&skb, maddr); if (fw_res < 0) { kfree_skb(skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 4436ac0d5add..f4fee5acd3f6 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1617,7 +1617,8 @@ static inline struct sock *tcp_v4_hnd_req(struct sock *sk,struct sk_buff *skb) sk = tcp_check_req(sk, skb, req); } #ifdef CONFIG_SYN_COOKIES - else if (flg == __constant_htonl(0x00120000)) { + else if ((flg & __constant_htonl(0x00120000))==__constant_htonl(0x00100000)) + { sk = cookie_v4_check(sk, skb, &(IPCB(skb)->opt)); } #endif -- 2.39.5