proxy_arp - BOOLEAN
Do proxy arp.
+hidden - BOOLEAN
+ Hide addresses attached to this device from another devices.
+ Such addresses will never be selected by source address autoselection
+ mechanism, host does not answer broadcast ARP requests for them,
+ does not announce it as source address of ARP requests, but they
+ are still reachable via IP. This flag is activated only if it is
+ enabled both in specific device section and in "all" section.
+
shared_media - BOOLEAN
undocumented.
Accept ICMP redirect messages only for gateways, listed in
default gateway list. Enabled by default.
+hidden
+ Hide addresses attached to this device from another devices.
+ Such addresses will never be selected by source address autoselection
+ mechanism, host does not answer broadcast ARP requests for them,
+ does not announce it as source address of ARP requests, but they
+ are still reachable via IP. This flag is activated only if it is
+ enabled both in specific device section and in "all" section.
+
shared_media
If it is not set the kernel does not assume that different subnets
on this device can communicate directly. Default setting is 'yes'.
Documentation for the NeoMagic 256AV/256ZX sound driver
=======================================================
-You're looking at version 1.0 of the driver. (Woohoo!) It has been
+You're looking at version 1.11 of the driver. (Woohoo!) It has been
successfully tested against the following laptop models:
- Sony Z505S/Z505SX/Z505DX
+ Sony Z505S/Z505SX/Z505DX/Z505RX
Sony F150, F160, F180, F250, F270, F280, PCG-F26
Dell Latitude CPi, CPt (various submodels)
NeoMagic. There is no warranty, expressed, implied, or otherwise. It
is free software in the public domain; feel free to use it, sell it,
give it to your best friends, even claim that you wrote it (but why?!)
-but don't come whining to me, NeoMagic, Sony, Dell, or anyone else
+but don't go whining to me, NeoMagic, Sony, Dell, or anyone else
when it blows up your computer.
+Version 1.11 fixes a bug uncovered by Timidity--the sound would start clicking
+after the first MIDI file was finished playing. There are no other changes
+over 1.1.
+
============
Installation
============
another device (it normally shares IRQ 9 with the builtin eepro100
ethernet on the Sony Z505 laptops).
-It does not run the card in any sort of compatibility mode. Thus it
-almost certainly will not work on laptops that have the
-SB16-compatible codec/mixer; you will want to use the standard SB16
-OSS driver with these chipsets. I cannot provide any assistance with
-machines using the SB-16 compatible version.
+It does not run the card in any sort of compatibility mode. It will
+not work on laptops that have the SB16-compatible, AD1848-compatible
+or CS4232-compatible codec/mixer; you will want to use the appropriate
+compatible OSS driver with these chipsets. I cannot provide any
+assistance with machines using the SB16, AD1848 or CS4232 compatible
+versions. (The driver now attempts to detect the mixer version, and
+will refuse to load if it believes the hardware is not not
+AC97-compatible.)
The sound support is very basic, but it does include simultaneous
playback and record capability. The mixer support is also quite
simple, although this is in keeping with the rather limited
-functionality of the chipset.
+functionality of the chipset.
There is no hardware synthesizer available, as the Losedows OPL-3 and
MIDI support is done via hardware emulation.
so of course the CD-ROM input doesn't work. It does work on laptops
with a builtin CD-ROM drive.
-Recording is mono 8-bit only.
-
The mixer device does not appear to have any tone controls, at least
on the Z505 series. The mixer module checks for tone controls in the
AC97 mixer, and will enable them if they are available.
limitation. It may be possible to support other speeds in the future.
* There is no support for the telephone mixer/codec. There is support
- for a phonein/phoneout device if your mixer program supports it;
- whether or not it does anything is anyone's guess. (Reports on this
- would be appreciated.)
-
+ for a phonein/phoneout device in the mixer driver; whether or not
+ it does anything is anyone's guess. (Reports on this would be
+ appreciated. You'll have to figure out how to get the phone to
+ go off-hook before it'll work, tho.)
+
* This driver was not written with any cooperation or support from
NeoMagic. If you have any questions about this, see their website
for their official stance on supporting open source drivers.
* Sometimes the NM256 driver has to guess at where the buffer
should be placed, especially if the module is loaded after the
- X server is started. It's usually correct, but it will fail on
- the Sony F250.
+ X server is started. It's usually correct, but it will consistently
+ fail on the Sony F250.
* Virtual screens greater than 1024x768x16 under XFree86 are
problematic on laptops with only 2.5MB of screen RAM. This
On the F250, it is possible to force the driver to load properly even
after the XFree86 server is started by doing:
- insmod nm256.o buffertop=0x25a800
+ insmod nm256 buffertop=0x25a800
This forces the audio buffers to the correct offset in screen RAM.
+One user has reported a similar problem on the Sony F270, although
+others apparently aren't seeing any problems. His suggested command
+is
+
+ insmod nm256 buffertop=0x272800
+
=================
Official WWW site
=================
You should always be able to get the latest version of the driver there,
and the driver will be supported for the foreseeable future.
+==============
+Z505RX and IDE
+==============
+
+There appears to be a problem with the IDE chipset on the Z505RX; one
+of the symptoms is that sound playback periodically hangs (when the
+disk is accessed). The user reporting the problem also reported that
+enabling all of the IDE chipset workarounds in the kernel solved the
+problem, tho obviously only one of them should be needed--if someone
+can give me more details I would appreciate it.
+
==============================
Z505S/Z505SX on-board Ethernet
==============================
==================================
There is also a known problem with the Sony Z505S and Z505SX hanging
-if a PCMCIA card is inserted while the ethernet driver is loaded.
-This is caused by tons of spurious IRQ 9s, probably generated from the
-PCMCIA or ACPI bridges. There is currently no fix for the problem,
-and the only known workaround is to disable the ethernet interface
-before inserting or removing a PCMCIA card.
+if a PCMCIA card is inserted while the ethernet driver is loaded, or
+in some cases if the laptop is suspended. This is caused by tons of
+spurious IRQ 9s, probably generated from the PCMCIA or ACPI bridges.
+
+There is currently no fix for the problem that works in every case.
+The only known workarounds are to disable the ethernet interface
+before inserting or removing a PCMCIA card, or with some cards
+disabling the PCMCIA card before ejecting it will also help the
+problem with the laptop hanging when the card is ejected.
+
+One user has reported that setting the tcic's cs_irq to some value
+other than 9 (like 11) fixed the problem. This doesn't work on my
+Z505S, however--changing the value causes the cardmgr to stop seeing
+card insertions and removals, cards don't seem to work correctly, and
+I still get hangs if a card is inserted when the kernel is booted.
+
+Using the latest ethernet driver and pcmcia package allows me to
+insert an Adaptec 1480A SlimScsi card without the laptop hanging,
+although I still have to shut down the card before ejecting or
+powering down the laptop. However, similar experiments with a DE-660
+ethernet card still result in hangs when the card is inserted. I am
+beginning to think that the interrupts are CardBus-related, since the
+Adaptec card is a CardBus card, and the DE-660 is not; however, I
+don't have any other CardBus cards to test with.
======
Thanks
Jeff Garzik, for various helpful suggestions on the AC97
interface
+ "Mr. Bumpy" for feedback on the Z505RX
+
+ Bill Nottingham, for generous assistance in getting the mixer ID
+ code working
+
=================
Previous versions
=================
Version 0.75 renamed all the functions and files with slightly more
generic names.
+
+Version 1.1 contains a change to try and detect non-AC97 versions of
+the hardware, and not install itself appropriately. It should also
+reinitialize the hardware on an APM resume event, assuming that APM
+was configured into your kernel.
+
+Note that previous versions of this document claimed that recording was
+8-bit only; it actually has been working for 16-bits all along.
S: Maintained
TLAN NETWORK DRIVER
+P: Torben Mathiasen
+M: torben.mathiasen@compaq.com
L: tlan@vuser.vu.union.edu
-S: Orphan
+S: Maintained
TOKEN-RING NETWORK DRIVER
P: Paul Norton
VERSION = 2
PATCHLEVEL = 2
SUBLEVEL = 14
-EXTRAVERSION = pre12
+EXTRAVERSION = pre13
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
if (cluster->usage & 3)
continue;
pfn = cluster->start_pfn;
+ if (pfn >= MAP_NR(end_mem)) /* if we overrode mem size */
+ continue;
nr = cluster->numpages;
+ if ((pfn + nr) > MAP_NR(end_mem)) /* if override in cluster */
+ nr = MAP_NR(end_mem) - pfn;
while (nr--)
clear_bit(PG_reserved, &mem_map[pfn++].flags);
the last slot of the L1 page table. */
memset((void *) ZERO_PAGE(0), 0, PAGE_SIZE);
memset(swapper_pg_dir, 0, PAGE_SIZE);
- newptbr = ((unsigned long) swapper_pg_dir - PAGE_OFFSET) >> PAGE_SHIFT;
+ newptbr = MAP_NR(swapper_pg_dir);
pgd_val(swapper_pg_dir[1023]) =
(newptbr << 32) | pgprot_val(PAGE_KERNEL);
kill_page(tmp);
free_page(tmp);
}
- tmp = nr_free_pages << PAGE_SHIFT;
- printk("Memory: %luk available\n", tmp >> 10);
+ tmp = nr_free_pages << (PAGE_SHIFT - 10);
+ printk("Memory: %luk available\n", tmp);
return;
}
i = max_mapnr;
val->totalram = 0;
val->sharedram = 0;
- val->freeram = nr_free_pages << PAGE_SHIFT;
+ val->freeram = ((unsigned long)nr_free_pages) << PAGE_SHIFT;
val->bufferram = buffermem;
while (i-- > 0) {
if (PageReserved(mem_map+i))
volatile unsigned long kstack_ptr; /* Stack vector for booting CPUs */
struct cpuinfo_x86 cpu_data[NR_CPUS]; /* Per CPU bogomips and other parameters */
static unsigned int num_processors = 1; /* Internal processor count */
-unsigned long mp_ioapic_addr = 0xFEC00000; /* Address of the I/O apic (not yet used) */
unsigned char boot_cpu_id = 0; /* Processor that is doing the boot up */
static int smp_activated = 0; /* Tripped once we need to start cross invalidating */
int apic_version[NR_CPUS]; /* APIC version number */
cpu_present_map=3;
num_processors=2;
printk("I/O APIC at 0xFEC00000.\n");
+ mp_apics[0].mpc_apicaddr = 0xFEC00000;
+ mp_apic_entries = 1;
/*
* Save the default type number, we
#endif
spin_lock_irqsave(&io_request_lock, flags); /* Is this really necessary? */
if ((rq->nr_sectors -= nsect) <= 0)
+ {
+ spin_unlock_irqrestore(&io_request_lock, flags);
break;
+ }
if ((rq->current_nr_sectors -= nsect) == 0) {
if ((rq->bh = rq->bh->b_reqnext) != NULL) {
rq->current_nr_sectors = rq->bh->b_size>>9;
/* Do what the default llseek() code would have done */
file->f_pos = new_offset;
file->f_reada = 0;
- file->f_version = ++event;
+ file->f_version = ++global_event;
}
if (file->f_op->write == NULL) {
* Alan Cox <alan@redhat.com>: Fixed the out of memory
* handling.
*
+ * Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer!
+ *
********************************************************************/
*
** This file is best viewed/edited with tabstop=4, colums>=132
*
+ * Dec 10, 1999 Torben Mathiasen <torben.mathiasen@compaq.com>
+ * New Maintainer
********************************************************************/
* along with this kernel; if not, write to the Free Software *
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
* *
- * Tested with Linux 1.2.13, ..., 2.2.4 *
+ * Tested with Linux 1.2.13, ..., 2.2.12 *
* *
* $Log: gdth.c,v $
+ * Revision 1.30 1999/11/02 13:42:39 achim
+ * ARRAY_DRV_LIST2 implemented
+ * Now 255 log. and 100 host drives supported
+ *
+ * Revision 1.29 1999/10/05 13:28:47 achim
+ * GDT_CLUST_RESET added
+ *
+ * Revision 1.28 1999/08/12 13:44:54 achim
+ * MOUNTALL removed
+ * Cluster drives -> removeable drives
+ *
+ * Revision 1.27 1999/06/22 07:22:38 achim
+ * Small changes
+ *
+ * Revision 1.26 1999/06/10 16:09:12 achim
+ * Cluster Host Drive support: Bugfixes
+ *
+ * Revision 1.25 1999/06/01 16:03:56 achim
+ * gdth_init_pci(): Manipulate config. space to start RP controller
+ *
+ * Revision 1.24 1999/05/26 11:53:06 achim
+ * Cluster Host Drive support added
+ *
* Revision 1.23 1999/03/26 09:12:31 achim
* Default value for hdr_channel set to 0
*
* Initial revision
*
************************************************************************/
-#ident "$Id: gdth.c,v 1.23 1999/03/26 09:12:31 achim Exp $"
+#ident "$Id: gdth.c,v 1.30 1999/11/02 13:42:39 achim Exp $"
/* All GDT Disk Array Controllers are fully supported by this driver.
* This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the
{
*cyls = size /HEADS/SECS;
if (*cyls <= MAXCYLS) {
- *heads = HEADS;
- *secs = SECS;
- } else { /* too high for 64*32 */
- *cyls = size /MEDHEADS/MEDSECS;
- if (*cyls <= MAXCYLS) {
- *heads = MEDHEADS;
- *secs = MEDSECS;
- } else { /* too high for 127*63 */
- *cyls = size /BIGHEADS/BIGSECS;
- *heads = BIGHEADS;
- *secs = BIGSECS;
- }
+ *heads = HEADS;
+ *secs = SECS;
+ } else { /* too high for 64*32 */
+ *cyls = size /MEDHEADS/MEDSECS;
+ if (*cyls <= MAXCYLS) {
+ *heads = MEDHEADS;
+ *secs = MEDSECS;
+ } else { /* too high for 127*63 */
+ *cyls = size /BIGHEADS/BIGSECS;
+ *heads = BIGHEADS;
+ *secs = BIGSECS;
+ }
}
}
}
TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n",
pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn),
- pcistr[cnt].irq, pcistr[cnt].dpmem));
+ pcistr[cnt].irq, (int)pcistr[cnt].dpmem));
cnt++;
}
#else
register gdt6m_dpram_str *dp6m_ptr;
ulong32 retries;
unchar prot_ver;
+ ushort command;
int i, found = FALSE;
+#if LINUX_VERSION_CODE < 0x2015C
+ int rom_addr;
+#endif
TRACE(("gdth_init_pci()\n"));
return 0;
}
+ /* manipulate config. space to enable DPMEM, start RP controller */
+#if LINUX_VERSION_CODE >= 0x2015C
+ pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command);
+ command |= 6;
+ pci_write_config_word(pcistr->pdev, PCI_COMMAND, command);
+ if (pcistr->pdev->rom_address == 1UL)
+ pcistr->pdev->rom_address = 0UL;
+ i = 0xFEFF0001UL;
+ pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, i);
+ gdth_delay(1);
+ pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS,
+ pcistr->pdev->rom_address);
+#else
+ pcibios_read_config_word(pcistr->bus, pcistr->device_fn,
+ PCI_COMMAND, &command);
+ command |= 6;
+ pcibios_write_config_word(pcistr->bus, pcistr->device_fn,
+ PCI_COMMAND, command);
+ pcibios_read_config_dword(pcistr->bus, pcistr->device_fn,
+ PCI_ROM_ADDRESS, &rom_addr);
+ if (rom_addr == 1UL)
+ rom_addr = 0UL;
+ i = 0xFEFF0001UL;
+ pcibios_write_config_dword(pcistr->bus, pcistr->device_fn,
+ PCI_ROM_ADDRESS, i);
+ gdth_delay(1);
+ pcibios_write_config_dword(pcistr->bus, pcistr->device_fn,
+ PCI_ROM_ADDRESS, rom_addr);
+#endif
+
/* check and reset interface area */
dp6m_ptr = (gdt6m_dpram_str *)ha->brd;
gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u);
gdth_drlist_str *drl;
gdth_iochan_str *ioc;
gdth_raw_iochan_str *iocr;
- gdth_arraylist_str *alst;
+ gdth_arcdl_str *alst;
+ gdth_alist_str *alst2;
TRACE(("gdth_search_drives() hanum %d\n",hanum));
ha = HADATA(gdth_ctr_tab[hanum]);
TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n"));
cdev_cnt = (ushort)ha->info;
- /* mount all cache devices */
- gdth_internal_cmd(hanum,CACHESERVICE,GDT_MOUNT,0xffff,1,0);
- TRACE2(("gdth_search_drives(): mountall CACHESERVICE OK\n"));
-
- /* initialize cache service after mountall */
- if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) {
- printk("GDT: Initialization error cache service (code %d)\n",
- ha->status);
- return 0;
- }
- TRACE2(("gdth_search_drives() CACHES. init. after mountall\n"));
- cdev_cnt = (ushort)ha->info;
-
/* detect number of buses - try new IOCTL */
iocr = (gdth_raw_iochan_str *)ha->pscratch;
iocr->hdr.version = 0xffffffff;
INVALID_CHANNEL,drv_cnt * sizeof(ulong32))) {
for (j = 0; j < drv_cnt; ++j) {
drv_no = ((ulong32 *)ha->pscratch)[j];
- if (drv_no < MAX_HDRIVES) {
+ if (drv_no < MAX_LDRIVES) {
ha->hdr[drv_no].is_logdrv = TRUE;
TRACE2(("Drive %d is log. drive\n",drv_no));
}
}
}
+ alst = (gdth_arcdl_str *)ha->pscratch;
+ alst->entries_avail = MAX_LDRIVES;
+ alst->first_entry = 0;
+ alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]);
if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
- ARRAY_DRV_LIST | LA_CTRL_PATTERN,
- 0, 35 * sizeof(gdth_arraylist_str))) {
+ ARRAY_DRV_LIST2 | LA_CTRL_PATTERN,
+ INVALID_CHANNEL, sizeof(gdth_arcdl_str) +
+ (alst->entries_avail-1) * sizeof(gdth_alist_str))) {
+ for (j = 0; j < alst->entries_init; ++j) {
+ ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd;
+ ha->hdr[j].is_master = alst->list[j].is_master;
+ ha->hdr[j].is_parity = alst->list[j].is_parity;
+ ha->hdr[j].is_hotfix = alst->list[j].is_hotfix;
+ ha->hdr[j].master_no = alst->list[j].cd_handle;
+ }
+ } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,
+ ARRAY_DRV_LIST | LA_CTRL_PATTERN,
+ 0, 35 * sizeof(gdth_alist_str))) {
for (j = 0; j < 35; ++j) {
- alst = &((gdth_arraylist_str *)ha->pscratch)[j];
- ha->hdr[j].is_arraydrv = alst->is_arrayd;
- ha->hdr[j].is_master = alst->is_master;
- ha->hdr[j].is_parity = alst->is_parity;
- ha->hdr[j].is_hotfix = alst->is_hotfix;
- ha->hdr[j].master_no = alst->cd_handle;
+ alst2 = &((gdth_alist_str *)ha->pscratch)[j];
+ ha->hdr[j].is_arraydrv = alst2->is_arrayd;
+ ha->hdr[j].is_master = alst2->is_master;
+ ha->hdr[j].is_parity = alst2->is_parity;
+ ha->hdr[j].is_hotfix = alst2->is_hotfix;
+ ha->hdr[j].master_no = alst2->cd_handle;
}
}
}
for (i=0; i<cdev_cnt && i<MAX_HDRIVES; ++i) {
TRACE(("gdth_search_drives() cachedev. %d\n",i));
if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,i,0,0)) {
- /* static relation between host drive number and Bus/ID */
- TRACE(("gdth_search_dr() drive %d mapped to bus/id %d/%d\n",
- i,ha->bus_cnt,i));
-
ha->hdr[i].present = TRUE;
ha->hdr[i].size = ha->info;
/* evaluate mapping (sectors per head, heads per cylinder) */
ha->hdr[i].size &= ~SECS32;
if (ha->info2 == 0) {
- gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs);
+ gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs);
} else {
drv_hds = ha->info2 & 0xff;
drv_secs = (ha->info2 >> 8) & 0xff;
/* get informations about device */
if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i,
0,0)) {
- TRACE(("gdth_search_dr() cache drive %d devtype %d\n",
+ TRACE2(("gdth_search_dr() cache drive %d devtype %d\n",
i,ha->info));
ha->hdr[i].devtype = (ushort)ha->info;
}
+
+ /* cluster info */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,i,
+ 0,0)) {
+ TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n",
+ i,ha->info));
+ ha->hdr[i].cluster_type = (unchar)ha->info;
+ } else {
+ ha->hdr[i].cluster_type = 0;
+ }
+
+ /* R/W attributes */
+ if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,i,
+ 0,0)) {
+ TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n",
+ i,ha->info));
+ ha->hdr[i].rw_attribs = (unchar)ha->info;
+ } else {
+ ha->hdr[i].rw_attribs = 0;
+ }
}
}
}
#if LINUX_VERSION_CODE >= 0x010300
- if (nscp->done != gdth_scsi_done)
+ if (nscp->done != gdth_scsi_done || nscp->cmnd[0] != 0xff)
#endif
{
if (nscp->SCp.phase == -1) {
- nscp->SCp.phase = SCSIRAWSERVICE; /* default: raw svc. */
+ nscp->SCp.phase = CACHESERVICE; /* default: cache svc. */
if (nscp->cmnd[0] == TEST_UNIT_READY) {
TRACE2(("TEST_UNIT_READY Bus %d Id %d LUN %d\n",
b, t, nscp->lun));
if (b == 0 && ((t == 0 && nscp->lun == 1) ||
(t == 1 && nscp->lun == 0))) {
nscp->SCp.Status = GDT_SCAN_START;
- nscp->SCp.phase |= ((ha->scan_mode & 0x10 ? 1:0) << 8);
+ nscp->SCp.phase = ((ha->scan_mode & 0x10 ? 1:0) << 8)
+ | SCSIRAWSERVICE;
ha->scan_mode = 0x12;
TRACE2(("Scan mode: 0x%x (SCAN_START)\n",
ha->scan_mode));
}
} else if (ha->scan_mode == 0x12) {
if (b == ha->bus_cnt && t == ha->tid_cnt-1) {
+ nscp->SCp.phase = SCSIRAWSERVICE;
nscp->SCp.Status = GDT_SCAN_END;
ha->scan_mode &= 0x10;
TRACE2(("Scan mode: 0x%x (SCAN_END)\n",
}
}
}
+ if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY &&
+ nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE &&
+ (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) {
+ if (!(ha->hdr[t].cluster_type & CLUSTER_MOUNTED)) {
+ /* cluster drive NOT MOUNTED */
+ if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) {
+ /* cluster drive NOT RESERVED */
+ nscp->SCp.Status = GDT_MOUNT;
+ } else {
+ /* cluster drive RESERVED (on the other node) */
+ nscp->SCp.Status = GDT_CLUST_INFO;
+ }
+ } else {
+ if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) {
+ /* cluster drive MOUNTED and not RESERVED */
+ nscp->SCp.Status = GDT_CLUST_INFO;
+ }
+ }
+ }
}
}
if (nscp->SCp.Status != -1) {
- if ((nscp->SCp.phase & 0xff) == SCSIRAWSERVICE) {
+ if ((nscp->SCp.phase & 0xff) == CACHESERVICE) {
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ next_cmd = FALSE;
+ } else if ((nscp->SCp.phase & 0xff) == SCSIRAWSERVICE) {
if (!(cmd_index=gdth_fill_raw_cmd(hanum,nscp,BUS_L2P(ha,b))))
this_cmd = FALSE;
next_cmd = FALSE;
+ } else {
+ memset((char*)nscp->sense_buffer,0,16);
+ nscp->sense_buffer[0] = 0x70;
+ nscp->sense_buffer[2] = NOT_READY;
+ nscp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ if (!nscp->SCp.have_data_in)
+ nscp->SCp.have_data_in++;
+ else {
+ GDTH_UNLOCK_HA(ha,flags);
+ /* io_request_lock already active ! */
+ nscp->scsi_done(nscp);
+ GDTH_LOCK_HA(ha,flags);
+ }
}
} else
#if LINUX_VERSION_CODE >= 0x010300
- if (nscp->done == gdth_scsi_done) {
+ if (nscp->done == gdth_scsi_done && nscp->cmnd[0] == 0xff) {
if (!(cmd_index=gdth_special_cmd(hanum,nscp)))
this_cmd = FALSE;
next_cmd = FALSE;
}
break;
+ case RESERVE:
+ case RELEASE:
+ TRACE2(("cache cmd %s\n",nscp->cmnd[0] == RESERVE ?
+ "RESERVE" : "RELEASE"));
+ if (!(cmd_index=gdth_fill_cache_cmd(hanum,nscp,t)))
+ this_cmd = FALSE;
+ break;
+
case READ_6:
case WRITE_6:
case READ_10:
inq.type_qual = (ha->hdr[t].devtype&4) ? TYPE_ROM:TYPE_DISK;
/* you can here set all disks to removable, if you want to do
a flush using the ALLOW_MEDIUM_REMOVAL command */
- inq.modif_rmb = ha->hdr[t].devtype&1 ? 0x80:0x00;
+ inq.modif_rmb = 0x00;
+ if ((ha->hdr[t].devtype & 1) ||
+ (ha->hdr[t].cluster_type & CLUSTER_DRIVE))
+ inq.modif_rmb = 0x80;
inq.version = 2;
inq.resp_aenc = 2;
inq.add_length= 32;
register gdth_cmd_str *cmdp;
struct scatterlist *sl;
ushort i;
- int cmd_index;
+ int cmd_index, read_write;
ha = HADATA(gdth_ctr_tab[hanum]);
cmdp = ha->pccb;
gdth_set_sema0(hanum);
/* fill command */
- if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
+ read_write = FALSE;
+ if (scp->SCp.Status != -1)
+ cmdp->OpCode = scp->SCp.Status; /* special cache cmd. */
+ else if (scp->cmnd[0] == RESERVE)
+ cmdp->OpCode = GDT_RESERVE_DRV;
+ else if (scp->cmnd[0] == RELEASE)
+ cmdp->OpCode = GDT_RELEASE_DRV;
+ else if (scp->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
if (scp->cmnd[4] & 1) /* prevent ? */
- cmdp->OpCode = GDT_MOUNT;
+ cmdp->OpCode = GDT_MOUNT;
else if (scp->cmnd[3] & 1) /* removable drive ? */
- cmdp->OpCode = GDT_UNMOUNT;
+ cmdp->OpCode = GDT_UNMOUNT;
+ else
+ cmdp->OpCode = GDT_FLUSH;
+ } else if (scp->cmnd[0] == WRITE_6 || scp->cmnd[0] == WRITE_10) {
+ read_write = TRUE;
+ if (gdth_write_through || ((ha->hdr[hdrive].rw_attribs & 1) &&
+ (ha->cache_feat & GDT_WR_THROUGH)))
+ cmdp->OpCode = GDT_WRITE_THR;
else
- cmdp->OpCode = GDT_FLUSH;
+ cmdp->OpCode = GDT_WRITE;
} else {
- if (scp->cmnd[0]==WRITE_6 || scp->cmnd[0]==WRITE_10) {
- if (gdth_write_through)
- cmdp->OpCode = GDT_WRITE_THR;
- else
- cmdp->OpCode = GDT_WRITE;
- } else {
- cmdp->OpCode = GDT_READ;
- }
+ read_write = TRUE;
+ cmdp->OpCode = GDT_READ;
}
+
+ cmdp->BoardNode = LOCALBOARD;
+ cmdp->u.cache.DeviceNo = hdrive;
+ cmdp->u.cache.BlockNo = 1;
+ cmdp->u.cache.sg_canz = 0;
- cmdp->BoardNode = LOCALBOARD;
- cmdp->u.cache.DeviceNo = hdrive;
-
- if (scp->cmnd[0]==ALLOW_MEDIUM_REMOVAL) {
- cmdp->u.cache.BlockNo = 1;
- cmdp->u.cache.sg_canz = 0;
- } else {
+ if (read_write) {
if (scp->cmd_len != 6) {
cmdp->u.cache.BlockNo = ntohl(*(ulong32*)&scp->cmnd[2]);
cmdp->u.cache.BlockCnt= (ulong32)ntohs(*(ushort*)&scp->cmnd[7]);
}
/* cache or raw service */
if (ha->status == S_OK) {
- scp->SCp.Message = S_OK;
+ scp->SCp.Message = (int)(ha->info<<16|S_OK);
if (scp->SCp.Status != -1) {
TRACE2(("gdth_sync_event(): special cmd 0x%x OK\n",
scp->SCp.Status));
- scp->SCp.Status = -1;
- scp->SCp.this_residual = HIGH_PRI;
- return 2;
+ /* special commands GDT_CLUST_INFO/GDT_MOUNT ? */
+ if (scp->SCp.Status == GDT_CLUST_INFO) {
+ ha->hdr[scp->target].cluster_type = (unchar)ha->info;
+ if (!(ha->hdr[scp->target].cluster_type &
+ CLUSTER_MOUNTED)) {
+ /* NOT MOUNTED -> MOUNT */
+ if (!(ha->hdr[scp->target].cluster_type &
+ CLUSTER_RESERVED)) {
+ /* cluster drive NOT RESERVED */
+ scp->SCp.Status = GDT_MOUNT;
+ } else {
+ /* cluster drive RESERVED (on the other node) */
+ scp->SCp.Status = GDT_MOUNT;
+ scp->SCp.phase = -2; /* reservation conflict */
+ }
+ } else {
+ scp->SCp.Status = -1;
+ }
+ /* retry */
+ scp->SCp.this_residual = HIGH_PRI;
+ return 2;
+ } else if (scp->SCp.Status == GDT_MOUNT) {
+ ha->hdr[scp->target].cluster_type |= CLUSTER_MOUNTED;
+ scp->SCp.Status = -1;
+ /* return UNIT_ATTENTION */
+ memset((char*)scp->sense_buffer,0,16);
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = UNIT_ATTENTION;
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ } else {
+ scp->SCp.Status = -1;
+ /* retry */
+ scp->SCp.this_residual = HIGH_PRI;
+ return 2;
+ }
+ } else {
+ /* RESERVE/RELEASE ? */
+ if (scp->cmnd[0] == RESERVE) {
+ ha->hdr[scp->target].cluster_type |= CLUSTER_RESERVED;
+ } else if (scp->cmnd[0] == RELEASE) {
+ ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED;
+ }
+ scp->result = DID_OK << 16;
}
- scp->result = DID_OK << 16;
} else if (ha->status == S_BSY) {
TRACE2(("Controller busy -> retry !\n"));
- scp->SCp.Message = S_BSY;
+ scp->SCp.Message = (int)(ha->info<<16|S_BSY);
+ if (scp->SCp.Status == GDT_MOUNT)
+ scp->SCp.Status = GDT_CLUST_INFO;
+ /* retry */
return 2;
} else {
scp->SCp.Message = (int)((ha->info<<16)|ha->status);
+ memset((char*)scp->sense_buffer,0,16);
+ scp->sense_buffer[0] = 0x70;
+ scp->sense_buffer[2] = NOT_READY;
+
if (scp->SCp.Status != -1) {
TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n",
scp->SCp.Status, ha->status));
- scp->SCp.Status = -1;
- scp->SCp.this_residual = HIGH_PRI;
- return 2;
- }
- if (service == CACHESERVICE) {
- memset((char*)scp->sense_buffer,0,16);
- scp->sense_buffer[0] = 0x70;
- scp->sense_buffer[2] = NOT_READY;
+ if (scp->SCp.Status == GDT_SCAN_START ||
+ scp->SCp.Status == GDT_SCAN_END) {
+ scp->SCp.Status = -1;
+ /* retry */
+ scp->SCp.this_residual = HIGH_PRI;
+ return 2;
+ }
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ } else if (scp->cmnd[0] == RESERVE ||
+ scp->cmnd[0] == RELEASE) {
+ scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
+ } else if (service == CACHESERVICE) {
+ if (ha->status == S_CACHE_UNKNOWN &&
+ (ha->hdr[scp->target].cluster_type &
+ CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) {
+ /* bus reset -> force GDT_CLUST_INFO */
+ ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED;
+ }
scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1);
-
#if LINUX_VERSION_CODE >= 0x010300
if (scp->done != gdth_scsi_done)
-#endif
+#endif
{
dvr.size = sizeof(dvr.eu.sync);
dvr.eu.sync.ionode = hanum;
TRACE2(("gdth_eh_bus_reset()\n"));
hanum = NUMDATA(scp->host)->hanum;
ha = HADATA(gdth_ctr_tab[hanum]);
- if (scp->channel == ha->virt_bus)
- return FAILED;
+ /* clear command tab */
GDTH_LOCK_HA(ha, flags);
- for (i = 0; i < MAXID; ++i)
- ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0;
for (i = 0; i < GDTH_MAXCMDS; ++i) {
cmnd = ha->cmd_tab[i].cmnd;
if (!SPECIAL_SCP(cmnd) && cmnd->channel == scp->channel)
ha->cmd_tab[i].cmnd = UNUSED_CMND;
}
- gdth_polling = TRUE;
- while (gdth_test_busy(hanum))
- gdth_delay(0);
- gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS,
- BUS_L2P(ha,scp->channel), 0, 0);
- gdth_polling = FALSE;
GDTH_UNLOCK_HA(ha, flags);
+
+ if (scp->channel == ha->virt_bus) {
+ /* host drives */
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (ha->hdr[i].present &&
+ (ha->hdr[i].cluster_type & CLUSTER_RESERVED)
+ == CLUSTER_RESERVED) {
+ GDTH_LOCK_HA(ha, flags);
+ gdth_polling = TRUE;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ if (gdth_internal_cmd(hanum, CACHESERVICE,
+ GDT_CLUST_RESET, i, 0, 0))
+ ha->hdr[i].cluster_type &= ~CLUSTER_RESERVED;
+ gdth_polling = FALSE;
+ GDTH_UNLOCK_HA(ha, flags);
+ }
+ }
+ } else {
+ /* raw devices */
+ GDTH_LOCK_HA(ha, flags);
+ for (i = 0; i < MAXID; ++i)
+ ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0;
+ gdth_polling = TRUE;
+ while (gdth_test_busy(hanum))
+ gdth_delay(0);
+ gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS,
+ BUS_L2P(ha,scp->channel), 0, 0);
+ gdth_polling = FALSE;
+ GDTH_UNLOCK_HA(ha, flags);
+ }
return SUCCESS;
}
if (disk->device->channel != ha->virt_bus || ha->hdr[t].heads == 0) {
/* raw device or host drive without mapping information */
- TRACE2(("Evaluate mapping\n"));
- gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]);
+ TRACE2(("Evaluate mapping\n"));
+ gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]);
} else {
- ip[0] = ha->hdr[t].heads;
- ip[1] = ha->hdr[t].secs;
- ip[2] = disk->capacity / ip[0] / ip[1];
+ ip[0] = ha->hdr[t].heads;
+ ip[1] = ha->hdr[t].secs;
+ ip[2] = disk->capacity / ip[0] / ip[1];
}
TRACE2(("gdth_bios_param(): %d heads, %d secs, %d cyls\n",
Scsi_Cmnd scp;
Scsi_Device sdev;
gdth_cmd_str gdtcmd;
+ char cmnd[12];
TRACE2(("gdth_flush() hanum %d\n",hanum));
ha = HADATA(gdth_ctr_tab[hanum]);
memset(&sdev,0,sizeof(Scsi_Device));
memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_tab[hanum];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_tab[hanum];
- scp.target = sdev.host->this_id;
+ memset(cmnd, 0xff, 12);
+ sdev.host = scp.host = gdth_ctr_tab[hanum];
+ sdev.id = scp.target = sdev.host->this_id;
scp.device = &sdev;
- scp.use_sg = 0;
for (i = 0; i < MAX_HDRIVES; ++i) {
if (ha->hdr[i].present) {
gdtcmd.u.cache.BlockNo = 1;
gdtcmd.u.cache.sg_canz = 0;
TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i));
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
}
}
}
Scsi_Cmnd scp;
Scsi_Device sdev;
gdth_cmd_str gdtcmd;
+ char cmnd[12];
#endif
#if LINUX_VERSION_CODE >= 0x020100
- TRACE2(("gdth_halt() event %d\n",event));
+ TRACE2(("gdth_halt() event %d\n",(int)event));
if (event != SYS_RESTART && event != SYS_HALT && event != SYS_POWER_OFF)
return NOTIFY_DONE;
#else
/* controller reset */
memset(&sdev,0,sizeof(Scsi_Device));
memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_tab[hanum];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_tab[hanum];
- scp.target = sdev.host->this_id;
+ memset(cmnd, 0xff, 12);
+ sdev.host = scp.host = gdth_ctr_tab[hanum];
+ sdev.id = scp.target = sdev.host->this_id;
scp.device = &sdev;
- scp.use_sg = 0;
gdtcmd.BoardNode = LOCALBOARD;
gdtcmd.Service = CACHESERVICE;
gdtcmd.OpCode = GDT_RESET;
TRACE2(("gdth_halt(): reset controller %d\n", hanum));
- gdth_do_cmd(&scp, &gdtcmd, 10);
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 10);
#endif
}
printk("Done.\n");
*
* <achim@vortex.de>
*
- * $Id: gdth.h,v 1.21 1999/03/26 09:12:24 achim Exp $
+ * $Id: gdth.h,v 1.24 1999/11/02 13:43:49 achim Exp $
*/
#include <linux/version.h>
/* defines, macros */
/* driver version */
-#define GDTH_VERSION_STR "1.14"
+#define GDTH_VERSION_STR "1.17"
#define GDTH_VERSION 1
-#define GDTH_SUBVERSION 14
+#define GDTH_SUBVERSION 17
/* protocol version */
#define PROTOCOL_VERSION 1
#define MAXID 127
#define MAXLUN 8
#define MAXBUS 6
-#define MAX_HDRIVES 35 /* max. host drive count */
+#define MAX_HDRIVES 100 /* max. host drive count */
+#define MAX_LDRIVES 255 /* max. log. drive count */
#define MAX_EVENTS 100 /* event buffer count */
#define MAX_RES_ARGS 40 /* device reservation,
must be a multiple of 4 */
#define IC_QUEUE_BYTES 4
#define DPMEM_COMMAND_OFFSET IC_HEADER_BYTES+IC_QUEUE_BYTES*MAXOFFSETS
+/* cluster_type constants */
+#define CLUSTER_DRIVE 1
+#define CLUSTER_MOUNTED 2
+#define CLUSTER_RESERVED 4
+#define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED)
+
/* cache/raw service commands */
#define GDT_INIT 0 /* service initialization */
#define GDT_READ 1 /* read command */
#define GDT_READ_THR 17 /* read through */
#define GDT_EXT_INFO 18 /* extended info */
#define GDT_RESET 19 /* controller reset */
+#define GDT_RESERVE_DRV 20 /* reserve host drive */
+#define GDT_RELEASE_DRV 21 /* release host drive */
+#define GDT_CLUST_INFO 22 /* cluster info */
+#define GDT_RW_ATTRIBS 23 /* R/W attribs (write thru,..)*/
+#define GDT_CLUST_RESET 24 /* releases the cluster drives*/
/* additional raw service commands */
#define GDT_RESERVE 14 /* reserve dev. to raw serv. */
#define SCSI_DEF_CNT 0x15 /* grown/primary defects */
#define DSK_STATISTICS 0x4b /* SCSI disk statistics */
#define IOCHAN_DESC 0x5d /* description of IO channel */
-#define IOCHAN_RAW_DESC 0x5e /* description of raw IO channel */
+#define IOCHAN_RAW_DESC 0x5e /* description of raw IO chn. */
#define L_CTRL_PATTERN 0x20000000L /* SCSI IOCTL mask */
#define ARRAY_INFO 0x12 /* array drive info */
#define ARRAY_DRV_LIST 0x0f /* array drive list */
+#define ARRAY_DRV_LIST2 0x34 /* array drive list (new) */
#define LA_CTRL_PATTERN 0x10000000L /* array IOCTL mask */
#define CACHE_DRV_CNT 0x01 /* cache drive count */
#define CACHE_DRV_LIST 0x02 /* cache drive list */
/* service errors */
#define S_OK 1 /* no error */
#define S_BSY 7 /* controller busy */
+#define S_CACHE_UNKNOWN 12 /* cache serv.: drive unknown */
#define S_RAW_SCSI 12 /* raw serv.: target error */
#define S_RAW_ILL 0xff /* raw serv.: illegal */
unchar revision[4]; /* revision */
ulong32 sy_rate; /* current rate for sync. tr. */
ulong32 sy_max_rate; /* max. rate for sync. tr. */
- ulong32 no_ldrive; /* belongs to this logical drv.*/
+ ulong32 no_ldrive; /* belongs to this log. drv.*/
ulong32 blkcnt; /* number of blocks */
ushort blksize; /* size of block in bytes */
unchar available; /* flag: access is available */
unchar is_parity; /* Flag: is parity drive? */
unchar is_hotfix; /* Flag: is hotfix drive? */
unchar res[3];
-} PACKED gdth_arraylist_str;
+} PACKED gdth_alist_str;
+
+typedef struct {
+ ulong32 entries_avail; /* allocated entries */
+ ulong32 entries_init; /* returned entries */
+ ulong32 first_entry; /* first entry number */
+ ulong32 list_offset; /* offset of following list */
+ gdth_alist_str list[1]; /* list */
+} PACKED gdth_arcdl_str;
/* cache info/config IOCTL */
typedef struct {
ulong32 size; /* capacity */
unchar ldr_no; /* log. drive no. */
unchar rw_attribs; /* r/w attributes */
+ unchar cluster_type; /* cluster properties */
+ unchar reserved;
ulong32 start_sec; /* start sector */
- } hdr[MAX_HDRIVES]; /* host drives */
+ } hdr[MAX_LDRIVES]; /* host drives */
struct {
unchar lock; /* channel locked? (hot plug) */
unchar pdev_cnt; /* physical device count */
unchar local_no; /* local channel number */
- unchar io_cnt[MAXID]; /* current IO count */
+ unchar io_cnt[MAXID]; /* current IO count */
ulong32 address; /* channel address */
ulong32 id_list[MAXID]; /* IDs of the phys. devices */
} raw[MAXBUS]; /* SCSI channels */
#define _GDTH_IOCTL_H
/* gdth_ioctl.h
- * $Id: gdth_ioctl.h,v 1.2 1998/12/17 15:42:49 achim Exp $
+ * $Id: gdth_ioctl.h,v 1.3 1999/05/26 11:49:57 achim Exp $
*/
/* IOCTLs */
#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */
#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */
#define GDTIOCTL_OSVERS (GDTIOCTL_MASK | 3) /* get OS version */
+#define GDTIOCTL_HDRLIST (GDTIOCTL_MASK | 4) /* get host drive list */
#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */
#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */
#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */
#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */
+#define GDTIOCTL_SCSI (GDTIOCTL_MASK | 9) /* SCSI command */
+#define GDTIOCTL_RESET_BUS (GDTIOCTL_MASK |10) /* reset SCSI bus */
#define GDTIOCTL_MAGIC 0xaffe0001UL
int handle;
unchar evt[34]; /* event structure */
} event;
+ struct {
+ unchar bus; /* SCSI bus */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar cmd_len; /* command length */
+ unchar cmd[12]; /* SCSI command */
+ } scsi;
} iu;
} gdth_iowr_str;
int handle;
unchar evt[34]; /* event structure */
} event;
+ struct {
+ unchar bus; /* SCSI bus, 0xff: invalid */
+ unchar target; /* target ID */
+ unchar lun; /* LUN */
+ unchar cluster_type; /* cluster properties */
+ } hdr_list[35]; /* index is host drive number */
} iu;
} gdth_iord_str;
/* gdth_proc.c
- * $Id: gdth_proc.c,v 1.13 1999/03/22 16:12:53 achim Exp $
+ * $Id: gdth_proc.c,v 1.16 1999/11/02 13:44:11 achim Exp $
*/
#include "gdth_ioctl.h"
-#include <linux/version.h>
int gdth_proc_info(char *buffer,char **start,off_t offset,int length,
int hostno,int inout)
memset(&sdev,0,sizeof(Scsi_Device));
memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_vtab[vh];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_vtab[vh];
- scp.target = sdev.host->this_id;
+ sdev.host = scp.host = gdth_ctr_vtab[vh];
+ sdev.id = scp.target = sdev.host->this_id;
scp.device = &sdev;
- scp.use_sg = 0;
if (length >= 4) {
if (strncmp(buffer,"gdth",4) == 0) {
gdth_ha_str *ha;
gdth_cmd_str gdtcmd;
gdth_cpar_str *pcpar;
+ char cmnd[12];
TRACE2(("gdth_set_asc_info() ha %d\n",hanum));
ha = HADATA(gdth_ctr_tab[hanum]);
drive = -1;
wb_mode = 0;
found = FALSE;
+ memset(cmnd, 0xff, 12);
if (length >= 5 && strncmp(buffer,"flush",5)==0) {
buffer += 6;
gdtcmd.u.cache.DeviceNo = i;
gdtcmd.u.cache.BlockNo = 1;
gdtcmd.u.cache.sg_canz = 0;
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
}
}
if (!found)
gdtcmd.u.ioctl.subfunc = CACHE_CONFIG;
gdtcmd.u.ioctl.channel = INVALID_CHANNEL;
pcpar->write_back = wb_mode==1 ? 0:1;
- gdth_do_cmd(&scp, &gdtcmd, 30);
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
gdth_ioctl_free(hanum);
printk("Done.\n");
return(orig_length);
ulong32 *ppadd, add_size;
ulong32 *ppadd2, add_size2;
ulong flags;
+ char cmnd[12];
+ gdth_cmd_str gdtcmd;
TRACE2(("gdth_set_bin_info() ha %d\n",hanum));
ha = HADATA(gdth_ctr_tab[hanum]);
pcmd = NULL;
ppadd = ppadd2 = NULL;
add_size = add_size2 = 0;
+ memset(cmnd, 0xff, 12);
if (length < GDTOFFSOF(gdth_iowr_str,iu))
return(-EINVAL);
*ppadd2 = virt_to_bus(piord->iu.general.data+add_size);
}
/* do IOCTL */
- gdth_do_cmd(&scp, pcmd, piowr->timeout);
+ gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout);
piord->status = (ulong32)scp.SCp.Message;
break;
pevt->event_data.size = sizeof(pevt->event_data.eu.async);
gdth_log_event(&pevt->event_data, NULL);
}
- GDTH_LOCK_HA(ha, flags);
+ GDTH_LOCK_HA(ha, flags);
gdth_store_event(ha, pevt->event_source, pevt->event_idx,
&pevt->event_data);
- GDTH_UNLOCK_HA(ha, flags);
+ GDTH_UNLOCK_HA(ha, flags);
} else if (piowr->iu.event.erase == 0xfe) {
gdth_clear_events();
} else if (piowr->iu.event.erase == 0) {
piord->status = S_OK;
break;
+ case GDTIOCTL_SCSI:
+ if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ))
+ return(-EBUSY);
+ piord = (gdth_iord_str *)ha->pscratch;
+ piord->size = sizeof(gdth_iord_str);
+ scp.target = scp.device->id = piowr->iu.scsi.target;
+ scp.channel = scp.device->channel = piowr->iu.scsi.bus;
+ memcpy(cmnd, piowr->iu.scsi.cmd, 12);
+ scp.cmd_len = piowr->iu.scsi.cmd_len;
+ gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout);
+ piord->status = (ulong32)scp.result;
+ break;
+
+ case GDTIOCTL_RESET_BUS:
+ if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ))
+ return(-EBUSY);
+ piord = (gdth_iord_str *)ha->pscratch;
+ piord->size = sizeof(gdth_iord_str);
+ scp.channel = scp.device->channel = piowr->iu.scsi.bus;
+ piord->status = (ulong32)gdth_eh_bus_reset( &scp );
+ break;
+
+ case GDTIOCTL_HDRLIST:
+ if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) ))
+ return(-EBUSY);
+ piord = (gdth_iord_str *)ha->pscratch;
+ piord->size = sizeof(gdth_iord_str);
+ piord->status = S_OK;
+ for (i = 0; i < MAX_HDRIVES; ++i) {
+ if (ha->hdr[i].present) {
+ piord->iu.hdr_list[i].bus = ha->virt_bus;
+ piord->iu.hdr_list[i].target = i;
+ piord->iu.hdr_list[i].lun = 0;
+ piord->iu.hdr_list[i].cluster_type = ha->hdr[i].cluster_type;
+ if (ha->hdr[i].cluster_type & CLUSTER_DRIVE) {
+ gdtcmd.BoardNode = LOCALBOARD;
+ gdtcmd.Service = CACHESERVICE;
+ gdtcmd.OpCode = GDT_CLUST_INFO;
+ gdtcmd.u.cache.DeviceNo = i;
+ gdtcmd.u.cache.BlockNo = 0;
+ gdtcmd.u.cache.sg_canz = 0;
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) == S_OK)
+ piord->iu.hdr_list[i].cluster_type =
+ (unchar)(scp.SCp.Message>>16);
+ }
+ } else {
+ piord->iu.hdr_list[i].bus = 0xff;
+ }
+ }
+ break;
+
default:
return(-EINVAL);
}
gdth_evt_str estr;
Scsi_Cmnd scp;
Scsi_Device sdev;
+ char cmnd[12];
char hrec[161];
struct timeval tv;
memset(&sdev,0,sizeof(Scsi_Device));
memset(&scp, 0,sizeof(Scsi_Cmnd));
- sdev.host = gdth_ctr_vtab[vh];
- sdev.id = sdev.host->this_id;
- scp.cmd_len = 12;
- scp.host = gdth_ctr_vtab[vh];
- scp.target = sdev.host->this_id;
+ memset(cmnd, 0xff, 12);
+ sdev.host = scp.host = gdth_ctr_vtab[vh];
+ sdev.id = scp.target = sdev.host->this_id;
scp.device = &sdev;
- scp.use_sg = 0;
/* look for buffer ID in length */
if (id > 1) {
/* 2. about physical devices */
size = sprintf(buffer+len,"\nPhysical Devices:");
len += size; pos = begin + len;
- flag = FALSE;
+ flag = FALSE;
if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH))
goto stop_output;
sizeof(pds->list[0]);
if (pds->entries > cnt)
pds->entries = cnt;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK)
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) != S_OK)
pds->count = 0;
TRACE2(("pdr_statistics() entries %d status %d\n",
pds->count, scp.SCp.Message));
gdtcmd.u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN;
gdtcmd.u.ioctl.channel =
ha->raw[i].address | ha->raw[i].id_list[j];
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) == S_OK) {
strncpy(hrec,pdi->vendor,8);
strncpy(hrec+8,pdi->product,16);
strncpy(hrec+24,pdi->revision,4);
"\n Chn/ID/LUN: \t%c/%02d/%d \tName: \t%s\n",
'A'+i,pdi->target_id,pdi->lun,hrec);
len += size; pos = begin + len;
- flag = TRUE;
+ flag = TRUE;
pdi->no_ldrive &= 0xffff;
if (pdi->no_ldrive == 0xffff)
strcpy(hrec,"--");
gdtcmd.u.ioctl.channel =
ha->raw[i].address | ha->raw[i].id_list[j];
pdef->sddc_type = 0x08;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) == S_OK) {
size = sprintf(buffer+len,
" Grown Defects:\t%d\n",
pdef->sddc_cnt);
}
gdth_ioctl_free(hanum);
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
if (pos < offset) {
len = 0;
begin = pos;
/* 3. about logical drives */
size = sprintf(buffer+len,"\nLogical Drives:");
len += size; pos = begin + len;
- flag = FALSE;
+ flag = FALSE;
if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH))
goto stop_output;
- for (i = 0; i < MAX_HDRIVES; ++i) {
+ for (i = 0; i < MAX_LDRIVES; ++i) {
if (!ha->hdr[i].is_logdrv)
continue;
drv_no = i;
gdtcmd.u.ioctl.param_size = sizeof(gdth_cdrinfo_str);
gdtcmd.u.ioctl.subfunc = CACHE_DRV_INFO;
gdtcmd.u.ioctl.channel = drv_no;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK)
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) != S_OK)
break;
pcdi->ld_dtype >>= 16;
j++;
"\n Number: \t%-2d \tStatus: \t%s\n",
drv_no, hrec);
len += size; pos = begin + len;
- flag = TRUE;
+ flag = TRUE;
no_mdrv = pcdi->cd_ldcnt;
if (no_mdrv > 1 || pcdi->ld_slave != -1) {
is_mirr = TRUE;
}
gdth_ioctl_free(hanum);
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
if (pos < offset) {
len = 0;
begin = pos;
/* 4. about array drives */
size = sprintf(buffer+len,"\nArray Drives:");
len += size; pos = begin + len;
- flag = FALSE;
+ flag = FALSE;
if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH))
goto stop_output;
- for (i = 0; i < MAX_HDRIVES; ++i) {
+ for (i = 0; i < MAX_LDRIVES; ++i) {
if (!(ha->hdr[i].is_arraydrv && ha->hdr[i].is_master))
continue;
/* 4.a array drive info */
gdtcmd.u.ioctl.param_size = sizeof(gdth_arrayinf_str);
gdtcmd.u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN;
gdtcmd.u.ioctl.channel = i;
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message == S_OK) {
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) == S_OK) {
if (pai->ai_state == 0)
strcpy(hrec, "idle");
else if (pai->ai_state == 2)
}
gdth_ioctl_free(hanum);
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
if (pos < offset) {
len = 0;
begin = pos;
/* 5. about host drives */
size = sprintf(buffer+len,"\nHost Drives:");
len += size; pos = begin + len;
- flag = FALSE;
+ flag = FALSE;
if (!gdth_ioctl_alloc(hanum, GDTH_SCRATCH))
goto stop_output;
- for (i = 0; i < MAX_HDRIVES; ++i) {
+ for (i = 0; i < MAX_LDRIVES; ++i) {
if (!ha->hdr[i].is_logdrv ||
(ha->hdr[i].is_arraydrv && !ha->hdr[i].is_master))
continue;
gdtcmd.u.ioctl.channel = i;
phg->entries = MAX_HDRIVES;
phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]);
- gdth_do_cmd(&scp, &gdtcmd, 30);
- if (scp.SCp.Message != S_OK) {
+ gdth_do_cmd(&scp, &gdtcmd, cmnd, 30);
+ if ((scp.SCp.Message & 0xffff) != S_OK) {
ha->hdr[i].ldr_no = i;
ha->hdr[i].rw_attribs = 0;
ha->hdr[i].start_sec = 0;
} else {
for (j = 0; j < phg->entries; ++j) {
k = phg->entry[j].host_drive;
- if (k >= MAX_HDRIVES)
+ if (k >= MAX_LDRIVES)
continue;
ha->hdr[k].ldr_no = phg->entry[j].log_drive;
ha->hdr[k].rw_attribs = phg->entry[j].rw_attribs;
"\n Number: \t%-2d \tArr/Log. Drive:\t%d\n",
i, ha->hdr[i].ldr_no);
len += size; pos = begin + len;
- flag = TRUE;
+ flag = TRUE;
size = sprintf(buffer+len,
" Capacity [MB]:\t%-6d \tStart Sector: \t%d\n",
len += size; pos = begin + len;
}
- if (!flag) {
- size = sprintf(buffer+len, "\n --\n");
- len += size; pos = begin + len;
- }
+ if (!flag) {
+ size = sprintf(buffer+len, "\n --\n");
+ len += size; pos = begin + len;
+ }
if (pos < offset) {
len = 0;
begin = pos;
gdth_log_event(&estr.event_data, hrec);
do_gettimeofday(&tv);
sec = (int)(tv.tv_sec - estr.first_stamp);
- if (sec < 0) sec = 0;
+ if (sec < 0) sec = 0;
size = sprintf(buffer+len," date- %02d:%02d:%02d\t%s\n",
sec/3600, sec%3600/60, sec%60, hrec);
len += size; pos = begin + len;
return(len);
}
-static void gdth_do_cmd(Scsi_Cmnd *scp,gdth_cmd_str *gdtcmd,int timeout)
+static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd,
+ char *cmnd, int timeout)
{
- char cmnd[12];
struct semaphore sem = MUTEX_LOCKED;
+ unsigned bufflen;
TRACE2(("gdth_do_cmd()\n"));
- memset(cmnd, 0, 12);
+ if (gdtcmd != NULL) {
+ scp->SCp.this_residual = IOCTL_PRI;
+ bufflen = sizeof(gdth_cmd_str);
+ } else {
+ scp->SCp.this_residual = DEFAULT_PRI;
+ bufflen = 0;
+ }
scp->request.rq_status = RQ_SCSI_BUSY;
scp->request.sem = &sem;
- scp->SCp.this_residual = IOCTL_PRI;
GDTH_LOCK_SCSI_DOCMD();
- scsi_do_cmd(scp, cmnd, gdtcmd, sizeof(gdth_cmd_str),
- gdth_scsi_done, timeout*HZ, 1);
+ scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1);
GDTH_UNLOCK_SCSI_DOCMD();
down(&sem);
}
#define _GDTH_PROC_H
/* gdth_proc.h
- * $Id: gdth_proc.h,v 1.6 1999/03/05 14:32:36 achim Exp $
+ * $Id: gdth_proc.h,v 1.7 1999/05/26 11:49:32 achim Exp $
*/
static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum);
static void gdth_start_timeout(int hanum, int busnum, int id);
static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout);
-static void gdth_do_cmd(Scsi_Cmnd *scp,gdth_cmd_str *cmd,int timeout);
+static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd,
+ char *cmnd, int timeout);
void gdth_scsi_done(Scsi_Cmnd *scp);
#endif
hostdata->control_block.firm_opts = 0x0108;
hostdata->control_block.max_frame_len = 2048;
hostdata->control_block.max_iocb = 256;
- hostdata->control_block.exec_throttle = 8;
+ hostdata->control_block.exec_throttle = QLOGICFC_CMD_PER_LUN;
hostdata->control_block.retry_delay = 5;
hostdata->control_block.retry_cnt = 1;
hostdata->control_block.node_name[0] = 0x0020;
/* scsi.c expects sense info in a different buffer */
cmd->dataseg[0].d_base = virt_to_bus_low32(Cmnd->sense_buffer);
#if BITS_PER_LONG > 32
- cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->request_buffer);
+ cmd->dataseg[0].d_base_hi = virt_to_bus_high32(Cmnd->sense_buffer);
#endif
+ cmd->dataseg[0].d_count = sizeof(Cmnd->sense_buffer);
cmd->segment_cnt = 1;
cmd->control_flags = CFLAG_READ;
break;
/* And for stereo. */
#define ST 1
+/* Whether or not the bits in the channel are inverted. */
+#define INV 1
+#define NINV 0
+
static struct ac97_chn_desc {
int ac97_regnum;
int oss_channel;
int oss_mask;
int recordNum;
u16 regmask;
+ int is_inverted;
} mixerRegs[] = {
- { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000 },
- { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000 },
- { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff },
- { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00 },
- { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e },
- { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000 },
- { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000 },
- { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000 },
- { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000 },
- { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000 },
- { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000 },
- { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000 },
- { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000 },
- { -1, -1, 0xff, 0, 0, -1, 0x0000 },
+ { AC97_MASTER_VOL_STEREO, SOUND_MIXER_VOLUME, 0x3f, ST, SOUND_MASK_VOLUME, 5, 0x0000, INV },
+ { AC97_MASTER_VOL_MONO, SOUND_MIXER_PHONEOUT, 0x3f, MO, SOUND_MASK_PHONEOUT, 6, 0x0000, INV },
+ { AC97_MASTER_TONE, SOUND_MIXER_TREBLE, 0x0f, MO, SOUND_MASK_TREBLE, -1, 0x00ff, INV },
+ { AC97_MASTER_TONE, SOUND_MIXER_BASS, 0x0f, MO, SOUND_MASK_BASS, -1, 0xff00, INV },
+ { AC97_PCBEEP_VOL, SOUND_MIXER_SPEAKER, 0x0f, MO, SOUND_MASK_SPEAKER, -1, 0x001e, INV },
+ { AC97_PHONE_VOL, SOUND_MIXER_PHONEIN, 0x1f, MO, SOUND_MASK_PHONEIN, 7, 0x0000, INV },
+ { AC97_MIC_VOL, SOUND_MIXER_MIC, 0x1f, MO, SOUND_MASK_MIC, 0, 0x0000, INV },
+ { AC97_LINEIN_VOL, SOUND_MIXER_LINE, 0x1f, ST, SOUND_MASK_LINE, 4, 0x0000, INV },
+ { AC97_CD_VOL, SOUND_MIXER_CD, 0x1f, ST, SOUND_MASK_CD, 1, 0x0000, INV },
+ { AC97_VIDEO_VOL, SOUND_MIXER_VIDEO, 0x1f, ST, SOUND_MASK_VIDEO, 2, 0x0000, INV },
+ { AC97_AUX_VOL, SOUND_MIXER_LINE1, 0x1f, ST, SOUND_MASK_LINE1, 3, 0x0000, INV },
+ { AC97_PCMOUT_VOL, SOUND_MIXER_PCM, 0x1f, ST, SOUND_MASK_PCM, -1, 0x0000, INV },
+ { AC97_RECORD_GAIN, SOUND_MIXER_IGAIN, 0x0f, ST, SOUND_MASK_IGAIN, -1, 0x0000, NINV },
+ { -1, -1, 0xff, 0, 0, -1, 0x0000, 0 },
};
static struct ac97_chn_desc *
return 0;
}
+/* Reset the mixer to the currently saved settings. */
+int
+ac97_reset (struct ac97_hwint *dev)
+{
+ int x;
+
+ if (dev->reset_device (dev))
+ return -1;
+
+ /* Now set the registers back to their last-written values. */
+ for (x = 0; mixerRegs[x].ac97_regnum != -1; x++) {
+ int regnum = mixerRegs[x].ac97_regnum;
+ int value = dev->last_written_mixer_values [regnum / 2];
+ if (value >= 0)
+ ac97_put_register (dev, regnum, value);
+ }
+ return 0;
+}
+
/* Return the contents of register REG; use the cache if the value in it
is valid. Returns a negative error code on failure. */
int
scaled value on success. */
static int
-ac97_scale_to_oss_val (int value, int maxval, int is_stereo)
+ac97_scale_to_oss_val (int value, int maxval, int is_stereo, int inv)
{
/* Muted? */
if (value & AC97_MUTE)
return 0;
if (is_stereo)
- return (ac97_scale_to_oss_val (value & 255, maxval, 0) << 8)
- | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0) << 0);
+ return (ac97_scale_to_oss_val (value & 255, maxval, 0, inv) << 8)
+ | (ac97_scale_to_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
else {
int i;
/* Inverted. */
- value = maxval - value;
+ if (inv)
+ value = maxval - value;
i = (value * 100 + (maxval / 2)) / maxval;
if (i > 100)
i = 100;
+ if (i < 0)
+ i = 0;
return i;
}
}
static int
-ac97_scale_from_oss_val (int value, int maxval, int is_stereo)
+ac97_scale_from_oss_val (int value, int maxval, int is_stereo, int inv)
{
if (is_stereo)
- return (ac97_scale_from_oss_val (value & 255, maxval, 0) << 8)
- | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0) << 0);
+ return (ac97_scale_from_oss_val (value & 255, maxval, 0, inv) << 8)
+ | (ac97_scale_from_oss_val ((value >> 8) & 255, maxval, 0, inv) << 0);
else {
- int i = maxval - ((value & 255) * maxval + 50) / 100;
+ int i = ((value & 255) * maxval + 50) / 100;
+ if (inv)
+ i = maxval - i;
if (i < 0)
i = 0;
+ if (i > maxval)
+ i = maxval;
return i;
}
}
if (! ac97_is_valid_channel (dev, channel))
return -ENODEV;
scaled_value = ac97_scale_from_oss_val (oss_value, channel->maxval,
- channel->is_stereo);
+ channel->is_stereo,
+ channel->is_inverted);
if (scaled_value < 0)
return scaled_value;
regval >>= 1;
}
return ac97_scale_to_oss_val (regval, channel->maxval,
- channel->is_stereo);
+ channel->is_stereo,
+ channel->is_inverted);
}
int
else
ret = -EFAULT;
}
- if (ret >= 0) {
- if (dev->last_written_OSS_values[channel] == AC97_REGVAL_UNKNOWN)
+ if (ret >= 0 && (dir & _IOC_READ)) {
+ if (dev->last_written_OSS_values[channel]
+ == AC97_REGVAL_UNKNOWN)
dev->last_written_OSS_values[channel]
= ac97_get_mixer_scaled (dev, channel);
ret = dev->last_written_OSS_values[channel];
#include "sound_config.h"
#include "sound_calls.h"
-#define AC97_RESET 0x0000 // */
+#define AC97_RESET 0x0000 //
#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out
#define AC97_HEADPHONE_VOL 0x0004 //
#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output
#define AC97_RESERVED_3A 0x003A /* Reserved */
/* range 0x3c-0x58 - MODEM */
+/* AC'97 2.0 */
+#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */
+#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */
+#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */
+#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */
+#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */
+#define AC97_PCM_LR_DAC_RATE 0x0032 /* PCM LR DAC Rate */
+#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */
+#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */
+#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */
+#define AC97_RESERVED_3A 0x003A /* Reserved */
+/* range 0x3c-0x58 - MODEM */
+
/* registers 0x005a - 0x007a are vendor reserved */
#define AC97_VENDOR_ID1 0x007c
/* Default ioctl. */
extern int ac97_mixer_ioctl (struct ac97_hwint *dev, unsigned int cmd,
caddr_t arg);
+
+/* Do a complete reset on the AC97 mixer, restoring all mixer registers to
+ the current values. Normally used after an APM resume event. */
+extern int ac97_reset (struct ac97_hwint *dev);
#endif
\f
/*
#include "ac97.h"
+/* The revisions that we currently handle. */
enum nm256rev {
REV_NM256AV, REV_NM256ZX
};
+/* Per-card structure. */
struct nm256_info
{
/* Magic number used to verify that this struct is valid. */
/* The mixer device. */
int mixer_oss_dev;
- /* Can only be opened once for each operation. These aren't set
- until an actual I/O operation is performed; this allows one
- device to be open for read/write without inhibiting I/O to
- the other device. */
+ /*
+ * Can only be opened once for each operation. These aren't set
+ * until an actual I/O operation is performed; this allows one
+ * device to be open for read/write without inhibiting I/O to
+ * the other device.
+ */
int is_open_play;
int is_open_record;
/* Ditto for recording a sample. */
int recording;
- /* The two memory ports. */
- char *ports[2];
-
- /* Starting offset of the port1 area mapped into memory. */
- u32 port1_start;
- /* Ending offset. */
- u32 port1_end;
- /* The offset of the end of the actual buffer area. */
- u32 bufend;
+ /* The two memory ports. */
+ struct nm256_ports {
+ /* Physical address of the port. */
+ u32 physaddr;
+ /* Our mapped-in pointer. */
+ char *ptr;
+ /* PTR's offset within the physical port. */
+ u32 start_offset;
+ /* And the offset of the end of the buffer. */
+ u32 end_offset;
+ } port[2];
/* The following are offsets within memory port 1. */
u32 coeffBuf;
u32 allCoeffBuf;
+
/* Record and playback buffers. */
u32 abuf1, abuf2;
/* Offset of the AC97 mixer in memory port 2. */
u32 mixer;
+ /* Offset of the mixer status register in memory port 2. */
+ u32 mixer_status_offset;
+
+ /* Non-zero if we have written initial values to the mixer. */
+ u8 mixer_values_init;
+
+ u16 playRingsize;
+
+ /*
+ * Status mask bit; (*mixer_status_loc & mixer_status_mask) == 0 means
+ * it's ready.
+ */
+ u16 mixer_status_mask;
+
/* The sizes of the playback and record ring buffers. */
u32 playbackBufferSize;
u32 recordBufferSize;
/* The start of the block currently playing. */
u32 curPlayPos;
- /* The amount of data we requested to record. */
+ /* The amount of data we were requested to record. */
u32 requestedRecAmt;
/* The offset of the currently-recording block. */
u32 curRecPos;
/* Debug flag--bigger numbers mean more output. */
extern int nm256_debug;
-/* Size of the second memory port. */
+/* The BIOS signature. */
+#define NM_SIGNATURE 0x4e4d0000
+/* Signature mask. */
+#define NM_SIG_MASK 0xffff0000
+
+/* Size of the second memory area. */
#define NM_PORT2_SIZE 4096
-/* The location of the mixer. */
-#define NM_MIXER_BASE 0x600
+
+/* The base offset of the mixer in the second memory area. */
+#define NM_MIXER_OFFSET 0x600
+
/* The maximum size of a coefficient entry. */
#define NM_MAX_COEFFICIENT 0x5000
#define NM_MISC_INT_2 0x1
#define NM_ACK_INT(CARD, X) nm256_writePort16((CARD), 2, NM_INT_REG, (X) << 1)
-/* For the second revision. It uses the same interrupt register, but it
- holds 32 bits instead of 16. */
+/* The AV's "mixer ready" status bit and location. */
+#define NM_MIXER_STATUS_OFFSET 0xa04
+#define NM_MIXER_READY_MASK 0x0800
+#define NM_MIXER_PRESENCE 0xa06
+#define NM_PRESENCE_MASK 0x0050
+#define NM_PRESENCE_VALUE 0x0040
+
+/*
+ * For the ZX. It uses the same interrupt register, but it holds 32
+ * bits instead of 16.
+ */
#define NM2_PLAYBACK_INT 0x10000
#define NM2_RECORD_INT 0x80000
#define NM2_MISC_INT_1 0x8
#define NM2_MISC_INT_2 0x2
#define NM2_ACK_INT(CARD, X) nm256_writePort32((CARD), 2, NM_INT_REG, (X))
+/* The ZX's "mixer ready" status bit and location. */
+#define NM2_MIXER_STATUS_OFFSET 0xa06
+#define NM2_MIXER_READY_MASK 0x0800
+
/* The playback registers start from here. */
#define NM_PLAYBACK_REG_OFFSET 0x0
/* The record registers start from here. */
#define NM_RECORD_REG_OFFSET 0x200
-/* The rate register is located 2 bytes from the start of the register
- area. */
+/* The rate register is located 2 bytes from the start of the register area. */
#define NM_RATE_REG_OFFSET 2
/* Mono/stereo flag, number of bits on playback, and rate mask. */
#define NM_AUDIO_MUTE_LEFT 0x8000
#define NM_AUDIO_MUTE_RIGHT 0x0080
-/* Recording enable register */
+/* Recording enable register. */
#define NM_RECORD_ENABLE_REG (NM_RECORD_REG_OFFSET + 0)
#define NM_RECORD_ENABLE_FLAG 1
#define NM_RECORD_FREERUN 2
if (port < 1 || port > 2 || card == NULL) \
return -1; \
\
- if (port == 1) { \
- if (offset < card->port1_start || offset >= card->port1_end) { \
- printk (KERN_ERR "Bad port request port 1:0x%x\n", offset); \
- return -1; \
- } \
- offset -= card->port1_start; \
- } else if (offset < 0 || offset > 4096) { \
- printk (KERN_ERR "Bad port request port 2: 0x%x\n", offset); \
- return -1; \
- }
+ if (offset < card->port[port - 1].start_offset \
+ || offset >= card->port[port - 1].end_offset) { \
+ printk (KERN_ERR "Bad access: port %d, offset 0x%x\n", port, offset); \
+ return -1; \
+ } \
+ offset -= card->port[port - 1].start_offset;
#define DEFwritePortX(X, func) \
static inline int nm256_writePort##X (struct nm256_info *card,\
- int port, int offset, int value)\
+ int port, int offset, int value)\
{\
u##X *addr;\
+\
+ if (nm256_debug > 1)\
+ printk (KERN_DEBUG "Writing 0x%x to %d:0x%x\n", value, port, offset);\
\
NM_FIX_PORT;\
\
- addr = (u##X *)(card->ports[port - 1] + offset);\
+ addr = (u##X *)(card->port[port - 1].ptr + offset);\
func (value, addr);\
return 0;\
}
DEFwritePortX (16, writew)
DEFwritePortX (32, writel)
-#define DEFreadPortX(X) \
+#define DEFreadPortX(X, func) \
static inline u##X nm256_readPort##X (struct nm256_info *card,\
int port, int offset)\
{\
- u##X *addr, res;\
+ u##X *addr;\
\
NM_FIX_PORT\
\
- addr = (u##X *)(card->ports[port - 1] + offset);\
- memcpy_fromio (&res, addr, sizeof (res));\
- return res;\
+ addr = (u##X *)(card->port[port - 1].ptr + offset);\
+ return func(addr);\
}
-DEFreadPortX (8)
-DEFreadPortX (16)
-DEFreadPortX (32)
+DEFreadPortX (8, readb)
+DEFreadPortX (16, readw)
+DEFreadPortX (32, readl)
static inline int
nm256_writeBuffer8 (struct nm256_info *card, u8 *src, int port, int offset,
int amt)
{
NM_FIX_PORT;
- memcpy_toio (card->ports[port - 1] + offset, src, amt);
+ memcpy_toio (card->port[port - 1].ptr + offset, src, amt);
return 0;
}
int amt)
{
NM_FIX_PORT;
- memcpy_fromio (dst, card->ports[port - 1] + offset, amt);
+ memcpy_fromio (dst, card->port[port - 1].ptr + offset, amt);
return 0;
}
-/* Audio driver for the NeoMagic 256AV and 256ZX chipsets in native
- mode, with AC97 mixer support.
-
- Overall design and parts of this code stolen from vidc_*.c and
- skeleton.c.
-
- Yeah, there are a lot of magic constants in here. You tell ME what
- they are. I just get this stuff psychically, remember?
-
- This driver was written by someone who wishes to remain anonymous.
- It is in the public domain, so share and enjoy. Try to make a profit
- off of it; go on, I dare you. */
-
+/*
+ * Audio driver for the NeoMagic 256AV and 256ZX chipsets in native
+ * mode, with AC97 mixer support.
+ *
+ * Overall design and parts of this code stolen from vidc_*.c and
+ * skeleton.c.
+ *
+ * Yeah, there are a lot of magic constants in here. You tell ME what
+ * they are. I just get this stuff psychically, remember?
+ *
+ * This driver was written by someone who wishes to remain anonymous.
+ * It is in the public domain, so share and enjoy. Try to make a profit
+ * off of it; go on, I dare you.
+ */
+
#include <linux/config.h>
#include <linux/pci.h>
#include <linux/module.h>
#include <linux/delay.h>
+#include <linux/apm_bios.h>
#include "sound_config.h"
#include "soundmodule.h"
#include "nm256_coeff.h"
int nm256_debug = 0;
+static int force_load = 0;
-/* The size of the playback reserve. */
+/*
+ * The size of the playback reserve. When the playback buffer has less
+ * than NM256_PLAY_WMARK_SIZE bytes to output, we request a new
+ * buffer.
+ */
#define NM256_PLAY_WMARK_SIZE 512
static struct audio_driver nm256_audio_driver;
static int nm256_grabInterrupt (struct nm256_info *card);
static int nm256_releaseInterrupt (struct nm256_info *card);
static void nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy);
-static void nm256_interrupt_zx (int irq, void *dev_id,
- struct pt_regs *dummy);
+static void nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy);
/* These belong in linux/pci.h. */
#define PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO 0x8005
#define PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO 0x8006
+/* eeeew. */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+#define RSRCADDRESS(dev,num) ((dev)->resource[(num)].start)
+#else
+#define RSRCADDRESS(dev,num) ((dev)->base_address[(num)] \
+ & PCI_BASE_ADDRESS_MEM_MASK)
+
+#endif
+
/* List of cards. */
static struct nm256_info *nmcard_list;
+/* Release the mapped-in memory for CARD. */
+static void
+nm256_release_ports (struct nm256_info *card)
+{
+ int x;
+
+ for (x = 0; x < 2; x++) {
+ if (card->port[x].ptr != NULL) {
+ u32 size =
+ card->port[x].end_offset - card->port[x].start_offset;
+ release_region ((unsigned long) card->port[x].ptr, size);
+ card->port[x].ptr = NULL;
+ }
+ }
+}
+
+/*
+ * Map in the memory ports for CARD, if they aren't already mapped in
+ * and have been configured. If successful, a zero value is returned;
+ * otherwise any previously mapped-in areas are released and a non-zero
+ * value is returned.
+ *
+ * This is invoked twice, once for each port. Ideally it would only be
+ * called once, but we now need to map in the second port in order to
+ * check how much memory the card has on the 256ZX.
+ */
+static int
+nm256_remap_ports (struct nm256_info *card)
+{
+ int x;
+
+ for (x = 0; x < 2; x++) {
+ if (card->port[x].ptr == NULL && card->port[x].end_offset > 0) {
+ u32 physaddr
+ = card->port[x].physaddr + card->port[x].start_offset;
+ u32 size
+ = card->port[x].end_offset - card->port[x].start_offset;
+
+ card->port[x].ptr = ioremap_nocache (physaddr, size);
+
+ if (card->port[x].ptr == NULL) {
+ printk (KERN_ERR "NM256: Unable to remap port %d\n", x + 1);
+ nm256_release_ports (card);
+ return -1;
+ }
+ }
+ }
+ return 0;
+}
+
/* Locate the card in our list. */
static struct nm256_info *
nm256_find_card (int dev)
return NULL;
}
-/* Ditto, but find the card struct corresponding to the mixer device DEV
- instead. */
+/*
+ * Ditto, but find the card struct corresponding to the mixer device DEV
+ * instead.
+ */
static struct nm256_info *
nm256_find_card_for_mixer (int dev)
{
8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999
};
-/* Set the card samplerate, word size and stereo mode to correspond to
- the settings in the CARD struct for the specified device in DEV.
- We keep two separate sets of information, one for each device; the
- hardware is not actually configured until a read or write is
- attempted. */
+/*
+ * Set the card samplerate, word size and stereo mode to correspond to
+ * the settings in the CARD struct for the specified device in DEV.
+ * We keep two separate sets of information, one for each device; the
+ * hardware is not actually configured until a read or write is
+ * attempted.
+ */
int
nm256_setInfo (int dev, struct nm256_info *card)
break;
if (x < 8) {
- u8 speedbits = ((x << 4) & NM_RATE_MASK)
- | (card->sinfo[w].bits == 16 ? NM_RATE_BITS_16: 0)
- | (card->sinfo[w].stereo ? NM_RATE_STEREO : 0);
+ u8 ratebits = ((x << 4) & NM_RATE_MASK);
+ if (card->sinfo[w].bits == 16)
+ ratebits |= NM_RATE_BITS_16;
+ if (card->sinfo[w].stereo)
+ ratebits |= NM_RATE_STEREO;
card->sinfo[w].samplerate = samplerates[x];
+
if (card->dev_for_play == dev && card->playing) {
+ if (nm256_debug)
+ printk (KERN_DEBUG "Setting play ratebits to 0x%x\n",
+ ratebits);
nm256_loadCoefficient (card, 0, x);
nm256_writePort8 (card, 2,
- NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
- speedbits);
+ NM_PLAYBACK_REG_OFFSET + NM_RATE_REG_OFFSET,
+ ratebits);
}
if (card->dev_for_record == dev && card->recording) {
+ if (nm256_debug)
+ printk (KERN_DEBUG "Setting record ratebits to 0x%x\n",
+ ratebits);
nm256_loadCoefficient (card, 1, x);
- nm256_writePort8 (card, 2,
- NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
- speedbits);
+ nm256_writePort8 (card, 2,
+ NM_RECORD_REG_OFFSET + NM_RATE_REG_OFFSET,
+ ratebits);
}
return 0;
}
/* Enable playback engine and interrupts. */
nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG,
- NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
+ NM_PLAYBACK_ENABLE_FLAG | NM_PLAYBACK_FREERUN);
/* Enable both channels. */
nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG, 0x0);
}
}
-/* Request one chunk of AMT bytes from the recording device. When the
- operation is complete, the data will be copied into BUFFER and the
- function DMAbuf_inputintr will be invoked. */
+/*
+ * Request one chunk of AMT bytes from the recording device. When the
+ * operation is complete, the data will be copied into BUFFER and the
+ * function DMAbuf_inputintr will be invoked.
+ */
static void
nm256_startRecording (struct nm256_info *card, char *buffer, u32 amt)
u32 endpos;
int enableEngine = 0;
u32 ringsize = card->recordBufferSize;
+ unsigned long flags;
if (amt > (ringsize / 2)) {
- /* Of course this won't actually work right, because the
- caller is going to assume we will give what we got asked
- for. */
+ /*
+ * Of course this won't actually work right, because the
+ * caller is going to assume we will give what we got asked
+ * for.
+ */
printk (KERN_ERR "NM256: Read request too large: %d\n", amt);
amt = ringsize / 2;
}
return;
}
- /* If we're not currently recording, set up the start and end registers
- for the recording engine. */
+ save_flags (flags);
+ cli ();
+ /*
+ * If we're not currently recording, set up the start and end registers
+ * for the recording engine.
+ */
if (! card->recording) {
card->recording = 1;
if (nm256_grabInterrupt (card) == 0) {
}
else {
/* Not sure what else to do here. */
+ restore_flags (flags);
return;
}
}
+ /*
+ * If we happen to go past the end of the buffer a bit (due to a
+ * delayed interrupt) it's OK. So might as well set the watermark
+ * right at the end of the data we want.
+ */
endpos = card->abuf2 + ((card->curRecPos + amt) % ringsize);
card->recBuf = buffer;
if (enableEngine)
nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG,
NM_RECORD_ENABLE_FLAG | NM_RECORD_FREERUN);
+
+ restore_flags (flags);
}
/* Stop the play engine. */
{
/* Shut off sound from both channels. */
nm256_writePort16 (card, 2, NM_AUDIO_MUTE_REG,
- NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
+ NM_AUDIO_MUTE_LEFT | NM_AUDIO_MUTE_RIGHT);
/* Disable play engine. */
nm256_writePort8 (card, 2, NM_PLAYBACK_ENABLE_REG, 0);
if (card->playing) {
}
}
-/* Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at.
- 1972?
-
- Write AMT bytes of BUFFER to the playback ring buffer, and start the
- playback engine running. It will only accept up to 1/2 of the total
- size of the ring buffer. */
+/*
+ * Ring buffers, man. That's where the hip-hop, wild-n-wooly action's at.
+ * 1972? (Well, I suppose it was cheep-n-easy to implement.)
+ *
+ * Write AMT bytes of BUFFER to the playback ring buffer, and start the
+ * playback engine running. It will only accept up to 1/2 of the total
+ * size of the ring buffer. No check is made that we're about to overwrite
+ * the currently-playing sample.
+ */
static void
nm256_write_block (struct nm256_info *card, char *buffer, u32 amt)
{
u32 ringsize = card->playbackBufferSize;
u32 endstop;
+ unsigned long flags;
if (amt > (ringsize / 2)) {
printk (KERN_ERR "NM256: Write request too large: %d\n", amt);
card->requested_amt = amt;
+ save_flags (flags);
+ cli ();
+
if ((card->curPlayPos + amt) >= ringsize) {
u32 rem = ringsize - card->curPlayPos;
nm256_writeBuffer8 (card, buffer, 1,
- card->abuf1 + card->curPlayPos,
- rem);
+ card->abuf1 + card->curPlayPos,
+ rem);
if (amt > rem)
- nm256_writeBuffer8 (card, buffer, 1, card->abuf1,
- amt - rem);
+ nm256_writeBuffer8 (card, buffer + rem, 1, card->abuf1,
+ amt - rem);
}
else
nm256_writeBuffer8 (card, buffer, 1,
card->abuf1 + card->curPlayPos,
amt);
- /* Setup the start-n-stop-n-limit registers, and start that engine
- goin'.
-
- Normally we just let it wrap around to avoid the click-click
- action scene. */
+ /*
+ * Setup the start-n-stop-n-limit registers, and start that engine
+ * goin'.
+ *
+ * Normally we just let it wrap around to avoid the click-click
+ * action scene.
+ */
if (! card->playing) {
- /* The PBUFFER_END register in this case points to one "word"
+ /* The PBUFFER_END register in this case points to one sample
before the end of the buffer. */
int w = (card->dev_for_play == card->dev[0] ? 0 : 1);
- int wordsize = (card->sinfo[w].bits == 16 ? 2 : 1)
- * (card->sinfo[w].stereo ? 2 : 1);
+ int sampsize = (card->sinfo[w].bits == 16 ? 2 : 1);
+
+ if (card->sinfo[w].stereo)
+ sampsize *= 2;
/* Need to set the not-normally-changing-registers up. */
nm256_writePort32 (card, 2, NM_PBUFFER_START,
card->abuf1 + card->curPlayPos);
nm256_writePort32 (card, 2, NM_PBUFFER_END,
- card->abuf1 + ringsize - wordsize);
+ card->abuf1 + ringsize - sampsize);
nm256_writePort32 (card, 2, NM_PBUFFER_CURRP,
card->abuf1 + card->curPlayPos);
}
endstop = (card->curPlayPos + amt - NM256_PLAY_WMARK_SIZE) % ringsize;
nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop);
+
if (! card->playing)
startPlay (card);
+
+ restore_flags (flags);
}
-/* We just got a card playback interrupt; process it. */
+/* We just got a card playback interrupt; process it. */
static void
nm256_get_new_block (struct nm256_info *card)
{
amt -= card->curPlayPos;
if (card->requested_amt > (amt + NM256_PLAY_WMARK_SIZE)) {
- u32 endstop =
+ u32 endstop =
card->curPlayPos + card->requested_amt - NM256_PLAY_WMARK_SIZE;
nm256_writePort32 (card, 2, NM_PBUFFER_WMARK, card->abuf1 + endstop);
- } else {
+ }
+ else {
card->curPlayPos += card->requested_amt;
/* Get a new block to write. This will eventually invoke
- nm256_write_block (). */
+ nm256_write_block () or stopPlay (). */
DMAbuf_outputintr (card->dev_for_play, 1);
}
}
/* Ultra cheez-whiz. But I'm too lazy to grep headers. */
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
-/* Read the last-recorded block from the ring buffer, copy it into the
- saved buffer pointer, and invoke DMAuf_inputintr() with the recording
- device. */
+/*
+ * Read the last-recorded block from the ring buffer, copy it into the
+ * saved buffer pointer, and invoke DMAuf_inputintr() with the recording
+ * device.
+ */
static void
nm256_read_block (struct nm256_info *card)
currptr = 0;
}
- /* This test is probably redundant; we shouldn't be here unless
- it's true. */
+ /*
+ * This test is probably redundant; we shouldn't be here unless
+ * it's true.
+ */
if (card->recording) {
/* If we wrapped around, copy everything from the start of our
recording buffer to the end of the buffer. */
}
#undef MIN
-/* Initialize the hardware and various other card data we'll need
- later. */
+/*
+ * Initialize the hardware.
+ */
static void
nm256_initHw (struct nm256_info *card)
{
- int x;
-
- card->playbackBufferSize = 16384;
- card->recordBufferSize = 16384;
-
- card->coeffBuf = card->bufend - NM_MAX_COEFFICIENT;
- card->abuf2 = card->coeffBuf - card->recordBufferSize;
- card->abuf1 = card->abuf2 - card->playbackBufferSize;
- card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4);
-
- /* Fixed setting. */
- card->mixer = NM_MIXER_BASE;
-
- card->playing = 0;
- card->is_open_play = 0;
- card->curPlayPos = 0;
-
- card->recording = 0;
- card->is_open_record = 0;
- card->curRecPos = 0;
-
- card->coeffsCurrent = 0;
-
- card->opencnt[0] = 0; card->opencnt[1] = 0;
-
/* Reset everything. */
- nm256_writePort8 (card, 2, 0, 0x11);
-
- /* Disable recording. */
- nm256_writePort8 (card, 2, NM_RECORD_ENABLE_REG, 0);
+ nm256_writePort8 (card, 2, 0x0, 0x11);
nm256_writePort16 (card, 2, 0x214, 0);
- /* Reasonable default settings, but largely unnecessary. */
- for (x = 0; x < 2; x++) {
- card->sinfo[x].bits = 8;
- card->sinfo[x].stereo = 0;
- card->sinfo[x].samplerate = 8000;
- }
+ stopRecord (card);
+ stopPlay (card);
}
-/* Handle a potential interrupt for the device referred to by DEV_ID. */
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ *
+ * I don't like the cut-n-paste job here either between the two routines,
+ * but there are sufficient differences between the two interrupt handlers
+ * that parameterizing it isn't all that great either. (Could use a macro,
+ * I suppose...yucky bleah.)
+ */
static void
nm256_interrupt (int irq, void *dev_id, struct pt_regs *dummy)
/* Not ours. */
if (status == 0) {
if (badintrcount++ > 1000) {
- printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n");
- nm256_releaseInterrupt (card);
+ /*
+ * I'm not sure if the best thing is to stop the card from
+ * playing or just release the interrupt (after all, we're in
+ * a bad situation, so doing fancy stuff may not be such a good
+ * idea).
+ *
+ * I worry about the card engine continuing to play noise
+ * over and over, however--that could become a very
+ * obnoxious problem. And we know that when this usually
+ * happens things are fairly safe, it just means the user's
+ * inserted a PCMCIA card and someone's spamming us with IRQ 9s.
+ */
+
+ if (card->playing)
+ stopPlay (card);
+ if (card->recording)
+ stopRecord (card);
+ badintrcount = 0;
}
return;
}
badintrcount = 0;
+ /* Rather boring; check for individual interrupts and process them. */
+
if (status & NM_PLAYBACK_INT) {
status &= ~NM_PLAYBACK_INT;
NM_ACK_INT (card, NM_PLAYBACK_INT);
nm256_writePort8 (card, 2, 0x400, cbyte & ~2);
}
+ /* Unknown interrupt. */
if (status) {
printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n",
status);
}
}
-/* Handle a potential interrupt for the device referred to by DEV_ID.
- This handler is for the 256ZX. */
+/*
+ * Handle a potential interrupt for the device referred to by DEV_ID.
+ * This handler is for the 256ZX, and is very similar to the non-ZX
+ * routine.
+ */
static void
nm256_interrupt_zx (int irq, void *dev_id, struct pt_regs *dummy)
if (status == 0) {
if (badintrcount++ > 1000) {
printk (KERN_ERR "NM256: Releasing interrupt, over 1000 invalid interrupts\n");
- nm256_releaseInterrupt (card);
+ /*
+ * I'm not sure if the best thing is to stop the card from
+ * playing or just release the interrupt (after all, we're in
+ * a bad situation, so doing fancy stuff may not be such a good
+ * idea).
+ *
+ * I worry about the card engine continuing to play noise
+ * over and over, however--that could become a very
+ * obnoxious problem. And we know that when this usually
+ * happens things are fairly safe, it just means the user's
+ * inserted a PCMCIA card and someone's spamming us with
+ * IRQ 9s.
+ */
+
+ if (card->playing)
+ stopPlay (card);
+ if (card->recording)
+ stopRecord (card);
+ badintrcount = 0;
}
return;
}
badintrcount = 0;
+ /* Rather boring; check for individual interrupts and process them. */
+
if (status & NM2_PLAYBACK_INT) {
status &= ~NM2_PLAYBACK_INT;
NM2_ACK_INT (card, NM2_PLAYBACK_INT);
nm256_writePort8 (card, 2, 0x400, cbyte & ~2);
}
+ /* Unknown interrupt. */
if (status) {
printk (KERN_ERR "NM256: Fire in the hole! Unknown status 0x%x\n",
status);
}
}
-/* Request our interrupt. */
+/*
+ * Request our interrupt.
+ */
static int
nm256_grabInterrupt (struct nm256_info *card)
{
return 0;
}
-/* Release our interrupt. */
+/*
+ * Release our interrupt.
+ */
static int
nm256_releaseInterrupt (struct nm256_info *card)
{
return 0;
}
+/*
+ * Waits for the mixer to become ready to be written; returns a zero value
+ * if it timed out.
+ */
+
static int
nm256_isReady (struct ac97_hwint *dev)
{
return 0;
}
- if (card->rev == REV_NM256AV) {
- testaddr = 0xa06;
- testb = 0x0100;
- } else if (card->rev == REV_NM256ZX) {
- testaddr = 0xa08;
- testb = 0x0800;
- } else {
- return -1;
- }
+ testaddr = card->mixer_status_offset;
+ testb = card->mixer_status_mask;
- while (t2-- > 0) {
- if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0) {
+ /*
+ * Loop around waiting for the mixer to become ready.
+ */
+ while (! done && t2-- > 0) {
+ if ((nm256_readPort16 (card, 2, testaddr) & testb) == 0)
done = 1;
- break;
- }
- udelay (100);
+ else
+ udelay (100);
}
return done;
}
+/*
+ * Return the contents of the AC97 mixer register REG. Returns a positive
+ * value if successful, or a negative error code.
+ */
static int
nm256_readAC97Reg (struct ac97_hwint *dev, u8 reg)
{
nm256_isReady (dev);
res = nm256_readPort16 (card, 2, card->mixer + reg);
+ /* Magic delay. Bleah yucky. */
udelay (1000);
return res;
}
return -EINVAL;
}
+/*
+ * Writes VALUE to AC97 mixer register REG. Returns 0 if successful, or
+ * a negative error code.
+ */
static int
nm256_writeAC97Reg (struct ac97_hwint *dev, u8 reg, u16 value)
{
return ! done;
}
+/*
+ * Initial register values to be written to the AC97 mixer.
+ * While most of these are identical to the reset values, we do this
+ * so that we have most of the register contents cached--this avoids
+ * reading from the mixer directly (which seems to be problematic,
+ * probably due to ignorance).
+ */
struct initialValues
{
unsigned short port;
static struct initialValues nm256_ac97_initial_values[] =
{
- { 0x0002, 0x8000 },
- { 0x0004, 0x0000 },
- { 0x0006, 0x0000 },
- { 0x000A, 0x0000 },
- { 0x000C, 0x0008 },
- { 0x000E, 0x8008 },
- { 0x0010, 0x8808 },
- { 0x0012, 0x8808 },
- { 0x0014, 0x8808 },
- { 0x0016, 0x8808 },
- { 0x0018, 0x0808 },
- { 0x001A, 0x0000 },
- { 0x001C, 0x0B0B },
- { 0x0020, 0x0000 },
+ { AC97_MASTER_VOL_STEREO, 0x8000 },
+ { AC97_HEADPHONE_VOL, 0x8000 },
+ { AC97_MASTER_VOL_MONO, 0x0000 },
+ { AC97_PCBEEP_VOL, 0x0000 },
+ { AC97_PHONE_VOL, 0x0008 },
+ { AC97_MIC_VOL, 0x8000 },
+ { AC97_LINEIN_VOL, 0x8808 },
+ { AC97_CD_VOL, 0x8808 },
+ { AC97_VIDEO_VOL, 0x8808 },
+ { AC97_AUX_VOL, 0x8808 },
+ { AC97_PCMOUT_VOL, 0x0808 },
+ { AC97_RECORD_SELECT, 0x0000 },
+ { AC97_RECORD_GAIN, 0x0B0B },
+ { AC97_GENERAL_PURPOSE, 0x0000 },
{ 0xffff, 0xffff }
};
+/* Initialize the AC97 into a known state. */
static int
nm256_resetAC97 (struct ac97_hwint *dev)
{
return -EINVAL;
}
- /* Reset the card. 'Tis magic! */
+ /* Reset the mixer. 'Tis magic! */
nm256_writePort8 (card, 2, 0x6c0, 1);
nm256_writePort8 (card, 2, 0x6cc, 0x87);
nm256_writePort8 (card, 2, 0x6cc, 0x80);
nm256_writePort8 (card, 2, 0x6cc, 0x0);
- for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) {
- ac97_put_register (dev,
- nm256_ac97_initial_values[x].port,
- nm256_ac97_initial_values[x].value);
+ if (! card->mixer_values_init) {
+ for (x = 0; nm256_ac97_initial_values[x].port != 0xffff; x++) {
+ ac97_put_register (dev,
+ nm256_ac97_initial_values[x].port,
+ nm256_ac97_initial_values[x].value);
+ card->mixer_values_init = 1;
+ }
}
return 0;
}
-/* We don't do anything special here. */
+/*
+ * We don't do anything particularly special here; it just passes the
+ * mixer ioctl to the AC97 driver.
+ */
static int
nm256_default_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
{
nm256_default_mixer_ioctl
};
-/* I "love" C sometimes. Got braces? */
+/*
+ * Default settings for the OSS mixer. These are set last, after the
+ * mixer is initialized.
+ *
+ * I "love" C sometimes. Got braces?
+ */
static struct ac97_mixer_value_list mixer_defaults[] = {
{ SOUND_MIXER_VOLUME, { { 85, 85 } } },
{ SOUND_MIXER_SPEAKER, { { 100 } } },
{ -1, { { 0, 0 } } }
};
+
+/* Installs the AC97 mixer into CARD. */
static int
nm256_install_mixer (struct nm256_info *card)
{
return 0;
}
-/* See if the signature left by the NM256 BIOS is intact; if so, we use
- the associated address as the end of our buffer. */
+/* Perform a full reset on the hardware; this is invoked when an APM
+ resume event occurs. */
static void
-nm256_peek_for_sig (struct nm256_info *card, u32 port1addr)
+nm256_full_reset (struct nm256_info *card)
{
- char *temp = ioremap_nocache (port1addr + card->port1_end - 0x0400, 16);
+ nm256_initHw (card);
+ ac97_reset (&(card->mdev));
+}
+
+/*
+ * See if the signature left by the NM256 BIOS is intact; if so, we use
+ * the associated address as the end of our audio buffer in the video
+ * RAM.
+ */
+
+static void
+nm256_peek_for_sig (struct nm256_info *card)
+{
+ u32 port1offset
+ = card->port[0].physaddr + card->port[0].end_offset - 0x0400;
+ /* The signature is located 1K below the end of video RAM. */
+ char *temp = ioremap_nocache (port1offset, 16);
+ /* Default buffer end is 5120 bytes below the top of RAM. */
+ u32 default_value = card->port[0].end_offset - 0x1400;
u32 sig;
+ /* Install the default value first, so we don't have to repeatedly
+ do it if there is a problem. */
+ card->port[0].end_offset = default_value;
+
if (temp == NULL) {
printk (KERN_ERR "NM256: Unable to scan for card signature in video RAM\n");
return;
}
- memcpy_fromio (&sig, temp, sizeof (u32));
- if ((sig & 0xffff0000) == 0x4e4d0000) {
- memcpy_fromio (&(card->bufend), temp + 4, sizeof (u32));
+ sig = readl (temp);
+ if ((sig & NM_SIG_MASK) == NM_SIGNATURE) {
+ u32 pointer = readl (temp + 4);
+
+ /*
+ * If it's obviously invalid, don't use it (the port already has a
+ * suitable default value set).
+ */
+ if (pointer != 0xffffffff)
+ card->port[0].end_offset = pointer;
+
printk (KERN_INFO "NM256: Found card signature in video RAM: 0x%x\n",
- card->bufend);
+ pointer);
}
release_region ((unsigned long) temp, 16);
}
-/* Install a driver for the soundcard referenced by PCIDEV. */
+/*
+ * Install a driver for the PCI device referenced by PCIDEV.
+ * VERSTR is a human-readable version string.
+ */
static int
nm256_install(struct pci_dev *pcidev, enum nm256rev rev, char *verstr)
{
struct nm256_info *card;
- u32 port1addr = (pcidev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK);
- u32 port2addr = (pcidev->base_address[1] & PCI_BASE_ADDRESS_MEM_MASK);
int x;
card = kmalloc (sizeof (struct nm256_info), GFP_KERNEL);
card->recording = 0;
card->rev = rev;
- /* The NM256 has two memory ports. The first port is nothing
- more than a chunk of video RAM, which is used as the I/O ring
- buffer. The second port has the actual juicy stuff (like the
- mixer and the playback engine control registers). */
+ /* Init the memory port info. */
+ for (x = 0; x < 2; x++) {
+ card->port[x].physaddr = RSRCADDRESS (pcidev, x);
+ card->port[x].ptr = NULL;
+ card->port[x].start_offset = 0;
+ card->port[x].end_offset = 0;
+ }
- card->ports[1] = ioremap_nocache (port2addr, NM_PORT2_SIZE);
+ /* Port 2 is easy. */
+ card->port[1].start_offset = 0;
+ card->port[1].end_offset = NM_PORT2_SIZE;
- if (card->ports[1] == NULL) {
- printk (KERN_ERR "NM256: Unable to remap port 2\n");
+ /* Yuck. But we have to map in port 2 so we can check how much RAM the
+ card has. */
+ if (nm256_remap_ports (card)) {
kfree_s (card, sizeof (struct nm256_info));
return 0;
}
+ /*
+ * The NM256 has two memory ports. The first port is nothing
+ * more than a chunk of video RAM, which is used as the I/O ring
+ * buffer. The second port has the actual juicy stuff (like the
+ * mixer and the playback engine control registers).
+ */
+
if (card->rev == REV_NM256AV) {
- card->port1_end = 2560 * 1024;
+ /* Ok, try to see if this is a non-AC97 version of the hardware. */
+ int pval = nm256_readPort16 (card, 2, NM_MIXER_PRESENCE);
+ if ((pval & NM_PRESENCE_MASK) != NM_PRESENCE_VALUE) {
+ if (! force_load) {
+ printk (KERN_ERR "NM256: This doesn't look to me like the AC97-compatible version.\n");
+ printk (KERN_ERR " You can force the driver to load by passing in the module\n");
+ printk (KERN_ERR " parameter:\n");
+ printk (KERN_ERR " force_ac97 = 1\n");
+ printk (KERN_ERR "\n");
+ printk (KERN_ERR " More likely, you should be using the appropriate SB-16 or\n");
+ printk (KERN_ERR " CS4232 driver instead. (If your BIOS has settings for\n");
+ printk (KERN_ERR " IRQ and/or DMA for the sound card, this is *not* the correct\n");
+ printk (KERN_ERR " driver to use.)\n");
+ nm256_release_ports (card);
+ kfree_s (card, sizeof (struct nm256_info));
+ return 0;
+ }
+ else {
+ printk (KERN_INFO "NM256: Forcing driver load as per user request.\n");
+ }
+ }
+ else {
+ printk (KERN_INFO "NM256: Congratulations. You're not running Eunice.\n");
+ }
+ card->port[0].end_offset = 2560 * 1024;
card->introutine = nm256_interrupt;
+ card->mixer_status_offset = NM_MIXER_STATUS_OFFSET;
+ card->mixer_status_mask = NM_MIXER_READY_MASK;
}
else {
+ /* Not sure if there is any relevant detect for the ZX or not. */
if (nm256_readPort8 (card, 2, 0xa0b) != 0)
- card->port1_end = 6144 * 1024;
+ card->port[0].end_offset = 6144 * 1024;
else
- card->port1_end = 4096 * 1024;
+ card->port[0].end_offset = 4096 * 1024;
card->introutine = nm256_interrupt_zx;
+ card->mixer_status_offset = NM2_MIXER_STATUS_OFFSET;
+ card->mixer_status_mask = NM2_MIXER_READY_MASK;
}
- /* Default value. */
- card->bufend = card->port1_end - 0x1400;
-
- if (buffertop >= 98304 && buffertop < card->port1_end)
- card->bufend = buffertop;
+ if (buffertop >= 98304 && buffertop < card->port[0].end_offset)
+ card->port[0].end_offset = buffertop;
else
- nm256_peek_for_sig (card, port1addr);
+ nm256_peek_for_sig (card);
- card->port1_start = card->bufend - 98304;
+ card->port[0].start_offset = card->port[0].end_offset - 98304;
printk (KERN_INFO "NM256: Mapping port 1 from 0x%x - 0x%x\n",
- card->port1_start, card->port1_end);
-
- card->ports[0] =
- ioremap_nocache (port1addr + card->port1_start,
- card->port1_end - card->port1_start);
+ card->port[0].start_offset, card->port[0].end_offset);
- if (card->ports[0] == NULL) {
- printk (KERN_ERR "NM256: Unable to remap port 1\n");
- release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE);
+ if (nm256_remap_ports (card)) {
kfree_s (card, sizeof (struct nm256_info));
return 0;
}
card->has_irq = 0;
if (nm256_grabInterrupt (card) != 0) {
- release_region ((unsigned long) card->ports[0],
- card->port1_end - card->port1_start);
- release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE);
+ nm256_release_ports (card);
kfree_s (card, sizeof (struct nm256_info));
return 0;
}
* Init the board.
*/
+ card->playbackBufferSize = 16384;
+ card->recordBufferSize = 16384;
+
+ card->coeffBuf = card->port[0].end_offset - NM_MAX_COEFFICIENT;
+ card->abuf2 = card->coeffBuf - card->recordBufferSize;
+ card->abuf1 = card->abuf2 - card->playbackBufferSize;
+ card->allCoeffBuf = card->abuf2 - (NM_TOTAL_COEFF_COUNT * 4);
+
+ /* Fixed setting. */
+ card->mixer = NM_MIXER_OFFSET;
+ card->mixer_values_init = 0;
+
+ card->is_open_play = 0;
+ card->is_open_record = 0;
+
+ card->coeffsCurrent = 0;
+
+ card->opencnt[0] = 0; card->opencnt[1] = 0;
+
+ /* Reasonable default settings, but largely unnecessary. */
+ for (x = 0; x < 2; x++) {
+ card->sinfo[x].bits = 8;
+ card->sinfo[x].stereo = 0;
+ card->sinfo[x].samplerate = 8000;
+ }
+
nm256_initHw (card);
for (x = 0; x < 2; x++) {
- if ((card->dev[x] =
+ if ((card->dev[x] =
sound_install_audiodrv(AUDIO_DRIVER_VERSION,
"NM256", &nm256_audio_driver,
sizeof(struct audio_driver),
}
else {
printk(KERN_ERR "NM256: Too many PCM devices available\n");
- release_region ((unsigned long) card->ports[0],
- card->port1_end - card->port1_start);
- release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE);
+ nm256_release_ports (card);
kfree_s (card, sizeof (struct nm256_info));
return 0;
}
return 1;
}
+
+#ifdef CONFIG_APM
+/*
+ * APM event handler, so the card is properly reinitialized after a power
+ * event.
+ */
+static int
+handle_apm_event (apm_event_t event)
+{
+ static int down = 0;
+
+ switch (event)
+ {
+ case APM_SYS_SUSPEND:
+ case APM_USER_SUSPEND:
+ down++;
+ break;
+ case APM_NORMAL_RESUME:
+ case APM_CRITICAL_RESUME:
+ if (down)
+ {
+ struct nm256_info *crd;
+
+ down = 0;
+ for (crd = nmcard_list; crd != NULL; crd = crd->next_card)
+ {
+ int playing = crd->playing;
+ nm256_full_reset (crd);
+ /*
+ * A little ugly, but that's ok; pretend the
+ * block we were playing is done.
+ */
+ if (playing)
+ DMAbuf_outputintr (crd->dev_for_play, 1);
+ }
+ }
+ break;
+ }
+ return 0;
+}
+#endif
+
/*
* This loop walks the PCI configuration database and finds where
* the sound cards are.
if (count == 0)
return -ENODEV;
+#ifdef CONFIG_APM
+ apm_register_callback (&handle_apm_event);
+#endif
+
printk (KERN_INFO "Done installing NM256 audio driver.\n");
return 0;
}
if (! ((mode & OPEN_READ) || (mode & OPEN_WRITE)))
return -EIO;
- /* If it's open for both read and write, and the card's currently
- being read or written to, then do the opposite of what has
- already been done. Otherwise, don't specify any mode until the
- user actually tries to do I/O. */
+ /*
+ * If it's open for both read and write, and the card's currently
+ * being read or written to, then do the opposite of what has
+ * already been done. Otherwise, don't specify any mode until the
+ * user actually tries to do I/O. (Some programs open the device
+ * for both read and write, but only actually do reading or writing.)
+ */
if ((mode & OPEN_WRITE) && (mode & OPEN_READ)) {
if (card->is_open_play)
}
}
+/* Standard ioctl handler. */
static int
nm256_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
{
else
w = 1;
+ /*
+ * The code here is messy. There are probably better ways to do
+ * it. (It should be possible to handle it the same way the AC97 mixer
+ * is done.)
+ */
switch (cmd)
{
case SOUND_PCM_WRITE_RATE:
return put_user(ret, (int *) arg);
}
-/* Given the dev DEV and an associated physical buffer PHYSBUF, return
- a pointer to the actual buffer in kernel space. */
+/*
+ * Given the sound device DEV and an associated physical buffer PHYSBUF,
+ * return a pointer to the actual buffer in kernel space.
+ *
+ * This routine should exist as part of the soundcore routines.
+ */
static char *
nm256_getDMAbuffer (int dev, unsigned long physbuf)
}
}
+/* Ditto, but do recording instead. */
static void
nm256_audio_start_input(int dev, unsigned long physbuf, int count,
- int intrflag)
+ int intrflag)
{
struct nm256_info *card = nm256_find_card (dev);
}
}
+/*
+ * Prepare for inputting samples to DEV.
+ * Each requested buffer will be BSIZE byes long, with a total of
+ * BCOUNT buffers.
+ */
+
static int
nm256_audio_prepare_for_input(int dev, int bsize, int bcount)
{
* 2. We get a write buffer without dma_mode setup (dmabuf.c:1152)
* 3. We restart a transfer (dmabuf.c:1324)
*/
+
static int
nm256_audio_prepare_for_output(int dev, int bsize, int bcount)
{
MODULE_PARM (usecache, "i");
MODULE_PARM (buffertop, "i");
MODULE_PARM (nm256_debug, "i");
+MODULE_PARM (force_load, "i");
int
init_module (void)
{
nmcard_list = NULL;
- printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.0\n");
+ printk (KERN_INFO "NeoMagic 256AV/256ZX audio driver, version 1.1\n");
if (init_nm256 () == 0) {
SOUND_LOCK;
stopRecord (card);
if (card->has_irq)
free_irq (card->irq, card);
- release_region ((unsigned long) card->ports[0],
- card->port1_end - card->port1_start);
- release_region ((unsigned long) card->ports[1], NM_PORT2_SIZE);
+ nm256_release_ports (card);
sound_unload_mixerdev (card->mixer_oss_dev);
sound_unload_audiodev (card->dev[0]);
sound_unload_audiodev (card->dev[1]);
}
nmcard_list = NULL;
}
+#ifdef CONFIG_APM
+ apm_unregister_callback (&handle_apm_event);
+#endif
}
#endif
\f
}
static void
-nm256_loadOneCoefficient (struct nm256_info *card, u32 port, u16 which)
+nm256_loadOneCoefficient (struct nm256_info *card, int devnum, u32 port,
+ u16 which)
{
u32 coeffBuf = (which < 8) ? card->coeffBuf : card->allCoeffBuf;
u16 offset = nm256_getStartOffset (which);
card->coeffsCurrent = 0;
if (nm256_debug)
- printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d\n",
- coeffBuf, coeffBuf + size - 1, which);
+ printk (KERN_INFO "NM256: Loading coefficient buffer 0x%x-0x%x with coefficient %d, size %d, port 0x%x\n",
+ coeffBuf, coeffBuf + size - 1, which, size, port);
nm256_writeBuffer8 (card, coefficients + offset, 1, coeffBuf, size);
nm256_writePort32 (card, 2, port + 0, coeffBuf);
- nm256_writePort32 (card, 2, port + 4, coeffBuf + size - 1);
+ /* ??? Record seems to behave differently than playback. */
+ if (devnum == 0)
+ size--;
+ nm256_writePort32 (card, 2, port + 4, coeffBuf + size);
}
static void
number += 8;
if (! nm256_cachedCoefficients (card))
- nm256_loadOneCoefficient (card, addrs[which], number);
+ nm256_loadOneCoefficient (card, which, addrs[which], number);
else {
u32 base = card->allCoeffBuf;
u32 offset = nm256_getStartOffset (number);
sb = inode->i_sb;
inode->i_uid = sb->u.adfs_sb.s_uid;
inode->i_gid = sb->u.adfs_sb.s_gid;
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) {
adfs_error (sb, "adfs_read_inode",
FILE_END(link_bh->b_data,link)->link_chain;
FILE_END(link_bh->b_data,link)->link_chain = cpu_to_be32(inode->i_ino);
affs_fix_checksum(AFFS_I2BSIZE(link),link_bh->b_data,5);
- link->i_version = ++event;
+ link->i_version = ++global_event;
mark_inode_dirty(link);
mark_buffer_dirty(link_bh,1);
}
affs_fix_checksum(AFFS_I2BSIZE(inode),inode_bh->b_data,5);
affs_fix_checksum(AFFS_I2BSIZE(dir),dir_bh->b_data,5);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME;
unlock_super(inode->i_sb);
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(inode);
d_delete(dentry);
mark_inode_dirty(dir);
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
d_instantiate(dentry,inode);
mark_inode_dirty(inode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(dir);
out:
return error;
inode->u.affs_i.i_protect = mode_to_prot(inode->i_mode);
d_instantiate(dentry,inode);
mark_inode_dirty(inode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(dir);
out:
return error;
inode->i_nlink = retval;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
retval = 0;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(dir);
mark_inode_dirty(inode);
d_delete(dentry);
if (error)
goto out_release;
d_instantiate(dentry,inode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(dir);
out:
if (error)
inode->i_nlink = 0;
else {
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_inode_dirty(dir);
mark_inode_dirty(oldinode);
oldinode->i_count++;
new_dir->i_ctime = new_dir->i_mtime = old_dir->i_ctime
= old_dir->i_mtime = CURRENT_TIME;
- new_dir->i_version = ++event;
- old_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
+ old_dir->i_version = ++global_event;
retval = 0;
mark_inode_dirty(new_dir);
mark_inode_dirty(old_dir);
return (count > 1); /* remaining users? */
}
+/*
+ * Search for at least 1 mount point in the dentry's subdirs.
+ * We descend to the next level whenever the d_subdirs
+ * list is non-empty and continue searching.
+ */
+int have_submounts(struct dentry *parent)
+{
+ struct dentry *this_parent = parent;
+ struct list_head *next;
+
+ if (parent->d_mounts != parent)
+ return 1;
+repeat:
+ next = this_parent->d_subdirs.next;
+resume:
+ while (next != &this_parent->d_subdirs) {
+ struct list_head *tmp = next;
+ struct dentry *dentry = list_entry(tmp, struct dentry, d_child); next = tmp->next;
+ /* Have we found a mount point ? */
+ if (dentry->d_mounts != dentry)
+ return 1;
+ if (!list_empty(&dentry->d_subdirs)) {
+ this_parent = dentry;
+ goto repeat;
+ }
+ }
+ /*
+ * All done at this level ... ascend and resume the search.
+ */
+ if (this_parent != parent) {
+ next = this_parent->d_child.next;
+ this_parent = this_parent->d_parent;
+ goto resume;
+ }
+ return 0; /* No mount points found in tree */
+}
+
/*
* Search the dentry child list for the specified parent,
* and move any unused dentries to the end of the unused
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
- file->f_version = ++event;
+ file->f_version = ++global_event;
}
return offset;
}
inode->u.ext2_i.i_dtime = le32_to_cpu(raw_inode->i_dtime);
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat), not the fs block size */
inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
inode->u.ext2_i.i_new_inode = 0;
inode->u.ext2_i.i_flags = le32_to_cpu(raw_inode->i_flags);
inode->u.ext2_i.i_faddr = le32_to_cpu(raw_inode->i_faddr);
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
dir->u.ext2_i.i_flags &= ~EXT2_BTREE_FL;
mark_inode_dirty(dir);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
*res_dir = de;
*err = 0;
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
EXT2_FEATURE_INCOMPAT_FILETYPE))
de->file_type = EXT2_FT_REG_FILE;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
if (!bh)
goto out_no_entry;
de->inode = cpu_to_le32(inode->i_ino);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
EXT2_FEATURE_INCOMPAT_FILETYPE))
de->file_type = EXT2_FT_DIR;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
goto end_rmdir;
retval = ext2_delete_entry (de, bh);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
if (retval)
goto end_rmdir;
mark_buffer_dirty(bh, 1);
ext2_warning (inode->i_sb, "ext2_rmdir",
"empty directory has nlink!=2 (%d)",
inode->i_nlink);
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
inode->i_nlink = 0;
inode->i_size = 0;
mark_inode_dirty(inode);
retval = ext2_delete_entry (de, bh);
if (retval)
goto end_unlink;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
if (EXT2_HAS_INCOMPAT_FEATURE(dir->i_sb,
EXT2_FEATURE_INCOMPAT_FILETYPE))
de->file_type = EXT2_FT_SYMLINK;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
else if (S_ISFIFO(inode->i_mode))
de->file_type = EXT2_FT_FIFO;
}
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
if (!new_bh)
goto end_rename;
}
- new_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
/*
* Like most other Unix systems, set the ctime for inodes on a
ext2_delete_entry (old_de, old_bh);
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
MSDOS_I(inode)->i_fat_inode = inode;
inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
MSDOS_I(inode)->i_last_pos = 0;
inode->i_mode = (S_IRWXUGO & ~MSDOS_SB(sb)->options.fs_umask) | S_IFDIR;
inode->i_op = MSDOS_SB(sb)->dir_ops;
MSDOS_I(inode)->i_fat_inode = inode;
inode->i_uid = MSDOS_SB(sb)->options.fs_uid;
inode->i_gid = MSDOS_SB(sb)->options.fs_gid;
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
MSDOS_I(inode)->i_last_pos = 0;
inode->i_mode = MSDOS_MKMODE(de->attr,S_IRWXUGO &
new_one:
memset(f, 0, sizeof(*f));
f->f_count = 1;
- f->f_version = ++event;
+ f->f_version = ++global_event;
f->f_uid = current->fsuid;
f->f_gid = current->fsgid;
put_inuse(f);
++(tmp->i_nlink);
}
tmp->i_size += HFS_I(tmp)->dir_size;
- tmp->i_version = ++event;
+ tmp->i_version = ++global_event;
}
tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
mark_inode_dirty(tmp);
--(tmp->i_nlink);
}
tmp->i_size -= HFS_I(tmp)->dir_size;
- tmp->i_version = ++event;
+ tmp->i_version = ++global_event;
}
tmp->i_ctime = tmp->i_mtime = CURRENT_TIME;
mark_inode_dirty(tmp);
mark_inode_dirty(dir);
for (i = 0; i < info->s_namelen ; i++)
de->name[i] = (i < namelen) ? name[i] : 0;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
*res_dir = de;
break;
if (inode->i_nlink != 2)
printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
de->inode = 0;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
inode->i_nlink=0;
mark_inode_dirty(inode);
inode->i_nlink=1;
}
de->inode = 0;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
dir->i_ctime = dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(dir);
new_de->inode = old_inode->i_ino;
old_de->inode = 0;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
mark_inode_dirty(old_dir);
new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
- new_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
mark_inode_dirty(new_dir);
if (new_inode) {
new_inode->i_nlink--;
if (error)
goto out;
}
- new_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
/* There we go */
else
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
mark_inode_dirty(old_inode);
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(old_dir);
if (new_inode) {
else
MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
mark_inode_dirty(old_inode);
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(old_dir);
return 0;
out_valid:
return 1;
out_bad:
- d_drop(dentry);
if (!list_empty(&dentry->d_subdirs))
shrink_dcache_parent(dentry);
+ /* If we have submounts, don't unhash ! */
+ if (have_submounts(dentry))
+ goto out_valid;
+ d_drop(dentry);
if (dentry->d_parent->d_inode)
nfs_invalidate_dircache(dentry->d_parent->d_inode);
if (inode && S_ISDIR(inode->i_mode))
fh = (u32 *) &fhandle;
dfprintk(PAGECACHE, " %08x%08x%08x%08x%08x%08x%08x%08x\n",
fh[0],fh[1],fh[2],fh[3],fh[4],fh[5],fh[6],fh[7]);
- if (!IS_ROOT(dentry))
+ if (!IS_ROOT(dentry) && !have_submounts(dentry))
d_drop(dentry);
goto out;
}
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
- file->f_version = ++event;
+ file->f_version = ++global_event;
}
retval = offset;
}
if (!data)
return 0;
+#if 0
vma = find_vma(current->mm, (unsigned long) data);
if (!vma || (unsigned long) data < vma->vm_start)
return -EFAULT;
i = vma->vm_end - (unsigned long) data;
if (PAGE_SIZE <= (unsigned long) i)
i = PAGE_SIZE-1;
- if (!(page = __get_free_page(GFP_KERNEL))) {
+#else
+ i = PAGE_SIZE;
+#endif
+ if (!(page = get_free_page(GFP_KERNEL))) {
return -ENOMEM;
}
- if (copy_from_user((void *) page,data,i)) {
+ if (copy_from_user((void *) page,data,i) == i) {
free_page(page);
return -EFAULT;
}
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
- file->f_version = ++event;
+ file->f_version = ++global_event;
}
retval = offset;
}
inode->i_mtime = SWAB32(ufs_inode->ui_mtime.tv_sec);
inode->i_blocks = SWAB32(ufs_inode->ui_blocks);
inode->i_blksize = PAGE_SIZE; /* This is the optimal IO size (for stat) */
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
inode->u.ufs_i.i_flags = SWAB32(ufs_inode->ui_flags);
inode->u.ufs_i.i_gen = SWAB32(ufs_inode->ui_gen);
*/
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
mark_inode_dirty(dir);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
*res_dir = de;
*err = 0;
}
de->d_ino = SWAB32(inode->i_ino);
ufs_set_de_type (de, inode->i_mode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
goto out_no_entry;
de->d_ino = SWAB32(inode->i_ino);
ufs_set_de_type (de, inode->i_mode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
goto out_no_entry;
de->d_ino = SWAB32(inode->i_ino);
ufs_set_de_type (de, inode->i_mode);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
goto end_rmdir;
retval = ufs_delete_entry (dir, de, bh);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
if (retval)
goto end_rmdir;
mark_buffer_dirty(bh, 1);
ufs_warning (inode->i_sb, "ufs_rmdir",
"empty directory has nlink!=2 (%d)",
inode->i_nlink);
- inode->i_version = ++event;
+ inode->i_version = ++global_event;
inode->i_nlink = 0;
inode->i_size = 0;
mark_inode_dirty(inode);
retval = ufs_delete_entry (dir, de, bh);
if (retval)
goto end_unlink;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
if (!bh)
goto out_no_entry;
de->d_ino = SWAB32(inode->i_ino);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
return err;
de->d_ino = SWAB32(inode->i_ino);
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
ll_rw_block (WRITE, 1, &bh);
&retval);
if (!new_bh)
goto end_rename;
- new_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
/*
* ok, that's it
new_de->d_ino = SWAB32(old_inode->i_ino);
ufs_delete_entry (old_dir, old_de, old_bh);
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
if (new_inode) {
new_inode->i_nlink--;
new_inode->i_ctime = CURRENT_TIME;
return res;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
- inode->i_version = ++event;
- dir->i_version = event;
+ inode->i_version = ++global_event;
+ dir->i_version = global_event;
MSDOS_I(dir)->i_last_pos = 0;
dentry->d_time = dentry->d_parent->d_inode->i_version;
d_instantiate(dentry,inode);
/* remove the shortname */
dir->i_mtime = CURRENT_TIME;
dir->i_atime = CURRENT_TIME;
- dir->i_version = ++event;
+ dir->i_version = ++global_event;
MSDOS_I(dir)->i_last_pos = 0;
mark_inode_dirty(dir);
de->name[0] = DELETED_FLAG;
goto out;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
mark_inode_dirty(inode);
- inode->i_version = ++event;
- dir->i_version = event;
+ inode->i_version = ++global_event;
+ dir->i_version = global_event;
MSDOS_I(dir)->i_last_pos = 0;
dir->i_nlink++;
inode->i_nlink = 2; /* no need to mark them dirty */
if (res < 0) goto rename_done;
}
- new_dir->i_version = ++event;
+ new_dir->i_version = ++global_event;
MSDOS_I(new_dir)->i_last_pos = 0;
/* releases old_bh */
fat_attach(old_inode, sinfo.ino);
mark_inode_dirty(old_inode);
- old_dir->i_version = ++event;
+ old_dir->i_version = ++global_event;
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
mark_inode_dirty(old_dir);
if (new_inode) {
/* test whether root is busy without destroying dcache */
extern int is_root_busy(struct dentry *);
+/* test whether we have any submounts in a subdir tree */
+extern int have_submounts(struct dentry *);
+
/*
* This adds the entry to the hash queues.
*/
#ifndef _LINUX_ERRQUEUE_H
#define _LINUX_ERRQUEUE_H 1
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
struct sock_extended_err
{
#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
-#ifdef __KERNEL__
-/*
- * The ifaddr structure contains information about one address
- * of an interface. They are maintained by the different address
- * families, are allocated and attached when an address is set,
- * and are linked together so all addresses for an interface can
- * be located.
- */
-
-struct ifaddr
-{
- struct sockaddr ifa_addr; /* address of interface */
- union {
- struct sockaddr ifu_broadaddr;
- struct sockaddr ifu_dstaddr;
- } ifa_ifu;
- struct iface *ifa_ifp; /* back-pointer to interface */
- struct ifaddr *ifa_next; /* next address for interface */
-};
-
-#define ifa_broadaddr ifa_ifu.ifu_broadaddr /* broadcast address */
-#define ifa_dstaddr ifa_ifu.ifu_dstaddr /* other end of link */
-
-#endif /* __KERNEL__ */
-
/*
* Device mapping structure. I'd just gone off and designed a
* beautiful scheme using only loadable modules with arguments
int log_martians;
int forwarding;
int mc_forwarding;
+ int hidden;
void *sysctl;
};
#define IN_DEV_LOG_MARTIANS(in_dev) (ipv4_devconf.log_martians || (in_dev)->cnf.log_martians)
#define IN_DEV_PROXY_ARP(in_dev) (ipv4_devconf.proxy_arp || (in_dev)->cnf.proxy_arp)
+#define IN_DEV_HIDDEN(in_dev) ((in_dev)->cnf.hidden && ipv4_devconf.hidden)
#define IN_DEV_SHARED_MEDIA(in_dev) (ipv4_devconf.shared_media || (in_dev)->cnf.shared_media)
#define IN_DEV_TX_REDIRECTS(in_dev) (ipv4_devconf.send_redirects || (in_dev)->cnf.send_redirects)
#define IN_DEV_SEC_REDIRECTS(in_dev) (ipv4_devconf.secure_redirects || (in_dev)->cnf.secure_redirects)
#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+#ifdef __KERNEL__
+
struct ip_options {
__u32 faddr; /* Saved first hop address */
unsigned char optlen;
unsigned char __data[0];
};
-#ifdef __KERNEL__
#define optlength(opt) (sizeof(struct ip_options) + opt->optlen)
#endif
#ifndef _LINUX_NETDEVICE_H
#define _LINUX_NETDEVICE_H
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
#include <linux/if.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#ifndef __LINUX_RTNETLINK_H
#define __LINUX_RTNETLINK_H
+#ifdef __KERNEL__
#include <linux/config.h>
+#endif
#include <linux/netlink.h>
#define RTNL_DEBUG 1
#include <asm/param.h> /* for HZ */
-extern unsigned long event;
+extern unsigned long global_event;
#include <linux/binfmts.h>
#include <linux/personality.h>
__ptr = (struct cmsghdr*)(((unsigned char *) __cmsg) + CMSG_ALIGN(__cmsg->cmsg_len));
if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size)
- return NULL;
+ return (struct cmsghdr*)0;
return __ptr;
}
NET_IPV4_CONF_RP_FILTER=8,
NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE=9,
NET_IPV4_CONF_BOOTP_RELAY=10,
- NET_IPV4_CONF_LOG_MARTIANS=11
+ NET_IPV4_CONF_LOG_MARTIANS=11,
+ NET_IPV4_CONF_HIDDEN=12
};
/* /proc/sys/net/ipv6 */
EXPORT_SYMBOL(d_alloc);
EXPORT_SYMBOL(d_lookup);
EXPORT_SYMBOL(d_path);
+EXPORT_SYMBOL(have_submounts);
EXPORT_SYMBOL(__mark_inode_dirty);
EXPORT_SYMBOL(get_empty_filp);
EXPORT_SYMBOL(init_private_file);
EXPORT_SYMBOL(remove_inode_hash);
EXPORT_SYMBOL(make_bad_inode);
EXPORT_SYMBOL(is_bad_inode);
-EXPORT_SYMBOL(event);
+EXPORT_SYMBOL(global_event);
EXPORT_SYMBOL(__down);
EXPORT_SYMBOL(__down_interruptible);
EXPORT_SYMBOL(__down_trylock);
long time_adjust = 0;
long time_adjust_step = 0;
-unsigned long event = 0;
+unsigned long global_event = 0;
extern int do_setitimer(int, struct itimerval *, struct itimerval *);
unsigned int * prof_buffer = NULL;
* tables to the global page map.
*/
set_pte(page_table, pte_mkold(pte));
+ flush_tlb_page(vma, address);
set_bit(PG_referenced, &page_map->flags);
return 0;
}
* some real work in the future in "shrink_mmap()".
*/
if (!pte_dirty(pte)) {
+ flush_cache_page(vma, address);
pte_clear(page_table);
goto drop_pte;
}
newsk->sleep = &newsock->wait;
/* Now attach up the new socket */
- skb->destructor = NULL;
kfree_skb(skb);
sk->ack_backlog--;
newsock->sk = newsk;
*/
if(clone) {
struct sk_buff *skb2 = skb;
- skb = skb_clone(skb2, GFP_KERNEL);
+ skb = skb_clone(skb2, GFP_ATOMIC);
if (skb == NULL) {
return(0);
}
* clean up the APFDDI & gen. FDDI bits.
* Alexey Kuznetsov: new arp state machine;
* now it is in net/core/neighbour.c.
+ * Julian Anastasov: "hidden" flag: hide the
+ * interface and don't reply for it
*/
/* RFC1122 Status:
u32 saddr;
u8 *dst_ha = NULL;
struct device *dev = neigh->dev;
+ struct device *dev2;
+ struct in_device *in_dev2;
u32 target = *(u32*)neigh->primary_key;
int probes = neigh->probes;
- if (skb && inet_addr_type(skb->nh.iph->saddr) == RTN_LOCAL)
+ if (skb &&
+ (dev2 = ip_dev_find(skb->nh.iph->saddr)) != NULL &&
+ (in_dev2 = dev2->ip_ptr) != NULL &&
+ !IN_DEV_HIDDEN(in_dev2))
saddr = skb->nh.iph->saddr;
else
saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);
/* Special case: IPv4 duplicate address detection packet (RFC2131) */
if (sip == 0) {
+ struct device *dev2;
+ struct in_device *in_dev2;
+
if (arp->ar_op == __constant_htons(ARPOP_REQUEST) &&
- inet_addr_type(tip) == RTN_LOCAL)
+ (dev2 = ip_dev_find(tip)) != NULL &&
+ (dev2 == dev ||
+ ((in_dev2 = dev2->ip_ptr) != NULL &&
+ !IN_DEV_HIDDEN(in_dev2))))
arp_send(ARPOP_REPLY,ETH_P_ARP,tip,dev,tip,sha,dev->dev_addr,dev->dev_addr);
goto out;
}
if (addr_type == RTN_LOCAL) {
n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
if (n) {
+ if (ipv4_devconf.hidden &&
+ skb->pkt_type != PACKET_HOST) {
+ struct device *dev2;
+ struct in_device *in_dev2;
+
+ if ((dev2 = ip_dev_find(tip)) != NULL &&
+ dev2 != dev &&
+ (in_dev2 = dev2->ip_ptr) != NULL &&
+ IN_DEV_HIDDEN(in_dev2)) {
+ neigh_release(n);
+ goto out;
+ }
+ }
+
arp_send(ARPOP_REPLY,ETH_P_ARP,sip,dev,tip,sha,dev->dev_addr,sha);
neigh_release(n);
}
addr = ifa->ifa_local;
} endfor_ifa(in_dev);
- if (addr || scope >= RT_SCOPE_LINK)
+ if (addr)
return addr;
/* Not loopback addresses on loopback should be preferred
continue;
for_primary_ifa(in_dev) {
- if (ifa->ifa_scope <= scope)
+ if (!IN_DEV_HIDDEN(in_dev) &&
+ ifa->ifa_scope <= scope &&
+ ifa->ifa_scope != RT_SCOPE_LINK)
return ifa->ifa_local;
} endfor_ifa(in_dev);
}
static struct devinet_sysctl_table
{
struct ctl_table_header *sysctl_header;
- ctl_table devinet_vars[12];
+ ctl_table devinet_vars[13];
ctl_table devinet_dev[2];
ctl_table devinet_conf_dir[2];
ctl_table devinet_proto_dir[2];
{NET_IPV4_CONF_LOG_MARTIANS, "log_martians",
&ipv4_devconf.log_martians, sizeof(int), 0644, NULL,
&proc_dointvec},
+ {NET_IPV4_CONF_HIDDEN, "hidden",
+ &ipv4_devconf.hidden, sizeof(int), 0644, NULL,
+ &proc_dointvec},
{0}},
{{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, devinet_sysctl.devinet_vars},{0}},
*/
if (atomic_read(&ms->refcnt) == 1) {
kfree_s(ms,sizeof(*ms));
+ sysctl_ip_always_defrag--;
MOD_DEC_USE_COUNT;
goto masq_expire_out;
}
}
return -1;
}
+
+static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev)
+{
+ int err = -1;
+ struct inet6_ifaddr *ifp;
+
+ for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
+ if (ifp->scope == IFA_LINK && !(ifp->flags&(ADDR_STATUS|DAD_STATUS))) {
+ memcpy(eui, ifp->addr.s6_addr+8, 8);
+ err = 0;
+ break;
+ }
+ }
+ return err;
+}
#endif
/*
#ifdef CONFIG_IPV6_EUI64
if (pinfo->prefix_len == 64) {
memcpy(&addr, &pinfo->prefix, 8);
- if (ipv6_generate_eui64(addr.s6_addr + 8, dev))
+ if (ipv6_generate_eui64(addr.s6_addr + 8, dev) &&
+ ipv6_inherit_eui64(addr.s6_addr + 8, in6_dev))
return;
goto ok;
}
*/
if (ifp->idev->cnf.forwarding == 0 &&
- (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) == 0 &&
+ (dev->flags&IFF_LOOPBACK) == 0 &&
(ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) {
struct in6_addr all_routers;