S: D-71679 Asperg
S: Germany
+N: Kenn Humborg
+E: kenn@wombat.ie
+D: Mods to loop device to support sparse backing files
+S: Ballinagard
+S: Roscommon
+S: Ireland
+
N: Miguel de Icaza Amozurrutia
E: miguel@nuclecu.unam.mx
D: Linux/SPARC team, Midnight Commander maintainer
EXPORT_SYMBOL(_writel);
EXPORT_SYMBOL(_memcpy_fromio);
EXPORT_SYMBOL(_memcpy_toio);
-EXPORT_SYMBOL(_memset_io);
+EXPORT_SYMBOL(_memset_c_io);
EXPORT_SYMBOL(insb);
EXPORT_SYMBOL(insw);
EXPORT_SYMBOL(insl);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memset);
+EXPORT_SYMBOL(__memsetw);
EXPORT_SYMBOL(__constant_c_memset);
EXPORT_SYMBOL(dump_thread);
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/io.h>
-##include <asm/smp.h>
+#include <asm/smp.h>
/*
* BIOS32-style PCI interface:
#include "irq.h"
#undef DEBUG
+#define DEBUG
#ifdef DEBUG
#define DBG(x...) printk(x)
sigpending = 8
addr_limit = 12
exec_domain = 16
+need_resched = 20
ENOSYS = 38
andl SYMBOL_NAME(bh_active),%eax
jne handle_bottom_half
ret_with_reschedule:
- cmpl $0,SYMBOL_NAME(need_resched)
+ cmpl $0,need_resched(%ebx)
jne reschedule
cmpl $0,sigpending(%ebx)
jne signal_return
* volatile is justified in this case, it might change
* spontaneously, GCC should not cache it
*/
-#define IO_APIC_BASE ((volatile int *)0xfec00000)
+#define IO_APIC_BASE ((volatile int *)fix_to_virt(FIX_IO_APIC_BASE))
enum mp_irq_source_types {
mp_INT = 0,
Only manipulate interrupt enable flag on local CPU.
Allow enclosed uncachable regions.
v1.21
+ 19980611 Richard Gooch <rgooch@atnf.csiro.au>
+ Always define <main_lock>.
+ v1.22
*/
#include <linux/types.h>
#include <linux/errno.h>
#include <asm/atomic.h>
#include <linux/smp.h>
-#define MTRR_VERSION "1.21 (19980521)"
+#define MTRR_VERSION "1.22 (19980611)"
#define TRUE 1
#define FALSE 0
static unsigned int ascii_buf_bytes = 0;
#endif
static unsigned int *usage_table = NULL;
-#ifdef __SMP__
static spinlock_t main_lock = SPIN_LOCK_UNLOCKED;
-#endif
/* Private functions */
#ifdef CONFIG_PROC_FS
static void hard_idle(void)
{
- while (!need_resched) {
+ while (!current->need_resched) {
if (boot_cpu_data.hlt_works_ok && !hlt_counter) {
#ifdef CONFIG_APM
/* If the APM BIOS is not enabled, or there
need_resched again because an interrupt
may have occurred in apm_do_idle(). */
start_bh_atomic();
- if (!apm_do_idle() && !need_resched)
+ if (!apm_do_idle() && !current->need_resched)
__asm__("hlt");
end_bh_atomic();
#else
__asm__("hlt");
#endif
}
- if (need_resched)
+ if (current->need_resched)
break;
schedule();
}
if (jiffies - start_idle > HARD_IDLE_TIMEOUT)
hard_idle();
else {
- if (boot_cpu_data.hlt_works_ok && !hlt_counter && !need_resched)
+ if (boot_cpu_data.hlt_works_ok && !hlt_counter && !current->need_resched)
__asm__("hlt");
}
run_task_queue(&tq_scheduler);
- if (need_resched)
+ if (current->need_resched)
start_idle = 0;
schedule();
}
while(1)
{
if(current_cpu_data.hlt_works_ok &&
- !hlt_counter && !need_resched)
+ !hlt_counter && !current->need_resched)
__asm("hlt");
check_pgt_cache();
/*
void show_regs(struct pt_regs * regs)
{
+ long cr0 = 0L, cr2 = 0L, cr3 = 0L;
+
printk("\n");
printk("EIP: %04x:[<%08lx>]",0xffff & regs->xcs,regs->eip);
if (regs->xcs & 3)
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x\n",
0xffff & regs->xds,0xffff & regs->xes);
+ __asm__("movl %%cr0, %0": "=r" (cr0));
+ __asm__("movl %%cr2, %0": "=r" (cr2));
+ __asm__("movl %%cr3, %0": "=r" (cr3));
+ printk("CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3);
}
/*
int i;
for (i=0 ; i<8 ; i++)
- current->debugreg[i] = 0;
+ current->tss.debugreg[i] = 0;
/*
* Forget coprocessor state..
dump->u_dsize -= dump->u_tsize;
dump->u_ssize = 0;
for (i = 0; i < 8; i++)
- dump->u_debugreg[i] = current->debugreg[i];
+ dump->u_debugreg[i] = current->tss.debugreg[i];
if (dump->start_stack < TASK_SIZE)
dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
#define loaddebug(tsk,register) \
__asm__("movl %0,%%db" #register \
: /* no output */ \
- :"r" (tsk->debugreg[register]))
+ :"r" (tsk->tss.debugreg[register]))
/*
/*
* Now maybe reload the debug registers
*/
- if (next->debugreg[7]){
+ if (next->tss.debugreg[7]){
loaddebug(next,0);
loaddebug(next,1);
loaddebug(next,2);
addr <= (long) &dummy->u_debugreg[7]){
addr -= (long) &dummy->u_debugreg[0];
addr = addr >> 2;
- tmp = child->debugreg[addr];
+ tmp = child->tss.debugreg[addr];
};
ret = put_user(tmp,(unsigned long *) data);
goto out;
addr -= (long) &dummy->u_debugreg;
addr = addr >> 2;
- child->debugreg[addr] = data;
+ child->tss.debugreg[addr] = data;
ret = 0;
goto out;
};
udelay(100);
}
+__initfunc(unsigned long init_smp_mappings(unsigned long memory_start))
+{
+ unsigned long apic_phys, ioapic_phys;
+
+ if (smp_found_config) {
+ apic_phys = mp_lapic_addr;
+ ioapic_phys = mp_ioapic_addr;
+ } else {
+ /*
+ * set up a fake all zeroes page to simulate the
+ * local APIC and another one for the IO-APIC. We
+ * could use the real zero-page, but it's safer
+ * this way if some buggy code writes to this page ...
+ */
+ apic_phys = __pa(memory_start);
+ ioapic_phys = __pa(memory_start+PAGE_SIZE);
+ memset((void *)memory_start, 0, 2*PAGE_SIZE);
+ memory_start += 2*PAGE_SIZE;
+ }
+
+ set_fixmap(FIX_APIC_BASE,apic_phys);
+ set_fixmap(FIX_IO_APIC_BASE,ioapic_phys);
+
+ printk("mapped APIC to %08lx (%08lx)\n", APIC_BASE, apic_phys);
+ printk("mapped IOAPIC to %08lx (%08lx)\n", fix_to_virt(FIX_IO_APIC_BASE), ioapic_phys);
+
+ return memory_start;
+}
+
__initfunc(void smp_callin(void))
{
extern void calibrate_delay(void);
set_bit(cpuid, (unsigned long *)&cpu_callin_map[0]);
}
-static int cpucount = 0;
+int cpucount = 0;
extern int cpu_idle(void * unused);
setup_IO_APIC();
smp_done:
-#ifdef CONFIG_MTRR
- /* Must be done after other processors booted */
- mtrr_init ();
-#endif
}
* enabled in a civilised fashion. That will also boost performance.
*/
+unsigned int TIME64 (void)
+{
+ unsigned int dummy,low;
+
+ __asm__("rdtsc"
+ :"=a" (low),
+ "=d" (dummy));
+ return low;
+}
+
+int ipi_timestamp;
+
void smp_message_pass(int target, int msg, unsigned long data, int wait)
{
unsigned long cfg;
cpu_callin_map[0]=0;
}
else
- panic("huh?");
+ {
+ dest=0;
+ target_map=(1<<target);
+ cpu_callin_map[0]=0;
+ }
/*
* Program the APIC to deliver the IPI
/* printk("SMID\n");*/
}
+
+void smp_send_reschedule(int cpu)
+{
+ unsigned long flags;
+
+ __save_flags(flags);
+ __cli();
+ smp_message_pass(MSG_ALL_BUT_SELF, MSG_RESCHEDULE, 0L, 2);
+ __restore_flags(flags);
+}
+
/*
* Local timer interrupt handler. It does both profiling and
* process statistics/rescheduling.
p->counter -= 1;
if (p->counter < 0) {
p->counter = 0;
- need_resched = 1;
+ p->need_resched = 1;
}
if (p->priority < DEF_PRIORITY) {
kstat.cpu_nice += user;
}
/*
- * Reschedule call back (not used currently)
+ * Reschedule call back
*/
-
asmlinkage void smp_reschedule_interrupt(void)
{
- int cpu = smp_processor_id();
-
ack_APIC_irq();
- /*
- * This looks silly, but we actually do need to wait
- * for the global interrupt lock.
- */
- irq_enter(cpu, 0);
- need_resched = 1;
- irq_exit(cpu, 0);
}
/*
RE_ENTRANT_CHECK_ON;
#endif DEBUG
- if (FPU_lookahead && !need_resched)
+ if (FPU_lookahead && !current->need_resched)
{
FPU_ORIG_EIP = FPU_EIP - code_base;
if ( valid_prefix(&byte1, (u_char **)&FPU_EIP,
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/dma.h>
+#include <asm/fixmap.h>
extern void show_net_buffers(void);
+extern unsigned long init_smp_mappings(unsigned long);
void __bad_pte_kernel(pmd_t *pmd)
{
:"ax");
}
+/*
+ * allocate page table(s) for compile-time fixed mappings
+ */
+static unsigned long fixmap_init (unsigned long start_mem)
+{
+ pgd_t * pg_dir;
+ unsigned int idx;
+ unsigned long address;
+
+ start_mem &= PAGE_MASK;
+
+ for (idx=1; idx < __end_of_fixed_addresses; idx += PTRS_PER_PTE)
+ {
+ address = fix_to_virt(__end_of_fixed_addresses-idx);
+ pg_dir = swapper_pg_dir + (address >> PGDIR_SHIFT);
+ memset((void *)start_mem, 0, PAGE_SIZE);
+ pgd_val(*pg_dir) = _PAGE_TABLE | __pa(start_mem);
+ start_mem += PAGE_SIZE;
+ }
+
+ return start_mem;
+}
+
+static void set_pte_phys (unsigned long vaddr, unsigned long phys)
+{
+ pgprot_t prot;
+ pte_t * pte;
+
+ pte = pte_offset(pmd_offset(pgd_offset_k(vaddr), vaddr), vaddr);
+ prot = PAGE_KERNEL;
+ if (boot_cpu_data.x86_capability & X86_FEATURE_PGE)
+ pgprot_val(prot) |= _PAGE_GLOBAL;
+ set_pte(pte, mk_pte_phys(phys, prot));
+
+ local_flush_tlb();
+}
+
+void set_fixmap (enum fixed_addresses idx, unsigned long phys)
+{
+ unsigned long address = fix_to_virt(idx);
+
+ set_pte_phys (address,phys);
+}
+
/*
* paging_init() sets up the page tables - note that the first 4MB are
* already mapped by head.S.
address += PAGE_SIZE;
}
}
+ start_mem = fixmap_init(start_mem);
#ifdef __SMP__
-{
- extern unsigned long mp_lapic_addr;
- extern unsigned long mp_ioapic_addr;
- pte_t pte;
- unsigned long apic_area = (unsigned long)APIC_BASE;
-
- pg_dir = swapper_pg_dir + ((apic_area) >> PGDIR_SHIFT);
- memset((void *)start_mem, 0, PAGE_SIZE);
- pgd_val(*pg_dir) = _PAGE_TABLE | __pa(start_mem);
- start_mem += PAGE_SIZE;
-
- if (smp_found_config) {
- /*
- * Map the local APIC to FEE00000. (it's only the default
- * value, thanks to Steve Hsieh for finding this out. We
- * now save the real local-APIC physical address in smp_scan(),
- * and use it here)
- */
- pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
- pte = mk_pte_phys(mp_lapic_addr, PAGE_KERNEL);
- set_pte(pg_table, pte);
-
- /*
- * Map the IO-APIC to FEC00000.
- */
- apic_area = 0xFEC00000; /*(unsigned long)IO_APIC_BASE;*/
- pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
- pte = mk_pte_phys(mp_ioapic_addr, PAGE_KERNEL);
- set_pte(pg_table, pte);
- } else {
- /*
- * No local APIC but we are compiled SMP ... set up a
- * fake all zeroes page to simulate the local APIC.
- */
- pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
- pte = mk_pte(start_mem, PAGE_KERNEL);
- memset((void *)start_mem, 0, PAGE_SIZE);
- start_mem += PAGE_SIZE;
- set_pte(pg_table, pte);
-
- /*
- * Do the same for the IO-APIC
- */
- apic_area = 0xFEC00000;
- pg_table = pte_offset((pmd_t *)pg_dir, apic_area);
- pte = mk_pte(start_mem, PAGE_KERNEL);
- memset((void *)start_mem, 0, PAGE_SIZE);
- start_mem += PAGE_SIZE;
- set_pte(pg_table, pte);
- }
-
- local_flush_tlb();
-}
+ start_mem = init_smp_mappings(start_mem);
#endif
local_flush_tlb();
* Adapted for 1.3.59 kernel - Andries Brouwer, 1 Feb 1996
*
* Fixed do_loop_request() re-entrancy - <Vincent.Renardias@waw.com> Mar 20, 1997
+ *
+ * Handle sparse backing files correctly - Kenn Humborg, Jun 28, 1998
*/
#include <linux/module.h>
static int loop_sizes[MAX_LOOP];
static int loop_blksizes[MAX_LOOP];
+#define FALSE 0
+#define TRUE (!FALSE)
+
+/* Forward declaration of function to create missing blocks in the
+ backing file (can happen if the backing file is sparse) */
+static int create_missing_block(struct loop_device *lo, int block, int blksize);
+
+
/*
* Transfer functions
*/
struct loop_device *lo;
struct buffer_head *bh;
struct request *current_request;
+ int block_present;
repeat:
INIT_REQUEST;
if (lo->lo_flags & LO_FLAGS_READ_ONLY)
goto error_out;
} else if (current_request->cmd != READ) {
- printk("unknown loop device command (%d)?!?", current_request->cmd);
+ printk(KERN_ERR "unknown loop device command (%d)?!?", current_request->cmd);
goto error_out;
}
spin_unlock_irq(&io_request_lock);
while (len > 0) {
+
+ size = blksize - offset;
+ if (size > len)
+ size = len;
+
real_block = block;
+ block_present = TRUE;
+
if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
real_block = bmap(lo->lo_dentry->d_inode, block);
if (!real_block) {
- printk("loop: block %d not present\n", block);
- goto error_out_lock;
+
+ /* The backing file is a sparse file and this block
+ doesn't exist. If reading, return zeros. If
+ writing, force the underlying FS to create
+ the block */
+ if (current_request->cmd == READ) {
+ memset(dest_addr, 0, size);
+ block_present = FALSE;
+ } else {
+ if (!create_missing_block(lo, block, blksize)) {
+ goto error_out_lock;
+ }
+ }
+
}
}
- bh = getblk(lo->lo_device, real_block, blksize);
- if (!bh) {
- printk("loop: device %s: getblk(-, %d, %d) returned NULL",
- kdevname(lo->lo_device),
- block, blksize);
- goto error_out_lock;
- }
- if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
- (offset || (len < blksize)))) {
- ll_rw_block(READ, 1, &bh);
- wait_on_buffer(bh);
- if (!buffer_uptodate(bh)) {
+
+ if (block_present) {
+ bh = getblk(lo->lo_device, real_block, blksize);
+ if (!bh) {
+ printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL",
+ kdevname(lo->lo_device),
+ block, blksize);
+ goto error_out_lock;
+ }
+ if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
+ (offset || (len < blksize)))) {
+ ll_rw_block(READ, 1, &bh);
+ wait_on_buffer(bh);
+ if (!buffer_uptodate(bh)) {
+ brelse(bh);
+ goto error_out_lock;
+ }
+ }
+
+ if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
+ dest_addr, size)) {
+ printk(KERN_ERR "loop: transfer error block %d\n", block);
brelse(bh);
goto error_out_lock;
}
- }
- size = blksize - offset;
- if (size > len)
- size = len;
-
- if ((lo->transfer)(lo, current_request->cmd, bh->b_data + offset,
- dest_addr, size)) {
- printk("loop: transfer error block %d\n", block);
+
+ if (current_request->cmd == WRITE) {
+ mark_buffer_uptodate(bh, 1);
+ mark_buffer_dirty(bh, 1);
+ }
brelse(bh);
- goto error_out_lock;
- }
- if (current_request->cmd == WRITE) {
- mark_buffer_uptodate(bh, 1);
- mark_buffer_dirty(bh, 1);
}
- brelse(bh);
dest_addr += size;
len -= size;
offset = 0;
goto repeat;
}
+static int create_missing_block(struct loop_device *lo, int block, int blksize)
+{
+ struct file *file;
+ loff_t new_offset;
+ char zero_buf[1] = { 0 };
+ ssize_t retval;
+ mm_segment_t old_fs;
+
+ file = lo->lo_backing_file;
+ if (file == NULL) {
+ printk(KERN_WARNING "loop: cannot create block - no backing file\n");
+ return FALSE;
+ }
+
+ if (file->f_op == NULL) {
+ printk(KERN_WARNING "loop: cannot create block - no file ops\n");
+ return FALSE;
+ }
+
+ new_offset = block * blksize;
+
+ if (file->f_op->llseek != NULL) {
+ file->f_op->llseek(file, new_offset, 0);
+ } else {
+ /* Do what the default llseek() code would have done */
+ file->f_pos = new_offset;
+ file->f_reada = 0;
+ file->f_version = ++event;
+ }
+
+ if (file->f_op->write == NULL) {
+ printk(KERN_WARNING "loop: cannot create block - no write file op\n");
+ return FALSE;
+ }
+
+ old_fs = get_fs();
+ set_fs(get_ds());
+
+ retval = file->f_op->write(file, zero_buf, 1, &file->f_pos);
+
+ set_fs(old_fs);
+
+ if (retval < 0) {
+ printk(KERN_WARNING "loop: cannot create block - FS write failed: code %d\n",
+ retval);
+ return FALSE;
+ } else {
+ return TRUE;
+ }
+}
+
static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
{
struct file *file;
error = -EINVAL;
inode = file->f_dentry->d_inode;
if (!inode) {
- printk("loop_set_fd: NULL inode?!?\n");
+ printk(KERN_ERR "loop_set_fd: NULL inode?!?\n");
goto out_putf;
}
error = blkdev_open(inode, file);
lo->lo_device = inode->i_rdev;
lo->lo_flags = 0;
+
+ /* Backed by a block device - don't need to hold onto
+ a file structure */
+ lo->lo_backing_file = NULL;
} else if (S_ISREG(inode->i_mode)) {
+
+ /* Backed by a regular file - we need to hold onto
+ a file structure for this file. We'll use it to
+ write to blocks that are not already present in
+ a sparse file. We create a new file structure
+ based on the one passed to us via 'arg'. This is
+ to avoid changing the file structure that the
+ caller is using */
+
lo->lo_device = inode->i_dev;
lo->lo_flags = LO_FLAGS_DO_BMAP;
- error = 0;
+
+ error = -ENFILE;
+ lo->lo_backing_file = get_empty_filp();
+ if (lo->lo_backing_file) {
+ lo->lo_backing_file->f_mode = file->f_mode;
+ lo->lo_backing_file->f_pos = file->f_pos;
+ lo->lo_backing_file->f_flags = file->f_flags;
+ lo->lo_backing_file->f_owner = file->f_owner;
+ lo->lo_backing_file->f_dentry = file->f_dentry;
+ lo->lo_backing_file->f_op = file->f_op;
+ lo->lo_backing_file->private_data = file->private_data;
+
+ error = 0;
+ }
}
if (error)
goto out_putf;
if (S_ISBLK(dentry->d_inode->i_mode))
blkdev_release (dentry->d_inode);
lo->lo_dentry = NULL;
- dput(dentry);
+
+ if (lo->lo_backing_file != NULL) {
+ fput(lo->lo_backing_file);
+ lo->lo_backing_file = NULL;
+ } else {
+ dput(dentry);
+ }
+
lo->lo_device = 0;
lo->lo_encrypt_type = 0;
lo->lo_offset = 0;
if (!inode)
return -EINVAL;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_ioctl: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_ioctl: pseudo-major != %d\n", MAJOR_NR);
return -ENODEV;
}
dev = MINOR(inode->i_rdev);
if (!inode)
return -EINVAL;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_open: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_open: pseudo-major != %d\n", MAJOR_NR);
return -ENODEV;
}
dev = MINOR(inode->i_rdev);
if (!inode)
return 0;
if (MAJOR(inode->i_rdev) != MAJOR_NR) {
- printk("lo_release: pseudo-major != %d\n", MAJOR_NR);
+ printk(KERN_WARNING "lo_release: pseudo-major != %d\n", MAJOR_NR);
return 0;
}
dev = MINOR(inode->i_rdev);
fsync_dev(inode->i_rdev);
lo = &loop_dev[dev];
if (lo->lo_refcnt <= 0)
- printk("lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
+ printk(KERN_ERR "lo_release: refcount(%d) <= 0\n", lo->lo_refcnt);
else {
lo->lo_refcnt--;
MOD_DEC_USE_COUNT;
int i;
if (register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
- printk("Unable to get major number %d for loop device\n",
+ printk(KERN_WARNING "Unable to get major number %d for loop device\n",
MAJOR_NR);
return -EIO;
}
#ifndef MODULE
- printk("loop: registered device at major %d\n", MAJOR_NR);
+ printk(KERN_INFO "loop: registered device at major %d\n", MAJOR_NR);
#ifdef DES_AVAILABLE
- printk("loop: DES encryption available\n");
+ printk(KERN_INFO "loop: DES encryption available\n");
#endif
#ifdef IDEA_AVAILABLE
- printk("loop: IDEA encryption available\n");
+ printk(KERN_INFO "loop: IDEA encryption available\n");
#endif
#endif
void
cleanup_module( void ) {
if (unregister_blkdev(MAJOR_NR, "loop") != 0)
- printk("loop: cleanup_module failed\n");
+ printk(KERN_WARNING "loop: cleanup_module failed\n");
}
#endif
wantlen -= t;
if (t < s)
break;
- if (need_resched)
+ if (current->need_resched)
schedule();
}
int l;
do {
l = qcam_read_bytes(q, tmpbuf, 3);
- if (need_resched)
+ if (current->need_resched)
schedule();
} while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e));
if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf)
int l;
do {
l = qcam_read_bytes(q, tmpbuf, 1);
- if (need_resched)
+ if (current->need_resched)
schedule();
} while (l && tmpbuf[0] == 0x7e);
l = qcam_read_bytes(q, tmpbuf+1, 2);
{
if (!parport_yield_blocking (lp_table[minor].dev))
{
- if (need_resched)
+ if (current->need_resched)
schedule ();
} else
lp_table[minor].irq_missed = 1;
status = (r_str(minor) & 0x40);
udelay(50);
counter++;
- if (need_resched)
+ if (current->need_resched)
schedule ();
} while ((status == 0x40) && (counter < 20));
if (counter == 20) {
status=(r_str(minor) & 0x40);
udelay(20);
counter++;
- if (need_resched)
+ if (current->need_resched)
schedule ();
} while ( (status == 0) && (counter < 20) );
if (counter == 20) { /* Timeout */
do {
count ++;
- if(need_resched)
+ if (current->need_resched)
schedule();
} while (lp_table[dev]->lp_is_busy(dev) && count < lp_table[dev]->chars);
*/
static inline size_t read_zero_pagealigned(char * buf, size_t size)
{
+ struct mm_struct *mm;
struct vm_area_struct * vma;
unsigned long addr=(unsigned long)buf;
+ mm = current->mm;
/* Oops, this was forgotten before. -ben */
- down(¤t->mm->mmap_sem);
+ down(&mm->mmap_sem);
/* For private mappings, just map in zero pages. */
- for (vma = find_vma(current->mm, addr); vma; vma = vma->vm_next) {
+ for (vma = find_vma(mm, addr); vma; vma = vma->vm_next) {
unsigned long count;
if (vma->vm_start > addr || (vma->vm_flags & VM_WRITE) == 0)
if (count > size)
count = size;
- flush_cache_range(current->mm, addr, addr + count);
- zap_page_range(vma, addr, count);
- zeromap_page_range(vma, addr, count, PAGE_COPY);
- flush_tlb_range(current->mm, addr, addr + count);
+ flush_cache_range(mm, addr, addr + count);
+ zap_page_range(mm, addr, count);
+ zeromap_page_range(addr, count, PAGE_COPY);
+ flush_tlb_range(mm, addr, addr + count);
size -= count;
buf += count;
goto out_up;
}
- up(¤t->mm->mmap_sem);
+ up(&mm->mmap_sem);
/* The shared case is hard. Let's do the conventional zeroing. */
do {
unsigned long unwritten = clear_user(buf, PAGE_SIZE);
if (unwritten)
return size + unwritten - PAGE_SIZE;
- if (need_resched)
+ if (current->need_resched)
schedule();
buf += PAGE_SIZE;
size -= PAGE_SIZE;
return size;
out_up:
- up(¤t->mm->mmap_sem);
+ up(&mm->mmap_sem);
return size;
}
nbytes -= i;
buf += i;
add_timer_randomness(r, &extract_timer_state, nbytes);
- if (to_user && need_resched)
+ if (to_user && current->need_resched)
schedule();
}
#define RESCHED \
do { \
- if (need_resched) \
+ if (current->need_resched) \
schedule(); \
} while (0)
ret = -ERESTARTSYS;
if (signal_pending(current))
break;
- if (need_resched)
+ if (current->need_resched)
schedule();
}
if (written) {
if ((status & mask) == result)
return 0;
udelay(25);
- if (need_resched)
+ if (current->need_resched)
schedule();
}
current->state = TASK_INTERRUPTIBLE;
"%s not found in port list!\n", port->name);
}
spin_unlock_irqrestore (&parportlist_lock, flags);
- if (p->probe_info.class_name)
- kfree (p->probe_info.class_name);
- if (p->probe_info.mfr)
- kfree (p->probe_info.mfr);
- if (p->probe_info.model)
- kfree (p->probe_info.model);
- if (p->probe_info.cmdset)
- kfree (p->probe_info.cmdset);
- if (p->probe_info.description)
- kfree (p->probe_info.description);
+ if (port->probe_info.class_name)
+ kfree (port->probe_info.class_name);
+ if (port->probe_info.mfr)
+ kfree (port->probe_info.mfr);
+ if (port->probe_info.model)
+ kfree (port->probe_info.model);
+ if (port->probe_info.cmdset)
+ kfree (port->probe_info.cmdset);
+ if (port->probe_info.description)
+ kfree (port->probe_info.description);
kfree(port->name);
kfree(port);
}
MODULE_PARM(irq,"1-8i");
MODULE_PARM(xcvr,"1-8i");
+MODULE_PARM(debug,"i");
+MODULE_PARM(irq,"1-8i");
+MODULE_PARM(xcvr,"1-8i");
+
int
init_module(void)
{
/* EtherLinkXL.c: A 3Com EtherLink PCI III/XL ethernet driver for linux. */
/*
- Written 1996-1997 by Donald Becker.
+ Written 1996-1998 by Donald Becker.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
*/
static char *version =
-"3c59x.c:v0.47H 12/4/97 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
+"3c59x.c:v0.99E 5/12/98 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/vortex.html\n";
/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
Setting to > 1512 effectively disables this feature. */
-static const int rx_copybreak = 200;
+static const rx_copybreak = 200;
/* Allow setting MTU to a larger size, bypassing the normal ethernet setup. */
-static const int mtu = 1500;
+static const mtu = 1500;
/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
static int max_interrupt_work = 20;
+/* Put out somewhat more debugging messages. (0: no msg, 1 minimal .. 6). */
+#ifdef VORTEX_DEBUG
+static int vortex_debug = VORTEX_DEBUG;
+#else
+static int vortex_debug = 1;
+#endif
+
+/* Some values here only for performance evaluation and path-coverage
+ debugging. */
+static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0, rx_csumhits;
+
/* Enable the automatic media selection code -- usually set. */
#define AUTOMEDIA 1
#define RX_RING_SIZE 32
#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+#include <linux/config.h>
#ifdef MODULE
#ifdef MODVERSIONS
#include <linux/modversions.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/bios32.h>
#include <linux/timer.h>
#include <asm/irq.h> /* For NR_IRQS only. */
#include <asm/bitops.h>
#if LINUX_VERSION_CODE < 0x10300
#define RUN_AT(x) (x) /* What to put in timer->expires. */
#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
-#if defined(__alpha__)
+#if defined(__alpha)
#error "The Alpha architecture is only support with kernel version 2.0."
#endif
#define virt_to_bus(addr) ((unsigned long)addr)
#define RUN_AT(x) (jiffies + (x))
#define DEV_ALLOC_SKB(len) dev_alloc_skb(len)
#endif
+#if LINUX_VERSION_CODE < 0x20159
+#define DEV_FREE_SKB(skb) dev_kfree_skb (skb, FREE_WRITE);
+#else /* Grrr, unneeded incompatible change. */
+#define DEV_FREE_SKB(skb) dev_kfree_skb(skb);
+#endif
#ifdef SA_SHIRQ
#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev)
#define udelay(microsec) do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
#endif
-#if LINUX_VERSION_CODE < 0x20115
+#if LINUX_VERSION_CODE < 0x20138
#define test_and_set_bit(val, addr) set_bit(val, addr)
-#include <linux/bios32.h>
-#elif defined(MODULE)
+#endif
+#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115)
MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
MODULE_DESCRIPTION("3Com 3c590/3c900 series Vortex/Boomerang driver");
MODULE_PARM(debug, "i");
MODULE_PARM(compaq_prod_id, "i");
#endif
-/* "Knobs" for adjusting internal parameters. */
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define VORTEX_DEBUG 1
-/* Some values here only for performance evaluation and path-coverage
- debugging. */
-static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;
-
/* Operational parameter that usually are not changed. */
/* The Vortex size is twice that of the original EtherLinkIII series: the
{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
#endif
-#ifdef VORTEX_DEBUG
-static int vortex_debug = VORTEX_DEBUG;
-#else
-static int vortex_debug = 1;
-#endif
-
-/* Set iff a MII transceiver on any interface requires mdio preamble. */
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+ This only set with the original DP83840 on older 3c905 boards, so the extra
+ code size of a per-interface flag is not worthwhile. */
static char mii_preamble_required = 0;
-/* Caution! These entries must be consistent, with the EISA ones last. */
+/* Caution! These entries must be consistent. */
static const int product_ids[] = {
- 0x5900, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001, 0x9050, 0x9051, 0x9055,
- 0, 0};
+ 0x5900, 0x5920, 0x5970, 0x5950, 0x5951, 0x5952, 0x9000, 0x9001,
+ 0x9050, 0x9051, 0x9055, 0x5057, 0 };
static const char *product_names[] = {
"3c590 Vortex 10Mbps",
+ "3c592 EISA 10mbps Demon/Vortex",
+ "3c597 EISA Fast Demon/Vortex",
"3c595 Vortex 100baseTX",
"3c595 Vortex 100baseT4",
"3c595 Vortex 100base-MII",
"3c905 Boomerang 100baseTx",
"3c905 Boomerang 100baseT4",
"3c905B Cyclone 100baseTx",
- "3c592 EISA 10mbps Demon/Vortex",
- "3c597 EISA Fast Demon/Vortex",
+ "3c575", /* Cardbus Boomerang */
};
-#define DEMON10_INDEX 9
-#define DEMON100_INDEX 10
/*
Theory of Operation
versions of the FastEtherLink cards. The supported product IDs are
3c590, 3c592, 3c595, 3c597, 3c900, 3c905
-The ISA 3c515 is supported with a separate driver, 3c515.c, included with
+The ISA 3c515 is supported with a seperate driver, 3c515.c, included with
the kernel source or available from
cesdis.gsfc.nasa.gov:/pub/linux/drivers/3c515.html
series. The primary interface is two programmed-I/O FIFOs, with an
alternate single-contiguous-region bus-master transfer (see next).
-The 3c900 "Boomerang" series uses a full-bus-master interface with separate
+The 3c900 "Boomerang" series uses a full-bus-master interface with seperate
lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
DEC Tulip and Intel Speedo3. The first chip version retains a compatible
programmed-I/O interface that will be removed in the 'B' and subsequent
/* Bits in the general status register. */
enum vortex_status {
- IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ IntLatch = 0x0001, HostError = 0x0002, TxComplete = 0x0004,
TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
IntReq = 0x0040, StatsFull = 0x0080,
DMADone = 1<<8, DownComplete = 1<<9, UpComplete = 1<<10,
struct w3_config_fields {
unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
int pad8:8;
- unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+ unsigned int ram_split:2, pad18:2, xcvr:4, autoselect:1;
int pad24:7;
} u;
};
struct boom_rx_desc {
u32 next; /* Last entry points to 0. */
s32 status;
- u32 addr; /* Up to addr/len possible.. */
- s32 length; /* set high bit to indicate last pair. */
+ u32 addr; /* Up to 63 addr/len pairs possible. */
+ s32 length; /* Set LAST_FRAG to indicate last pair. */
};
/* Values for the Rx status entry. */
enum rx_desc_status {
RxDComplete=0x00008000, RxDError=0x4000,
/* See boomerang_rx() for actual error bits */
+ IPChksumErr=1<<25, TCPChksumErr=1<<26, UDPChksumErr=1<<27,
+ IPChksumValid=1<<29, TCPChksumValid=1<<30, UDPChksumValid=1<<31,
};
struct boom_tx_desc {
/* Values for the Tx status entry. */
enum tx_desc_status {
CRCDisable=0x2000, TxDComplete=0x8000,
+ AddIPChksum=0x02000000, AddTCPChksum=0x04000000, AddUDPChksum=0x08000000,
TxIntrUploaded=0x80000000, /* IRQ when in FIFO, but maybe not sent. */
};
+/* Chip features we care about in vp->capabilities, read from the EEPROM. */
+enum ChipCaps { CapBusMaster=0x20 };
+
struct vortex_private {
char devname[8]; /* "ethN" string, also for kernel debug. */
const char *product_name;
unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
struct enet_statistics stats;
struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+
+ /* PCI configuration space information. */
+ u8 pci_bus, pci_dev_fn; /* PCI bus location, for power management. */
+ u16 pci_device_id;
+
+ /* The remainder are related to chip state, mostly media selection. */
+ int in_interrupt;
struct timer_list timer; /* Media selection timer. */
int options; /* User-settable misc. driver options. */
- int last_rx_packets; /* For media autoselection. */
- unsigned int available_media:8, /* From Wn3_Options */
+ unsigned int
media_override:3, /* Passed-in media type. */
- default_media:3, /* Read from the EEPROM. */
+ default_media:3, /* Read from the EEPROM/Wn3_Config. */
full_duplex:1, autoselect:1,
bus_master:1, /* Vortex can only do a fragment bus-m. */
full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
+ hw_csums:1, /* Has hardware checksums. */
tx_full:1;
- u16 capabilities; /* Adapter capabilities word. */
- u16 info1, info2; /* Software information information. */
- unsigned char phys[2]; /* MII device addresses. */
+ u16 status_enable;
+ u16 available_media; /* From Wn3_Options. */
+ u16 capabilities, info1, info2; /* Various, from EEPROM. */
+ u16 advertising; /* NWay media advertisement */
+ unsigned char phys[2]; /* MII device addresses. */
};
/* The action to take with a media selection timer tick.
*/
enum xcvr_types {
XCVR_10baseT=0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
- XCVR_100baseFx, XCVR_MII=6, XCVR_Default=8,
+ XCVR_100baseFx, XCVR_MII=6, XCVR_NWAY=8, XCVR_ExtMII=9, XCVR_Default=10,
};
static struct media_table {
- char *name;
- unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
- mask:8, /* The transceiver-present bit in Wn3_Config.*/
- next:8; /* The media type to try next. */
- short wait; /* Time before we check media status. */
+ char *name;
+ unsigned int media_bits:16, /* Bits to set in Wn4_Media register. */
+ mask:8, /* The transceiver-present bit in Wn3_Config.*/
+ next:8; /* The media type to try next. */
+ int wait; /* Time before we check media status. */
} media_tbl[] = {
{ "10baseT", Media_10TP,0x08, XCVR_10base2, (14*HZ)/10},
{ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1*HZ)/10},
{ "10base2", 0, 0x10, XCVR_AUI, (1*HZ)/10},
{ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14*HZ)/10},
{ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14*HZ)/10},
- { "MII", 0, 0x40, XCVR_10baseT, 3*HZ },
+ { "MII", 0, 0x41, XCVR_10baseT, 3*HZ },
{ "undefined", 0, 0x01, XCVR_10baseT, 10000},
+ { "Autonegotiate", 0, 0x41, XCVR_10baseT, 3*HZ},
+ { "MII-External", 0, 0x41, XCVR_10baseT, 3*HZ },
{ "Default", 0, 0xFF, XCVR_10baseT, 10000},
};
static int vortex_scan(struct device *dev);
static struct device *vortex_found_device(struct device *dev, int ioaddr,
- int irq, const char *product_name,
+ int irq, int device_id,
int options, int card_idx);
static int vortex_probe1(struct device *dev);
static int vortex_open(struct device *dev);
/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
/* Note: this is the only limit on the number of cards supported!! */
static int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
-#ifdef MODULE
static int full_duplex[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
/* A list of all installed Vortex devices, for removing the driver module. */
static struct device *root_vortex_dev = NULL;
-#endif
#ifdef MODULE
/* Variables to work-around the Compaq PCI BIOS32 problem. */
-static int compaq_ioaddr = 0, compaq_irq = 0;
+static int compaq_ioaddr = 0, compaq_irq = 0, compaq_device_id = 0x5900;
static int debug = -1;
+#ifdef CARDBUS
+
+#include <pcmcia/driver_ops.h>
+
+static dev_node_t *vortex_attach(dev_locator_t *loc)
+{
+ u16 dev_id;
+ u32 io;
+ u8 bus, devfn, irq;
+ struct device *dev;
+
+ if (loc->bus != LOC_PCI) return NULL;
+ bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
+ printk(KERN_INFO "vortex_attach(bus %d, function %d)\n", bus, devfn);
+ pcibios_read_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, &io);
+ pcibios_read_config_byte(bus, devfn, PCI_INTERRUPT_LINE, &irq);
+ pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &dev_id);
+ io &= ~3;
+ dev = vortex_found_device(NULL, io, irq, dev_id, 0, -1);
+ if (dev) {
+ dev_node_t *node = kmalloc(sizeof(dev_node_t), GFP_KERNEL);
+ strcpy(node->dev_name, dev->name);
+ node->major = node->minor = 0;
+ node->next = NULL;
+ MOD_INC_USE_COUNT;
+ return node;
+ }
+ return NULL;
+}
+
+static void vortex_detach(dev_node_t *node)
+{
+ struct device **devp, **next;
+ printk(KERN_INFO "vortex_detach(%s)\n", node->dev_name);
+ for (devp = &root_vortex_dev; *devp; devp = next) {
+ next = &((struct vortex_private *)(*devp)->priv)->next_module;
+ if (strcmp((*devp)->name, node->dev_name) == 0) break;
+ }
+ if (*devp) {
+ struct device *dev = *devp;
+ if (dev->flags & IFF_UP)
+ vortex_close(dev);
+ dev->flags &= ~(IFF_UP|IFF_RUNNING);
+ unregister_netdev(dev);
+ kfree(dev);
+ *devp = *next;
+ kfree(node);
+ MOD_DEC_USE_COUNT;
+ }
+}
+
+struct driver_operations vortex_ops = {
+ "3c59x_cb", vortex_attach, NULL, NULL, vortex_detach
+};
+
+#endif /* Cardbus support */
+
+
int
init_module(void)
{
- int cards_found;
-
if (debug >= 0)
vortex_debug = debug;
if (vortex_debug)
printk(version);
root_vortex_dev = NULL;
- cards_found = vortex_scan(0);
- return cards_found ? 0 : -ENODEV;
+#ifdef CARDBUS
+ register_driver(&vortex_ops);
+ return 0;
+#else
+ {
+ int cards_found = vortex_scan(0);
+ if (cards_found == 0)
+ printk("No 3Com Vortex/Boomerang cards found.\n");
+ return cards_found ? 0 : -ENODEV;
+ }
+#endif
}
#else
static int vortex_scan(struct device *dev)
{
int cards_found = 0;
- const char *product_name;
-#ifndef NO_PCI /* Allow an EISA-only driver. */
+ /* Allow an EISA-only driver. */
+#if defined(CONFIG_PCI) || (defined(MODULE) && !defined(NO_PCI))
/* Ideally we would detect all cards in slot order. That would
be best done a central PCI probe dispatch, which wouldn't work
well with the current structure. So instead we detect 3Com cards
in slot order. */
- if (pci_present()) {
+ if (pcibios_present()) {
static int pci_index = 0;
unsigned char pci_bus, pci_device_fn;
for (;pci_index < 0xff; pci_index++) {
-#if LINUX_VERSION_CODE >= 0x20155
- unsigned int pci_irq_line;
- struct pci_dev *pdev;
-#else
- unsigned char pci_irq_line;
-#endif
- unsigned char pci_latency;
- unsigned short pci_command, new_command, vendor, device;
- unsigned int pci_ioaddr;
- int board_index = 0;
+ u8 pci_latency;
+ u16 pci_command, new_command, vendor, device;
+ int irq;
+ long ioaddr;
if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
pci_index, &pci_bus, &pci_device_fn)
PCI_VENDOR_ID, &vendor);
pcibios_read_config_word(pci_bus, pci_device_fn,
PCI_DEVICE_ID, &device);
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ {
#if LINUX_VERSION_CODE >= 0x20155
- pdev = pci_find_slot(pci_bus, pci_device_fn);
- pci_irq_line = pdev->irq;
- pci_ioaddr = pdev->base_address[0];
+ struct pci_dev *pdev = pci_find_slot(pci_bus, pci_device_fn);
+ ioaddr = pdev->base_address[0];
+ irq = pdev->irq;
#else
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq_line);
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ u32 pci_ioaddr;
+ u8 pci_irq_line;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ ioaddr = pci_ioaddr;
+ irq = pci_irq_line;
#endif
- pcibios_read_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command);
+ }
/* Remove I/O space marker in bit 0. */
- pci_ioaddr &= ~3;
+ ioaddr &= ~3;
if (vendor != TCOM_VENDOR_ID)
continue;
- for (board_index = 0; product_ids[board_index]; board_index++) {
- if (device == product_ids[board_index])
- break;
- }
- if (product_ids[board_index])
- product_name = product_names[board_index];
- else if ((device & 0xfff0) == 0x9000)
- product_name = "3c900";
- else if ((device & 0xfff0) == 0x9050)
- product_name = "3c905";
- else {
- printk("Unknown 3Com PCI ethernet adapter type %4.4x detected:"
- " not configured.\n", device);
+ if (ioaddr == 0) {
+ printk(KERN_WARNING " A 3Com network adapter has been found, "
+ "however it has not been assigned an I/O address.\n"
+ " You may need to power-cycle the machine for this "
+ "device to work!\n");
continue;
}
- if (check_region(pci_ioaddr, VORTEX_TOTAL_SIZE))
+
+ if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
continue;
+ /* Activate the card. */
new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
if (pci_command != new_command) {
printk(KERN_INFO " The PCI BIOS has not enabled this"
PCI_COMMAND, new_command);
}
- dev = vortex_found_device(dev, pci_ioaddr, pci_irq_line,
- product_name, dev && dev->mem_start
+ dev = vortex_found_device(dev, ioaddr, irq,
+ device, dev && dev->mem_start
? dev->mem_start : options[cards_found],
cards_found);
if (dev) {
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
/* Get and check the latency values. On the 3c590 series
the latency timer must be set to the maximum value to avoid
data corruption that occurs when the timer expires during
a transfer -- a bug in the Vortex chip only. */
- unsigned char new_latency = (device&0xff00) == 0x5900 ? 248 : 32;
+ u8 new_latency = (device&0xff00) == 0x5900 ? 248 : 32;
+ vp->pci_bus = pci_bus;
+ vp->pci_dev_fn = pci_device_fn;
+ vp->pci_device_id = device;
+
pcibios_read_config_byte(pci_bus, pci_device_fn,
PCI_LATENCY_TIMER, &pci_latency);
if (pci_latency < new_latency) {
- printk("%s: Overriding PCI latency"
+ printk(KERN_INFO "%s: Overriding PCI latency"
" timer (CFLT) setting of %d, new value is %d.\n",
dev->name, pci_latency, new_latency);
pcibios_write_config_byte(pci_bus, pci_device_fn,
/* Now check all slots of the EISA bus. */
if (EISA_bus) {
static int ioaddr = 0x1000;
- for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
- int product_id;
- char *product_name;
+ for ( ; ioaddr < 0x9000; ioaddr += 0x1000) {
+ int device_id;
if (check_region(ioaddr, VORTEX_TOTAL_SIZE))
continue;
/* Check the standard EISA ID register for an encoded '3Com'. */
if (inw(ioaddr + 0xC80) != 0x6d50)
continue;
/* Check for a product that we support, 3c59{2,7} any rev. */
- product_id = inw(ioaddr + 0xC82) & 0xF0FF;
- if (product_id == 0x7059) /* 597 */
- product_name = "3c597 EISA Fast Demon/Vortex";
- else if (product_id == 0x2059) /* 592 */
- product_name = "3c592 EISA 10mbps Demon/Vortex";
- else
+ device_id = (inb(ioaddr + 0xC82)<<8) + inb(ioaddr + 0xC83);
+ if ((device_id & 0xFF00) != 0x5900)
continue;
vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
- product_name, dev && dev->mem_start
+ device_id, dev && dev->mem_start
? dev->mem_start : options[cards_found],
cards_found);
dev = 0;
#ifdef MODULE
/* Special code to work-around the Compaq PCI BIOS32 problem. */
if (compaq_ioaddr) {
- vortex_found_device(dev, compaq_ioaddr, compaq_irq, "3Com Vortex",
+ vortex_found_device(dev, compaq_ioaddr, compaq_irq, compaq_device_id,
dev && dev->mem_start ? dev->mem_start
: options[cards_found], cards_found);
cards_found++;
static struct device *
vortex_found_device(struct device *dev, int ioaddr, int irq,
- const char *product_name, int options, int card_idx)
+ int device_id, int option, int card_idx)
{
struct vortex_private *vp;
+ const char *product_name;
+ int board_index = 0;
+
+ for (board_index = 0; product_ids[board_index]; board_index++) {
+ if (device_id == product_ids[board_index])
+ break;
+ }
+ /* Handle products we don't recognize, but might still work with. */
+ if (product_ids[board_index])
+ product_name = product_names[board_index];
+ else if ((device_id & 0xff00) == 0x5900)
+ product_name = "3c590 Vortex";
+ else if ((device_id & 0xfff0) == 0x9000)
+ product_name = "3c900";
+ else if ((device_id & 0xfff0) == 0x9050)
+ product_name = "3c905";
+ else {
+ printk(KERN_WARNING "Unknown 3Com PCI ethernet adapter type %4.4x detected:"
+ " not configured.\n", device_id);
+ return 0;
+ }
#ifdef MODULE
/* Allocate and fill new device structure. */
- int dev_size = sizeof(struct device) +
- sizeof(struct vortex_private) + 15; /* Pad for alignment */
+ {
+ int dev_size = sizeof(struct device) +
+ sizeof(struct vortex_private) + 15; /* Pad for alignment */
- dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
- memset(dev, 0, dev_size);
+ dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
+ memset(dev, 0, dev_size);
+ }
/* Align the Rx and Tx ring entries. */
dev->priv = (void *)(((long)dev + sizeof(struct device) + 15) & ~15);
vp = (struct vortex_private *)dev->priv;
dev->irq = irq;
dev->init = vortex_probe1;
vp->product_name = product_name;
- vp->options = options;
+ vp->options = option;
if (card_idx >= 0) {
if (full_duplex[card_idx] >= 0)
vp->full_duplex = full_duplex[card_idx];
} else
- vp->full_duplex = (options >= 0 && (options & 0x10) ? 1 : 0);
+ vp->full_duplex = (option > 0 && (option & 0x10) ? 1 : 0);
- if (options >= 0) {
- vp->media_override = ((options & 7) == XCVR_10baseTOnly) ?
- XCVR_10baseT : options & 7;
- vp->bus_master = (options & 16) ? 1 : 0;
+ if (option > 0) {
+ vp->media_override = ((option & 7) == XCVR_10baseTOnly) ?
+ XCVR_10baseT : option & 7;
+ vp->bus_master = (option & 16) ? 1 : 0;
} else {
vp->media_override = 7;
vp->bus_master = 0;
vp = (struct vortex_private *)dev->priv;
vp->product_name = product_name;
- vp->options = options;
- if (options >= 0) {
- vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
- vp->full_duplex = (options & 8) ? 1 : 0;
- vp->bus_master = (options & 16) ? 1 : 0;
+ vp->options = option;
+ if (option >= 0) {
+ vp->media_override = ((option & 7) == 2) ? 0 : option & 7;
+ vp->full_duplex = (option & 8) ? 1 : 0;
+ vp->bus_master = (option & 16) ? 1 : 0;
} else {
vp->media_override = 7;
vp->full_duplex = 0;
{
int ioaddr = dev->base_addr;
struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ u16 *ether_addr = (u16 *)dev->dev_addr;
unsigned int eeprom[0x40], checksum = 0; /* EEPROM contents */
int i;
- printk("%s: 3Com %s at %#3x,", dev->name,
- vp->product_name, ioaddr);
+ printk(KERN_INFO "%s: 3Com %s at %#3x,",
+ dev->name, vp->product_name, ioaddr);
/* Read the station address from the EEPROM. */
EL3WINDOW(0);
- for (i = 0; i < 0x18; i++) {
- u16 *phys_addr = (u16 *)dev->dev_addr;
+ for (i = 0; i < 0x40; i++) {
int timer;
+#ifdef CARDBUS
+ outw(0x230 + i, ioaddr + Wn0EepromCmd);
+#else
outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
+#endif
/* Pause for at least 162 us. for the read to take place. */
- for (timer = 4; timer >= 0; timer--) {
+ for (timer = 10; timer >= 0; timer--) {
udelay(162);
if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
break;
}
eeprom[i] = inw(ioaddr + Wn0EepromData);
- checksum ^= eeprom[i];
- if (i >= 10 && i < 13)
- phys_addr[i - 10] = htons(inw(ioaddr + Wn0EepromData));
}
+ for (i = 0; i < 0x18; i++)
+ checksum ^= eeprom[i];
checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ if (checksum != 0x00) { /* Grrr, needless incompatible change 3Com. */
+ while (i < 0x21)
+ checksum ^= eeprom[i++];
+ checksum = (checksum ^ (checksum >> 8)) & 0xff;
+ }
if (checksum != 0x00)
printk(" ***INVALID CHECKSUM %4.4x*** ", checksum);
+
+ for (i = 0; i < 3; i++)
+ ether_addr[i] = htons(eeprom[i + 10]);
for (i = 0; i < 6; i++)
printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
printk(", IRQ %d\n", dev->irq);
/* Tell them about an invalid IRQ. */
if (vortex_debug && (dev->irq <= 0 || dev->irq >= NR_IRQS))
- printk(" *** Warning: this IRQ is unlikely to work! ***\n");
+ printk(KERN_WARNING " *** Warning: IRQ %d is unlikely to work! ***\n",
+ dev->irq);
+
+ /* Extract our information from the EEPROM data. */
+ vp->info1 = eeprom[13];
+ vp->info2 = eeprom[15];
+ vp->capabilities = eeprom[16];
+
+ if (vp->info1 & 0x8000)
+ vp->full_duplex = 1;
{
char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
union wn3_config config;
EL3WINDOW(3);
vp->available_media = inw(ioaddr + Wn3_Options);
+ if ((vp->available_media & 0xff) == 0) /* Broken 3c916 */
+ vp->available_media = 0x40;
config.i = inl(ioaddr + Wn3_Config);
if (vortex_debug > 1)
- printk(" Internal config register is %4.4x, transceivers %#x.\n",
- config.i, inw(ioaddr + Wn3_Options));
- printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ printk(KERN_DEBUG " Internal config register is %4.4x, "
+ "transceivers %#x.\n", config.i, inw(ioaddr + Wn3_Options));
+ printk(KERN_INFO " %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
8 << config.u.ram_size,
config.u.ram_width ? "word" : "byte",
ram_split[config.u.ram_split],
config.u.autoselect ? "autoselect/" : "",
+ config.u.xcvr ? "NWay Autonegotiation" :
media_tbl[config.u.xcvr].name);
- dev->if_port = config.u.xcvr;
vp->default_media = config.u.xcvr;
vp->autoselect = config.u.autoselect;
}
+
if (vp->media_override != 7) {
- printk(" Media override to transceiver type %d (%s).\n",
+ printk(KERN_INFO " Media override to transceiver type %d (%s).\n",
vp->media_override, media_tbl[vp->media_override].name);
dev->if_port = vp->media_override;
- }
+ } else
+ dev->if_port = vp->default_media;
if (dev->if_port == XCVR_MII) {
int phy, phy_idx = 0;
for (phy = 0; phy < 32 && phy_idx < sizeof(vp->phys); phy++) {
int mii_status;
mdio_sync(ioaddr, 32);
- mii_status = mdio_read(ioaddr, phy, 0);
- if (mii_status != 0xffff) {
+ mii_status = mdio_read(ioaddr, phy, 1);
+ if (mii_status && mii_status != 0xffff) {
vp->phys[phy_idx++] = phy;
- printk("%s: MII transceiver found at address %d.\n",
- dev->name, phy);
+ printk(KERN_INFO " MII transceiver found at address %d, status %4x.\n",
+ phy, mii_status);
mdio_sync(ioaddr, 32);
if ((mdio_read(ioaddr, phy, 1) & 0x0040) == 0)
mii_preamble_required = 1;
}
}
if (phy_idx == 0) {
- printk("%s: ***WARNING*** No MII transceivers found!\n",
- dev->name);
- vp->phys[0] = 0;
+ printk(KERN_WARNING" ***WARNING*** No MII transceivers found!\n");
+ vp->phys[0] = 24;
+ } else {
+ vp->advertising = mdio_read(ioaddr, vp->phys[0], 4);
+ if (vp->full_duplex) {
+ /* Only advertise the FD media types. */
+ vp->advertising &= 0x015F;
+ mdio_write(ioaddr, vp->phys[0], 4, vp->advertising);
+ }
}
}
- vp->info1 = eeprom[13];
- vp->info2 = eeprom[15];
- vp->capabilities = eeprom[16];
- if (vp->capabilities & 0x20) {
- vp->full_bus_master_tx = 1;
- printk(" Enabling bus-master transmits and %s receives.\n",
- (vp->info2 & 1) ? "early" : "whole-frame" );
- vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
+ if (vp->capabilities & CapBusMaster) {
+ vp->full_bus_master_tx = 1;
+ printk(KERN_INFO" Enabling bus-master transmits and %s receives.\n",
+ (vp->info2 & 1) ? "early" : "whole-frame" );
+ vp->full_bus_master_rx = (vp->info2 & 1) ? 1 : 2;
}
/* We do a request_region() to register /proc/ioports info. */
return 0;
}
-\f
-/* Read and write the MII registers using software-generated serial
- MDIO protocol. The maxium data clock rate is 2.5 Mhz. */
-#define mdio_delay() udelay(1)
-
-#define MDIO_SHIFT_CLK 0x01
-#define MDIO_DIR_WRITE 0x04
-#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
-#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
-#define MDIO_DATA_READ 0x02
-#define MDIO_ENB_IN 0x00
-
-static void mdio_sync(int ioaddr, int bits)
-{
- int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- /* Establish sync by sending at least 32 logic ones. */
- while (-- bits >= 0) {
- outw(MDIO_DATA_WRITE1, mdio_addr);
- mdio_delay();
- outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
-}
-static int mdio_read(int ioaddr, int phy_id, int location)
-{
- int i;
- int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
- unsigned int retval = 0;
- int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the read command bits out. */
- for (i = 14; i >= 0; i--) {
- int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- mdio_delay();
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Read the two transition, 16 data, and wire-idle bits. */
- for (i = 19; i > 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- return retval>>1 & 0xffff;
-}
-
-static void mdio_write(int ioaddr, int phy_id, int location, int value)
-{
- int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
- int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
- int i;
-
- if (mii_preamble_required)
- mdio_sync(ioaddr, 32);
-
- /* Shift the command bits out. */
- for (i = 31; i >= 0; i--) {
- int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
- outw(dataval, mdio_addr);
- mdio_delay();
- outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
- /* Leave the interface idle. */
- for (i = 1; i >= 0; i--) {
- outw(MDIO_ENB_IN, mdio_addr);
- mdio_delay();
- outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
- mdio_delay();
- }
-
- return;
-}
\f
static int
if (vp->media_override != 7) {
if (vortex_debug > 1)
- printk("%s: Media override to transceiver %d (%s).\n",
+ printk(KERN_INFO "%s: Media override to transceiver %d (%s).\n",
dev->name, vp->media_override,
media_tbl[vp->media_override].name);
dev->if_port = vp->media_override;
dev->if_port = media_tbl[dev->if_port].next;
if (vortex_debug > 1)
- printk("%s: Initial media type %s.\n",
+ printk(KERN_DEBUG "%s: Initial media type %s.\n",
dev->name, media_tbl[dev->if_port].name);
init_timer(&vp->timer);
if (dev->if_port == XCVR_MII) {
int mii_reg1, mii_reg5;
- /* We cheat here: we know that we are using the 83840 transceiver
- which summarizes the FD status in an extended register. */
EL3WINDOW(4);
/* Read BMSR (reg1) only to clear old status. */
mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
|| (mii_reg5 & 0x00C0) == 0x0040) /* 10T-FD, but not 100-HD */
vp->full_duplex = 1;
if (vortex_debug > 1)
- printk("%s: MII #%d status %4.4x, link partner capability %4.4x,"
+ printk(KERN_INFO "%s: MII #%d status %4.4x, link partner capability %4.4x,"
" setting %s-duplex.\n", dev->name, vp->phys[0],
mii_reg1, mii_reg5, vp->full_duplex ? "full" : "half");
EL3WINDOW(3);
(dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
if (vortex_debug > 1) {
- printk("%s: vortex_open() InternalConfig %8.8x.\n",
+ printk(KERN_DEBUG "%s: vortex_open() InternalConfig %8.8x.\n",
dev->name, config.i);
}
outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0 ; i--)
+ for (i = 2000; i >= 0 ; i--)
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
outw(RxReset, ioaddr + EL3_CMD);
/* Wait a few ticks for the RxReset command to complete. */
- for (i = 20; i >= 0 ; i--)
+ for (i = 2000; i >= 0 ; i--)
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
#ifdef SA_SHIRQ
/* Use the now-standard shared IRQ implementation. */
- if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ,
- vp->product_name, dev)) {
+ if (request_irq(dev->irq, &vortex_interrupt, SA_SHIRQ, dev->name, dev)) {
return -EAGAIN;
}
#else
if (vortex_debug > 1) {
EL3WINDOW(4);
- printk("%s: vortex_open() irq %d media status %4.4x.\n",
+ printk(KERN_DEBUG "%s: vortex_open() irq %d media status %4.4x.\n",
dev->name, dev->irq, inw(ioaddr + Wn4_Media));
}
outw(SetRxThreshold + (1536>>2), ioaddr + EL3_CMD);
outl(0x0020, ioaddr + PktStatus);
if (vortex_debug > 2)
- printk("%s: Filling in the Rx ring.\n", dev->name);
+ printk(KERN_DEBUG "%s: Filling in the Rx ring.\n", dev->name);
for (i = 0; i < RX_RING_SIZE; i++) {
struct sk_buff *skb;
vp->rx_ring[i].next = virt_to_bus(&vp->rx_ring[i+1]);
set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+ vp->in_interrupt = 0;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
/* Allow status bits to be seen. */
- outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull|TxComplete|
- (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
- (vp->full_bus_master_rx ? UpComplete : RxComplete) |
- (vp->bus_master ? DMADone : 0),
- ioaddr + EL3_CMD);
+ vp->status_enable = SetStatusEnb | HostError|IntReq|StatsFull|TxComplete|
+ (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
+ (vp->full_bus_master_rx ? UpComplete : RxComplete) |
+ (vp->bus_master ? DMADone : 0);
+ outw(vp->status_enable, ioaddr + EL3_CMD);
/* Ack all pending events, and set active indicator mask. */
outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
ioaddr + EL3_CMD);
outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
- | AdapterFailure | TxComplete
+ | HostError | TxComplete
| (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
ioaddr + EL3_CMD);
int ok = 0;
if (vortex_debug > 1)
- printk("%s: Media selection timer tick happened, %s.\n",
+ printk(KERN_DEBUG "%s: Media selection timer tick happened, %s.\n",
dev->name, media_tbl[dev->if_port].name);
save_flags(flags); cli(); {
if (media_status & Media_LnkBeat) {
ok = 1;
if (vortex_debug > 1)
- printk("%s: Media %s has link beat, %x.\n",
+ printk(KERN_DEBUG "%s: Media %s has link beat, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
} else if (vortex_debug > 1)
- printk("%s: Media %s is has no link beat, %x.\n",
+ printk(KERN_DEBUG "%s: Media %s is has no link beat, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
break;
int mii_reg1 = mdio_read(ioaddr, vp->phys[0], 1);
int mii_reg5 = mdio_read(ioaddr, vp->phys[0], 5);
if (vortex_debug > 1)
- printk("%s: MII #%d status register is %4.4x, "
+ printk(KERN_DEBUG "%s: MII #%d status register is %4.4x, "
"link partner capability %4.4x.\n",
dev->name, vp->phys[0], mii_reg1, mii_reg5);
if (mii_reg1 & 0x0004)
}
default: /* Other media types handled by Tx timeouts. */
if (vortex_debug > 1)
- printk("%s: Media %s is has no indication, %x.\n",
+ printk(KERN_DEBUG "%s: Media %s is has no indication, %x.\n",
dev->name, media_tbl[dev->if_port].name, media_status);
ok = 1;
}
if (dev->if_port == XCVR_Default) { /* Go back to default. */
dev->if_port = vp->default_media;
if (vortex_debug > 1)
- printk("%s: Media selection failing, using default %s port.\n",
+ printk(KERN_DEBUG "%s: Media selection failing, using default "
+ "%s port.\n",
dev->name, media_tbl[dev->if_port].name);
} else {
if (vortex_debug > 1)
- printk("%s: Media selection failed, now trying %s port.\n",
+ printk(KERN_DEBUG "%s: Media selection failed, now trying "
+ "%s port.\n",
dev->name, media_tbl[dev->if_port].name);
vp->timer.expires = RUN_AT(media_tbl[dev->if_port].wait);
add_timer(&vp->timer);
EL3WINDOW(old_window);
} restore_flags(flags);
if (vortex_debug > 1)
- printk("%s: Media selection timer finished, %s.\n",
+ printk(KERN_DEBUG "%s: Media selection timer finished, %s.\n",
dev->name, media_tbl[dev->if_port].name);
#endif /* AUTOMEDIA*/
{
struct vortex_private *vp = (struct vortex_private *)dev->priv;
int ioaddr = dev->base_addr;
- int i;
+ int j;
- printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ printk(KERN_ERR "%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
dev->name, inb(ioaddr + TxStatus),
inw(ioaddr + EL3_STATUS));
/* Slight code bloat to be user friendly. */
if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
- printk("%s: Transmitter encountered 16 collisions --"
+ printk(KERN_ERR "%s: Transmitter encountered 16 collisions --"
" network cable problem?\n", dev->name);
if (inw(ioaddr + EL3_STATUS) & IntLatch) {
- printk("%s: Interrupt posted but not handled --"
+ printk(KERN_ERR "%s: Interrupt posted but not delivered --"
" IRQ blocked by another device?\n", dev->name);
/* Bad idea here.. but we might as well handle a few events. */
vortex_interrupt IRQ(dev->irq, dev, 0);
}
-#ifndef final_version
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 200; j >= 0 ; j--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+
+#if ! defined(final_version) && LINUX_VERSION_CODE >= 0x10300
if (vp->full_bus_master_tx) {
- printk(" Flags; bus-master %d, full %d; dirty %d current %d.\n",
+ int i;
+ printk(KERN_DEBUG " Flags; bus-master %d, full %d; dirty %d "
+ "current %d.\n",
vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx, vp->cur_tx);
- printk(" Transmit list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
+ printk(KERN_DEBUG " Transmit list %8.8x vs. %p.\n",
+ inl(ioaddr + DownListPtr),
&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]);
for (i = 0; i < TX_RING_SIZE; i++) {
- printk(" %d: @%p length %8.8x status %8.8x\n", i,
+ printk(KERN_DEBUG " %d: @%p length %8.8x status %8.8x\n", i,
&vp->tx_ring[i],
vp->tx_ring[i].length,
vp->tx_ring[i].status);
}
}
-#ifdef notdef
- if (vp->full_bus_master_rx) {
- printk(" Switching to non-bus-master receives.\n");
- outw(SetStatusEnb | AdapterFailure|IntReq|StatsFull |
- (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
- RxComplete | (vp->bus_master ? DMADone : 0),
- ioaddr + EL3_CMD);
- }
- /* Issue TX_RESET and TX_START commands. */
- outw(TxReset, ioaddr + EL3_CMD);
- for (i = 20; i >= 0 ; i--)
- if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
-#endif
#endif
+ vp->stats.tx_errors++;
if (vp->full_bus_master_tx) {
- /* Change 6/25/97 Michael Sievers sieversm@mail.desy.de
- The card has been resetted, but the Tx Ring is still full.
- Since the card won't know where to resume, 'update' the
- Tx Ring. Probably, we'll lose 16 packets this way, these
- will be accounted for as 'dropped'. The code to update the
- Tx Ring is taken from the Interrupt handler. */
-
- unsigned int dirty_tx = vp->dirty_tx;
-
- if (vortex_debug > 0)
- printk("%s: Freeing Tx ring entries:", dev->name);
- while (vp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- if (inl(ioaddr + DownListPtr) ==
- virt_to_bus(&vp->tx_ring[entry]))
- break; /* It still hasn't been processed. */
- if (vp->tx_skbuff[entry]) {
- if (vortex_debug > 0)
- printk(" %d\n", entry);
- dev_kfree_skb(vp->tx_skbuff[entry]);
- vp->tx_skbuff[entry] = 0;
- vp->stats.tx_dropped++;
- }
if (vortex_debug > 0)
- printk(".\n");
- vp->stats.tx_errors++;
- dirty_tx++;
- }
- vp->dirty_tx = dirty_tx;
- vp->tx_full= 0;
- } else { /* not bus-master, no Tx ring to clear */
- vp->stats.tx_errors++;
- vp->stats.tx_dropped++;
- }
+ printk(KERN_DEBUG "%s: Resetting the Tx ring pointer.\n",
+ dev->name);
+ if (vp->cur_tx - vp->dirty_tx > 0 && inl(ioaddr + DownListPtr) == 0)
+ outl(virt_to_bus(&vp->tx_ring[vp->dirty_tx % TX_RING_SIZE]),
+ ioaddr + DownListPtr);
+ if (vp->tx_full && (vp->cur_tx - vp->dirty_tx <= TX_RING_SIZE - 1)) {
+ vp->tx_full = 0;
+ clear_bit(0, (void*)&dev->tbusy);
+ }
+ outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
+ outw(DownUnstall, ioaddr + EL3_CMD);
+ } else
+ vp->stats.tx_dropped++;
/* Issue Tx Enable */
outw(TxEnable, ioaddr + EL3_CMD);
/* Switch to register set 7 for normal use. */
EL3WINDOW(7);
-
- /* The TxFreeThreshold has to be set again after a reset! */
- if (vp->full_bus_master_tx) {
- outb(PKT_BUF_SZ>>8, ioaddr + TxFreeThreshold);
- /* This is to be sure that all bus-master Tx features
- are correctly re-initialized after a reset, although
- the DownListPtr should already be 0 at this point. */
- outl(0, ioaddr + DownListPtr);
+}
+
+/*
+ * Handle uncommon interrupt sources. This is a seperate routine to minimize
+ * the cache impact.
+ */
+static void
+vortex_error(struct device *dev, int status)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int do_tx_reset = 0;
+ int i;
+
+ if (status & TxComplete) { /* Really "TxError" for us. */
+ unsigned char tx_status = inb(ioaddr + TxStatus);
+ /* Presumably a tx-timeout. We must merely re-enable. */
+ if (vortex_debug > 2
+ || (tx_status != 0x88 && vortex_debug > 0))
+ printk(KERN_DEBUG"%s: Transmit error, Tx status register %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x14) vp->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+ outb(0, ioaddr + TxStatus);
+ if (tx_status & 0x30)
+ do_tx_reset = 1;
+ else /* Merely re-enable the transmitter. */
+ outw(TxEnable, ioaddr + EL3_CMD);
}
- /* finally, allow new Transmits */
- dev->tbusy = 0;
- /* End of Michael Sievers <sieversm@mail.desy.de> changes. */
+ if (status & RxEarly) { /* Rx early is unused. */
+ vortex_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & StatsFull) { /* Empty statistics. */
+ static int DoneDidThat = 0;
+ if (vortex_debug > 4)
+ printk(KERN_DEBUG "%s: Updating stats.\n", dev->name);
+ update_stats(ioaddr, dev);
+ /* HACK: Disable statistics as an interrupt source. */
+ /* This occurs when we have the wrong media type! */
+ if (DoneDidThat == 0 &&
+ inw(ioaddr + EL3_STATUS) & StatsFull) {
+ printk(KERN_WARNING "%s: Updating statistics failed, disabling "
+ "stats as an interrupt source.\n", dev->name);
+ EL3WINDOW(5);
+ outw(SetIntrEnb | (inw(ioaddr + 10) & ~StatsFull), ioaddr + EL3_CMD);
+ EL3WINDOW(7);
+ DoneDidThat++;
+ }
+ }
+ if (status & IntReq) /* Restore all interrupt sources. */
+ outw(ioaddr + EL3_CMD, vp->status_enable);
+ if (status & HostError) {
+ u16 fifo_diag;
+ EL3WINDOW(4);
+ fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+ if (vortex_debug > 0)
+ printk(KERN_ERR "%s: Host error, FIFO diagnostic register %4.4x.\n",
+ dev->name, fifo_diag);
+ /* Adapter failure requires Tx/Rx reset and reinit. */
+ if (vp->full_bus_master_tx) {
+ outw(TotalReset | 0xff, ioaddr + EL3_CMD);
+ for (i = 2000; i >= 0 ; i--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ /* Re-enable the receiver. */
+ outw(RxEnable, ioaddr + EL3_CMD);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ } else if (fifo_diag & 0x0400)
+ do_tx_reset = 1;
+ if (fifo_diag & 0x3000) {
+ outw(RxReset, ioaddr + EL3_CMD);
+ for (i = 2000; i >= 0 ; i--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ /* Set the Rx filter to the current state. */
+ set_rx_mode(dev);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | HostError, ioaddr + EL3_CMD);
+ }
+ }
+ if (do_tx_reset) {
+ int j;
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 200; j >= 0 ; j--)
+ if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
+ break;
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+
}
+
static int
vortex_start_xmit(struct sk_buff *skb, struct device *dev)
{
#ifdef VORTEX_BUS_MASTER
if (vp->bus_master) {
/* Set the bus-master controller to transfer the packet. */
- outl((int)(skb->data), ioaddr + Wn7_MasterAddr);
+ outl(virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
vp->tx_skb = skb;
outw(StartDMADown, ioaddr + EL3_CMD);
} else {
/* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb (skb);
+ DEV_FREE_SKB(skb);
if (inw(ioaddr + TxFree) > 1536) {
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
#else
/* ... and the packet rounded to a doubleword. */
outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
- dev_kfree_skb (skb);
+ DEV_FREE_SKB(skb);
if (inw(ioaddr + TxFree) > 1536) {
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
} else
/* Interrupt us when the FIFO has room for max-sized packet. */
outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
/* Clear the Tx status stack. */
{
- short tx_status;
+ int tx_status;
int i = 32;
while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
if (tx_status & 0x3C) { /* A Tx-disabling error occurred. */
if (vortex_debug > 2)
- printk("%s: Tx error, status %2.2x.\n",
+ printk(KERN_DEBUG "%s: Tx error, status %2.2x.\n",
dev->name, tx_status);
if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
if (tx_status & 0x30) {
int j;
outw(TxReset, ioaddr + EL3_CMD);
- for (j = 20; j >= 0 ; j--)
+ for (j = 200; j >= 0 ; j--)
if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
break;
}
int i;
if (vortex_debug > 3)
- printk("%s: Trying to send a packet, Tx index %d.\n",
+ printk(KERN_DEBUG "%s: Trying to send a packet, Tx index %d.\n",
dev->name, vp->cur_tx);
if (vp->tx_full) {
if (vortex_debug >0)
- printk("%s: Tx Ring full, refusing to send buffer.\n",
+ printk(KERN_WARNING "%s: Tx Ring full, refusing to send buffer.\n",
dev->name);
return 1;
}
cli();
outw(DownStall, ioaddr + EL3_CMD);
/* Wait for the stall to complete. */
- for (i = 60; i >= 0 ; i--)
+ for (i = 600; i >= 0 ; i--)
if ( (inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
break;
prev_entry->next = virt_to_bus(&vp->tx_ring[entry]);
vp->tx_full = 1;
else { /* Clear previous interrupt enable. */
prev_entry->status &= ~TxIntrUploaded;
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
}
dev->trans_start = jiffies;
return 0;
#else
struct device *dev = (struct device *)(irq2dev_map[irq]);
#endif
- struct vortex_private *lp;
+ struct vortex_private *vp;
int ioaddr, status;
int latency;
- int i = max_interrupt_work;
+ int work_done = max_interrupt_work;
- if (test_and_set_bit(0, (void*)&dev->interrupt)) {
+ vp = (struct vortex_private *)dev->priv;
+ if (test_and_set_bit(0, (void*)&vp->in_interrupt)) {
printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
return;
}
+ dev->interrupt = 1;
ioaddr = dev->base_addr;
latency = inb(ioaddr + Timer);
- lp = (struct vortex_private *)dev->priv;
status = inw(ioaddr + EL3_STATUS);
if (vortex_debug > 4)
- printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
- status, latency);
-#ifdef notdef
- /* This code guard against bogus hangs, but fails with shared IRQs. */
- if ((status & ~0xE000) == 0x0000) {
- static int donedidthis=0;
- /* Some interrupt controllers store a bogus interrupt from boot-time.
- Ignore a single early interrupt, but don't hang the machine for
- other interrupt problems. */
- if (donedidthis++ > 100) {
- printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
- dev->name, status, dev->start);
- FREE_IRQ(dev->irq, dev);
- }
- }
-#endif
-
+ printk(KERN_DEBUG "%s: interrupt, status %4.4x, latency %d ticks.\n",
+ dev->name, status, latency);
do {
if (vortex_debug > 5)
- printk("%s: In interrupt loop, status %4.4x.\n",
+ printk(KERN_DEBUG "%s: In interrupt loop, status %4.4x.\n",
dev->name, status);
if (status & RxComplete)
vortex_rx(dev);
+ if (status & UpComplete) {
+ outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
+ boomerang_rx(dev);
+ }
if (status & TxAvailable) {
if (vortex_debug > 5)
- printk(" TX room bit was handled.\n");
+ printk(KERN_DEBUG " TX room bit was handled.\n");
/* There's room in the FIFO for a full-sized packet. */
outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
- dev->tbusy = 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
- if (status & TxComplete) { /* Really "TxError" for us. */
- unsigned char tx_status = inb(ioaddr + TxStatus);
- /* Presumably a tx-timeout. We must merely re-enable. */
- if (vortex_debug > 2
- || (tx_status != 0x88 && vortex_debug > 0))
- printk("%s: Transmit error, Tx status register %2.2x.\n",
- dev->name, tx_status);
- if (tx_status & 0x04) lp->stats.tx_fifo_errors++;
- if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
- outb(0, ioaddr + TxStatus);
- outw(TxEnable, ioaddr + EL3_CMD);
- }
+
if (status & DownComplete) {
- unsigned int dirty_tx = lp->dirty_tx;
+ unsigned int dirty_tx = vp->dirty_tx;
- while (lp->cur_tx - dirty_tx > 0) {
+ while (vp->cur_tx - dirty_tx > 0) {
int entry = dirty_tx % TX_RING_SIZE;
if (inl(ioaddr + DownListPtr) ==
- virt_to_bus(&lp->tx_ring[entry]))
+ virt_to_bus(&vp->tx_ring[entry]))
break; /* It still hasn't been processed. */
- if (lp->tx_skbuff[entry]) {
- dev_kfree_skb(lp->tx_skbuff[entry]);
- lp->tx_skbuff[entry] = 0;
+ if (vp->tx_skbuff[entry]) {
+ DEV_FREE_SKB(vp->tx_skbuff[entry]);
+ vp->tx_skbuff[entry] = 0;
}
- /* lp->stats.tx_packets++; Counted below. */
+ /* vp->stats.tx_packets++; Counted below. */
dirty_tx++;
}
- lp->dirty_tx = dirty_tx;
+ vp->dirty_tx = dirty_tx;
outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
- if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
- lp->tx_full= 0;
- dev->tbusy = 0;
+ if (vp->tx_full && (vp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
+ vp->tx_full= 0;
+ clear_bit(0, (void*)&dev->tbusy);
mark_bh(NET_BH);
}
}
#ifdef VORTEX_BUS_MASTER
if (status & DMADone) {
outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
- dev->tbusy = 0;
- dev_kfree_skb (lp->tx_skb); /* Release the transfered buffer */
+ clear_bit(0, (void*)&dev->tbusy);
+ DEV_FREE_SKB(vp->tx_skb); /* Release the transfered buffer */
mark_bh(NET_BH);
}
#endif
- if (status & UpComplete) {
- boomerang_rx(dev);
- outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
- }
- if (status & (AdapterFailure | RxEarly | StatsFull)) {
- /* Handle all uncommon interrupts at once. */
- if (status & RxEarly) { /* Rx early is unused. */
- vortex_rx(dev);
- outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
- }
- if (status & StatsFull) { /* Empty statistics. */
- static int DoneDidThat = 0;
- if (vortex_debug > 4)
- printk("%s: Updating stats.\n", dev->name);
- update_stats(ioaddr, dev);
- /* DEBUG HACK: Disable statistics as an interrupt source. */
- /* This occurs when we have the wrong media type! */
- if (DoneDidThat == 0 &&
- inw(ioaddr + EL3_STATUS) & StatsFull) {
- int win, reg;
- printk("%s: Updating stats failed, disabling stats as an"
- " interrupt source.\n", dev->name);
- for (win = 0; win < 8; win++) {
- EL3WINDOW(win);
- printk("\n Vortex window %d:", win);
- for (reg = 0; reg < 16; reg++)
- printk(" %2.2x", inb(ioaddr+reg));
- }
- EL3WINDOW(7);
- outw(SetIntrEnb | TxAvailable | RxComplete | AdapterFailure
- | UpComplete | DownComplete | TxComplete,
- ioaddr + EL3_CMD);
- DoneDidThat++;
- }
- }
- if (status & AdapterFailure) {
- u16 fifo_diag;
- EL3WINDOW(4);
- fifo_diag = inw(ioaddr + Wn4_FIFODiag);
- if (vortex_debug > 0)
- printk("%s: Host error, FIFO diagnostic register %4.4x.\n",
- dev->name, fifo_diag);
- /* Adapter failure requires Tx/Rx reset and reinit. */
- if (lp->full_bus_master_tx) {
- int j;
- outw(TotalReset | 0xff, ioaddr + EL3_CMD);
- for (j = 200; j >= 0 ; j--)
- if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- /* Re-enable the receiver. */
- outw(RxEnable, ioaddr + EL3_CMD);
- outw(TxEnable, ioaddr + EL3_CMD);
- } else if (fifo_diag & 0x0400) {
- int j;
- outw(TxReset, ioaddr + EL3_CMD);
- for (j = 20; j >= 0 ; j--)
- if ( ! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
- break;
- outw(TxEnable, ioaddr + EL3_CMD);
- }
- if (fifo_diag & 0x2000) {
- outw(RxReset, ioaddr + EL3_CMD);
- /* Set the Rx filter to the current state. */
- set_rx_mode(dev);
- outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
- outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
- }
+ /* Check for all uncommon interrupts at once. */
+ if (status & (HostError | RxEarly | StatsFull | TxComplete | IntReq))
+ vortex_error(dev, status);
+
+ if (--work_done < 0) {
+ if ((status & (0x7fe - (UpComplete | DownComplete))) == 0) {
+ /* Just ack these and return. */
+ outw(AckIntr | UpComplete | DownComplete, ioaddr + EL3_CMD);
+ } else {
+ printk(KERN_WARNING "%s: Too much work in interrupt, status "
+ "%4.4x. Temporarily disabling functions (%4.4x).\n",
+ dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
+ /* Disable all pending interrupts. */
+ outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
+ outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
+ /* Set a timer to reenable interrupts. */
+
+ break;
}
}
-
- if (--i < 0) {
- printk("%s: Too much work in interrupt, status %4.4x. "
- "Disabling functions (%4.4x).\n",
- dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
- /* Disable all pending interrupts. */
- outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
- outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
- break;
- }
/* Acknowledge the IRQ. */
outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
if (vortex_debug > 4)
- printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+ printk(KERN_DEBUG "%s: exiting interrupt, status %4.4x.\n",
+ dev->name, status);
dev->interrupt = 0;
+ clear_bit(0, (void*)&vp->in_interrupt);
return;
}
short rx_status;
if (vortex_debug > 5)
- printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ printk(KERN_DEBUG" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
if (rx_status & 0x4000) { /* Error, update stats. */
unsigned char rx_error = inb(ioaddr + RxErrors);
if (vortex_debug > 2)
- printk(" Rx error: status %2.2x.\n", rx_error);
+ printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error);
vp->stats.rx_errors++;
if (rx_error & 0x01) vp->stats.rx_over_errors++;
if (rx_error & 0x02) vp->stats.rx_length_errors++;
if (rx_error & 0x10) vp->stats.rx_length_errors++;
} else {
/* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
+ int pkt_len = rx_status & 0x1fff;
struct sk_buff *skb;
skb = DEV_ALLOC_SKB(pkt_len + 5);
if (vortex_debug > 4)
- printk("Receiving packet size %d status %4.4x.\n",
+ printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
if (skb != NULL) {
skb->dev = dev;
break;
continue;
} else if (vortex_debug)
- printk("%s: Couldn't allocate a sk_buff of size %d.\n",
- dev->name, pkt_len);
+ printk(KERN_NOTICE "%s: No memory to allocate a sk_buff of "
+ "size %d.\n", dev->name, pkt_len);
}
outw(RxDiscard, ioaddr + EL3_CMD);
vp->stats.rx_dropped++;
int rx_work_limit = vp->dirty_rx + RX_RING_SIZE - vp->cur_rx;
if (vortex_debug > 5)
- printk(" In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
+ printk(KERN_DEBUG " In boomerang_rx(), status %4.4x, rx_status "
+ "%4.4x.\n",
inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
- while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
+ while ((--rx_work_limit >= 0) &&
+ ((rx_status = vp->rx_ring[entry].status) & RxDComplete)) {
if (rx_status & RxDError) { /* Error, update stats. */
unsigned char rx_error = rx_status >> 16;
if (vortex_debug > 2)
- printk(" Rx error: status %2.2x.\n", rx_error);
+ printk(KERN_DEBUG " Rx error: status %2.2x.\n", rx_error);
vp->stats.rx_errors++;
if (rx_error & 0x01) vp->stats.rx_over_errors++;
if (rx_error & 0x02) vp->stats.rx_length_errors++;
if (rx_error & 0x10) vp->stats.rx_length_errors++;
} else {
/* The packet length: up to 4.5K!. */
- short pkt_len = rx_status & 0x1fff;
+ int pkt_len = rx_status & 0x1fff;
struct sk_buff *skb;
if (vortex_debug > 4)
- printk("Receiving packet size %d status %4.4x.\n",
+ printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n",
pkt_len, rx_status);
/* Check if the packet is long enough to just accept without
void *temp;
/* Pass up the skbuff already on the Rx ring. */
skb = vp->rx_skbuff[entry];
+ if (skb == NULL) {
+ printk(KERN_WARNING "%s: in boomerang_rx -- attempt to use NULL skb caught\n", dev->name);
+ break;
+ }
vp->rx_skbuff[entry] = NULL;
+#if LINUX_VERSION_CODE >= 0x10300
temp = skb_put(skb, pkt_len);
+#else
+ temp = skb->data;
+#endif
/* Remove this checking code for final release. */
if (bus_to_virt(vp->rx_ring[entry].addr) != temp)
- printk("%s: Warning -- the skbuff addresses do not match"
- " in boomerang_rx: %p vs. %p / %p.\n", dev->name,
- bus_to_virt(vp->rx_ring[entry].addr),
- skb->head, temp);
+ printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
+ " in boomerang_rx: %p vs. %p.\n", dev->name,
+ bus_to_virt(vp->rx_ring[entry].addr), temp);
rx_nocopy++;
}
#if LINUX_VERSION_CODE > 0x10300
skb->protocol = eth_type_trans(skb, dev);
+ { /* Use hardware checksum info. */
+ int csum_bits = rx_status & 0xee000000;
+ if (csum_bits &&
+ (csum_bits == (IPChksumValid | TCPChksumValid) ||
+ csum_bits == (IPChksumValid | UDPChksumValid))) {
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ rx_csumhits++;
+ }
+ }
#else
skb->len = pkt_len;
#endif
vp->stats.rx_packets++;
}
entry = (++vp->cur_rx) % RX_RING_SIZE;
- if (--rx_work_limit < 0)
- break;
}
/* Refill the Rx ring buffers. */
for (; vp->dirty_rx < vp->cur_rx; vp->dirty_rx++) {
entry = vp->dirty_rx % RX_RING_SIZE;
if (vp->rx_skbuff[entry] == NULL) {
skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
- if (skb == NULL)
+ if (skb == NULL) {
+ printk(KERN_DEBUG "%s: in boomerang_rx -- could not allocate skbuff\n", dev->name);
break; /* Bad news! */
+ }
skb->dev = dev; /* Mark as being used by this device. */
#if LINUX_VERSION_CODE > 0x10300
skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
vp->rx_skbuff[entry] = skb;
}
vp->rx_ring[entry].status = 0; /* Clear complete bit. */
+ outw(UpUnstall, ioaddr + EL3_CMD);
}
+
+ if (vp->dirty_rx >= RX_RING_SIZE ) {
+ vp->cur_rx -= RX_RING_SIZE;
+ vp->dirty_rx -= RX_RING_SIZE;
+ }
+
return 0;
}
dev->tbusy = 1;
if (vortex_debug > 1) {
- printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+ printk(KERN_DEBUG"%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
- printk("%s: vortex close stats: rx_nocopy %d rx_copy %d"
- " tx_queued %d.\n",
- dev->name, rx_nocopy, rx_copy, queued_packet);
+ printk(KERN_DEBUG "%s: vortex close stats: rx_nocopy %d rx_copy %d"
+ " tx_queued %d Rx pre-checksummed %d.\n",
+ dev->name, rx_nocopy, rx_copy, queued_packet, rx_csumhits);
}
del_timer(&vp->timer);
- /* Turn off statistics ASAP. We update lp->stats below. */
+ /* Turn off statistics ASAP. We update vp->stats below. */
outw(StatsDisable, ioaddr + EL3_CMD);
/* Disable the receiver and transmitter. */
#if LINUX_VERSION_CODE < 0x20100
vp->rx_skbuff[i]->free = 1;
#endif
- dev_kfree_skb (vp->rx_skbuff[i]);
+ DEV_FREE_SKB(vp->rx_skbuff[i]);
vp->rx_skbuff[i] = 0;
}
}
outl(0, ioaddr + DownListPtr);
for (i = 0; i < TX_RING_SIZE; i++)
if (vp->tx_skbuff[i]) {
- dev_kfree_skb(vp->tx_skbuff[i]);
+ DEV_FREE_SKB(vp->tx_skbuff[i]);
vp->tx_skbuff[i] = 0;
}
}
int phy = vp->phys[0] & 0x1f;
if (vortex_debug > 2)
- printk("%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+ printk(KERN_DEBUG "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
dev->name, rq->ifr_ifrn.ifrn_name, cmd,
data[0], data[1], data[2], data[3]);
data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
return 0;
case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!capable(CAP_NET_ADMIN))
+ if (!suser())
return -EPERM;
mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
return 0;
set_rx_mode(struct device *dev)
{
int ioaddr = dev->base_addr;
- short new_mode;
+ int new_mode;
if (dev->flags & IFF_PROMISC) {
- if (vortex_debug > 3)
- printk("%s: Setting promiscuous mode.\n", dev->name);
+ if (vortex_debug > 0)
+ printk(KERN_NOTICE "%s: Setting promiscuous mode.\n", dev->name);
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast|RxProm;
} else if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
new_mode = SetRxFilter|RxStation|RxMulticast|RxBroadcast;
set_rx_mode(dev);
}
#endif
+
+\f
+/* MII transceiver control section.
+ Read and write the MII registers using software-generated serial
+ MDIO protocol. See the MII specifications or DP83840A data sheet
+ for details. */
+
+/* The maximum data clock rate is 2.5 Mhz. The minimum timing is usually
+ met by back-to-back PCI I/O cycles, but we insert a delay to avoid
+ "overclocking" issues. */
+#define mdio_delay() udelay(1)
+
+#define MDIO_SHIFT_CLK 0x01
+#define MDIO_DIR_WRITE 0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ 0x02
+#define MDIO_ENB_IN 0x00
+
+/* Generate the preamble required for initial synchronization and
+ a few older transceivers. */
+static void mdio_sync(int ioaddr, int bits)
+{
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ /* Establish sync by sending at least 32 logic ones. */
+ while (-- bits >= 0) {
+ outw(MDIO_DATA_WRITE1, mdio_addr);
+ mdio_delay();
+ outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+}
+
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+ int i;
+ int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+ unsigned int retval = 0;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the read command bits out. */
+ for (i = 14; i >= 0; i--) {
+ int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ mdio_delay();
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Read the two transition, 16 data, and wire-idle bits. */
+ for (i = 19; i > 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ return retval>>1 & 0xffff;
+}
+
+static void mdio_write(int ioaddr, int phy_id, int location, int value)
+{
+ int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+ int mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+ int i;
+
+ if (mii_preamble_required)
+ mdio_sync(ioaddr, 32);
+
+ /* Shift the command bits out. */
+ for (i = 31; i >= 0; i--) {
+ int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+ outw(dataval, mdio_addr);
+ mdio_delay();
+ outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+ /* Leave the interface idle. */
+ for (i = 1; i >= 0; i--) {
+ outw(MDIO_ENB_IN, mdio_addr);
+ mdio_delay();
+ outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+ mdio_delay();
+ }
+
+ return;
+}
+
\f
#ifdef MODULE
void
{
struct device *next_dev;
+#ifdef CARDBUS
+ unregister_driver(&vortex_ops);
+#endif
+
/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
while (root_vortex_dev) {
next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
\f
/*
* Local variables:
- * compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
+ * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c"
+ * compile-command-alt1: "gcc -DCARDBUS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c 3c59x.c -o 3c59x_cb.o"
* c-indent-level: 4
* c-basic-offset: 4
* tab-width: 4
--- /dev/null
+/* 82596.c: A generic 82596 ethernet driver for linux. */
+/*
+ Based on Apricot.c
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+
+ Modified to support the 82596 ethernet chips on 680x0 VME boards.
+ by Richard Hirst <richard@sleepie.demon.co.uk>
+ Renamed to be 82596.c
+
+ *** Untested on Apricot hardware, and may require some hacking
+ *** to make it work. The old 82596.c reported hasn't worked
+ *** since 1.3.xx anyway. I have been unable to find any users
+ *** of Apricot hardware to test this on.
+
+ Most of my modifications relate to the braindead big-endian
+ implementation by Intel. When the i596 is operating in
+ 'big-endian' mode, it thinks a 32 bit value of 0x12345678
+ should be stored as 0x56781234. This is a real pain, when
+ you have linked lists which are shared by the 680x0 and the
+ i596.
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ */
+
+static const char *version = "82596.c:v1.0 15/07/98\n";
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/pgtable.h> /*?? */
+
+#ifdef CONFIG_MVME16x_NET
+#include <asm/mvme16xhw.h>
+#endif
+#ifdef CONFIG_BVME6000_NET
+#include <asm/bvme6000hw.h>
+#endif
+
+/*
+ * Define various macros for Channel Attention, word swapping etc., dependant
+ * on architecture. MVME and BVME are 680x0 based, otherwise it is Intel.
+ */
+
+#ifdef __mc68000__
+#define WSWAPrfd(x) ((struct i596_rfd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPrbd(x) ((struct i596_rbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPiscp(x) ((struct i596_iscp *)(((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPscb(x) ((struct i596_scb *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPcmd(x) ((struct i596_cmd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPtbd(x) ((struct i596_tbd *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define WSWAPchar(x) ((char *) (((u32)(x)<<16) | ((((u32)(x)))>>16)))
+#define ISCP_BUSY 0x00010000
+#define MACH_IS_APRICOT 0
+#else
+#define WSWAPrfd(x) x
+#define WSWAPiscp(x) ((struct i596_iscp *)(x))
+#define WSWAPscb(x) ((struct i596_scb *)(x))
+#define WSWAPcmd(x) x
+#define WSWAPtbd(x) x
+#define WSWAPchar(x) x
+#define ISCP_BUSY 0x0001
+#define MACH_IS_APRICOT 1
+#endif
+
+/*
+ * The MPU_PORT command allows direct access to the 82596. With PORT access
+ * the following commands are available (p5-18). The 32-bit port command
+ * must be word-swapped with the most significant word written first.
+ * This only applies to VME boards.
+ */
+#define PORT_RESET 0x00 /* reset 82596 */
+#define PORT_SELFTEST 0x01 /* selftest */
+#define PORT_ALTSCP 0x02 /* alternate SCB address */
+#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(addr, size) 0
+#define request_region(addr, size,name) do ; while(0)
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#endif
+
+#define APRICOT_DEBUG 2
+
+#ifdef APRICOT_DEBUG
+int i596_debug = APRICOT_DEBUG;
+#else
+int i596_debug = 1;
+#endif
+
+#define I596_TOTAL_SIZE 17
+
+#define I596_NULL -1
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7
+};
+
+#define STAT_C 0x8000 /* Set to 0 after execution */
+#define STAT_B 0x4000 /* Command being executed */
+#define STAT_OK 0x2000 /* Command executed ok */
+#define STAT_A 0x1000 /* Command aborted */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+struct i596_reg {
+ unsigned short porthi;
+ unsigned short portlo;
+ unsigned long ca;
+};
+
+struct i596_cmd {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *next;
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ struct i596_tbd *next;
+ char *data;
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ struct i596_tbd *tbd;
+ unsigned short size;
+ unsigned short pad;
+ struct sk_buff *skb; /* So we can free it after tx */
+};
+
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ struct i596_rfd *next;
+ long rbd;
+ unsigned short count;
+ unsigned short size;
+ char data[1532];
+};
+
+#define RX_RING_SIZE 16
+
+struct i596_scb {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ unsigned long crc_err;
+ unsigned long align_err;
+ unsigned long resource_err;
+ unsigned long over_err;
+ unsigned long rcvdt_err;
+ unsigned long short_err;
+ unsigned short t_on;
+ unsigned short t_off;
+};
+
+struct i596_iscp {
+ unsigned long stat;
+ struct i596_scb *scb;
+};
+
+struct i596_scp {
+ unsigned long sysbus;
+ unsigned long pad;
+ struct i596_iscp *iscp;
+};
+
+struct i596_private {
+ volatile struct i596_scp scp;
+ volatile struct i596_iscp iscp;
+ volatile struct i596_scb scb;
+ struct i596_cmd set_add;
+ char eth_addr[8];
+ struct i596_cmd set_conf;
+ char i596_config[16];
+ struct i596_cmd tdr;
+ unsigned long stat;
+ int last_restart __attribute__((aligned(4)));
+ struct i596_rfd *rx_tail;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ unsigned long last_cmd;
+ struct net_device_stats stats;
+};
+
+char init_setup[] =
+{
+ 0x8E, /* length, prefetch on */
+ 0xC8, /* fifo to 8, monitor off */
+#ifdef CONFIG_VME
+ 0xc0, /* don't save bad frames */
+#else
+ 0x80, /* don't save bad frames */
+#endif
+ 0x2E, /* No source address insertion, 8 byte preamble */
+ 0x00, /* priority and backoff defaults */
+ 0x60, /* interframe spacing */
+ 0x00, /* slot time LSB */
+ 0xf2, /* slot time and retries */
+ 0x00, /* promiscuous mode */
+ 0x00, /* collision detect */
+ 0x40, /* minimum frame length */
+ 0xff,
+ 0x00,
+ 0x7f /* *multi IA */ };
+
+static int i596_open(struct device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct device *dev);
+static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static int i596_close(struct device *dev);
+static struct net_device_stats *i596_get_stats(struct device *dev);
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct device *dev);
+
+static int ticks_limit = 25;
+static int max_cmd_backlog = 16;
+\f
+
+static inline void CA(struct device *dev)
+{
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ ((struct i596_reg *) dev->base_addr)->ca = 1;
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile u32 i = *(volatile u32 *) (dev->base_addr);
+ }
+#endif
+#ifdef CONFIG_APRICOT_i596
+ if (MACH_IS_APRICOT) {
+ outw(0, (short) (dev->base_addr) + 4);
+ }
+#endif
+}
+
+
+static inline void MPU_PORT(struct device *dev, int c, volatile void *x)
+{
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ struct i596_reg *p = (struct i596_reg *) (dev->base_addr);
+ p->porthi = ((c) | (u32) (x)) & 0xffff;
+ p->portlo = ((c) | (u32) (x)) >> 16;
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ u32 v = (u32) (c) | (u32) (x);
+ v = ((u32) (v) << 16) | ((u32) (v) >> 16);
+ *(volatile u32 *) dev->base_addr = v;
+ udelay(1);
+ *(volatile u32 *) dev->base_addr = v;
+ }
+#endif
+}
+
+
+#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET)
+static void i596_error(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = dev_id;
+ struct i596_cmd *cmd;
+
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ printk("i596_error: lp = 0x%08x\n", (u32) lp);
+ printk("scp at %08x, .sysbus = %08x, .iscp = %08x\n",
+ (u32) & lp->scp, (u32) lp->scp.sysbus, (u32) lp->scp.iscp);
+ printk("iscp at %08x, .stat = %08x, .scb = %08x\n",
+ (u32) & lp->iscp, (u32) lp->iscp.stat, (u32) lp->iscp.scb);
+ printk("scb at %08x, .status = %04x, .command = %04x\n",
+ (u32) & lp->scb, lp->scb.status, lp->scb.command);
+ printk(" .cmd = %08x, .rfd = %08x\n", (u32) lp->scb.cmd,
+ (u32) lp->scb.rfd);
+ cmd = WSWAPcmd(lp->scb.cmd);
+ while (cmd && (u32) cmd < 0x1000000) {
+ printk("cmd at %08x, .status = %04x, .command = %04x, .next = %08x\n",
+ (u32) cmd, cmd->status, cmd->command, (u32) cmd->next);
+ cmd = WSWAPcmd(cmd->next);
+ }
+ while (1);
+}
+#endif
+
+static inline int init_rx_bufs(struct device *dev, int num)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ int i;
+ struct i596_rfd *rfd;
+
+ lp->scb.rfd = (struct i596_rfd *) I596_NULL;
+
+ if (i596_debug > 1)
+ printk("%s: init_rx_bufs %d.\n", dev->name, num);
+
+ for (i = 0; i < num; i++) {
+ if (!(rfd = (struct i596_rfd *) kmalloc(sizeof(struct i596_rfd), GFP_KERNEL)))
+ break;
+
+ rfd->stat = 0x0000;
+ rfd->rbd = I596_NULL;
+ rfd->count = 0;
+ rfd->size = 1532;
+ if (i == 0) {
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail = rfd;
+ } else
+ rfd->cmd = 0x0000;
+
+ rfd->next = lp->scb.rfd;
+ lp->scb.rfd = WSWAPrfd(rfd);
+ }
+
+ if (i != 0)
+ lp->rx_tail->next = lp->scb.rfd;
+
+ return (i);
+}
+
+static inline void remove_rx_bufs(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ struct i596_rfd *rfd = WSWAPrfd(lp->scb.rfd);
+
+ lp->rx_tail->next = (struct i596_rfd *) I596_NULL;
+
+ do {
+ lp->scb.rfd = rfd->next;
+ kfree(rfd);
+ rfd = WSWAPrfd(lp->scb.rfd);
+ }
+ while (rfd != lp->rx_tail);
+}
+
+static inline void init_i596_mem(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+#if !defined(CONFIG_MVME16x_NET) && !defined(CONFIG_BVME6000_NET)
+ short ioaddr = dev->base_addr;
+#endif
+ int boguscnt = 100000;
+ unsigned long flags;
+
+#if defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET)
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Disable all ints for now */
+ pcc2[0x28] = 1;
+ pcc2[0x2a] = 0x40;
+ pcc2[0x2b] = 0x40; /* Set snooping bits now! */
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ }
+#endif
+
+ MPU_PORT(dev, PORT_RESET, 0);
+
+ udelay(100); /* Wait 100us - seems to help */
+
+ /* change the scp address */
+
+ MPU_PORT(dev, PORT_ALTSCP, &lp->scp);
+
+#else
+
+ /* change the scp address */
+ outw(0, ioaddr);
+ outw(0, ioaddr);
+ outb(4, ioaddr + 0xf);
+ outw(((((int) &lp->scp) & 0xffff) | 2), ioaddr);
+ outw((((int) &lp->scp) >> 16) & 0xffff, ioaddr);
+#endif
+
+ lp->last_cmd = jiffies;
+
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x)
+ lp->scp.sysbus = 0x00000054;
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000)
+ lp->scp.sysbus = 0x0000004c;
+#endif
+#ifdef CONFIG_APRICOT_i596
+ if (MACH_IS_APRICOT)
+ lp->scp.sysbus = 0x00440000;
+#endif
+
+ lp->scp.iscp = WSWAPiscp(&(lp->iscp));
+ lp->iscp.scb = WSWAPscb(&(lp->scb));
+ lp->iscp.stat = ISCP_BUSY;
+ lp->cmd_backlog = 0;
+
+ lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+
+ if (i596_debug > 1)
+ printk("%s: starting i82596.\n", dev->name);
+
+#if !defined(CONFIG_MVME16x_NET) && !defined(CONFIG_BVME6000_NET)
+ (void) inb(ioaddr + 0x10);
+ outb(4, ioaddr + 0xf);
+#endif
+ CA(dev);
+
+ while (lp->iscp.stat)
+ if (--boguscnt == 0) {
+ printk("%s: i82596 initialization timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = 0;
+
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Enable ints, etc. now */
+ pcc2[0x2a] = 0x08;
+ pcc2[0x2a] = 0x55; /* Edge sensitive */
+ pcc2[0x2b] = 0x55;
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 3;
+ }
+#endif
+
+ memcpy(lp->i596_config, init_setup, 14);
+ lp->set_conf.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->set_conf);
+
+ memcpy(lp->eth_addr, dev->dev_addr, 6);
+ lp->set_add.command = CmdSASetup;
+ i596_add_cmd(dev, &lp->set_add);
+
+ lp->tdr.command = CmdTDR;
+ i596_add_cmd(dev, &lp->tdr);
+
+ boguscnt = 200000;
+
+ save_flags(flags);
+ cli();
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("%s: receive unit start timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = RX_START;
+ CA(dev);
+
+ restore_flags(flags);
+
+ boguscnt = 2000;
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("i82596 init timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+ return;
+}
+
+static inline int i596_rx(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ struct i596_rfd *rfd;
+ int frames = 0;
+
+ if (i596_debug > 3)
+ printk("i596_rx()\n");
+
+ rfd = WSWAPrfd(lp->scb.rfd); /* Reference next frame descriptor to check */
+
+ while ((rfd->stat) & STAT_C) { /* Loop while we have complete frames */
+ if (i596_debug > 2)
+ print_eth(rfd->data);
+
+ if ((rfd->stat) & STAT_OK) {
+ /* a good frame */
+ int pkt_len = rfd->count & 0x3fff;
+ struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+ frames++;
+
+ if (skb == NULL) {
+ printk("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ } else {
+ skb->dev = dev;
+ memcpy(skb_put(skb, pkt_len), rfd->data, pkt_len);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += pkt_len;
+ }
+ } else {
+ lp->stats.rx_errors++;
+ if ((rfd->stat) & 0x0001)
+ lp->stats.collisions++;
+ if ((rfd->stat) & 0x0080)
+ lp->stats.rx_length_errors++;
+ if ((rfd->stat) & 0x0100)
+ lp->stats.rx_over_errors++;
+ if ((rfd->stat) & 0x0200)
+ lp->stats.rx_fifo_errors++;
+ if ((rfd->stat) & 0x0400)
+ lp->stats.rx_frame_errors++;
+ if ((rfd->stat) & 0x0800)
+ lp->stats.rx_crc_errors++;
+ if ((rfd->stat) & 0x1000)
+ lp->stats.rx_length_errors++;
+ }
+
+ /* Clear the buffer descriptor count and EOF + F flags */
+
+ rfd->stat = 0;
+ rfd->count = 0;
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail->cmd = 0;
+ lp->rx_tail = rfd;
+ lp->scb.rfd = rfd->next;
+ rfd = WSWAPrfd(lp->scb.rfd); /* Next frame descriptor to check */
+ }
+
+ if (i596_debug > 3)
+ printk("frames %d\n", frames);
+
+ return 0;
+}
+
+static inline void i596_cleanup_cmd(struct i596_private *lp)
+{
+ struct i596_cmd *ptr;
+ int boguscnt = 1000;
+
+ if (i596_debug > 4)
+ printk("i596_cleanup_cmd\n");
+
+ while (lp->cmd_head != (struct i596_cmd *) I596_NULL) {
+ ptr = lp->cmd_head;
+
+ lp->cmd_head = WSWAPcmd(lp->cmd_head->next);
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+
+ dev_kfree_skb(skb);
+
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ kfree(tx_cmd);
+ break;
+ }
+ case CmdMulticastList:
+ {
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ kfree(ptr);
+ break;
+ }
+ default:
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ }
+ }
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("i596_cleanup_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.cmd = WSWAPcmd(lp->cmd_head);
+}
+
+static inline void i596_reset(struct device *dev, struct i596_private *lp, int ioaddr)
+{
+ int boguscnt = 1000;
+ unsigned long flags;
+
+ if (i596_debug > 1)
+ printk("i596_reset\n");
+
+ save_flags(flags);
+ cli();
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("i596_reset timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ lp->scb.command = CUC_ABORT | RX_ABORT;
+ CA(dev);
+
+ /* wait for shutdown */
+ boguscnt = 4000;
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("i596_reset 2 timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+ restore_flags(flags);
+
+ i596_cleanup_cmd(lp);
+ i596_rx(dev);
+
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ init_i596_mem(dev);
+}
+
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ int boguscnt = 1000;
+
+ if (i596_debug > 4)
+ printk("i596_add_cmd\n");
+
+ cmd->status = 0;
+ cmd->command |= (CMD_EOL | CMD_INTR);
+ cmd->next = (struct i596_cmd *) I596_NULL;
+ save_flags(flags);
+ cli();
+
+ /*
+ * RGH 300597: Looks to me like there could be a race condition
+ * here. Just because we havn't picked up all the command items
+ * yet, doesn't mean that the 82596 hasn't finished processing
+ * them. So, we may need to do a CUC_START anyway.
+ * Maybe not. If it interrupts saying the CU is idle when there
+ * is still something in the cmd queue, the int handler with restart
+ * the CU.
+ */
+
+ if (lp->cmd_head != (struct i596_cmd *) I596_NULL) {
+ lp->cmd_tail->next = WSWAPcmd(cmd);
+ } else {
+ lp->cmd_head = cmd;
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("i596_add_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.cmd = WSWAPcmd(cmd);
+ lp->scb.command = CUC_START;
+ CA(dev);
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ lp->cmd_head = WSWAPcmd(lp->scb.cmd); /* Is this redundant? RGH 300597 */
+ restore_flags(flags);
+
+ if (lp->cmd_backlog > max_cmd_backlog) {
+ unsigned long tickssofar = jiffies - lp->last_cmd;
+
+ if (tickssofar < ticks_limit)
+ return;
+
+ printk("%s: command unit timed out, status resetting.\n", dev->name);
+
+ i596_reset(dev, lp, ioaddr);
+ }
+}
+
+static int i596_open(struct device *dev)
+{
+ int i;
+
+ if (i596_debug > 1)
+ printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
+
+ if (request_irq(dev->irq, &i596_interrupt, 0, "apricot", dev))
+ return -EAGAIN;
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ if (request_irq(0x56, &i596_error, 0, "apricot_error", dev))
+ return -EAGAIN;
+ }
+#endif
+ if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+ printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
+
+ if (i < 4) {
+ free_irq(dev->irq, dev);
+ return -EAGAIN;
+ }
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+
+ /* Initialize the 82596 memory */
+ init_i596_mem(dev);
+
+ return 0; /* Always succeed */
+}
+
+static int i596_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ int ioaddr = dev->base_addr;
+ struct tx_cmd *tx_cmd;
+
+ if (i596_debug > 2)
+ printk("%s: 82596 start xmit\n", dev->name);
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, status resetting.\n",
+ dev->name);
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (i596_debug > 1)
+ printk("Resetting board.\n");
+
+ /* Shutdown and restart */
+ i596_reset(dev, lp, ioaddr);
+ } else {
+ /* Issue a channel attention signal */
+ if (i596_debug > 1)
+ printk("Kicking board.\n");
+ lp->scb.command = CUC_START | RX_START;
+ CA(dev);
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+ if (i596_debug > 3)
+ printk("%s: i596_start_xmit() called\n", dev->name);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (test_and_set_bit(0, (void *) &dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ dev->trans_start = jiffies;
+
+ tx_cmd = (struct tx_cmd *) kmalloc((sizeof(struct tx_cmd) + sizeof(struct i596_tbd)), GFP_ATOMIC);
+ if (tx_cmd == NULL) {
+ printk("%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.tx_dropped++;
+
+ dev_kfree_skb(skb);
+ } else {
+ struct i596_tbd *tbd = (struct i596_tbd *) (tx_cmd + 1);
+ tx_cmd->tbd = WSWAPtbd(tbd);
+ tbd->next = (struct i596_tbd *) I596_NULL;
+
+ tx_cmd->cmd.command = CMD_FLEX | CmdTx;
+ tx_cmd->skb = skb;
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tbd->pad = 0;
+ tbd->size = EOF | length;
+
+ tbd->data = WSWAPchar(skb->data);
+
+ if (i596_debug > 3)
+ print_eth(skb->data);
+ i596_add_cmd(dev, (struct i596_cmd *) tx_cmd);
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += length;
+ }
+ }
+
+ dev->tbusy = 0;
+
+ return 0;
+}
+\f
+
+static void print_eth(char *add)
+{
+ int i;
+
+ printk("print_eth(%08x)\n", (unsigned int) add);
+ printk("Dest ");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char) add[i]);
+ printk("\n");
+
+ printk("Source");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char) add[i + 6]);
+ printk("\n");
+ printk("type %2.2X%2.2X\n", (unsigned char) add[12], (unsigned char) add[13]);
+}
+
+__initfunc(int i82596_probe(struct device *dev))
+{
+ int i;
+ struct i596_private *lp;
+ char eth_addr[6];
+
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ static int probed = 0;
+
+ if (mvme16x_config & MVME16x_CONFIG_NO_ETHERNET) {
+ printk("Ethernet probe disabled - chip not present\n");
+ return ENODEV;
+ }
+ if (probed)
+ return ENODEV;
+ probed++;
+ memcpy(eth_addr, (void *) 0xfffc1f2c, 6); /* YUCK! Get addr from NOVRAM */
+ dev->base_addr = MVME_I596_BASE;
+ dev->irq = (unsigned) MVME16x_IRQ_I596;
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *rtc = (unsigned char *) BVME_RTC_BASE;
+ unsigned char msr = rtc[3];
+ int i;
+
+ rtc[3] |= 0x80;
+ for (i = 0; i < 6; i++)
+ eth_addr[i] = rtc[i * 4 + 7]; /* Stored in RTC RAM at offset 1 */
+ rtc[3] = msr;
+ dev->base_addr = BVME_I596_BASE;
+ dev->irq = (unsigned) BVME_IRQ_I596;
+ }
+#endif
+#ifdef CONFIG_APRICOT_INTEL
+ int checksum = 0;
+ int ioaddr = 0x300;
+
+ /* this is easy the ethernet interface can only be at 0x300 */
+ /* first check nothing is already registered here */
+
+ if (check_region(ioaddr, I596_TOTAL_SIZE))
+ return ENODEV;
+
+ for (i = 0; i < 8; i++) {
+ eth_addr[i] = inb(ioaddr + 8 + i);
+ checksum += eth_addr[i];
+ }
+
+ /* checksum is a multiple of 0x100, got this wrong first time
+ some machines have 0x100, some 0x200. The DOS driver doesn't
+ even bother with the checksum */
+
+ if (checksum % 0x100)
+ return ENODEV;
+
+ /* Some other boards trip the checksum.. but then appear as ether
+ address 0. Trap these - AC */
+
+ if (memcmp(eth_addr, "\x00\x00\x49", 3) != 0)
+ return ENODEV;
+
+ request_region(ioaddr, I596_TOTAL_SIZE, "i596");
+
+ dev->base_addr = ioaddr;
+ dev->irq = 10;
+#endif
+ ether_setup(dev);
+ printk("%s: 82596 at %#3lx,", dev->name, dev->base_addr);
+
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+
+ printk(" IRQ %d.\n", dev->irq);
+
+ if (i596_debug > 0)
+ printk(version);
+
+ /* The APRICOT-specific entries in the device structure. */
+ dev->open = &i596_open;
+ dev->stop = &i596_close;
+ dev->hard_start_xmit = &i596_start_xmit;
+ dev->get_stats = &i596_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ dev->mem_start = (int) kmalloc(sizeof(struct i596_private) + 0x0f, GFP_KERNEL);
+ /* align for scp */
+ dev->priv = (void *) ((dev->mem_start + 0xf) & 0xfffffff0);
+
+ lp = (struct i596_private *) dev->priv;
+ if (i596_debug)
+ printk("%s: lp at 0x%08lx, lp->scb at 0x%08lx\n"
+ ,dev->name, (unsigned long) lp, (unsigned long) &lp->scb);
+ memset((void *) lp, 0, sizeof(struct i596_private));
+ lp->scb.command = 0;
+ lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+ lp->scb.rfd = (struct i596_rfd *) I596_NULL;
+
+ return 0;
+}
+
+static void i596_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct device *dev = dev_id;
+ struct i596_private *lp;
+ short ioaddr;
+ int boguscnt = 2000;
+ unsigned short status, ack_cmd = 0;
+
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ if (*(char *) BVME_LOCAL_IRQ_STAT & BVME_ETHERR) {
+ i596_error(BVME_IRQ_I596, NULL, NULL);
+ return;
+ }
+ }
+#endif
+ if (dev == NULL) {
+ printk("i596_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ if (i596_debug > 3)
+ printk("%s: i596_interrupt(): irq %d\n", dev->name, irq);
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ lp = (struct i596_private *) dev->priv;
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ status = lp->scb.status;
+
+ if (i596_debug > 4)
+ printk("%s: i596 interrupt, status %4.4x.\n", dev->name, status);
+
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x8000) || (status & 0x2000)) {
+ struct i596_cmd *ptr;
+
+ if ((i596_debug > 4) && (status & 0x8000))
+ printk("%s: i596 interrupt completed command.\n", dev->name);
+ if ((i596_debug > 4) && (status & 0x2000))
+ printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700);
+
+ while ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (lp->cmd_head->status & STAT_C)) {
+ ptr = lp->cmd_head;
+
+ if (i596_debug > 2)
+ printk("cmd_head->status = %04x, ->command = %04x\n",
+ lp->cmd_head->status, lp->cmd_head->command);
+ lp->cmd_head = WSWAPcmd(lp->cmd_head->next);
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7) {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = tx_cmd->skb;
+
+ if ((ptr->status) & STAT_OK) {
+ if (i596_debug > 2)
+ print_eth(skb->data);
+ } else {
+ lp->stats.tx_errors++;
+ if ((ptr->status) & 0x0020)
+ lp->stats.collisions++;
+ if (!((ptr->status) & 0x0040))
+ lp->stats.tx_heartbeat_errors++;
+ if ((ptr->status) & 0x0400)
+ lp->stats.tx_carrier_errors++;
+ if ((ptr->status) & 0x0800)
+ lp->stats.collisions++;
+ if ((ptr->status) & 0x1000)
+ lp->stats.tx_aborted_errors++;
+ }
+
+ dev_kfree_skb(skb);
+
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ kfree(tx_cmd);
+ break;
+ }
+ case CmdMulticastList:
+ {
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ kfree(ptr);
+ break;
+ }
+ case CmdTDR:
+ {
+ unsigned long status = *((unsigned long *) (ptr + 1));
+
+ if (status & 0x8000) {
+ if (i596_debug > 3)
+ printk("%s: link ok.\n", dev->name);
+ } else {
+ if (status & 0x4000)
+ printk("%s: Transceiver problem.\n", dev->name);
+ if (status & 0x2000)
+ printk("%s: Termination problem.\n", dev->name);
+ if (status & 0x1000)
+ printk("%s: Short circuit.\n", dev->name);
+
+ if (i596_debug > 1)
+ printk("%s: Time %ld.\n", dev->name, status & 0x07ff);
+ }
+ break;
+ }
+ case CmdConfigure:
+ {
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ /* Zap command so set_multicast_list() knows it is free */
+ ptr->command = 0;
+ break;
+ }
+ default:
+ ptr->next = (struct i596_cmd *) I596_NULL;
+ }
+ lp->last_cmd = jiffies;
+ }
+
+ ptr = lp->cmd_head;
+ while ((ptr != (struct i596_cmd *) I596_NULL) && (ptr != lp->cmd_tail)) {
+ ptr->command &= 0x1fff;
+ ptr = WSWAPcmd(ptr->next);
+ }
+
+ if ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (dev->start))
+ ack_cmd |= CUC_START;
+ lp->scb.cmd = WSWAPcmd(lp->cmd_head);
+ }
+ if ((status & 0x1000) || (status & 0x4000)) {
+ if ((i596_debug > 4) && (status & 0x4000))
+ printk("%s: i596 interrupt received a frame.\n", dev->name);
+ /* Only RX_START if stopped - RGH 07-07-96 */
+ if (status & 0x1000) {
+ if (dev->start)
+ ack_cmd |= RX_START;
+ if (i596_debug > 1)
+ printk("%s: i596 interrupt receive unit inactive %x.\n", dev->name, status & 0x00f0);
+ }
+ i596_rx(dev);
+ }
+ /* acknowledge the interrupt */
+
+/* COMMENTED OUT <<<<<
+ if ((lp->scb.cmd != (struct i596_cmd *) I596_NULL) && (dev->start))
+ ack_cmd |= CUC_START;
+ */
+ boguscnt = 1000;
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = ack_cmd;
+
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ /* Ack the interrupt */
+
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ pcc2[0x2a] |= 0x08;
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ *ethirq = 3;
+ }
+#endif
+#ifdef CONFIG_APRICOT_INTEL
+ (void) inb(ioaddr + 0x10);
+ outb(4, ioaddr + 0xf);
+#endif
+ CA(dev);
+
+ if (i596_debug > 4)
+ printk("%s: exiting interrupt.\n", dev->name);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int i596_close(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ int boguscnt = 2000;
+ unsigned long flags;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (i596_debug > 1)
+ printk("%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, lp->scb.status);
+
+ save_flags(flags);
+ cli();
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("%s: close1 timed timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = CUC_ABORT | RX_ABORT;
+ CA(dev);
+
+ boguscnt = 2000;
+
+ while (lp->scb.command)
+ if (--boguscnt == 0) {
+ printk("%s: close2 timed timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ restore_flags(flags);
+
+ i596_cleanup_cmd(lp);
+
+#ifdef CONFIG_MVME16x_NET
+ if (MACH_IS_MVME16x) {
+ volatile unsigned char *pcc2 = (unsigned char *) 0xfff42000;
+
+ /* Disable all ints */
+ pcc2[0x28] = 1;
+ pcc2[0x2a] = 0x40;
+ pcc2[0x2b] = 0x40; /* Set snooping bits now! */
+ }
+#endif
+#ifdef CONFIG_BVME6000_NET
+ if (MACH_IS_BVME6000) {
+ volatile unsigned char *ethirq = (unsigned char *) BVME_ETHIRQ_REG;
+
+ *ethirq = 1;
+ }
+#endif
+
+ free_irq(dev->irq, dev);
+ remove_rx_bufs(dev);
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct net_device_stats *
+ i596_get_stats(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *) dev->priv;
+ struct i596_cmd *cmd;
+ int config = 0;
+
+ if (i596_debug > 1)
+ printk("%s: set multicast list, %d entries, promisc %s, allmulti %s\n", dev->name, dev->mc_count, dev->flags & IFF_PROMISC ? "ON" : "OFF", dev->flags & IFF_ALLMULTI ? "ON" : "OFF");
+
+ if ((dev->flags & IFF_PROMISC) && !(lp->i596_config[8] & 0x01)) {
+ lp->i596_config[8] |= 0x01;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_PROMISC) && (lp->i596_config[8] & 0x01)) {
+ lp->i596_config[8] &= ~0x01;
+ config = 1;
+ }
+ if ((dev->flags & IFF_ALLMULTI) && (lp->i596_config[11] & 0x20)) {
+ lp->i596_config[11] &= ~0x20;
+ config = 1;
+ }
+ if (!(dev->flags & IFF_ALLMULTI) && !(lp->i596_config[11] & 0x20)) {
+ lp->i596_config[11] |= 0x20;
+ config = 1;
+ }
+ if (config) {
+ if (lp->set_conf.command)
+ printk("%s: config change request already queued\n",
+ dev->name);
+ else {
+ lp->set_conf.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->set_conf);
+ }
+ }
+ if (dev->mc_count > 0) {
+ struct dev_mc_list *dmi;
+ unsigned char *cp;
+ cmd = (struct i596_cmd *) kmalloc(sizeof(struct i596_cmd) + 2 + dev->mc_count * 6, GFP_ATOMIC);
+ if (cmd == NULL) {
+ printk("%s: set_multicast Memory squeeze.\n", dev->name);
+ return;
+ }
+ cmd->command = CmdMulticastList;
+ *((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
+ cp = ((unsigned char *) (cmd + 1)) + 2;
+ for (dmi = dev->mc_list; dmi != NULL; dmi = dmi->next) {
+ memcpy(cp, dmi->dmi_addr, 6);
+ if (i596_debug > 1)
+ printk("%s: Adding address %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name, *(cp + 0), *(cp + 1), *(cp + 2), *(cp + 3), *(cp + 4), *(cp + 5));
+ cp += 6;
+ }
+ if (i596_debug > 2)
+ print_eth(((char *) (cmd + 1)) + 2);
+ i596_add_cmd(dev, cmd);
+ }
+}
+
+#ifdef HAVE_DEVLIST
+static unsigned int i596_portlist[] __initdata =
+{0x300, 0};
+struct netdev_entry i596_drv =
+{"apricot", i82596_probe, I596_TOTAL_SIZE, apricot_portlist};
+#endif
+
+#ifdef MODULE
+static char devicename[9] =
+{0,};
+static struct device dev_apricot =
+{
+ devicename, /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 10,
+ 0, 0, 0, NULL, i82596_probe};
+
+static int io = 0x300;
+static int irq = 10;
+MODULE_PARM(irq, "i");
+
+int init_module(void)
+{
+ dev_apricot.base_addr = io;
+ dev_apricot.irq = irq;
+ if (register_netdev(&dev_apricot) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unregister_netdev(&dev_apricot);
+ kfree((void *) dev_apricot.mem_start);
+ dev_apricot.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_apricot.base_addr, I596_TOTAL_SIZE);
+}
+
+#endif /* MODULE */
+\f
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 82596.c"
+ * End:
+ */
CONFIG_PPPDEF_MODULE :=
CONFIG_7990_BUILTIN :=
CONFIG_7990_MODULE :=
+CONFIG_82596_BUILTIN :=
+CONFIG_82596_MODULE :=
ifeq ($(CONFIG_ISDN),y)
ifeq ($(CONFIG_ISDN_PPP),y)
endif
ifeq ($(CONFIG_APRICOT),y)
-L_OBJS += apricot.o
+CONFIG_82596_BUILTIN = y
else
ifeq ($(CONFIG_APRICOT),m)
- M_OBJS += apricot.o
+ CONFIG_82596_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_MVME16x_NET),y)
+CONFIG_82596_BUILTIN = y
+else
+ ifeq ($(CONFIG_MVME16x_NET),m)
+ CONFIG_82596_MODULE = y
+ endif
+endif
+
+ifeq ($(CONFIG_BVME6000_NET),y)
+CONFIG_82596_BUILTIN = y
+else
+ ifeq ($(CONFIG_BVME6000_NET),m)
+ CONFIG_82596_MODULE = y
endif
endif
endif
endif
+# If anything built-in uses the 82596, then build it into the kernel also.
+# If not, but a module uses it, build as a module.
+ifdef CONFIG_82596_BUILTIN
+L_OBJS += 82596.o
+else
+ ifdef CONFIG_82596_MODULE
+ M_OBJS += 82596.o
+ endif
+endif
+
ifeq ($(CONFIG_EQUALIZER),y)
L_OBJS += eql.o
else
*
* Holds initial configuration information for devices.
*
- * NOTE: This file is a nice idea, but its current format does not work
- * well for drivers that support multiple units, like the SLIP
- * driver. We should actually have only one pointer to a driver
- * here, with the driver knowing how many units it supports.
- * Currently, the SLIP driver abuses the "base_addr" integer
- * field of the 'device' structure to store the unit number...
- * -FvK
- *
* Version: @(#)Space.c 1.0.7 08/12/93
*
* Authors: Ross Biro, <bir7@leland.Stanford.Edu>
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald J. Becker, <becker@super.org>
*
+ * Changelog:
+ * Paul Gortmaker (06/98):
+ * - sort probes in a sane way, make sure all (safe) probes
+ * get run once & failed autoprobes don't autoprobe again.
+ *
* FIXME:
- * Sort the device chain fastest first.
+ * Phase out placeholder dev entries put in the linked list
+ * here in favour of drivers using init_etherdev(NULL, ...)
+ * combined with a single find_all_devs() function (for 2.3)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
extern int fmv18x_probe(struct device *);
extern int eth16i_probe(struct device *);
extern int depca_probe(struct device *);
-extern int apricot_probe(struct device *);
+extern int i82596_probe(struct device *);
extern int ewrk3_probe(struct device *);
extern int de4x5_probe(struct device *);
extern int el1_probe(struct device *);
extern int mace_probe(struct device *);
extern int cs89x0_probe(struct device *dev);
extern int ethertap_probe(struct device *dev);
-extern int acorn_ethif_probe(struct device *dev);
+extern int ether1_probe (struct device *dev);
+extern int ether3_probe (struct device *dev);
+extern int etherh_probe (struct device *dev);
extern int am79c961_probe(struct device *dev);
extern int epic100_probe(struct device *dev);
extern int rtl8139_probe(struct device *dev);
/* HIPPI boards */
extern int cern_hippi_probe(struct device *);
-
-__initfunc(static int ethif_probe(struct device *dev))
+struct devprobe
{
- u_long base_addr = dev->base_addr;
+ int (*probe)(struct device *dev);
+ int status; /* non-zero if autoprobe has failed */
+};
- if ((base_addr == 0xffe0) || (base_addr == 1))
- return 1; /* ENXIO */
+/*
+ * probe_list walks a list of probe functions and calls each so long
+ * as a non-zero ioaddr is given, or as long as it hasn't already failed
+ * to find a card in the past (as recorded by "status") when asked to
+ * autoprobe (i.e. a probe that fails to find a card when autoprobing
+ * will not be asked to autoprobe again). It exits when a card is found.
+ */
+__initfunc(static int probe_list(struct device *dev, struct devprobe *plist))
+{
+ struct devprobe *p = plist;
+ unsigned long base_addr = dev->base_addr;
+
+ while (p->probe != NULL) {
+ if (base_addr && p->probe(dev) == 0) /* probe given addr */
+ return 0;
+ else if (p->status == 0) { /* has autoprobe failed yet? */
+ p->status = p->probe(dev); /* no, try autoprobe */
+ if (p->status == 0)
+ return 0;
+ }
+ p++;
+ }
+ return -ENODEV;
+}
- if (1
-#ifdef CONFIG_HAPPYMEAL
- /* Please keep this one first, we'd like the on-board ethernet
- * to be probed first before other PCI cards on Ultra/PCI. -DaveM
- */
- && happy_meal_probe(dev)
-#endif
+/*
+ * If your probe touches ISA ports (<0x400) in addition to
+ * looking for PCI cards, then put it in the isa_probes
+ * list instead.
+ */
+struct devprobe pci_probes[] __initdata = {
#ifdef CONFIG_DGRS
- && dgrs_probe(dev)
+ {dgrs_probe, 0},
#endif
-#if defined(CONFIG_VORTEX)
- && tc59x_probe(dev)
+#ifdef CONFIG_VORTEX
+ {tc59x_probe, 0},
#endif
-#if defined(CONFIG_SEEQ8005)
- && seeq8005_probe(dev)
+#ifdef CONFIG_DEC_ELCP
+ {tulip_probe, 0},
#endif
-#if defined(CONFIG_DEC_ELCP)
- && tulip_probe(dev)
+#ifdef CONFIG_NE2K_PCI
+ {ne2k_pci_probe, 0},
#endif
-#if defined(CONFIG_HP100)
- && hp100_probe(dev)
+#ifdef CONFIG_PCNET32
+ {pcnet32_probe, 0},
#endif
-#if defined(CONFIG_ULTRA)
- && ultra_probe(dev)
-#endif
-#if defined(CONFIG_ULTRAMCA)
- && ultramca_probe(dev)
+#ifdef CONFIG_EEXPRESS_PRO100 /* Intel EtherExpress Pro/100 */
+ {eepro100_probe, 0},
#endif
-#if defined(CONFIG_ULTRA32)
- && ultra32_probe(dev)
+#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
+ {de4x5_probe, 0},
#endif
-#if defined(CONFIG_SMC9194)
- && smc_init(dev)
+#ifdef CONFIG_TLAN
+ {tlan_probe, 0},
#endif
-#if defined(CONFIG_WD80x3) || defined(WD80x3)
- && wd_probe(dev)
+#ifdef CONFIG_EPIC100
+ {epic100_probe, 0},
#endif
-#if defined(CONFIG_EL2) || defined(EL2) /* 3c503 */
- && el2_probe(dev)
+#ifdef CONFIG_RTL8139
+ {rtl8139_probe, 0},
#endif
-#if defined(CONFIG_HPLAN) || defined(HPLAN)
- && hp_probe(dev)
+#ifdef CONFIG_YELLOWFIN
+ {yellowfin_probe, 0},
#endif
-#if defined(CONFIG_HPLAN_PLUS)
- && hp_plus_probe(dev)
+ {NULL, 0},
+};
+
+/*
+ * This is a bit of an artificial separation as there are PCI drivers
+ * that also probe for EISA cards (in the PCI group) and there are ISA
+ * drivers that probe for EISA cards (in the ISA group). These are the
+ * EISA only driver probes.
+ */
+struct devprobe eisa_probes[] __initdata = {
+#ifdef CONFIG_ULTRA32
+ {ultra32_probe, 0},
#endif
-#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */
- && ac3200_probe(dev)
+#ifdef CONFIG_AC3200
+ {ac3200_probe, 0},
#endif
#ifdef CONFIG_ES3210
- && es_probe(dev)
+ {es_probe, 0},
#endif
#ifdef CONFIG_LNE390
- && lne390_probe(dev)
+ {lne390_probe, 0},
+#endif
+ {NULL, 0},
+};
+
+struct devprobe sparc_probes[] __initdata = {
+#ifdef CONFIG_HAPPYMEAL
+ {happy_meal_probe, 0},
+#endif
+#ifdef CONFIG_SUNLANCE
+ {sparc_lance_probe, 0},
+#endif
+#ifdef CONFIG_SUNQE
+ {qec_probe, 0},
+#endif
+#ifdef CONFIG_MYRI_SBUS
+ {myri_sbus_probe, 0},
+#endif
+ {NULL, 0},
+};
+
+struct devprobe mca_probes[] __initdata = {
+#ifdef CONFIG_ULTRAMCA
+ {ultramca_probe, 0},
+#endif
+#ifdef CONFIG_ELMC /* 3c523 */
+ {elmc_probe, 0},
+#endif
+ {NULL, 0},
+};
+
+/*
+ * ISA probes that touch addresses < 0x400 (including those that also
+ * look for EISA/PCI cards in addition to ISA cards).
+ */
+struct devprobe isa_probes[] __initdata = {
+#ifdef CONFIG_EL3 /* ISA, EISA (MCA someday) 3c5x9 */
+ {el3_probe, 0},
+#endif
+#ifdef CONFIG_HP100 /* ISA, EISA & PCI */
+ {hp100_probe, 0},
+#endif
+#ifdef CONFIG_ULTRA
+ {ultra_probe, 0},
+#endif
+#ifdef CONFIG_WD80x3
+ {wd_probe, 0},
+#endif
+#ifdef CONFIG_EL2 /* 3c503 */
+ {el2_probe, 0},
+#endif
+#ifdef CONFIG_HPLAN
+ {hp_probe, 0},
+#endif
+#ifdef CONFIG_HPLAN_PLUS
+ {hp_plus_probe, 0},
#endif
#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
- && e2100_probe(dev)
+ {e2100_probe, 0},
#endif
-#if defined(CONFIG_NE2K_PCI)
- && ne2k_pci_probe(dev)
+#ifdef CONFIG_NE2000 /* ISA (use ne2k-pci for PCI cards) */
+ {ne_probe, 0},
#endif
-#if defined(CONFIG_NE2000)
- && ne_probe(dev)
+#ifdef CONFIG_SMC9194
+ {smc_init, 0},
+#endif
+#ifdef CONFIG_SEEQ8005
+ {seeq8005_probe, 0},
#endif
#ifdef CONFIG_AT1500
- && at1500_probe(dev)
+ {at1500_probe, 0},
#endif
#ifdef CONFIG_CS89x0
- && cs89x0_probe(dev)
+ {cs89x0_probe, 0},
#endif
-#ifdef CONFIG_PCNET32
- && pcnet32_probe(dev)
-#endif
#ifdef CONFIG_AT1700
- && at1700_probe(dev)
+ {at1700_probe, 0},
#endif
#ifdef CONFIG_FMV18X /* Fujitsu FMV-181/182 */
- && fmv18x_probe(dev)
+ {fmv18x_probe, 0},
#endif
#ifdef CONFIG_ETH16I
- && eth16i_probe(dev) /* ICL EtherTeam 16i/32 */
-#endif
-#ifdef CONFIG_EL3 /* 3c509 */
- && el3_probe(dev)
+ {eth16i_probe, 0}, /* ICL EtherTeam 16i/32 */
#endif
#ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */
- && znet_probe(dev)
+ {znet_probe, 0},
#endif
#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */
- && express_probe(dev)
+ {express_probe, 0},
#endif
#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
- && eepro_probe(dev)
-#endif
-#ifdef CONFIG_EEXPRESS_PRO100 /* Intel EtherExpress Pro/100 */
- && eepro100_probe(dev)
+ {eepro_probe, 0},
#endif
#ifdef CONFIG_DEPCA /* DEC DEPCA */
- && depca_probe(dev)
+ {depca_probe, 0},
#endif
#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
- && ewrk3_probe(dev)
+ {ewrk3_probe, 0},
#endif
-#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
- && de4x5_probe(dev)
-#endif
-#ifdef CONFIG_APRICOT /* Apricot I82596 */
- && apricot_probe(dev)
+#if defined(CONFIG_APRICOT) || defined(CONFIG_MVME16x_NET) || defined(CONFIG_BVME6000_NET) /* Intel I82596 */
+ {i82596_probe, 0},
#endif
#ifdef CONFIG_EL1 /* 3c501 */
- && el1_probe(dev)
+ {el1_probe, 0},
#endif
-#if defined(CONFIG_WAVELAN) /* WaveLAN */
- && wavelan_probe(dev)
-#endif /* defined(CONFIG_WAVELAN) */
-#ifdef CONFIG_EL16 /* 3c507 */
- && el16_probe(dev)
+#ifdef CONFIG_WAVELAN /* WaveLAN */
+ {wavelan_probe, 0},
#endif
-#ifdef CONFIG_ELMC /* 3c523 */
- && elmc_probe(dev)
+#ifdef CONFIG_EL16 /* 3c507 */
+ {el16_probe, 0},
#endif
#ifdef CONFIG_ELPLUS /* 3c505 */
- && elplus_probe(dev)
-#endif
-#ifdef CONFIG_DE600 /* D-Link DE-600 adapter */
- && de600_probe(dev)
+ {elplus_probe, 0},
#endif
-#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
- && de620_probe(dev)
-#endif
-#if defined(CONFIG_SK_G16)
- && SK_init(dev)
+#ifdef CONFIG_SK_G16
+ {SK_init, 0},
#endif
#ifdef CONFIG_NI5010
- && ni5010_probe(dev)
+ {ni5010_probe, 0},
#endif
#ifdef CONFIG_NI52
- && ni52_probe(dev)
+ {ni52_probe, 0},
#endif
#ifdef CONFIG_NI65
- && ni65_probe(dev)
+ {ni65_probe, 0},
+#endif
+ {NULL, 0},
+};
+
+struct devprobe parport_probes[] __initdata = {
+#ifdef CONFIG_DE600 /* D-Link DE-600 adapter */
+ {de600_probe, 0},
+#endif
+#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
+ {de620_probe, 0},
#endif
+#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
+ {atp_init, 0},
+#endif
+ {NULL, 0},
+};
+
+struct devprobe m68k_probes[] __initdata = {
#ifdef CONFIG_ATARILANCE /* Lance-based Atari ethernet boards */
- && atarilance_probe(dev)
+ {atarilance_probe, 0},
#endif
#ifdef CONFIG_A2065 /* Commodore/Ameristar A2065 Ethernet Board */
- && a2065_probe(dev)
+ {a2065_probe, 0},
#endif
#ifdef CONFIG_ARIADNE /* Village Tronic Ariadne Ethernet Board */
- && ariadne_probe(dev)
+ {ariadne_probe, 0},
#endif
#ifdef CONFIG_HYDRA /* Hydra Systems Amiganet Ethernet board */
- && hydra_probe(dev)
+ {hydra_probe, 0},
#endif
#ifdef CONFIG_ATARI_BIONET /* Atari Bionet Ethernet board */
- && bionet_probe(dev)
+ {bionet_probe, 0},
#endif
#ifdef CONFIG_ATARI_PAMSNET /* Atari PAMsNet Ethernet board */
- && pamsnet_probe(dev)
+ {pamsnet_probe, 0},
#endif
#ifdef CONFIG_HPLANCE /* HP300 internal Ethernet */
- && hplance_probe(dev)
-#endif
-#ifdef CONFIG_SUNLANCE
- && sparc_lance_probe(dev)
-#endif
-#ifdef CONFIG_TLAN
- && tlan_probe(dev)
-#endif
-#ifdef CONFIG_SUNQE
- && qec_probe(dev)
-#endif
-#ifdef CONFIG_MYRI_SBUS
- && myri_sbus_probe(dev)
+ {hplance_probe, 0},
#endif
+ {NULL, 0},
+};
+
+struct devprobe ppc_probes[] __initdata = {
#ifdef CONFIG_MACE
- && mace_probe(dev)
+ {mace_probe, 0},
#endif
+ {NULL, 0},
+};
+
+struct devprobe sgi_probes[] __initdata = {
#ifdef CONFIG_SGISEEQ
- && sgiseeq_probe(dev)
+ {sgiseeq_probe, 0},
#endif
+ {NULL, 0},
+};
+
+struct devprobe mips_probes[] __initdata = {
#ifdef CONFIG_MIPS_JAZZ_SONIC
- && sonic_probe(dev)
-#endif
-#ifdef CONFIG_ARCH_ACORN
- && acorn_ethif_probe(dev)
+ {sonic_probe, 0},
#endif
-#ifdef CONFIG_ARM_AM79C961A
- && am79c961_probe(dev)
+ {NULL, 0},
+};
+
+struct devprobe arm_probes[] __initdata = {
+#ifdef CONFIG_ARM_ETHERH
+ {etherh_probe , 0},
#endif
-#ifdef CONFIG_EPIC100
- && epic100_probe(dev)
+#ifdef CONFIG_ARM_ETHER3
+ {ether3_probe , 0},
#endif
-#ifdef CONFIG_RTL8139
- && rtl8139_probe(dev)
+#ifdef CONFIG_ARM_ETHER1
+ {ether1_probe , 0},
#endif
-#ifdef CONFIG_YELLOWFIN
- && yellowfin_probe(dev)
+#ifdef CONFIG_ARM_AM79C961A
+ {am79c961_probe, 0},
#endif
- && 1 ) {
- return 1; /* -ENODEV or -EAGAIN would be more accurate. */
- }
- return 0;
-}
+ {NULL, 0},
+};
+/*
+ * Unified ethernet device probe, segmented per architecture and
+ * per bus interface.
+ */
+__initfunc(static int ethif_probe(struct device *dev))
+{
+ unsigned long base_addr = dev->base_addr;
+
+ /*
+ * Backwards compatibility - historically an I/O base of 1 was
+ * used to indicate not to probe for this ethN interface
+ */
+ if (base_addr == 1)
+ return 1; /* ENXIO */
+
+ /*
+ * The arch specific probes are 1st so that any on-board ethernet
+ * will be probed before other ISA/EISA/MCA/PCI bus cards.
+ */
+ if (probe_list(dev, arm_probes) == 0)
+ return 0;
+ if (probe_list(dev, m68k_probes) == 0)
+ return 0;
+ if (probe_list(dev, mips_probes) == 0)
+ return 0;
+ if (probe_list(dev, ppc_probes) == 0)
+ return 0;
+ if (probe_list(dev, sgi_probes) == 0)
+ return 0;
+ if (probe_list(dev, sparc_probes) == 0)
+ return 0;
+ if (probe_list(dev, pci_probes) == 0)
+ return 0;
+ if (probe_list(dev, eisa_probes) == 0)
+ return 0;
+ if (probe_list(dev, mca_probes) == 0)
+ return 0;
+ /*
+ * Backwards compatibility - an I/O of 0xffe0 was used to indicate
+ * that we shouldn't do a bunch of potentially risky ISA probes
+ * for ethN (N>1). Since the widespread use of modules, *nobody*
+ * compiles a kernel with all the ISA drivers built in anymore,
+ * and so we should delete this check in linux 2.3 - Paul G.
+ */
+ if (base_addr != 0xffe0 && probe_list(dev, isa_probes) == 0)
+ return 0;
+ if (probe_list(dev, parport_probes) == 0)
+ return 0;
+ return -ENODEV;
+}
#ifdef CONFIG_FDDI
__initfunc(static int fddiif_probe(struct device *dev))
{
unsigned long base_addr = dev->base_addr;
- if ((base_addr == 0xffe0) || (base_addr == 1))
+ if (base_addr == 1)
return 1; /* ENXIO */
if (1
# define NEXT_DEV (&sdla0_dev)
#endif
-/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
-#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
-static struct device atp_dev = {
- "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ };
-# undef NEXT_DEV
-# define NEXT_DEV (&atp_dev)
-#endif
-
#if defined(CONFIG_LTPC)
extern int ltpc_probe(struct device *);
static struct device dev_ltpc = {
# define ETH0_IRQ 0
#endif
-#if !defined(__sparc__) && !defined(CONFIG_ARCH_ACORN)
-#define ETH_NOPROBE_ADDR 0xffe0
-#else
-#define ETH_NOPROBE_ADDR 0
-#endif
-
/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
- which means "don't probe". These entries exist to only to provide empty
- slots which may be enabled at boot-time. */
+ which means "don't do ISA probes". Distributions don't ship kernels with
+ all ISA drivers compiled in anymore, so its probably no longer an issue. */
+
+#define ETH_NOPROBE_ADDR 0xffe0
static struct device eth7_dev = {
"eth7", 0,0,0,0,ETH_NOPROBE_ADDR /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
if (pci_present()) {
static int pci_index = 0;
+
for (; pci_index < 8; pci_index++) {
unsigned char pci_bus, pci_device_fn, pci_latency;
#if (LINUX_VERSION_CODE >= VERSION(2,1,85))
unsigned short pci_command;
if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82557,
- pci_index, &pci_bus,
- &pci_device_fn))
+ PCI_DEVICE_ID_INTEL_82557,
+ pci_index, &pci_bus,
+ &pci_device_fn))
break;
#if (LINUX_VERSION_CODE >= VERSION(2,1,85))
pdev = pci_find_slot(pci_bus, pci_device_fn);
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
+#include <linux/init.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/spinlock.h>
}
/* called from init/main.c */
-void aha1542_setup( char *str, int *ints)
+__initfunc(void aha1542_setup( char *str, int *ints))
{
const char *ahausage = "aha1542: usage: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]\n";
static int setup_idx = 0;
return result;
out_swapfile:
- printk("NFS: attempt to write to active swap file!\n");
+ printk(KERN_ERR "NFS: attempt to write to active swap file!\n");
goto out;
}
static void nfs_delete_inode(struct inode *);
static int nfs_notify_change(struct dentry *, struct iattr *);
static void nfs_put_super(struct super_block *);
+static void nfs_umount_begin(struct super_block *);
static int nfs_statfs(struct super_block *, struct statfs *, int);
static struct super_operations nfs_sops = {
nfs_put_super, /* put superblock */
NULL, /* write superblock */
nfs_statfs, /* stat filesystem */
- NULL
+ NULL, /* no remount */
+ NULL, /* no clear inode */
+ nfs_umount_begin /* umount attempt begin */
};
struct rpc_stat nfs_rpcstat = { &nfs_program };
MOD_DEC_USE_COUNT;
}
+void
+nfs_umount_begin(struct super_block *sb)
+{
+ struct nfs_server *server = &sb->u.nfs_sb.s_server;
+ struct rpc_clnt *rpc;
+
+ /* -EIO all pending I/O */
+ if ((rpc = server->client) != NULL)
+ rpc_killall_tasks(rpc);
+}
+
/*
* Compute and set NFS server blocksize
*/
interruptible_sleep_on(&inode->i_wait);
#else
dprintk("nfsd: write defer %d\n", current->pid);
- need_resched = 1;
+ current->need_resched = 1;
current->timeout = jiffies + HZ / 100;
schedule();
dprintk("nfsd: write resume %d\n", current->pid);
(void) acct_auto_close(dev);
#endif
+ /*
+ * If we may have to abort operations to get out of this
+ * mount, and they will themselves hold resources we must
+ * allow the fs to do things. In the Unix tradition of
+ * 'Gee thats tricky lets do it in userspace' the umount_begin
+ * might fail to complete on the first run through as other tasks
+ * must return, and the like. Thats for the mount program to worry
+ * about for the moment.
+ */
+
+ if(sb->s_op->umount_begin)
+ sb->s_op->umount_begin(sb);
+
/*
* Shrink dcache, then fsync. This guarantees that if the
* filesystem is quiescent at this point, then (a) only the
* we give them the info they need without using a real inode.
* If any other fields are ever needed by any block device release
* functions, they should be faked here. -- jrs
+ *
+ * For 2.3.x we want a new sys_umount syscall with flags (ie 'force')
*/
asmlinkage int sys_umount(char * name)
return qofs*8 + bofs;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
/*
* Find next zero bit in a bitmap reasonably efficiently..
return k;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
#ifdef __KERNEL__
return ((mask & *addr) != 0);
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
+
#endif /* _ASM_GENERIC_BITOPS_H */
return word;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
#ifdef __KERNEL__
check_amd_k6();
check_pentium_f00f();
system_utsname.machine[1] = '0' + boot_cpu_data.x86;
-#if !defined(__SMP__) && defined(CONFIG_MTRR)
- /* Must be done after other processors booted: at this point we are
- called before SMP initialisation, so this is for the non-SMP case
- only. The SMP case is handled in arch/i386/kernel/smp.c */
+#if defined(CONFIG_MTRR)
mtrr_init ();
#endif
}
--- /dev/null
+/*
+ * fixmap.h: compile-time virtual memory allocation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 Ingo Molnar
+ */
+
+#ifndef _ASM_FIXMAP_H
+#define _ASM_FIXMAP_H
+
+#include <asm/page.h>
+#include <linux/kernel.h>
+
+/*
+ * Here we define all the compile-time 'special' virtual
+ * addresses. The point is to have a constant address at
+ * compile time, but to set the physical address only
+ * in the boot process. We allocate these special addresses
+ * from the end of virtual memory (0xfffff000) backwards.
+ * Also this lets us do fail-safe vmalloc(), we
+ * can guarantee that these special addresses and
+ * vmalloc()-ed addresses never overlap.
+ *
+ * these 'compile-time allocated' memory buffers are
+ * fixed-size 4k pages. (or larger if used with an increment
+ * bigger than 1) use fixmap_set(idx,phys) to associate
+ * physical memory with fixmap indices.
+ *
+ * TLB entries of such buffers will not be flushed across
+ * task switches.
+ */
+
+enum fixed_addresses {
+/*
+ * on UP currently we will have no trace of the fixmap mechanizm,
+ * no page table allocations, etc. This might change in the
+ * future, say framebuffers for the console driver(s) could be
+ * fix-mapped?
+ */
+#if __SMP__
+ FIX_APIC_BASE = 1, /* 0xfffff000 */
+ FIX_IO_APIC_BASE = 2, /* 0xffffe000 */
+#endif
+ __end_of_fixed_addresses
+};
+
+extern void set_fixmap (enum fixed_addresses idx, unsigned long phys);
+
+/*
+ * used by vmalloc.c:
+ */
+#define FIXADDR_START (0UL-((__end_of_fixed_addresses-1)<<PAGE_SHIFT))
+
+/*
+ * 'index to address' translation. If anyone tries to use the idx
+ * directly without tranlation, we catch the bug with a NULL-deference
+ * kernel oops. Illegal ranges of incoming indices are caught too.
+ */
+extern inline unsigned long fix_to_virt(const unsigned int idx)
+{
+ /*
+ * this branch gets completely eliminated after inlining,
+ * except when someone tries to use fixaddr indices in an
+ * illegal way. (such as mixing up address types or using
+ * out-of-range indices)
+ */
+ if ((!idx) || (idx >= __end_of_fixed_addresses))
+ panic("illegal fixaddr index!");
+
+ return (0UL-(unsigned long)(idx<<PAGE_SHIFT));
+}
+
+#endif
*/
#ifndef __ASSEMBLY__
#include <asm/processor.h>
+#include <asm/fixmap.h>
#include <linux/tasks.h>
/* Caches aren't brain-dead on the intel. */
#define VMALLOC_OFFSET (8*1024*1024)
#define VMALLOC_START (((unsigned long) high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END (FIXADDR_START)
/*
* The 4MB page is guessing.. Detailed in the infamous "Chapter H"
unsigned long tr;
unsigned long cr2, trap_no, error_code;
mm_segment_t segment;
+/* debug registers */
+ long debugreg[8]; /* Hardware debugging registers */
/* floating point info */
union i387_union i387;
/* virtual 86 mode info */
0, 0x8000, /* tace, bitmap */ \
{~0, }, /* ioperm */ \
_TSS(0), 0, 0, 0, (mm_segment_t) { 0 }, /* obsolete */ \
+ { 0, }, \
{ { 0, }, }, /* 387 state */ \
NULL, 0, 0, 0, 0, 0, /* vm86_info */ \
}
#include <asm/i82489.h>
#include <asm/bitops.h>
+#include <asm/fixmap.h>
#include <linux/tasks.h>
#include <linux/ptrace.h>
* "Back to Back Assertions of HOLD May Cause Lost APIC Write Cycle"
*/
-#define APIC_BASE ((char *)0xFEE00000)
+#define APIC_BASE (fix_to_virt(FIX_APIC_BASE))
extern __inline void apic_write(unsigned long reg, unsigned long v)
{
return res ^ 31;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
+
/* Bitmap functions for the minix filesystem */
extern __inline__ int
return __res;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
+
#ifdef __MIPSEB__
/* For now I steal the Sparc C versions, no need for speed, just need to
* get it working.
return 31 - n;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
+
/*
* This implementation of find_{first,next}_zero_bit was stolen from
* Linus' asm-alpha/bitops.h.
return result;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight16(x) generic_hweight16(x)
#define hweight8(x) generic_hweight8(x)
+#endif /* __KERNEL__ */
/* find_next_zero_bit() finds the first zero bit in a bit string of length
* 'size' bits, starting the search at bit 'offset'. This is largely based
return result;
}
+#ifdef __KERNEL__
+
/*
* ffs: find first bit set. This is defined the same way as
* the libc and compiler builtin ffs routines, therefore
#define hweight8(x) generic_hweight8(x)
#endif
+#endif /* __KERNEL__ */
/* find_next_zero_bit() finds the first zero bit in a bit string of length
* 'size' bits, starting the search at bit 'offset'. This is largely based
{
struct device *dev;
struct at_addr address; /* Our address */
- int status; /* What are we doing ?? */
+ int status; /* What are we doing? */
#define ATIF_PROBE 1 /* Probing for an address */
#define ATIF_PROBE_FAIL 2 /* Probe collided */
struct netrange nets; /* Associated direct netrange */
/* And netatalk apps expect to stick the type in themselves */
};
+/*
+ * Don't drop the struct into the struct above. You'll get some
+ * surprise padding.
+ */
+
+struct ddpebits
+{
+#ifdef __LITTLE_ENDIAN_BITFIELD
+ __u16 deh_len:10, deh_hops:4, deh_pad:2;
+#else
+ __u16 deh_pad:2, deh_hops:4, deh_len:10;
+#endif
+};
+
/*
* Short form header
*/
int (*statfs) (struct super_block *, struct statfs *, int);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
+ void (*umount_begin) (struct super_block *);
};
struct dquot_operations {
idea_key lo_idea_en_key;
idea_key lo_idea_de_key;
#endif
+ struct file * lo_backing_file;
};
typedef int (* transfer_proc_t)(struct loop_device *, int cmd,
#ifdef __KERNEL__
#include <linux/types.h>
+#include <linux/config.h>
/*
* There is one pci_dev structure for each slot-number/function-number
0-0xFFFFFFFF for kernel-thread
*/
struct exec_domain *exec_domain;
+ long need_resched;
/* various fields */
- long debugreg[8]; /* Hardware debugging registers */
long counter;
long priority;
struct linux_binfmt *binfmt;
* your own risk!. Base=0, limit=0x1fffff (=2MB)
*/
#define INIT_TASK \
-/* state etc */ { 0,0,0,KERNEL_DS,&default_exec_domain, \
-/* debugregs */ { 0, }, \
+/* state etc */ { 0,0,0,KERNEL_DS,&default_exec_domain,0, \
/* counter */ DEF_PRIORITY,DEF_PRIORITY, \
/* binfmt */ NULL, \
/* schedlink */ &init_task,&init_task, &init_task, &init_task, \
extern unsigned long itimer_ticks;
extern unsigned long itimer_next;
extern struct timeval xtime;
-extern int need_resched;
extern void do_timer(struct pt_regs *);
extern unsigned int * prof_buffer;
#if defined(CONFIG_QUOTA)
dquot_init_hash();
#endif
- check_bugs();
-
- printk("POSIX conformance testing by UNIFIX\n");
#ifdef __SMP__
smp_init();
#endif
+ printk("POSIX conformance testing by UNIFIX\n");
+ check_bugs();
+
sock_init();
#ifdef CONFIG_SYSCTL
sysctl_init();
EXPORT_SYMBOL(xtime);
EXPORT_SYMBOL(do_gettimeofday);
EXPORT_SYMBOL(loops_per_sec);
-EXPORT_SYMBOL(need_resched);
EXPORT_SYMBOL(kstat);
/* misc */
long time_adjust = 0;
long time_adjust_step = 0;
-int need_resched = 0;
unsigned long event = 0;
extern int do_setitimer(int, struct itimerval *, struct itimerval *);
void scheduling_functions_start_here(void) { }
-static inline void add_to_runqueue(struct task_struct * p)
+static inline void reschedule_idle(struct task_struct * p)
{
+ /*
+ * For SMP, we try to find another CPU to put the
+ * new task on, and fall back on the local CPU only
+ * if no other CPU is idle.
+ *
+ * FIXME: try to select the idle CPU to be the old
+ * CPU of the task 'p' if possible.
+ */
+#ifdef __SMP__
+ struct task_struct **idle = task;
+ int current_cpu = smp_processor_id();
+ int i = smp_num_cpus;
+
+ do {
+ struct task_struct *tsk = *idle;
+ idle++;
+ /* Something like this.. */
+ if (tsk->has_cpu && !tsk->need_resched && tsk->processor != current_cpu) {
+ tsk->need_resched = 1;
+ smp_send_reschedule(tsk->processor);
+ return;
+ }
+ } while (--i > 0);
+#endif
if (p->policy != SCHED_OTHER || p->counter > current->counter + 3)
- need_resched = 1;
+ current->need_resched = 1;
+}
+
+
+static inline void add_to_runqueue(struct task_struct * p)
+{
nr_running++;
+ reschedule_idle(p);
(p->prev_run = init_task.prev_run)->next_run = p;
p->next_run = &init_task;
init_task.prev_run = p;
unsigned long timeout;
int this_cpu;
- need_resched = 0;
prev = current;
this_cpu = smp_processor_id();
if (in_interrupt())
if (timeout)
del_timer(&timer);
}
+
spin_unlock(&scheduler_lock);
+ /*
+ * At this point "prev" is "current", as we just
+ * switched into it (from an even more "previous"
+ * prev)
+ */
+ prev->need_resched = 0;
reacquire_kernel_lock(prev, smp_processor_id(), lock_depth);
return;
p->counter -= ticks;
if (p->counter < 0) {
p->counter = 0;
- need_resched = 1;
+ p->need_resched = 1;
}
if (p->priority < DEF_PRIORITY)
kstat.cpu_nice += user;
if (p->next_run)
move_first_runqueue(p);
- need_resched = 1;
+ current->need_resched = 1;
out_unlock:
read_unlock(&tasklist_lock);
spin_lock(&scheduler_lock);
spin_lock_irq(&runqueue_lock);
current->policy |= SCHED_YIELD;
+ current->need_resched = 1;
move_last_runqueue(current);
spin_unlock_irq(&runqueue_lock);
spin_unlock(&scheduler_lock);
- need_resched = 1;
return 0;
}
struct vm_struct * get_vm_area(unsigned long size)
{
- void *addr;
+ unsigned long addr;
struct vm_struct **p, *tmp, *area;
area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
if (!area)
return NULL;
- addr = (void *) VMALLOC_START;
+ addr = VMALLOC_START;
for (p = &vmlist; (tmp = *p) ; p = &tmp->next) {
- if (size + (unsigned long) addr < (unsigned long) tmp->addr)
+ if (size + addr < (unsigned long) tmp->addr)
break;
- addr = (void *) (tmp->size + (unsigned long) tmp->addr);
+ if (addr > VMALLOC_END-size)
+ return NULL;
+ addr = tmp->size + (unsigned long) tmp->addr;
}
- area->addr = addr;
+ area->addr = (void *)addr;
area->size = size + PAGE_SIZE;
area->next = *p;
*p = area;
*
* References:
* Inside AppleTalk (2nd Ed).
+ * Fixes:
+ * Jaume Grau - flush caches on AARP_PROBE
+ *
*/
#include <linux/config.h>
sa.s_node=ea->pa_dst_node;
sa.s_net=ea->pa_dst_net;
+ if(ea->function==AARP_PROBE)
+ {
+ /* A probe implies someone trying to get an
+ address. So as a precaution flush any
+ entries we have for this address */
+ struct aarp_entry *a=aarp_find_entry(
+ resolved[sa.s_node%(AARP_HASH_SIZE-1)],
+ skb->dev,
+ &sa);
+ /* Make it expire next tick - that avoids us
+ getting into a probe/flush/learn/probe/flush/learn
+ cycle during probing of a slow to respond host addr */
+ if(a!=NULL)
+ a->expires_at=jiffies-1;
+ }
if(sa.s_node!=ma->s_node)
break;
if(sa.s_net && ma->s_net && sa.s_net!=ma->s_net)
struct atalk_iface *atif;
struct sockaddr_at tosat;
int origlen;
+ struct ddpebits ddphv;
/* Size check */
if(skb->len < sizeof(*ddp))
* run until we put it back)
*/
- *((__u16 *)ddp) = ntohs(*((__u16 *)ddp));
+ *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp));
/*
* Trim buffer in case of stray trailing data
origlen = skb->len;
- skb_trim(skb, min(skb->len, ddp->deh_len));
+ skb_trim(skb, min(skb->len, ddphv.deh_len));
/*
* Size check to see if ddp->deh_len was crap
* Any checksums. Note we don't do htons() on this == is assumed to be
* valid for net byte orders all over the networking code...
*/
- if(ddp->deh_sum && atalk_checksum(ddp, ddp->deh_len) != ddp->deh_sum)
+ if(ddp->deh_sum && atalk_checksum(ddp, ddphv.deh_len) != ddp->deh_sum)
{
/* Not a valid AppleTalk frame - dustbin time */
kfree_skb(skb);
/* Route the packet */
rt = atrtr_find(&ta);
- if(rt == NULL || ddp->deh_hops == DDP_MAXHOPS)
+ if(rt == NULL || ddphv.deh_hops == DDP_MAXHOPS)
{
kfree_skb(skb);
return (0);
}
- ddp->deh_hops++;
+ ddphv.deh_hops++;
/*
* Route goes through another gateway, so
/* Fix up skb->len field */
skb_trim(skb, min(origlen, rt->dev->hard_header_len +
- ddp_dl->header_length + ddp->deh_len));
+ ddp_dl->header_length + ddphv.deh_len));
/* Mend the byte order */
- *((__u16 *)ddp) = ntohs(*((__u16 *)ddp));
+ *((__u16 *)ddp) = ntohs(*((__u16 *)&ddphv));
/*
* Send the buffer onwards
struct sock *sk=sock->sk;
struct sockaddr_at *sat=(struct sockaddr_at *)msg->msg_name;
struct ddpehdr *ddp = NULL;
+ struct ddpebits ddphv;
int copied = 0;
struct sk_buff *skb;
int err = 0;
+
skb = skb_recv_datagram(sk,flags&~MSG_DONTWAIT,flags&MSG_DONTWAIT,&err);
if(skb == NULL)
return (err);
ddp = (struct ddpehdr *)(skb->h.raw);
+ *((__u16 *)&ddphv) = ntohs(*((__u16 *)ddp));
+
if(sk->type == SOCK_RAW)
{
- copied = ddp->deh_len;
+ copied = ddphv.deh_len;
if(copied > size)
{
copied = size;
}
else
{
- copied = ddp->deh_len - sizeof(*ddp);
+ copied = ddphv.deh_len - sizeof(*ddp);
if(copied > size)
{
copied = size;
unsigned long irqflags;
int async, status;
+ /* If this client is slain all further I/O fails */
+ if (clnt->cl_dead)
+ return -EIO;
+
/* Turn off various signals */
if (clnt->cl_intr) {
struct k_sigaction *action = current->sig->action;
* The following is an NFS-specific hack to cater for setuid
* processes whose uid is mapped to nobody on the server.
*/
- if (task->tk_client->cl_prog == 100003 && ntohl(*p) == NFSERR_PERM) {
+ if (task->tk_client->cl_prog == 100003 &&
+ (ntohl(*p) == NFSERR_ACCES || ntohl(*p) == NFSERR_PERM)) {
if (RPC_IS_SETUID(task) && (task->tk_suid_retry)--) {
dprintk("RPC: %4d retry squashed uid\n", task->tk_pid);
task->tk_flags ^= RPC_CALL_REALUID;
struct rpc_task *task;
int count = 0;
unsigned long oldflags;
+ int need_resched = current->need_resched;
dprintk("RPC: rpc_schedule enter\n");
+ save_flags(oldflags);
while (1) {
- save_flags(oldflags); cli();
+ cli();
if (!(task = schedq.task))
break;
rpc_del_timer(task);
EXPORT_SYMBOL(rpc_create_client);
EXPORT_SYMBOL(rpc_destroy_client);
EXPORT_SYMBOL(rpc_shutdown_client);
+EXPORT_SYMBOL(rpc_killall_tasks);
EXPORT_SYMBOL(rpc_do_call);
EXPORT_SYMBOL(rpc_call_setup);
EXPORT_SYMBOL(rpc_delay);
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+/*
+ * Changes by Clifford Wolf (god@clifford.at)
+ *
+ * [ 1998-06-13 ]
+ *
+ * *) A bugfix for the Page-Down problem
+ *
+ * *) Formerly when I used Page Down and Page Up, the cursor would be set
+ * to the first position in the menu box. Now lxdialog is a bit
+ * smarter and works more like other menu systems (just have a look at
+ * it).
+ *
+ * *) Formerly if I selected something my scrolling would be broken because
+ * lxdialog is re-invoked by the Menuconfig shell script, can't
+ * remember the last scrolling position, and just sets it so that the
+ * cursor is at the bottom of the box. Now it writes the temporary file
+ * lxdialog.scrltmp which contains this information. The file is
+ * deleted by lxdialog if the user leaves a submenu or enters a new
+ * one, but it would be nice if Menuconfig could make another "rm -f"
+ * just to be sure. Just try it out - you will recognise a difference!
+ *
+ * [ 1998-06-14 ]
+ *
+ * *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
+ * and menus change their size on the fly.
+ *
+ * *) If for some reason the last scrolling position is not saved by
+ * lxdialog, it sets the scrolling so that the selected item is in the
+ * middle of the menu box, not at the bottom.
+ */
+
#include "dialog.h"
static int menu_width, item_x;
int i, j, x, y, box_x, box_y;
int key = 0, button = 0, scroll = 0, choice = 0, first_item = 0, max_choice;
WINDOW *dialog, *menu;
+ FILE *f;
max_choice = MIN (menu_height, item_no);
item_x = (menu_width - item_x) / 2;
- if (choice >= max_choice){
- scroll = first_item = choice - max_choice + 1;
- choice = max_choice-1;
+ /* get the scroll info from the temp file */
+ if ( (f=fopen("lxdialog.scrltmp","r")) != NULL ) {
+ if ( (fscanf(f,"%d\n",&scroll) == 1) && (scroll <= choice) &&
+ (scroll+max_choice > choice) && (scroll >= 0) &&
+ (scroll+max_choice <= item_no) ) {
+ first_item = scroll;
+ choice = choice - scroll;
+ fclose(f);
+ } else {
+ remove("lxdialog.scrltmp");
+ fclose(f);
+ f=NULL;
+ }
+ }
+ if ( (choice >= max_choice) || (f==NULL && choice >= max_choice/2) ) {
+ if (choice >= item_no-max_choice/2)
+ scroll = first_item = item_no-max_choice;
+ else
+ scroll = first_item = choice - max_choice/2;
+ choice = choice - scroll;
}
/* Print the menu */
} else if (key == KEY_PPAGE) {
scrollok (menu, TRUE);
- for (i=0; (i < max_choice) && (scroll > 0); i++) {
- wscrl (menu, -1);
- scroll--;
- print_item (menu, items[scroll * 2 + 1], 0, FALSE,
- (items[scroll*2][0] != ':'));
+ for (i=0; (i < max_choice); i++) {
+ if (scroll > 0) {
+ wscrl (menu, -1);
+ scroll--;
+ print_item (menu, items[scroll * 2 + 1], 0, FALSE,
+ (items[scroll*2][0] != ':'));
+ } else {
+ if (choice > 0)
+ choice--;
+ }
}
scrollok (menu, FALSE);
- choice = 0;
} else if (key == KEY_NPAGE) {
- scrollok (menu, TRUE);
- for (i=0; (i < max_choice) && (scroll+max_choice < item_no); i++) {
- scroll(menu);
- scroll++;
- print_item (menu, items[(scroll+max_choice-1)*2+1],
- max_choice-1, FALSE,
- (items[(scroll+max_choice-1)*2][0] != ':'));
+ for (i=0; (i < max_choice); i++) {
+ if (scroll+max_choice < item_no) {
+ scrollok (menu, TRUE);
+ scroll(menu);
+ scrollok (menu, FALSE);
+ scroll++;
+ print_item (menu, items[(scroll+max_choice-1)*2+1],
+ max_choice-1, FALSE,
+ (items[(scroll+max_choice-1)*2][0] != ':'));
+ } else {
+ if (choice+1 < max_choice)
+ choice++;
+ }
}
- scrollok (menu, FALSE);
- choice = 0;
} else
choice = i;
case 'y':
case 'n':
case 'm':
+ /* save scroll info */
+ if ( (f=fopen("lxdialog.scrltmp","w")) != NULL ) {
+ fprintf(f,"%d\n",scroll);
+ fclose(f);
+ }
delwin (dialog);
fprintf(stderr, "%s\n", items[(scroll + choice) * 2]);
switch (key) {
else
fprintf(stderr, "%s\n", items[(scroll + choice) * 2]);
+ remove("lxdialog.scrltmp");
return button;
case 'e':
case 'x':
}
delwin (dialog);
+ remove("lxdialog.scrltmp");
return -1; /* ESC pressed */
}