diff -u --recursive --new-file v2.2.4/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v2.2.4/linux/Documentation/Configure.help Tue Mar 23 14:35:46 1999 +++ linux/Documentation/Configure.help Fri Mar 26 13:23:20 1999 @@ -7957,6 +7957,19 @@ read Documentation/modules.txt. The module will be called istallion.o. +Microgate SyncLink adapter support +CONFIG_SYNCLINK + Provides support for the SyncLink ISA and PCI + multiprotocol serial adapters. These adapters + support asynchronous and HDLC bit synchronous + communication up to 10Mbps (PCI adapter) + +Synchronous HDLC line discipline support +CONFIG_N_HDLC + Allows synchronous HDLC communications with + tty device drivers that support synchronous + HDLC such as the Microgate SyncLink adapter. + Hayes ESP serial port support CONFIG_ESPSERIAL This is a driver which supports Hayes ESP serial ports. Both single @@ -8029,15 +8042,14 @@ removed from the running kernel whenever you want), say M here and read Documentation/modules.txt. The module will be called lp.o. - If you have several parallel ports, you should specify the base - address for the port to be used by the printer with the "lp" kernel - command line option. (Try "man bootparam" or see the documentation - of your boot loader (lilo or loadlin) about how to pass options to - the kernel at boot time. The lilo procedure is also explained in the - SCSI-HOWTO, available via FTP (user: anonymous) in - ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.) The standard base - addresses as well as the syntax of the "lp" command line option can - be found in drivers/char/lp.c. + If you have several parallel ports, you can specify which ports to + use with the "lp" kernel command line option. (Try "man bootparam" + or see the documentation of your boot loader (lilo or loadlin) + about how to pass options to the kernel at boot time. The lilo + procedure is also explained in the SCSI-HOWTO, available via FTP + (user: anonymous) in ftp://metalab.unc.edu/pub/Linux/docs/HOWTO.) + The syntax of the "lp" command line option can be found in + drivers/char/lp.c. If you have more than 3 printers, you need to increase the LP_NO variable in lp.c. diff -u --recursive --new-file v2.2.4/linux/Documentation/parport.txt linux/Documentation/parport.txt --- v2.2.4/linux/Documentation/parport.txt Tue Jul 21 00:15:29 1998 +++ linux/Documentation/parport.txt Fri Mar 26 13:23:24 1999 @@ -87,7 +87,7 @@ If you have configured the /proc filesystem into your kernel, you will see a new directory entry: /proc/parport. In there will be a directory entry for each parallel port for which parport is -configured. In each of those directories are three files describing +configured. In each of those directories are four files describing that parallel port. For example: File: Contents: diff -u --recursive --new-file v2.2.4/linux/Makefile linux/Makefile --- v2.2.4/linux/Makefile Tue Mar 23 14:35:46 1999 +++ linux/Makefile Tue Mar 23 14:48:17 1999 @@ -1,6 +1,6 @@ VERSION = 2 PATCHLEVEL = 2 -SUBLEVEL = 4 +SUBLEVEL = 5 EXTRAVERSION = ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) diff -u --recursive --new-file v2.2.4/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c --- v2.2.4/linux/arch/i386/kernel/ptrace.c Tue Mar 23 14:35:46 1999 +++ linux/arch/i386/kernel/ptrace.c Wed Mar 24 13:18:46 1999 @@ -386,7 +386,7 @@ (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || - (cap_issubset(child->cap_permitted, current->cap_permitted)) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ diff -u --recursive --new-file v2.2.4/linux/arch/sparc/kernel/process.c linux/arch/sparc/kernel/process.c --- v2.2.4/linux/arch/sparc/kernel/process.c Tue Mar 23 14:35:46 1999 +++ linux/arch/sparc/kernel/process.c Thu Mar 25 09:23:33 1999 @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.132 1999/03/22 02:12:13 davem Exp $ +/* $Id: process.c,v 1.133 1999/03/24 11:42:30 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -517,9 +517,16 @@ p->tss.kregs = childregs; if(regs->psr & PSR_PS) { - childregs->u_regs[UREG_FP] = p->tss.ksp; + new_stack = (struct reg_window *) + ((((unsigned long)p) + + (TASK_UNION_SIZE)) - + (REGWIN_SZ)); + childregs->u_regs[UREG_FP] = (unsigned long) new_stack; p->tss.flags |= SPARC_FLAG_KTHREAD; p->tss.current_ds = KERNEL_DS; + memcpy((void *)new_stack, + (void *)regs->u_regs[UREG_FP], + sizeof(struct reg_window)); childregs->u_regs[UREG_G6] = (unsigned long) p; } else { childregs->u_regs[UREG_FP] = sp; diff -u --recursive --new-file v2.2.4/linux/arch/sparc/kernel/ptrace.c linux/arch/sparc/kernel/ptrace.c --- v2.2.4/linux/arch/sparc/kernel/ptrace.c Tue Mar 23 14:35:46 1999 +++ linux/arch/sparc/kernel/ptrace.c Wed Mar 24 15:10:40 1999 @@ -542,7 +542,7 @@ (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || - (cap_issubset(child->cap_permitted, current->cap_permitted)) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; diff -u --recursive --new-file v2.2.4/linux/arch/sparc/mm/srmmu.c linux/arch/sparc/mm/srmmu.c --- v2.2.4/linux/arch/sparc/mm/srmmu.c Tue Mar 23 14:35:47 1999 +++ linux/arch/sparc/mm/srmmu.c Thu Mar 25 09:23:33 1999 @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.184 1999/03/20 22:02:03 davem Exp $ +/* $Id: srmmu.c,v 1.185 1999/03/24 11:42:35 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -817,19 +817,11 @@ static void srmmu_switch_to_context(struct task_struct *tsk) { - int set = 0; - if(tsk->mm->context == NO_CONTEXT) { alloc_context(tsk->mm); - flush_cache_mm(tsk->mm); ctxd_set(&srmmu_context_table[tsk->mm->context], tsk->mm->pgd); - flush_tlb_mm(tsk->mm); - set = 1; - } else if(tsk->mm != current->mm) - set = 1; - - if(set != 0) - srmmu_set_context(tsk->mm->context); + } + srmmu_set_context(tsk->mm->context); } static void srmmu_init_new_context(struct mm_struct *mm) @@ -1353,7 +1345,7 @@ static void viking_update_rootmmu_dir(struct task_struct *tsk, pgd_t *pgdp) { if(pgdp != swapper_pg_dir) - viking_flush_page((unsigned long)pgdp); + flush_chunk((unsigned long)pgdp); if(tsk->mm->context != NO_CONTEXT && tsk->mm->pgd != pgdp) { flush_cache_mm(tsk->mm); @@ -1402,8 +1394,6 @@ static void hypersparc_switch_to_context(struct task_struct *tsk) { - int set = 0; - if(tsk->mm->context == NO_CONTEXT) { ctxd_t *ctxp; @@ -1411,14 +1401,9 @@ ctxp = &srmmu_context_table[tsk->mm->context]; srmmu_set_entry((pte_t *)ctxp, __pte((SRMMU_ET_PTD | (srmmu_v2p((unsigned long) tsk->mm->pgd) >> 4)))); hypersparc_flush_page_to_ram((unsigned long)ctxp); - set = 1; - } else if(tsk->mm != current->mm) - set = 1; - - if(set != 0) { - hyper_flush_whole_icache(); - srmmu_set_context(tsk->mm->context); } + hyper_flush_whole_icache(); + srmmu_set_context(tsk->mm->context); } static void hypersparc_init_new_context(struct mm_struct *mm) @@ -2739,11 +2724,10 @@ BTFIXUPSET_CALL(flush_page_for_dma, viking_flush_page_for_dma, BTFIXUPCALL_NOP); } - /* flush_cache_* are nops */ - BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NOP); + BTFIXUPSET_CALL(flush_cache_all, viking_flush_cache_all, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_mm, viking_flush_cache_mm, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_page, viking_flush_cache_page, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(flush_cache_range, viking_flush_cache_range, BTFIXUPCALL_NORM); #ifdef __SMP__ if (sparc_cpu_model == sun4d) { diff -u --recursive --new-file v2.2.4/linux/arch/sparc/mm/viking.S linux/arch/sparc/mm/viking.S --- v2.2.4/linux/arch/sparc/mm/viking.S Tue Mar 23 14:35:47 1999 +++ linux/arch/sparc/mm/viking.S Thu Mar 25 09:23:33 1999 @@ -1,4 +1,4 @@ -/* $Id: viking.S,v 1.12 1999/02/23 13:23:50 jj Exp $ +/* $Id: viking.S,v 1.13 1999/03/24 11:42:32 davem Exp $ * viking.S: High speed Viking cache/mmu operations * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -67,7 +67,7 @@ clr %o1 ! set counter, 0 - 127 sethi %hi(KERNBASE + PAGE_SIZE - 0x80000000), %o3 sethi %hi(0x80000000), %o4 - sethi %hi(VIKING_PTAG_VALID | VIKING_PTAG_DIRTY), %o5 + sethi %hi(VIKING_PTAG_VALID), %o5 sethi %hi(2*PAGE_SIZE), %o0 sethi %hi(PAGE_SIZE), %g7 clr %o2 ! block counter, 0 - 3 @@ -80,15 +80,12 @@ or %g5, %g4, %g5 ldda [%g5] ASI_M_DATAC_TAG, %g2 cmp %g3, %g1 ! ptag == ppage? - bne,a 7f + bne 7f inc %o2 - and %g2, %o5, %g3 ! ptag VALID and DIRTY? - cmp %g3, %o5 - bne,a 7f - inc %o2 - - add %g4, %o3, %g2 ! (KERNBASE + PAGE_SIZE) | (set << 5) + andcc %g2, %o5, %g0 ! ptag VALID? + be 7f + add %g4, %o3, %g2 ! (KERNBASE + PAGE_SIZE) | (set << 5) ld [%g2], %g3 ld [%g2 + %g7], %g3 add %g2, %o0, %g2 @@ -99,18 +96,15 @@ ld [%g2 + %g7], %g3 add %g2, %o0, %g2 ld [%g2], %g3 - ld [%g2 + %g7], %g3 - b 8f - inc %o1 + ld [%g2 + %g7], %g3 7: cmp %o2, 3 ble 6b sll %o2, 26, %g5 ! block << 26 - inc %o1 -8: +8: inc %o1 cmp %o1, 0x7f ble 5b clr %o2 @@ -148,10 +142,33 @@ retl nop -viking_flush_cache_all: +#define WINDOW_FLUSH(tmp1, tmp2) \ + mov 0, tmp1; \ +98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \ + orcc %g0, tmp2, %g0; \ + add tmp1, 1, tmp1; \ + bne 98b; \ + save %sp, -64, %sp; \ +99: subcc tmp1, 1, tmp1; \ + bne 99b; \ + restore %g0, %g0, %g0; + +viking_flush_cache_page: +#ifndef __SMP__ + ld [%o0 + 0x0], %o0 /* XXX vma->vm_mm, GROSS XXX */ +#endif viking_flush_cache_mm: viking_flush_cache_range: -viking_flush_cache_page: +#ifndef __SMP__ + ld [%o0 + AOFF_mm_context], %g1 + cmp %g1, -1 + bne viking_flush_cache_all + nop + b,a viking_flush_cache_out +#endif +viking_flush_cache_all: + WINDOW_FLUSH(%g4, %g5) +viking_flush_cache_out: retl nop diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c --- v2.2.4/linux/arch/sparc64/kernel/ioctl32.c Tue Mar 23 14:35:47 1999 +++ linux/arch/sparc64/kernel/ioctl32.c Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.59 1999/03/12 13:30:21 jj Exp $ +/* $Id: ioctl32.c,v 1.60 1999/03/22 10:40:54 jj Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -577,8 +577,7 @@ cmap.transp = kmalloc(cmap.len * sizeof(__u16), GFP_KERNEL); if (!cmap.transp) goto out; - } else - cmap.transp = NULL; + } if (cmd == FBIOGETCMAP) break; diff -u --recursive --new-file v2.2.4/linux/arch/sparc64/kernel/ptrace.c linux/arch/sparc64/kernel/ptrace.c --- v2.2.4/linux/arch/sparc64/kernel/ptrace.c Tue Mar 23 14:35:47 1999 +++ linux/arch/sparc64/kernel/ptrace.c Wed Mar 24 15:10:28 1999 @@ -605,7 +605,7 @@ (current->uid != child->uid) || (current->gid != child->egid) || (current->gid != child->sgid) || - (cap_issubset(child->cap_permitted, current->cap_permitted)) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) { pt_error_return(regs, EPERM); goto out; diff -u --recursive --new-file v2.2.4/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v2.2.4/linux/drivers/block/floppy.c Wed Jan 13 15:00:41 1999 +++ linux/drivers/block/floppy.c Fri Mar 26 13:29:14 1999 @@ -3592,7 +3592,7 @@ /* read drive info out of physical CMOS */ drive=0; if (!UDP->cmos) - UDP->cmos= FLOPPY0_TYPE; + UDP->cmos = FLOPPY0_TYPE; drive=1; if (!UDP->cmos && FLOPPY1_TYPE) UDP->cmos = FLOPPY1_TYPE; @@ -3601,26 +3601,31 @@ /* additional physical CMOS drive detection should go here */ for (drive=0; drive < N_DRIVE; drive++){ - if (UDP->cmos >= 16) - UDP->cmos = 0; - if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params)) - memcpy((char *) UDP, - (char *) (&default_drive_params[(int)UDP->cmos].params), - sizeof(struct floppy_drive_params)); - if (UDP->cmos){ - if (first) - printk(KERN_INFO "Floppy drive(s): "); - else - printk(", "); - first=0; - if (UDP->cmos > 0){ + unsigned int type = UDP->cmos; + struct floppy_drive_params *params; + const char *name = NULL; + static char temparea[32]; + + if (type < NUMBER(default_drive_params)) { + params = &default_drive_params[type].params; + if (type) { + name = default_drive_params[type].name; allowed_drive_mask |= 1 << drive; - printk("fd%d is %s", drive, - default_drive_params[(int)UDP->cmos].name); - } else - printk("fd%d is unknown type %d",drive, - UDP->cmos); + } + } else { + params = &default_drive_params[0].params; + sprintf(temparea, "unknown type %d (usb?)", type); + name = temparea; + } + if (name) { + const char * prepend = ","; + if (first) { + prepend = KERN_INFO "Floppy drive(s):"; + first = 0; + } + printk("%s fd%d is %s", prepend, drive, name); } + *UDP = *params; } if (!first) printk("\n"); @@ -4020,11 +4025,6 @@ } if (current_drive >= 4 && !FDC2) FDC2 = 0x370; - if (ints[2] <= 0 || - (ints[2] >= NUMBER(default_drive_params) && ints[2] != 16)){ - DPRINT("bad CMOS code %d\n", ints[2]); - return; - } DP->cmos = ints[2]; DPRINT("setting CMOS code to %d\n", ints[2]); } diff -u --recursive --new-file v2.2.4/linux/drivers/block/rd.c linux/drivers/block/rd.c --- v2.2.4/linux/drivers/block/rd.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/block/rd.c Fri Mar 26 13:57:41 1999 @@ -158,8 +158,6 @@ static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int err; - if (!inode || !inode->i_rdev) return -EINVAL; diff -u --recursive --new-file v2.2.4/linux/drivers/char/Config.in linux/drivers/char/Config.in --- v2.2.4/linux/drivers/char/Config.in Wed Mar 10 15:29:45 1999 +++ linux/drivers/char/Config.in Wed Mar 24 11:46:00 1999 @@ -42,6 +42,8 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then dep_tristate 'Multi-Tech multiport card support' CONFIG_ISI m fi + dep_tristate 'Microgate SyncLink card support' CONFIG_SYNCLINK m + dep_tristate 'HDLC line discipline support' CONFIG_N_HDLC m fi bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then diff -u --recursive --new-file v2.2.4/linux/drivers/char/Makefile linux/drivers/char/Makefile --- v2.2.4/linux/drivers/char/Makefile Tue Mar 23 14:35:47 1999 +++ linux/drivers/char/Makefile Wed Mar 24 11:46:00 1999 @@ -139,6 +139,14 @@ endif endif +ifeq ($(CONFIG_SYNCLINK),m) + M_OBJS += synclink.o +endif + +ifeq ($(CONFIG_N_HDLC),m) + M_OBJS += n_hdlc.o +endif + ifeq ($(CONFIG_SPECIALIX),y) L_OBJS += specialix.o else diff -u --recursive --new-file v2.2.4/linux/drivers/char/bttv.c linux/drivers/char/bttv.c --- v2.2.4/linux/drivers/char/bttv.c Wed Mar 10 15:29:45 1999 +++ linux/drivers/char/bttv.c Fri Mar 26 13:57:41 1999 @@ -3751,6 +3751,8 @@ #ifdef MODULE +EXPORT_NO_SYMBOLS; + int init_module(void) { #else diff -u --recursive --new-file v2.2.4/linux/drivers/char/bw-qcam.c linux/drivers/char/bw-qcam.c --- v2.2.4/linux/drivers/char/bw-qcam.c Tue Mar 23 14:35:47 1999 +++ linux/drivers/char/bw-qcam.c Fri Mar 26 13:57:41 1999 @@ -986,11 +986,41 @@ kfree(qcam); } +/* The parport parameter controls which parports will be scanned. + * Scanning all parports causes some printers to print a garbage page. + * -- March 14, 1999 Billy Donahue */ +#ifdef MODULE +static char *parport[MAX_CAMS] = { NULL, }; +MODULE_PARM(parport, "1-" __MODULE_STRING(MAX_CAMS) "s"); +#endif + #ifdef MODULE int init_module(void) { struct parport *port; - + int n; + if(parport[0] && strncmp(parport[0], "auto", 4)){ + /* user gave parport parameters */ + for(n=0; parport[n] && nnext){ + if(r!=port->number) + continue; + init_bwqcam(port); + break; + } + } + return (num_cams)?0:-ENODEV; + } + /* no parameter or "auto" */ for (port = parport_enumerate(); port; port=port->next) init_bwqcam(port); diff -u --recursive --new-file v2.2.4/linux/drivers/char/misc.c linux/drivers/char/misc.c --- v2.2.4/linux/drivers/char/misc.c Wed Mar 10 15:29:46 1999 +++ linux/drivers/char/misc.c Fri Mar 26 13:57:41 1999 @@ -73,6 +73,7 @@ extern void watchdog_init(void); extern void wdt_init(void); extern void acq_init(void); +extern void dtlk_init(void); extern void pcwatchdog_init(void); extern int rtc_init(void); extern int rtc_DP8570A_init(void); diff -u --recursive --new-file v2.2.4/linux/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c --- v2.2.4/linux/drivers/char/n_hdlc.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/n_hdlc.c Fri Mar 12 08:20:38 1999 @@ -0,0 +1,1151 @@ +/* generic HDLC line discipline for Linux + * + * Written by Paul Fulghum paulkf@microgate.com + * for Microgate Corporation + * + * Microgate and SyncLink are registered trademarks of Microgate Corporation + * + * Adapted from ppp.c, written by Michael Callahan , + * Al Longyear , Paul Mackerras + * + * Original release 01/11/99 + * + * This code is released under the GNU General Public License (GPL) + * + * This module implements the tty line discipline N_HDLC for use with + * tty device drivers that support bit-synchronous HDLC communications. + * + * All HDLC data is frame oriented which means: + * + * 1. tty write calls represent one complete transmit frame of data + * The device driver should accept the complete frame or none of + * the frame (busy) in the write method. Each write call should have + * a byte count in the range of 2-4096 bytes (2 is min HDLC frame + * with 1 addr byte and 1 ctrl byte). + * + * 2. receive callbacks from the device driver represents + * one received frame. The device driver should bypass + * the tty flip buffer and call the line discipline receive + * callback directly to avoid fragmenting or concatenating + * multiple frames into a single receive callback. + * + * The HDLC line discipline queues the receive frames in seperate + * buffers so complete receive frames can be returned by the + * tty read calls. + * + * 3. tty read calls returns an entire frame of data or nothing. + * + * 4. all send and receive data is considered raw. No processing + * or translation is performed by the line discipline, regardless + * of the tty flags + * + * 5. When line discipline is queried for the amount of receive + * data available (FIOC), 0 is returned if no data available, + * otherwise the count of the next available frame is returned. + * (instead of the sum of all received frame counts). + * + * These conventions allow the standard tty programming interface + * to be used for synchronous HDLC applications when used with + * this line discipline (or another line discipline that is frame + * oriented such as N_PPP). + * + * The SyncLink driver (synclink.c) implements both asynchronous + * (using standard line discipline N_TTY) and synchronous HDLC + * (using N_HDLC) communications, with the latter using the above + * conventions. + * + * This implementation is very basic and does not maintain + * any statistics. The main point is to enforce the raw data + * and frame orientation of HDLC communications. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define HDLC_MAGIC 0x239e +#define HDLC_VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef VERSION +#define VERSION(major,minor,patch) (((((major)<<8)+(minor))<<8)+(patch)) + +#if LINUX_VERSION_CODE < VERSION(2,1,14) +#include +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,23) +#include +#endif + +#include +#include +#include +#include +#include /* to get the struct task_struct */ +#include /* used in new tty drivers */ +#include /* used in new tty drivers */ +#include +#include +#include +#include + +#include + +#ifdef CONFIG_KERNELD +#include +#endif + +#if LINUX_VERSION_CODE >= VERSION(2,1,4) +#include +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include +#endif + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \ + if (error == 0) \ + value = get_user(addr); \ +} while (0) + +#define COPY_FROM_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_READ, (void *) src, size); \ + if (error == 0) \ + memcpy_fromfs (dest, src, size); \ +} while (0) + +#define PUT_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \ + if (error == 0) \ + put_user (value, addr); \ +} while (0) + +#define COPY_TO_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) dest, size); \ + if (error == 0) \ + memcpy_tofs (dest, src, size); \ +} while (0) + +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,0) +#define __init +typedef int spinlock_t; +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define spin_lock(a) +#define spin_unlock(a) +#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();} +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,37) +#define test_and_set_bit(nr, addr) set_bit(nr, addr) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,57) +#define signal_pending(p) ((p)->signal & ~(p)->blocked) +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,25) +#define net_device_stats enet_statistics +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,60) +typedef int rw_ret_t; +typedef unsigned int rw_count_t; +#else +typedef ssize_t rw_ret_t; +typedef size_t rw_count_t; +#endif + +/* + * Buffers for individual HDLC frames + */ +#define MAX_HDLC_FRAME_SIZE 4096 +#define DEFAULT_RX_BUF_COUNT 10 +#define MAX_RX_BUF_COUNT 30 +#define DEFAULT_TX_BUF_COUNT 1 + +typedef struct _n_hdlc_buf +{ + struct _n_hdlc_buf *link; + int count; + char buf[MAX_HDLC_FRAME_SIZE]; +} N_HDLC_BUF; + +typedef struct _n_hdlc_buf_list +{ + N_HDLC_BUF *head; + N_HDLC_BUF *tail; + int count; + spinlock_t spinlock; + +} N_HDLC_BUF_LIST; + +/* + * Per device instance data structure + */ +struct n_hdlc { + int magic; /* magic value for structure */ + __u32 flags; /* miscellaneous control flags */ + + struct tty_struct *tty; /* ptr to TTY structure */ + struct tty_struct *backup_tty; /* TTY to use if tty gets closed */ + + /* Queues for select() functionality */ + struct wait_queue *read_wait; + struct wait_queue *write_wait; + + int tbusy; /* reentrancy flag for tx wakeup code */ + int woke_up; + N_HDLC_BUF *tbuf; /* currently transmitting tx buffer */ + N_HDLC_BUF_LIST tx_buf_list; /* list of pending transmit frame buffers */ + N_HDLC_BUF_LIST rx_buf_list; /* list of received frame buffers */ + N_HDLC_BUF_LIST tx_free_buf_list; /* list unused transmit frame buffers */ + N_HDLC_BUF_LIST rx_free_buf_list; /* list unused received frame buffers */ +}; + +/* + * HDLC buffer list manipulation functions + */ +void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list); +void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf); +N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list); + +/* Local functions */ + +static struct n_hdlc *n_hdlc_alloc (void); + +#if LINUX_VERSION_CODE >= VERSION(2,1,19) +MODULE_PARM(debuglevel, "i"); +#endif + +/* debug level can be set by insmod for debugging purposes */ +#define DEBUG_LEVEL_INFO 1 +int debuglevel=0; + +/* TTY callbacks */ + +static rw_ret_t n_hdlc_tty_read(struct tty_struct *, + struct file *, __u8 *, rw_count_t); +static rw_ret_t n_hdlc_tty_write(struct tty_struct *, + struct file *, const __u8 *, rw_count_t); +static int n_hdlc_tty_ioctl(struct tty_struct *, + struct file *, unsigned int, unsigned long); +#if LINUX_VERSION_CODE < VERSION(2,1,23) +static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait); +#else +static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, struct file *filp, + poll_table * wait); +#endif +static int n_hdlc_tty_open (struct tty_struct *); +static void n_hdlc_tty_close (struct tty_struct *); +static int n_hdlc_tty_room (struct tty_struct *tty); +static void n_hdlc_tty_receive (struct tty_struct *tty, + const __u8 * cp, char *fp, int count); +static void n_hdlc_tty_wakeup (struct tty_struct *tty); + +#define bset(p,b) ((p)[(b) >> 5] |= (1 << ((b) & 0x1f))) + +#define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) +#define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) + +/* Define this string only once for all macro invocations */ +static char szVersion[] = HDLC_VERSION; + +/* n_hdlc_release() + * + * release an n_hdlc per device line discipline info structure + * + */ +static void n_hdlc_release (struct n_hdlc *n_hdlc) +{ + struct tty_struct *tty = n_hdlc2tty (n_hdlc); + N_HDLC_BUF *buf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__); + + /* Ensure that the n_hdlcd process is not hanging on select()/poll() */ + wake_up_interruptible (&n_hdlc->read_wait); + wake_up_interruptible (&n_hdlc->write_wait); + + if (tty != NULL && tty->disc_data == n_hdlc) + tty->disc_data = NULL; /* Break the tty->n_hdlc link */ + + /* Release transmit and receive buffers */ + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + for(;;) { + buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + if (buf) { + kfree(buf); + } else + break; + } + + kfree(n_hdlc); + +} /* end of n_hdlc_release() */ + +/* n_hdlc_tty_close() + * + * Called when the line discipline is changed to something + * else, the tty is closed, or the tty detects a hangup. + */ +static void n_hdlc_tty_close(struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_close() called\n",__FILE__,__LINE__); + + if (n_hdlc != NULL) { + if (n_hdlc->magic != HDLC_MAGIC) { + printk (KERN_WARNING"n_hdlc: trying to close unopened tty!\n"); + return; + } + tty->disc_data = NULL; + if (tty == n_hdlc->backup_tty) + n_hdlc->backup_tty = 0; + if (tty != n_hdlc->tty) + return; + if (n_hdlc->backup_tty) { + n_hdlc->tty = n_hdlc->backup_tty; + } else { + n_hdlc_release (n_hdlc); + MOD_DEC_USE_COUNT; + } + } + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_close() success\n",__FILE__,__LINE__); + +} /* end of n_hdlc_tty_close() */ + +/* n_hdlc_tty_open + * + * called when line discipline changed to n_hdlc + * + * Arguments: tty pointer to tty info structure + * Return Value: 0 if success, otherwise error code + */ +static int n_hdlc_tty_open (struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_open() called\n",__FILE__,__LINE__); + + /* There should not be an existing table for this slot. */ + if (n_hdlc) { + printk (KERN_ERR"n_hdlc_tty_open:tty already associated!\n" ); + return -EEXIST; + } + + n_hdlc = n_hdlc_alloc(); + if (!n_hdlc) { + printk (KERN_ERR "n_hdlc_alloc failed\n"); + return -ENFILE; + } + + tty->disc_data = n_hdlc; + n_hdlc->tty = tty; + + MOD_INC_USE_COUNT; + + /* Flush any pending characters in the driver and discipline. */ + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer (tty); + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_open() success\n",__FILE__,__LINE__); + + return 0; + +} /* end of n_tty_hdlc_open() */ + +/* n_hdlc_send_frames() + * + * send frames on pending send buffer list until the + * driver does not accept a frame (busy) + * this function is called after adding a frame to the + * send buffer list and by the tty wakeup callback + * + * Arguments: n_hdlc pointer to ldisc instance data + * tty pointer to tty instance data + * Return Value: None + */ +static void n_hdlc_send_frames (struct n_hdlc *n_hdlc, struct tty_struct *tty) +{ + register int actual; + unsigned long flags; + N_HDLC_BUF *tbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_send_frames() called\n",__FILE__,__LINE__); + + save_flags(flags); + cli (); + if (n_hdlc->tbusy) { + n_hdlc->woke_up = 1; + restore_flags(flags); + return; + } + n_hdlc->tbusy = 1; + restore_flags(flags); + + /* get current transmit buffer or get new transmit */ + /* buffer from list of pending transmit buffers */ + + tbuf = n_hdlc->tbuf; + if (!tbuf) + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + + while (tbuf) { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)sending frame %p, count=%d\n", + __FILE__,__LINE__,tbuf,tbuf->count); + + /* Send the next block of data to device */ + n_hdlc->woke_up = 0; + tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = tty->driver.write(tty, 0, tbuf->buf, tbuf->count); + + /* if transmit error, throw frame away by */ + /* pretending it was accepted by driver */ + if (actual < 0) + actual = tbuf->count; + + if (actual == tbuf->count) { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)frame %p completed\n", + __FILE__,__LINE__,tbuf); + + /* free current transmit buffer */ + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf); + + /* this tx buffer is done */ + n_hdlc->tbuf = NULL; + + /* wait up sleeping writers */ + wake_up_interruptible(&n_hdlc->write_wait); + + /* get next pending transmit buffer */ + tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list); + } else { + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)frame %p pending\n", + __FILE__,__LINE__,tbuf); + + /* buffer not accepted by driver */ + + /* check if wake up code called since last write call */ + if (n_hdlc->woke_up) + continue; + + /* set this buffer as pending buffer */ + n_hdlc->tbuf = tbuf; + break; + } + } + + if (!tbuf) + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + + /* Clear the re-entry flag */ + save_flags(flags); + cli (); + n_hdlc->tbusy = 0; + restore_flags(flags); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_send_frames() exit\n",__FILE__,__LINE__); + +} /* end of n_hdlc_send_frames() */ + +/* n_hdlc_tty_wakeup() + * + * Callback for transmit wakeup. Called when low level + * device driver can accept more send data. + * + * Arguments: tty pointer to associated tty instance data + * Return Value: None + */ +static void n_hdlc_tty_wakeup (struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_wakeup() called\n",__FILE__,__LINE__); + + if (!n_hdlc) + return; + + if (tty != n_hdlc->tty) { + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + return; + } + + if (!n_hdlc->tbuf) + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + else + n_hdlc_send_frames (n_hdlc, tty); + +} /* end of n_hdlc_tty_wakeup() */ + +/* n_hdlc_tty_room() + * + * Callback function from tty driver. Return the amount of + * space left in the receiver's buffer to decide if remote + * transmitter is to be throttled. + * + * Arguments: tty pointer to associated tty instance data + * Return Value: number of bytes left in receive buffer + */ +static int n_hdlc_tty_room (struct tty_struct *tty) +{ + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_room() called\n",__FILE__,__LINE__); + /* always return a larger number to prevent */ + /* throttling of remote transmitter. */ + return 65536; +} /* end of n_hdlc_tty_root() */ + +/* n_hdlc_tty_receive() + * + * Called by tty low level driver when receive data is + * available. Data is interpreted as one HDLC frame. + * + * Arguments: tty pointer to tty isntance data + * data pointer to received data + * flags pointer to flags for data + * count count of received data in bytes + * + * Return Value: None + */ +static void n_hdlc_tty_receive(struct tty_struct *tty, + const __u8 * data, char *flags, int count) +{ + register struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + register N_HDLC_BUF *buf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_receive() called count=%d\n", + __FILE__,__LINE__, count); + + /* This can happen if stuff comes in on the backup tty */ + if (n_hdlc == 0 || tty != n_hdlc->tty) + return; + + /* verify line is using HDLC discipline */ + if (n_hdlc->magic != HDLC_MAGIC) { + printk("%s(%d) line not using HDLC discipline\n", + __FILE__,__LINE__); + return; + } + + /* get a free HDLC buffer */ + buf = n_hdlc_buf_get(&n_hdlc->rx_free_buf_list); + if (!buf) { + /* no buffers in free list, attempt to allocate another rx buffer */ + /* unless the maximum count has been reached */ + if (n_hdlc->rx_buf_list.count < MAX_RX_BUF_COUNT) + buf = (N_HDLC_BUF*)kmalloc(sizeof(N_HDLC_BUF),GFP_ATOMIC); + } + + if (!buf) { + printk("%s(%d) no more rx buffers, data discarded\n", + __FILE__,__LINE__); + return; + } + + /* copy received data to HDLC buffer */ + memcpy(buf->buf,data,count); + buf->count=count; + + /* add HDLC buffer to list of received frames */ + n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf); + + /* wake up any blocked reads and perform async signalling */ + wake_up_interruptible (&n_hdlc->read_wait); + if (n_hdlc->tty->fasync != NULL) + kill_fasync (n_hdlc->tty->fasync, SIGIO); + +} /* end of n_hdlc_tty_receive() */ + +/* n_hdlc_tty_read() + * + * Called to retreive one frame of data (if available) + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to open file object + * buf pointer to returned data buffer + * nr size of returned data buffer + * + * Return Value: + * + * Number of bytes returned or error code + */ +static rw_ret_t n_hdlc_tty_read (struct tty_struct *tty, + struct file *file, __u8 * buf, rw_count_t nr) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + int error; + rw_ret_t ret; + N_HDLC_BUF *rbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_read() called\n",__FILE__,__LINE__); + + /* Validate the pointers */ + if (!n_hdlc) + return -EIO; + + /* verify user access to buffer */ + error = verify_area (VERIFY_WRITE, buf, nr); + if (error != 0) { + printk(KERN_WARNING"%s(%d) n_hdlc_tty_read() can't verify user " + "buffer\n",__FILE__,__LINE__); + return (error); + } + + for (;;) { + n_hdlc = tty2n_hdlc (tty); + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || + tty != n_hdlc->tty) + return 0; + + rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); + if (rbuf) + break; + + /* no data */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + /* TODO: no timeout? current->timeout = 0;*/ + interruptible_sleep_on (&n_hdlc->read_wait); + if (signal_pending(current)) + return -EINTR; + } + + if (rbuf->count > nr) { + /* frame too large for caller's buffer (discard frame) */ + ret = (rw_ret_t)-EOVERFLOW; + } else { + /* Copy the data to the caller's buffer */ + COPY_TO_USER(error,buf,rbuf->buf,rbuf->count); + if (error) + ret = (rw_ret_t)error; + else + ret = (rw_ret_t)rbuf->count; + } + + /* return HDLC buffer to free list unless the free list */ + /* count has exceeded the default value, in which case the */ + /* buffer is freed back to the OS to conserve memory */ + if (n_hdlc->rx_free_buf_list.count > DEFAULT_RX_BUF_COUNT) + kfree(rbuf); + else + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); + + return ret; + +} /* end of n_hdlc_tty_read() */ + +/* n_hdlc_tty_write() + * + * write a single frame of data to device + * + * Arguments: tty pointer to associated tty device instance data + * file pointer to file object data + * data pointer to transmit data (one frame) + * count size of transmit frame in bytes + * + * Return Value: number of bytes written (or error code) + */ +static rw_ret_t n_hdlc_tty_write (struct tty_struct *tty, struct file *file, + const __u8 * data, rw_count_t count) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + int error = 0; + struct wait_queue wait = {current, NULL}; + N_HDLC_BUF *tbuf; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_write() called count=%d\n", + __FILE__,__LINE__,count); + + /* Verify pointers */ + if (!n_hdlc) + return -EIO; + + if (n_hdlc->magic != HDLC_MAGIC) + return -EIO; + + /* verify frame size */ + if (count > MAX_HDLC_FRAME_SIZE) { + if (debuglevel & DEBUG_LEVEL_INFO) + printk (KERN_WARNING + "n_hdlc_tty_write: truncating user packet " + "from %lu to %d\n", (unsigned long) count, + MAX_HDLC_FRAME_SIZE); + count = MAX_HDLC_FRAME_SIZE; + } + + /* Allocate transmit buffer */ + tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + if (!tbuf) { + /* sleep until transmit buffer available */ + add_wait_queue(&n_hdlc->write_wait, &wait); + while (!tbuf) { + /* TODO: no timeout? current->timeout = 0;*/ + current->state = TASK_INTERRUPTIBLE; + schedule(); + + n_hdlc = tty2n_hdlc (tty); + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || + tty != n_hdlc->tty) { + printk("n_hdlc_tty_write: %p invalid after wait!\n", n_hdlc); + error = -EIO; + break; + } + + if (signal_pending(current)) { + error = -EINTR; + break; + } + + tbuf = n_hdlc_buf_get(&n_hdlc->tx_free_buf_list); + } + current->state = TASK_RUNNING; + remove_wait_queue(&n_hdlc->write_wait, &wait); + } + + if (!error) { + /* Retrieve the user's buffer */ + COPY_FROM_USER (error, tbuf->buf, data, count); + if (error) { + /* return tx buffer to free list */ + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list,tbuf); + } else { + /* Send the data */ + tbuf->count = error = count; + n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf); + n_hdlc_send_frames(n_hdlc,tty); + } + } + + return error; + +} /* end of n_hdlc_tty_write() */ + +/* n_hdlc_tty_ioctl() + * + * Process IOCTL system call for the tty device. + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to open file object for device + * cmd IOCTL command code + * arg argument for IOCTL call (cmd dependent) + * + * Return Value: Command dependent + */ +static int n_hdlc_tty_ioctl (struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + int error = 0; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_ioctl() called %d\n", + __FILE__,__LINE__,cmd); + + /* Verify the status of the device */ + if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC) + return -EBADF; + + switch (cmd) { + case FIONREAD: + { + /* report count of read data available */ + /* in next available frame (if any) */ + int count; + unsigned long flags; + spin_lock_irqsave(&n_hdlc->rx_buf_list.spinlock,flags); + if (n_hdlc->rx_buf_list.head) + count = n_hdlc->rx_buf_list.head->count; + else + count = 0; + spin_unlock_irqrestore(&n_hdlc->rx_buf_list.spinlock,flags); + PUT_USER (error, count, (int *) arg); + } + break; + + default: + error = n_tty_ioctl (tty, file, cmd, arg); + break; + } + return error; + +} /* end of n_hdlc_tty_ioctl() */ + +#if LINUX_VERSION_CODE < VERSION(2,1,23) +/* n_hdlc_tty_select() + * + * Device select method. Determine if operation requires + * blocking and if so put appropriate wait queue in select + * table and return 0, otherwise return 1. + * + * Arguments: + * + * tty pointer to tty device instance data + * inode pointer to inode for device + * filp pointer to file object + * sel_type identified the select type (read/write/exception) + * wait select table for adding wait queue if appropriate + * + * Return Value: + * + * 1 if no need to block on operation + * 0 if must block and wait queue added to select table + */ +static int n_hdlc_tty_select (struct tty_struct *tty, struct inode *inode, + struct file *filp, int sel_type, select_table * wait) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + int result = 1; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_select() called\n",__FILE__,__LINE__); + + /* Verify the status of the device */ + if (!n_hdlc) + return -EBADF; + + if (n_hdlc->magic != HDLC_MAGIC || tty != n_hdlc->tty) + return -EBADF; + + switch (sel_type) { + case SEL_IN: + if (n_hdlc->rx_buf_list.head) + break; + + case SEL_EX: /* Exceptions or read errors */ + /* Is this a pty link and the remote disconnected? */ + if (tty->flags & (1 << TTY_OTHER_CLOSED)) + break; + + /* Is this a local link and the modem disconnected? */ + if (tty_hung_up_p (filp)) + break; + + select_wait (&n_hdlc->read_wait, wait); + result = 0; + break; + + /* Write mode. A write is allowed if there is no current transmission */ + case SEL_OUT: + if (!n_hdlc->tx_free_buf_list.head) { + select_wait (&n_hdlc->write_wait, wait); + result = 0; + } + break; + } + return result; +} /* end of n_hdlc_tty_select() */ + +#else /* 2.1.23 or later */ + +/* n_hdlc_tty_poll() + * + * TTY callback for poll system call. Determine which + * operations (read/write) will not block and return + * info to caller. + * + * Arguments: + * + * tty pointer to tty instance data + * filp pointer to open file object for device + * poll_table wait queue for operations + * + * Return Value: + * + * bit mask containing info on which ops will not block + */ +static unsigned int n_hdlc_tty_poll (struct tty_struct *tty, + struct file *filp, poll_table * wait) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc (tty); + unsigned int mask = 0; + + if (debuglevel >= DEBUG_LEVEL_INFO) + printk("%s(%d)n_hdlc_tty_poll() called\n",__FILE__,__LINE__); + + if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) { + /* queue current process into any wait queue that */ + /* may awaken in the future (read and write) */ +#if LINUX_VERSION_CODE < VERSION(2,1,89) + poll_wait(&n_hdlc->read_wait, wait); + poll_wait(&n_hdlc->write_wait, wait); +#else + poll_wait(filp, &n_hdlc->read_wait, wait); + poll_wait(filp, &n_hdlc->write_wait, wait); +#endif + /* set bits for operations that wont block */ + if(n_hdlc->rx_buf_list.head) + mask |= POLLIN | POLLRDNORM; /* readable */ + if(tty->flags & (1 << TTY_OTHER_CLOSED)) + mask |= POLLHUP; + if(tty_hung_up_p(filp)) + mask |= POLLHUP; + if(n_hdlc->tx_free_buf_list.head) + mask |= POLLOUT | POLLWRNORM; /* writable */ + } + return mask; +} /* end of n_hdlc_tty_poll() */ + +#endif + +/* n_hdlc_alloc() + * + * Allocate an n_hdlc instance data structure + * + * Arguments: None + * Return Value: pointer to structure if success, otherwise 0 + */ +static struct n_hdlc *n_hdlc_alloc (void) +{ + struct n_hdlc *n_hdlc; + N_HDLC_BUF *buf; + int i; + + n_hdlc = (struct n_hdlc *)kmalloc(sizeof(struct n_hdlc), GFP_KERNEL); + if (!n_hdlc) + return 0; + + memset(n_hdlc, 0, sizeof(*n_hdlc)); + + n_hdlc_buf_list_init(&n_hdlc->rx_free_buf_list); + n_hdlc_buf_list_init(&n_hdlc->tx_free_buf_list); + n_hdlc_buf_list_init(&n_hdlc->rx_buf_list); + n_hdlc_buf_list_init(&n_hdlc->tx_buf_list); + + /* allocate free rx buffer list */ + for(i=0;irx_free_buf_list,buf); + } + + /* allocate free rx buffer list */ + for(i=0;itx_free_buf_list,buf); + } + + /* Initialize the control block */ + n_hdlc->magic = HDLC_MAGIC; + + n_hdlc->flags = 0; + n_hdlc->read_wait = NULL; + n_hdlc->write_wait = NULL; + + return n_hdlc; + +} /* end of n_hdlc_alloc() */ + +/* n_hdlc_buf_list_init() + * + * initialize specified HDLC buffer list + * + * Arguments: list pointer to buffer list + * Return Value: None + */ +void n_hdlc_buf_list_init(N_HDLC_BUF_LIST *list) +{ + memset(list,0,sizeof(N_HDLC_BUF_LIST)); + +} /* end of n_hdlc_buf_list_init() */ + +/* n_hdlc_buf_put() + * + * add specified HDLC buffer to tail of specified list + * + * Arguments: + * + * list pointer to buffer list + * buf pointer to buffer + * + * Return Value: None + */ +void n_hdlc_buf_put(N_HDLC_BUF_LIST *list,N_HDLC_BUF *buf) +{ + unsigned long flags; + spin_lock_irqsave(&list->spinlock,flags); + + buf->link=NULL; + if(list->tail) + list->tail->link = buf; + else + list->head = buf; + list->tail = buf; + (list->count)++; + + spin_unlock_irqrestore(&list->spinlock,flags); + +} /* end of n_hdlc_buf_put() */ + +/* n_hdlc_buf_get() + * + * remove and return an HDLC buffer from the + * head of the specified HDLC buffer list + * + * Arguments: + * + * list pointer to HDLC buffer list + * + * Return Value: + * + * pointer to HDLC buffer if available, otherwise NULL + */ +N_HDLC_BUF* n_hdlc_buf_get(N_HDLC_BUF_LIST *list) +{ + unsigned long flags; + N_HDLC_BUF *buf; + spin_lock_irqsave(&list->spinlock,flags); + + buf = list->head; + if (buf) { + list->head = buf->link; + (list->count)--; + } + if (!list->head) + list->tail = NULL; + + spin_unlock_irqrestore(&list->spinlock,flags); + return buf; + +} /* end of n_hdlc_buf_get() */ + +/* init_module() + * + * called when module is loading to register line discipline + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int init_module(void) +{ + static struct tty_ldisc n_hdlc_ldisc; + int status; + + printk("HDLC line discipline: version %s\n", szVersion); + + /* Register the tty discipline */ + + memset(&n_hdlc_ldisc, 0, sizeof (n_hdlc_ldisc)); + n_hdlc_ldisc.magic = TTY_LDISC_MAGIC; +#if LINUX_VERSION_CODE >= VERSION(2,1,28) + n_hdlc_ldisc.name = "hdlc"; +#endif + n_hdlc_ldisc.open = n_hdlc_tty_open; + n_hdlc_ldisc.close = n_hdlc_tty_close; + n_hdlc_ldisc.read = n_hdlc_tty_read; + n_hdlc_ldisc.write = n_hdlc_tty_write; + n_hdlc_ldisc.ioctl = n_hdlc_tty_ioctl; +#if LINUX_VERSION_CODE < VERSION(2,1,23) + n_hdlc_ldisc.select = n_hdlc_tty_select; +#else + n_hdlc_ldisc.poll = n_hdlc_tty_poll; +#endif + n_hdlc_ldisc.receive_room = n_hdlc_tty_room; + n_hdlc_ldisc.receive_buf = n_hdlc_tty_receive; + n_hdlc_ldisc.write_wakeup = n_hdlc_tty_wakeup; + + status = tty_register_ldisc(N_HDLC, &n_hdlc_ldisc); + if (!status) + printk (KERN_INFO"N_HDLC line discipline registered.\n"); + else + printk (KERN_ERR"error registering line discipline: %d\n",status); + + if (status) + printk(KERN_INFO"N_HDLC: init failure %d\n", status); + return (status); + +} /* end of init_module() */ + +/* cleanup_module() + * + * called when module is unloading to unregister line discipline + * + * Arguments: None + * Return Value: None + */ +void cleanup_module(void) +{ + int status; + /* Release tty registration of line discipline */ + if ((status = tty_register_ldisc(N_HDLC, NULL))) + printk("N_HDLC: can't unregister line discipline (err = %d)\n", status); + else + printk("N_HDLC: line discipline unregistered\n"); +} diff -u --recursive --new-file v2.2.4/linux/drivers/char/synclink.c linux/drivers/char/synclink.c --- v2.2.4/linux/drivers/char/synclink.c Wed Dec 31 16:00:00 1969 +++ linux/drivers/char/synclink.c Thu Mar 11 13:50:36 1999 @@ -0,0 +1,6989 @@ +/* + * linux/drivers/char/synclink.c + * + * Device driver for Microgate SyncLink ISA and PCI + * high speed multiprotocol serial adapters. + * + * written by Paul Fulghum for Microgate Corporation + * paulkf@microgate.com + * + * Microgate and SyncLink are trademarks of Microgate Corporation + * + * Derived from serial.c written by Theodore Ts'o and Linus Torvalds + * + * Original release 01/11/99 + * + * This code is released under the GNU General Public License (GPL) + * + * This driver is primarily intended for use in synchronous + * HDLC mode. Asynchronous mode is also provided. + * + * When operating in synchronous mode, each call to mgsl_write() + * contains exactly one complete HDLC frame. Calling mgsl_put_char + * will start assembling an HDLC frame that will not be sent until + * mgsl_flush_chars or mgsl_write is called. + * + * Synchronous receive data is reported as complete frames. To accomplish + * this, the TTY flip buffer is bypassed (too small to hold largest + * frame and may fragment frames) and the line discipline + * receive entry point is called directly. + * + * This driver has been tested with a slightly modified ppp.c driver + * for synchronous PPP. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define VERSION(ver,rel,seq) (((ver)<<16) | ((rel)<<8) | (seq)) +#define BREAKPOINT() asm(" int $3"); + +#define MAX_ISA_DEVICES 10 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +#include +#include +#include +#else +#include +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= VERSION(2,1,4) +#include +#define GET_USER(error,value,addr) error = get_user(value,addr) +#define COPY_FROM_USER(error,dest,src,size) error = copy_from_user(dest,src,size) ? -EFAULT : 0 +#define PUT_USER(error,value,addr) error = put_user(value,addr) +#define COPY_TO_USER(error,dest,src,size) error = copy_to_user(dest,src,size) ? -EFAULT : 0 + +#if LINUX_VERSION_CODE >= VERSION(2,1,5) +#include +#endif + +#else /* 2.0.x and 2.1.x before 2.1.4 */ + +#define GET_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_READ, (void *) addr, sizeof (value)); \ + if (error == 0) \ + value = get_user(addr); \ +} while (0) + +#define COPY_FROM_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_READ, (void *) src, size); \ + if (error == 0) \ + memcpy_fromfs (dest, src, size); \ +} while (0) + +#define PUT_USER(error,value,addr) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) addr, sizeof (value)); \ + if (error == 0) \ + put_user (value, addr); \ +} while (0) + +#define COPY_TO_USER(error,dest,src,size) \ +do { \ + error = verify_area (VERIFY_WRITE, (void *) dest, size); \ + if (error == 0) \ + memcpy_tofs (dest, src, size); \ +} while (0) + +#endif + +#if LINUX_VERSION_CODE < VERSION(2,1,0) +/* + * This is used to figure out the divisor speeds and the timeouts + */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0 }; + +#define __init +#define ioremap(a,b) vremap((a),(b)) +#define iounmap(a) vfree((a)) +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +typedef int spinlock_t; +#define spin_lock_irqsave(a,b) {save_flags((b));cli();} +#define spin_unlock_irqrestore(a,b) {restore_flags((b));} +#define spin_lock(a) +#define spin_unlock(a) +#define schedule_timeout(a){current->timeout = jiffies + (a); schedule();} +#define signal_pending(a) ((a)->signal & ~(a)->blocked) +#endif + + + +#include "linux/synclink.h" + +#define RCLRVALUE 0xffff + +MGSL_PARAMS default_params = { + MGSL_MODE_HDLC, /* unsigned long mode */ + 0, /* unsigned char loopback; */ + HDLC_FLAG_UNDERRUN_ABORT15, /* unsigned short flags; */ + HDLC_ENCODING_NRZI_SPACE, /* unsigned char encoding; */ + 0, /* unsigned long clock_speed; */ + 0xff, /* unsigned char addr_filter; */ + HDLC_CRC_16_CCITT, /* unsigned short crc_type; */ + HDLC_PREAMBLE_LENGTH_8BITS, /* unsigned char preamble_length; */ + HDLC_PREAMBLE_PATTERN_NONE, /* unsigned char preamble; */ + 9600, /* unsigned long data_rate; */ + 8, /* unsigned char data_bits; */ + 1, /* unsigned char stop_bits; */ + ASYNC_PARITY_NONE /* unsigned char parity; */ +}; + +#define SHARED_MEM_ADDRESS_SIZE 0x40000 +#define BUFFERLISTSIZE (PAGE_SIZE) +#define DMABUFFERSIZE (PAGE_SIZE) +#define MAXRXFRAMES 7 + +typedef struct _DMABUFFERENTRY +{ + u32 phys_addr; /* 32-bit flat physical address of data buffer */ + u16 count; /* buffer size/data count */ + u16 status; /* Control/status field */ + u16 rcc; /* character count field */ + u16 reserved; /* padding required by 16C32 */ + u32 link; /* 32-bit flat link to next buffer entry */ + char *virt_addr; /* virtual address of data buffer */ + u32 phys_entry; /* physical address of this buffer entry */ +} DMABUFFERENTRY, *DMAPBUFFERENTRY; + +/* The queue of BH actions to be performed */ + +#define BH_TYPE_RECEIVE_DATA 1 +#define BH_TYPE_RECEIVE_STATUS 2 +#define BH_TYPE_RECEIVE_DMA 3 +#define BH_TYPE_TRANSMIT_DATA 4 +#define BH_TYPE_TRANSMIT_STATUS 5 +#define BH_TYPE_STATUS 6 + +typedef struct _BH_EVENT { + unsigned char type; /* Set by interrupt routines to reqst */ + u16 status; + struct _BH_EVENT *link; + +} BH_EVENT, *BH_QUEUE; /* Queue of BH actions to be done. */ + +#define MAX_BH_QUEUE_ENTRIES 200 + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) +/* + * Device instance data structure + */ + +struct mgsl_struct { + int magic; + int flags; + int count; /* count of opens */ + int line; + unsigned short close_delay; + unsigned short closing_wait; /* time to wait before closing */ + + struct mgsl_icount icount; + + struct termios normal_termios; + struct termios callout_termios; + + struct tty_struct *tty; + int timeout; + int x_char; /* xon/xoff character */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + u16 read_status_mask; + u16 ignore_status_mask; + unsigned char *xmit_buf; + int xmit_head; + int xmit_tail; + int xmit_cnt; + + struct wait_queue *open_wait; + struct wait_queue *close_wait; + + struct wait_queue *status_event_wait_q; + struct wait_queue *event_wait_q; + struct timer_list tx_timer; /* HDLC transmit timeout timer */ + struct mgsl_struct *next_device; /* device list link */ + + spinlock_t irq_spinlock; /* spinlock for synchronizing with ISR */ + struct tq_struct task; /* task structure for scheduling bh */ + + u32 EventMask; /* event trigger mask */ + u32 RecordedEvents; /* pending events */ + + u32 max_frame_size; /* as set by device config */ + + BH_EVENT bh_queue[MAX_BH_QUEUE_ENTRIES]; /* Pointer to alloc'ed block */ + BH_QUEUE bh_queue_head; /* Queue of BH actions */ + BH_QUEUE bh_queue_tail; /* Tail of above for perf. */ + BH_QUEUE free_bh_queue_head; /* Queue of Free BH */ + BH_QUEUE free_bh_queue_tail; /* Tail of above for perf. */ + BH_QUEUE bh_action; /* Action for BH */ + int bh_running; /* Protection from multiple */ + int isr_overflow; + int bh_requested; + + char *buffer_list; /* virtual address of Rx & Tx buffer lists */ + unsigned long buffer_list_phys; + + unsigned int rx_buffer_count; /* count of total allocated Rx buffers */ + DMABUFFERENTRY *rx_buffer_list; /* list of receive buffer entries */ + unsigned int current_rx_buffer; + + unsigned int tx_buffer_count; /* count of total allocated Tx buffers */ + DMABUFFERENTRY *tx_buffer_list; /* list of transmit buffer entries */ + + int rx_enabled; + int rx_overflow; + + int tx_enabled; + int tx_active; + u32 idle_mode; + + u16 cmr_value; + + char device_name[25]; /* device instance name */ + + unsigned int bus_type; /* expansion bus type (ISA,EISA,PCI) */ + unsigned char bus; /* expansion bus number (zero based) */ + unsigned char function; /* PCI device number */ + + unsigned int io_base; /* base I/O address of adapter */ + unsigned int io_addr_size; /* size of the I/O address range */ + int io_addr_requested; /* nonzero if I/O address requested */ + + unsigned int irq_level; /* interrupt level */ + unsigned long irq_flags; + int irq_requested; /* nonzero if IRQ requested */ + + unsigned int dma_level; /* DMA channel */ + int dma_requested; /* nonzero if dma channel requested */ + + u16 mbre_bit; + u16 loopback_bits; + u16 usc_idle_mode; + + MGSL_PARAMS params; /* communications parameters */ + + unsigned char serial_signals; /* current serial signal states */ + + int irq_occurred; /* for diagnostics use */ + unsigned int init_error; /* Initialization startup error (DIAGS) */ + int fDiagnosticsmode; /* Driver in Diagnostic mode? (DIAGS) */ + + u32 last_mem_alloc; + unsigned char* memory_base; /* shared memory address (PCI only) */ + u32 phys_memory_base; + + unsigned char* lcr_base; /* local config registers (PCI only) */ + u32 phys_lcr_base; + u32 lcr_offset; + + u32 misc_ctrl_value; + char flag_buf[HDLC_MAX_FRAME_SIZE]; + char char_buf[HDLC_MAX_FRAME_SIZE]; + BOOLEAN drop_rts_on_tx_done; +}; + +#define MGSL_MAGIC 0x5401 + +/* + * The size of the serial xmit buffer is 1 page, or 4096 bytes + */ +#define SERIAL_XMIT_SIZE 4096 + + +/* + * These macros define the offsets used in calculating the + * I/O address of the specified USC registers. + */ + + +#define DCPIN 2 /* Bit 1 of I/O address */ +#define SDPIN 4 /* Bit 2 of I/O address */ + +#define DCAR 0 /* DMA command/address register */ +#define CCAR SDPIN /* channel command/address register */ +#define DATAREG DCPIN + SDPIN /* serial data register */ +#define MSBONLY 0x41 +#define LSBONLY 0x40 + +/* + * These macros define the register address (ordinal number) + * used for writing address/value pairs to the USC. + */ + +#define CMR 0x02 /* Channel mode Register */ +#define CCSR 0x04 /* Channel Command/status Register */ +#define CCR 0x06 /* Channel Control Register */ +#define PSR 0x08 /* Port status Register */ +#define PCR 0x0a /* Port Control Register */ +#define TMDR 0x0c /* Test mode Data Register */ +#define TMCR 0x0e /* Test mode Control Register */ +#define CMCR 0x10 /* Clock mode Control Register */ +#define HCR 0x12 /* Hardware Configuration Register */ +#define IVR 0x14 /* Interrupt Vector Register */ +#define IOCR 0x16 /* Input/Output Control Register */ +#define ICR 0x18 /* Interrupt Control Register */ +#define DCCR 0x1a /* Daisy Chain Control Register */ +#define MISR 0x1c /* Misc Interrupt status Register */ +#define SICR 0x1e /* status Interrupt Control Register */ +#define RDR 0x20 /* Receive Data Register */ +#define RMR 0x22 /* Receive mode Register */ +#define RCSR 0x24 /* Receive Command/status Register */ +#define RICR 0x26 /* Receive Interrupt Control Register */ +#define RSR 0x28 /* Receive Sync Register */ +#define RCLR 0x2a /* Receive count Limit Register */ +#define RCCR 0x2c /* Receive Character count Register */ +#define TC0R 0x2e /* Time Constant 0 Register */ +#define TDR 0x30 /* Transmit Data Register */ +#define TMR 0x32 /* Transmit mode Register */ +#define TCSR 0x34 /* Transmit Command/status Register */ +#define TICR 0x36 /* Transmit Interrupt Control Register */ +#define TSR 0x38 /* Transmit Sync Register */ +#define TCLR 0x3a /* Transmit count Limit Register */ +#define TCCR 0x3c /* Transmit Character count Register */ +#define TC1R 0x3e /* Time Constant 1 Register */ + + +/* + * MACRO DEFINITIONS FOR DMA REGISTERS + */ + +#define DCR 0x06 /* DMA Control Register (shared) */ +#define DACR 0x08 /* DMA Array count Register (shared) */ +#define BDCR 0x12 /* Burst/Dwell Control Register (shared) */ +#define DIVR 0x14 /* DMA Interrupt Vector Register (shared) */ +#define DICR 0x18 /* DMA Interrupt Control Register (shared) */ +#define CDIR 0x1a /* Clear DMA Interrupt Register (shared) */ +#define SDIR 0x1c /* Set DMA Interrupt Register (shared) */ + +#define TDMR 0x02 /* Transmit DMA mode Register */ +#define TDIAR 0x1e /* Transmit DMA Interrupt Arm Register */ +#define TBCR 0x2a /* Transmit Byte count Register */ +#define TARL 0x2c /* Transmit Address Register (low) */ +#define TARU 0x2e /* Transmit Address Register (high) */ +#define NTBCR 0x3a /* Next Transmit Byte count Register */ +#define NTARL 0x3c /* Next Transmit Address Register (low) */ +#define NTARU 0x3e /* Next Transmit Address Register (high) */ + +#define RDMR 0x82 /* Receive DMA mode Register (non-shared) */ +#define RDIAR 0x9e /* Receive DMA Interrupt Arm Register */ +#define RBCR 0xaa /* Receive Byte count Register */ +#define RARL 0xac /* Receive Address Register (low) */ +#define RARU 0xae /* Receive Address Register (high) */ +#define NRBCR 0xba /* Next Receive Byte count Register */ +#define NRARL 0xbc /* Next Receive Address Register (low) */ +#define NRARU 0xbe /* Next Receive Address Register (high) */ + + +/* + * MACRO DEFINITIONS FOR MODEM STATUS BITS + */ + +#define MODEMSTATUS_DTR 0x80 +#define MODEMSTATUS_DSR 0x40 +#define MODEMSTATUS_RTS 0x20 +#define MODEMSTATUS_CTS 0x10 +#define MODEMSTATUS_RI 0x04 +#define MODEMSTATUS_DCD 0x01 + + +/* + * Channel Command/Address Register (CCAR) Command Codes + */ + +#define RTCmd_Null 0x0000 +#define RTCmd_ResetHighestIus 0x1000 +#define RTCmd_TriggerChannelLoadDma 0x2000 +#define RTCmd_TriggerRxDma 0x2800 +#define RTCmd_TriggerTxDma 0x3000 +#define RTCmd_TriggerRxAndTxDma 0x3800 +#define RTCmd_PurgeRxFifo 0x4800 +#define RTCmd_PurgeTxFifo 0x5000 +#define RTCmd_PurgeRxAndTxFifo 0x5800 +#define RTCmd_LoadRcc 0x6800 +#define RTCmd_LoadTcc 0x7000 +#define RTCmd_LoadRccAndTcc 0x7800 +#define RTCmd_LoadTC0 0x8800 +#define RTCmd_LoadTC1 0x9000 +#define RTCmd_LoadTC0AndTC1 0x9800 +#define RTCmd_SerialDataLSBFirst 0xa000 +#define RTCmd_SerialDataMSBFirst 0xa800 +#define RTCmd_SelectBigEndian 0xb000 +#define RTCmd_SelectLittleEndian 0xb800 + + +/* + * DMA Command/Address Register (DCAR) Command Codes + */ + +#define DmaCmd_Null 0x0000 +#define DmaCmd_ResetTxChannel 0x1000 +#define DmaCmd_ResetRxChannel 0x1200 +#define DmaCmd_StartTxChannel 0x2000 +#define DmaCmd_StartRxChannel 0x2200 +#define DmaCmd_ContinueTxChannel 0x3000 +#define DmaCmd_ContinueRxChannel 0x3200 +#define DmaCmd_PauseTxChannel 0x4000 +#define DmaCmd_PauseRxChannel 0x4200 +#define DmaCmd_AbortTxChannel 0x5000 +#define DmaCmd_AbortRxChannel 0x5200 +#define DmaCmd_InitTxChannel 0x7000 +#define DmaCmd_InitRxChannel 0x7200 +#define DmaCmd_ResetHighestDmaIus 0x8000 +#define DmaCmd_ResetAllChannels 0x9000 +#define DmaCmd_StartAllChannels 0xa000 +#define DmaCmd_ContinueAllChannels 0xb000 +#define DmaCmd_PauseAllChannels 0xc000 +#define DmaCmd_AbortAllChannels 0xd000 +#define DmaCmd_InitAllChannels 0xf000 + +#define TCmd_Null 0x0000 +#define TCmd_ClearTxCRC 0x2000 +#define TCmd_SelectTicrTtsaData 0x4000 +#define TCmd_SelectTicrTxFifostatus 0x5000 +#define TCmd_SelectTicrIntLevel 0x6000 +#define TCmd_SelectTicrdma_level 0x7000 +#define TCmd_SendFrame 0x8000 +#define TCmd_SendAbort 0x9000 +#define TCmd_EnableDleInsertion 0xc000 +#define TCmd_DisableDleInsertion 0xd000 +#define TCmd_ClearEofEom 0xe000 +#define TCmd_SetEofEom 0xf000 + +#define RCmd_Null 0x0000 +#define RCmd_ClearRxCRC 0x2000 +#define RCmd_EnterHuntmode 0x3000 +#define RCmd_SelectRicrRtsaData 0x4000 +#define RCmd_SelectRicrRxFifostatus 0x5000 +#define RCmd_SelectRicrIntLevel 0x6000 +#define RCmd_SelectRicrdma_level 0x7000 + +/* + * Bits for enabling and disabling IRQs in Interrupt Control Register (ICR) + */ + +#define RECEIVE_STATUS BIT5 +#define RECEIVE_DATA BIT4 +#define TRANSMIT_STATUS BIT3 +#define TRANSMIT_DATA BIT2 +#define IO_PIN BIT1 +#define MISC BIT0 + + +/* + * Receive status Bits in Receive Command/status Register RCSR + */ + +#define RXSTATUS_SHORT_FRAME BIT8 +#define RXSTATUS_CODE_VIOLATION BIT8 +#define RXSTATUS_EXITED_HUNT BIT7 +#define RXSTATUS_IDLE_RECEIVED BIT6 +#define RXSTATUS_BREAK_RECEIVED BIT5 +#define RXSTATUS_ABORT_RECEIVED BIT5 +#define RXSTATUS_RXBOUND BIT4 +#define RXSTATUS_CRC_ERROR BIT3 +#define RXSTATUS_FRAMING_ERROR BIT3 +#define RXSTATUS_ABORT BIT2 +#define RXSTATUS_PARITY_ERROR BIT2 +#define RXSTATUS_OVERRUN BIT1 +#define RXSTATUS_DATA_AVAILABLE BIT0 +#define RXSTATUS_ALL 0x01f6 +#define usc_UnlatchRxstatusBits(a,b) usc_OutReg( (a), RCSR, (u16)((b) & RXSTATUS_ALL) ) + +/* + * Values for setting transmit idle mode in + * Transmit Control/status Register (TCSR) + */ +#define IDLEMODE_FLAGS 0x0000 +#define IDLEMODE_ALT_ONE_ZERO 0x0100 +#define IDLEMODE_ZERO 0x0200 +#define IDLEMODE_ONE 0x0300 +#define IDLEMODE_ALT_MARK_SPACE 0x0500 +#define IDLEMODE_SPACE 0x0600 +#define IDLEMODE_MARK 0x0700 + +/* + * Transmit status Bits in Transmit Command/status Register (TCSR) + */ + +#define TCSR_PRESERVE 0x0700 + +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF_SENT BIT4 +#define TXSTATUS_EOM_SENT BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 +#define TXSTATUS_ALL 0x00fa +#define usc_UnlatchTxstatusBits(a,b) usc_OutReg( (a), TCSR, (u16)((a)->usc_idle_mode + ((b) & 0x00FF)) ) + + +#define MISCSTATUS_RXC_LATCHED BIT15 +#define MISCSTATUS_RXC BIT14 +#define MISCSTATUS_TXC_LATCHED BIT13 +#define MISCSTATUS_TXC BIT12 +#define MISCSTATUS_RI_LATCHED BIT11 +#define MISCSTATUS_RI BIT10 +#define MISCSTATUS_DSR_LATCHED BIT9 +#define MISCSTATUS_DSR BIT8 +#define MISCSTATUS_DCD_LATCHED BIT7 +#define MISCSTATUS_DCD BIT6 +#define MISCSTATUS_CTS_LATCHED BIT5 +#define MISCSTATUS_CTS BIT4 +#define MISCSTATUS_RCC_UNDERRUN BIT3 +#define MISCSTATUS_DPLL_NO_SYNC BIT2 +#define MISCSTATUS_BRG1_ZERO BIT1 +#define MISCSTATUS_BRG0_ZERO BIT0 + +#define usc_UnlatchIostatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0xaaa0)) +#define usc_UnlatchMiscstatusBits(a,b) usc_OutReg((a),MISR,(u16)((b) & 0x000f)) + +#define SICR_RXC_ACTIVE BIT15 +#define SICR_RXC_INACTIVE BIT14 +#define SICR_RXC (BIT15+BIT14) +#define SICR_TXC_ACTIVE BIT13 +#define SICR_TXC_INACTIVE BIT12 +#define SICR_TXC (BIT13+BIT12) +#define SICR_RI_ACTIVE BIT11 +#define SICR_RI_INACTIVE BIT10 +#define SICR_RI (BIT11+BIT10) +#define SICR_DSR_ACTIVE BIT9 +#define SICR_DSR_INACTIVE BIT8 +#define SICR_DSR (BIT9+BIT8) +#define SICR_DCD_ACTIVE BIT7 +#define SICR_DCD_INACTIVE BIT6 +#define SICR_DCD (BIT7+BIT6) +#define SICR_CTS_ACTIVE BIT5 +#define SICR_CTS_INACTIVE BIT4 +#define SICR_CTS (BIT5+BIT4) +#define SICR_RCC_UNDERFLOW BIT3 +#define SICR_DPLL_NO_SYNC BIT2 +#define SICR_BRG1_ZERO BIT1 +#define SICR_BRG0_ZERO BIT0 + +void usc_DisableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableMasterIrqBit( struct mgsl_struct *info ); +void usc_EnableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_DisableInterrupts( struct mgsl_struct *info, u16 IrqMask ); +void usc_ClearIrqPendingBits( struct mgsl_struct *info, u16 IrqMask ); + +#define usc_EnableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0xc0 + (b)) ) + +#define usc_DisableInterrupts( a, b ) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0xff00) + 0x80 + (b)) ) + +#define usc_EnableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)((usc_InReg((a),ICR) & 0x0f00) + 0xb000) ) + +#define usc_DisableMasterIrqBit(a) \ + usc_OutReg( (a), ICR, (u16)(usc_InReg((a),ICR) & 0x7f00) ) + +#define usc_ClearIrqPendingBits( a, b ) usc_OutReg( (a), DCCR, 0x40 + (b) ) + +/* + * Transmit status Bits in Transmit Control status Register (TCSR) + * and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) + */ + +#define TXSTATUS_PREAMBLE_SENT BIT7 +#define TXSTATUS_IDLE_SENT BIT6 +#define TXSTATUS_ABORT_SENT BIT5 +#define TXSTATUS_EOF BIT4 +#define TXSTATUS_CRC_SENT BIT3 +#define TXSTATUS_ALL_SENT BIT2 +#define TXSTATUS_UNDERRUN BIT1 +#define TXSTATUS_FIFO_EMPTY BIT0 + +#define DICR_MASTER BIT15 +#define DICR_TRANSMIT BIT0 +#define DICR_RECEIVE BIT1 + +#define usc_EnableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) | (b)) ) + +#define usc_DisableDmaInterrupts(a,b) \ + usc_OutDmaReg( (a), DICR, (u16)(usc_InDmaReg((a),DICR) & ~(b)) ) + +#define usc_EnableStatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) | (b)) ) + +#define usc_DisablestatusIrqs(a,b) \ + usc_OutReg( (a), SICR, (u16)(usc_InReg((a),SICR) & ~(b)) ) + +/* Transmit status Bits in Transmit Control status Register (TCSR) */ +/* and Transmit Interrupt Control Register (TICR) (except BIT2, BIT0) */ + + +#define DISABLE_UNCONDITIONAL 0 +#define DISABLE_END_OF_FRAME 1 +#define ENABLE_UNCONDITIONAL 2 +#define ENABLE_AUTO_CTS 3 +#define ENABLE_AUTO_DCD 3 +#define usc_EnableTransmitter(a,b) \ + usc_OutReg( (a), TMR, (u16)((usc_InReg((a),TMR) & 0xfffc) | (b)) ) +#define usc_EnableReceiver(a,b) \ + usc_OutReg( (a), RMR, (u16)((usc_InReg((a),RMR) & 0xfffc) | (b)) ) + +u16 usc_InDmaReg( struct mgsl_struct *info, u16 Port ); +void usc_OutDmaReg( struct mgsl_struct *info, u16 Port, u16 Value ); +void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ); + +u16 usc_InReg( struct mgsl_struct *info, u16 Port ); +void usc_OutReg( struct mgsl_struct *info, u16 Port, u16 Value ); +void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_RCmd( struct mgsl_struct *info, u16 Cmd ); +void usc_TCmd( struct mgsl_struct *info, u16 Cmd ); + +#define usc_TCmd(a,b) usc_OutReg((a), TCSR, (u16)((a)->usc_idle_mode + (b))) +#define usc_RCmd(a,b) usc_OutReg((a), RCSR, (b)) + +void usc_start_receiver( struct mgsl_struct *info ); +void usc_stop_receiver( struct mgsl_struct *info ); + +void usc_start_transmitter( struct mgsl_struct *info ); +void usc_stop_transmitter( struct mgsl_struct *info ); +void usc_set_txidle( struct mgsl_struct *info ); +void usc_load_txfifo( struct mgsl_struct *info ); + +void usc_enable_aux_clock( struct mgsl_struct *info, u32 DataRate ); +void usc_enable_loopback( struct mgsl_struct *info, int enable ); + +void usc_get_serial_signals( struct mgsl_struct *info ); +void usc_set_serial_signals( struct mgsl_struct *info ); + +void usc_reset( struct mgsl_struct *info ); + +void usc_set_sync_mode( struct mgsl_struct *info ); +void usc_set_sdlc_mode( struct mgsl_struct *info ); +void usc_set_async_mode( struct mgsl_struct *info ); +void usc_enable_async_clock( struct mgsl_struct *info, u32 DataRate ); + +void usc_loopback_frame( struct mgsl_struct *info ); + +void mgsl_tx_timeout(unsigned long context); + +/* + * Defines a BUS descriptor value for the PCI adapter + * local bus address ranges. + */ + +#define BUS_DESCRIPTOR( WrHold, WrDly, RdDly, Nwdd, Nwad, Nxda, Nrdd, Nrad ) \ +(0x00400020 + \ +((WrHold) << 30) + \ +((WrDly) << 28) + \ +((RdDly) << 26) + \ +((Nwdd) << 20) + \ +((Nwad) << 15) + \ +((Nxda) << 13) + \ +((Nrdd) << 11) + \ +((Nrad) << 6) ) + +void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit); + +/* + * Adapter diagnostic routines + */ +BOOLEAN mgsl_register_test( struct mgsl_struct *info ); +BOOLEAN mgsl_irq_test( struct mgsl_struct *info ); +BOOLEAN mgsl_dma_test( struct mgsl_struct *info ); +BOOLEAN mgsl_memory_test( struct mgsl_struct *info ); +int mgsl_adapter_test( struct mgsl_struct *info ); + +/* + * device and resource management routines + */ +int mgsl_claim_resources(struct mgsl_struct *info); +void mgsl_release_resources(struct mgsl_struct *info); +void mgsl_add_device(struct mgsl_struct *info); +struct mgsl_struct* mgsl_allocate_device(void); +int mgsl_enumerate_devices(void); + +/* + * DMA buffer manupulation functions. + */ +void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ); +int mgsl_get_rx_frame( struct mgsl_struct *info ); +void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ); +void mgsl_load_tx_dma_buffer( struct mgsl_struct *info, const char *Buffer, unsigned int BufferSize); +void mgsl_load_pci_memory(char* TargetPtr, const char* SourcePtr, unsigned short count); + +/* + * DMA and Shared Memory buffer allocation and formatting + */ +int mgsl_allocate_dma_buffers(struct mgsl_struct *info); +void mgsl_free_dma_buffers(struct mgsl_struct *info); +int mgsl_alloc_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList,int Buffercount); +int mgsl_alloc_buffer_list_memory(struct mgsl_struct *info); +void mgsl_free_buffer_list_memory(struct mgsl_struct *info); + +/* + * Bottom half interrupt handlers + */ +void mgsl_bh_handler(void* Context); +void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ); +void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ); +void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ); + +void mgsl_format_bh_queue( struct mgsl_struct *info ); +void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status ); +int mgsl_bh_queue_get( struct mgsl_struct *info ); + + +/* + * Interrupt handler routines and dispatch table. + */ +void mgsl_isr_null( struct mgsl_struct *info ); +void mgsl_isr_transmit_data( struct mgsl_struct *info ); +void mgsl_isr_receive_data( struct mgsl_struct *info ); +void mgsl_isr_receive_status( struct mgsl_struct *info ); +void mgsl_isr_transmit_status( struct mgsl_struct *info ); +void mgsl_isr_io_pin( struct mgsl_struct *info ); +void mgsl_isr_misc( struct mgsl_struct *info ); +void mgsl_isr_receive_dma( struct mgsl_struct *info ); + +typedef void (*isr_dispatch_func)(struct mgsl_struct *); + +isr_dispatch_func UscIsrTable[7] = +{ + mgsl_isr_null, + mgsl_isr_misc, + mgsl_isr_io_pin, + mgsl_isr_transmit_data, + mgsl_isr_transmit_status, + mgsl_isr_receive_data, + mgsl_isr_receive_status +}; + +/* + * ioctl call handlers + */ +static int set_modem_info(struct mgsl_struct * info, unsigned int cmd, + unsigned int *value); +static int get_modem_info(struct mgsl_struct * info, unsigned int *value); +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount + *user_icount); +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params); +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params); +static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode); +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode); +static int mgsl_txenable(struct mgsl_struct * info, int enable); +static int mgsl_txabort(struct mgsl_struct * info); +static int mgsl_rxenable(struct mgsl_struct * info, int enable); +static int mgsl_wait_event(struct mgsl_struct * info, int mask); + +#define jiffies_from_ms(a) ((((a) * HZ)/1000)+1) + +/* + * Global linked list of SyncLink devices + */ +struct mgsl_struct *mgsl_device_list = NULL; +int mgsl_device_count = 0; + +/* + * Set this param to non-zero to load eax with the + * .text section address and breakpoint on module load. + * This is useful for use with gdb and add-symbol-file command. + */ +int break_on_load=0; + +/* + * Driver major number, defaults to zero to get auto + * assigned major number. May be forced as module parameter. + */ +int ttymajor=0; + +int cuamajor=0; + +/* + * Array of user specified options for ISA adapters. + */ +static int io[MAX_ISA_DEVICES] = {0,}; +static int irq[MAX_ISA_DEVICES] = {0,}; +static int dma[MAX_ISA_DEVICES] = {0,}; +static int debug_level = 0; + + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +MODULE_PARM(break_on_load,"i"); +MODULE_PARM(ttymajor,"i"); +MODULE_PARM(cuamajor,"i"); +MODULE_PARM(io,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(irq,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(dma,"1-" __MODULE_STRING(MAX_ISA_DEVICES) "i"); +MODULE_PARM(debug_level,"i"); +#endif + +static char *driver_name = "SyncLink serial driver"; +static char *driver_version = "1.00"; + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + + +static void mgsl_change_params(struct mgsl_struct *info); +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout); + +static struct tty_struct **serial_table = NULL; +static struct termios **serial_termios = NULL; +static struct termios **serial_termios_locked = NULL; + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * 1st function defined in .text section. Calling this function in + * init_module() followed by a breakpoint allows a remote debugger + * (gdb) to get the .text address for the add-symbol-file command. + * This allows remote debugging of dynamically loadable modules. + */ +void* mgsl_get_text_ptr(void); +void* mgsl_get_text_ptr() {return mgsl_get_text_ptr;} + +/* + * tmp_buf is used as a temporary buffer by mgsl_write. We need to + * lock it in case the COPY_FROM_USER blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ioports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +static struct semaphore tmp_buf_sem = MUTEX; + +static inline int mgsl_paranoia_check(struct mgsl_struct *info, + kdev_t device, const char *routine) +{ +#ifdef MGSL_PARANOIA_CHECK + static const char *badmagic = + "Warning: bad magic number for mgsl struct (%s) in %s\n"; + static const char *badinfo = + "Warning: null mgsl_struct for (%s) in %s\n"; + + if (!info) { + printk(badinfo, kdevname(device), routine); + return 1; + } + if (info->magic != MGSL_MAGIC) { + printk(badmagic, kdevname(device), routine); + return 1; + } +#endif + return 0; +} + +/* mgsl_stop() throttle (stop) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_stop(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_stop")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_stop(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (info->tx_enabled) + usc_stop_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_stop() */ + +/* mgsl_start() release (start) transmitter + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_start(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_start")) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("mgsl_start(%s)\n",info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_start() */ + +/* + * Bottom half work queue access functions + */ + +/* mgsl_format_bh_queue() + * + * Initialize the bottom half processing queue + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_format_bh_queue( struct mgsl_struct *info ) +{ + BH_QUEUE bh_queue = info->bh_queue; + int i; + + /* go through sequentially tacking the little bits together */ + + for ( i=0; i < MAX_BH_QUEUE_ENTRIES; i++ ) { + if ( info->free_bh_queue_tail == NULL ) + info->free_bh_queue_head = bh_queue; + else + info->free_bh_queue_tail->link = bh_queue; + info->free_bh_queue_tail = bh_queue++; + } + + /* As a safety measure, mark the end of the chain with a NULL */ + info->free_bh_queue_tail->link = NULL; + +} /* end of mgsl_format_bh_queue() */ + +/* mgsl_bh_queue_put() + * + * Add a BH event to the BH queue + * + * Arguments: info pointer to device instance data + * type BH event type + * status BH event status + * + * Return Value: None + */ +void mgsl_bh_queue_put( struct mgsl_struct *info, unsigned char type, unsigned short status ) +{ + BH_EVENT *event = info->free_bh_queue_head; + + if ( event != NULL ) { + /* remove free element from head of free list */ + info->free_bh_queue_head = event->link; + event->link = NULL; + + /* file out new BH event */ + event->type = type; + event->status = status; + + /* add element to tail of pending list */ + if ( info->bh_queue_head != NULL ){ + /* BH queue is not empty, add current element to tail */ + info->bh_queue_tail->link = event; + } else { + /* the BH queue is empty so this element becomes the head of queue */ + info->bh_queue_head = event; + } + + /* the new element becomes tail of queue */ + info->bh_queue_tail = event; + } else { + /* No more free BH action elements in queue. */ + /* This happens when too many interrupts are occuring */ + /* for the mgsl_bh_handler to process so set a flag. */ + + info->isr_overflow = 1; + } + +} /* end of mgsl_bh_queue_put() */ + +/* mgsl_bh_queue_get() + * + * Free the current work item (if any) and get the + * next work item from the head of the pending work item queue. + * + * Effects: + * + * If a BH action element is available on the BH action queue + * then the head of the queue is removed and bh_action + * is set to point to the removed element. + * + * Arguments: info pointer to device instance data + * Return Value: 1 if BH action removed from queue + */ +int mgsl_bh_queue_get( struct mgsl_struct *info ) +{ + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if ( info->bh_action ) { + /* free the current work item */ + if ( info->free_bh_queue_head != NULL ){ + /* free queue is not empty, add current element to tail */ + info->free_bh_queue_tail->link = info->bh_action; + } else { + /* free queue is empty so this element becomes the head of queue */ + info->free_bh_queue_head = info->bh_action; + } + + /* add element to tail of free queue */ + info->free_bh_queue_tail = info->bh_action; + info->free_bh_queue_tail->link = NULL; + } + + /* attempt to remove element from head of queue */ + info->bh_action = info->bh_queue_head; + + if ( info->bh_action != NULL ){ + /* BH queue is not empty, remove element from queue head */ + info->bh_queue_head = info->bh_action->link; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 1; + } + + /* Mark BH routine as complete */ + info->bh_running = 0; + info->bh_requested = 0; + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; + +} /* end of mgsl_bh_queue_get() */ + +/* mgsl_bh_handler() + * + * Perform bottom half processing of work items queued by ISR. + * + * Arguments: Context pointer to device instance data + * Return Value: None + */ +void mgsl_bh_handler(void* Context) +{ + struct mgsl_struct *info = (struct mgsl_struct*)Context; + + if (!info) + return; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) entry\n", + __FILE__,__LINE__,info->device_name); + + info->bh_running = 1; + + /* Attempt to clear out the BH queue */ + + while( mgsl_bh_queue_get(info) ) { + + /* Process work item */ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler() work item action=%d\n", + __FILE__,__LINE__,info->bh_action->type); + + switch ( info->bh_action->type ) { + + case BH_TYPE_RECEIVE_DMA: + mgsl_bh_receive_dma( info, info->bh_action->status ); + break; + + case BH_TYPE_TRANSMIT_STATUS: + case BH_TYPE_TRANSMIT_DATA: + mgsl_bh_transmit_data( info, info->bh_action->status ); + break; + + case BH_TYPE_STATUS: + mgsl_bh_status_handler( info, info->bh_action->status ); + break; + + default: + /* unknown work item ID */ + printk("Unknown work item ID=%08X!\n", + info->bh_action->type ); + break; + } + } + + if ( info->isr_overflow ) { + printk("ISR overflow detected.\n"); + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_handler(%s) exit\n", + __FILE__,__LINE__,info->device_name); + +} /* end of mgsl_bh_handler() */ + +/* mgsl_bh_receive_dma() + * + * Perform bottom half processing for a receive DMA interrupt + * This occurs in HDLC mode after a DMA buffer has terminated + * or the DMA buffers have been exhausted. + * + * Arguments: + * + * info pointer to device instance data + * status status word + * + * Return Value: None + */ +void mgsl_bh_receive_dma( struct mgsl_struct *info, unsigned short status ) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_receive_dma(%s)\n", + __FILE__,__LINE__,info->device_name); + + while( mgsl_get_rx_frame(info) ); + +} /* end of mgsl_bh_receive_dma() */ + +/* mgsl_bh_transmit_data() + * + * Process a transmit data interrupt event + * This occurs in asynchronous communications mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_bh_transmit_data( struct mgsl_struct *info, unsigned short Datacount ) +{ + struct tty_struct *tty = info->tty; + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_transmit_data() entry on %s\n", + __FILE__,__LINE__,info->device_name); + + /* wakeup any waiting write requests */ + if (tty) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) { + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):calling ldisc.write_wakeup on %s\n", + __FILE__,__LINE__,info->device_name); + (tty->ldisc.write_wakeup)(tty); + } + wake_up_interruptible(&tty->write_wait); + } + +} /* End Of mgsl_bh_transmit_data() */ + +/* mgsl_bh_status_handler() + * + * Peform bottom half processing for a status interrupt + * + * This event is generated when a I/O pin (serial signal) + * has a transition. If there is a pending WaitEvent call + * and the status transition is identified in the EventMast + * of the pending call then complete the pending call. + * + * Arguments: + * + * info pointer to device instance data + * status status word + * + * Return Value: None + */ +void mgsl_bh_status_handler( struct mgsl_struct *info, unsigned short status ) +{ + if ( debug_level >= DEBUG_LEVEL_BH ) + printk( "%s(%d):mgsl_bh_status_handler() entry on %s\n", + __FILE__,__LINE__,info->device_name); + +} /* End Of mgsl_bh_status_handler() */ + +/* mgsl_isr_receive_status() + * + * Service a receive status interrupt. The type of status + * interrupt is indicated by the state of the RCSR. + * This is only used for HDLC mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, RCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_status status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + usc_UnlatchRxstatusBits( info, status ); + + if (status & (RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)) { + if (status & RXSTATUS_EXITED_HUNT) + info->icount.exithunt++; + if (status & RXSTATUS_IDLE_RECEIVED) + info->icount.rxidle++; + wake_up_interruptible(&info->event_wait_q); + } + + if (status & RXSTATUS_OVERRUN){ + /* Purge receive FIFO to allow DMA buffer completion + * with overrun status stored in the receive status block. + */ + usc_RCmd( info, RCmd_EnterHuntmode ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + } + +} /* end of mgsl_isr_receive_status() */ + +/* mgsl_isr_transmit_status() + * + * Service a transmit status interrupt + * HDLC mode :end of transmit frame + * Async mode:all data is sent + * transmit status is indicated by bits in the TCSR. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_transmit_status( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, TCSR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_status status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_UnlatchTxstatusBits( info, status ); + + if ( status & TXSTATUS_EOF_SENT ) + info->icount.txok++; + else if ( status & TXSTATUS_UNDERRUN ) + info->icount.txunder++; + else if ( status & TXSTATUS_ABORT_SENT ) + info->icount.txabort++; + else + info->icount.txunder++; + + info->tx_active = 0; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + + if ( info->drop_rts_on_tx_done ) { + usc_get_serial_signals( info ); + if ( info->serial_signals & SerialSignal_RTS ) { + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals( info ); + } + info->drop_rts_on_tx_done = 0; + } + + if (info->tty->stopped || info->tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + + mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_STATUS, status); + +} /* end of mgsl_isr_transmit_status() */ + +/* mgsl_isr_io_pin() + * + * Service an Input/Output pin interrupt. The type of + * interrupt is indicated by bits in the MISR + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_io_pin( struct mgsl_struct *info ) +{ + struct mgsl_icount *icount; + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_io_pin status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, IO_PIN ); + usc_UnlatchIostatusBits( info, status ); + + if (status & (MISCSTATUS_CTS_LATCHED | MISCSTATUS_DCD_LATCHED | + MISCSTATUS_DSR_LATCHED | MISCSTATUS_RI_LATCHED) ) { + icount = &info->icount; + /* update input line counters */ + if (status & MISCSTATUS_RI_LATCHED) + icount->rng++; + if (status & MISCSTATUS_DSR_LATCHED) + icount->dsr++; + if (status & MISCSTATUS_DCD_LATCHED) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & MISCSTATUS_DCD_LATCHED)) + hardpps(); +#endif + } + if (status & MISCSTATUS_CTS_LATCHED) + icount->cts++; + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if ( (info->flags & ASYNC_CHECK_CD) && + (status & MISCSTATUS_DCD_LATCHED) ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s CD now %s...", info->device_name, + (status & MISCSTATUS_DCD) ? "on" : "off"); + if (status & MISCSTATUS_DCD) + wake_up_interruptible(&info->open_wait); + else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_CALLOUT_NOHUP))) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("doing serial hangup..."); + if (info->tty) + tty_hangup(info->tty); + } + } + + if ( (info->flags & ASYNC_CTS_FLOW) && + (status & MISCSTATUS_CTS_LATCHED) ) { + if (info->tty->hw_stopped) { + if (status & MISCSTATUS_CTS) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx start..."); + info->tty->hw_stopped = 0; + usc_start_transmitter(info); + mgsl_bh_queue_put( info, BH_TYPE_TRANSMIT_DATA, status ); + return; + } + } else { + if (!(status & MISCSTATUS_CTS)) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("CTS tx stop..."); + info->tty->hw_stopped = 1; + usc_stop_transmitter(info); + } + } + } + } + + /* for diagnostics set IRQ flag */ + if ( status & MISCSTATUS_TXC_LATCHED ){ + usc_OutReg( info, SICR, + (unsigned short)(usc_InReg(info,SICR) & ~(SICR_TXC_ACTIVE+SICR_TXC_INACTIVE)) ); + usc_UnlatchIostatusBits( info, MISCSTATUS_TXC_LATCHED ); + info->irq_occurred = 1; + } + +} /* end of mgsl_isr_io_pin() */ + +/* mgsl_isr_transmit_data() + * + * Service a transmit data interrupt (async mode only). + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_transmit_data( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_transmit_data xmit_cnt=%d\n", + __FILE__,__LINE__,info->xmit_cnt); + + usc_ClearIrqPendingBits( info, TRANSMIT_DATA ); + + if (info->tty->stopped || info->tty->hw_stopped) { + usc_stop_transmitter(info); + return; + } + + if ( info->xmit_cnt ) + usc_load_txfifo( info ); + else + info->tx_active = 0; + + if (info->xmit_cnt < WAKEUP_CHARS) + mgsl_bh_queue_put(info, BH_TYPE_TRANSMIT_DATA, (unsigned short)(info->xmit_cnt)); + +} /* end of mgsl_isr_transmit_data() */ + +/* mgsl_isr_receive_data() + * + * Service a receive data interrupt. This occurs + * when operating in asynchronous interrupt transfer mode. + * The receive data FIFO is flushed to the receive data buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_data( struct mgsl_struct *info ) +{ + int Fifocount; + u16 status; + unsigned char DataByte; + struct tty_struct *tty = info->tty; + struct mgsl_icount *icount = &info->icount; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_data\n", + __FILE__,__LINE__); + + usc_ClearIrqPendingBits( info, RECEIVE_DATA ); + + /* select FIFO status for RICR readback */ + usc_RCmd( info, RCmd_SelectRicrRxFifostatus ); + + /* clear the Wordstatus bit so that status readback */ + /* only reflects the status of this byte */ + usc_OutReg( info, RICR+LSBONLY, (u16)(usc_InReg(info, RICR+LSBONLY) & ~BIT3 )); + + /* flush the receive FIFO */ + + while( (Fifocount = (usc_InReg(info,RICR) >> 8)) ) { + /* read one byte from RxFIFO */ + outw( (inw(info->io_base + CCAR) & 0x0780) | (RDR+LSBONLY), + info->io_base + CCAR ); + DataByte = inb( info->io_base + CCAR ); + + /* get the status of the received byte */ + status = usc_InReg(info, RCSR); + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) + usc_UnlatchRxstatusBits(info,RXSTATUS_ALL); + + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + continue; + + *tty->flip.char_buf_ptr = DataByte; + icount->rx++; + + *tty->flip.flag_buf_ptr = 0; + if ( status & (RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR + + RXSTATUS_OVERRUN + RXSTATUS_BREAK_RECEIVED) ) { + printk("rxerr=%04X\n",status); + /* update error statistics */ + if ( status & RXSTATUS_BREAK_RECEIVED ) { + status &= ~(RXSTATUS_FRAMING_ERROR + RXSTATUS_PARITY_ERROR); + icount->brk++; + } else if (status & RXSTATUS_PARITY_ERROR) + icount->parity++; + else if (status & RXSTATUS_FRAMING_ERROR) + icount->frame++; + else if (status & RXSTATUS_OVERRUN) { + /* must issue purge fifo cmd before */ + /* 16C32 accepts more receive chars */ + usc_RTCmd(info,RTCmd_PurgeRxFifo); + icount->overrun++; + } + + /* discard char if tty control flags say so */ + if (status & info->ignore_status_mask) + continue; + + status &= info->read_status_mask; + + if (status & RXSTATUS_BREAK_RECEIVED) { + *tty->flip.flag_buf_ptr = TTY_BREAK; + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (status & RXSTATUS_PARITY_ERROR) + *tty->flip.flag_buf_ptr = TTY_PARITY; + else if (status & RXSTATUS_FRAMING_ERROR) + *tty->flip.flag_buf_ptr = TTY_FRAME; + if (status & RXSTATUS_OVERRUN) { + /* Overrun is special, since it's + * reported immediately, and doesn't + * affect the current character + */ + if (tty->flip.count < TTY_FLIPBUF_SIZE) { + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } + } + } /* end of if (error) */ + + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + + if ( debug_level >= DEBUG_LEVEL_ISR ) { + printk("%s(%d):mgsl_isr_receive_data flip count=%d\n", + __FILE__,__LINE__,tty->flip.count); + printk("%s(%d):rx=%d brk=%d parity=%d frame=%d overrun=%d\n", + __FILE__,__LINE__,icount->rx,icount->brk, + icount->parity,icount->frame,icount->overrun); + } + + if ( tty->flip.count ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + tty_flip_buffer_push(tty); +#else + queue_task(&tty->flip.tqueue, &tq_timer); +#endif + } + + +} /* end of mgsl_isr_receive_data() */ + +/* mgsl_isr_misc() + * + * Service a miscellaneos interrupt source. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void mgsl_isr_misc( struct mgsl_struct *info ) +{ + u16 status = usc_InReg( info, MISR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_misc status=%04X\n", + __FILE__,__LINE__,status); + + usc_ClearIrqPendingBits( info, MISC ); + usc_UnlatchMiscstatusBits( info, status ); + +} /* end of mgsl_isr_misc() */ + +/* mgsl_isr_null() + * + * Services undefined interrupt vectors from the + * USC. (hence this function SHOULD never be called) + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void mgsl_isr_null( struct mgsl_struct *info ) +{ + +} /* end of mgsl_isr_null() */ + +/* mgsl_isr_receive_dma() + * + * Service a receive DMA channel interrupt. + * For this driver there are two sources of receive DMA interrupts + * as identified in the Receive DMA mode Register (RDMR): + * + * BIT3 EOA/EOL End of List, all receive buffers in receive + * buffer list have been filled (no more free buffers + * available). The DMA controller has shut down. + * + * BIT2 EOB End of Buffer. This interrupt occurs when a receive + * DMA buffer is terminated in response to completion + * of a good frame or a frame with errors. The status + * of the frame is stored in the buffer entry in the + * list of receive buffer entries. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_isr_receive_dma( struct mgsl_struct *info ) +{ + u16 status; + + /* clear interrupt pending and IUS bit for Rx DMA IRQ */ + usc_OutDmaReg( info, CDIR, BIT9+BIT1 ); + + /* Read the receive DMA status to identify interrupt type. */ + /* This also clears the status bits. */ + status = usc_InDmaReg( info, RDMR ); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_isr_receive_dma(%s) status=%04X\n", + __FILE__,__LINE__,info->device_name,status); + + /* Post a receive event for BH processing. */ + mgsl_bh_queue_put( info, BH_TYPE_RECEIVE_DMA, status ); + + if ( status & BIT3 ) + info->rx_overflow = 1; + +} /* end of mgsl_isr_receive_dma() */ + +/* mgsl_interrupt() + * + * Interrupt service routine entry point. + * + * Arguments: + * + * irq interrupt number that caused interrupt + * dev_id device ID supplied during interrupt registration + * regs interrupted processor context + * + * Return Value: None + */ +static void mgsl_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct mgsl_struct * info; + u16 UscVector; + u16 DmaVector; + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_interrupt(%d)entry.\n", + __FILE__,__LINE__,irq); + + info = (struct mgsl_struct *)dev_id; + if (!info) + return; + + spin_lock(&info->irq_spinlock); + + for(;;) { + /* Read the interrupt vectors from hardware. */ + UscVector = usc_InReg(info, IVR) >> 9; + DmaVector = usc_InDmaReg(info, DIVR); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s UscVector=%08X DmaVector=%08X\n", + __FILE__,__LINE__,info->device_name,UscVector,DmaVector); + + if ( !UscVector && !DmaVector ) + break; + + /* Dispatch interrupt vector */ + if ( UscVector ) + (*UscIsrTable[UscVector])(info); + else + mgsl_isr_receive_dma(info); + + if ( info->isr_overflow ) { + printk(KERN_ERR"%s(%d):%s isr overflow irq=%d\n", + __FILE__,__LINE__,info->device_name, irq); + /* Interrupt overflow. Reset adapter and exit. */ +// UscReset(info); +// break; + } + } + + /* Request bottom half processing if there's something + * for it to do and the bh is not already running + */ + + if ( info->bh_queue_head && !info->bh_running && !info->bh_requested ) { + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):%s queueing bh task.\n", + __FILE__,__LINE__,info->device_name); + queue_task(&info->task, &tq_immediate); + mark_bh(IMMEDIATE_BH); + info->bh_requested = 1; + } + + spin_unlock(&info->irq_spinlock); + + if ( debug_level >= DEBUG_LEVEL_ISR ) + printk("%s(%d):mgsl_interrupt(%d)exit.\n", + __FILE__,__LINE__,irq); + +} /* end of mgsl_interrupt() */ + +/* startup() + * + * Initialize and start device. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int startup(struct mgsl_struct * info) +{ + int retval = 0; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):mgsl_startup(%s)\n",__FILE__,__LINE__,info->device_name); + + if (info->flags & ASYNC_INITIALIZED) + return 0; + + if (!info->xmit_buf) { + /* allocate a page of memory for a transmit buffer */ + info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL); + if (!info->xmit_buf) { + printk(KERN_ERR"%s(%d):%s can't allocate transmit buffer\n", + __FILE__,__LINE__,info->device_name); + return -ENOMEM; + } + } + + mgsl_format_bh_queue(info); + + init_timer(&info->tx_timer); + info->tx_timer.data = (unsigned long)info; + info->tx_timer.function = mgsl_tx_timeout; + + /* Allocate and claim adapter resources */ + retval = mgsl_claim_resources(info); + + /* perform existance check and diagnostics */ + if ( !retval ) + retval = mgsl_adapter_test(info); + + if ( retval ) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + if (capable(CAP_SYS_ADMIN) && info->tty) +#else + if (suser() && info->tty) +#endif + set_bit(TTY_IO_ERROR, &info->tty->flags); + mgsl_release_resources(info); + return retval; + } + + /* program hardware for current parameters */ + mgsl_change_params(info); + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags |= ASYNC_INITIALIZED; + + return 0; + +} /* end of startup() */ + +/* shutdown() + * + * Called by mgsl_close() and mgsl_hangup() to shutdown hardware + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void shutdown(struct mgsl_struct * info) +{ + unsigned long flags; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_shutdown(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* clear status wait queue because status changes */ + /* can't happen after shutting down the hardware */ + wake_up_interruptible(&info->status_event_wait_q); + wake_up_interruptible(&info->event_wait_q); + + if (info->xmit_buf) { + free_page((unsigned long) info->xmit_buf); + info->xmit_buf = 0; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_DisableMasterIrqBit(info); + usc_stop_receiver(info); + usc_stop_transmitter(info); + usc_DisableInterrupts(info,RECEIVE_DATA + RECEIVE_STATUS + + TRANSMIT_DATA + TRANSMIT_STATUS + IO_PIN + MISC ); + usc_DisableDmaInterrupts(info,DICR_MASTER + DICR_TRANSMIT + DICR_RECEIVE); + + /* Disable DMAEN (Port 7, Bit 14) */ + /* This disconnects the DMA request signal from the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) | BIT14)); + + /* Disable INTEN (Port 6, Bit12) */ + /* This disconnects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) | BIT12)); + + if (!info->tty || info->tty->termios->c_cflag & HUPCL) { + info->serial_signals &= ~(SerialSignal_DTR + SerialSignal_RTS); + usc_set_serial_signals(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_release_resources(info); + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + +} /* end of shutdown() */ + +/* mgsl_change_params() + * + * Reconfigure adapter based on new parameters + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +static void mgsl_change_params(struct mgsl_struct *info) +{ + unsigned cflag; + unsigned long flags; + int bits_per_char; + + if (!info->tty || !info->tty->termios) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_change_params(%s)\n", + __FILE__,__LINE__, info->device_name ); + + cflag = info->tty->termios->c_cflag; + + /* if B0 rate (hangup) specified then negate DTR and RTS */ + /* otherwise assert DTR and RTS */ + if (cflag & CBAUD) + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + else + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + + /* byte size and parity */ + + switch (cflag & CSIZE) { + case CS5: info->params.data_bits = 5; break; + case CS6: info->params.data_bits = 6; break; + case CS7: info->params.data_bits = 7; break; + case CS8: info->params.data_bits = 8; break; + /* Never happens, but GCC is too dumb to figure it out */ + default: info->params.data_bits = 7; break; + } + + if (cflag & CSTOPB) + info->params.stop_bits = 2; + else + info->params.stop_bits = 1; + + info->params.parity = ASYNC_PARITY_NONE; + if (cflag & PARENB) { + if (cflag & PARODD) + info->params.parity = ASYNC_PARITY_ODD; + else + info->params.parity = ASYNC_PARITY_EVEN; +#ifdef CMSPAR + if (cflag & CMSPAR) + info->params.parity = ASYNC_PARITY_SPACE; +#endif + } + + /* calculate number of jiffies to transmit a full + * FIFO (32 bytes) at specified data rate + */ + bits_per_char = info->params.data_bits + + info->params.stop_bits + 1; + + /* if port data rate is set to 460800 or less then + * allow tty settings to override, otherwise keep the + * current data rate. + */ + if (info->params.data_rate <= 460800) { +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->params.data_rate = tty_get_baud_rate(info->tty); +#else + int i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 4) + info->tty->termios->c_cflag &= ~CBAUDEX; + else + i += 15; + } + info->params.data_rate = baud_table[i]; +#endif + } + + if ( info->params.data_rate ) { + info->timeout = (32*HZ*bits_per_char) / + info->params.data_rate; + } + info->timeout += HZ/50; /* Add .02 seconds of slop */ + + if (cflag & CRTSCTS) + info->flags |= ASYNC_CTS_FLOW; + else + info->flags &= ~ASYNC_CTS_FLOW; + + if (cflag & CLOCAL) + info->flags &= ~ASYNC_CHECK_CD; + else + info->flags |= ASYNC_CHECK_CD; + + /* process tty input control flags */ + + info->read_status_mask = RXSTATUS_OVERRUN; + if (I_INPCK(info->tty)) + info->read_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) + info->read_status_mask |= RXSTATUS_BREAK_RECEIVED; + + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= RXSTATUS_PARITY_ERROR | RXSTATUS_FRAMING_ERROR; + if (I_IGNBRK(info->tty)) { + info->ignore_status_mask |= RXSTATUS_BREAK_RECEIVED; + /* If ignoring parity and break indicators, ignore + * overruns too. (For real raw support). + */ + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= RXSTATUS_OVERRUN; + } + + /* reprogram the hardware */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + usc_stop_receiver(info); + usc_stop_transmitter(info); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + + if ( info->params.mode == MGSL_MODE_HDLC ) + usc_set_sync_mode(info); + else + usc_set_async_mode(info); + + usc_set_serial_signals(info); + + /* enable modem signal IRQs and read initial signal states */ + usc_EnableStatusIrqs(info,SICR_CTS+SICR_DSR+SICR_DCD+SICR_RI); + usc_EnableInterrupts(info, IO_PIN); + usc_get_serial_signals(info); + + if ( cflag & CREAD ) + usc_start_receiver(info); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_change_params() */ + +/* mgsl_put_char() + * + * Add a character to the transmit buffer. + * + * Arguments: tty pointer to tty information structure + * ch character to add to transmit buffer + * + * Return Value: None + */ +static void mgsl_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) { + printk( "%s(%d):mgsl_put_char(%d) on %s\n", + __FILE__,__LINE__,ch,info->device_name); + } + + if (mgsl_paranoia_check(info, tty->device, "mgsl_put_char")) + return; + + if (!tty || !info->xmit_buf) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if ( (info->params.mode != MGSL_MODE_HDLC) || + !info->tx_active ) { + + if (info->xmit_cnt < SERIAL_XMIT_SIZE - 1) { + info->xmit_buf[info->xmit_head++] = ch; + info->xmit_head &= SERIAL_XMIT_SIZE-1; + info->xmit_cnt++; + } + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_put_char() */ + +/* mgsl_flush_chars() + * + * Enable transmitter so remaining characters in the + * transmit buffer are sent. + * + * Arguments: tty pointer to tty information structure + * Return Value: None + */ +static void mgsl_flush_chars(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s xmit_cnt=%d\n", + __FILE__,__LINE__,info->device_name,info->xmit_cnt); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_chars")) + return; + + if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || + !info->xmit_buf) + return; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_flush_chars() entry on %s starting transmitter\n", + __FILE__,__LINE__,info->device_name ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + if (!info->tx_active) { + if ( (info->params.mode == MGSL_MODE_HDLC) && + info->xmit_cnt ) { + /* operating in synchronous (frame oriented) mode */ + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + } + usc_start_transmitter(info); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_flush_chars() */ + +/* mgsl_write() + * + * Send a block of data + * + * Arguments: + * + * tty pointer to tty information structure + * from_user flag: 1 = from user process + * buf pointer to buffer containing send data + * count size of send data in bytes + * + * Return Value: number of characters written + */ +static int mgsl_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0, err; + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) count=%d\n", + __FILE__,__LINE__,info->device_name,count); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_write")) + goto cleanup; + + if (!tty || !info->xmit_buf || !tmp_buf) + goto cleanup; + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + + if (info->tx_active) { + ret = 0; goto cleanup; + } + + if ( info->xmit_cnt ) { + /* Send accumulated from send_char() calls */ + /* as frame and wait before accepting more data. */ + ret = 0; + + /* copy data from circular xmit_buf to */ + /* transmit DMA buffer. */ + mgsl_load_tx_dma_buffer(info, + info->xmit_buf,info->xmit_cnt); + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync xmit_cnt flushing\n", + __FILE__,__LINE__,info->device_name); + } else { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync transmit accepted\n", + __FILE__,__LINE__,info->device_name); + ret = count; + info->xmit_cnt = count; + if (from_user) { + down(&tmp_buf_sem); + COPY_FROM_USER(err,tmp_buf, buf, count); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) sync user buf copy failed\n", + __FILE__,__LINE__,info->device_name); + ret = -EFAULT; + } else + mgsl_load_tx_dma_buffer(info,tmp_buf,count); + up(&tmp_buf_sem); + } + else + mgsl_load_tx_dma_buffer(info,buf,count); + } + } else { + if (from_user) { + down(&tmp_buf_sem); + while (1) { + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) + break; + + COPY_FROM_USER(err,tmp_buf, buf, c); + c -= err; + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + while (1) { + spin_lock_irqsave(&info->irq_spinlock,flags); + c = MIN(count, + MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, + SERIAL_XMIT_SIZE - info->xmit_head)); + if (c <= 0) { + spin_unlock_irqrestore(&info->irq_spinlock,flags); + break; + } + memcpy(info->xmit_buf + info->xmit_head, buf, c); + info->xmit_head = ((info->xmit_head + c) & + (SERIAL_XMIT_SIZE-1)); + info->xmit_cnt += c; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + buf += c; + count -= c; + ret += c; + } + } + } + + if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_active) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +cleanup: + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_write(%s) returning=%d\n", + __FILE__,__LINE__,info->device_name,ret); + + return ret; + +} /* end of mgsl_write() */ + +/* mgsl_write_room() + * + * Return the count of free bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_write_room(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + int ret; + + if (mgsl_paranoia_check(info, tty->device, "mgsl_write_room")) + return 0; + ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; + if (ret < 0) + ret = 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_write_room(%s)=%d\n", + __FILE__,__LINE__, info->device_name,ret ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return 0; + else + return HDLC_MAX_FRAME_SIZE; + } + + return ret; + +} /* end of mgsl_write_room() */ + +/* mgsl_chars_in_buffer() + * + * Return the count of bytes in transmit buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static int mgsl_chars_in_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_chars_in_buffer")) + return 0; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_chars_in_buffer(%s)=%d\n", + __FILE__,__LINE__, info->device_name,info->xmit_cnt ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* operating in synchronous (frame oriented) mode */ + if ( info->tx_active ) + return info->tx_buffer_list[0].rcc; + else + return 0; + } + + return info->xmit_cnt; +} /* end of mgsl_chars_in_buffer() */ + +/* mgsl_flush_buffer() + * + * Discard all data in the send buffer + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_flush_buffer(struct tty_struct *tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_flush_buffer(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_flush_buffer")) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + del_timer(&info->tx_timer); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + +} /* end of mgsl_flush_buffer() */ + +/* mgsl_send_xchar() + * + * Send a high-priority XON/XOFF character + * + * Arguments: tty pointer to tty info structure + * ch character to send + * Return Value: None + */ +static void mgsl_send_xchar(struct tty_struct *tty, char ch) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_send_xchar(%s,%d)\n", + __FILE__,__LINE__, info->device_name, ch ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_send_xchar")) + return; + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->tx_enabled) + usc_start_transmitter(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_send_xchar() */ + +/* mgsl_throttle() + * + * Signal remote device to throttle send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_throttle(struct tty_struct * tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_throttle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_throttle")) + return; + + if (I_IXOFF(tty)) + mgsl_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals &= ~SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } +} /* end of mgsl_throttle() */ + +/* mgsl_unthrottle() + * + * Signal remote device to stop throttling send data (our receive data) + * + * Arguments: tty pointer to tty info structure + * Return Value: None + */ +static void mgsl_unthrottle(struct tty_struct * tty) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_unthrottle(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_unthrottle")) + return; + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + mgsl_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + +} /* end of mgsl_unthrottle() */ + +/* mgsl_get_stats() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_icount pointer to buffer to hold returned stats + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_stats(struct mgsl_struct * info, struct mgsl_icount *user_icount) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + COPY_TO_USER(err,user_icount, &info->icount, sizeof(struct mgsl_icount)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_stats(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_stats() */ + +/* mgsl_get_params() + * + * get the current serial parameters information + * + * Arguments: info pointer to device instance data + * user_params pointer to buffer to hold returned params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_params(struct mgsl_struct * info, MGSL_PARAMS *user_params) +{ + int err; + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_params(%s)\n", + __FILE__,__LINE__, info->device_name); + + COPY_TO_USER(err,user_params, &info->params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_params() */ + +/* mgsl_set_params() + * + * set the serial parameters + * + * Arguments: + * + * info pointer to device instance data + * new_params user buffer containing new serial params + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_params(struct mgsl_struct * info, MGSL_PARAMS *new_params) +{ + unsigned long flags; + MGSL_PARAMS tmp_params; + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_params %s\n", __FILE__,__LINE__, + info->device_name ); + COPY_FROM_USER(err,&tmp_params, new_params, sizeof(MGSL_PARAMS)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_set_params(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_change_params(info); + + return 0; + +} /* end of mgsl_set_params() */ + +/* mgsl_get_txidle() + * + * get the current transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode pointer to buffer to hold returned idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_get_txidle(struct mgsl_struct * info, int*idle_mode) +{ + int err; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_txidle(%s)=%d\n", + __FILE__,__LINE__, info->device_name, info->idle_mode); + + COPY_TO_USER(err,idle_mode, &info->idle_mode, sizeof(int)); + if (err) { + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_get_txidle(%s) user buffer copy failed\n", + __FILE__,__LINE__,info->device_name); + return -EFAULT; + } + + return 0; + +} /* end of mgsl_get_txidle() */ + +/* mgsl_set_txidle() service ioctl to set transmit idle mode + * + * Arguments: info pointer to device instance data + * idle_mode new idle mode + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_set_txidle(struct mgsl_struct * info, int idle_mode) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_txidle(%s,%d)\n", __FILE__,__LINE__, + info->device_name, idle_mode ); + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->idle_mode = idle_mode; + usc_set_txidle( info ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_set_txidle() */ + +/* mgsl_txenable() + * + * enable or disable the transmitter + * + * Arguments: + * + * info pointer to device instance data + * enable 1 = enable, 0 = disable + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->tx_enabled ) + usc_start_transmitter(info); + } else { + if ( info->tx_enabled ) + usc_stop_transmitter(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txenable() */ + +/* mgsl_txabort() abort send HDLC frame + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_txabort(struct mgsl_struct * info) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_txabort(%s)\n", __FILE__,__LINE__, + info->device_name); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( info->tx_active && info->params.mode == MGSL_MODE_HDLC ) + usc_TCmd(info,TCmd_SendAbort); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_txabort() */ + +/* mgsl_rxenable() enable or disable the receiver + * + * Arguments: info pointer to device instance data + * enable 1 = enable, 0 = disable + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_rxenable(struct mgsl_struct * info, int enable) +{ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_rxenable(%s,%d)\n", __FILE__,__LINE__, + info->device_name, enable); + + spin_lock_irqsave(&info->irq_spinlock,flags); + if ( enable ) { + if ( !info->rx_enabled ) + usc_start_receiver(info); + } else { + if ( info->rx_enabled ) + usc_stop_receiver(info); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + return 0; + +} /* end of mgsl_rxenable() */ + +/* mgsl_wait_event() wait for specified event to occur + * + * Arguments: info pointer to device instance data + * mask bitmask of events to wait for + * Return Value: bit mask of triggering event, otherwise error code + */ +static int mgsl_wait_event(struct mgsl_struct * info, int mask) +{ + unsigned long flags; + int s; + int rc=0; + u16 regval; + struct mgsl_icount cprev, cnow; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_event(%s,%d)\n", __FILE__,__LINE__, + info->device_name, mask); + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* note the counters on entry */ + cprev = info->icount; + + if (mask & MgslEvent_ExitHuntMode) { + /* enable exit hunt mode IRQ */ + regval = usc_InReg(info,RICR); + if (!(regval & RXSTATUS_EXITED_HUNT)) + usc_OutReg(info, RICR, regval | RXSTATUS_EXITED_HUNT); + } + + if (mask & MgslEvent_IdleReceived) { + /* enable idle mode received IRQ */ + regval = usc_InReg(info,RICR); + if (!(regval & RXSTATUS_IDLE_RECEIVED)) + usc_OutReg(info, RICR, regval | RXSTATUS_IDLE_RECEIVED); + } + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + while(!rc) { + /* sleep until event occurs */ + interruptible_sleep_on(&info->event_wait_q); + + /* see if a signal woke us */ + if (signal_pending(current)) { + rc = -ERESTARTSYS; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + /* get icount and serial signal states */ + cnow = info->icount; + s = info->serial_signals; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + rc = 0; + + if (cnow.dsr != cprev.dsr) + rc |= (mask & ((s & SerialSignal_DSR) ? + MgslEvent_DsrActive:MgslEvent_DsrInactive)); + + if (cnow.dcd != cprev.dcd) + rc |= (mask & ((s & SerialSignal_DCD) ? + MgslEvent_DcdActive:MgslEvent_DcdInactive)); + + if (cnow.cts != cprev.cts) + rc |= (mask & ((s & SerialSignal_CTS) ? + MgslEvent_CtsActive:MgslEvent_CtsInactive)); + + if (cnow.rng != cprev.rng) + rc |= (mask & ((s & SerialSignal_RI) ? + MgslEvent_RiActive:MgslEvent_RiInactive)); + + if (cnow.exithunt != cprev.exithunt) + rc |= (mask & MgslEvent_ExitHuntMode); + + if (cnow.rxidle != cprev.rxidle) + rc |= (mask & MgslEvent_ExitHuntMode); + + if (!rc) + rc = -EIO; /* no change => error */ + + cprev = cnow; + } + + if (mask & (MgslEvent_ExitHuntMode + MgslEvent_IdleReceived)) { + spin_lock_irqsave(&info->irq_spinlock,flags); + if (!info->event_wait_q) { + /* disable enable exit hunt mode/idle rcvd IRQs */ + regval = usc_InReg(info,RICR); + usc_OutReg(info, RICR, regval & + ~(RXSTATUS_EXITED_HUNT + RXSTATUS_IDLE_RECEIVED)); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + return rc; + +} /* end of mgsl_wait_event() */ + +/* get_modem_info() + * + * Read the state of the serial control and + * status signals and return to caller. + * + * Arguments: info pointer to device instance data + * value pointer to int to hold returned info + * + * Return Value: 0 if success, otherwise error code + */ +static int get_modem_info(struct mgsl_struct * info, unsigned int *value) +{ + unsigned int result = 0; + unsigned long flags; + int err; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (info->serial_signals & SerialSignal_RTS) + result |= TIOCM_RTS; + if (info->serial_signals & SerialSignal_DTR) + result |= TIOCM_DTR; + if (info->serial_signals & SerialSignal_DCD) + result |= TIOCM_CAR; + if (info->serial_signals & SerialSignal_RI) + result |= TIOCM_RNG; + if (info->serial_signals & SerialSignal_DSR) + result |= TIOCM_DSR; + if (info->serial_signals & SerialSignal_CTS) + result |= TIOCM_CTS; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_get_modem_info %s value=%08X\n", + __FILE__,__LINE__, info->device_name, *value ); + + PUT_USER(err,result,value); + return err; +} /* end of get_modem_info() */ + +/* set_modem_info() + * + * Set the state of the modem control signals (DTR/RTS) + * + * Arguments: + * + * info pointer to device instance data + * cmd signal command: TIOCMBIS = set bit TIOCMBIC = clear bit + * TIOCMSET = set/clear signal values + * value bit mask for command + * + * Return Value: 0 if success, otherwise error code + */ +static int set_modem_info(struct mgsl_struct * info, unsigned int cmd, + unsigned int *value) +{ + int error; + unsigned int arg; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_modem_info %s\n", __FILE__,__LINE__, + info->device_name ); + + GET_USER(error,arg,value); + if (error) + return error; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + if (arg & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->serial_signals &= ~SerialSignal_RTS; + if (arg & TIOCM_DTR) + info->serial_signals &= ~SerialSignal_DTR; + break; + case TIOCMSET: + if (arg & TIOCM_RTS) + info->serial_signals |= SerialSignal_RTS; + else + info->serial_signals &= ~SerialSignal_RTS; + + if (arg & TIOCM_DTR) + info->serial_signals |= SerialSignal_DTR; + else + info->serial_signals &= ~SerialSignal_DTR; + break; + default: + return -EINVAL; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return 0; + +} /* end of set_modem_info() */ + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) +/* mgsl_break() Set or clear transmit break condition + * + * Arguments: tty pointer to tty instance data + * break_state -1=set break condition, 0=clear + * Return Value: None + */ +static void mgsl_break(struct tty_struct *tty, int break_state) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_break(%s,%d)\n", + __FILE__,__LINE__, info->device_name, break_state); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_break")) + return; + + spin_lock_irqsave(&info->irq_spinlock,flags); + if (break_state == -1) + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) | BIT7)); + else + usc_OutReg(info,IOCR,(u16)(usc_InReg(info,IOCR) & ~BIT7)); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +} /* end of mgsl_break() */ +#endif + +/* mgsl_ioctl() Service an IOCTL request + * + * Arguments: + * + * tty pointer to tty instance data + * file pointer to associated file object for device + * cmd IOCTL command code + * arg command argument/context + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + struct mgsl_icount cprev, cnow; /* kernel counter temps */ + struct serial_icounter_struct *p_cuser; /* user space */ + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_ioctl %s cmd=%08X\n", __FILE__,__LINE__, + info->device_name, cmd ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case MGSL_IOCGPARAMS: + return mgsl_get_params(info,(MGSL_PARAMS *)arg); + case MGSL_IOCSPARAMS: + return mgsl_set_params(info,(MGSL_PARAMS *)arg); + case MGSL_IOCGTXIDLE: + return mgsl_get_txidle(info,(int*)arg); + case MGSL_IOCSTXIDLE: + return mgsl_set_txidle(info,(int)arg); + case MGSL_IOCTXENABLE: + return mgsl_txenable(info,(int)arg); + case MGSL_IOCRXENABLE: + return mgsl_rxenable(info,(int)arg); + case MGSL_IOCTXABORT: + return mgsl_txabort(info); + case MGSL_IOCGSTATS: + return mgsl_get_stats(info,(struct mgsl_icount*)arg); + case MGSL_IOCWAITEVENT: + return mgsl_wait_event(info,(int)arg); + case MGSL_IOCCLRMODCOUNT: + while(MOD_IN_USE) + MOD_DEC_USE_COUNT; + return 0; + /* + * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change + * - mask passed in arg for lines of interest + * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) + * Caller should use TIOCGICOUNT to see which one it was + */ + case TIOCMIWAIT: + spin_lock_irqsave(&info->irq_spinlock,flags); + /* note the counters on entry */ + cprev = info->icount; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + while (1) { + interruptible_sleep_on(&info->status_event_wait_q); + /* see if a signal did it */ + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->icount; /* atomic copy */ + restore_flags(flags); + if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && + cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) + return -EIO; /* no change => error */ + if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { + return 0; + } + cprev = cnow; + } + /* NOTREACHED */ + + /* + * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) + * Return: write counters to the user passed counter struct + * NB: both 1->0 and 0->1 transitions are counted except for + * RI where only 0->1 is counted. + */ + case TIOCGICOUNT: + spin_lock_irqsave(&info->irq_spinlock,flags); + cnow = info->icount; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + p_cuser = (struct serial_icounter_struct *) arg; + PUT_USER(error,cnow.cts, &p_cuser->cts); + if (error) return error; + PUT_USER(error,cnow.dsr, &p_cuser->dsr); + if (error) return error; + PUT_USER(error,cnow.rng, &p_cuser->rng); + if (error) return error; + PUT_USER(error,cnow.dcd, &p_cuser->dcd); + if (error) return error; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + PUT_USER(error,cnow.rx, &p_cuser->rx); + if (error) return error; + PUT_USER(error,cnow.tx, &p_cuser->tx); + if (error) return error; + PUT_USER(error,cnow.frame, &p_cuser->frame); + if (error) return error; + PUT_USER(error,cnow.overrun, &p_cuser->overrun); + if (error) return error; + PUT_USER(error,cnow.parity, &p_cuser->parity); + if (error) return error; + PUT_USER(error,cnow.brk, &p_cuser->brk); + if (error) return error; + PUT_USER(error,cnow.buf_overrun, &p_cuser->buf_overrun); + if (error) return error; +#endif + return 0; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* mgsl_set_termios() + * + * Set new termios settings + * + * Arguments: + * + * tty pointer to tty structure + * termios pointer to buffer to hold returned old termios + * + * Return Value: None + */ +static void mgsl_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct mgsl_struct *info = (struct mgsl_struct *)tty->driver_data; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_set_termios %s\n", __FILE__,__LINE__, + tty->driver.name ); + + /* just return if nothing has changed */ + if ((tty->termios->c_cflag == old_termios->c_cflag) + && (RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + mgsl_change_params(info); + + /* Handle transition to B0 status */ + if (old_termios->c_cflag & CBAUD && + !(tty->termios->c_cflag & CBAUD)) { + info->serial_signals &= ~(SerialSignal_RTS + SerialSignal_DTR); + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && + tty->termios->c_cflag & CBAUD) { + info->serial_signals |= SerialSignal_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->serial_signals |= SerialSignal_RTS; + } + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + /* Handle turning off CRTSCTS */ + if (old_termios->c_cflag & CRTSCTS && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + mgsl_start(tty); + } + +} /* end of mgsl_set_termios() */ + +/* mgsl_close() + * + * Called when port is closed. Wait for remaining data to be + * sent. Disable port and free resources. + * + * Arguments: + * + * tty pointer to open tty structure + * filp pointer to open file object + * + * Return Value: None + */ +static void mgsl_close(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + + if (!info || mgsl_paranoia_check(info, tty->device, "mgsl_close")) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) entry, count=%d\n", + __FILE__,__LINE__, info->device_name, info->count); + + if (!info->count || tty_hung_up_p(filp)) + goto cleanup; + + if ((tty->count == 1) && (info->count != 1)) { + /* + * tty->count is 1 and the tty structure will be freed. + * info->count should be one in this case. + * if it's not, correct it so that the port is shutdown. + */ + printk("mgsl_close: bad refcount; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + + info->count--; + + /* if at least one open remaining, leave hardware active */ + if (info->count) + goto cleanup; + + info->flags |= ASYNC_CLOSING; + + /* Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + + /* set tty->closing to notify line discipline to + * only process XON/XOFF characters. Only the N_TTY + * discipline appears to use this (ppp does not). + */ + tty->closing = 1; + + /* wait for transmit data to clear all layers */ + + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) calling tty_wait_until_sent\n", + __FILE__,__LINE__, info->device_name ); + tty_wait_until_sent(tty, info->closing_wait); + } + + if (info->flags & ASYNC_INITIALIZED) + mgsl_wait_until_sent(tty, info->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->tty = 0; + + if (info->blocked_open) { + if (info->close_delay) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + + wake_up_interruptible(&info->close_wait); + +cleanup: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_close(%s) exit, count=%d\n", __FILE__,__LINE__, + tty->driver.name, info->count); + if(MOD_IN_USE) + MOD_DEC_USE_COUNT; + +} /* end of mgsl_close() */ + +/* mgsl_wait_until_sent() + * + * Wait until the transmitter is empty. + * + * Arguments: + * + * tty pointer to tty info structure + * timeout time to wait for send completion + * + * Return Value: None + */ +static void mgsl_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + + if (!info ) + return; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) entry\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_wait_until_sent")) + return; + + if (!(info->flags & ASYNC_INITIALIZED)) + goto exit; + + orig_jiffies = jiffies; + + /* Set check interval to 1/5 of estimated time to + * send a character, and make it at least 1. The check + * interval should also be less than the timeout. + * Note: use tight timings here to satisfy the NIST-PCTS. + */ + + if ( info->params.data_rate ) { + char_time = info->timeout/(32 * 5); + if (!char_time) + char_time++; + } else + char_time = 1; + + if (timeout) + char_time = MIN(char_time, timeout); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + while (info->tx_active) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + } else { + while (!(usc_InReg(info,TCSR) & TXSTATUS_ALL_SENT) && + info->tx_enabled) { + current->state = TASK_INTERRUPTIBLE; + current->counter = 0; /* make us low-priority */ + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && ((orig_jiffies + timeout) < jiffies)) + break; + } + } + + current->state = TASK_RUNNING; +exit: + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_wait_until_sent(%s) exit\n", + __FILE__,__LINE__, info->device_name ); + +} /* end of mgsl_wait_until_sent() */ + +/* mgsl_hangup() + * + * Called by tty_hangup() when a hangup is signaled. + * This is the same as to closing all open files for the port. + * + * Arguments: tty pointer to associated tty object + * Return Value: None + */ +static void mgsl_hangup(struct tty_struct *tty) +{ + struct mgsl_struct * info = (struct mgsl_struct *)tty->driver_data; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_hangup(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if (mgsl_paranoia_check(info, tty->device, "mgsl_hangup")) + return; + + mgsl_flush_buffer(tty); + shutdown(info); + + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + + wake_up_interruptible(&info->open_wait); + +} /* end of mgsl_hangup() */ + +/* block_til_ready() + * + * Block the current process until the specified port + * is ready to be opened. + * + * Arguments: + * + * tty pointer to tty info structure + * filp pointer to open file object + * info pointer to device instance data + * + * Return Value: 0 if success, otherwise error code + */ +static int block_til_ready(struct tty_struct *tty, struct file * filp, + struct mgsl_struct *info) +{ + struct wait_queue wait = { current, NULL }; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready on %s\n", + __FILE__,__LINE__, tty->driver.name ); + + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + /* this is a callout device */ + /* just verify that normal device is not in use */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + if (filp->f_flags & O_NONBLOCK || tty->flags & (1 << TTY_IO_ERROR)){ + /* nonblock mode is set or port is not enabled */ + /* just verify that callout device is not active */ + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * mgsl_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&info->open_wait, &wait); + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready before block on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + info->count--; + } + restore_flags(flags); + info->blocked_open++; + + while (1) { + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) { + spin_lock_irqsave(&info->irq_spinlock,flags); + info->serial_signals |= SerialSignal_RTS + SerialSignal_DTR; + usc_set_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + + current->state = TASK_INTERRUPTIBLE; + + if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)){ + retval = (info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && + (do_clocal || (info->serial_signals & SerialSignal_DCD)) ) { + break; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&info->open_wait, &wait); + + if (extra_count) + info->count++; + info->blocked_open--; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready after blocking on %s count=%d\n", + __FILE__,__LINE__, tty->driver.name, info->count ); + + if (!retval) + info->flags |= ASYNC_NORMAL_ACTIVE; + + return retval; + +} /* end of block_til_ready() */ + +/* mgsl_open() + * + * Called when a port is opened. Init and enable port. + * Perform serial-specific initialization for the tty structure. + * + * Arguments: tty pointer to tty info structure + * filp associated file pointer + * + * Return Value: 0 if success, otherwise error code + */ +static int mgsl_open(struct tty_struct *tty, struct file * filp) +{ + struct mgsl_struct *info; + int retval, line; + unsigned long page; + + /* verify range of specified line number */ + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= mgsl_device_count)) { + printk("%s(%d):mgsl_open with illegal line #%d.\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + /* find the info structure for the specified line */ + info = mgsl_device_list; + while(info && info->line != line) + info = info->next_device; + if ( !info ){ + printk("%s(%d):Can't find specified device on open (line=%d)\n", + __FILE__,__LINE__,line); + return -ENODEV; + } + + tty->driver_data = info; + info->tty = tty; + if (mgsl_paranoia_check(info, tty->device, "mgsl_open")) + return -ENODEV; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s), old ref count = %d\n", + __FILE__,__LINE__,tty->driver.name, info->count); + + MOD_INC_USE_COUNT; + + /* If port is closing, signal caller to try again */ + if (tty_hung_up_p(filp) || info->flags & ASYNC_CLOSING){ + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + retval = ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); + goto cleanup; + } + + if (!tmp_buf) { + page = get_free_page(GFP_KERNEL); + if (!page) { + retval = -ENOMEM; + goto cleanup; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + info->count++; + if (info->count == 1) { + /* 1st open on this device, init hardware */ + retval = startup(info); + if (retval < 0) + goto cleanup; + } + + retval = block_til_ready(tty, filp, info); + if (retval) { + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):block_til_ready(%s) returned %d\n", + __FILE__,__LINE__, info->device_name, retval); + goto cleanup; + } + + if ((info->count == 1) && + info->flags & ASYNC_SPLIT_TERMIOS) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + mgsl_change_params(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + + if (debug_level >= DEBUG_LEVEL_INFO) + printk("%s(%d):mgsl_open(%s) success\n", + __FILE__,__LINE__, info->device_name); + retval = 0; + +cleanup: + if (retval) { + if(MOD_IN_USE) + MOD_DEC_USE_COUNT; + if(info->count) + info->count--; + } + + return retval; + +} /* end of mgsl_open() */ + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct mgsl_struct *info) +{ + char stat_buf[30]; + int ret; + unsigned long flags; + + if (info->bus_type == MGSL_BUS_TYPE_PCI) { + ret = sprintf(buf, "%s:PCI io:%04X irq:%d mem:%08X lcr:%08X", + info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base); + } else { + ret = sprintf(buf, "%s:(E)ISA io:%04X irq:%d dma:%d", + info->device_name, info->io_base, + info->irq_level, info->dma_level); + } + + /* output current serial signal states */ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_get_serial_signals(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (info->serial_signals & SerialSignal_RTS) + strcat(stat_buf, "|RTS"); + if (info->serial_signals & SerialSignal_CTS) + strcat(stat_buf, "|CTS"); + if (info->serial_signals & SerialSignal_DTR) + strcat(stat_buf, "|DTR"); + if (info->serial_signals & SerialSignal_DSR) + strcat(stat_buf, "|DSR"); + if (info->serial_signals & SerialSignal_DCD) + strcat(stat_buf, "|CD"); + if (info->serial_signals & SerialSignal_RI) + strcat(stat_buf, "|RI"); + + if (info->params.mode == MGSL_MODE_HDLC) { + ret += sprintf(buf+ret, " HDLC txok:%d rxok:%d", + info->icount.txok, info->icount.rxok); + if (info->icount.txunder) + ret += sprintf(buf+ret, " txunder:%d", info->icount.txunder); + if (info->icount.txabort) + ret += sprintf(buf+ret, " txabort:%d", info->icount.txabort); + if (info->icount.rxshort) + ret += sprintf(buf+ret, " rxshort:%d", info->icount.rxshort); + if (info->icount.rxlong) + ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxlong); + if (info->icount.rxover) + ret += sprintf(buf+ret, " rxover:%d", info->icount.rxover); + if (info->icount.rxcrc) + ret += sprintf(buf+ret, " rxlong:%d", info->icount.rxcrc); + } else { + ret += sprintf(buf+ret, " ASYNC tx:%d rx:%d", + info->icount.tx, info->icount.rx); + if (info->icount.frame) + ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + if (info->icount.parity) + ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + if (info->icount.brk) + ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + if (info->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + } + + /* Append serial signal status to end */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + + ret += sprintf(buf+ret, "txactive=%d bh_req=%d bh_run=%d bh_q=%p\n", + info->tx_active,info->bh_requested,info->bh_running, + info->bh_queue_head); + + spin_lock_irqsave(&info->irq_spinlock,flags); + { + u16 Tscr = usc_InReg( info, TCSR ); + u16 Tdmr = usc_InDmaReg( info, TDMR ); + u16 Ticr = usc_InReg( info, TICR ); + u16 Rscr = usc_InReg( info, RCSR ); + u16 Rdmr = usc_InDmaReg( info, RDMR ); + u16 Ricr = usc_InReg( info, RICR ); + u16 Icr = usc_InReg( info, ICR ); + u16 Dccr = usc_InReg( info, DCCR ); + u16 Tmr = usc_InReg( info, TMR ); + u16 Tccr = usc_InReg( info, TCCR ); + u16 Ccar = inw( info->io_base + CCAR ); + ret += sprintf(buf+ret, "tcsr=%04X tdmr=%04X ticr=%04X rcsr=%04X rdmr=%04X\n" + "ricr=%04X icr =%04X dccr=%04X tmr=%04X tccr=%04X ccar=%04X\n", + Tscr,Tdmr,Ticr,Rscr,Rdmr,Ricr,Icr,Dccr,Tmr,Tccr,Ccar ); + } + spin_unlock_irqrestore(&info->irq_spinlock,flags); + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + ret += sprintf(buf+ret, "irq_spinlock=%08X\n", + info->irq_spinlock.lock ); +#endif + + return ret; + +} /* end of line_info() */ + +/* mgsl_read_proc() + * + * Called to print information about devices + * + * Arguments: + * page page of memory to hold returned info + * start + * off + * count + * eof + * data + * + * Return Value: + */ +int mgsl_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int len = 0, l; + off_t begin = 0; + struct mgsl_struct *info; + + len += sprintf(page, "synclink driver:%s\n", driver_version); + + info = mgsl_device_list; + while( info ) { + l = line_info(page + len, info); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + info = info->next_device; + } + + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (begin-off); + return ((count < begin+len-off) ? count : begin+len-off); + +} /* end of mgsl_read_proc() */ + +/* mgsl_allocate_dma_buffers() + * + * Allocate and format DMA buffers (ISA adapter) + * or format shared memory buffers (PCI adapter). + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +int mgsl_allocate_dma_buffers(struct mgsl_struct *info) +{ + unsigned short BuffersPerFrame; + + info->last_mem_alloc = 0; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* + * The PCI adapter has 256KBytes of shared memory to use. + * This is 64 PAGE_SIZE buffers. 1 is used for the buffer + * list. 2 are used for the transmit and one is left as + * a spare. The 4K buffer list can hold 128 DMA_BUFFER + * structures at 32bytes each. + */ + + info->rx_buffer_count = 60; + info->tx_buffer_count = 2; + } else { + /* Calculate the number of PAGE_SIZE buffers needed for */ + /* receive and transmit DMA buffers. */ + + /* Calculate the number of DMA buffers necessary to hold the */ + /* largest allowable frame size. Note: If the max frame size is */ + /* not an even multiple of the DMA buffer size then we need to */ + /* round the buffer count per frame up one. */ + + BuffersPerFrame = (unsigned short)(info->max_frame_size/DMABUFFERSIZE); + if ( info->max_frame_size % DMABUFFERSIZE ) + BuffersPerFrame++; + + /* Calculate the number of DMA buffers necessary to */ + /* hold 7 max size receive frames and one max size transmit frame. */ + /* The receive buffer count is bumped by one so we avoid an */ + /* End of List condition if all receive buffers are used when */ + /* using linked list DMA buffers. */ + + info->rx_buffer_count = (BuffersPerFrame * MAXRXFRAMES) + 6; + info->tx_buffer_count = BuffersPerFrame; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("%s(%d):Allocating %d TX and %d RX DMA buffers.\n", + __FILE__,__LINE__, info->tx_buffer_count,info->rx_buffer_count); + + if ( mgsl_alloc_buffer_list_memory( info ) < 0 || + mgsl_alloc_frame_memory(info, info->rx_buffer_list, info->rx_buffer_count) < 0 || + mgsl_alloc_frame_memory(info, info->tx_buffer_list, info->tx_buffer_count) < 0) { + printk("%s(%d):Can't allocate DMA buffer memory\n",__FILE__,__LINE__); + return -ENOMEM; + } + + mgsl_reset_rx_dma_buffers( info ); + + return 0; + +} /* end of mgsl_allocate_dma_buffers() */ + +/* + * mgsl_alloc_buffer_list_memory() + * + * Allocate a common DMA buffer for use as the + * receive and transmit buffer lists. + * + * A buffer list is a set of buffer entries where each entry contains + * a pointer to an actual buffer and a pointer to the next buffer entry + * (plus some other info about the buffer). + * + * The buffer entries for a list are built to form a circular list so + * that when the entire list has been traversed you start back at the + * beginning. + * + * This function allocates memory for just the buffer entries. + * The links (pointer to next entry) are filled in with the physical + * address of the next entry so the adapter can navigate the list + * using bus master DMA. The pointers to the actual buffers are filled + * out later when the actual buffers are allocated. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise error + */ +int mgsl_alloc_buffer_list_memory( struct mgsl_struct *info ) +{ + unsigned int i; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory. */ + info->buffer_list = info->memory_base + info->last_mem_alloc; + info->buffer_list_phys = info->last_mem_alloc; + info->last_mem_alloc += BUFFERLISTSIZE; + } else { + /* ISA adapter uses system memory. */ + /* The buffer lists are allocated as a common buffer that both */ + /* the processor and adapter can access. This allows the driver to */ + /* inspect portions of the buffer while other portions are being */ + /* updated by the adapter using Bus Master DMA. */ + + info->buffer_list = kmalloc(BUFFERLISTSIZE, GFP_KERNEL | GFP_DMA); + if ( info->buffer_list == NULL ) + return -ENOMEM; + + info->buffer_list_phys = virt_to_bus(info->buffer_list); + } + + /* We got the memory for the buffer entry lists. */ + /* Initialize the memory block to all zeros. */ + memset( info->buffer_list, 0, BUFFERLISTSIZE ); + + /* Save virtual address pointers to the receive and */ + /* transmit buffer lists. (Receive 1st). These pointers will */ + /* be used by the processor to access the lists. */ + info->rx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list = (DMABUFFERENTRY *)info->buffer_list; + info->tx_buffer_list += info->rx_buffer_count; + + /* + * Build the links for the buffer entry lists such that + * two circular lists are built. (Transmit and Receive). + * + * Note: the links are physical addresses + * which are read by the adapter to determine the next + * buffer entry to use. + */ + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->rx_buffer_list[i].phys_entry = + info->buffer_list_phys + (i * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->rx_buffer_list[i].link = info->buffer_list_phys; + + if ( i < info->rx_buffer_count - 1 ) + info->rx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + for ( i = 0; i < info->tx_buffer_count; i++ ) { + /* calculate and store physical address of this buffer entry */ + info->tx_buffer_list[i].phys_entry = info->buffer_list_phys + + ((info->rx_buffer_count + i) * sizeof(DMABUFFERENTRY)); + + /* calculate and store physical address of */ + /* next entry in cirular list of entries */ + + info->tx_buffer_list[i].link = info->buffer_list_phys + + info->rx_buffer_count * sizeof(DMABUFFERENTRY); + + if ( i < info->tx_buffer_count - 1 ) + info->tx_buffer_list[i].link += (i + 1) * sizeof(DMABUFFERENTRY); + } + + return 0; + +} /* end of mgsl_alloc_buffer_list_memory() */ + +/* + * mgsl_free_buffer_list_memory() + * + * Free the common DMA buffer allocated for use as the + * receive and transmit buffer lists. The associated Memory + * Descriptor List (MDL) is also freed. + * + * Warning: + * + * The data transfer buffers associated with the buffer list + * MUST be freed before freeing the buffer list itself because + * the buffer list contains the information necessary to free + * the individual buffers! + * + * Arguments: info pointer to device extension + * Return Value: None + */ +void mgsl_free_buffer_list_memory( struct mgsl_struct *info ) +{ + if ( info->buffer_list && info->bus_type != MGSL_BUS_TYPE_PCI ) + kfree_s(info->buffer_list, BUFFERLISTSIZE); + + info->buffer_list = NULL; + info->rx_buffer_list = NULL; + info->tx_buffer_list = NULL; + +} /* end of mgsl_free_buffer_list_memory() */ + +/* + * mgsl_alloc_frame_memory() + * + * Allocate the frame DMA buffers used by the specified buffer list. + * Each DMA buffer will be one memory page in size. This is necessary + * because memory can fragment enough that it may be impossible + * contiguous pages. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: 0 if success, otherwise -ENOMEM + */ +int mgsl_alloc_frame_memory(struct mgsl_struct *info,DMABUFFERENTRY *BufferList,int Buffercount) +{ + int i; + unsigned long phys_addr; + + /* Allocate page sized buffers for the receive buffer list */ + + for ( i = 0; i < Buffercount; i++ ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter uses shared memory buffers. */ + BufferList[i].virt_addr = info->memory_base + info->last_mem_alloc; + phys_addr = info->last_mem_alloc; + info->last_mem_alloc += DMABUFFERSIZE; + } else { + /* ISA adapter uses system memory. */ + BufferList[i].virt_addr = + kmalloc(DMABUFFERSIZE, GFP_KERNEL | GFP_DMA); + if ( BufferList[i].virt_addr == NULL ) + return -ENOMEM; + phys_addr = virt_to_bus(BufferList[i].virt_addr); + } + BufferList[i].phys_addr = phys_addr; + } + + return 0; + +} /* end of mgsl_alloc_frame_memory() */ + +/* + * mgsl_free_frame_memory() + * + * Free the buffers associated with + * each buffer entry of a buffer list. + * + * Arguments: + * + * info pointer to device instance data + * BufferList pointer to list of buffer entries + * Buffercount count of buffer entries in buffer list + * + * Return Value: None + */ +void mgsl_free_frame_memory(struct mgsl_struct *info, DMABUFFERENTRY *BufferList, int Buffercount) +{ + int i; + + if ( BufferList ) { + for ( i = 0 ; i < Buffercount ; i++ ) { + if ( BufferList[i].virt_addr ) { + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + kfree_s(BufferList[i].virt_addr, DMABUFFERSIZE); + BufferList[i].virt_addr = NULL; + } + } + } + +} /* end of mgsl_free_frame_memory() */ + +/* mgsl_free_dma_buffers() + * + * Free DMA buffers + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_free_dma_buffers( struct mgsl_struct *info ) +{ + mgsl_free_frame_memory( info, info->rx_buffer_list, info->rx_buffer_count ); + mgsl_free_frame_memory( info, info->tx_buffer_list, info->tx_buffer_count ); + mgsl_free_buffer_list_memory( info ); + +} /* end of mgsl_free_dma_buffers() */ + +/* mgsl_claim_resources() + * + * Claim all resources used by a device + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise -ENODEV + */ +int mgsl_claim_resources(struct mgsl_struct *info) +{ + /* claim 16C32 I/O base address */ + + if ( check_region(info->io_base,info->io_addr_size) < 0 ) { + printk( "%s(%d):I/O address conflict on device %s Addr=%08X\n", + __FILE__,__LINE__,info->device_name, info->io_base ); + return -ENODEV; + } + request_region(info->io_base,info->io_addr_size,"synclink.o"); + info->io_addr_requested = 1; + + /* claim interrupt level */ + + if ( request_irq(info->irq_level,mgsl_interrupt,info->irq_flags, + info->device_name, info ) < 0 ) { + printk( "%s(%d):Cant request interrupt on device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, info->irq_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + info->irq_requested = 1; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* claim shared memory range */ + info->memory_base = ioremap(info->phys_memory_base,0x40000); + if (!info->memory_base) { + printk( "%s(%d):Cant map shared memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + /* test the shared memory range */ + if ( !mgsl_memory_test(info) ) { + printk( "%s(%d):Failed shared memory test %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_memory_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + /* claim LCR memory range */ + info->lcr_base = ioremap(info->phys_lcr_base,PAGE_SIZE) + info->lcr_offset; + if (!info->lcr_base) { + printk( "%s(%d):Cant map LCR memory on device %s MemAddr=%08X\n", + __FILE__,__LINE__,info->device_name, info->phys_lcr_base ); + mgsl_release_resources( info ); + return -ENODEV; + } + + } else { + /* claim DMA channel */ + + if (request_dma(info->dma_level,info->device_name) < 0){ + printk( "%s(%d):Cant request DMA channel on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + info->dma_requested = 1; + + /* ISA adapter uses bus master DMA */ + set_dma_mode(info->dma_level,DMA_MODE_CASCADE); + enable_dma(info->dma_level); + } + + if ( mgsl_allocate_dma_buffers(info) < 0 ) { + printk( "%s(%d):Cant allocate DMA buffers on device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, info->dma_level ); + mgsl_release_resources( info ); + return -ENODEV; + } + + return 0; + +} /* end of mgsl_claim_resources() */ + +/* mgsl_release_resources() + * + * Release all resources used by a device + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_release_resources(struct mgsl_struct *info) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) entry\n", + __FILE__,__LINE__,info->device_name ); + + if ( info->irq_requested ) { + free_irq(info->irq_level, info); + info->irq_requested = 0; + } + + if ( info->dma_requested ) { + disable_dma(info->dma_level); + free_dma(info->dma_level); + info->dma_requested = 0; + } + mgsl_free_dma_buffers(info); + + if ( info->io_addr_requested ) { + release_region(info->io_base,info->io_addr_size); + info->io_addr_requested = 0; + } + + if (info->memory_base){ + iounmap(info->memory_base); + info->memory_base = 0; + } + + if (info->lcr_base){ + iounmap(info->lcr_base - info->lcr_offset); + info->lcr_base = 0; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_release_resources(%s) exit\n", + __FILE__,__LINE__,info->device_name ); + +} /* end of mgsl_release_resources() */ + +/* mgsl_add_device() + * + * Add the specified device instance data structure to the + * global linked list of devices and increment the device count. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_add_device( struct mgsl_struct *info ) +{ + info->next_device = NULL; + info->line = mgsl_device_count; + sprintf(info->device_name,"ttySL%d",info->line); + + mgsl_device_count++; + + if ( !mgsl_device_list ) + mgsl_device_list = info; + else { + struct mgsl_struct *current_dev = mgsl_device_list; + while( current_dev->next_device ) + current_dev = current_dev->next_device; + current_dev->next_device = info; + } + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + printk( "SyncLink device %s added:PCI bus IO=%04X IRQ=%d Mem=%08X LCR=%08X\n", + info->device_name, info->io_base, info->irq_level, + info->phys_memory_base, info->phys_lcr_base ); + } else { + printk( "SyncLink device %s added:ISA bus IO=%04X IRQ=%d DMA=%d\n", + info->device_name, info->io_base, info->irq_level, info->dma_level ); + } + +} /* end of mgsl_add_device() */ + +/* mgsl_allocate_device() + * + * Allocate and initialize a device instance structure + * + * Arguments: None + * Return Value: pointer to mgsl_struct if success, otherwise NULL + */ +struct mgsl_struct* mgsl_allocate_device() +{ + struct mgsl_struct *info; + + info = (struct mgsl_struct *)kmalloc(sizeof(struct mgsl_struct), + GFP_KERNEL); + + if (!info) { + printk("Error can't allocate device instance data\n"); + } else { + memset(info, 0, sizeof(struct mgsl_struct)); + info->magic = MGSL_MAGIC; + info->task.sync = 0; + info->task.routine = mgsl_bh_handler; + info->task.data = info; + info->max_frame_size = 4096; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + info->idle_mode = HDLC_TXIDLE_FLAGS; + } + + return info; + +} /* end of mgsl_allocate_device()*/ + +/* mgsl_enumerate_devices() + * + * Enumerate SyncLink serial devices based on user specified + * options for ISA adapters and autodetected PCI adapters. + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int mgsl_enumerate_devices() +{ + struct mgsl_struct *info; + int i; + + /* Check for user specified ISA devices */ + + for (i=0 ;(i < MAX_ISA_DEVICES) && io[i] && irq[i]; i++){ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk("ISA device specified io=%04X,irq=%d,dma=%d\n", + io[i], irq[i], dma[i] ); + + info = mgsl_allocate_device(); + if ( !info ) { + /* error allocating device instance data */ + if ( debug_level >= DEBUG_LEVEL_ERROR ) + printk( "can't allocate device instance data.\n"); + continue; + } + + /* Copy user configuration info to device instance data */ + info->io_base = (unsigned int)io[i]; + info->irq_level = (unsigned int)irq[i]; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->irq_level = irq_cannonicalize(info->irq_level); +#else + if (info->irq_level == 2) + info->irq_level = 9; +#endif + info->dma_level = (unsigned int)dma[i]; + info->bus_type = MGSL_BUS_TYPE_ISA; + info->io_addr_size = 16; + info->irq_flags = 0; + + /* add new device to device list */ + mgsl_add_device( info ); + } + + +#ifdef CONFIG_PCI + /* Auto detect PCI adapters */ + + if ( pcibios_present() ) { + unsigned char bus; + unsigned char func; + unsigned int shared_mem_base; + unsigned int lcr_mem_base; + unsigned int io_base; + unsigned char irq_line; + + for(i=0;;i++){ + if ( PCIBIOS_SUCCESSFUL == pcibios_find_device( + MICROGATE_VENDOR_ID, SYNCLINK_DEVICE_ID, i, &bus, &func) ) { + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_3,&shared_mem_base) ) { + printk( "%s(%d):Shared mem addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_0,&lcr_mem_base) ) { + printk( "%s(%d):LCR mem addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_dword(bus,func, + PCI_BASE_ADDRESS_2,&io_base) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } + + if (pcibios_read_config_byte(bus,func, + PCI_INTERRUPT_LINE,&irq_line) ) { + printk( "%s(%d):USC I/O addr not set.\n", + __FILE__,__LINE__); + continue; + } + + info = mgsl_allocate_device(); + if ( !info ) { + /* error allocating device instance data */ + if ( debug_level >= DEBUG_LEVEL_ERROR ) + printk( "can't allocate device instance data.\n"); + continue; + } + + /* Copy user configuration info to device instance data */ + + info->io_base = io_base & PCI_BASE_ADDRESS_IO_MASK; + info->irq_level = (unsigned int)irq_line; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + info->irq_level = irq_cannonicalize(info->irq_level); +#else + if (info->irq_level == 2) + info->irq_level = 9; +#endif + info->phys_memory_base = shared_mem_base & PCI_BASE_ADDRESS_MEM_MASK; + + /* Because veremap only works on page boundaries we must map + * a larger area than is actually implemented for the LCR + * memory range. We map a full page starting at the page boundary. + */ + info->phys_lcr_base = lcr_mem_base & PCI_BASE_ADDRESS_MEM_MASK; + info->lcr_offset = info->phys_lcr_base & (PAGE_SIZE-1); + info->phys_lcr_base &= ~(PAGE_SIZE-1); + + info->bus_type = MGSL_BUS_TYPE_PCI; + info->io_addr_size = 8; + info->irq_flags = SA_SHIRQ; + info->bus = bus; + info->function = func; + + /* Store the PCI9050 misc control register value because a flaw + * in the PCI9050 prevents LCR registers from being read if + * BIOS assigns an LCR base address with bit 7 set. + * + * Only the misc control register is accessed for which only + * write access is needed, so set an initial value and change + * bits to the device instance data as we write the value + * to the actual misc control register. + */ + info->misc_ctrl_value = 0x087e4546; + + /* add new device to device list */ + mgsl_add_device( info ); + } else { + break; + } + } + } +#endif + + /* + * Allocate memory to hold the following tty/termios arrays + * with an element for each enumerated device. + */ + + serial_table = (struct tty_struct**)kmalloc(sizeof(struct tty_struct*)*mgsl_device_count, GFP_KERNEL); + serial_termios = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL); + serial_termios_locked = (struct termios**)kmalloc(sizeof(struct termios*)*mgsl_device_count, GFP_KERNEL); + + if (!serial_table || !serial_termios || !serial_termios_locked){ + printk("%s(%d):Can't allocate tty/termios arrays.\n", + __FILE__,__LINE__); + return -ENOMEM; + } + + memset(serial_table,0,sizeof(struct tty_struct*)*mgsl_device_count); + memset(serial_termios,0,sizeof(struct termios*)*mgsl_device_count); + memset(serial_termios_locked,0,sizeof(struct termios*)*mgsl_device_count); + + return 0; + +} /* end of mgsl_enumerate_devices() */ + +/* mgsl_init() + * + * Driver initialization entry point. + * + * Arguments: None + * Return Value: 0 if success, otherwise error code + */ +int __init mgsl_init(void) +{ + struct mgsl_struct *info; + +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + EXPORT_NO_SYMBOLS; +#else + register_symtab(NULL); +#endif + + printk("%s version %s\n", driver_name, driver_version); + + /* determine how many SyncLink devices are installed */ + mgsl_enumerate_devices(); + if ( !mgsl_device_list ) { + printk("%s(%d):No SyncLink devices found.\n",__FILE__,__LINE__); + return -ENODEV; + } + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + serial_driver.driver_name = "synclink"; +#endif + serial_driver.name = "ttySL"; + serial_driver.major = ttymajor; + serial_driver.minor_start = 64; + serial_driver.num = mgsl_device_count; + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = mgsl_open; + serial_driver.close = mgsl_close; + serial_driver.write = mgsl_write; + serial_driver.put_char = mgsl_put_char; + serial_driver.flush_chars = mgsl_flush_chars; + serial_driver.write_room = mgsl_write_room; + serial_driver.chars_in_buffer = mgsl_chars_in_buffer; + serial_driver.flush_buffer = mgsl_flush_buffer; + serial_driver.ioctl = mgsl_ioctl; + serial_driver.throttle = mgsl_throttle; + serial_driver.unthrottle = mgsl_unthrottle; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + serial_driver.send_xchar = mgsl_send_xchar; + serial_driver.break_ctl = mgsl_break; + serial_driver.wait_until_sent = mgsl_wait_until_sent; + serial_driver.read_proc = mgsl_read_proc; +#endif + serial_driver.set_termios = mgsl_set_termios; + serial_driver.stop = mgsl_stop; + serial_driver.start = mgsl_start; + serial_driver.hangup = mgsl_hangup; + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cuaSL"; + callout_driver.major = cuamajor; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if LINUX_VERSION_CODE >= VERSION(2,1,0) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + if (tty_register_driver(&serial_driver) < 0) + printk("%s(%d):Couldn't register serial driver\n", + __FILE__,__LINE__); + + if (tty_register_driver(&callout_driver) < 0) + printk("%s(%d):Couldn't register callout driver\n", + __FILE__,__LINE__); + + printk("%s version %s, tty major#%d callout major#%d\n", + driver_name, driver_version, + serial_driver.major, callout_driver.major); + + /* Propagate these values to all device instances */ + + info = mgsl_device_list; + while(info){ + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + info = info->next_device; + } + + return 0; + +} /* end of mgsl_init() */ + +#ifdef MODULE +int init_module(void) +{ +/* Uncomment this to kernel debug module. + * mgsl_get_text_ptr() leaves the .text address in eax + * which can be used with add-symbol-file with gdb. + */ + if (break_on_load) { + mgsl_get_text_ptr(); + BREAKPOINT(); + } + + return mgsl_init(); +} + +void cleanup_module(void) +{ + unsigned long flags; + int rc; + struct mgsl_struct *info; + + printk("Unloading %s: version %s\n", driver_name, driver_version); + save_flags(flags); + cli(); + if ((rc = tty_unregister_driver(&serial_driver))) + printk("%s(%d) failed to unregister tty driver err=%d\n", + __FILE__,__LINE__,rc); + if ((rc = tty_unregister_driver(&callout_driver))) + printk("%s(%d) failed to unregister callout driver err=%d\n", + __FILE__,__LINE__,rc); + restore_flags(flags); + + info = mgsl_device_list; + while(info) { + mgsl_release_resources(info); + info = info->next_device; + } + + if (tmp_buf) { + free_page((unsigned long) tmp_buf); + tmp_buf = NULL; + } + + if (serial_table) + kfree_s(serial_table,sizeof(struct tty_struct*)*mgsl_device_count); + + if (serial_termios) + kfree_s(serial_termios,sizeof(struct termios*)*mgsl_device_count); + + if (serial_termios_locked) + kfree_s(serial_termios_locked,sizeof(struct termios*)*mgsl_device_count); + +} /* end of cleanup_module() */ + +#endif /* MODULE */ + + +/* + * usc_RTCmd() + * + * Issue a USC Receive/Transmit command to the + * Channel Command/Address Register (CCAR). + * + * Notes: + * + * The command is encoded in the most significant 5 bits <15..11> + * of the CCAR value. Bits <10..7> of the CCAR must be preserved + * and Bits <6..0> must be written as zeros. + * + * Arguments: + * + * info pointer to device information structure + * Cmd command mask (use symbolic macros) + * + * Return Value: + * + * None + */ +void usc_RTCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* output command to CCAR in bits <15..11> */ + /* preserve bits <10..7>, bits <6..0> must be zero */ + + outw( Cmd + info->loopback_bits, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_RTCmd() */ + +/* + * usc_DmaCmd() + * + * Issue a DMA command to the DMA Command/Address Register (DCAR). + * + * Arguments: + * + * info pointer to device information structure + * Cmd DMA command mask (usc_DmaCmd_XX Macros) + * + * Return Value: + * + * None + */ +void usc_DmaCmd( struct mgsl_struct *info, u16 Cmd ) +{ + /* write command mask to DCAR */ + outw( Cmd + info->mbre_bit, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_DmaCmd() */ + +/* + * usc_OutDmaReg() + * + * Write a 16-bit value to a USC DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) for write + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +void usc_OutDmaReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + outw( RegValue, info->io_base ); + + /* Read to flush write to DCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base ); + +} /* end of usc_OutDmaReg() */ + +/* + * usc_InDmaReg() + * + * Read a 16-bit value from a DMA register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to read from + * + * Return Value: + * + * The 16-bit value read from register + * + */ +u16 usc_InDmaReg( struct mgsl_struct *info, u16 RegAddr ) +{ + /* Note: The DCAR is located at the adapter base address */ + /* Note: must preserve state of BIT8 in DCAR */ + + outw( RegAddr + info->mbre_bit, info->io_base ); + return inw( info->io_base ); + +} /* end of usc_InDmaReg() */ + +/* + * + * usc_OutReg() + * + * Write a 16-bit value to a USC serial channel register + * + * Arguments: + * + * info pointer to device info structure + * RegAddr register address (number) to write to + * RegValue 16-bit value to write to register + * + * Return Value: + * + * None + * + */ +void usc_OutReg( struct mgsl_struct *info, u16 RegAddr, u16 RegValue ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + outw( RegValue, info->io_base + CCAR ); + + /* Read to flush write to CCAR */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + inw( info->io_base + CCAR ); + +} /* end of usc_OutReg() */ + +/* + * usc_InReg() + * + * Reads a 16-bit value from a USC serial channel register + * + * Arguments: + * + * info pointer to device extension + * RegAddr register address (number) to read from + * + * Return Value: + * + * 16-bit value read from register + */ +u16 usc_InReg( struct mgsl_struct *info, u16 RegAddr ) +{ + outw( RegAddr + info->loopback_bits, info->io_base + CCAR ); + return inw( info->io_base + CCAR ); + +} /* end of usc_InReg() */ + +/* usc_set_sdlc_mode() + * + * Set up the adapter for SDLC DMA communications. + * + * Arguments: info pointer to device instance data + * Return Value: NONE + */ +void usc_set_sdlc_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, Underrun Action + * <13> 0 1 = Send Preamble before opening flag + * <12> 0 1 = Consecutive Idles share common 0 + * <11..8> 0110 Transmitter mode = HDLC/SDLC + * <7..4> 0000 Rx Sub modes, addr/ctrl field handling + * <3..0> 0110 Receiver mode = HDLC/SDLC + * + * 0000 0110 0000 0110 = 0x0606 + */ + + RegValue = 0x0606; + + if ( info->params.flags & HDLC_FLAG_UNDERRUN_ABORT15 ) + RegValue |= BIT14; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_FLAG ) + RegValue |= BIT15; + else if ( info->params.flags & HDLC_FLAG_UNDERRUN_CRC ) + RegValue |= BIT15 + BIT14; + + if ( info->params.preamble != HDLC_PREAMBLE_PATTERN_NONE ) + RegValue |= BIT13; + + if ( info->params.flags & HDLC_FLAG_SHARE_ZERO ) + RegValue |= BIT12; + + if ( info->params.addr_filter != 0xff ) + { + /* set up receive address filtering */ + usc_OutReg( info, RSR, info->params.addr_filter ); + RegValue |= BIT4; + } + + usc_OutReg( info, CMR, RegValue ); + info->cmr_value = RegValue; + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Set CRC to all 1s (use for SDLC/HDLC) + * <9> 0 1 = Include Receive chars in CRC + * <8> 1 1 = Use Abort/PE bit as abort indicator + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0101 0000 0000 = 0x0500 + */ + + RegValue = 0x0500; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + RegValue |= BIT9; + + usc_OutReg( info, RMR, RegValue ); + + + + /* Set the Receive count Limit Register (RCLR) to 0xffff. */ + /* When an opening flag of an SDLC frame is recognized the */ + /* Receive Character count (RCC) is loaded with the value in */ + /* RCLR. The RCC is decremented for each received byte. The */ + /* value of RCC is stored after the closing flag of the frame */ + /* allowing the frame size to be computed. */ + + usc_OutReg( info, RCLR, RCLRVALUE ); + + usc_RCmd( info, RCmd_SelectRicrdma_level ); + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO DMA Request Level + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 1 Queued status reflects oldest 2 bytes in FIFO + * <2> 0 Abort/PE IA + * <1> 1 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0000 1000 = 0x000a + */ + + /* Carry over the Exit Hunt and Idle Received bits */ + /* in case they have been armed by usc_ArmEvents. */ + + RegValue = usc_InReg( info, RICR ) & 0xc0; + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, RICR, (u16)(0x030a | RegValue) ); + else + usc_OutReg( info, RICR, (u16)(0x140a | RegValue) ); + + /* Unlatch all Rx status bits and clear Rx status IRQ Pending */ + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding + * <12..11> 00 FCS = 16bit CRC CCITT (x15 + x12 + x5 + 1) + * <10> 1 1 = Start CRC as all 1s (use for SDLC/HDLC) + * <9> 0 1 = Tx CRC Enabled + * <8> 0 1 = Append CRC to end of transmit frame + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0100 0000 0000 = 0x0400 + */ + + RegValue = 0x0400; + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZB: RegValue |= BIT13; break; + case HDLC_ENCODING_NRZI_MARK: RegValue |= BIT14; break; + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT14 + BIT13; break; + case HDLC_ENCODING_BIPHASE_MARK: RegValue |= BIT15; break; + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT15 + BIT13; break; + case HDLC_ENCODING_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14; break; + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT15 + BIT14 + BIT13; break; + } + + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + RegValue |= BIT9 + BIT8; + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + usc_TCmd( info, TCmd_SelectTicrdma_level ); + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO DMA Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 0 Idle Sent IA + * <5> 1 Abort Sent IA + * <4> 1 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 1 1 = Wait for SW Trigger to Start Frame + * <1> 1 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0011 0110 = 0x0036 + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TICR, 0x0736 ); + else + usc_OutReg( info, TICR, 0x1436 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Source = Disabled + * <13..12> 00 counter 0 Source = Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> XXX TxCLK comes from Port 0 + * <2..0> XXX RxCLK comes from Port 1 + * + * 0000 1111 0111 0111 = 0x0f77 + */ + + RegValue = 0x0f40; + + if ( info->params.flags & HDLC_FLAG_RXC_DPLL ) + RegValue |= 0x0003; /* RxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_RXC_BRG ) + RegValue |= 0x0004; /* RxCLK from BRG0 */ + else + RegValue |= 0x0007; /* RxCLK from Port1 */ + + if ( info->params.flags & HDLC_FLAG_TXC_DPLL ) + RegValue |= 0x0018; /* TxCLK from DPLL */ + else if ( info->params.flags & HDLC_FLAG_TXC_BRG ) + RegValue |= 0x0020; /* TxCLK from BRG0 */ + else + RegValue |= 0x0030; /* TxCLK from Port0 */ + + usc_OutReg( info, CMCR, RegValue ); + + + /* Hardware Configuration Register (HCR) + * + * <15..14> 00 CTR0 Divisor:00=32,01=16,10=8,11=4 + * <13> 0 CTR1DSel:0=CTR0Div determines CTR0Div + * <12> 0 CVOK:0=report code violation in biphase + * <11..10> 00 DPLL Divisor:00=32,01=16,10=8,11=4 + * <9..8> XX DPLL mode:00=disable,01=NRZ,10=Biphase,11=Biphase Level + * <7..6> 00 reserved + * <5> 0 BRG1 mode:0=continuous,1=single cycle + * <4> X BRG1 Enable + * <3..2> 00 reserved + * <1> 0 BRG0 mode:0=continuous,1=single cycle + * <0> 0 BRG0 Enable + */ + + RegValue = 0x0000; + + if ( info->params.flags & (HDLC_FLAG_RXC_DPLL + HDLC_FLAG_TXC_DPLL) ) { + u32 XtalSpeed; + u32 DpllDivisor; + u16 Tc; + + /* DPLL is enabled. Use BRG1 to provide continuous reference clock */ + /* for DPLL. DPLL mode in HCR is dependent on the encoding used. */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + if ( info->params.flags & HDLC_FLAG_DPLL_DIV16 ) { + DpllDivisor = 16; + RegValue |= BIT10; + } + else if ( info->params.flags & HDLC_FLAG_DPLL_DIV8 ) { + DpllDivisor = 8; + RegValue |= BIT11; + } + else + DpllDivisor = 32; + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + Tc = (u16)((XtalSpeed/DpllDivisor)/info->params.clock_speed); + if ( !((((XtalSpeed/DpllDivisor) % info->params.clock_speed) * 2) + / info->params.clock_speed) ) + Tc--; + + /* Write 16-bit Time Constant for BRG1 */ + usc_OutReg( info, TC1R, Tc ); + + RegValue |= BIT4; /* enable BRG1 */ + + switch ( info->params.encoding ) { + case HDLC_ENCODING_NRZ: + case HDLC_ENCODING_NRZB: + case HDLC_ENCODING_NRZI_MARK: + case HDLC_ENCODING_NRZI_SPACE: RegValue |= BIT8; break; + case HDLC_ENCODING_BIPHASE_MARK: + case HDLC_ENCODING_BIPHASE_SPACE: RegValue |= BIT9; break; + case HDLC_ENCODING_BIPHASE_LEVEL: + case HDLC_ENCODING_DIFF_BIPHASE_LEVEL: RegValue |= BIT9 + BIT8; break; + } + } + + usc_OutReg( info, HCR, RegValue ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL Sync (RW) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x1020 ); + + + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) { + usc_OutReg( info, SICR, + (u16)(usc_InReg(info,SICR) | SICR_CTS_INACTIVE) ); + } + + + /* enable Master Interrupt Enable bit (MIE) */ + usc_EnableMasterIrqBit( info ); + + usc_ClearIrqPendingBits( info, RECEIVE_STATUS + RECEIVE_DATA + + TRANSMIT_STATUS + TRANSMIT_DATA ); + + info->mbre_bit = 0; + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + info->mbre_bit = BIT8; + outw( BIT8, info->io_base ); /* set Master Bus Enable (DCAR) */ + + /* Enable DMAEN (Port 7, Bit 14) */ + /* This connects the DMA request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT15) & ~BIT14) ); + + /* DMA Control Register (DCR) + * + * <15..14> 10 Priority mode = Alternating Tx/Rx + * 01 Rx has priority + * 00 Tx has priority + * + * <13> 1 Enable Priority Preempt per DCR<15..14> + * (WARNING DCR<11..10> must be 00 when this is 1) + * 0 Choose activate channel per DCR<11..10> + * + * <12> 0 Little Endian for Array/List + * <11..10> 00 Both Channels can use each bus grant + * <9..6> 0000 reserved + * <5> 0 7 CLK - Minimum Bus Re-request Interval + * <4> 0 1 = drive D/C and S/D pins + * <3> 1 1 = Add one wait state to all DMA cycles. + * <2> 0 1 = Strobe /UAS on every transfer. + * <1..0> 11 Addr incrementing only affects LS24 bits + * + * 0110 0000 0000 1011 = 0x600b + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* PCI adapter does not need DMA wait state */ + usc_OutDmaReg( info, DCR, 0xa00b ); + } + else + usc_OutDmaReg( info, DCR, 0x800b ); + + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in Arrary/List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, RDMR, 0xf200 ); + + + /* Transmit DMA mode Register (TDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 TCBinA/L = fetch Tx Control Block from List entry + * <12> 1 Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on end of frame + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (Read Only so write as 0) + * + * 1111 0010 0000 0000 = 0xf200 + */ + + usc_OutDmaReg( info, TDMR, 0xf200 ); + + + /* DMA Interrupt Control Register (DICR) + * + * <15> 1 DMA Interrupt Enable + * <14> 0 1 = Disable IEO from USC + * <13> 0 1 = Don't provide vector during IntAck + * <12> 1 1 = Include status in Vector + * <10..2> 0 reserved, Must be 0s + * <1> 0 1 = Rx DMA Interrupt Enabled + * <0> 0 1 = Tx DMA Interrupt Enabled + * + * 1001 0000 0000 0000 = 0x9000 + */ + + usc_OutDmaReg( info, DICR, 0x9000 ); + + usc_InDmaReg( info, RDMR ); /* clear pending receive DMA IRQ bits */ + usc_InDmaReg( info, TDMR ); /* clear pending transmit DMA IRQ bits */ + usc_OutDmaReg( info, CDIR, 0x0303 ); /* clear IUS and Pending for Tx and Rx */ + + /* Channel Control Register (CCR) + * + * <15..14> 10 Use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length + * <9..8> 00 Preamble Pattern + * <7..6> 10 Use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 1000 0000 1000 0000 = 0x8080 + */ + + RegValue = 0x8080; + + switch ( info->params.preamble_length ) { + case HDLC_PREAMBLE_LENGTH_16BITS: RegValue |= BIT10; break; + case HDLC_PREAMBLE_LENGTH_32BITS: RegValue |= BIT11; break; + case HDLC_PREAMBLE_LENGTH_64BITS: RegValue |= BIT11 + BIT10; break; + } + + switch ( info->params.preamble ) { + case HDLC_PREAMBLE_PATTERN_FLAGS: RegValue |= BIT8 + BIT12; break; + case HDLC_PREAMBLE_PATTERN_ONES: RegValue |= BIT8; break; + case HDLC_PREAMBLE_PATTERN_10: RegValue |= BIT9; break; + case HDLC_PREAMBLE_PATTERN_01: RegValue |= BIT9 + BIT8; break; + } + + usc_OutReg( info, CCR, RegValue ); + + + /* + * Burst/Dwell Control Register + * + * <15..8> 0x20 Maximum number of transfers per bus grant + * <7..0> 0x00 Maximum number of clock cycles per bus grant + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + /* don't limit bus occupancy on PCI adapter */ + usc_OutDmaReg( info, BDCR, 0x0000 ); + } + else + usc_OutDmaReg( info, BDCR, 0x2000 ); + + usc_stop_transmitter(info); + usc_stop_receiver(info); + +} /* end of usc_set_sdlc_mode() */ + +/* usc_enable_loopback() + * + * Set the 16C32 for internal loopback mode. + * The TxCLK and RxCLK signals are generated from the BRG0 and + * the TxD is looped back to the RxD internally. + * + * Arguments: info pointer to device instance data + * enable 1 = enable loopback, 0 = disable + * Return Value: None + */ +void usc_enable_loopback(struct mgsl_struct *info, int enable) +{ + if (enable) { + /* blank external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) | (BIT7+BIT6)); + + /* Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + /* Write 16-bit Time Constant for BRG0 */ + /* use clock speed if available, otherwise use 8 for diagnostics */ + if (info->params.clock_speed) { + if (info->bus_type == MGSL_BUS_TYPE_PCI) + usc_OutReg(info, TC0R, (u16)((11059200/info->params.clock_speed)-1)); + else + usc_OutReg(info, TC0R, (u16)((14745600/info->params.clock_speed)-1)); + } else + usc_OutReg(info, TC0R, (u16)8); + + /* Hardware Configuration Register (HCR) Clear Bit 1, BRG0 + mode = Continuous Set Bit 0 to enable BRG0. */ + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg(info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004)); + + /* set Internal Data loopback mode */ + info->loopback_bits = 0x300; + outw( 0x0300, info->io_base + CCAR ); + } else { + /* enable external TXD output */ + usc_OutReg(info,IOCR,usc_InReg(info,IOCR) & ~(BIT7+BIT6)); + + /* clear Internal Data loopback mode */ + info->loopback_bits = 0; + outw( 0,info->io_base + CCAR ); + } + +} /* end of usc_enable_loopback() */ + +/* usc_enable_aux_clock() + * + * Enabled the AUX clock output at the specified frequency. + * + * Arguments: + * + * info pointer to device extension + * data_rate data rate of clock in bits per second + * A data rate of 0 disables the AUX clock. + * + * Return Value: None + */ +void usc_enable_aux_clock( struct mgsl_struct *info, u32 data_rate ) +{ + u32 XtalSpeed; + u16 Tc; + + if ( data_rate ) { + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + XtalSpeed = 11059200; + else + XtalSpeed = 14745600; + + + /* Tc = (Xtal/Speed) - 1 */ + /* If twice the remainder of (Xtal/Speed) is greater than Speed */ + /* then rounding up gives a more precise time constant. Instead */ + /* of rounding up and then subtracting 1 we just don't subtract */ + /* the one in this case. */ + + + Tc = (u16)(XtalSpeed/data_rate); + if ( !(((XtalSpeed % data_rate) * 2) / data_rate) ) + Tc--; + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, Tc ); + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + usc_OutReg( info, IOCR, (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_aux_clock() */ + +/* usc_stop_receiver() + * + * Disable USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_stop_receiver( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + /* Disable receive DMA channel. */ + /* This also disables receive DMA channel interrupts */ + usc_DmaCmd( info, DmaCmd_ResetRxChannel ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_DisableInterrupts( info, RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableReceiver(info,DISABLE_UNCONDITIONAL); + + /* This empties the receive FIFO and loads the RCC with RCLR */ + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + info->rx_enabled = 0; + info->rx_overflow = 0; + +} /* end of stop_receiver() */ + +/* usc_start_receiver() + * + * Enable the USC receiver + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_start_receiver( struct mgsl_struct *info ) +{ + u32 phys_addr; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_receiver(%s)\n", + __FILE__,__LINE__, info->device_name ); + + mgsl_reset_rx_dma_buffers( info ); + usc_stop_receiver( info ); + + usc_OutReg( info, CCSR, (u16)(usc_InReg(info,CCSR) | BIT13) ); + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + if ( info->params.mode == MGSL_MODE_HDLC ) { + /* DMA mode Transfers */ + /* Program the DMA controller. */ + /* Enable the DMA controller end of buffer interrupt. */ + + /* program 16C32 with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (u16)phys_addr ); + usc_OutDmaReg( info, NRARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_DATA + RECEIVE_STATUS ); + usc_EnableInterrupts( info, RECEIVE_STATUS ); + + /* 1. Arm End of Buffer (EOB) Receive DMA Interrupt (BIT2 of RDIAR) */ + /* 2. Enable Receive DMA Interrupts (BIT1 of DICR) */ + + usc_OutDmaReg( info, RDIAR, BIT3 + BIT2 ); + usc_OutDmaReg( info, DICR, (u16)(usc_InDmaReg(info,DICR) | BIT1) ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + if ( info->params.flags & HDLC_FLAG_AUTO_DCD ) + usc_EnableReceiver(info,ENABLE_AUTO_DCD); + else + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } else { + usc_UnlatchRxstatusBits(info, RXSTATUS_ALL); + usc_ClearIrqPendingBits(info, RECEIVE_DATA + RECEIVE_STATUS); + usc_EnableInterrupts(info, RECEIVE_DATA); + + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_RCmd( info, RCmd_EnterHuntmode ); + + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + } + + usc_OutReg( info, CCSR, 0x1020 ); + + info->rx_enabled = 1; + +} /* end of usc_start_receiver() */ + +/* usc_start_transmitter() + * + * Enable the USC transmitter and send a transmit frame if + * one is loaded in the DMA buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_start_transmitter( struct mgsl_struct *info ) +{ + u32 phys_addr; + unsigned int FrameSize; + + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_start_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + if ( info->xmit_cnt ) { + + /* If auto RTS enabled and RTS is inactive, then assert */ + /* RTS and set a flag indicating that the driver should */ + /* negate RTS when the transmission completes. */ + + info->drop_rts_on_tx_done = 0; + + if ( info->params.flags & HDLC_FLAG_AUTO_RTS ) { + usc_get_serial_signals( info ); + if ( !(info->serial_signals & SerialSignal_RTS) ) { + info->serial_signals |= SerialSignal_RTS; + usc_set_serial_signals( info ); + info->drop_rts_on_tx_done = 1; + } + } + + + if ( info->params.mode == MGSL_MODE_ASYNC ) { + if ( !info->tx_active ) { + usc_UnlatchTxstatusBits(info, TXSTATUS_ALL); + usc_ClearIrqPendingBits(info, TRANSMIT_STATUS + TRANSMIT_DATA); + usc_EnableInterrupts(info, TRANSMIT_DATA); + usc_load_txfifo(info); + } + } else { + /* Disable transmit DMA controller while programming. */ + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + + /* Transmit DMA buffer is loaded, so program USC */ + /* to send the frame contained in the buffers. */ + + + FrameSize = info->tx_buffer_list[0].rcc; + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, (u16)FrameSize ); + + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + phys_addr = info->tx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NTARL, (u16)phys_addr ); + usc_OutDmaReg( info, NTARU, (u16)(phys_addr >> 16) ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + usc_EnableInterrupts( info, TRANSMIT_STATUS ); + + /* Initialize Transmit DMA Channel */ + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + usc_TCmd( info, TCmd_SendFrame ); + + info->tx_timer.expires = jiffies + jiffies_from_ms(5000); + add_timer(&info->tx_timer); + } + info->tx_active = 1; + } + + if ( !info->tx_enabled ) { + info->tx_enabled = 1; + if ( info->params.flags & HDLC_FLAG_AUTO_CTS ) + usc_EnableTransmitter(info,ENABLE_AUTO_CTS); + else + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + } + +} /* end of usc_start_transmitter() */ + +/* usc_stop_transmitter() + * + * Stops the transmitter and DMA + * + * Arguments: info pointer to device isntance data + * Return Value: None + */ +void usc_stop_transmitter( struct mgsl_struct *info ) +{ + if (debug_level >= DEBUG_LEVEL_ISR) + printk("%s(%d):usc_stop_transmitter(%s)\n", + __FILE__,__LINE__, info->device_name ); + + del_timer(&info->tx_timer); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA ); + + usc_EnableTransmitter(info,DISABLE_UNCONDITIONAL); + usc_DmaCmd( info, DmaCmd_ResetTxChannel ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + info->tx_enabled = 0; + info->tx_active = 0; + +} /* end of usc_stop_transmitter() */ + +/* usc_load_txfifo() + * + * Fill the transmit FIFO until the FIFO is full or + * there is no more data to load. + * + * Arguments: info pointer to device extension (instance data) + * Return Value: None + */ +void usc_load_txfifo( struct mgsl_struct *info ) +{ + int Fifocount; + u8 TwoBytes[2]; + + if ( !info->xmit_cnt && !info->x_char ) + return; + + /* Select transmit FIFO status readback in TICR */ + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + /* load the Transmit FIFO until FIFOs full or all data sent */ + + while( (Fifocount = usc_InReg(info, TICR) >> 8) && info->xmit_cnt ) { + /* there is more space in the transmit FIFO and */ + /* there is more data in transmit buffer */ + + if ( (info->xmit_cnt > 1) && (Fifocount > 1) && !info->x_char ) { + /* write a 16-bit word from transmit buffer to 16C32 */ + + TwoBytes[0] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + TwoBytes[1] = info->xmit_buf[info->xmit_tail++]; + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + + outw( *((u16 *)TwoBytes), info->io_base + DATAREG); + + info->xmit_cnt -= 2; + info->icount.tx += 2; + } else { + /* only 1 byte left to transmit or 1 FIFO slot left */ + + outw( (inw( info->io_base + CCAR) & 0x0780) | (TDR+LSBONLY), + info->io_base + CCAR ); + + if (info->x_char) { + /* transmit pending high priority char */ + outw( info->x_char,info->io_base + CCAR ); + info->x_char = 0; + } else { + outw( info->xmit_buf[info->xmit_tail++],info->io_base + CCAR ); + info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); + info->xmit_cnt--; + } + info->icount.tx++; + } + } + +} /* end of usc_load_txfifo() */ + +/* usc_reset() + * + * Reset the adapter to a known state and prepare it for further use. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_reset( struct mgsl_struct *info ) +{ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) { + int i; + volatile u32 readval; + + /* Set BIT30 of Misc Control Register */ + /* (Local Control Register 0x50) to force reset of USC. */ + + u32 *MiscCtrl = (u32 *)(info->lcr_base + 0x50); + u32 *LCR0BRDR = (u32 *)(info->lcr_base + 0x28); + + info->misc_ctrl_value |= BIT30; + *MiscCtrl = info->misc_ctrl_value; + + /* + * Force at least 170ns delay before clearing + * reset bit. Each read from LCR takes at least + * 30ns so 10 times for 300ns to be safe. + */ + for(i=0;i<10;i++) + readval = *MiscCtrl; + + info->misc_ctrl_value &= ~BIT30; + *MiscCtrl = info->misc_ctrl_value; + + *LCR0BRDR = BUS_DESCRIPTOR( + 1, // Write Strobe Hold (0-3) + 2, // Write Strobe Delay (0-3) + 2, // Read Strobe Delay (0-3) + 0, // NWDD (Write data-data) (0-3) + 4, // NWAD (Write Addr-data) (0-31) + 0, // NXDA (Read/Write Data-Addr) (0-3) + 0, // NRDD (Read Data-Data) (0-3) + 5 // NRAD (Read Addr-Data) (0-31) + ); + } else { + /* do HW reset */ + outb( 0,info->io_base + 8 ); + } + + info->mbre_bit = 0; + info->loopback_bits = 0; + info->usc_idle_mode = 0; + + /* + * Program the Bus Configuration Register (BCR) + * + * <15> 0 Don't use seperate address + * <14..6> 0 reserved + * <5..4> 00 IAckmode = Default, don't care + * <3> 1 Bus Request Totem Pole output + * <2> 1 Use 16 Bit data bus + * <1> 0 IRQ Totem Pole output + * <0> 0 Don't Shift Right Addr + * + * 0000 0000 0000 1100 = 0x000c + * + * By writing to io_base + SDPIN the Wait/Ack pin is + * programmed to work as a Wait pin. + */ + + outw( 0x000c,info->io_base + SDPIN ); + + + outw( 0,info->io_base ); + outw( 0,info->io_base + CCAR ); + + /* select little endian byte ordering */ + usc_RTCmd( info, RTCmd_SelectLittleEndian ); + + + /* Port Control Register (PCR) + * + * <15..14> 11 Port 7 is Output (~DMAEN, Bit 14 : 0 = Enabled) + * <13..12> 11 Port 6 is Output (~INTEN, Bit 12 : 0 = Enabled) + * <11..10> 00 Port 5 is Input (No Connect, Don't Care) + * <9..8> 00 Port 4 is Input (No Connect, Don't Care) + * <7..6> 11 Port 3 is Output (~RTS, Bit 6 : 0 = Enabled ) + * <5..4> 11 Port 2 is Output (~DTR, Bit 4 : 0 = Enabled ) + * <3..2> 01 Port 1 is Input (Dedicated RxC) + * <1..0> 01 Port 0 is Input (Dedicated TxC) + * + * 1111 0000 1111 0101 = 0xf0f5 + */ + + usc_OutReg( info, PCR, 0xf0f5 ); + + + /* + * Input/Output Control Register + * + * <15..14> 00 CTS is active low input + * <13..12> 00 DCD is active low input + * <11..10> 00 TxREQ pin is input (DSR) + * <9..8> 00 RxREQ pin is input (RI) + * <7..6> 00 TxD is output (Transmit Data) + * <5..3> 000 TxC Pin in Input (14.7456MHz Clock) + * <2..0> 100 RxC is Output (drive with BRG0) + * + * 0000 0000 0000 0100 = 0x0004 + */ + + usc_OutReg( info, IOCR, 0x0004 ); + +} /* end of usc_reset() */ + +/* usc_set_async_mode() + * + * Program adapter for asynchronous communications. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_async_mode( struct mgsl_struct *info ) +{ + u16 RegValue; + + /* disable interrupts while programming USC */ + usc_DisableMasterIrqBit( info ); + + outw( 0, info->io_base ); /* clear Master Bus Enable (DCAR) */ + usc_DmaCmd( info, DmaCmd_ResetAllChannels ); /* disable both DMA channels */ + + usc_loopback_frame( info ); + + /* Channel mode Register (CMR) + * + * <15..14> 00 Tx Sub modes, 00 = 1 Stop Bit + * <13..12> 00 00 = 16X Clock + * <11..8> 0000 Transmitter mode = Asynchronous + * <7..6> 00 reserved? + * <5..4> 00 Rx Sub modes, 00 = 16X Clock + * <3..0> 0000 Receiver mode = Asynchronous + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + if ( info->params.stop_bits != 1 ) + RegValue |= BIT14; + usc_OutReg( info, CMR, RegValue ); + + + /* Receiver mode Register (RMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Even parity + * <5> 0 parity disabled + * <4..2> 000 Receive Char Length = 8 bits + * <1..0> 00 Disable Receiver + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, RMR, RegValue ); + + + /* Set IRQ trigger level */ + + usc_RCmd( info, RCmd_SelectRicrIntLevel ); + + + /* Receive Interrupt Control Register (RICR) + * + * <15..8> ? RxFIFO IRQ Request Level + * + * Note: For async mode the receive FIFO level must be set + * to 0 to aviod the situation where the FIFO contains fewer bytes + * than the trigger level and no more data is expected. + * + * <7> 0 Exited Hunt IA (Interrupt Arm) + * <6> 0 Idle Received IA + * <5> 0 Break/Abort IA + * <4> 0 Rx Bound IA + * <3> 0 Queued status reflects oldest byte in FIFO + * <2> 0 Abort/PE IA + * <1> 0 Rx Overrun IA + * <0> 0 Select TC0 value for readback + * + * 0000 0000 0100 0000 = 0x0000 + (FIFOLEVEL in MSB) + */ + + usc_OutReg( info, RICR, 0x0000 ); + + usc_UnlatchRxstatusBits( info, RXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, RECEIVE_STATUS ); + + + /* Transmit mode Register (TMR) + * + * <15..13> 000 encoding = None + * <12..08> 00000 reserved (Sync Only) + * <7..6> 00 Transmit parity Even + * <5> 0 Transmit parity Disabled + * <4..2> 000 Tx Char Length = 8 bits + * <1..0> 00 Disable Transmitter + * + * 0000 0000 0000 0000 = 0x0 + */ + + RegValue = 0; + + if ( info->params.data_bits != 8 ) + RegValue |= BIT4+BIT3+BIT2; + + if ( info->params.parity != ASYNC_PARITY_NONE ) { + RegValue |= BIT5; + if ( info->params.parity != ASYNC_PARITY_ODD ) + RegValue |= BIT6; + } + + usc_OutReg( info, TMR, RegValue ); + + usc_set_txidle( info ); + + + /* Set IRQ trigger level */ + + usc_TCmd( info, TCmd_SelectTicrIntLevel ); + + + /* Transmit Interrupt Control Register (TICR) + * + * <15..8> ? Transmit FIFO IRQ Level + * <7> 0 Present IA (Interrupt Arm) + * <6> 1 Idle Sent IA + * <5> 0 Abort Sent IA + * <4> 0 EOF/EOM Sent IA + * <3> 0 CRC Sent IA + * <2> 0 1 = Wait for SW Trigger to Start Frame + * <1> 0 Tx Underrun IA + * <0> 0 TC0 constant on read back + * + * 0000 0000 0100 0000 = 0x0040 + */ + + usc_OutReg( info, TICR, 0x1f40 ); + + usc_UnlatchTxstatusBits( info, TXSTATUS_ALL ); + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS ); + + usc_enable_async_clock( info, info->params.data_rate ); + + + /* Channel Control/status Register (CCSR) + * + * <15> X RCC FIFO Overflow status (RO) + * <14> X RCC FIFO Not Empty status (RO) + * <13> 0 1 = Clear RCC FIFO (WO) + * <12> X DPLL in Sync status (RO) + * <11> X DPLL 2 Missed Clocks status (RO) + * <10> X DPLL 1 Missed Clock status (RO) + * <9..8> 00 DPLL Resync on rising and falling edges (RW) + * <7> X SDLC Loop On status (RO) + * <6> X SDLC Loop Send status (RO) + * <5> 1 Bypass counters for TxClk and RxClk (RW) + * <4..2> 000 Last Char of SDLC frame has 8 bits (RW) + * <1..0> 00 reserved + * + * 0000 0000 0010 0000 = 0x0020 + */ + + usc_OutReg( info, CCSR, 0x0020 ); + + usc_DisableInterrupts( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_ClearIrqPendingBits( info, TRANSMIT_STATUS + TRANSMIT_DATA + + RECEIVE_DATA + RECEIVE_STATUS ); + + usc_EnableMasterIrqBit( info ); + + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); + +} /* end of usc_set_async_mode() */ + +/* usc_loopback_frame() + * + * Loop back a small (2 byte) dummy SDLC frame. + * Interrupts and DMA are NOT used. The purpose of this is to + * clear any 'stale' status info left over from running in async mode. + * + * The 16C32 shows the strange behaviour of marking the 1st + * received SDLC frame with a CRC error even when there is no + * CRC error. To get around this a small dummy from of 2 bytes + * is looped back when switching from async to sync mode. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_loopback_frame( struct mgsl_struct *info ) +{ + int i; + + usc_DisableMasterIrqBit( info ); + + usc_set_sdlc_mode( info ); + usc_enable_loopback( info, 1 ); + + /* Write 16-bit Time Constant for BRG0 */ + usc_OutReg( info, TC0R, 0 ); + + /* Channel Control Register (CCR) + * + * <15..14> 00 Don't use 32-bit Tx Control Blocks (TCBs) + * <13> 0 Trigger Tx on SW Command Disabled + * <12> 0 Flag Preamble Disabled + * <11..10> 00 Preamble Length = 8-Bits + * <9..8> 01 Preamble Pattern = flags + * <7..6> 10 Don't use 32-bit Rx status Blocks (RSBs) + * <5> 0 Trigger Rx on SW Command Disabled + * <4..0> 0 reserved + * + * 0000 0001 0000 0000 = 0x0100 + */ + + usc_OutReg( info, CCR, 0x0100 ); + + /* SETUP RECEIVER */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + usc_EnableReceiver(info,ENABLE_UNCONDITIONAL); + + /* SETUP TRANSMITTER */ + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + usc_OutReg( info, TCLR, 2 ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* unlatch Tx status bits, and start transmit channel. */ + usc_UnlatchTxstatusBits(info,TXSTATUS_ALL); + outw(0,info->io_base + DATAREG); + + /* ENABLE TRANSMITTER */ + usc_TCmd( info, TCmd_SendFrame ); + usc_EnableTransmitter(info,ENABLE_UNCONDITIONAL); + + /* WAIT FOR RECEIVE COMPLETE */ + for (i=0 ; i<1000 ; i++) + if (usc_InReg( info, RCSR ) & (BIT8 + BIT4 + BIT3 + BIT1)) + break; + + /* clear Internal Data loopback mode */ + usc_enable_loopback(info, 0); + + usc_EnableMasterIrqBit(info); + +} /* end of usc_loopback_frame() */ + +/* usc_set_sync_mode() Programs the USC for SDLC communications. + * + * Arguments: info pointer to adapter info structure + * Return Value: None + */ +void usc_set_sync_mode( struct mgsl_struct *info ) +{ + usc_loopback_frame( info ); + usc_set_sdlc_mode( info ); + + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg(info, PCR, (u16)((usc_InReg(info, PCR) | BIT13) & ~BIT12)); + + usc_enable_aux_clock(info, info->params.clock_speed); + + if (info->params.loopback) + usc_enable_loopback(info,1); + +} /* end of mgsl_set_sync_mode() */ + +/* usc_set_txidle() Set the HDLC idle mode for the transmitter. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_txidle( struct mgsl_struct *info ) +{ + u16 usc_idle_mode = IDLEMODE_FLAGS; + + /* Map API idle mode to USC register bits */ + + switch( info->idle_mode ){ + case HDLC_TXIDLE_FLAGS: usc_idle_mode = IDLEMODE_FLAGS; break; + case HDLC_TXIDLE_ALT_ZEROS_ONES: usc_idle_mode = IDLEMODE_ALT_ONE_ZERO; break; + case HDLC_TXIDLE_ZEROS: usc_idle_mode = IDLEMODE_ZERO; break; + case HDLC_TXIDLE_ONES: usc_idle_mode = IDLEMODE_ONE; break; + case HDLC_TXIDLE_ALT_MARK_SPACE: usc_idle_mode = IDLEMODE_ALT_MARK_SPACE; break; + case HDLC_TXIDLE_SPACE: usc_idle_mode = IDLEMODE_SPACE; break; + case HDLC_TXIDLE_MARK: usc_idle_mode = IDLEMODE_MARK; break; + } + + info->usc_idle_mode = usc_idle_mode; + usc_OutReg(info, TCSR, usc_idle_mode); + +} /* end of usc_set_txidle() */ + +/* usc_get_serial_signals() + * + * Query the adapter for the state of the V24 status (input) signals. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_get_serial_signals( struct mgsl_struct *info ) +{ + u16 status; + + /* clear all serial signals except DTR and RTS */ + info->serial_signals &= SerialSignal_DTR + SerialSignal_RTS; + + /* Read the Misc Interrupt status Register (MISR) to get */ + /* the V24 status signals. */ + + status = usc_InReg( info, MISR ); + + /* set serial signal bits to reflect MISR */ + + if ( status & MISCSTATUS_CTS ) + info->serial_signals |= SerialSignal_CTS; + + if ( status & MISCSTATUS_DCD ) + info->serial_signals |= SerialSignal_DCD; + + if ( status & MISCSTATUS_RI ) + info->serial_signals |= SerialSignal_RI; + + if ( status & MISCSTATUS_DSR ) + info->serial_signals |= SerialSignal_DSR; + +} /* end of usc_get_serial_signals() */ + +/* usc_set_serial_signals() + * + * Set the state of DTR and RTS based on contents of + * serial_signals member of device extension. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void usc_set_serial_signals( struct mgsl_struct *info ) +{ + u16 Control; + unsigned char V24Out = info->serial_signals; + + /* get the current value of the Port Control Register (PCR) */ + + Control = usc_InReg( info, PCR ); + + if ( V24Out & SerialSignal_RTS ) + Control &= ~(BIT6); + else + Control |= BIT6; + + if ( V24Out & SerialSignal_DTR ) + Control &= ~(BIT4); + else + Control |= BIT4; + + usc_OutReg( info, PCR, Control ); + +} /* end of usc_set_serial_signals() */ + +/* usc_enable_async_clock() + * + * Enable the async clock at the specified frequency. + * + * Arguments: info pointer to device instance data + * data_rate data rate of clock in bps + * 0 disables the AUX clock. + * Return Value: None + */ +void usc_enable_async_clock( struct mgsl_struct *info, u32 data_rate ) +{ + if ( data_rate ) { + /* + * Clock mode Control Register (CMCR) + * + * <15..14> 00 counter 1 Disabled + * <13..12> 00 counter 0 Disabled + * <11..10> 11 BRG1 Input is TxC Pin + * <9..8> 11 BRG0 Input is TxC Pin + * <7..6> 01 DPLL Input is BRG1 Output + * <5..3> 100 TxCLK comes from BRG0 + * <2..0> 100 RxCLK comes from BRG0 + * + * 0000 1111 0110 0100 = 0x0f64 + */ + + usc_OutReg( info, CMCR, 0x0f64 ); + + + /* + * Write 16-bit Time Constant for BRG0 + * Time Constant = (ClkSpeed / data_rate) - 1 + * ClkSpeed = 921600 (ISA), 691200 (PCI) + */ + + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + usc_OutReg( info, TC0R, (u16)((691200/data_rate) - 1) ); + else + usc_OutReg( info, TC0R, (u16)((921600/data_rate) - 1) ); + + + /* + * Hardware Configuration Register (HCR) + * Clear Bit 1, BRG0 mode = Continuous + * Set Bit 0 to enable BRG0. + */ + + usc_OutReg( info, HCR, + (u16)((usc_InReg( info, HCR ) & ~BIT1) | BIT0) ); + + + /* Input/Output Control Reg, <2..0> = 100, Drive RxC pin with BRG0 */ + + usc_OutReg( info, IOCR, + (u16)((usc_InReg(info, IOCR) & 0xfff8) | 0x0004) ); + } else { + /* data rate == 0 so turn off BRG0 */ + usc_OutReg( info, HCR, (u16)(usc_InReg( info, HCR ) & ~BIT0) ); + } + +} /* end of usc_enable_async_clock() */ + +/* + * Buffer Structures: + * + * Normal memory access uses virtual addresses that can make discontiguous + * physical memory pages appear to be contiguous in the virtual address + * space (the processors memory mapping handles the conversions). + * + * DMA transfers require physically contiguous memory. This is because + * the DMA system controller and DMA bus masters deal with memory using + * only physical addresses. + * + * This causes a problem under Windows NT when large DMA buffers are + * needed. Fragmentation of the nonpaged pool prevents allocations of + * physically contiguous buffers larger than the PAGE_SIZE. + * + * However the 16C32 supports Bus Master Scatter/Gather DMA which + * allows DMA transfers to physically discontiguous buffers. Information + * about each data transfer buffer is contained in a memory structure + * called a 'buffer entry'. A list of buffer entries is maintained + * to track and control the use of the data transfer buffers. + * + * To support this strategy we will allocate sufficient PAGE_SIZE + * contiguous memory buffers to allow for the total required buffer + * space. + * + * The 16C32 accesses the list of buffer entries using Bus Master + * DMA. Control information is read from the buffer entries by the + * 16C32 to control data transfers. status information is written to + * the buffer entries by the 16C32 to indicate the status of completed + * transfers. + * + * The CPU writes control information to the buffer entries to control + * the 16C32 and reads status information from the buffer entries to + * determine information about received and transmitted frames. + * + * Because the CPU and 16C32 (adapter) both need simultaneous access + * to the buffer entries, the buffer entry memory is allocated with + * HalAllocateCommonBuffer(). This restricts the size of the buffer + * entry list to PAGE_SIZE. + * + * The actual data buffers on the other hand will only be accessed + * by the CPU or the adapter but not by both simultaneously. This allows + * Scatter/Gather packet based DMA procedures for using physically + * discontiguous pages. + */ + +/* + * mgsl_reset_rx_dma_buffers() + * + * Set the count for all receive buffers to DMABUFFERSIZE + * and set the current buffer to the first buffer. This effectively + * makes all buffers free and discards any data in buffers. + * + * Arguments: info pointer to device instance data + * Return Value: None + */ +void mgsl_reset_rx_dma_buffers( struct mgsl_struct *info ) +{ + unsigned int i; + + for ( i = 0; i < info->rx_buffer_count; i++ ) { + *((unsigned long *)&(info->rx_buffer_list[i].count)) = DMABUFFERSIZE; +// info->rx_buffer_list[i].count = DMABUFFERSIZE; +// info->rx_buffer_list[i].status = 0; + } + + info->current_rx_buffer = 0; + +} /* end of mgsl_reset_rx_dma_buffers() */ + +/* + * mgsl_free_rx_frame_buffers() + * + * Free the receive buffers used by a received SDLC + * frame such that the buffers can be reused. + * + * Arguments: + * + * info pointer to device instance data + * StartIndex index of 1st receive buffer of frame + * EndIndex index of last receive buffer of frame + * + * Return Value: None + */ +void mgsl_free_rx_frame_buffers( struct mgsl_struct *info, unsigned int StartIndex, unsigned int EndIndex ) +{ + int Done = 0; + DMABUFFERENTRY *pBufEntry; + unsigned int Index; + + /* Starting with 1st buffer entry of the frame clear the status */ + /* field and set the count field to DMA Buffer Size. */ + + Index = StartIndex; + + while( !Done ) { + pBufEntry = &(info->rx_buffer_list[Index]); + + if ( Index == EndIndex ) { + /* This is the last buffer of the frame! */ + Done = 1; + } + + /* reset current buffer for reuse */ +// pBufEntry->status = 0; +// pBufEntry->count = DMABUFFERSIZE; + *((unsigned long *)&(pBufEntry->count)) = DMABUFFERSIZE; + + /* advance to next buffer entry in linked list */ + Index++; + if ( Index == info->rx_buffer_count ) + Index = 0; + } + + /* set current buffer to next buffer after last buffer of frame */ + info->current_rx_buffer = Index; + +} /* end of free_rx_frame_buffers() */ + +/* mgsl_get_rx_frame() + * + * This function attempts to return a received SDLC frame from the + * receive DMA buffers. Only frames received without errors are returned. + * + * Arguments: info pointer to device extension + * Return Value: 1 if frame returned, otherwise 0 + */ +int mgsl_get_rx_frame(struct mgsl_struct *info) +{ + unsigned int StartIndex, EndIndex; /* index of 1st and last buffers of Rx frame */ + unsigned short status; + DMABUFFERENTRY *pBufEntry; + unsigned int framesize; + int ReturnCode = 0; + unsigned long flags; + struct tty_struct *tty = info->tty; + + /* + * current_rx_buffer points to the 1st buffer of the next available + * receive frame. To find the last buffer of the frame look for + * a non-zero status field in the buffer entries. (The status + * field is set by the 16C32 after completing a receive frame. + */ + + StartIndex = EndIndex = info->current_rx_buffer; + + while( !info->rx_buffer_list[EndIndex].status ) { + /* + * If the count field of the buffer entry is non-zero then + * this buffer has not been used. (The 16C32 clears the count + * field when it starts using the buffer.) If an unused buffer + * is encountered then there are no frames available. + */ + + if ( info->rx_buffer_list[EndIndex].count ) + goto Cleanup; + + /* advance to next buffer entry in linked list */ + EndIndex++; + if ( EndIndex == info->rx_buffer_count ) + EndIndex = 0; + + /* if entire list searched then no frame available */ + if ( EndIndex == StartIndex ) { + /* If this occurs then something bad happened, + * all buffers have been 'used' but none mark + * the end of a frame. Reset buffers and receiver. + */ + + if ( info->rx_enabled ){ + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + goto Cleanup; + } + } + + + /* check status of receive frame */ + + status = info->rx_buffer_list[EndIndex].status; + + if ( status & (RXSTATUS_SHORT_FRAME + RXSTATUS_OVERRUN + + RXSTATUS_CRC_ERROR + RXSTATUS_ABORT) ) { + if ( status & RXSTATUS_SHORT_FRAME ) + info->icount.rxshort++; + else if ( status & RXSTATUS_ABORT ) + info->icount.rxabort++; + else if ( status & RXSTATUS_OVERRUN ) + info->icount.rxover++; + else + info->icount.rxcrc++; + framesize = 0; + } else { + /* receive frame has no errors, get frame size. + * The frame size is the starting value of the RCC (which was + * set to 0xffff) minus the ending value of the RCC (decremented + * once for each receive character) minus 2 for the 16-bit CRC. + */ + + framesize = RCLRVALUE - info->rx_buffer_list[EndIndex].rcc; + + /* adjust frame size for CRC if any */ + if ( info->params.crc_type == HDLC_CRC_16_CCITT ) + framesize -= 2; + } + + if ( debug_level >= DEBUG_LEVEL_BH ) + printk("%s(%d):mgsl_get_rx_frame(%s) status=%04X size=%d\n", + __FILE__,__LINE__,info->device_name,status,framesize); + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,info->rx_buffer_list[StartIndex].virt_addr, + framesize,0); + + if (framesize) { + if (framesize > HDLC_MAX_FRAME_SIZE) + info->icount.rxlong++; + else { + info->icount.rxok++; + pBufEntry = &(info->rx_buffer_list[StartIndex]); + /* Call the line discipline receive callback directly. */ + tty->ldisc.receive_buf(tty, pBufEntry->virt_addr, info->flag_buf, framesize); + } + } + /* Free the buffers used by this frame. */ + mgsl_free_rx_frame_buffers( info, StartIndex, EndIndex ); + + ReturnCode = 1; + +Cleanup: + + if ( info->rx_enabled && info->rx_overflow ) { + /* The receiver needs to restarted because of + * a receive overflow (buffer or FIFO). If the + * receive buffers are now empty, then restart receiver. + */ + + if ( !info->rx_buffer_list[info->current_rx_buffer].status && + info->rx_buffer_list[info->current_rx_buffer].count ) { + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_start_receiver(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + return ReturnCode; + +} /* end of mgsl_get_rx_frame() */ + +/* mgsl_load_tx_dma_buffer() + * + * Load the transmit DMA buffer with the specified data. + * + * Arguments: + * + * info pointer to device extension + * Buffer pointer to buffer containing frame to load + * BufferSize size in bytes of frame in Buffer + * + * Return Value: None + */ +void mgsl_load_tx_dma_buffer(struct mgsl_struct *info, const char *Buffer, + unsigned int BufferSize) +{ + unsigned short Copycount; + unsigned int i = 0; + DMABUFFERENTRY *pBufEntry; + + if ( debug_level >= DEBUG_LEVEL_DATA ) + mgsl_trace_block(info,Buffer,BufferSize,1); + + /* Setup the status and RCC (Frame Size) fields of the 1st */ + /* buffer entry in the transmit DMA buffer list. */ + + info->tx_buffer_list[0].status = info->cmr_value & 0xf000; + info->tx_buffer_list[0].rcc = BufferSize; + info->tx_buffer_list[0].count = BufferSize; + + /* Copy frame data from 1st source buffer to the DMA buffers. */ + /* The frame data may span multiple DMA buffers. */ + + while( BufferSize ){ + /* Get a pointer to next DMA buffer entry. */ + pBufEntry = &info->tx_buffer_list[i++]; + + /* Calculate the number of bytes that can be copied from */ + /* the source buffer to this DMA buffer. */ + if ( BufferSize > DMABUFFERSIZE ) + Copycount = DMABUFFERSIZE; + else + Copycount = BufferSize; + + /* Actually copy data from source buffer to DMA buffer. */ + /* Also set the data count for this individual DMA buffer. */ + if ( info->bus_type == MGSL_BUS_TYPE_PCI ) + mgsl_load_pci_memory(pBufEntry->virt_addr, Buffer,Copycount); + else + memcpy(pBufEntry->virt_addr, Buffer, Copycount); + + pBufEntry->count = Copycount; + + /* Advance source pointer and reduce remaining data count. */ + Buffer += Copycount; + BufferSize -= Copycount; + } + +} /* end of mgsl_load_tx_dma_buffer() */ + +/* + * mgsl_register_test() + * + * Performs a register test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_register_test( struct mgsl_struct *info ) +{ + static unsigned short BitPatterns[] = + { 0x0000, 0xffff, 0xaaaa, 0x5555, 0x1234, 0x6969, 0x9696, 0x0f0f }; + static unsigned int Patterncount = sizeof(BitPatterns)/sizeof(unsigned short); + unsigned int i; + BOOLEAN rc = TRUE; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* Verify the reset state of some registers. */ + + if ( (usc_InReg( info, SICR ) != 0) || + (usc_InReg( info, IVR ) != 0) || + (usc_InDmaReg( info, DIVR ) != 0) ){ + rc = FALSE; + } + + if ( rc == TRUE ){ + /* Write bit patterns to various registers but do it out of */ + /* sync, then read back and verify values. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + usc_OutReg( info, TC0R, BitPatterns[i] ); + usc_OutReg( info, TC1R, BitPatterns[(i+1)%Patterncount] ); + usc_OutReg( info, TCLR, BitPatterns[(i+2)%Patterncount] ); + usc_OutReg( info, RCLR, BitPatterns[(i+3)%Patterncount] ); + usc_OutReg( info, RSR, BitPatterns[(i+4)%Patterncount] ); + usc_OutDmaReg( info, TBCR, BitPatterns[(i+5)%Patterncount] ); + + if ( (usc_InReg( info, TC0R ) != BitPatterns[i]) || + (usc_InReg( info, TC1R ) != BitPatterns[(i+1)%Patterncount]) || + (usc_InReg( info, TCLR ) != BitPatterns[(i+2)%Patterncount]) || + (usc_InReg( info, RCLR ) != BitPatterns[(i+3)%Patterncount]) || + (usc_InReg( info, RSR ) != BitPatterns[(i+4)%Patterncount]) || + (usc_InDmaReg( info, TBCR ) != BitPatterns[(i+5)%Patterncount]) ){ + rc = FALSE; + break; + } + } + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + return rc; + +} /* end of mgsl_register_test() */ + +/* mgsl_irq_test() Perform interrupt test of the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_irq_test( struct mgsl_struct *info ) +{ + unsigned long EndTime; + unsigned long flags; + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + /* + * Setup 16C32 to interrupt on TxC pin (14MHz clock) transition. + * The ISR sets irq_occurred to 1. + */ + + info->irq_occurred = FALSE; + + /* Enable INTEN gate for ISA adapter (Port 6, Bit12) */ + /* Enable INTEN (Port 6, Bit12) */ + /* This connects the IRQ request signal to the ISA bus */ + /* on the ISA adapter. This has no effect for the PCI adapter */ + usc_OutReg( info, PCR, (unsigned short)((usc_InReg(info, PCR) | BIT13) & ~BIT12) ); + + usc_EnableMasterIrqBit(info); + usc_EnableInterrupts(info, IO_PIN); + usc_ClearIrqPendingBits(info, IO_PIN); + + usc_UnlatchIostatusBits(info, MISCSTATUS_TXC_LATCHED); + usc_EnableStatusIrqs(info, SICR_TXC_ACTIVE + SICR_TXC_INACTIVE); + + EndTime=100; + while( EndTime-- && !info->irq_occurred ) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(jiffies_from_ms(10)); + current->state = TASK_RUNNING; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + usc_reset(info); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( !info->irq_occurred ) + return FALSE; + else + return TRUE; + +} /* end of mgsl_irq_test() */ + +/* mgsl_dma_test() + * + * Perform a DMA test of the 16C32. A small frame is + * transmitted via DMA from a transmit buffer to a receive buffer + * using single buffer DMA mode. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_dma_test( struct mgsl_struct *info ) +{ + unsigned short FifoLevel; + unsigned long phys_addr; + unsigned int FrameSize; + unsigned int i; + char *TmpPtr; + BOOLEAN rc = TRUE; + volatile unsigned short status; + volatile unsigned long EndTime; + unsigned long flags; + MGSL_PARAMS tmp_params; + + /* save current port options */ + memcpy(&tmp_params,&info->params,sizeof(MGSL_PARAMS)); + /* load default port options */ + memcpy(&info->params,&default_params,sizeof(MGSL_PARAMS)); + +#define TESTFRAMESIZE 40 + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup 16C32 for SDLC DMA transfer mode */ + + usc_reset(info); + usc_set_sdlc_mode(info); + usc_enable_loopback(info,1); + + /* Reprogram the RDMR so that the 16C32 does NOT clear the count + * field of the buffer entry after fetching buffer address. This + * way we can detect a DMA failure for a DMA read (which should be + * non-destructive to system memory) before we try and write to + * memory (where a failure could corrupt system memory). + */ + + /* Receive DMA mode Register (RDMR) + * + * <15..14> 11 DMA mode = Linked List Buffer mode + * <13> 1 RSBinA/L = store Rx status Block in List entry + * <12> 0 1 = Clear count of List Entry after fetching + * <11..10> 00 Address mode = Increment + * <9> 1 Terminate Buffer on RxBound + * <8> 0 Bus Width = 16bits + * <7..0> ? status Bits (write as 0s) + * + * 1110 0010 0000 0000 = 0xe200 + */ + + usc_OutDmaReg( info, RDMR, 0xe200 ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /* SETUP TRANSMIT AND RECEIVE DMA BUFFERS */ + + FrameSize = TESTFRAMESIZE; + + /* setup 1st transmit buffer entry: */ + /* with frame size and transmit control word */ + + info->tx_buffer_list[0].count = FrameSize; + info->tx_buffer_list[0].rcc = FrameSize; + info->tx_buffer_list[0].status = 0x4000; + + /* build a transmit frame in 1st transmit DMA buffer */ + + TmpPtr = info->tx_buffer_list[0].virt_addr; + for (i = 0; i < FrameSize; i++ ) + *TmpPtr++ = i; + + /* setup 1st receive buffer entry: */ + /* clear status, set max receive buffer size */ + + info->rx_buffer_list[0].status = 0; + info->rx_buffer_list[0].count = FrameSize + 4; + + /* zero out the 1st receive buffer */ + + memset( info->rx_buffer_list[0].virt_addr, 0, FrameSize + 4 ); + + /* Set count field of next buffer entries to prevent */ + /* 16C32 from using buffers after the 1st one. */ + + info->tx_buffer_list[1].count = 0; + info->rx_buffer_list[1].count = 0; + + + /***************************/ + /* Program 16C32 receiver. */ + /***************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* setup DMA transfers */ + usc_RTCmd( info, RTCmd_PurgeRxFifo ); + + /* program 16C32 receiver with physical address of 1st DMA buffer entry */ + phys_addr = info->rx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NRARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NRARU, (unsigned short)(phys_addr >> 16) ); + + /* Clear the Rx DMA status bits (read RDMR) and start channel */ + usc_InDmaReg( info, RDMR ); + usc_DmaCmd( info, DmaCmd_InitRxChannel ); + + /* Enable Receiver (RMR <1..0> = 10) */ + usc_OutReg( info, RMR, (unsigned short)((usc_InReg(info, RMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /*************************************************************/ + /* WAIT FOR RECEIVER TO DMA ALL PARAMETERS FROM BUFFER ENTRY */ + /*************************************************************/ + + /* Wait 100ms for interrupt. */ + EndTime = jiffies + jiffies_from_ms(100); + + for(;;) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InDmaReg( info, RDMR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( !(status & BIT4) && (status & BIT5) ) { + /* INITG (BIT 4) is inactive (no entry read in progress) AND */ + /* BUSY (BIT 5) is active (channel still active). */ + /* This means the buffer entry read has completed. */ + break; + } + } + + + /******************************/ + /* Program 16C32 transmitter. */ + /******************************/ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Program the Transmit Character Length Register (TCLR) */ + /* and clear FIFO (TCC is loaded with TCLR on FIFO clear) */ + + usc_OutReg( info, TCLR, (unsigned short)info->tx_buffer_list[0].count ); + usc_RTCmd( info, RTCmd_PurgeTxFifo ); + + /* Program the address of the 1st DMA Buffer Entry in linked list */ + + phys_addr = info->tx_buffer_list[0].phys_entry; + usc_OutDmaReg( info, NTARL, (unsigned short)phys_addr ); + usc_OutDmaReg( info, NTARU, (unsigned short)(phys_addr >> 16) ); + + /* unlatch Tx status bits, and start transmit channel. */ + + usc_OutReg( info, TCSR, (unsigned short)(( usc_InReg(info, TCSR) & 0x0700) | 0xfa) ); + usc_DmaCmd( info, DmaCmd_InitTxChannel ); + + /* wait for DMA controller to fill transmit FIFO */ + + usc_TCmd( info, TCmd_SelectTicrTxFifostatus ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /**********************************/ + /* WAIT FOR TRANSMIT FIFO TO FILL */ + /**********************************/ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + for(;;) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + FifoLevel = usc_InReg(info, TICR) >> 8; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + if ( FifoLevel < 16 ) + break; + else + if ( FrameSize < 32 ) { + /* This frame is smaller than the entire transmit FIFO */ + /* so wait for the entire frame to be loaded. */ + if ( FifoLevel <= (32 - FrameSize) ) + break; + } + } + + + if ( rc == TRUE ) + { + /* Enable 16C32 transmitter. */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + + /* Transmit mode Register (TMR), <1..0> = 10, Enable Transmitter */ + usc_TCmd( info, TCmd_SendFrame ); + usc_OutReg( info, TMR, (unsigned short)((usc_InReg(info, TMR) & 0xfffc) | 0x0002) ); + + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + + /******************************/ + /* WAIT FOR TRANSMIT COMPLETE */ + /******************************/ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + /* While timer not expired wait for transmit complete */ + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + while ( !(status & (BIT6+BIT5+BIT4+BIT2+BIT1)) ) { + if ( jiffies > EndTime ) { + rc = FALSE; + break; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + status = usc_InReg( info, TCSR ); + spin_unlock_irqrestore(&info->irq_spinlock,flags); + } + } + + + if ( rc == TRUE ){ + /* CHECK FOR TRANSMIT ERRORS */ + if ( status & (BIT5 + BIT1) ) + rc = FALSE; + } + + if ( rc == TRUE ) { + /* WAIT FOR RECEIVE COMPLETE */ + + /* Wait 100ms */ + EndTime = jiffies + jiffies_from_ms(100); + + /* Wait for 16C32 to write receive status to buffer entry. */ + status=info->rx_buffer_list[0].status; + while ( status == 0 ) { + if ( jiffies > EndTime ) { + printk(KERN_ERR"mark 4\n"); + rc = FALSE; + break; + } + status=info->rx_buffer_list[0].status; + } + } + + + if ( rc == TRUE ) { + /* CHECK FOR RECEIVE ERRORS */ + status = info->rx_buffer_list[0].status; + + if ( status & (BIT8 + BIT3 + BIT1) ) { + /* receive error has occured */ + rc = FALSE; + } else { + if ( memcmp( info->tx_buffer_list[0].virt_addr , + info->rx_buffer_list[0].virt_addr, FrameSize ) ){ + rc = FALSE; + } + } + } + + usc_reset( info ); + + /* restore current port options */ + memcpy(&info->params,&tmp_params,sizeof(MGSL_PARAMS)); + + return rc; + +} /* end of mgsl_dma_test() */ + +/* mgsl_adapter_test() + * + * Perform the register, IRQ, and DMA tests for the 16C32. + * + * Arguments: info pointer to device instance data + * Return Value: 0 if success, otherwise -ENODEV + */ +int mgsl_adapter_test( struct mgsl_struct *info ) +{ + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):Testing device %s\n", + __FILE__,__LINE__,info->device_name ); + + if ( !mgsl_register_test( info ) ) { + info->init_error = DiagStatus_AddressFailure; + printk( "%s(%d):Register test failure for device %s Addr=%04X\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->io_base) ); + return -ENODEV; + } + + if ( !mgsl_irq_test( info ) ) { + info->init_error = DiagStatus_IrqFailure; + printk( "%s(%d):Interrupt test failure for device %s IRQ=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->irq_level) ); + return -ENODEV; + } + + if ( !mgsl_dma_test( info ) ) { + info->init_error = DiagStatus_DmaFailure; + printk( "%s(%d):DMA test failure for device %s DMA=%d\n", + __FILE__,__LINE__,info->device_name, (unsigned short)(info->dma_level) ); + return -ENODEV; + } + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):device %s passed diagnostics\n", + __FILE__,__LINE__,info->device_name ); + + return 0; + +} /* end of mgsl_adapter_test() */ + +/* mgsl_memory_test() + * + * Test the shared memory on a PCI adapter. + * + * Arguments: info pointer to device instance data + * Return Value: TRUE if test passed, otherwise FALSE + */ +BOOLEAN mgsl_memory_test( struct mgsl_struct *info ) +{ + static unsigned long BitPatterns[] = { 0x0, 0x55555555, 0xaaaaaaaa, + 0x66666666, 0x99999999, 0xffffffff, 0x12345678 }; + unsigned long Patterncount = sizeof(BitPatterns)/sizeof(unsigned long); + unsigned long i; + unsigned long TestLimit = SHARED_MEM_ADDRESS_SIZE/sizeof(unsigned long); + unsigned long * TestAddr; + + if ( info->bus_type != MGSL_BUS_TYPE_PCI ) + return TRUE; + + TestAddr = (unsigned long *)info->memory_base; + + /* Test data lines with test pattern at one location. */ + + for ( i = 0 ; i < Patterncount ; i++ ) { + *TestAddr = BitPatterns[i]; + if ( *TestAddr != BitPatterns[i] ) + return FALSE; + } + + /* Test address lines with incrementing pattern over */ + /* entire address range. */ + + for ( i = 0 ; i < TestLimit ; i++ ) { + *TestAddr = i * 4; + TestAddr++; + } + + TestAddr = (unsigned long *)info->memory_base; + + for ( i = 0 ; i < TestLimit ; i++ ) { + if ( *TestAddr != i * 4 ) + return FALSE; + TestAddr++; + } + + memset( info->memory_base, 0, SHARED_MEM_ADDRESS_SIZE ); + + return TRUE; + +} /* End Of mgsl_memory_test() */ + + +#pragma optimize( "", off ) +/* mgsl_load_pci_memory() + * + * Load a large block of data into the PCI shared memory. + * Use this instead of memcpy() or memmove() to move data + * into the PCI shared memory. + * + * Notes: + * + * This function prevents the PCI9050 interface chip from hogging + * the adapter local bus, which can starve the 16C32 by preventing + * 16C32 bus master cycles. + * + * The PCI9050 documentation says that the 9050 will always release + * control of the local bus after completing the current read + * or write operation. + * + * It appears that as long as the PCI9050 write FIFO is full, the + * PCI9050 treats all of the writes as a single burst transaction + * and will not release the bus. This causes DMA latency problems + * at high speeds when copying large data blocks to the shared + * memory. + * + * This function in effect, breaks the a large shared memory write + * into multiple transations by interleaving a shared memory read + * which will flush the write FIFO and 'complete' the write + * transation. This allows any pending DMA request to gain control + * of the local bus in a timely fasion. + * + * Arguments: + * + * TargetPtr pointer to target address in PCI shared memory + * SourcePtr pointer to source buffer for data + * count count in bytes of data to copy + * + * Return Value: None + */ +void mgsl_load_pci_memory( char* TargetPtr, const char* SourcePtr, + unsigned short count ) +{ + /*******************************************************/ + /* A load interval of 16 allows for 4 32-bit writes at */ + /* 60ns each for a maximum latency of 240ns on the */ + /* local bus. */ + /*******************************************************/ + +#define PCI_LOAD_INTERVAL 64 + + unsigned short Intervalcount = count / PCI_LOAD_INTERVAL; + unsigned short Index; + unsigned long Dummy; + + for ( Index = 0 ; Index < Intervalcount ; Index++ ) + { + memcpy(TargetPtr, SourcePtr, PCI_LOAD_INTERVAL); + Dummy = *((unsigned long *)TargetPtr); + TargetPtr += PCI_LOAD_INTERVAL; + SourcePtr += PCI_LOAD_INTERVAL; + } + + memcpy( TargetPtr, SourcePtr, count % PCI_LOAD_INTERVAL ); + +} /* End Of mgsl_load_pci_memory() */ +#pragma optimize( "", on ) + +void mgsl_trace_block(struct mgsl_struct *info,const char* data, int count, int xmit) +{ + int i; + int linecount; + if (xmit) + printk("%s tx data:\n",info->device_name); + else + printk("%s rx data:\n",info->device_name); + + while(count) { + if (count > 16) + linecount = 16; + else + linecount = count; + + for(i=0;i=040 && data[i]<=0176) + printk("%c",data[i]); + else + printk("."); + } + printk("\n"); + + data += linecount; + count -= linecount; + } +} /* end of mgsl_trace_block() */ + +/* mgsl_tx_timeout() + * + * called when HDLC frame times out + * update stats and do tx completion processing + * + * Arguments: context pointer to device instance data + * Return Value: None + */ +void mgsl_tx_timeout(unsigned long context) +{ + struct mgsl_struct *info = (struct mgsl_struct*)context; + unsigned long flags; + + if ( debug_level >= DEBUG_LEVEL_INFO ) + printk( "%s(%d):mgsl_tx_timeout(%s)\n", + __FILE__,__LINE__,info->device_name); + if(info->tx_active && info->params.mode == MGSL_MODE_HDLC) { + info->icount.txtimeout++; + } + + spin_lock_irqsave(&info->irq_spinlock,flags); + info->tx_active = 0; + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + spin_unlock_irqrestore(&info->irq_spinlock,flags); + + mgsl_bh_transmit_data(info,0); + +} /* end of mgsl_tx_timeout() */ + diff -u --recursive --new-file v2.2.4/linux/drivers/fc4/soc.c linux/drivers/fc4/soc.c --- v2.2.4/linux/drivers/fc4/soc.c Tue Mar 23 14:35:47 1999 +++ linux/drivers/fc4/soc.c Fri Mar 26 13:57:41 1999 @@ -41,7 +41,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.2.4/linux/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c --- v2.2.4/linux/drivers/net/hamradio/baycom_epp.c Tue Feb 23 15:21:33 1999 +++ linux/drivers/net/hamradio/baycom_epp.c Fri Mar 26 13:57:41 1999 @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include diff -u --recursive --new-file v2.2.4/linux/drivers/net/myri_sbus.c linux/drivers/net/myri_sbus.c --- v2.2.4/linux/drivers/net/myri_sbus.c Tue Mar 23 14:35:47 1999 +++ linux/drivers/net/myri_sbus.c Fri Mar 26 13:57:41 1999 @@ -34,7 +34,6 @@ #include #include #include -#include #include #include diff -u --recursive --new-file v2.2.4/linux/drivers/sbus/char/sab82532.c linux/drivers/sbus/char/sab82532.c --- v2.2.4/linux/drivers/sbus/char/sab82532.c Tue Mar 23 14:35:48 1999 +++ linux/drivers/sbus/char/sab82532.c Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: sab82532.c,v 1.28 1999/01/02 16:47:35 davem Exp $ +/* $Id: sab82532.c,v 1.30 1999/03/24 11:34:52 davem Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -2136,7 +2136,7 @@ __initfunc(static inline void show_serial_version(void)) { - char *revision = "$Revision: 1.28 $"; + char *revision = "$Revision: 1.30 $"; char *version, *p; version = strchr(revision, ' '); @@ -2359,8 +2359,10 @@ restore_flags(flags); for (i = 0; i < NR_PORTS; i++) { - if (sab82532_table[i].type != PORT_UNKNOWN) - release_region(sab82532_table[i].port, 8); + struct sab82532 *info = (struct sab82532 *)sab82532_table[i]->driver_data; + if (info->type != PORT_UNKNOWN) + release_region((unsigned long)info->regs, + sizeof(union sab82532_async_regs)); } if (tmp_buf) { free_page((unsigned long) tmp_buf); diff -u --recursive --new-file v2.2.4/linux/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c --- v2.2.4/linux/drivers/scsi/ppa.c Wed Jan 13 15:00:42 1999 +++ linux/drivers/scsi/ppa.c Fri Mar 26 13:57:41 1999 @@ -48,7 +48,6 @@ } #include "ppa.h" -#include #define NO_HOSTS 4 static ppa_struct ppa_hosts[NO_HOSTS] = diff -u --recursive --new-file v2.2.4/linux/drivers/sound/es1370.c linux/drivers/sound/es1370.c --- v2.2.4/linux/drivers/sound/es1370.c Tue Mar 23 14:35:48 1999 +++ linux/drivers/sound/es1370.c Fri Mar 26 14:52:34 1999 @@ -83,6 +83,10 @@ * 16.12.98 0.16 Don't wake up app until there are fragsize bytes to read/write * 06.01.99 0.17 remove the silly SA_INTERRUPT flag. * hopefully killed the egcs section type conflict + * 12.03.99 0.18 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.19 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed * * some important things missing in Ensoniq documentation: * @@ -1097,7 +1101,7 @@ if (cnt <= 0) { start_adc(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_adc.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1152,7 +1156,7 @@ if (cnt <= 0) { start_dac2(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_dac2.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1455,7 +1459,7 @@ spin_lock_irqsave(&s->lock, flags); es1370_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; cinfo.ptr = s->dma_adc.hwptr; if (s->dma_adc.mapped) s->dma_adc.count &= s->dma_adc.fragsize-1; @@ -1468,7 +1472,7 @@ spin_lock_irqsave(&s->lock, flags); es1370_update_ptr(s); cinfo.bytes = s->dma_dac2.total_bytes; - cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift; cinfo.ptr = s->dma_dac2.hwptr; if (s->dma_dac2.mapped) s->dma_dac2.count &= s->dma_dac2.fragsize-1; @@ -1664,7 +1668,7 @@ if (cnt <= 0) { start_dac1(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_dac1.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1866,7 +1870,7 @@ spin_lock_irqsave(&s->lock, flags); es1370_update_ptr(s); cinfo.bytes = s->dma_dac1.total_bytes; - cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift; cinfo.ptr = s->dma_dac1.hwptr; if (s->dma_dac1.mapped) s->dma_dac1.count &= s->dma_dac1.fragsize-1; @@ -2021,7 +2025,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.iwait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2068,7 +2072,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.owait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2271,7 +2275,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1370: version v0.17 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1370: version v0.19 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1370, pcidev))) { if (pcidev->base_address[0] == 0 || diff -u --recursive --new-file v2.2.4/linux/drivers/sound/es1371.c linux/drivers/sound/es1371.c --- v2.2.4/linux/drivers/sound/es1371.c Wed Mar 10 15:29:47 1999 +++ linux/drivers/sound/es1371.c Fri Mar 26 14:52:34 1999 @@ -54,6 +54,10 @@ * Don't wake up app until there are fragsize bytes to read/write * 06.01.99 0.8 remove the silly SA_INTERRUPT flag. * hopefully killed the egcs section type conflict + * 12.03.99 0.9 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.10 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed * */ @@ -1542,7 +1546,7 @@ if (cnt <= 0) { start_adc(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_adc.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1597,7 +1601,7 @@ if (cnt <= 0) { start_dac2(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_dac2.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1897,7 +1901,7 @@ spin_lock_irqsave(&s->lock, flags); es1371_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; cinfo.ptr = s->dma_adc.hwptr; if (s->dma_adc.mapped) s->dma_adc.count &= s->dma_adc.fragsize-1; @@ -1910,7 +1914,7 @@ spin_lock_irqsave(&s->lock, flags); es1371_update_ptr(s); cinfo.bytes = s->dma_dac2.total_bytes; - cinfo.blocks = s->dma_dac2.total_bytes >> s->dma_dac2.fragshift; + cinfo.blocks = s->dma_dac2.count >> s->dma_dac2.fragshift; cinfo.ptr = s->dma_dac2.hwptr; if (s->dma_dac2.mapped) s->dma_dac2.count &= s->dma_dac2.fragsize-1; @@ -2108,7 +2112,7 @@ if (cnt <= 0) { start_dac1(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_dac1.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2301,7 +2305,7 @@ spin_lock_irqsave(&s->lock, flags); es1371_update_ptr(s); cinfo.bytes = s->dma_dac1.total_bytes; - cinfo.blocks = s->dma_dac1.total_bytes >> s->dma_dac1.fragshift; + cinfo.blocks = s->dma_dac1.count >> s->dma_dac1.fragshift; cinfo.ptr = s->dma_dac1.hwptr; if (s->dma_dac1.mapped) s->dma_dac1.count &= s->dma_dac1.fragsize-1; @@ -2455,7 +2459,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.iwait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2502,7 +2506,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.owait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2708,7 +2712,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "es1371: version v0.8 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "es1371: version v0.10 time " __TIME__ " " __DATE__ "\n"); while (index < NR_DEVICE && (pcidev = pci_find_device(PCI_VENDOR_ID_ENSONIQ, PCI_DEVICE_ID_ENSONIQ_ES1371, pcidev))) { if (pcidev->base_address[0] == 0 || diff -u --recursive --new-file v2.2.4/linux/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c --- v2.2.4/linux/drivers/sound/sonicvibes.c Wed Mar 10 15:29:47 1999 +++ linux/drivers/sound/sonicvibes.c Fri Mar 26 14:52:34 1999 @@ -51,6 +51,10 @@ * 16.12.98 0.9 Fix a few f_file & FMODE_ bugs * 06.01.99 0.10 remove the silly SA_INTERRUPT flag. * hopefully killed the egcs section type conflict + * 12.03.99 0.11 cinfo.blocks should be reset after GETxPTR ioctl. + * reported by Johan Maes + * 22.03.99 0.12 return EAGAIN instead of EBUSY when O_NONBLOCK + * read/write cannot be executed * */ @@ -1293,7 +1297,7 @@ if (cnt <= 0) { start_adc(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_adc.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1353,7 +1357,7 @@ if (cnt <= 0) { start_dac(s); if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->dma_dac.wait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1646,7 +1650,7 @@ spin_lock_irqsave(&s->lock, flags); sv_update_ptr(s); cinfo.bytes = s->dma_adc.total_bytes; - cinfo.blocks = s->dma_adc.total_bytes >> s->dma_adc.fragshift; + cinfo.blocks = s->dma_adc.count >> s->dma_adc.fragshift; cinfo.ptr = s->dma_adc.hwptr; if (s->dma_adc.mapped) s->dma_adc.count &= s->dma_adc.fragsize-1; @@ -1659,7 +1663,7 @@ spin_lock_irqsave(&s->lock, flags); sv_update_ptr(s); cinfo.bytes = s->dma_dac.total_bytes; - cinfo.blocks = s->dma_dac.total_bytes >> s->dma_dac.fragshift; + cinfo.blocks = s->dma_dac.count >> s->dma_dac.fragshift; cinfo.ptr = s->dma_dac.hwptr; if (s->dma_dac.mapped) s->dma_dac.count &= s->dma_dac.fragsize-1; @@ -1839,7 +1843,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.iwait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -1886,7 +1890,7 @@ cnt = count; if (cnt <= 0) { if (file->f_flags & O_NONBLOCK) - return ret ? ret : -EBUSY; + return ret ? ret : -EAGAIN; interruptible_sleep_on(&s->midi.owait); if (signal_pending(current)) return ret ? ret : -ERESTARTSYS; @@ -2271,7 +2275,7 @@ if (!pci_present()) /* No PCI bus in this machine! */ return -ENODEV; - printk(KERN_INFO "sv: version v0.10 time " __TIME__ " " __DATE__ "\n"); + printk(KERN_INFO "sv: version v0.12 time " __TIME__ " " __DATE__ "\n"); #if 0 if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT))) printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n"); diff -u --recursive --new-file v2.2.4/linux/fs/adfs/inode.c linux/fs/adfs/inode.c --- v2.2.4/linux/fs/adfs/inode.c Fri Jan 30 11:28:08 1998 +++ linux/fs/adfs/inode.c Fri Mar 26 13:57:41 1999 @@ -27,7 +27,7 @@ #define inode_dirindex(idx) (((idx) & 0xff) * 26 - 21) #define frag_id(x) (((x) >> 8) & 0x7fff) -#define off(x) (((x) & 0xff) ? ((x) & 0xff) - 1 : 0) +#define off(x) (((x) & 0xff) ? (((x) & 0xff) - 1) << sb->u.adfs_sb.s_dr->log2sharesize : 0) static inline int adfs_inode_validate_no (struct super_block *sb, unsigned int inode_no) { @@ -83,11 +83,20 @@ return 0; } + if (block < 0) { + adfs_error(sb, "adfs_bmap", "block(%d) < 0", block); + return 0; + } + + if (block > inode->i_blocks) + return 0; + + block += off(inode->u.adfs_i.file_id); + if (frag_id(inode->u.adfs_i.file_id) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + off(inode_frag (inode->i_ino)) + block; + blk = sb->u.adfs_sb.s_map_block + block; else - blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), - off (inode->u.adfs_i.file_id) + block); + blk = adfs_map_lookup (sb, frag_id(inode->u.adfs_i.file_id), block); return blk; } @@ -105,13 +114,13 @@ fragment = inode_frag (inode->i_ino); if (frag_id (fragment) == ADFS_ROOT_FRAG) - blk = sb->u.adfs_sb.s_map_block + off (fragment) + block; + blk = sb->u.adfs_sb.s_map_block + off(fragment) + block; else - blk = adfs_map_lookup (sb, frag_id (fragment), off (fragment) + block); + blk = adfs_map_lookup (sb, frag_id (fragment), off(fragment) + block); return blk; } -static int adfs_atts2mode (unsigned char mode, unsigned int filetype) +static int adfs_atts2mode(struct super_block *sb, unsigned char mode, unsigned int filetype) { int omode = 0; @@ -120,24 +129,29 @@ S_IRGRP|S_IWGRP|S_IXGRP| S_IROTH|S_IWOTH|S_IXOTH; } else { - if (mode & ADFS_NDA_DIRECTORY) - omode |= S_IFDIR|S_IRUSR|S_IXUSR|S_IXGRP|S_IXOTH; - else + if (mode & ADFS_NDA_DIRECTORY) { + omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; + omode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH; + } else omode |= S_IFREG; + if (mode & ADFS_NDA_OWNER_READ) { - omode |= S_IRUSR; + omode |= S_IRUGO & sb->u.adfs_sb.s_owner_mask; if (filetype == 0xfe6 /* UnixExec */) - omode |= S_IXUSR; + omode |= S_IXUGO & sb->u.adfs_sb.s_owner_mask; } + if (mode & ADFS_NDA_OWNER_WRITE) - omode |= S_IWUSR; + omode |= S_IWUGO & sb->u.adfs_sb.s_owner_mask; + if (mode & ADFS_NDA_PUBLIC_READ) { - omode |= S_IRGRP | S_IROTH; - if (filetype == 0xfe6) - omode |= S_IXGRP | S_IXOTH; + omode |= S_IRUGO & sb->u.adfs_sb.s_other_mask; + if (filetype == 0xfe6 /* UnixExec */) + omode |= S_IXUGO & sb->u.adfs_sb.s_other_mask; } + if (mode & ADFS_NDA_PUBLIC_WRITE) - omode |= S_IWGRP | S_IWOTH; + omode |= S_IWUGO & sb->u.adfs_sb.s_other_mask; } return omode; } @@ -150,8 +164,8 @@ int buffers; sb = inode->i_sb; - inode->i_uid = 0; - inode->i_gid = 0; + inode->i_uid = sb->u.adfs_sb.s_uid; + inode->i_gid = sb->u.adfs_sb.s_gid; inode->i_version = ++event; if (adfs_inode_validate_no (sb, inode->i_ino & 0xffffff00)) { @@ -186,7 +200,7 @@ goto bad; } adfs_dir_free (bh, buffers); - inode->i_mode = adfs_atts2mode (ide.mode, ide.filetype); + inode->i_mode = adfs_atts2mode(sb, ide.mode, ide.filetype); inode->i_nlink = 2; inode->i_size = ide.size; inode->i_blksize = PAGE_SIZE; @@ -204,13 +218,5 @@ return; bad: - inode->i_mode = 0; - inode->i_nlink = 1; - inode->i_size = 0; - inode->i_blksize = 0; - inode->i_blocks = 0; - inode->i_mtime = - inode->i_atime = - inode->i_ctime = 0; - inode->i_op = NULL; + make_bad_inode(inode); } diff -u --recursive --new-file v2.2.4/linux/fs/adfs/super.c linux/fs/adfs/super.c --- v2.2.4/linux/fs/adfs/super.c Mon Apr 6 17:41:00 1998 +++ linux/fs/adfs/super.c Fri Mar 26 13:57:41 1999 @@ -21,25 +21,26 @@ #include -static void adfs_put_super (struct super_block *sb); -static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz); -void adfs_read_inode (struct inode *inode); +static void adfs_put_super(struct super_block *sb); +static int adfs_remount(struct super_block *sb, int *flags, char *data); +static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz); +void adfs_read_inode(struct inode *inode); -void adfs_error (struct super_block *sb, const char *function, const char *fmt, ...) +void adfs_error(struct super_block *sb, const char *function, const char *fmt, ...) { char error_buf[128]; va_list args; - va_start (args, fmt); - vsprintf (error_buf, fmt, args); - va_end (args); + va_start(args, fmt); + vsprintf(error_buf, fmt, args); + va_end(args); - printk (KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", - kdevname (sb->s_dev), function ? ": " : "", + printk(KERN_CRIT "ADFS-fs error (device %s)%s%s: %s\n", + kdevname(sb->s_dev), function ? ": " : "", function ? function : "", error_buf); } -unsigned char adfs_calccrosscheck (struct super_block *sb, char *map) +static unsigned char adfs_calczonecheck(struct super_block *sb, char *map) { unsigned int v0, v1, v2, v3; int i; @@ -63,7 +64,7 @@ return v0 ^ v1 ^ v2 ^ v3; } -static int adfs_checkmap (struct super_block *sb) +static int adfs_checkmap(struct super_block *sb) { unsigned char crosscheck = 0, zonecheck = 1; int i; @@ -72,14 +73,14 @@ char *map; map = sb->u.adfs_sb.s_map[i]->b_data; - if (adfs_calccrosscheck (sb, map) != map[0]) { - adfs_error (sb, "adfs_checkmap", "zone %d fails zonecheck", i); + if (adfs_calczonecheck(sb, map) != map[0]) { + adfs_error(sb, "adfs_checkmap", "zone %d fails zonecheck", i); zonecheck = 0; } crosscheck ^= map[3]; } if (crosscheck != 0xff) - adfs_error (sb, "adfs_checkmap", "crosscheck != 0xff"); + adfs_error(sb, "adfs_checkmap", "crosscheck != 0xff"); return crosscheck == 0xff && zonecheck; } @@ -92,21 +93,73 @@ adfs_put_super, NULL, adfs_statfs, - NULL + adfs_remount }; -static void adfs_put_super (struct super_block *sb) +static void adfs_put_super(struct super_block *sb) { int i; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - kfree (sb->u.adfs_sb.s_map); - brelse (sb->u.adfs_sb.s_sbh); + brelse(sb->u.adfs_sb.s_map[i]); + kfree(sb->u.adfs_sb.s_map); + brelse(sb->u.adfs_sb.s_sbh); MOD_DEC_USE_COUNT; } -struct super_block *adfs_read_super (struct super_block *sb, void *data, int silent) +static int parse_options(struct super_block *sb, char *options) +{ + char *value, *opt; + + if (!options) + return 0; + + for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) { + value = strchr(opt, '='); + if (value) + *value++ = '\0'; + + if (!strcmp(opt, "uid")) { /* owner of all files */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "gid")) { /* group owner of all files */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "ownmask")) { /* owner permission mask */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8); + if (*value) + return -EINVAL; + } else + if (!strcmp(opt, "othmask")) { /* others permission mask */ + if (!value || !*value) + return -EINVAL; + sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8); + if (*value) + return -EINVAL; + } else { /* eh? say again. */ + printk("ADFS-fs: unrecognised mount option %s\n", opt); + return -EINVAL; + } + } + return 0; +} + +static int adfs_remount(struct super_block *sb, int *flags, char *data) +{ + return parse_options(sb, data); +} + +struct super_block *adfs_read_super(struct super_block *sb, void *data, int silent) { struct adfs_discrecord *dr; struct buffer_head *bh; @@ -114,28 +167,30 @@ kdev_t dev = sb->s_dev; int i, j; + /* set default options */ + sb->u.adfs_sb.s_uid = 0; + sb->u.adfs_sb.s_gid = 0; + sb->u.adfs_sb.s_owner_mask = S_IRWXU; + sb->u.adfs_sb.s_other_mask = S_IRWXG | S_IRWXO; + + if (parse_options(sb, data)) + goto error; + MOD_INC_USE_COUNT; - lock_super (sb); - set_blocksize (dev, BLOCK_SIZE); - if (!(bh = bread (dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { - unlock_super (sb); - adfs_error (sb, NULL, "unable to read superblock"); - MOD_DEC_USE_COUNT; - return NULL; + lock_super(sb); + set_blocksize(dev, BLOCK_SIZE); + if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) { + adfs_error(sb, NULL, "unable to read superblock"); + goto error_unlock; } b_data = bh->b_data + (ADFS_DISCRECORD % BLOCK_SIZE); - if (adfs_checkbblk (b_data)) { + if (adfs_checkbblk(b_data)) { if (!silent) - printk ("VFS: Can't find an adfs filesystem on dev " + printk("VFS: Can't find an adfs filesystem on dev " "%s.\n", kdevname(dev)); -failed_mount: - unlock_super (sb); - if (bh) - brelse (bh); - MOD_DEC_USE_COUNT; - return NULL; + goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); @@ -145,26 +200,26 @@ (sb->s_blocksize == 512 || sb->s_blocksize == 1024 || sb->s_blocksize == 2048 || sb->s_blocksize == 4096)) { - brelse (bh); - set_blocksize (dev, sb->s_blocksize); - bh = bread (dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize); + brelse(bh); + set_blocksize(dev, sb->s_blocksize); + bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize); if (!bh) { - adfs_error (sb, NULL, "couldn't read superblock on " + adfs_error(sb, NULL, "couldn't read superblock on " "2nd try."); - goto failed_mount; + goto error_unlock; } b_data = bh->b_data + (ADFS_DISCRECORD % sb->s_blocksize); - if (adfs_checkbblk (b_data)) { - adfs_error (sb, NULL, "disc record mismatch, very weird!"); - goto failed_mount; + if (adfs_checkbblk(b_data)) { + adfs_error(sb, NULL, "disc record mismatch, very weird!"); + goto error_free_bh; } dr = (struct adfs_discrecord *)(b_data + ADFS_DR_OFFSET); } if (sb->s_blocksize != bh->b_size) { if (!silent) - printk (KERN_ERR "VFS: Unsupported blocksize on dev " - "%s.\n", kdevname (dev)); - goto failed_mount; + printk(KERN_ERR "VFS: Unsupported blocksize on dev " + "%s.\n", kdevname(dev)); + goto error_free_bh; } /* blocksize on this device should now be set to the adfs log2secsize */ @@ -202,71 +257,81 @@ else sb->u.adfs_sb.s_map_block >>= -sb->u.adfs_sb.s_map2blk; - printk (KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n", + printk(KERN_DEBUG "ADFS: zone size %d, IDs per zone %d, map address %X size %d sectors\n", sb->u.adfs_sb.s_zone_size, sb->u.adfs_sb.s_ids_per_zone, sb->u.adfs_sb.s_map_block, sb->u.adfs_sb.s_map_size); - printk (KERN_DEBUG "ADFS: sector size %d, map bit size %d\n", - 1 << dr->log2secsize, 1 << dr->log2bpmb); + printk(KERN_DEBUG "ADFS: sector size %d, map bit size %d, share size %d\n", + 1 << dr->log2secsize, 1 << dr->log2bpmb, + 1 << (dr->log2secsize + dr->log2sharesize)); sb->s_magic = ADFS_SUPER_MAGIC; - sb->s_flags |= MS_RDONLY; /* we don't support writing yet */ - sb->u.adfs_sb.s_map = kmalloc (sb->u.adfs_sb.s_map_size * - sizeof (struct buffer_head *), GFP_KERNEL); + sb->u.adfs_sb.s_map = kmalloc(sb->u.adfs_sb.s_map_size * + sizeof(struct buffer_head *), GFP_KERNEL); if (sb->u.adfs_sb.s_map == NULL) { - adfs_error (sb, NULL, "not enough memory"); - goto failed_mount; + adfs_error(sb, NULL, "not enough memory"); + goto error_free_bh; } for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) { - sb->u.adfs_sb.s_map[i] = bread (dev, + sb->u.adfs_sb.s_map[i] = bread(dev, sb->u.adfs_sb.s_map_block + i, sb->s_blocksize); if (!sb->u.adfs_sb.s_map[i]) { for (j = 0; j < i; j++) - brelse (sb->u.adfs_sb.s_map[j]); - kfree (sb->u.adfs_sb.s_map); - adfs_error (sb, NULL, "unable to read map"); - goto failed_mount; + brelse(sb->u.adfs_sb.s_map[j]); + kfree(sb->u.adfs_sb.s_map); + adfs_error(sb, NULL, "unable to read map"); + goto error_free_bh; } } - if (!adfs_checkmap (sb)) { + if (!adfs_checkmap(sb)) { for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - adfs_error (sb, NULL, "map corrupted"); - goto failed_mount; + brelse(sb->u.adfs_sb.s_map[i]); + adfs_error(sb, NULL, "map corrupted"); + goto error_free_bh; } dr = (struct adfs_discrecord *)(sb->u.adfs_sb.s_map[0]->b_data + 4); - unlock_super (sb); + unlock_super(sb); /* * set up enough so that it can read an inode */ sb->s_op = &adfs_sops; - sb->u.adfs_sb.s_root = adfs_inode_generate (dr->root, 0); + sb->u.adfs_sb.s_root = adfs_inode_generate(dr->root, 0); sb->s_root = d_alloc_root(iget(sb, sb->u.adfs_sb.s_root), NULL); if (!sb->s_root) { - sb->s_dev = 0; for (i = 0; i < sb->u.adfs_sb.s_map_size; i++) - brelse (sb->u.adfs_sb.s_map[i]); - brelse (bh); - adfs_error (sb, NULL, "get root inode failed\n"); - MOD_DEC_USE_COUNT; - return NULL; + brelse(sb->u.adfs_sb.s_map[i]); + brelse(bh); + adfs_error(sb, NULL, "get root inode failed\n"); + goto error_dec_use; } return sb; + +error_free_bh: + if (bh) + brelse(bh); +error_unlock: + unlock_super(sb); +error_dec_use: + MOD_DEC_USE_COUNT; +error: + sb->s_dev = 0; + return NULL; } -static int adfs_statfs (struct super_block *sb, struct statfs *buf, int bufsiz) +static int adfs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz) { struct statfs tmp; const unsigned int nidlen = sb->u.adfs_sb.s_idlen + 1; tmp.f_type = ADFS_SUPER_MAGIC; tmp.f_bsize = sb->s_blocksize; - tmp.f_blocks = (sb->u.adfs_sb.s_dr->disc_size) >> (sb->s_blocksize_bits); + tmp.f_blocks = sb->u.adfs_sb.s_dr->disc_size_high << (32 - sb->s_blocksize_bits) | + sb->u.adfs_sb.s_dr->disc_size >> sb->s_blocksize_bits; tmp.f_files = tmp.f_blocks >> nidlen; { unsigned int i, j = 0; @@ -305,35 +370,35 @@ if (freelink <= nidlen) break; } while (mapindex < 8 * sb->s_blocksize); if (mapindex > 8 * sb->s_blocksize) - adfs_error (sb, NULL, "oversized free fragment\n"); + adfs_error(sb, NULL, "oversized free fragment\n"); else if (freelink) - adfs_error (sb, NULL, "undersized free fragment\n"); + adfs_error(sb, NULL, "undersized free fragment\n"); } tmp.f_bfree = tmp.f_bavail = j << (sb->u.adfs_sb.s_dr->log2bpmb - sb->s_blocksize_bits); } tmp.f_ffree = tmp.f_bfree >> nidlen; tmp.f_namelen = ADFS_NAME_LEN; - return copy_to_user (buf, &tmp, bufsiz) ? -EFAULT : 0; + return copy_to_user(buf, &tmp, bufsiz) ? -EFAULT : 0; } static struct file_system_type adfs_fs_type = { "adfs", FS_REQUIRES_DEV, adfs_read_super, NULL }; -__initfunc(int init_adfs_fs (void)) +__initfunc(int init_adfs_fs(void)) { - return register_filesystem (&adfs_fs_type); + return register_filesystem(&adfs_fs_type); } #ifdef MODULE -int init_module (void) +int init_module(void) { return init_adfs_fs(); } -void cleanup_module (void) +void cleanup_module(void) { - unregister_filesystem (&adfs_fs_type); + unregister_filesystem(&adfs_fs_type); } #endif diff -u --recursive --new-file v2.2.4/linux/fs/buffer.c linux/fs/buffer.c --- v2.2.4/linux/fs/buffer.c Tue Mar 23 14:35:48 1999 +++ linux/fs/buffer.c Wed Mar 24 09:37:18 1999 @@ -853,6 +853,7 @@ __brelse(buf); return; } + buf->b_count = 0; remove_from_queues(buf); put_last_free(buf); } diff -u --recursive --new-file v2.2.4/linux/fs/ioctl.c linux/fs/ioctl.c --- v2.2.4/linux/fs/ioctl.c Thu Nov 19 09:56:28 1998 +++ linux/fs/ioctl.c Wed Mar 24 08:08:53 1999 @@ -74,14 +74,20 @@ filp->f_flags &= ~flag; break; - case FIOASYNC: /* O_SYNC is not yet implemented, - but it's here for completeness. */ + case FIOASYNC: if ((error = get_user(on, (int *)arg)) != 0) break; + flag = on ? FASYNC : 0; + + /* Did FASYNC state change ? */ + if ((flag ^ filp->f_flags) & FASYNC) { + if (filp->f_op && filp->f_op->fasync) + filp->f_op->fasync(fd, filp, on); + } if (on) - filp->f_flags |= O_SYNC; + filp->f_flags |= FASYNC; else - filp->f_flags &= ~O_SYNC; + filp->f_flags &= ~FASYNC; break; default: diff -u --recursive --new-file v2.2.4/linux/fs/lockd/svcsubs.c linux/fs/lockd/svcsubs.c --- v2.2.4/linux/fs/lockd/svcsubs.c Wed Mar 10 15:29:49 1999 +++ linux/fs/lockd/svcsubs.c Thu Mar 25 09:23:34 1999 @@ -127,7 +127,7 @@ kfree(file); return; } - fp = &file->f_next; + fp = &f->f_next; } printk(KERN_WARNING "lockd: attempt to release unknown file!\n"); diff -u --recursive --new-file v2.2.4/linux/fs/ncpfs/sock.c linux/fs/ncpfs/sock.c --- v2.2.4/linux/fs/ncpfs/sock.c Sun Nov 8 14:03:06 1998 +++ linux/fs/ncpfs/sock.c Fri Mar 26 13:57:41 1999 @@ -94,7 +94,7 @@ poll_table wait_table; struct poll_table_entry entry; int init_timeout, max_timeout; - int timeout; long tmp_timeout; + int timeout; int retrans; int major_timeout_seen; int acknowledge_seen; diff -u --recursive --new-file v2.2.4/linux/fs/qnx4/file.c linux/fs/qnx4/file.c --- v2.2.4/linux/fs/qnx4/file.c Thu Nov 12 16:21:23 1998 +++ linux/fs/qnx4/file.c Fri Mar 26 13:57:41 1999 @@ -33,8 +33,6 @@ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) -#include -#include static int qnx4_readpage(struct file *file, struct page *page); diff -u --recursive --new-file v2.2.4/linux/fs/sysv/CHANGES linux/fs/sysv/CHANGES --- v2.2.4/linux/fs/sysv/CHANGES Thu Dec 31 10:29:02 1998 +++ linux/fs/sysv/CHANGES Wed Mar 24 12:34:35 1999 @@ -1,45 +1,55 @@ -Mon Dec 15 1997 Krzysztof G. Baranowski +Mon, 15 Dec 1997 Krzysztof G. Baranowski * namei.c: struct sysv_dir_inode_operations updated to use dentries. -Fri Jan 23 1998 Krzysztof G. Baranowski +Fri, 23 Jan 1998 Krzysztof G. Baranowski * inode.c: corrected 1 track offset setting (in sb->sv_block_base). Originally it was overridden (by setting to zero) in detected_[xenix,sysv4,sysv2,coherent]. Thanks to Andrzej Krzysztofowicz for identifying the problem. -Tue Jan 27 1998 Krzysztof G. Baranowski +Tue, 27 Jan 1998 Krzysztof G. Baranowski * inode.c: added 2048-byte block support to SystemV FS. Merged detected_bs[512,1024,2048]() into one function: void detected_bs (u_char type, struct super_block *sb). Thanks to Andrzej Krzysztofowicz for the patch. -Wed Feb 4 1998 Krzysztof G. Baranowski +Wed, 4 Feb 1998 Krzysztof G. Baranowski * namei.c: removed static subdir(); is_subdir() from dcache.c is used instead. Cosmetic changes. -Thu Dec 3 1998 Al Viro (viro@math.psu.edu) +Thu, 3 Dec 1998 Al Viro (viro@math.psu.edu) * namei.c (sysv_rmdir): Bugectomy: old check for victim being busy (inode->i_count) wasn't replaced (with checking dentry->d_count) and escaped Linus in the last round of changes. Shot and buried. -Wed Dec 9 1998 AV +Wed, 9 Dec 1998 AV * namei.c (do_sysv_rename): Fixed incorrect check for other owners + race. Removed checks that went to VFS. * namei.c (sysv_unlink): Removed checks that went to VFS. -Thu Dec 10 1998 AV +Thu, 10 Dec 1998 AV * namei.c (do_mknod): Removed dead code - mknod is never asked to create a symlink or directory. Incidentially, it wouldn't do it right if it would be called. -Sat Dec 26 1998 KGB +Sat, 26 Dec 1998 KGB * inode.c (detect_sysv4): Added detection of expanded s_type field (0x10, 0x20 and 0x30). Forced read-only access in this case. + +Sun, 21 Mar 1999 AV + * namei.c (sysv_link): + Fixed i_count usage that resulted in dcache corruption. + * inode.c: + Filled ->delete_inode() method with sysv_delete_inode(). + sysv_put_inode() is gone, as it tried to do ->delete_ + _inode()'s job. + * ialloc.c: (sysv_free_inode): + Fixed race. diff -u --recursive --new-file v2.2.4/linux/include/asm-alpha/mmu_context.h linux/include/asm-alpha/mmu_context.h --- v2.2.4/linux/include/asm-alpha/mmu_context.h Wed Sep 9 14:51:10 1998 +++ linux/include/asm-alpha/mmu_context.h Wed Mar 24 07:41:55 1999 @@ -14,11 +14,11 @@ /* * The maximum ASN's the processor supports. On the EV4 this is 63 * but the PAL-code doesn't actually use this information. On the - * EV5 this is 127. + * EV5 this is 127, and EV6 has 255. * * On the EV4, the ASNs are more-or-less useless anyway, as they are - * only used as an icache tag, not for TB entries. On the EV5 ASN's - * also validate the TB entries, and thus make a lot more sense. + * only used as an icache tag, not for TB entries. On the EV5 and EV6, + * ASN's also validate the TB entries, and thus make a lot more sense. * * The EV4 ASN's don't even match the architecture manual, ugh. And * I quote: "If a processor implements address space numbers (ASNs), @@ -73,7 +73,7 @@ extern unsigned long asn_cache; #endif /* __SMP__ */ -#define WIDTH_HARDWARE_ASN 7 +#define WIDTH_HARDWARE_ASN 8 #define ASN_FIRST_VERSION (1UL << (WIDTH_THIS_PROCESSOR + WIDTH_HARDWARE_ASN)) #define HARDWARE_ASN_MASK ((1UL << WIDTH_HARDWARE_ASN) - 1) diff -u --recursive --new-file v2.2.4/linux/include/asm-alpha/termios.h linux/include/asm-alpha/termios.h --- v2.2.4/linux/include/asm-alpha/termios.h Tue Dec 22 14:16:57 1998 +++ linux/include/asm-alpha/termios.h Wed Mar 24 11:46:00 1999 @@ -79,6 +79,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ /* eof=^D eol=\0 eol2=\0 erase=del diff -u --recursive --new-file v2.2.4/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h --- v2.2.4/linux/include/asm-i386/termios.h Tue Dec 22 14:16:58 1998 +++ linux/include/asm-i386/termios.h Wed Mar 24 11:45:59 1999 @@ -52,6 +52,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IR - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-m68k/termios.h linux/include/asm-m68k/termios.h --- v2.2.4/linux/include/asm-m68k/termios.h Tue Dec 22 14:16:58 1998 +++ linux/include/asm-m68k/termios.h Wed Mar 24 11:46:00 1999 @@ -60,6 +60,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-mips/termios.h linux/include/asm-mips/termios.h --- v2.2.4/linux/include/asm-mips/termios.h Tue Dec 22 14:16:58 1998 +++ linux/include/asm-mips/termios.h Wed Mar 24 11:45:59 1999 @@ -98,6 +98,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-ppc/termios.h linux/include/asm-ppc/termios.h --- v2.2.4/linux/include/asm-ppc/termios.h Thu Dec 31 10:29:02 1998 +++ linux/include/asm-ppc/termios.h Wed Mar 24 11:46:00 1999 @@ -184,6 +184,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/mmu_context.h linux/include/asm-sparc/mmu_context.h --- v2.2.4/linux/include/asm-sparc/mmu_context.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc/mmu_context.h Thu Mar 25 09:23:34 1999 @@ -22,9 +22,9 @@ #define destroy_context(mm) BTFIXUP_CALL(destroy_context)(mm) -/* After we have set current->mm to a new value, this activates - * the context for the new mm so we see the new mappings. +/* This need not do anything on Sparc32. The switch happens + * properly later as a side effect of calling flush_thread. */ -#define activate_context(tsk) switch_to_context(tsk) +#define activate_context(tsk) do { } while(0) #endif /* !(__SPARC_MMU_CONTEXT_H) */ diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/processor.h linux/include/asm-sparc/processor.h --- v2.2.4/linux/include/asm-sparc/processor.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc/processor.h Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: processor.h,v 1.69 1999/01/19 07:57:44 davem Exp $ +/* $Id: processor.h,v 1.70 1999/03/24 11:42:44 davem Exp $ * include/asm-sparc/processor.h * * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu) @@ -148,7 +148,10 @@ extern pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags); -#define copy_segments(nr, tsk, mm) do { } while (0) +#define copy_segments(__nr, __tsk, __mm) \ + if((__tsk) == current && \ + (__mm) != NULL) \ + flush_user_windows() #define release_segments(mm) do { } while (0) #define forget_segments() do { } while (0) diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/termios.h linux/include/asm-sparc/termios.h --- v2.2.4/linux/include/asm-sparc/termios.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc/termios.h Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: termios.h,v 1.28 1999/01/02 16:50:22 davem Exp $ */ +/* $Id: termios.h,v 1.29 1999/03/25 09:11:18 davem Exp $ */ #ifndef _SPARC_TERMIOS_H #define _SPARC_TERMIOS_H @@ -68,6 +68,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc/unistd.h linux/include/asm-sparc/unistd.h --- v2.2.4/linux/include/asm-sparc/unistd.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc/unistd.h Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: unistd.h,v 1.53 1999/02/21 02:34:54 anton Exp $ */ +/* $Id: unistd.h,v 1.54 1999/03/25 00:40:12 davem Exp $ */ #ifndef _SPARC_UNISTD_H #define _SPARC_UNISTD_H @@ -428,6 +428,8 @@ static __inline__ _syscall0(int,sync) static __inline__ _syscall0(pid_t,setsid) static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count) +static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) +static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) static __inline__ _syscall1(int,dup,int,fd) static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp) static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/termios.h linux/include/asm-sparc64/termios.h --- v2.2.4/linux/include/asm-sparc64/termios.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc64/termios.h Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: termios.h,v 1.7 1999/01/02 16:50:29 davem Exp $ */ +/* $Id: termios.h,v 1.8 1999/03/25 09:11:26 davem Exp $ */ #ifndef _SPARC64_TERMIOS_H #define _SPARC64_TERMIOS_H @@ -68,6 +68,7 @@ #define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ #define N_IRDA 11 /* Linux IrDa - http://www.cs.uit.no/~dagb/irda/irda.html */ #define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ #ifdef __KERNEL__ diff -u --recursive --new-file v2.2.4/linux/include/asm-sparc64/unistd.h linux/include/asm-sparc64/unistd.h --- v2.2.4/linux/include/asm-sparc64/unistd.h Tue Mar 23 14:35:48 1999 +++ linux/include/asm-sparc64/unistd.h Thu Mar 25 09:23:34 1999 @@ -1,4 +1,4 @@ -/* $Id: unistd.h,v 1.26 1999/02/10 22:24:35 jj Exp $ */ +/* $Id: unistd.h,v 1.27 1999/03/25 00:40:14 davem Exp $ */ #ifndef _SPARC64_UNISTD_H #define _SPARC64_UNISTD_H @@ -417,6 +417,7 @@ static __inline__ _syscall0(pid_t,setsid) static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count) static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count) +static __inline__ _syscall3(off_t,lseek,int,fd,off_t,offset,int,count) static __inline__ _syscall1(int,dup,int,fd) static __inline__ _syscall3(int,execve,__const__ char *,file,char **,argv,char **,envp) static __inline__ _syscall3(int,open,__const__ char *,file,int,flag,int,mode) diff -u --recursive --new-file v2.2.4/linux/include/linux/adfs_fs_sb.h linux/include/linux/adfs_fs_sb.h --- v2.2.4/linux/include/linux/adfs_fs_sb.h Fri Jul 31 17:05:52 1998 +++ linux/include/linux/adfs_fs_sb.h Fri Mar 26 13:57:41 1999 @@ -15,6 +15,10 @@ struct adfs_sb_info { struct buffer_head *s_sbh; /* buffer head containing disc record */ struct adfs_discrecord *s_dr; /* pointer to disc record in s_sbh */ + uid_t s_uid; /* owner uid */ + gid_t s_gid; /* owner gid */ + int s_owner_mask; /* ADFS Owner perm -> unix perm */ + int s_other_mask; /* ADFS Other perm -> unix perm */ __u16 s_zone_size; /* size of a map zone in bits */ __u16 s_ids_per_zone; /* max. no ids in one zone */ __u32 s_idlen; /* length of ID in map */ diff -u --recursive --new-file v2.2.4/linux/include/linux/hippidevice.h linux/include/linux/hippidevice.h --- v2.2.4/linux/include/linux/hippidevice.h Fri Oct 9 13:27:16 1998 +++ linux/include/linux/hippidevice.h Fri Mar 26 13:57:41 1999 @@ -52,6 +52,7 @@ void hippi_setup(struct device *dev); extern struct device *init_hippi_dev(struct device *, int); +extern void unregister_hipdev(struct device *dev); #endif #endif /* _LINUX_HIPPIDEVICE_H */ diff -u --recursive --new-file v2.2.4/linux/include/linux/if_arp.h linux/include/linux/if_arp.h --- v2.2.4/linux/include/linux/if_arp.h Tue Dec 22 14:16:58 1998 +++ linux/include/linux/if_arp.h Fri Mar 26 13:57:41 1999 @@ -66,6 +66,13 @@ #define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ #define ARPHRD_ECONET 782 /* Acorn Econet */ #define ARPHRD_IRDA 783 /* Linux/IR */ +/* ARP works differently on different FC media .. so */ +#define ARPHRD_FCPP 784 /* Point to point fibrechanel */ +#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +#define ARPHRD_FCFABRIC 786 /* Fibrechannel fabric */ + /* 787->799 reserved for fibrechannel media types */ + /* ARP protocol opcodes. */ #define ARPOP_REQUEST 1 /* ARP request */ diff -u --recursive --new-file v2.2.4/linux/include/linux/nfsd/nfsfh.h linux/include/linux/nfsd/nfsfh.h --- v2.2.4/linux/include/linux/nfsd/nfsfh.h Sat Sep 5 16:46:41 1998 +++ linux/include/linux/nfsd/nfsfh.h Fri Mar 26 13:08:01 1999 @@ -107,6 +107,9 @@ void nfsd_fh_init(void); void nfsd_fh_free(void); +void expire_all(void); +void expire_by_dentry(struct dentry *); + static __inline__ struct svc_fh * fh_copy(struct svc_fh *dst, struct svc_fh *src) { diff -u --recursive --new-file v2.2.4/linux/include/linux/synclink.h linux/include/linux/synclink.h --- v2.2.4/linux/include/linux/synclink.h Wed Dec 31 16:00:00 1969 +++ linux/include/linux/synclink.h Fri Mar 12 08:38:44 1999 @@ -0,0 +1,243 @@ +/* + * SyncLink Multiprotocol Serial Adapter Driver + * + * Copyright (C) 1998 by Microgate Corporation + * + * Redistribution of this file is permitted under + * the terms of the GNU Public License (GPL) + */ + +#ifndef _SYNCLINK_H_ +#define _SYNCLINK_H_ + +#define BOOLEAN int +#define TRUE 1 +#define FALSE 0 + +#define BIT0 0x0001 +#define BIT1 0x0002 +#define BIT2 0x0004 +#define BIT3 0x0008 +#define BIT4 0x0010 +#define BIT5 0x0020 +#define BIT6 0x0040 +#define BIT7 0x0080 +#define BIT8 0x0100 +#define BIT9 0x0200 +#define BIT10 0x0400 +#define BIT11 0x0800 +#define BIT12 0x1000 +#define BIT13 0x2000 +#define BIT14 0x4000 +#define BIT15 0x8000 +#define BIT16 0x00010000 +#define BIT17 0x00020000 +#define BIT18 0x00040000 +#define BIT19 0x00080000 +#define BIT20 0x00100000 +#define BIT21 0x00200000 +#define BIT22 0x00400000 +#define BIT23 0x00800000 +#define BIT24 0x01000000 +#define BIT25 0x02000000 +#define BIT26 0x04000000 +#define BIT27 0x08000000 +#define BIT28 0x10000000 +#define BIT29 0x20000000 +#define BIT30 0x40000000 +#define BIT31 0x80000000 + + +#define HDLC_MAX_FRAME_SIZE 4096 +#define MAX_ASYNC_TRANSMIT 4096 + +#define ASYNC_PARITY_NONE 0 +#define ASYNC_PARITY_EVEN 1 +#define ASYNC_PARITY_ODD 2 +#define ASYNC_PARITY_SPACE 3 + +#define HDLC_FLAG_UNDERRUN_ABORT7 0x0000 +#define HDLC_FLAG_UNDERRUN_ABORT15 0x0001 +#define HDLC_FLAG_UNDERRUN_FLAG 0x0002 +#define HDLC_FLAG_UNDERRUN_CRC 0x0004 +#define HDLC_FLAG_SHARE_ZERO 0x0010 +#define HDLC_FLAG_AUTO_CTS 0x0020 +#define HDLC_FLAG_AUTO_DCD 0x0040 +#define HDLC_FLAG_AUTO_RTS 0x0080 +#define HDLC_FLAG_RXC_DPLL 0x0100 +#define HDLC_FLAG_RXC_BRG 0x0200 +#define HDLC_FLAG_TXC_DPLL 0x0400 +#define HDLC_FLAG_TXC_BRG 0x0800 +#define HDLC_FLAG_DPLL_DIV8 0x1000 +#define HDLC_FLAG_DPLL_DIV16 0x2000 +#define HDLC_FLAG_DPLL_DIV32 0x0000 + +#define HDLC_CRC_NONE 0 +#define HDLC_CRC_16_CCITT 1 + +#define HDLC_TXIDLE_FLAGS 0 +#define HDLC_TXIDLE_ALT_ZEROS_ONES 1 +#define HDLC_TXIDLE_ZEROS 2 +#define HDLC_TXIDLE_ONES 3 +#define HDLC_TXIDLE_ALT_MARK_SPACE 4 +#define HDLC_TXIDLE_SPACE 5 +#define HDLC_TXIDLE_MARK 6 + +#define HDLC_ENCODING_NRZ 0 +#define HDLC_ENCODING_NRZB 1 +#define HDLC_ENCODING_NRZI_MARK 2 +#define HDLC_ENCODING_NRZI_SPACE 3 +#define HDLC_ENCODING_BIPHASE_MARK 4 +#define HDLC_ENCODING_BIPHASE_SPACE 5 +#define HDLC_ENCODING_BIPHASE_LEVEL 6 +#define HDLC_ENCODING_DIFF_BIPHASE_LEVEL 7 + +#define HDLC_PREAMBLE_LENGTH_8BITS 0 +#define HDLC_PREAMBLE_LENGTH_16BITS 1 +#define HDLC_PREAMBLE_LENGTH_32BITS 2 +#define HDLC_PREAMBLE_LENGTH_64BITS 3 + +#define HDLC_PREAMBLE_PATTERN_NONE 0 +#define HDLC_PREAMBLE_PATTERN_ZEROS 1 +#define HDLC_PREAMBLE_PATTERN_FLAGS 2 +#define HDLC_PREAMBLE_PATTERN_10 3 +#define HDLC_PREAMBLE_PATTERN_01 4 +#define HDLC_PREAMBLE_PATTERN_ONES 5 + +#define MGSL_MODE_ASYNC 1 +#define MGSL_MODE_HDLC 2 + +#define MGSL_BUS_TYPE_ISA 1 +#define MGSL_BUS_TYPE_EISA 2 +#define MGSL_BUS_TYPE_PCI 5 + +typedef struct _MGSL_PARAMS +{ + /* Common */ + + unsigned long mode; /* Asynchronous or HDLC */ + unsigned char loopback; /* internal loopback mode */ + + /* HDLC Only */ + + unsigned short flags; + unsigned char encoding; /* NRZ, NRZI, etc. */ + unsigned long clock_speed; /* external clock speed in bits per second */ + unsigned char addr_filter; /* receive HDLC address filter, 0xFF = disable */ + unsigned short crc_type; /* None, CRC16 or CRC16-CCITT */ + unsigned char preamble_length; + unsigned char preamble; + + /* Async Only */ + + unsigned long data_rate; /* bits per second */ + unsigned char data_bits; /* 7 or 8 data bits */ + unsigned char stop_bits; /* 1 or 2 stop bits */ + unsigned char parity; /* none, even, or odd */ + +} MGSL_PARAMS, *PMGSL_PARAMS; + +#define MICROGATE_VENDOR_ID 0x13c0 +#define SYNCLINK_DEVICE_ID 0x0010 +#define MGSL_MAX_SERIAL_NUMBER 30 + +/* +** device diagnostics status +*/ + +#define DiagStatus_OK 0 +#define DiagStatus_AddressFailure 1 +#define DiagStatus_AddressConflict 2 +#define DiagStatus_IrqFailure 3 +#define DiagStatus_IrqConflict 4 +#define DiagStatus_DmaFailure 5 +#define DiagStatus_DmaConflict 6 +#define DiagStatus_PciAdapterNotFound 7 +#define DiagStatus_CantAssignPciResources 8 +#define DiagStatus_CantAssignPciMemAddr 9 +#define DiagStatus_CantAssignPciIoAddr 10 +#define DiagStatus_CantAssignPciIrq 11 +#define DiagStatus_MemoryError 12 + +#define SerialSignal_DCD 0x01 /* Data Carrier Detect */ +#define SerialSignal_TXD 0x02 /* Transmit Data */ +#define SerialSignal_RI 0x04 /* Ring Indicator */ +#define SerialSignal_RXD 0x08 /* Receive Data */ +#define SerialSignal_CTS 0x10 /* Clear to Send */ +#define SerialSignal_RTS 0x20 /* Request to Send */ +#define SerialSignal_DSR 0x40 /* Data Set Ready */ +#define SerialSignal_DTR 0x80 /* Data Terminal Ready */ + + +/* + * Counters of the input lines (CTS, DSR, RI, CD) interrupts + */ +struct mgsl_icount { + __u32 cts, dsr, rng, dcd, tx, rx; + __u32 frame, parity, overrun, brk; + __u32 buf_overrun; + __u32 txok; + __u32 txunder; + __u32 txabort; + __u32 txtimeout; + __u32 rxshort; + __u32 rxlong; + __u32 rxabort; + __u32 rxover; + __u32 rxcrc; + __u32 rxok; + __u32 exithunt; + __u32 rxidle; +}; + + +#define DEBUG_LEVEL_DATA 1 +#define DEBUG_LEVEL_ERROR 2 +#define DEBUG_LEVEL_INFO 3 +#define DEBUG_LEVEL_BH 4 +#define DEBUG_LEVEL_ISR 5 + +/* +** Event bit flags for use with MgslWaitEvent +*/ + +#define MgslEvent_DsrActive 0x0001 +#define MgslEvent_DsrInactive 0x0002 +#define MgslEvent_Dsr 0x0003 +#define MgslEvent_CtsActive 0x0004 +#define MgslEvent_CtsInactive 0x0008 +#define MgslEvent_Cts 0x000c +#define MgslEvent_DcdActive 0x0010 +#define MgslEvent_DcdInactive 0x0020 +#define MgslEvent_Dcd 0x0030 +#define MgslEvent_RiActive 0x0040 +#define MgslEvent_RiInactive 0x0080 +#define MgslEvent_Ri 0x00c0 +#define MgslEvent_ExitHuntMode 0x0100 +#define MgslEvent_IdleReceived 0x0200 + +/* Private IOCTL codes: + * + * MGSL_IOCSPARAMS set MGSL_PARAMS structure values + * MGSL_IOCGPARAMS get current MGSL_PARAMS structure values + * MGSL_IOCSTXIDLE set current transmit idle mode + * MGSL_IOCGTXIDLE get current transmit idle mode + * MGSL_IOCTXENABLE enable or disable transmitter + * MGSL_IOCRXENABLE enable or disable receiver + * MGSL_IOCTXABORT abort transmitting frame (HDLC) + * MGSL_IOCGSTATS return current statistics + * MGSL_IOCWAITEVENT wait for specified event to occur + */ +#define MGSL_MAGIC_IOC 'm' +#define MGSL_IOCSPARAMS _IOW(MGSL_MAGIC_IOC,0,sizeof(MGSL_PARAMS)) +#define MGSL_IOCGPARAMS _IOR(MGSL_MAGIC_IOC,1,sizeof(MGSL_PARAMS)) +#define MGSL_IOCSTXIDLE _IO(MGSL_MAGIC_IOC,2) +#define MGSL_IOCGTXIDLE _IO(MGSL_MAGIC_IOC,3) +#define MGSL_IOCTXENABLE _IO(MGSL_MAGIC_IOC,4) +#define MGSL_IOCRXENABLE _IO(MGSL_MAGIC_IOC,5) +#define MGSL_IOCTXABORT _IO(MGSL_MAGIC_IOC,6) +#define MGSL_IOCGSTATS _IO(MGSL_MAGIC_IOC,7) +#define MGSL_IOCWAITEVENT _IO(MGSL_MAGIC_IOC,8) +#define MGSL_IOCCLRMODCOUNT _IO(MGSL_MAGIC_IOC,15) + +#endif /* _SYNCLINK_H_ */ diff -u --recursive --new-file v2.2.4/linux/include/net/pkt_cls.h linux/include/net/pkt_cls.h --- v2.2.4/linux/include/net/pkt_cls.h Tue Mar 23 14:35:48 1999 +++ linux/include/net/pkt_cls.h Thu Mar 25 09:23:34 1999 @@ -79,9 +79,8 @@ extern __inline__ unsigned long cls_set_class(unsigned long *clp, unsigned long cl) { - net_serialize_enter(); cl = xchg(clp, cl); - net_serialize_leave(); + synchronize_bh(); return cl; } diff -u --recursive --new-file v2.2.4/linux/include/net/sock.h linux/include/net/sock.h --- v2.2.4/linux/include/net/sock.h Tue Mar 23 14:35:48 1999 +++ linux/include/net/sock.h Fri Mar 26 13:08:33 1999 @@ -923,14 +923,6 @@ return in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; } -#ifdef __SMP__ -#define net_serialize_enter() start_bh_atomic() -#define net_serialize_leave() end_bh_atomic() -#else -#define net_serialize_enter() barrier(); -#define net_serialize_leave() barrier(); -#endif - /* * Enable debug/info messages */ diff -u --recursive --new-file v2.2.4/linux/kernel/acct.c linux/kernel/acct.c --- v2.2.4/linux/kernel/acct.c Tue Mar 23 14:35:48 1999 +++ linux/kernel/acct.c Tue Mar 23 16:57:38 1999 @@ -194,13 +194,13 @@ } if (old_acct) { do_acct_process(0,old_acct); - filp_close(old_acct); + filp_close(old_acct, NULL); } out: unlock_kernel(); return error; out_err: - filp_close(file); + filp_close(file, NULL); goto out; } diff -u --recursive --new-file v2.2.4/linux/kernel/fork.c linux/kernel/fork.c --- v2.2.4/linux/kernel/fork.c Tue Mar 23 14:35:48 1999 +++ linux/kernel/fork.c Fri Mar 26 09:45:07 1999 @@ -57,33 +57,39 @@ #define uidhashfn(uid) (((uid >> 8) ^ uid) & (UIDHASH_SZ - 1)) +/* + * These routines must be called with the uidhash spinlock held! + */ static inline void uid_hash_insert(struct user_struct *up, unsigned int hashent) { - spin_lock(&uidhash_lock); if((up->next = uidhash[hashent]) != NULL) uidhash[hashent]->pprev = &up->next; up->pprev = &uidhash[hashent]; uidhash[hashent] = up; - spin_unlock(&uidhash_lock); } static inline void uid_hash_remove(struct user_struct *up) { - spin_lock(&uidhash_lock); if(up->next) up->next->pprev = up->pprev; *up->pprev = up->next; - spin_unlock(&uidhash_lock); } -static inline struct user_struct *uid_find(unsigned short uid, unsigned int hashent) +static inline struct user_struct *uid_hash_find(unsigned short uid, unsigned int hashent) { - struct user_struct *up; + struct user_struct *up, *next; - spin_lock(&uidhash_lock); - for(up = uidhash[hashent]; (up && up->uid != uid); up = up->next) - ; - spin_unlock(&uidhash_lock); + next = uidhash[hashent]; + for (;;) { + up = next; + if (next) { + next = up->next; + if (up->uid != uid) + continue; + atomic_inc(&up->count); + } + break; + } return up; } @@ -94,7 +100,9 @@ if (up) { p->user = NULL; if (atomic_dec_and_test(&up->count)) { + spin_lock(&uidhash_lock); uid_hash_remove(up); + spin_unlock(&uidhash_lock); kmem_cache_free(uid_cachep, up); } } @@ -103,20 +111,37 @@ int alloc_uid(struct task_struct *p) { unsigned int hashent = uidhashfn(p->uid); - struct user_struct *up = uid_find(p->uid, hashent); + struct user_struct *up; + + spin_lock(&uidhash_lock); + up = uid_hash_find(p->uid, hashent); + spin_unlock(&uidhash_lock); - p->user = up; if (!up) { - up = kmem_cache_alloc(uid_cachep, SLAB_KERNEL); - if (!up) + struct user_struct *new; + + new = kmem_cache_alloc(uid_cachep, SLAB_KERNEL); + if (!new) return -EAGAIN; - p->user = up; - up->uid = p->uid; - atomic_set(&up->count, 0); - uid_hash_insert(up, hashent); - } + new->uid = p->uid; + atomic_set(&new->count, 1); + + /* + * Before adding this, check whether we raced + * on adding the same user already.. + */ + spin_lock(&uidhash_lock); + up = uid_hash_find(p->uid, hashent); + if (up) { + kmem_cache_free(uid_cachep, new); + } else { + uid_hash_insert(new, hashent); + up = new; + } + spin_unlock(&uidhash_lock); - atomic_inc(&up->count); + } + p->user = up; return 0; } diff -u --recursive --new-file v2.2.4/linux/kernel/signal.c linux/kernel/signal.c --- v2.2.4/linux/kernel/signal.c Tue Dec 22 14:16:59 1998 +++ linux/kernel/signal.c Fri Mar 26 14:01:26 1999 @@ -363,8 +363,27 @@ } sigaddset(&t->signal, sig); - if (!sigismember(&t->blocked, sig)) + if (!sigismember(&t->blocked, sig)) { t->sigpending = 1; +#ifdef __SMP__ + /* + * If the task is running on a different CPU + * force a reschedule on the other CPU - note that + * the code below is a tad loose and might occasionally + * kick the wrong CPU if we catch the process in the + * process of changing - but no harm is done by that + * other than doing an extra (lightweight) IPI interrupt. + * + * note that we rely on the previous spin_lock to + * lock interrupts for us! No need to set need_resched + * since signal event passing goes through ->blocked. + */ + spin_lock(&runqueue_lock); + if (t->has_cpu && t->processor != smp_processor_id()) + smp_send_reschedule(t->processor); + spin_unlock(&runqueue_lock); +#endif /* __SMP__ */ + } out: spin_unlock_irqrestore(&t->sigmask_lock, flags); diff -u --recursive --new-file v2.2.4/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c --- v2.2.4/linux/net/appletalk/ddp.c Thu Jan 7 15:11:41 1999 +++ linux/net/appletalk/ddp.c Fri Mar 26 14:01:40 1999 @@ -356,7 +356,7 @@ /* * Scan the networks. */ - + atif->status |= ATIF_PROBE; for(netct = 0; netct <= netrange; netct++) { /* @@ -374,8 +374,10 @@ */ aarp_probe_network(atif); - if(!(atif->status & ATIF_PROBE_FAIL)) + if(!(atif->status & ATIF_PROBE_FAIL)) { + atif->status &= ~ATIF_PROBE; return (0); + } } atif->status &= ~ATIF_PROBE_FAIL; } @@ -383,7 +385,7 @@ if(probe_net > ntohs(atif->nets.nr_lastnet)) probe_net = ntohs(atif->nets.nr_firstnet); } - + atif->status &= ~ATIF_PROBE; return (-EADDRINUSE); /* Network is full... */ } diff -u --recursive --new-file v2.2.4/linux/net/core/dev.c linux/net/core/dev.c --- v2.2.4/linux/net/core/dev.c Tue Mar 23 14:35:48 1999 +++ linux/net/core/dev.c Thu Mar 25 09:23:34 1999 @@ -232,9 +232,8 @@ { if(pt==(*pt1)) { - net_serialize_enter(); *pt1=pt->next; - net_serialize_leave(); + synchronize_bh(); #ifdef CONFIG_NET_FASTROUTE if (pt->data) netdev_fastroute_obstacles--; @@ -1823,9 +1822,8 @@ /* And unlink it from device chain. */ for (dp = &dev_base; (d=*dp) != NULL; dp=&d->next) { if (d == dev) { - net_serialize_enter(); *dp = d->next; - net_serialize_leave(); + synchronize_bh(); d->next = NULL; if (dev->destructor) @@ -1986,9 +1984,8 @@ /* * It failed to come up. Unhook it. */ - net_serialize_enter(); *dp = dev->next; - net_serialize_leave(); + synchronize_bh(); } else { diff -u --recursive --new-file v2.2.4/linux/net/core/filter.c linux/net/core/filter.c --- v2.2.4/linux/net/core/filter.c Tue Mar 23 14:35:48 1999 +++ linux/net/core/filter.c Thu Mar 25 09:23:34 1999 @@ -441,9 +441,8 @@ if ((err = sk_chk_filter(fp->insns, fp->len))==0) { struct sk_filter *old_fp = sk->filter; - net_serialize_enter(); sk->filter = fp; - net_serialize_leave(); + synchronize_bh(); fp = old_fp; } diff -u --recursive --new-file v2.2.4/linux/net/core/neighbour.c linux/net/core/neighbour.c --- v2.2.4/linux/net/core/neighbour.c Tue Mar 23 14:35:48 1999 +++ linux/net/core/neighbour.c Thu Mar 25 09:23:34 1999 @@ -337,9 +337,8 @@ for (np = &tbl->phash_buckets[hash_val]; (n=*np) != NULL; np = &n->next) { if (memcmp(n->key, pkey, key_len) == 0 && n->dev == dev) { - net_serialize_enter(); *np = n->next; - net_serialize_leave(); + synchronize_bh(); if (tbl->pdestructor) tbl->pdestructor(n); kfree(n); @@ -358,9 +357,8 @@ np = &tbl->phash_buckets[h]; for (np = &tbl->phash_buckets[h]; (n=*np) != NULL; np = &n->next) { if (n->dev == dev || dev == NULL) { - net_serialize_enter(); *np = n->next; - net_serialize_leave(); + synchronize_bh(); if (tbl->pdestructor) tbl->pdestructor(n); kfree(n); @@ -963,9 +961,8 @@ return; for (p = &tbl->parms.next; *p; p = &(*p)->next) { if (*p == parms) { - net_serialize_enter(); *p = parms->next; - net_serialize_leave(); + synchronize_bh(); #ifdef CONFIG_SYSCTL neigh_sysctl_unregister(parms); #endif @@ -1014,9 +1011,8 @@ printk(KERN_CRIT "neighbour leakage\n"); for (tp = &neigh_tables; *tp; tp = &(*tp)->next) { if (*tp == tbl) { - net_serialize_enter(); *tp = tbl->next; - net_serialize_leave(); + synchronize_bh(); break; } } diff -u --recursive --new-file v2.2.4/linux/net/core/sock.c linux/net/core/sock.c --- v2.2.4/linux/net/core/sock.c Tue Mar 23 14:35:48 1999 +++ linux/net/core/sock.c Thu Mar 25 09:23:34 1999 @@ -7,7 +7,7 @@ * handler for protocols to use and generic option handler. * * - * Version: $Id: sock.c,v 1.77 1999/03/21 05:22:26 davem Exp $ + * Version: $Id: sock.c,v 1.78 1999/03/25 10:03:55 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -346,9 +346,8 @@ filter = sk->filter; - net_serialize_enter(); sk->filter = NULL; - net_serialize_leave(); + synchronize_bh(); if (filter) sk_filter_release(sk, filter); diff -u --recursive --new-file v2.2.4/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c --- v2.2.4/linux/net/ipv4/af_inet.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/af_inet.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * PF_INET protocol family socket handler. * - * Version: $Id: af_inet.c,v 1.85 1999/03/21 05:22:28 davem Exp $ + * Version: $Id: af_inet.c,v 1.86 1999/03/25 00:38:15 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -661,6 +661,7 @@ lock_sock(sk); tcp_set_state(sk, TCP_CLOSE); release_sock(sk); + sk->zapped = 0; } sock->state = SS_UNCONNECTED; return sock_error(sk); diff -u --recursive --new-file v2.2.4/linux/net/ipv4/devinet.c linux/net/ipv4/devinet.c --- v2.2.4/linux/net/ipv4/devinet.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/devinet.c Thu Mar 25 09:23:34 1999 @@ -1,7 +1,7 @@ /* * NET3 IP device support routines. * - * Version: $Id: devinet.c,v 1.26 1999/03/21 05:22:31 davem Exp $ + * Version: $Id: devinet.c,v 1.27 1999/03/25 10:04:06 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -138,9 +138,8 @@ #ifdef CONFIG_SYSCTL devinet_sysctl_unregister(&in_dev->cnf); #endif - net_serialize_enter(); in_dev->dev->ip_ptr = NULL; - net_serialize_leave(); + synchronize_bh(); neigh_parms_release(&arp_tbl, in_dev->arp_parms); kfree(in_dev); } @@ -174,9 +173,8 @@ ifap1 = &ifa->ifa_next; continue; } - net_serialize_enter(); *ifap1 = ifa->ifa_next; - net_serialize_leave(); + synchronize_bh(); rtmsg_ifa(RTM_DELADDR, ifa); notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); @@ -186,9 +184,8 @@ /* 2. Unlink it */ - net_serialize_enter(); *ifap = ifa1->ifa_next; - net_serialize_leave(); + synchronize_bh(); /* 3. Announce address deletion */ @@ -244,9 +241,8 @@ } ifa->ifa_next = *ifap; - net_serialize_enter(); + wmb(); *ifap = ifa; - net_serialize_leave(); /* Send message first, then call notifier. Notifier will trigger FIB update, so that diff -u --recursive --new-file v2.2.4/linux/net/ipv4/fib_hash.c linux/net/ipv4/fib_hash.c --- v2.2.4/linux/net/ipv4/fib_hash.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/fib_hash.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * IPv4 FIB: lookup engine and maintenance routines. * - * Version: $Id: fib_hash.c,v 1.7 1999/03/21 05:22:32 davem Exp $ + * Version: $Id: fib_hash.c,v 1.8 1999/03/25 10:04:17 davem Exp $ * * Authors: Alexey Kuznetsov, * @@ -566,9 +566,8 @@ if (del_fp) { f = *del_fp; /* Unlink replaced node */ - net_serialize_enter(); *del_fp = f->fn_next; - net_serialize_leave(); + synchronize_bh(); if (!(f->fn_state&FN_S_ZOMBIE)) rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); @@ -656,9 +655,8 @@ rtmsg_fib(RTM_DELROUTE, f, z, tb->tb_id, n, req); if (matched != 1) { - net_serialize_enter(); *del_fp = f->fn_next; - net_serialize_leave(); + synchronize_bh(); if (f->fn_state&FN_S_ACCESSED) rt_cache_flush(-1); @@ -689,9 +687,8 @@ struct fib_info *fi = FIB_INFO(f); if (fi && ((f->fn_state&FN_S_ZOMBIE) || (fi->fib_flags&RTNH_F_DEAD))) { - net_serialize_enter(); *fp = f->fn_next; - net_serialize_leave(); + synchronize_bh(); fn_free_node(f); found++; diff -u --recursive --new-file v2.2.4/linux/net/ipv4/fib_rules.c linux/net/ipv4/fib_rules.c --- v2.2.4/linux/net/ipv4/fib_rules.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/fib_rules.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * IPv4 Forwarding Information Base: policy rules. * - * Version: $Id: fib_rules.c,v 1.8 1999/03/21 05:22:33 davem Exp $ + * Version: $Id: fib_rules.c,v 1.9 1999/03/25 10:04:23 davem Exp $ * * Authors: Alexey Kuznetsov, * @@ -101,9 +101,10 @@ (!rtm->rtm_table || (r && rtm->rtm_table == r->r_table))) { if (r == &local_rule) return -EPERM; - net_serialize_enter(); + *rp = r->r_next; - net_serialize_leave(); + synchronize_bh(); + if (r != &default_rule && r != &main_rule) kfree(r); return 0; diff -u --recursive --new-file v2.2.4/linux/net/ipv4/igmp.c linux/net/ipv4/igmp.c --- v2.2.4/linux/net/ipv4/igmp.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/igmp.c Thu Mar 25 09:23:34 1999 @@ -8,7 +8,7 @@ * the older version didn't come out right using gcc 2.5.8, the newer one * seems to fall out with gcc 2.6.2. * - * Version: $Id: igmp.c,v 1.29 1999/03/21 05:22:36 davem Exp $ + * Version: $Id: igmp.c,v 1.30 1999/03/25 10:04:10 davem Exp $ * * Authors: * Alan Cox @@ -463,9 +463,9 @@ for (ip=&in_dev->mc_list; (i=*ip)!=NULL; ip=&i->next) { if (i->multiaddr==addr) { if (--i->users == 0) { - net_serialize_enter(); *ip = i->next; - net_serialize_leave(); + synchronize_bh(); + igmp_group_dropped(i); if (in_dev->dev->flags & IFF_UP) ip_rt_multicast_event(in_dev); @@ -613,9 +613,10 @@ struct in_device *in_dev; if (--iml->count) return 0; - net_serialize_enter(); + *imlp = iml->next; - net_serialize_leave(); + synchronize_bh(); + in_dev = inetdev_by_index(iml->multi.imr_ifindex); if (in_dev) ip_mc_dec_group(in_dev, imr->imr_multiaddr.s_addr); diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_gre.c linux/net/ipv4/ip_gre.c --- v2.2.4/linux/net/ipv4/ip_gre.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/ip_gre.c Thu Mar 25 09:23:34 1999 @@ -211,10 +211,9 @@ { struct ip_tunnel **tp = ipgre_bucket(t); - net_serialize_enter(); t->next = *tp; + wmb(); *tp = t; - net_serialize_leave(); } static void ipgre_tunnel_unlink(struct ip_tunnel *t) @@ -223,9 +222,8 @@ for (tp = ipgre_bucket(t); *tp; tp = &(*tp)->next) { if (t == *tp) { - net_serialize_enter(); *tp = t->next; - net_serialize_leave(); + synchronize_bh(); break; } } diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_output.c linux/net/ipv4/ip_output.c --- v2.2.4/linux/net/ipv4/ip_output.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/ip_output.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * The Internet Protocol (IP) output module. * - * Version: $Id: ip_output.c,v 1.66 1999/03/21 05:22:41 davem Exp $ + * Version: $Id: ip_output.c,v 1.67 1999/03/25 00:43:00 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -350,7 +350,7 @@ into account). Actually, tcp should make it. --ANK (980801) */ iph->frag_off |= __constant_htons(IP_DF); - printk(KERN_DEBUG "sending pkt_too_big to self\n"); + NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n")); /* icmp_send is not reenterable, so that bh_atomic... --ANK */ start_bh_atomic(); diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c --- v2.2.4/linux/net/ipv4/ip_sockglue.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/ip_sockglue.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * The IP to API glue. * - * Version: $Id: ip_sockglue.c,v 1.40 1999/03/21 05:22:42 davem Exp $ + * Version: $Id: ip_sockglue.c,v 1.41 1999/03/25 10:04:29 davem Exp $ * * Authors: see ip.c * @@ -209,9 +209,9 @@ kfree(new_ra); return -EADDRINUSE; } - net_serialize_enter(); *rap = ra->next; - net_serialize_leave(); + synchronize_bh(); + if (ra->destructor) ra->destructor(sk); kfree(ra); @@ -222,10 +222,11 @@ return -ENOBUFS; new_ra->sk = sk; new_ra->destructor = destructor; + new_ra->next = ra; - net_serialize_enter(); + wmb(); *rap = new_ra; - net_serialize_leave(); + return 0; } diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c --- v2.2.4/linux/net/ipv4/ipip.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/ipip.c Thu Mar 25 09:23:34 1999 @@ -1,7 +1,7 @@ /* * Linux NET3: IP/IP protocol decoder. * - * Version: $Id: ipip.c,v 1.25 1999/03/21 05:22:43 davem Exp $ + * Version: $Id: ipip.c,v 1.26 1999/03/25 10:04:32 davem Exp $ * * Authors: * Sam Lantinga (slouken@cs.ucdavis.edu) 02/01/95 @@ -182,9 +182,8 @@ for (tp = ipip_bucket(t); *tp; tp = &(*tp)->next) { if (t == *tp) { - net_serialize_enter(); *tp = t->next; - net_serialize_leave(); + synchronize_bh(); break; } } @@ -194,10 +193,9 @@ { struct ip_tunnel **tp = ipip_bucket(t); - net_serialize_enter(); t->next = *tp; + wmb(); *tp = t; - net_serialize_leave(); } struct ip_tunnel * ipip_tunnel_locate(struct ip_tunnel_parm *parms, int create) @@ -265,9 +263,8 @@ static void ipip_tunnel_destroy(struct device *dev) { if (dev == &ipip_fb_tunnel_dev) { - net_serialize_enter(); tunnels_wc[0] = NULL; - net_serialize_leave(); + synchronize_bh(); } else { ipip_tunnel_unlink((struct ip_tunnel*)dev->priv); kfree(dev); diff -u --recursive --new-file v2.2.4/linux/net/ipv4/ipmr.c linux/net/ipv4/ipmr.c --- v2.2.4/linux/net/ipv4/ipmr.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/ipmr.c Thu Mar 25 09:23:34 1999 @@ -9,7 +9,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: ipmr.c,v 1.39 1999/03/21 05:22:44 davem Exp $ + * Version: $Id: ipmr.c,v 1.40 1999/03/25 10:04:25 davem Exp $ * * Fixes: * Michael Chastain : Incorrect size of copying. @@ -661,9 +661,10 @@ { if (sk == mroute_socket) { ipv4_devconf.mc_forwarding = 0; - net_serialize_enter(); + mroute_socket=NULL; - net_serialize_leave(); + synchronize_bh(); + mroute_close(sk); } } diff -u --recursive --new-file v2.2.4/linux/net/ipv4/route.c linux/net/ipv4/route.c --- v2.2.4/linux/net/ipv4/route.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv4/route.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * * ROUTE - implementation of the IP router. * - * Version: $Id: route.c,v 1.64 1999/03/23 21:21:13 davem Exp $ + * Version: $Id: route.c,v 1.65 1999/03/25 10:04:35 davem Exp $ * * Authors: Ross Biro, * Fred N. van Kempen, @@ -338,11 +338,11 @@ rt_deadline = 0; - net_serialize_enter(); + start_bh_atomic(); for (i=0; iu.rt_next; @@ -350,9 +350,9 @@ rt_free(rth); } - net_serialize_enter(); + start_bh_atomic(); } - net_serialize_leave(); + end_bh_atomic(); } void rt_cache_flush(int delay) diff -u --recursive --new-file v2.2.4/linux/net/ipv6/addrconf.c linux/net/ipv6/addrconf.c --- v2.2.4/linux/net/ipv6/addrconf.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv6/addrconf.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: addrconf.c,v 1.47 1999/03/21 05:22:50 davem Exp $ + * $Id: addrconf.c,v 1.48 1999/03/25 10:04:43 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -340,9 +340,9 @@ for (; iter; iter = iter->lst_next) { if (iter == ifp) { - net_serialize_enter(); *back = ifp->lst_next; - net_serialize_leave(); + synchronize_bh(); + ifp->lst_next = NULL; break; } @@ -354,9 +354,9 @@ for (; iter; iter = iter->if_next) { if (iter == ifp) { - net_serialize_enter(); *back = ifp->if_next; - net_serialize_leave(); + synchronize_bh(); + ifp->if_next = NULL; break; } diff -u --recursive --new-file v2.2.4/linux/net/ipv6/ipv6_sockglue.c linux/net/ipv6/ipv6_sockglue.c --- v2.2.4/linux/net/ipv6/ipv6_sockglue.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv6/ipv6_sockglue.c Thu Mar 25 09:23:34 1999 @@ -7,7 +7,7 @@ * * Based on linux/net/ipv4/ip_sockglue.c * - * $Id: ipv6_sockglue.c,v 1.25 1999/03/21 05:22:54 davem Exp $ + * $Id: ipv6_sockglue.c,v 1.26 1999/03/25 10:04:53 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -86,9 +86,10 @@ kfree(new_ra); return -EADDRINUSE; } - net_serialize_enter(); + *rap = ra->next; - net_serialize_leave(); + synchronize_bh(); + if (ra->destructor) ra->destructor(sk); kfree(ra); diff -u --recursive --new-file v2.2.4/linux/net/ipv6/mcast.c linux/net/ipv6/mcast.c --- v2.2.4/linux/net/ipv6/mcast.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv6/mcast.c Thu Mar 25 09:23:34 1999 @@ -5,7 +5,7 @@ * Authors: * Pedro Roque * - * $Id: mcast.c,v 1.18 1999/03/21 05:22:55 davem Exp $ + * $Id: mcast.c,v 1.19 1999/03/25 10:04:50 davem Exp $ * * Based on linux/ipv4/igmp.c and linux/ipv4/ip_sockglue.c * @@ -132,9 +132,10 @@ if (mc_lst->ifindex == ifindex && ipv6_addr_cmp(&mc_lst->addr, addr) == 0) { struct device *dev; - net_serialize_enter(); + *lnk = mc_lst->next; - net_serialize_leave(); + synchronize_bh(); + if ((dev = dev_get_by_index(ifindex)) != NULL) ipv6_dev_mc_dec(dev, &mc_lst->addr); sock_kfree_s(sk, mc_lst, sizeof(*mc_lst)); @@ -254,9 +255,8 @@ for (lnk = &idev->mc_list; (iter = *lnk) != NULL; lnk = &iter->if_next) { if (iter == ma) { - net_serialize_enter(); *lnk = iter->if_next; - net_serialize_leave(); + synchronize_bh(); return; } } @@ -277,9 +277,10 @@ if (ipv6_addr_cmp(&ma->mca_addr, addr) == 0 && ma->dev == dev) { if (atomic_dec_and_test(&ma->mca_users)) { igmp6_group_dropped(ma); - net_serialize_enter(); + *lnk = ma->next; - net_serialize_leave(); + synchronize_bh(); + ipv6_mca_remove(dev, ma); kfree(ma); } @@ -595,9 +596,8 @@ for (lnk = &inet6_mcast_lst[hash]; *lnk; lnk = &(*lnk)->next) { if (*lnk == i) { - net_serialize_enter(); *lnk = i->next; - net_serialize_leave(); + synchronize_bh(); break; } } diff -u --recursive --new-file v2.2.4/linux/net/ipv6/sit.c linux/net/ipv6/sit.c --- v2.2.4/linux/net/ipv6/sit.c Tue Mar 23 14:35:48 1999 +++ linux/net/ipv6/sit.c Thu Mar 25 09:23:34 1999 @@ -6,7 +6,7 @@ * Pedro Roque * Alexey Kuznetsov * - * $Id: sit.c,v 1.30 1999/03/21 05:22:58 davem Exp $ + * $Id: sit.c,v 1.31 1999/03/25 10:04:55 davem Exp $ * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -118,9 +118,8 @@ for (tp = ipip6_bucket(t); *tp; tp = &(*tp)->next) { if (t == *tp) { - net_serialize_enter(); *tp = t->next; - net_serialize_leave(); + synchronize_bh(); break; } } @@ -130,10 +129,9 @@ { struct ip_tunnel **tp = ipip6_bucket(t); - net_serialize_enter(); t->next = *tp; + wmb(); *tp = t; - net_serialize_leave(); } struct ip_tunnel * ipip6_tunnel_locate(struct ip_tunnel_parm *parms, int create) @@ -200,9 +198,8 @@ static void ipip6_tunnel_destroy(struct device *dev) { if (dev == &ipip6_fb_tunnel_dev) { - net_serialize_enter(); tunnels_wc[0] = NULL; - net_serialize_leave(); + synchronize_bh(); return; } else { ipip6_tunnel_unlink((struct ip_tunnel*)dev->priv); diff -u --recursive --new-file v2.2.4/linux/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c --- v2.2.4/linux/net/netlink/af_netlink.c Tue Mar 23 14:35:48 1999 +++ linux/net/netlink/af_netlink.c Thu Mar 25 09:23:34 1999 @@ -747,9 +747,9 @@ { struct socket *sock = netlink_kernel[unit]; - net_serialize_enter(); netlink_kernel[unit] = NULL; - net_serialize_leave(); + synchronize_bh(); + sock_release(sock); } diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_api.c linux/net/sched/cls_api.c --- v2.2.4/linux/net/sched/cls_api.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/cls_api.c Thu Mar 25 09:23:34 1999 @@ -226,9 +226,9 @@ if (fh == 0) { if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { - net_serialize_enter(); *back = tp->next; - net_serialize_leave(); + synchronize_bh(); + tp->ops->destroy(tp); kfree(tp); err = 0; diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_fw.c linux/net/sched/cls_fw.c --- v2.2.4/linux/net/sched/cls_fw.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/cls_fw.c Thu Mar 25 09:23:34 1999 @@ -157,9 +157,8 @@ if (*fp == f) { unsigned long cl; - net_serialize_enter(); *fp = f->next; - net_serialize_leave(); + synchronize_bh(); if ((cl = cls_set_class(&f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); @@ -207,9 +206,10 @@ #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_FW_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_FW_POLICE-1], tca[TCA_RATE-1]); - net_serialize_enter(); + police = xchg(&f->police, police); - net_serialize_leave(); + synchronize_bh(); + tcf_police_release(police); } #endif @@ -224,9 +224,9 @@ if (head == NULL) return -ENOBUFS; memset(head, 0, sizeof(*head)); - net_serialize_enter(); + tp->root = head; - net_serialize_leave(); + synchronize_bh(); } f = kmalloc(sizeof(struct fw_filter), GFP_KERNEL); @@ -250,9 +250,9 @@ #endif f->next = head->ht[fw_hash(handle)]; - net_serialize_enter(); + wmb(); head->ht[fw_hash(handle)] = f; - net_serialize_leave(); + *arg = (unsigned long)f; return 0; @@ -303,7 +303,11 @@ t->tcm_handle = f->id; - if (!f->res.classid && !f->police) + if (!f->res.classid +#ifdef CONFIG_NET_CLS_POLICE + && !f->police +#endif + ) return skb->len; rta = (struct rtattr*)b; diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_route.c linux/net/sched/cls_route.c --- v2.2.4/linux/net/sched/cls_route.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/cls_route.c Thu Mar 25 09:23:34 1999 @@ -329,9 +329,9 @@ if (*fp == f) { unsigned long cl; - net_serialize_enter(); *fp = f->next; - net_serialize_leave(); + synchronize_bh(); + route4_reset_fastmap(head, f->id); if ((cl = cls_set_class(&f->res.class, 0)) != 0) @@ -349,9 +349,9 @@ return 0; /* OK, session has no flows */ - net_serialize_enter(); head->table[to_hash(h)] = NULL; - net_serialize_leave(); + synchronize_bh(); + kfree(b); return 0; } @@ -394,9 +394,10 @@ #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_ROUTE4_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_ROUTE4_POLICE-1], tca[TCA_RATE-1]); - net_serialize_enter(); + police = xchg(&f->police, police); - net_serialize_leave(); + synchronize_bh(); + tcf_police_release(police); } #endif @@ -410,9 +411,9 @@ if (head == NULL) return -ENOBUFS; memset(head, 0, sizeof(struct route4_head)); - net_serialize_enter(); + tp->root = head; - net_serialize_leave(); + synchronize_bh(); } f = kmalloc(sizeof(struct route4_filter), GFP_KERNEL); @@ -473,9 +474,9 @@ if (b == NULL) goto errout; memset(b, 0, sizeof(*b)); - net_serialize_enter(); + head->table[h1] = b; - net_serialize_leave(); + synchronize_bh(); } f->bkt = b; @@ -495,9 +496,9 @@ #endif f->next = f1; - net_serialize_enter(); + wmb(); *ins_f = f; - net_serialize_leave(); + route4_reset_fastmap(head, f->id); *arg = (unsigned long)f; return 0; diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_rsvp.h linux/net/sched/cls_rsvp.h --- v2.2.4/linux/net/sched/cls_rsvp.h Tue Mar 23 14:35:48 1999 +++ linux/net/sched/cls_rsvp.h Thu Mar 25 09:23:34 1999 @@ -149,7 +149,7 @@ struct iphdr *nhptr = skb->nh.iph; #endif -#if !defined( __i386__) && !defined(__m68k__) +#if !defined( __i386__) && !defined(__mc68000__) if ((unsigned long)nhptr & 3) return -1; #endif @@ -309,9 +309,9 @@ if (*fp == f) { unsigned long cl; - net_serialize_enter(); + *fp = f->next; - net_serialize_leave(); + synchronize_bh(); if ((cl = cls_set_class(&f->res.class, 0)) != 0) tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); @@ -332,9 +332,9 @@ for (sp = &((struct rsvp_head*)tp->root)->ht[h&0xFF]; *sp; sp = &(*sp)->next) { if (*sp == s) { - net_serialize_enter(); *sp = s->next; - net_serialize_leave(); + synchronize_bh(); + kfree(s); return 0; } @@ -453,9 +453,10 @@ #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_RSVP_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_RSVP_POLICE-1], tca[TCA_RATE-1]); - net_serialize_enter(); + police = xchg(&f->police, police); - net_serialize_leave(); + synchronize_bh(); + tcf_police_release(police); } #endif @@ -545,9 +546,9 @@ if (((*fp)->spi.mask&f->spi.mask) != f->spi.mask) break; f->next = *fp; - net_serialize_enter(); + wmb(); *fp = f; - net_serialize_leave(); + *arg = (unsigned long)f; return 0; } @@ -569,9 +570,8 @@ break; } s->next = *sp; - net_serialize_enter(); + wmb(); *sp = s; - net_serialize_leave(); goto insert; diff -u --recursive --new-file v2.2.4/linux/net/sched/cls_u32.c linux/net/sched/cls_u32.c --- v2.2.4/linux/net/sched/cls_u32.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/cls_u32.c Thu Mar 25 09:23:34 1999 @@ -114,7 +114,7 @@ int sel = 0; int i; -#if !defined(__i386__) && !defined(__m68k__) +#if !defined(__i386__) && !defined(__mc68000__) if ((unsigned long)ptr & 3) return -1; #endif @@ -326,9 +326,8 @@ if (ht) { for (kp = &ht->ht[TC_U32_HASH(key->handle)]; *kp; kp = &(*kp)->next) { if (*kp == key) { - net_serialize_enter(); *kp = key->next; - net_serialize_leave(); + synchronize_bh(); u32_destroy_key(tp, key); return 0; @@ -346,9 +345,9 @@ for (h=0; h<=ht->divisor; h++) { while ((n = ht->ht[h]) != NULL) { - net_serialize_enter(); ht->ht[h] = n->next; - net_serialize_leave(); + synchronize_bh(); + u32_destroy_key(tp, n); } } @@ -466,9 +465,8 @@ ht_down->refcnt++; } - net_serialize_enter(); ht_down = xchg(&n->ht_down, ht_down); - net_serialize_leave(); + synchronize_bh(); if (ht_down) ht_down->refcnt--; @@ -484,9 +482,10 @@ #ifdef CONFIG_NET_CLS_POLICE if (tb[TCA_U32_POLICE-1]) { struct tcf_police *police = tcf_police_locate(tb[TCA_U32_POLICE-1], est); - net_serialize_enter(); + police = xchg(&n->police, police); - net_serialize_leave(); + synchronize_bh(); + tcf_police_release(police); } #endif @@ -588,10 +587,11 @@ for (ins = &ht->ht[TC_U32_HASH(handle)]; *ins; ins = &(*ins)->next) if (TC_U32_NODE(handle) < TC_U32_NODE((*ins)->handle)) break; - net_serialize_enter(); + n->next = *ins; + wmb(); *ins = n; - net_serialize_leave(); + *arg = (unsigned long)n; return 0; } diff -u --recursive --new-file v2.2.4/linux/net/sched/estimator.c linux/net/sched/estimator.c --- v2.2.4/linux/net/sched/estimator.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/estimator.c Thu Mar 25 09:23:34 1999 @@ -171,9 +171,10 @@ pest = &est->next; continue; } - net_serialize_enter(); + *pest = est->next; - net_serialize_leave(); + synchronize_bh(); + kfree(est); killed++; } diff -u --recursive --new-file v2.2.4/linux/net/sched/sch_prio.c linux/net/sched/sch_prio.c --- v2.2.4/linux/net/sched/sch_prio.c Tue Mar 23 14:35:48 1999 +++ linux/net/sched/sch_prio.c Thu Mar 25 09:23:34 1999 @@ -195,9 +195,9 @@ struct Qdisc *child; child = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops); if (child) { - net_serialize_enter(); child = xchg(&q->queues[band], child); - net_serialize_leave(); + synchronize_bh(); + if (child != &noop_qdisc) qdisc_destroy(child); } diff -u --recursive --new-file v2.2.4/linux/net/unix/af_unix.c linux/net/unix/af_unix.c --- v2.2.4/linux/net/unix/af_unix.c Tue Mar 23 14:35:48 1999 +++ linux/net/unix/af_unix.c Thu Mar 25 09:23:34 1999 @@ -8,7 +8,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version: $Id: af_unix.c,v 1.74 1999/03/21 05:23:16 davem Exp $ + * Version: $Id: af_unix.c,v 1.75 1999/03/22 05:02:45 davem Exp $ * * Fixes: * Linus Torvalds : Assorted bug cures. Binary files v2.2.4/linux/scripts/ksymoops/ksymoops and linux/scripts/ksymoops/ksymoops differ diff -u --recursive --new-file v2.2.4/linux/scripts/ksymoops/oops.c linux/scripts/ksymoops/oops.c --- v2.2.4/linux/scripts/ksymoops/oops.c Tue Mar 23 14:35:48 1999 +++ linux/scripts/ksymoops/oops.c Wed Mar 24 16:07:59 1999 @@ -288,9 +288,11 @@ errno = 0; eip_relative = strtoul(string[5], NULL, 16); if (errno) { +#if 0 /* Try strtoull also, e.g. sparc binutils print <_PC+0xfffffffffffffd58> */ errno = 0; eip_relative = strtoull(string[5], NULL, 16); +#endif if (errno) { fprintf(stderr, "%s Invalid hex value in objdump line, "