From: Linus Torvalds Date: Fri, 23 Nov 2007 20:13:10 +0000 (-0500) Subject: Import 2.1.37pre2 X-Git-Tag: 2.1.37pre2 X-Git-Url: http://git.neil.brown.name/?a=commitdiff_plain;h=9d0031fc8bf474d0bb2c92b60a6261ed82b8a960;p=history.git Import 2.1.37pre2 --- diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index 09d482dac082..63466ffd52a7 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -26,6 +26,8 @@ devices.txt - plain ASCII listing of all the nodes in /dev/ with major minor #'s digiboard.txt - info on the Digiboard PC/X{i,e,eve} multiport boards. +digiepca.txt + - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. exception.txt - how linux v2.1 handles exceptions without verify_area etc. ez.txt diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 469fb2e020c3..062bf99434f7 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -4036,6 +4036,16 @@ CONFIG_SERIAL_CONSOLE N here so that they can use the serial port for modem, mouse or some other device. +Digi Intl. epca support +CONFIG_DIGIEPCA + This is a driver for Digi Internationals Xx, Xeve, and Xem + series of cards. This driver supports the original PC (ISA) boards as + well as PCI, and EISA. If you have a card like this, say Y here and read + the file Documentation/digiepca.txt. NOTE: This driver is seperate from + the driver written and copyrighted by Troy De Jongh. Because they both + attempt (In some cases) to access the same hardware only one of these + drivers (CONFIG_DIGIEPCA or CONFIG_DIGI) should be selected. + Digiboard PC/Xx Support CONFIG_DIGI This is a driver for the Digiboard PC/Xe, PC/Xi, and PC/Xeve cards @@ -5317,7 +5327,7 @@ CONFIG_MSDOS_PARTITION # LocalWords: mgetty sendfax gert greenie muc lowlevel Lasermate LanManager io # LocalWords: OOPSes trackball binghamton mobileip ncr IOMAPPED settags ns ser # LocalWords: setsync NEGO MPARITY autotuning prefetch PIIX cdwrite utils rc -# LocalWords: PCWATCHDOG berkprod bitgate boldt ucsb jf kyoto jp euc Tetsuyasu +# LocalWords: PCWATCHDOG berkprod bitgate boldt ucsb jf kyoto jp euc Tetsuyasu # LocalWords: YAMADA tetsu cauchy nslab ntt nevod perm su doc kaf kheops wsc # LocalWords: traduc Bourgin dbourgin helptext menuconfig kfill READMEs HOWTOs # LocalWords: IDEDISK IDEFLOPPY EIDE firewalls QMAGIC ZMAGIC LocalWords opti diff --git a/Documentation/digiepca.txt b/Documentation/digiepca.txt new file mode 100644 index 000000000000..9904afb392bb --- /dev/null +++ b/Documentation/digiepca.txt @@ -0,0 +1,96 @@ +The Digi Intl. epca driver. +---------------------------- +The Digi Intl. epca driver for Linux supports the following boards: + +Digi PC/Xem, PC/Xr, PC/Xe, PC/Xi, PC/Xeve +Digi EISA/Xem, PCI/Xem, PCI/Xr + +Limitations: +------------ +Currently the driver only autoprobes for supported PCI boards. + +The Linux MAKEDEV command does not support generating the Digiboard +Devices. Users executing digiConfig to setup EISA and PC series cards +will have their device nodes automaticly constructed (cud?? for ~CLOCAL, +and ttyD?? for CLOCAL). Users wishing to boot their board from the LILO +prompt, or those users booting PCI cards may use buildDIGI to construct +the necessary nodes. + +Notes: +------ +This driver may be configured via LILO. For users who have already configured +their driver using digiConfig, configuring from lilo will override previous +settings. Multiple boards may be configured by issuing multiple LILO command +lines. For examples see the bottom of this document. + +Device names start at 0 and continue up. Beware of this as previous Digi +drivers started device names with 1. + +PCI boards are auto-detected and configured by the driver. PCI boards will +be allocated device numbers (internally) begining with the lowest PCI slot +first. In other words a PCI card in slot 3 will always have higher device +nodes than a PCI card in slot 1. + +LILO config examples: +--------------------- +Using LILO's APPEND command, a string of comma separated identifiers or +integers can be used to configure supported boards. The six values in order +are: + + Enable/Disable this card or Override, + Type of card: PC/Xe (AccelePort) (0), PC/Xeve (1), PC/Xem or PC/Xr (2), + EISA/Xem (3), PC/64Xe (4), PC/Xi (5), + Enable/Disable alternate pin arrangement, + Number of ports on this card, + I/O Port where card is configured (in HEX if using string identifiers), + Base of memory window (in HEX if using string identifiers), + +NOTE : PCI boards are auto-detected and configured. Do not attempt to +configure PCI boards with the LILO append comand. If you wish to override +previous configuration data (As set by digiConfig), but you do not wish to +configure any specific card (Example if there are PCI cards in the system) +the following override command will accomplish this: +-> append="digi=2" + +Samples: + append="digiepca=E,PC/Xe,D,16,200,D0000" + or + append="digi=1,0,0,16,512,851968" + +Supporting Tools: +----------------- +Supporting tools include digiDload, digiConfig, buildPCI, and ditty. See +/usr/src/linux/Documentation/README.epca.dir/user.doc for more details. Note, +this driver REQUIRES that digiDload be executed prior to it being used. +Failure to do this will result in an ENODEV error. + +The latest version of the tool package is available at: +ftp://ftp.dgii.com/drivers/linux/released/async/ + +Documentation: +-------------- +Complete documentation for this product may be found in the tool package. + +Sources of information and support: +----------------------------------- +Digi Intl. support site for this product: +-> digilnux@dgii.com + +Related information and information concerning other drivers supporting +Digi Intl. products: + +-> FTP: ftp://dgii.com +-> Webpage: http://www.dgii.com +-> Webpage: http://private.fuller.edu/clameter/digi.html +-> Mailing List: digiboard@list.fuller.edu Note write e-mail to subscribe + common ListServ commands will not work. + +Acknowledgments: +---------------- +Much of this work (And even text) was derived from a similar document +supporting the original public domain DigiBoard driver Copyright (C) +1994,1995 Troy De Jongh. Many thanks to Christoph Lameter +(clameter@fuller.edu) and Mike McLagan (mike.mclagan@linux.org) who authored +and contributed to the original document. + + diff --git a/MAINTAINERS b/MAINTAINERS index 588898993187..be976ea8a2c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -342,6 +342,13 @@ M: clameter@fuller.edu L: digiboard@list.fuller.edu S: Maintained +DIGI INTL. EPCA DRIVER: +P: Daniel Taylor +M: support@dgii.com +M: digilnux@dgii.com +L: digiboard@list.fuller.edu +S: Maintained + RISCOM8 DRIVER: P: Dmitry Gorodchanin M: begemot@bgm.rosprint.net diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index fc5d4822a67b..ac67da797b02 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -110,45 +110,24 @@ ENOSYS = 38 addl $4,%esp; \ iret -#ifdef __SMP__ -/* Get the processor ID multiplied by 4 */ -#if 0 -#define GET_PROCESSOR_OFFSET(reg) \ - movl SYMBOL_NAME(apic_reg), reg; \ - movl 32(reg), reg; \ - shrl $22, reg; \ - andl $0x3C, reg; - -#define GET_CURRENT(reg) \ - GET_PROCESSOR_OFFSET(reg) \ - movl SYMBOL_NAME(current_set)(reg),reg -#endif - #define GET_CURRENT(reg) \ movl %esp, reg; \ andl $-8192, reg; -#else - -#define GET_CURRENT(reg) \ - movl SYMBOL_NAME(current_set),reg - -#endif ENTRY(lcall7) pushfl # We get a different stack layout with call gates, pushl %eax # which has to be cleaned up later.. SAVE_ALL - GET_CURRENT(%ebx) movl EIP(%esp),%eax # due to call gates, this is eflags, not eip.. movl CS(%esp),%edx # this is eip.. movl EFLAGS(%esp),%ecx # and this is cs.. movl %eax,EFLAGS(%esp) # movl %edx,EIP(%esp) # Now we move them to their "normal" places movl %ecx,CS(%esp) # - movl %esp,%eax - GET_CURRENT(%edx) - pushl %eax - movl exec_domain(%edx),%edx # Get the execution domain + movl %esp,%ebx + pushl %ebx + andl $-8192,%ebx # GET_CURRENT + movl exec_domain(%ebx),%edx # Get the execution domain movl 4(%edx),%edx # Get the lcall7 handler for the domain call *%edx popl %eax @@ -159,6 +138,7 @@ ENTRY(lcall7) ALIGN .globl ret_from_smpfork ret_from_smpfork: + GET_CURRENT(%ebx) btrl $0, SYMBOL_NAME(scheduler_lock) jmp ret_from_sys_call #endif /* __SMP__ */ @@ -190,7 +170,6 @@ ret_from_sys_call: ret_with_reschedule: cmpl $0,SYMBOL_NAME(need_resched) jne reschedule - GET_CURRENT(%ebx) movl blocked(%ebx),%eax movl %eax,%esi # save blocked in %esi for signal handling notl %eax @@ -235,6 +214,7 @@ ret_from_exception: jne handle_bottom_half ALIGN ret_from_intr: + GET_CURRENT(%ebx) movl EFLAGS(%esp),%eax # mix EFLAGS and CS movb CS(%esp),%al testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor? diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index be0f864e03ea..f815352b0a70 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -40,19 +40,20 @@ startup_32: /* * New page tables may be in 4Mbyte page mode and may * be using the global pages. + * + * NOTE! We have to correct for the fact that we're + * not yet offset 0xC0000000.. */ -#define mmu_cr4_features $128+16 +#define cr4_bits mmu_cr4_features-0xC0000000 #ifdef GAS_KNOWS_CR4 movl %cr4,%eax # Turn on 4Mb pages - orl mmu_cr4_features,%eax + orl cr4_bits,%eax movl %eax,%cr4 #else .byte 0x0f,0x20,0xe0 - orl mmu_cr4_features,%eax + orl cr4_bits,%eax .byte 0x0f,0x22,0xe0 #endif - movl %eax,%cr3 /* flush TLB as per app note */ - movl %cr0,%eax #endif /* * Setup paging (the tables are already set up, just switch them on) diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 3aec838cb931..61f4f5e63708 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -36,8 +36,6 @@ EXPORT_SYMBOL(__intel_bh_counter); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); -EXPORT_SYMBOL(synchronize_irq); - #ifdef __SMP__ EXPORT_SYMBOL(apic_reg); /* Needed internally for the I386 inlines */ EXPORT_SYMBOL(cpu_data); @@ -47,6 +45,7 @@ EXPORT_SYMBOL(smp_invalidate_needed); EXPORT_SYMBOL_NOVERS(__lock_kernel); /* Global SMP irq stuff */ +EXPORT_SYMBOL(synchronize_irq); EXPORT_SYMBOL(global_irq_holder); EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index 6ca54c7313ec..f8bc39514174 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -44,8 +44,8 @@ extern volatile unsigned long smp_local_timer_ticks[1+NR_CPUS]; #define CR0_NE 32 -static unsigned char cache_21 = 0xff; -static unsigned char cache_A1 = 0xff; +/* This contains the irq mask for both irq controllers */ +static unsigned long cached_irq_mask = 0xffff; unsigned int local_irq_count[NR_CPUS]; #ifdef __SMP__ @@ -58,32 +58,33 @@ int __intel_bh_counter; static unsigned int int_count[NR_CPUS][NR_IRQS] = {{0},}; #endif -static inline void mask_irq(unsigned int irq_nr) +static inline void ack_irq(int irq_nr) { - unsigned char mask; + if (irq_nr & 8) { + outb(0x20,0xA0); + } + outb(0x20,0x20); +} - mask = 1 << (irq_nr & 7); - if (irq_nr < 8) { - cache_21 |= mask; - outb(cache_21,0x21); +static inline void set_irq_mask(int irq_nr) +{ + if (irq_nr & 8) { + outb(cached_irq_mask>>8,0xA1); } else { - cache_A1 |= mask; - outb(cache_A1,0xA1); + outb(cached_irq_mask,0x21); } +} + +static inline void mask_irq(unsigned int irq_nr) +{ + set_bit(irq_nr, &cached_irq_mask); + set_irq_mask(irq_nr); } static inline void unmask_irq(unsigned int irq_nr) { - unsigned char mask; - - mask = ~(1 << (irq_nr & 7)); - if (irq_nr < 8) { - cache_21 &= mask; - outb(cache_21,0x21); - } else { - cache_A1 &= mask; - outb(cache_A1,0xA1); - } + clear_bit(irq_nr, &cached_irq_mask); + set_irq_mask(irq_nr); } void disable_irq(unsigned int irq_nr) @@ -134,7 +135,8 @@ void enable_irq(unsigned int irq_nr) #error make irq stub building NR_IRQS dependent and remove me. #endif -BUILD_TIMER_IRQ(FIRST,0,0x01) +BUILD_COMMON_IRQ() +BUILD_IRQ(FIRST,0,0x01) BUILD_IRQ(FIRST,1,0x02) BUILD_IRQ(FIRST,2,0x04) BUILD_IRQ(FIRST,3,0x08) @@ -158,10 +160,6 @@ BUILD_SMP_INTERRUPT(stop_cpu_interrupt) BUILD_SMP_TIMER_INTERRUPT(apic_timer_interrupt) #endif -/* - * Pointers to the low-level handlers: first the general ones, then the - * fast ones, then the bad ones. - */ static void (*interrupt[17])(void) = { IRQ0_interrupt, IRQ1_interrupt, IRQ2_interrupt, IRQ3_interrupt, IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, @@ -169,17 +167,6 @@ static void (*interrupt[17])(void) = { IRQ12_interrupt, IRQ13_interrupt, IRQ14_interrupt, IRQ15_interrupt }; -static void (*bad_interrupt[16])(void) = { - bad_IRQ0_interrupt, bad_IRQ1_interrupt, - bad_IRQ2_interrupt, bad_IRQ3_interrupt, - bad_IRQ4_interrupt, bad_IRQ5_interrupt, - bad_IRQ6_interrupt, bad_IRQ7_interrupt, - bad_IRQ8_interrupt, bad_IRQ9_interrupt, - bad_IRQ10_interrupt, bad_IRQ11_interrupt, - bad_IRQ12_interrupt, bad_IRQ13_interrupt, - bad_IRQ14_interrupt, bad_IRQ15_interrupt -}; - /* * Initial irq handlers. */ @@ -520,25 +507,34 @@ void __global_restore_flags(unsigned long flags) * SMP cross-CPU interrupts have their own specific * handlers). */ -asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +asmlinkage void do_IRQ(struct pt_regs regs) { + int irq = regs.orig_eax & 0xff; struct irqaction * action; - int do_random, cpu = smp_processor_id(); + int status, cpu = smp_processor_id(); irq_enter(cpu, irq); + mask_irq(irq); + ack_irq(irq); kstat.interrupts[irq]++; + /* Return with this interrupt masked if no action */ + status = 0; action = *(irq + irq_action); - do_random = 0; - while (action) { - do_random |= action->flags; - action->handler(irq, action->dev_id, regs); - action = action->next; + if (action) { + do { + status |= action->flags; + action->handler(irq, action->dev_id, ®s); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + + __cli(); + unmask_irq(irq); } - if (do_random & SA_SAMPLE_RANDOM) - add_interrupt_randomness(irq); - irq_exit(cpu, irq); + irq_exit(cpu, irq); /* * This should be conditional: we should really get * a return code from the irq handler to tell us @@ -635,10 +631,6 @@ void free_irq(unsigned int irq, void *dev_id) save_flags(flags); cli(); *p = action->next; - if (!irq[irq_action]) { - mask_irq(irq); - set_intr_gate(0x20+irq,bad_interrupt[irq]); - } restore_flags(flags); kfree(action); return; @@ -648,7 +640,7 @@ void free_irq(unsigned int irq, void *dev_id) unsigned long probe_irq_on (void) { - unsigned int i, irqs = 0, irqmask; + unsigned int i, irqs = 0; unsigned long delay; /* first, enable any unassigned irqs */ @@ -664,19 +656,17 @@ unsigned long probe_irq_on (void) /* about 100ms delay */; /* now filter out any obviously spurious interrupts */ - irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; - return irqs & ~irqmask; + return irqs & ~cached_irq_mask; } int probe_irq_off (unsigned long irqs) { - unsigned int i, irqmask; + unsigned int i; - irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; #ifdef DEBUG - printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, irqmask); + printk("probe_irq_off: irqs=0x%04lx irqmask=0x%04x\n", irqs, cached_irq_mask); #endif - irqs &= irqmask; + irqs &= cached_irq_mask; if (!irqs) return 0; i = ffz(~irqs); @@ -699,7 +689,7 @@ __initfunc(void init_IRQ(void)) outb(LATCH >> 8 , 0x40); /* MSB */ for (i = 0; i < NR_IRQS ; i++) - set_intr_gate(0x20+i,bad_interrupt[i]); + set_intr_gate(0x20+i,interrupt[i]); #ifdef __SMP__ /* diff --git a/arch/i386/kernel/irq.h b/arch/i386/kernel/irq.h index 26fc98911746..628d87068f6d 100644 --- a/arch/i386/kernel/irq.h +++ b/arch/i386/kernel/irq.h @@ -23,10 +23,7 @@ static inline void irq_enter(int cpu, int irq) hardirq_enter(cpu); while (test_bit(0,&global_irq_lock)) { if ((unsigned char) cpu == global_irq_holder) { - static int printed = 0; - if (!printed) - printk("BAD! Local interrupts enabled, global disabled\n"); - printed++; + printk("BAD! Local interrupts enabled, global disabled\n"); break; } STUCK; @@ -36,7 +33,6 @@ static inline void irq_enter(int cpu, int irq) static inline void irq_exit(int cpu, int irq) { - __cli(); hardirq_exit(cpu); release_irqlock(cpu); } @@ -89,54 +85,6 @@ static inline void irq_exit(int cpu, int irq) "pop %es\n\t" \ "iret" -/* - * The "inb" instructions are not needed, but seem to change the timings - * a bit - without them it seems that the harddisk driver won't work on - * all hardware. Arghh. - */ -#define ACK_FIRST(mask,nr) \ - "inb $0x21,%al\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\torb $" #mask ","SYMBOL_NAME_STR(cache_21)"\n\t" \ - "movb "SYMBOL_NAME_STR(cache_21)",%al\n\t" \ - "outb %al,$0x21\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\tmovb $0x20,%al\n\t" \ - "outb %al,$0x20\n\t" - -#define ACK_SECOND(mask,nr) \ - "inb $0xA1,%al\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\torb $" #mask ","SYMBOL_NAME_STR(cache_A1)"\n\t" \ - "movb "SYMBOL_NAME_STR(cache_A1)",%al\n\t" \ - "outb %al,$0xA1\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\tmovb $0x20,%al\n\t" \ - "outb %al,$0xA0\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\toutb %al,$0x20\n\t" - -#define UNBLK_FIRST(mask) \ - "inb $0x21,%al\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\tandb $~(" #mask "),"SYMBOL_NAME_STR(cache_21)"\n\t" \ - "movb "SYMBOL_NAME_STR(cache_21)",%al\n\t" \ - "outb %al,$0x21\n\t" - -#define UNBLK_SECOND(mask) \ - "inb $0xA1,%al\n\t" \ - "jmp 1f\n" \ - "1:\tjmp 1f\n" \ - "1:\tandb $~(" #mask "),"SYMBOL_NAME_STR(cache_A1)"\n\t" \ - "movb "SYMBOL_NAME_STR(cache_A1)",%al\n\t" \ - "outb %al,$0xA1\n\t" - #define IRQ_NAME2(nr) nr##_interrupt(void) #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr) #define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr) @@ -176,45 +124,21 @@ SYMBOL_NAME_STR(x) ":\n\t" \ #endif /* __SMP__ */ -#define BUILD_IRQ(chip,nr,mask) \ -asmlinkage void IRQ_NAME(nr); \ -asmlinkage void BAD_IRQ_NAME(nr); \ +#define BUILD_COMMON_IRQ() \ __asm__( \ -"\n"__ALIGN_STR"\n" \ -SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ - "pushl $-"#nr"-2\n\t" \ + "\n" __ALIGN_STR"\n" \ + "common_interrupt:\n\t" \ SAVE_ALL \ - ACK_##chip(mask,(nr&7)) \ - "movl %esp,%eax\n\t" \ - "pushl %eax\n\t" \ - "pushl $" #nr "\n\t" \ - "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \ - "addl $8,%esp\n\t" \ - UNBLK_##chip(mask) \ - "jmp ret_from_intr\n" \ -"\n"__ALIGN_STR"\n" \ -SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ - SAVE_MOST \ - ACK_##chip(mask,(nr&7)) \ - RESTORE_MOST); + "pushl $ret_from_intr\n\t" \ + "jmp "SYMBOL_NAME_STR(do_IRQ)); -#define BUILD_TIMER_IRQ(chip,nr,mask) \ +#define BUILD_IRQ(chip,nr,mask) \ asmlinkage void IRQ_NAME(nr); \ -asmlinkage void BAD_IRQ_NAME(nr); \ __asm__( \ "\n"__ALIGN_STR"\n" \ -SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \ SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \ - "pushl $-"#nr"-2\n\t" \ - SAVE_ALL \ - ACK_##chip(mask,(nr&7)) \ - "movl %esp,%eax\n\t" \ - "pushl %eax\n\t" \ - "pushl $" #nr "\n\t" \ - "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \ - "addl $8,%esp\n\t" \ - UNBLK_##chip(mask) \ - "jmp ret_from_intr\n"); + "pushl $"#nr"-256\n\t" \ + "jmp common_interrupt"); /* * x86 profiling function, SMP safe. We might want to do this in diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 2b4f06905006..476657e2ea8b 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -678,8 +678,6 @@ __initfunc(void initialize_secondary(void)) { struct thread_struct * p = ¤t->tss; -printk("current = %p, real_esp = %p, real_eip = %p\n", current, p->esp, p->eip); -printk("start_secondary = %p, cpu=%d\n", start_secondary, smp_processor_id()); /* * We don't actually need to load the full TSS, * just the stack pointer and the eip. diff --git a/drivers/char/Config.in b/drivers/char/Config.in index c32475be7dd7..4216b315408b 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -19,7 +19,10 @@ if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then fi bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then - tristate 'Digiboard PC/Xx Support' CONFIG_DIGI + tristate 'Digiboard Intelligent Async Support' CONFIG_DIGIEPCA + if [ "$CONFIG_DIGIEPCA" = "n" ]; then + tristate 'Digiboard PC/Xx Support' CONFIG_DIGI + fi tristate 'Cyclades async mux support' CONFIG_CYCLADES bool 'Stallion multiport serial support' CONFIG_STALDRV if [ "$CONFIG_STALDRV" = "y" ]; then diff --git a/drivers/char/Makefile b/drivers/char/Makefile index cf50335d48ad..ff9bd02130a7 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -61,6 +61,14 @@ else endif endif +ifeq ($(CONFIG_DIGIEPCA),y) +L_OBJS += epca.o +else + ifeq ($(CONFIG_DIGIEPCA),m) + M_OBJS += epca.o + endif +endif + ifeq ($(CONFIG_CYCLADES),y) L_OBJS += cyclades.o else diff --git a/drivers/char/README.epca b/drivers/char/README.epca new file mode 100644 index 000000000000..3561608f1949 --- /dev/null +++ b/drivers/char/README.epca @@ -0,0 +1,506 @@ +user.doc +Digi International driver package for the PC/Xe, PC/Xi, PC/Xr, PC/Xem as well +the EISA and PCI variants of these boards where applicable. +Copyright (C) 1996 Digi International. Written by Ronnie Sanford digilnux@dgii.com + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (At your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not write to the Free Software Foundation, Inc., + 675 Mass Ave, Cambridge, MA 02139, USA. + + +This document describes the software used with the Digi/Linux driver package. +The four user programs listed below are described in this document: + + 1. digiConfig -> Application that configures the Digi driver. + + 2. digiDload -> Application which initializes the Digi hardware. + + 3. buildPCI -> Application which provides the user a method of + building device nodes for PCI devices. + + 4. ditty -> Application which provides the user a method of + configuring terminal options on Digi hardware. + + + +-------------------------------------------------------------------------- +1. Configuring driver/kernel for Digi products +-------------------------------------------------------------------------- + + The Digi driver must be configured each time Digi hardware is added + or removed. There are two supported methods of doing this. The + first method configures the driver dynamically at boot time but requires + the user to utilize the lilo loader. This method is the preffered method + as it does not require the rebuilding of the kernel. In order to use lilo + to configure Digi boards at boot time an appropriate append command should + be added to /etc/lilo.conf below the appropriate label decleration. + See footer 4. The append commands format is a string of comma seperated + identifiers or integers used to configure supported boards. These six + values in order are: + + Enable/Disable this card or Override, + Type of card: PC/Xe (AccelePort) (0), PC/Xeve (1), PC/Xem or PC/Xr (2), + EISA/Xem (3), PC/Xe (64K) (4), PC/Xi (5). + Enable/Disable alternate pin arrangement, + Number of ports on this card, + I/O Port where card is configured (in HEX if using string identifiers), + Base of memory window (in HEX if using string identifiers) + + A sample append command is given below which if used would configure and + enable a PC/Xe with 8 ports, at i/o address 200, memory address 0xd0000 + with alt pin turned off. The lilo.conf file should look like this: + + image = /vmlinuz + root = /dev/hda2 + label = vmlinuz + append="digiepca=E,PC/Xe,D,8,200,D0000" + + likewise the below will perform the same function: + + image = /vmlinuz + root = /dev/hda2 + label = vmlinuz + append="digiepca=1,0,0,8,512,851968" + + Note: + + PCI boards are auto-detected and configured (Hence their codes are + not given here). Do not attempt to configure PCI boards with the lilo + append command. + + If configuration data has been specified by using digiConfig (Described + below), and you wish to override this configuration using lilo without + specifying a specific card (Example if there are PCI cards in the system) + the following override command will accomplish this: + + -> append="digiepca=2" + + If lilo is not enabled, the second method of configuring Digi hardware + will have to be used. digiConfig is an application that can be used + to inform the system of any additions, deletions, or modifications + involving Digi hardware. To use this method the operator executes + digiConfig anytime an EISA or ISA card is added that he wishes to use. + This routine is also used to remove cards from the system, and to modify + parameters of those cards already present in the system. Upon being + executed digiConfig modifies files accessed by the Digi driver. To make + these changes permanent; the operating system must be recompiled. After + the operating system has been recompiled and booted, the changes made with + digiConfig will be introduced to the user. This program MUST be executed + every time Digi EISA/ISA hardware configuration changes. Note, it is not + necessary to execute digiConfig in order to configure the Digi PCI cards. + These cards are self-identifying and will be recognized by the driver. + They cannot be displayed using digiConfig nor will digiConfig build the + device nodes their device nodes. See footer 1. + + To execute digiConfig; simply type: digiConfig + + The application will query you for the type, memory address, port + address, number of ports, alt pin disposition and status of each board + that exist on the system. Note, currently this driver only supports + PC/Xe, PC/Xeve, PC/Xi, PC/Xr, and PC/Xem as well as their EISA and PCI + implementations if applicable. All supported cards (Other than PCI) that + are present should be registered via digiConfig. See footer 2. + + After all cards have been configured select exit. The system will then + inform you if any changes have been made, and ask you if it is okay to + make these changes permanent. If the data entered is correct, select okay. + Selecting cancel will prevent the changes from becoming active. digiConfig + can then be re-executed to configure the system again. + +-------------------------------------------------------------------------- +2. Initializing Digi hardware with digiDload +-------------------------------------------------------------------------- + + digiDload is the application executed after the Digi driver has been + loaded. It is responsible for initializing the hardware and leaving + it in a state such that the Digi board may be operated by the user. + The application may be placed anywhere on the path, but its related + support files must be located in /etc/digi. The related files are: + + sxfep.bin + sxbios.bin + xxfep.bin + xxbios.bin + + The format for this command is "digiDload [v]". If given the "v" + option turns on verbosity. If not given the application runs in quite + mode. To execute the program simply type: + + digiDload + + Upon completion digiDload will generate the below message: + + "digiDload complete: Card initialized" + + At this point the card is configured and ready for normal usage. See + technotes.doc for information on how how ports are determined and + assigned. + +-------------------------------------------------------------------------- +3. Build PCI device nodes with buildPCI +-------------------------------------------------------------------------- + + buildPCI is an application useful for building the necessary device nodes + for Digi PCI cards. It is reccomended that this tool be used because the + current digiConfig application does not provide this function for PCI cards + (Though it does build device nodes for non-PCI cards). To use this program + execute the following:first install the driver, and execute digiDload (See above). After digiDload + has sucessfully loaded, execute the following: + + buildPCI + + Where arg1 is the number of ports connected to Digi cards that are not PCI + (As shown by the digiConfig utility), and arg2 is the number of ports + connected to Digi cards that are PCI. + + Note, buildPCI only has to be ran once to build the necessary device + nodes. Though this program may be executed at anytime, we reccomend + delaying execution until the first time you install the package and after + digiDload has been executed. + +-------------------------------------------------------------------------- +4. Setting Terminal Options with ditty +-------------------------------------------------------------------------- + +ditty is a utility program that sets and displays the terminal options +for Digi intelligent serial products. See man ditty for detailed information. + + +Footnotes: + +1. The 1.2.x kernel does not provide a method of mapping the high + addresses (Normally higher than RAM) associated with PCI. For this + reason, this driver disables PCI support while running under the 1.2.x + kernels. + +2. PCI cards should not and cannot be registered with digiConfig. After + the driver has been loaded buildPCI may be executed to construct the + necessary device nodes. This step is not necessary for system not + having Digi PCI cards. + +3. This is because we forsee a time when buildPCI may auto-detect the + available Digi PCI cards and this would only work if the program is + executed after digiDload. + +4. A complete example is given in install.doc. + +-------------CHANGES-------------------- + +All changes should be recorded here. All changes should be explained in +verbose detail. +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : June 1, 1996 +Description (Verbose) : Initial release of driver package. +Files affected : all +Release version : 1.0.0f (BETA) +----------------------------------------------------------------------- +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 7, 1996 +Description (Verbose) : Made several modifications to provide PCI and EISA + support: + + 1. We now allocate the termios structures based on + the maximum number of channels that COULD be + available to the system. We no longer use the + number of channels declared in epcaconfig.h + (NBDEVS) as the total channel number. This is + because this value does not represent channels + available to potential PCI cards. This new + larger value is also passed back to the os in + the num field of tty_driver. + + 2. Added code to copy the previous board structure + (Now called static_boards) into a new local + copy of the boards structure. This has been + done so that PCI cards may be added to this + board array and later referenced (And even + queried.). + + 3. Added code to pc_init that checks for supported + PCI cards. If found this code initializes a new + entry into the drivers local board structure + with the PCI cards address, and type, etc.. It + also bumps the card count (num_cards). + + 4. Modified code in post_fep_init so that when this + routine is executed the number of ports supported + by a particular PCI card will be determined and + loaded into the board structure. It would be + much better if this code was placed in pc_init + (Because we could then report to the os the true + number of ports available; not just the max), but + since the card has to be booted to determine the + number of ports it supports, we are forced to do it + after DIGI_INIT has called post_fep_init. In the + future we may attempt to read the num ports + attached directly (address 0x1ac). + + 5. Added board types to epca.h in support of various + PCI boards (Some of which do not exist yet). + Added procedures for these boards throughout the + code. Note, windowing is not necessary for PCI + boards. + + 6. Added code supporting the EISA/XEM. This included + modifying epca.h with the new board type and + adding this type into the driver. The EISA/XEM + is basically identical to the PC/XEM, other than + it's base address does not have to be (And cannot + be configured directly). + + 7. Modified digiConfig to prompt for EISA/XEM cards. + +Files affected : epca.c, epca.h, digi1.h, digiConfig +Release version : 1.0.0g (BETA) +----------------------------------------------------------------------- +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 21, 1996 +Description (Verbose) : Made the following modifications: + + 1. A problem affecting hard flow control was found + in the termios2digi_h routine. Specifically, + when the user activated hard flow control using + the CRTSCTS specification, the values used to + program hard flow control on the board were + incorrect. The solution was to change a line + that read "res |= ((ch->m_dtr) | (ch->m_rts));" + to "res |= ((ch->m_cts) | (ch->m_rts));" This + line only applies if cflag & CRTSCTS. Special + thanks to Matt Robinson (matt@mania.com.au) who + found and fixed this problem. + + 2. In previous betas the cud device was set to CLOCAL + on driver boot up. Likewise the ttyD device was + set to ~CLOCAL. This has been fixed in this driver. + Now ttyD is CLOCAL and cud is ~CLOCAL. The fix + for this can be found in pc_init. + + 3. In ditty.c many changes were made to eliminate bugs + and warning messages. Two ioctl calls were eliminated + as well a problem involving using the returned baud + index to determine the drivers baud rate. Newer + Linux kernels support higher baud rates by using + 0x1000 bit. When the returned value (ored with + 0x1000) was used to reference our fbaud table a + serious memory problem occured. This has been fixed. + + 4. Added a request_region call to post_fep_init. This + should cause the i/o ports being used to be + registered with proc. + + 5. Modified digiConfig to set all cud and ttyD devices + to read/write all permission. + + 6. Developed a new apps called buildPCI that provides + an easy way to build device nodes for PCI cards. + + 7. Modified user.doc and technotes.doc document the + use of buildPCI. + +Files affected : epca.c, ditty.c, digiConfig, user.doc, technotes.doc +Release version : 1.0.0 (Official release) +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : August 21, 1996 +Description (Verbose) : Made the following modifications: + + 1. Removed code from pc_close which closes the + drivers line discipline and restores its original + line discipline. This is currently unecessary, + though future fast cook enhancements may require + this. + + 2. Removed code in block_til_ready that set the + asyncflags to either ASYNC_CALLOUT_ACTIVE, or + ASYNC_NORMAL_ACTIVE. This code was redundant + as it already existed in block_til_ready. + + 3. Added code in block_til_ready to cause a return + prior to schedule being called if the device + was a CALLOUT device. CALLOUT devices never + block on CD. (This was a serious bug that + prevented the CALLOUT devices (ttyD) from + functioning properly in some instances. + + Make a change in the MODEMCHG_IND case of doevent + such that it does not require ASYNC_CALLOUT_ACTIVE + or ASYNC_NORMAL_ACTIVE to be set in order to + unblock an open (Using wait_interruptible). + + Thanks to Mike McLagan (mike.mclagan@linux.org) + for diagnosing and fixing this problem. + + 4. Made changes to the disposition of CLOCAL on + both SERIAL NORMAL and CALLOUT devices. Both + device types now have CLOCAL active at default. + This may be changed with a stty command. + + 5. Made changes to digiConfig such that it checks + major.h (If valid) for the correct major + numbers to use. + +Files affected : epca.c, digiConfig +Release version : 1.0.1a + + +----------------------------------------------------------------------- +Programmer : Ronnie Sanford +Date : September 17, 1996 +Description (Verbose) : Made the following modifications: + + 1. Modified pc_open such that it no longer checks + the cflag value returned by termios2digi_c for + CLOCAL. Digi hardware does not use this value + and thus termios2digi_c rightly screens this + value out. This driver checks for CLOCAL using + the drivers cflag value as known by the Linux OS. + (The value passed into termios2digi_c) + + 2. Modified termios2digi_c to screen out the + CBAUDEX in CBAUD. This error caused parity to + automaticaly be enabled on at higher baud rates. + + + 3. Added the "disable_bh()" call to the shutdown + subroutine. Hopefully this will allow the driver + to correctly clean up after itself when used as a + module. + + 4. Added support for the PC/XI and 64K PC/XE cards. + This involved primarily modifying digiDload to + initialize and boot the new cards; however + driver modifications were also required to + provide the proper windowing for the newly + supported cards. (Code was also added to + determine the memory segment of the XI card as + that card may have more than 64K. Currently + digiDload assumes a 64K XI card.) + + 5. Added subroutine called epca_setup that can be + called during LILO boot up. This provides the + user an easy way to change cards; without + running digiConfig and without recompiling the + kernel. Added code in pc_init and pc_open to + support the epca_setup routine. pc_init checks + the liloconfig flag (Which is set by epca_setup) + to determine if the driver is using the LILO + arguments. If not pc_init loads the board data + found in epcaconfig.h; if so it DOESN'T load + epcaconfig data depending on epca_setup to handle + board configuration. pc_open has been modified + such that it checks to insure that no errors + occured during the LILO boot process. If a + user attempts to boot the driver (via. LILO) + with incorrect data, the open will fail. + + 6. Modified the windowing routines pcxe_rxwinon + and pcxe_txwinon routines. A bug existed such + that those routines checked to see if the rxwin + and txwin flags were reset. If so they assumed + the board was an XI or 64K XE. Furthermore since + these flags were never initialized in our driver + sometimes they were 0 and therefore caused a + memory fault (Or at least a window overrun). This + code has been removed since the pcxe shares + nothing in common with the 64K XI and XE. + + 7. Added code in pc_init to set the memory_seg for + the various boards. This code was necessary to + correct a bug in the PCXE, PCXEVE code where + receive and transmit pointers were being calculated + from an uninitialized variable (memory_seg). + + 8. Modified digiConfig to allow 64K PC/XI and 64K + PC/XE cards to be configured. + + 9. Made changes to support the new 2.1.x development + kernel. In particular this required changing all + references to vremap to ioremap. + + 10. Modified digiConfig such that it now generates + node names corresponding to their internal + as opposed to the label on the port itself. Nodes + (ttyD?? and cud??) now start at 0. Example: + ttyD0 and cud0 represent port 1 on any supported + Digi product. A similar change has been made + in buildPCI.c. + + 12. At the early portion of post_fep_init if a PCI + card is detected a warning message could be given + incorrectly if 64 ports were attached to a PCI + card. The below line : + + epcaassert(bd->numports > 64,"PCI returned a invalid number of ports"); + + was changed to : + + epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); + + Remember that epcaassert checks for NOT true. + Special thanks to Daniel Taylor for fixing this. + + 13. Modified the epcaparam routine. In version 100 + and 101a there was a line that looked like the + below: + + if (ch->omodem != mval) + + The problem with this line was that the first time + through omodem was not initialized. Secondly, since + many TIOC commands did not alter mval (They use + a different variable) changes made by these commands + could be lost. This line was changed to: + + mval ^= ch->modemfake & (mval ^ ch->modem); + + if (ch->omodem ^ mval) + + 14. Modified digiConfig in such a way that it checks + the version number of the kernel and if it finds + a 2.x.x kernel or higher it reads the necessary + major numbers for cud and ttyD devices from major.h. + This was also done in prior versions but these + versions required a #define which identified the + kernel as a version which did not have major numbers + assigned to Digi systems. This #define is no + longer required allowing the same source tree for + multiple kernel releases. + + 15. Used macros to replace kernel specific calls such + as put_fs_long, get_fs_long, put_user, and get_user + the kernel version is now detected and the macro + is defined as to correspond with the kernel it + is being compiled into. Again this was done to + allow one source tree for multiple kernel releases. + + 16. Added support for the new 2.1.x development kernels + to digiInstall. + +Files affected : epca.c, digiConfig +Release version : 1.1.0 +----------------------------------------------------------------------- +Programmer : Daniel Taylor +Date : April 25, 1997 +Description (Verbose) : Updated driver: + 1. Fixed DCD bug. (&tq_scheduler) + 2. Removed BH handler code, as it was only handling + hangups, and not being called for that. + 3. Namespace cleanup (DIGI_TIMER2 => DIGI_TIMER) + 4. Updated to 2.1.36, removed #ifdefs for earlier + kernel revisions. +Files affected : epca.c +Release version : 1.1.1 (BETA) +----------------------------------------------------------------------- diff --git a/drivers/char/epca.c b/drivers/char/epca.c new file mode 100644 index 000000000000..3a40a0504620 --- /dev/null +++ b/drivers/char/epca.c @@ -0,0 +1,4313 @@ +/* + + + Copyright (C) 1996 Digi International. + + For technical support please email digiLinux@dgii.com or + call Digi tech support at (612) 912-3456 + + Much of this design and code came from epca.c which was + copyright (C) 1994, 1995 Troy De Jongh, and subsquently + modified by David Nugent, Christoph Lameter, Mike McLagan. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +--------------------------------------------------------------------------- */ +/* See README.epca for change history --DAT*/ + + +#ifdef MODVERSIONS +#define MODULE +#endif + +/* ----------------------------------------------------------------------- + This way modules should work regardless if they defined MODULE or + MODVERSIONS. (MODVERSIONS is for the newer kernels ... +-------------------------------------------------------------------------- */ + +#ifdef MODULE +#include +#endif /* MODULE */ + +#include + +#define NEW_MODULES + +#ifdef NEW_MODULES +#ifdef MODVERSIONS +#include +#endif /* MODVERSIONS */ +#endif /* NEW_MODULES */ + +#ifdef MODULE +#include +#endif /* MODULE */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef MODULE +#ifndef NEW_MODULES +char kernel_version[]=UTS_RELEASE; +#endif /* NEW_MODULE */ +#endif /* MODULE */ + + +#ifdef CONFIG_PCI +#define ENABLE_PCI +#endif /* CONFIG_PCI */ + + + +#include +#define putUser(arg1, arg2) put_user(arg1, (unsigned long *)arg2) +#define getUser(arg1, arg2) get_user(arg1, (unsigned int *)arg2) + + + +#ifdef ENABLE_PCI +#include +#include +#include +#endif /* ENABLE_PCI */ + +#include +#include +#include +#include + +/* ---------------------- Begin defines ------------------------ */ + +#define VERSION "1.1.0" + +/* This major needs to be submitted to Linux to join the majors list */ + +#define DIGIINFOMAJOR 35 /* For Digi specific ioctl */ + + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAXCARDS 7 +#define epcaassert(x, msg) if (!(x)) epca_error(__LINE__, msg) + +/* ----------------- Begin global definitions ------------------- */ + +static char mesg[100]; +static int pc_refcount, nbdevs = 0, num_cards = 0, liloconfig = 0; +static int digi_poller_inhibited = 1 ; + +static int setup_error_code = 0; +static int invalid_lilo_config = 0; + +/* ----------------------------------------------------------------------- + MAXBOARDS is typically 12, but ISA and EISA cards are restricted to + 7 below. +--------------------------------------------------------------------------*/ +static struct board_info boards[7]; + + +/* ------------- Begin structures used for driver registeration ---------- */ + +struct tty_driver pc_driver; +struct tty_driver pc_callout; +struct tty_driver pc_info; + +/* The below structures are used to initialize the tty_driver structures. */ + +/* ------------------------------------------------------------------------- + Note : MAX_ALLOC is currently limited to 0x100. This restriction is + placed on us by Linux not Digi. +----------------------------------------------------------------------------*/ +static struct tty_struct *pc_table[MAX_ALLOC]; +static struct termios *pc_termios[MAX_ALLOC]; +static struct termios *pc_termios_locked[MAX_ALLOC]; + + +/* ------------------ Begin Digi specific structures -------------------- */ + +/* ------------------------------------------------------------------------ + digi_channels represents an array of structures that keep track of + each channel of the Digi product. Information such as transmit and + receive pointers, termio data, and signal definitions (DTR, CTS, etc ...) + are stored here. This structure is NOT used to overlay the cards + physical channel structure. +-------------------------------------------------------------------------- */ + +static struct channel digi_channels[MAX_ALLOC]; + +/* ------------------------------------------------------------------------ + card_ptr is an array used to hold the address of the + first channel structure of each card. This array will hold + the addresses of various channels located in digi_channels. +-------------------------------------------------------------------------- */ +static struct channel *card_ptr[MAXCARDS]; + +/* ---------------------- Begin function prototypes --------------------- */ + +/* ---------------------------------------------------------------------- + Begin generic memory functions. These functions will be alias + (point at) more specific functions dependant on the board being + configured. +----------------------------------------------------------------------- */ + + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +#endif /* MODULE */ + +static inline void memwinon(struct board_info *b, unsigned int win); +static inline void memwinoff(struct board_info *b, unsigned int win); +static inline void globalwinon(struct channel *ch); +static inline void rxwinon(struct channel *ch); +static inline void txwinon(struct channel *ch); +static inline void memoff(struct channel *ch); +static inline void assertgwinon(struct channel *ch); +static inline void assertmemoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for cx_like products --- */ + +static inline void pcxem_memwinon(struct board_info *b, unsigned int win); +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxem_globalwinon(struct channel *ch); +static inline void pcxem_rxwinon(struct channel *ch); +static inline void pcxem_txwinon(struct channel *ch); +static inline void pcxem_memoff(struct channel *ch); + +/* ------ Begin more 'specific' memory functions for the pcxe ------- */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win); +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxe_globalwinon(struct channel *ch); +static inline void pcxe_rxwinon(struct channel *ch); +static inline void pcxe_txwinon(struct channel *ch); +static inline void pcxe_memoff(struct channel *ch); + +/* ---- Begin more 'specific' memory functions for the pc64xe and pcxi ---- */ +/* Note : pc64xe and pcxi share the same windowing routines */ + +static inline void pcxi_memwinon(struct board_info *b, unsigned int win); +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win); +static inline void pcxi_globalwinon(struct channel *ch); +static inline void pcxi_rxwinon(struct channel *ch); +static inline void pcxi_txwinon(struct channel *ch); +static inline void pcxi_memoff(struct channel *ch); + +/* - Begin 'specific' do nothing memory functions needed for some cards - */ + +static inline void dummy_memwinon(struct board_info *b, unsigned int win); +static inline void dummy_memwinoff(struct board_info *b, unsigned int win); +static inline void dummy_globalwinon(struct channel *ch); +static inline void dummy_rxwinon(struct channel *ch); +static inline void dummy_txwinon(struct channel *ch); +static inline void dummy_memoff(struct channel *ch); +static inline void dummy_assertgwinon(struct channel *ch); +static inline void dummy_assertmemoff(struct channel *ch); + +/* ------------------- Begin declare functions ----------------------- */ + +static inline struct channel *verifyChannel(register struct tty_struct *); +static inline void pc_sched_event(struct channel *, int); +static void epca_error(int, char *); +static void pc_close(struct tty_struct *, struct file *); +static void shutdown(struct channel *); +static void pc_hangup(struct tty_struct *); +static void pc_put_char(struct tty_struct *, unsigned char); +static int pc_write_room(struct tty_struct *); +static int pc_chars_in_buffer(struct tty_struct *); +static void pc_flush_buffer(struct tty_struct *); +static void pc_flush_chars(struct tty_struct *); +static int block_til_ready(struct tty_struct *, struct file *, + struct channel *); +static int pc_open(struct tty_struct *, struct file *); +static void post_fep_init(unsigned int crd); +static void epcapoll(unsigned long); +static void doevent(int); +static void fepcmd(struct channel *, int, int, int, int, int); +static unsigned termios2digi_h(struct channel *ch, unsigned); +static unsigned termios2digi_i(struct channel *ch, unsigned); +static unsigned termios2digi_c(struct channel *ch, unsigned); +static void epcaparam(struct tty_struct *, struct channel *); +static void receive_data(struct channel *); +static int pc_ioctl(struct tty_struct *, struct file *, + unsigned int, unsigned long); +static void pc_set_termios(struct tty_struct *, struct termios *); +static void do_softint(void *); +static void pc_stop(struct tty_struct *); +static void pc_start(struct tty_struct *); +static void pc_throttle(struct tty_struct * tty); +static void pc_unthrottle(struct tty_struct *tty); +static void digi_send_break(struct channel *ch, int msec); +static void setup_empty_event(struct tty_struct *tty, struct channel *ch); +void epca_setup(char *, int *); +void console_print(const char *); + +static int get_termio(struct tty_struct *, struct termio *); +static int pc_write(struct tty_struct *, int, const unsigned char *, int); +int pc_init(void); + +#ifdef ENABLE_PCI +static int init_PCI(int); +static int get_PCI_configuration(char, char, unsigned int *, unsigned int *, + unsigned int *, unsigned int *, + unsigned int *, unsigned int *); +#endif /* ENABLE_PCI */ + + +/* ------------------------------------------------------------------ + Table of functions for each board to handle memory. Mantaining + parallelism is a *very* good idea here. The idea is for the + runtime code to blindly call these functions, not knowing/caring + about the underlying hardware. This stuff should contain no + conditionals; if more functionality is needed a different entry + should be established. These calls are the interface calls and + are the only functions that should be accessed. Anyone caught + making direct calls deserves what they get. +-------------------------------------------------------------------- */ + +static inline void memwinon(struct board_info *b, unsigned int win) +{ + (b->memwinon)(b, win); +} + +static inline void memwinoff(struct board_info *b, unsigned int win) +{ + (b->memwinoff)(b, win); +} + +static inline void globalwinon(struct channel *ch) +{ + (ch->board->globalwinon)(ch); +} + +static inline void rxwinon(struct channel *ch) +{ + (ch->board->rxwinon)(ch); +} + +static inline void txwinon(struct channel *ch) +{ + (ch->board->txwinon)(ch); +} + +static inline void memoff(struct channel *ch) +{ + (ch->board->memoff)(ch); +} +static inline void assertgwinon(struct channel *ch) +{ + (ch->board->assertgwinon)(ch); +} + +static inline void assertmemoff(struct channel *ch) +{ + (ch->board->assertmemoff)(ch); +} + +/* --------------------------------------------------------- + PCXEM windowing is the same as that used in the PCXR + and CX series cards. +------------------------------------------------------------ */ + +static inline void pcxem_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN|win, (int)b->port + 1); +} + +static inline void pcxem_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(0, (int)b->port + 1); +} + +static inline void pcxem_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxem_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxem_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline void pcxem_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port + 1); +} + +/* ----------------- Begin pcxe memory window stuff ------------------ */ + +static inline void pcxe_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(FEPWIN | win, (int)b->port + 1); +} + +static inline void pcxe_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, + (int)b->port + 1); + outb_p(0, (int)b->port + 1); +} + +static inline void pcxe_globalwinon(struct channel *ch) +{ + outb_p( FEPWIN, (int)ch->board->port + 1); +} + +static inline void pcxe_rxwinon(struct channel *ch) +{ + outb_p(ch->rxwin, (int)ch->board->port + 1); +} + +static inline void pcxe_txwinon(struct channel *ch) +{ + outb_p(ch->txwin, (int)ch->board->port + 1); +} + +static inline void pcxe_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); + outb_p(0, (int)ch->board->port + 1); +} + +/* ------------- Begin pc64xe and pcxi memory window stuff -------------- */ + +static inline void pcxi_memwinon(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) | FEPMEM, (int)b->port); +} + +static inline void pcxi_memwinoff(struct board_info *b, unsigned int win) +{ + outb_p(inb((int)b->port) & ~FEPMEM, (int)b->port); +} + +static inline void pcxi_globalwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_rxwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_txwinon(struct channel *ch) +{ + outb_p(FEPMEM, (int)ch->board->port); +} + +static inline void pcxi_memoff(struct channel *ch) +{ + outb_p(0, (int)ch->board->port); +} + +static inline void pcxi_assertgwinon(struct channel *ch) +{ + epcaassert(inb((int)ch->board->port) & FEPMEM, "Global memory off"); +} + +static inline void pcxi_assertmemoff(struct channel *ch) +{ + epcaassert(!(inb((int)ch->board->port) & FEPMEM), "Memory on"); +} + + +/* ---------------------------------------------------------------------- + Not all of the cards need specific memory windowing routines. Some + cards (Such as PCI) needs no windowing routines at all. We provide + these do nothing routines so that the same code base can be used. + The driver will ALWAYS call a windowing routine if it thinks it needs + to; regardless of the card. However, dependant on the card the routine + may or may not do anything. +---------------------------------------------------------------------------*/ + +static inline void dummy_memwinon(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_memwinoff(struct board_info *b, unsigned int win) +{ +} + +static inline void dummy_globalwinon(struct channel *ch) +{ +} + +static inline void dummy_rxwinon(struct channel *ch) +{ +} + +static inline void dummy_txwinon(struct channel *ch) +{ +} + +static inline void dummy_memoff(struct channel *ch) +{ +} + +static inline void dummy_assertgwinon(struct channel *ch) +{ +} + +static inline void dummy_assertmemoff(struct channel *ch) +{ +} + +/* ----------------- Begin verifyChannel function ----------------------- */ +static inline struct channel *verifyChannel(register struct tty_struct *tty) +{ /* Begin verifyChannel */ + + /* -------------------------------------------------------------------- + This routine basically provides a sanity check. It insures that + the channel returned is within the proper range of addresses as + well as properly initialized. If some bogus info gets passed in + through tty->driver_data this should catch it. + --------------------------------------------------------------------- */ + + if (tty) + { /* Begin if tty */ + + register struct channel *ch = (struct channel *)tty->driver_data; + + if ((ch >= &digi_channels[0]) && (ch < &digi_channels[nbdevs])) + { + if (ch->magic == EPCA_MAGIC) + return ch; + } + + } /* End if tty */ + + /* Else return a NULL for invalid */ + return NULL; + +} /* End verifyChannel */ + +/* ------------------ Begin pc_sched_event ------------------------- */ + +static inline void pc_sched_event(struct channel *ch, int event) +{ /* Begin pc_sched_event */ + + + /* ---------------------------------------------------------------------- + We call this to schedule interrupt processing on some event. The + kernel sees our request and calls the related routine in OUR driver. + -------------------------------------------------------------------------*/ + + ch->event |= 1 << event; + queue_task(&ch->tqueue, &tq_scheduler); + + +} /* End pc_sched_event */ + +/* ------------------ Begin epca_error ------------------------- */ + +static void epca_error(int line, char *msg) +{ /* Begin epca_error */ + + printk(KERN_ERR "epca_error (Digi): line = %d %s\n",line,msg); + return; + +} /* End epca_error */ + +/* ------------------ Begin pc_close ------------------------- */ +static void pc_close(struct tty_struct * tty, struct file * filp) +{ /* Begin pc_close */ + + struct channel *ch; + unsigned long flags; + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return; + } + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) + { + restore_flags(flags); + return; + } + + /* Check to see if the channel is open more than once */ + if (ch->count-- > 1) + { /* Begin channel is open more than once */ + + /* ------------------------------------------------------------- + Return without doing anything. Someone might still be using + the channel. + ---------------------------------------------------------------- */ + + restore_flags(flags); + return; + } /* End channel is open more than once */ + + /* Port open only once go ahead with shutdown & reset */ + + if (ch->count < 0) + { + ch->count = 0; + } + + /* --------------------------------------------------------------- + Let the rest of the driver know the channel is being closed. + This becomes important if an open is attempted before close + is finished. + ------------------------------------------------------------------ */ + + ch->asyncflags |= ASYNC_CLOSING; + + /* ------------------------------------------------------------- + Save the termios structure, since this port may have + separate termios for callout and dialin. + --------------------------------------------------------------- */ + + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + ch->normal_termios = *tty->termios; + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + ch->callout_termios = *tty->termios; + + tty->closing = 1; + + if (ch->asyncflags & ASYNC_INITIALIZED) + { + /* Setup an event to indicate when the transmit buffer empties */ + setup_empty_event(tty, ch); + tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ + } + + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + shutdown(ch); + tty->closing = 0; + ch->event = 0; + ch->tty = NULL; + + if (ch->blocked_open) + { /* Begin if blocked_open */ + + if (ch->close_delay) + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + ch->close_delay; + schedule(); + } + + wake_up_interruptible(&ch->open_wait); + + } /* End if blocked_open */ + + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | + ASYNC_CALLOUT_ACTIVE | ASYNC_CLOSING); + wake_up_interruptible(&ch->close_wait); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + + restore_flags(flags); + + } /* End if ch != NULL */ + +} /* End pc_close */ + +/* ------------------ Begin shutdown ------------------------- */ + +static void shutdown(struct channel *ch) +{ /* Begin shutdown */ + + unsigned long flags; + struct tty_struct *tty; + volatile struct board_chan *bc; + + if (!(ch->asyncflags & ASYNC_INITIALIZED)) + return; + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + + /* ------------------------------------------------------------------ + In order for an event to be generated on the receipt of data the + idata flag must be set. Since we are shutting down, this is not + necessary clear this flag. + --------------------------------------------------------------------- */ + + if (bc) + bc->idata = 0; + + tty = ch->tty; + + /* ---------------------------------------------------------------- + If we're a modem control device and HUPCL is on, drop RTS & DTR. + ------------------------------------------------------------------ */ + + if (tty->termios->c_cflag & HUPCL) + { + ch->omodem &= ~(ch->m_rts | ch->m_dtr); + fepcmd(ch, SETMODEM, 0, ch->m_dtr | ch->m_rts, 10, 1); + } + + memoff(ch); + + /* ------------------------------------------------------------------ + The channel has officialy been closed. The next time it is opened + it will have to reinitialized. Set a flag to indicate this. + ---------------------------------------------------------------------- */ + + /* Prevent future Digi programmed interrupts from coming active */ + + ch->asyncflags &= ~ASYNC_INITIALIZED; + restore_flags(flags); + +} /* End shutdown */ + +/* ------------------ Begin pc_hangup ------------------------- */ + +static void pc_hangup(struct tty_struct *tty) +{ /* Begin pc_hangup */ + + struct channel *ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if ch != NULL */ + + unsigned long flags; + + save_flags(flags); + cli(); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + shutdown(ch); + +#ifdef MODULE + if (ch->count) + MOD_DEC_USE_COUNT; +#endif /* MODULE */ + + + ch->tty = NULL; + ch->event = 0; + ch->count = 0; + restore_flags(flags); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | ASYNC_CALLOUT_ACTIVE); + wake_up_interruptible(&ch->open_wait); + + } /* End if ch != NULL */ + +} /* End pc_hangup */ + +/* ------------------ Begin pc_write ------------------------- */ + +static int pc_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int bytesAvailable) +{ /* Begin pc_write */ + + register unsigned int head, tail; + register int dataLen; + register int size; + register int amountCopied; + + + struct channel *ch; + unsigned long flags; + int remain; + volatile struct board_chan *bc; + + + /* ---------------------------------------------------------------- + pc_write is primarily called directly by the kernel routine + tty_write (Though it can also be called by put_char) found in + tty_io.c. pc_write is passed a line discipline buffer where + the data to be written out is stored. The line discipline + implementation itself is done at the kernel level and is not + brought into the driver. + ------------------------------------------------------------------- */ + + /* Stop users from hurting themselves on control minor */ + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return (0) ; + } + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return 0; + + /* Make a pointer to the channel data structure found on the board. */ + + bc = ch->brdchan; + size = ch->txbufsize; + + if (from_user) + { /* Begin from_user */ + + save_flags(flags); + cli(); + + globalwinon(ch); + + /* ----------------------------------------------------------------- + Anding against size will wrap the pointer back to its begining + position if it is necessary. This will only work if size is + a power of 2 which should always be the case. Size is determined + by the cards on board FEP/OS. + -------------------------------------------------------------------- */ + + /* head refers to the next empty location in which data may be stored */ + + head = bc->tin & (size - 1); + + /* tail refers to the next data byte to be transmitted */ + + tail = bc->tout; + + /* Consider changing this to a do statement to make sure */ + + if (tail != bc->tout) + tail = bc->tout; + + /* ------------------------------------------------------------------ + Anding against size will wrap the pointer back to its begining + position if it is necessary. This will only work if size is + a power of 2 which should always be the case. Size is determined + by the cards on board FEP/OS. + --------------------------------------------------------------------- */ + + tail &= (size - 1); + + /* ----------------------------------------------------------------- + Two situations can affect how space in the transmit buffer + is calculated. You can have a situation where the transmit + in pointer (tin) head has wrapped around and actually has a + lower address than the transmit out pointer (tout) tail; or + the transmit in pointer (tin) head will not be wrapped around + yet, and have a higher address than the transmit out pointer + (tout) tail. Obviously space available in the transmit buffer + is calculated differently for each case. + + Example 1: + + Consider a 10 byte buffer where head is a pointer to the next + empty location in the buffer and tail is a pointer to the next + byte to transmit. In this example head will not have wrapped + around and therefore head > tail. + + 0 1 2 3 4 5 6 7 8 9 + tail head + + The above diagram shows that buffer locations 2,3,4,5 and 6 have + data to be transmited, while head points at the next empty + location. To calculate how much space is available first we have + to determine if the head pointer (tin) has wrapped. To do this + compare the head pointer to the tail pointer, If head is equal + or greater than tail; then it has not wrapped; and the space may + be calculated by subtracting tail from head and then subtracting + that value from the buffers size. A one is subtracted from the + new value to indicate how much space is available between the + head pointer and end of buffer; as well as the space between the + begining of the buffer and the tail. If the head is not greater + or equal to the tail this indicates that the head has wrapped + around to the begining of the buffer. To calculate the space + available in this case simply subtract head from tail. This new + value minus one represents the space available betwwen the head + and tail pointers. In this example head (7) is greater than tail (2) + and therefore has not wrapped around. We find the space by first + subtracting tail from head (7-2=5). We then subtract this value + from the buffer size of ten and subtract one (10-5-1=4). The space + remaining is 4 bytes. + + Example 2: + + Consider a 10 byte buffer where head is a pointer to the next + empty location in the buffer and tail is a pointer to the next + byte to transmit. In this example head will wrapped around and + therefore head < tail. + + 0 1 2 3 4 5 6 7 8 9 + head tail + + The above diagram shows that buffer locations 7,8,9,0 and 1 have + data to be transmited, while head points at the next empty + location. To find the space available we compare head to tail. If + head is not equal to, or greater than tail this indicates that head + has wrapped around. In this case head (2) is not equal to, or + greater than tail (7) and therefore has already wrapped around. To + calculate the available space between the two pointers we subtract + head from tail (7-2=5). We then subtract one from this new value + (5-1=4). We have 5 bytes empty remaining in the buffer. Unlike the + previous example these five bytes are located between the head and + tail pointers. + + ----------------------------------------------------------------------- */ + + dataLen = (head >= tail) ? (size - (head - tail) - 1) : (tail - head - 1); + + /* ---------------------------------------------------------------------- + In this case bytesAvailable has been passed into pc_write and + represents the amount of data that needs to be written. dataLen + represents the amount of space available on the card. Whichever + value is smaller will be the amount actually written. + bytesAvailable will then take on this newly calculated value. + ---------------------------------------------------------------------- */ + + bytesAvailable = MIN(dataLen, bytesAvailable); + + /* First we read the data in from the file system into a temp buffer */ + + if (bytesAvailable) + { /* Begin bytesAvailable */ + + /* Can the user buffer be accessed at the moment ? */ + if (verify_area(VERIFY_READ, (char*)buf, bytesAvailable)) + bytesAvailable = 0; /* Can't do; try again later */ + else /* Evidently it can, began transmission */ + { /* Begin if area verified */ + /* --------------------------------------------------------------- + The below function reads data from user memory. This routine + can not be used in an interrupt routine. (Because it may + generate a page fault) It can only be called while we can the + user context is accessible. + + The prototype is : + inline void copy_from_user(void * to, const void * from, + unsigned long count); + + You must include + I also think (Check hackers guide) that optimization must + be turned ON. (Which sounds strange to me...) + + Remember copy_from_user WILL generate a page fault if the + user memory being accessed has been swapped out. This can + cause this routine to temporarily sleep while this page + fault is occuring. + + ----------------------------------------------------------------- */ + + copy_from_user(ch->tmp_buf, buf, bytesAvailable); + + } /* End if area verified */ + + } /* End bytesAvailable */ + + /* ------------------------------------------------------------------ + Set buf to this address for the moment. tmp_buf was allocated in + post_fep_init. + --------------------------------------------------------------------- */ + buf = ch->tmp_buf; + memoff(ch); + restore_flags(flags); + + } /* End from_user */ + + /* All data is now local */ + + amountCopied = 0; + save_flags(flags); + cli(); + + globalwinon(ch); + + head = bc->tin & (size - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + tail &= (size - 1); + + /* If head >= tail, head has not wrapped around. */ + if (head >= tail) + { /* Begin head has not wrapped */ + + /* --------------------------------------------------------------- + remain (much like dataLen above) represents the total amount of + space available on the card for data. Here dataLen represents + the space existing between the head pointer and the end of + buffer. This is important because a memcpy cannot be told to + automatically wrap around when it hits the buffer end. + ------------------------------------------------------------------ */ + + dataLen = size - head; + remain = size - (head - tail) - 1; + + } /* End head has not wrapped */ + else + { /* Begin head has wrapped around */ + + remain = tail - head - 1; + dataLen = remain; + + } /* End head has wrapped around */ + + /* ------------------------------------------------------------------- + Check the space on the card. If we have more data than + space; reduce the amount of data to fit the space. + ---------------------------------------------------------------------- */ + + bytesAvailable = MIN(remain, bytesAvailable); + + txwinon(ch); + while (bytesAvailable > 0) + { /* Begin while there is data to copy onto card */ + + /* ----------------------------------------------------------------- + If head is not wrapped, the below will make sure the first + data copy fills to the end of card buffer. + ------------------------------------------------------------------- */ + + dataLen = MIN(bytesAvailable, dataLen); + memcpy(ch->txptr + head, buf, dataLen); + buf += dataLen; + head += dataLen; + amountCopied += dataLen; + bytesAvailable -= dataLen; + + if (head >= size) + { + head = 0; + dataLen = tail; + } + + } /* End while there is data to copy onto card */ + + ch->statusflags |= TXBUSY; + globalwinon(ch); + bc->tin = head; + + if ((ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + + return(amountCopied); + +} /* End pc_write */ + +/* ------------------ Begin pc_put_char ------------------------- */ + +static void pc_put_char(struct tty_struct *tty, unsigned char c) +{ /* Begin pc_put_char */ + + + pc_write(tty, 0, &c, 1); + return; + +} /* End pc_put_char */ + +/* ------------------ Begin pc_write_room ------------------------- */ + +static int pc_write_room(struct tty_struct *tty) +{ /* Begin pc_write_room */ + + int remain; + struct channel *ch; + unsigned long flags; + unsigned int head, tail; + volatile struct board_chan *bc; + + remain = 0; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + head = bc->tin & (ch->txbufsize - 1); + tail = bc->tout; + + if (tail != bc->tout) + tail = bc->tout; + /* Wrap tail if necessary */ + tail &= (ch->txbufsize - 1); + + if ((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + if (remain && (ch->statusflags & LOWWAIT) == 0) + { + ch->statusflags |= LOWWAIT; + bc->ilow = 1; + } + memoff(ch); + restore_flags(flags); + } + + /* Return how much room is left on card */ + return remain; + +} /* End pc_write_room */ + +/* ------------------ Begin pc_chars_in_buffer ---------------------- */ + +static int pc_chars_in_buffer(struct tty_struct *tty) +{ /* Begin pc_chars_in_buffer */ + + int chars; + unsigned int ctail, head, tail; + int remain; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return(0); + + save_flags(flags); + cli(); + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + head = bc->tin; + ctail = ch->mailbox->cout; + + if (tail == head && ch->mailbox->cin == ctail && bc->tbusy == 0) + chars = 0; + else + { /* Begin if some space on the card has been used */ + + head = bc->tin & (ch->txbufsize - 1); + tail &= (ch->txbufsize - 1); + + /* -------------------------------------------------------------- + The logic here is basically opposite of the above pc_write_room + here we are finding the amount of bytes in the buffer filled. + Not the amount of bytes empty. + ------------------------------------------------------------------- */ + + if ((remain = tail - head - 1) < 0 ) + remain += ch->txbufsize; + + chars = (int)(ch->txbufsize - remain); + + /* ------------------------------------------------------------- + Make it possible to wakeup anything waiting for output + in tty_ioctl.c, etc. + + If not already set. Setup an event to indicate when the + transmit buffer empties + ----------------------------------------------------------------- */ + + if (!(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + + } /* End if some space on the card has been used */ + + memoff(ch); + restore_flags(flags); + + /* Return number of characters residing on card. */ + return(chars); + +} /* End pc_chars_in_buffer */ + +/* ------------------ Begin pc_flush_buffer ---------------------- */ + +static void pc_flush_buffer(struct tty_struct *tty) +{ /* Begin pc_flush_buffer */ + + unsigned int tail; + unsigned long flags; + struct channel *ch; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) == NULL) + return; + + save_flags(flags); + cli(); + + globalwinon(ch); + + bc = ch->brdchan; + tail = bc->tout; + + /* Have FEP move tout pointer; effectively flushing transmit buffer */ + + fepcmd(ch, STOUT, (unsigned) tail, 0, 0, 0); + + memoff(ch); + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + +} /* End pc_flush_buffer */ + +/* ------------------ Begin pc_flush_chars ---------------------- */ + +static void pc_flush_chars(struct tty_struct *tty) +{ /* Begin pc_flush_chars */ + + struct channel * ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { + unsigned long flags; + + save_flags(flags); + cli(); + + /* ---------------------------------------------------------------- + If not already set and the transmitter is busy setup an event + to indicate when the transmit empties. + ------------------------------------------------------------------- */ + + if ((ch->statusflags & TXBUSY) && !(ch->statusflags & EMPTYWAIT)) + setup_empty_event(tty,ch); + + restore_flags(flags); + } + +} /* End pc_flush_chars */ + +/* ------------------ Begin block_til_ready ---------------------- */ + +static int block_til_ready(struct tty_struct *tty, + struct file *filp, struct channel *ch) +{ /* Begin block_til_ready */ + + struct wait_queue wait = {current, NULL}; + int retval, do_clocal = 0; + unsigned long flags; + + + if (tty_hung_up_p(filp)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + return(retval); + } + + /* ----------------------------------------------------------------- + If the device is in the middle of being closed, then block + until it's done, and then try again. + -------------------------------------------------------------------- */ + if (ch->asyncflags & ASYNC_CLOSING) + { + interruptible_sleep_on(&ch->close_wait); + + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; + } + + /* ----------------------------------------------------------------- + If this is a callout device, then just make sure the normal + device isn't being used. + -------------------------------------------------------------------- */ + + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) + { /* A cud device has been opened */ + if (ch->asyncflags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_SESSION_LOCKOUT) && + (ch->session != current->session)) + return -EBUSY; + + if ((ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (ch->asyncflags & ASYNC_PGRP_LOCKOUT) && + (ch->pgrp != current->pgrp)) + return -EBUSY; + + ch->asyncflags |= ASYNC_CALLOUT_ACTIVE; + + return 0; + } /* End a cud device has been opened */ + + if (filp->f_flags & O_NONBLOCK) + { + /* ----------------------------------------------------------------- + If non-blocking mode is set, then make the check up front + and then exit. + -------------------------------------------------------------------- */ + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + } + + + if (ch->asyncflags & ASYNC_CALLOUT_ACTIVE) + { + if (ch->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } + else + { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* Block waiting for the carrier detect and the line to become free */ + + retval = 0; + add_wait_queue(&ch->open_wait, &wait); + save_flags(flags); + cli(); + + + /* We dec count so that pc_close will know when to free things */ + if (!tty_hung_up_p(filp)) + ch->count--; + + restore_flags(flags); + + ch->blocked_open++; + + while(1) + { /* Begin forever while */ + + current->state = TASK_INTERRUPTIBLE; + + if (tty_hung_up_p(filp) || + !(ch->asyncflags & ASYNC_INITIALIZED)) + { + if (ch->asyncflags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; + break; + } + + if (!(ch->asyncflags & ASYNC_CLOSING) && + !(ch->asyncflags & ASYNC_CALLOUT_ACTIVE) && + (do_clocal || (ch->imodem & ch->dcd))) + break; + + if (current->signal & ~current->blocked) + { + retval = -ERESTARTSYS; + break; + } + + /* --------------------------------------------------------------- + Allow someone else to be scheduled. We will occasionaly go + through this loop until one of the above conditions change. + The below schedule call will allow other processes to enter and + prevent this loop from hogging the cpu. + ------------------------------------------------------------------ */ + schedule(); + + } /* End forever while */ + + current->state = TASK_RUNNING; + remove_wait_queue(&ch->open_wait, &wait); + cli(); + if (!tty_hung_up_p(filp)) + ch->count++; + restore_flags(flags); + + ch->blocked_open--; + + if (retval) + return retval; + + ch->asyncflags |= ASYNC_NORMAL_ACTIVE; + + return 0; + +} /* End block_til_ready */ + +/* ------------------ Begin pc_open ---------------------- */ + +static int pc_open(struct tty_struct *tty, struct file * filp) +{ /* Begin pc_open */ + + struct channel *ch; + unsigned long flags; + int line, retval, boardnum; + volatile struct board_chan *bc; + volatile unsigned int head; + + /* Nothing "real" happens in open of control device */ + + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { + return (0) ; + } + + line = MINOR(tty->device) - tty->driver.minor_start; + if (line < 0 || line >= nbdevs) + { + printk(KERN_ERR " - pc_open : line out of range in pc_open\n"); + tty->driver_data = NULL; + return(-ENODEV); + } + +#ifdef MODULE + + MOD_INC_USE_COUNT; + +#endif + + ch = &digi_channels[line]; + boardnum = ch->boardnum; + + /* Check status of board configured in system. */ + + /* ----------------------------------------------------------------- + I check to see if the epca_setup routine detected an user error. + It might be better to put this in pc_init, but for the moment it + goes here. + ---------------------------------------------------------------------- */ + + if (invalid_lilo_config) + { + if (setup_error_code & INVALID_BOARD_TYPE) + printk(KERN_ERR " - pc_open: Invalid board type specified in LILO command\n"); + + if (setup_error_code & INVALID_NUM_PORTS) + printk(KERN_ERR " - pc_open: Invalid number of ports specified in LILO command\n"); + + if (setup_error_code & INVALID_MEM_BASE) + printk(KERN_ERR " - pc_open: Invalid board memory address specified in LILO command\n"); + + if (setup_error_code & INVALID_PORT_BASE) + printk(KERN_ERR " - pc_open: Invalid board port address specified in LILO command\n"); + + if (setup_error_code & INVALID_BOARD_STATUS) + printk(KERN_ERR " - pc_open: Invalid board status specified in LILO command\n"); + + if (setup_error_code & INVALID_ALTPIN) + printk(KERN_ERR " - pc_open: Invalid board altpin specified in LILO command\n"); + + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + if ((boardnum >= num_cards) || (boards[boardnum].status == DISABLED)) + { + tty->driver_data = NULL; /* Mark this device as 'down' */ + return(-ENODEV); + } + + if (( bc = ch->brdchan) == 0) + { + tty->driver_data = NULL; + return(-ENODEV); + } + + /* ------------------------------------------------------------------ + Every time a channel is opened, increment a counter. This is + necessary because we do not wish to flush and shutdown the channel + until the last app holding the channel open, closes it. + --------------------------------------------------------------------- */ + + ch->count++; + + /* ---------------------------------------------------------------- + Set a kernel structures pointer to our local channel + structure. This way we can get to it when passed only + a tty struct. + ------------------------------------------------------------------ */ + + tty->driver_data = ch; + + /* ---------------------------------------------------------------- + If this is the first time the channel has been opened, initialize + the tty->termios struct otherwise let pc_close handle it. + -------------------------------------------------------------------- */ + + /* Should this be here except for SPLIT termios ? */ + if (ch->count == 1) + { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = ch->normal_termios; + else + *tty->termios = ch->callout_termios; + } + + ch->session = current->session; + ch->pgrp = current->pgrp; + + save_flags(flags); + cli(); + + globalwinon(ch); + ch->statusflags = 0; + + /* Save boards current modem status */ + ch->imodem = bc->mstat; + + /* ---------------------------------------------------------------- + Set receive head and tail ptrs to each other. This indicates + no data available to read. + ----------------------------------------------------------------- */ + head = bc->rin; + bc->rout = head; + + /* Set the channels associated tty structure */ + ch->tty = tty; + + /* ----------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + -------------------------------------------------------------------- */ + epcaparam(tty,ch); + + ch->asyncflags |= ASYNC_INITIALIZED; + memoff(ch); + + restore_flags(flags); + + retval = block_til_ready(tty, filp, ch); + if (retval) + { + return retval; + } + + /* ------------------------------------------------------------- + Set this again in case a hangup set it to zero while this + open() was waiting for the line... + --------------------------------------------------------------- */ + ch->tty = tty; + + save_flags(flags); + cli(); + globalwinon(ch); + + /* Enable Digi Data events */ + bc->idata = 1; + + memoff(ch); + restore_flags(flags); + + return 0; + +} /* End pc_open */ + +#ifdef MODULE +/* -------------------- Begin init_module ---------------------- */ +int init_module() +{ /* Begin init_module */ + + unsigned long flags; + + save_flags(flags); + cli(); + + pc_init(); + + restore_flags(flags); + + return(0); +} /* End init_module */ + +#endif +#ifdef MODULE +/* -------------------- Begin cleanup_module ---------------------- */ +void cleanup_module() +{ /* Begin cleanup_module */ + + int count, crd; + struct board_info *bd; + struct channel *ch; + unsigned long flags; + + + save_flags(flags); + cli(); + + timer_table[DIGI_TIMER].fn = 0; + + if ((tty_unregister_driver(&pc_driver)) || + (tty_unregister_driver(&pc_callout))) + { + printk(KERN_WARNING " - DIGI : cleanup_module failed to un-register tty driver\n"); + restore_flags(flags); + return; + } + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + bd = &boards[crd]; + + if (!bd) + { /* Begin sanity check */ + printk(KERN_ERR " - Digi : cleanup_module failed\n"); + return; + } /* End sanity check */ + + ch = card_ptr[crd]; + + for (count = 0; count < bd->numports; count++, ch++) + { /* Begin for each port */ + + if (ch) + { + if (ch->tty) + tty_hangup(ch->tty); + kfree_s(ch->tmp_buf, ch->txbufsize); + } + + } /* End for each port */ + } /* End for each card */ + + + restore_flags(flags); + +} /* End cleanup_module */ +#endif /* MODULE */ + +/* ------------------ Begin pc_init ---------------------- */ + +int pc_init(void) +{ /* Begin pc_init */ + + /* ---------------------------------------------------------------- + pc_init is called by the operating system during boot up prior to + any open calls being made. In the older versions of Linux (Prior + to 2.0.0) an entry is made into tty_io.c. A pointer to the last + memory location (from kernel space) used (kmem_start) is passed + to pc_init. It is pc_inits responsibility to modify this value + for any memory that the Digi driver might need and then return + this value to the operating system. For example if the driver + wishes to allocate 1K of kernel memory, pc_init would return + (kmem_start + 1024). This memory (Between kmem_start and kmem_start + + 1024) would then be available for use exclusively by the driver. + In this case our driver does not allocate any of this kernel + memory. + ------------------------------------------------------------------*/ + + ulong flags, save_loops_per_sec; + int crd; + struct board_info *bd; + unsigned char board_id = 0; + + +#ifdef ENABLE_PCI + int pci_boards_found, pci_count; + + pci_count = 0; +#endif /* ENABLE_PCI */ + + /* ----------------------------------------------------------------------- + If epca_setup has not been ran by LILO set num_cards to defaults; copy + board structure defined by digiConfig into drivers board structure. + Note : If LILO has ran epca_setup then epca_setup will handle defining + num_cards as well as copying the data into the board structure. + -------------------------------------------------------------------------- */ + if (!liloconfig) + { /* Begin driver has been configured via. epcaconfig */ + + nbdevs = NBDEVS; + num_cards = NUMCARDS; + memcpy((void *)&boards, (void *)&static_boards, + (sizeof(struct board_info) * NUMCARDS)); + } /* End driver has been configured via. epcaconfig */ + + /* ----------------------------------------------------------------- + Note : If lilo was used to configure the driver and the + ignore epcaconfig option was choosen (digiepca=2) then + nbdevs and num_cards will equal 0 at this point. This is + okay; PCI cards will still be picked up if detected. + --------------------------------------------------------------------- */ + + /* ----------------------------------------------------------- + Set up interrupt, we will worry about memory allocation in + post_fep_init. + --------------------------------------------------------------- */ + + + printk(KERN_INFO "DIGI epca driver version %s loaded.\n",VERSION); + +#ifdef ENABLE_PCI + + /* ------------------------------------------------------------------ + NOTE : This code assumes that the number of ports found in + the boards array is correct. This could be wrong if + the card in question is PCI (And therefore has no ports + entry in the boards structure.) The rest of the + information will be valid for PCI because the begining + of pc_init scans for PCI and determines i/o and base + memory addresses. I am not sure if it is possible to + read the number of ports supported by the card prior to + it being booted (Since that is the state it is in when + pc_init is run). Because it is not possible to query the + number of supported ports until after the card has booted; + we are required to calculate the card_ptrs as the card is + is initialized (Inside post_fep_init). The negative thing + about this approach is that digiDload's call to GET_INFO + will have a bad port value. (Since this is called prior + to post_fep_init.) + + --------------------------------------------------------------------- */ + + pci_boards_found = 0; + if (pcibios_present()) + { + if(num_cards < MAXBOARDS) + pci_boards_found += init_PCI(num_cards); + num_cards += pci_boards_found; + } + else + { + printk(KERN_ERR " - No PCI BIOS found\n"); + } + +#endif /* ENABLE_PCI */ + + memset(&pc_driver, 0, sizeof(struct tty_driver)); + memset(&pc_callout, 0, sizeof(struct tty_driver)); + memset(&pc_info, 0, sizeof(struct tty_driver)); + + pc_driver.magic = TTY_DRIVER_MAGIC; + pc_driver.name = "ttyD"; + pc_driver.major = DIGI_MAJOR; + pc_driver.minor_start = 0; + pc_driver.num = MAX_ALLOC; + pc_driver.type = TTY_DRIVER_TYPE_SERIAL; + pc_driver.subtype = SERIAL_TYPE_NORMAL; + pc_driver.init_termios = tty_std_termios; + pc_driver.init_termios.c_iflag = 0; + pc_driver.init_termios.c_oflag = 0; + + pc_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pc_driver.init_termios.c_lflag = 0; + pc_driver.flags = TTY_DRIVER_REAL_RAW; + pc_driver.refcount = &pc_refcount; + pc_driver.table = pc_table; + + /* pc_termios is an array of pointers pointing at termios structs */ + /* The below should get the first pointer */ + pc_driver.termios = pc_termios; + pc_driver.termios_locked = pc_termios_locked; + + /* ------------------------------------------------------------------ + Setup entry points for the driver. These are primarily called by + the kernel in tty_io.c and n_tty.c + --------------------------------------------------------------------- */ + + pc_driver.open = pc_open; + pc_driver.close = pc_close; + pc_driver.write = pc_write; + pc_driver.write_room = pc_write_room; + pc_driver.flush_buffer = pc_flush_buffer; + pc_driver.chars_in_buffer = pc_chars_in_buffer; + pc_driver.flush_chars = pc_flush_chars; + pc_driver.put_char = pc_put_char; + pc_driver.ioctl = pc_ioctl; + pc_driver.set_termios = pc_set_termios; + pc_driver.stop = pc_stop; + pc_driver.start = pc_start; + pc_driver.throttle = pc_throttle; + pc_driver.unthrottle = pc_unthrottle; + pc_driver.hangup = pc_hangup; + pc_callout = pc_driver; + + pc_callout.name = "cud"; + pc_callout.major = DIGICU_MAJOR; + pc_callout.minor_start = 0; + pc_callout.init_termios.c_cflag = B9600 | CS8 | CREAD | CLOCAL | HUPCL; + pc_callout.subtype = SERIAL_TYPE_CALLOUT; + + pc_info = pc_driver; + pc_info.name = "digiCtl"; + pc_info.major = DIGIINFOMAJOR; + pc_info.minor_start = 0; + pc_info.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL; + pc_info.subtype = SERIAL_TYPE_INFO; + + + /* --------------------------------------------------------------------- + loops_per_sec hasn't been set at this point :-(, so fake it out... + I set it, so that I can use the __delay() function. + ------------------------------------------------------------------------ */ + save_loops_per_sec = loops_per_sec; + loops_per_sec = 13L * 500000L; + + save_flags(flags); + cli(); + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + /* ------------------------------------------------------------------ + This is where the appropriate memory handlers for the hardware is + set. Everything at runtime blindly jumps through these vectors. + ---------------------------------------------------------------------- */ + + /* defined in epcaconfig.h */ + bd = &boards[crd]; + + switch (bd->type) + { /* Begin switch on bd->type {board type} */ + case PCXEM: + case EISAXEM: + bd->memwinon = pcxem_memwinon ; + bd->memwinoff = pcxem_memwinoff ; + bd->globalwinon = pcxem_globalwinon ; + bd->txwinon = pcxem_txwinon ; + bd->rxwinon = pcxem_rxwinon ; + bd->memoff = pcxem_memoff ; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCIXEM: + case PCIXRJ: + case PCIXR: + bd->memwinon = dummy_memwinon; + bd->memwinoff = dummy_memwinoff; + bd->globalwinon = dummy_globalwinon; + bd->txwinon = dummy_txwinon; + bd->rxwinon = dummy_rxwinon; + bd->memoff = dummy_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXE: + case PCXEVE: + + bd->memwinon = pcxe_memwinon; + bd->memwinoff = pcxe_memwinoff; + bd->globalwinon = pcxe_globalwinon; + bd->txwinon = pcxe_txwinon; + bd->rxwinon = pcxe_rxwinon; + bd->memoff = pcxe_memoff; + bd->assertgwinon = dummy_assertgwinon; + bd->assertmemoff = dummy_assertmemoff; + break; + + case PCXI: + case PC64XE: + + bd->memwinon = pcxi_memwinon; + bd->memwinoff = pcxi_memwinoff; + bd->globalwinon = pcxi_globalwinon; + bd->txwinon = pcxi_txwinon; + bd->rxwinon = pcxi_rxwinon; + bd->memoff = pcxi_memoff; + bd->assertgwinon = pcxi_assertgwinon; + bd->assertmemoff = pcxi_assertmemoff; + break; + + default: + break; + + } /* End switch on bd->type */ + + /* --------------------------------------------------------------- + Some cards need a memory segment to be defined for use in + transmit and receive windowing operations. These boards + are listed in the below switch. In the case of the XI the + amount of memory on the board is variable so the memory_seg + is also variable. This code determines what they segment + should be. + ----------------------------------------------------------------- */ + + switch (bd->type) + { /* Begin switch on bd->type {board type} */ + + case PCXE: + case PCXEVE: + case PC64XE: + bd->memory_seg = 0xf000; + break; + + case PCXI: + board_id = inb((int)bd->port); + if ((board_id & 0x1) == 0x1) + { /* Begin its an XI card */ + + /* Is it a 64K board */ + if ((board_id & 0x30) == 0) + bd->memory_seg = 0xf000; + + /* Is it a 128K board */ + if ((board_id & 0x30) == 0x10) + bd->memory_seg = 0xe000; + + /* Is is a 256K board */ + if ((board_id & 0x30) == 0x20) + bd->memory_seg = 0xc000; + + /* Is it a 512K board */ + if ((board_id & 0x30) == 0x30) + bd->memory_seg = 0x8000; + + } /* End it is an XI card */ + else + { + printk(KERN_ERR " - Board at 0x%x doesn't appear to be an XI\n",(int)bd->port); + } + break; + + } /* End switch on bd->type */ + + } /* End for each card */ + + if (tty_register_driver(&pc_driver)) + panic("Couldn't register Digi PC/ driver"); + + if (tty_register_driver(&pc_callout)) + panic("Couldn't register Digi PC/ callout"); + + if (tty_register_driver(&pc_info)) + panic("Couldn't register Digi PC/ info "); + + loops_per_sec = save_loops_per_sec; /* reset it to what it should be */ + + /* ------------------------------------------------------------------- + Start up the poller to check for events on all enabled boards + ---------------------------------------------------------------------- */ + + timer_table[DIGI_TIMER].fn = (void *)epcapoll; + timer_table[DIGI_TIMER].expires = 0; + + restore_flags(flags); + + timer_active |= 1 << DIGI_TIMER; + return 0; + +} /* End pc_init */ + +/* ------------------ Begin post_fep_init ---------------------- */ + +static void post_fep_init(unsigned int crd) +{ /* Begin post_fep_init */ + + int i; + unchar *memaddr; + volatile struct global_data *gd; + struct board_info *bd; + volatile struct board_chan *bc; + struct channel *ch; + int shrinkmem = 0, lowwater ; + + /* ------------------------------------------------------------- + This call is made by the user via. the ioctl call DIGI_INIT. + It is resposible for setting up all the card specific stuff. + ---------------------------------------------------------------- */ + bd = &boards[crd]; + + /* ----------------------------------------------------------------- + If this is a PCI board, get the port info. Remember PCI cards + do not have entries into the epcaconfig.h file, so we can't get + the number of ports from it. Unfortunetly, this means that anyone + doing a DIGI_GETINFO before the board has booted will get an invalid + number of ports returned (It should return 0). Calls to DIGI_GETINFO + after DIGI_INIT has been called will return the proper values. + ------------------------------------------------------------------- */ + + if (bd->type >= PCIXEM) /* If the board in question is PCI */ + { /* Begin get PCI number of ports */ + + /* -------------------------------------------------------------------- + Below we use XEMPORTS as a memory offset regardless of which PCI + card it is. This is because all of the supported PCI cards have + the same memory offset for the channel data. This will have to be + changed if we ever develop a PCI/XE card. NOTE : The FEP manual + states that the port offset is 0xC22 as opposed to 0xC02. This is + only true for PC/XE, and PC/XI cards; not for the XEM, or CX series. + On the PCI cards the number of ports is determined by reading a + ID PROM located in the box attached to the card. The card can then + determine the index the id to determine the number of ports available. + (FYI - The id should be located at 0x1ac (And may use up to 4 bytes + if the box in question is a XEM or CX)). + ------------------------------------------------------------------------ */ + + bd->numports = (unsigned short)*(unsigned char *)bus_to_virt((unsigned long) + (bd->re_map_membase + XEMPORTS)); + + + epcaassert(bd->numports <= 64,"PCI returned a invalid number of ports"); + nbdevs += (bd->numports); + + } /* End get PCI number of ports */ + + if (crd != 0) + card_ptr[crd] = card_ptr[crd-1] + boards[crd-1].numports; + else + card_ptr[crd] = &digi_channels[crd]; /* <- For card 0 only */ + + ch = card_ptr[crd]; + + + epcaassert(ch <= &digi_channels[nbdevs - 1], "ch out of range"); + + if (bd->membase < (unsigned char *)0x100000) + memaddr = (unchar *) bd->membase; + else /* Else get special mapped memory above RAM */ + memaddr = (unchar *)bd->re_map_membase; + + /* + The below command is necessary because newer kernels (2.1.x and + up) do not have a 1:1 virtual to physical mapping. The below + call adjust for that. + */ + + memaddr = (unsigned char *)bus_to_virt((unsigned long)memaddr); + + /* ----------------------------------------------------------------- + The below assignment will set bc to point at the BEGINING of + the cards channel structures. For 1 card there will be between + 8 and 64 of these structures. + -------------------------------------------------------------------- */ + + bc = (volatile struct board_chan *)((ulong)memaddr + CHANSTRUCT); + + /* ------------------------------------------------------------------- + The below assignment will set gd to point at the BEGINING of + global memory address 0xc00. The first data in that global + memory actually starts at address 0xc1a. The command in + pointer begins at 0xd10. + ---------------------------------------------------------------------- */ + + gd = (volatile struct global_data *)((ulong)memaddr + GLOBAL); + + /* -------------------------------------------------------------------- + XEPORTS (address 0xc22) points at the number of channels the + card supports. (For 64XE, XI, XEM, and XR use 0xc02) + ----------------------------------------------------------------------- */ + + if (((bd->type == PCXEVE) | (bd->type == PCXE)) && + (*(ushort *)((ulong)memaddr + XEPORTS) < 3)) + shrinkmem = 1; + if (bd->type < PCIXEM) + request_region((int)bd->port, 4, board_desc[bd->type]); + + memwinon(bd, 0); + + /* -------------------------------------------------------------------- + Remember ch is the main drivers channels structure, while bc is + the cards channel structure. + ------------------------------------------------------------------------ */ + + /* For every port on the card do ..... */ + + for (i = 0; i < bd->numports; i++, ch++, bc++) + { /* Begin for each port */ + + ch->brdchan = bc; + ch->mailbox = gd; + ch->tqueue.routine = do_softint; + ch->tqueue.data = ch; + ch->board = &boards[crd]; + + switch (bd->type) + { /* Begin switch bd->type */ + + /* ---------------------------------------------------------------- + Since some of the boards use different bitmaps for their + control signals we cannot hard code these values and retain + portability. We virtualize this data here. + ------------------------------------------------------------------- */ + case EISAXEM: + case PCXEM: + case PCIXEM: + case PCIXRJ: + case PCIXR: + ch->m_rts = 0x02 ; + ch->m_dcd = 0x80 ; + ch->m_dsr = 0x20 ; + ch->m_cts = 0x10 ; + ch->m_ri = 0x40 ; + ch->m_dtr = 0x01 ; + break; + + case PCXE: + case PCXEVE: + case PCXI: + case PC64XE: + ch->m_rts = 0x02 ; + ch->m_dcd = 0x08 ; + ch->m_dsr = 0x10 ; + ch->m_cts = 0x20 ; + ch->m_ri = 0x40 ; + ch->m_dtr = 0x80 ; + break; + + } /* End switch bd->type */ + + if (boards[crd].altpin) + { + ch->dsr = ch->m_dcd; + ch->dcd = ch->m_dsr; + ch->digiext.digi_flags |= DIGI_ALTPIN; + } + else + { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + ch->boardnum = crd; + ch->channelnum = i; + ch->magic = EPCA_MAGIC; + ch->tty = 0; + + if (shrinkmem) + { + fepcmd(ch, SETBUFFER, 32, 0, 0, 0); + shrinkmem = 0; + } + + switch (bd->type) + { /* Begin switch bd->type */ + + case PCIXEM: + case PCIXRJ: + case PCIXR: + /* Cover all the 2MEG cards */ + ch->txptr = memaddr + (((bc->tseg) << 4) & 0x1fffff); + ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x1fffff); + ch->txwin = FEPWIN | ((bc->tseg) >> 11); + ch->rxwin = FEPWIN | ((bc->rseg) >> 11); + break; + + case PCXEM: + case EISAXEM: + /* Cover all the 32K windowed cards */ + /* Mask equal to window size - 1 */ + ch->txptr = memaddr + (((bc->tseg) << 4) & 0x7fff); + ch->rxptr = memaddr + (((bc->rseg) << 4) & 0x7fff); + ch->txwin = FEPWIN | ((bc->tseg) >> 11); + ch->rxwin = FEPWIN | ((bc->rseg) >> 11); + break; + + case PCXEVE: + case PCXE: + ch->txptr = memaddr + (((bc->tseg - bd->memory_seg) << 4) & 0x1fff); + ch->txwin = FEPWIN | ((bc->tseg - bd->memory_seg) >> 9); + ch->rxptr = memaddr + (((bc->rseg - bd->memory_seg) << 4) & 0x1fff); + ch->rxwin = FEPWIN | ((bc->rseg - bd->memory_seg) >>9 ); + break; + + case PCXI: + case PC64XE: + ch->txptr = memaddr + ((bc->tseg - bd->memory_seg) << 4); + ch->rxptr = memaddr + ((bc->rseg - bd->memory_seg) << 4); + ch->txwin = ch->rxwin = 0; + break; + + } /* End switch bd->type */ + + ch->txbufhead = 0; + ch->txbufsize = bc->tmax + 1; + + ch->rxbufhead = 0; + ch->rxbufsize = bc->rmax + 1; + + lowwater = ch->txbufsize >= 2000 ? 1024 : (ch->txbufsize / 2); + + /* Set transmitter low water mark */ + fepcmd(ch, STXLWATER, lowwater, 0, 10, 0); + + /* Set receiver low water mark */ + + fepcmd(ch, SRXLWATER, (ch->rxbufsize / 4), 0, 10, 0); + + /* Set receiver high water mark */ + + fepcmd(ch, SRXHWATER, (3 * ch->rxbufsize / 4), 0, 10, 0); + + bc->edelay = 100; + bc->idata = 1; + + ch->startc = bc->startc; + ch->stopc = bc->stopc; + ch->startca = bc->startca; + ch->stopca = bc->stopca; + + ch->fepcflag = 0; + ch->fepiflag = 0; + ch->fepoflag = 0; + ch->fepstartc = 0; + ch->fepstopc = 0; + ch->fepstartca = 0; + ch->fepstopca = 0; + + ch->close_delay = 50; + ch->count = 0; + ch->blocked_open = 0; + ch->callout_termios = pc_callout.init_termios; + ch->normal_termios = pc_driver.init_termios; + ch->open_wait = 0; + ch->close_wait = 0; + ch->tmp_buf = kmalloc(ch->txbufsize,GFP_KERNEL); + if (!(ch->tmp_buf)) + { + printk(KERN_ERR "POST FEP INIT : kmalloc failed for port 0x%x\n",i); + + } + memset((void *)ch->tmp_buf,0,ch->txbufsize); + } /* End for each port */ + + printk(KERN_INFO + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); + sprintf(mesg, + "Digi PC/Xx Driver V%s: %s I/O = 0x%lx Mem = 0x%lx Ports = %d\n", + VERSION, board_desc[bd->type], (long)bd->port, (long)bd->membase, bd->numports); + console_print(mesg); + + memwinoff(bd, 0); + +} /* End post_fep_init */ + +/* --------------------- Begin epcapoll ------------------------ */ + +static void epcapoll(unsigned long ignored) +{ /* Begin epcapoll */ + + unsigned long flags; + int crd; + volatile unsigned int head, tail; + struct channel *ch; + struct board_info *bd; + + /* ------------------------------------------------------------------- + This routine is called upon every timer interrupt. Even though + the Digi series cards are capable of generating interupts this + method of non-looping polling is more efficient. This routine + checks for card generated events (Such as receive data, are transmit + buffer empty) and acts on those events. + ----------------------------------------------------------------------- */ + + save_flags(flags); + cli(); + + for (crd = 0; crd < num_cards; crd++) + { /* Begin for each card */ + + bd = &boards[crd]; + ch = card_ptr[crd]; + + if ((bd->status == DISABLED) || digi_poller_inhibited) + continue; /* Begin loop next interation */ + + /* ----------------------------------------------------------- + assertmemoff is not needed here; indeed it is an empty subroutine. + It is being kept because future boards may need this as well as + some legacy boards. + ---------------------------------------------------------------- */ + + assertmemoff(ch); + + globalwinon(ch); + + /* --------------------------------------------------------------- + In this case head and tail actually refer to the event queue not + the transmit or receive queue. + ------------------------------------------------------------------- */ + + head = ch->mailbox->ein; + tail = ch->mailbox->eout; + + /* If head isn't equal to tail we have an event */ + + if (head != tail) + doevent(crd); + + memoff(ch); + + } /* End for each card */ + + timer_table[DIGI_TIMER].fn = (void *)epcapoll; + timer_table[DIGI_TIMER].expires = jiffies + (HZ / 25); + timer_active |= 1 << DIGI_TIMER; + + restore_flags(flags); + +} /* End epcapoll */ + +/* --------------------- Begin doevent ------------------------ */ + +static void doevent(int crd) +{ /* Begin doevent */ + + volatile unchar *eventbuf; + struct channel *ch, *chan0; + static struct tty_struct *tty; + volatile struct board_info *bd; + volatile struct board_chan *bc; + register volatile unsigned int tail, head; + register int event, channel; + register int mstat, lstat; + + /* ------------------------------------------------------------------- + This subroutine is called by epcapoll when an event is detected + in the event queue. This routine responds to those events. + --------------------------------------------------------------------- */ + + bd = &boards[crd]; + + chan0 = card_ptr[crd]; + epcaassert(chan0 <= &digi_channels[nbdevs - 1], "ch out of range"); + + assertgwinon(chan0); + + while ((tail = chan0->mailbox->eout) != (head = chan0->mailbox->ein)) + { /* Begin while something in event queue */ + + assertgwinon(chan0); + + if (bd->membase < (unsigned char *)0x100000) + eventbuf = (volatile unchar *)bus_to_virt((ulong)(bd->membase + tail + ISTART)); + else + { + eventbuf = (volatile unchar *)bus_to_virt((ulong)(bd->re_map_membase + tail + ISTART)); + } + + /* Get the channel the event occured on */ + channel = eventbuf[0]; + + /* Get the actual event code that occured */ + event = eventbuf[1]; + + /* ---------------------------------------------------------------- + The two assignments below get the current modem status (mstat) + and the previous modem status (lstat). These are useful becuase + an event could signal a change in modem signals itself. + ------------------------------------------------------------------- */ + + mstat = eventbuf[2]; + lstat = eventbuf[3]; + + ch = chan0 + channel; + + if ((unsigned)channel >= bd->numports || !ch) + { + if (channel >= bd->numports) + ch = chan0; + bc = ch->brdchan; + goto next; + } + + if ((bc = ch->brdchan) == NULL) + goto next; + + if (event & DATA_IND) + { /* Begin DATA_IND */ + + receive_data(ch); + assertgwinon(ch); + + } /* End DATA_IND */ + else + if (event & MODEMCHG_IND) + { /* Begin MODEMCHG_IND */ + + /* A modem signal change has been indicated */ + + ch->imodem = mstat; + + if (ch->asyncflags & ASYNC_CHECK_CD) + { + if (mstat & ch->dcd) /* We are now receiving dcd */ + wake_up_interruptible(&ch->open_wait); + else + pc_sched_event(ch, EPCA_EVENT_HANGUP); /* No dcd; hangup */ + } + + } /* End MODEMCHG_IND */ + + tty = ch->tty; + if (tty) + { /* Begin if valid tty */ + + if (event & BREAK_IND) + { /* Begin if BREAK_IND */ + + /* A break has been indicated */ + + tty->flip.count++; + *tty->flip.flag_buf_ptr++ = TTY_BREAK; + + *tty->flip.char_buf_ptr++ = 0; + + tty_schedule_flip(tty); + + } /* End if BREAK_IND */ + else + if (event & LOWTX_IND) + { /* Begin LOWTX_IND */ + + if (ch->statusflags & LOWWAIT) + { /* Begin if LOWWAIT */ + + ch->statusflags &= ~LOWWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + + } /* End if LOWWAIT */ + + } /* End LOWTX_IND */ + else + if (event & EMPTYTX_IND) + { /* Begin EMPTYTX_IND */ + + /* This event is generated by setup_empty_event */ + + ch->statusflags &= ~TXBUSY; + if (ch->statusflags & EMPTYWAIT) + { /* Begin if EMPTYWAIT */ + + ch->statusflags &= ~EMPTYWAIT; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + } /* End if EMPTYWAIT */ + + } /* End EMPTYTX_IND */ + + } /* End if valid tty */ + + + next: + globalwinon(ch); + + if (!bc) + printk(KERN_ERR " - bc == NULL in doevent!\n"); + else + bc->idata = 1; + + chan0->mailbox->eout = (tail + 4) & (IMAX - ISTART - 4); + globalwinon(chan0); + + } /* End while something in event queue */ + +} /* End doevent */ + +/* --------------------- Begin fepcmd ------------------------ */ + +static void fepcmd(struct channel *ch, int cmd, int word_or_byte, + int byte2, int ncmds, int bytecmd) +{ /* Begin fepcmd */ + + unchar *memaddr; + unsigned int head, cmdTail, cmdStart, cmdMax; + long count; + int n; + + /* This is the routine in which commands may be passed to the card. */ + + if (ch->board->status == DISABLED) + { + return; + } + + assertgwinon(ch); + + /* Remember head (As well as max) is just an offset not a base addr */ + head = ch->mailbox->cin; + + /* cmdStart is a base address */ + cmdStart = ch->mailbox->cstart; + + /* ------------------------------------------------------------------ + We do the addition below because we do not want a max pointer + relative to cmdStart. We want a max pointer that points at the + physical end of the command queue. + -------------------------------------------------------------------- */ + + cmdMax = (cmdStart + 4 + (ch->mailbox->cmax)); + + if (ch->board->membase < (unsigned char *)0x100000) + memaddr = ch->board->membase; + else + memaddr = ch->board->re_map_membase; + + /* + The below command is necessary because newer kernels (2.1.x and + up) do not have a 1:1 virtual to physical mapping. The below + call adjust for that. + */ + + memaddr = (unsigned char *)bus_to_virt((unsigned long)memaddr); + + if (head >= (cmdMax - cmdStart) || (head & 03)) + { + printk(KERN_ERR "line %d: Out of range, cmd = %x, head = %x\n", __LINE__, + cmd, head); + printk(KERN_ERR "line %d: Out of range, cmdMax = %x, cmdStart = %x\n", __LINE__, + cmdMax, cmdStart); + return; + } + + if (bytecmd) + { + *(volatile unchar *)(memaddr + head + cmdStart + 0) = (unchar)cmd; + + *(volatile unchar *)(memaddr + head + cmdStart + 1) = (unchar)ch->channelnum; + /* Below word_or_byte is bits to set */ + *(volatile unchar *)(memaddr + head + cmdStart + 2) = (unchar)word_or_byte; + /* Below byte2 is bits to reset */ + *(volatile unchar *)(memaddr + head + cmdStart + 3) = (unchar)byte2; + + } + else + { + *(volatile unchar *)(memaddr + head + cmdStart + 0) = (unchar)cmd; + *(volatile unchar *)(memaddr + head + cmdStart + 1) = (unchar)ch->channelnum; + *(volatile ushort*)(memaddr + head + cmdStart + 2) = (ushort)word_or_byte; + } + + head = (head + 4) & (cmdMax - cmdStart - 4); + ch->mailbox->cin = head; + + count = FEPTIMEOUT; + + for (;;) + { /* Begin forever loop */ + + count--; + if (count == 0) + { + printk(KERN_ERR " - Fep not responding in fepcmd()\n"); + return; + } + + head = ch->mailbox->cin; + cmdTail = ch->mailbox->cout; + + n = (head - cmdTail) & (cmdMax - cmdStart - 4); + + /* ---------------------------------------------------------- + Basically this will break when the FEP acknowledges the + command by incrementing cmdTail (Making it equal to head). + ------------------------------------------------------------- */ + + if (n <= ncmds * (sizeof(short) * 4)) + break; /* Well nearly forever :-) */ + + } /* End forever loop */ + +} /* End fepcmd */ + +/* --------------------------------------------------------------------- + Digi products use fields in their channels structures that are very + similar to the c_cflag and c_iflag fields typically found in UNIX + termios structures. The below three routines allow mappings + between these hardware "flags" and their respective Linux flags. +------------------------------------------------------------------------- */ + +/* --------------------- Begin termios2digi_h -------------------- */ + +static unsigned termios2digi_h(struct channel *ch, unsigned cflag) +{ /* Begin termios2digi_h */ + + unsigned res = 0; + + if (cflag & CRTSCTS) + { + ch->digiext.digi_flags |= (RTSPACE | CTSPACE); + res |= ((ch->m_cts) | (ch->m_rts)); + } + + if (ch->digiext.digi_flags & RTSPACE) + res |= ch->m_rts; + + if (ch->digiext.digi_flags & DTRPACE) + res |= ch->m_dtr; + + if (ch->digiext.digi_flags & CTSPACE) + res |= ch->m_cts; + + if (ch->digiext.digi_flags & DSRPACE) + res |= ch->dsr; + + if (ch->digiext.digi_flags & DCDPACE) + res |= ch->dcd; + + if (res & (ch->m_rts)) + ch->digiext.digi_flags |= RTSPACE; + + if (res & (ch->m_cts)) + ch->digiext.digi_flags |= CTSPACE; + + return res; + +} /* End termios2digi_h */ + +/* --------------------- Begin termios2digi_i -------------------- */ +static unsigned termios2digi_i(struct channel *ch, unsigned iflag) +{ /* Begin termios2digi_i */ + + unsigned res = iflag & (IGNBRK | BRKINT | IGNPAR | PARMRK | + INPCK | ISTRIP|IXON|IXANY|IXOFF); + + if (ch->digiext.digi_flags & DIGI_AIXON) + res |= IAIXON; + return res; + +} /* End termios2digi_i */ + +/* --------------------- Begin termios2digi_c -------------------- */ + +static unsigned termios2digi_c(struct channel *ch, unsigned cflag) +{ /* Begin termios2digi_c */ + + unsigned res = 0; + +#ifdef SPEED_HACK + /* CL: HACK to force 115200 at 38400 and 57600 at 19200 Baud */ + if ((cflag & CBAUD)== B38400) cflag=cflag - B38400 + B115200; + if ((cflag & CBAUD)== B19200) cflag=cflag - B19200 + B57600; +#endif /* SPEED_HACK */ + + if (cflag & CBAUDEX) + { /* Begin detected CBAUDEX */ + + ch->digiext.digi_flags |= DIGI_FAST; + + /* ------------------------------------------------------------- + HUPCL bit is used by FEP to indicate fast baud + table is to be used. + ----------------------------------------------------------------- */ + + res |= FEP_HUPCL; + + } /* End detected CBAUDEX */ + else ch->digiext.digi_flags &= ~DIGI_FAST; + + /* ------------------------------------------------------------------- + CBAUD has bit position 0x1000 set these days to indicate Linux + baud rate remap. Digi hardware can't handle the bit assignment. + (We use a different bit assignment for high speed.). Clear this + bit out. + ---------------------------------------------------------------------- */ + res |= cflag & ((CBAUD ^ CBAUDEX) | PARODD | PARENB | CSTOPB | CSIZE); + + /* ------------------------------------------------------------- + This gets a little confusing. The Digi cards have their own + representation of c_cflags controling baud rate. For the most + part this is identical to the Linux implementation. However; + Digi supports one rate (76800) that Linux doesn't. This means + that the c_cflag entry that would normally mean 76800 for Digi + actually means 115200 under Linux. Without the below mapping, + a stty 115200 would only drive the board at 76800. Since + the rate 230400 is also found after 76800, the same problem afflicts + us when we choose a rate of 230400. Without the below modificiation + stty 230400 would actually give us 115200. + + There are two additional differences. The Linux value for CLOCAL + (0x800; 0004000) has no meaning to the Digi hardware. Also in + later releases of Linux; the CBAUD define has CBAUDEX (0x1000; + 0010000) ored into it (CBAUD = 0x100f as opposed to 0xf). CBAUDEX + should be checked for a screened out prior to termios2digi_c + returning. Since CLOCAL isn't used by the board this can be + ignored as long as the returned value is used only by Digi hardware. + ----------------------------------------------------------------- */ + + if (cflag & CBAUDEX) + { + /* ------------------------------------------------------------- + The below code is trying to guarantee that only baud rates + 115200 and 230400 are remapped. We use exclusive or because + the various baud rates share common bit positions and therefore + can't be tested for easily. + ----------------------------------------------------------------- */ + + + if ((!((cflag & 0x7) ^ (B115200 & ~CBAUDEX))) || + (!((cflag & 0x7) ^ (B230400 & ~CBAUDEX)))) + { + res += 1; + } + } + + return res; + +} /* End termios2digi_c */ + +/* --------------------- Begin epcaparam ----------------------- */ + +static void epcaparam(struct tty_struct *tty, struct channel *ch) +{ /* Begin epcaparam */ + + unsigned int cmdHead; + struct termios *ts; + volatile struct board_chan *bc; + unsigned mval, hflow, cflag, iflag; + + bc = ch->brdchan; + epcaassert(bc !=0, "bc out of range"); + + assertgwinon(ch); + + ts = tty->termios; + + if ((ts->c_cflag & CBAUD) == 0) + { /* Begin CBAUD detected */ + + cmdHead = bc->rin; + bc->rout = cmdHead; + cmdHead = bc->tin; + + /* Changing baud in mid-stream transmission can be wonderful */ + /* --------------------------------------------------------------- + Flush current transmit buffer by setting cmdTail pointer (tout) + to cmdHead pointer (tin). Hopefully the transmit buffer is empty. + ----------------------------------------------------------------- */ + + fepcmd(ch, STOUT, (unsigned) cmdHead, 0, 0, 0); + mval = 0; + + } /* End CBAUD detected */ + else + { /* Begin CBAUD not detected */ + + /* ------------------------------------------------------------------- + c_cflags have changed but that change had nothing to do with BAUD. + Propagate the change to the card. + ---------------------------------------------------------------------- */ + + cflag = termios2digi_c(ch, ts->c_cflag); + + if (cflag != ch->fepcflag) + { + ch->fepcflag = cflag; + /* Set baud rate, char size, stop bits, parity */ + fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); + } + + + /* ---------------------------------------------------------------- + If the user has not forced CLOCAL and if the device is not a + CALLOUT device (Which is always CLOCAL) we set flags such that + the driver will wait on carrier detect. + ------------------------------------------------------------------- */ + + if ((ts->c_cflag & CLOCAL) || (tty->driver.subtype == SERIAL_TYPE_CALLOUT)) + { /* Begin it is a cud device or a ttyD device with CLOCAL on */ + ch->asyncflags &= ~ASYNC_CHECK_CD; + } /* End it is a cud device or a ttyD device with CLOCAL on */ + else + { /* Begin it is a ttyD device */ + ch->asyncflags |= ASYNC_CHECK_CD; + } /* End it is a ttyD device */ + + mval = ch->m_dtr | ch->m_rts; + + } /* End CBAUD not detected */ + + iflag = termios2digi_i(ch, ts->c_iflag); + + /* Check input mode flags */ + + if (iflag != ch->fepiflag) + { + ch->fepiflag = iflag; + + /* --------------------------------------------------------------- + Command sets channels iflag structure on the board. Such things + as input soft flow control, handeling of parity errors, and + break handeling are all set here. + ------------------------------------------------------------------- */ + + /* break handeling, parity handeling, input stripping, flow control chars */ + fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); + } + + /* --------------------------------------------------------------- + Set the board mint value for this channel. This will cause hardware + events to be generated each time the DCD signal (Described in mint) + changes. + ------------------------------------------------------------------- */ + bc->mint = ch->dcd; + + if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) + if (ch->digiext.digi_flags & DIGI_FORCEDCD) + bc->mint = 0; + + ch->imodem = bc->mstat; + + hflow = termios2digi_h(ch, ts->c_cflag); + + if (hflow != ch->hflow) + { + ch->hflow = hflow; + + /* -------------------------------------------------------------- + Hard flow control has been selected but the board is not + using it. Activate hard flow control now. + ----------------------------------------------------------------- */ + + fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); + } + + + mval ^= ch->modemfake & (mval ^ ch->modem); + + if (ch->omodem ^ mval) + { + ch->omodem = mval; + + /* -------------------------------------------------------------- + The below command sets the DTR and RTS mstat structure. If + hard flow control is NOT active these changes will drive the + output of the actual DTR and RTS lines. If hard flow control + is active, the changes will be saved in the mstat structure and + only asserted when hard flow control is turned off. + ----------------------------------------------------------------- */ + + /* First reset DTR & RTS; then set them */ + fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); + fepcmd(ch, SETMODEM, mval, 0, 0, 1); + + } + + if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) + { + ch->fepstartc = ch->startc; + ch->fepstopc = ch->stopc; + + /* ------------------------------------------------------------ + The XON / XOFF characters have changed; propogate these + changes to the card. + --------------------------------------------------------------- */ + + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + + if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) + { + ch->fepstartca = ch->startca; + ch->fepstopca = ch->stopca; + + /* --------------------------------------------------------------- + Similar to the above, this time the auxilarly XON / XOFF + characters have changed; propogate these changes to the card. + ------------------------------------------------------------------ */ + + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } + +} /* End epcaparam */ + +/* --------------------- Begin receive_data ----------------------- */ + +static void receive_data(struct channel *ch) +{ /* Begin receive_data */ + + unchar *rptr; + struct termios *ts = 0; + struct tty_struct *tty; + volatile struct board_chan *bc; + register int dataToRead, wrapgap, bytesAvailable; + register unsigned int tail, head; + unsigned int wrapmask; + int rc; + + + /* --------------------------------------------------------------- + This routine is called by doint when a receive data event + has taken place. + ------------------------------------------------------------------- */ + + globalwinon(ch); + + if (ch->statusflags & RXSTOPPED) + return; + + tty = ch->tty; + if (tty) + ts = tty->termios; + + bc = ch->brdchan; + + if (!bc) + { + printk(KERN_ERR " - bc is NULL in receive_data!\n"); + return; + } + + wrapmask = ch->rxbufsize - 1; + + /* --------------------------------------------------------------------- + Get the head and tail pointers to the receiver queue. Wrap the + head pointer if it has reached the end of the buffer. + ------------------------------------------------------------------------ */ + + head = bc->rin; + head &= wrapmask; + tail = bc->rout & wrapmask; + + bytesAvailable = (head - tail) & wrapmask; + + if (bytesAvailable == 0) + return; + + /* ------------------------------------------------------------------ + If CREAD bit is off or device not open, set TX tail to head + --------------------------------------------------------------------- */ + + if (!tty || !ts || !(ts->c_cflag & CREAD)) + { + bc->rout = head; + return; + } + + if (tty->flip.count == TTY_FLIPBUF_SIZE) + return; + + if (bc->orun) + { + bc->orun = 0; + printk(KERN_WARNING "overrun! DigiBoard device minor = %d\n",MINOR(tty->device)); + } + + rxwinon(ch); + rptr = tty->flip.char_buf_ptr; + rc = tty->flip.count; + + while (bytesAvailable > 0) + { /* Begin while there is data on the card */ + + wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; + + /* --------------------------------------------------------------- + Even if head has wrapped around only report the amount of + data to be equal to the size - tail. Remember memcpy can't + automaticly wrap around the receive buffer. + ----------------------------------------------------------------- */ + + dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable; + + /* -------------------------------------------------------------- + Make sure we don't overflow the buffer + ----------------------------------------------------------------- */ + + if ((rc + dataToRead) > TTY_FLIPBUF_SIZE) + dataToRead = TTY_FLIPBUF_SIZE - rc; + + if (dataToRead == 0) + break; + + /* --------------------------------------------------------------- + Move data read from our card into the line disciplines buffer + for translation if necessary. + ------------------------------------------------------------------ */ + + if ((memcpy(rptr, ch->rxptr + tail, dataToRead)) != rptr) + printk(KERN_ERR " - receive_data : memcpy failed\n"); + + rc += dataToRead; + rptr += dataToRead; + tail = (tail + dataToRead) & wrapmask; + bytesAvailable -= dataToRead; + + } /* End while there is data on the card */ + + + tty->flip.count = rc; + tty->flip.char_buf_ptr = rptr; + globalwinon(ch); + bc->rout = tail; + + /* Must be called with global data */ + tty_schedule_flip(ch->tty); + return; + +} /* End receive_data */ + +/* --------------------- Begin pc_ioctl ----------------------- */ + +static int pc_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ /* Begin pc_ioctl */ + + digiflow_t dflow; + int retval, error; + unsigned long flags; + unsigned int mflag, mstat; + unsigned char startc, stopc; + volatile struct board_chan *bc; + struct channel *ch = (struct channel *) tty->driver_data; + + /* The control device has it's own set of commands */ + if (tty->driver.subtype == SERIAL_TYPE_INFO) + { /* Begin if subtype is the control device */ + + switch (cmd) + { /* Begin switch cmd */ + + case DIGI_GETINFO: + { /* Begin case DIGI_GETINFO */ + + struct digi_info di ; + int brd; + + getUser(brd, (unsigned int *)arg); + + if ((error = verify_area(VERIFY_WRITE, (char*)arg, sizeof(di)))) + { + printk(KERN_ERR "DIGI_GETINFO : verify area size 0x%x failed\n",sizeof(di)); + return(error); + } + + if ((brd < 0) || (brd >= num_cards) || (num_cards == 0)) + return (-ENODEV); + + memset(&di, 0, sizeof(di)); + + di.board = brd ; + di.status = boards[brd].status; + di.type = boards[brd].type ; + di.numports = boards[brd].numports ; + di.port = boards[brd].port ; + di.membase = boards[brd].membase ; + + copy_to_user((char *)arg, &di, sizeof (di)); + break; + + } /* End case DIGI_GETINFO */ + + case DIGI_POLLER: + { /* Begin case DIGI_POLLER */ + + int brd = arg & 0xff000000 >> 16 ; + unsigned char state = arg & 0xff ; + + if ((brd < 0) || (brd >= num_cards)) + { + printk(KERN_ERR " - DIGI POLLER : brd not valid!\n"); + return (-ENODEV); + } + + digi_poller_inhibited = state ; + break ; + + } /* End case DIGI_POLLER */ + + case DIGI_INIT: + { /* Begin case DIGI_INIT */ + + /* ------------------------------------------------------------ + This call is made by the apps to complete the initilization + of the board(s). This routine is responsible for setting + the card to its initial state and setting the drivers control + fields to the sutianle settings for the card in question. + ---------------------------------------------------------------- */ + + int crd ; + for (crd = 0; crd < num_cards; crd++) + post_fep_init (crd); + + break ; + + } /* End case DIGI_INIT */ + + + default: + return -ENOIOCTLCMD; + + } /* End switch cmd */ + return (0) ; + + } /* End if subtype is the control device */ + + if (ch) + bc = ch->brdchan; + else + { + printk(KERN_ERR " - ch is NULL in pc_ioctl!\n"); + return(-EINVAL); + } + + save_flags(flags); + + /* ------------------------------------------------------------------- + For POSIX compliance we need to add more ioctls. See tty_ioctl.c + in /usr/src/linux/drivers/char for a good example. In particular + think about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS. + ---------------------------------------------------------------------- */ + + switch (cmd) + { /* Begin switch cmd */ + + case TCGETS: + retval = verify_area(VERIFY_WRITE, (void *)arg, + sizeof(struct termios)); + + if (retval) + return(retval); + + copy_to_user((struct termios *)arg, + tty->termios, sizeof(struct termios)); + return(0); + + case TCGETA: + return get_termio(tty, (struct termio *)arg); + + case TCSBRK: /* SVID version: non-zero arg --> no break */ + + retval = tty_check_change(tty); + if (retval) + return retval; + + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + if (!arg) + digi_send_break(ch, HZ/4); /* 1/4 second */ + return 0; + + case TCSBRKP: /* support for POSIX tcsendbreak() */ + + retval = tty_check_change(tty); + if (retval) + return retval; + + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); + return 0; + + case TIOCGSOFTCAR: + + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if (error) + return error; + + putUser(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + + case TIOCSSOFTCAR: + /*RONNIE PUT VERIFY_READ (See above) check here */ + { + unsigned int value; + + getUser(value, (unsigned int *)arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (value ? CLOCAL : 0)); + return 0; + } + + case TIOCMODG: + case TIOCMGET: + + mflag = 0; + + cli(); + globalwinon(ch); + mstat = bc->mstat; + memoff(ch); + restore_flags(flags); + + if (mstat & ch->m_dtr) + mflag |= TIOCM_DTR; + + if (mstat & ch->m_rts) + mflag |= TIOCM_RTS; + + if (mstat & ch->m_cts) + mflag |= TIOCM_CTS; + + if (mstat & ch->dsr) + mflag |= TIOCM_DSR; + + if (mstat & ch->m_ri) + mflag |= TIOCM_RI; + + if (mstat & ch->dcd) + mflag |= TIOCM_CD; + + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + + if (error) + return error; + + putUser(mflag, (unsigned long *) arg); + + break; + + case TIOCMBIS: + case TIOCMBIC: + case TIOCMODS: + case TIOCMSET: + + getUser(mstat, (unsigned int *)arg); + + mflag = 0; + if (mstat & TIOCM_DTR) + mflag |= ch->m_dtr; + + if (mstat & TIOCM_RTS) + mflag |= ch->m_rts; + + switch (cmd) + { /* Begin switch cmd */ + + case TIOCMODS: + case TIOCMSET: + ch->modemfake = ch->m_dtr|ch->m_rts; + ch->modem = mflag; + break; + + case TIOCMBIS: + ch->modemfake |= mflag; + ch->modem |= mflag; + break; + + case TIOCMBIC: + ch->modemfake |= mflag; + ch->modem &= ~mflag; + break; + + } /* End switch cmd */ + + cli(); + globalwinon(ch); + + /* -------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + ------------------------------------------------------------------ */ + + epcaparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case TIOCSDTR: + ch->omodem |= ch->m_dtr; + cli(); + globalwinon(ch); + fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case TIOCCDTR: + ch->omodem &= ~ch->m_dtr; + cli(); + globalwinon(ch); + fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETA: + if ((error= + verify_area(VERIFY_WRITE, (char*)arg, sizeof(digi_t)))) + { + printk(KERN_ERR " - Digi GETA failed\n"); + return(error); + } + + copy_to_user((char*)arg, &ch->digiext, sizeof(digi_t)); + break; + + case DIGI_SETAW: + case DIGI_SETAF: + if ((cmd) == (DIGI_SETAW)) + { + /* Setup an event to indicate when the transmit buffer empties */ + + setup_empty_event(tty,ch); + tty_wait_until_sent(tty, 0); + } + else + { + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + } + + /* Fall Thru */ + + case DIGI_SETA: + if ((error = + verify_area(VERIFY_READ, (char*)arg,sizeof(digi_t)))) + return(error); + + copy_from_user(&ch->digiext, (char*)arg, sizeof(digi_t)); + + if (ch->digiext.digi_flags & DIGI_ALTPIN) + { + ch->dcd = ch->m_dsr; + ch->dsr = ch->m_dcd; + } + else + { + ch->dcd = ch->m_dcd; + ch->dsr = ch->m_dsr; + } + + cli(); + globalwinon(ch); + + /* ----------------------------------------------------------------- + The below routine generally sets up parity, baud, flow control + issues, etc.... It effect both control flags and input flags. + ------------------------------------------------------------------- */ + + epcaparam(tty,ch); + memoff(ch); + restore_flags(flags); + break; + + case DIGI_GETFLOW: + case DIGI_GETAFLOW: + cli(); + globalwinon(ch); + if ((cmd) == (DIGI_GETFLOW)) + { + dflow.startc = bc->startc; + dflow.stopc = bc->stopc; + } + else + { + dflow.startc = bc->startca; + dflow.stopc = bc->stopca; + } + memoff(ch); + restore_flags(flags); + + if ((error = verify_area(VERIFY_WRITE, (char*)arg,sizeof(dflow)))) + return(error); + + copy_to_user((char*)arg, &dflow, sizeof(dflow)); + break; + + case DIGI_SETAFLOW: + case DIGI_SETFLOW: + if ((cmd) == (DIGI_SETFLOW)) + { + startc = ch->startc; + stopc = ch->stopc; + } + else + { + startc = ch->startca; + stopc = ch->stopca; + } + + if ((error = verify_area(VERIFY_READ, (char*)arg,sizeof(dflow)))) + return(error); + + copy_from_user(&dflow, (char*)arg, sizeof(dflow)); + + if (dflow.startc != startc || dflow.stopc != stopc) + { /* Begin if setflow toggled */ + cli(); + globalwinon(ch); + + if ((cmd) == (DIGI_SETFLOW)) + { + ch->fepstartc = ch->startc = dflow.startc; + ch->fepstopc = ch->stopc = dflow.stopc; + fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); + } + else + { + ch->fepstartca = ch->startca = dflow.startc; + ch->fepstopca = ch->stopca = dflow.stopc; + fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); + } + + if (ch->statusflags & TXSTOPPED) + pc_start(tty); + + memoff(ch); + restore_flags(flags); + + } /* End if setflow toggled */ + break; + + default: + return -ENOIOCTLCMD; + + } /* End switch cmd */ + + return 0; + +} /* End pc_ioctl */ + +/* --------------------- Begin pc_set_termios ----------------------- */ + +static void pc_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ /* Begin pc_set_termios */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + save_flags(flags); + cli(); + globalwinon(ch); + epcaparam(tty, ch); + memoff(ch); + + if ((old_termios->c_cflag & CRTSCTS) && + ((tty->termios->c_cflag & CRTSCTS) == 0)) + tty->hw_stopped = 0; + + if (!(old_termios->c_cflag & CLOCAL) && + (tty->termios->c_cflag & CLOCAL)) + wake_up_interruptible(&ch->open_wait); + + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_set_termios */ + +/* --------------------- Begin do_softint ----------------------- */ + +static void do_softint(void *private_) +{ /* Begin do_softint */ + + struct channel *ch = (struct channel *) private_; + + + /* Called in response to a modem change event */ + + if (ch && ch->magic == EPCA_MAGIC) + { /* Begin EPCA_MAGIC */ + + struct tty_struct *tty = ch->tty; + + if (tty && tty->driver_data) + { + if (clear_bit(EPCA_EVENT_HANGUP, &ch->event)) + { /* Begin if clear_bit */ + + tty_hangup(tty); + wake_up_interruptible(&ch->open_wait); + ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CALLOUT_ACTIVE); + + } /* End if clear_bit */ + } + + } /* End EPCA_MAGIC */ + +} /* End do_softint */ + +/* ------------------------------------------------------------ + pc_stop and pc_start provide software flow control to the + routine and the pc_ioctl routine. +---------------------------------------------------------------- */ + +/* --------------------- Begin pc_stop ----------------------- */ + +static void pc_stop(struct tty_struct *tty) +{ /* Begin pc_stop */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if valid channel */ + + save_flags(flags); + cli(); + + if ((ch->statusflags & TXSTOPPED) == 0) + { /* Begin if transmit stop requested */ + + globalwinon(ch); + + /* STOP transmitting now !! */ + + fepcmd(ch, PAUSETX, 0, 0, 0, 0); + + ch->statusflags |= TXSTOPPED; + memoff(ch); + + } /* End if transmit stop requested */ + + restore_flags(flags); + + } /* End if valid channel */ + +} /* End pc_stop */ + +/* --------------------- Begin pc_start ----------------------- */ + +static void pc_start(struct tty_struct *tty) +{ /* Begin pc_start */ + + struct channel *ch; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + unsigned long flags; + + save_flags(flags); + cli(); + + /* Just in case output was resumed because of a change in Digi-flow */ + if (ch->statusflags & TXSTOPPED) + { /* Begin transmit resume requested */ + + volatile struct board_chan *bc; + + globalwinon(ch); + bc = ch->brdchan; + if (ch->statusflags & LOWWAIT) + bc->ilow = 1; + + /* Okay, you can start transmitting again... */ + + fepcmd(ch, RESUMETX, 0, 0, 0, 0); + + ch->statusflags &= ~TXSTOPPED; + memoff(ch); + + } /* End transmit resume requested */ + + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_start */ + +/* ------------------------------------------------------------------ + The below routines pc_throttle and pc_unthrottle are used + to slow (And resume) the receipt of data into the kernels + receive buffers. The exact occurence of this depends on the + size of the kernels receive buffer and what the 'watermarks' + are set to for that buffer. See the n_ttys.c file for more + details. +______________________________________________________________________ */ +/* --------------------- Begin throttle ----------------------- */ + +static void pc_throttle(struct tty_struct * tty) +{ /* Begin pc_throttle */ + + struct channel *ch; + unsigned long flags; + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + + save_flags(flags); + cli(); + + if ((ch->statusflags & RXSTOPPED) == 0) + { + globalwinon(ch); + fepcmd(ch, PAUSERX, 0, 0, 0, 0); + + ch->statusflags |= RXSTOPPED; + memoff(ch); + } + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_throttle */ + +/* --------------------- Begin unthrottle ----------------------- */ + +static void pc_unthrottle(struct tty_struct *tty) +{ /* Begin pc_unthrottle */ + + struct channel *ch; + unsigned long flags; + volatile struct board_chan *bc; + + + /* --------------------------------------------------------- + verifyChannel returns the channel from the tty struct + if it is valid. This serves as a sanity check. + ------------------------------------------------------------- */ + + if ((ch = verifyChannel(tty)) != NULL) + { /* Begin if channel valid */ + + + /* Just in case output was resumed because of a change in Digi-flow */ + save_flags(flags); + cli(); + + if (ch->statusflags & RXSTOPPED) + { + + globalwinon(ch); + bc = ch->brdchan; + fepcmd(ch, RESUMERX, 0, 0, 0, 0); + + ch->statusflags &= ~RXSTOPPED; + memoff(ch); + } + restore_flags(flags); + + } /* End if channel valid */ + +} /* End pc_unthrottle */ + +/* --------------------- Begin digi_send_break ----------------------- */ + +void digi_send_break(struct channel *ch, int msec) +{ /* Begin digi_send_break */ + + unsigned long flags; + + save_flags(flags); + cli(); + globalwinon(ch); + + /* -------------------------------------------------------------------- + Maybe I should send an infinite break here, schedule() for + msec amount of time, and then stop the break. This way, + the user can't screw up the FEP by causing digi_send_break() + to be called (i.e. via an ioctl()) more than once in msec amount + of time. Try this for now... + ------------------------------------------------------------------------ */ + + fepcmd(ch, SENDBREAK, msec, 0, 10, 0); + memoff(ch); + + restore_flags(flags); + +} /* End digi_send_break */ + +/* --------------------- Begin setup_empty_event ----------------------- */ + +static void setup_empty_event(struct tty_struct *tty, struct channel *ch) +{ /* Begin setup_empty_event */ + + volatile struct board_chan *bc = ch->brdchan; + unsigned long int flags; + + save_flags(flags); + cli(); + globalwinon(ch); + ch->statusflags |= EMPTYWAIT; + + /* ------------------------------------------------------------------ + When set the iempty flag request a event to be generated when the + transmit buffer is empty (If there is no BREAK in progress). + --------------------------------------------------------------------- */ + + bc->iempty = 1; + memoff(ch); + restore_flags(flags); + +} /* End setup_empty_event */ + +/* --------------------- Begin get_termio ----------------------- */ + +static int get_termio(struct tty_struct * tty, struct termio * termio) +{ /* Begin get_termio */ + int error; + + error = verify_area(VERIFY_WRITE, termio, sizeof (struct termio)); + if (error) + return error; + + kernel_termios_to_user_termio(termio, tty->termios); + + return 0; +} /* End get_termio */ +/* ---------------------- Begin epca_setup -------------------------- */ +void epca_setup(char *str, int *ints) +{ /* Begin epca_setup */ + + struct board_info board; + int index, loop, last; + char *temp, *t2; + unsigned len; + + /* ---------------------------------------------------------------------- + If this routine looks a little strange it is because it is only called + if a LILO append command is given to boot the kernel with parameters. + In this way, we can provide the user a method of changing his board + configuration without rebuilding the kernel. + ----------------------------------------------------------------------- */ + if (!liloconfig) + liloconfig = 1; + + memset(&board, 0, sizeof(board)); + + /* Assume the data is int first, later we can change it */ + /* I think that array position 0 of ints holds the number of args */ + for (last = 0, index = 1; index <= ints[0]; index++) + switch(index) + { /* Begin parse switch */ + + case 1: + board.status = ints[index]; + + /* --------------------------------------------------------- + We check for 2 (As opposed to 1; because 2 is a flag + instructing the driver to ignore epcaconfig.) For this + reason we check for 2. + ------------------------------------------------------------ */ + if (board.status == 2) + { /* Begin ignore epcaconfig as well as lilo cmd line */ + nbdevs = 0; + num_cards = 0; + return; + } /* End ignore epcaconfig as well as lilo cmd line */ + + if (board.status > 2) + { + printk(KERN_ERR " - epca_setup: Invalid board status 0x%x\n", board.status); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + + case 2: + board.type = ints[index]; + if (board.type >= PCIXEM) + { + printk(KERN_ERR " - epca_setup: Invalid board type 0x%x\n", board.type); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + + case 3: + board.altpin = ints[index]; + if (board.altpin > 1) + { + printk(KERN_ERR " - epca_setup: Invalid board altpin 0x%x\n", board.altpin); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + board.numports = ints[index]; + if ((board.numports < 2) || (board.numports > 256)) + { + printk(KERN_ERR " - epca_setup: Invalid board numports 0x%x\n", board.numports); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + nbdevs += board.numports; + last = index; + break; + + case 5: + board.port = (unsigned char *)ints[index]; + if (board.port <= 0) + { + printk(KERN_ERR " - epca_setup: Invalid io port 0x%x\n", (unsigned int)board.port); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + last = index; + break; + + case 6: + board.membase = (unsigned char *)ints[index]; + if (board.membase <= 0) + { + printk(KERN_ERR " - epca_setup: Invalid memory base 0x%x\n",(unsigned int)board.membase); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + last = index; + break; + + default: + printk(KERN_ERR " - epca_setup: Too many integer parms\n"); + return; + + } /* End parse switch */ + + while (str && *str) + { /* Begin while there is a string arg */ + + /* find the next comma or terminator */ + temp = str; + + /* While string is not null, and a comma hasn't been found */ + while (*temp && (*temp != ',')) + temp++; + + if (!*temp) + temp = NULL; + else + *temp++ = 0; + + /* Set index to the number of args + 1 */ + index = last + 1; + + switch(index) + { + case 1: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.status = 0; + else + if (strncmp("Enable", str, len) == 0) + board.status = 1; + else + { + printk(KERN_ERR " - epca_setup: Invalid status %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_STATUS; + return; + } + last = index; + break; + + case 2: + + for(loop = 0; loop < EPCA_NUM_TYPES; loop++) + if (strcmp(board_desc[loop], str) == 0) + break; + + + /* --------------------------------------------------------------- + If the index incremented above refers to a legitamate board + type set it here. + ------------------------------------------------------------------*/ + + if (index < EPCA_NUM_TYPES) + board.type = loop; + else + { + printk(KERN_ERR " - epca_setup: Invalid board type: %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_BOARD_TYPE; + return; + } + last = index; + break; + + case 3: + len = strlen(str); + if (strncmp("Disable", str, len) == 0) + board.altpin = 0; + else + if (strncmp("Enable", str, len) == 0) + board.altpin = 1; + else + { + printk(KERN_ERR " - epca_setup: Invalid altpin %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_ALTPIN; + return; + } + last = index; + break; + + case 4: + t2 = str; + while (isdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR " - epca_setup: Invalid port count %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_NUM_PORTS; + return; + } + + /* ------------------------------------------------------------ + There is not a man page for simple_strtoul but the code can be + found in vsprintf.c. The first argument is the string to + translate (To an unsigned long obviously), the second argument + can be the address of any character variable or a NULL. If a + variable is given, the end pointer of the string will be stored + in that variable; if a NULL is given the the end pointer will + not be returned. The last argument is the base to use. If + a 0 is indicated, the routine will attempt to determine the + proper base by looking at the values prefix (A '0' for octal, + a 'x' for hex, etc ... If a value is given it will use that + value as the base. + ---------------------------------------------------------------- */ + board.numports = simple_strtoul(str, NULL, 0); + nbdevs += board.numports; + last = index; + break; + + case 5: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR " - epca_setup: Invalid i/o address %s\n", str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_PORT_BASE; + return; + } + + board.port = (unsigned char *)simple_strtoul(str, NULL, 16); + last = index; + break; + + case 6: + t2 = str; + while (isxdigit(*t2)) + t2++; + + if (*t2) + { + printk(KERN_ERR " - epca_setup: Invalid memory base %s\n",str); + invalid_lilo_config = 1; + setup_error_code |= INVALID_MEM_BASE; + return; + } + + board.membase = (unsigned char *)simple_strtoul(str, NULL, 16); + last = index; + break; + + default: + printk(KERN_ERR "PC/Xx: Too many string parms\n"); + return; + } + str = temp; + + } /* End while there is a string arg */ + + + if (last < 6) + { + printk(KERN_ERR "PC/Xx: Insufficient parms specified\n"); + return; + } + + /* I should REALLY validate the stuff here */ + + /* Copies our local copy of board into boards */ + memcpy((void *)&boards[num_cards],(void *)&board, sizeof(board)); + + + /* Does this get called once per lilo arg are what ? */ + + printk(KERN_INFO "PC/Xx: Added board %i, %s %i ports at 0x%4.4X base 0x%6.6X\n", + num_cards, board_desc[board.type], + board.numports, (int)board.port, (unsigned int) board.membase); + + num_cards++; + +} /* End epca_setup */ + + + +#ifdef ENABLE_PCI +/* --------------------- Begin get_PCI_configuration ---------------------- */ + +int get_PCI_configuration(char bus, char device_fn, + unsigned int *base_addr0, unsigned int *base_addr1, + unsigned int *base_addr2, unsigned int *base_addr3, + unsigned int *base_addr4, unsigned int *base_addr5) +{ /* Begin get_PCI_configuration */ + + int error; + + error = pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, + base_addr0); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, + base_addr1); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_2, + base_addr2); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_3, + base_addr3); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_4, + base_addr4); + + error |= pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_5, + base_addr5); + + /* ------------------------------------------------------------------------ + NOTE - The code below mask out either the 2 or 4 bits dependant on the + space being addressed. (base_addr value reflecting io space, have their + first 2 bits mask out, while base_addr value reflecting mem space, have + their first 4 bits mask out.) These bits are flag bits and should always + be 0 when used as an address. + ---------------------------------------------------------------------------- */ + + if ((*base_addr0) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr0) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr0) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr1) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr1) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr1) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr2) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr2) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr2) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr3) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr3) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr3) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr4) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr4) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr4) &= PCI_BASE_ADDRESS_MEM_MASK; + + if ((*base_addr5) & PCI_BASE_ADDRESS_SPACE_IO) /* Is this an io address ? */ + (*base_addr5) &= PCI_BASE_ADDRESS_IO_MASK; + else + (*base_addr5) &= PCI_BASE_ADDRESS_MEM_MASK; + + if (error) + { + printk(KERN_ERR " - DIGI PCI error: board not initializing due to error\n"); + return(0); + } + return(1); +} /* End get_PCI_configuration */ + +/* ------------------------ Begin init_PCI --------------------------- */ + +int init_PCI(int boards_found) +{ /* Begin init_PCI */ + + unsigned char bus, device_fn; + int i, pci_count = 0; + unsigned int base_addr0, base_addr1, base_addr2, + base_addr3, base_addr4, base_addr5; + + base_addr0 = base_addr1 = base_addr2 = 0; + base_addr3 = base_addr4 = base_addr5 = 0; + + for(i = 0; i < (MAXBOARDS - boards_found); i++) + { /* Begin for each POSSIBLE remaining board */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XR, + i, &bus, &device_fn)) + { /* Begin found XR */ + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Store a PCI/XR into the boards structure */ + + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXR; + + boards[boards_found + pci_count].numports = 0x0; + + boards[boards_found + pci_count].port = (unsigned char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + ioremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/XR into the board structure */ + + } /* End found XR */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XEM, + i, &bus, &device_fn)) + { /* Begin found XEM */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/XEM into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXEM; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/XEM into the boards structure */ + + } /* End found XEM */ + + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_CX, + i, &bus, &device_fn)) + { /* Begin found CX */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/CX into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCICX; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (char *)((char *)base_addr0 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr0; + + if (base_addr0 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr0 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr0, 0x200000); + } + + pci_count++; + /* -------------------------------------------------------------- + I don't know what the below does, but the hardware guys say + its required on everything except PLX (In this case XRJ). + ---------------------------------------------------------------- */ + pcibios_write_config_byte(bus, device_fn, 0x40, 0); + pcibios_write_config_byte(bus, device_fn, 0x46, 0); + + } /* End store a PCI/CX into the boards structure */ + + } /* End found CX */ + + if (!pcibios_find_device(PCI_VENDOR_DIGI, PCI_DEVICE_XRJ, + i, &bus, &device_fn)) + { /* Begin found XRJ */ + + if (get_PCI_configuration(bus, device_fn, &base_addr0, &base_addr1, + &base_addr2, &base_addr3, + &base_addr4, &base_addr5)) + { /* Begin store a PCI/XRJ into the boards structure */ + + boards[boards_found + pci_count].status = ENABLED; + boards[boards_found + pci_count].type = PCIXRJ; + + boards[boards_found + pci_count].numports = 0x0; + boards[boards_found + pci_count].port = (unsigned char *)(base_addr2 + PCI_IO_OFFSET); + /* Most cards use base_addr0, but some use base_addr2 */ + boards[boards_found + pci_count].membase = (unsigned char *)base_addr2; + + if (base_addr2 >= 0x100000) + { + /* ------------------------------------------------------------ + Standard physical addresses are valid to the kernel as long + as they aren't above RAM. Higher addresses (Such as are + typical of a PCI system) need to be mapped in with the + ioremap command. For boards using such high addresses the + driver will store both addresses. One address (The physical) + will be held for the apps use (And mmap) and the other (The + vremapped address) will be used in the kernel. + + ---------------------------------------------------------------- */ + boards[boards_found + pci_count].re_map_port = ioremap((base_addr2 + PCI_IO_OFFSET),0x200000); + boards[boards_found + pci_count].re_map_membase = ioremap(base_addr2, 0x200000); + } + + pci_count++; + + } /* End store a PCI/XRJ into the boards structure */ + + } /* End found XRJ */ + + } /* End for each POSSIBLE remaining board */ + + return(pci_count); + +} /* End init_PCI */ + +#endif /* ENABLE_PCI */ + + diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 969fb552ee7d..adac2831a9b0 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1763,6 +1763,9 @@ __initfunc(int tty_init(void)) #ifdef CONFIG_DIGI pcxe_init(); #endif +#ifdef CONFIG_DIGIEPCA + pc_init(); +#endif #ifdef CONFIG_RISCOM8 riscom8_init(); #endif diff --git a/include/asm-i386/locks.h b/include/asm-i386/locks.h index a506f3399494..357d309fa23c 100644 --- a/include/asm-i386/locks.h +++ b/include/asm-i386/locks.h @@ -12,7 +12,7 @@ extern __inline__ void prim_spin_lock(struct spinlock *sp) { - int processor=hard_smp_processor_id(); + int processor=smp_processor_id(); /* * Grab the lock bit diff --git a/include/linux/digi1.h b/include/linux/digi1.h new file mode 100644 index 000000000000..184378d23f8c --- /dev/null +++ b/include/linux/digi1.h @@ -0,0 +1,100 @@ +/* Definitions for DigiBoard ditty(1) command. */ + +#if !defined(TIOCMODG) +#define TIOCMODG ('d'<<8) | 250 /* get modem ctrl state */ +#define TIOCMODS ('d'<<8) | 251 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMSET) +#define TIOCMSET ('d'<<8) | 252 /* set modem ctrl state */ +#define TIOCMGET ('d'<<8) | 253 /* set modem ctrl state */ +#endif + +#if !defined(TIOCMBIC) +#define TIOCMBIC ('d'<<8) | 254 /* set modem ctrl state */ +#define TIOCMBIS ('d'<<8) | 255 /* set modem ctrl state */ +#endif + +#if !defined(TIOCSDTR) +#define TIOCSDTR ('e'<<8) | 0 /* set DTR */ +#define TIOCCDTR ('e'<<8) | 1 /* clear DTR */ +#endif + +/************************************************************************ + * Ioctl command arguments for DIGI parameters. + ************************************************************************/ +#define DIGI_GETA ('e'<<8) | 94 /* Read params */ + +#define DIGI_SETA ('e'<<8) | 95 /* Set params */ +#define DIGI_SETAW ('e'<<8) | 96 /* Drain & set params */ +#define DIGI_SETAF ('e'<<8) | 97 /* Drain, flush & set params */ + +#define DIGI_GETFLOW ('e'<<8) | 99 /* Get startc/stopc flow */ + /* control characters */ +#define DIGI_SETFLOW ('e'<<8) | 100 /* Set startc/stopc flow */ + /* control characters */ +#define DIGI_GETAFLOW ('e'<<8) | 101 /* Get Aux. startc/stopc */ + /* flow control chars */ +#define DIGI_SETAFLOW ('e'<<8) | 102 /* Set Aux. startc/stopc */ + /* flow control chars */ + +#define DIGI_GETINFO ('e'<<8) | 103 /* Fill in digi_info */ +#define DIGI_POLLER ('e'<<8) | 104 /* Turn on/off poller */ +#define DIGI_INIT ('e'<<8) | 105 /* Allow things to run. */ + +struct digiflow_struct +{ + unsigned char startc; /* flow cntl start char */ + unsigned char stopc; /* flow cntl stop char */ +}; + +typedef struct digiflow_struct digiflow_t; + + +/************************************************************************ + * Values for digi_flags + ************************************************************************/ +#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */ +#define DIGI_FAST 0x0002 /* Fast baud rates */ +#define RTSPACE 0x0004 /* RTS input flow control */ +#define CTSPACE 0x0008 /* CTS output flow control */ +#define DSRPACE 0x0010 /* DSR output flow control */ +#define DCDPACE 0x0020 /* DCD output flow control */ +#define DTRPACE 0x0040 /* DTR input flow control */ +#define DIGI_FORCEDCD 0x0100 /* Force carrier */ +#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */ +#define DIGI_AIXON 0x0400 /* Aux flow control in fep */ + + +/************************************************************************ + * Values for digiDload + ************************************************************************/ +#define NORMAL 0 +#define PCI_CTL 1 + +#define SIZE8 0 +#define SIZE16 1 +#define SIZE32 2 + +/************************************************************************ + * Structure used with ioctl commands for DIGI parameters. + ************************************************************************/ +struct digi_struct +{ + unsigned short digi_flags; /* Flags (see above) */ +}; + +typedef struct digi_struct digi_t; + +struct digi_info +{ + unsigned long board; /* Which board is this ? */ + unsigned char status; /* Alive or dead */ + unsigned char type; /* see epca.h */ + unsigned char subtype; /* For future XEM, XR, etc ... */ + unsigned short numports; /* Number of ports configured */ + unsigned char *port; /* I/O Address */ + unsigned char *membase; /* DPR Address */ + unsigned char *version; /* For future ... */ + unsigned short windowData; /* For future ... */ +} ; diff --git a/include/linux/digiFep1.h b/include/linux/digiFep1.h new file mode 100644 index 000000000000..c47d7fcb8400 --- /dev/null +++ b/include/linux/digiFep1.h @@ -0,0 +1,136 @@ + +#define CSTART 0x400L +#define CMAX 0x800L +#define ISTART 0x800L +#define IMAX 0xC00L +#define CIN 0xD10L +#define GLOBAL 0xD10L +#define EIN 0xD18L +#define FEPSTAT 0xD20L +#define CHANSTRUCT 0x1000L +#define RXTXBUF 0x4000L + + +struct global_data +{ + volatile ushort cin; + volatile ushort cout; + volatile ushort cstart; + volatile ushort cmax; + volatile ushort ein; + volatile ushort eout; + volatile ushort istart; + volatile ushort imax; +}; + + +struct board_chan +{ + int filler1; + int filler2; + volatile ushort tseg; + volatile ushort tin; + volatile ushort tout; + volatile ushort tmax; + + volatile ushort rseg; + volatile ushort rin; + volatile ushort rout; + volatile ushort rmax; + + volatile ushort tlow; + volatile ushort rlow; + volatile ushort rhigh; + volatile ushort incr; + + volatile ushort etime; + volatile ushort edelay; + volatile unchar *dev; + + volatile ushort iflag; + volatile ushort oflag; + volatile ushort cflag; + volatile ushort gmask; + + volatile ushort col; + volatile ushort delay; + volatile ushort imask; + volatile ushort tflush; + + int filler3; + int filler4; + int filler5; + int filler6; + + volatile unchar num; + volatile unchar ract; + volatile unchar bstat; + volatile unchar tbusy; + volatile unchar iempty; + volatile unchar ilow; + volatile unchar idata; + volatile unchar eflag; + + volatile unchar tflag; + volatile unchar rflag; + volatile unchar xmask; + volatile unchar xval; + volatile unchar mstat; + volatile unchar mchange; + volatile unchar mint; + volatile unchar lstat; + + volatile unchar mtran; + volatile unchar orun; + volatile unchar startca; + volatile unchar stopca; + volatile unchar startc; + volatile unchar stopc; + volatile unchar vnext; + volatile unchar hflow; + + volatile unchar fillc; + volatile unchar ochar; + volatile unchar omask; + + unchar filler7; + unchar filler8[28]; +}; + + +#define SRXLWATER 0xE0 +#define SRXHWATER 0xE1 +#define STOUT 0xE2 +#define PAUSETX 0xE3 +#define RESUMETX 0xE4 +#define SAUXONOFFC 0xE6 +#define SENDBREAK 0xE8 +#define SETMODEM 0xE9 +#define SETIFLAGS 0xEA +#define SONOFFC 0xEB +#define STXLWATER 0xEC +#define PAUSERX 0xEE +#define RESUMERX 0xEF +#define SETBUFFER 0xF2 +#define SETCOOKED 0xF3 +#define SETHFLOW 0xF4 +#define SETCTRLFLAGS 0xF5 +#define SETVNEXT 0xF6 + + + +#define BREAK_IND 0x01 +#define LOWTX_IND 0x02 +#define EMPTYTX_IND 0x04 +#define DATA_IND 0x08 +#define MODEMCHG_IND 0x20 + +#define FEP_HUPCL 0002000 +#if 0 +#define RTS 0x02 +#define CD 0x08 +#define DSR 0x10 +#define CTS 0x20 +#define RI 0x40 +#define DTR 0x80 +#endif diff --git a/include/linux/digiPCI.h b/include/linux/digiPCI.h new file mode 100644 index 000000000000..6ca7819e5069 --- /dev/null +++ b/include/linux/digiPCI.h @@ -0,0 +1,42 @@ +/************************************************************************* + * Defines and structure definitions for PCI BIOS Interface + *************************************************************************/ +#define PCIMAX 32 /* maximum number of PCI boards */ + + +#define PCI_VENDOR_DIGI 0x114F +#define PCI_DEVICE_EPC 0x0002 +#define PCI_DEVICE_RIGHTSWITCH 0x0003 /* For testing */ +#define PCI_DEVICE_XEM 0x0004 +#define PCI_DEVICE_XR 0x0005 +#define PCI_DEVICE_CX 0x0006 +#define PCI_DEVICE_XRJ 0x0009 /* Jupiter boards with */ +#define PCI_DEVICE_EPCJ 0x000a /* PLX 9060 chip for PCI */ + + +/* + * On the PCI boards, there is no IO space allocated + * The I/O registers will be in the first 3 bytes of the + * upper 2MB of the 4MB memory space. The board memory + * will be mapped into the low 2MB of the 4MB memory space + */ + +/* Potential location of PCI Bios from E0000 to FFFFF*/ +#define PCI_BIOS_SIZE 0x00020000 + +/* Size of Memory and I/O for PCI (4MB) */ +#define PCI_RAM_SIZE 0x00400000 + +/* Size of Memory (2MB) */ +#define PCI_MEM_SIZE 0x00200000 + +/* Offset of I/0 in Memory (2MB) */ +#define PCI_IO_OFFSET 0x00200000 + +#define MEMOUTB(basemem, pnum, setmemval) *(caddr_t)((basemem) + ( PCI_IO_OFFSET | pnum << 4 | pnum )) = (setmemval) +#define MEMINB(basemem, pnum) *(caddr_t)((basemem) + (PCI_IO_OFFSET | pnum << 4 | pnum )) /* for PCI I/O */ + + + + + diff --git a/include/linux/epca.h b/include/linux/epca.h new file mode 100644 index 000000000000..504948149d0b --- /dev/null +++ b/include/linux/epca.h @@ -0,0 +1,170 @@ +#define XEMPORTS 0xC02 +#define XEPORTS 0xC22 + +#define MAX_ALLOC 0x100 + +#define MAXBOARDS 12 +#define FEPCODESEG 0x0200L +#define FEPCODE 0x2000L +#define BIOSCODE 0xf800L + +#define MISCGLOBAL 0x0C00L +#define NPORT 0x0C22L +#define MBOX 0x0C40L +#define PORTBASE 0x0C90L + +/* Begin code defines used for epca_setup */ + +#define INVALID_BOARD_TYPE 0x1 +#define INVALID_NUM_PORTS 0x2 +#define INVALID_MEM_BASE 0x4 +#define INVALID_PORT_BASE 0x8 +#define INVALID_BOARD_STATUS 0x10 +#define INVALID_ALTPIN 0x20 + +/* End code defines used for epca_setup */ + + +#define FEPCLR 0x00 +#define FEPMEM 0x02 +#define FEPRST 0x04 +#define FEPINT 0x08 +#define FEPMASK 0x0e +#define FEPWIN 0x80 + +#define PCXE 0 +#define PCXEVE 1 +#define PCXEM 2 +#define EISAXEM 3 +#define PC64XE 4 +#define PCXI 5 +#define PCIXEM 7 +#define PCICX 8 +#define PCIXR 9 +#define PCIXRJ 10 +#define EPCA_NUM_TYPES 6 + + +static char *board_desc[] = +{ + "PC/Xe", + "PC/Xeve", + "PC/Xem", + "EISA/Xem", + "PC/64Xe", + "PC/Xi", + "unknown", + "PCI/Xem", + "PCI/CX", + "PCI/Xr", + "PCI/Xrj", +}; + +#define STARTC 021 +#define STOPC 023 +#define IAIXON 0x2000 + + +#define TXSTOPPED 0x1 +#define LOWWAIT 0x2 +#define EMPTYWAIT 0x4 +#define RXSTOPPED 0x8 +#define TXBUSY 0x10 + +#define DISABLED 0 +#define ENABLED 1 +#define OFF 0 +#define ON 1 + +#define FEPTIMEOUT 200000 +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#define SERIAL_TYPE_INFO 3 +#define EPCA_EVENT_HANGUP 1 +#define EPCA_MAGIC 0x5c6df104L + +struct channel +{ + long magic; + unchar boardnum; + unchar channelnum; + unchar omodem; /* FEP output modem status */ + unchar imodem; /* FEP input modem status */ + unchar modemfake; /* Modem values to be forced */ + unchar modem; /* Force values */ + unchar hflow; + unchar dsr; + unchar dcd; + unchar m_rts ; /* The bits used in whatever FEP */ + unchar m_dcd ; /* is indiginous to this board to */ + unchar m_dsr ; /* represent each of the physical */ + unchar m_cts ; /* handshake lines */ + unchar m_ri ; + unchar m_dtr ; + unchar stopc; + unchar startc; + unchar stopca; + unchar startca; + unchar fepstopc; + unchar fepstartc; + unchar fepstopca; + unchar fepstartca; + unchar txwin; + unchar rxwin; + ushort fepiflag; + ushort fepcflag; + ushort fepoflag; + ushort txbufhead; + ushort txbufsize; + ushort rxbufhead; + ushort rxbufsize; + int close_delay; + int count; + int blocked_open; + int event; + int asyncflags; + uint dev; + long session; + long pgrp; + ulong statusflags; + ulong c_iflag; + ulong c_cflag; + ulong c_lflag; + ulong c_oflag; + unchar *txptr; + unchar *rxptr; + unchar *tmp_buf; + struct board_info *board; + volatile struct board_chan *brdchan; + struct digi_struct digiext; + struct tty_struct *tty; + struct termios normal_termios; + struct termios callout_termios; + struct wait_queue *open_wait; + struct wait_queue *close_wait; + struct tq_struct tqueue; + volatile struct global_data *mailbox; +}; + +struct board_info +{ + unchar status; + unchar type; + unchar altpin; + ushort numports; + unchar *port; + unchar *membase; + unchar *re_map_port; + unchar *re_map_membase; + ulong memory_seg; + void ( * memwinon ) (struct board_info *, unsigned int) ; + void ( * memwinoff ) (struct board_info *, unsigned int) ; + void ( * globalwinon ) (struct channel *) ; + void ( * txwinon ) (struct channel *) ; + void ( * rxwinon ) (struct channel *) ; + void ( * memoff ) (struct channel *) ; + void ( * assertgwinon ) (struct channel *) ; + void ( * assertmemoff ) (struct channel *) ; + unchar poller_inhibited ; +}; + diff --git a/include/linux/epcaconfig.h b/include/linux/epcaconfig.h new file mode 100644 index 000000000000..c840c6735830 --- /dev/null +++ b/include/linux/epcaconfig.h @@ -0,0 +1,8 @@ +#define NUMCARDS 1 +#define NBDEVS 2 + +struct board_info static_boards[NUMCARDS]={ + { ENABLED, 0, OFF, 2, (unchar*) 0x320, (unchar*) 0xd0000 }, +}; + +/* DO NOT HAND EDIT THIS FILE! */ diff --git a/include/linux/tty.h b/include/linux/tty.h index ca74d3ec9fe6..cab84d21299b 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -288,6 +288,7 @@ extern int lp_init(void); extern int pty_init(void); extern int tty_init(void); extern int pcxe_init(void); +extern int pc_init(void); extern int vcs_init(void); extern int cy_init(void); extern int stl_init(void); @@ -330,11 +331,15 @@ extern int n_tty_ioctl(struct tty_struct * tty, struct file * file, /* serial.c */ extern long serial_console_init(long kmem_start, long kmem_end); - + /* pcxx.c */ extern int pcxe_open(struct tty_struct *tty, struct file *filp); +/* epca.c */ + +extern int pc_open(struct tty_struct *tty, struct file *filp); + /* console.c */ extern void update_screen(int new_console); diff --git a/init/main.c b/init/main.c index 0d7d8570a465..0f6cba379873 100644 --- a/init/main.c +++ b/init/main.c @@ -175,6 +175,9 @@ extern void teles_setup(char *str, int *ints); #ifdef CONFIG_ISDN_DRV_HISAX extern void HiSax_setup(char *str, int *ints); #endif +#ifdef CONFIG_DIGIEPCA +extern void epca_setup(char *str, int *ints); +#endif #ifdef CONFIG_ISDN_DRV_PCBIT extern void pcbit_setup(char *str, int *ints); #endif @@ -481,6 +484,9 @@ struct { #ifdef CONFIG_DIGI { "digi=", pcxx_setup }, #endif +#ifdef CONFIG_DIGIEPCA + { "digiepca=", epca_setup }, +#endif #ifdef CONFIG_RISCOM8 { "riscom8=", riscom8_setup }, #endif diff --git a/kernel/exit.c b/kernel/exit.c index 55a9dbf48316..1abe1ef2ea1b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -520,7 +520,7 @@ static void exit_notify(void) NORET_TYPE void do_exit(long code) { if (in_interrupt()) { - local_irq_count[hard_smp_processor_id()] = 0; /* Not really correct */ + local_irq_count[smp_processor_id()] = 0; /* Not really correct */ printk("Aiee, killing interrupt handler\n"); } fake_volatile: diff --git a/kernel/printk.c b/kernel/printk.c index 64d264d63374..0d5d619b0932 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -28,7 +28,7 @@ #include -#define LOG_BUF_LEN (8192*4) +#define LOG_BUF_LEN 8192 static char buf[1024]; diff --git a/kernel/softirq.c b/kernel/softirq.c index 89745bbdbb86..6b9b41aa588c 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -62,7 +62,7 @@ static inline void run_bottom_halves(void) asmlinkage void do_bottom_half(void) { - int cpu = hard_smp_processor_id(); + int cpu = smp_processor_id(); if (hardirq_trylock(cpu)) { if (softirq_trylock()) {