diff -u --recursive --new-file v1.3.82/linux/CREDITS linux/CREDITS --- v1.3.82/linux/CREDITS Sun Mar 31 00:13:16 1996 +++ linux/CREDITS Wed Apr 3 09:27:33 1996 @@ -867,9 +867,8 @@ S: Germany N: David C. Niemi -E: niemidc@clark.net -D: FSSTND, The XFree86 Project -D: DMA memory support, floppy driver +E: niemi@erols.com +D: Mtools/VFAT/floppy work, benchmarking, random kernel dilettante S: 2364 Old Trail Drive S: Reston, Virginia 22091 S: USA diff -u --recursive --new-file v1.3.82/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v1.3.82/linux/Documentation/Configure.help Tue Apr 2 13:32:18 1996 +++ linux/Documentation/Configure.help Tue Apr 2 13:33:13 1996 @@ -937,13 +937,14 @@ turn your Linux box into a fully featured Netware file server and IPX router, say Y here and fetch either lwared from sunsite.unc.edu:/pub/Linux/system/Network/daemons/ or mars_nwe from - linux01.gwdg.de:/pub/ncpfs. For more information, read the IPX-HOWTO - in sunsite.unc.edu:/pub/Linux/docs/howto. The IPX driver would - enlarge your kernel by about 5 kB. This driver is also available as - a module ( = code which can be inserted in and removed from the - running kernel whenever you want). If you want to compile it as a - module, say M here and read Documentation/modules.txt. Unless you - want to integrate your Linux box with a local Novell network, say N. + ftp.gwdg.de:/pub/linux/misc/ncpfs. For more information, read the + IPX-HOWTO in sunsite.unc.edu:/pub/Linux/docs/howto. The IPX driver + would enlarge your kernel by about 5 kB. This driver is also + available as a module ( = code which can be inserted in and removed + from the running kernel whenever you want). If you want to compile + it as a module, say M here and read Documentation/modules.txt. + Unless you want to integrate your Linux box with a local Novell + network, say N. Full internal IPX network CONFIG_IPX_INTERN @@ -958,7 +959,7 @@ packets targeted at 'special' sockets to sockets listening on the primary network is disabled. This might break existing applications, especially RIP/SAP daemons. A RIP/SAP daemon that works well with the - full internal net can be found on linux01.gwdg.de:/pub/ncpfs. + full internal net can be found on ftp.gwdg.de:/pub/linux/misc/ncpfs. If you don't know what you are doing, say N. Appletalk DDP diff -u --recursive --new-file v1.3.82/linux/Documentation/filesystems/ncpfs.txt linux/Documentation/filesystems/ncpfs.txt --- v1.3.82/linux/Documentation/filesystems/ncpfs.txt Thu Feb 22 15:20:19 1996 +++ linux/Documentation/filesystems/ncpfs.txt Tue Apr 2 13:33:13 1996 @@ -1,11 +1,12 @@ ncpfs is a filesystem which understands the NCP protocol, designed by the Novell Corporation for their NetWare(tm) product. NCP is functionally similar to the NFS used in the tcp/ip community. -To mount a Netware-Filesystem, you need a special mount program, which can -be found in ncpfs package. Homesite for ncpfs is linux01.gwdg.de/pub/ncpfs, -but sunsite and its many mirrors will have it as well. +To mount a Netware-Filesystem, you need a special mount program, which +can be found in ncpfs package. Homesite for ncpfs is +ftp.gwdg.de/pub/linux/misc/ncpfs, but sunsite and its many mirrors +will have it as well. Related products are linware and mars_nwe, which will give Linux partial -Netware Server functionality. +NetWare Server functionality. Linware's home site is: klokan.sh.cvut.cz/pub/linux/linware, -Mars_nwe can be found on linux01.gwdg.de/pub/ncpfs. +Mars_nwe can be found on ftp.gwdg.de/pub/linux/misc/ncpfs. diff -u --recursive --new-file v1.3.82/linux/Documentation/filesystems/smbfs.txt linux/Documentation/filesystems/smbfs.txt --- v1.3.82/linux/Documentation/filesystems/smbfs.txt Mon Aug 28 09:48:36 1995 +++ linux/Documentation/filesystems/smbfs.txt Tue Apr 2 13:33:13 1996 @@ -8,5 +8,6 @@ netbios name or share. To use smbfs, you need a special mount program, which can be found in -the ksmbfs package, found on sunsite.unc.edu:/pub/Linux/ALPHA/smbfs. +the ksmbfs package, found on +sunsite.unc.edu:/pub/Linux/system/Filesystems/smbfs. diff -u --recursive --new-file v1.3.82/linux/Makefile linux/Makefile --- v1.3.82/linux/Makefile Tue Apr 2 13:32:18 1996 +++ linux/Makefile Tue Apr 2 13:33:31 1996 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 82 +SUBLEVEL = 83 ARCH = i386 diff -u --recursive --new-file v1.3.82/linux/arch/alpha/boot/main.c linux/arch/alpha/boot/main.c --- v1.3.82/linux/arch/alpha/boot/main.c Sat Mar 16 13:52:12 1996 +++ linux/arch/alpha/boot/main.c Wed Apr 3 10:59:32 1996 @@ -149,7 +149,7 @@ printk("Ok (rev %lx)\n", rev); /* remove the old virtual page-table mapping */ L1[1] = 0; - invalidate_all(); + flush_tlb_all(); } extern int _end; diff -u --recursive --new-file v1.3.82/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c --- v1.3.82/linux/arch/alpha/kernel/ptrace.c Sun Oct 29 14:54:27 1995 +++ linux/arch/alpha/kernel/ptrace.c Wed Apr 3 10:59:32 1996 @@ -257,7 +257,7 @@ /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); - invalidate(); + flush_tlb(); } static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, diff -u --recursive --new-file v1.3.82/linux/arch/alpha/mm/init.c linux/arch/alpha/mm/init.c --- v1.3.82/linux/arch/alpha/mm/init.c Sun Mar 31 00:13:16 1996 +++ linux/arch/alpha/mm/init.c Wed Apr 3 10:59:32 1996 @@ -135,7 +135,7 @@ init_task.kernel_stack_page = INIT_STACK; load_PCB(&init_task.tss); - invalidate_all(); + flush_tlb_all(); return start_mem; } diff -u --recursive --new-file v1.3.82/linux/arch/i386/defconfig linux/arch/i386/defconfig --- v1.3.82/linux/arch/i386/defconfig Tue Apr 2 13:32:19 1996 +++ linux/arch/i386/defconfig Wed Apr 3 11:55:34 1996 @@ -141,6 +141,7 @@ # Character devices # CONFIG_SERIAL=y +# CONFIG_DIGI is not set # CONFIG_CYCLADES is not set # CONFIG_STALDRV is not set # CONFIG_PRINTER is not set diff -u --recursive --new-file v1.3.82/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c --- v1.3.82/linux/arch/i386/kernel/irq.c Tue Apr 2 13:32:19 1996 +++ linux/arch/i386/kernel/irq.c Wed Apr 3 10:59:32 1996 @@ -370,7 +370,7 @@ { struct irqaction * action = *(irq + irq_action); #ifdef __SMP__ - /* IRQ 13 is allowed - thats an invalidate */ + /* IRQ 13 is allowed - thats a flush tlb */ if(smp_threads_ready && active_kernel_processor!=smp_processor_id() && irq!=13) panic("fast_IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id()); #endif diff -u --recursive --new-file v1.3.82/linux/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c --- v1.3.82/linux/arch/i386/kernel/process.c Sat Mar 16 13:52:10 1996 +++ linux/arch/i386/kernel/process.c Wed Apr 3 10:59:32 1996 @@ -160,7 +160,7 @@ * Oops.. This is kind of important in some cases... */ if(clear_bit(smp_processor_id(), &smp_invalidate_needed)) - local_invalidate(); + local_flush_tlb(); } if (0==(0x7fffffff & smp_process_available)){ clear_bit(31,&smp_process_available); diff -u --recursive --new-file v1.3.82/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v1.3.82/linux/arch/i386/kernel/ptrace.c Thu Sep 7 17:54:27 1995 +++ linux/arch/i386/kernel/ptrace.c Wed Apr 3 10:59:32 1996 @@ -179,7 +179,7 @@ /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); - invalidate(); + flush_tlb(); } static struct vm_area_struct * find_extend_vma(struct task_struct * tsk, unsigned long addr) diff -u --recursive --new-file v1.3.82/linux/arch/i386/kernel/smp.c linux/arch/i386/kernel/smp.c --- v1.3.82/linux/arch/i386/kernel/smp.c Mon Mar 25 08:58:20 1996 +++ linux/arch/i386/kernel/smp.c Wed Apr 3 10:59:32 1996 @@ -346,7 +346,7 @@ cfg=pg0[0]; pg0[0] = (apic_addr | 7); - local_invalidate(); + local_flush_tlb(); boot_cpu_id = GET_APIC_ID(*((volatile unsigned long *) APIC_ID)); @@ -355,7 +355,7 @@ */ pg0[0]= cfg; - local_invalidate(); + local_flush_tlb(); /* * @@ -563,11 +563,11 @@ load_ldt(0); /* printk("Testing faulting...\n"); *(long *)0=1; OOPS... */ - local_invalidate(); + local_flush_tlb(); while(!smp_commenced); if (cpu_number_map[cpuid] == -1) while(1); - local_invalidate(); + local_flush_tlb(); SMP_PRINTK(("Commenced..\n")); load_TR(cpu_number_map[cpuid]); @@ -716,7 +716,7 @@ CMOS_WRITE(0xa, 0xf); pg0[0]=7; - local_invalidate(); + local_flush_tlb(); *((volatile unsigned short *) 0x469) = ((unsigned long)stack)>>4; *((volatile unsigned short *) 0x467) = 0; @@ -725,7 +725,7 @@ */ pg0[0]= cfg; - local_invalidate(); + local_flush_tlb(); /* * Be paranoid about clearing APIC errors. @@ -874,7 +874,7 @@ cfg = pg0[0]; pg0[0] = 3; /* writeable, present, addr 0 */ - local_invalidate(); + local_flush_tlb(); /* * Paranoid: Set warm reset code and vector here back @@ -890,7 +890,7 @@ */ pg0[0] = cfg; - local_invalidate(); + local_flush_tlb(); /* * Allow the user to impress friends. @@ -927,7 +927,7 @@ * get stuck with irq's off waiting to send a message and thus not replying to the person * spinning for a reply.... * - * In the end invalidate ought to be the NMI and a very very short function (to avoid the old + * In the end flush tlb ought to be the NMI and a very very short function (to avoid the old * IDE disk problems), and other messages sent with IRQ's enabled in a civilised fashion. That * will also boost performance. */ @@ -966,7 +966,7 @@ * Sanity check we don't re-enter this across CPU's. Only the kernel * lock holder may send messages. For a STOP_CPU we are bringing the * entire box to the fastest halt we can.. A reschedule carries - * no data and can occur during an invalidate.. guess what panic + * no data and can occur during a flush.. guess what panic * I got to notice this bug... */ @@ -1081,22 +1081,22 @@ } /* - * This is fraught with deadlocks. Linus does an invalidate at a whim - * even with IRQ's off. We have to avoid a pair of crossing invalidates + * This is fraught with deadlocks. Linus does a flush tlb at a whim + * even with IRQ's off. We have to avoid a pair of crossing flushes * or we are doomed. See the notes about smp_message_pass. */ -void smp_invalidate(void) +void smp_flush_tlb(void) { unsigned long flags; if(smp_activated && smp_processor_id()!=active_kernel_processor) - panic("CPU #%d:Attempted invalidate IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor); + panic("CPU #%d:Attempted flush tlb IPI when not AKP(=%d)\n",smp_processor_id(),active_kernel_processor); /* printk("SMI-");*/ /* * The assignment is safe because its volatile so the compiler cannot reorder it, * because the i586 has strict memory ordering and because only the kernel lock holder - * may issue an invalidate. If you break any one of those three change this to an atomic + * may issue a tlb flush. If you break any one of those three change this to an atomic * bus locked or. */ @@ -1104,7 +1104,7 @@ /* * Processors spinning on the lock will see this IRQ late. The smp_invalidate_needed map will - * ensure they dont do a spurious invalidate or miss one. + * ensure they dont do a spurious flush tlb or miss one. */ save_flags(flags); @@ -1115,7 +1115,7 @@ * Flush the local TLB */ - local_invalidate(); + local_flush_tlb(); restore_flags(flags); @@ -1174,7 +1174,7 @@ case MSG_INVALIDATE_TLB: if(clear_bit(i,(unsigned long *)&smp_invalidate_needed)) - local_invalidate(); + local_flush_tlb(); set_bit(i, (unsigned long *)&cpu_callin_map[0]); /* cpu_callin_map[0]|=1< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VERSION "1.5.4" +/*#define DEFAULT_HW_FLOW 1 */ +/*#define DEBUG_IOCTL */ + +#include "digi.h" +#include "fep.h" +#include "pcxx.h" +#include "digi_fep.h" +#include "digi_bios.h" +#include "pcxxconfig.h" + +#define DIGIMAJOR 30 +#define DIGICUMAJOR 31 + +#define MAXPORTS 16 /* Max ports per PC/Xx type board */ +#define NBDEVS (NUMCARDS * MAXPORTS) +#define PORTNUM(x) ((x)->dev % MAXPORTS) +#define LINENUM(x) (MINOR((x)->device) - (x)->driver.minor_start) + +int pcxx_ncook=sizeof(pcxx_cook); +int pcxx_nbios=sizeof(pcxx_bios); + +struct channel digi_channels[NBDEVS]; + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define pcxxassert(x, msg) if(!(x)) pcxx_error(__LINE__, msg) + +#define FEPTIMEOUT 200000 +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#define PCXE_EVENT_HANGUP 1 + +struct tty_driver pcxe_driver; +struct tty_driver pcxe_callout; +static int pcxe_refcount; + +DECLARE_TASK_QUEUE(tq_pcxx); +static struct tty_struct *pcxe_table[NBDEVS]; +static struct termios *pcxe_termios[NBDEVS]; +static struct termios *pcxe_termios_locked[NBDEVS]; + +static void pcxxpoll(void); +static void pcxxdelay(int); +static void fepcmd(struct channel *, int, int, int, int, int); +static void pcxe_put_char(struct tty_struct *, unsigned char); +static void pcxe_flush_chars(struct tty_struct *); +static void pcxx_error(int, char *); +static void pcxe_close(struct tty_struct *, struct file *); +static int pcxe_ioctl(struct tty_struct *, struct file *, unsigned int, unsigned long); +static void pcxe_set_termios(struct tty_struct *, struct termios *); +static int pcxe_write(struct tty_struct *, int, const unsigned char *, int); +static int pcxe_write_room(struct tty_struct *); +static int pcxe_chars_in_buffer(struct tty_struct *); +static void pcxe_flush_buffer(struct tty_struct *); +static void doevent(int); +static void receive_data(struct channel *); +static void pcxxparam(struct tty_struct *, struct channel *ch); +static void do_softint(void *); +static inline void pcxe_sched_event(struct channel *, int); +static void do_pcxe_bh(void); +static void pcxe_start(struct tty_struct *); +static void pcxe_stop(struct tty_struct *); +static void pcxe_throttle(struct tty_struct *); +static void pcxe_unthrottle(struct tty_struct *); +static void digi_send_break(struct channel *ch, int msec); +static void shutdown(struct channel *); +static void setup_empty_event(struct tty_struct *tty, struct channel *ch); +static inline void memwinon(struct board_info *b, unsigned int win); +static inline void memwinoff(struct board_info *b, unsigned int win); +static inline void globalwinon(struct channel *ch); +static inline void rxwinon(struct channel *ch); +static inline void txwinon(struct channel *ch); +static inline void memoff(struct channel *ch); +static inline void assertgwinon(struct channel *ch); +static inline void assertmemoff(struct channel *ch); + +#define TZ_BUFSZ 4096 + +static inline struct channel *chan(register struct tty_struct *tty) +{ + if (tty) { + register struct channel *ch=(struct channel *)tty->driver_data; + if ((ch >= &digi_channels[0]) && (ch < &digi_channels[NBDEVS])) { + if (ch->magic==PCXX_MAGIC) + return ch; + } + } + return NULL; +} + +/* These inline routines are to turn board memory on and off */ +static inline void memwinon(struct board_info *b, unsigned int win) +{ + if(b->type == PCXEVE) + outb_p(FEPWIN|win, b->port+1); + else + outb_p(inb(b->port)|FEPMEM, b->port); +} + +static inline void memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb(b->port)&~FEPMEM, b->port); + if(b->type == PCXEVE) + outb_p(0, b->port + 1); +} + +static inline void globalwinon(struct channel *ch) +{ + if(ch->board->type == PCXEVE) + outb_p(FEPWIN, ch->board->port+1); + else + outb_p(FEPMEM, ch->board->port); +} + +static inline void rxwinon(struct channel *ch) +{ + if(ch->rxwin == 0) + outb_p(FEPMEM, ch->board->port); + else + outb_p(ch->rxwin, ch->board->port+1); +} + +static inline void txwinon(struct channel *ch) +{ + if(ch->txwin == 0) + outb_p(FEPMEM, ch->board->port); + else + outb_p(ch->txwin, ch->board->port+1); +} + +static inline void memoff(struct channel *ch) +{ + outb_p(0, ch->board->port); + if(ch->board->type == PCXEVE) + outb_p(0, ch->board->port+1); +} + +static inline void assertgwinon(struct channel *ch) +{ + if(ch->board->type != PCXEVE) + pcxxassert(inb(ch->board->port) & FEPMEM, "Global memory off"); +} + +static inline void assertmemoff(struct channel *ch) +{ + if(ch->board->type != PCXEVE) + pcxxassert(!(inb(ch->board->port) & FEPMEM), "Memory on"); +} + +static inline void pcxe_sched_event(struct channel *info, int event) +{ + info->event |= 1 << event; + queue_task_irq_off(&info->tqueue, &tq_pcxx); + mark_bh(DIGI_BH); +} + +static void pcxx_error(int line, char *msg) +{ + printk("pcxx_error (DigiBoard): line=%d %s\n", line, msg); +} + +static int pcxx_waitcarrier(struct tty_struct *tty,struct file *filp,struct channel *info) +{ + struct wait_queue wait = { current, NULL }; + int retval = 0; + int do_clocal = 0; + + if (info->asyncflags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become free + */ + + retval = 0; + add_wait_queue(&info->open_wait, &wait); + info->count--; + info->blocked_open++; + + for (;;) { + cli(); + if ((info->asyncflags & ASYNC_CALLOUT_ACTIVE) == 0) { + globalwinon(info); + info->omodem |= DTR|RTS; + fepcmd(info, SETMODEM, DTR|RTS, 0, 10, 1); + memoff(info); + } + sti(); + current->state = TASK_INTERRUPTIBLE; + if(tty_hung_up_p(filp) || (info->asyncflags & ASYNC_INITIALIZED) == 0) { + if(info->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + if ((info->asyncflags & ASYNC_CALLOUT_ACTIVE) == 0 && + (info->asyncflags & ASYNC_CLOSING) == 0 && + (do_clocal || (info->imodem & info->dcd))) + break; + if(current->signal & ~current->blocked) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + + if(!tty_hung_up_p(filp)) + info->count++; + info->blocked_open--; + + return retval; +} + + +/* static ???why static??? */ +int pcxe_open(struct tty_struct *tty, struct file * filp) +{ + volatile struct board_chan *bc; + struct channel *ch; + unsigned long flags; + int line; + int boardnum; + int retval; + + line = MINOR(tty->device) - tty->driver.minor_start; + if(line < 0 || line >= NBDEVS) { + printk("line out of range in pcxe_open\n"); + tty->driver_data = NULL; + return(-ENODEV); + } + + boardnum = line / 16; + if(boardnum >= NUMCARDS || boards[boardnum].status == DISABLED || + (line % MAXPORTS) >= boards[boardnum].numports) { + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + ch = &digi_channels[line]; + + if(ch->brdchan == 0) { + tty->driver_data = NULL; + return(-ENODEV); + } + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if(ch->asyncflags & ASYNC_CLOSING) { + interruptible_sleep_on(&ch->close_wait); + if(ch->asyncflags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + save_flags(flags); + cli(); + ch->count++; + tty->driver_data = ch; + ch->tty = tty; + + if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) { + unsigned int head; + + globalwinon(ch); + ch->statusflags = 0; + bc=ch->brdchan; + ch->imodem = bc->mstat; + head = bc->rin; + bc->rout = head; + ch->tty = tty; + pcxxparam(tty,ch); + ch->imodem = bc->mstat; + bc->idata = 1; + ch->omodem = DTR|RTS; + fepcmd(ch, SETMODEM, DTR|RTS, 0, 10, 1); + memoff(ch); + ch->asyncflags |= ASYNC_INITIALIZED; + } + restore_flags(flags); + + if(ch->asyncflags & ASYNC_CLOSING) { + interruptible_sleep_on(&ch->close_wait); + if(ch->asyncflags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) { + if ((ch->asyncflags & ASYNC_SESSION_LOCKOUT) && + (ch->session != current->session)) + return -EBUSY; + if((ch->asyncflags & ASYNC_PGRP_LOCKOUT) && + (ch->pgrp != current->pgrp)) + return -EBUSY; + } + ch->asyncflags |= ASYNC_CALLOUT_ACTIVE; + } + else { + if (filp->f_flags & O_NONBLOCK) { + if(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + } + else { + if ((retval = pcxx_waitcarrier(tty, filp, ch)) != 0) + return retval; + } + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + } + + save_flags(flags); + cli(); + if((ch->count == 1) && (ch->asyncflags & ASYNC_SPLIT_TERMIOS)) { + if(tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = ch->normal_termios; + else + *tty->termios = ch->callout_termios; + globalwinon(ch); + pcxxparam(tty,ch); + memoff(ch); + } + + ch->session = current->session; + ch->pgrp = current->pgrp; + restore_flags(flags); + return 0; +} + +static void shutdown(struct channel *info) +{ + unsigned long flags; + volatile struct board_chan *bc; + struct tty_struct *tty; + + if (!(info->asyncflags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); + globalwinon(info); + + bc = info->brdchan; + if(bc) + bc->idata = 0; + + tty = info->tty; + + /* + * If we're a modem control device and HUPCL is on, drop RTS & DTR. + */ + if(tty->termios->c_cflag & HUPCL) { + info->omodem &= ~(RTS|DTR); + fepcmd(info, SETMODEM, 0, DTR|RTS, 10, 1); + } + + memoff(info); + info->asyncflags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + + +static void pcxe_close(struct tty_struct * tty, struct file * filp) +{ + struct channel *info; + + if ((info=chan(tty))!=NULL) { + unsigned long flags; + save_flags(flags); + cli(); + + if(tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + if (info->count-- > 1) { + restore_flags(flags); + return; + } + if (info->count < 0) { + info->count = 0; + } + + info->asyncflags |= ASYNC_CLOSING; + + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if(info->asyncflags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if(info->asyncflags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + tty->closing = 1; + if(info->asyncflags & ASYNC_INITIALIZED) { + setup_empty_event(tty,info); + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + } + + if(tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if(tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + shutdown(info); + tty->closing = 0; + info->event = 0; + info->tty = NULL; + if(tty->ldisc.num != ldiscs[N_TTY].num) { + if(tty->ldisc.close) + (tty->ldisc.close)(tty); + tty->ldisc = ldiscs[N_TTY]; + tty->termios->c_line = N_TTY; + if(tty->ldisc.open) + (tty->ldisc.open)(tty); + } + if(info->blocked_open) { + if(info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + info->close_delay; + schedule(); + } + wake_up_interruptible(&info->open_wait); + } + info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE| + ASYNC_CALLOUT_ACTIVE|ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + restore_flags(flags); + } +} + + +void pcxe_hangup(struct tty_struct *tty) +{ + struct channel *ch; + + if ((ch=chan(tty))!=NULL) { + unsigned long flags; + + save_flags(flags); + cli(); + shutdown(ch); + ch->event = 0; + ch->count = 0; + ch->tty = NULL; + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&ch->open_wait); + restore_flags(flags); + } +} + + + +static int pcxe_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +{ + struct channel *ch; + volatile struct board_chan *bc; + int total, remain, size, stlen; + unsigned int head, tail; + unsigned long flags; + + /* printk("Entering pcxe_write()\n"); */ + + if ((ch=chan(tty))==NULL) + return 0; + + bc = ch->brdchan; + size = ch->txbufsize; + + if (from_user) { + + save_flags(flags); + cli(); + globalwinon(ch); + head = bc->tin & (size - 1); + tail = bc->tout; + if (tail != bc->tout) + tail = bc->tout; + tail &= (size - 1); + stlen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1); + count = MIN(stlen, count); + if (count) { + if (verify_area(VERIFY_READ, (char*)buf, count)) + count=0; + else memcpy_fromfs(ch->tmp_buf, buf, count); + } + buf = ch->tmp_buf; + memoff(ch); + restore_flags(flags); + } + + /* + * All data is now local + */ + + total = 0; + save_flags(flags); + cli(); + globalwinon(ch); + head = bc->tin & (size - 1); + tail = bc->tout; + if (tail != bc->tout) + tail = bc->tout; + tail &= (size - 1); + if (head >= tail) { + remain = size - (head - tail) - 1; + stlen = size - head; + } + else { + remain = tail - head - 1; + stlen = remain; + } + count = MIN(remain, count); + + txwinon(ch); + while (count > 0) { + stlen = MIN(count, stlen); + memcpy(ch->txptr + head, buf, stlen); + buf += stlen; + count -= stlen; + total += stlen; + head += stlen; + if (head >= size) { + head = 0; + stlen = tail; + } + } + ch->statusflags |= TXBUSY; + globalwinon(ch); + bc->tin = head; + if ((ch->statusflags & LOWWAIT) == 0) { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + + return(total); +} + + +static void pcxe_put_char(struct tty_struct *tty, unsigned char c) +{ + pcxe_write(tty, 0, &c, 1); + return; +} + + +static int pcxe_write_room(struct tty_struct *tty) +{ + struct channel *ch; + int remain; + + remain = 0; + if ((ch=chan(tty))!=NULL) { + volatile struct board_chan *bc; + unsigned int head, tail; + unsigned long flags; + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + head = bc->tin & (ch->txbufsize - 1); + tail = bc->tout; + if (tail != bc->tout) + tail = bc->tout; + tail &= (ch->txbufsize - 1); + + if((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + if (remain && (ch->statusflags & LOWWAIT) == 0) { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + } + + return remain; +} + + +static int pcxe_chars_in_buffer(struct tty_struct *tty) +{ + int chars; + unsigned int ctail, head, tail; + int remain; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + if ((ch=chan(tty))==NULL) + return(0); + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + head = bc->tin; + ctail = ch->mailbox->cout; + if(tail == head && ch->mailbox->cin == ctail && bc->tbusy == 0) + chars = 0; + else { + head = bc->tin & (ch->txbufsize - 1); + tail &= (ch->txbufsize - 1); + if((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + chars = (int)(ch->txbufsize - remain); + + /* + * Make it possible to wakeup anything waiting for output + * in tty_ioctl.c, etc. + */ + if(!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + } + + memoff(ch); + restore_flags(flags); + + return(chars); +} + + +static void pcxe_flush_buffer(struct tty_struct *tty) +{ + unsigned int tail; + volatile struct board_chan *bc; + struct channel *ch; + unsigned long flags; + + if ((ch=chan(tty))==NULL) + return; + + save_flags(flags); + cli(); + + globalwinon(ch); + bc = ch->brdchan; + tail = bc->tout; + fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); + + memoff(ch); + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +static void pcxe_flush_chars(struct tty_struct *tty) +{ + struct channel * ch; + + if ((ch=chan(tty))!=NULL) { + unsigned long flags; + + save_flags(flags); + cli(); + if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + restore_flags(flags); + } +} + + +int pcxe_init(void) +{ + ulong flags, save_loops_per_sec, memory_seg=0, memory_size; + int lowwater, i, crd, shrinkmem=0, topwin = 0xff00L, botwin=0x100L; + unchar *fepos, *memaddr, *bios, v; + volatile struct global_data *gd; + struct board_info *bd; + volatile struct board_chan *bc; + struct channel *ch; + + init_bh(DIGI_BH,do_pcxe_bh); + enable_bh(DIGI_BH); + + timer_table[DIGI_TIMER].fn = pcxxpoll; + timer_table[DIGI_TIMER].expires = 0; + + memset(&pcxe_driver, 0, sizeof(struct tty_driver)); + pcxe_driver.magic = TTY_DRIVER_MAGIC; + pcxe_driver.name = "ttyd"; + pcxe_driver.major = DIGIMAJOR; + pcxe_driver.minor_start = 0; + pcxe_driver.num = NBDEVS; + pcxe_driver.type = TTY_DRIVER_TYPE_SERIAL; + pcxe_driver.subtype = SERIAL_TYPE_NORMAL; + pcxe_driver.init_termios = tty_std_termios; + pcxe_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pcxe_driver.flags = TTY_DRIVER_REAL_RAW; + pcxe_driver.refcount = &pcxe_refcount; + pcxe_driver.table = pcxe_table; + pcxe_driver.termios = pcxe_termios; + pcxe_driver.termios_locked = pcxe_termios_locked; + + pcxe_driver.open = pcxe_open; + pcxe_driver.close = pcxe_close; + pcxe_driver.write = pcxe_write; + pcxe_driver.put_char = pcxe_put_char; + pcxe_driver.flush_chars = pcxe_flush_chars; + pcxe_driver.write_room = pcxe_write_room; + pcxe_driver.chars_in_buffer = pcxe_chars_in_buffer; + pcxe_driver.flush_buffer = pcxe_flush_buffer; + pcxe_driver.ioctl = pcxe_ioctl; + pcxe_driver.throttle = pcxe_throttle; + pcxe_driver.unthrottle = pcxe_unthrottle; + pcxe_driver.set_termios = pcxe_set_termios; + pcxe_driver.stop = pcxe_stop; + pcxe_driver.start = pcxe_start; + pcxe_driver.hangup = pcxe_hangup; + + pcxe_callout = pcxe_driver; + pcxe_callout.name = "ttyD"; + pcxe_callout.major = DIGICUMAJOR; + pcxe_callout.subtype = SERIAL_TYPE_CALLOUT; + pcxe_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + + /* + * loops_per_sec hasn't been set at this point :-(, so fake it out... + * I set it so that I can use the __delay() function. + */ + save_loops_per_sec = loops_per_sec; + loops_per_sec = 13L*500000L; + + save_flags(flags); + cli(); + + for(crd=0; crd < NUMCARDS; crd++) { + bd = &boards[crd]; + outb(FEPRST, bd->port); + pcxxdelay(1); + + for(i=0; (inb(bd->port) & FEPMASK) != FEPRST; i++) { + if(i > 1000) { + printk("Board not found at port 0x%x! Check switch settings.\n", + bd->port); + bd->status = DISABLED; + break; + } + pcxxdelay(1); + } + if(bd->status == DISABLED) + continue; + + v = inb(bd->port); + + if((v & 0x1) == 0x1) { + if((v & 0x30) == 0) { /* PC/Xi 64K card */ + memory_seg = 0xf000; + memory_size = 0x10000; + } + + if((v & 0x30) == 0x10) { /* PC/Xi 128K card */ + memory_seg = 0xe000; + memory_size = 0x20000; + } + + if((v & 0x30) == 0x20) { /* PC/Xi 256K card */ + memory_seg = 0xc000; + memory_size = 0x40000; + } + + if((v & 0x30) == 0x30) { /* PC/Xi 512K card */ + memory_seg = 0x8000; + memory_size = 0x80000; + } + bd->type = PCXI; + } else { + if((v & 0x1) == 0x1) { + bd->status = DISABLED; /* PC/Xm unsupported card */ + printk("PC/Xm at 0x%x not supported!!\n", bd->port); + continue; + } else { + if(v & 0xC0) { + topwin = 0x1f00L; + outb((((ulong)bd->membase>>8) & 0xe0) | 0x10, bd->port+2); + outb(((ulong)bd->membase>>16) & 0xff, bd->port+3); + bd->type = PCXEVE; /* PC/Xe 8K card */ + } else { + bd->type = PCXE; /* PC/Xe 64K card */ + } + + memory_seg = 0xf000; + memory_size = 0x10000; + } + } + + memaddr = (unchar *) bd->membase; + + outb(FEPRST|FEPMEM, bd->port); + + for(i=0; (inb(bd->port) & FEPMASK) != (FEPRST|FEPMEM); i++) { + if(i > 10000) { + printk("%s not resetting at port 0x%x! Check switch settings.\n", + board_desc[bd->type], bd->port); + bd->status = DISABLED; + break; + } + pcxxdelay(1); + } + if(bd->status == DISABLED) + continue; + + memwinon(bd,0); + *(ulong *)(memaddr + botwin) = 0xa55a3cc3; + *(ulong *)(memaddr + topwin) = 0x5aa5c33c; + + if(*(ulong *)(memaddr + botwin) != 0xa55a3cc3 || + *(ulong *)(memaddr + topwin) != 0x5aa5c33c) { + printk("Failed memory test at %lx for %s at port %x, check switch settings.\n", + bd->membase, board_desc[bd->type], bd->port); + bd->status = DISABLED; + continue; + } + + for(i=0; i < 16; i++) { + memaddr[MISCGLOBAL+i] = 0; + } + + if(bd->type == PCXI || bd->type == PCXE) { + bios = memaddr + BIOSCODE + ((0xf000 - memory_seg) << 4); + + memcpy(bios, pcxx_bios, pcxx_nbios); + + outb(FEPMEM, bd->port); + + for(i=0; i <= 10000; i++) { + if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) { + goto load_fep; + } + pcxxdelay(1); + } + + printk("BIOS download failed on the %s at 0x%x!\n", + board_desc[bd->type], bd->port); + bd->status = DISABLED; + continue; + } + + if(bd->type == PCXEVE) { + bios = memaddr + (BIOSCODE & 0x1fff); + memwinon(bd,0xff); + + memcpy(bios, pcxx_bios, pcxx_nbios); + + outb(FEPCLR, bd->port); + memwinon(bd,0); + + for(i=0; i <= 10000; i++) { + if(*(ushort *)((ulong)memaddr + MISCGLOBAL) == *(ushort *)"GD" ) { + goto load_fep; + } + pcxxdelay(1); + } + + printk("BIOS download failed on the %s at 0x%x!\n", + board_desc[bd->type], bd->port); + bd->status = DISABLED; + continue; + } + +load_fep: + fepos = memaddr + FEPCODE; + if(bd->type == PCXEVE) + fepos = memaddr + (FEPCODE & 0x1fff); + + memwinon(bd, (FEPCODE >> 13)); + memcpy(fepos, pcxx_cook, pcxx_ncook); + memwinon(bd, 0); + + *(ushort *)((ulong)memaddr + MBOX + 0) = 2; + *(ushort *)((ulong)memaddr + MBOX + 2) = memory_seg + FEPCODESEG; + *(ushort *)((ulong)memaddr + MBOX + 4) = 0; + *(ushort *)((ulong)memaddr + MBOX + 6) = FEPCODESEG; + *(ushort *)((ulong)memaddr + MBOX + 8) = 0; + *(ushort *)((ulong)memaddr + MBOX + 10) = pcxx_ncook; + + outb(FEPMEM|FEPINT, bd->port); + outb(FEPMEM, bd->port); + + for(i=0; *(ushort *)((ulong)memaddr + MBOX); i++) { + if(i > 2000) { + printk("Command failed for the %s at 0x%x!\n", + board_desc[bd->type], bd->port); + bd->status = DISABLED; + break; + } + pcxxdelay(1); + } + + if(bd->status == DISABLED) + continue; + + *(ushort *)(memaddr + FEPSTAT) = 0; + *(ushort *)(memaddr + MBOX + 0) = 1; + *(ushort *)(memaddr + MBOX + 2) = FEPCODESEG; + *(ushort *)(memaddr + MBOX + 4) = 0x4L; + + outb(FEPINT, bd->port); + outb(FEPCLR, bd->port); + memwinon(bd, 0); + + for(i=0; *(ushort *)((ulong)memaddr + FEPSTAT) != *(ushort *)"OS"; i++) { + if(i > 10000) { + printk("FEP/OS download failed on the %s at 0x%x!\n", + board_desc[bd->type], bd->port); + bd->status = DISABLED; + break; + } + pcxxdelay(1); + } + if(bd->status == DISABLED) + continue; + + ch = &digi_channels[MAXPORTS * crd]; + pcxxassert(ch <= &digi_channels[NBDEVS-1], "ch out of range"); + + bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT); + gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL); + + if((bd->type == PCXEVE) && (*(ushort *)((ulong)memaddr+NPORT) < 3)) + shrinkmem = 1; + + request_region(bd->port, 4, "PC/Xx"); + + for(i=0; i < bd->numports; i++, ch++, bc++) { + if(((ushort *)((ulong)memaddr + PORTBASE))[i] == 0) { + ch->brdchan = 0; + continue; + } + ch->brdchan = bc; + ch->mailbox = gd; + ch->tqueue.routine = do_softint; + ch->tqueue.data = ch; + ch->board = &boards[crd]; +#ifdef DEFAULT_HW_FLOW + ch->digiext.digi_flags = RTSPACE|CTSPACE; +#endif + if(boards[crd].altpin) { + ch->dsr = CD; + ch->dcd = DSR; + ch->digiext.digi_flags |= DIGI_ALTPIN; + } else { + ch->dcd = CD; + ch->dsr = DSR; + } + + ch->magic = PCXX_MAGIC; + ch->boardnum = crd; + ch->channelnum = i; + + ch->dev = (MAXPORTS * crd) + i; + ch->tty = 0; + + if(shrinkmem) { + fepcmd(ch, SETBUFFER, 32, 0, 0, 0); + shrinkmem = 0; + } + + if(bd->type != PCXEVE) { + ch->txptr = memaddr+((bc->tseg-memory_seg) << 4); + ch->rxptr = memaddr+((bc->rseg-memory_seg) << 4); + ch->txwin = ch->rxwin = 0; + } else { + ch->txptr = memaddr+(((bc->tseg-memory_seg) << 4) & 0x1fff); + ch->txwin = FEPWIN | ((bc->tseg-memory_seg) >> 9); + ch->rxptr = memaddr+(((bc->rseg-memory_seg) << 4) & 0x1fff); + ch->rxwin = FEPWIN | ((bc->rseg-memory_seg) >>9 ); + } + + ch->txbufsize = bc->tmax + 1; + ch->rxbufsize = bc->rmax + 1; + + ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); + + lowwater = ch->txbufsize >= 2000 ? 1024 : ch->txbufsize/2; + fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); + fepcmd(ch, SRXLWATER, ch->rxbufsize/4, 0, 10, 0); + fepcmd(ch, SRXHWATER, 3 * ch->rxbufsize/4, 0, 10, 0); + + bc->edelay = 100; + bc->idata = 1; + + ch->startc = bc->startc; + ch->stopc = bc->stopc; + ch->startca = bc->startca; + ch->stopca = bc->stopca; + + ch->fepcflag = 0; + ch->fepiflag = 0; + ch->fepoflag = 0; + ch->fepstartc = 0; + ch->fepstopc = 0; + ch->fepstartca = 0; + ch->fepstopca = 0; + + ch->close_delay = 50; + ch->count = 0; + ch->blocked_open = 0; + ch->callout_termios = pcxe_callout.init_termios; + ch->normal_termios = pcxe_driver.init_termios; + ch->open_wait = 0; + ch->close_wait = 0; + } + + printk("DigiBoard PC/Xx Driver V%s: %s I/O=0x%x Mem=0x%lx Ports=%d\n", + VERSION, board_desc[bd->type], bd->port, bd->membase, bd->numports); + + memwinoff(bd, 0); + } + + if(tty_register_driver(&pcxe_driver)) + panic("Couldn't register PC/Xe driver"); + + if(tty_register_driver(&pcxe_callout)) + panic("Couldn't register PC/Xe callout"); + + + loops_per_sec = save_loops_per_sec; /* reset it to what it should be */ + + /* + * Start up the poller to check for events on all enabled boards + */ + timer_active |= 1 << DIGI_TIMER; + restore_flags(flags); + + return 0; +} + + +static void pcxxpoll(void) +{ + unsigned long flags; + int crd; + volatile unsigned int head, tail; + struct channel *ch; + struct board_info *bd; + + save_flags(flags); + cli(); + + for(crd=0; crd < NUMCARDS; crd++) { + bd = &boards[crd]; + ch = &digi_channels[MAXPORTS*crd]; + + if(bd->status == DISABLED) + continue; + + assertmemoff(ch); + + globalwinon(ch); + head = ch->mailbox->ein; + tail = ch->mailbox->eout; + + if(head != tail) + doevent(crd); + + memoff(ch); + } + + timer_table[DIGI_TIMER].fn = pcxxpoll; + timer_table[DIGI_TIMER].expires = jiffies + HZ/25; + timer_active |= 1 << DIGI_TIMER; + restore_flags(flags); +} + +static void doevent(int crd) +{ + volatile struct board_info *bd; + static struct tty_struct *tty; + volatile struct board_chan *bc; + volatile unchar *eventbuf; + volatile unsigned int head; + volatile unsigned int tail; + struct channel *ch; + struct channel *chan0; + int channel, event, mstat, lstat; + + bd = &boards[crd]; + + chan0 = &digi_channels[MAXPORTS * crd]; + pcxxassert(chan0 <= &digi_channels[NBDEVS-1], "ch out of range"); + + assertgwinon(chan0); + + while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) { + assertgwinon(chan0); + eventbuf = (volatile unchar *)bd->membase + tail + ISTART; + channel = eventbuf[0]; + event = eventbuf[1]; + mstat = eventbuf[2]; + lstat = eventbuf[3]; + + ch=chan0+channel; + + if ((unsigned)channel >= bd->numports || !ch) { + printk("physmem=%lx, tail=%x, head=%x\n", bd->membase, tail, head); + printk("doevent(%x) channel %x, event %x, mstat %x, lstat %x\n", + crd, (unsigned)channel, event, (unsigned)mstat, lstat); + if(channel >= bd->numports) + ch = chan0; + bc = ch->brdchan; + goto next; + } + if ((bc = ch->brdchan) == NULL) + goto next; + + if (event & DATA_IND) { + receive_data(ch); + assertgwinon(ch); + } + + if (event & MODEMCHG_IND) { + ch->imodem = mstat; + if (ch->asyncflags & (ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE)) { + if (ch->asyncflags & ASYNC_CHECK_CD) { + if (mstat & ch->dcd) { + wake_up_interruptible(&ch->open_wait); + } else { + pcxe_sched_event(ch, PCXE_EVENT_HANGUP); + } + } + } + } + + tty = ch->tty; + + if (tty) { + + if (event & BREAK_IND) { + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + *tty->flip.char_buf_ptr++ = 0; +#if 0 + if (ch->asyncflags & ASYNC_SAK) + do_SAK(tty); +#endif + tty_schedule_flip(tty); + } + + if (event & LOWTX_IND) { + if (ch->statusflags & LOWWAIT) { + ch->statusflags &= ~LOWWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } + } + + if (event & EMPTYTX_IND) { + ch->statusflags &= ~TXBUSY; + if (ch->statusflags & EMPTYWAIT) { + ch->statusflags &= ~EMPTYWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } + } + } + + next: + globalwinon(ch); + if(!bc) printk("bc == NULL in doevent!\n"); + else bc->idata = 1; + + chan0->mailbox->eout = (tail+4) & (IMAX-ISTART-4); + globalwinon(chan0); + } + +} + + +/* + * pcxxdelay - delays a specified number of milliseconds + */ +static void pcxxdelay(int msec) +{ + while(msec-- > 0) + __delay(loops_per_sec/1000); +} + + +static void +fepcmd(struct channel *ch, int cmd, int word_or_byte, int byte2, int ncmds, + int bytecmd) +{ + unchar *memaddr; + unsigned int head, tail; + long count; + int n; + + if(ch->board->status == DISABLED) + return; + + assertgwinon(ch); + + memaddr = (unchar *)ch->board->membase; + head = ch->mailbox->cin; + + if(head >= (CMAX-CSTART) || (head & 03)) { + printk("line %d: Out of range, cmd=%x, head=%x\n", __LINE__, cmd, head); + return; + } + + if(bytecmd) { + *(unchar *)(memaddr+head+CSTART+0) = cmd; + *(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch); + *(unchar *)(memaddr+head+CSTART+2) = word_or_byte; + *(unchar *)(memaddr+head+CSTART+3) = byte2; + } else { + *(unchar *)(memaddr+head+CSTART+0) = cmd; + *(unchar *)(memaddr+head+CSTART+1) = PORTNUM(ch); + *(ushort*)(memaddr+head+CSTART+2) = word_or_byte; + } + + head = (head+4) & (CMAX-CSTART-4); + ch->mailbox->cin = head; + + count = FEPTIMEOUT; + + while(1) { + count--; + if(count == 0) { + printk("Fep not responding in fepcmd()\n"); + return; + } + + head = ch->mailbox->cin; + tail = ch->mailbox->cout; + + n = (head-tail) & (CMAX-CSTART-4); + + if(n <= ncmds * (sizeof(short)*4)) + break; + } +} + + +static unsigned termios2digi_c(struct channel *ch, unsigned cflag) +{ + unsigned res = 0; +#ifdef SPEED_HACK + /* CL: HACK to force 115200 at 38400 and 57600 at 19200 Baud */ + if ((cflag & CBAUD)== B38400) cflag=cflag - B38400 + B115200; + if ((cflag & CBAUD)== B19200) cflag=cflag - B19200 + B57600; +#endif + if (cflag & CBAUDEX) + { + ch->digiext.digi_flags |= DIGI_FAST; + res |= FEP_HUPCL; + /* This gets strange but if we dont do this we will get 78600 + * instead of 115200. 57600 is mapped to 50 baud yielding 57600 in + * FAST mode. 115200 is mapped to 75. We need to map it to 110 to + * do 115K + */ + if (cflag & B115200) res|=1; + } + else ch->digiext.digi_flags &= ~DIGI_FAST; + res |= cflag & (CBAUD | PARODD | PARENB | CSTOPB | CSIZE); + return res; +} + +static unsigned termios2digi_i(struct channel *ch, unsigned iflag) +{ + unsigned res = iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|IXON|IXANY|IXOFF); + + if(ch->digiext.digi_flags & DIGI_AIXON) + res |= IAIXON; + return res; +} + +static unsigned termios2digi_h(struct channel *ch, unsigned cflag) +{ + unsigned res = 0; + + if(cflag & CRTSCTS) { + ch->digiext.digi_flags |= (RTSPACE|CTSPACE); + res |= (CTS | RTS); + } + if(ch->digiext.digi_flags & RTSPACE) + res |= RTS; + if(ch->digiext.digi_flags & DTRPACE) + res |= DTR; + if(ch->digiext.digi_flags & CTSPACE) + res |= CTS; + if(ch->digiext.digi_flags & DSRPACE) + res |= ch->dsr; + if(ch->digiext.digi_flags & DCDPACE) + res |= ch->dcd; + + if (res & RTS) + ch->digiext.digi_flags |= RTSPACE; + if (res & CTS) + ch->digiext.digi_flags |= CTSPACE; + + return res; +} + +static void pcxxparam(struct tty_struct *tty, struct channel *ch) +{ + volatile struct board_chan *bc; + unsigned int head; + unsigned mval, hflow, cflag, iflag; + struct termios *ts; + + bc = ch->brdchan; + assertgwinon(ch); + ts = tty->termios; + + if((ts->c_cflag & CBAUD) == 0) { + head = bc->rin; + bc->rout = head; + head = bc->tin; + fepcmd(ch, STOUT, (unsigned) head, 0, 0, 0); + mval = 0; + } else { + + cflag = termios2digi_c(ch, ts->c_cflag); + + if(cflag != ch->fepcflag) { + ch->fepcflag = cflag; + fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); + } + + if(cflag & CLOCAL) + ch->asyncflags &= ~ASYNC_CHECK_CD; + else { + ch->asyncflags |= ASYNC_CHECK_CD; + } + + mval = DTR | RTS; + } + + iflag = termios2digi_i(ch, ts->c_iflag); + + if(iflag != ch->fepiflag) { + ch->fepiflag = iflag; + fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); + } + + bc->mint = ch->dcd; + if((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) + if(ch->digiext.digi_flags & DIGI_FORCEDCD) + bc->mint = 0; + + ch->imodem = bc->mstat; + + hflow = termios2digi_h(ch, ts->c_cflag); + + if(hflow != ch->hflow) { + ch->hflow = hflow; + fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); + } + + /* mval ^= ch->modemfake & (mval ^ ch->modem); */ + + if(ch->omodem != mval) { + ch->omodem = mval; + fepcmd(ch, SETMODEM, mval, RTS|DTR, 0, 1); + } + + if(ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { + ch->fepstartc = ch->startc; + ch->fepstopc = ch->stopc; + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + + if(ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { + ch->fepstartca = ch->startca; + ch->fepstopca = ch->stopca; + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } +} + + +static void receive_data(struct channel *ch) +{ + volatile struct board_chan *bc; + struct tty_struct *tty; + unsigned int tail, head, wrapmask; + int n; + int piece; + struct termios *ts=0; + unchar *rptr; + int rc; + int wrapgap; + + globalwinon(ch); + + if (ch->statusflags & RXSTOPPED) + return; + + tty = ch->tty; + if(tty) + ts = tty->termios; + + bc = ch->brdchan; + + if(!bc) { + printk("bc is NULL in receive_data!\n"); + return; + } + + wrapmask = ch->rxbufsize - 1; + + head = bc->rin; + head &= wrapmask; + tail = bc->rout & wrapmask; + + n = (head-tail) & wrapmask; + + if(n == 0) + return; + + /* + * If CREAD bit is off or device not open, set TX tail to head + */ + if(!tty || !ts || !(ts->c_cflag & CREAD)) { + bc->rout = head; + return; + } + + if(tty->flip.count == TTY_FLIPBUF_SIZE) { + /* printk("tty->flip.count = TTY_FLIPBUF_SIZE\n"); */ + return; + } + + if(bc->orun) { + bc->orun = 0; + printk("overrun! DigiBoard device minor=%d\n",MINOR(tty->device)); + } + + rxwinon(ch); + rptr = tty->flip.char_buf_ptr; + rc = tty->flip.count; + while(n > 0) { + wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; + piece = (wrapgap < n) ? wrapgap : n; + + /* + * Make sure we don't overflow the buffer + */ + + if ((rc + piece) > TTY_FLIPBUF_SIZE) + piece = TTY_FLIPBUF_SIZE - rc; + + if (piece == 0) + break; + + memcpy(rptr, ch->rxptr + tail, piece); + rptr += piece; + rc += piece; + tail = (tail + piece) & wrapmask; + n -= piece; + } + tty->flip.count = rc; + tty->flip.char_buf_ptr = rptr; + globalwinon(ch); + bc->rout = tail; + + /* Must be called with global data */ + tty_schedule_flip(ch->tty); + return; +} + + +static int pcxe_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct channel *ch = (struct channel *) tty->driver_data; + volatile struct board_chan *bc; + int retval; + unsigned int mflag, mstat; + unsigned char startc, stopc; + unsigned long flags; + digiflow_t dflow; + + if(ch) + bc = ch->brdchan; + else { + printk("ch is NULL in pcxe_ioctl!\n"); + return(-EINVAL); + } + + save_flags(flags); + + switch(cmd) { + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if(retval) + return retval; + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + if(!arg) + digi_send_break(ch, HZ/4); /* 1/4 second */ + return 0; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if(retval) + return retval; + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if(error) + return error; + put_fs_long(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + + case TIOCSSOFTCAR: + arg = get_fs_long((unsigned long *) arg); + tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); + return 0; + + case TIOCMODG: + case TIOCMGET: + mflag = 0; + + cli(); + globalwinon(ch); + mstat = bc->mstat; + memoff(ch); + restore_flags(flags); + + if(mstat & DTR) + mflag |= TIOCM_DTR; + if(mstat & RTS) + mflag |= TIOCM_RTS; + if(mstat & CTS) + mflag |= TIOCM_CTS; + if(mstat & ch->dsr) + mflag |= TIOCM_DSR; + if(mstat & RI) + mflag |= TIOCM_RI; + if(mstat & ch->dcd) + mflag |= TIOCM_CD; + + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if(error) + return error; + put_fs_long(mflag, (unsigned long *) arg); + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMODS: + case TIOCMSET: + mstat = get_fs_long((unsigned long *) arg); + + mflag = 0; + if(mstat & TIOCM_DTR) + mflag |= DTR; + if(mstat & TIOCM_RTS) + mflag |= RTS; + + switch(cmd) { + case TIOCMODS: + case TIOCMSET: + ch->modemfake = DTR|RTS; + ch->modem = mflag; + break; + + case TIOCMBIS: + ch->modemfake |= mflag; + ch->modem |= mflag; + break; + + case TIOCMBIC: + ch->modemfake |= mflag; + ch->modem &= ~mflag; + break; + } + + cli(); + globalwinon(ch); + pcxxparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case TIOCSDTR: + cli(); + ch->omodem |= DTR; + globalwinon(ch); + fepcmd(ch, SETMODEM, DTR, 0, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case TIOCCDTR: + ch->omodem &= ~DTR; + cli(); + globalwinon(ch); + fepcmd(ch, SETMODEM, 0, DTR, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETA: + if((error=verify_area(VERIFY_WRITE, (char*)arg, sizeof(digi_t)))) + return(error); + + memcpy_tofs((char*)arg, &ch->digiext, sizeof(digi_t)); + break; + + case DIGI_SETAW: + case DIGI_SETAF: + if(cmd == DIGI_SETAW) { + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + } + else { + if(tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + } + + /* Fall Thru */ + + case DIGI_SETA: + if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(digi_t)))) + return(error); + + memcpy_fromfs(&ch->digiext, (char*)arg, sizeof(digi_t)); +#ifdef DEBUG_IOCTL + printk("ioctl(DIGI_SETA): flags = %x\n", ch->digiext.digi_flags); +#endif + + if(ch->digiext.digi_flags & DIGI_ALTPIN) { + ch->dcd = DSR; + ch->dsr = CD; + } else { + ch->dcd = CD; + ch->dsr = DSR; + } + + cli(); + globalwinon(ch); + pcxxparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETFLOW: + case DIGI_GETAFLOW: + cli(); + globalwinon(ch); + if(cmd == DIGI_GETFLOW) { + dflow.startc = bc->startc; + dflow.stopc = bc->stopc; + } else { + dflow.startc = bc->startca; + dflow.stopc = bc->stopca; + } + memoff(ch); + restore_flags(flags); + + if((error=verify_area(VERIFY_WRITE, (char*)arg,sizeof(dflow)))) + return(error); + + memcpy_tofs((char*)arg, &dflow, sizeof(dflow)); + break; + + case DIGI_SETAFLOW: + case DIGI_SETFLOW: + if(cmd == DIGI_SETFLOW) { + startc = ch->startc; + stopc = ch->stopc; + } else { + startc = ch->startca; + stopc = ch->stopca; + } + + if((error=verify_area(VERIFY_READ, (char*)arg,sizeof(dflow)))) + return(error); + + memcpy_fromfs(&dflow, (char*)arg, sizeof(dflow)); + + if(dflow.startc != startc || dflow.stopc != stopc) { + cli(); + globalwinon(ch); + + if(cmd == DIGI_SETFLOW) { + ch->fepstartc = ch->startc = dflow.startc; + ch->fepstopc = ch->stopc = dflow.stopc; + fepcmd(ch,SONOFFC,ch->fepstartc,ch->fepstopc,0, 1); + } else { + ch->fepstartca = ch->startca = dflow.startc; + ch->fepstopca = ch->stopca = dflow.stopc; + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } + + if(ch->statusflags & TXSTOPPED) + pcxe_start(tty); + + memoff(ch); + restore_flags(flags); + } + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static void pcxe_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct channel *info; + + if ((info=chan(tty))!=NULL) { + unsigned long flags; + save_flags(flags); + cli(); + globalwinon(info); + pcxxparam(tty,info); + memoff(info); + + if ((old_termios->c_cflag & CRTSCTS) && + ((tty->termios->c_cflag & CRTSCTS) == 0)) + tty->hw_stopped = 0; + if(!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&info->open_wait); + restore_flags(flags); + } +} + + +static void do_pcxe_bh(void) +{ + run_task_queue(&tq_pcxx); +} + + +static void do_softint(void *private_) +{ + struct channel *info = (struct channel *) private_; + + if(info && info->magic == PCXX_MAGIC) { + struct tty_struct *tty = info->tty; + if (tty && tty->driver_data) { + if(clear_bit(PCXE_EVENT_HANGUP, &info->event)) { + tty_hangup(tty); + wake_up_interruptible(&info->open_wait); + info->asyncflags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + } + } + } +} + + +static void pcxe_stop(struct tty_struct *tty) +{ + struct channel *info; + + if ((info=chan(tty))!=NULL) { + unsigned long flags; + save_flags(flags); + cli(); + if ((info->statusflags & TXSTOPPED) == 0) { + globalwinon(info); + fepcmd(info, PAUSETX, 0, 0, 0, 0); + info->statusflags |= TXSTOPPED; + memoff(info); + } + restore_flags(flags); + } +} + +static void pcxe_throttle(struct tty_struct * tty) +{ + struct channel *info; + + if ((info=chan(tty))!=NULL) { + unsigned long flags; + save_flags(flags); + cli(); + if ((info->statusflags & RXSTOPPED) == 0) { + globalwinon(info); + fepcmd(info, PAUSERX, 0, 0, 0, 0); + info->statusflags |= RXSTOPPED; + memoff(info); + } + restore_flags(flags); + } +} + +static void pcxe_unthrottle(struct tty_struct *tty) +{ + struct channel *info; + + if ((info=chan(tty)) != NULL) { + unsigned long flags; + + /* Just in case output was resumed because of a change in Digi-flow */ + save_flags(flags); + cli(); + if(info->statusflags & RXSTOPPED) { + volatile struct board_chan *bc; + globalwinon(info); + bc = info->brdchan; + fepcmd(info, RESUMERX, 0, 0, 0, 0); + info->statusflags &= ~RXSTOPPED; + memoff(info); + } + restore_flags(flags); + } +} + + +static void pcxe_start(struct tty_struct *tty) +{ + struct channel *info; + + if ((info=chan(tty))!=NULL) { + unsigned long flags; + + save_flags(flags); + cli(); + /* Just in case output was resumed because of a change in Digi-flow */ + if(info->statusflags & TXSTOPPED) { + volatile struct board_chan *bc; + globalwinon(info); + bc = info->brdchan; + if(info->statusflags & LOWWAIT) + bc->ilow = 1; + fepcmd(info, RESUMETX, 0, 0, 0, 0); + info->statusflags &= ~TXSTOPPED; + memoff(info); + } + restore_flags(flags); + } +} + + +void digi_send_break(struct channel *ch, int msec) +{ + unsigned long flags; + + save_flags(flags); + cli(); + globalwinon(ch); + + /* + * Maybe I should send an infinite break here, schedule() for + * msec amount of time, and then stop the break. This way, + * the user can't screw up the FEP by causing digi_send_break() + * to be called (i.e. via an ioctl()) more than once in msec amount + * of time. Try this for now... + */ + + fepcmd(ch, SENDBREAK, msec, 0, 10, 0); + memoff(ch); + + restore_flags(flags); +} + +static void setup_empty_event(struct tty_struct *tty, struct channel *ch) +{ + volatile struct board_chan *bc; + unsigned long flags; + + save_flags(flags); + cli(); + globalwinon(ch); + ch->statusflags |= EMPTYWAIT; + bc = ch->brdchan; + bc->iempty = 1; + memoff(ch); + restore_flags(flags); +} + diff -u --recursive --new-file v1.3.82/linux/drivers/char/pcxx.h linux/drivers/char/pcxx.h --- v1.3.82/linux/drivers/char/pcxx.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/char/pcxx.h Wed Apr 3 09:20:45 1996 @@ -0,0 +1,116 @@ +#define FEPCODESEG 0x0200L +#define FEPCODE 0x2000L +#define BIOSCODE 0xf800L + +#define MISCGLOBAL 0x0C00L +#define NPORT 0x0C22L +#define MBOX 0x0C40L +#define PORTBASE 0x0C90L + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define PCXI 0 +#define PCXE 1 +#define PCXEVE 2 + +static char *board_desc[] = { + "PC/Xi (64K)", + "PC/Xe (64K)", + "PC/Xe (8K) ", +}; + +#define STARTC 021 +#define STOPC 023 +#define IAIXON 0x2000 + + +struct board_info { + unchar status; + unchar type; + unchar altpin; + ushort numports; + ushort port; + ulong membase; +}; + + +#define TXSTOPPED 0x01 +#define LOWWAIT 0x02 +#define EMPTYWAIT 0x04 +#define RXSTOPPED 0x08 +#define TXBUSY 0x10 + +#define DISABLED 0 +#define ENABLED 1 +#define OFF 0 +#define ON 1 + +#define FEPTIMEOUT 200000 +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#define PCXE_EVENT_HANGUP 1 +#define PCXX_MAGIC 0x5c6df104L + +struct channel { + /* --------- Board/channel information ---------- */ + long magic; + unchar boardnum; + unchar channelnum; + uint dev; + long session; + long pgrp; + struct tty_struct *tty; + struct board_info *board; + volatile struct board_chan *brdchan; + volatile struct global_data *mailbox; + int asyncflags; + int count; + int blocked_open; + int close_delay; + int event; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + /* ------------ Async control data ------------- */ + unchar modemfake; /* Modem values to be forced */ + unchar modem; /* Force values */ + ulong statusflags; + unchar omodem; /* FEP output modem status */ + unchar imodem; /* FEP input modem status */ + unchar hflow; + unchar dsr; + unchar dcd; + unchar stopc; + unchar startc; + unchar stopca; + unchar startca; + unchar fepstopc; + unchar fepstartc; + unchar fepstopca; + unchar fepstartca; + ushort fepiflag; + ushort fepcflag; + ushort fepoflag; + /* ---------- Transmit/receive system ---------- */ + unchar txwin; + unchar rxwin; + ushort txbufsize; + ushort rxbufsize; + unchar *txptr; + unchar *rxptr; + unchar *tmp_buf; /* Temp buffer */ + /* ---- Termios data ---- */ + ulong c_iflag; + ulong c_cflag; + ulong c_lflag; + ulong c_oflag; + struct termios normal_termios; + struct termios callout_termios; + struct digi_struct digiext; + ulong dummy[8]; +}; diff -u --recursive --new-file v1.3.82/linux/drivers/char/pcxxconfig.h linux/drivers/char/pcxxconfig.h --- v1.3.82/linux/drivers/char/pcxxconfig.h Thu Jan 1 02:00:00 1970 +++ linux/drivers/char/pcxxconfig.h Wed Apr 3 09:20:45 1996 @@ -0,0 +1,5 @@ +#define NUMCARDS 1 + +struct board_info boards[NUMCARDS]={ +{ ENABLED, 0, ON, 16, 0x200, 0xd0000 } +}; diff -u --recursive --new-file v1.3.82/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c --- v1.3.82/linux/drivers/char/tty_io.c Sun Mar 31 00:13:17 1996 +++ linux/drivers/char/tty_io.c Wed Apr 3 09:20:45 1996 @@ -1838,6 +1838,9 @@ #ifdef CONFIG_ISTALLION stli_init(); #endif +#ifdef CONFIG_DIGI + pcxe_init(); +#endif pty_init(); vcs_init(); if (register_chrdev(TTY_MAJOR,"tty",&tty_fops)) diff -u --recursive --new-file v1.3.82/linux/drivers/net/3c505.c linux/drivers/net/3c505.c --- v1.3.82/linux/drivers/net/3c505.c Fri Mar 1 07:50:42 1996 +++ linux/drivers/net/3c505.c Wed Apr 3 08:51:36 1996 @@ -1,36 +1,37 @@ /* * Linux ethernet device driver for the 3Com Etherlink Plus (3C505) - * By Craig Southeren and Juha Laiho + * By Craig Southeren and Juha Laiho * - * 3c505.c This module implements an interface to the 3Com - * Etherlink Plus (3c505) ethernet card. Linux device - * driver interface reverse engineered from the Linux 3C509 - * device drivers. Some 3C505 information gleaned from - * the Crynwr packet driver. Still this driver would not - * be here without 3C505 technical reference provided by - * 3Com. + * 3c505.c This module implements an interface to the 3Com + * Etherlink Plus (3c505) ethernet card. Linux device + * driver interface reverse engineered from the Linux 3C509 + * device drivers. Some 3C505 information gleaned from + * the Crynwr packet driver. Still this driver would not + * be here without 3C505 technical reference provided by + * 3Com. * - * Version: @(#)3c505.c 0.8.4 17-Dec-95 + * $Id: 3c505.c,v 0.9.1.1 1996/04/02 13:58:06 root Exp $ * - * Authors: Linux 3c505 device driver by - * Craig Southeren, + * Authors: Linux 3c505 device driver by + * Craig Southeren, * Final debugging by - * Andrew Tridgell, - * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by - * Juha Laiho, + * Andrew Tridgell, + * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by + * Juha Laiho, * Linux 3C509 driver by - * Donald Becker, - * Crynwr packet driver by - * Krishnan Gopalan and Gregg Stefancik, - * Clemson University Engineering Computer Operations. - * Portions of the code have been adapted from the 3c505 - * driver for NCSA Telnet by Bruce Orchard and later - * modified by Warren Van Houten and krus@diku.dk. + * Donald Becker, + * Crynwr packet driver by + * Krishnan Gopalan and Gregg Stefancik, + * Clemson University Engineering Computer Operations. + * Portions of the code have been adapted from the 3c505 + * driver for NCSA Telnet by Bruce Orchard and later + * modified by Warren Van Houten and krus@diku.dk. * 3C505 technical information provided by * Terry Murphy, of 3Com Network Adapter Division - * Linux 1.3.0 changes by - * Alan Cox - * + * Linux 1.3.0 changes by + * Alan Cox + * More debugging by Philip Blundell + * */ #include @@ -61,10 +62,6 @@ static const char * filename = __FILE__; -static const char * null_msg = "*** NULL at %s:%s (line %d) ***\n"; -#define CHECK_NULL(p) \ - if (!p) printk(null_msg, filename,__FUNCTION__,__LINE__) - static const char * timeout_msg = "*** timeout at %s:%s (line %d) ***\n"; #define TIMEOUT_MSG(lineno) \ printk(timeout_msg, filename,__FUNCTION__,(lineno)) @@ -103,8 +100,6 @@ * 3 = messages when interrupts received */ -#define ELP_VERSION "0.8.4" - /***************************************************************** * * useful macros @@ -230,34 +225,6 @@ sti(); } -#define WAIT_HCRE(addr,toval) wait_hcre((addr),(toval),__LINE__) -static inline int -wait_hcre (unsigned int base_addr, int toval, int lineno) -{ - int timeout = jiffies + toval; - while (((inb_status(base_addr)&HCRE)==0) && (jiffies <= timeout)) - ; - if (jiffies >= timeout) { - TIMEOUT_MSG(lineno); - return FALSE; - } - return TRUE; -} - -static inline int -wait_fast_hcre (unsigned int base_addr, int toval, int lineno) -{ - int timeout = 0; - while (((inb_status(base_addr)&HCRE)==0) && (timeout++ < toval)) - ; - if (timeout >= toval) { - sti(); - TIMEOUT_MSG(lineno); - return FALSE; - } - return TRUE; -} - static int start_receive (struct device *, pcb_struct *); static void adapter_hard_reset (struct device *); @@ -303,6 +270,46 @@ printk("%s: start receive command failed \n", dev->name); } +/* These should be moved to the private data area */ +static int send_pcb_semaphore = 0; +static int rx_restart_needed = 0; + +/* Primitive functions used by send_pcb() */ +static inline int send_pcb_slow(unsigned int base_addr, unsigned char byte) +{ + int timeout; + outb_command(byte, base_addr); + for (timeout = jiffies + 5; jiffies < timeout; ) { + if (inb_status(base_addr) & HCRE) return FALSE; + } + printk("3c505: send_pcb_slow timed out\n"); + return TRUE; +} + +static inline int send_pcb_fast(unsigned int base_addr, unsigned char byte) +{ + int timeout; + outb_command(byte, base_addr); + for (timeout = 0; timeout < 40000; timeout++) { + if (inb_status(base_addr) & HCRE) return FALSE; + } + printk("3c505: send_pcb_fast timed out\n"); + return TRUE; +} + +static int +start_receive (struct device * dev, pcb_struct * tx_pcb); + +/* Check to see if the receiver needs restarting, and kick it if so */ +static inline void prime_rx(struct device *dev) +{ + if (rx_restart_needed) { + elp_device *adapter = dev->priv; + rx_restart_needed = 0; + start_receive(dev, &adapter->itx_pcb); + } +} + /***************************************************************** * * send_pcb @@ -318,71 +325,79 @@ * *****************************************************************/ +/* This can be quite slow -- the adapter is allowed to take up to 40ms + * to respond to the initial interrupt. + * + * We run initially with interrupts turned on, but with a semaphore set + * so that nobody tries to re-enter this code. Once the first byte has + * gone through, we turn interrupts off and then send the others (the + * timeout is reduced to 500us). + */ + static int send_pcb (struct device * dev, pcb_struct * pcb) { int i; int timeout; - int cont; + + /* Avoid contention */ + if (set_bit(1, &send_pcb_semaphore)) { + if (elp_debug >= 3) { + printk("%s: send_pcb entered while threaded\n", dev->name); + } + return FALSE; + } /* * load each byte into the command register and * wait for the HCRE bit to indicate the adapter * had read the byte */ - set_hsf(dev->base_addr,0); - if ((cont = WAIT_HCRE(dev->base_addr,5))) { - cli(); - if (pcb->command==CMD_TRANSMIT_PACKET) - outb_control(inb_control(dev->base_addr)&~DIR,dev->base_addr); - outb_command(pcb->command, dev->base_addr); - sti(); - cont = WAIT_HCRE(dev->base_addr,5); - } + set_hsf(dev->base_addr,0); - if (cont) { - outb_command(pcb->length, dev->base_addr); - cont = WAIT_HCRE(dev->base_addr,5); - } + if (send_pcb_slow(dev->base_addr, pcb->command)) goto abort; cli(); - for (i = 0; cont && (i < pcb->length); i++) { - outb_command(pcb->data.raw[i], dev->base_addr); - cont = wait_fast_hcre(dev->base_addr,20000,__LINE__); - } /* if wait_fast_hcre() failed, has already done sti() */ - - /* set the host status bits to indicate end of PCB */ - /* send the total packet length as well */ - /* wait for the adapter to indicate that it has read the PCB */ - if (cont) { - set_hsf(dev->base_addr,HSF_PCB_END); - outb_command(2+pcb->length, dev->base_addr); - sti(); - timeout = jiffies + 7; - while (jiffies < timeout) { - i = GET_ASF(dev->base_addr); - if ((i == ASF_PCB_ACK) || (i == ASF_PCB_NAK)) - break; - } - if (i == ASF_PCB_ACK) { - reset_count=0; + if (send_pcb_fast(dev->base_addr, pcb->length)) goto sti_abort; + + for (i = 0; i < pcb->length; i++) { + if (send_pcb_fast(dev->base_addr, pcb->data.raw[i])) goto sti_abort; + } + + outb_control(inb_control(dev->base_addr) | 3, dev->base_addr); /* signal end of PCB */ + outb_command(2+pcb->length, dev->base_addr); + + /* now wait for the acknowledgement */ + sti(); + + for (timeout = jiffies+5; jiffies < timeout; ) { + switch (GET_ASF(dev->base_addr)) { + case ASF_PCB_ACK: + send_pcb_semaphore = 0; + if (pcb->command != CMD_TRANSMIT_PACKET) { + prime_rx(dev); + } return TRUE; + break; + case ASF_PCB_NAK: + printk(KERN_INFO "%s: send_pcb got NAK\n", dev->name); + goto abort; + break; } - else if (i == ASF_PCB_NAK) { - printk("%s: PCB send was NAKed\n", dev->name); - } else { - printk("%s: timeout after sending PCB\n", dev->name); - } - } else { - sti(); - printk("%s: timeout in middle of sending PCB\n", dev->name); } - adapter_reset(dev); + printk("%s: timeout waiting for PCB acknowledge (status %02x)\n", dev->name, inb_status(dev->base_addr)); + +sti_abort: + sti(); +abort: + send_pcb_semaphore = 0; + prime_rx(dev); return FALSE; } + /***************************************************************** * * receive_pcb @@ -404,9 +419,6 @@ int stat; int timeout; - CHECK_NULL(pcb); - CHECK_NULL(dev); - set_hsf(dev->base_addr,0); /* get the command code */ @@ -426,6 +438,7 @@ ; if (jiffies >= timeout) { TIMEOUT_MSG(__LINE__); + printk("%s: status %02x\n", dev->name, stat); return FALSE; } pcb->length = inb_command(dev->base_addr); @@ -475,8 +488,6 @@ int timeout; long flags; - CHECK_NULL(dev); - save_flags(flags); sti(); @@ -524,9 +535,6 @@ static int start_receive (struct device * dev, pcb_struct * tx_pcb) { - CHECK_NULL(dev); - CHECK_NULL(tx_pcb); - if (elp_debug >= 3) printk("%s: restarting receiver\n", dev->name); tx_pcb->command = CMD_RECEIVE_PACKET; @@ -557,7 +565,6 @@ struct sk_buff *skb; elp_device * adapter; - CHECK_NULL(dev); adapter=dev->priv; if (len <= 0 || ((len & ~1) != len)) @@ -581,6 +588,7 @@ * if buffer could not be allocated, swallow it */ if (skb == NULL) { + printk(KERN_INFO "%s: memory squeeze, dropping packet\n", dev->name); for (i = 0; i < (rlen/2); i++) { timeout = 0; while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000) @@ -657,8 +665,6 @@ adapter = (elp_device *) dev->priv; - CHECK_NULL(adapter); - if (dev->interrupt) if (elp_debug >= 2) printk("%s: Re-entering the interrupt handler.\n", dev->name); @@ -687,9 +693,6 @@ if (dev->start == 0) break; cli(); - /* Set direction of adapter FIFO */ - outb_control(inb_control(dev->base_addr)|DIR, - dev->base_addr); len = adapter->irx_pcb.data.rcv_resp.pkt_len; dlen = adapter->irx_pcb.data.rcv_resp.buf_len; if (adapter->irx_pcb.data.rcv_resp.timeout != 0) { @@ -706,9 +709,11 @@ if (elp_debug >= 3) printk("%s: packet received\n", dev->name); } - if (dev->start && !start_receive(dev, &adapter->itx_pcb)) + if (dev->start && !start_receive(dev, &adapter->itx_pcb)) { if (elp_debug >= 2) - printk("%s: interrupt - failed to send receive start PCB\n", dev->name); + printk("%s: interrupt - receiver start deferred\n", dev->name); + rx_restart_needed = 1; + } if (elp_debug >= 3) printk("%s: receive procedure complete\n", dev->name); @@ -815,8 +820,6 @@ { elp_device * adapter; - CHECK_NULL(dev); - adapter = dev->priv; if (elp_debug >= 3) @@ -923,6 +926,12 @@ */ if (!start_receive(dev, &adapter->tx_pcb)) printk("%s: start receive command failed \n", dev->name); + if (!start_receive(dev, &adapter->tx_pcb)) + printk("%s: start receive command failed \n", dev->name); + if (!start_receive(dev, &adapter->tx_pcb)) + printk("%s: start receive command failed \n", dev->name); + if (!start_receive(dev, &adapter->tx_pcb)) + printk("%s: start receive command failed \n", dev->name); if (elp_debug >= 3) printk("%s: start receive command sent\n", dev->name); @@ -950,9 +959,6 @@ */ unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1); - CHECK_NULL(dev); - CHECK_NULL(ptr); - adapter = dev->priv; if (nlen < len) @@ -991,6 +997,8 @@ } sti(); + prime_rx(dev); + return TRUE; } @@ -1004,19 +1012,28 @@ static int elp_start_xmit (struct sk_buff *skb, struct device *dev) { - CHECK_NULL(dev); - /* - * not sure what this does, but the 3c509 driver does it, so... + * if the transmitter is still busy, we have a transmit timeout... */ + if (dev->tbusy) { + int tickssofar = jiffies - dev->trans_start; + int stat; + if (tickssofar < 40) + return 1; + stat = inb_status(dev->base_addr); + printk("%s: transmit timed out, %s?\n", dev->name, (stat & ACRF)?"IRQ conflict":"network cable problem"); + if (elp_debug >= 1) + printk("%s: status %#02x\n", dev->name, stat); + dev->trans_start = jiffies; + dev->tbusy = 0; + } + + /* Some upper layer thinks we've missed a tx-done interrupt */ if (skb == NULL) { dev_tint(dev); return 0; } - /* - * if we ended up with a munged length, don't send it - */ if (skb->len <= 0) return 0; @@ -1024,26 +1041,10 @@ printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len); /* - * if the transmitter is still busy, we have a transmit timeout... - */ - if (dev->tbusy) { - int tickssofar = jiffies - dev->trans_start; - int stat; - if (tickssofar < 50) /* was 500, AJT */ - return 1; - printk("%s: transmit timed out, not resetting adapter\n", dev->name); - if (((stat=inb_status(dev->base_addr))&ACRF) != 0) - printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name); - printk("%s: status %#02x\n", dev->name, stat); - dev->trans_start = jiffies; - dev->tbusy = 0; - } - - /* * send the packet at skb->data for skb->len */ if (!send_packet(dev, skb->data, skb->len)) { - printk("%s: send packet PCB failed\n", dev->name); + printk("%s: failed to transmit packet\n", dev->name); return 1; } @@ -1119,9 +1120,7 @@ { elp_device * adapter; - CHECK_NULL(dev); adapter = dev->priv; - CHECK_NULL(adapter); if (elp_debug >= 3) printk("%s: request to close device\n", dev->name); @@ -1242,8 +1241,6 @@ { elp_device * adapter; - CHECK_NULL(dev); - /* * set ptrs to various functions */ @@ -1260,7 +1257,6 @@ * setup ptr to adapter specific information */ adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL)); - CHECK_NULL(adapter); if (adapter == NULL) return; memset(&(adapter->stats), 0, sizeof(struct enet_statistics)); @@ -1392,8 +1388,6 @@ elp_device adapter; int i; - CHECK_NULL(dev); - /* * setup adapter structure */ @@ -1420,6 +1414,7 @@ (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) || (adapter.rx_pcb.length != 6)) { printk("%s: not responding to first PCB\n", dev->name); + autoirq_report(0); return -ENODEV; } diff -u --recursive --new-file v1.3.82/linux/drivers/net/README.3c505 linux/drivers/net/README.3c505 --- v1.3.82/linux/drivers/net/README.3c505 Wed Jun 7 11:39:06 1995 +++ linux/drivers/net/README.3c505 Wed Apr 3 08:51:36 1996 @@ -16,17 +16,21 @@ to 1 may help. As of 3c505.c v0.8 the driver should be able to find out whether of not this is needed, but I'm not completely sure. ELP_DEBUG - The driver debug level. 1 is ok for most everything, 0 will provide - less verbose bootup messages, and 2 and 3 are usually too verbose - for anything. + The driver debug level. It's probably best to leave it at 0 most of the time. + If you are having trouble, setting it to 1 may give you more information. + Any higher setting is too verbose for most purposes. Known problems: - During startup the driver shows the following two messages: - *** timeout at 3c505.c:elp_set_mc_list (line 1158) *** - *** timeout at 3c505.c:elp_set_mc_list (line 1183) *** - These are because upper parts of the networking code attempt - to load multicast address lists to the adapter before the - adapter is properly up and running. + The 3c505 is a slow card, mostly because of the way it talks to the host. + Don't expect any great performance from it. + + I am seeing periodic "transmit timed out" and "timeout waiting for PCB + acknowledge" messages under high load. I'm not sure what's causing these - + it seems that the 3c505 occasionally just loses a command. They seem not to + be fatal, anyway. + + There may be some initialisation problems still lurking, particularly when + warm-booting from DOS (ELP_NEED_HARD_RESET seems not to help). Authors: The driver is mainly written by Craig Southeren, email @@ -34,3 +38,4 @@ Parts of the driver (adapting the driver to 1.1.4+ kernels, IRQ/address detection, some changes) and this README by Juha Laiho . + Philip Blundell made some more changes. diff -u --recursive --new-file v1.3.82/linux/drivers/sound/pss.c linux/drivers/sound/pss.c --- v1.3.82/linux/drivers/sound/pss.c Sun Mar 24 22:50:16 1996 +++ linux/drivers/sound/pss.c Wed Apr 3 09:02:40 1996 @@ -426,7 +426,7 @@ #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) return probe_mpu401 (hw_config); #else - return 0 + return 0; #endif } @@ -775,15 +775,17 @@ long attach_pss_mpu (long mem_start, struct address_info *hw_config) { - int prev_devs; long ret; #if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI) - prev_devs = num_midis; - ret = attach_mpu401 (mem_start, hw_config); - - if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */ - midi_devs[prev_devs]->coproc = &pss_coproc_operations; + { + int prev_devs; + prev_devs = num_midis; + ret = attach_mpu401 (mem_start, hw_config); + + if (num_midis == (prev_devs + 1)) /* The MPU driver installed itself */ + midi_devs[prev_devs]->coproc = &pss_coproc_operations; + } #endif return ret; } diff -u --recursive --new-file v1.3.82/linux/fs/ncpfs/dir.c linux/fs/ncpfs/dir.c --- v1.3.82/linux/fs/ncpfs/dir.c Wed Mar 27 08:19:29 1996 +++ linux/fs/ncpfs/dir.c Tue Apr 2 13:33:13 1996 @@ -125,10 +125,10 @@ /* Here we encapsulate the inode number handling that depends upon the * mount mode: When we mount a complete server, the memory address of - * the npc_inode_info is used as an inode. When only a single volume - * is mounted, then the DosDirNum is used as the inode number. As this - * is unique for the complete volume, this should enable the NFS - * exportability of a ncpfs-mounted volume. + * the ncp_inode_info is used as the inode number. When only a single + * volume is mounted, then the DosDirNum is used as the inode + * number. As this is unique for the complete volume, this should + * enable the NFS exportability of a ncpfs-mounted volume. */ static inline int @@ -183,10 +183,6 @@ return -EISDIR; } -/* In ncpfs, we have unique inodes across all mounted filesystems, for - all inodes that are in memory. That's why it's enough to index the - directory cache by the inode number. */ - static kdev_t c_dev = 0; static unsigned long c_ino = 0; static int c_size; @@ -320,11 +316,6 @@ while (index < c_size) { - /* We found it. For getwd(), we have to return the - correct inode in d_ino if the inode is currently in - use. Otherwise the inode number does not - matter. (You can argue a lot about this..) */ - ino_t ino; if (ncp_single_volume(server)) @@ -333,6 +324,10 @@ } else { + /* For getwd() we have to return the correct + * inode in d_ino if the inode is currently in + * use. Otherwise the inode number does not + * matter. (You can argue a lot about this..) */ struct ncp_inode_info *ino_info; ino_info = ncp_find_dir_inode(inode, entry->i.entryName); diff -u --recursive --new-file v1.3.82/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c --- v1.3.82/linux/fs/ncpfs/sock.c Sun Mar 10 13:49:16 1996 +++ linux/fs/ncpfs/sock.c Tue Apr 2 13:33:13 1996 @@ -1,5 +1,5 @@ /* - * linux/fs/ncp/sock.c + * linux/fs/ncpfs/sock.c * * Copyright (C) 1992, 1993 Rick Sladkey * diff -u --recursive --new-file v1.3.82/linux/fs/nfs/rpcsock.c linux/fs/nfs/rpcsock.c --- v1.3.82/linux/fs/nfs/rpcsock.c Tue Apr 2 13:32:22 1996 +++ linux/fs/nfs/rpcsock.c Wed Apr 3 08:29:11 1996 @@ -412,10 +412,8 @@ while (rsock->pending != slot) { if (!slot->w_gotit) interruptible_sleep_on(&slot->w_wait); - if (slot->w_gotit) { - result = slot->w_result; /* quite important */ - return result; - } + if (slot->w_gotit) + return slot->w_result; /* quite important */ if (current->signal & ~current->blocked) return -ERESTARTSYS; if (rsock->shutdown) @@ -427,15 +425,15 @@ /* Wait for data to arrive */ if ((result = rpc_select(rsock)) < 0) { dprintk("RPC: select error = %d\n", result); - break; + return result; } /* Receive and dispatch */ if ((result = rpc_grok(rsock)) < 0) - break; + return result; } while (current->timeout && !slot->w_gotit); - return slot->w_gotit? result : -ETIMEDOUT; + return slot->w_gotit? slot->w_result : -ETIMEDOUT; } /* diff -u --recursive --new-file v1.3.82/linux/fs/proc/mem.c linux/fs/proc/mem.c --- v1.3.82/linux/fs/proc/mem.c Fri Dec 22 13:00:19 1995 +++ linux/fs/proc/mem.c Wed Apr 3 10:59:32 1996 @@ -267,6 +267,8 @@ stmp = vma->vm_offset; dtmp = vma->vm_start; + flush_cache_range(vma->vm_mm, vma->vm_start, vma->vm_end); + flush_cache_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end); while (dtmp < vma->vm_end) { while (src_vma && stmp > src_vma->vm_end) src_vma = src_vma->vm_next; @@ -297,8 +299,8 @@ dtmp += PAGE_SIZE; } - invalidate_range(vma->vm_mm, vma->vm_start, vma->vm_end); - invalidate_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end); + flush_tlb_range(vma->vm_mm, vma->vm_start, vma->vm_end); + flush_tlb_range(src_vma->vm_mm, src_vma->vm_start, src_vma->vm_end); return 0; } diff -u --recursive --new-file v1.3.82/linux/include/asm-alpha/hwrpb.h linux/include/asm-alpha/hwrpb.h --- v1.3.82/linux/include/asm-alpha/hwrpb.h Tue Apr 2 13:32:22 1996 +++ linux/include/asm-alpha/hwrpb.h Wed Apr 3 09:22:22 1996 @@ -37,7 +37,7 @@ #define ST_DEC_EB64P 20 /* EB64+ systype */ #define ST_DEC_EB66P -19 /* EB66 systype */ #define ST_DEC_EBPC64 -20 /* Cabriolet (AlphaPC64) systype */ -#defien ST_DEC_EB164 26 /* EB164 systype */ +#define ST_DEC_EB164 26 /* EB164 systype */ struct pcb_struct { unsigned long ksp; diff -u --recursive --new-file v1.3.82/linux/include/asm-alpha/pgtable.h linux/include/asm-alpha/pgtable.h --- v1.3.82/linux/include/asm-alpha/pgtable.h Thu Mar 14 16:33:27 1996 +++ linux/include/asm-alpha/pgtable.h Wed Apr 3 11:01:23 1996 @@ -11,27 +11,34 @@ #include +/* Caches aren't brain-dead on the alpha. */ +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(mm, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) + /* - * Invalidate current user mapping. + * Flush current user mapping. */ -static inline void invalidate(void) +static inline void flush_tlb(void) { tbiap(); } /* - * Invalidate everything (kernel mapping may also have + * Flush everything (kernel mapping may also have * changed due to vmalloc/vfree) */ -static inline void invalidate_all(void) +static inline void flush_tlb_all(void) { tbia(); } /* - * Invalidate a specified user mapping + * Flush a specified user mapping */ -static inline void invalidate_mm(struct mm_struct *mm) +static inline void flush_tlb_mm(struct mm_struct *mm) { if (mm != current->mm) mm->context = 0; @@ -40,14 +47,14 @@ } /* - * Page-granular invalidate. + * Page-granular tlb flush. * * do a tbisd (type = 2) normally, and a tbis (type = 3) * if it is an executable mapping. We want to avoid the - * itlb invalidate, because that potentially also does a - * icache invalidate. + * itlb flush, because that potentially also does a + * icache flush. */ -static inline void invalidate_page(struct vm_area_struct *vma, +static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { struct mm_struct * mm = vma->vm_mm; @@ -59,10 +66,10 @@ } /* - * Invalidate a specified range of user mapping: on the - * alpha we invalidate the whole user tlb + * Flush a specified range of user mapping: on the + * alpha we flush the whole user tlb */ -static inline void invalidate_range(struct mm_struct *mm, +static inline void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { if (mm != current->mm) diff -u --recursive --new-file v1.3.82/linux/include/asm-i386/locks.h linux/include/asm-i386/locks.h --- v1.3.82/linux/include/asm-i386/locks.h Mon Oct 2 14:25:11 1995 +++ linux/include/asm-i386/locks.h Wed Apr 3 10:59:32 1996 @@ -40,7 +40,7 @@ if(smp_invalidate_needed&(1<spins++; } /* diff -u --recursive --new-file v1.3.82/linux/include/asm-i386/pgtable.h linux/include/asm-i386/pgtable.h --- v1.3.82/linux/include/asm-i386/pgtable.h Thu Mar 14 16:33:04 1996 +++ linux/include/asm-i386/pgtable.h Wed Apr 3 11:01:23 1996 @@ -19,139 +19,146 @@ * the i386 page table tree. */ +/* Caches aren't brain-dead on the intel. */ +#define flush_cache_all() do { } while (0) +#define flush_cache_mm(mm) do { } while (0) +#define flush_cache_range(mm, start, end) do { } while (0) +#define flush_cache_page(vma, vmaddr) do { } while (0) +#define flush_page_to_ram(page) do { } while (0) + /* - * TLB invalidation: + * TLB flushing: * - * - invalidate() invalidates the current mm struct TLBs - * - invalidate_all() invalidates all processes TLBs - * - invalidate_mm(mm) invalidates the specified mm context TLB's - * - invalidate_page(mm, vmaddr) invalidates one page - * - invalidate_range(mm, start, end) invalidates a range of pages + * - flush_tlb() flushes the current mm struct TLBs + * - flush_tlb_all() flushes all processes TLBs + * - flush_tlb_mm(mm) flushes the specified mm context TLB's + * - flush_tlb_page(vma, vmaddr) flushes one page + * - flush_tlb_range(mm, start, end) flushes a range of pages * - * ..but the i386 has somewhat limited invalidation capabilities, - * and page-granular invalidates are available only on i486 and up. + * ..but the i386 has somewhat limited tlb flushing capabilities, + * and page-granular flushes are available only on i486 and up. */ -#define __invalidate() \ +#define __flush_tlb() \ __asm__ __volatile__("movl %%cr3,%%eax\n\tmovl %%eax,%%cr3": : :"ax") #ifdef CONFIG_M386 -#define __invalidate_one(addr) invalidate() +#define __flush_tlb_one(addr) flush_tlb() #else -#define __invalidate_one(addr) \ +#define __flush_tlb_one(addr) \ __asm__ __volatile__("invlpg %0": :"m" (*(char *) addr)) #endif #ifndef __SMP__ -#define invalidate() __invalidate() -#define invalidate_all() __invalidate() +#define flush_tlb() __flush_tlb() +#define flush_tlb_all() __flush_tlb() -static inline void invalidate_mm(struct mm_struct *mm) +static inline void flush_tlb_mm(struct mm_struct *mm) { if (mm == current->mm) - __invalidate(); + __flush_tlb(); } -static inline void invalidate_page(struct vm_area_struct *vma, +static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { if (vma->vm_mm == current->mm) - __invalidate_one(addr); + __flush_tlb_one(addr); } -static inline void invalidate_range(struct mm_struct *mm, +static inline void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { if (mm == current->mm) - __invalidate(); + __flush_tlb(); } #else /* * We aren't very clever about this yet - SMP could certainly - * avoid some global invalidates.. + * avoid some global flushes.. */ #include -#define local_invalidate() \ - __invalidate() +#define local_flush_tlb() \ + __flush_tlb() #undef CLEVER_SMP_INVALIDATE #ifdef CLEVER_SMP_INVALIDATE /* - * Smarter SMP invalidation macros. + * Smarter SMP flushing macros. * c/o Linus Torvalds. * * These mean you can really definitely utterly forget about * writing to user space from interrupts. (Its not allowed anyway). * - * Doesn't currently work as Linus makes invalidate calls before + * Doesn't currently work as Linus makes flush tlb calls before * stuff like current/current->mm are setup properly */ -static inline void invalidate_current_task(void) +static inline void flush_tlb_current_task(void) { if (current->mm->count == 1) /* just one copy of this mm */ - local_invalidate(); /* and that's us, so.. */ + local_flush_tlb(); /* and that's us, so.. */ else - smp_invalidate(); + smp_flush_tlb(); } -#define invalidate() invalidate_current_task() +#define flush_tlb() flush_tlb_current_task() -#define invalidate_all() smp_invalidate() +#define flush_tlb_all() smp_flush_tlb() -static inline void invalidate_mm(struct mm_struct * mm) +static inline void flush_tlb_mm(struct mm_struct * mm) { if (mm == current->mm && mm->count == 1) - local_invalidate(); + local_flush_tlb(); else - smp_invalidate(); + smp_flush_tlb(); } -static inline void invalidate_page(struct vm_area_struct * vma, +static inline void flush_tlb_page(struct vm_area_struct * vma, unsigned long va) { if (vma->vm_mm == current->mm && current->mm->count == 1) - __invalidate_one(va); + __flush_tlb_one(va); else - smp_invalidate(); + smp_flush_tlb(); } -static inline void invalidate_range(struct mm_struct * mm, +static inline void flush_tlb_range(struct mm_struct * mm, unsigned long start, unsigned long end) { - invalidate_mm(mm); + flush_tlb_mm(mm); } #else -#define invalidate() \ - smp_invalidate() +#define flush_tlb() \ + smp_flush_tlb() -#define invalidate_all() invalidate() +#define flush_tlb_all() flush_tlb() -static inline void invalidate_mm(struct mm_struct *mm) +static inline void flush_tlb_mm(struct mm_struct *mm) { - invalidate(); + flush_tlb(); } -static inline void invalidate_page(struct vm_area_struct *vma, +static inline void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr) { - invalidate(); + flush_tlb(); } -static inline void invalidate_range(struct mm_struct *mm, +static inline void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - invalidate(); + flush_tlb(); } #endif #endif diff -u --recursive --new-file v1.3.82/linux/include/asm-i386/smp.h linux/include/asm-i386/smp.h --- v1.3.82/linux/include/asm-i386/smp.h Mon Mar 25 08:58:21 1996 +++ linux/include/asm-i386/smp.h Wed Apr 3 10:59:32 1996 @@ -184,7 +184,7 @@ extern volatile int cpu_number_map[NR_CPUS]; extern volatile int cpu_logical_map[NR_CPUS]; extern volatile unsigned long smp_invalidate_needed; -extern void smp_invalidate(void); +extern void smp_flush_tlb(void); extern volatile unsigned long kernel_flag, kernel_counter; extern volatile unsigned long cpu_callin_map[NR_CPUS]; extern volatile unsigned char active_kernel_processor; diff -u --recursive --new-file v1.3.82/linux/include/asm-i386/smp_lock.h linux/include/asm-i386/smp_lock.h --- v1.3.82/linux/include/asm-i386/smp_lock.h Fri Feb 9 16:59:04 1996 +++ linux/include/asm-i386/smp_lock.h Wed Apr 3 10:59:32 1996 @@ -32,7 +32,7 @@ */ if (test_bit(proc, (void *)&smp_invalidate_needed)) if (clear_bit(proc, (void *)&smp_invalidate_needed)) - local_invalidate(); + local_flush_tlb(); } while(test_bit(0, (void *)&kernel_flag)); } diff -u --recursive --new-file v1.3.82/linux/include/asm-i386/system.h linux/include/asm-i386/system.h --- v1.3.82/linux/include/asm-i386/system.h Tue Apr 2 13:32:22 1996 +++ linux/include/asm-i386/system.h Wed Apr 3 11:25:40 1996 @@ -199,17 +199,17 @@ switch (size) { case 1: __asm__("xchgb %b0,%1" - :"=q" (x), "=m" (*__xg(ptr)) + :"=&q" (x), "=m" (*__xg(ptr)) :"0" (x), "m" (*__xg(ptr))); break; case 2: __asm__("xchgw %w0,%1" - :"=r" (x), "=m" (*__xg(ptr)) + :"=&r" (x), "=m" (*__xg(ptr)) :"0" (x), "m" (*__xg(ptr))); break; case 4: __asm__("xchgl %0,%1" - :"=r" (x), "=m" (*__xg(ptr)) + :"=&r" (x), "=m" (*__xg(ptr)) :"0" (x), "m" (*__xg(ptr))); break; } diff -u --recursive --new-file v1.3.82/linux/include/linux/interrupt.h linux/include/linux/interrupt.h --- v1.3.82/linux/include/linux/interrupt.h Sat Mar 16 14:29:29 1996 +++ linux/include/linux/interrupt.h Wed Apr 3 09:20:45 1996 @@ -29,6 +29,7 @@ TIMER_BH = 0, CONSOLE_BH, TQUEUE_BH, + DIGI_BH, SERIAL_BH, NET_BH, IMMEDIATE_BH, diff -u --recursive --new-file v1.3.82/linux/include/linux/timer.h linux/include/linux/timer.h --- v1.3.82/linux/include/linux/timer.h Fri Feb 23 09:39:35 1996 +++ linux/include/linux/timer.h Wed Apr 3 09:20:45 1996 @@ -55,6 +55,8 @@ #define HD_TIMER2 24 #define GSCD_TIMER 25 +#define DIGI_TIMER 29 + struct timer_struct { unsigned long expires; void (*fn)(void); diff -u --recursive --new-file v1.3.82/linux/include/linux/tty.h linux/include/linux/tty.h --- v1.3.82/linux/include/linux/tty.h Mon Mar 25 10:22:43 1996 +++ linux/include/linux/tty.h Wed Apr 3 14:07:04 1996 @@ -284,6 +284,7 @@ extern int lp_init(void); extern int pty_init(void); extern int tty_init(void); +extern int pcxe_init(void); extern int vcs_init(void); extern int cy_init(void); extern int stl_init(void); @@ -328,6 +329,7 @@ /* pty.c */ extern int pty_open(struct tty_struct * tty, struct file * filp); +extern int pcxe_open(struct tty_struct *tty, struct file *filp); /* console.c */ diff -u --recursive --new-file v1.3.82/linux/ipc/shm.c linux/ipc/shm.c --- v1.3.82/linux/ipc/shm.c Wed Mar 13 13:56:21 1996 +++ linux/ipc/shm.c Wed Apr 3 10:59:32 1996 @@ -433,6 +433,7 @@ /* map page range */ shm_sgn = shmd->vm_pte + SWP_ENTRY(0, (shmd->vm_offset >> PAGE_SHIFT) << SHM_IDX_SHIFT); + flush_cache_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end); for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE, shm_sgn += SWP_ENTRY(0, 1 << SHM_IDX_SHIFT)) @@ -446,7 +447,7 @@ return -ENOMEM; set_pte(page_table, __pte(shm_sgn)); } - invalidate_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end); + flush_tlb_range(shmd->vm_mm, shmd->vm_start, shmd->vm_end); return 0; } @@ -760,12 +761,13 @@ } if (pte_page(pte) != pte_page(page)) printk("shm_swap_out: page and pte mismatch\n"); + flush_cache_page(shmd, tmp); set_pte(page_table, __pte(shmd->vm_pte + SWP_ENTRY(0, idx << SHM_IDX_SHIFT))); mem_map[MAP_NR(pte_page(pte))].count--; if (shmd->vm_mm->rss > 0) shmd->vm_mm->rss--; - invalidate_page(shmd, tmp); + flush_tlb_page(shmd, tmp); /* continue looping through circular list */ } while (0); if ((shmd = shmd->vm_next_share) == shp->attaches) diff -u --recursive --new-file v1.3.82/linux/kernel/sched.c linux/kernel/sched.c --- v1.3.82/linux/kernel/sched.c Tue Apr 2 13:32:23 1996 +++ linux/kernel/sched.c Wed Apr 3 10:59:33 1996 @@ -125,7 +125,7 @@ { if(clear_bit(cpu,&smp_invalidate_needed)) { - local_invalidate(); + local_flush_tlb(); set_bit(cpu,&cpu_callin_map[0]); } } diff -u --recursive --new-file v1.3.82/linux/mm/filemap.c linux/mm/filemap.c --- v1.3.82/linux/mm/filemap.c Tue Apr 2 13:32:23 1996 +++ linux/mm/filemap.c Wed Apr 3 10:59:33 1996 @@ -579,9 +579,9 @@ unsigned long page = pte_page(*page_table); unsigned long entry = SWP_ENTRY(SHM_SWP_TYPE, MAP_NR(page)); + flush_cache_page(vma, (offset + vma->vm_start - vma->vm_offset)); set_pte(page_table, __pte(entry)); - /* Yuck, perhaps a slightly modified swapout parameter set? */ - invalidate_page(vma, (offset + vma->vm_start - vma->vm_offset)); + flush_tlb_page(vma, (offset + vma->vm_start - vma->vm_offset)); error = filemap_write_page(vma, offset, page); if (pte_val(*page_table) == entry) pte_clear(page_table); @@ -618,15 +618,17 @@ return 0; if (!pte_dirty(pte)) return 0; + flush_cache_page(vma, address); set_pte(ptep, pte_mkclean(pte)); - invalidate_page(vma, address); + flush_tlb_page(vma, address); page = pte_page(pte); mem_map[MAP_NR(page)].count++; } else { if (pte_none(pte)) return 0; + flush_cache_page(vma, address); pte_clear(ptep); - invalidate_page(vma, address); + flush_tlb_page(vma, address); if (!pte_present(pte)) { swap_free(pte_val(pte)); return 0; @@ -710,12 +712,13 @@ int error = 0; dir = pgd_offset(current->mm, address); + flush_cache_range(vma->vm_mm, end - size, end); while (address < end) { error |= filemap_sync_pmd_range(dir, address, end - address, vma, flags); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_range(vma->vm_mm, end - size, end); + flush_tlb_range(vma->vm_mm, end - size, end); return error; } diff -u --recursive --new-file v1.3.82/linux/mm/memory.c linux/mm/memory.c --- v1.3.82/linux/mm/memory.c Fri Mar 22 14:05:44 1996 +++ linux/mm/memory.c Wed Apr 3 14:12:34 1996 @@ -135,9 +135,10 @@ printk("%s trying to clear kernel page-directory: not good\n", tsk->comm); return; } + flush_cache_mm(tsk->mm); for (i = 0 ; i < USER_PTRS_PER_PGD ; i++) free_one_pgd(page_dir + i); - invalidate_mm(tsk->mm); + flush_tlb_mm(tsk->mm); } /* @@ -156,7 +157,8 @@ printk("%s trying to free kernel page-directory: not good\n", tsk->comm); return; } - invalidate_mm(tsk->mm); + flush_cache_mm(tsk->mm); + flush_tlb_mm(tsk->mm); SET_PAGE_DIR(tsk, swapper_pg_dir); tsk->mm->pgd = swapper_pg_dir; /* or else... */ for (i = 0 ; i < USER_PTRS_PER_PGD ; i++) @@ -171,9 +173,10 @@ if (!(new_pg = pgd_alloc())) return -ENOMEM; page_dir = pgd_offset(&init_mm, 0); + flush_cache_mm(tsk->mm); memcpy(new_pg + USER_PTRS_PER_PGD, page_dir + USER_PTRS_PER_PGD, (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof (pgd_t)); - invalidate_mm(tsk->mm); + flush_tlb_mm(tsk->mm); SET_PAGE_DIR(tsk, new_pg); tsk->mm->pgd = new_pg; return 0; @@ -285,6 +288,8 @@ cow = (vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE; src_pgd = pgd_offset(src, address); dst_pgd = pgd_offset(dst, address); + flush_cache_range(src, vma->vm_start, vma->vm_end); + flush_cache_range(dst, vma->vm_start, vma->vm_end); while (address < end) { error = copy_pmd_range(dst_pgd++, src_pgd++, address, end - address, cow); if (error) @@ -292,8 +297,8 @@ address = (address + PGDIR_SIZE) & PGDIR_MASK; } /* Note that the src ptes get c-o-w treatment, so they change too. */ - invalidate_range(src, vma->vm_start, vma->vm_end); - invalidate_range(dst, vma->vm_start, vma->vm_end); + flush_tlb_range(src, vma->vm_start, vma->vm_end); + flush_tlb_range(dst, vma->vm_start, vma->vm_end); return error; } @@ -373,12 +378,13 @@ unsigned long end = address + size; dir = pgd_offset(mm, address); + flush_cache_range(mm, end - size, end); while (address < end) { zap_pmd_range(dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_range(mm, end - size, end); + flush_tlb_range(mm, end - size, end); return 0; } @@ -428,6 +434,7 @@ zero_pte = pte_wrprotect(mk_pte(ZERO_PAGE, prot)); dir = pgd_offset(current->mm, address); + flush_cache_range(current->mm, beg, end); while (address < end) { pmd_t *pmd = pmd_alloc(dir, address); error = -ENOMEM; @@ -439,7 +446,7 @@ address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_range(current->mm, beg, end); + flush_tlb_range(current->mm, beg, end); return error; } @@ -499,6 +506,7 @@ offset -= from; dir = pgd_offset(current->mm, from); + flush_cache_range(current->mm, beg, from); while (from < end) { pmd_t *pmd = pmd_alloc(dir, from); error = -ENOMEM; @@ -510,7 +518,7 @@ from = (from + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_range(current->mm, beg, from); + flush_tlb_range(current->mm, beg, from); return error; } @@ -619,19 +627,24 @@ if (mem_map[MAP_NR(old_page)].reserved) ++vma->vm_mm->rss; copy_page(old_page,new_page); + flush_page_to_ram(old_page); + flush_page_to_ram(new_page); + flush_cache_page(vma, address); set_pte(page_table, pte_mkwrite(pte_mkdirty(mk_pte(new_page, vma->vm_page_prot)))); free_page(old_page); - invalidate_page(vma, address); + flush_tlb_page(vma, address); return; } + flush_cache_page(vma, address); set_pte(page_table, BAD_PAGE); + flush_tlb_page(vma, address); free_page(old_page); oom(tsk); - invalidate_page(vma, address); return; } + flush_cache_page(vma, address); set_pte(page_table, pte_mkdirty(pte_mkwrite(pte))); - invalidate_page(vma, address); + flush_tlb_page(vma, address); if (new_page) free_page(new_page); return; @@ -901,6 +914,7 @@ } address &= PAGE_MASK; if (!vma->vm_ops || !vma->vm_ops->nopage) { + flush_cache_page(vma, address); get_empty_page(tsk, vma, page_table, write_access); return; } @@ -914,7 +928,9 @@ page = vma->vm_ops->nopage(vma, address, write_access && !(vma->vm_flags & VM_SHARED)); if (!page) { send_sig(SIGBUS, current, 1); + flush_cache_page(vma, address); put_page(page_table, BAD_PAGE); + flush_tlb_page(vma, address); return; } /* @@ -932,7 +948,9 @@ entry = pte_mkwrite(pte_mkdirty(entry)); } else if (mem_map[MAP_NR(page)].count > 1 && !(vma->vm_flags & VM_SHARED)) entry = pte_wrprotect(entry); + flush_cache_page(vma, address); put_page(page_table, entry); + flush_tlb_page(vma, address); } /* diff -u --recursive --new-file v1.3.82/linux/mm/mprotect.c linux/mm/mprotect.c --- v1.3.82/linux/mm/mprotect.c Mon Dec 11 15:41:12 1995 +++ linux/mm/mprotect.c Wed Apr 3 10:59:33 1996 @@ -75,12 +75,13 @@ unsigned long beg = start; dir = pgd_offset(current->mm, start); + flush_cache_range(current->mm, beg, end); while (start < end) { change_pmd_range(dir, start, end - start, newprot); start = (start + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_range(current->mm, beg, end); + flush_tlb_range(current->mm, beg, end); return; } diff -u --recursive --new-file v1.3.82/linux/mm/mremap.c linux/mm/mremap.c --- v1.3.82/linux/mm/mremap.c Sat Mar 23 19:25:55 1996 +++ linux/mm/mremap.c Wed Apr 3 10:59:33 1996 @@ -93,7 +93,8 @@ { unsigned long offset = len; - invalidate_range(mm, old_addr, old_addr + len); + flush_cache_range(mm, old_addr, old_addr + len); + flush_tlb_range(mm, old_addr, old_addr + len); /* * This is not the clever way to do this, but we're taking the @@ -115,9 +116,10 @@ * the old page tables) */ oops_we_failed: + flush_cache_range(mm, new_addr, new_addr + len); while ((offset += PAGE_SIZE) < len) move_one_page(mm, new_addr + offset, old_addr + offset); - invalidate_range(mm, new_addr, new_addr + len); + flush_tlb_range(mm, new_addr, new_addr + len); zap_page_range(mm, new_addr, new_addr + len); return -1; } diff -u --recursive --new-file v1.3.82/linux/mm/vmalloc.c linux/mm/vmalloc.c --- v1.3.82/linux/mm/vmalloc.c Mon Dec 11 15:41:12 1995 +++ linux/mm/vmalloc.c Wed Apr 3 10:59:33 1996 @@ -100,12 +100,13 @@ unsigned long end = address + size; dir = pgd_offset(&init_mm, address); + flush_cache_all(); while (address < end) { free_area_pmd(dir, address, end - address); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_all(); + flush_tlb_all(); } static inline int alloc_area_pte(pte_t * pte, unsigned long address, unsigned long size) @@ -156,6 +157,7 @@ unsigned long end = address + size; dir = pgd_offset(&init_mm, address); + flush_cache_all(); while (address < end) { pmd_t *pmd = pmd_alloc_kernel(dir, address); if (!pmd) @@ -166,7 +168,7 @@ address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_all(); + flush_tlb_all(); return 0; } @@ -217,6 +219,7 @@ offset -= address; dir = pgd_offset(&init_mm, address); + flush_cache_all(); while (address < end) { pmd_t *pmd = pmd_alloc_kernel(dir, address); if (!pmd) @@ -227,7 +230,7 @@ address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } - invalidate_all(); + flush_tlb_all(); return 0; } diff -u --recursive --new-file v1.3.82/linux/mm/vmscan.c linux/mm/vmscan.c --- v1.3.82/linux/mm/vmscan.c Wed Mar 13 15:15:22 1996 +++ linux/mm/vmscan.c Wed Apr 3 10:59:33 1996 @@ -110,8 +110,9 @@ if (!(entry = get_swap_page())) return 0; vma->vm_mm->rss--; + flush_cache_page(vma, address); set_pte(page_table, __pte(entry)); - invalidate_page(vma, address); + flush_tlb_page(vma, address); tsk->nswap++; rw_swap_page(WRITE, entry, (char *) page, wait); } @@ -125,14 +126,16 @@ return 0; } vma->vm_mm->rss--; + flush_cache_page(vma, address); set_pte(page_table, __pte(entry)); - invalidate_page(vma, address); + flush_tlb_page(vma, address); free_page(page); return 1; } vma->vm_mm->rss--; + flush_cache_page(vma, address); pte_clear(page_table); - invalidate_page(vma, address); + flush_tlb_page(vma, address); entry = page_unuse(page); free_page(page); return entry; diff -u --recursive --new-file v1.3.82/linux/net/core/firewall.c linux/net/core/firewall.c --- v1.3.82/linux/net/core/firewall.c Mon Nov 6 12:59:01 1995 +++ linux/net/core/firewall.c Wed Apr 3 11:29:39 1996 @@ -41,13 +41,18 @@ p=&((*p)->next); } - fw->next=*p; + /* - * We need to set p atomically in case someone runs down the list - * at the wrong moment. This saves locking it + * We need to use a memory barrier to make sure that this + * works correctly even in SMP with weakly ordered writes. + * + * This is atomic wrt interrupts (and generally walking the + * chain), but not wrt itself (so you can't call this from + * an interrupt. Not that you'd want to). */ - - xchg(p,fw); + fw->next=*p; + mb(); + *p = fw; /* * And release the sleep lock @@ -83,7 +88,7 @@ if(*nl==fw) { struct firewall_ops *f=fw->next; - xchg(nl,f); + *nl = f; firewall_lock=0; return 0; } diff -u --recursive --new-file v1.3.82/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c --- v1.3.82/linux/net/ipv4/tcp_input.c Tue Apr 2 13:32:24 1996 +++ linux/net/ipv4/tcp_input.c Wed Apr 3 11:56:50 1996 @@ -1384,35 +1384,6 @@ if (!skb->acked) { - - /* - * This is important. If we don't have much room left, - * we need to throw out a few packets so we have a good - * window. Note that mtu is used, not mss, because mss is really - * for the send side. He could be sending us stuff as large as mtu. - */ - - while (sock_rspace(sk) < sk->mtu) - { - struct sk_buff * skb1 = skb_peek(&sk->receive_queue); - if (skb1 == NULL) - { - printk("INET: tcp.c:tcp_data memory leak detected.\n"); - break; - } - - /* - * Don't throw out something that has been acked. - */ - - if (skb1->acked) - { - break; - } - - skb_unlink(skb1); - kfree_skb(skb1, FREE_READ); - } tcp_send_ack(sk->sent_seq, sk->acked_seq, sk, th, saddr); sk->ack_backlog++; tcp_reset_xmit_timer(sk, TIME_WRITE, min(sk->ato, HZ/2)); @@ -1500,6 +1471,66 @@ } } +/* + * Throw out all unnecessary packets: we've gone over the + * receive queue limit. This shouldn't happen in a normal + * TCP connection, but we might have gotten duplicates etc. + */ +static inline void tcp_forget_unacked(struct sk_buff_head * list) +{ + for (;;) { + struct sk_buff * skb = list->prev; + + /* gone through it all? */ + if (skb == (struct sk_buff *) list) + break; + if (skb->acked) + break; + __skb_unlink(skb, list); + } +} + +/* + * This should be a bit smarter and remove partially + * overlapping stuff too, but this should be good + * enough for any even remotely normal case (and the + * worst that can happen is that we have a few + * unnecessary packets in the receive queue). + */ +static inline void tcp_remove_dups(struct sk_buff_head * list) +{ + struct sk_buff * skb = list->next; + + for (;;) { + struct sk_buff * next; + + if (skb == (struct sk_buff *) list) + break; + next = skb->next; + if (next->seq == skb->seq) { + if (before(next->end_seq, skb->end_seq)) { + __skb_unlink(next, list); + continue; + } + __skb_unlink(skb, list); + } + skb = next; + } +} + +static void prune_queue(struct sk_buff_head * list) +{ + /* + * Throw out things we haven't acked. + */ + tcp_forget_unacked(list); + + /* + * Throw out duplicates + */ + tcp_remove_dups(list); +} + /* * A TCP packet has arrived. @@ -1846,19 +1877,6 @@ * now drop it (we must process the ack first to avoid * deadlock cases). */ -#if 0 - /* - * Is this test really a good idea? We should - * throw away packets that aren't in order, not - * new packets. - */ - if (sk->rmem_alloc >= sk->rcvbuf) - { - kfree_skb(skb, FREE_READ); - return(0); - } -#endif - /* * Process urgent data @@ -1872,6 +1890,13 @@ if(tcp_data(skb,sk, saddr, len)) kfree_skb(skb, FREE_READ); + + /* + * If our receive queue has grown past its limits, + * try to prune away duplicates etc.. + */ + if (sk->rmem_alloc > sk->rcvbuf) + prune_queue(&sk->receive_queue); /* * And done